def announce(self, event=None): self.last_time = time() s = ('%s&uploaded=%s&downloaded=%s&left=%s' % (self.url, str( self.up()), str(self.down()), str(self.amount_left()))) if self.last is not None: s += '&last=' + quote(str(self.last)) if self.trackerid is not None: s += '&trackerid=' + quote(str(self.trackerid)) if self.howmany() >= self.maxpeers: s += '&numwant=0' else: s += '&compact=1' if event != None: s += '&event=' + ['started', 'completed', 'stopped'][event] set = SetOnce().set def checkfail(self=self, set=set): if set(): if self.last_failed and self.upratefunc( ) < 100 and self.downratefunc() < 100: self.errorfunc( 'Problem connecting to tracker - timeout exceeded') self.last_failed = True self.sched(checkfail, self.timeout) Thread(target=self.rerequest, args=[s, set]).start()
def announce(self, event=None): self.last_time = time() s = "%s&uploaded=%s&downloaded=%s&left=%s" % ( self.url, str(self.up()), str(self.down()), str(self.amount_left()), ) if self.last is not None: s += "&last=" + quote(str(self.last)) if self.trackerid is not None: s += "&trackerid=" + quote(str(self.trackerid)) if self.howmany() >= self.maxpeers: s += "&numwant=0" else: s += "&compact=1" if event != None: s += "&event=" + ["started", "completed", "stopped"][event] set = SetOnce().set def checkfail(self=self, set=set): if set(): if self.last_failed and self.upratefunc() < 100 and self.downratefunc() < 100: self.errorfunc("Problem connecting to tracker - timeout exceeded") self.last_failed = True self.sched(checkfail, self.timeout) Thread(target=self.rerequest, args=[s, set]).start()
def __init__(self, url, interval, sched, howmany, minpeers, connect, externalsched, amount_left, up, down, port, ip, myid, infohash, timeout, errorfunc, maxpeers, doneflag, upratefunc, downratefunc, ever_got_incoming): self.url = ('%s?info_hash=%s&peer_id=%s&port=%s&key=%s' % (url, quote(infohash), quote(myid), str(port), b2a_hex(''.join([chr(randrange(256)) for i in xrange(4)])))) if ip != '': self.url += '&ip=' + quote(ip) self.interval = interval self.last = None self.trackerid = None self.announce_interval = 30 * 60 self.sched = sched self.howmany = howmany self.minpeers = minpeers self.connect = connect self.externalsched = externalsched self.amount_left = amount_left self.up = up self.down = down self.timeout = timeout self.errorfunc = errorfunc self.maxpeers = maxpeers self.doneflag = doneflag self.upratefunc = upratefunc self.downratefunc = downratefunc self.ever_got_incoming = ever_got_incoming self.last_failed = True self.last_time = 0
def __init__(self, url, interval, sched, howmany, minpeers, connect, externalsched, amount_left, up, down, port, ip, myid, infohash, timeout, errorfunc, maxpeers, doneflag, upratefunc, downratefunc, ever_got_incoming): self.url = ('%s?info_hash=%s&peer_id=%s&port=%s&key=%s' % (url, quote(infohash), quote(myid), str(port), b2a_hex(''.join([chr(randrange(256)) for i in xrange(4)])))) self.ip = ip self.interval = interval self.last = None self.trackerid = None self.announce_interval = 30 * 60 self.sched = sched self.howmany = howmany self.minpeers = minpeers self.connect = connect self.externalsched = externalsched self.amount_left = amount_left self.up = up self.down = down self.timeout = timeout self.errorfunc = errorfunc self.maxpeers = maxpeers self.doneflag = doneflag self.upratefunc = upratefunc self.downratefunc = downratefunc self.ever_got_incoming = ever_got_incoming self.last_failed = True self.last_time = 0
def announce(self, event = None): self.last_time = time() url = self.get_url() s = ('%s&uploaded=%s&downloaded=%s&left=%s' % (url, str(self.up()), str(self.down()), str(self.amount_left()))) if self.last is not None: s += '&last=' + quote(str(self.last)) if self.trackerid is not None: s += '&trackerid=' + quote(str(self.trackerid)) if self.howmany() >= self.maxpeers: s += '&numwant=0' else: s += '&compact=1' if event != None: s += '&event=' + ['started', 'completed', 'stopped'][event] set = SetOnce().set def checkfail(self = self, set = set): if set(): if self.last_failed: if self.upratefunc() < 100 and self.downratefunc() < 100: self.errorfunc('Problem connecting to tracker - timeout exceeded', 'tracker_timeout') else: self.errorfunc('Problem connecting to tracker - timeout exceeded', 'tracker_bcool') self.last_failed = True self.sched(checkfail, self.timeout) Thread(target = self.rerequest, args = [s, set]).start()
def announce(self, event = None): # Update the time we last made a request to the tracker. self.last_time = time() # Append total uploaded, total downloaded, and bytes left to download. s = ('%s&uploaded=%s&downloaded=%s&left=%s' % (self.url, str(self.up()), str(self.down()), str(self.amount_left()))) if self.last is not None: # Append the last time this client made a request to the tracker. s += '&last=' + quote(str(self.last)) if self.trackerid is not None: # If not the first request, append the id this tracker previously returned. s += '&trackerid=' + quote(str(self.trackerid)) if self.howmany() >= self.maxpeers: # Don't need any more peers to connect to. s += '&numwant=0' else: # Return peer IP and port addresses in 6 binary bytes. s += '&compact=1' # Event is not specified if this request is one performed at regular intervals. if event != None: s += '&event=' + ['started', 'completed', 'stopped'][event] # Method that returns True the first time and False every subsequent time. set = SetOnce().set def checkfail(self = self, set = set): if set(): # Only get here if the tracker did not reply and call set() in rerequest first. if self.last_failed and self.upratefunc() < 100 and self.downratefunc() < 100: self.errorfunc('Problem connecting to tracker - timeout exceeded') self.last_failed = True # Method checkfail will run if the tracker does not reply to this request. self.sched(checkfail, self.timeout) Thread(target = self.rerequest, args = [s, set]).start()
def __init__(self, url, interval, sched, howmany, minpeers, connect, externalsched, amount_left, up, down, port, ip, myid, infohash, timeout, errorfunc, maxpeers, doneflag, upratefunc, downratefunc, ever_got_incoming): # The URL and query paramters to always pass. self.url = ('%s?info_hash=%s&peer_id=%s&port=%s&key=%s' % (url, quote(infohash), quote(myid), str(port), b2a_hex(''.join([chr(randrange(256)) for i in xrange(4)])))) # The IP address of this client. self.ip = ip # The time in seconds between requesting more peers. self.interval = interval # The last time this client got a reply from the tracker. self.last = None # The identifier returned by the tracker, which this client uses on subsequent requests. self.trackerid = None # Maximum seconds between sending requests to the tracker. self.announce_interval = 30 * 60 # Function to schedule events in the reactor loop of RawServer. self.sched = sched # Method that returns how many peers this client is connected to. self.howmany = howmany # If connected to this many peers, may skip making a request to the tracker. self.minpeers = minpeers # Method on Connecter that starts a connection to a peer. self.connect = connect # Function to schedule events in the reactor loop of RawServer. self.externalsched = externalsched # Method to get the amount of data left. self.amount_left = amount_left # Method to get the total bytes uploaded. self.up = up # Method to get the total bytes downloaded. self.down = down # HTTP timeout when making a request to the tracker. self.timeout = timeout # Callback invoked with a string describing any error. self.errorfunc = errorfunc # If connected to this many peers, will not request any more from the tracker. self.maxpeers = maxpeers # Flag set if we have all pieces and are seeding. self.doneflag = doneflag # Method to get the upload rate. self.upratefunc = upratefunc # Method to get the download rate. self.downratefunc = downratefunc # Method that returns True if we ever got an incoming connection. self.ever_got_incoming = ever_got_incoming # True if the last request to the tracker failed. self.last_failed = True # The last time this client made a request to the tracker. self.last_time = 0
def __init__(self, url, interval, url_list, sched, howmany, minpeers, connect, externalsched, amount_left, up, down, port, ip, myid, infohash, timeout, errorfunc, maxpeers, doneflag, upratefunc, downratefunc, ever_got_incoming): self.url_params = ('?info_hash=%s&peer_id=%s&port=%s&key=%s' % (quote(infohash), quote(myid), str(port), b2a_hex(''.join([chr(randrange(256)) for i in xrange(4)])))) if ip != '': self.url_params += '&ip=' + quote(ip) self.url_list = [] if url_list == None: self.url_list.append(url) else: for ugroup in url_list: for url in ugroup: self.url_list.append(url) self.interval = interval self.last = None self.trackerid = None self.announce_interval = 30 * 60 self.sched = sched self.howmany = howmany self.minpeers = minpeers self.connect = connect self.externalsched = externalsched self.amount_left = amount_left self.up = up self.down = down self.timeout = timeout self.errorfunc = errorfunc self.maxpeers = maxpeers self.doneflag = doneflag self.upratefunc = upratefunc self.downratefunc = downratefunc self.ever_got_incoming = ever_got_incoming self.last_failed = True self.last_time = 0
def announce(self, event=None): # Update the time we last made a request to the tracker. self.last_time = time() # Append total uploaded, total downloaded, and bytes left to download. s = ('%s&uploaded=%s&downloaded=%s&left=%s' % (self.url, str( self.up()), str(self.down()), str(self.amount_left()))) if self.last is not None: # Append the last time this client made a request to the tracker. s += '&last=' + quote(str(self.last)) if self.trackerid is not None: # If not the first request, append the id this tracker previously returned. s += '&trackerid=' + quote(str(self.trackerid)) if self.howmany() >= self.maxpeers: # Don't need any more peers to connect to. s += '&numwant=0' else: # Return peer IP and port addresses in 6 binary bytes. s += '&compact=1' # Event is not specified if this request is one performed at regular intervals. if event != None: s += '&event=' + ['started', 'completed', 'stopped'][event] # Method that returns True the first time and False every subsequent time. set = SetOnce().set def checkfail(self=self, set=set): if set(): # Only get here if the tracker did not reply and call set() in rerequest first. if self.last_failed and self.upratefunc( ) < 100 and self.downratefunc() < 100: self.errorfunc( 'Problem connecting to tracker - timeout exceeded') self.last_failed = True # Method checkfail will run if the tracker does not reply to this request. self.sched(checkfail, self.timeout) Thread(target=self.rerequest, args=[s, set]).start()
def encode_for_filesystem(path): assert isinstance(path, unicode) bad = False encoding = get_filesystem_encoding() if encoding == None: encoded_path = path else: try: encoded_path = path.encode(encoding) except: bad = True path.replace(u"%", urllib.quote(u"%")) encoded_path = path.encode(encoding, 'urlquote') return (encoded_path, bad)
def get_infopage(self): 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 != 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())) names = 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 uc = {} ud = {} if self.allowed != None 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 in names: l = self.downloads[name] n = self.completed.get(name, 0) tn = tn + n lc = [] for i in l.values(): if type(i) == DictType: if i['left'] == 0: lc.append(1) uc[i['ip']] = 1 else: ud[i['ip']] = 1 c = len(lc) tc = tc + c d = len(l) - c td = td + d if self.allowed != None and self.show_names: if self.allowed.has_key(name): nf = nf + 1 sz = self.allowed[name]['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(name) + '">' + self.allowed[name]['name'] + '</a>' else: linkname = self.allowed[name]['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(name), 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(name), c, d, n)) ttn = 0 for i in self.completed.values(): ttn = ttn + i if self.allowed != None and self.show_names: s.write('<tr><td align="right" colspan="2">%i files</td><td align="right">%s</td><td align="right">%i/%i</td><td align="right">%i/%i</td><td align="right">%i/%i</td><td align="right">%s</td></tr>\n' % (nf, size_format(ts), len(uc), tc, len(ud), td, tn, ttn, size_format(tt))) else: s.write('<tr><td align="right">%i files</td><td align="right">%i/%i</td><td align="right">%i/%i</td><td align="right">%i/%i</td></tr>\n' % (nf, len(uc), tc, len(ud), 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 (total: unique IPs/total connections)</li>\n' \ '<li><em>downloading:</em> number of connected clients still downloading (total: unique IPs/total connections)</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())
class Tracker: def __init__(self, config, rawserver): self.response_size = config['response_size'] self.dfile = config['dfile'] self.natcheck = config['nat_check'] self.max_give = config['max_give'] self.reannounce_interval = config['reannounce_interval'] self.save_dfile_interval = config['save_dfile_interval'] self.show_names = config['show_names'] self.only_local_override_ip = config['only_local_override_ip'] favicon = config['favicon'] self.favicon = None if favicon: if isfile(favicon): h = open(favicon, 'rb') self.favicon = h.read() h.close() else: print "**warning** specified favicon file -- %s -- does not exist." % favicon self.rawserver = rawserver self.becache1 = {} self.becache2 = {} self.cache1 = {} self.cache2 = {} self.times = {} if exists(self.dfile): h = open(self.dfile, 'rb') ds = h.read() h.close() tempstate = bdecode(ds) else: tempstate = {} if tempstate.has_key('peers'): self.state = tempstate else: self.state = {} self.state['peers'] = tempstate self.downloads = self.state.setdefault('peers', {}) self.completed = self.state.setdefault('completed', {}) statefiletemplate(self.state) for x, dl in self.downloads.items(): self.times[x] = {} for y, dat in dl.items(): self.times[x][y] = 0 if not dat.get('nat', 1): ip = dat['ip'] gip = dat.get('given ip') if gip and is_valid_ipv4(gip) and ( not self.only_local_override_ip or is_local_ip(ip)): ip = gip self.becache1.setdefault(x, {})[y] = Bencached( bencode({ 'ip': ip, 'port': dat['port'], 'peer id': y })) self.becache2.setdefault(x, {})[y] = compact_peer_info( ip, dat['port']) rawserver.add_task(self.save_dfile, self.save_dfile_interval) self.prevtime = time() 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 "Error trying to redirect stdout to log file:", sys.exc_info( )[0] self.allow_get = config['allow_get'] if config['allowed_dir'] != '': self.allowed_dir = config['allowed_dir'] self.parse_allowed_interval = config['parse_allowed_interval'] self.parse_allowed() else: self.allowed = None if unquote('+') != ' ': self.uq_broken = 1 else: self.uq_broken = 0 self.keep_dead = config['keep_dead'] def get(self, connection, path, headers): try: (scheme, netloc, path, pars, query, fragment) = urlparse(path) if self.uq_broken == 1: path = path.replace('+', ' ') query = query.replace('+', ' ') path = unquote(path)[1:] params = {} for s in query.split('&'): if s != '': i = s.index('=') params[unquote(s[:i])] = unquote(s[i + 1:]) except ValueError, e: return (400, 'Bad Request', { 'Content-Type': 'text/plain' }, 'you sent me garbage - ' + str(e)) if path == '' or path == 'index.html': 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 != 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())) names = self.downloads.keys() if names: names.sort() tn = 0 tc = 0 td = 0 tt = 0 # Total transferred ts = 0 # Total size nf = 0 # Number of files displayed uc = {} ud = {} if self.allowed != None 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 in names: l = self.downloads[name] n = self.completed.get(name, 0) tn = tn + n lc = [] for i in l.values(): if type(i) == DictType: if i['left'] == 0: lc.append(1) uc[i['ip']] = 1 else: ud[i['ip']] = 1 c = len(lc) tc = tc + c d = len(l) - c td = td + d if self.allowed != None and self.show_names: if self.allowed.has_key(name): nf = nf + 1 sz = self.allowed[name]['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( name ) + '">' + self.allowed[name]['name'] + '</a>' else: linkname = self.allowed[name]['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(name), 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(name), c, d, n)) ttn = 0 for i in self.completed.values(): ttn = ttn + i if self.allowed != None and self.show_names: s.write( '<tr><td align="right" colspan="2">%i files</td><td align="right">%s</td><td align="right">%i/%i</td><td align="right">%i/%i</td><td align="right">%i/%i</td><td align="right">%s</td></tr>\n' % (nf, size_format(ts), len(uc), tc, len(ud), td, tn, ttn, size_format(tt))) else: s.write( '<tr><td align="right">%i files</td><td align="right">%i/%i</td><td align="right">%i/%i</td><td align="right">%i/%i</td></tr>\n' % (nf, len(uc), tc, len(ud), 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 (total: unique IPs/total connections)</li>\n' \ '<li><em>downloading:</em> number of connected clients still downloading (total: unique IPs/total connections)</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') else: s.write('<p>not tracking any files yet...</p>\n') s.write('</body>\n' \ '</html>\n') return (200, 'OK', { 'Content-Type': 'text/html; charset=iso-8859-1' }, s.getvalue()) elif path == 'scrape': fs = {} names = [] if params.has_key('info_hash'): if self.downloads.has_key(params['info_hash']): names = [params['info_hash']] # else return nothing else: names = self.downloads.keys() names.sort() for name in names: l = self.downloads[name] n = self.completed.get(name, 0) c = len([ 1 for i in l.values() if type(i) == DictType and i['left'] == 0 ]) d = len(l) - c fs[name] = {'complete': c, 'incomplete': d, 'downloaded': n} if (self.allowed is not None ) and self.allowed.has_key(name) and self.show_names: fs[name]['name'] = self.allowed[name]['name'] r = {'files': fs} return (200, 'OK', {'Content-Type': 'text/plain'}, bencode(r)) elif (path == 'file') and (self.allow_get == 1) and params.has_key( 'info_hash') and self.allowed.has_key(params['info_hash']): hash = params['info_hash'] fname = self.allowed[hash]['file'] fpath = self.allowed[hash]['path'] return (200, 'OK', { 'Content-Type': 'application/x-bittorrent', 'Content-Disposition': 'attachment; filename=' + fname }, open(fpath, 'rb').read()) elif path == 'favicon.ico' and self.favicon != None: return (200, 'OK', {'Content-Type': 'image/x-icon'}, self.favicon) if path != 'announce': return (404, 'Not Found', { 'Content-Type': 'text/plain', 'Pragma': 'no-cache' }, alas) try: if not params.has_key('info_hash'): raise ValueError, 'no info hash' if params.has_key('ip') and not is_valid_ipv4(params['ip']): raise ValueError('DNS name or invalid IP address given for IP') infohash = params['info_hash'] if self.allowed != None: if not self.allowed.has_key(infohash): return ( 200, 'OK', { 'Content-Type': 'text/plain', 'Pragma': 'no-cache' }, bencode({ 'failure reason': 'Requested download is not authorized for use with this tracker.' })) ip = connection.get_ip() ip_override = 0 if params.has_key('ip') and is_valid_ipv4( params['ip']) and (not self.only_local_override_ip or is_local_ip(ip)): ip_override = 1 if params.has_key('event') and params['event'] not in [ 'started', 'completed', 'stopped' ]: raise ValueError, 'invalid event' port = long(params.get('port', '')) uploaded = long(params.get('uploaded', '')) downloaded = long(params.get('downloaded', '')) left = long(params.get('left', '')) myid = params.get('peer_id', '') if len(myid) != 20: raise ValueError, 'id not of length 20' rsize = self.response_size if params.has_key('numwant'): rsize = min(long(params['numwant']), self.max_give) except ValueError, e: return (400, 'Bad Request', { 'Content-Type': 'text/plain' }, 'you sent me garbage - ' + str(e))
def get_infopage(self): 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 != 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())) names = 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 uc = {} ud = {} if self.allowed != None 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 in names: l = self.downloads[name] n = self.completed.get(name, 0) tn = tn + n lc = [] for i in l.values(): if type(i) == DictType: if i['left'] == 0: lc.append(1) uc[i['ip']] = 1 else: ud[i['ip']] = 1 c = len(lc) tc = tc + c d = len(l) - c td = td + d if self.allowed != None and self.show_names: if self.allowed.has_key(name): nf = nf + 1 sz = self.allowed[name]['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( name ) + '">' + self.allowed[name]['name'] + '</a>' else: linkname = self.allowed[name]['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(name), 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(name), c, d, n)) ttn = 0 for i in self.completed.values(): ttn = ttn + i if self.allowed != None and self.show_names: s.write( '<tr><td align="right" colspan="2">%i files</td><td align="right">%s</td><td align="right">%i/%i</td><td align="right">%i/%i</td><td align="right">%i/%i</td><td align="right">%s</td></tr>\n' % (nf, size_format(ts), len(uc), tc, len(ud), td, tn, ttn, size_format(tt))) else: s.write( '<tr><td align="right">%i files</td><td align="right">%i/%i</td><td align="right">%i/%i</td><td align="right">%i/%i</td></tr>\n' % (nf, len(uc), tc, len(ud), 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 (total: unique IPs/total connections)</li>\n' \ '<li><em>downloading:</em> number of connected clients still downloading (total: unique IPs/total connections)</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())