Esempio n. 1
0
def create_bencoded_torrent(torrent, bencoded_info, metadata_base=None):
    ''' Creates a bencoded torrent metadata for a given torrent,
        optionally using a given metadata_base dict (note: 'info' key will be
        popped off the dict) '''
    if metadata_base is None:
        metadata_base = create_default_metadata_base(torrent)

    metadata_base['encoding'] = torrent.encoding

    # Make sure info doesn't exist on the base
    metadata_base.pop('info', None)
    prefixed_dict = {
        key: metadata_base[key]
        for key in metadata_base if key < 'info'
    }
    suffixed_dict = {
        key: metadata_base[key]
        for key in metadata_base if key > 'info'
    }

    prefix = bencode.encode(prefixed_dict)
    suffix = bencode.encode(suffixed_dict)

    bencoded_torrent = prefix[:-1] + b'4:info' + bencoded_info + suffix[1:]

    return bencoded_torrent
    def test_encode(self):
        exception_test_cases = [  # (raw, raised_exception, expected_result_regexp)
            # test unsupported type
            (None, bencode.BencodeException, r'Unsupported type'),
            (1.6, bencode.BencodeException, r'Unsupported type'),
        ]

        test_cases = [  # (raw, expected_result)
            (100, b'i100e'),  # int
            (-5, b'i-5e'),  # int
            ('test', b'4:test'),  # str
            (b'test', b'4:test'),  # byte
            (['test', 100], b'l4:testi100ee'),  # list
            ({
                'numbers': [1, 2],
                'hello': 'world'
            }, b'd5:hello5:world7:numbersli1ei2eee')  # dict
        ]

        for raw, raised_exception, expected_result_regexp in exception_test_cases:
            self.assertRaisesRegexp(raised_exception, expected_result_regexp,
                                    bencode.encode, raw)

        for raw, expected_result in test_cases:
            self.assertEqual(bencode.encode(raw), expected_result)
Esempio n. 3
0
def validate_torrent_file(torrent_file_name, torrent_file):
    # Decode and ensure data is bencoded data
    try:
        torrent_dict = bencode.decode(torrent_file)
    except (bencode.MalformedBencodeException, UnicodeError):
        return False, 'Malformed torrent file'

    # Uncomment for debug print of the torrent
    # forms._debug_print_torrent_metadata(torrent_dict)

    try:
        forms._validate_torrent_metadata(torrent_dict)
    except AssertionError as e:
        return False, 'Malformed torrent metadata ({})'.format(e.args[0])

    # Note! bencode will sort dict keys, as per the spec
    # This may result in a different hash if the uploaded torrent does not match the
    # spec, but it's their own fault for using broken software! Right?
    bencoded_info_dict = bencode.encode(torrent_dict['info'])
    info_hash = utils.sha1_hash(bencoded_info_dict)

    # Check if the info_hash exists already in the database
    existing_torrent = models.Torrent.by_info_hash(info_hash)
    if existing_torrent:
        return False, 'That torrent already exists (#{})'.format(
            existing_torrent.id)

    # Torrent is legit, pass original filename and dict along
    return True, forms.TorrentFileData(
        filename=os.path.basename(torrent_file_name),
        torrent_dict=torrent_dict,
        info_hash=info_hash,
        bencoded_info_dict=bencoded_info_dict)
Esempio n. 4
0
    def validate_torrent_file(form, field):
        # Decode and ensure data is bencoded data
        try:
            torrent_dict = bencode.decode(field.data)
            # field.data.close()
        except (bencode.MalformedBencodeException, UnicodeError):
            raise ValidationError('Malformed torrent file')

        # Uncomment for debug print of the torrent
        # _debug_print_torrent_metadata(torrent_dict)

        try:
            _validate_torrent_metadata(torrent_dict)
        except AssertionError as e:
            raise ValidationError('Malformed torrent metadata ({})'.format(
                e.args[0]))

        site_tracker = app.config.get('MAIN_ANNOUNCE_URL')
        ensure_tracker = app.config.get('ENFORCE_MAIN_ANNOUNCE_URL')

        try:
            tracker_found = _validate_trackers(torrent_dict, site_tracker)
        except AssertionError as e:
            raise ValidationError('Malformed torrent trackers ({})'.format(
                e.args[0]))

        # Ensure private torrents are using our tracker
        if torrent_dict['info'].get('private') == 1:
            if torrent_dict['announce'].decode('utf-8') != site_tracker:
                raise ValidationError(
                    'Private torrent: please set {} as the main tracker'.
                    format(site_tracker))

        elif ensure_tracker and not tracker_found:
            raise ValidationError(
                'Please include {} in the trackers of the torrent'.format(
                    site_tracker))

        # Note! bencode will sort dict keys, as per the spec
        # This may result in a different hash if the uploaded torrent does not match the
        # spec, but it's their own fault for using broken software! Right?
        bencoded_info_dict = bencode.encode(torrent_dict['info'])
        info_hash = utils.sha1_hash(bencoded_info_dict)

        # Check if the info_hash exists already in the database
        existing_torrent = models.Torrent.by_info_hash(info_hash)
        if existing_torrent:
            raise ValidationError('That torrent already exists (#{})'.format(
                existing_torrent.id))

        # Torrent is legit, pass original filename and dict along
        field.parsed_data = TorrentFileData(
            filename=os.path.basename(field.data.filename),
            torrent_dict=torrent_dict,
            info_hash=info_hash,
            bencoded_info_dict=bencoded_info_dict)
Esempio n. 5
0
    def validate_torrent_file(form, field):
        # Decode and ensure data is bencoded data
        try:
            torrent_dict = bencode.decode(field.data)
            #field.data.close()
        except (bencode.MalformedBencodeException, UnicodeError):
            raise ValidationError('Malformed torrent file')

        # Uncomment for debug print of the torrent
        # _debug_print_torrent_metadata(torrent_dict)

        try:
            _validate_torrent_metadata(torrent_dict)
        except AssertionError as e:
            raise ValidationError('Malformed torrent metadata ({})'.format(
                e.args[0]))

        try:
            _validate_trackers(torrent_dict)
        except AssertionError as e:
            raise ValidationError('Malformed torrent trackers ({})'.format(
                e.args[0]))

        if app.config.get('ENFORCE_MAIN_ANNOUNCE_URL'):
            main_announce_url = app.config.get('MAIN_ANNOUNCE_URL')
            if not main_announce_url:
                raise Exception('Config MAIN_ANNOUNCE_URL not set!')

            announce = torrent_dict.get('announce', b'').decode('utf-8')
            if announce != main_announce_url:
                raise ValidationError(
                    'Please set {} as the first tracker in the torrent'.format(
                        main_announce_url))

        # Note! bencode will sort dict keys, as per the spec
        # This may result in a different hash if the uploaded torrent does not match the
        # spec, but it's their own fault for using broken software! Right?
        bencoded_info_dict = bencode.encode(torrent_dict['info'])
        info_hash = utils.sha1_hash(bencoded_info_dict)

        # Check if the info_hash exists already in the database
        existing_torrent = models.Torrent.by_info_hash(info_hash)
        if existing_torrent:
            raise ValidationError('That torrent already exists (#{})'.format(
                existing_torrent.id))

        # Torrent is legit, pass original filename and dict along
        field.parsed_data = TorrentFileData(
            filename=os.path.basename(field.data.filename),
            torrent_dict=torrent_dict,
            info_hash=info_hash,
            bencoded_info_dict=bencoded_info_dict)
Esempio n. 6
0
def ghetto_import():
    if flask.request.remote_addr != '127.0.0.1':
        return flask.error(403)

    torrent_file = flask.request.files.get('torrent')

    try:
        torrent_dict = bencode.decode(torrent_file)
        # field.data.close()
    except (bencode.MalformedBencodeException, UnicodeError):
        return 'Malformed torrent file', 500

    try:
        forms._validate_torrent_metadata(torrent_dict)
    except AssertionError as e:
        return 'Malformed torrent metadata ({})'.format(e.args[0]), 500

    try:
        tracker_found = forms._validate_trackers(torrent_dict)
    except AssertionError as e:
        return 'Malformed torrent trackers ({})'.format(e.args[0]), 500

    bencoded_info_dict = bencode.encode(torrent_dict['info'])
    info_hash = utils.sha1_hash(bencoded_info_dict)

    # Check if the info_hash exists already in the database
    torrent = models.Torrent.by_info_hash(info_hash)
    if not torrent:
        return 'This torrent does not exists', 500

    if torrent.has_torrent:
        return 'This torrent already has_torrent', 500

    # Torrent is legit, pass original filename and dict along
    torrent_data = forms.TorrentFileData(filename=os.path.basename(
        torrent_file.filename),
                                         torrent_dict=torrent_dict,
                                         info_hash=info_hash,
                                         bencoded_info_dict=bencoded_info_dict)

    # The torrent has been  validated and is safe to access with ['foo'] etc - all relevant
    # keys and values have been checked for (see UploadForm in forms.py for details)
    info_dict = torrent_data.torrent_dict['info']

    changed_to_utf8 = backend._replace_utf8_values(torrent_data.torrent_dict)

    torrent_filesize = info_dict.get('length') or sum(
        f['length'] for f in info_dict.get('files'))

    # In case no encoding, assume UTF-8.
    torrent_encoding = torrent_data.torrent_dict.get('encoding',
                                                     b'utf-8').decode('utf-8')

    # Store bencoded info_dict
    torrent.info = models.TorrentInfo(
        info_dict=torrent_data.bencoded_info_dict)
    torrent.has_torrent = True

    # To simplify parsing the filelist, turn single-file torrent into a list
    torrent_filelist = info_dict.get('files')

    used_path_encoding = changed_to_utf8 and 'utf-8' or torrent_encoding

    parsed_file_tree = dict()
    if not torrent_filelist:
        # If single-file, the root will be the file-tree (no directory)
        file_tree_root = parsed_file_tree
        torrent_filelist = [{
            'length': torrent_filesize,
            'path': [info_dict['name']]
        }]
    else:
        # If multi-file, use the directory name as root for files
        file_tree_root = parsed_file_tree.setdefault(
            info_dict['name'].decode(used_path_encoding), {})

    # Parse file dicts into a tree
    for file_dict in torrent_filelist:
        # Decode path parts from utf8-bytes
        path_parts = [
            path_part.decode(used_path_encoding)
            for path_part in file_dict['path']
        ]

        filename = path_parts.pop()
        current_directory = file_tree_root

        for directory in path_parts:
            current_directory = current_directory.setdefault(directory, {})

        # Don't add empty filenames (BitComet directory)
        if filename:
            current_directory[filename] = file_dict['length']

    parsed_file_tree = utils.sorted_pathdict(parsed_file_tree)

    json_bytes = json.dumps(parsed_file_tree,
                            separators=(',', ':')).encode('utf8')
    torrent.filelist = models.TorrentFilelist(filelist_blob=json_bytes)

    db.session.add(torrent)
    db.session.flush()

    # Store the users trackers
    trackers = OrderedSet()
    announce = torrent_data.torrent_dict.get('announce', b'').decode('ascii')
    if announce:
        trackers.add(announce)

    # List of lists with single item
    announce_list = torrent_data.torrent_dict.get('announce-list', [])
    for announce in announce_list:
        trackers.add(announce[0].decode('ascii'))

    # Remove our trackers, maybe? TODO ?

    # Search for/Add trackers in DB
    db_trackers = OrderedSet()
    for announce in trackers:
        tracker = models.Trackers.by_uri(announce)

        # Insert new tracker if not found
        if not tracker:
            tracker = models.Trackers(uri=announce)
            db.session.add(tracker)
            db.session.flush()

        db_trackers.add(tracker)

    # Store tracker refs in DB
    for order, tracker in enumerate(db_trackers):
        torrent_tracker = models.TorrentTrackers(torrent_id=torrent.id,
                                                 tracker_id=tracker.id,
                                                 order=order)
        db.session.add(torrent_tracker)

    db.session.commit()

    return 'success'