def connGotBitfield(self, connId):
     #got a bitfield from the peer, need to check if the peer already has some of the offered pieces
     self.lock.acquire()
     connSet = self.connInfo[connId]
     connStatus = connSet['conn'].getStatus()
     
     #check for offered pieces which the peer already has
     for pieceIndex in connStatus.getMatchingGotPieces(connSet['upPieces']):
         connSet['upPieces'].remove(pieceIndex)
         self.pieceStatus.decreaseAssignedUploads(pieceIndex)
         
     #offer new pieces if needed
     if len(connSet['upPieces']) < 2:
         wantedPieces = connStatus.getMatchingMissingPieces(self.ownStatus.getGotPieces())
         wantedPieces.difference_update(connSet['upPieces'])
         pieces = self.pieceStatus.getUpPieces(wantedPieces, 2 - len(connSet['upPieces']))
         if len(pieces) == 0:
             #no piece to offer
             self.log.debug('Conn %-6i - Nothing to offer (adding to waiting list)', connId)
             self.waitingConns.add(connId)
         else:
             #offer pieces
             self.log.debug('Conn %-6i - Offering pieces %s', connId, ', '.join(str(x) for x in pieces))
             for pieceIndex in pieces:
                 connSet['upPieces'].add(pieceIndex)
                 connSet['conn'].send(Messages.generateHave(pieceIndex))
     self.lock.release()
 def gotNewPiece(self, pieceIndex):
     #we got a new piece - shouldn't happen during super seeding but whatever
     self.lock.acquire()
     for connId in self.waitingConns.copy():
         connSet = self.connInfo[connId]
         if not connSet['conn'].getStatus().hasPiece(pieceIndex):
             self.log.debug('Conn %-6i - Offering piece %i', connId, pieceIndex)
             connSet['upPieces'].add(pieceIndex)
             self.pieceStatus.increaseAssignedUploads(pieceIndex)
             connSet['conn'].send(Messages.generateHave(pieceIndex))
             if len(connSet['upPieces']) == 2:
                 self.waitingConns.remove(connId)
     self.lock.release()
 def addConn(self, connId, conn):
     #called when a new connection is established (before we get a bitfield from the peer)
     self.lock.acquire()
     upPieces = self.pieceStatus.getUpPieces(self.ownStatus.getGotPieces(), 2)
     assert len(upPieces) <= 2, 'Got more then we want: '+str(len(upPieces))
     self.connInfo[connId] = {'conn':conn,
                              'upPieces':upPieces}
     if len(upPieces) == 2:
         #enough pieces to offer
         self.log.debug('Conn %-6i - Added, offering pieces %s', connId, ', '.join(str(x) for x in upPieces))
     else:
         #not enough pieces to offer
         self.log.debug('Conn %-6i - Added, offering pieces %s (adding to waiting list)', connId, ', '.join(str(x) for x in upPieces))
         self.waitingConns.add(connId)
         
     for pieceIndex in upPieces:
         conn.send(Messages.generateHave(pieceIndex))
     self.lock.release()
 def connGotPiece(self, connId, pieceIndex):
     #peer got a new piece, need to offer a new one if it was one of the pieces we offered
     self.lock.acquire()
     connSet = self.connInfo[connId]
     connStatus = connSet['conn'].getStatus()
     if connStatus.isSeed():
         #does not need any pieces
         if pieceIndex in connSet['upPieces']:
             connSet['upPieces'].remove(pieceIndex)
             self.pieceStatus.decreaseAssignedUploads(pieceIndex)
             
         assert len(connSet['upPieces']) == 0, 'Does not need any pieces but we offer some?! (len: '+str(len(connSet['upPieces']))+')'
         self.log.debug('Conn %-6i - Got piece %i and does not need any other pieces (is now seed)', connId, pieceIndex)
         self.waitingConns.remove(connId)
     else:
         #needs some, so offer one more if the one obtained from it was one of the offered ones
         if not pieceIndex in connSet['upPieces']:
             #was not one of the offered pieces, nothing to do
             self.log.debug('Conn %-6i - Got piece %i which is not one of the offered pieces', connId, pieceIndex)
         else:
             #was one of the offered pieces, offer another one
             connSet['upPieces'].remove(pieceIndex)
             self.pieceStatus.decreaseAssignedUploads(pieceIndex)
             
             wantedPieces = connStatus.getMatchingMissingPieces(self.ownStatus.getGotPieces())
             wantedPieces.difference_update(connSet['upPieces'])
             upPiece = self.pieceStatus.getUpPieces(wantedPieces, 1)
             assert len(upPiece) <= 1, 'Got more then we want: '+str(len(upPiece))
             if len(upPiece) == 0:
                 #no piece to offer
                 self.log.debug('Conn %-6i - Got piece %i but nothing to offer (adding to waiting list)', connId, pieceIndex)
                 self.waitingConns.add(connId)
             else:
                 #offer piece
                 upPiece = upPiece.pop()
                 self.log.debug('Conn %-6i - Got piece %i, offering piece %i', connId, pieceIndex, upPiece)
                 connSet['upPieces'].add(upPiece)
                 connSet['conn'].send(Messages.generateHave(upPiece))
     self.lock.release()
    def _handleMessage(self, connId, conn, message):
        if message[0] == -1:
            #keepalive
            self.log.debug('Conn %i: Got keepalive', connId)
            
        elif message[0] == 0:
            #remote choke
            self.log.debug('Conn %i: Got choked', connId)
            self._getTorrentInfo(conn)['requester'].connGotChoked(conn)
            conn.setRemoteChoke(True)
            
        elif message[0] == 1:
            #remote unchoke
            self.log.debug('Conn %i: Got unchoked', connId)
            conn.setRemoteChoke(False)
            if conn.localInterested():
                #and we are interested - means there should be something requestable
                self._getTorrentInfo(conn)['requester'].connGotUnchoked(conn)  
            
        elif message[0] == 2:
            #remote interested
            self.log.debug('Conn %i: Peer is interested in us', connId)
            conn.setRemoteInterest(True)
            
        elif message[0] == 3:
            #remote not interested
            self.log.debug('Conn %i: Peer is no longer interested in us', connId)
            conn.setRemoteInterest(False)
                
        elif message[0] == 4:
            #remote got a new piece
            self.log.debug('Conn %i: Peer finished piece %i',\
                           connId, message[1])
            torrent = self._getTorrentInfo(conn)
            status = conn.getStatus()
            status.gotPiece(message[1])
            
            if torrent['ownStatus'].isFinished() and not status.hasMatchingMissingPieces(torrent['ownStatus'].getGotPieces()):
                #nothing to gain, nothing to give - diconnect
                self._removeConnection(connId, "we are finished downloading and this peer has already all pieces which we have", False)
            
            else:
                #inform superseeding handler if needed
                if torrent['superSeedingEnabled']:
                    torrent['superSeedingHandler'].connGotPiece(connId, message[1])
                    
                #check if we are again interested in that peer
                if (not conn.localInterested()) and torrent['ownStatus'].needsPiece(message[1]):
                    #we were not interested, but now this peer got a piece we need, so we are interested
                    self.log.debug('Conn %i: Interested in peer after he got piece %i', connId, message[1])
                    conn.setLocalInterest(True)
                    
                    if (not conn.remoteChoked()):
                        #we are already unchoked, spawn some requests!
                        self.log.debug('Conn %i: Peer unchoked us in the past!', connId)
                        torrent['requester'].connGotUnchoked(conn)
            
        elif message[0] == 5:
            #remotes bitfield
            self.log.debug('Conn %i: Got bitfield', connId)
            torrent = self._getTorrentInfo(conn)
            status = conn.getStatus()
            status.addBitfield(message[1])

            if torrent['ownStatus'].isFinished() and not status.hasMatchingMissingPieces(torrent['ownStatus'].getGotPieces()):
                #nothing to gain, nothing to give - diconnect
                self._removeConnection(connId, "we are finished downloading and this peer has already all pieces which we have", False)
            
            else:
                #inform superseeding handler if needed
                if torrent['superSeedingEnabled']:
                    torrent['superSeedingHandler'].connGotBitfield(connId)
                    
                #check if the peer has something interesting
                if status.hasMatchingGotPieces(torrent['ownStatus'].getNeededPieces()):
                    #yep he has
                    self.log.debug('Conn %i: Interested in peer after getting bitfield', connId)
                    conn.setLocalInterest(True)
                
        elif message[0] == 6:
            #remote request
            self.log.debug('Conn %i: Got request for piece %i with offset %i and length %i',\
                            connId, message[1][0], message[1][1], message[1][2])
            dataHandle = self._getTorrentInfo(conn)['storage'].getDataHandle(message[1][0],message[1][1],message[1][2])
            conn.addOutRequest(message[1][0],message[1][1],message[1][2], dataHandle)
            
        elif message[0] == 7:
            #got data
            self.log.debug('Conn %i: Got data for piece %i with offset %i and length %i',\
                           connId, message[1][0], message[1][1], len(message[1][2]))
                        
            #remove request from list
            conn.finishedInRequest(message[1][0], message[1][1], len(message[1][2]))
            
            #notify requester
            torrent = self._getTorrentInfo(conn)
            if torrent['requester'].finishedRequest(message[1][2], conn, message[1][0], message[1][1]):
                #finished to retrieve a whole piece
                self.log.debug('Piece %i is finished', message[1][0])
                
                #deal with peers
                gotPieces = torrent['ownStatus'].getGotPieces()
                neededPieces = torrent['ownStatus'].getNeededPieces()
                weAreFinished = torrent['ownStatus'].isFinished()
                weAreSuperSeeding = torrent['superSeedingEnabled']
                
                for connId in torrent['connIds'].copy():
                    conn = self.conns[connId]
                    status = conn.getStatus()
                    
                    #send have if needed
                    if not weAreSuperSeeding:
                        conn.send(Messages.generateHave(message[1][0]))
                    
                    if weAreFinished and not status.hasMatchingMissingPieces(gotPieces):
                        #nothing to gain, nothing to give - diconnect
                        self._removeConnection(connId, "we are finished downloading and this peer has already all pieces which we have", False)
                    
                    else:
                        if conn.localInterested():
                            #we were interested up to now
                            if not status.hasMatchingGotPieces(neededPieces):
                                #nothing to request anymore
                                conn.setLocalInterest(False)
                                torrent['requester'].connGotNotInteresting(conn)
                
                if weAreSuperSeeding:
                    torrent['superSeedingHandler'].gotNewPiece(message[1][0])
                            
        elif message[0] == 8:
            #cancel
            self.log.debug('Conn %i: Peer canceled request of piece %i with offset %i and length %i',
                           connId, message[1][0], message[1][1], message[1][2])
            conn.delOutRequest(message[1][0], message[1][1], message[1][2])
            
        else:
            self.log.error('Conn %i: Unmatched message with ID: %d - shouldn\'t reach this point!', connId, message[0])