def _parseScrapeResponse(self, trackerSet, data): url = trackerSet['scrapeLogUrl'] infoHash = self.torrent.getTorrentHash() valid = False try: response = bdecode(data) except: self.log.warn('Failed to parse scrape response from tracker "%s":\n%s', url, logTraceback()) response = None if response is not None: if not isinstance(response, dict): #whatever this is, its not a standard response self.log.error('Scrape response from tracker "%s" is in an unknown format', url) else: valid = True if 'failure reason' in response: #request failed self.log.warn('Scrape request to tracker "%s" failed: "%s"', url, str(response['failure reason'])) else: if 'warning message' in response: #just a warning self.log.warn('Scrape request to tracker "%s" got warned: "%s"', url, str(response['warning message'])) if not 'files' in response: #files missing self.log.warn('Scrape response from tracker "%s" is incomplete ("file" key is missing)', url) elif not isinstance(response['files'], dict): #invalid format self.log.warn('Scrape response from tracker "%s" is in an unknown format (invalid type "%s" for key "files")', url, type(response['files'])) elif not infoHash in response['files']: #missing stats for this torrent self.log.warn('Scrape response from tracker "%s" contains no stats for this torrent', url) elif not isinstance(response['files'][infoHash], dict): #invalid format self.log.warn('Scrape response from tracker "%s" is in an unknown format (invalid type "%s" for torrent stats entry)', url, type(response['files'][infoHash])) else: #ok stats = response['files'][infoHash] #try to get counts seeds = stats.get('complete', 0) if not (isinstance(seeds, int) or isinstance(seeds, long)): self.log.warn('Scrape response from tracker "%s" contains invalid "complete" stats of type "%s"', url, type(seeds)) seeds = 0 leeches = stats.get('incomplete', 0) if not (isinstance(leeches, int) or isinstance(leeches, long)): self.log.warn('Scrape response from tracker "%s" contains invalid "incomplete" stats of type "%s"', url, type(leeches)) leeches = 0 downloads = stats.get('downloaded', 0) if not (isinstance(downloads, int) or isinstance(downloads, long)): self.log.warn('Scrape response from tracker "%s" contains invalid "downloaded" stats of type "%s"', url, type(downloads)) downloads = 0 #report self.log.info('Scrape response from tracker "%s" reported %i seeds, %i leeches and %i finished downloads', url, seeds, leeches, downloads) self.trackerInfo.setScrapeStats(trackerSet['id'], seeds, leeches, downloads) return valid
def load(self, torrentdata): #decode torrentdata torrentdata = bdecode(torrentdata) #encoding if 'encoding' in torrentdata: self.charset = torrentdata['encoding'] else: self.charset = 'UTF-8' #tracker urls self.announce = [[torrentdata['announce']]] self.amountOfTrackers = 1 if 'announce-list' in torrentdata: self.amountOfTrackers = 0 self.announce = torrentdata['announce-list'] for tier in self.announce: shuffle(tier) self.amountOfTrackers += len(tier) #check urls tierIdx = 0 while tierIdx < len(self.announce): tier = self.announce[tierIdx] urlIdx = 0 #check all url of tier while urlIdx < len(tier): if i2pHttpUrlRegexObj.match(tier[urlIdx]) is None: #invalid tracker url self.log.warn('Found invalid tracker url "%s", ignoring it', tier[urlIdx]) del tier[urlIdx] self.amountOfTrackers -= 1 else: #valid tracker url urlIdx += 1 #check if tier is empty if len(tier) == 0: self.log.warn('No valid tracker urls in tier, ignoring entire tier') del self.announce[tierIdx] else: tierIdx += 1 if self.amountOfTrackers == 0: raise TorrentException('Torrent contains no valid tracker urls!') #creation date of the torrent if 'creation date' in torrentdata: self.creationDate = torrentdata['creation date'] else: self.creationDate = None #torrent comment if 'comment' in torrentdata: self.comment = unicode(torrentdata['comment'], self.charset, 'ignore') else: self.comment = '' #creator of the torrent if 'created by' in torrentdata: self.createdBy = unicode(torrentdata['created by'], self.charset, 'ignore') else: self.createdBy = '' #torrent hash and name info = torrentdata['info'] self.torrentHash = sha1(bencode(info)).digest() self.torrentName = unicode(info['name'], self.charset, 'ignore') #files if 'files' in info: #multiple files self.files = [] sizesum = 0 for sfile in info['files']: #decode path for y in xrange(0, len(sfile['path'])): sfile['path'][y] = unicode(sfile['path'][y], self.charset, 'ignore') #add to list size = sfile['length'] self.files.append({'path':os.path.join(*sfile['path']), 'size':size, 'offset':sizesum}) sizesum += size #add dir name dirName = unicode(info['name'], self.charset, 'ignore') for i in xrange(0, len(self.files)): filePath = os.path.normpath(os.path.join(dirName, self.files[i]['path'])) if not filePath.startswith(dirName): #dangerous relative stuff => ../ and the like raise Exception('Security violation: file "%s" is not inside base directory "%s" (original path: "%s")' % (filePath, dirName, os.path.join((dirName, self.files[i]['path'])))) else: self.files[i]['path'] = filePath else: #only one file self.files = [{'path':os.path.normpath(unicode(info['name'], self.charset, 'ignore')), 'size':info['length'], 'offset':0}] #piece length self.pieceLength = info['piece length'] #piece hashes rawpieces = info['pieces'] self.pieceHashes = [] place = 0 while place < len(rawpieces): self.pieceHashes.append(rawpieces[place:place+20]) place += 20
def _parseAnnounceResponse(self, trackerSet, data): url = trackerSet['logUrl'] result = u'Invalid Response' #May be "Invalid Response", "Request Failed", "No Peers" or "Ok" errorMsg = None try: response = bdecode(data) except: self.log.warn('Failed to parse announce response from tracker "%s":\n%s', logTraceback(), url) response = None if response is not None: if not isinstance(response, dict): #whatever this is, its not a standard response self.log.error('Announce response from tracker "%s" is in an unknown format', url) else: if 'failure reason' in response: #request failed result = u'Request Failed' errorMsg = unicode(response['failure reason'], 'ascii', 'ignore') self.log.warn('Announce request to tracker "%s" failed: "%s"', url, unicode(response['failure reason'], 'ascii', 'ignore')) else: if 'warning message' in response: #just a warning self.log.warn('Announce request to tracker "%s" got warned: "%s"', url, unicode(response['warning message'], 'ascii', 'ignore')) if not 'peers' in response: #no peers in response result = u'No Peers' self.log.info('Tracker "%s" did not return any peers in its announce response', url) elif not isinstance(response['peers'], list): #probably a compact response - can only be used for IPs, so how should this be used with I2P? self.log.error('Tracker "%s" responded with a compact response to the announce request - not interpretable!', url) elif len(response['peers'])==0: #no peers in response result = u'No Peers' self.log.info('Tracker "%s" did not supply any peers in its announce response', url) else: #something valid result = u'No Peers' ownAddr = self.ownAddrFunc() for peer in response['peers']: #check each peer if not isinstance(peer, dict): #whatever this is, its nothing normal self.log.error('Tracker "%s" supplied peers in an unknown format in its announce response', url) elif not 'ip' in peer: #uhm, a peer without ip?! self.log.error('Tracker "%s" supplied peer data without desintations in its announce response!', url) elif not isinstance(peer['ip'], str): #uh, what kind of destination is this?! self.log.error('Tracker "%s" supplied a peer destination of the type "%s" in its announce response!', url, type(peer['ip'])) else: #finally, all checks passed, now parse the peer address parseResult = self.i2pHostChecker.search(peer['ip']) if parseResult is None: #urgh, address is invalid, all the trouble for nothing self.log.error('Tracker "%s" returned invalid peer with address "%s" in its announce response', url, peer['ip']) else: #valid address peerAddr = parseResult.group(1) if not peerAddr == ownAddr: result = u'Ok' self.log.debug('Tracker "%s" returned valid peer with address "%s" in its announce response', url, peerAddr) self.peerPool.addPossibleConnections(self.torrentIdent, [peerAddr]) return result, errorMsg