예제 #1
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)
예제 #2
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'