📄 track.py
字号:
self.times[x][y] = 0
self.trackerid = createPeerID('-T-')
seed(self.trackerid)
self.reannounce_interval = config['reannounce_interval']
self.save_dfile_interval = config['save_dfile_interval']
self.show_names = config['show_names']
rawserver.add_task(self.save_state, self.save_dfile_interval)
self.prevtime = clock()
self.timeout_downloaders_interval = config['timeout_downloaders_interval']
rawserver.add_task(self.expire_downloaders, self.timeout_downloaders_interval)
self.logfile = None
self.log = None
if (config['logfile']) and (config['logfile'] != '-'):
try:
self.logfile = config['logfile']
self.log = open(self.logfile,'a')
sys.stdout = self.log
print "# Log Started: ", isotime()
except:
print "**warning** could not redirect stdout to log file: ", sys.exc_info()[0]
if config['hupmonitor']:
def huphandler(signum, frame, self = self):
try:
self.log.close()
self.log = open(self.logfile,'a')
sys.stdout = self.log
print "# Log reopened: ", isotime()
except:
print "**warning** could not reopen logfile"
signal.signal(signal.SIGHUP, huphandler)
self.allow_get = config['allow_get']
self.t2tlist = T2TList(config['multitracker_enabled'], self.trackerid,
config['multitracker_reannounce_interval'],
config['multitracker_maxpeers'], config['http_timeout'],
self.rawserver)
if config['allowed_list']:
if config['allowed_dir']:
print '**warning** allowed_dir and allowed_list options cannot be used together'
print '**warning** disregarding allowed_dir'
config['allowed_dir'] = ''
self.allowed = self.state.setdefault('allowed_list',{})
self.allowed_list_mtime = 0
self.parse_allowed()
self.remove_from_state('allowed','allowed_dir_files')
if config['multitracker_allowed'] == 'autodetect':
config['multitracker_allowed'] = 'none'
config['allowed_controls'] = 0
elif config['allowed_dir']:
self.allowed = self.state.setdefault('allowed',{})
self.allowed_dir_files = self.state.setdefault('allowed_dir_files',{})
self.allowed_dir_blocked = {}
self.parse_allowed()
self.remove_from_state('allowed_list')
else:
self.allowed = None
self.remove_from_state('allowed','allowed_dir_files', 'allowed_list')
if config['multitracker_allowed'] == 'autodetect':
config['multitracker_allowed'] = 'none'
config['allowed_controls'] = 0
self.uq_broken = unquote('+') != ' '
self.keep_dead = config['keep_dead']
self.Filter = Filter(rawserver.add_task)
aggregator = config['aggregator']
if aggregator == '0':
self.is_aggregator = False
self.aggregator_key = None
else:
self.is_aggregator = True
if aggregator == '1':
self.aggregator_key = None
else:
self.aggregator_key = aggregator
self.natcheck = False
send = config['aggregate_forward']
if not send:
self.aggregate_forward = None
else:
try:
self.aggregate_forward, self.aggregate_password = send.split(',')
except:
self.aggregate_forward = send
self.aggregate_password = None
self.cachetime = 0
self.cachetimeupdate()
def cachetimeupdate(self):
self.cachetime += 1 # raw clock, but more efficient for cache
self.rawserver.add_task(self.cachetimeupdate,1)
def aggregate_senddata(self, query):
url = self.aggregate_forward+'?'+query
if self.aggregate_password is not None:
url += '&password='+self.aggregate_password
rq = Thread(target = self._aggregate_senddata, args = [url])
rq.setDaemon(False)
rq.start()
def _aggregate_senddata(self, url): # just send, don't attempt to error check,
try: # discard any returned data
h = urlopen(url)
h.read()
h.close()
except:
return
def get_infopage(self):
try:
if not self.config['show_infopage']:
return (404, 'Not Found', {'Content-Type': 'text/plain', 'Pragma': 'no-cache'}, alas)
red = self.config['infopage_redirect']
if red:
return (302, 'Found', {'Content-Type': 'text/html', 'Location': red},
'<A HREF="'+red+'">Click Here</A>')
s = StringIO()
s.write('<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">\n' \
'<html><head><title>BitTorrent download info</title>\n')
if self.favicon is not None:
s.write('<link rel="shortcut icon" href="/favicon.ico">\n')
s.write('</head>\n<body>\n' \
'<h3>BitTorrent download info</h3>\n'\
'<ul>\n'
'<li><strong>tracker version:</strong> %s</li>\n' \
'<li><strong>server time:</strong> %s</li>\n' \
'</ul>\n' % (version, isotime()))
if self.config['allowed_dir']:
if self.show_names:
names = [ (self.allowed[hash]['name'],hash)
for hash in self.allowed.keys() ]
else:
names = [ (None,hash)
for hash in self.allowed.keys() ]
else:
names = [ (None,hash) for hash in self.downloads.keys() ]
if not names:
s.write('<p>not tracking any files yet...</p>\n')
else:
names.sort()
tn = 0
tc = 0
td = 0
tt = 0 # Total transferred
ts = 0 # Total size
nf = 0 # Number of files displayed
if self.config['allowed_dir'] and self.show_names:
s.write('<table summary="files" border="1">\n' \
'<tr><th>info hash</th><th>torrent name</th><th align="right">size</th><th align="right">complete</th><th align="right">downloading</th><th align="right">downloaded</th><th align="right">transferred</th></tr>\n')
else:
s.write('<table summary="files">\n' \
'<tr><th>info hash</th><th align="right">complete</th><th align="right">downloading</th><th align="right">downloaded</th></tr>\n')
for name,hash in names:
l = self.downloads[hash]
n = self.completed.get(hash, 0)
tn = tn + n
c = self.seedcount[hash]
tc = tc + c
d = len(l) - c
td = td + d
if self.config['allowed_dir'] and self.show_names:
if self.allowed.has_key(hash):
nf = nf + 1
sz = self.allowed[hash]['length'] # size
ts = ts + sz
szt = sz * n # Transferred for this torrent
tt = tt + szt
if self.allow_get == 1:
linkname = '<a href="/file?info_hash=' + quote(hash) + '">' + name + '</a>'
else:
linkname = name
s.write('<tr><td><code>%s</code></td><td>%s</td><td align="right">%s</td><td align="right">%i</td><td align="right">%i</td><td align="right">%i</td><td align="right">%s</td></tr>\n' \
% (b2a_hex(hash), linkname, size_format(sz), c, d, n, size_format(szt)))
else:
s.write('<tr><td><code>%s</code></td><td align="right"><code>%i</code></td><td align="right"><code>%i</code></td><td align="right"><code>%i</code></td></tr>\n' \
% (b2a_hex(hash), c, d, n))
ttn = 0
for i in self.completed.values():
ttn = ttn + i
if self.config['allowed_dir'] and self.show_names:
s.write('<tr><td align="right" colspan="2">%i files</td><td align="right">%s</td><td align="right">%i</td><td align="right">%i</td><td align="right">%i/%i</td><td align="right">%s</td></tr>\n'
% (nf, size_format(ts), tc, td, tn, ttn, size_format(tt)))
else:
s.write('<tr><td align="right">%i files</td><td align="right">%i</td><td align="right">%i</td><td align="right">%i/%i</td></tr>\n'
% (nf, tc, td, tn, ttn))
s.write('</table>\n' \
'<ul>\n' \
'<li><em>info hash:</em> SHA1 hash of the "info" section of the metainfo (*.torrent)</li>\n' \
'<li><em>complete:</em> number of connected clients with the complete file</li>\n' \
'<li><em>downloading:</em> number of connected clients still downloading</li>\n' \
'<li><em>downloaded:</em> reported complete downloads (total: current/all)</li>\n' \
'<li><em>transferred:</em> torrent size * total downloaded (does not include partial transfers)</li>\n' \
'</ul>\n')
s.write('</body>\n' \
'</html>\n')
return (200, 'OK', {'Content-Type': 'text/html; charset=iso-8859-1'}, s.getvalue())
except:
print_exc()
return (500, 'Internal Server Error', {'Content-Type': 'text/html; charset=iso-8859-1'}, 'Server Error')
def scrapedata(self, hash, return_name = True):
l = self.downloads[hash]
n = self.completed.get(hash, 0)
c = self.seedcount[hash]
d = len(l) - c
f = {'complete': c, 'incomplete': d, 'downloaded': n}
if return_name and self.show_names and self.config['allowed_dir']:
f['name'] = self.allowed[hash]['name']
return (f)
def get_scrape(self, paramslist):
fs = {}
if paramslist.has_key('info_hash'):
if self.config['scrape_allowed'] not in ['specific', 'full']:
return (400, 'Not Authorized', {'Content-Type': 'text/plain', 'Pragma': 'no-cache'},
bencode({'failure reason':
'specific scrape function is not available with this tracker.'}))
for hash in paramslist['info_hash']:
if self.allowed is not None:
if self.allowed.has_key(hash):
fs[hash] = self.scrapedata(hash)
else:
if self.downloads.has_key(hash):
fs[hash] = self.scrapedata(hash)
else:
if self.config['scrape_allowed'] != 'full':
return (400, 'Not Authorized', {'Content-Type': 'text/plain', 'Pragma': 'no-cache'},
bencode({'failure reason':
'full scrape function is not available with this tracker.'}))
if self.allowed is not None:
keys = self.allowed.keys()
else:
keys = self.downloads.keys()
for hash in keys:
fs[hash] = self.scrapedata(hash)
return (200, 'OK', {'Content-Type': 'text/plain'}, bencode({'files': fs}))
def get_file(self, hash):
if not self.allow_get:
return (400, 'Not Authorized', {'Content-Type': 'text/plain', 'Pragma': 'no-cache'},
'get function is not available with this tracker.')
if not self.allowed.has_key(hash):
return (404, 'Not Found', {'Content-Type': 'text/plain', 'Pragma': 'no-cache'}, alas)
fname = self.allowed[hash]['file']
fpath = self.allowed[hash]['path']
return (200, 'OK', {'Content-Type': 'application/x-bittorrent',
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -