Browse Source

Bramify this... work around the stupidity that is BEP-52

in most cases, keys are us-ascii, but BEP-52 allows some
keys to be binary data..  It does appear that it only applies to
dictionaries that are under 'piece layer', so pass down the parent
key, and don't do encoding for the dictionary that is under it.

It also had a bug where if a dictionary key had code points >127,
it'd encode the values incorrectly, because it'd take the length of
the string and not the encoded string..
main
John-Mark Gurney 1 year ago
parent
commit
ed59121c5d
2 changed files with 31 additions and 6 deletions
  1. +31
    -6
      ui/medashare/btv/bencode.py
  2. BIN
      ui/medashare/btv/fixtures/bittorrent-v2-test.torrent

+ 31
- 6
ui/medashare/btv/bencode.py View File

@@ -68,16 +68,23 @@ def decode_list(x, f):
r.append(v)
return (r, f + 1)

def decode_dict(x, f):
def decode_dict(x, f, parent=None):
r, f = {}, f+1
lastkey = None
while x[f] != b'e'[0]:
k, f = decode_string(x, f)
k = k.decode('us-ascii')
if not parent or parent not in { 'piece layers' }:
k = k.decode('us-ascii')
if lastkey is not None and lastkey >= k:
raise ValueError
lastkey = k
r[k], f = decode_func[x[f]](x, f)

#decode value
fun = decode_func[x[f]]
kwargs = {}
if fun is decode_dict:
kwargs['parent'] = k
r[k], f = fun(x, f, **kwargs)
return (r, f + 1)

decode_func = {}
@@ -285,11 +292,18 @@ def encode_list(x,r):
encode_func[type(e)](e, r)
r.append(b'e')

def encode_dict(x,r):
def encode_dict(x,r, parent=None):
r.append(b'd')
for k,v in sorted(x.items()):
r.extend((b'%d:' % len(k),k.encode('UTF-8')))
encode_func[type(v)](v, r)
origk = k
if not parent or parent not in { 'piece layers' }:
k = k.encode('us-ascii')
r.extend((b'%d:' % len(k),k))
efun = encode_func[type(v)]
kwargs = {}
if efun is encode_dict:
kwargs['parent'] = origk
efun(v, r, **kwargs)
r.append(b'e')

encode_func = {}
@@ -341,6 +355,17 @@ class _TestCases(unittest.TestCase):

self.assertEqual(bencode({'': 5}), b'd0:i5ee')

def test_round_trip_files(self):
import importlib

fixtures = importlib.resources.files(__name__[:__name__.rindex('.')]) / 'fixtures'

for i in fixtures.iterdir():
with self.subTest(file=str(i)):
data = i.read_bytes()

self.assertEqual(data, bencode(bdecode(data)))

def test_bdecode(self):
test_bdecode()



BIN
ui/medashare/btv/fixtures/bittorrent-v2-test.torrent View File


Loading…
Cancel
Save