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)
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'