Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c15ecb6c8e | ||
|
|
ee96005026 | ||
|
|
5b55d05a20 | ||
|
|
2f09c62c4e | ||
|
|
1cc8b873d4 | ||
|
|
15d5859750 |
@@ -104,10 +104,8 @@ in the `scripts` folder:
|
||||
|
||||
roughly sorted by priority
|
||||
|
||||
* sortable browser columns
|
||||
* up2k handle filename too long
|
||||
* up2k fails on empty files? alert then stuck
|
||||
* unexpected filepath on dupe up2k
|
||||
* drop onto folders
|
||||
* look into android thumbnail cache file format
|
||||
* support pillow-simd
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
# coding: utf-8
|
||||
|
||||
VERSION = (0, 3, 0)
|
||||
VERSION = (0, 3, 1)
|
||||
CODENAME = "docuparty"
|
||||
BUILD_DT = (2020, 5, 6)
|
||||
BUILD_DT = (2020, 5, 7)
|
||||
|
||||
S_VERSION = ".".join(map(str, VERSION))
|
||||
S_BUILD_DT = "{0:04d}-{1:02d}-{2:02d}".format(*BUILD_DT)
|
||||
|
||||
@@ -4,7 +4,6 @@ from __future__ import print_function, unicode_literals
|
||||
|
||||
import traceback
|
||||
|
||||
from .__init__ import PY2
|
||||
from .util import Pebkac, Queue
|
||||
|
||||
|
||||
|
||||
@@ -420,9 +420,11 @@ class HttpCli(object):
|
||||
vfs, rem = self.conn.auth.vfs.get(self.vpath, self.uname, False, True)
|
||||
self._assert_safe_rem(rem)
|
||||
|
||||
sanitized = sanitize_fn(new_dir)
|
||||
|
||||
if not nullwrite:
|
||||
fdir = os.path.join(vfs.realpath, rem)
|
||||
fn = os.path.join(fdir, sanitize_fn(new_dir))
|
||||
fn = os.path.join(fdir, sanitized)
|
||||
|
||||
if not os.path.isdir(fsenc(fdir)):
|
||||
raise Pebkac(500, "parent folder does not exist")
|
||||
@@ -435,7 +437,7 @@ class HttpCli(object):
|
||||
except:
|
||||
raise Pebkac(500, "mkdir failed, check the logs")
|
||||
|
||||
vpath = "{}/{}".format(self.vpath, new_dir).lstrip("/")
|
||||
vpath = "{}/{}".format(self.vpath, sanitized).lstrip("/")
|
||||
html = self.conn.tpl_msg.render(
|
||||
h2='<a href="/{}">go to /{}</a>'.format(
|
||||
quotep(vpath), html_escape(vpath, quote=False)
|
||||
@@ -457,9 +459,11 @@ class HttpCli(object):
|
||||
if not new_file.endswith(".md"):
|
||||
new_file += ".md"
|
||||
|
||||
sanitized = sanitize_fn(new_file)
|
||||
|
||||
if not nullwrite:
|
||||
fdir = os.path.join(vfs.realpath, rem)
|
||||
fn = os.path.join(fdir, sanitize_fn(new_file))
|
||||
fn = os.path.join(fdir, sanitized)
|
||||
|
||||
if os.path.exists(fsenc(fn)):
|
||||
raise Pebkac(500, "that file exists already")
|
||||
@@ -467,7 +471,7 @@ class HttpCli(object):
|
||||
with open(fsenc(fn), "wb") as f:
|
||||
f.write(b"`GRUNNUR`\n")
|
||||
|
||||
vpath = "{}/{}".format(self.vpath, new_file).lstrip("/")
|
||||
vpath = "{}/{}".format(self.vpath, sanitized).lstrip("/")
|
||||
html = self.conn.tpl_msg.render(
|
||||
h2='<a href="/{}?edit">go to /{}?edit</a>'.format(
|
||||
quotep(vpath), html_escape(vpath, quote=False)
|
||||
@@ -600,10 +604,10 @@ class HttpCli(object):
|
||||
self.reply(response.encode("utf-8"))
|
||||
return True
|
||||
|
||||
fn = os.path.join(vfs.realpath, rem)
|
||||
fp = os.path.join(vfs.realpath, rem)
|
||||
srv_lastmod = -1
|
||||
try:
|
||||
st = os.stat(fsenc(fn))
|
||||
st = os.stat(fsenc(fp))
|
||||
srv_lastmod = st.st_mtime
|
||||
srv_lastmod3 = int(srv_lastmod * 1000)
|
||||
except OSError as ex:
|
||||
@@ -631,16 +635,22 @@ class HttpCli(object):
|
||||
return True
|
||||
|
||||
# TODO another hack re: pending permissions rework
|
||||
os.rename(fn, "{}.bak-{:.3f}.md".format(fn[:-3], srv_lastmod))
|
||||
mdir, mfile = os.path.split(fp)
|
||||
mfile2 = "{}.{:.3f}.md".format(mfile[:-3], srv_lastmod)
|
||||
try:
|
||||
os.mkdir(os.path.join(mdir, ".hist"))
|
||||
except:
|
||||
pass
|
||||
os.rename(fp, os.path.join(mdir, ".hist", mfile2))
|
||||
|
||||
p_field, _, p_data = next(self.parser.gen)
|
||||
if p_field != "body":
|
||||
raise Pebkac(400, "expected body, got {}".format(p_field))
|
||||
|
||||
with open(fn, "wb") as f:
|
||||
with open(fp, "wb") as f:
|
||||
sz, sha512, _ = hashcopy(self.conn, p_data, f)
|
||||
|
||||
new_lastmod = os.stat(fsenc(fn)).st_mtime
|
||||
new_lastmod = os.stat(fsenc(fp)).st_mtime
|
||||
new_lastmod3 = int(new_lastmod * 1000)
|
||||
sha512 = sha512[:56]
|
||||
|
||||
@@ -909,12 +919,30 @@ class HttpCli(object):
|
||||
fsroot, vfs_ls, vfs_virt = vn.ls(rem, self.uname)
|
||||
vfs_ls.extend(vfs_virt.keys())
|
||||
|
||||
# check for old versions of files,
|
||||
hist = {} # [num-backups, most-recent, hist-path]
|
||||
histdir = os.path.join(fsroot, ".hist")
|
||||
ptn = re.compile(r"(.*)\.([0-9]+\.[0-9]{3})(\.[^\.]+)$")
|
||||
try:
|
||||
for hfn in os.listdir(histdir):
|
||||
m = ptn.match(hfn)
|
||||
if not m:
|
||||
continue
|
||||
|
||||
fn = m.group(1) + m.group(3)
|
||||
n, ts, _ = hist.get(fn, [0, 0, ""])
|
||||
hist[fn] = [n + 1, max(ts, float(m.group(2))), hfn]
|
||||
except:
|
||||
pass
|
||||
|
||||
dirs = []
|
||||
files = []
|
||||
for fn in exclude_dotfiles(vfs_ls):
|
||||
base = ""
|
||||
href = fn
|
||||
if self.absolute_urls and vpath:
|
||||
href = "/" + vpath + "/" + fn
|
||||
base = "/" + vpath + "/"
|
||||
href = base + fn
|
||||
|
||||
if fn in vfs_virt:
|
||||
fspath = vfs_virt[fn].realpath
|
||||
@@ -931,6 +959,10 @@ class HttpCli(object):
|
||||
if is_dir:
|
||||
margin = "DIR"
|
||||
href += "/"
|
||||
elif fn in hist:
|
||||
margin = '<a href="{}.hist/{}">#{}</a>'.format(
|
||||
base, html_escape(hist[fn][2], quote=True), hist[fn][0]
|
||||
)
|
||||
else:
|
||||
margin = "-"
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ class TcpSrv(object):
|
||||
ip = "127.0.0.1"
|
||||
eps = {ip: "local only"}
|
||||
if self.args.i != ip:
|
||||
eps = self.detect_interfaces(self.args.i) or eps
|
||||
eps = self.detect_interfaces(self.args.i) or {self.args.i: "external"}
|
||||
|
||||
for ip, desc in sorted(eps.items(), key=lambda x: x[1]):
|
||||
self.log(
|
||||
|
||||
@@ -13,7 +13,7 @@ import threading
|
||||
from copy import deepcopy
|
||||
|
||||
from .__init__ import WINDOWS
|
||||
from .util import Pebkac, Queue, fsenc
|
||||
from .util import Pebkac, Queue, fsenc, sanitize_fn
|
||||
|
||||
|
||||
class Up2k(object):
|
||||
@@ -48,6 +48,7 @@ class Up2k(object):
|
||||
self.r_hash = re.compile("^[0-9a-zA-Z_-]{43}$")
|
||||
|
||||
def handle_json(self, cj):
|
||||
cj["name"] = sanitize_fn(cj["name"])
|
||||
wark = self._get_wark(cj)
|
||||
now = time.time()
|
||||
with self.mutex:
|
||||
|
||||
@@ -356,7 +356,30 @@ def undot(path):
|
||||
|
||||
|
||||
def sanitize_fn(fn):
|
||||
return fn.replace("\\", "/").split("/")[-1].strip()
|
||||
fn = fn.replace("\\", "/").split("/")[-1]
|
||||
|
||||
if WINDOWS:
|
||||
for bad, good in [
|
||||
["<", "<"],
|
||||
[">", ">"],
|
||||
[":", ":"],
|
||||
['"', """],
|
||||
["/", "/"],
|
||||
["\\", "\"],
|
||||
["|", "|"],
|
||||
["?", "?"],
|
||||
["*", "*"],
|
||||
]:
|
||||
fn = fn.replace(bad, good)
|
||||
|
||||
bad = ["con", "prn", "aux", "nul"]
|
||||
for n in range(1, 10):
|
||||
bad += "com{0} lpt{0}".format(n).split(" ")
|
||||
|
||||
if fn.lower() in bad:
|
||||
fn = "_" + fn
|
||||
|
||||
return fn.strip()
|
||||
|
||||
|
||||
def exclude_dotfiles(filepaths):
|
||||
|
||||
@@ -68,7 +68,7 @@ a {
|
||||
}
|
||||
#files thead th:last-child {
|
||||
background: #444;
|
||||
border-radius: .7em 0 0 0;
|
||||
border-radius: .7em .7em 0 0;
|
||||
}
|
||||
#files thead th:first-child {
|
||||
background: #222;
|
||||
|
||||
@@ -34,6 +34,7 @@ window.onerror = function (msg, url, lineNo, columnNo, error) {
|
||||
esc(String(error[find[a]])).replace(/\n/g, '<br />\n'));
|
||||
}
|
||||
document.body.style.fontSize = '0.8em';
|
||||
document.body.style.padding = '0 1em 1em 1em';
|
||||
hcroak(html.join('\n'));
|
||||
};
|
||||
|
||||
@@ -78,6 +79,39 @@ function ev(e) {
|
||||
}
|
||||
|
||||
|
||||
function sortTable(table, col) {
|
||||
var tb = table.tBodies[0], // use `<tbody>` to ignore `<thead>` and `<tfoot>` rows
|
||||
th = table.tHead.rows[0].cells,
|
||||
tr = Array.prototype.slice.call(tb.rows, 0),
|
||||
i, reverse = th[col].className == 'sort1' ? -1 : 1;
|
||||
for (var a = 0, thl = th.length; a < thl; a++)
|
||||
th[a].className = '';
|
||||
th[col].className = 'sort' + reverse;
|
||||
var stype = th[col].getAttribute('sort');
|
||||
tr = tr.sort(function (a, b) {
|
||||
var v1 = a.cells[col].textContent.trim();
|
||||
var v2 = b.cells[col].textContent.trim();
|
||||
if (stype == 'int') {
|
||||
v1 = parseInt(v1.replace(/,/g, ''));
|
||||
v2 = parseInt(v2.replace(/,/g, ''));
|
||||
return reverse * (v1 - v2);
|
||||
}
|
||||
return reverse * (v1.localeCompare(v2));
|
||||
});
|
||||
for (i = 0; i < tr.length; ++i) tb.appendChild(tr[i]);
|
||||
}
|
||||
function makeSortable(table) {
|
||||
var th = table.tHead, i;
|
||||
th && (th = th.rows[0]) && (th = th.cells);
|
||||
if (th) i = th.length;
|
||||
else return; // if no `<thead>` then do nothing
|
||||
while (--i >= 0) (function (i) {
|
||||
th[i].addEventListener('click', function () { sortTable(table, i) });
|
||||
}(i));
|
||||
}
|
||||
makeSortable(o('files'));
|
||||
|
||||
|
||||
// extract songs + add play column
|
||||
var mp = (function () {
|
||||
var tracks = [];
|
||||
|
||||
@@ -239,8 +239,8 @@ blink {
|
||||
height: 1.05em;
|
||||
margin: -.2em .3em -.2em -.4em;
|
||||
display: inline-block;
|
||||
border: 1px solid rgba(0,0,0,0.3);
|
||||
border-width: .05em .05em 0 0;
|
||||
border: 1px solid rgba(0,0,0,0.2);
|
||||
border-width: .2em .2em 0 0;
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
#mn a:hover {
|
||||
|
||||
@@ -22,7 +22,9 @@ var dom_md = document.getElementById('mt');
|
||||
if (a > 0)
|
||||
loc.push(n[a]);
|
||||
|
||||
nav.push('<a href="/' + loc.join('/') + '">' + n[a] + '</a>');
|
||||
var dec = decodeURIComponent(n[a]).replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
||||
|
||||
nav.push('<a href="/' + loc.join('/') + '">' + dec + '</a>');
|
||||
}
|
||||
dom_nav.innerHTML = nav.join('');
|
||||
})();
|
||||
|
||||
@@ -44,8 +44,8 @@ html, body {
|
||||
height: 1.05em;
|
||||
margin: -.2em .3em -.2em -.4em;
|
||||
display: inline-block;
|
||||
border: 1px solid rgba(0,0,0,0.3);
|
||||
border-width: .05em .05em 0 0;
|
||||
border: 1px solid rgba(0,0,0,0.2);
|
||||
border-width: .2em .2em 0 0;
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
#mn a:hover {
|
||||
|
||||
@@ -13,7 +13,9 @@ var dom_md = document.getElementById('mt');
|
||||
if (a > 0)
|
||||
loc.push(n[a]);
|
||||
|
||||
nav.push('<a href="/' + loc.join('/') + '">' + n[a] + '</a>');
|
||||
var dec = decodeURIComponent(n[a]).replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
||||
|
||||
nav.push('<a href="/' + loc.join('/') + '">' + dec + '</a>');
|
||||
}
|
||||
dom_nav.innerHTML = nav.join('');
|
||||
})();
|
||||
|
||||
@@ -34,6 +34,7 @@ window.onerror = function (msg, url, lineNo, columnNo, error) {
|
||||
esc(String(error[find[a]])).replace(/\n/g, '<br />\n'));
|
||||
}
|
||||
document.body.style.fontSize = '0.8em';
|
||||
document.body.style.padding = '0 1em 1em 1em';
|
||||
hcroak(html.join('\n'));
|
||||
};
|
||||
|
||||
@@ -211,7 +212,8 @@ function up2k_init(have_crypto) {
|
||||
// handle user intent to use the basic uploader instead
|
||||
o('u2nope').onclick = function (e) {
|
||||
e.preventDefault();
|
||||
un2k();
|
||||
setmsg('');
|
||||
goto('bup');
|
||||
};
|
||||
|
||||
if (!String.prototype.format) {
|
||||
|
||||
@@ -66,5 +66,5 @@
|
||||
</table>
|
||||
|
||||
<p id="u2foot"></p>
|
||||
<p>( if you don't need lastmod timestamps, resumable uploads or progress bars just use the <a href="#" id="u2nope" onclick="javascript:goto('bup');">basic uploader</a>)</p>
|
||||
<p>( if you don't need lastmod timestamps, resumable uploads or progress bars just use the <a href="#" id="u2nope">basic uploader</a>)</p>
|
||||
</div>
|
||||
|
||||
@@ -101,7 +101,7 @@ grep -lE '\.full\.(js|css)' copyparty/web/* |
|
||||
while IFS= read -r x; do sed -ri 's/\.full\.(js|css)/.\1/g' "$x"; done
|
||||
|
||||
[ $no_ogv ] &&
|
||||
rm -rf copyparty/web/deps/{dynamicaudio,ogv}* copyparty/web/browser.js
|
||||
rm -rf copyparty/web/deps/{dynamicaudio,ogv}*
|
||||
|
||||
echo creating tar
|
||||
args=(--owner=1000 --group=1000)
|
||||
|
||||
Reference in New Issue
Block a user