示例#1
0
 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
示例#2
0
    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
示例#3
0
 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