def setUpDB(self):
        try:
            self.richMetadata_db = self.session.open_dbhandler(
                NTFY_RICH_METADATA)

            #add some metadata
            self.mdto = MetadataDTO(self.anotherpermid, self.testInfohash)
            subtitle1 = SubtitleInfo("nld", self.sub1)
            subtitle1.computeChecksum()
            subtitle2 = SubtitleInfo("eng", os.path.join(RES_DIR, "fake0.srt"))
            subtitle2.computeChecksum()
            self.mdto.addSubtitle(subtitle1)
            self.mdto.addSubtitle(subtitle2)

            self.mdto.sign(self.another_keypair)

            self.richMetadata_db.insertMetadata(self.mdto)

            #hisoermid has the nld subtitle but doesn't have the english one
            self.richMetadata_db.updateSubtitlePath(self.mdto.channel,
                                                    self.mdto.infohash, "eng",
                                                    None)

        except:
            print_exc()
Esempio n. 2
0
    def getSomeMetadata(self, channel_id, infohash):


        s1 = SubtitleInfo("eng", None)
        s2 = SubtitleInfo("rus", None)

        self.content1 = u"Subtitle Content 1"
        self.content2 = u"Subtitle Content 2"

        hasher = sha()
        hasher.update(self.content1)
        s1.checksum = hasher.digest()

        hasher = sha()
        hasher.update(self.content2)
        s2.checksum = hasher.digest()

        metadata = MetadataDTO(channel_id, infohash, time.time(),
                               "", {"eng":s1, "rus":s2})

        if self.nextKeypair is None:
            metadata.signature = "fake"
        else:
            metadata.sign(self.nextKeypair)

        return metadata
    def getSomeMetadata(self, channel_id, infohash):

        s1 = SubtitleInfo("eng", None)
        s2 = SubtitleInfo("rus", None)

        self.content1 = u"Subtitle Content 1"
        self.content2 = u"Subtitle Content 2"

        hasher = sha()
        hasher.update(self.content1)
        s1.checksum = hasher.digest()

        hasher = sha()
        hasher.update(self.content2)
        s2.checksum = hasher.digest()

        metadata = MetadataDTO(channel_id, infohash, time.time(), "", {
            "eng": s1,
            "rus": s2
        })

        if self.nextKeypair is None:
            metadata.signature = "fake"
        else:
            metadata.sign(self.nextKeypair)

        return metadata
    def testDesrialize(self):
        badInfohash = str2bin("GEh/o8rtTLB1wZJzFcSZSS4u9qo=")
        dto = MetadataDTO(test_perm_id, badInfohash)
        dto.description = u"Sample Description"
        dto.sign(test_keypair)

        serialized = dto.serialize()
        newDto = MDUtil.deserialize(serialized)
        self.assertEquals(dto, newDto)
    def testSerialize(self):
        badInfohash = str2bin("GEh/o8rtTLB1wZJzFcSZSS4u9qo=")
        dto = MetadataDTO(test_perm_id, badInfohash)
        dto.description = u"Sample Description"
        dto.sign(test_keypair)

        serialized = dto.serialize()
        self.assertEquals(7, len(serialized))
        signature = serialized[6]
        self.assertEquals(dto.signature, signature)
 def getMetadata(self, channel, infohash):
     """
     Returns a MetadataDTO instance for channel/infohash if available in DB
     
     Given a channel/infhash couple returns a MetadataDTO instance, built
     with the values retrieved from the Metadata and Subtitles DB. If
     no result returns None
     
     @param channel: the permid of the channel's owner (binary)
     @param infohash: the infohash of the item the metadata refers to
                      (binary)
     @return: a MetadataDTO instance comprehensive of subtitles if any
              metadata is found in the DB. None otherwise.
     """
     
     query = QUERIES["SELECT METADATA"]
     
     infohash = bin2str(infohash)
     channel = bin2str(channel)
     
     res = self._db.fetchall(query, (infohash, channel))
     
     if len(res) == 0:
         return None
     if len(res) > 1:
         raise MetadataDBException("Metadata DB Constraint violated")
     
     metaTuple = res[0]
     
     subsDictionary = self._getAllSubtitlesByKey(metaTuple[0])
     
     publisher = str2bin(metaTuple[1])
     infohash =  str2bin(metaTuple[2])
     timestamp = int(metaTuple[4])
     description = unicode(metaTuple[3])
     signature = str2bin(metaTuple[5])
     
     toReturn = MetadataDTO(publisher, infohash,
                            timestamp, description, None,
                            signature)
     
     for sub in subsDictionary.itervalues():
         toReturn.addSubtitle(sub)
     
     return toReturn
Esempio n. 7
0
    def getMetadata(self, channel, infohash):
        """
        Returns a MetadataDTO instance for channel/infohash if available in DB
        
        Given a channel/infhash couple returns a MetadataDTO instance, built
        with the values retrieved from the Metadata and Subtitles DB. If
        no result returns None
        
        @param channel: the permid of the channel's owner (binary)
        @param infohash: the infohash of the item the metadata refers to
                         (binary)
        @return: a MetadataDTO instance comprehensive of subtitles if any
                 metadata is found in the DB. None otherwise.
        """

        query = QUERIES["SELECT METADATA"]

        infohash = bin2str(infohash)
        channel = bin2str(channel)

        res = self._db.fetchall(query, (infohash, channel))

        if len(res) == 0:
            return None
        if len(res) > 1:
            raise MetadataDBException("Metadata DB Constraint violated")

        metaTuple = res[0]

        subsDictionary = self._getAllSubtitlesByKey(metaTuple[0])

        publisher = str2bin(metaTuple[1])
        infohash = str2bin(metaTuple[2])
        timestamp = int(metaTuple[4])
        description = unicode(metaTuple[3])
        signature = str2bin(metaTuple[5])

        toReturn = MetadataDTO(publisher, infohash, timestamp, description,
                               None, signature)

        for sub in subsDictionary.itervalues():
            toReturn.addSubtitle(sub)

        return toReturn
    def testSignature(self):
        badInfohash = str2bin("GEh/o8rtTLB1wZJzFcSZSS4u9qo=")
        dto = MetadataDTO(test_perm_id, badInfohash)

        dto.sign(test_keypair)
        self.assertTrue(dto.verifySignature())
        dto.timestamp = 2
        ok = dto.verifySignature()
        self.assertFalse(ok)
 def testMetadataDTOInit(self):
     badInfohash = str2bin("GEh/o8rtTLB1wZJzFcSZSS4u9qo=")
     dto = MetadataDTO(test_perm_id, badInfohash)
     self.assertFalse(dto is None)
     self.assertEqual(test_perm_id, dto.channel)
     self.assertEquals(badInfohash, dto.infohash)
     current = time.time()
     self.assertTrue(current - 1 <= int(dto.timestamp) <= current)
     self.assertEquals("", dto.description)
     self.assertEquals({}, dto._subtitles)
     self.assertTrue(dto.signature is None)
    def setupDB(self, nickname):
        TestChannels.setupDB(self, nickname)
        try:
            self.richMetadata_db = self.session.open_dbhandler(
                NTFY_RICH_METADATA)
            #add some metadata for torrents (they are defined in TestChannels.setupDB()
            self.mdto = MetadataDTO(self.hispermid, self.infohash1)
            subtitle1 = SubtitleInfo("nld", os.path.join(RES_DIR, "fake.srt"))
            subtitle1.computeChecksum()

            subtitle2 = SubtitleInfo("eng", os.path.join(RES_DIR, "fake0.srt"))
            subtitle2.computeChecksum()
            self.mdto.addSubtitle(subtitle1)
            self.mdto.addSubtitle(subtitle2)

            self.mdto.sign(self.his_keypair)

            self.richMetadata_db.insertMetadata(self.mdto)
        except:
            print_exc()
Esempio n. 11
0
    def test_packData(self):
        badInfohash = str2bin("GEh/o8rtTLB1wZJzFcSZSS4u9qo=")
        dto = MetadataDTO(test_perm_id, badInfohash)
        dto.description = u"Sample Description\u041f"

        bla = dto._packData()
        decoded = bdecode(bla)

        self.assertTrue(len(decoded) == 6)
        decodedChannelId = decoded[0]
        decodedInfohash = decoded[1]
        decodedDescription = decoded[2].decode("utf-8")
        decodedTimestamp = decoded[3]
        bin_decodedBitmask = decoded[4]
        decodedBitmask, = unpack("!L", bin_decodedBitmask)
        self.assertEquals(dto.channel, decodedChannelId)
        self.assertEquals(dto.infohash, decodedInfohash)
        self.assertEquals(dto.description, decodedDescription)
        self.assertAlmostEquals(dto.timestamp, decodedTimestamp)
        self.assertEquals(0, decodedBitmask)
        self.assertEquals(0, len(decoded[5]))
    def test_packData(self):
        badInfohash = str2bin("GEh/o8rtTLB1wZJzFcSZSS4u9qo=")
        dto = MetadataDTO(test_perm_id, badInfohash)
        dto.description = u"Sample Description\u041f"

        bla = dto._packData()
        decoded = bdecode(bla)

        self.assertTrue(len(decoded) == 6)
        decodedChannelId = decoded[0]
        decodedInfohash = decoded[1]
        decodedDescription = decoded[2].decode("utf-8")
        decodedTimestamp = decoded[3]
        bin_decodedBitmask = decoded[4]
        decodedBitmask, = unpack("!L", bin_decodedBitmask)
        self.assertEquals(dto.channel, decodedChannelId)
        self.assertEquals(dto.infohash, decodedInfohash)
        self.assertEquals(dto.description, decodedDescription)
        self.assertAlmostEquals(dto.timestamp, decodedTimestamp)
        self.assertEquals(0, decodedBitmask)
        self.assertEquals(0, len(decoded[5]))
    def testSignatureOnChecksums(self):
        badInfohash = str2bin("GEh/o8rtTLB1wZJzFcSZSS4u9qo=")
        dto = MetadataDTO(test_perm_id, badInfohash)

        subtitles = [
            SubtitleInfo(lang, path)
            for lang, path in self._srtSubs.iteritems()
        ]

        for sub in subtitles:
            sub.computeChecksum()
            dto.addSubtitle(sub)

        dto.sign(test_keypair)
        self.assertTrue(dto.verifySignature())

        dto.getSubtitle("rus").checksum = "ABCDEFGHILMOPQRS"

        self.assertFalse(dto.verifySignature())
Esempio n. 14
0
 def test_packDataWithSubs(self):
     badInfohash = str2bin("GEh/o8rtTLB1wZJzFcSZSS4u9qo=")
     dto = MetadataDTO(test_perm_id, badInfohash)
     
     subtitles = [SubtitleInfo(lang,path) for lang,path in self._srtSubs.iteritems()]
     
     for sub in subtitles :
         sub.computeChecksum()
         dto.addSubtitle(sub)
     
     packed = dto._packData()
     decoded = bdecode(packed)
     
     self.assertTrue(len(decoded) == 6)
     decodedChannelId = decoded[0]
     decodedInfohash = decoded[1]
     decodedDescription = decoded[2]
     decodedTimestamp = decoded[3]
     decodedBitmask = decoded[4]
     checksums = decoded[5]
     
     expectedMask = \
       LanguagesProvider.getLanguagesInstance().langCodesToMask(self._srtSubs.keys())
       
     binaryExpexted = pack("!L", expectedMask)
     
     self.assertEquals(dto.channel, decodedChannelId)
     self.assertEquals(dto.infohash, decodedInfohash)
     self.assertEquals(dto.description,decodedDescription)
     self.assertAlmostEquals(dto.timestamp,decodedTimestamp)
     self.assertEquals(binaryExpexted,decodedBitmask)
     self.assertEquals(3,len(checksums))
     
     subs = dto.getAllSubtitles()
     i=0
     for key in sorted(subs.iterkeys()):
         self.assertEquals(subs[key].checksum, checksums[i])
         i += 1
Esempio n. 15
0
    def testSignatureOnChecksums(self):
        badInfohash = str2bin("GEh/o8rtTLB1wZJzFcSZSS4u9qo=")
        dto = MetadataDTO(test_perm_id, badInfohash)

        subtitles = [SubtitleInfo(lang, path) for lang, path in self._srtSubs.iteritems()]

        for sub in subtitles:
            sub.computeChecksum()
            dto.addSubtitle(sub)

        dto.sign(test_keypair)
        self.assertTrue(dto.verifySignature())

        dto.getSubtitle("rus").checksum = "ABCDEFGHILMOPQRS"

        self.assertFalse(dto.verifySignature())
Esempio n. 16
0
    def testSignature(self):
        badInfohash = str2bin("GEh/o8rtTLB1wZJzFcSZSS4u9qo=")
        dto = MetadataDTO(test_perm_id, badInfohash)

        dto.sign(test_keypair)
        self.assertTrue(dto.verifySignature())
        dto.timestamp = 2
        ok = dto.verifySignature()
        self.assertFalse(ok)
    def testDesrialize(self):
        badInfohash = str2bin("GEh/o8rtTLB1wZJzFcSZSS4u9qo=")
        dto = MetadataDTO(test_perm_id, badInfohash)
        dto.description = u"Sample Description"
        dto.sign(test_keypair)

        serialized = dto.serialize()
        newDto = MDUtil.deserialize(serialized)
        self.assertEquals(dto, newDto)
    def testSerialize(self):
        badInfohash = str2bin("GEh/o8rtTLB1wZJzFcSZSS4u9qo=")
        dto = MetadataDTO(test_perm_id, badInfohash)
        dto.description = u"Sample Description"
        dto.sign(test_keypair)

        serialized = dto.serialize()
        self.assertEquals(7, len(serialized))
        signature = serialized[6]
        self.assertEquals(dto.signature, signature)
    def testDeserializeWithSubs(self):
        badInfohash = str2bin("GEh/o8rtTLB1wZJzFcSZSS4u9qo=")
        dto = MetadataDTO(test_perm_id, badInfohash)

        subtitles = [
            SubtitleInfo(lang, path)
            for lang, path in self._srtSubs.iteritems()
        ]

        for sub in subtitles:
            sub.computeChecksum()
            dto.addSubtitle(sub)
        dto.sign(test_keypair)

        serial = dto.serialize()
        newDto = MDUtil.deserialize(serial)
        self.assertEquals(dto, newDto)
 def setupDB(self,nickname):
     TestChannels.setupDB(self,nickname)
     try:
         self.richMetadata_db = self.session.open_dbhandler(NTFY_RICH_METADATA)
         #add some metadata for torrents (they are defined in TestChannels.setupDB()
         self.mdto = MetadataDTO(self.hispermid, self.infohash1)
         subtitle1 = SubtitleInfo("nld", os.path.join(RES_DIR,"fake.srt"))
         subtitle1.computeChecksum()
         
         subtitle2 = SubtitleInfo("eng", os.path.join(RES_DIR, "fake0.srt"))
         subtitle2.computeChecksum()
         self.mdto.addSubtitle(subtitle1)
         self.mdto.addSubtitle(subtitle2)
         
         self.mdto.sign(self.his_keypair)
         
         self.richMetadata_db.insertMetadata(self.mdto)
     except:
         print_exc()
Esempio n. 21
0
    def testDeserializeWithSubs(self):
        badInfohash = str2bin("GEh/o8rtTLB1wZJzFcSZSS4u9qo=")
        dto = MetadataDTO(test_perm_id, badInfohash)

        subtitles = [SubtitleInfo(lang, path) for lang, path in self._srtSubs.iteritems()]

        for sub in subtitles:
            sub.computeChecksum()
            dto.addSubtitle(sub)
        dto.sign(test_keypair)

        serial = dto.serialize()
        newDto = MDUtil.deserialize(serial)
        self.assertEquals(dto, newDto)
    def testSerializeWithSubs(self):
        badInfohash = str2bin("GEh/o8rtTLB1wZJzFcSZSS4u9qo=")
        dto = MetadataDTO(test_perm_id, badInfohash)

        subtitles = [
            SubtitleInfo(lang, path)
            for lang, path in self._srtSubs.iteritems()
        ]

        for sub in subtitles:
            sub.computeChecksum()
            dto.addSubtitle(sub)
        dto.sign(test_keypair)

        serial = dto.serialize()
        decoded = serial
        self.assertEquals(7, len(decoded))
        signature = decoded[6]
        self.assertEquals(dto.signature, signature)
Esempio n. 23
0
    def testSerializeWithSubs(self):
        badInfohash = str2bin("GEh/o8rtTLB1wZJzFcSZSS4u9qo=")
        dto = MetadataDTO(test_perm_id, badInfohash)

        subtitles = [SubtitleInfo(lang, path) for lang, path in self._srtSubs.iteritems()]

        for sub in subtitles:
            sub.computeChecksum()
            dto.addSubtitle(sub)
        dto.sign(test_keypair)

        serial = dto.serialize()
        decoded = serial
        self.assertEquals(7, len(decoded))
        signature = decoded[6]
        self.assertEquals(dto.signature, signature)
    def setUpDB(self):
        try:
            self.richMetadata_db = self.session.open_dbhandler(NTFY_RICH_METADATA)

            #add some metadata
            self.mdto = MetadataDTO(self.anotherpermid, self.testInfohash)
            subtitle1 = SubtitleInfo("nld", self.sub1)
            subtitle1.computeChecksum()
            subtitle2 = SubtitleInfo("eng", os.path.join(RES_DIR, "fake0.srt"))
            subtitle2.computeChecksum()
            self.mdto.addSubtitle(subtitle1)
            self.mdto.addSubtitle(subtitle2)
            
            self.mdto.sign(self.another_keypair)
            
            self.richMetadata_db.insertMetadata(self.mdto)
            
            
            
            #hisoermid has the nld subtitle but doesn't have the english one
            self.richMetadata_db.updateSubtitlePath(self.mdto.channel,self.mdto.infohash,"eng",None)
            
        except:
            print_exc()
    def test_packDataWithSubs(self):
        badInfohash = str2bin("GEh/o8rtTLB1wZJzFcSZSS4u9qo=")
        dto = MetadataDTO(test_perm_id, badInfohash)

        subtitles = [
            SubtitleInfo(lang, path)
            for lang, path in self._srtSubs.iteritems()
        ]

        for sub in subtitles:
            sub.computeChecksum()
            dto.addSubtitle(sub)

        packed = dto._packData()
        decoded = bdecode(packed)

        self.assertTrue(len(decoded) == 6)
        decodedChannelId = decoded[0]
        decodedInfohash = decoded[1]
        decodedDescription = decoded[2]
        decodedTimestamp = decoded[3]
        decodedBitmask = decoded[4]
        checksums = decoded[5]

        expectedMask = \
          LanguagesProvider.getLanguagesInstance().langCodesToMask(self._srtSubs.keys())

        binaryExpexted = pack("!L", expectedMask)

        self.assertEquals(dto.channel, decodedChannelId)
        self.assertEquals(dto.infohash, decodedInfohash)
        self.assertEquals(dto.description, decodedDescription)
        self.assertAlmostEquals(dto.timestamp, decodedTimestamp)
        self.assertEquals(binaryExpexted, decodedBitmask)
        self.assertEquals(3, len(checksums))

        subs = dto.getAllSubtitles()
        i = 0
        for key in sorted(subs.iterkeys()):
            self.assertEquals(subs[key].checksum, checksums[i])
            i += 1
class TestSubtitleMessages(TestAsServer):
    
    def setUpPreSession(self):
        """ override TestAsServer """
        TestAsServer.setUpPreSession(self)
        self.config.set_buddycast(True)
        BuddyCastCore.TESTASSERVER = True
        ChannelCastCore.TESTASSERVER = True
        VoteCastCore.TESTASSERVER = True
        self.config.set_start_recommender(True)
        self.config.set_bartercast(True) 
        self.config.set_remote_query(True)
        self.config.set_crawler(False)       
        self.config.set_torrent_collecting_dir(os.path.join(self.config_path, "tmp_torrent_collecting"))
        
        self.collecting_dir = os.path.join(self.config_path, "temp_subtitles_collecting")
        os.makedirs(self.collecting_dir)
        self.config.set_subtitles_collecting(True)
        self.config.set_subtitles_collecting_dir(self.collecting_dir)
        
        

#        # Write superpeers.txt and DB schema
        self.install_path = tempfile.mkdtemp()
        spdir = os.path.join(self.install_path, LIBRARYNAME, 'Core')
        os.makedirs(spdir)

        statsdir = os.path.join(self.install_path, LIBRARYNAME, 'Core', 'Statistics')
        os.makedirs(statsdir)
        
        superpeerfilename = os.path.join(spdir, 'superpeer.txt')
        print >> sys.stderr,"test: writing empty superpeers to",superpeerfilename
        f = open(superpeerfilename, "w")
        f.write('# Leeg')
        f.close()

        self.config.set_install_dir(self.install_path)
        
        srcfiles = []
        srcfiles.append(os.path.join(LIBRARYNAME,"schema_sdb_v5.sql"))
        for srcfile in srcfiles:
            sfn = os.path.join('..','..',srcfile)
            dfn = os.path.join(self.install_path,srcfile)
            print >>sys.stderr,"test: copying",sfn,dfn
            shutil.copyfile(sfn,dfn)
            
        #copy subtitles files in the appropriate subtitles folder
        self.src1 = os.path.join(RES_DIR,'fake.srt')
        self.src2 = os.path.join(RES_DIR,'fake0.srt')
        

            
            
    def setUpPostSession(self):
        """ override TestAsServer """
        TestAsServer.setUpPostSession(self)

        self.mypermid = str(self.my_keypair.pub().get_der())
        self.hispermid = str(self.his_keypair.pub().get_der())
        
        self.another_keypair = generate_keypair()
        self.anotherpermid = str(self.another_keypair.pub().get_der())
        
        self.testInfohash = hashlib.sha1("yoman!").digest()
        
        #copy subtitles in the collecting dir
        nldName = SubUtils.getSubtitleFileRelativeName(self.anotherpermid, self.testInfohash, "nld")
        engName = SubUtils.getSubtitleFileRelativeName(self.anotherpermid, self.testInfohash, "eng")
        
        self.sub1 = os.path.join(self.collecting_dir, nldName)
        self.sub2 = os.path.join(self.collecting_dir, engName)
        
        shutil.copyfile(self.src1, self.sub1)
        # Let's say that the receiving peer has only the nld subtitle
        # avialable locally 
        # shutil.copyfile(self.src2, self.sub2)
        
    
    def setUpDB(self):
        try:
            self.richMetadata_db = self.session.open_dbhandler(NTFY_RICH_METADATA)

            #add some metadata
            self.mdto = MetadataDTO(self.anotherpermid, self.testInfohash)
            subtitle1 = SubtitleInfo("nld", self.sub1)
            subtitle1.computeChecksum()
            subtitle2 = SubtitleInfo("eng", os.path.join(RES_DIR, "fake0.srt"))
            subtitle2.computeChecksum()
            self.mdto.addSubtitle(subtitle1)
            self.mdto.addSubtitle(subtitle2)
            
            self.mdto.sign(self.another_keypair)
            
            self.richMetadata_db.insertMetadata(self.mdto)
            
            
            
            #hisoermid has the nld subtitle but doesn't have the english one
            self.richMetadata_db.updateSubtitlePath(self.mdto.channel,self.mdto.infohash,"eng",None)
            
        except:
            print_exc()
        
        
    def tearDown(self):
        TestAsServer.tearDown(self)
        self.session.close_dbhandler(self.richMetadata_db)
        
    
        
    def subtest_receptionOfSUBS(self):
        '''
        Asking for the single available subtitle. The response should be 
        a valid SUBS message containing its contents
        '''
        
        print >> sys.stderr, "test: test_subtitles_msgs_1_1 -----------------------"
        ol_conn = OLConnection(self.my_keypair,'localhost',self.hisport)
        
        bitmask = LanguagesProvider.getLanguagesInstance().langCodesToMask(['nld'])
        binmask = utilities.uintToBinaryString(bitmask, length=4)
        
        request = GET_SUBS + \
                      bencode((
                              self.anotherpermid,
                              self.testInfohash,
                              binmask
                              ))
                      
        subshandler = SubtitlesHandler()
        subshandler.register(ol_conn, self.richMetadata_db, self.session)
        
        ol_conn.send(request)
        subs_data = ol_conn.recv()
        print >> sys.stderr, "test: subtitles_messages : received SUBS response: len",len(subs_data)
        self.assertEquals(SUBS, subs_data[0])
        data = bdecode(subs_data[1:])
        print >> sys.stderr, "test: subtitles_messages : received SUBS response: ", data
        
        #check on the format of the response
        self.assertTrue(isinstance(data,list))
        self.assertEquals(4, len(data)) # for fields
        self.assertEquals(self.mdto.channel,data[0])
        self.assertEquals(self.mdto.infohash, data[1])
        self.assertEquals(binmask, data[2])
        self.assertTrue(isinstance(data[3],list))
        self.assertEquals(1, len(data[3]))
        with codecs.open(self.sub1, "rb", "utf-8") as sub:
            expectedContents = sub.read()
        self.assertEquals(expectedContents, data[3][0])
        
        ol_conn.close()
        
        print >> sys.stderr, "test: subtitles_messages: received content is valid."
        print >> sys.stderr, "End of test_subtitles_msgs_1_1 test --------------------"
        
        
    def subtest_receptionOfSUBSTwoRequestsOneAvailable(self):
        """
        Asking for two subtitles while the recipent of the request has only one.
        The response should contain only the one available subtitle content,
        plus a bitmask that reflects the contents of the response.
        """
        
        print >> sys.stderr, "test: test_subtitles_msgs_2_1 -----------------------"
        ol_conn = OLConnection(self.my_keypair,'localhost',self.hisport)
        
        bitmask = LanguagesProvider.getLanguagesInstance().langCodesToMask(['nld','eng'])
        binmask = utilities.uintToBinaryString(bitmask, length=4)
        
        request = GET_SUBS + \
                      bencode((
                              self.anotherpermid,
                              self.testInfohash,
                              binmask
                              ))
                      
        subshandler = SubtitlesHandler()
        subshandler.register(ol_conn, self.richMetadata_db, self.session)
        
        ol_conn.send(request)
        subs_data = ol_conn.recv()
        self.assertEquals(SUBS, subs_data[0])
        data = bdecode(subs_data[1:])
        print >> sys.stderr, "test: subtitles_messages : received SUBS repsonse: ", data
        
        #check on the format of the response
        self.assertTrue(isinstance(data,list))
        self.assertEquals(4, len(data)) # for fields
        self.assertEquals(self.mdto.channel,data[0])
        self.assertEquals(self.mdto.infohash, data[1])
        
        #the receiver had only one of the two requested subtitles
        # so I expect a different bitmask
        bitmask = LanguagesProvider.getLanguagesInstance().langCodesToMask(['nld'])
        expectedBinarymask = utilities.uintToBinaryString(bitmask, length=4)
        
        self.assertEquals(expectedBinarymask, data[2])
        self.assertTrue(isinstance(data[3],list))
        self.assertEquals(1, len(data[3]))
        with codecs.open(self.sub1, "rb", "utf-8") as sub:
            expectedContents = sub.read()
        self.assertEquals(expectedContents, data[3][0])
        
        ol_conn.close()
        print >> sys.stderr, "test: subtitles_messages: received content is valid."
        print >> sys.stderr, "End of test_subtitles_msgs_2_1 test --------------------"
        
    def subtest_invalidRequest1(self):
        """
        Trying to send an empty message.
        The connection should be closed by the receiver
        """
        print >> sys.stderr, "test: test_subtitles_msgs_invalid_request_1 ------------------"
        ol_conn = OLConnection(self.my_keypair,'localhost',self.hisport)

        
        request = GET_SUBS + \
                    bencode({})
        
        ol_conn.send(request)
        self.assertEquals(0, len(ol_conn.recv()))
        print >> sys.stderr, "test: test_subtitles_msgs_invalid_request_1: connection closed as expected"
        
        ol_conn.close()
        print >> sys.stderr, "End of test_subtitles_msgs_invalid_request_1 ------------------"
        
    def subtest_invalidRequest2(self):
        """
        Trying to send an invalid message (an integer instead of a 4 bytes binary string)
        The connection should be closed by the receiver
        """
        print >> sys.stderr, "test: test_subtitles_msgs_invalid_request_2 ------------------"
        ol_conn = OLConnection(self.my_keypair,'localhost',self.hisport)

        
        request = GET_SUBS + \
                      bencode((
                              self.anotherpermid,
                              self.testInfohash,
                              42
                              ))
        
        ol_conn.send(request)
        self.assertEquals(0, len(ol_conn.recv()))
        print >> sys.stderr, "test: test_subtitles_msgs_invalid_request_2: connection closed as expected"
        
        ol_conn.close()
        print >> sys.stderr, "End of test_subtitles_msgs_invalid_request_2 ------------------"
        
    def subtest_invalidRequest3(self):
        """
        Trying to send an invalid message (valid for everythin except that there is one field more)
        The connection should be closed by the receiver
        """
        print >> sys.stderr, "test: test_subtitles_msgs_invalid_request_3 ------------------"
        ol_conn = OLConnection(self.my_keypair,'localhost',self.hisport)

        bitmask = LanguagesProvider.getLanguagesInstance().langCodesToMask(['nld','eng'])
        binmask = utilities.uintToBinaryString(bitmask, length=4)
        
        request = GET_SUBS + \
                      bencode((
                              self.anotherpermid,
                              self.testInfohash,
                              binmask,
                              42
                              ))
        
        ol_conn.send(request)
        self.assertEquals(0, len(ol_conn.recv()))
        print >> sys.stderr, "test: test_subtitles_msgs_invalid_request_3: connection closed as expected"
        
        ol_conn.close()
        print >> sys.stderr, "End of test_subtitles_msgs_invalid_request_3 ------------------"
        
    def singtest_subs_messages(self):
        self.setUpDB()
        
        self.subtest_receptionOfSUBS()
        self.subtest_receptionOfSUBSTwoRequestsOneAvailable()
        self.subtest_invalidRequest1()
        self.subtest_invalidRequest2()
        self.subtest_invalidRequest3()
class TestChannelsPlusSubtitles(TestChannels):
    """ 
    Testing the rich metadata extension of channelcast.
    
    The test suite defined in this module executes all the old 
    channelcast tests, plus a test to validate that the rich metadata
    (currently subtitles) extension works properly
    """
    def setupDB(self, nickname):
        TestChannels.setupDB(self, nickname)
        try:
            self.richMetadata_db = self.session.open_dbhandler(
                NTFY_RICH_METADATA)
            #add some metadata for torrents (they are defined in TestChannels.setupDB()
            self.mdto = MetadataDTO(self.hispermid, self.infohash1)
            subtitle1 = SubtitleInfo("nld", os.path.join(RES_DIR, "fake.srt"))
            subtitle1.computeChecksum()

            subtitle2 = SubtitleInfo("eng", os.path.join(RES_DIR, "fake0.srt"))
            subtitle2.computeChecksum()
            self.mdto.addSubtitle(subtitle1)
            self.mdto.addSubtitle(subtitle2)

            self.mdto.sign(self.his_keypair)

            self.richMetadata_db.insertMetadata(self.mdto)
        except:
            print_exc()

    def tearDown(self):
        TestChannels.tearDown(self)
        self.session.close_dbhandler(self.richMetadata_db)

    def _test_all(self, nickname):
        """ 
            I want to start a Tribler client once and then connect to
            it many times. So there must be only one test method
            to prevent setUp() from creating a new client every time.

            The code is constructed so unittest will show the name of the
            (sub)test where the error occured in the traceback it prints.
        """

        TestChannels._test_all(self, nickname)
        self.subtest_channelcastPlusMetadata()

    def subtest_channelcastPlusMetadata(self):
        '''
        Extends channelcast test to channelcast messages enriched with
        metadata (subtitles) informations
        '''
        print >> sys.stderr, "test: channelcast_subtitles ---------------------------"
        s = OLConnection(self.my_keypair, 'localhost', self.hisport)
        chcast = ChannelCastCore(None,
                                 s,
                                 self.session,
                                 None,
                                 log='',
                                 dnsindb=None)

        #test send standard channelcast
        chdata = {}
        print >> sys.stderr, "Test Good ChannelCast Plus Subtitles", ` chdata `
        msg = CHANNELCAST + bencode(chdata)
        s.send(msg)
        resp = s.recv()
        if len(resp) > 0:
            print >> sys.stderr, "test: channelcast_subtitles: got", getMessageName(
                resp[0])
        self.assert_(resp[0] == CHANNELCAST)
        print >> sys.stderr, "test: channelcast_subtitles: got msg", ` bdecode(
            resp[1:]) `
        chdata_rcvd = bdecode(resp[1:])
        self.assertTrue(validChannelCastMsg(chdata_rcvd))

        for entry in chdata_rcvd.itervalues():
            if entry[
                    'infohash'] == self.infohash1:  #the torrent for which two subtitles exist
                self.assertTrue('rich_metadata' in entry.keys())
                richMetadata = entry['rich_metadata']
                print >> sys.stderr, "test: channelcast_subtitles: richMetadata entry is ", richMetadata
                self.assertEquals(6, len(richMetadata))
                self.assertEquals(self.mdto.description, richMetadata[0])
                self.assertEquals(4, len(
                    richMetadata[2]))  #the subtitles mask 4 bytes
                self.assertTrue(isinstance(richMetadata[3],
                                           list))  #the subtitles checsums
                for checksum in richMetadata[3]:
                    self.assertEquals(20,
                                      len(checksum))  #160 bit sha1 checksum
                self.assertEquals(self.mdto.signature, richMetadata[4])
                self.assertEquals(4, len(
                    richMetadata[5]))  #the subtitles have mask 32 bit
                #also must (in this case) be equal to the subtitles mask
                self.assertEquals(richMetadata[2], richMetadata[5])

                print >> sys.stderr, "test: channelcast_subtitles; richMetadata entry is valid and correct"
            else:
                self.assertFalse('rich_metadata' in entry.keys())

        s.close()

        #Now, send a bad ChannelCast message.
        # The other side should close the connection
        # Create bad message by manipulating a good one
        #bad bitmask
        chdata = deepcopy(chdata_rcvd)
        for k, v in chdata.items():
            if 'rich_metadata' in v:
                v['rich_metadata'][
                    2] = 44  #an integer instead of a 4bytes bitmask
        self.subtest_bad_channelcast(chdata)

        #Bad message format
        chdata = deepcopy(chdata_rcvd)
        for k, v in chdata.items():
            if 'rich_metadata' in v:
                v['rich_metadata'].insert(0, u"asdfafa22")
        self.subtest_bad_channelcast(chdata)

        #Bad
        print >> sys.stderr, "End of channelcast_subtitles test ---------------------------"
class TestSubtitleMessages(TestAsServer):
    def setUpPreSession(self):
        """ override TestAsServer """
        TestAsServer.setUpPreSession(self)
        self.config.set_buddycast(True)
        BuddyCastCore.TESTASSERVER = True
        ChannelCastCore.TESTASSERVER = True
        VoteCastCore.TESTASSERVER = True
        self.config.set_start_recommender(True)
        self.config.set_bartercast(True)
        self.config.set_remote_query(True)
        self.config.set_crawler(False)
        self.config.set_torrent_collecting_dir(
            os.path.join(self.config_path, "tmp_torrent_collecting"))

        self.collecting_dir = os.path.join(self.config_path,
                                           "temp_subtitles_collecting")
        os.makedirs(self.collecting_dir)
        self.config.set_subtitles_collecting(True)
        self.config.set_subtitles_collecting_dir(self.collecting_dir)

        #        # Write superpeers.txt and DB schema
        self.install_path = tempfile.mkdtemp()
        spdir = os.path.join(self.install_path, LIBRARYNAME, 'Core')
        os.makedirs(spdir)

        statsdir = os.path.join(self.install_path, LIBRARYNAME, 'Core',
                                'Statistics')
        os.makedirs(statsdir)

        superpeerfilename = os.path.join(spdir, 'superpeer.txt')
        print >> sys.stderr, "test: writing empty superpeers to", superpeerfilename
        f = open(superpeerfilename, "w")
        f.write('# Leeg')
        f.close()

        self.config.set_install_dir(self.install_path)

        srcfiles = []
        srcfiles.append(os.path.join(LIBRARYNAME, "schema_sdb_v5.sql"))
        for srcfile in srcfiles:
            sfn = os.path.join('..', '..', srcfile)
            dfn = os.path.join(self.install_path, srcfile)
            print >> sys.stderr, "test: copying", sfn, dfn
            shutil.copyfile(sfn, dfn)

        #copy subtitles files in the appropriate subtitles folder
        self.src1 = os.path.join(RES_DIR, 'fake.srt')
        self.src2 = os.path.join(RES_DIR, 'fake0.srt')

    def setUpPostSession(self):
        """ override TestAsServer """
        TestAsServer.setUpPostSession(self)

        self.mypermid = str(self.my_keypair.pub().get_der())
        self.hispermid = str(self.his_keypair.pub().get_der())

        self.another_keypair = generate_keypair()
        self.anotherpermid = str(self.another_keypair.pub().get_der())

        self.testInfohash = hashlib.sha1("yoman!").digest()

        #copy subtitles in the collecting dir
        nldName = SubUtils.getSubtitleFileRelativeName(self.anotherpermid,
                                                       self.testInfohash,
                                                       "nld")
        engName = SubUtils.getSubtitleFileRelativeName(self.anotherpermid,
                                                       self.testInfohash,
                                                       "eng")

        self.sub1 = os.path.join(self.collecting_dir, nldName)
        self.sub2 = os.path.join(self.collecting_dir, engName)

        shutil.copyfile(self.src1, self.sub1)
        # Let's say that the receiving peer has only the nld subtitle
        # avialable locally
        # shutil.copyfile(self.src2, self.sub2)

    def setUpDB(self):
        try:
            self.richMetadata_db = self.session.open_dbhandler(
                NTFY_RICH_METADATA)

            #add some metadata
            self.mdto = MetadataDTO(self.anotherpermid, self.testInfohash)
            subtitle1 = SubtitleInfo("nld", self.sub1)
            subtitle1.computeChecksum()
            subtitle2 = SubtitleInfo("eng", os.path.join(RES_DIR, "fake0.srt"))
            subtitle2.computeChecksum()
            self.mdto.addSubtitle(subtitle1)
            self.mdto.addSubtitle(subtitle2)

            self.mdto.sign(self.another_keypair)

            self.richMetadata_db.insertMetadata(self.mdto)

            #hisoermid has the nld subtitle but doesn't have the english one
            self.richMetadata_db.updateSubtitlePath(self.mdto.channel,
                                                    self.mdto.infohash, "eng",
                                                    None)

        except:
            print_exc()

    def tearDown(self):
        TestAsServer.tearDown(self)
        self.session.close_dbhandler(self.richMetadata_db)

    def subtest_receptionOfSUBS(self):
        '''
        Asking for the single available subtitle. The response should be
        a valid SUBS message containing its contents
        '''

        print >> sys.stderr, "test: test_subtitles_msgs_1_1 -----------------------"
        ol_conn = OLConnection(self.my_keypair, 'localhost', self.hisport)

        bitmask = LanguagesProvider.getLanguagesInstance().langCodesToMask(
            ['nld'])
        binmask = utilities.uintToBinaryString(bitmask, length=4)

        request = GET_SUBS + \
                      bencode((
                              self.anotherpermid,
                              self.testInfohash,
                              binmask
                              ))

        subshandler = SubtitlesHandler()
        subshandler.register(ol_conn, self.richMetadata_db, self.session)

        ol_conn.send(request)
        subs_data = ol_conn.recv()
        print >> sys.stderr, "test: subtitles_messages : received SUBS response: len", len(
            subs_data)
        self.assertEquals(SUBS, subs_data[0])
        data = bdecode(subs_data[1:])
        print >> sys.stderr, "test: subtitles_messages : received SUBS response: ", data

        #check on the format of the response
        self.assertTrue(isinstance(data, list))
        self.assertEquals(4, len(data))  # for fields
        self.assertEquals(self.mdto.channel, data[0])
        self.assertEquals(self.mdto.infohash, data[1])
        self.assertEquals(binmask, data[2])
        self.assertTrue(isinstance(data[3], list))
        self.assertEquals(1, len(data[3]))
        with codecs.open(self.sub1, "rb", "utf-8") as sub:
            expectedContents = sub.read()
        self.assertEquals(expectedContents, data[3][0])

        ol_conn.close()

        print >> sys.stderr, "test: subtitles_messages: received content is valid."
        print >> sys.stderr, "End of test_subtitles_msgs_1_1 test --------------------"

    def subtest_receptionOfSUBSTwoRequestsOneAvailable(self):
        """
        Asking for two subtitles while the recipent of the request has only one.
        The response should contain only the one available subtitle content,
        plus a bitmask that reflects the contents of the response.
        """

        print >> sys.stderr, "test: test_subtitles_msgs_2_1 -----------------------"
        ol_conn = OLConnection(self.my_keypair, 'localhost', self.hisport)

        bitmask = LanguagesProvider.getLanguagesInstance().langCodesToMask(
            ['nld', 'eng'])
        binmask = utilities.uintToBinaryString(bitmask, length=4)

        request = GET_SUBS + \
                      bencode((
                              self.anotherpermid,
                              self.testInfohash,
                              binmask
                              ))

        subshandler = SubtitlesHandler()
        subshandler.register(ol_conn, self.richMetadata_db, self.session)

        ol_conn.send(request)
        subs_data = ol_conn.recv()
        self.assertEquals(SUBS, subs_data[0])
        data = bdecode(subs_data[1:])
        print >> sys.stderr, "test: subtitles_messages : received SUBS repsonse: ", data

        #check on the format of the response
        self.assertTrue(isinstance(data, list))
        self.assertEquals(4, len(data))  # for fields
        self.assertEquals(self.mdto.channel, data[0])
        self.assertEquals(self.mdto.infohash, data[1])

        #the receiver had only one of the two requested subtitles
        # so I expect a different bitmask
        bitmask = LanguagesProvider.getLanguagesInstance().langCodesToMask(
            ['nld'])
        expectedBinarymask = utilities.uintToBinaryString(bitmask, length=4)

        self.assertEquals(expectedBinarymask, data[2])
        self.assertTrue(isinstance(data[3], list))
        self.assertEquals(1, len(data[3]))
        with codecs.open(self.sub1, "rb", "utf-8") as sub:
            expectedContents = sub.read()
        self.assertEquals(expectedContents, data[3][0])

        ol_conn.close()
        print >> sys.stderr, "test: subtitles_messages: received content is valid."
        print >> sys.stderr, "End of test_subtitles_msgs_2_1 test --------------------"

    def subtest_invalidRequest1(self):
        """
        Trying to send an empty message.
        The connection should be closed by the receiver
        """
        print >> sys.stderr, "test: test_subtitles_msgs_invalid_request_1 ------------------"
        ol_conn = OLConnection(self.my_keypair, 'localhost', self.hisport)


        request = GET_SUBS + \
                    bencode({})

        ol_conn.send(request)
        self.assertEquals(0, len(ol_conn.recv()))
        print >> sys.stderr, "test: test_subtitles_msgs_invalid_request_1: connection closed as expected"

        ol_conn.close()
        print >> sys.stderr, "End of test_subtitles_msgs_invalid_request_1 ------------------"

    def subtest_invalidRequest2(self):
        """
        Trying to send an invalid message (an integer instead of a 4 bytes binary string)
        The connection should be closed by the receiver
        """
        print >> sys.stderr, "test: test_subtitles_msgs_invalid_request_2 ------------------"
        ol_conn = OLConnection(self.my_keypair, 'localhost', self.hisport)


        request = GET_SUBS + \
                      bencode((
                              self.anotherpermid,
                              self.testInfohash,
                              42
                              ))

        ol_conn.send(request)
        self.assertEquals(0, len(ol_conn.recv()))
        print >> sys.stderr, "test: test_subtitles_msgs_invalid_request_2: connection closed as expected"

        ol_conn.close()
        print >> sys.stderr, "End of test_subtitles_msgs_invalid_request_2 ------------------"

    def subtest_invalidRequest3(self):
        """
        Trying to send an invalid message (valid for everythin except that there is one field more)
        The connection should be closed by the receiver
        """
        print >> sys.stderr, "test: test_subtitles_msgs_invalid_request_3 ------------------"
        ol_conn = OLConnection(self.my_keypair, 'localhost', self.hisport)

        bitmask = LanguagesProvider.getLanguagesInstance().langCodesToMask(
            ['nld', 'eng'])
        binmask = utilities.uintToBinaryString(bitmask, length=4)

        request = GET_SUBS + \
                      bencode((
                              self.anotherpermid,
                              self.testInfohash,
                              binmask,
                              42
                              ))

        ol_conn.send(request)
        self.assertEquals(0, len(ol_conn.recv()))
        print >> sys.stderr, "test: test_subtitles_msgs_invalid_request_3: connection closed as expected"

        ol_conn.close()
        print >> sys.stderr, "End of test_subtitles_msgs_invalid_request_3 ------------------"

    def singtest_subs_messages(self):
        self.setUpDB()

        self.subtest_receptionOfSUBS()
        self.subtest_receptionOfSUBSTwoRequestsOneAvailable()
        self.subtest_invalidRequest1()
        self.subtest_invalidRequest2()
        self.subtest_invalidRequest3()
Esempio n. 29
0
class SubtitlesSupport(object):
    '''
    Subtitle dissemination system facade.
    
    Acts as the only faced between the subtitle dissemination system and 
    the GUI (or whoever needs to subtitles).
    
    Provides methods to query the subtitles database. Allows publishers to
    add their own subtitles, and if necessary permits to retrieve the subtitle
    remotely if not available.
    '''

    __single = None
    _singletonLock = threading.RLock()

    def __init__(self):

        #singleton pattern not really enforced if someone just calls
        # the normal constructor. But this way I can test the instance easier
        try:
            SubtitlesSupport._singletonLock.acquire()
            SubtitlesSupport.__single = self
        finally:
            SubtitlesSupport._singletonLock.release()

        self.richMetadata_db = None
        self.subtitlesHandler = None
        self.channelcast_db = None
        self.langUtility = LanguagesProvider.getLanguagesInstance()
        self._registered = False

    @staticmethod
    def getInstance(*args, **kw):
        try:
            SubtitlesSupport._singletonLock.acquire()
            if SubtitlesSupport.__single == None:
                SubtitlesSupport(*args, **kw)
        finally:
            SubtitlesSupport._singletonLock.release()

        return SubtitlesSupport.__single

    def _register(self, richMetadataDBHandler, subtitlesHandler,
                  channelcast_db, my_permid, my_keypair, peersHaveManger,
                  ol_bridge):
        assert richMetadataDBHandler is not None
        assert subtitlesHandler is not None
        assert channelcast_db is not None
        assert peersHaveManger is not None
        assert ol_bridge is not None
        assert isValidPermid(my_permid)

        self.richMetadata_db = richMetadataDBHandler
        self.subtitlesHandler = subtitlesHandler
        self.channelcast_db = channelcast_db
        self.my_permid = my_permid
        self.my_keypair = my_keypair
        self._peersHaveManager = peersHaveManger

        #used to decouple calls to SubtitleHandler
        self._ol_bridge = ol_bridge
        self._registered = True

    def getSubtileInfosForInfohash(self, infohash):
        '''
        Retrieve available information about subtitles for the given infohash.
        
        Given the infohash of a .torrent, retrieves every
        information about subtitles published for that .torrent that is
        currently available in the DB. 
        
        @param infohash: a .torrent infohash (binary)
        @return: a dictionary. The dictionary looks like this::
                { 
                  channel_id1 : {langCode : L{SubtitleInfo}, ...} ,
                  channel_id2 : {langCode : L{SubtitleInfo}, ... },
                  ...
                } 
            Each entry in the dictionary has the following semantics:
                - channel_id is the permid identifiying the channel (binary).
                - langCode is an ISO 693-2 three characters language code
        '''
        assert utilities.isValidInfohash(infohash)
        assert self._registered, "Instance is not registered"

        returnDictionary = dict()

        #a metadataDTO corresponds to all metadata for a pair channel, infohash
        metadataDTOs = self.richMetadata_db.getAllMetadataForInfohash(infohash)

        for metadataDTO in metadataDTOs:
            channel = metadataDTO.channel
            subtitles = metadataDTO.getAllSubtitles()
            if len(subtitles) > 0:
                returnDictionary[channel] = subtitles

        return returnDictionary

    def getSubtitleInfos(self, channel, infohash):
        '''
        Retrieve subtitles information for the given channel-infohash pair.
        
        Searches in the local database for information about subtitles that
        are currently available.
        
        @param channel: the channel_id (perm_id) of a channel (binary)
        @param infohash: a .torrent infohash (binary)
        @return: a dictionary of SubtitleInfo instances. The keys are the 
                language codes of the subtitles
        '''
        assert self._registered, "Instance is not registered"

        metadataDTO = self.richMetadata_db.getMetadata(channel, infohash)
        if metadataDTO:
            return metadataDTO.getAllSubtitles()
        return {}

    def publishSubtitle(self, infohash, lang, pathToSrtSubtitle):
        '''
        Allows an user to publish an srt subtitle file in his channel.
        
        Called by a channel owner this method inserts a new subtitle for
        a torrent published in his channel. 
        The method assumes that the torrent identified by the infohash
        parameter is already in the channel, and that the parameter 
        pathToSrtSubtitle points to an existing srt file on the local
        filesystem.
        If a subtitle for the same language was already associated to the 
        specified infohash and channel, it will be overwritten.
        After calling this method the newly inserted subtitle will be 
        disseminated via Channelcast.
        
        @param infohash: the infohash of the torrent to associate the subtitle
                         with, binary
        @param lang: a 3 characters code for the language of the subtitle as
                     specified in ISO 639-2. Currently just 32 language codes
                     will be supported.
        @param pathToSrtSubtitle: a path in the local filesystem to a subtitle
                                  in srt format.
        
        @raise RichMetadataException: if something "general" goes wrong while
                                      adding new metadata
        @raise IOError: if disk related problems occur
        '''
        assert utilities.isValidInfohash(infohash), "Invalid Infohash"
        assert lang is not None and self.langUtility.isLangCodeSupported(lang)
        assert self._registered, "Instance is not registered"

        channelid = bin2str(self.my_permid)
        base64infohash = bin2str(infohash)
        # consisnstency check: I want to assure that this method is called
        # for an item that is actually in my channel
        consinstent = self.channelcast_db.isItemInChannel(
            channelid, base64infohash)

        if not consinstent:
            msg = "Infohash %s not found in my channel. Rejecting subtitle" % base64infohash
            if DEBUG:
                print >> sys.stderr, msg
            raise RichMetadataException(msg)
        try:
            filepath = self.subtitlesHandler.copyToSubtitlesFolder(
                pathToSrtSubtitle, self.my_permid, infohash, lang)
        except Exception, e:
            if DEBUG:
                print >> sys.stderr, "Failed to read and copy subtitle to appropriate folder: %s" % str(
                    e)

        # retrieve existing metadata from my channel, infohash
        metadataDTO = self.richMetadata_db.getMetadata(self.my_permid,
                                                       infohash)

        # can be none if no metadata was available
        if metadataDTO is None:
            metadataDTO = MetadataDTO(self.my_permid, infohash)
        else:
            #update the timestamp
            metadataDTO.resetTimestamp()
        newSubtitle = SubtitleInfo(lang, filepath)

        # this check should be redundant, since i should be sure that subtitle
        # exists at this point

        if newSubtitle.subtitleExists():
            newSubtitle.computeChecksum()
        else:
            msg = "Inconsistency found. The subtitle was not published"
            if DEBUG:
                print >> sys.stderr, msg
            raise RichMetadataException(msg)

        metadataDTO.addSubtitle(newSubtitle)
        metadataDTO.sign(self.my_keypair)
        self.richMetadata_db.insertMetadata(metadataDTO)
class TestChannelsPlusSubtitles(TestChannels):
    """ 
    Testing the rich metadata extension of channelcast.
    
    The test suite defined in this module executes all the old 
    channelcast tests, plus a test to validate that the rich metadata
    (currently subtitles) extension works properly
    """


        
    def setupDB(self,nickname):
        TestChannels.setupDB(self,nickname)
        try:
            self.richMetadata_db = self.session.open_dbhandler(NTFY_RICH_METADATA)
            #add some metadata for torrents (they are defined in TestChannels.setupDB()
            self.mdto = MetadataDTO(self.hispermid, self.infohash1)
            subtitle1 = SubtitleInfo("nld", os.path.join(RES_DIR,"fake.srt"))
            subtitle1.computeChecksum()
            
            subtitle2 = SubtitleInfo("eng", os.path.join(RES_DIR, "fake0.srt"))
            subtitle2.computeChecksum()
            self.mdto.addSubtitle(subtitle1)
            self.mdto.addSubtitle(subtitle2)
            
            self.mdto.sign(self.his_keypair)
            
            self.richMetadata_db.insertMetadata(self.mdto)
        except:
            print_exc()
        
        
    def tearDown(self):
        TestChannels.tearDown(self)
        self.session.close_dbhandler(self.richMetadata_db)
        
    def _test_all(self,nickname):
        """ 
            I want to start a Tribler client once and then connect to
            it many times. So there must be only one test method
            to prevent setUp() from creating a new client every time.

            The code is constructed so unittest will show the name of the
            (sub)test where the error occured in the traceback it prints.
        """
        
        TestChannels._test_all(self,nickname)
        self.subtest_channelcastPlusMetadata()
        
      
    def subtest_channelcastPlusMetadata(self):
        '''
        Extends channelcast test to channelcast messages enriched with
        metadata (subtitles) informations
        '''
        print >>sys.stderr,"test: channelcast_subtitles ---------------------------"
        s = OLConnection(self.my_keypair,'localhost',self.hisport)
        chcast = ChannelCastCore(None, s, self.session, None, log = '', dnsindb = None)
        
        #test send standard channelcast
        chdata = {}
        print >> sys.stderr, "Test Good ChannelCast Plus Subtitles", `chdata`
        msg = CHANNELCAST+bencode(chdata)
        s.send(msg)
        resp = s.recv()
        if len(resp) > 0:
            print >>sys.stderr,"test: channelcast_subtitles: got",getMessageName(resp[0])
        self.assert_(resp[0]==CHANNELCAST)
        print >>sys.stderr, "test: channelcast_subtitles: got msg", `bdecode(resp[1:])`
        chdata_rcvd = bdecode(resp[1:])
        self.assertTrue(validChannelCastMsg(chdata_rcvd))
        
        for entry in chdata_rcvd.itervalues():
            if entry['infohash'] == self.infohash1: #the torrent for which two subtitles exist
                self.assertTrue('rich_metadata' in entry.keys())
                richMetadata = entry['rich_metadata']
                print >> sys.stderr, "test: channelcast_subtitles: richMetadata entry is ", richMetadata
                self.assertEquals(6, len(richMetadata))
                self.assertEquals(self.mdto.description, richMetadata[0])
                self.assertEquals(4, len(richMetadata[2])) #the subtitles mask 4 bytes
                self.assertTrue(isinstance(richMetadata[3],list)) #the subtitles checsums
                for checksum in richMetadata[3]:
                    self.assertEquals(20,len(checksum)) #160 bit sha1 checksum
                self.assertEquals(self.mdto.signature, richMetadata[4])
                self.assertEquals(4,len(richMetadata[5])) #the subtitles have mask 32 bit
                #also must (in this case) be equal to the subtitles mask
                self.assertEquals(richMetadata[2], richMetadata[5])
                
                print >> sys.stderr, "test: channelcast_subtitles; richMetadata entry is valid and correct"
            else:
                self.assertFalse('rich_metadata' in entry.keys())
                
        s.close()
        
        #Now, send a bad ChannelCast message.
        # The other side should close the connection
        # Create bad message by manipulating a good one
        #bad bitmask
        chdata = deepcopy(chdata_rcvd)
        for k,v in chdata.items():
            if 'rich_metadata' in v:
                v['rich_metadata'][2] = 44 #an integer instead of a 4bytes bitmask
        self.subtest_bad_channelcast(chdata)
    
                
        #Bad message format
        chdata = deepcopy(chdata_rcvd)
        for k,v in chdata.items():
            if 'rich_metadata' in v:
                v['rich_metadata'].insert(0, u"asdfafa22")
        self.subtest_bad_channelcast(chdata)
        
        #Bad 
        print>>sys.stderr, "End of channelcast_subtitles test ---------------------------"