murder 0.0.0.pre

Sign up to get free protection for your applications and to get access to all the features.
Files changed (65) hide show
  1. data/.gitignore +1 -0
  2. data/LICENSE +17 -0
  3. data/README +224 -0
  4. data/Rakefile +52 -0
  5. data/VERSION +1 -0
  6. data/dist/BitTornado/BT1/Choker.py +128 -0
  7. data/dist/BitTornado/BT1/Connecter.py +288 -0
  8. data/dist/BitTornado/BT1/Downloader.py +594 -0
  9. data/dist/BitTornado/BT1/DownloaderFeedback.py +155 -0
  10. data/dist/BitTornado/BT1/Encrypter.py +333 -0
  11. data/dist/BitTornado/BT1/FileSelector.py +245 -0
  12. data/dist/BitTornado/BT1/Filter.py +12 -0
  13. data/dist/BitTornado/BT1/HTTPDownloader.py +251 -0
  14. data/dist/BitTornado/BT1/NatCheck.py +95 -0
  15. data/dist/BitTornado/BT1/PiecePicker.py +320 -0
  16. data/dist/BitTornado/BT1/Rerequester.py +426 -0
  17. data/dist/BitTornado/BT1/Statistics.py +177 -0
  18. data/dist/BitTornado/BT1/Storage.py +584 -0
  19. data/dist/BitTornado/BT1/StorageWrapper.py +1045 -0
  20. data/dist/BitTornado/BT1/StreamCheck.py +135 -0
  21. data/dist/BitTornado/BT1/T2T.py +193 -0
  22. data/dist/BitTornado/BT1/Uploader.py +145 -0
  23. data/dist/BitTornado/BT1/__init__.py +1 -0
  24. data/dist/BitTornado/BT1/btformats.py +100 -0
  25. data/dist/BitTornado/BT1/fakeopen.py +89 -0
  26. data/dist/BitTornado/BT1/makemetafile.py +263 -0
  27. data/dist/BitTornado/BT1/track.py +1067 -0
  28. data/dist/BitTornado/ConfigDir.py +401 -0
  29. data/dist/BitTornado/ConfigReader.py +1068 -0
  30. data/dist/BitTornado/ConnChoice.py +31 -0
  31. data/dist/BitTornado/CreateIcons.py +105 -0
  32. data/dist/BitTornado/CurrentRateMeasure.py +37 -0
  33. data/dist/BitTornado/HTTPHandler.py +167 -0
  34. data/dist/BitTornado/PSYCO.py +5 -0
  35. data/dist/BitTornado/RateLimiter.py +153 -0
  36. data/dist/BitTornado/RateMeasure.py +75 -0
  37. data/dist/BitTornado/RawServer.py +195 -0
  38. data/dist/BitTornado/ServerPortHandler.py +188 -0
  39. data/dist/BitTornado/SocketHandler.py +375 -0
  40. data/dist/BitTornado/__init__.py +63 -0
  41. data/dist/BitTornado/bencode.py +319 -0
  42. data/dist/BitTornado/bitfield.py +162 -0
  43. data/dist/BitTornado/clock.py +27 -0
  44. data/dist/BitTornado/download_bt1.py +882 -0
  45. data/dist/BitTornado/inifile.py +169 -0
  46. data/dist/BitTornado/iprangeparse.py +194 -0
  47. data/dist/BitTornado/launchmanycore.py +381 -0
  48. data/dist/BitTornado/natpunch.py +254 -0
  49. data/dist/BitTornado/parseargs.py +137 -0
  50. data/dist/BitTornado/parsedir.py +150 -0
  51. data/dist/BitTornado/piecebuffer.py +86 -0
  52. data/dist/BitTornado/selectpoll.py +109 -0
  53. data/dist/BitTornado/subnetparse.py +218 -0
  54. data/dist/BitTornado/torrentlistparse.py +38 -0
  55. data/dist/BitTornado/zurllib.py +100 -0
  56. data/dist/murder_client.py +291 -0
  57. data/dist/murder_make_torrent.py +46 -0
  58. data/dist/murder_tracker.py +28 -0
  59. data/doc/examples/Capfile +28 -0
  60. data/lib/capistrano/recipes/deploy/strategy/murder.rb +52 -0
  61. data/lib/murder.rb +43 -0
  62. data/lib/murder/admin.rb +47 -0
  63. data/lib/murder/murder.rb +121 -0
  64. data/murder.gemspec +101 -0
  65. metadata +129 -0
@@ -0,0 +1,375 @@
1
+ # Written by Bram Cohen
2
+ # see LICENSE.txt for license information
3
+
4
+ import socket
5
+ from errno import EWOULDBLOCK, ECONNREFUSED, EHOSTUNREACH
6
+ try:
7
+ from select import poll, error, POLLIN, POLLOUT, POLLERR, POLLHUP
8
+ timemult = 1000
9
+ except ImportError:
10
+ from selectpoll import poll, error, POLLIN, POLLOUT, POLLERR, POLLHUP
11
+ timemult = 1
12
+ from time import sleep
13
+ from clock import clock
14
+ import sys
15
+ from random import shuffle, randrange
16
+ from natpunch import UPnP_open_port, UPnP_close_port
17
+ # from BT1.StreamCheck import StreamCheck
18
+ # import inspect
19
+ try:
20
+ True
21
+ except:
22
+ True = 1
23
+ False = 0
24
+
25
+ all = POLLIN | POLLOUT
26
+
27
+ UPnP_ERROR = "unable to forward port via UPnP"
28
+
29
+ class SingleSocket:
30
+ def __init__(self, socket_handler, sock, handler, ip = None):
31
+ self.socket_handler = socket_handler
32
+ self.socket = sock
33
+ self.handler = handler
34
+ self.buffer = []
35
+ self.last_hit = clock()
36
+ self.fileno = sock.fileno()
37
+ self.connected = False
38
+ self.skipped = 0
39
+ # self.check = StreamCheck()
40
+ try:
41
+ self.ip = self.socket.getpeername()[0]
42
+ except:
43
+ if ip is None:
44
+ self.ip = 'unknown'
45
+ else:
46
+ self.ip = ip
47
+
48
+ def get_ip(self, real=False):
49
+ if real:
50
+ try:
51
+ self.ip = self.socket.getpeername()[0]
52
+ except:
53
+ pass
54
+ return self.ip
55
+
56
+ def close(self):
57
+ '''
58
+ for x in xrange(5,0,-1):
59
+ try:
60
+ f = inspect.currentframe(x).f_code
61
+ print (f.co_filename,f.co_firstlineno,f.co_name)
62
+ del f
63
+ except:
64
+ pass
65
+ print ''
66
+ '''
67
+ assert self.socket
68
+ self.connected = False
69
+ sock = self.socket
70
+ self.socket = None
71
+ self.buffer = []
72
+ del self.socket_handler.single_sockets[self.fileno]
73
+ self.socket_handler.poll.unregister(sock)
74
+ sock.close()
75
+
76
+ def shutdown(self, val):
77
+ self.socket.shutdown(val)
78
+
79
+ def is_flushed(self):
80
+ return not self.buffer
81
+
82
+ def write(self, s):
83
+ # self.check.write(s)
84
+ assert self.socket is not None
85
+ self.buffer.append(s)
86
+ if len(self.buffer) == 1:
87
+ self.try_write()
88
+
89
+ def try_write(self):
90
+ if self.connected:
91
+ dead = False
92
+ try:
93
+ while self.buffer:
94
+ buf = self.buffer[0]
95
+ amount = self.socket.send(buf)
96
+ if amount == 0:
97
+ self.skipped += 1
98
+ break
99
+ self.skipped = 0
100
+ if amount != len(buf):
101
+ self.buffer[0] = buf[amount:]
102
+ break
103
+ del self.buffer[0]
104
+ except socket.error, e:
105
+ try:
106
+ dead = e[0] != EWOULDBLOCK
107
+ except:
108
+ dead = True
109
+ self.skipped += 1
110
+ if self.skipped >= 3:
111
+ dead = True
112
+ if dead:
113
+ self.socket_handler.dead_from_write.append(self)
114
+ return
115
+ if self.buffer:
116
+ self.socket_handler.poll.register(self.socket, all)
117
+ else:
118
+ self.socket_handler.poll.register(self.socket, POLLIN)
119
+
120
+ def set_handler(self, handler):
121
+ self.handler = handler
122
+
123
+ class SocketHandler:
124
+ def __init__(self, timeout, ipv6_enable, readsize = 100000):
125
+ self.timeout = timeout
126
+ self.ipv6_enable = ipv6_enable
127
+ self.readsize = readsize
128
+ self.poll = poll()
129
+ # {socket: SingleSocket}
130
+ self.single_sockets = {}
131
+ self.dead_from_write = []
132
+ self.max_connects = 1000
133
+ self.port_forwarded = None
134
+ self.servers = {}
135
+
136
+ def scan_for_timeouts(self):
137
+ t = clock() - self.timeout
138
+ tokill = []
139
+ for s in self.single_sockets.values():
140
+ if s.last_hit < t:
141
+ tokill.append(s)
142
+ for k in tokill:
143
+ if k.socket is not None:
144
+ self._close_socket(k)
145
+
146
+ def bind(self, port, bind = '', reuse = False, ipv6_socket_style = 1, upnp = 0):
147
+ port = int(port)
148
+ addrinfos = []
149
+ self.servers = {}
150
+ self.interfaces = []
151
+ # if bind != "" thread it as a comma seperated list and bind to all
152
+ # addresses (can be ips or hostnames) else bind to default ipv6 and
153
+ # ipv4 address
154
+ if bind:
155
+ if self.ipv6_enable:
156
+ socktype = socket.AF_UNSPEC
157
+ else:
158
+ socktype = socket.AF_INET
159
+ bind = bind.split(',')
160
+ for addr in bind:
161
+ if sys.version_info < (2,2):
162
+ addrinfos.append((socket.AF_INET, None, None, None, (addr, port)))
163
+ else:
164
+ addrinfos.extend(socket.getaddrinfo(addr, port,
165
+ socktype, socket.SOCK_STREAM))
166
+ else:
167
+ if self.ipv6_enable:
168
+ addrinfos.append([socket.AF_INET6, None, None, None, ('', port)])
169
+ if not addrinfos or ipv6_socket_style != 0:
170
+ addrinfos.append([socket.AF_INET, None, None, None, ('', port)])
171
+ for addrinfo in addrinfos:
172
+ try:
173
+ server = socket.socket(addrinfo[0], socket.SOCK_STREAM)
174
+ if reuse:
175
+ server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
176
+ server.setblocking(0)
177
+ server.bind(addrinfo[4])
178
+ self.servers[server.fileno()] = server
179
+ if bind:
180
+ self.interfaces.append(server.getsockname()[0])
181
+ server.listen(64)
182
+ self.poll.register(server, POLLIN)
183
+ except socket.error, e:
184
+ for server in self.servers.values():
185
+ try:
186
+ server.close()
187
+ except:
188
+ pass
189
+ if self.ipv6_enable and ipv6_socket_style == 0 and self.servers:
190
+ raise socket.error('blocked port (may require ipv6_binds_v4 to be set)')
191
+ raise socket.error(str(e))
192
+ if not self.servers:
193
+ raise socket.error('unable to open server port')
194
+ if upnp:
195
+ if not UPnP_open_port(port):
196
+ for server in self.servers.values():
197
+ try:
198
+ server.close()
199
+ except:
200
+ pass
201
+ self.servers = None
202
+ self.interfaces = None
203
+ raise socket.error(UPnP_ERROR)
204
+ self.port_forwarded = port
205
+ self.port = port
206
+
207
+ def find_and_bind(self, minport, maxport, bind = '', reuse = False,
208
+ ipv6_socket_style = 1, upnp = 0, randomizer = False):
209
+ e = 'maxport less than minport - no ports to check'
210
+ if maxport-minport < 50 or not randomizer:
211
+ portrange = range(minport, maxport+1)
212
+ if randomizer:
213
+ shuffle(portrange)
214
+ portrange = portrange[:20] # check a maximum of 20 ports
215
+ else:
216
+ portrange = []
217
+ while len(portrange) < 20:
218
+ listen_port = randrange(minport, maxport+1)
219
+ if not listen_port in portrange:
220
+ portrange.append(listen_port)
221
+ for listen_port in portrange:
222
+ try:
223
+ self.bind(listen_port, bind,
224
+ ipv6_socket_style = ipv6_socket_style, upnp = upnp)
225
+ return listen_port
226
+ except socket.error, e:
227
+ pass
228
+ raise socket.error(str(e))
229
+
230
+
231
+ def set_handler(self, handler):
232
+ self.handler = handler
233
+
234
+
235
+ def start_connection_raw(self, dns, socktype = socket.AF_INET, handler = None):
236
+ if handler is None:
237
+ handler = self.handler
238
+ sock = socket.socket(socktype, socket.SOCK_STREAM)
239
+ sock.setblocking(0)
240
+ try:
241
+ sock.connect_ex(dns)
242
+ except socket.error:
243
+ raise
244
+ except Exception, e:
245
+ raise socket.error(str(e))
246
+ self.poll.register(sock, POLLIN)
247
+ s = SingleSocket(self, sock, handler, dns[0])
248
+ self.single_sockets[sock.fileno()] = s
249
+ return s
250
+
251
+
252
+ def start_connection(self, dns, handler = None, randomize = False):
253
+ if handler is None:
254
+ handler = self.handler
255
+ if sys.version_info < (2,2):
256
+ s = self.start_connection_raw(dns,socket.AF_INET,handler)
257
+ else:
258
+ if self.ipv6_enable:
259
+ socktype = socket.AF_UNSPEC
260
+ else:
261
+ socktype = socket.AF_INET
262
+ try:
263
+ addrinfos = socket.getaddrinfo(dns[0], int(dns[1]),
264
+ socktype, socket.SOCK_STREAM)
265
+ except socket.error, e:
266
+ raise
267
+ except Exception, e:
268
+ raise socket.error(str(e))
269
+ if randomize:
270
+ shuffle(addrinfos)
271
+ for addrinfo in addrinfos:
272
+ try:
273
+ s = self.start_connection_raw(addrinfo[4],addrinfo[0],handler)
274
+ break
275
+ except:
276
+ pass
277
+ else:
278
+ raise socket.error('unable to connect')
279
+ return s
280
+
281
+
282
+ def _sleep(self):
283
+ sleep(1)
284
+
285
+ def handle_events(self, events):
286
+ for sock, event in events:
287
+ s = self.servers.get(sock)
288
+ if s:
289
+ if event & (POLLHUP | POLLERR) != 0:
290
+ self.poll.unregister(s)
291
+ s.close()
292
+ del self.servers[sock]
293
+ print "lost server socket"
294
+ elif len(self.single_sockets) < self.max_connects:
295
+ try:
296
+ newsock, addr = s.accept()
297
+ newsock.setblocking(0)
298
+ nss = SingleSocket(self, newsock, self.handler)
299
+ self.single_sockets[newsock.fileno()] = nss
300
+ self.poll.register(newsock, POLLIN)
301
+ self.handler.external_connection_made(nss)
302
+ except socket.error:
303
+ self._sleep()
304
+ else:
305
+ s = self.single_sockets.get(sock)
306
+ if not s:
307
+ continue
308
+ s.connected = True
309
+ if (event & (POLLHUP | POLLERR)):
310
+ self._close_socket(s)
311
+ continue
312
+ if (event & POLLIN):
313
+ try:
314
+ s.last_hit = clock()
315
+ data = s.socket.recv(100000)
316
+ if not data:
317
+ self._close_socket(s)
318
+ else:
319
+ s.handler.data_came_in(s, data)
320
+ except socket.error, e:
321
+ code, msg = e
322
+ if code != EWOULDBLOCK:
323
+ self._close_socket(s)
324
+ continue
325
+ if (event & POLLOUT) and s.socket and not s.is_flushed():
326
+ s.try_write()
327
+ if s.is_flushed():
328
+ s.handler.connection_flushed(s)
329
+
330
+ def close_dead(self):
331
+ while self.dead_from_write:
332
+ old = self.dead_from_write
333
+ self.dead_from_write = []
334
+ for s in old:
335
+ if s.socket:
336
+ self._close_socket(s)
337
+
338
+ def _close_socket(self, s):
339
+ s.close()
340
+ s.handler.connection_lost(s)
341
+
342
+ def do_poll(self, t):
343
+ r = self.poll.poll(t*timemult)
344
+ if r is None:
345
+ connects = len(self.single_sockets)
346
+ to_close = int(connects*0.05)+1 # close 5% of sockets
347
+ self.max_connects = connects-to_close
348
+ closelist = self.single_sockets.values()
349
+ shuffle(closelist)
350
+ closelist = closelist[:to_close]
351
+ for sock in closelist:
352
+ self._close_socket(sock)
353
+ return []
354
+ return r
355
+
356
+ def get_stats(self):
357
+ return { 'interfaces': self.interfaces,
358
+ 'port': self.port,
359
+ 'upnp': self.port_forwarded is not None }
360
+
361
+
362
+ def shutdown(self):
363
+ for ss in self.single_sockets.values():
364
+ try:
365
+ ss.close()
366
+ except:
367
+ pass
368
+ for server in self.servers.values():
369
+ try:
370
+ server.close()
371
+ except:
372
+ pass
373
+ if self.port_forwarded is not None:
374
+ UPnP_close_port(self.port_forwarded)
375
+
@@ -0,0 +1,63 @@
1
+ product_name = 'BitTornado'
2
+ version_short = 'T-0.3.17'
3
+
4
+ version = version_short+' ('+product_name+')'
5
+ report_email = version_short+'@degreez.net'
6
+
7
+ from types import StringType
8
+ from sha import sha
9
+ from time import time, clock
10
+ try:
11
+ from os import getpid
12
+ except ImportError:
13
+ def getpid():
14
+ return 1
15
+
16
+ mapbase64 = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz.-'
17
+
18
+ _idprefix = version_short[0]
19
+ for subver in version_short[2:].split('.'):
20
+ try:
21
+ subver = int(subver)
22
+ except:
23
+ subver = 0
24
+ _idprefix += mapbase64[subver]
25
+ _idprefix += ('-' * (6-len(_idprefix)))
26
+ _idrandom = [None]
27
+
28
+ def resetPeerIDs():
29
+ try:
30
+ f = open('/dev/urandom','rb')
31
+ x = f.read(20)
32
+ f.close()
33
+ except:
34
+ x = ''
35
+
36
+ l1 = 0
37
+ t = clock()
38
+ while t == clock():
39
+ l1 += 1
40
+ l2 = 0
41
+ t = long(time()*100)
42
+ while t == long(time()*100):
43
+ l2 += 1
44
+ l3 = 0
45
+ if l2 < 1000:
46
+ t = long(time()*10)
47
+ while t == long(clock()*10):
48
+ l3 += 1
49
+ x += ( repr(time()) + '/' + str(time()) + '/'
50
+ + str(l1) + '/' + str(l2) + '/' + str(l3) + '/'
51
+ + str(getpid()) )
52
+
53
+ s = ''
54
+ for i in sha(x).digest()[-11:]:
55
+ s += mapbase64[ord(i) & 0x3F]
56
+ _idrandom[0] = s
57
+
58
+ resetPeerIDs()
59
+
60
+ def createPeerID(ins = '---'):
61
+ assert type(ins) is StringType
62
+ assert len(ins) == 3
63
+ return _idprefix + ins + _idrandom[0]
@@ -0,0 +1,319 @@
1
+ # Written by Petru Paler, Uoti Urpala, Ross Cohen and John Hoffman
2
+ # see LICENSE.txt for license information
3
+
4
+ from types import IntType, LongType, StringType, ListType, TupleType, DictType
5
+ try:
6
+ from types import BooleanType
7
+ except ImportError:
8
+ BooleanType = None
9
+ try:
10
+ from types import UnicodeType
11
+ except ImportError:
12
+ UnicodeType = None
13
+ from cStringIO import StringIO
14
+
15
+ def decode_int(x, f):
16
+ f += 1
17
+ newf = x.index('e', f)
18
+ try:
19
+ n = int(x[f:newf])
20
+ except:
21
+ n = long(x[f:newf])
22
+ if x[f] == '-':
23
+ if x[f + 1] == '0':
24
+ raise ValueError
25
+ elif x[f] == '0' and newf != f+1:
26
+ raise ValueError
27
+ return (n, newf+1)
28
+
29
+ def decode_string(x, f):
30
+ colon = x.index(':', f)
31
+ try:
32
+ n = int(x[f:colon])
33
+ except (OverflowError, ValueError):
34
+ n = long(x[f:colon])
35
+ if x[f] == '0' and colon != f+1:
36
+ raise ValueError
37
+ colon += 1
38
+ return (x[colon:colon+n], colon+n)
39
+
40
+ def decode_unicode(x, f):
41
+ s, f = decode_string(x, f+1)
42
+ return (s.decode('UTF-8'),f)
43
+
44
+ def decode_list(x, f):
45
+ r, f = [], f+1
46
+ while x[f] != 'e':
47
+ v, f = decode_func[x[f]](x, f)
48
+ r.append(v)
49
+ return (r, f + 1)
50
+
51
+ def decode_dict(x, f):
52
+ r, f = {}, f+1
53
+ lastkey = None
54
+ while x[f] != 'e':
55
+ k, f = decode_string(x, f)
56
+ if lastkey >= k:
57
+ raise ValueError
58
+ lastkey = k
59
+ r[k], f = decode_func[x[f]](x, f)
60
+ return (r, f + 1)
61
+
62
+ decode_func = {}
63
+ decode_func['l'] = decode_list
64
+ decode_func['d'] = decode_dict
65
+ decode_func['i'] = decode_int
66
+ decode_func['0'] = decode_string
67
+ decode_func['1'] = decode_string
68
+ decode_func['2'] = decode_string
69
+ decode_func['3'] = decode_string
70
+ decode_func['4'] = decode_string
71
+ decode_func['5'] = decode_string
72
+ decode_func['6'] = decode_string
73
+ decode_func['7'] = decode_string
74
+ decode_func['8'] = decode_string
75
+ decode_func['9'] = decode_string
76
+ #decode_func['u'] = decode_unicode
77
+
78
+ def bdecode(x, sloppy = 0):
79
+ try:
80
+ r, l = decode_func[x[0]](x, 0)
81
+ # except (IndexError, KeyError):
82
+ except (IndexError, KeyError, ValueError):
83
+ raise ValueError, "bad bencoded data"
84
+ if not sloppy and l != len(x):
85
+ raise ValueError, "bad bencoded data"
86
+ return r
87
+
88
+ def test_bdecode():
89
+ try:
90
+ bdecode('0:0:')
91
+ assert 0
92
+ except ValueError:
93
+ pass
94
+ try:
95
+ bdecode('ie')
96
+ assert 0
97
+ except ValueError:
98
+ pass
99
+ try:
100
+ bdecode('i341foo382e')
101
+ assert 0
102
+ except ValueError:
103
+ pass
104
+ assert bdecode('i4e') == 4L
105
+ assert bdecode('i0e') == 0L
106
+ assert bdecode('i123456789e') == 123456789L
107
+ assert bdecode('i-10e') == -10L
108
+ try:
109
+ bdecode('i-0e')
110
+ assert 0
111
+ except ValueError:
112
+ pass
113
+ try:
114
+ bdecode('i123')
115
+ assert 0
116
+ except ValueError:
117
+ pass
118
+ try:
119
+ bdecode('')
120
+ assert 0
121
+ except ValueError:
122
+ pass
123
+ try:
124
+ bdecode('i6easd')
125
+ assert 0
126
+ except ValueError:
127
+ pass
128
+ try:
129
+ bdecode('35208734823ljdahflajhdf')
130
+ assert 0
131
+ except ValueError:
132
+ pass
133
+ try:
134
+ bdecode('2:abfdjslhfld')
135
+ assert 0
136
+ except ValueError:
137
+ pass
138
+ assert bdecode('0:') == ''
139
+ assert bdecode('3:abc') == 'abc'
140
+ assert bdecode('10:1234567890') == '1234567890'
141
+ try:
142
+ bdecode('02:xy')
143
+ assert 0
144
+ except ValueError:
145
+ pass
146
+ try:
147
+ bdecode('l')
148
+ assert 0
149
+ except ValueError:
150
+ pass
151
+ assert bdecode('le') == []
152
+ try:
153
+ bdecode('leanfdldjfh')
154
+ assert 0
155
+ except ValueError:
156
+ pass
157
+ assert bdecode('l0:0:0:e') == ['', '', '']
158
+ try:
159
+ bdecode('relwjhrlewjh')
160
+ assert 0
161
+ except ValueError:
162
+ pass
163
+ assert bdecode('li1ei2ei3ee') == [1, 2, 3]
164
+ assert bdecode('l3:asd2:xye') == ['asd', 'xy']
165
+ assert bdecode('ll5:Alice3:Bobeli2ei3eee') == [['Alice', 'Bob'], [2, 3]]
166
+ try:
167
+ bdecode('d')
168
+ assert 0
169
+ except ValueError:
170
+ pass
171
+ try:
172
+ bdecode('defoobar')
173
+ assert 0
174
+ except ValueError:
175
+ pass
176
+ assert bdecode('de') == {}
177
+ assert bdecode('d3:agei25e4:eyes4:bluee') == {'age': 25, 'eyes': 'blue'}
178
+ assert bdecode('d8:spam.mp3d6:author5:Alice6:lengthi100000eee') == {'spam.mp3': {'author': 'Alice', 'length': 100000}}
179
+ try:
180
+ bdecode('d3:fooe')
181
+ assert 0
182
+ except ValueError:
183
+ pass
184
+ try:
185
+ bdecode('di1e0:e')
186
+ assert 0
187
+ except ValueError:
188
+ pass
189
+ try:
190
+ bdecode('d1:b0:1:a0:e')
191
+ assert 0
192
+ except ValueError:
193
+ pass
194
+ try:
195
+ bdecode('d1:a0:1:a0:e')
196
+ assert 0
197
+ except ValueError:
198
+ pass
199
+ try:
200
+ bdecode('i03e')
201
+ assert 0
202
+ except ValueError:
203
+ pass
204
+ try:
205
+ bdecode('l01:ae')
206
+ assert 0
207
+ except ValueError:
208
+ pass
209
+ try:
210
+ bdecode('9999:x')
211
+ assert 0
212
+ except ValueError:
213
+ pass
214
+ try:
215
+ bdecode('l0:')
216
+ assert 0
217
+ except ValueError:
218
+ pass
219
+ try:
220
+ bdecode('d0:0:')
221
+ assert 0
222
+ except ValueError:
223
+ pass
224
+ try:
225
+ bdecode('d0:')
226
+ assert 0
227
+ except ValueError:
228
+ pass
229
+
230
+ bencached_marker = []
231
+
232
+ class Bencached:
233
+ def __init__(self, s):
234
+ self.marker = bencached_marker
235
+ self.bencoded = s
236
+
237
+ BencachedType = type(Bencached('')) # insufficient, but good as a filter
238
+
239
+ def encode_bencached(x,r):
240
+ assert x.marker == bencached_marker
241
+ r.append(x.bencoded)
242
+
243
+ def encode_int(x,r):
244
+ r.extend(('i',str(x),'e'))
245
+
246
+ def encode_bool(x,r):
247
+ encode_int(int(x),r)
248
+
249
+ def encode_string(x,r):
250
+ r.extend((str(len(x)),':',x))
251
+
252
+ def encode_unicode(x,r):
253
+ #r.append('u')
254
+ encode_string(x.encode('UTF-8'),r)
255
+
256
+ def encode_list(x,r):
257
+ r.append('l')
258
+ for e in x:
259
+ encode_func[type(e)](e, r)
260
+ r.append('e')
261
+
262
+ def encode_dict(x,r):
263
+ r.append('d')
264
+ ilist = x.items()
265
+ ilist.sort()
266
+ for k,v in ilist:
267
+ r.extend((str(len(k)),':',k))
268
+ encode_func[type(v)](v, r)
269
+ r.append('e')
270
+
271
+ encode_func = {}
272
+ encode_func[BencachedType] = encode_bencached
273
+ encode_func[IntType] = encode_int
274
+ encode_func[LongType] = encode_int
275
+ encode_func[StringType] = encode_string
276
+ encode_func[ListType] = encode_list
277
+ encode_func[TupleType] = encode_list
278
+ encode_func[DictType] = encode_dict
279
+ if BooleanType:
280
+ encode_func[BooleanType] = encode_bool
281
+ if UnicodeType:
282
+ encode_func[UnicodeType] = encode_unicode
283
+
284
+ def bencode(x):
285
+ r = []
286
+ try:
287
+ encode_func[type(x)](x, r)
288
+ except:
289
+ print "*** error *** could not encode type %s (value: %s)" % (type(x), x)
290
+ assert 0
291
+ return ''.join(r)
292
+
293
+ def test_bencode():
294
+ assert bencode(4) == 'i4e'
295
+ assert bencode(0) == 'i0e'
296
+ assert bencode(-10) == 'i-10e'
297
+ assert bencode(12345678901234567890L) == 'i12345678901234567890e'
298
+ assert bencode('') == '0:'
299
+ assert bencode('abc') == '3:abc'
300
+ assert bencode('1234567890') == '10:1234567890'
301
+ assert bencode([]) == 'le'
302
+ assert bencode([1, 2, 3]) == 'li1ei2ei3ee'
303
+ assert bencode([['Alice', 'Bob'], [2, 3]]) == 'll5:Alice3:Bobeli2ei3eee'
304
+ assert bencode({}) == 'de'
305
+ assert bencode({'age': 25, 'eyes': 'blue'}) == 'd3:agei25e4:eyes4:bluee'
306
+ assert bencode({'spam.mp3': {'author': 'Alice', 'length': 100000}}) == 'd8:spam.mp3d6:author5:Alice6:lengthi100000eee'
307
+ try:
308
+ bencode({1: 'foo'})
309
+ assert 0
310
+ except AssertionError:
311
+ pass
312
+
313
+
314
+ try:
315
+ import psyco
316
+ psyco.bind(bdecode)
317
+ psyco.bind(bencode)
318
+ except ImportError:
319
+ pass