u2c: remove all deps to become 3x faster on small files

reduces performance on python 2.7, but that is ok

also fixes `unknown encoding: idna` due to cpython race
This commit is contained in:
ed 2024-09-22 18:07:36 +00:00
parent 66b260cea9
commit 9daeed923f
7 changed files with 258 additions and 197 deletions

View file

@ -1,34 +1,36 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
from __future__ import print_function, unicode_literals from __future__ import print_function, unicode_literals
S_VERSION = "1.24" S_VERSION = "2.0"
S_BUILD_DT = "2024-09-05" S_BUILD_DT = "2024-09-22"
""" """
u2c.py: upload to copyparty u2c.py: upload to copyparty
2021, ed <irc.rizon.net>, MIT-Licensed 2021, ed <irc.rizon.net>, MIT-Licensed
https://github.com/9001/copyparty/blob/hovudstraum/bin/u2c.py https://github.com/9001/copyparty/blob/hovudstraum/bin/u2c.py
- dependencies: requests - dependencies: no
- supports python 2.6, 2.7, and 3.3 through 3.12 - supports python 2.6, 2.7, and 3.3 through 3.12
(for higher performance on 2.6 and 2.7, use u2c v1.x)
- if something breaks just try again and it'll autoresume - if something breaks just try again and it'll autoresume
""" """
import re
import os
import sys
import stat
import math
import time
import json
import atexit import atexit
import base64
import binascii
import datetime
import hashlib
import json
import math
import os
import platform
import re
import signal import signal
import socket import socket
import base64 import stat
import hashlib import sys
import platform
import threading import threading
import datetime import time
EXE = bool(getattr(sys, "frozen", False)) EXE = bool(getattr(sys, "frozen", False))
@ -39,32 +41,10 @@ except:
print(m) print(m)
raise raise
try:
import requests
req_ses = requests.Session()
except ImportError as ex:
if "-" in sys.argv or "-h" in sys.argv:
m = ""
elif EXE:
raise
elif sys.version_info > (2, 7):
m = "\nERROR: need 'requests'{0}; please run this command:\n {1} -m pip install --user requests\n"
else:
m = "requests/2.18.4 urllib3/1.23 chardet/3.0.4 certifi/2020.4.5.1 idna/2.7"
m = [" https://pypi.org/project/" + x + "/#files" for x in m.split()]
m = "\n ERROR: need these{0}:\n" + "\n".join(m) + "\n"
m += "\n for f in *.whl; do unzip $f; done; rm -r *.dist-info\n"
if m:
t = " when not running with '-h' or url '-'"
print(m.format(t, sys.executable), "\nspecifically,", ex)
sys.exit(1)
# from copyparty/__init__.py
PY2 = sys.version_info < (3,) PY2 = sys.version_info < (3,)
if PY2: if PY2:
import httplib as http_client
from Queue import Queue from Queue import Queue
from urllib import quote, unquote from urllib import quote, unquote
from urlparse import urlsplit, urlunsplit from urlparse import urlsplit, urlunsplit
@ -72,11 +52,13 @@ if PY2:
sys.dont_write_bytecode = True sys.dont_write_bytecode = True
bytes = str bytes = str
else: else:
from queue import Queue
from urllib.parse import unquote_to_bytes as unquote
from urllib.parse import quote_from_bytes as quote from urllib.parse import quote_from_bytes as quote
from urllib.parse import unquote_to_bytes as unquote
from urllib.parse import urlsplit, urlunsplit from urllib.parse import urlsplit, urlunsplit
import http.client as http_client
from queue import Queue
unicode = str unicode = str
VT100 = platform.system() != "Windows" VT100 = platform.system() != "Windows"
@ -100,6 +82,22 @@ except:
UTC = _UTC() UTC = _UTC()
try:
_b64etl = bytes.maketrans(b"+/", b"-_")
def ub64enc(bs):
x = binascii.b2a_base64(bs, newline=False)
return x.translate(_b64etl)
ub64enc(b"a")
except:
ub64enc = base64.urlsafe_b64encode
class BadAuth(Exception):
pass
class Daemon(threading.Thread): class Daemon(threading.Thread):
def __init__(self, target, name=None, a=None): def __init__(self, target, name=None, a=None):
threading.Thread.__init__(self, name=name) threading.Thread.__init__(self, name=name)
@ -117,6 +115,108 @@ class Daemon(threading.Thread):
self.fun(*self.a) self.fun(*self.a)
class HSQueue(Queue):
def _init(self, maxsize):
from collections import deque
self.q = deque()
def _qsize(self):
return len(self.q)
def _put(self, item):
if item and item.nhs:
self.q.appendleft(item)
else:
self.q.append(item)
def _get(self):
return self.q.popleft()
class HCli(object):
def __init__(self, ar):
self.ar = ar
url = urlsplit(ar.url)
tls = url.scheme.lower() == "https"
try:
addr, port = url.netloc.split(":")
except:
addr = url.netloc
port = 443 if tls else 80
self.addr = addr
self.port = int(port)
self.tls = tls
self.verify = ar.te or not ar.td
self.conns = []
if tls:
import ssl
if not self.verify:
self.ctx = ssl._create_unverified_context()
elif self.verify is True:
self.ctx = None
else:
self.ctx = ssl.SSLContext(ssl.PROTOCOL_TLS)
self.ctx.load_verify_locations(self.verify)
self.base_hdrs = {
"Accept": "*/*",
"Connection": "keep-alive",
"Host": url.netloc,
"Origin": self.ar.burl,
"User-Agent": "u2c/%s" % (S_VERSION,),
}
def _connect(self):
sbs = "blocksize"
args = {sbs: 1048576}
if not self.tls:
C = http_client.HTTPConnection
else:
C = http_client.HTTPSConnection
if self.ctx:
args = {"context": self.ctx}
for _ in range(2):
try:
return C(self.addr, self.port, timeout=999, **args)
except:
if sbs not in args:
raise
del args[sbs]
def req(self, meth, vpath, hdrs, body=None, ctype=None):
hdrs.update(self.base_hdrs)
if self.ar.a:
hdrs["PW"] = self.ar.a
if ctype:
hdrs["Content-Type"] = ctype
if meth == "POST" and CLEN not in hdrs:
hdrs[CLEN] = (
0 if not body else body.len if hasattr(body, "len") else len(body)
)
c = self.conns.pop() if self.conns else self._connect()
try:
c.request(meth, vpath, body, hdrs)
rsp = c.getresponse()
data = rsp.read()
self.conns.append(c)
return rsp.status, data.decode("utf-8")
except:
c.close()
raise
MJ = "application/json"
MO = "application/octet-stream"
CLEN = "Content-Length"
web = None # type: HCli
class File(object): class File(object):
"""an up2k upload task; represents a single file""" """an up2k upload task; represents a single file"""
@ -149,9 +249,6 @@ class File(object):
self.up_c = 0 # type: int self.up_c = 0 # type: int
self.cd = 0 # type: int self.cd = 0 # type: int
# t = "size({}) lmod({}) top({}) rel({}) abs({}) name({})\n"
# eprint(t.format(self.size, self.lmod, self.top, self.rel, self.abs, self.name))
class FileSlice(object): class FileSlice(object):
"""file-like object providing a fixed window into a file""" """file-like object providing a fixed window into a file"""
@ -284,8 +381,7 @@ class MTHash(object):
chunk_rem -= len(buf) chunk_rem -= len(buf)
ofs += len(buf) ofs += len(buf)
digest = hashobj.digest()[:33] digest = ub64enc(hashobj.digest()[:33]).decode("utf-8")
digest = base64.urlsafe_b64encode(digest).decode("utf-8")
return nch, digest, ofs0, chunk_sz return nch, digest, ofs0, chunk_sz
@ -329,7 +425,9 @@ def termsize():
def ioctl_GWINSZ(fd): def ioctl_GWINSZ(fd):
try: try:
import fcntl, termios, struct import fcntl
import struct
import termios
r = struct.unpack(b"hh", fcntl.ioctl(fd, termios.TIOCGWINSZ, b"AAAA")) r = struct.unpack(b"hh", fcntl.ioctl(fd, termios.TIOCGWINSZ, b"AAAA"))
return r[::-1] return r[::-1]
@ -387,8 +485,8 @@ class CTermsize(object):
eprint("\033[s\033[r\033[u") eprint("\033[s\033[r\033[u")
else: else:
self.g = 1 + self.h - margin self.g = 1 + self.h - margin
t = "{0}\033[{1}A".format("\n" * margin, margin) t = "%s\033[%dA" % ("\n" * margin, margin)
eprint("{0}\033[s\033[1;{1}r\033[u".format(t, self.g - 1)) eprint("%s\033[s\033[1;%dr\033[u" % (t, self.g - 1))
ss = CTermsize() ss = CTermsize()
@ -405,14 +503,14 @@ def undns(url):
except KeyboardInterrupt: except KeyboardInterrupt:
raise raise
except: except:
t = "\n\033[31mfailed to resolve upload destination host;\033[0m\ngai={0}\n" t = "\n\033[31mfailed to resolve upload destination host;\033[0m\ngai=%r\n"
eprint(t.format(repr(gai))) eprint(t % (gai,))
raise raise
if usp.port: if usp.port:
hn = "{0}:{1}".format(hn, usp.port) hn = "%s:%s" % (hn, usp.port)
if usp.username or usp.password: if usp.username or usp.password:
hn = "{0}:{1}@{2}".format(usp.username, usp.password, hn) hn = "%s:%s@%s" % (usp.username, usp.password, hn)
usp = usp._replace(netloc=hn) usp = usp._replace(netloc=hn)
url = urlunsplit(usp) url = urlunsplit(usp)
@ -577,8 +675,7 @@ def get_hashlist(file, pcb, mth):
hashobj.update(buf) hashobj.update(buf)
chunk_rem -= len(buf) chunk_rem -= len(buf)
digest = hashobj.digest()[:33] digest = ub64enc(hashobj.digest()[:33]).decode("utf-8")
digest = base64.urlsafe_b64encode(digest).decode("utf-8")
ret.append([digest, file_ofs, chunk_sz]) ret.append([digest, file_ofs, chunk_sz])
file_ofs += chunk_sz file_ofs += chunk_sz
@ -603,9 +700,6 @@ def handshake(ar, file, search):
otherwise, a list of chunks to upload otherwise, a list of chunks to upload
""" """
url = ar.url
pw = ar.a
req = { req = {
"hash": [x[0] for x in file.cids], "hash": [x[0] for x in file.cids],
"name": file.name, "name": file.name,
@ -620,28 +714,26 @@ def handshake(ar, file, search):
if ar.ow: if ar.ow:
req["replace"] = True req["replace"] = True
headers = {"Content-Type": "text/plain"} # <=1.5.1 compat
if pw:
headers["Cookie"] = "=".join(["cppwd", pw])
file.recheck = False file.recheck = False
if file.url: if file.url:
url = file.url url = file.url
elif b"/" in file.rel: else:
url += quotep(file.rel.rsplit(b"/", 1)[0]).decode("utf-8", "replace") if b"/" in file.rel:
url = quotep(file.rel.rsplit(b"/", 1)[0]).decode("utf-8", "replace")
else:
url = ""
url = ar.vtop + url
while True: while True:
sc = 600 sc = 600
txt = "" txt = ""
try: try:
zs = json.dumps(req, separators=(",\n", ": ")) zs = json.dumps(req, separators=(",\n", ": "))
r = req_ses.post(url, headers=headers, data=zs) sc, txt = web.req("POST", url, {}, zs.encode("utf-8"), MJ)
sc = r.status_code
txt = r.text
if sc < 400: if sc < 400:
break break
raise Exception("http {0}: {1}".format(sc, txt)) raise Exception("http %d: %s" % (sc, txt))
except Exception as ex: except Exception as ex:
em = str(ex).split("SSLError(")[-1].split("\nURL: ")[0].strip() em = str(ex).split("SSLError(")[-1].split("\nURL: ")[0].strip()
@ -655,35 +747,30 @@ def handshake(ar, file, search):
return [], False return [], False
elif sc == 409 or "<pre>upload rejected, file already exists" in txt: elif sc == 409 or "<pre>upload rejected, file already exists" in txt:
return [], False return [], False
elif "<pre>you don't have " in txt: elif sc == 403:
raise print("\nERROR: login required, or wrong password:\n%s" % (txt,))
raise BadAuth()
eprint("handshake failed, retrying: {0}\n {1}\n\n".format(file.name, em)) eprint("handshake failed, retrying: %s\n %s\n\n" % (file.name, em))
time.sleep(ar.cd) time.sleep(ar.cd)
try: try:
r = r.json() r = json.loads(txt)
except: except:
raise Exception(r.text) raise Exception(txt)
if search: if search:
return r["hits"], False return r["hits"], False
try: file.url = r["purl"]
pre, url = url.split("://")
pre += "://"
except:
pre = ""
file.url = pre + url.split("/")[0] + r["purl"]
file.name = r["name"] file.name = r["name"]
file.wark = r["wark"] file.wark = r["wark"]
return r["hash"], r["sprs"] return r["hash"], r["sprs"]
def upload(fsl, pw, stats): def upload(fsl, stats):
# type: (FileSlice, str, str) -> None # type: (FileSlice, str) -> None
"""upload a range of file data, defined by one or more `cid` (chunk-hash)""" """upload a range of file data, defined by one or more `cid` (chunk-hash)"""
ctxt = fsl.cids[0] ctxt = fsl.cids[0]
@ -696,20 +783,15 @@ def upload(fsl, pw, stats):
headers = { headers = {
"X-Up2k-Hash": ctxt, "X-Up2k-Hash": ctxt,
"X-Up2k-Wark": fsl.file.wark, "X-Up2k-Wark": fsl.file.wark,
"Content-Type": "application/octet-stream",
} }
if stats: if stats:
headers["X-Up2k-Stat"] = stats headers["X-Up2k-Stat"] = stats
if pw:
headers["Cookie"] = "=".join(["cppwd", pw])
try: try:
r = req_ses.post(fsl.file.url, headers=headers, data=fsl) sc, txt = web.req("POST", fsl.file.url, headers, fsl, MO)
if r.status_code == 400: if sc == 400:
txt = r.text
if ( if (
"already being written" in txt "already being written" in txt
or "already got that" in txt or "already got that" in txt
@ -717,10 +799,8 @@ def upload(fsl, pw, stats):
): ):
fsl.file.nojoin = 1 fsl.file.nojoin = 1
if not r: if sc >= 400:
raise Exception(repr(r)) raise Exception("http %s: %s" % (sc, txt))
_ = r.content
finally: finally:
fsl.f.close() fsl.f.close()
@ -733,7 +813,7 @@ class Ctl(object):
def _scan(self): def _scan(self):
ar = self.ar ar = self.ar
eprint("\nscanning {0} locations\n".format(len(ar.files))) eprint("\nscanning %d locations\n" % (len(ar.files),))
nfiles = 0 nfiles = 0
nbytes = 0 nbytes = 0
err = [] err = []
@ -745,14 +825,14 @@ class Ctl(object):
nbytes += inf.st_size nbytes += inf.st_size
if err: if err:
eprint("\n# failed to access {0} paths:\n".format(len(err))) eprint("\n# failed to access %d paths:\n" % (len(err),))
for ap, msg in err: for ap, msg in err:
if ar.v: if ar.v:
eprint("{0}\n `-{1}\n\n".format(ap.decode("utf-8", "replace"), msg)) eprint("%s\n `-%s\n\n" % (ap.decode("utf-8", "replace"), msg))
else: else:
eprint(ap.decode("utf-8", "replace") + "\n") eprint(ap.decode("utf-8", "replace") + "\n")
eprint("^ failed to access those {0} paths ^\n\n".format(len(err))) eprint("^ failed to access those %d paths ^\n\n" % (len(err),))
if not ar.v: if not ar.v:
eprint("hint: set -v for detailed error messages\n") eprint("hint: set -v for detailed error messages\n")
@ -761,11 +841,12 @@ class Ctl(object):
eprint("hint: aborting because --ok is not set\n") eprint("hint: aborting because --ok is not set\n")
return return
eprint("found {0} files, {1}\n\n".format(nfiles, humansize(nbytes))) eprint("found %d files, %s\n\n" % (nfiles, humansize(nbytes)))
return nfiles, nbytes return nfiles, nbytes
def __init__(self, ar, stats=None): def __init__(self, ar, stats=None):
self.ok = False self.ok = False
self.panik = 0
self.errs = 0 self.errs = 0
self.ar = ar self.ar = ar
self.stats = stats or self._scan() self.stats = stats or self._scan()
@ -773,13 +854,6 @@ class Ctl(object):
return return
self.nfiles, self.nbytes = self.stats self.nfiles, self.nbytes = self.stats
if ar.td:
requests.packages.urllib3.disable_warnings()
req_ses.verify = False
if ar.te:
req_ses.verify = ar.te
self.filegen = walkdirs([], ar.files, ar.x) self.filegen = walkdirs([], ar.files, ar.x)
self.recheck = [] # type: list[File] self.recheck = [] # type: list[File]
@ -808,7 +882,7 @@ class Ctl(object):
self.exit_cond = threading.Condition() self.exit_cond = threading.Condition()
self.uploader_alive = ar.j self.uploader_alive = ar.j
self.handshaker_alive = ar.j self.handshaker_alive = ar.j
self.q_handshake = Queue() # type: Queue[File] self.q_handshake = HSQueue() # type: Queue[File]
self.q_upload = Queue() # type: Queue[FileSlice] self.q_upload = Queue() # type: Queue[FileSlice]
self.st_hash = [None, "(idle, starting...)"] # type: tuple[File, int] self.st_hash = [None, "(idle, starting...)"] # type: tuple[File, int]
@ -823,24 +897,29 @@ class Ctl(object):
def _safe(self): def _safe(self):
"""minimal basic slow boring fallback codepath""" """minimal basic slow boring fallback codepath"""
search = self.ar.s search = self.ar.s
for nf, (top, rel, inf) in enumerate(self.filegen): nf = 0
for top, rel, inf in self.filegen:
if stat.S_ISDIR(inf.st_mode) or not rel: if stat.S_ISDIR(inf.st_mode) or not rel:
continue continue
nf += 1
file = File(top, rel, inf.st_size, inf.st_mtime) file = File(top, rel, inf.st_size, inf.st_mtime)
upath = file.abs.decode("utf-8", "replace") upath = file.abs.decode("utf-8", "replace")
print("{0} {1}\n hash...".format(self.nfiles - nf, upath)) print("%d %s\n hash..." % (self.nfiles - nf, upath))
get_hashlist(file, None, None) get_hashlist(file, None, None)
burl = self.ar.url[:12] + self.ar.url[8:].split("/")[0] + "/"
while True: while True:
print(" hs...") print(" hs...")
hs, _ = handshake(self.ar, file, search) try:
hs, _ = handshake(self.ar, file, search)
except BadAuth:
sys.exit(1)
if search: if search:
if hs: if hs:
for hit in hs: for hit in hs:
print(" found: {0}{1}".format(burl, hit["rp"])) print(" found: %s/%s" % (self.ar.burl, hit["rp"]))
else: else:
print(" NOT found") print(" NOT found")
break break
@ -849,13 +928,13 @@ class Ctl(object):
if not hs: if not hs:
break break
print("{0} {1}".format(self.nfiles - nf, upath)) print("%d %s" % (self.nfiles - nf, upath))
ncs = len(hs) ncs = len(hs)
for nc, cid in enumerate(hs): for nc, cid in enumerate(hs):
print(" {0} up {1}".format(ncs - nc, cid)) print(" %d up %s" % (ncs - nc, cid))
stats = "{0}/0/0/{1}".format(nf, self.nfiles - nf) stats = "%d/0/0/%d" % (nf, self.nfiles - nf)
fslice = FileSlice(file, [cid]) fslice = FileSlice(file, [cid])
upload(fslice, self.ar.a, stats) upload(fslice, stats)
print(" ok!") print(" ok!")
if file.recheck: if file.recheck:
@ -866,7 +945,7 @@ class Ctl(object):
eprint("finalizing %d duplicate files\n" % (len(self.recheck),)) eprint("finalizing %d duplicate files\n" % (len(self.recheck),))
for file in self.recheck: for file in self.recheck:
handshake(self.ar, file, search) handshake(self.ar, file, False)
def _fancy(self): def _fancy(self):
if VT100 and not self.ar.ns: if VT100 and not self.ar.ns:
@ -881,6 +960,8 @@ class Ctl(object):
while True: while True:
with self.exit_cond: with self.exit_cond:
self.exit_cond.wait(0.07) self.exit_cond.wait(0.07)
if self.panik:
sys.exit(1)
with self.mutex: with self.mutex:
if not self.handshaker_alive and not self.uploader_alive: if not self.handshaker_alive and not self.uploader_alive:
break break
@ -889,15 +970,15 @@ class Ctl(object):
if VT100 and not self.ar.ns: if VT100 and not self.ar.ns:
maxlen = ss.w - len(str(self.nfiles)) - 14 maxlen = ss.w - len(str(self.nfiles)) - 14
txt = "\033[s\033[{0}H".format(ss.g) txt = "\033[s\033[%dH" % (ss.g,)
for y, k, st, f in [ for y, k, st, f in [
[0, "hash", st_hash, self.hash_f], [0, "hash", st_hash, self.hash_f],
[1, "send", st_up, self.up_f], [1, "send", st_up, self.up_f],
]: ]:
txt += "\033[{0}H{1}:".format(ss.g + y, k) txt += "\033[%dH%s:" % (ss.g + y, k)
file, arg = st file, arg = st
if not file: if not file:
txt += " {0}\033[K".format(arg) txt += " %s\033[K" % (arg,)
else: else:
if y: if y:
p = 100 * file.up_b / file.size p = 100 * file.up_b / file.size
@ -906,12 +987,11 @@ class Ctl(object):
name = file.abs.decode("utf-8", "replace")[-maxlen:] name = file.abs.decode("utf-8", "replace")[-maxlen:]
if "/" in name: if "/" in name:
name = "\033[36m{0}\033[0m/{1}".format(*name.rsplit("/", 1)) name = "\033[36m%s\033[0m/%s" % tuple(name.rsplit("/", 1))
t = "{0:6.1f}% {1} {2}\033[K" txt += "%6.1f%% %d %s\033[K" % (p, self.nfiles - f, name)
txt += t.format(p, self.nfiles - f, name)
txt += "\033[{0}H ".format(ss.g + 2) txt += "\033[%dH " % (ss.g + 2,)
else: else:
txt = " " txt = " "
@ -929,7 +1009,7 @@ class Ctl(object):
nleft = self.nfiles - self.up_f nleft = self.nfiles - self.up_f
tail = "\033[K\033[u" if VT100 and not self.ar.ns else "\r" tail = "\033[K\033[u" if VT100 and not self.ar.ns else "\r"
t = "{0} eta @ {1}/s, {2}, {3}# left".format(self.eta, spd, sleft, nleft) t = "%s eta @ %s/s, %s, %d# left\033[K" % (self.eta, spd, sleft, nleft)
eprint(txt + "\033]0;{0}\033\\\r{0}{1}".format(t, tail)) eprint(txt + "\033]0;{0}\033\\\r{0}{1}".format(t, tail))
if self.hash_b and self.at_hash: if self.hash_b and self.at_hash:
@ -965,20 +1045,18 @@ class Ctl(object):
srd = rd.decode("utf-8", "replace").replace("\\", "/") srd = rd.decode("utf-8", "replace").replace("\\", "/")
if prd != rd: if prd != rd:
prd = rd prd = rd
headers = {}
if self.ar.a:
headers["Cookie"] = "=".join(["cppwd", self.ar.a])
ls = {} ls = {}
try: try:
print(" ls ~{0}".format(srd)) print(" ls ~{0}".format(srd))
zb = self.ar.url.encode("utf-8") zt = (
zb += quotep(rd.replace(b"\\", b"/")) self.ar.vtop,
r = req_ses.get(zb + b"?ls&lt&dots", headers=headers) quotep(rd.replace(b"\\", b"/")).decode("utf-8", "replace"),
if not r: )
raise Exception("HTTP {0}".format(r.status_code)) sc, txt = web.req("GET", "%s%s?ls&lt&dots" % zt, {})
if sc >= 400:
raise Exception("http %s" % (sc,))
j = r.json() j = json.loads(txt)
for f in j["dirs"] + j["files"]: for f in j["dirs"] + j["files"]:
rfn = f["href"].split("?")[0].rstrip("/") rfn = f["href"].split("?")[0].rstrip("/")
ls[unquote(rfn.encode("utf-8", "replace"))] = f ls[unquote(rfn.encode("utf-8", "replace"))] = f
@ -1001,14 +1079,17 @@ class Ctl(object):
req = locs req = locs
while req: while req:
print("DELETING ~%s/#%s" % (srd, len(req))) print("DELETING ~%s/#%s" % (srd, len(req)))
r = req_ses.post(self.ar.url + "?delete", json=req) body = json.dumps(req).encode("utf-8")
if r.status_code == 413 and "json 2big" in r.text: sc, txt = web.req(
"POST", self.ar.url + "?delete", {}, body, MJ
)
if sc == 413 and "json 2big" in txt:
print(" (delete request too big; slicing...)") print(" (delete request too big; slicing...)")
req = req[: len(req) // 2] req = req[: len(req) // 2]
continue continue
elif not r: elif sc >= 400:
t = "delete request failed: %r %s" t = "delete request failed: %s %s"
raise Exception(t % (r, r.text)) raise Exception(t % (sc, txt))
break break
locs = locs[len(req) :] locs = locs[len(req) :]
@ -1056,7 +1137,7 @@ class Ctl(object):
if self.ar.wlist: if self.ar.wlist:
zsl = [self.ar.wsalt, str(file.size)] + [x[0] for x in file.kchunks] zsl = [self.ar.wsalt, str(file.size)] + [x[0] for x in file.kchunks]
zb = hashlib.sha512("\n".join(zsl).encode("utf-8")).digest()[:33] zb = hashlib.sha512("\n".join(zsl).encode("utf-8")).digest()[:33]
wark = base64.urlsafe_b64encode(zb).decode("utf-8") wark = ub64enc(zb).decode("utf-8")
vp = file.rel.decode("utf-8") vp = file.rel.decode("utf-8")
if self.ar.jw: if self.ar.jw:
print("%s %s" % (wark, vp)) print("%s %s" % (wark, vp))
@ -1087,7 +1168,6 @@ class Ctl(object):
def handshaker(self): def handshaker(self):
search = self.ar.s search = self.ar.s
burl = self.ar.url[:8] + self.ar.url[8:].split("/")[0] + "/"
while True: while True:
file = self.q_handshake.get() file = self.q_handshake.get()
if not file: if not file:
@ -1109,12 +1189,16 @@ class Ctl(object):
while time.time() < file.cd: while time.time() < file.cd:
time.sleep(0.1) time.sleep(0.1)
hs, sprs = handshake(self.ar, file, search) try:
hs, sprs = handshake(self.ar, file, search)
except BadAuth:
self.panik = 1
break
if search: if search:
if hs: if hs:
for hit in hs: for hit in hs:
t = "found: {0}\n {1}{2}" print("found: %s\n %s/%s" % (upath, self.ar.burl, hit["rp"]))
print(t.format(upath, burl, hit["rp"]))
else: else:
print("NOT found: {0}".format(upath)) print("NOT found: {0}".format(upath))
@ -1236,7 +1320,7 @@ class Ctl(object):
) )
try: try:
upload(fsl, self.ar.a, stats) upload(fsl, stats)
except Exception as ex: except Exception as ex:
t = "upload failed, retrying: %s #%s+%d (%s)\n" t = "upload failed, retrying: %s #%s+%d (%s)\n"
eprint(t % (file.name, cids[0][:8], len(cids) - 1, ex)) eprint(t % (file.name, cids[0][:8], len(cids) - 1, ex))
@ -1270,14 +1354,17 @@ class APF(argparse.ArgumentDefaultsHelpFormatter, argparse.RawDescriptionHelpFor
def main(): def main():
global web
time.strptime("19970815", "%Y%m%d") # python#7980 time.strptime("19970815", "%Y%m%d") # python#7980
"".encode("idna") # python#29288
if not VT100: if not VT100:
os.system("rem") # enables colors os.system("rem") # enables colors
cores = (os.cpu_count() if hasattr(os, "cpu_count") else 0) or 2 cores = (os.cpu_count() if hasattr(os, "cpu_count") else 0) or 2
hcores = min(cores, 3) # 4% faster than 4+ on py3.9 @ r5-4500U hcores = min(cores, 3) # 4% faster than 4+ on py3.9 @ r5-4500U
ver = "{0}, v{1}".format(S_BUILD_DT, S_VERSION) ver = "{0} v{1} https://youtu.be/BIcOO6TLKaY".format(S_BUILD_DT, S_VERSION)
if "--version" in sys.argv: if "--version" in sys.argv:
print(ver) print(ver)
return return
@ -1285,7 +1372,7 @@ def main():
sys.argv = [x for x in sys.argv if x != "--ws"] sys.argv = [x for x in sys.argv if x != "--ws"]
# fmt: off # fmt: off
ap = app = argparse.ArgumentParser(formatter_class=APF, description="copyparty up2k uploader / filesearch tool, " + ver, epilog=""" ap = app = argparse.ArgumentParser(formatter_class=APF, description="copyparty up2k uploader / filesearch tool " + ver, epilog="""
NOTE: NOTE:
source file/folder selection uses rsync syntax, meaning that: source file/folder selection uses rsync syntax, meaning that:
"foo" uploads the entire folder to URL/foo/ "foo" uploads the entire folder to URL/foo/
@ -1366,13 +1453,20 @@ source file/folder selection uses rsync syntax, meaning that:
for x in ar.files for x in ar.files
] ]
ar.url = ar.url.rstrip("/") + "/" # urlsplit needs scheme;
if "://" not in ar.url: zs = ar.url.rstrip("/") + "/"
ar.url = "http://" + ar.url if "://" not in zs:
zs = "http://" + zs
ar.url = zs
url = urlsplit(zs)
ar.burl = "%s://%s" % (url.scheme, url.netloc)
ar.vtop = url.path
if "https://" in ar.url.lower(): if "https://" in ar.url.lower():
try: try:
import ssl, zipfile import ssl
import zipfile
except: except:
t = "ERROR: https is not available for some reason; please use http" t = "ERROR: https is not available for some reason; please use http"
print("\n\n %s\n\n" % (t,)) print("\n\n %s\n\n" % (t,))
@ -1397,6 +1491,7 @@ source file/folder selection uses rsync syntax, meaning that:
if ar.cls: if ar.cls:
eprint("\033[H\033[2J\033[3J", end="") eprint("\033[H\033[2J\033[3J", end="")
web = HCli(ar)
ctl = Ctl(ar) ctl = Ctl(ar)
if ar.dr and not ar.drd and ctl.ok: if ar.dr and not ar.drd and ctl.ok:

View file

@ -4,12 +4,6 @@ f117016b1e6a7d7e745db30d3e67f1acf7957c443a0dd301b6c5e10b8368f2aa4db6be9782d2d3f8
749a473646c6d4c7939989649733d4c7699fd1c359c27046bf5bc9c070d1a4b8b986bbc65f60d7da725baf16dbfdd75a4c2f5bb8335f2cb5685073f5fee5c2d1 pywin32_ctypes-0.2.2-py3-none-any.whl 749a473646c6d4c7939989649733d4c7699fd1c359c27046bf5bc9c070d1a4b8b986bbc65f60d7da725baf16dbfdd75a4c2f5bb8335f2cb5685073f5fee5c2d1 pywin32_ctypes-0.2.2-py3-none-any.whl
085d39ef4426aa5f097fbc484595becc16e61ca23fc7da4d2a8bba540a3b82e789e390b176c7151bdc67d01735cce22b1562cdb2e31273225a2d3e275851a4ad setuptools-70.3.0-py3-none-any.whl 085d39ef4426aa5f097fbc484595becc16e61ca23fc7da4d2a8bba540a3b82e789e390b176c7151bdc67d01735cce22b1562cdb2e31273225a2d3e275851a4ad setuptools-70.3.0-py3-none-any.whl
360a141928f4a7ec18a994602cbb28bbf8b5cc7c077a06ac76b54b12fa769ed95ca0333a5cf728923a8e0baeb5cc4d5e73e5b3de2666beb05eb477d8ae719093 upx-4.2.4-win32.zip 360a141928f4a7ec18a994602cbb28bbf8b5cc7c077a06ac76b54b12fa769ed95ca0333a5cf728923a8e0baeb5cc4d5e73e5b3de2666beb05eb477d8ae719093 upx-4.2.4-win32.zip
# u2c (win7)
7a3bd4849f95e1715fe2e99613df70a0fedd944a9bfde71a0fadb837fe62c3431c30da4f0b75c74de6f1a459f1fdf7cb62eaf404fdbe45e2d121e0b1021f1580 certifi-2024.2.2-py3-none-any.whl
9cc8acc5e269e6421bc32bb89261101da29d6ca337d39d60b9106de9ed7904e188716e4a48d78a2c4329026443fcab7acab013d2fe43778e30d6c4e4506a1b91 charset_normalizer-3.3.2-cp37-cp37m-win32.whl
0ec1ae5c928b4a0001a254c8598b746049406e1eed720bfafa94d4474078eff76bf6e032124e2d4df4619052836523af36162443c6d746487b387d2e3476e691 idna-3.6-py3-none-any.whl
cc08d0d87d184401872a2f82266d589253979b4cd02f23b51290fbb2a20082848fc72acbed8aacb74ac4af068d575ef96e66196c5068bc38fb0bcafdc7626869 requests-2.29.0-py3-none-any.whl
fe5fee6cb8a2c68800b32353a0015e5d2e1ad1cb6e0c9e6acf86e48e5cdb5606ad465dc4485ea5fbc8701d8716a8a7f7148c57724ef9da26b0c0a76f6dbbd698 urllib3-1.26.19-py2.py3-none-any.whl
# win7 # win7
3253e86471e6f9fa85bfdb7684cd2f964ed6e35c6a4db87f81cca157c049bef43e66dfcae1e037b2fb904567b1e028aaeefe8983ba3255105df787406d2aa71e en_windows_7_professional_with_sp1_x86_dvd_u_677056.iso 3253e86471e6f9fa85bfdb7684cd2f964ed6e35c6a4db87f81cca157c049bef43e66dfcae1e037b2fb904567b1e028aaeefe8983ba3255105df787406d2aa71e en_windows_7_professional_with_sp1_x86_dvd_u_677056.iso
ab0db0283f61a5bbe44797d74546786bf41685175764a448d2e3bd629f292f1e7d829757b26be346b5044d78c9c1891736d93237cee4b1b6f5996a902c86d15f en_windows_7_professional_with_sp1_x64_dvd_u_676939.iso ab0db0283f61a5bbe44797d74546786bf41685175764a448d2e3bd629f292f1e7d829757b26be346b5044d78c9c1891736d93237cee4b1b6f5996a902c86d15f en_windows_7_professional_with_sp1_x64_dvd_u_676939.iso

View file

@ -13,13 +13,6 @@ https://pypi.org/project/MarkupSafe/#files
https://pypi.org/project/mutagen/#files https://pypi.org/project/mutagen/#files
https://pypi.org/project/Pillow/#files https://pypi.org/project/Pillow/#files
# u2c (win7) additionals
https://pypi.org/project/certifi/#files
https://pypi.org/project/charset-normalizer/#files # cp37-cp37m-win32.whl
https://pypi.org/project/idna/#files
https://pypi.org/project/requests/#files
https://pypi.org/project/urllib3/#files
# win7 additionals # win7 additionals
https://pypi.org/project/future/#files https://pypi.org/project/future/#files
https://pypi.org/project/importlib-metadata/#files https://pypi.org/project/importlib-metadata/#files

View file

@ -43,13 +43,6 @@ fns=(
pyinstaller_hooks_contrib-2024.8-py3-none-any.whl pyinstaller_hooks_contrib-2024.8-py3-none-any.whl
python-3.12.6-amd64.exe python-3.12.6-amd64.exe
) )
[ $w7 ] && fns+=( # u2c stuff
certifi-2024.2.2-py3-none-any.whl
charset_normalizer-3.3.2-cp37-cp37m-win32.whl
idna-3.6-py3-none-any.whl
requests-2.29.0-py3-none-any.whl
urllib3-1.26.19-py2.py3-none-any.whl
)
[ $w7 ] && fns+=( [ $w7 ] && fns+=(
future-1.0.0-py3-none-any.whl future-1.0.0-py3-none-any.whl
importlib_metadata-6.7.0-py3-none-any.whl importlib_metadata-6.7.0-py3-none-any.whl
@ -96,12 +89,11 @@ python -m ensurepip &&
{ [ $w10 ] || python -m pip install --user -U pip-*.whl; } && { [ $w10 ] || python -m pip install --user -U pip-*.whl; } &&
python -m pip install --user -U packaging-*.whl && python -m pip install --user -U packaging-*.whl &&
{ [ $w7 ] || python -m pip install --user -U {setuptools,mutagen,pillow,jinja2,MarkupSafe}-*.whl; } && { [ $w7 ] || python -m pip install --user -U {setuptools,mutagen,pillow,jinja2,MarkupSafe}-*.whl; } &&
{ [ $w10 ] || python -m pip install --user -U {requests,urllib3,charset_normalizer,certifi,idna}-*.whl; } &&
{ [ $w10 ] || python -m pip install --user -U future-*.whl importlib_metadata-*.whl typing_extensions-*.whl zipp-*.whl; } && { [ $w10 ] || python -m pip install --user -U future-*.whl importlib_metadata-*.whl typing_extensions-*.whl zipp-*.whl; } &&
python -m pip install --user -U pyinstaller-*.whl pefile-*.whl pywin32_ctypes-*.whl pyinstaller_hooks_contrib-*.whl altgraph-*.whl && python -m pip install --user -U pyinstaller-*.whl pefile-*.whl pywin32_ctypes-*.whl pyinstaller_hooks_contrib-*.whl altgraph-*.whl &&
sed -ri 's/--lzma/--best/' $appd/Python/Python$pyv/site-packages/pyinstaller/building/utils.py && sed -ri 's/--lzma/--best/' $appd/Python/Python$pyv/site-packages/pyinstaller/building/utils.py &&
curl -fkLO https://192.168.123.1:3923/cpp/scripts/uncomment.py && curl -fkLO https://192.168.123.1:3923/cpp/scripts/uncomment.py &&
python uncomment.py 1 $(for d in $appd/Python/Python$pyv/site-packages/{requests,urllib3,charset_normalizer,certifi,idna,mutagen,PIL,jinja2,markupsafe}; do find $d -name \*.py; done) && python uncomment.py 1 $(for d in $appd/Python/Python$pyv/site-packages/{mutagen,PIL,jinja2,markupsafe}; do find $d -name \*.py; done) &&
cd && cd &&
rm -f build.sh && rm -f build.sh &&
curl -fkLO https://192.168.123.1:3923/cpp/scripts/pyinstaller/build.sh && curl -fkLO https://192.168.123.1:3923/cpp/scripts/pyinstaller/build.sh &&

View file

@ -19,25 +19,12 @@ dl https://192.168.123.1:3923/cpp/scripts/pyinstaller/up2k.ico
dl https://192.168.123.1:3923/cpp/scripts/pyinstaller/up2k.rc dl https://192.168.123.1:3923/cpp/scripts/pyinstaller/up2k.rc
dl https://192.168.123.1:3923/cpp/scripts/pyinstaller/up2k.spec dl https://192.168.123.1:3923/cpp/scripts/pyinstaller/up2k.spec
# $LOCALAPPDATA/programs/python/python37-32/python -m pip install --user -U pyinstaller requests # $LOCALAPPDATA/programs/python/python37-32/python -m pip install --user -U pyinstaller
grep -E '^from .ssl_ import' $APPDATA/python/python37/site-packages/urllib3/util/proxy.py && { sed -ri 's/^(import .*), selectors$/\1\ntry: import selectors\nexcept: pass/' $LOCALAPPDATA/programs/python/python37-32/Lib/socket.py
echo golfing
echo > $APPDATA/python/python37/site-packages/requests/certs.py
sed -ri 's/^(DEFAULT_CA_BUNDLE_PATH = ).*/\1""/' $APPDATA/python/python37/site-packages/requests/utils.py
sed -ri '/^import zipfile$/d' $APPDATA/python/python37/site-packages/requests/utils.py
sed -ri 's/"idna"//' $APPDATA/python/python37/site-packages/requests/packages.py
sed -ri 's/import charset_normalizer.*/pass/' $APPDATA/python/python37/site-packages/requests/compat.py
sed -ri 's/raise.*charset_normalizer.*/pass/' $APPDATA/python/python37/site-packages/requests/__init__.py
sed -ri 's/import charset_normalizer.*//' $APPDATA/python/python37/site-packages/requests/packages.py
sed -ri 's/chardet.__name__/"\\roll\\tide"/' $APPDATA/python/python37/site-packages/requests/packages.py
sed -ri 's/chardet,//' $APPDATA/python/python37/site-packages/requests/models.py
for n in util/__init__.py connection.py; do awk -i inplace '/^from (\.util)?\.ssl_ /{s=1} !s; /^\)/{s=0}' $APPDATA/python/python37/site-packages/urllib3/$n; done
sed -ri 's/^from .ssl_ import .*//' $APPDATA/python/python37/site-packages/urllib3/util/proxy.py
echo golfed
}
sed -ri 's/(add_argument."-t[de]",.*help=")[^"]+/\1not applicable; HTTPS is disabled in this exe/; s/for some reason/in this exe for safety reasons/' u2c.py sed -ri 's/(add_argument."-t[de]",.*help=")[^"]+/\1not applicable; HTTPS is disabled in this exe/; s/for some reason/in this exe for safety reasons/' u2c.py
sed -ri '/^import platform/d;s/^(VT100 = )pla.*/\1False/' u2c.py
read a b _ < <(awk -F\" '/^S_VERSION =/{$0=$2;sub(/\./," ");print}' < u2c.py) read a b _ < <(awk -F\" '/^S_VERSION =/{$0=$2;sub(/\./," ");print}' < u2c.py)
sed -r 's/1,2,3,0/'$a,$b,0,0'/;s/1\.2\.3/'$a.$b.0/ <up2k.rc >up2k.rc2 sed -r 's/1,2,3,0/'$a,$b,0,0'/;s/1\.2\.3/'$a.$b.0/ <up2k.rc >up2k.rc2

View file

@ -14,22 +14,21 @@ a = Analysis(
hooksconfig={}, hooksconfig={},
runtime_hooks=[], runtime_hooks=[],
excludes=[ excludes=[
'bz2',
'ftplib', 'ftplib',
'getpass',
'lzma', 'lzma',
'pickle', 'pickle',
'platform',
'selectors',
'ssl', 'ssl',
'subprocess',
'tarfile', 'tarfile',
'bz2', 'tempfile',
'zipfile',
'tracemalloc', 'tracemalloc',
'typing',
'zipfile',
'zlib', 'zlib',
'urllib3.util.ssl_',
'urllib3.contrib.pyopenssl',
'urllib3.contrib.socks',
'certifi',
'idna',
'chardet',
'charset_normalizer',
'email.contentmanager', 'email.contentmanager',
'email.policy', 'email.policy',
'encodings.zlib_codec', 'encodings.zlib_codec',
@ -40,6 +39,8 @@ a = Analysis(
'encodings.palmos', 'encodings.palmos',
'encodings.punycode', 'encodings.punycode',
'encodings.rot_13', 'encodings.rot_13',
'urllib.response',
'urllib.robotparser',
], ],
win_no_prefer_redirects=False, win_no_prefer_redirects=False,
win_private_assemblies=False, win_private_assemblies=False,

View file

@ -6,7 +6,6 @@ set -e
ex=( ex=(
ftplib lzma pickle ssl tarfile bz2 zipfile tracemalloc zlib ftplib lzma pickle ssl tarfile bz2 zipfile tracemalloc zlib
urllib3.util.ssl_ urllib3.contrib.pyopenssl urllib3.contrib.socks certifi idna chardet charset_normalizer
email.contentmanager email.policy email.contentmanager email.policy
encodings.{zlib_codec,base64_codec,bz2_codec,charmap,hex_codec,palmos,punycode,rot_13} encodings.{zlib_codec,base64_codec,bz2_codec,charmap,hex_codec,palmos,punycode,rot_13}
); );