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,100 @@
1
+ # Written by John Hoffman
2
+ # see LICENSE.txt for license information
3
+
4
+ from httplib import HTTPConnection, HTTPSConnection, HTTPException
5
+ from urlparse import urlparse
6
+ from bencode import bdecode
7
+ import socket
8
+ from gzip import GzipFile
9
+ from StringIO import StringIO
10
+ from urllib import quote, unquote
11
+ from __init__ import product_name, version_short
12
+
13
+ VERSION = product_name+'/'+version_short
14
+ MAX_REDIRECTS = 10
15
+
16
+
17
+ class btHTTPcon(HTTPConnection): # attempt to add automatic connection timeout
18
+ def connect(self):
19
+ HTTPConnection.connect(self)
20
+ try:
21
+ self.sock.settimeout(30)
22
+ except:
23
+ pass
24
+
25
+ class btHTTPScon(HTTPSConnection): # attempt to add automatic connection timeout
26
+ def connect(self):
27
+ HTTPSConnection.connect(self)
28
+ try:
29
+ self.sock.settimeout(30)
30
+ except:
31
+ pass
32
+
33
+ class urlopen:
34
+ def __init__(self, url):
35
+ self.tries = 0
36
+ self._open(url.strip())
37
+ self.error_return = None
38
+
39
+ def _open(self, url):
40
+ self.tries += 1
41
+ if self.tries > MAX_REDIRECTS:
42
+ raise IOError, ('http error', 500,
43
+ "Internal Server Error: Redirect Recursion")
44
+ (scheme, netloc, path, pars, query, fragment) = urlparse(url)
45
+ if scheme != 'http' and scheme != 'https':
46
+ raise IOError, ('url error', 'unknown url type', scheme, url)
47
+ url = path
48
+ if pars:
49
+ url += ';'+pars
50
+ if query:
51
+ url += '?'+query
52
+ # if fragment:
53
+ try:
54
+ if scheme == 'http':
55
+ self.connection = btHTTPcon(netloc)
56
+ else:
57
+ self.connection = btHTTPScon(netloc)
58
+ self.connection.request('GET', url, None,
59
+ { 'User-Agent': VERSION,
60
+ 'Accept-Encoding': 'gzip' } )
61
+ self.response = self.connection.getresponse()
62
+ except HTTPException, e:
63
+ raise IOError, ('http error', str(e))
64
+ status = self.response.status
65
+ if status in (301,302):
66
+ try:
67
+ self.connection.close()
68
+ except:
69
+ pass
70
+ self._open(self.response.getheader('Location'))
71
+ return
72
+ if status != 200:
73
+ try:
74
+ data = self._read()
75
+ d = bdecode(data)
76
+ if d.has_key('failure reason'):
77
+ self.error_return = data
78
+ return
79
+ except:
80
+ pass
81
+ raise IOError, ('http error', status, self.response.reason)
82
+
83
+ def read(self):
84
+ if self.error_return:
85
+ return self.error_return
86
+ return self._read()
87
+
88
+ def _read(self):
89
+ data = self.response.read()
90
+ if self.response.getheader('Content-Encoding','').find('gzip') >= 0:
91
+ try:
92
+ compressed = StringIO(data)
93
+ f = GzipFile(fileobj = compressed)
94
+ data = f.read()
95
+ except:
96
+ raise IOError, ('http error', 'got corrupt response')
97
+ return data
98
+
99
+ def close(self):
100
+ self.connection.close()
@@ -0,0 +1,291 @@
1
+ # Copyright 2010 Twitter, Inc.
2
+ # Copyright 2010 Larry Gadea <lg@twitter.com>
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License"); you may
5
+ # not use this file except in compliance with the License. You may obtain
6
+ # a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ # Usage: python murder_client.py peer/seed out.torrent OUT.OUT 127.0.0.1
17
+ # last parameter is the local ip address, normally 10.x.x.x
18
+
19
+ import warnings
20
+ warnings.filterwarnings('ignore', category=DeprecationWarning)
21
+
22
+ from BitTornado import PSYCO
23
+ if PSYCO.psyco:
24
+ try:
25
+ import psyco
26
+ assert psyco.__version__ >= 0x010100f0
27
+ psyco.full()
28
+ except:
29
+ pass
30
+
31
+ from BitTornado.download_bt1 import BT1Download, defaults, parse_params, get_usage, get_response
32
+ from BitTornado.RawServer import RawServer, UPnP_ERROR
33
+ from random import seed
34
+ from socket import error as socketerror
35
+ from BitTornado.bencode import bencode
36
+ from BitTornado.natpunch import UPnP_test
37
+ from threading import Event
38
+ from os.path import abspath
39
+ from sys import argv, stdout
40
+ import sys
41
+ import os
42
+ import threading
43
+ from sha import sha
44
+ from time import strftime
45
+ from BitTornado.clock import clock
46
+ from BitTornado import createPeerID, version
47
+ from BitTornado.ConfigDir import ConfigDir
48
+
49
+ assert sys.version >= '2', "Install Python 2.0 or greater"
50
+ try:
51
+ True
52
+ except:
53
+ True = 1
54
+ False = 0
55
+
56
+ doneFlag = None
57
+ isPeer = False
58
+
59
+ def ok_close_now():
60
+ doneFlag.set()
61
+
62
+ def hours(n):
63
+ if n == 0:
64
+ return 'complete!'
65
+ try:
66
+ n = int(n)
67
+ assert n >= 0 and n < 5184000 # 60 days
68
+ except:
69
+ return '<unknown>'
70
+ m, s = divmod(n, 60)
71
+ h, m = divmod(m, 60)
72
+ if h > 0:
73
+ return '%d hour %02d min %02d sec' % (h, m, s)
74
+ else:
75
+ return '%d min %02d sec' % (m, s)
76
+
77
+ class HeadlessDisplayer:
78
+ def __init__(self):
79
+ self.done = False
80
+ self.file = ''
81
+ self.percentDone = ''
82
+ self.timeEst = ''
83
+ self.downloadTo = ''
84
+ self.downRate = ''
85
+ self.upRate = ''
86
+ self.shareRating = ''
87
+ self.seedStatus = ''
88
+ self.peerStatus = ''
89
+ self.errors = []
90
+ self.last_update_time = -1
91
+
92
+ def finished(self):
93
+ global doneFlag
94
+
95
+ self.done = True
96
+ self.percentDone = '100'
97
+ self.timeEst = 'Download Succeeded!'
98
+ self.downRate = ''
99
+ #self.display()
100
+
101
+ global isPeer
102
+
103
+ print "done and done"
104
+
105
+ if isPeer:
106
+ if os.fork():
107
+ os._exit(0)
108
+ return
109
+
110
+ os.setsid()
111
+ if os.fork():
112
+ os._exit(0)
113
+ return
114
+
115
+ os.close(0)
116
+ os.close(1)
117
+ os.close(2)
118
+
119
+ t = threading.Timer(30.0, ok_close_now)
120
+ t.start()
121
+
122
+ def failed(self):
123
+ self.done = True
124
+ self.percentDone = '0'
125
+ self.timeEst = 'Download Failed!'
126
+ self.downRate = ''
127
+ global doneFlag
128
+ doneFlag.set()
129
+ #self.display()
130
+
131
+ def error(self, errormsg):
132
+ #self.errors.append(errormsg)
133
+ self.display()
134
+ global doneFlag
135
+ print errormsg
136
+ doneFlag.set()
137
+
138
+ def display(self, dpflag = Event(), fractionDone = None, timeEst = None,
139
+ downRate = None, upRate = None, activity = None,
140
+ statistics = None, **kws):
141
+ if self.last_update_time + 0.1 > clock() and fractionDone not in (0.0, 1.0) and activity is not None:
142
+ return
143
+ self.last_update_time = clock()
144
+ if fractionDone is not None:
145
+ self.percentDone = str(float(int(fractionDone * 1000)) / 10)
146
+ if timeEst is not None:
147
+ self.timeEst = hours(timeEst)
148
+ if activity is not None and not self.done:
149
+ self.timeEst = activity
150
+ if downRate is not None:
151
+ self.downRate = '%.1f kB/s' % (float(downRate) / (1 << 10))
152
+ if upRate is not None:
153
+ self.upRate = '%.1f kB/s' % (float(upRate) / (1 << 10))
154
+ if statistics is not None:
155
+ if (statistics.shareRating < 0) or (statistics.shareRating > 100):
156
+ self.shareRating = 'oo (%.1f MB up / %.1f MB down)' % (float(statistics.upTotal) / (1<<20), float(statistics.downTotal) / (1<<20))
157
+ else:
158
+ self.shareRating = '%.3f (%.1f MB up / %.1f MB down)' % (statistics.shareRating, float(statistics.upTotal) / (1<<20), float(statistics.downTotal) / (1<<20))
159
+ if not self.done:
160
+ self.seedStatus = '%d seen now, plus %.3f distributed copies' % (statistics.numSeeds,0.001*int(1000*statistics.numCopies))
161
+ else:
162
+ self.seedStatus = '%d seen recently, plus %.3f distributed copies' % (statistics.numOldSeeds,0.001*int(1000*statistics.numCopies))
163
+ self.peerStatus = '%d seen now, %.1f%% done at %.1f kB/s' % (statistics.numPeers,statistics.percentDone,float(statistics.torrentRate) / (1 << 10))
164
+ #print '\n\n\n\n'
165
+ for err in self.errors:
166
+ print 'ERROR:\n' + err + '\n'
167
+ #print 'saving: ', self.file
168
+ #print 'percent done: ', self.percentDone
169
+ #print 'time left: ', self.timeEst
170
+ #print 'download to: ', self.downloadTo
171
+ #print 'download rate: ', self.downRate
172
+ #print 'upload rate: ', self.upRate
173
+ #print 'share rating: ', self.shareRating
174
+ #print 'seed status: ', self.seedStatus
175
+ #print 'peer status: ', self.peerStatus
176
+ #stdout.flush()
177
+ dpflag.set()
178
+
179
+ def chooseFile(self, default, size, saveas, dir):
180
+ self.file = '%s (%.1f MB)' % (default, float(size) / (1 << 20))
181
+ if saveas != '':
182
+ default = saveas
183
+ self.downloadTo = abspath(default)
184
+ return default
185
+
186
+ def newpath(self, path):
187
+ self.downloadTo = path
188
+
189
+ def run(params):
190
+ cols = 80
191
+
192
+ h = HeadlessDisplayer()
193
+ while 1:
194
+ configdir = ConfigDir('downloadheadless')
195
+ defaultsToIgnore = ['responsefile', 'url', 'priority']
196
+ configdir.setDefaults(defaults,defaultsToIgnore)
197
+ configdefaults = configdir.loadConfig()
198
+ defaults.append(('save_options',0,
199
+ "whether to save the current options as the new default configuration " +
200
+ "(only for btdownloadheadless.py)"))
201
+ try:
202
+ config = parse_params(params, configdefaults)
203
+ except ValueError, e:
204
+ print 'error: ' + str(e) + '\nrun with no args for parameter explanations'
205
+ break
206
+ if not config:
207
+ print get_usage(defaults, 80, configdefaults)
208
+ break
209
+ if config['save_options']:
210
+ configdir.saveConfig(config)
211
+ configdir.deleteOldCacheData(config['expire_cache_data'])
212
+
213
+ myid = createPeerID()
214
+ seed(myid)
215
+
216
+ global doneFlag
217
+ doneFlag = Event()
218
+ def disp_exception(text):
219
+ print text
220
+ rawserver = RawServer(doneFlag, config['timeout_check_interval'],
221
+ config['timeout'], ipv6_enable = config['ipv6_enabled'],
222
+ failfunc = h.failed, errorfunc = disp_exception)
223
+ upnp_type = UPnP_test(config['upnp_nat_access'])
224
+ while True:
225
+ try:
226
+ listen_port = rawserver.find_and_bind(config['minport'], config['maxport'],
227
+ config['bind'], ipv6_socket_style = config['ipv6_binds_v4'],
228
+ upnp = upnp_type, randomizer = config['random_port'])
229
+ break
230
+ except socketerror, e:
231
+ if upnp_type and e == UPnP_ERROR:
232
+ print 'WARNING: COULD NOT FORWARD VIA UPnP'
233
+ upnp_type = 0
234
+ continue
235
+ print "error: Couldn't listen - " + str(e)
236
+ h.failed()
237
+ return
238
+
239
+ response = get_response(config['responsefile'], config['url'], h.error)
240
+ if not response:
241
+ break
242
+
243
+ infohash = sha(bencode(response['info'])).digest()
244
+
245
+ dow = BT1Download(h.display, h.finished, h.error, disp_exception, doneFlag,
246
+ config, response, infohash, myid, rawserver, listen_port,
247
+ configdir)
248
+
249
+ if not dow.saveAs(h.chooseFile, h.newpath):
250
+ break
251
+
252
+ if not dow.initFiles(old_style = True):
253
+ break
254
+ if not dow.startEngine():
255
+ dow.shutdown()
256
+ break
257
+ dow.startRerequester()
258
+ dow.autoStats()
259
+
260
+ if not dow.am_I_finished():
261
+ h.display(activity = 'connecting to peers')
262
+ rawserver.listen_forever(dow.getPortHandler())
263
+ h.display(activity = 'shutting down')
264
+ dow.shutdown()
265
+ break
266
+ try:
267
+ rawserver.shutdown()
268
+ except:
269
+ pass
270
+ if not h.done:
271
+ h.failed()
272
+
273
+ if __name__ == '__main__':
274
+
275
+ if len(argv) != 5:
276
+ print "Incorrect number of arguments"
277
+ print
278
+ print """Usage:
279
+ python murder_client.py peer/seed out.torrent OUT.OUT 127.0.0.1
280
+
281
+ The last parameter is the local ip address, normally 10.x.x.x
282
+ """
283
+ sys.exit(1)
284
+
285
+ argv = ["--responsefile", sys.argv[2],
286
+ "--saveas", sys.argv[3],
287
+ "--ip", sys.argv[4]]
288
+
289
+ isPeer = sys.argv[1] == "peer"
290
+
291
+ run(argv[1:])
@@ -0,0 +1,46 @@
1
+ # Copyright 2010 Twitter, Inc.
2
+ # Copyright 2010 Larry Gadea <lg@twitter.com>
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License"); you may
5
+ # not use this file except in compliance with the License. You may obtain
6
+ # a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ # Usage: python murder_make_torrent.py <file> <trackerhost:port> <target>
17
+ # Usage: python murder_make_torrent.py deploy.tar.gz tracker.twitter.com:8998 deploy.torrent
18
+
19
+ import warnings
20
+ warnings.filterwarnings('ignore', category=DeprecationWarning)
21
+
22
+ from sys import argv, version, exit
23
+ from os.path import split
24
+ assert version >= '2', "Install Python 2.0 or greater"
25
+ from BitTornado.BT1.makemetafile import make_meta_file
26
+
27
+ if __name__ == '__main__':
28
+
29
+ if len(argv) != 4:
30
+ print "Incorrect number of arguments"
31
+ print
32
+ print """Usage:
33
+ python murder_make_torrent.py <file> <trackerhost:port> <target>
34
+
35
+ For example:
36
+ python murder_make_torrent.py deploy.tar.gz tracker.twitter.com:8998 deploy.torrent
37
+ """
38
+ exit(1)
39
+
40
+ try:
41
+ params = {}
42
+ params["target"] = argv[3]
43
+ make_meta_file(argv[1], "http://" + argv[2] + "/announce", params)
44
+ except ValueError, e:
45
+ print str(e)
46
+ exit(1)
@@ -0,0 +1,28 @@
1
+ # Copyright 2010 Twitter, Inc.
2
+ # Copyright 2010 Larry Gadea <lg@twitter.com>
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License"); you may
5
+ # not use this file except in compliance with the License. You may obtain
6
+ # a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ # Usage: python murder_tracker.py <optionalparams>
17
+ # Usage: python murder_tracker.py
18
+
19
+ import warnings
20
+ warnings.filterwarnings('ignore', category=DeprecationWarning)
21
+
22
+ from BitTornado.BT1.track import track
23
+ from sys import argv
24
+
25
+ if __name__ == '__main__':
26
+ args = ["--dfile", "data",
27
+ "--port", "8998"] + argv[1:]
28
+ track(args)