Compare commits

..

12 Commits

Author SHA1 Message Date
ed
7e8daf650e v0.10.14 2021-04-21 22:04:21 +02:00
ed
0cf737b4ce 404 rather than redirect home if 404 or 403 2021-04-21 21:51:27 +02:00
ed
74635e0113 phew 2021-04-21 21:42:37 +02:00
ed
e5c4f49901 ok ok 2021-04-21 21:26:55 +02:00
ed
e4654ee7f1 uhh 2021-04-21 21:13:16 +02:00
ed
e5d05c05ed up2k ui tweaks 2021-04-21 20:50:10 +02:00
ed
73c4f99687 add markdown streaming 2021-04-21 20:28:50 +02:00
ed
28c12ef3bf cleanup 2021-04-21 18:48:23 +02:00
ed
eed82dbb54 remove dead code 2021-04-21 18:44:47 +02:00
ed
2c4b4ab928 up2k-cli: cond. readahead 2021-04-21 18:39:55 +02:00
ed
505a8fc6f6 up2k: sparse alloc on windows 2021-04-21 18:32:21 +02:00
ed
e4801d9b06 support msys2-python 2021-04-21 18:28:44 +02:00
12 changed files with 245 additions and 214 deletions

View File

@@ -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

View File

@@ -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"

View File

@@ -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")

View File

@@ -1,8 +1,8 @@
# coding: utf-8 # coding: utf-8
VERSION = (0, 10, 13) VERSION = (0, 10, 14)
CODENAME = "zip it" CODENAME = "zip it"
BUILD_DT = (2021, 4, 20) BUILD_DT = (2021, 4, 21)
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)

View File

@@ -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
@@ -261,12 +261,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:
self.log("inaccessible: [{}]".format(self.vpath)) if 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:
@@ -626,7 +628,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 +682,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 +1183,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"&amp;") 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 +1200,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 +1308,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
@@ -1394,7 +1402,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 = "-"
@@ -1536,7 +1544,7 @@ class HttpCli(object):
have_b_u=(self.writable and self.uparam.get("b") == "u"), have_b_u=(self.writable and self.uparam.get("b") == "u"),
url_suf=url_suf, url_suf=url_suf,
logues=logues, logues=logues,
title=html_escape(self.vpath), title=html_escape(self.vpath, crlf=True),
srv_info=srv_info, srv_info=srv_info,
) )
self.reply(html.encode("utf-8", "replace")) self.reply(html.encode("utf-8", "replace"))

View File

@@ -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)
@@ -668,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
@@ -698,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):
@@ -1110,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(
@@ -1212,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")
@@ -1223,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

View File

@@ -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("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;")
s.replace("&", "&amp;")
.replace("<", "&lt;")
.replace(">", "&gt;")
.replace("\r", "&#13;")
.replace("\n", "&#10;")
)
if quote: if quote:
s = s.replace('"', "&quot;").replace("'", "&#x27;") s = s.replace('"', "&quot;").replace("'", "&#x27;")
if crlf:
s = s.replace("\r", "&#13;").replace("\n", "&#10;")
return s
def html_bescape(s, quote=False, crlf=False):
"""html.escape but bytestrings"""
s = s.replace(b"&", b"&amp;").replace(b"<", b"&lt;").replace(b">", b"&gt;")
if quote:
s = s.replace(b'"', b"&quot;").replace(b"'", b"&#x27;")
if crlf:
s = s.replace(b"\r", b"&#13;").replace(b"\n", b"&#10;")
return s return s

View File

@@ -219,25 +219,28 @@ function U2pvis(act, btns) {
}; };
this.hashed = function (fobj) { this.hashed = function (fobj) {
var fo = this.tab[fobj.n]; var fo = this.tab[fobj.n],
var nb = fo.bt * (++fo.nh / fo.cb.length); nb = fo.bt * (++fo.nh / fo.cb.length),
var p = this.perc(nb, 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 + '%, #09d ' + o2 + '%, #333 ' + o3 + '%, #333 99%, #777)';
}; };
this.prog = function (fobj, nchunk, cbd) { this.prog = function (fobj, nchunk, cbd) {
var fo = this.tab[fobj.n]; var fo = this.tab[fobj.n],
var delta = cbd - fo.cb[nchunk]; delta = cbd - fo.cb[nchunk];
fo.cb[nchunk] = cbd; fo.cb[nchunk] = cbd;
fo.bd += delta; fo.bd += delta;
@@ -249,10 +252,11 @@ 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 + '%, #333 99%, #777)';
}; };
@@ -287,15 +291,6 @@ 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 = document.querySelector('#u2tab>tbody>tr:first-child');
if (!first) if (!first)
@@ -304,7 +299,6 @@ function U2pvis(act, btns) {
var last = document.querySelector('#u2tab>tbody>tr:last-child'); var last = document.querySelector('#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++));
@@ -315,8 +309,6 @@ 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) {
@@ -343,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)) {
@@ -428,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';
@@ -486,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": {
@@ -542,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
@@ -557,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) {
@@ -644,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);
@@ -663,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 = new Date().getTime(),
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,
@@ -701,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 = document.createElement('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);
@@ -748,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 defer() {
running = false;
clearTimeout(tto);
tto = setTimeout(taskerd, 100);
}
function taskerd() { function taskerd() {
if (mutex) if (running)
return; return;
mutex = true; clearTimeout(tto);
running = true;
while (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;
}
var is_busy = 0 != var is_busy = 0 !=
st.todo.hash.length + st.todo.hash.length +
st.todo.handshake.length + st.todo.handshake.length +
@@ -779,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 = new Date().getTime();
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();
@@ -835,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();
@@ -853,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);
@@ -906,25 +893,11 @@ function up2k_init(have_crypto) {
} }
} }
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() {
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 bpend = 0, var bpend = 0,
nchunk = 0, nchunk = 0,
@@ -936,13 +909,14 @@ function up2k_init(have_crypto) {
pvis.move(t.n, 'bz'); pvis.move(t.n, 'bz');
var segm_next = function () { var segm_next = function () {
if (nchunk >= nchunks || (bpend > chunksize && bpend >= 32 * 1024 * 1024)) if (nchunk >= nchunks || (bpend > chunksize && bpend >= min_filebuf))
return false; return false;
var reader = new FileReader(), var reader = new FileReader(),
nch = nchunk++, nch = nchunk++,
car = nch * chunksize, car = nch * chunksize,
cdr = car + chunksize; cdr = car + chunksize,
t0 = new Date().getTime();
if (cdr >= t.size) if (cdr >= t.size)
cdr = t.size; cdr = t.size;
@@ -950,9 +924,19 @@ function up2k_init(have_crypto) {
bpend += cdr - car; bpend += cdr - car;
reader.onload = function (e) { reader.onload = function (e) {
if (!min_filebuf && nch == 1) {
min_filebuf = 1;
var td = (new Date().getTime()) - 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); hash_calc(nch, e.target.result);
}; };
reader.onerror = segm_err; 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));
@@ -963,8 +947,9 @@ function up2k_init(have_crypto) {
while (segm_next()); while (segm_next());
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(/=$/, '');
hashtab[nch] = b64str; hashtab[nch] = b64str;
t.hash.push(nch); t.hash.push(nch);
pvis.hashed(t); pvis.hashed(t);
@@ -1000,10 +985,7 @@ function up2k_init(have_crypto) {
} }
}; };
var segm_err = function () { t.t1 = new Date().getTime();
alert('y o u b r o k e i t\nerror: ' + reader.error);
};
segm_next(); segm_next();
} }
@@ -1022,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';
@@ -1056,10 +1039,11 @@ 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); var chunksize = get_chunksize(t.size),
var cdr_idx = Math.ceil(t.size / chunksize) - 1; cdr_idx = Math.ceil(t.size / chunksize) - 1,
var cdr_sz = (t.size % chunksize) || chunksize; cdr_sz = (t.size % chunksize) || chunksize,
var cbd = []; cbd = [];
for (var a = 0; a <= cdr_idx; a++) { for (var a = 0; a <= cdr_idx; a++) {
cbd.push(a == cdr_idx ? cdr_sz : chunksize); cbd.push(a == cdr_idx ? cdr_sz : chunksize);
} }
@@ -1080,8 +1064,9 @@ function up2k_init(have_crypto) {
pvis.setat(t.n, cbd); pvis.setat(t.n, cbd);
pvis.prog(t, 0, cbd[0]); pvis.prog(t, 0, cbd[0]);
var done = true; var done = true,
var msg = '&#x1f3b7;&#x1f41b;'; msg = '&#x1f3b7;&#x1f41b;';
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({
@@ -1098,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;
@@ -1168,16 +1155,18 @@ 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) if (!t.t3)
t.t3 = new Date().getTime(); t.t3 = new Date().getTime();
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;
@@ -1242,10 +1231,10 @@ 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) {
@@ -1309,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;
@@ -1331,11 +1320,11 @@ function up2k_init(have_crypto) {
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) { }

View File

@@ -90,8 +90,10 @@
background: #222; background: #222;
} }
#u2cards { #u2cards {
margin: 2.5em auto -2.5em auto; padding: 1em 0 .3em 0;
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,13 @@
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 { #u2conf .txtbox.err {
background: #922; background: #922;
@@ -151,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;
@@ -208,12 +215,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;
@@ -256,7 +264,10 @@ html.light #u2cards a {
background: linear-gradient(to bottom, #eee, #fff); background: linear-gradient(to bottom, #eee, #fff);
} }
html.light #u2cards a.act { html.light #u2cards a.act {
color: #037;
background: inherit; background: inherit;
box-shadow: 0 -.17em .67em #0ad;
border-color: #09c #05a #eee #05a;
} }
html.light #u2conf .txtbox { html.light #u2conf .txtbox {
background: #fff; background: #fff;

View File

@@ -59,9 +59,9 @@
</tr> </tr>
<tr> <tr>
<td> <td>
<a href="#" id="nthread_sub">&ndash;</a> <a href="#" id="nthread_sub">&ndash;</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>

View File

@@ -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

View File

@@ -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