130 lines
3.2 KiB
Python
130 lines
3.2 KiB
Python
|
# The contents of this file are subject to the BitTorrent Open Source License
|
||
|
# Version 1.1 (the License). You may not copy or use this file, in either
|
||
|
# source code or executable form, except in compliance with the License. You
|
||
|
# may obtain a copy of the License at http://www.bittorrent.com/license/.
|
||
|
#
|
||
|
# Software distributed under the License is distributed on an AS IS basis,
|
||
|
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||
|
# for the specific language governing rights and limitations under the
|
||
|
# License.
|
||
|
|
||
|
# Written by Petru Paler
|
||
|
|
||
|
class BTFailure(Exception):
|
||
|
pass
|
||
|
|
||
|
def decode_int(x, f):
|
||
|
f += 1
|
||
|
newf = x.index('e', f)
|
||
|
n = int(x[f:newf])
|
||
|
if x[f] == '-':
|
||
|
if x[f + 1] == '0':
|
||
|
raise ValueError
|
||
|
elif x[f] == '0' and newf != f+1:
|
||
|
raise ValueError
|
||
|
return (n, newf+1)
|
||
|
|
||
|
def decode_string(x, f):
|
||
|
colon = x.index(':', f)
|
||
|
n = int(x[f:colon])
|
||
|
if x[f] == '0' and colon != f+1:
|
||
|
raise ValueError
|
||
|
colon += 1
|
||
|
return (x[colon:colon+n], colon+n)
|
||
|
|
||
|
def decode_list(x, f):
|
||
|
r, f = [], f+1
|
||
|
while x[f] != 'e':
|
||
|
v, f = decode_func[x[f]](x, f)
|
||
|
r.append(v)
|
||
|
return (r, f + 1)
|
||
|
|
||
|
def decode_dict(x, f):
|
||
|
r, f = {}, f+1
|
||
|
while x[f] != 'e':
|
||
|
k, f = decode_string(x, f)
|
||
|
r[k], f = decode_func[x[f]](x, f)
|
||
|
return (r, f + 1)
|
||
|
|
||
|
decode_func = {}
|
||
|
decode_func['l'] = decode_list
|
||
|
decode_func['d'] = decode_dict
|
||
|
decode_func['i'] = decode_int
|
||
|
decode_func['0'] = decode_string
|
||
|
decode_func['1'] = decode_string
|
||
|
decode_func['2'] = decode_string
|
||
|
decode_func['3'] = decode_string
|
||
|
decode_func['4'] = decode_string
|
||
|
decode_func['5'] = decode_string
|
||
|
decode_func['6'] = decode_string
|
||
|
decode_func['7'] = decode_string
|
||
|
decode_func['8'] = decode_string
|
||
|
decode_func['9'] = decode_string
|
||
|
|
||
|
def bdecode(x):
|
||
|
try:
|
||
|
r, l = decode_func[x[0]](x, 0)
|
||
|
except (IndexError, KeyError, ValueError):
|
||
|
raise BTFailure("not a valid bencoded string")
|
||
|
if l != len(x):
|
||
|
raise BTFailure("invalid bencoded value (data after valid prefix)")
|
||
|
return r
|
||
|
|
||
|
from types import StringType, IntType, LongType, DictType, ListType, TupleType
|
||
|
|
||
|
|
||
|
class Bencached(object):
|
||
|
__slots__ = ['bencoded']
|
||
|
def __init__(self, s):
|
||
|
self.bencoded = s
|
||
|
|
||
|
def encode_bencached(x,r):
|
||
|
r.append(x.bencoded)
|
||
|
|
||
|
def encode_int(x, r):
|
||
|
r.extend(('i', str(x), 'e'))
|
||
|
|
||
|
def encode_bool(x, r):
|
||
|
if x:
|
||
|
encode_int(1, r)
|
||
|
else:
|
||
|
encode_int(0, r)
|
||
|
|
||
|
def encode_string(x, r):
|
||
|
r.extend((str(len(x)), ':', x))
|
||
|
|
||
|
def encode_list(x, r):
|
||
|
r.append('l')
|
||
|
for i in x:
|
||
|
encode_func[type(i)](i, r)
|
||
|
r.append('e')
|
||
|
|
||
|
def encode_dict(x,r):
|
||
|
r.append('d')
|
||
|
ilist = x.items()
|
||
|
ilist.sort()
|
||
|
for k, v in ilist:
|
||
|
r.extend((str(len(k)), ':', k))
|
||
|
encode_func[type(v)](v, r)
|
||
|
r.append('e')
|
||
|
|
||
|
encode_func = {}
|
||
|
encode_func[Bencached] = encode_bencached
|
||
|
encode_func[IntType] = encode_int
|
||
|
encode_func[LongType] = encode_int
|
||
|
encode_func[StringType] = encode_string
|
||
|
encode_func[ListType] = encode_list
|
||
|
encode_func[TupleType] = encode_list
|
||
|
encode_func[DictType] = encode_dict
|
||
|
|
||
|
try:
|
||
|
from types import BooleanType
|
||
|
encode_func[BooleanType] = encode_bool
|
||
|
except ImportError:
|
||
|
pass
|
||
|
|
||
|
def bencode(x):
|
||
|
r = []
|
||
|
encode_func[type(x)](x, r)
|
||
|
return ''.join(r)
|