mirror of
				https://github.com/9001/copyparty.git
				synced 2025-10-31 03:53:31 +00:00 
			
		
		
		
	Compare commits
	
		
			30 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | c3e4d65b80 | ||
|  | 27a03510c5 | ||
|  | ed7727f7cb | ||
|  | 127ec10c0d | ||
|  | 5a9c0ad225 | ||
|  | 7e8daf650e | ||
|  | 0cf737b4ce | ||
|  | 74635e0113 | ||
|  | e5c4f49901 | ||
|  | e4654ee7f1 | ||
|  | e5d05c05ed | ||
|  | 73c4f99687 | ||
|  | 28c12ef3bf | ||
|  | eed82dbb54 | ||
|  | 2c4b4ab928 | ||
|  | 505a8fc6f6 | ||
|  | e4801d9b06 | ||
|  | 04f1b2cf3a | ||
|  | c06d928bb5 | ||
|  | ab09927e7b | ||
|  | 779437db67 | ||
|  | 28cbdb652e | ||
|  | 2b2415a7d8 | ||
|  | 746a8208aa | ||
|  | a2a041a98a | ||
|  | 10b436e449 | ||
|  | 4d62b34786 | ||
|  | 0546210687 | ||
|  | f8c11faada | ||
|  | 16d6e9be1f | 
| @@ -101,6 +101,11 @@ summary: it works! you can use it! (but technically not even close to beta) | |||||||
| * hiding the contents at url `/d1/d2/d3` using `-v :d1/d2/d3:cd2d` has the side-effect of creating databases (for files/tags) inside folders d1 and d2, and those databases take precedence over the main db at the top of the vfs - this means all files in d2 and below will be reindexed unless you already had a vfs entry at or below d2 | * hiding the contents at url `/d1/d2/d3` using `-v :d1/d2/d3:cd2d` has the side-effect of creating databases (for files/tags) inside folders d1 and d2, and those databases take precedence over the main db at the top of the vfs - this means all files in d2 and below will be reindexed unless you already had a vfs entry at or below d2 | ||||||
| * probably more, pls let me know | * probably more, pls let me know | ||||||
|  |  | ||||||
|  | ## not my bugs | ||||||
|  |  | ||||||
|  | * Windows: msys2-python 3.8.6 occasionally throws "RuntimeError: release unlocked lock" when leaving a scoped mutex in up2k | ||||||
|  |   * this is an msys2 bug, the regular windows edition of python is fine | ||||||
|  |  | ||||||
|  |  | ||||||
| # usage | # usage | ||||||
|  |  | ||||||
| @@ -111,6 +116,8 @@ the browser has the following hotkeys | |||||||
| * `I/K` prev/next folder | * `I/K` prev/next folder | ||||||
| * `P` parent folder | * `P` parent folder | ||||||
|  |  | ||||||
|  | you can link a particular timestamp in an audio file by adding it to the URL, such as `&20` / `&20s` / `&1m20` / `&1:20` after the `.../#af-c8960dab` | ||||||
|  |  | ||||||
|  |  | ||||||
| ## zip downloads | ## zip downloads | ||||||
|  |  | ||||||
| @@ -339,7 +346,6 @@ in the `scripts` folder: | |||||||
|  |  | ||||||
| roughly sorted by priority | roughly sorted by priority | ||||||
|  |  | ||||||
| * audio link with timestamp |  | ||||||
| * separate sqlite table per tag | * separate sqlite table per tag | ||||||
| * audio fingerprinting | * audio fingerprinting | ||||||
| * readme.md as epilogue | * readme.md as epilogue | ||||||
|   | |||||||
| @@ -16,6 +16,8 @@ if platform.system() == "Windows": | |||||||
| VT100 = not WINDOWS or WINDOWS >= [10, 0, 14393] | VT100 = not WINDOWS or WINDOWS >= [10, 0, 14393] | ||||||
| # introduced in anniversary update | # introduced in anniversary update | ||||||
|  |  | ||||||
|  | ANYWIN = WINDOWS or sys.platform in ["msys"] | ||||||
|  |  | ||||||
| MACOS = platform.system() == "Darwin" | MACOS = platform.system() == "Darwin" | ||||||
|  |  | ||||||
|  |  | ||||||
|   | |||||||
| @@ -247,6 +247,7 @@ def run_argparse(argv, formatter): | |||||||
|     ap.add_argument("--no-zip", action="store_true", help="disable download as zip/tar") |     ap.add_argument("--no-zip", action="store_true", help="disable download as zip/tar") | ||||||
|     ap.add_argument("--no-sendfile", action="store_true", help="disable sendfile (for debugging)") |     ap.add_argument("--no-sendfile", action="store_true", help="disable sendfile (for debugging)") | ||||||
|     ap.add_argument("--no-scandir", action="store_true", help="disable scandir (for debugging)") |     ap.add_argument("--no-scandir", action="store_true", help="disable scandir (for debugging)") | ||||||
|  |     ap.add_argument("--sparse", metavar="MiB", type=int, default=4, help="up2k min.size threshold (mswin-only)") | ||||||
|     ap.add_argument("--urlform", metavar="MODE", type=str, default="print,get", help="how to handle url-forms") |     ap.add_argument("--urlform", metavar="MODE", type=str, default="print,get", help="how to handle url-forms") | ||||||
|     ap.add_argument("--salt", type=str, default="hunter2", help="up2k file-hash salt") |     ap.add_argument("--salt", type=str, default="hunter2", help="up2k file-hash salt") | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,8 +1,8 @@ | |||||||
| # coding: utf-8 | # coding: utf-8 | ||||||
|  |  | ||||||
| VERSION = (0, 10, 9) | VERSION = (0, 10, 15) | ||||||
| CODENAME = "zip it" | CODENAME = "zip it" | ||||||
| BUILD_DT = (2021, 4, 17) | BUILD_DT = (2021, 4, 24) | ||||||
|  |  | ||||||
| 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) | ||||||
|   | |||||||
| @@ -13,7 +13,7 @@ import ctypes | |||||||
| from datetime import datetime | from datetime import datetime | ||||||
| import calendar | import calendar | ||||||
|  |  | ||||||
| from .__init__ import E, PY2, WINDOWS | 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 .szip import StreamZip | from .szip import StreamZip | ||||||
| from .star import StreamTar | from .star import StreamTar | ||||||
| @@ -182,10 +182,8 @@ class HttpCli(object): | |||||||
|         self.out_headers.update(headers) |         self.out_headers.update(headers) | ||||||
|  |  | ||||||
|         # default to utf8 html if no content-type is set |         # default to utf8 html if no content-type is set | ||||||
|         try: |         if not mime: | ||||||
|             mime = mime or self.out_headers["Content-Type"] |             mime = self.out_headers.get("Content-Type", "text/html; charset=UTF-8") | ||||||
|         except KeyError: |  | ||||||
|             mime = "text/html; charset=UTF-8" |  | ||||||
|  |  | ||||||
|         self.out_headers["Content-Type"] = mime |         self.out_headers["Content-Type"] = mime | ||||||
|  |  | ||||||
| @@ -261,12 +259,14 @@ class HttpCli(object): | |||||||
|  |  | ||||||
|                 self.absolute_urls = True |                 self.absolute_urls = True | ||||||
|  |  | ||||||
|         # go home if verboten |  | ||||||
|         self.readable, self.writable = self.conn.auth.vfs.can_access( |         self.readable, self.writable = self.conn.auth.vfs.can_access( | ||||||
|             self.vpath, self.uname |             self.vpath, self.uname | ||||||
|         ) |         ) | ||||||
|         if not self.readable and not self.writable: |         if not self.readable and not self.writable: | ||||||
|  |             if self.vpath: | ||||||
|                 self.log("inaccessible: [{}]".format(self.vpath)) |                 self.log("inaccessible: [{}]".format(self.vpath)) | ||||||
|  |                 raise Pebkac(404) | ||||||
|  |  | ||||||
|             self.uparam = {"h": False} |             self.uparam = {"h": False} | ||||||
|  |  | ||||||
|         if "h" in self.uparam: |         if "h" in self.uparam: | ||||||
| @@ -534,7 +534,7 @@ class HttpCli(object): | |||||||
|             self.log("qj: " + repr(vbody)) |             self.log("qj: " + repr(vbody)) | ||||||
|             hits = idx.fsearch(vols, body) |             hits = idx.fsearch(vols, body) | ||||||
|             msg = repr(hits) |             msg = repr(hits) | ||||||
|             taglist = [] |             taglist = {} | ||||||
|         else: |         else: | ||||||
|             # search by query params |             # search by query params | ||||||
|             self.log("qj: " + repr(body)) |             self.log("qj: " + repr(body)) | ||||||
| @@ -626,7 +626,7 @@ class HttpCli(object): | |||||||
|             self.loud_reply(x, status=500) |             self.loud_reply(x, status=500) | ||||||
|             return False |             return False | ||||||
|  |  | ||||||
|         if not WINDOWS and num_left == 0: |         if not ANYWIN and num_left == 0: | ||||||
|             times = (int(time.time()), int(lastmod)) |             times = (int(time.time()), int(lastmod)) | ||||||
|             self.log("no more chunks, setting times {}".format(times)) |             self.log("no more chunks, setting times {}".format(times)) | ||||||
|             try: |             try: | ||||||
| @@ -680,7 +680,7 @@ class HttpCli(object): | |||||||
|                 raise Pebkac(500, "mkdir failed, check the logs") |                 raise Pebkac(500, "mkdir failed, check the logs") | ||||||
|  |  | ||||||
|         vpath = "{}/{}".format(self.vpath, sanitized).lstrip("/") |         vpath = "{}/{}".format(self.vpath, sanitized).lstrip("/") | ||||||
|         esc_paths = [quotep(vpath), html_escape(vpath)] |         esc_paths = [quotep(vpath), html_escape(vpath, crlf=True)] | ||||||
|         html = self.j2( |         html = self.j2( | ||||||
|             "msg", |             "msg", | ||||||
|             h2='<a href="/{}">go to /{}</a>'.format(*esc_paths), |             h2='<a href="/{}">go to /{}</a>'.format(*esc_paths), | ||||||
| @@ -1181,17 +1181,16 @@ class HttpCli(object): | |||||||
|         template = self.j2(tpl) |         template = self.j2(tpl) | ||||||
|  |  | ||||||
|         st = os.stat(fsenc(fs_path)) |         st = os.stat(fsenc(fs_path)) | ||||||
|         # sz_md = st.st_size |  | ||||||
|         ts_md = st.st_mtime |         ts_md = st.st_mtime | ||||||
|  |  | ||||||
|         st = os.stat(fsenc(html_path)) |         st = os.stat(fsenc(html_path)) | ||||||
|         ts_html = st.st_mtime |         ts_html = st.st_mtime | ||||||
|  |  | ||||||
|         # TODO dont load into memory ;_; |         sz_md = 0 | ||||||
|         #   (trivial fix, count the &'s) |         for buf in yieldfile(fs_path): | ||||||
|         with open(fsenc(fs_path), "rb") as f: |             sz_md += len(buf) | ||||||
|             md = f.read().replace(b"&", b"&") |             for c, v in [[b"&", 4], [b"<", 3], [b">", 3]]: | ||||||
|             sz_md = len(md) |                 sz_md += (len(buf) - len(buf.replace(c, b""))) * v | ||||||
|  |  | ||||||
|         file_ts = max(ts_md, ts_html) |         file_ts = max(ts_md, ts_html) | ||||||
|         file_lastmod, do_send = self._chk_lastmod(file_ts) |         file_lastmod, do_send = self._chk_lastmod(file_ts) | ||||||
| @@ -1199,27 +1198,34 @@ class HttpCli(object): | |||||||
|         self.out_headers["Cache-Control"] = "no-cache" |         self.out_headers["Cache-Control"] = "no-cache" | ||||||
|         status = 200 if do_send else 304 |         status = 200 if do_send else 304 | ||||||
|  |  | ||||||
|  |         boundary = "\roll\tide" | ||||||
|         targs = { |         targs = { | ||||||
|             "edit": "edit" in self.uparam, |             "edit": "edit" in self.uparam, | ||||||
|             "title": html_escape(self.vpath), |             "title": html_escape(self.vpath, crlf=True), | ||||||
|             "lastmod": int(ts_md * 1000), |             "lastmod": int(ts_md * 1000), | ||||||
|             "md_plug": "true" if self.args.emp else "false", |             "md_plug": "true" if self.args.emp else "false", | ||||||
|             "md_chk_rate": self.args.mcr, |             "md_chk_rate": self.args.mcr, | ||||||
|             "md": "", |             "md": boundary, | ||||||
|         } |         } | ||||||
|         sz_html = len(template.render(**targs).encode("utf-8")) |         html = template.render(**targs).encode("utf-8") | ||||||
|         self.send_headers(sz_html + sz_md, status) |         html = html.split(boundary.encode("utf-8")) | ||||||
|  |         if len(html) != 2: | ||||||
|  |             raise Exception("boundary appears in " + html_path) | ||||||
|  |  | ||||||
|  |         self.send_headers(sz_md + len(html[0]) + len(html[1]), status) | ||||||
|  |  | ||||||
|         logmsg += unicode(status) |         logmsg += unicode(status) | ||||||
|         if self.mode == "HEAD" or not do_send: |         if self.mode == "HEAD" or not do_send: | ||||||
|             self.log(logmsg) |             self.log(logmsg) | ||||||
|             return True |             return True | ||||||
|  |  | ||||||
|         # TODO jinja2 can stream this right? |  | ||||||
|         targs["md"] = md.decode("utf-8", "replace") |  | ||||||
|         html = template.render(**targs).encode("utf-8") |  | ||||||
|         try: |         try: | ||||||
|             self.s.sendall(html) |             self.s.sendall(html[0]) | ||||||
|  |             for buf in yieldfile(fs_path): | ||||||
|  |                 self.s.sendall(html_bescape(buf)) | ||||||
|  |  | ||||||
|  |             self.s.sendall(html[1]) | ||||||
|  |  | ||||||
|         except: |         except: | ||||||
|             self.log(logmsg + " \033[31md/c\033[0m") |             self.log(logmsg + " \033[31md/c\033[0m") | ||||||
|             return False |             return False | ||||||
| @@ -1300,7 +1306,7 @@ class HttpCli(object): | |||||||
|                 else: |                 else: | ||||||
|                     vpath += "/" + node |                     vpath += "/" + node | ||||||
|  |  | ||||||
|                 vpnodes.append([quotep(vpath) + "/", html_escape(node)]) |                 vpnodes.append([quotep(vpath) + "/", html_escape(node, crlf=True)]) | ||||||
|  |  | ||||||
|         vn, rem = self.auth.vfs.get( |         vn, rem = self.auth.vfs.get( | ||||||
|             self.vpath, self.uname, self.readable, self.writable |             self.vpath, self.uname, self.readable, self.writable | ||||||
| @@ -1311,6 +1317,77 @@ class HttpCli(object): | |||||||
|             # print(abspath) |             # print(abspath) | ||||||
|             raise Pebkac(404) |             raise Pebkac(404) | ||||||
|  |  | ||||||
|  |         srv_info = [] | ||||||
|  |  | ||||||
|  |         try: | ||||||
|  |             if not self.args.nih: | ||||||
|  |                 srv_info.append(unicode(socket.gethostname()).split(".")[0]) | ||||||
|  |         except: | ||||||
|  |             self.log("#wow #whoa") | ||||||
|  |  | ||||||
|  |         try: | ||||||
|  |             # some fuses misbehave | ||||||
|  |             if not self.args.nid: | ||||||
|  |                 if WINDOWS: | ||||||
|  |                     bfree = ctypes.c_ulonglong(0) | ||||||
|  |                     ctypes.windll.kernel32.GetDiskFreeSpaceExW( | ||||||
|  |                         ctypes.c_wchar_p(abspath), None, None, ctypes.pointer(bfree) | ||||||
|  |                     ) | ||||||
|  |                     srv_info.append(humansize(bfree.value) + " free") | ||||||
|  |                 else: | ||||||
|  |                     sv = os.statvfs(abspath) | ||||||
|  |                     free = humansize(sv.f_frsize * sv.f_bfree, True) | ||||||
|  |                     total = humansize(sv.f_frsize * sv.f_blocks, True) | ||||||
|  |  | ||||||
|  |                     srv_info.append(free + " free") | ||||||
|  |                     srv_info.append(total) | ||||||
|  |         except: | ||||||
|  |             pass | ||||||
|  |  | ||||||
|  |         srv_info = "</span> /// <span>".join(srv_info) | ||||||
|  |  | ||||||
|  |         perms = [] | ||||||
|  |         if self.readable: | ||||||
|  |             perms.append("read") | ||||||
|  |         if self.writable: | ||||||
|  |             perms.append("write") | ||||||
|  |  | ||||||
|  |         url_suf = self.urlq() | ||||||
|  |         is_ls = "ls" in self.uparam | ||||||
|  |         ts = ""  # "?{}".format(time.time()) | ||||||
|  |  | ||||||
|  |         tpl = "browser" | ||||||
|  |         if "b" in self.uparam: | ||||||
|  |             tpl = "browser2" | ||||||
|  |  | ||||||
|  |         j2a = { | ||||||
|  |             "vdir": quotep(self.vpath), | ||||||
|  |             "vpnodes": vpnodes, | ||||||
|  |             "files": [], | ||||||
|  |             "ts": ts, | ||||||
|  |             "perms": json.dumps(perms), | ||||||
|  |             "taglist": [], | ||||||
|  |             "tag_order": [], | ||||||
|  |             "have_up2k_idx": ("e2d" in vn.flags), | ||||||
|  |             "have_tags_idx": ("e2t" in vn.flags), | ||||||
|  |             "have_zip": (not self.args.no_zip), | ||||||
|  |             "have_b_u": (self.writable and self.uparam.get("b") == "u"), | ||||||
|  |             "url_suf": url_suf, | ||||||
|  |             "logues": ["", ""], | ||||||
|  |             "title": html_escape(self.vpath, crlf=True), | ||||||
|  |             "srv_info": srv_info, | ||||||
|  |         } | ||||||
|  |         if not self.readable: | ||||||
|  |             if is_ls: | ||||||
|  |                 raise Pebkac(403) | ||||||
|  |  | ||||||
|  |             if not os.path.isdir(fsenc(abspath)): | ||||||
|  |                 raise Pebkac(404) | ||||||
|  |  | ||||||
|  |             html = self.j2(tpl, **j2a) | ||||||
|  |             self.reply(html.encode("utf-8", "replace")) | ||||||
|  |             return True | ||||||
|  |  | ||||||
|         if not os.path.isdir(fsenc(abspath)): |         if not os.path.isdir(fsenc(abspath)): | ||||||
|             if abspath.endswith(".md") and "raw" not in self.uparam: |             if abspath.endswith(".md") and "raw" not in self.uparam: | ||||||
|                 return self.tx_md(abspath) |                 return self.tx_md(abspath) | ||||||
| @@ -1354,15 +1431,11 @@ class HttpCli(object): | |||||||
|         if rem == ".hist": |         if rem == ".hist": | ||||||
|             hidden = ["up2k."] |             hidden = ["up2k."] | ||||||
|  |  | ||||||
|         is_ls = "ls" in self.uparam |  | ||||||
|  |  | ||||||
|         icur = None |         icur = None | ||||||
|         if "e2t" in vn.flags: |         if "e2t" in vn.flags: | ||||||
|             idx = self.conn.get_u2idx() |             idx = self.conn.get_u2idx() | ||||||
|             icur = idx.get_cur(vn.realpath) |             icur = idx.get_cur(vn.realpath) | ||||||
|  |  | ||||||
|         url_suf = self.urlq() |  | ||||||
|  |  | ||||||
|         dirs = [] |         dirs = [] | ||||||
|         files = [] |         files = [] | ||||||
|         for fn in vfs_ls: |         for fn in vfs_ls: | ||||||
| @@ -1394,7 +1467,7 @@ class HttpCli(object): | |||||||
|                     margin = '<a href="{}?zip">zip</a>'.format(quotep(href)) |                     margin = '<a href="{}?zip">zip</a>'.format(quotep(href)) | ||||||
|             elif fn in hist: |             elif fn in hist: | ||||||
|                 margin = '<a href="{}.hist/{}">#{}</a>'.format( |                 margin = '<a href="{}.hist/{}">#{}</a>'.format( | ||||||
|                     base, html_escape(hist[fn][2], quote=True), hist[fn][0] |                     base, html_escape(hist[fn][2], quote=True, crlf=True), hist[fn][0] | ||||||
|                 ) |                 ) | ||||||
|             else: |             else: | ||||||
|                 margin = "-" |                 margin = "-" | ||||||
| @@ -1453,42 +1526,6 @@ class HttpCli(object): | |||||||
|             for f in dirs: |             for f in dirs: | ||||||
|                 f["tags"] = {} |                 f["tags"] = {} | ||||||
|  |  | ||||||
|         srv_info = [] |  | ||||||
|  |  | ||||||
|         try: |  | ||||||
|             if not self.args.nih: |  | ||||||
|                 srv_info.append(unicode(socket.gethostname()).split(".")[0]) |  | ||||||
|         except: |  | ||||||
|             self.log("#wow #whoa") |  | ||||||
|             pass |  | ||||||
|  |  | ||||||
|         try: |  | ||||||
|             # some fuses misbehave |  | ||||||
|             if not self.args.nid: |  | ||||||
|                 if WINDOWS: |  | ||||||
|                     bfree = ctypes.c_ulonglong(0) |  | ||||||
|                     ctypes.windll.kernel32.GetDiskFreeSpaceExW( |  | ||||||
|                         ctypes.c_wchar_p(abspath), None, None, ctypes.pointer(bfree) |  | ||||||
|                     ) |  | ||||||
|                     srv_info.append(humansize(bfree.value) + " free") |  | ||||||
|                 else: |  | ||||||
|                     sv = os.statvfs(abspath) |  | ||||||
|                     free = humansize(sv.f_frsize * sv.f_bfree, True) |  | ||||||
|                     total = humansize(sv.f_frsize * sv.f_blocks, True) |  | ||||||
|  |  | ||||||
|                     srv_info.append(free + " free") |  | ||||||
|                     srv_info.append(total) |  | ||||||
|         except: |  | ||||||
|             pass |  | ||||||
|  |  | ||||||
|         srv_info = "</span> /// <span>".join(srv_info) |  | ||||||
|  |  | ||||||
|         perms = [] |  | ||||||
|         if self.readable: |  | ||||||
|             perms.append("read") |  | ||||||
|         if self.writable: |  | ||||||
|             perms.append("write") |  | ||||||
|  |  | ||||||
|         logues = ["", ""] |         logues = ["", ""] | ||||||
|         for n, fn in enumerate([".prologue.html", ".epilogue.html"]): |         for n, fn in enumerate([".prologue.html", ".epilogue.html"]): | ||||||
|             fn = os.path.join(abspath, fn) |             fn = os.path.join(abspath, fn) | ||||||
| @@ -1510,34 +1547,12 @@ class HttpCli(object): | |||||||
|             self.reply(ret.encode("utf-8", "replace"), mime="application/json") |             self.reply(ret.encode("utf-8", "replace"), mime="application/json") | ||||||
|             return True |             return True | ||||||
|  |  | ||||||
|         ts = "" |         j2a["files"] = dirs + files | ||||||
|         # ts = "?{}".format(time.time()) |         j2a["logues"] = logues | ||||||
|  |         j2a["taglist"] = taglist | ||||||
|  |         if "mte" in vn.flags: | ||||||
|  |             j2a["tag_order"] = json.dumps(vn.flags["mte"].split(",")) | ||||||
|  |  | ||||||
|         dirs.extend(files) |         html = self.j2(tpl, **j2a) | ||||||
|  |  | ||||||
|         tpl = "browser" |  | ||||||
|         if "b" in self.uparam: |  | ||||||
|             tpl = "browser2" |  | ||||||
|  |  | ||||||
|         html = self.j2( |  | ||||||
|             tpl, |  | ||||||
|             vdir=quotep(self.vpath), |  | ||||||
|             vpnodes=vpnodes, |  | ||||||
|             files=dirs, |  | ||||||
|             ts=ts, |  | ||||||
|             perms=json.dumps(perms), |  | ||||||
|             taglist=taglist, |  | ||||||
|             tag_order=json.dumps( |  | ||||||
|                 vn.flags["mte"].split(",") if "mte" in vn.flags else [] |  | ||||||
|             ), |  | ||||||
|             have_up2k_idx=("e2d" in vn.flags), |  | ||||||
|             have_tags_idx=("e2t" in vn.flags), |  | ||||||
|             have_zip=(not self.args.no_zip), |  | ||||||
|             have_b_u=(self.writable and self.uparam.get("b") == "u"), |  | ||||||
|             url_suf=url_suf, |  | ||||||
|             logues=logues, |  | ||||||
|             title=html_escape(self.vpath), |  | ||||||
|             srv_info=srv_info, |  | ||||||
|         ) |  | ||||||
|         self.reply(html.encode("utf-8", "replace")) |         self.reply(html.encode("utf-8", "replace")) | ||||||
|         return True |         return True | ||||||
|   | |||||||
| @@ -16,7 +16,7 @@ import traceback | |||||||
| import subprocess as sp | import subprocess as sp | ||||||
| from copy import deepcopy | from copy import deepcopy | ||||||
|  |  | ||||||
| from .__init__ import WINDOWS | from .__init__ import WINDOWS, ANYWIN | ||||||
| from .util import ( | from .util import ( | ||||||
|     Pebkac, |     Pebkac, | ||||||
|     Queue, |     Queue, | ||||||
| @@ -79,7 +79,7 @@ class Up2k(object): | |||||||
|             if self.sqlite_ver < (3, 9): |             if self.sqlite_ver < (3, 9): | ||||||
|                 self.no_expr_idx = True |                 self.no_expr_idx = True | ||||||
|  |  | ||||||
|         if WINDOWS: |         if ANYWIN: | ||||||
|             # usually fails to set lastmod too quickly |             # usually fails to set lastmod too quickly | ||||||
|             self.lastmod_q = Queue() |             self.lastmod_q = Queue() | ||||||
|             thr = threading.Thread(target=self._lastmodder) |             thr = threading.Thread(target=self._lastmodder) | ||||||
| @@ -101,11 +101,12 @@ class Up2k(object): | |||||||
|             thr.daemon = True |             thr.daemon = True | ||||||
|             thr.start() |             thr.start() | ||||||
|  |  | ||||||
|             thr = threading.Thread(target=self._tagger) |             thr = threading.Thread(target=self._hasher) | ||||||
|             thr.daemon = True |             thr.daemon = True | ||||||
|             thr.start() |             thr.start() | ||||||
|  |  | ||||||
|             thr = threading.Thread(target=self._hasher) |             if self.mtag: | ||||||
|  |                 thr = threading.Thread(target=self._tagger) | ||||||
|                 thr.daemon = True |                 thr.daemon = True | ||||||
|                 thr.start() |                 thr.start() | ||||||
|  |  | ||||||
| @@ -667,12 +668,6 @@ class Up2k(object): | |||||||
|             cur.close() |             cur.close() | ||||||
|  |  | ||||||
|     def _start_mpool(self): |     def _start_mpool(self): | ||||||
|         if WINDOWS and False: |  | ||||||
|             nah = open(os.devnull, "wb") |  | ||||||
|             wmic = "processid={}".format(os.getpid()) |  | ||||||
|             wmic = ["wmic", "process", "where", wmic, "call", "setpriority"] |  | ||||||
|             sp.call(wmic + ["below normal"], stdout=nah, stderr=nah) |  | ||||||
|  |  | ||||||
|         # mp.pool.ThreadPool and concurrent.futures.ThreadPoolExecutor |         # mp.pool.ThreadPool and concurrent.futures.ThreadPoolExecutor | ||||||
|         # both do crazy runahead so lets reinvent another wheel |         # both do crazy runahead so lets reinvent another wheel | ||||||
|         nw = os.cpu_count() if hasattr(os, "cpu_count") else 4 |         nw = os.cpu_count() if hasattr(os, "cpu_count") else 4 | ||||||
| @@ -697,12 +692,6 @@ class Up2k(object): | |||||||
|  |  | ||||||
|         mpool.join() |         mpool.join() | ||||||
|         done = self._flush_mpool(wcur) |         done = self._flush_mpool(wcur) | ||||||
|         if WINDOWS and False: |  | ||||||
|             nah = open(os.devnull, "wb") |  | ||||||
|             wmic = "processid={}".format(os.getpid()) |  | ||||||
|             wmic = ["wmic", "process", "where", wmic, "call", "setpriority"] |  | ||||||
|             sp.call(wmic + ["below normal"], stdout=nah, stderr=nah) |  | ||||||
|  |  | ||||||
|         return done |         return done | ||||||
|  |  | ||||||
|     def _tag_thr(self, q): |     def _tag_thr(self, q): | ||||||
| @@ -1109,8 +1098,9 @@ class Up2k(object): | |||||||
|  |  | ||||||
|             atomic_move(src, dst) |             atomic_move(src, dst) | ||||||
|  |  | ||||||
|             if WINDOWS: |             if ANYWIN: | ||||||
|                 self.lastmod_q.put([dst, (int(time.time()), int(job["lmod"]))]) |                 a = [dst, job["size"], (int(time.time()), int(job["lmod"]))] | ||||||
|  |                 self.lastmod_q.put(a) | ||||||
|  |  | ||||||
|             # legit api sware 2 me mum |             # legit api sware 2 me mum | ||||||
|             if self.idx_wark( |             if self.idx_wark( | ||||||
| @@ -1211,6 +1201,17 @@ class Up2k(object): | |||||||
|         suffix = ".{:.6f}-{}".format(job["t0"], job["addr"]) |         suffix = ".{:.6f}-{}".format(job["t0"], job["addr"]) | ||||||
|         with ren_open(tnam, "wb", fdir=pdir, suffix=suffix) as f: |         with ren_open(tnam, "wb", fdir=pdir, suffix=suffix) as f: | ||||||
|             f, job["tnam"] = f["orz"] |             f, job["tnam"] = f["orz"] | ||||||
|  |             if ( | ||||||
|  |                 ANYWIN | ||||||
|  |                 and self.args.sparse | ||||||
|  |                 and self.args.sparse * 1024 * 1024 <= job["size"] | ||||||
|  |             ): | ||||||
|  |                 fp = os.path.join(pdir, job["tnam"]) | ||||||
|  |                 try: | ||||||
|  |                     sp.check_call(["fsutil", "sparse", "setflag", fp]) | ||||||
|  |                 except: | ||||||
|  |                     self.log("could not sparse [{}]".format(fp), 3) | ||||||
|  |  | ||||||
|             f.seek(job["size"] - 1) |             f.seek(job["size"] - 1) | ||||||
|             f.write(b"e") |             f.write(b"e") | ||||||
|  |  | ||||||
| @@ -1222,13 +1223,19 @@ class Up2k(object): | |||||||
|  |  | ||||||
|             # self.log("lmod: got {}".format(len(ready))) |             # self.log("lmod: got {}".format(len(ready))) | ||||||
|             time.sleep(5) |             time.sleep(5) | ||||||
|             for path, times in ready: |             for path, sz, times in ready: | ||||||
|                 self.log("lmod: setting times {} on {}".format(times, path)) |                 self.log("lmod: setting times {} on {}".format(times, path)) | ||||||
|                 try: |                 try: | ||||||
|                     os.utime(fsenc(path), times) |                     os.utime(fsenc(path), times) | ||||||
|                 except: |                 except: | ||||||
|                     self.log("lmod: failed to utime ({}, {})".format(path, times)) |                     self.log("lmod: failed to utime ({}, {})".format(path, times)) | ||||||
|  |  | ||||||
|  |                 if self.args.sparse and self.args.sparse * 1024 * 1024 <= sz: | ||||||
|  |                     try: | ||||||
|  |                         sp.check_call(["fsutil", "sparse", "setflag", path, "0"]) | ||||||
|  |                     except: | ||||||
|  |                         self.log("could not unsparse [{}]".format(path), 3) | ||||||
|  |  | ||||||
|     def _snapshot(self): |     def _snapshot(self): | ||||||
|         persist_interval = 30  # persist unfinished uploads index every 30 sec |         persist_interval = 30  # persist unfinished uploads index every 30 sec | ||||||
|         discard_interval = 21600  # drop unfinished uploads after 6 hours inactivity |         discard_interval = 21600  # drop unfinished uploads after 6 hours inactivity | ||||||
|   | |||||||
| @@ -16,7 +16,7 @@ import mimetypes | |||||||
| import contextlib | import contextlib | ||||||
| import subprocess as sp  # nosec | import subprocess as sp  # nosec | ||||||
|  |  | ||||||
| from .__init__ import PY2, WINDOWS | from .__init__ import PY2, WINDOWS, ANYWIN | ||||||
| from .stolen import surrogateescape | from .stolen import surrogateescape | ||||||
|  |  | ||||||
| FAKE_MP = False | FAKE_MP = False | ||||||
| @@ -580,8 +580,8 @@ def sanitize_fn(fn, ok=""): | |||||||
|     if "/" not in ok: |     if "/" not in ok: | ||||||
|         fn = fn.replace("\\", "/").split("/")[-1] |         fn = fn.replace("\\", "/").split("/")[-1] | ||||||
|  |  | ||||||
|     if WINDOWS: |     if ANYWIN: | ||||||
|         for bad, good in [x for x in [ |         remap = [ | ||||||
|             ["<", "<"], |             ["<", "<"], | ||||||
|             [">", ">"], |             [">", ">"], | ||||||
|             [":", ":"], |             [":", ":"], | ||||||
| @@ -591,7 +591,8 @@ def sanitize_fn(fn, ok=""): | |||||||
|             ["|", "|"], |             ["|", "|"], | ||||||
|             ["?", "?"], |             ["?", "?"], | ||||||
|             ["*", "*"], |             ["*", "*"], | ||||||
|         ] if x[0] not in ok]: |         ] | ||||||
|  |         for bad, good in [x for x in remap if x[0] not in ok]: | ||||||
|             fn = fn.replace(bad, good) |             fn = fn.replace(bad, good) | ||||||
|  |  | ||||||
|         bad = ["con", "prn", "aux", "nul"] |         bad = ["con", "prn", "aux", "nul"] | ||||||
| @@ -615,17 +616,24 @@ def exclude_dotfiles(filepaths): | |||||||
|     return [x for x in filepaths if not x.split("/")[-1].startswith(".")] |     return [x for x in filepaths if not x.split("/")[-1].startswith(".")] | ||||||
|  |  | ||||||
|  |  | ||||||
| def html_escape(s, quote=False): | def html_escape(s, quote=False, crlf=False): | ||||||
|     """html.escape but also newlines""" |     """html.escape but also newlines""" | ||||||
|     s = ( |     s = s.replace("&", "&").replace("<", "<").replace(">", ">") | ||||||
|         s.replace("&", "&") |  | ||||||
|         .replace("<", "<") |  | ||||||
|         .replace(">", ">") |  | ||||||
|         .replace("\r", "
") |  | ||||||
|         .replace("\n", "
") |  | ||||||
|     ) |  | ||||||
|     if quote: |     if quote: | ||||||
|         s = s.replace('"', """).replace("'", "'") |         s = s.replace('"', """).replace("'", "'") | ||||||
|  |     if crlf: | ||||||
|  |         s = s.replace("\r", "
").replace("\n", "
") | ||||||
|  |  | ||||||
|  |     return s | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def html_bescape(s, quote=False, crlf=False): | ||||||
|  |     """html.escape but bytestrings""" | ||||||
|  |     s = s.replace(b"&", b"&").replace(b"<", b"<").replace(b">", b">") | ||||||
|  |     if quote: | ||||||
|  |         s = s.replace(b'"', b""").replace(b"'", b"'") | ||||||
|  |     if crlf: | ||||||
|  |         s = s.replace(b"\r", b"
").replace(b"\n", b"
") | ||||||
|  |  | ||||||
|     return s |     return s | ||||||
|  |  | ||||||
|   | |||||||
| @@ -68,7 +68,7 @@ a, #files tbody div a:last-child { | |||||||
| 	color: #999; | 	color: #999; | ||||||
| 	font-weight: normal; | 	font-weight: normal; | ||||||
| } | } | ||||||
| #files tr+tr:hover { | #files tr:hover { | ||||||
| 	background: #1c1c1c; | 	background: #1c1c1c; | ||||||
| } | } | ||||||
| #files thead th { | #files thead th { | ||||||
| @@ -98,7 +98,7 @@ a, #files tbody div a:last-child { | |||||||
| 	max-width: 30em; | 	max-width: 30em; | ||||||
| 	overflow: hidden; | 	overflow: hidden; | ||||||
| } | } | ||||||
| #files tr+tr td { | #files tr td { | ||||||
| 	border-top: 1px solid #383838; | 	border-top: 1px solid #383838; | ||||||
| } | } | ||||||
| #files tbody td:nth-child(3) { | #files tbody td:nth-child(3) { | ||||||
| @@ -284,7 +284,7 @@ a, #files tbody div a:last-child { | |||||||
| 	line-height: 1em; | 	line-height: 1em; | ||||||
| } | } | ||||||
| #wtoggle.sel { | #wtoggle.sel { | ||||||
| 	width: 6em; | 	width: 6.4em; | ||||||
| } | } | ||||||
| #wtoggle.sel #wzip { | #wtoggle.sel #wzip { | ||||||
| 	display: inline-block; | 	display: inline-block; | ||||||
| @@ -685,3 +685,186 @@ input[type="checkbox"]:checked+label { | |||||||
| 	font-family: monospace, monospace; | 	font-family: monospace, monospace; | ||||||
| 	line-height: 2em; | 	line-height: 2em; | ||||||
| } | } | ||||||
|  | #pvol, | ||||||
|  | #barbuf, | ||||||
|  | #barpos, | ||||||
|  | #u2conf label { | ||||||
|  | 	-webkit-user-select: none; | ||||||
|  | 	-moz-user-select: none; | ||||||
|  | 	-ms-user-select: none; | ||||||
|  | 	user-select: none; | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | html.light { | ||||||
|  | 	color: #333; | ||||||
|  | 	background: #eee; | ||||||
|  | 	text-shadow: none; | ||||||
|  | } | ||||||
|  | html.light #ops, | ||||||
|  | html.light .opbox, | ||||||
|  | html.light #srch_form { | ||||||
|  | 	background: #f7f7f7; | ||||||
|  | 	box-shadow: 0 0 .3em #ddd; | ||||||
|  | 	border-color: #f7f7f7; | ||||||
|  | } | ||||||
|  | html.light #ops a.act { | ||||||
|  | 	box-shadow: 0 .2em .2em #ccc; | ||||||
|  | 	background: #fff; | ||||||
|  | 	border-color: #07a; | ||||||
|  | 	padding-top: .4em; | ||||||
|  | } | ||||||
|  | html.light #op_cfg h3 { | ||||||
|  | 	border-color: #ccc; | ||||||
|  | } | ||||||
|  | html.light .tglbtn, | ||||||
|  | html.light #tree > a + a { | ||||||
|  | 	color: #666; | ||||||
|  | 	background: #ddd; | ||||||
|  | 	box-shadow: none; | ||||||
|  | } | ||||||
|  | html.light .tglbtn:hover, | ||||||
|  | html.light #tree > a + a:hover { | ||||||
|  | 	background: #caf; | ||||||
|  | } | ||||||
|  | html.light .tglbtn.on, | ||||||
|  | html.light #tree > a + a.on { | ||||||
|  | 	background: #4a0; | ||||||
|  | 	color: #fff; | ||||||
|  | } | ||||||
|  | html.light #srv_info { | ||||||
|  | 	color: #c83; | ||||||
|  | 	text-shadow: 1px 1px 0 #fff; | ||||||
|  | } | ||||||
|  | html.light #srv_info span { | ||||||
|  | 	color: #000; | ||||||
|  | } | ||||||
|  | html.light #treeul a+a { | ||||||
|  | 	background: inherit; | ||||||
|  | 	color: #06a; | ||||||
|  | } | ||||||
|  | html.light #treeul a.hl { | ||||||
|  | 	background: #07a; | ||||||
|  | 	color: #fff; | ||||||
|  | } | ||||||
|  | html.light #tree li { | ||||||
|  | 	border-color: #ddd #fff #f7f7f7 #fff; | ||||||
|  | } | ||||||
|  | html.light #tree ul { | ||||||
|  | 	border-color: #ccc; | ||||||
|  | } | ||||||
|  | html.light a, | ||||||
|  | html.light #ops a, | ||||||
|  | html.light #files tbody div a:last-child { | ||||||
|  | 	color: #06a; | ||||||
|  | } | ||||||
|  | html.light #files tbody { | ||||||
|  | 	background: #f7f7f7; | ||||||
|  | } | ||||||
|  | html.light #files { | ||||||
|  | 	box-shadow: 0 0 .3em #ccc; | ||||||
|  | } | ||||||
|  | html.light #files thead th { | ||||||
|  | 	background: #eee; | ||||||
|  | } | ||||||
|  | html.light #files tr td { | ||||||
|  | 	border-top: 1px solid #ddd; | ||||||
|  | } | ||||||
|  | html.light #files td { | ||||||
|  | 	border-bottom: 1px solid #f7f7f7; | ||||||
|  | } | ||||||
|  | html.light #files tbody tr:last-child td { | ||||||
|  | 	border-bottom: .2em solid #ccc; | ||||||
|  | } | ||||||
|  | html.light #files td:nth-child(2n) { | ||||||
|  | 	color: #d38; | ||||||
|  | } | ||||||
|  | html.light #files tr:hover td { | ||||||
|  | 	background: #fff; | ||||||
|  | } | ||||||
|  | html.light #files tbody a.play { | ||||||
|  | 	color: #c0f; | ||||||
|  | } | ||||||
|  | html.light tr.play td { | ||||||
|  | 	background: #fc5; | ||||||
|  | } | ||||||
|  | html.light tr.play a { | ||||||
|  | 	color: #406; | ||||||
|  | } | ||||||
|  | html.light #files th:hover .cfg, | ||||||
|  | html.light #files th.min .cfg { | ||||||
|  | 	background: #ccc; | ||||||
|  | } | ||||||
|  | html.light #files > thead > tr > th.min span { | ||||||
|  | 	background: linear-gradient(90deg, rgba(204,204,204,0), rgba(204,204,204,0.5) 70%, #ccc); | ||||||
|  | } | ||||||
|  | html.light #blocked { | ||||||
|  | 	background: #eee; | ||||||
|  | } | ||||||
|  | html.light #blk_play a, | ||||||
|  | html.light #blk_abrt a { | ||||||
|  | 	background: #fff; | ||||||
|  | 	box-shadow: 0 .2em .4em #ddd; | ||||||
|  | } | ||||||
|  | html.light #widget a { | ||||||
|  | 	color: #fc5; | ||||||
|  | } | ||||||
|  | html.light #files tr.sel:hover td { | ||||||
|  | 	background: #c37; | ||||||
|  | } | ||||||
|  | html.light #files tr.sel td { | ||||||
|  | 	color: #fff; | ||||||
|  | } | ||||||
|  | html.light #files tr.sel a { | ||||||
|  | 	color: #fff; | ||||||
|  | } | ||||||
|  | html.light input[type="checkbox"] + label { | ||||||
|  | 	color: #333; | ||||||
|  | } | ||||||
|  | html.light .opview input[type="text"] { | ||||||
|  | 	background: #fff; | ||||||
|  | 	color: #333; | ||||||
|  | 	box-shadow: 0 0 2px #888; | ||||||
|  | 	border-color: #38d; | ||||||
|  | } | ||||||
|  | html.light #ops:hover #opdesc { | ||||||
|  | 	background: #fff; | ||||||
|  | 	box-shadow: 0 .3em 1em #ccc; | ||||||
|  | } | ||||||
|  | html.light #opdesc code { | ||||||
|  | 	background: #060; | ||||||
|  | 	color: #fff; | ||||||
|  | } | ||||||
|  | html.light #u2tab a>span, | ||||||
|  | html.light #files td div span { | ||||||
|  | 	color: #000; | ||||||
|  | } | ||||||
|  | html.light #path { | ||||||
|  | 	background: #f7f7f7; | ||||||
|  | 	text-shadow: none; | ||||||
|  | 	box-shadow: 0 0 .3em #bbb; | ||||||
|  | } | ||||||
|  | html.light #path a { | ||||||
|  | 	color: #333; | ||||||
|  | } | ||||||
|  | html.light #path a:not(:last-child)::after { | ||||||
|  | 	border-color: #ccc; | ||||||
|  | 	background: none; | ||||||
|  | 	border-width: .1em .1em 0 0; | ||||||
|  | 	margin: -.2em .3em -.2em -.3em; | ||||||
|  | } | ||||||
|  | html.light #path a:hover { | ||||||
|  | 	background: none; | ||||||
|  | 	color: #60a; | ||||||
|  | } | ||||||
|  | html.light #files tbody div a { | ||||||
|  | 	color: #d38; | ||||||
|  | } | ||||||
|  | html.light #files a:hover, | ||||||
|  | html.light #files tr.sel a:hover { | ||||||
|  | 	color: #000; | ||||||
|  | 	background: #fff; | ||||||
|  | } | ||||||
| @@ -39,14 +39,17 @@ | |||||||
|     {%- include 'upload.html' %} |     {%- include 'upload.html' %} | ||||||
|  |  | ||||||
|     <div id="op_cfg" class="opview opbox"> |     <div id="op_cfg" class="opview opbox"> | ||||||
|         <h3>key notation</h3> |         <h3>switches</h3> | ||||||
|         <div id="key_notation"></div> |         <div> | ||||||
|  |             <a id="tooltips" class="tglbtn" href="#">tooltips</a> | ||||||
|  |             <a id="lightmode" class="tglbtn" href="#">lightmode</a> | ||||||
|  |         </div> | ||||||
|         {%- if have_zip %} |         {%- if have_zip %} | ||||||
|         <h3>folder download</h3> |         <h3>folder download</h3> | ||||||
|         <div id="arc_fmt"></div> |         <div id="arc_fmt"></div> | ||||||
|         {%- endif %} |         {%- endif %} | ||||||
|         <h3>tooltips</h3> |         <h3>key notation</h3> | ||||||
|         <div><a id="tooltips" class="tglbtn" href="#">enable</a></div> |         <div id="key_notation"></div> | ||||||
|     </div> |     </div> | ||||||
|      |      | ||||||
|     <h1 id="path"> |     <h1 id="path"> | ||||||
|   | |||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -50,6 +50,9 @@ pre code:last-child { | |||||||
| pre code::before { | pre code::before { | ||||||
| 	content: counter(precode); | 	content: counter(precode); | ||||||
| 	-webkit-user-select: none; | 	-webkit-user-select: none; | ||||||
|  | 	-moz-user-select: none; | ||||||
|  | 	-ms-user-select: none; | ||||||
|  | 	user-select: none; | ||||||
| 	display: inline-block; | 	display: inline-block; | ||||||
| 	text-align: right; | 	text-align: right; | ||||||
| 	font-size: .75em; | 	font-size: .75em; | ||||||
|   | |||||||
| @@ -138,10 +138,10 @@ var md_opt = { | |||||||
|         document.documentElement.setAttribute("class", dark ? "dark" : ""); |         document.documentElement.setAttribute("class", dark ? "dark" : ""); | ||||||
|         btn.innerHTML = "go " + (dark ? "light" : "dark"); |         btn.innerHTML = "go " + (dark ? "light" : "dark"); | ||||||
|         if (window.localStorage) |         if (window.localStorage) | ||||||
|             localStorage.setItem('darkmode', dark ? 1 : 0); |             localStorage.setItem('lightmode', dark ? 0 : 1); | ||||||
|     }; |     }; | ||||||
|     btn.onclick = toggle; |     btn.onclick = toggle; | ||||||
|     if (window.localStorage && localStorage.getItem('darkmode') == 1) |     if (window.localStorage && localStorage.getItem('lightmode') != 1) | ||||||
| 		toggle(); | 		toggle(); | ||||||
| })(); | })(); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -46,7 +46,7 @@ function statify(obj) { | |||||||
|     var ua = navigator.userAgent; |     var ua = navigator.userAgent; | ||||||
|     if (ua.indexOf(') Gecko/') !== -1 && /Linux| Mac /.exec(ua)) { |     if (ua.indexOf(') Gecko/') !== -1 && /Linux| Mac /.exec(ua)) { | ||||||
|         // necessary on ff-68.7 at least |         // necessary on ff-68.7 at least | ||||||
|         var s = document.createElement('style'); |         var s = mknod('style'); | ||||||
|         s.innerHTML = '@page { margin: .5in .6in .8in .6in; }'; |         s.innerHTML = '@page { margin: .5in .6in .8in .6in; }'; | ||||||
|         console.log(s.innerHTML); |         console.log(s.innerHTML); | ||||||
|         document.head.appendChild(s); |         document.head.appendChild(s); | ||||||
| @@ -175,12 +175,12 @@ function md_plug_err(ex, js) { | |||||||
|         msg = "Line " + ln + ", " + msg; |         msg = "Line " + ln + ", " + msg; | ||||||
|         var lns = js.split('\n'); |         var lns = js.split('\n'); | ||||||
|         if (ln < lns.length) { |         if (ln < lns.length) { | ||||||
|             o = document.createElement('span'); |             o = mknod('span'); | ||||||
|             o.style.cssText = 'color:#ac2;font-size:.9em;font-family:scp;display:block'; |             o.style.cssText = 'color:#ac2;font-size:.9em;font-family:scp;display:block'; | ||||||
|             o.textContent = lns[ln - 1]; |             o.textContent = lns[ln - 1]; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     errbox = document.createElement('div'); |     errbox = mknod('div'); | ||||||
|     errbox.setAttribute('id', 'md_errbox'); |     errbox.setAttribute('id', 'md_errbox'); | ||||||
|     errbox.style.cssText = 'position:absolute;top:0;left:0;padding:1em .5em;background:#2b2b2b;color:#fc5' |     errbox.style.cssText = 'position:absolute;top:0;left:0;padding:1em .5em;background:#2b2b2b;color:#fc5' | ||||||
|     errbox.textContent = msg; |     errbox.textContent = msg; | ||||||
|   | |||||||
| @@ -16,7 +16,7 @@ var dom_sbs = ebi('sbs'); | |||||||
| var dom_nsbs = ebi('nsbs'); | var dom_nsbs = ebi('nsbs'); | ||||||
| var dom_tbox = ebi('toolsbox'); | var dom_tbox = ebi('toolsbox'); | ||||||
| var dom_ref = (function () { | var dom_ref = (function () { | ||||||
|     var d = document.createElement('div'); |     var d = mknod('div'); | ||||||
|     d.setAttribute('id', 'mtr'); |     d.setAttribute('id', 'mtr'); | ||||||
|     dom_swrap.appendChild(d); |     dom_swrap.appendChild(d); | ||||||
|     d = ebi('mtr'); |     d = ebi('mtr'); | ||||||
| @@ -71,7 +71,7 @@ var map_src = []; | |||||||
| var map_pre = []; | var map_pre = []; | ||||||
| function genmap(dom, oldmap) { | function genmap(dom, oldmap) { | ||||||
|     var find = nlines; |     var find = nlines; | ||||||
|     while (oldmap && find --> 0) { |     while (oldmap && find-- > 0) { | ||||||
|         var tmap = genmapq(dom, '*[data-ln="' + find + '"]'); |         var tmap = genmapq(dom, '*[data-ln="' + find + '"]'); | ||||||
|         if (!tmap || !tmap.length) |         if (!tmap || !tmap.length) | ||||||
|             continue; |             continue; | ||||||
| @@ -94,7 +94,7 @@ var nlines = 0; | |||||||
| var draw_md = (function () { | var draw_md = (function () { | ||||||
|     var delay = 1; |     var delay = 1; | ||||||
|     function draw_md() { |     function draw_md() { | ||||||
|         var t0 = new Date().getTime(); |         var t0 = Date.now(); | ||||||
|         var src = dom_src.value; |         var src = dom_src.value; | ||||||
|         convert_markdown(src, dom_pre); |         convert_markdown(src, dom_pre); | ||||||
|  |  | ||||||
| @@ -110,7 +110,7 @@ var draw_md = (function () { | |||||||
|  |  | ||||||
|         cls(ebi('save'), 'disabled', src == server_md); |         cls(ebi('save'), 'disabled', src == server_md); | ||||||
|  |  | ||||||
|         var t1 = new Date().getTime(); |         var t1 = Date.now(); | ||||||
|         delay = t1 - t0 > 100 ? 25 : 1; |         delay = t1 - t0 > 100 ? 25 : 1; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -252,7 +252,7 @@ function Modpoll() { | |||||||
|         } |         } | ||||||
|  |  | ||||||
|         console.log('modpoll...'); |         console.log('modpoll...'); | ||||||
|         var url = (document.location + '').split('?')[0] + '?raw&_=' + new Date().getTime(); |         var url = (document.location + '').split('?')[0] + '?raw&_=' + Date.now(); | ||||||
|         var xhr = new XMLHttpRequest(); |         var xhr = new XMLHttpRequest(); | ||||||
|         xhr.modpoll = this; |         xhr.modpoll = this; | ||||||
|         xhr.open('GET', url, true); |         xhr.open('GET', url, true); | ||||||
| @@ -399,7 +399,7 @@ function save_cb() { | |||||||
|  |  | ||||||
| function run_savechk(lastmod, txt, btn, ntry) { | function run_savechk(lastmod, txt, btn, ntry) { | ||||||
|     // download the saved doc from the server and compare |     // download the saved doc from the server and compare | ||||||
|     var url = (document.location + '').split('?')[0] + '?raw&_=' + new Date().getTime(); |     var url = (document.location + '').split('?')[0] + '?raw&_=' + Date.now(); | ||||||
|     var xhr = new XMLHttpRequest(); |     var xhr = new XMLHttpRequest(); | ||||||
|     xhr.open('GET', url, true); |     xhr.open('GET', url, true); | ||||||
|     xhr.responseType = 'text'; |     xhr.responseType = 'text'; | ||||||
| @@ -455,7 +455,7 @@ function toast(autoclose, style, width, msg) { | |||||||
|         ok.parentNode.removeChild(ok); |         ok.parentNode.removeChild(ok); | ||||||
|  |  | ||||||
|     style = "width:" + width + "em;left:calc(50% - " + (width / 2) + "em);" + style; |     style = "width:" + width + "em;left:calc(50% - " + (width / 2) + "em);" + style; | ||||||
|     ok = document.createElement('div'); |     ok = mknod('div'); | ||||||
|     ok.setAttribute('id', 'toast'); |     ok.setAttribute('id', 'toast'); | ||||||
|     ok.setAttribute('style', style); |     ok.setAttribute('style', style); | ||||||
|     ok.innerHTML = msg; |     ok.innerHTML = msg; | ||||||
| @@ -1049,7 +1049,7 @@ action_stack = (function () { | |||||||
|         var p1 = from.length, |         var p1 = from.length, | ||||||
|             p2 = to.length; |             p2 = to.length; | ||||||
|  |  | ||||||
|         while (p1 --> 0 && p2 --> 0) |         while (p1-- > 0 && p2-- > 0) | ||||||
|             if (from[p1] != to[p2]) |             if (from[p1] != to[p2]) | ||||||
|                 break; |                 break; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -31,12 +31,12 @@ var md_opt = { | |||||||
|  |  | ||||||
| var lightswitch = (function () { | var lightswitch = (function () { | ||||||
| 	var fun = function () { | 	var fun = function () { | ||||||
| 		var dark = !!!document.documentElement.getAttribute("class"); | 		var dark = !document.documentElement.getAttribute("class"); | ||||||
| 		document.documentElement.setAttribute("class", dark ? "dark" : ""); | 		document.documentElement.setAttribute("class", dark ? "dark" : ""); | ||||||
| 		if (window.localStorage) | 		if (window.localStorage) | ||||||
| 			localStorage.setItem('darkmode', dark ? 1 : 0); | 			localStorage.setItem('lightmode', dark ? 0 : 1); | ||||||
| 	}; | 	}; | ||||||
| 	if (window.localStorage && localStorage.getItem('darkmode') == 1) | 	if (window.localStorage && localStorage.getItem('lightmode') != 1) | ||||||
| 		fun(); | 		fun(); | ||||||
| 	 | 	 | ||||||
| 	return fun; | 	return fun; | ||||||
|   | |||||||
| @@ -71,7 +71,7 @@ var mde = (function () { | |||||||
| })(); | })(); | ||||||
|  |  | ||||||
| function set_jumpto() { | function set_jumpto() { | ||||||
|     document.querySelector('.editor-preview-side').onclick = jumpto; |     QS('.editor-preview-side').onclick = jumpto; | ||||||
| } | } | ||||||
|  |  | ||||||
| function jumpto(ev) { | function jumpto(ev) { | ||||||
| @@ -94,7 +94,7 @@ function md_changed(mde, on_srv) { | |||||||
|         window.md_saved = mde.value(); |         window.md_saved = mde.value(); | ||||||
|  |  | ||||||
|     var md_now = mde.value(); |     var md_now = mde.value(); | ||||||
|     var save_btn = document.querySelector('.editor-toolbar button.save'); |     var save_btn = QS('.editor-toolbar button.save'); | ||||||
|  |  | ||||||
|     if (md_now == window.md_saved) |     if (md_now == window.md_saved) | ||||||
|         save_btn.classList.add('disabled'); |         save_btn.classList.add('disabled'); | ||||||
| @@ -105,7 +105,7 @@ function md_changed(mde, on_srv) { | |||||||
| } | } | ||||||
|  |  | ||||||
| function save(mde) { | function save(mde) { | ||||||
|     var save_btn = document.querySelector('.editor-toolbar button.save'); |     var save_btn = QS('.editor-toolbar button.save'); | ||||||
|     if (save_btn.classList.contains('disabled')) { |     if (save_btn.classList.contains('disabled')) { | ||||||
|         alert('there is nothing to save'); |         alert('there is nothing to save'); | ||||||
|         return; |         return; | ||||||
| @@ -212,7 +212,7 @@ function save_chk() { | |||||||
|     last_modified = this.lastmod; |     last_modified = this.lastmod; | ||||||
|     md_changed(this.mde, true); |     md_changed(this.mde, true); | ||||||
|  |  | ||||||
|     var ok = document.createElement('div'); |     var ok = mknod('div'); | ||||||
|     ok.setAttribute('style', 'font-size:6em;font-family:serif;font-weight:bold;color:#cf6;background:#444;border-radius:.3em;padding:.6em 0;position:fixed;top:30%;left:calc(50% - 2em);width:4em;text-align:center;z-index:9001;transition:opacity 0.2s ease-in-out;opacity:1'); |     ok.setAttribute('style', 'font-size:6em;font-family:serif;font-weight:bold;color:#cf6;background:#444;border-radius:.3em;padding:.6em 0;position:fixed;top:30%;left:calc(50% - 2em);width:4em;text-align:center;z-index:9001;transition:opacity 0.2s ease-in-out;opacity:1'); | ||||||
|     ok.innerHTML = 'OK✔️'; |     ok.innerHTML = 'OK✔️'; | ||||||
|     var parent = ebi('m'); |     var parent = ebi('m'); | ||||||
|   | |||||||
| @@ -38,7 +38,7 @@ | |||||||
|     </div> |     </div> | ||||||
|     <script> |     <script> | ||||||
|  |  | ||||||
| if (window.localStorage && localStorage.getItem('darkmode') == 1) | if (window.localStorage && localStorage.getItem('lightmode') != 1) | ||||||
|     document.documentElement.setAttribute("class", "dark"); |     document.documentElement.setAttribute("class", "dark"); | ||||||
|  |  | ||||||
| </script> | </script> | ||||||
|   | |||||||
| @@ -55,7 +55,7 @@ function up2k_flagbus() { | |||||||
|             dbg(who, 'hi me (??)'); |             dbg(who, 'hi me (??)'); | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
|         flag.act = new Date().getTime(); |         flag.act = Date.now(); | ||||||
|         if (what == "want") { |         if (what == "want") { | ||||||
|             // lowest id wins, don't care if that's us |             // lowest id wins, don't care if that's us | ||||||
|             if (who < flag.id) { |             if (who < flag.id) { | ||||||
| @@ -141,7 +141,7 @@ function U2pvis(act, btns) { | |||||||
|     this.tail = -1; |     this.tail = -1; | ||||||
|     this.wsz = 3; |     this.wsz = 3; | ||||||
|  |  | ||||||
|     this.addfile = function (entry) { |     this.addfile = function (entry, sz) { | ||||||
|         this.tab.push({ |         this.tab.push({ | ||||||
|             "hn": entry[0], |             "hn": entry[0], | ||||||
|             "ht": entry[1], |             "ht": entry[1], | ||||||
| @@ -149,8 +149,10 @@ function U2pvis(act, btns) { | |||||||
|             "in": 'q', |             "in": 'q', | ||||||
|             "nh": 0, //hashed |             "nh": 0, //hashed | ||||||
|             "nd": 0, //done |             "nd": 0, //done | ||||||
|             "pa": [], //percents |             "cb": [], // bytes done in chunk | ||||||
|             "pb": []  //active-list |             "bt": sz, // bytes total | ||||||
|  |             "bd": 0,  // bytes done | ||||||
|  |             "bd0": 0  // upload start | ||||||
|         }); |         }); | ||||||
|         this.ctr["q"]++; |         this.ctr["q"]++; | ||||||
|         this.drawcard("q"); |         this.drawcard("q"); | ||||||
| @@ -172,6 +174,9 @@ function U2pvis(act, btns) { | |||||||
|     this.seth = function (nfile, field, html) { |     this.seth = function (nfile, field, html) { | ||||||
|         var fo = this.tab[nfile]; |         var fo = this.tab[nfile]; | ||||||
|         field = ['hn', 'ht', 'hp'][field]; |         field = ['hn', 'ht', 'hp'][field]; | ||||||
|  |         if (fo[field] === html) | ||||||
|  |             return; | ||||||
|  |  | ||||||
|         fo[field] = html; |         fo[field] = html; | ||||||
|         if (!this.is_act(fo.in)) |         if (!this.is_act(fo.in)) | ||||||
|             return; |             return; | ||||||
| @@ -184,61 +189,62 @@ function U2pvis(act, btns) { | |||||||
|         } |         } | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     this.setab = function (nfile, blocks) { |     this.setab = function (nfile, nblocks) { | ||||||
|         var t = []; |         var t = []; | ||||||
|         for (var a = 0; a < blocks; a++) |         for (var a = 0; a < nblocks; a++) | ||||||
|             t.push(0); |             t.push(0); | ||||||
|  |  | ||||||
|         this.tab[nfile].pa = t; |         this.tab[nfile].cb = t; | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     this.perc = function (n, t, e, sz, t0) { |     this.setat = function (nfile, blocktab) { | ||||||
|         var p = (n + e) * 100.0 / t, |         this.tab[nfile].cb = blocktab; | ||||||
|             td = new Date().getTime() - t0, |  | ||||||
|             pp = (td / 1000) / p, |         var bd = 0; | ||||||
|             spd = (sz / 100) / pp, |         for (var a = 0; a < blocktab.length; a++) | ||||||
|             eta = pp * (100 - p); |             bd += blocktab[a]; | ||||||
|  |  | ||||||
|  |         this.tab[nfile].bd = bd; | ||||||
|  |         this.tab[nfile].bd0 = bd; | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     this.perc = function (bd, bd0, sz, t0) { | ||||||
|  |         var td = Date.now() - t0, | ||||||
|  |             p = bd * 100.0 / sz, | ||||||
|  |             nb = bd - bd0, | ||||||
|  |             spd = nb / (td / 1000), | ||||||
|  |             eta = (sz - bd) / spd; | ||||||
|  |  | ||||||
|         return [p, s2ms(eta), spd / (1024 * 1024)]; |         return [p, s2ms(eta), spd / (1024 * 1024)]; | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     this.hashed = function (fobj) { |     this.hashed = function (fobj) { | ||||||
|         var fo = this.tab[fobj.n]; |         var fo = this.tab[fobj.n], | ||||||
|         fo.nh++; |             nb = fo.bt * (++fo.nh / fo.cb.length), | ||||||
|         var p = this.perc(fo.nh, fo.pa.length, 0, fobj.size, fobj.t1); |             p = this.perc(nb, 0, fobj.size, fobj.t1); | ||||||
|  |  | ||||||
|         fo.hp = '{0}%, {1}, {2} MB/s'.format( |         fo.hp = '{0}%, {1}, {2} MB/s'.format( | ||||||
|             p[0].toFixed(2), p[1], p[2].toFixed(2) |             p[0].toFixed(2), p[1], p[2].toFixed(2) | ||||||
|         ); |         ); | ||||||
|         if (!this.is_act(fo.in)) |         if (!this.is_act(fo.in)) | ||||||
|             return; |             return; | ||||||
|  |  | ||||||
|         var obj = ebi('f{0}p'.format(fobj.n)); |         var obj = ebi('f{0}p'.format(fobj.n)), | ||||||
|  |             o1 = p[0] - 2, o2 = p[0] - 0.1, o3 = p[0]; | ||||||
|  |  | ||||||
|         obj.innerHTML = fo.hp; |         obj.innerHTML = fo.hp; | ||||||
|         obj.style.color = '#fff'; |         obj.style.color = '#fff'; | ||||||
|         var o1 = p[0] - 2, o2 = p[0] - 0.1, o3 = p[0]; |         obj.style.background = 'linear-gradient(90deg, #025, #06a ' + o1 + '%, #09d ' + o2 + '%, #333 ' + o3 + '%, #333 99%, #777)'; | ||||||
|         obj.style.background = 'linear-gradient(90deg, #025, #06a ' + o1 + '%, #08d ' + o2 + '%, #333 ' + o3 + '%)'; |  | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     this.prog = function (fobj, nchunk, percent) { |     this.prog = function (fobj, nchunk, cbd) { | ||||||
|         var fo = this.tab[fobj.n], pb = fo.pb; |         var fo = this.tab[fobj.n], | ||||||
|         var i = pb.indexOf(nchunk); |             delta = cbd - fo.cb[nchunk]; | ||||||
|         fo.pa[nchunk] = percent; |  | ||||||
|         if (percent == 101) { |  | ||||||
|             fo.nd++; |  | ||||||
|             if (i >= 0) |  | ||||||
|                 pb.splice(i); |  | ||||||
|         } |  | ||||||
|         else if (i == -1) { |  | ||||||
|             pb.push(nchunk); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         var extra = 0; |         fo.cb[nchunk] = cbd; | ||||||
|         for (var a = 0; a < pb.length; a++) |         fo.bd += delta; | ||||||
|             extra += fo.pa[a]; |  | ||||||
|  |  | ||||||
|         extra /= fo.pa.length; |         var p = this.perc(fo.bd, fo.bd0, fo.bt, fobj.t3); | ||||||
|  |  | ||||||
|         var p = this.perc(fo.nd, fo.pa.length, extra, fobj.size, fobj.t3); |  | ||||||
|         fo.hp = '{0}%, {1}, {2} MB/s'.format( |         fo.hp = '{0}%, {1}, {2} MB/s'.format( | ||||||
|             p[0].toFixed(2), p[1], p[2].toFixed(2) |             p[0].toFixed(2), p[1], p[2].toFixed(2) | ||||||
|         ); |         ); | ||||||
| @@ -246,11 +252,12 @@ function U2pvis(act, btns) { | |||||||
|         if (!this.is_act(fo.in)) |         if (!this.is_act(fo.in)) | ||||||
|             return; |             return; | ||||||
|  |  | ||||||
|         var obj = ebi('f{0}p'.format(fobj.n)); |         var obj = ebi('f{0}p'.format(fobj.n)), | ||||||
|  |             o1 = p[0] - 2, o2 = p[0] - 0.1, o3 = p[0]; | ||||||
|  |  | ||||||
|         obj.innerHTML = fo.hp; |         obj.innerHTML = fo.hp; | ||||||
|         obj.style.color = '#fff'; |         obj.style.color = '#fff'; | ||||||
|         var o1 = p[0] - 2, o2 = p[0] - 0.1, o3 = p[0]; |         obj.style.background = 'linear-gradient(90deg, #050, #270 ' + o1 + '%, #4b0 ' + o2 + '%, #333 ' + o3 + '%, #333 99%, #777)'; | ||||||
|         obj.style.background = 'linear-gradient(90deg, #050, #270 ' + o1 + '%, #4b0 ' + o2 + '%, #333 ' + o3 + '%)'; |  | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     this.move = function (nfile, newcat) { |     this.move = function (nfile, newcat) { | ||||||
| @@ -284,24 +291,14 @@ function U2pvis(act, btns) { | |||||||
|         } |         } | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     this.bzw_log = function (first, last) { |  | ||||||
|         console.log("first %d   head %d   tail %d   last %d", first, this.head, this.tail, last); |  | ||||||
|         var trs = document.querySelectorAll('#u2tab>tbody>tr'), msg = []; |  | ||||||
|         for (var a = 0; a < trs.length; a++) |  | ||||||
|             msg.push(trs[a].getAttribute('id')); |  | ||||||
|  |  | ||||||
|         console.log(msg.join(' ')); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     this.bzw = function () { |     this.bzw = function () { | ||||||
|         var first = document.querySelector('#u2tab>tbody>tr:first-child'); |         var first = QS('#u2tab>tbody>tr:first-child'); | ||||||
|         if (!first) |         if (!first) | ||||||
|             return; |             return; | ||||||
|  |  | ||||||
|         var last = document.querySelector('#u2tab>tbody>tr:last-child'); |         var last = QS('#u2tab>tbody>tr:last-child'); | ||||||
|         first = parseInt(first.getAttribute('id').slice(1)); |         first = parseInt(first.getAttribute('id').slice(1)); | ||||||
|         last = parseInt(last.getAttribute('id').slice(1)); |         last = parseInt(last.getAttribute('id').slice(1)); | ||||||
|         //this.bzw_log(first, last); |  | ||||||
|  |  | ||||||
|         while (this.head - first > this.wsz) { |         while (this.head - first > this.wsz) { | ||||||
|             var obj = ebi('f' + (first++)); |             var obj = ebi('f' + (first++)); | ||||||
| @@ -312,12 +309,10 @@ function U2pvis(act, btns) { | |||||||
|             if (!obj) |             if (!obj) | ||||||
|                 this.addrow(last); |                 this.addrow(last); | ||||||
|         } |         } | ||||||
|         //this.bzw_log(first, last); |  | ||||||
|         //console.log('--'); |  | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     this.drawcard = function (cat) { |     this.drawcard = function (cat) { | ||||||
|         var cards = document.querySelectorAll('#u2cards>a>span'); |         var cards = QSA('#u2cards>a>span'); | ||||||
|  |  | ||||||
|         if (cat == "q") { |         if (cat == "q") { | ||||||
|             cards[4].innerHTML = this.ctr[cat]; |             cards[4].innerHTML = this.ctr[cat]; | ||||||
| @@ -340,9 +335,9 @@ function U2pvis(act, btns) { | |||||||
|  |  | ||||||
|     this.changecard = function (card) { |     this.changecard = function (card) { | ||||||
|         this.act = card; |         this.act = card; | ||||||
|         var html = []; |  | ||||||
|         this.head = -1; |         this.head = -1; | ||||||
|         this.tail = -1; |         this.tail = -1; | ||||||
|  |         var html = []; | ||||||
|         for (var a = 0; a < this.tab.length; a++) { |         for (var a = 0; a < this.tab.length; a++) { | ||||||
|             var rt = this.tab[a].in; |             var rt = this.tab[a].in; | ||||||
|             if (this.is_act(rt)) { |             if (this.is_act(rt)) { | ||||||
| @@ -379,7 +374,7 @@ function U2pvis(act, btns) { | |||||||
|         if (as_html) |         if (as_html) | ||||||
|             return '<tr id="f' + nfile + '">' + ret + '</tr>'; |             return '<tr id="f' + nfile + '">' + ret + '</tr>'; | ||||||
|  |  | ||||||
|         var obj = document.createElement('tr'); |         var obj = mknod('tr'); | ||||||
|         obj.setAttribute('id', 'f' + nfile); |         obj.setAttribute('id', 'f' + nfile); | ||||||
|         obj.innerHTML = ret; |         obj.innerHTML = ret; | ||||||
|         return obj; |         return obj; | ||||||
| @@ -391,7 +386,7 @@ function U2pvis(act, btns) { | |||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     var that = this; |     var that = this; | ||||||
|     btns = document.querySelectorAll(btns + '>a[act]'); |     btns = QSA(btns + '>a[act]'); | ||||||
|     for (var a = 0; a < btns.length; a++) { |     for (var a = 0; a < btns.length; a++) { | ||||||
|         btns[a].onclick = function (e) { |         btns[a].onclick = function (e) { | ||||||
|             ev(e); |             ev(e); | ||||||
| @@ -408,7 +403,6 @@ function U2pvis(act, btns) { | |||||||
|  |  | ||||||
| function up2k_init(have_crypto) { | function up2k_init(have_crypto) { | ||||||
|     //have_crypto = false; |     //have_crypto = false; | ||||||
|     var need_filereader_cache = undefined; |  | ||||||
|  |  | ||||||
|     // show modal message |     // show modal message | ||||||
|     function showmodal(msg) { |     function showmodal(msg) { | ||||||
| @@ -426,8 +420,9 @@ function up2k_init(have_crypto) { | |||||||
|         ebi('u2notbtn').innerHTML = ''; |         ebi('u2notbtn').innerHTML = ''; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     var shame = 'your browser <a href="https://www.chromium.org/blink/webcrypto">disables sha512</a> unless you <a href="' + (window.location + '').replace(':', 's:') + '">use https</a>' |     var shame = 'your browser <a href="https://www.chromium.org/blink/webcrypto">disables sha512</a> unless you <a href="' + (window.location + '').replace(':', 's:') + '">use https</a>', | ||||||
|     var is_https = (window.location + '').indexOf('https:') === 0; |         is_https = (window.location + '').indexOf('https:') === 0; | ||||||
|  |  | ||||||
|     if (is_https) |     if (is_https) | ||||||
|         // chrome<37 firefox<34 edge<12 ie<11 opera<24 safari<10.1 |         // chrome<37 firefox<34 edge<12 ie<11 opera<24 safari<10.1 | ||||||
|         shame = 'your browser is impressively ancient'; |         shame = 'your browser is impressively ancient'; | ||||||
| @@ -484,13 +479,14 @@ function up2k_init(have_crypto) { | |||||||
|         }; |         }; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     var parallel_uploads = icfg_get('nthread'); |     var parallel_uploads = icfg_get('nthread'), | ||||||
|     var multitask = bcfg_get('multitask', true); |         multitask = bcfg_get('multitask', true), | ||||||
|     var ask_up = bcfg_get('ask_up', true); |         ask_up = bcfg_get('ask_up', true), | ||||||
|     var flag_en = bcfg_get('flag_en', false); |         flag_en = bcfg_get('flag_en', false), | ||||||
|     var fsearch = bcfg_get('fsearch', false); |         fsearch = bcfg_get('fsearch', false), | ||||||
|  |         fdom_ctr = 0, | ||||||
|  |         min_filebuf = 0; | ||||||
|  |  | ||||||
|     var fdom_ctr = 0; |  | ||||||
|     var st = { |     var st = { | ||||||
|         "files": [], |         "files": [], | ||||||
|         "todo": { |         "todo": { | ||||||
| @@ -540,8 +536,9 @@ function up2k_init(have_crypto) { | |||||||
|         e.stopPropagation(); |         e.stopPropagation(); | ||||||
|         e.preventDefault(); |         e.preventDefault(); | ||||||
|  |  | ||||||
|         var files; |         var files, | ||||||
|         var is_itemlist = false; |             is_itemlist = false; | ||||||
|  |  | ||||||
|         if (e.dataTransfer) { |         if (e.dataTransfer) { | ||||||
|             if (e.dataTransfer.items) { |             if (e.dataTransfer.items) { | ||||||
|                 files = e.dataTransfer.items; // DataTransferItemList |                 files = e.dataTransfer.items; // DataTransferItemList | ||||||
| @@ -555,9 +552,10 @@ function up2k_init(have_crypto) { | |||||||
|             return alert('no files selected??'); |             return alert('no files selected??'); | ||||||
|  |  | ||||||
|         more_one_file(); |         more_one_file(); | ||||||
|         var bad_files = []; |         var bad_files = [], | ||||||
|         var good_files = []; |             good_files = [], | ||||||
|         var dirs = []; |             dirs = []; | ||||||
|  |  | ||||||
|         for (var a = 0; a < files.length; a++) { |         for (var a = 0; a < files.length; a++) { | ||||||
|             var fobj = files[a]; |             var fobj = files[a]; | ||||||
|             if (is_itemlist) { |             if (is_itemlist) { | ||||||
| @@ -642,12 +640,13 @@ function up2k_init(have_crypto) { | |||||||
|  |  | ||||||
|     function gotallfiles(good_files, bad_files) { |     function gotallfiles(good_files, bad_files) { | ||||||
|         if (bad_files.length > 0) { |         if (bad_files.length > 0) { | ||||||
|             var ntot = bad_files.length + good_files.length; |             var ntot = bad_files.length + good_files.length, | ||||||
|             var msg = 'These {0} files (of {1} total) were skipped because they are empty:\n'.format(bad_files.length, ntot); |                 msg = 'These {0} files (of {1} total) were skipped because they are empty:\n'.format(bad_files.length, ntot); | ||||||
|  |  | ||||||
|             for (var a = 0, aa = Math.min(20, bad_files.length); a < aa; a++) |             for (var a = 0, aa = Math.min(20, bad_files.length); a < aa; a++) | ||||||
|                 msg += '-- ' + bad_files[a] + '\n'; |                 msg += '-- ' + bad_files[a] + '\n'; | ||||||
|  |  | ||||||
|             if (good_files.length - bad_files.length <= 1 && /(android)/i.test(navigator.userAgent)) |             if (good_files.length - bad_files.length <= 1 && ANDROID) | ||||||
|                 msg += '\nFirefox-Android has a bug which prevents selecting multiple files. Try selecting one file at a time. For more info, see firefox bug 1456557'; |                 msg += '\nFirefox-Android has a bug which prevents selecting multiple files. Try selecting one file at a time. For more info, see firefox bug 1456557'; | ||||||
|  |  | ||||||
|             alert(msg); |             alert(msg); | ||||||
| @@ -661,9 +660,10 @@ function up2k_init(have_crypto) { | |||||||
|             return; |             return; | ||||||
|  |  | ||||||
|         for (var a = 0; a < good_files.length; a++) { |         for (var a = 0; a < good_files.length; a++) { | ||||||
|             var fobj = good_files[a][0]; |             var fobj = good_files[a][0], | ||||||
|             var now = new Date().getTime(); |                 now = Date.now(), | ||||||
|             var lmod = fobj.lastModified || now; |                 lmod = fobj.lastModified || now; | ||||||
|  |  | ||||||
|             var entry = { |             var entry = { | ||||||
|                 "n": parseInt(st.files.length.toString()), |                 "n": parseInt(st.files.length.toString()), | ||||||
|                 "t0": now, |                 "t0": now, | ||||||
| @@ -690,7 +690,7 @@ function up2k_init(have_crypto) { | |||||||
|                     esc(uricom_dec(entry.purl)[0] + entry.name)).join(' '), |                     esc(uricom_dec(entry.purl)[0] + entry.name)).join(' '), | ||||||
|                 '📐 hash', |                 '📐 hash', | ||||||
|                 '' |                 '' | ||||||
|             ]); |             ], fobj.size); | ||||||
|             st.files.push(entry); |             st.files.push(entry); | ||||||
|             st.todo.hash.push(entry); |             st.todo.hash.push(entry); | ||||||
|         } |         } | ||||||
| @@ -699,7 +699,7 @@ function up2k_init(have_crypto) { | |||||||
|  |  | ||||||
|     function more_one_file() { |     function more_one_file() { | ||||||
|         fdom_ctr++; |         fdom_ctr++; | ||||||
|         var elm = document.createElement('div') |         var elm = mknod('div'); | ||||||
|         elm.innerHTML = '<input id="file{0}" type="file" name="file{0}[]" multiple="multiple" />'.format(fdom_ctr); |         elm.innerHTML = '<input id="file{0}" type="file" name="file{0}[]" multiple="multiple" />'.format(fdom_ctr); | ||||||
|         ebi('u2form').appendChild(elm); |         ebi('u2form').appendChild(elm); | ||||||
|         ebi('file' + fdom_ctr).addEventListener('change', gotfile, false); |         ebi('file' + fdom_ctr).addEventListener('change', gotfile, false); | ||||||
| @@ -737,7 +737,7 @@ function up2k_init(have_crypto) { | |||||||
|     function hashing_permitted() { |     function hashing_permitted() { | ||||||
|         if (multitask) { |         if (multitask) { | ||||||
|             var ahead = st.bytes.hashed - st.bytes.uploaded; |             var ahead = st.bytes.hashed - st.bytes.uploaded; | ||||||
|             return ahead < 1024 * 1024 * 128 && |             return ahead < 1024 * 1024 * 1024 * 4 && | ||||||
|                 st.todo.handshake.length + st.busy.handshake.length < 16; |                 st.todo.handshake.length + st.busy.handshake.length < 16; | ||||||
|         } |         } | ||||||
|         return handshakes_permitted() && 0 == |         return handshakes_permitted() && 0 == | ||||||
| @@ -746,26 +746,23 @@ function up2k_init(have_crypto) { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     var tasker = (function () { |     var tasker = (function () { | ||||||
|         var mutex = false; |         var tto = null, | ||||||
|         var was_busy = false; |             running = false, | ||||||
|  |             was_busy = false; | ||||||
|  |  | ||||||
|         function taskerd() { |         function defer() { | ||||||
|             if (mutex) |             running = false; | ||||||
|                 return; |             clearTimeout(tto); | ||||||
|  |             tto = setTimeout(taskerd, 100); | ||||||
|             mutex = true; |  | ||||||
|             while (true) { |  | ||||||
|                 if (false) { |  | ||||||
|                     ebi('srv_info').innerHTML = |  | ||||||
|                         new Date().getTime() + ", " + |  | ||||||
|                         st.todo.hash.length + ", " + |  | ||||||
|                         st.todo.handshake.length + ", " + |  | ||||||
|                         st.todo.upload.length + ", " + |  | ||||||
|                         st.busy.hash.length + ", " + |  | ||||||
|                         st.busy.handshake.length + ", " + |  | ||||||
|                         st.busy.upload.length; |  | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         function taskerd() { | ||||||
|  |             if (running) | ||||||
|  |                 return; | ||||||
|  |  | ||||||
|  |             clearTimeout(tto); | ||||||
|  |             running = true; | ||||||
|  |             while (true) { | ||||||
|                 var is_busy = 0 != |                 var is_busy = 0 != | ||||||
|                     st.todo.hash.length + |                     st.todo.hash.length + | ||||||
|                     st.todo.handshake.length + |                     st.todo.handshake.length + | ||||||
| @@ -777,21 +774,16 @@ function up2k_init(have_crypto) { | |||||||
|                 if (was_busy != is_busy) { |                 if (was_busy != is_busy) { | ||||||
|                     was_busy = is_busy; |                     was_busy = is_busy; | ||||||
|  |  | ||||||
|                     if (is_busy) |                     window[(is_busy ? "add" : "remove") + | ||||||
|                         window.addEventListener("beforeunload", warn_uploader_busy); |                         "EventListener"]("beforeunload", warn_uploader_busy); | ||||||
|                     else |  | ||||||
|                         window.removeEventListener("beforeunload", warn_uploader_busy); |  | ||||||
|                 } |                 } | ||||||
|  |  | ||||||
|                 if (flag) { |                 if (flag) { | ||||||
|                     if (is_busy) { |                     if (is_busy) { | ||||||
|                         var now = new Date().getTime(); |                         var now = Date.now(); | ||||||
|                         flag.take(now); |                         flag.take(now); | ||||||
|                         if (!flag.ours) { |                         if (!flag.ours) | ||||||
|                             setTimeout(taskerd, 100); |                             return defer(); | ||||||
|                             mutex = false; |  | ||||||
|                             return; |  | ||||||
|                         } |  | ||||||
|                     } |                     } | ||||||
|                     else if (flag.ours) { |                     else if (flag.ours) { | ||||||
|                         flag.give(); |                         flag.give(); | ||||||
| @@ -833,11 +825,8 @@ function up2k_init(have_crypto) { | |||||||
|                     mou_ikkai = true; |                     mou_ikkai = true; | ||||||
|                 } |                 } | ||||||
|  |  | ||||||
|                 if (!mou_ikkai) { |                 if (!mou_ikkai) | ||||||
|                     setTimeout(taskerd, 100); |                     return defer(); | ||||||
|                     mutex = false; |  | ||||||
|                     return; |  | ||||||
|                 } |  | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         taskerd(); |         taskerd(); | ||||||
| @@ -851,47 +840,47 @@ function up2k_init(have_crypto) { | |||||||
|  |  | ||||||
|     // https://gist.github.com/jonleighton/958841 |     // https://gist.github.com/jonleighton/958841 | ||||||
|     function buf2b64(arrayBuffer) { |     function buf2b64(arrayBuffer) { | ||||||
|         var base64 = ''; |         var base64 = '', | ||||||
|         var encodings = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_'; |             cset = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_', | ||||||
|         var bytes = new Uint8Array(arrayBuffer); |             src = new Uint8Array(arrayBuffer), | ||||||
|         var byteLength = bytes.byteLength; |             nbytes = src.byteLength, | ||||||
|         var byteRemainder = byteLength % 3; |             byteRem = nbytes % 3, | ||||||
|         var mainLength = byteLength - byteRemainder; |             mainLen = nbytes - byteRem, | ||||||
|         var a, b, c, d; |             a, b, c, d, chunk; | ||||||
|         var chunk; |  | ||||||
|  |  | ||||||
|         for (var i = 0; i < mainLength; i = i + 3) { |         for (var i = 0; i < mainLen; i = i + 3) { | ||||||
|             chunk = (bytes[i] << 16) | (bytes[i + 1] << 8) | bytes[i + 2]; |             chunk = (src[i] << 16) | (src[i + 1] << 8) | src[i + 2]; | ||||||
|             // create 8*3=24bit segment then split into 6bit segments |             // create 8*3=24bit segment then split into 6bit segments | ||||||
|             a = (chunk & 16515072) >> 18; // 16515072 = (2^6 - 1) << 18 |             a = (chunk & 16515072) >> 18; // (2^6 - 1) << 18 | ||||||
|             b = (chunk & 258048) >> 12; // 258048   = (2^6 - 1) << 12 |             b = (chunk & 258048) >> 12; // (2^6 - 1) << 12 | ||||||
|             c = (chunk & 4032) >> 6; // 4032     = (2^6 - 1) << 6 |             c = (chunk & 4032) >> 6; // (2^6 - 1) << 6 | ||||||
|             d = chunk & 63;               // 63       = 2^6 - 1 |             d = chunk & 63; // 2^6 - 1 | ||||||
|  |  | ||||||
|             // Convert the raw binary segments to the appropriate ASCII encoding |             // Convert the raw binary segments to the appropriate ASCII encoding | ||||||
|             base64 += encodings[a] + encodings[b] + encodings[c] + encodings[d]; |             base64 += cset[a] + cset[b] + cset[c] + cset[d]; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         if (byteRemainder == 1) { |         if (byteRem == 1) { | ||||||
|             chunk = bytes[mainLength]; |             chunk = src[mainLen]; | ||||||
|             a = (chunk & 252) >> 2; // 252 = (2^6 - 1) << 2 |             a = (chunk & 252) >> 2; // (2^6 - 1) << 2 | ||||||
|             b = (chunk & 3) << 4; // 3   = 2^2 - 1  (zero 4 LSB) |             b = (chunk & 3) << 4; // 2^2 - 1  (zero 4 LSB) | ||||||
|             base64 += encodings[a] + encodings[b];//+ '=='; |             base64 += cset[a] + cset[b];//+ '=='; | ||||||
|         } |         } | ||||||
|         else if (byteRemainder == 2) { |         else if (byteRem == 2) { | ||||||
|             chunk = (bytes[mainLength] << 8) | bytes[mainLength + 1]; |             chunk = (src[mainLen] << 8) | src[mainLen + 1]; | ||||||
|             a = (chunk & 64512) >> 10; // 64512 = (2^6 - 1) << 10 |             a = (chunk & 64512) >> 10; // (2^6 - 1) << 10 | ||||||
|             b = (chunk & 1008) >> 4; // 1008  = (2^6 - 1) << 4 |             b = (chunk & 1008) >> 4; // (2^6 - 1) << 4 | ||||||
|             c = (chunk & 15) << 2; // 15    = 2^4 - 1  (zero 2 LSB) |             c = (chunk & 15) << 2; // 2^4 - 1  (zero 2 LSB) | ||||||
|             base64 += encodings[a] + encodings[b] + encodings[c];//+ '='; |             base64 += cset[a] + cset[b] + cset[c];//+ '='; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         return base64; |         return base64; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     function get_chunksize(filesize) { |     function get_chunksize(filesize) { | ||||||
|         var chunksize = 1024 * 1024; |         var chunksize = 1024 * 1024, | ||||||
|         var stepsize = 512 * 1024; |             stepsize = 512 * 1024; | ||||||
|  |  | ||||||
|         while (true) { |         while (true) { | ||||||
|             for (var mul = 1; mul <= 2; mul++) { |             for (var mul = 1; mul <= 2; mul++) { | ||||||
|                 var nchunks = Math.ceil(filesize / chunksize); |                 var nchunks = Math.ceil(filesize / chunksize); | ||||||
| @@ -904,141 +893,77 @@ function up2k_init(have_crypto) { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     function test_filereader_speed(segm_err) { |  | ||||||
|         var f = st.todo.hash[0].fobj, |  | ||||||
|             sz = Math.min(2, f.size), |  | ||||||
|             reader = new FileReader(), |  | ||||||
|             t0, ctr = 0; |  | ||||||
|  |  | ||||||
|         var segm_next = function () { |  | ||||||
|             var t = new Date().getTime(), |  | ||||||
|                 td = t - t0; |  | ||||||
|  |  | ||||||
|             if (++ctr > 2) { |  | ||||||
|                 need_filereader_cache = td > 50; |  | ||||||
|                 st.busy.hash.pop(); |  | ||||||
|                 return; |  | ||||||
|             } |  | ||||||
|             t0 = t; |  | ||||||
|             reader.onload = segm_next; |  | ||||||
|             reader.onerror = segm_err; |  | ||||||
|             reader.readAsArrayBuffer( |  | ||||||
|                 bobslice.call(f, 0, sz)); |  | ||||||
|         }; |  | ||||||
|  |  | ||||||
|         segm_next(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     function ensure_rendered(func) { |  | ||||||
|         var hidden = false; |  | ||||||
|         var keys = ['hidden', 'msHidden', 'webkitHidden']; |  | ||||||
|         for (var a = 0; a < keys.length; a++) |  | ||||||
|             if (typeof document[keys[a]] !== "undefined") |  | ||||||
|                 hidden = document[keys[a]]; |  | ||||||
|  |  | ||||||
|         if (hidden) |  | ||||||
|             return func(); |  | ||||||
|  |  | ||||||
|         window.requestAnimationFrame(func); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     function exec_hash() { |     function exec_hash() { | ||||||
|         if (need_filereader_cache === undefined) { |  | ||||||
|             st.busy.hash.push(1); |  | ||||||
|             return test_filereader_speed(segm_err); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         var t = st.todo.hash.shift(); |         var t = st.todo.hash.shift(); | ||||||
|         st.busy.hash.push(t); |         st.busy.hash.push(t); | ||||||
|         st.bytes.hashed += t.size; |         st.bytes.hashed += t.size; | ||||||
|         t.bytes_uploaded = 0; |         t.bytes_uploaded = 0; | ||||||
|         t.t1 = new Date().getTime(); |  | ||||||
|  |  | ||||||
|         var nchunk = 0; |         var bpend = 0, | ||||||
|         var chunksize = get_chunksize(t.size); |             nchunk = 0, | ||||||
|         var nchunks = Math.ceil(t.size / chunksize); |             chunksize = get_chunksize(t.size), | ||||||
|  |             nchunks = Math.ceil(t.size / chunksize), | ||||||
|         // android-chrome has 180ms latency on FileReader calls, |             hashtab = {}; | ||||||
|         // detect this and do 32MB at a time |  | ||||||
|         var cache_buf = undefined, |  | ||||||
|             cache_ofs = 0, |  | ||||||
|             subchunks = 2; |  | ||||||
|  |  | ||||||
|         while (subchunks * chunksize <= 32 * 1024 * 1024) |  | ||||||
|             subchunks++; |  | ||||||
|  |  | ||||||
|         subchunks--; |  | ||||||
|         if (!need_filereader_cache) |  | ||||||
|             subchunks = 1; |  | ||||||
|  |  | ||||||
|         pvis.setab(t.n, nchunks); |         pvis.setab(t.n, nchunks); | ||||||
|         pvis.move(t.n, 'bz'); |         pvis.move(t.n, 'bz'); | ||||||
|  |  | ||||||
|         var reader = new FileReader(); |  | ||||||
|  |  | ||||||
|         var segm_next = function () { |         var segm_next = function () { | ||||||
|             if (cache_buf) { |             if (nchunk >= nchunks || (bpend > chunksize && bpend >= min_filebuf)) | ||||||
|                 return hash_calc(); |                 return false; | ||||||
|             } |  | ||||||
|             reader.onload = segm_load; |             var reader = new FileReader(), | ||||||
|             reader.onerror = segm_err; |                 nch = nchunk++, | ||||||
|  |                 car = nch * chunksize, | ||||||
|  |                 cdr = car + chunksize, | ||||||
|  |                 t0 = Date.now(); | ||||||
|  |  | ||||||
|             var car = nchunk * chunksize; |  | ||||||
|             var cdr = car + chunksize * subchunks; |  | ||||||
|             if (cdr >= t.size) |             if (cdr >= t.size) | ||||||
|                 cdr = t.size; |                 cdr = t.size; | ||||||
|  |  | ||||||
|  |             bpend += cdr - car; | ||||||
|  |  | ||||||
|  |             reader.onload = function (e) { | ||||||
|  |                 if (!min_filebuf && nch == 1) { | ||||||
|  |                     min_filebuf = 1; | ||||||
|  |                     var td = Date.now() - t0; | ||||||
|  |                     if (td > 50) { | ||||||
|  |                         ebi('u2foot').innerHTML += "<p>excessive filereader latency (" + td + " ms), increasing readahead</p>"; | ||||||
|  |                         min_filebuf = 32 * 1024 * 1024; | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |                 hash_calc(nch, e.target.result); | ||||||
|  |             }; | ||||||
|  |             reader.onerror = function () { | ||||||
|  |                 alert('y o u   b r o k e    i t\nerror: ' + reader.error); | ||||||
|  |             }; | ||||||
|             reader.readAsArrayBuffer( |             reader.readAsArrayBuffer( | ||||||
|                 bobslice.call(t.fobj, car, cdr)); |                 bobslice.call(t.fobj, car, cdr)); | ||||||
|  |  | ||||||
|  |             return true; | ||||||
|         }; |         }; | ||||||
|  |  | ||||||
|         var segm_load = function (e) { |         var hash_calc = function (nch, buf) { | ||||||
|             cache_buf = e.target.result; |             while (segm_next()); | ||||||
|             cache_ofs = 0; |  | ||||||
|             hash_calc(); |  | ||||||
|         }; |  | ||||||
|  |  | ||||||
|         var hash_calc = function () { |  | ||||||
|             var buf = cache_buf; |  | ||||||
|             if (chunksize >= buf.byteLength) |  | ||||||
|                 cache_buf = undefined; |  | ||||||
|             else { |  | ||||||
|                 var ofs = cache_ofs; |  | ||||||
|                 var ofs2 = ofs + Math.min(chunksize, cache_buf.byteLength - cache_ofs); |  | ||||||
|                 cache_ofs = ofs2; |  | ||||||
|                 buf = new Uint8Array(cache_buf).subarray(ofs, ofs2); |  | ||||||
|                 if (ofs2 >= cache_buf.byteLength) |  | ||||||
|                     cache_buf = undefined; |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             var func = function () { |  | ||||||
|                 if (have_crypto) |  | ||||||
|                     crypto.subtle.digest('SHA-512', buf).then(hash_done); |  | ||||||
|                 else { |  | ||||||
|                     var hasher = new asmCrypto.Sha512(); |  | ||||||
|                     hasher.process(new Uint8Array(buf)); |  | ||||||
|                     hasher.finish(); |  | ||||||
|                     hash_done(hasher.result); |  | ||||||
|                 } |  | ||||||
|             }; |  | ||||||
|  |  | ||||||
|             if (cache_buf) |  | ||||||
|                 ensure_rendered(func); |  | ||||||
|             else |  | ||||||
|                 func(); |  | ||||||
|         }; |  | ||||||
|  |  | ||||||
|             var hash_done = function (hashbuf) { |             var hash_done = function (hashbuf) { | ||||||
|             var hslice = new Uint8Array(hashbuf).subarray(0, 32); |                 var hslice = new Uint8Array(hashbuf).subarray(0, 32), | ||||||
|             var b64str = buf2b64(hslice).replace(/=$/, ''); |                     b64str = buf2b64(hslice).replace(/=$/, ''); | ||||||
|             t.hash.push(b64str); |  | ||||||
|  |  | ||||||
|  |                 hashtab[nch] = b64str; | ||||||
|  |                 t.hash.push(nch); | ||||||
|                 pvis.hashed(t); |                 pvis.hashed(t); | ||||||
|             if (++nchunk < nchunks) { |  | ||||||
|  |                 bpend -= buf.byteLength; | ||||||
|  |                 if (t.hash.length < nchunks) { | ||||||
|                     return segm_next(); |                     return segm_next(); | ||||||
|                 } |                 } | ||||||
|  |                 t.hash = []; | ||||||
|  |                 for (var a = 0; a < nchunks; a++) { | ||||||
|  |                     t.hash.push(hashtab[a]); | ||||||
|  |                 } | ||||||
|  |  | ||||||
|             t.t2 = new Date().getTime(); |                 t.t2 = Date.now(); | ||||||
|                 if (t.n == 0 && window.location.hash == '#dbg') { |                 if (t.n == 0 && window.location.hash == '#dbg') { | ||||||
|                     var spd = (t.size / ((t.t2 - t.t1) / 1000.)) / (1024 * 1024.); |                     var spd = (t.size / ((t.t2 - t.t1) / 1000.)) / (1024 * 1024.); | ||||||
|                     alert('{0} ms, {1} MB/s\n'.format(t.t2 - t.t1, spd.toFixed(3)) + t.hash.join('\n')); |                     alert('{0} ms, {1} MB/s\n'.format(t.t2 - t.t1, spd.toFixed(3)) + t.hash.join('\n')); | ||||||
| @@ -1050,10 +975,17 @@ function up2k_init(have_crypto) { | |||||||
|                 st.todo.handshake.push(t); |                 st.todo.handshake.push(t); | ||||||
|             }; |             }; | ||||||
|  |  | ||||||
|         var segm_err = function () { |             if (have_crypto) | ||||||
|             alert('y o u   b r o k e    i t\n\n(was that a folder? just files please)'); |                 crypto.subtle.digest('SHA-512', buf).then(hash_done); | ||||||
|  |             else { | ||||||
|  |                 var hasher = new asmCrypto.Sha512(); | ||||||
|  |                 hasher.process(new Uint8Array(buf)); | ||||||
|  |                 hasher.finish(); | ||||||
|  |                 hash_done(hasher.result); | ||||||
|  |             } | ||||||
|         }; |         }; | ||||||
|  |  | ||||||
|  |         t.t1 = Date.now(); | ||||||
|         segm_next(); |         segm_next(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -1072,8 +1004,9 @@ function up2k_init(have_crypto) { | |||||||
|                 var response = JSON.parse(xhr.responseText); |                 var response = JSON.parse(xhr.responseText); | ||||||
|  |  | ||||||
|                 if (!response.name) { |                 if (!response.name) { | ||||||
|                     var msg = ''; |                     var msg = '', | ||||||
|                     var smsg = ''; |                         smsg = ''; | ||||||
|  |  | ||||||
|                     if (!response || !response.hits || !response.hits.length) { |                     if (!response || !response.hits || !response.hits.length) { | ||||||
|                         msg = 'not found on server'; |                         msg = 'not found on server'; | ||||||
|                         smsg = '404'; |                         smsg = '404'; | ||||||
| @@ -1106,6 +1039,15 @@ function up2k_init(have_crypto) { | |||||||
|                     pvis.seth(t.n, 0, linksplit(esc(t.purl + t.name)).join(' ')); |                     pvis.seth(t.n, 0, linksplit(esc(t.purl + t.name)).join(' ')); | ||||||
|                 } |                 } | ||||||
|  |  | ||||||
|  |                 var chunksize = get_chunksize(t.size), | ||||||
|  |                     cdr_idx = Math.ceil(t.size / chunksize) - 1, | ||||||
|  |                     cdr_sz = (t.size % chunksize) || chunksize, | ||||||
|  |                     cbd = []; | ||||||
|  |  | ||||||
|  |                 for (var a = 0; a <= cdr_idx; a++) { | ||||||
|  |                     cbd.push(a == cdr_idx ? cdr_sz : chunksize); | ||||||
|  |                 } | ||||||
|  |  | ||||||
|                 t.postlist = []; |                 t.postlist = []; | ||||||
|                 t.wark = response.wark; |                 t.wark = response.wark; | ||||||
|                 var missing = response.hash; |                 var missing = response.hash; | ||||||
| @@ -1116,10 +1058,15 @@ function up2k_init(have_crypto) { | |||||||
|                             missing[a], JSON.stringify(t))); |                             missing[a], JSON.stringify(t))); | ||||||
|  |  | ||||||
|                     t.postlist.push(idx); |                     t.postlist.push(idx); | ||||||
|  |                     cbd[idx] = 0; | ||||||
|                 } |                 } | ||||||
|  |  | ||||||
|                 var done = true; |                 pvis.setat(t.n, cbd); | ||||||
|                 var msg = '🎷🐛'; |                 pvis.prog(t, 0, cbd[0]); | ||||||
|  |  | ||||||
|  |                 var done = true, | ||||||
|  |                     msg = '🎷🐛'; | ||||||
|  |  | ||||||
|                 if (t.postlist.length > 0) { |                 if (t.postlist.length > 0) { | ||||||
|                     for (var a = 0; a < t.postlist.length; a++) |                     for (var a = 0; a < t.postlist.length; a++) | ||||||
|                         st.todo.upload.push({ |                         st.todo.upload.push({ | ||||||
| @@ -1136,10 +1083,12 @@ function up2k_init(have_crypto) { | |||||||
|                 if (done) { |                 if (done) { | ||||||
|                     t.done = true; |                     t.done = true; | ||||||
|                     st.bytes.uploaded += t.size - t.bytes_uploaded; |                     st.bytes.uploaded += t.size - t.bytes_uploaded; | ||||||
|                     var spd1 = (t.size / ((t.t2 - t.t1) / 1000.)) / (1024 * 1024.); |                     var spd1 = (t.size / ((t.t2 - t.t1) / 1000.)) / (1024 * 1024.), | ||||||
|                     var spd2 = (t.size / ((t.t4 - t.t3) / 1000.)) / (1024 * 1024.); |                         spd2 = (t.size / ((t.t4 - t.t3) / 1000.)) / (1024 * 1024.); | ||||||
|  |  | ||||||
|                     pvis.seth(t.n, 2, 'hash {0}, up {1} MB/s'.format( |                     pvis.seth(t.n, 2, 'hash {0}, up {1} MB/s'.format( | ||||||
|                         spd1.toFixed(2), spd2.toFixed(2))); |                         spd1.toFixed(2), spd2.toFixed(2))); | ||||||
|  |  | ||||||
|                     pvis.move(t.n, 'ok'); |                     pvis.move(t.n, 'ok'); | ||||||
|                 } |                 } | ||||||
|                 else t.t4 = undefined; |                 else t.t4 = undefined; | ||||||
| @@ -1206,38 +1155,34 @@ function up2k_init(have_crypto) { | |||||||
|         var upt = st.todo.upload.shift(); |         var upt = st.todo.upload.shift(); | ||||||
|         st.busy.upload.push(upt); |         st.busy.upload.push(upt); | ||||||
|  |  | ||||||
|         var npart = upt.npart; |         var npart = upt.npart, | ||||||
|         var t = st.files[upt.nfile]; |             t = st.files[upt.nfile]; | ||||||
|  |  | ||||||
|  |         if (!t.t3) | ||||||
|  |             t.t3 = Date.now(); | ||||||
|  |  | ||||||
|         pvis.seth(t.n, 1, "🚀 send"); |         pvis.seth(t.n, 1, "🚀 send"); | ||||||
|  |  | ||||||
|         var chunksize = get_chunksize(t.size); |         var chunksize = get_chunksize(t.size), | ||||||
|         var car = npart * chunksize; |             car = npart * chunksize, | ||||||
|         var cdr = car + chunksize; |             cdr = car + chunksize; | ||||||
|  |  | ||||||
|         if (cdr >= t.size) |         if (cdr >= t.size) | ||||||
|             cdr = t.size; |             cdr = t.size; | ||||||
|  |  | ||||||
|         var reader = new FileReader(); |  | ||||||
|  |  | ||||||
|         reader.onerror = function () { |  | ||||||
|             alert('y o u   b r o k e    i t\n\n(was that a folder? just files please)'); |  | ||||||
|         }; |  | ||||||
|  |  | ||||||
|         reader.onload = function (e) { |  | ||||||
|         var xhr = new XMLHttpRequest(); |         var xhr = new XMLHttpRequest(); | ||||||
|         xhr.upload.onprogress = function (xev) { |         xhr.upload.onprogress = function (xev) { | ||||||
|                 var perc = xev.loaded / (cdr - car) * 100; |             pvis.prog(t, npart, xev.loaded); | ||||||
|                 pvis.prog(t, npart, perc, t); |  | ||||||
|         }; |         }; | ||||||
|         xhr.onload = function (xev) { |         xhr.onload = function (xev) { | ||||||
|             if (xhr.status == 200) { |             if (xhr.status == 200) { | ||||||
|                     pvis.prog(t, npart, 101, t); |                 pvis.prog(t, npart, cdr - car); | ||||||
|                 st.bytes.uploaded += cdr - car; |                 st.bytes.uploaded += cdr - car; | ||||||
|                 t.bytes_uploaded += cdr - car; |                 t.bytes_uploaded += cdr - car; | ||||||
|                 st.busy.upload.splice(st.busy.upload.indexOf(upt), 1); |                 st.busy.upload.splice(st.busy.upload.indexOf(upt), 1); | ||||||
|                 t.postlist.splice(t.postlist.indexOf(npart), 1); |                 t.postlist.splice(t.postlist.indexOf(npart), 1); | ||||||
|                 if (t.postlist.length == 0) { |                 if (t.postlist.length == 0) { | ||||||
|                         t.t4 = new Date().getTime(); |                     t.t4 = Date.now(); | ||||||
|                     pvis.seth(t.n, 1, 'verifying'); |                     pvis.seth(t.n, 1, 'verifying'); | ||||||
|                     st.todo.handshake.unshift(t); |                     st.todo.handshake.unshift(t); | ||||||
|                 } |                 } | ||||||
| @@ -1251,7 +1196,6 @@ function up2k_init(have_crypto) { | |||||||
|                         "no further information")); |                         "no further information")); | ||||||
|         }; |         }; | ||||||
|         xhr.open('POST', t.purl + 'chunkpit.php', true); |         xhr.open('POST', t.purl + 'chunkpit.php', true); | ||||||
|             //xhr.setRequestHeader("X-Up2k-Hash", t.hash[npart].substr(1) + "x"); |  | ||||||
|         xhr.setRequestHeader("X-Up2k-Hash", t.hash[npart]); |         xhr.setRequestHeader("X-Up2k-Hash", t.hash[npart]); | ||||||
|         xhr.setRequestHeader("X-Up2k-Wark", t.wark); |         xhr.setRequestHeader("X-Up2k-Wark", t.wark); | ||||||
|         xhr.setRequestHeader('Content-Type', 'application/octet-stream'); |         xhr.setRequestHeader('Content-Type', 'application/octet-stream'); | ||||||
| @@ -1259,13 +1203,7 @@ function up2k_init(have_crypto) { | |||||||
|             xhr.overrideMimeType('Content-Type', 'application/octet-stream'); |             xhr.overrideMimeType('Content-Type', 'application/octet-stream'); | ||||||
|  |  | ||||||
|         xhr.responseType = 'text'; |         xhr.responseType = 'text'; | ||||||
|             xhr.send(e.target.result); |         xhr.send(bobslice.call(t.fobj, car, cdr)); | ||||||
|  |  | ||||||
|             if (!t.t3) |  | ||||||
|                 t.t3 = new Date().getTime(); |  | ||||||
|         }; |  | ||||||
|  |  | ||||||
|         reader.readAsArrayBuffer(bobslice.call(t.fobj, car, cdr)); |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     ///// |     ///// | ||||||
| @@ -1293,20 +1231,20 @@ function up2k_init(have_crypto) { | |||||||
|     onresize(); |     onresize(); | ||||||
|  |  | ||||||
|     function desc_show(e) { |     function desc_show(e) { | ||||||
|         var msg = this.getAttribute('alt'); |         var msg = this.getAttribute('alt'), | ||||||
|         msg = msg.replace(/\$N/g, "<br />"); |             cdesc = ebi('u2cdesc'); | ||||||
|         var cdesc = ebi('u2cdesc'); |  | ||||||
|         cdesc.innerHTML = msg; |         cdesc.innerHTML = msg.replace(/\$N/g, "<br />"); | ||||||
|         cdesc.setAttribute('class', 'show'); |         cdesc.setAttribute('class', 'show'); | ||||||
|     } |     } | ||||||
|     function desc_hide(e) { |     function desc_hide(e) { | ||||||
|         ebi('u2cdesc').setAttribute('class', ''); |         ebi('u2cdesc').setAttribute('class', ''); | ||||||
|     } |     } | ||||||
|     var o = document.querySelectorAll('#u2conf *[alt]'); |     var o = QSA('#u2conf *[alt]'); | ||||||
|     for (var a = o.length - 1; a >= 0; a--) { |     for (var a = o.length - 1; a >= 0; a--) { | ||||||
|         o[a].parentNode.getElementsByTagName('input')[0].setAttribute('alt', o[a].getAttribute('alt')); |         o[a].parentNode.getElementsByTagName('input')[0].setAttribute('alt', o[a].getAttribute('alt')); | ||||||
|     } |     } | ||||||
|     var o = document.querySelectorAll('#u2conf *[alt]'); |     var o = QSA('#u2conf *[alt]'); | ||||||
|     for (var a = 0; a < o.length; a++) { |     for (var a = 0; a < o.length; a++) { | ||||||
|         o[a].onfocus = desc_show; |         o[a].onfocus = desc_show; | ||||||
|         o[a].onblur = desc_hide; |         o[a].onblur = desc_hide; | ||||||
| @@ -1322,14 +1260,14 @@ function up2k_init(have_crypto) { | |||||||
|  |  | ||||||
|         var obj = ebi('nthread'); |         var obj = ebi('nthread'); | ||||||
|         if (dir.target) { |         if (dir.target) { | ||||||
|             obj.style.background = '#922'; |             clmod(obj, 'err', 1); | ||||||
|             var v = Math.floor(parseInt(obj.value)); |             var v = Math.floor(parseInt(obj.value)); | ||||||
|             if (v < 1 || v > 8 || v !== v) |             if (v < 1 || v > 8 || v !== v) | ||||||
|                 return; |                 return; | ||||||
|  |  | ||||||
|             parallel_uploads = v; |             parallel_uploads = v; | ||||||
|             swrite('nthread', v); |             swrite('nthread', v); | ||||||
|             obj.style.background = '#444'; |             clmod(obj, 'err'); | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
|  |  | ||||||
| @@ -1360,8 +1298,8 @@ function up2k_init(have_crypto) { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     function set_fsearch(new_state) { |     function set_fsearch(new_state) { | ||||||
|         var perms = document.body.getAttribute('perms'); |         var perms = document.body.getAttribute('perms'), | ||||||
|         var read_only = false; |             read_only = false; | ||||||
|  |  | ||||||
|         if (!ebi('fsearch')) { |         if (!ebi('fsearch')) { | ||||||
|             new_state = false; |             new_state = false; | ||||||
| @@ -1377,16 +1315,16 @@ function up2k_init(have_crypto) { | |||||||
|         } |         } | ||||||
|  |  | ||||||
|         try { |         try { | ||||||
|             document.querySelector('label[for="fsearch"]').style.opacity = read_only ? '0' : '1'; |             QS('label[for="fsearch"]').style.opacity = read_only ? '0' : '1'; | ||||||
|         } |         } | ||||||
|         catch (ex) { } |         catch (ex) { } | ||||||
|  |  | ||||||
|         try { |         try { | ||||||
|             var fun = fsearch ? 'add' : 'remove'; |             var fun = fsearch ? 'add' : 'remove', | ||||||
|             ebi('op_up2k').classList[fun]('srch'); |                 ico = fsearch ? '🔎' : '🚀', | ||||||
|  |                 desc = fsearch ? 'Search' : 'Upload'; | ||||||
|  |  | ||||||
|             var ico = fsearch ? '🔎' : '🚀'; |             ebi('op_up2k').classList[fun]('srch'); | ||||||
|             var desc = fsearch ? 'Search' : 'Upload'; |  | ||||||
|             ebi('u2bm').innerHTML = ico + ' <sup>' + desc + '</sup>'; |             ebi('u2bm').innerHTML = ico + ' <sup>' + desc + '</sup>'; | ||||||
|         } |         } | ||||||
|         catch (ex) { } |         catch (ex) { } | ||||||
| @@ -1453,5 +1391,5 @@ function warn_uploader_busy(e) { | |||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
| if (document.querySelector('#op_up2k.act')) | if (QS('#op_up2k.act')) | ||||||
|     goto_up2k(); |     goto_up2k(); | ||||||
|   | |||||||
| @@ -90,8 +90,10 @@ | |||||||
| 	background: #222; | 	background: #222; | ||||||
| } | } | ||||||
| #u2cards { | #u2cards { | ||||||
| 	margin: 2.5em auto -2.5em auto; | 	padding: 1em 0 .3em 1em; | ||||||
|  | 	margin: 1.5em auto -2.5em auto; | ||||||
| 	text-align: center; | 	text-align: center; | ||||||
|  | 	overflow: hidden; | ||||||
| } | } | ||||||
| #u2cards.w { | #u2cards.w { | ||||||
| 	width: 45em; | 	width: 45em; | ||||||
| @@ -110,10 +112,15 @@ | |||||||
| 	border-radius: 0 .4em 0 0; | 	border-radius: 0 .4em 0 0; | ||||||
| } | } | ||||||
| #u2cards a.act { | #u2cards a.act { | ||||||
| 	border-width: 1px 1px 0 1px; | 	padding-bottom: .5em; | ||||||
|  | 	border-width: 1px 1px .1em 1px; | ||||||
| 	border-radius: .3em .3em 0 0; | 	border-radius: .3em .3em 0 0; | ||||||
| 	margin-left: -1px; | 	margin-left: -1px; | ||||||
| 	background: transparent; | 	background: linear-gradient(to bottom, #464, #333 80%); | ||||||
|  | 	box-shadow: 0 -.17em .67em #280; | ||||||
|  | 	border-color: #7c5 #583 #333 #583; | ||||||
|  | 	position: relative; | ||||||
|  | 	color: #fd7; | ||||||
| } | } | ||||||
| #u2cards span { | #u2cards span { | ||||||
| 	color: #fff; | 	color: #fff; | ||||||
| @@ -134,12 +141,16 @@ | |||||||
| 	outline: none; | 	outline: none; | ||||||
| } | } | ||||||
| #u2conf .txtbox { | #u2conf .txtbox { | ||||||
| 	width: 4em; | 	width: 3em; | ||||||
| 	color: #fff; | 	color: #fff; | ||||||
| 	background: #444; | 	background: #444; | ||||||
| 	border: 1px solid #777; | 	border: 1px solid #777; | ||||||
| 	font-size: 1.2em; | 	font-size: 1.2em; | ||||||
| 	padding: .15em 0; | 	padding: .15em 0; | ||||||
|  | 	height: 1.05em; | ||||||
|  | } | ||||||
|  | #u2conf .txtbox.err { | ||||||
|  | 	background: #922; | ||||||
| } | } | ||||||
| #u2conf a { | #u2conf a { | ||||||
| 	color: #fff; | 	color: #fff; | ||||||
| @@ -148,13 +159,12 @@ | |||||||
| 	border-radius: .1em; | 	border-radius: .1em; | ||||||
| 	font-size: 1.5em; | 	font-size: 1.5em; | ||||||
| 	padding: .1em 0; | 	padding: .1em 0; | ||||||
| 	margin: 0 -.25em; | 	margin: 0 -1px; | ||||||
| 	width: 1.5em; | 	width: 1.5em; | ||||||
| 	height: 1em; | 	height: 1em; | ||||||
| 	display: inline-block; | 	display: inline-block; | ||||||
| 	position: relative; | 	position: relative; | ||||||
| 	line-height: 1em; | 	bottom: -0.08em; | ||||||
| 	bottom: -.08em; |  | ||||||
| } | } | ||||||
| #u2conf input+a { | #u2conf input+a { | ||||||
| 	background: #d80; | 	background: #d80; | ||||||
| @@ -165,7 +175,6 @@ | |||||||
| 	height: 1em; | 	height: 1em; | ||||||
| 	padding: .4em 0; | 	padding: .4em 0; | ||||||
| 	display: block; | 	display: block; | ||||||
| 	user-select: none; |  | ||||||
| 	border-radius: .25em; | 	border-radius: .25em; | ||||||
| } | } | ||||||
| #u2conf input[type="checkbox"] { | #u2conf input[type="checkbox"] { | ||||||
| @@ -205,12 +214,13 @@ | |||||||
| 	text-align: center; | 	text-align: center; | ||||||
| 	overflow: hidden; | 	overflow: hidden; | ||||||
| 	margin: 0 -2em; | 	margin: 0 -2em; | ||||||
| 	height: 0; |  | ||||||
| 	padding: 0 1em; | 	padding: 0 1em; | ||||||
|  | 	height: 0; | ||||||
| 	opacity: .1; | 	opacity: .1; | ||||||
|     transition: all 0.14s ease-in-out; |     transition: all 0.14s ease-in-out; | ||||||
| 	border-radius: .4em; |  | ||||||
| 	box-shadow: 0 .2em .5em #222; | 	box-shadow: 0 .2em .5em #222; | ||||||
|  | 	border-radius: .4em; | ||||||
|  | 	z-index: 1; | ||||||
| } | } | ||||||
| #u2cdesc.show { | #u2cdesc.show { | ||||||
| 	padding: 1em; | 	padding: 1em; | ||||||
| @@ -238,3 +248,41 @@ | |||||||
| 	float: right; | 	float: right; | ||||||
| 	margin-bottom: -.3em; | 	margin-bottom: -.3em; | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | html.light #u2btn { | ||||||
|  | 	box-shadow: .4em .4em 0 #ccc; | ||||||
|  | } | ||||||
|  | html.light #u2cards span { | ||||||
|  | 	color: #000; | ||||||
|  | } | ||||||
|  | html.light #u2cards a { | ||||||
|  | 	background: linear-gradient(to bottom, #eee, #fff); | ||||||
|  | } | ||||||
|  | html.light #u2cards a.act { | ||||||
|  | 	color: #037; | ||||||
|  | 	background: inherit; | ||||||
|  | 	box-shadow: 0 -.17em .67em #0ad; | ||||||
|  | 	border-color: #09c #05a #eee #05a; | ||||||
|  | } | ||||||
|  | html.light #u2conf .txtbox { | ||||||
|  | 	background: #fff; | ||||||
|  | 	color: #444; | ||||||
|  | } | ||||||
|  | html.light #u2conf .txtbox.err { | ||||||
|  | 	background: #f96; | ||||||
|  | 	color: #300; | ||||||
|  | } | ||||||
|  | html.light #u2cdesc { | ||||||
|  | 	background: #fff; | ||||||
|  | 	border: none; | ||||||
|  | } | ||||||
|  | html.light #op_up2k.srch #u2btn { | ||||||
|  | 	border-color: #a80; | ||||||
|  | } | ||||||
|  | html.light #u2foot { | ||||||
|  | 	color: #000; | ||||||
|  | } | ||||||
|   | |||||||
| @@ -59,9 +59,9 @@ | |||||||
|                 </tr> |                 </tr> | ||||||
|                 <tr> |                 <tr> | ||||||
|                     <td> |                     <td> | ||||||
|                         <a href="#" id="nthread_sub">–</a> |                         <a href="#" id="nthread_sub">–</a><input | ||||||
|                         <input class="txtbox" id="nthread" value="2" /> |                             class="txtbox" id="nthread" value="2"/><a | ||||||
|                         <a href="#" id="nthread_add">+</a> |                             href="#" id="nthread_add">+</a> | ||||||
|                     </td> |                     </td> | ||||||
|                 </tr> |                 </tr> | ||||||
|             </table> |             </table> | ||||||
|   | |||||||
| @@ -6,7 +6,8 @@ if (!window['console']) | |||||||
|     }; |     }; | ||||||
|  |  | ||||||
|  |  | ||||||
| var clickev = window.Touch ? 'touchstart' : 'click'; | var clickev = window.Touch ? 'touchstart' : 'click', | ||||||
|  |     ANDROID = /(android)/i.test(navigator.userAgent); | ||||||
|  |  | ||||||
|  |  | ||||||
| // error handler for mobile devices | // error handler for mobile devices | ||||||
| @@ -49,9 +50,11 @@ function vis_exh(msg, url, lineNo, columnNo, error) { | |||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
| function ebi(id) { | var ebi = document.getElementById.bind(document), | ||||||
|     return document.getElementById(id); |     QS = document.querySelector.bind(document), | ||||||
| } |     QSA = document.querySelectorAll.bind(document), | ||||||
|  |     mknod = document.createElement.bind(document); | ||||||
|  |  | ||||||
|  |  | ||||||
| function ev(e) { | function ev(e) { | ||||||
|     e = e || window.event; |     e = e || window.event; | ||||||
| @@ -89,7 +92,7 @@ if (!String.startsWith) { | |||||||
| // https://stackoverflow.com/a/950146 | // https://stackoverflow.com/a/950146 | ||||||
| function import_js(url, cb) { | function import_js(url, cb) { | ||||||
|     var head = document.head || document.getElementsByTagName('head')[0]; |     var head = document.head || document.getElementsByTagName('head')[0]; | ||||||
|     var script = document.createElement('script'); |     var script = mknod('script'); | ||||||
|     script.type = 'text/javascript'; |     script.type = 'text/javascript'; | ||||||
|     script.src = url; |     script.src = url; | ||||||
|  |  | ||||||
| @@ -274,7 +277,7 @@ function makeSortable(table, cb) { | |||||||
|  |  | ||||||
|  |  | ||||||
| (function () { | (function () { | ||||||
|     var ops = document.querySelectorAll('#ops>a'); |     var ops = QSA('#ops>a'); | ||||||
|     for (var a = 0; a < ops.length; a++) { |     for (var a = 0; a < ops.length; a++) { | ||||||
|         ops[a].onclick = opclick; |         ops[a].onclick = opclick; | ||||||
|     } |     } | ||||||
| @@ -289,25 +292,25 @@ function opclick(e) { | |||||||
|  |  | ||||||
|     swrite('opmode', dest || null); |     swrite('opmode', dest || null); | ||||||
|  |  | ||||||
|     var input = document.querySelector('.opview.act input:not([type="hidden"])') |     var input = QS('.opview.act input:not([type="hidden"])') | ||||||
|     if (input) |     if (input) | ||||||
|         input.focus(); |         input.focus(); | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
| function goto(dest) { | function goto(dest) { | ||||||
|     var obj = document.querySelectorAll('.opview.act'); |     var obj = QSA('.opview.act'); | ||||||
|     for (var a = obj.length - 1; a >= 0; a--) |     for (var a = obj.length - 1; a >= 0; a--) | ||||||
|         clmod(obj[a], 'act'); |         clmod(obj[a], 'act'); | ||||||
|  |  | ||||||
|     obj = document.querySelectorAll('#ops>a'); |     obj = QSA('#ops>a'); | ||||||
|     for (var a = obj.length - 1; a >= 0; a--) |     for (var a = obj.length - 1; a >= 0; a--) | ||||||
|         clmod(obj[a], 'act'); |         clmod(obj[a], 'act'); | ||||||
|  |  | ||||||
|     if (dest) { |     if (dest) { | ||||||
|         var ui = ebi('op_' + dest); |         var ui = ebi('op_' + dest); | ||||||
|         clmod(ui, 'act', true); |         clmod(ui, 'act', true); | ||||||
|         document.querySelector('#ops>a[data-dest=' + dest + ']').className += " act"; |         QS('#ops>a[data-dest=' + dest + ']').className += " act"; | ||||||
|  |  | ||||||
|         var fn = window['goto_' + dest]; |         var fn = window['goto_' + dest]; | ||||||
|         if (fn) |         if (fn) | ||||||
|   | |||||||
| @@ -171,7 +171,7 @@ Range: bytes=26-         Content-Range: bytes */26 | |||||||
|  |  | ||||||
| var tsh = []; | var tsh = []; | ||||||
| function convert_markdown(md_text, dest_dom) { | function convert_markdown(md_text, dest_dom) { | ||||||
|     tsh.push(new Date().getTime()); |     tsh.push(Date.now()); | ||||||
|     while (tsh.length > 10) |     while (tsh.length > 10) | ||||||
|         tsh.shift(); |         tsh.shift(); | ||||||
|     if (tsh.length > 1) { |     if (tsh.length > 1) { | ||||||
|   | |||||||
| @@ -26,7 +26,7 @@ CKSUM = None | |||||||
| STAMP = None | STAMP = None | ||||||
|  |  | ||||||
| PY2 = sys.version_info[0] == 2 | PY2 = sys.version_info[0] == 2 | ||||||
| WINDOWS = sys.platform == "win32" | WINDOWS = sys.platform in ["win32", "msys"] | ||||||
| sys.dont_write_bytecode = True | sys.dont_write_bytecode = True | ||||||
| me = os.path.abspath(os.path.realpath(__file__)) | me = os.path.abspath(os.path.realpath(__file__)) | ||||||
| cpp = None | cpp = None | ||||||
|   | |||||||
							
								
								
									
										0
									
								
								tests/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								tests/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										33
									
								
								tests/run.py
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										33
									
								
								tests/run.py
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,33 @@ | |||||||
|  | #!/usr/bin/env python3 | ||||||
|  |  | ||||||
|  | import sys | ||||||
|  | import runpy | ||||||
|  |  | ||||||
|  | host = sys.argv[1] | ||||||
|  | sys.argv = sys.argv[:1] + sys.argv[2:] | ||||||
|  | sys.path.insert(0, ".") | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def rp(): | ||||||
|  |     runpy.run_module("unittest", run_name="__main__") | ||||||
|  |  | ||||||
|  |  | ||||||
|  | if host == "vmprof": | ||||||
|  |     rp() | ||||||
|  |  | ||||||
|  | elif host == "cprofile": | ||||||
|  |     import cProfile | ||||||
|  |     import pstats | ||||||
|  |  | ||||||
|  |     log_fn = "cprofile.log" | ||||||
|  |     cProfile.run("rp()", log_fn) | ||||||
|  |     p = pstats.Stats(log_fn) | ||||||
|  |     p.sort_stats(pstats.SortKey.CUMULATIVE).print_stats(64) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | """ | ||||||
|  | python3.9 tests/run.py cprofile -v tests/test_httpcli.py | ||||||
|  |  | ||||||
|  | python3.9 -m pip install --user vmprof | ||||||
|  | python3.9 -m vmprof --lines -o vmprof.log tests/run.py vmprof -v tests/test_httpcli.py | ||||||
|  | """ | ||||||
							
								
								
									
										202
									
								
								tests/test_httpcli.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										202
									
								
								tests/test_httpcli.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,202 @@ | |||||||
|  | #!/usr/bin/env python | ||||||
|  | # coding: utf-8 | ||||||
|  | from __future__ import print_function, unicode_literals | ||||||
|  |  | ||||||
|  | import io | ||||||
|  | import os | ||||||
|  | import time | ||||||
|  | import shutil | ||||||
|  | import pprint | ||||||
|  | import tarfile | ||||||
|  | import unittest | ||||||
|  |  | ||||||
|  | from argparse import Namespace | ||||||
|  | from copyparty.authsrv import AuthSrv | ||||||
|  | from copyparty.httpcli import HttpCli | ||||||
|  |  | ||||||
|  | from tests import util as tu | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def hdr(query): | ||||||
|  |     h = "GET /{} HTTP/1.1\r\nCookie: cppwd=o\r\nConnection: close\r\n\r\n" | ||||||
|  |     return h.format(query).encode("utf-8") | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class Cfg(Namespace): | ||||||
|  |     def __init__(self, a=[], v=[], c=None): | ||||||
|  |         super(Cfg, self).__init__( | ||||||
|  |             a=a, | ||||||
|  |             v=v, | ||||||
|  |             c=c, | ||||||
|  |             ed=False, | ||||||
|  |             no_zip=False, | ||||||
|  |             no_scandir=False, | ||||||
|  |             no_sendfile=True, | ||||||
|  |             nih=True, | ||||||
|  |             mtp=[], | ||||||
|  |             mte="a", | ||||||
|  |             **{k: False for k in "e2d e2ds e2dsa e2t e2ts e2tsr".split()} | ||||||
|  |         ) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class TestHttpCli(unittest.TestCase): | ||||||
|  |     def test(self): | ||||||
|  |         td = os.path.join(tu.get_ramdisk(), "vfs") | ||||||
|  |         try: | ||||||
|  |             shutil.rmtree(td) | ||||||
|  |         except OSError: | ||||||
|  |             pass | ||||||
|  |  | ||||||
|  |         os.mkdir(td) | ||||||
|  |         os.chdir(td) | ||||||
|  |  | ||||||
|  |         self.dtypes = ["ra", "ro", "rx", "wa", "wo", "wx", "aa", "ao", "ax"] | ||||||
|  |         self.can_read = ["ra", "ro", "aa", "ao"] | ||||||
|  |         self.can_write = ["wa", "wo", "aa", "ao"] | ||||||
|  |         self.fn = "g{:x}g".format(int(time.time() * 3)) | ||||||
|  |  | ||||||
|  |         allfiles = [] | ||||||
|  |         allvols = [] | ||||||
|  |         for top in self.dtypes: | ||||||
|  |             allvols.append(top) | ||||||
|  |             allfiles.append("/".join([top, self.fn])) | ||||||
|  |             for s1 in self.dtypes: | ||||||
|  |                 p = "/".join([top, s1]) | ||||||
|  |                 allvols.append(p) | ||||||
|  |                 allfiles.append(p + "/" + self.fn) | ||||||
|  |                 allfiles.append(p + "/n/" + self.fn) | ||||||
|  |                 for s2 in self.dtypes: | ||||||
|  |                     p = "/".join([top, s1, "n", s2]) | ||||||
|  |                     os.makedirs(p) | ||||||
|  |                     allvols.append(p) | ||||||
|  |                     allfiles.append(p + "/" + self.fn) | ||||||
|  |  | ||||||
|  |         for fp in allfiles: | ||||||
|  |             with open(fp, "w") as f: | ||||||
|  |                 f.write("ok {}\n".format(fp)) | ||||||
|  |  | ||||||
|  |         for top in self.dtypes: | ||||||
|  |             vcfg = [] | ||||||
|  |             for vol in allvols: | ||||||
|  |                 if not vol.startswith(top): | ||||||
|  |                     continue | ||||||
|  |  | ||||||
|  |                 mode = vol[-2] | ||||||
|  |                 usr = vol[-1] | ||||||
|  |                 if usr == "a": | ||||||
|  |                     usr = "" | ||||||
|  |  | ||||||
|  |                 if "/" not in vol: | ||||||
|  |                     vol += "/" | ||||||
|  |  | ||||||
|  |                 top, sub = vol.split("/", 1) | ||||||
|  |                 vcfg.append("{0}/{1}:{1}:{2}{3}".format(top, sub, mode, usr)) | ||||||
|  |  | ||||||
|  |             pprint.pprint(vcfg) | ||||||
|  |  | ||||||
|  |             self.args = Cfg(v=vcfg, a=["o:o", "x:x"]) | ||||||
|  |             self.auth = AuthSrv(self.args, self.log) | ||||||
|  |             vfiles = [x for x in allfiles if x.startswith(top)] | ||||||
|  |             for fp in vfiles: | ||||||
|  |                 rok, wok = self.can_rw(fp) | ||||||
|  |                 furl = fp.split("/", 1)[1] | ||||||
|  |                 durl = furl.rsplit("/", 1)[0] if "/" in furl else "" | ||||||
|  |  | ||||||
|  |                 # file download | ||||||
|  |                 h, ret = self.curl(furl) | ||||||
|  |                 res = "ok " + fp in ret | ||||||
|  |                 print("[{}] {} {} = {}".format(fp, rok, wok, res)) | ||||||
|  |                 if rok != res: | ||||||
|  |                     print("\033[33m{}\n# {}\033[0m".format(ret, furl)) | ||||||
|  |                     self.fail() | ||||||
|  |  | ||||||
|  |                 # file browser: html | ||||||
|  |                 h, ret = self.curl(durl) | ||||||
|  |                 res = "'{}'".format(self.fn) in ret | ||||||
|  |                 print(res) | ||||||
|  |                 if rok != res: | ||||||
|  |                     print("\033[33m{}\n# {}\033[0m".format(ret, durl)) | ||||||
|  |                     self.fail() | ||||||
|  |  | ||||||
|  |                 # file browser: json | ||||||
|  |                 url = durl + "?ls" | ||||||
|  |                 h, ret = self.curl(url) | ||||||
|  |                 res = '"{}"'.format(self.fn) in ret | ||||||
|  |                 print(res) | ||||||
|  |                 if rok != res: | ||||||
|  |                     print("\033[33m{}\n# {}\033[0m".format(ret, url)) | ||||||
|  |                     self.fail() | ||||||
|  |  | ||||||
|  |                 # tar | ||||||
|  |                 url = durl + "?tar" | ||||||
|  |                 h, b = self.curl(url, True) | ||||||
|  |                 # with open(os.path.join(td, "tar"), "wb") as f: | ||||||
|  |                 #    f.write(b) | ||||||
|  |                 try: | ||||||
|  |                     tar = tarfile.open(fileobj=io.BytesIO(b)).getnames() | ||||||
|  |                 except: | ||||||
|  |                     tar = [] | ||||||
|  |                 tar = ["/".join([y for y in [top, durl, x] if y]) for x in tar] | ||||||
|  |                 tar = [[x] + self.can_rw(x) for x in tar] | ||||||
|  |                 tar_ok = [x[0] for x in tar if x[1]] | ||||||
|  |                 tar_ng = [x[0] for x in tar if not x[1]] | ||||||
|  |                 self.assertEqual([], tar_ng) | ||||||
|  |  | ||||||
|  |                 if durl.split("/")[-1] in self.can_read: | ||||||
|  |                     ref = [x for x in vfiles if self.in_dive(top + "/" + durl, x)] | ||||||
|  |                     for f in ref: | ||||||
|  |                         print("{}: {}".format("ok" if f in tar_ok else "NG", f)) | ||||||
|  |                     ref.sort() | ||||||
|  |                     tar_ok.sort() | ||||||
|  |                     self.assertEqual(ref, tar_ok) | ||||||
|  |  | ||||||
|  |                 # stash | ||||||
|  |                 h, ret = self.put(url) | ||||||
|  |                 res = h.startswith("HTTP/1.1 200 ") | ||||||
|  |                 self.assertEqual(res, wok) | ||||||
|  |  | ||||||
|  |     def can_rw(self, fp): | ||||||
|  |         # lowest non-neutral folder declares permissions | ||||||
|  |         expect = fp.split("/")[:-1] | ||||||
|  |         for x in reversed(expect): | ||||||
|  |             if x != "n": | ||||||
|  |                 expect = x | ||||||
|  |                 break | ||||||
|  |  | ||||||
|  |         return [expect in self.can_read, expect in self.can_write] | ||||||
|  |  | ||||||
|  |     def in_dive(self, top, fp): | ||||||
|  |         # archiver bails at first inaccessible subvolume | ||||||
|  |         top = top.strip("/").split("/") | ||||||
|  |         fp = fp.split("/") | ||||||
|  |         for f1, f2 in zip(top, fp): | ||||||
|  |             if f1 != f2: | ||||||
|  |                 return False | ||||||
|  |  | ||||||
|  |         for f in fp[len(top) :]: | ||||||
|  |             if f == self.fn: | ||||||
|  |                 return True | ||||||
|  |             if f not in self.can_read and f != "n": | ||||||
|  |                 return False | ||||||
|  |  | ||||||
|  |         return True | ||||||
|  |  | ||||||
|  |     def put(self, url): | ||||||
|  |         buf = "PUT /{0} HTTP/1.1\r\nCookie: cppwd=o\r\nConnection: close\r\nContent-Length: {1}\r\n\r\nok {0}\n" | ||||||
|  |         buf = buf.format(url, len(url) + 4).encode("utf-8") | ||||||
|  |         conn = tu.VHttpConn(self.args, self.auth, self.log, buf) | ||||||
|  |         HttpCli(conn).run() | ||||||
|  |         return conn.s._reply.decode("utf-8").split("\r\n\r\n", 1) | ||||||
|  |  | ||||||
|  |     def curl(self, url, binary=False): | ||||||
|  |         conn = tu.VHttpConn(self.args, self.auth, self.log, hdr(url)) | ||||||
|  |         HttpCli(conn).run() | ||||||
|  |         if binary: | ||||||
|  |             h, b = conn.s._reply.split(b"\r\n\r\n", 1) | ||||||
|  |             return [h.decode("utf-8"), b] | ||||||
|  |  | ||||||
|  |         return conn.s._reply.decode("utf-8").split("\r\n\r\n", 1) | ||||||
|  |  | ||||||
|  |     def log(self, src, msg, c=0): | ||||||
|  |         # print(repr(msg)) | ||||||
|  |         pass | ||||||
| @@ -3,18 +3,18 @@ | |||||||
| from __future__ import print_function, unicode_literals | from __future__ import print_function, unicode_literals | ||||||
|  |  | ||||||
| import os | import os | ||||||
| import time |  | ||||||
| import json | import json | ||||||
| import shutil | import shutil | ||||||
| import tempfile | import tempfile | ||||||
| import unittest | import unittest | ||||||
| import subprocess as sp  # nosec |  | ||||||
|  |  | ||||||
| from textwrap import dedent | from textwrap import dedent | ||||||
| from argparse import Namespace | from argparse import Namespace | ||||||
| from copyparty.authsrv import AuthSrv | from copyparty.authsrv import AuthSrv | ||||||
| from copyparty import util | from copyparty import util | ||||||
|  |  | ||||||
|  | from tests import util as tu | ||||||
|  |  | ||||||
|  |  | ||||||
| class Cfg(Namespace): | class Cfg(Namespace): | ||||||
|     def __init__(self, a=[], v=[], c=None): |     def __init__(self, a=[], v=[], c=None): | ||||||
| @@ -51,52 +51,11 @@ class TestVFS(unittest.TestCase): | |||||||
|         real = [x[0] for x in real] |         real = [x[0] for x in real] | ||||||
|         return fsdir, real, virt |         return fsdir, real, virt | ||||||
|  |  | ||||||
|     def runcmd(self, *argv): |  | ||||||
|         p = sp.Popen(argv, stdout=sp.PIPE, stderr=sp.PIPE) |  | ||||||
|         stdout, stderr = p.communicate() |  | ||||||
|         stdout = stdout.decode("utf-8") |  | ||||||
|         stderr = stderr.decode("utf-8") |  | ||||||
|         return [p.returncode, stdout, stderr] |  | ||||||
|  |  | ||||||
|     def chkcmd(self, *argv): |  | ||||||
|         ok, sout, serr = self.runcmd(*argv) |  | ||||||
|         if ok != 0: |  | ||||||
|             raise Exception(serr) |  | ||||||
|  |  | ||||||
|         return sout, serr |  | ||||||
|  |  | ||||||
|     def get_ramdisk(self): |  | ||||||
|         for vol in ["/dev/shm", "/Volumes/cptd"]:  # nosec (singleton test) |  | ||||||
|             if os.path.exists(vol): |  | ||||||
|                 return vol |  | ||||||
|  |  | ||||||
|         if os.path.exists("/Volumes"): |  | ||||||
|             devname, _ = self.chkcmd("hdiutil", "attach", "-nomount", "ram://8192") |  | ||||||
|             devname = devname.strip() |  | ||||||
|             print("devname: [{}]".format(devname)) |  | ||||||
|             for _ in range(10): |  | ||||||
|                 try: |  | ||||||
|                     _, _ = self.chkcmd( |  | ||||||
|                         "diskutil", "eraseVolume", "HFS+", "cptd", devname |  | ||||||
|                     ) |  | ||||||
|                     return "/Volumes/cptd" |  | ||||||
|                 except Exception as ex: |  | ||||||
|                     print(repr(ex)) |  | ||||||
|                     time.sleep(0.25) |  | ||||||
|  |  | ||||||
|             raise Exception("ramdisk creation failed") |  | ||||||
|  |  | ||||||
|         ret = os.path.join(tempfile.gettempdir(), "copyparty-test") |  | ||||||
|         try: |  | ||||||
|             os.mkdir(ret) |  | ||||||
|         finally: |  | ||||||
|             return ret |  | ||||||
|  |  | ||||||
|     def log(self, src, msg, c=0): |     def log(self, src, msg, c=0): | ||||||
|         pass |         pass | ||||||
|  |  | ||||||
|     def test(self): |     def test(self): | ||||||
|         td = os.path.join(self.get_ramdisk(), "vfs") |         td = os.path.join(tu.get_ramdisk(), "vfs") | ||||||
|         try: |         try: | ||||||
|             shutil.rmtree(td) |             shutil.rmtree(td) | ||||||
|         except OSError: |         except OSError: | ||||||
| @@ -268,7 +227,7 @@ class TestVFS(unittest.TestCase): | |||||||
|         self.assertEqual(list(v1), list(v2)) |         self.assertEqual(list(v1), list(v2)) | ||||||
|  |  | ||||||
|         # config file parser |         # config file parser | ||||||
|         cfg_path = os.path.join(self.get_ramdisk(), "test.cfg") |         cfg_path = os.path.join(tu.get_ramdisk(), "test.cfg") | ||||||
|         with open(cfg_path, "wb") as f: |         with open(cfg_path, "wb") as f: | ||||||
|             f.write( |             f.write( | ||||||
|                 dedent( |                 dedent( | ||||||
|   | |||||||
							
								
								
									
										97
									
								
								tests/util.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										97
									
								
								tests/util.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,97 @@ | |||||||
|  | import os | ||||||
|  | import time | ||||||
|  | import jinja2 | ||||||
|  | import tempfile | ||||||
|  | import subprocess as sp | ||||||
|  |  | ||||||
|  | from copyparty.util import Unrecv | ||||||
|  |  | ||||||
|  |  | ||||||
|  | J2_ENV = jinja2.Environment(loader=jinja2.BaseLoader) | ||||||
|  | J2_FILES = J2_ENV.from_string("{{ files|join('\n') }}") | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def runcmd(*argv): | ||||||
|  |     p = sp.Popen(argv, stdout=sp.PIPE, stderr=sp.PIPE) | ||||||
|  |     stdout, stderr = p.communicate() | ||||||
|  |     stdout = stdout.decode("utf-8") | ||||||
|  |     stderr = stderr.decode("utf-8") | ||||||
|  |     return [p.returncode, stdout, stderr] | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def chkcmd(*argv): | ||||||
|  |     ok, sout, serr = runcmd(*argv) | ||||||
|  |     if ok != 0: | ||||||
|  |         raise Exception(serr) | ||||||
|  |  | ||||||
|  |     return sout, serr | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def get_ramdisk(): | ||||||
|  |     for vol in ["/dev/shm", "/Volumes/cptd"]:  # nosec (singleton test) | ||||||
|  |         if os.path.exists(vol): | ||||||
|  |             return vol | ||||||
|  |  | ||||||
|  |     if os.path.exists("/Volumes"): | ||||||
|  |         devname, _ = chkcmd("hdiutil", "attach", "-nomount", "ram://32768") | ||||||
|  |         devname = devname.strip() | ||||||
|  |         print("devname: [{}]".format(devname)) | ||||||
|  |         for _ in range(10): | ||||||
|  |             try: | ||||||
|  |                 _, _ = chkcmd("diskutil", "eraseVolume", "HFS+", "cptd", devname) | ||||||
|  |                 return "/Volumes/cptd" | ||||||
|  |             except Exception as ex: | ||||||
|  |                 print(repr(ex)) | ||||||
|  |                 time.sleep(0.25) | ||||||
|  |  | ||||||
|  |         raise Exception("ramdisk creation failed") | ||||||
|  |  | ||||||
|  |     ret = os.path.join(tempfile.gettempdir(), "copyparty-test") | ||||||
|  |     try: | ||||||
|  |         os.mkdir(ret) | ||||||
|  |     finally: | ||||||
|  |         return ret | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class NullBroker(object): | ||||||
|  |     def put(*args): | ||||||
|  |         pass | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class VSock(object): | ||||||
|  |     def __init__(self, buf): | ||||||
|  |         self._query = buf | ||||||
|  |         self._reply = b"" | ||||||
|  |         self.sendall = self.send | ||||||
|  |  | ||||||
|  |     def recv(self, sz): | ||||||
|  |         ret = self._query[:sz] | ||||||
|  |         self._query = self._query[sz:] | ||||||
|  |         return ret | ||||||
|  |  | ||||||
|  |     def send(self, buf): | ||||||
|  |         self._reply += buf | ||||||
|  |         return len(buf) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class VHttpSrv(object): | ||||||
|  |     def __init__(self): | ||||||
|  |         self.broker = NullBroker() | ||||||
|  |  | ||||||
|  |         aliases = ["splash", "browser", "browser2", "msg", "md", "mde"] | ||||||
|  |         self.j2 = {x: J2_FILES for x in aliases} | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class VHttpConn(object): | ||||||
|  |     def __init__(self, args, auth, log, buf): | ||||||
|  |         self.s = VSock(buf) | ||||||
|  |         self.sr = Unrecv(self.s) | ||||||
|  |         self.addr = ("127.0.0.1", "42069") | ||||||
|  |         self.args = args | ||||||
|  |         self.auth = auth | ||||||
|  |         self.log_func = log | ||||||
|  |         self.log_src = "a" | ||||||
|  |         self.hsrv = VHttpSrv() | ||||||
|  |         self.nbyte = 0 | ||||||
|  |         self.workload = 0 | ||||||
|  |         self.t0 = time.time() | ||||||
		Reference in New Issue
	
	Block a user