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 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 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 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 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 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)
Beispiel #8
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 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 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())
    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 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
    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]))
Beispiel #14
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)