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,254 @@
1
+ # Written by John Hoffman
2
+ # derived from NATPortMapping.py by Yejun Yang
3
+ # and from example code by Myers Carpenter
4
+ # see LICENSE.txt for license information
5
+
6
+ import socket
7
+ from traceback import print_exc
8
+ from subnetparse import IP_List
9
+ from clock import clock
10
+ from __init__ import createPeerID
11
+ try:
12
+ True
13
+ except:
14
+ True = 1
15
+ False = 0
16
+
17
+ DEBUG = False
18
+
19
+ EXPIRE_CACHE = 30 # seconds
20
+ ID = "BT-"+createPeerID()[-4:]
21
+
22
+ try:
23
+ import pythoncom, win32com.client
24
+ _supported = 1
25
+ except ImportError:
26
+ _supported = 0
27
+
28
+
29
+
30
+ class _UPnP1: # derived from Myers Carpenter's code
31
+ # seems to use the machine's local UPnP
32
+ # system for its operation. Runs fairly fast
33
+
34
+ def __init__(self):
35
+ self.map = None
36
+ self.last_got_map = -10e10
37
+
38
+ def _get_map(self):
39
+ if self.last_got_map + EXPIRE_CACHE < clock():
40
+ try:
41
+ dispatcher = win32com.client.Dispatch("HNetCfg.NATUPnP")
42
+ self.map = dispatcher.StaticPortMappingCollection
43
+ self.last_got_map = clock()
44
+ except:
45
+ self.map = None
46
+ return self.map
47
+
48
+ def test(self):
49
+ try:
50
+ assert self._get_map() # make sure a map was found
51
+ success = True
52
+ except:
53
+ success = False
54
+ return success
55
+
56
+
57
+ def open(self, ip, p):
58
+ map = self._get_map()
59
+ try:
60
+ map.Add(p,'TCP',p,ip,True,ID)
61
+ if DEBUG:
62
+ print 'port opened: '+ip+':'+str(p)
63
+ success = True
64
+ except:
65
+ if DEBUG:
66
+ print "COULDN'T OPEN "+str(p)
67
+ print_exc()
68
+ success = False
69
+ return success
70
+
71
+
72
+ def close(self, p):
73
+ map = self._get_map()
74
+ try:
75
+ map.Remove(p,'TCP')
76
+ success = True
77
+ if DEBUG:
78
+ print 'port closed: '+str(p)
79
+ except:
80
+ if DEBUG:
81
+ print 'ERROR CLOSING '+str(p)
82
+ print_exc()
83
+ success = False
84
+ return success
85
+
86
+
87
+ def clean(self, retry = False):
88
+ if not _supported:
89
+ return
90
+ try:
91
+ map = self._get_map()
92
+ ports_in_use = []
93
+ for i in xrange(len(map)):
94
+ try:
95
+ mapping = map[i]
96
+ port = mapping.ExternalPort
97
+ prot = str(mapping.Protocol).lower()
98
+ desc = str(mapping.Description).lower()
99
+ except:
100
+ port = None
101
+ if port and prot == 'tcp' and desc[:3] == 'bt-':
102
+ ports_in_use.append(port)
103
+ success = True
104
+ for port in ports_in_use:
105
+ try:
106
+ map.Remove(port,'TCP')
107
+ except:
108
+ success = False
109
+ if not success and not retry:
110
+ self.clean(retry = True)
111
+ except:
112
+ pass
113
+
114
+
115
+ class _UPnP2: # derived from Yejun Yang's code
116
+ # apparently does a direct search for UPnP hardware
117
+ # may work in some cases where _UPnP1 won't, but is slow
118
+ # still need to implement "clean" method
119
+
120
+ def __init__(self):
121
+ self.services = None
122
+ self.last_got_services = -10e10
123
+
124
+ def _get_services(self):
125
+ if not self.services or self.last_got_services + EXPIRE_CACHE < clock():
126
+ self.services = []
127
+ try:
128
+ f=win32com.client.Dispatch("UPnP.UPnPDeviceFinder")
129
+ for t in ( "urn:schemas-upnp-org:service:WANIPConnection:1",
130
+ "urn:schemas-upnp-org:service:WANPPPConnection:1" ):
131
+ try:
132
+ conns = f.FindByType(t,0)
133
+ for c in xrange(len(conns)):
134
+ try:
135
+ svcs = conns[c].Services
136
+ for s in xrange(len(svcs)):
137
+ try:
138
+ self.services.append(svcs[s])
139
+ except:
140
+ pass
141
+ except:
142
+ pass
143
+ except:
144
+ pass
145
+ except:
146
+ pass
147
+ self.last_got_services = clock()
148
+ return self.services
149
+
150
+ def test(self):
151
+ try:
152
+ assert self._get_services() # make sure some services can be found
153
+ success = True
154
+ except:
155
+ success = False
156
+ return success
157
+
158
+
159
+ def open(self, ip, p):
160
+ svcs = self._get_services()
161
+ success = False
162
+ for s in svcs:
163
+ try:
164
+ s.InvokeAction('AddPortMapping',['',p,'TCP',p,ip,True,ID,0],'')
165
+ success = True
166
+ except:
167
+ pass
168
+ if DEBUG and not success:
169
+ print "COULDN'T OPEN "+str(p)
170
+ print_exc()
171
+ return success
172
+
173
+
174
+ def close(self, p):
175
+ svcs = self._get_services()
176
+ success = False
177
+ for s in svcs:
178
+ try:
179
+ s.InvokeAction('DeletePortMapping', ['',p,'TCP'], '')
180
+ success = True
181
+ except:
182
+ pass
183
+ if DEBUG and not success:
184
+ print "COULDN'T OPEN "+str(p)
185
+ print_exc()
186
+ return success
187
+
188
+
189
+ class _UPnP: # master holding class
190
+ def __init__(self):
191
+ self.upnp1 = _UPnP1()
192
+ self.upnp2 = _UPnP2()
193
+ self.upnplist = (None, self.upnp1, self.upnp2)
194
+ self.upnp = None
195
+ self.local_ip = None
196
+ self.last_got_ip = -10e10
197
+
198
+ def get_ip(self):
199
+ if self.last_got_ip + EXPIRE_CACHE < clock():
200
+ local_ips = IP_List()
201
+ local_ips.set_intranet_addresses()
202
+ try:
203
+ for info in socket.getaddrinfo(socket.gethostname(),0,socket.AF_INET):
204
+ # exception if socket library isn't recent
205
+ self.local_ip = info[4][0]
206
+ if local_ips.includes(self.local_ip):
207
+ self.last_got_ip = clock()
208
+ if DEBUG:
209
+ print 'Local IP found: '+self.local_ip
210
+ break
211
+ else:
212
+ raise ValueError('couldn\'t find intranet IP')
213
+ except:
214
+ self.local_ip = None
215
+ if DEBUG:
216
+ print 'Error finding local IP'
217
+ print_exc()
218
+ return self.local_ip
219
+
220
+ def test(self, upnp_type):
221
+ if DEBUG:
222
+ print 'testing UPnP type '+str(upnp_type)
223
+ if not upnp_type or not _supported or self.get_ip() is None:
224
+ if DEBUG:
225
+ print 'not supported'
226
+ return 0
227
+ pythoncom.CoInitialize() # leave initialized
228
+ self.upnp = self.upnplist[upnp_type] # cache this
229
+ if self.upnp.test():
230
+ if DEBUG:
231
+ print 'ok'
232
+ return upnp_type
233
+ if DEBUG:
234
+ print 'tested bad'
235
+ return 0
236
+
237
+ def open(self, p):
238
+ assert self.upnp, "must run UPnP_test() with the desired UPnP access type first"
239
+ return self.upnp.open(self.get_ip(), p)
240
+
241
+ def close(self, p):
242
+ assert self.upnp, "must run UPnP_test() with the desired UPnP access type first"
243
+ return self.upnp.close(p)
244
+
245
+ def clean(self):
246
+ return self.upnp1.clean()
247
+
248
+ _upnp_ = _UPnP()
249
+
250
+ UPnP_test = _upnp_.test
251
+ UPnP_open_port = _upnp_.open
252
+ UPnP_close_port = _upnp_.close
253
+ UPnP_reset = _upnp_.clean
254
+
@@ -0,0 +1,137 @@
1
+ # Written by Bill Bumgarner and Bram Cohen
2
+ # see LICENSE.txt for license information
3
+
4
+ from types import *
5
+ from cStringIO import StringIO
6
+
7
+
8
+ def splitLine(line, COLS=80, indent=10):
9
+ indent = " " * indent
10
+ width = COLS - (len(indent) + 1)
11
+ if indent and width < 15:
12
+ width = COLS - 2
13
+ indent = " "
14
+ s = StringIO()
15
+ i = 0
16
+ for word in line.split():
17
+ if i == 0:
18
+ s.write(indent+word)
19
+ i = len(word)
20
+ continue
21
+ if i + len(word) >= width:
22
+ s.write('\n'+indent+word)
23
+ i = len(word)
24
+ continue
25
+ s.write(' '+word)
26
+ i += len(word) + 1
27
+ return s.getvalue()
28
+
29
+ def formatDefinitions(options, COLS, presets = {}):
30
+ s = StringIO()
31
+ for (longname, default, doc) in options:
32
+ s.write('--' + longname + ' <arg>\n')
33
+ default = presets.get(longname, default)
34
+ if type(default) in (IntType, LongType):
35
+ try:
36
+ default = int(default)
37
+ except:
38
+ pass
39
+ if default is not None:
40
+ doc += ' (defaults to ' + repr(default) + ')'
41
+ s.write(splitLine(doc,COLS,10))
42
+ s.write('\n\n')
43
+ return s.getvalue()
44
+
45
+
46
+ def usage(str):
47
+ raise ValueError(str)
48
+
49
+
50
+ def defaultargs(options):
51
+ l = {}
52
+ for (longname, default, doc) in options:
53
+ if default is not None:
54
+ l[longname] = default
55
+ return l
56
+
57
+
58
+ def parseargs(argv, options, minargs = None, maxargs = None, presets = {}):
59
+ config = {}
60
+ longkeyed = {}
61
+ for option in options:
62
+ longname, default, doc = option
63
+ longkeyed[longname] = option
64
+ config[longname] = default
65
+ for longname in presets.keys(): # presets after defaults but before arguments
66
+ config[longname] = presets[longname]
67
+ options = []
68
+ args = []
69
+ pos = 0
70
+ while pos < len(argv):
71
+ if argv[pos][:2] != '--':
72
+ args.append(argv[pos])
73
+ pos += 1
74
+ else:
75
+ if pos == len(argv) - 1:
76
+ usage('parameter passed in at end with no value')
77
+ key, value = argv[pos][2:], argv[pos+1]
78
+ pos += 2
79
+ if not longkeyed.has_key(key):
80
+ usage('unknown key --' + key)
81
+ longname, default, doc = longkeyed[key]
82
+ try:
83
+ t = type(config[longname])
84
+ if t is NoneType or t is StringType:
85
+ config[longname] = value
86
+ elif t in (IntType, LongType):
87
+ config[longname] = long(value)
88
+ elif t is FloatType:
89
+ config[longname] = float(value)
90
+ else:
91
+ assert 0
92
+ except ValueError, e:
93
+ usage('wrong format of --%s - %s' % (key, str(e)))
94
+ for key, value in config.items():
95
+ if value is None:
96
+ usage("Option --%s is required." % key)
97
+ if minargs is not None and len(args) < minargs:
98
+ usage("Must supply at least %d args." % minargs)
99
+ if maxargs is not None and len(args) > maxargs:
100
+ usage("Too many args - %d max." % maxargs)
101
+ return (config, args)
102
+
103
+ def test_parseargs():
104
+ assert parseargs(('d', '--a', 'pq', 'e', '--b', '3', '--c', '4.5', 'f'), (('a', 'x', ''), ('b', 1, ''), ('c', 2.3, ''))) == ({'a': 'pq', 'b': 3, 'c': 4.5}, ['d', 'e', 'f'])
105
+ assert parseargs([], [('a', 'x', '')]) == ({'a': 'x'}, [])
106
+ assert parseargs(['--a', 'x', '--a', 'y'], [('a', '', '')]) == ({'a': 'y'}, [])
107
+ try:
108
+ parseargs([], [('a', 'x', '')])
109
+ except ValueError:
110
+ pass
111
+ try:
112
+ parseargs(['--a', 'x'], [])
113
+ except ValueError:
114
+ pass
115
+ try:
116
+ parseargs(['--a'], [('a', 'x', '')])
117
+ except ValueError:
118
+ pass
119
+ try:
120
+ parseargs([], [], 1, 2)
121
+ except ValueError:
122
+ pass
123
+ assert parseargs(['x'], [], 1, 2) == ({}, ['x'])
124
+ assert parseargs(['x', 'y'], [], 1, 2) == ({}, ['x', 'y'])
125
+ try:
126
+ parseargs(['x', 'y', 'z'], [], 1, 2)
127
+ except ValueError:
128
+ pass
129
+ try:
130
+ parseargs(['--a', '2.0'], [('a', 3, '')])
131
+ except ValueError:
132
+ pass
133
+ try:
134
+ parseargs(['--a', 'z'], [('a', 2.1, '')])
135
+ except ValueError:
136
+ pass
137
+
@@ -0,0 +1,150 @@
1
+ # Written by John Hoffman and Uoti Urpala
2
+ # see LICENSE.txt for license information
3
+ from bencode import bencode, bdecode
4
+ from BT1.btformats import check_info
5
+ from os.path import exists, isfile
6
+ from sha import sha
7
+ import sys, os
8
+
9
+ try:
10
+ True
11
+ except:
12
+ True = 1
13
+ False = 0
14
+
15
+ NOISY = False
16
+
17
+ def _errfunc(x):
18
+ print ":: "+x
19
+
20
+ def parsedir(directory, parsed, files, blocked,
21
+ exts = ['.torrent'], return_metainfo = False, errfunc = _errfunc):
22
+ if NOISY:
23
+ errfunc('checking dir')
24
+ dirs_to_check = [directory]
25
+ new_files = {}
26
+ new_blocked = {}
27
+ torrent_type = {}
28
+ while dirs_to_check: # first, recurse directories and gather torrents
29
+ directory = dirs_to_check.pop()
30
+ newtorrents = False
31
+ for f in os.listdir(directory):
32
+ newtorrent = None
33
+ for ext in exts:
34
+ if f.endswith(ext):
35
+ newtorrent = ext[1:]
36
+ break
37
+ if newtorrent:
38
+ newtorrents = True
39
+ p = os.path.join(directory, f)
40
+ new_files[p] = [(os.path.getmtime(p), os.path.getsize(p)), 0]
41
+ torrent_type[p] = newtorrent
42
+ if not newtorrents:
43
+ for f in os.listdir(directory):
44
+ p = os.path.join(directory, f)
45
+ if os.path.isdir(p):
46
+ dirs_to_check.append(p)
47
+
48
+ new_parsed = {}
49
+ to_add = []
50
+ added = {}
51
+ removed = {}
52
+ # files[path] = [(modification_time, size), hash], hash is 0 if the file
53
+ # has not been successfully parsed
54
+ for p,v in new_files.items(): # re-add old items and check for changes
55
+ oldval = files.get(p)
56
+ if not oldval: # new file
57
+ to_add.append(p)
58
+ continue
59
+ h = oldval[1]
60
+ if oldval[0] == v[0]: # file is unchanged from last parse
61
+ if h:
62
+ if blocked.has_key(p): # parseable + blocked means duplicate
63
+ to_add.append(p) # other duplicate may have gone away
64
+ else:
65
+ new_parsed[h] = parsed[h]
66
+ new_files[p] = oldval
67
+ else:
68
+ new_blocked[p] = 1 # same broken unparseable file
69
+ continue
70
+ if parsed.has_key(h) and not blocked.has_key(p):
71
+ if NOISY:
72
+ errfunc('removing '+p+' (will re-add)')
73
+ removed[h] = parsed[h]
74
+ to_add.append(p)
75
+
76
+ to_add.sort()
77
+ for p in to_add: # then, parse new and changed torrents
78
+ new_file = new_files[p]
79
+ v,h = new_file
80
+ if new_parsed.has_key(h): # duplicate
81
+ if not blocked.has_key(p) or files[p][0] != v:
82
+ errfunc('**warning** '+
83
+ p +' is a duplicate torrent for '+new_parsed[h]['path'])
84
+ new_blocked[p] = 1
85
+ continue
86
+
87
+ if NOISY:
88
+ errfunc('adding '+p)
89
+ try:
90
+ ff = open(p, 'rb')
91
+ d = bdecode(ff.read())
92
+ check_info(d['info'])
93
+ h = sha(bencode(d['info'])).digest()
94
+ new_file[1] = h
95
+ if new_parsed.has_key(h):
96
+ errfunc('**warning** '+
97
+ p +' is a duplicate torrent for '+new_parsed[h]['path'])
98
+ new_blocked[p] = 1
99
+ continue
100
+
101
+ a = {}
102
+ a['path'] = p
103
+ f = os.path.basename(p)
104
+ a['file'] = f
105
+ a['type'] = torrent_type[p]
106
+ i = d['info']
107
+ l = 0
108
+ nf = 0
109
+ if i.has_key('length'):
110
+ l = i.get('length',0)
111
+ nf = 1
112
+ elif i.has_key('files'):
113
+ for li in i['files']:
114
+ nf += 1
115
+ if li.has_key('length'):
116
+ l += li['length']
117
+ a['numfiles'] = nf
118
+ a['length'] = l
119
+ a['name'] = i.get('name', f)
120
+ def setkey(k, d = d, a = a):
121
+ if d.has_key(k):
122
+ a[k] = d[k]
123
+ setkey('failure reason')
124
+ setkey('warning message')
125
+ setkey('announce-list')
126
+ if return_metainfo:
127
+ a['metainfo'] = d
128
+ except:
129
+ errfunc('**warning** '+p+' has errors')
130
+ new_blocked[p] = 1
131
+ continue
132
+ try:
133
+ ff.close()
134
+ except:
135
+ pass
136
+ if NOISY:
137
+ errfunc('... successful')
138
+ new_parsed[h] = a
139
+ added[h] = a
140
+
141
+ for p,v in files.items(): # and finally, mark removed torrents
142
+ if not new_files.has_key(p) and not blocked.has_key(p):
143
+ if NOISY:
144
+ errfunc('removing '+p)
145
+ removed[v[1]] = parsed[v[1]]
146
+
147
+ if NOISY:
148
+ errfunc('done checking')
149
+ return (new_parsed, new_files, new_blocked, added, removed)
150
+