mirror of
https://github.com/9001/copyparty.git
synced 2025-08-17 00:52:16 -06:00
support mojibake and py3.2
This commit is contained in:
parent
bf95527e92
commit
63e089a5f0
|
@ -7,7 +7,7 @@
|
|||
|
||||
turn your phone or raspi into a portable file server with resumable uploads/downloads using IE6 or any other browser
|
||||
|
||||
* server runs on anything with `py2.7` or `py3.3+`
|
||||
* server runs on anything with `py2.7` or `py3.2+`
|
||||
* *resumable* uploads need `firefox 12+` / `chrome 6+` / `safari 6+` / `IE 10+`
|
||||
* code standard: `black`
|
||||
|
||||
|
@ -31,7 +31,7 @@ summary: it works
|
|||
# dependencies
|
||||
|
||||
* `jinja2`
|
||||
* pulls in `markupsafe`
|
||||
* pulls in `markupsafe` as of v2.7; use jinja 2.6 on py3.2
|
||||
|
||||
optional, enables thumbnails:
|
||||
* `Pillow` (requires py2.7 or py3.5+)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#!/usr/bin/env python
|
||||
# coding: utf-8
|
||||
from __future__ import print_function
|
||||
from __future__ import print_function, unicode_literals
|
||||
|
||||
import platform
|
||||
import sys
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#!/usr/bin/env python
|
||||
# coding: utf-8
|
||||
from __future__ import print_function
|
||||
from __future__ import print_function, unicode_literals
|
||||
|
||||
"""copyparty: http file sharing hub (py2/py3)"""
|
||||
__author__ = "ed <copyparty@ocv.me>"
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
#!/usr/bin/env python
|
||||
# coding: utf-8
|
||||
from __future__ import print_function
|
||||
from __future__ import print_function, unicode_literals
|
||||
|
||||
import os
|
||||
import threading
|
||||
|
||||
from .__init__ import PY2
|
||||
from .util import undot, Pebkac
|
||||
from .util import undot, Pebkac, fsdec, fsenc
|
||||
|
||||
|
||||
class VFS(object):
|
||||
|
@ -90,12 +90,12 @@ class VFS(object):
|
|||
if rem:
|
||||
rp += "/" + rem
|
||||
|
||||
return os.path.realpath(rp)
|
||||
return fsdec(os.path.realpath(fsenc(rp)))
|
||||
|
||||
def ls(self, rem, uname):
|
||||
"""return user-readable [fsdir,real,virt] items at vpath"""
|
||||
abspath = self.canonical(rem)
|
||||
real = os.listdir(abspath)
|
||||
real = [fsdec(x) for x in os.listdir(fsenc(abspath))]
|
||||
real.sort()
|
||||
if rem:
|
||||
virt_vis = []
|
||||
|
@ -182,7 +182,7 @@ class AuthSrv(object):
|
|||
raise Exception('invalid mountpoint "{}"'.format(vol_dst))
|
||||
|
||||
# cfg files override arguments and previous files
|
||||
vol_src = os.path.abspath(vol_src)
|
||||
vol_src = fsdec(os.path.abspath(fsenc(vol_src)))
|
||||
vol_dst = vol_dst.strip("/")
|
||||
mount[vol_dst] = vol_src
|
||||
mread[vol_dst] = []
|
||||
|
@ -217,7 +217,7 @@ class AuthSrv(object):
|
|||
# list of src:dst:permset:permset:...
|
||||
# permset is [rwa]username
|
||||
for src, dst, perms in [x.split(":", 2) for x in self.args.v]:
|
||||
src = os.path.abspath(src)
|
||||
src = fsdec(os.path.abspath(fsenc(src)))
|
||||
dst = dst.strip("/")
|
||||
mount[dst] = src
|
||||
mread[dst] = []
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#!/usr/bin/env python
|
||||
# coding: utf-8
|
||||
from __future__ import print_function
|
||||
from __future__ import print_function, unicode_literals
|
||||
|
||||
import os
|
||||
import stat
|
||||
|
@ -14,11 +14,6 @@ from .util import * # noqa # pylint: disable=unused-wildcard-import
|
|||
|
||||
if not PY2:
|
||||
unicode = str
|
||||
from urllib.parse import unquote_plus
|
||||
from urllib.parse import quote_plus
|
||||
else:
|
||||
from urllib import unquote_plus # pylint: disable=no-name-in-module
|
||||
from urllib import quote_plus
|
||||
|
||||
|
||||
class HttpCli(object):
|
||||
|
@ -76,8 +71,6 @@ class HttpCli(object):
|
|||
if self.uname:
|
||||
self.rvol = self.auth.vfs.user_tree(self.uname, readable=True)
|
||||
self.wvol = self.auth.vfs.user_tree(self.uname, writable=True)
|
||||
self.log(self.rvol)
|
||||
self.log(self.wvol)
|
||||
|
||||
# split req into vpath + uparam
|
||||
uparam = {}
|
||||
|
@ -100,7 +93,7 @@ class HttpCli(object):
|
|||
uparam[k.lower()] = True
|
||||
|
||||
self.uparam = uparam
|
||||
self.vpath = unquote_plus(vpath)
|
||||
self.vpath = unquotep(vpath)
|
||||
|
||||
try:
|
||||
if mode == "GET":
|
||||
|
@ -108,7 +101,7 @@ class HttpCli(object):
|
|||
elif mode == "POST":
|
||||
self.handle_post()
|
||||
else:
|
||||
self.loud_reply(u'invalid HTTP mode "{0}"'.format(mode))
|
||||
self.loud_reply('invalid HTTP mode "{0}"'.format(mode))
|
||||
|
||||
except Pebkac as ex:
|
||||
self.loud_reply(str(ex))
|
||||
|
@ -119,16 +112,16 @@ class HttpCli(object):
|
|||
def reply(self, body, status="200 OK", mime="text/html", headers=[]):
|
||||
# TODO something to reply with user-supplied values safely
|
||||
response = [
|
||||
u"HTTP/1.1 " + status,
|
||||
u"Connection: Keep-Alive",
|
||||
u"Content-Type: " + mime,
|
||||
u"Content-Length: " + str(len(body)),
|
||||
"HTTP/1.1 " + status,
|
||||
"Connection: Keep-Alive",
|
||||
"Content-Type: " + mime,
|
||||
"Content-Length: " + str(len(body)),
|
||||
]
|
||||
for k, v in self.out_headers.items():
|
||||
response.append("{}: {}".format(k, v))
|
||||
|
||||
response.extend(headers)
|
||||
response_str = u"\r\n".join(response).encode("utf-8")
|
||||
response_str = "\r\n".join(response).encode("utf-8")
|
||||
if self.ok:
|
||||
self.s.send(response_str + b"\r\n\r\n" + body)
|
||||
|
||||
|
@ -143,7 +136,7 @@ class HttpCli(object):
|
|||
self.log("GET " + self.req)
|
||||
|
||||
# "embedded" resources
|
||||
if self.vpath.startswith(u".cpr"):
|
||||
if self.vpath.startswith(".cpr"):
|
||||
static_path = os.path.join(E.mod, "web/", self.vpath[5:])
|
||||
|
||||
if os.path.isfile(static_path):
|
||||
|
@ -193,11 +186,11 @@ class HttpCli(object):
|
|||
|
||||
act = self.parser.require("act", 64)
|
||||
|
||||
if act == u"bput":
|
||||
if act == "bput":
|
||||
self.handle_plain_upload()
|
||||
return
|
||||
|
||||
if act == u"login":
|
||||
if act == "login":
|
||||
self.handle_login()
|
||||
return
|
||||
|
||||
|
@ -208,10 +201,10 @@ class HttpCli(object):
|
|||
self.parser.drop()
|
||||
|
||||
if pwd in self.auth.iuser:
|
||||
msg = u"login ok"
|
||||
msg = "login ok"
|
||||
else:
|
||||
msg = u"naw dude"
|
||||
pwd = u"x" # nosec
|
||||
msg = "naw dude"
|
||||
pwd = "x" # nosec
|
||||
|
||||
h = ["Set-Cookie: cppwd={}; Path=/".format(pwd)]
|
||||
html = self.conn.tpl_msg.render(h1=msg, h2='<a href="/">ack</a>', redir="/")
|
||||
|
@ -235,7 +228,7 @@ class HttpCli(object):
|
|||
|
||||
# TODO broker which avoid this race
|
||||
# and provides a new filename if taken
|
||||
if os.path.exists(fn):
|
||||
if os.path.exists(fsenc(fn)):
|
||||
fn += ".{:.6f}".format(time.time())
|
||||
|
||||
with open(fn, "wb") as f:
|
||||
|
@ -254,10 +247,10 @@ class HttpCli(object):
|
|||
if not self.ok:
|
||||
status = "ERROR"
|
||||
|
||||
msg = u"{0} // {1} bytes // {2:.3f} MiB/s\n".format(status, sz_total, spd)
|
||||
msg = "{0} // {1} bytes // {2:.3f} MiB/s\n".format(status, sz_total, spd)
|
||||
|
||||
for sz, sha512 in files:
|
||||
msg += u"sha512: {0} // {1} bytes\n".format(sha512[:56], sz)
|
||||
msg += "sha512: {0} // {1} bytes\n".format(sha512[:56], sz)
|
||||
# truncated SHA-512 prevents length extension attacks;
|
||||
# using SHA-512/224, optionally SHA-512/256 = :64
|
||||
|
||||
|
@ -276,10 +269,10 @@ class HttpCli(object):
|
|||
with open(log_fn, "wb") as f:
|
||||
f.write(
|
||||
(
|
||||
u"\n".join(
|
||||
"\n".join(
|
||||
unicode(x)
|
||||
for x in [
|
||||
u":".join(unicode(x) for x in self.addr),
|
||||
":".join(unicode(x) for x in self.addr),
|
||||
msg.rstrip(),
|
||||
]
|
||||
)
|
||||
|
@ -288,7 +281,7 @@ class HttpCli(object):
|
|||
)
|
||||
|
||||
def tx_file(self, path):
|
||||
sz = os.path.getsize(path)
|
||||
sz = os.path.getsize(fsenc(path))
|
||||
mime = mimetypes.guess_type(path)[0]
|
||||
header = "HTTP/1.1 200 OK\r\nConnection: Keep-Alive\r\nContent-Type: {}\r\nContent-Length: {}\r\n\r\n".format(
|
||||
mime, sz
|
||||
|
@ -299,7 +292,7 @@ class HttpCli(object):
|
|||
if self.ok:
|
||||
self.s.send(header)
|
||||
|
||||
with open(path, "rb") as f:
|
||||
with open(fsenc(path), "rb") as f:
|
||||
while self.ok:
|
||||
buf = f.read(4096)
|
||||
if not buf:
|
||||
|
@ -321,20 +314,20 @@ class HttpCli(object):
|
|||
self.loud_reply("TODO jupper {}".format(self.vpath))
|
||||
|
||||
def tx_browser(self):
|
||||
vpath = u""
|
||||
vpnodes = [[u"/", u"/"]]
|
||||
vpath = ""
|
||||
vpnodes = [["/", "/"]]
|
||||
for node in self.vpath.split("/"):
|
||||
vpath += u"/" + node
|
||||
vpnodes.append([quote_plus(vpath, safe="/") + "/", cgi.escape(node)])
|
||||
vpath += "/" + node
|
||||
vpnodes.append([quotep(vpath) + "/", cgi.escape(node)])
|
||||
|
||||
vn, rem = self.auth.vfs.get(self.vpath, self.uname, True, False)
|
||||
abspath = vn.canonical(rem)
|
||||
|
||||
if not os.path.exists(abspath):
|
||||
if not os.path.exists(fsenc(abspath)):
|
||||
print(abspath)
|
||||
raise Pebkac("404 not found")
|
||||
|
||||
if not os.path.isdir(abspath):
|
||||
if not os.path.isdir(fsenc(abspath)):
|
||||
return self.tx_file(abspath)
|
||||
|
||||
fsroot, vfs_ls, vfs_virt = vn.ls(rem, self.uname)
|
||||
|
@ -348,7 +341,7 @@ class HttpCli(object):
|
|||
href = vpath + "/" + fn
|
||||
|
||||
fspath = fsroot + "/" + fn
|
||||
inf = os.stat(fspath)
|
||||
inf = os.stat(fsenc(fspath))
|
||||
|
||||
is_dir = stat.S_ISDIR(inf.st_mode)
|
||||
if is_dir:
|
||||
|
@ -361,13 +354,7 @@ class HttpCli(object):
|
|||
dt = datetime.utcfromtimestamp(inf.st_mtime)
|
||||
dt = dt.strftime("%Y-%m-%d %H:%M:%S")
|
||||
|
||||
item = [
|
||||
margin,
|
||||
quote_plus(href, safe="/"),
|
||||
cgi.escape(fn, quote=True),
|
||||
sz,
|
||||
dt,
|
||||
]
|
||||
item = [margin, quotep(href), cgi.escape(fn, quote=True), sz, dt]
|
||||
if is_dir:
|
||||
dirs.append(item)
|
||||
else:
|
||||
|
@ -377,4 +364,4 @@ class HttpCli(object):
|
|||
html = self.conn.tpl_browser.render(
|
||||
vpnodes=vpnodes, files=dirs, can_upload=self.writable
|
||||
)
|
||||
self.reply(html.encode("utf-8"))
|
||||
self.reply(html.encode("utf-8", "replace"))
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#!/usr/bin/env python
|
||||
# coding: utf-8
|
||||
from __future__ import print_function
|
||||
from __future__ import print_function, unicode_literals
|
||||
|
||||
import os
|
||||
import jinja2
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#!/usr/bin/env python
|
||||
# coding: utf-8
|
||||
from __future__ import print_function
|
||||
from __future__ import print_function, unicode_literals
|
||||
|
||||
import time
|
||||
import threading
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#!/usr/bin/env python
|
||||
# coding: utf-8
|
||||
from __future__ import print_function
|
||||
from __future__ import print_function, unicode_literals
|
||||
|
||||
import sys
|
||||
import time
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#!/usr/bin/env python
|
||||
# coding: utf-8
|
||||
from __future__ import print_function
|
||||
from __future__ import print_function, unicode_literals
|
||||
|
||||
|
||||
class MsgSvc(object):
|
||||
|
|
|
@ -2,7 +2,10 @@
|
|||
This is Victor Stinner's pure-Python implementation of PEP 383: the "surrogateescape" error
|
||||
handler of Python 3.
|
||||
|
||||
Source: misc/python/surrogateescape.py in https://bitbucket.org/haypo/misc
|
||||
Scissored from the python-future module to avoid 4.4MB of additional dependencies:
|
||||
https://github.com/PythonCharmers/python-future/blob/e12549c42ed3a38ece45b9d88c75f5f3ee4d658d/src/future/utils/surrogateescape.py
|
||||
|
||||
Original source: misc/python/surrogateescape.py in https://bitbucket.org/haypo/misc
|
||||
"""
|
||||
|
||||
# This code is released under the Python license and the BSD 2-clause license
|
||||
|
@ -10,33 +13,33 @@ Source: misc/python/surrogateescape.py in https://bitbucket.org/haypo/misc
|
|||
import codecs
|
||||
import sys
|
||||
|
||||
from future import utils
|
||||
PY3 = sys.version_info[0] > 2
|
||||
|
||||
FS_ERRORS = "surrogateescape"
|
||||
|
||||
FS_ERRORS = 'surrogateescape'
|
||||
|
||||
# # -- Python 2/3 compatibility -------------------------------------
|
||||
# FS_ERRORS = 'my_surrogateescape'
|
||||
|
||||
def u(text):
|
||||
if utils.PY3:
|
||||
if PY3:
|
||||
return text
|
||||
else:
|
||||
return text.decode('unicode_escape')
|
||||
return text.decode("unicode_escape")
|
||||
|
||||
|
||||
def b(data):
|
||||
if utils.PY3:
|
||||
return data.encode('latin1')
|
||||
if PY3:
|
||||
return data.encode("latin1")
|
||||
else:
|
||||
return data
|
||||
|
||||
if utils.PY3:
|
||||
|
||||
if PY3:
|
||||
_unichr = chr
|
||||
bytes_chr = lambda code: bytes((code,))
|
||||
else:
|
||||
_unichr = unichr
|
||||
bytes_chr = chr
|
||||
|
||||
|
||||
def surrogateescape_handler(exc):
|
||||
"""
|
||||
Pure Python implementation of the PEP 383: the "surrogateescape" error
|
||||
|
@ -44,7 +47,7 @@ def surrogateescape_handler(exc):
|
|||
character U+DCxx on decoding, and these are translated into the
|
||||
original bytes on encoding.
|
||||
"""
|
||||
mystring = exc.object[exc.start:exc.end]
|
||||
mystring = exc.object[exc.start : exc.end]
|
||||
|
||||
try:
|
||||
if isinstance(exc, UnicodeDecodeError):
|
||||
|
@ -75,9 +78,6 @@ def replace_surrogate_encode(mystring):
|
|||
"""
|
||||
decoded = []
|
||||
for ch in mystring:
|
||||
# if utils.PY3:
|
||||
# code = ch
|
||||
# else:
|
||||
code = ord(ch)
|
||||
|
||||
# The following magic comes from Py3.3's Python/codecs.c file:
|
||||
|
@ -114,16 +114,12 @@ def replace_surrogate_decode(mybytes):
|
|||
elif code <= 0x7F:
|
||||
decoded.append(_unichr(code))
|
||||
else:
|
||||
# # It may be a bad byte
|
||||
# # Try swallowing it.
|
||||
# continue
|
||||
# print("RAISE!")
|
||||
raise NotASurrogateError
|
||||
return str().join(decoded)
|
||||
|
||||
|
||||
def encodefilename(fn):
|
||||
if FS_ENCODING == 'ascii':
|
||||
if FS_ENCODING == "ascii":
|
||||
# ASCII encoder of Python 2 expects that the error handler returns a
|
||||
# Unicode string encodable to ASCII, whereas our surrogateescape error
|
||||
# handler has to return bytes in 0x80-0xFF range.
|
||||
|
@ -135,12 +131,12 @@ def encodefilename(fn):
|
|||
elif 0xDC80 <= code <= 0xDCFF:
|
||||
ch = bytes_chr(code - 0xDC00)
|
||||
else:
|
||||
raise UnicodeEncodeError(FS_ENCODING,
|
||||
fn, index, index+1,
|
||||
'ordinal not in range(128)')
|
||||
raise UnicodeEncodeError(
|
||||
FS_ENCODING, fn, index, index + 1, "ordinal not in range(128)"
|
||||
)
|
||||
encoded.append(ch)
|
||||
return bytes().join(encoded)
|
||||
elif FS_ENCODING == 'utf-8':
|
||||
elif FS_ENCODING == "utf-8":
|
||||
# UTF-8 encoder of Python 2 encodes surrogates, so U+DC80-U+DCFF
|
||||
# doesn't go through our error handler
|
||||
encoded = []
|
||||
|
@ -152,19 +148,22 @@ def encodefilename(fn):
|
|||
encoded.append(ch)
|
||||
else:
|
||||
raise UnicodeEncodeError(
|
||||
FS_ENCODING,
|
||||
fn, index, index+1, 'surrogates not allowed')
|
||||
FS_ENCODING, fn, index, index + 1, "surrogates not allowed"
|
||||
)
|
||||
else:
|
||||
ch_utf8 = ch.encode('utf-8')
|
||||
ch_utf8 = ch.encode("utf-8")
|
||||
encoded.append(ch_utf8)
|
||||
return bytes().join(encoded)
|
||||
else:
|
||||
return fn.encode(FS_ENCODING, FS_ERRORS)
|
||||
|
||||
|
||||
def decodefilename(fn):
|
||||
return fn.decode(FS_ENCODING, FS_ERRORS)
|
||||
|
||||
FS_ENCODING = 'ascii'; fn = b('[abc\xff]'); encoded = u('[abc\udcff]')
|
||||
|
||||
FS_ENCODING = sys.getfilesystemencoding()
|
||||
# FS_ENCODING = "ascii"; fn = b("[abc\xff]"); encoded = u("[abc\udcff]")
|
||||
# FS_ENCODING = 'cp932'; fn = b('[abc\x81\x00]'); encoded = u('[abc\udc81\x00]')
|
||||
# FS_ENCODING = 'UTF-8'; fn = b('[abc\xff]'); encoded = u('[abc\udcff]')
|
||||
|
||||
|
@ -178,21 +177,9 @@ def register_surrogateescape():
|
|||
"""
|
||||
Registers the surrogateescape error handler on Python 2 (only)
|
||||
"""
|
||||
if utils.PY3:
|
||||
if PY3:
|
||||
return
|
||||
try:
|
||||
codecs.lookup_error(FS_ERRORS)
|
||||
except LookupError:
|
||||
codecs.register_error(FS_ERRORS, surrogateescape_handler)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
pass
|
||||
# # Tests:
|
||||
# register_surrogateescape()
|
||||
|
||||
# b = decodefilename(fn)
|
||||
# assert b == encoded, "%r != %r" % (b, encoded)
|
||||
# c = encodefilename(b)
|
||||
# assert c == fn, '%r != %r' % (c, fn)
|
||||
# # print("ok")
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#!/usr/bin/env python
|
||||
# coding: utf-8
|
||||
from __future__ import print_function
|
||||
from __future__ import print_function, unicode_literals
|
||||
|
||||
import sys
|
||||
import time
|
||||
|
@ -31,8 +31,10 @@ class TcpSrv(object):
|
|||
try:
|
||||
s.connect(("10.255.255.255", 1))
|
||||
ip = s.getsockname()[0]
|
||||
except OSError:
|
||||
pass
|
||||
except (OSError, socket.error) as ex:
|
||||
if ex.errno != 101:
|
||||
raise
|
||||
|
||||
s.close()
|
||||
|
||||
self.log("root", "available @ http://{0}:{1}/".format(ip, self.args.p))
|
||||
|
@ -41,7 +43,7 @@ class TcpSrv(object):
|
|||
self.srv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
try:
|
||||
self.srv.bind((self.args.i, self.args.p))
|
||||
except OSError as ex:
|
||||
except (OSError, socket.error) as ex:
|
||||
if ex.errno != 98:
|
||||
raise
|
||||
|
||||
|
|
|
@ -1,10 +1,25 @@
|
|||
#!/usr/bin/env python
|
||||
# coding: utf-8
|
||||
from __future__ import print_function
|
||||
from __future__ import print_function, unicode_literals
|
||||
|
||||
import re
|
||||
import sys
|
||||
import hashlib
|
||||
|
||||
from .__init__ import PY2
|
||||
|
||||
if not PY2:
|
||||
from urllib.parse import unquote_to_bytes as unquote
|
||||
from urllib.parse import quote_from_bytes as quote
|
||||
else:
|
||||
from urllib import unquote # pylint: disable=no-name-in-module
|
||||
from urllib import quote
|
||||
|
||||
from .stolen import surrogateescape
|
||||
|
||||
surrogateescape.register_surrogateescape()
|
||||
FS_ENCODING = sys.getfilesystemencoding()
|
||||
|
||||
|
||||
class Unrecv(object):
|
||||
"""
|
||||
|
@ -103,18 +118,18 @@ class MultipartParser(object):
|
|||
# this breaks on firefox uploads that contain \"
|
||||
# since firefox escapes " but forgets to escape \
|
||||
# so it'll truncate after the \
|
||||
ret = u""
|
||||
ret = ""
|
||||
esc = False
|
||||
for ch in fn:
|
||||
if esc:
|
||||
if ch in [u'"', u"\\"]:
|
||||
ret += u'"'
|
||||
if ch in ['"', "\\"]:
|
||||
ret += '"'
|
||||
else:
|
||||
ret += esc + ch
|
||||
esc = False
|
||||
elif ch == u"\\":
|
||||
elif ch == "\\":
|
||||
esc = True
|
||||
elif ch == u'"':
|
||||
elif ch == '"':
|
||||
break
|
||||
else:
|
||||
ret += ch
|
||||
|
@ -204,7 +219,7 @@ class MultipartParser(object):
|
|||
# discard junk before the first boundary
|
||||
for junk in self._read_data():
|
||||
self.log(
|
||||
u"discarding preamble: [{}]".format(junk.decode("utf-8", "ignore"))
|
||||
"discarding preamble: [{}]".format(junk.decode("utf-8", "replace"))
|
||||
)
|
||||
|
||||
# nice, now make it fast
|
||||
|
@ -220,7 +235,7 @@ class MultipartParser(object):
|
|||
if p_field != field_name:
|
||||
raise Pebkac('expected field "{}", got "{}"'.format(field_name, p_field))
|
||||
|
||||
return self._read_value(p_data, max_len).decode("utf-8", "ignore")
|
||||
return self._read_value(p_data, max_len).decode("utf-8", "surrogateescape")
|
||||
|
||||
def drop(self):
|
||||
"""discards the remaining multipart body"""
|
||||
|
@ -261,29 +276,64 @@ def read_header(sr):
|
|||
|
||||
ret += buf
|
||||
|
||||
return ret[:-4].decode("utf-8", "replace").split("\r\n")
|
||||
return ret[:-4].decode("utf-8", "surrogateescape").split("\r\n")
|
||||
|
||||
|
||||
def undot(path):
|
||||
ret = []
|
||||
for node in path.split(u"/"):
|
||||
if node in [u"", u"."]:
|
||||
for node in path.split("/"):
|
||||
if node in ["", "."]:
|
||||
continue
|
||||
|
||||
if node == u"..":
|
||||
if node == "..":
|
||||
if ret:
|
||||
ret.pop()
|
||||
continue
|
||||
|
||||
ret.append(node)
|
||||
|
||||
return u"/".join(ret)
|
||||
return "/".join(ret)
|
||||
|
||||
|
||||
def sanitize_fn(fn):
|
||||
return fn.replace("\\", "/").split("/")[-1].strip()
|
||||
|
||||
|
||||
def quotep(txt):
|
||||
"""url quoter which deals with bytes correctly"""
|
||||
btxt = fsenc(txt)
|
||||
quot1 = quote(btxt, safe=b"/")
|
||||
if not PY2:
|
||||
quot1 = quot1.encode('ascii')
|
||||
|
||||
quot2 = quot1.replace(b" ", b"+")
|
||||
return fsdec(quot2)
|
||||
|
||||
|
||||
def unquotep(txt):
|
||||
"""url unquoter which deals with bytes correctly"""
|
||||
btxt = fsenc(txt)
|
||||
unq1 = btxt.replace(b"+", b" ")
|
||||
unq2 = unquote(unq1)
|
||||
return fsdec(unq2)
|
||||
|
||||
|
||||
def fsdec(txt):
|
||||
"""decodes filesystem-bytes to wtf8"""
|
||||
if PY2:
|
||||
return surrogateescape.decodefilename(txt)
|
||||
|
||||
return txt.decode(FS_ENCODING, "surrogateescape")
|
||||
|
||||
|
||||
def fsenc(txt):
|
||||
"""encodes wtf8 to filesystem-bytes"""
|
||||
if PY2:
|
||||
return surrogateescape.encodefilename(txt)
|
||||
|
||||
return txt.encode(FS_ENCODING, "surrogateescape")
|
||||
|
||||
|
||||
def hashcopy(actor, fin, fout):
|
||||
u32_lim = int((2 ** 31) * 0.9)
|
||||
hashobj = hashlib.sha512()
|
||||
|
@ -302,10 +352,10 @@ def hashcopy(actor, fin, fout):
|
|||
|
||||
def unescape_cookie(orig):
|
||||
# mw=idk; doot=qwe%2Crty%3Basd+fgh%2Bjkl%25zxc%26vbn # qwe,rty;asd fgh+jkl%zxc&vbn
|
||||
ret = u""
|
||||
esc = u""
|
||||
ret = ""
|
||||
esc = ""
|
||||
for ch in orig:
|
||||
if ch == u"%":
|
||||
if ch == "%":
|
||||
if len(esc) > 0:
|
||||
ret += esc
|
||||
esc = ch
|
||||
|
@ -317,7 +367,7 @@ def unescape_cookie(orig):
|
|||
ret += chr(int(esc[1:], 16))
|
||||
except:
|
||||
ret += esc
|
||||
esc = u""
|
||||
esc = ""
|
||||
|
||||
else:
|
||||
ret += ch
|
||||
|
|
|
@ -38,7 +38,9 @@ avg() { awk 'function pr(ncsz) {if (nsmp>0) {printf "%3s %s\n", csz, sum/nsmp} c
|
|||
##
|
||||
## bad filenames
|
||||
|
||||
echo hi > 'qwe,rty;asd fgh+jkl%zxc&vbn <qwe>"rty'"'"'uio&asd fgh'.html
|
||||
dirs=("$HOME/vfs/ほげ" "$HOME/vfs/ほげ/ぴよ" "$HOME/vfs/$(printf \\xed\\x91)" "$HOME/vfs/$(printf \\xed\\x91/\\xed\\x92)")
|
||||
mkdir -p "${dirs[@]}"
|
||||
for dir in "${dirs[@]}"; do for fn in ふが "$(printf \\xed\\x93)" 'qwe,rty;asd fgh+jkl%zxc&vbn <qwe>"rty'"'"'uio&asd fgh'; do echo "$dir" > "$dir/$fn.html"; done; done
|
||||
|
||||
|
||||
##
|
||||
|
|
1
setup.py
1
setup.py
|
@ -168,6 +168,7 @@ args = {
|
|||
"Programming Language :: Python :: 2",
|
||||
"Programming Language :: Python :: 2.7",
|
||||
"Programming Language :: Python :: 3",
|
||||
"Programming Language :: Python :: 3.2",
|
||||
"Programming Language :: Python :: 3.3",
|
||||
"Programming Language :: Python :: 3.4",
|
||||
"Programming Language :: Python :: 3.5",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#!/usr/bin/env python
|
||||
# coding: utf-8
|
||||
from __future__ import print_function
|
||||
from __future__ import print_function, unicode_literals
|
||||
|
||||
import os
|
||||
import json
|
||||
|
|
Loading…
Reference in a new issue