def test_parse_magnetlink_lowercase(): """ Test if a lowercase magnet link can be parsed """ _, hashed, _ = parse_magnetlink( 'magnet:?xt=urn:btih:apctqfwnowubxzoidazgaj2ba6fs6juc') assert hashed == b"\x03\xc58\x16\xcdu\xa8\x1b\xe5\xc8\x182`'A\x07\x8b/&\x82"
def test_parse_magnetlink_uppercase(): """ Test if an uppercase magnet link can be parsed """ _, hashed, _ = parse_magnetlink( 'magnet:?xt=urn:btih:APCTQFWNOWUBXZOIDAZGAJ2BA6FS6JUC') assert hashed == b"\x03\xc58\x16\xcdu\xa8\x1b\xe5\xc8\x182`'A\x07\x8b/&\x82"
def test_parse_magnetlink_valid(self): result = parse_magnetlink("magnet:?xt=urn:ed2k:354B15E68FB8F36D7CD88FF94116CDC1&xl=10826029&dn=mediawiki-1.15.1" ".tar.gz&xt=urn:tree:tiger:7N5OAMRNGMSSEUE3ORHOKWN4WWIQ5X4EBOOTLJY&xt=urn:btih:QHQXPY" "WMACKDWKP47RRVIV7VOURXFE5Q&tr=http%3A%2F%2Ftracker.example.org%2Fannounce.php%3Fuk" "%3D1111111111%26&as=http%3A%2F%2Fdownload.wikimedia.org%2Fmediawiki%2F1.15%2Fmediawi" "ki-1.15.1.tar.gz&xs=http%3A%2F%2Fcache.example.org%2FXRX2PEFXOOEJFRVUCX6HMZMKS5TWG4K" "5&xs=dchub://example.org") self.assertEqual(result, ('mediawiki-1.15.1.tar.gz', b'\x81\xe1w\xe2\xcc\x00\x94;)\xfc\xfccTW\xf5u#r\x93\xb0', ['http://tracker.example.org/announce.php?uk=1111111111&']))
async def start_download_from_uri(self, uri, config=None): if uri.startswith("http"): tdef = await TorrentDef.load_from_url(uri) return self.start_download(tdef=tdef, config=config) if uri.startswith("magnet:"): name, infohash, _ = parse_magnetlink(uri) if infohash is None: raise RuntimeError("Missing infohash") if infohash in self.metainfo_cache: tdef = TorrentDef.load_from_dict(self.metainfo_cache[infohash]['meta_info']) else: tdef = TorrentDefNoMetainfo(infohash, "Unknown name" if name is None else name, url=uri) return self.start_download(tdef=tdef, config=config) if uri.startswith("file:"): argument = url2pathname(uri[5:]) return self.start_download(torrent_file=argument, config=config) raise Exception("invalid uri")
async def start_download_from_uri(self, uri, config=None): scheme = scheme_from_uri(uri) if scheme in (HTTP_SCHEME, HTTPS_SCHEME): tdef = await TorrentDef.load_from_url(uri) return self.start_download(tdef=tdef, config=config) if scheme == MAGNET_SCHEME: name, infohash, _ = parse_magnetlink(uri) if infohash is None: raise RuntimeError("Missing infohash") if infohash in self.metainfo_cache: tdef = TorrentDef.load_from_dict(self.metainfo_cache[infohash]['meta_info']) else: tdef = TorrentDefNoMetainfo(infohash, "Unknown name" if name is None else name, url=uri) return self.start_download(tdef=tdef, config=config) if scheme == FILE_SCHEME: file = uri_to_path(uri) return self.start_download(torrent_file=file, config=config) raise Exception("invalid uri")
def get_trackers_as_single_tuple(self): if self.url and self.url.startswith('magnet:'): _, _, trs = parse_magnetlink(self.url) return tuple(trs) return ()
def test_parse_magnetlink_nomagnet(): result = parse_magnetlink("http://") assert result == (None, None, [])
async def get_torrent_info(self, request): args = request.query hops = None if 'hops' in args: try: hops = int(args['hops']) except ValueError: return RESTResponse( { "error": f"wrong value of 'hops' parameter: {repr(args['hops'])}" }, status=HTTP_BAD_REQUEST) if 'uri' not in args or not args['uri']: return RESTResponse({"error": "uri parameter missing"}, status=HTTP_BAD_REQUEST) uri = args['uri'] if uri.startswith('file:'): try: filename = url2pathname(uri[5:]) tdef = TorrentDef.load(filename) metainfo = tdef.get_metainfo() except (TypeError, RuntimeError): return RESTResponse( {"error": "error while decoding torrent file"}, status=HTTP_INTERNAL_SERVER_ERROR) elif uri.startswith('http'): try: async with ClientSession(raise_for_status=True) as session: response = await session.get(uri) response = await response.read() except (ServerConnectionError, ClientResponseError) as e: return RESTResponse({"error": str(e)}, status=HTTP_INTERNAL_SERVER_ERROR) if response.startswith(b'magnet'): _, infohash, _ = parse_magnetlink(response) if infohash: metainfo = await self.session.dlmgr.get_metainfo( infohash, timeout=60, hops=hops, url=response) else: metainfo = bdecode_compat(response) elif uri.startswith('magnet'): infohash = parse_magnetlink(uri)[1] if infohash is None: return RESTResponse({"error": "missing infohash"}, status=HTTP_BAD_REQUEST) metainfo = await self.session.dlmgr.get_metainfo(infohash, timeout=60, hops=hops, url=uri) else: return RESTResponse({"error": "invalid uri"}, status=HTTP_BAD_REQUEST) if not metainfo: return RESTResponse({"error": "metainfo error"}, status=HTTP_INTERNAL_SERVER_ERROR) if not isinstance(metainfo, dict) or b'info' not in metainfo: self._logger.warning("Received metainfo is not a valid dictionary") return RESTResponse({"error": "invalid response"}, status=HTTP_INTERNAL_SERVER_ERROR) # Add the torrent to GigaChannel as a free-for-all entry, so others can search it self.session.mds.TorrentMetadata.add_ffa_from_dict( tdef_to_metadata_dict(TorrentDef.load_from_dict(metainfo))) # TODO(Martijn): store the stuff in a database!!! # TODO(Vadim): this means cache the downloaded torrent in a binary storage, like LevelDB infohash = hashlib.sha1(bencode(metainfo[b'info'])).digest() download = self.session.dlmgr.downloads.get(infohash) metainfo_request = self.session.dlmgr.metainfo_requests.get( infohash, [None])[0] download_is_metainfo_request = download == metainfo_request # Check if the torrent is already in the downloads encoded_metainfo = deepcopy(metainfo) # FIXME: json.dumps garbles binary data that is used by the 'pieces' field # However, this is fine as long as the GUI does not use this field. encoded_metainfo[b'info'][b'pieces'] = hexlify( encoded_metainfo[b'info'][b'pieces']).encode('utf-8') encoded_metainfo = hexlify( json.dumps(recursive_unicode(encoded_metainfo, ignore_errors=True), ensure_ascii=False).encode('utf-8')) return RESTResponse({ "metainfo": encoded_metainfo, "download_exists": download and not download_is_metainfo_request })
async def add_torrent_to_channel(self, request): channel_pk, channel_id = self.get_channel_from_request(request) with db_session: channel = self.session.mds.CollectionNode.get( public_key=database_blob(channel_pk), id_=channel_id) if not channel: return RESTResponse({"error": "Unknown channel"}, status=HTTP_NOT_FOUND) parameters = await request.json() extra_info = {} if parameters.get('description', None): extra_info = {'description': parameters['description']} # First, check whether we did upload a magnet link or URL if parameters.get('uri', None): uri = parameters['uri'] if uri.startswith("http:") or uri.startswith("https:"): async with ClientSession() as session: response = await session.get(uri) data = await response.read() tdef = TorrentDef.load_from_memory(data) elif uri.startswith("magnet:"): _, xt, _ = parse_magnetlink(uri) if (xt and is_infohash(codecs.encode(xt, 'hex')) and (self.session.mds.torrent_exists_in_personal_channel(xt) or channel.copy_torrent_from_infohash(xt))): return RESTResponse({"added": 1}) meta_info = await self.session.dlmgr.get_metainfo(xt, timeout=30, url=uri) if not meta_info: raise RuntimeError("Metainfo timeout") tdef = TorrentDef.load_from_dict(meta_info) else: return RESTResponse({"error": "unknown uri type"}, status=HTTP_BAD_REQUEST) added = 0 if tdef: channel.add_torrent_to_channel(tdef, extra_info) added = 1 return RESTResponse({"added": added}) torrents_dir = None if parameters.get('torrents_dir', None): torrents_dir = parameters['torrents_dir'] if not path_util.isabs(torrents_dir): return RESTResponse( {"error": "the torrents_dir should point to a directory"}, status=HTTP_BAD_REQUEST) recursive = False if parameters.get('recursive'): recursive = parameters['recursive'] if not torrents_dir: return RESTResponse( { "error": "the torrents_dir parameter should be provided when the recursive parameter is set" }, status=HTTP_BAD_REQUEST, ) if torrents_dir: torrents_list, errors_list = channel.add_torrents_from_dir( torrents_dir, recursive) return RESTResponse({ "added": len(torrents_list), "errors": errors_list }) if not parameters.get('torrent', None): return RESTResponse({"error": "torrent parameter missing"}, status=HTTP_BAD_REQUEST) # Try to parse the torrent data # Any errors will be handled by the error_middleware torrent = base64.b64decode(parameters['torrent']) torrent_def = TorrentDef.load_from_memory(torrent) channel.add_torrent_to_channel(torrent_def, extra_info) return RESTResponse({"added": 1})
def test_parse_magnetlink_nomagnet(self): result = parse_magnetlink("http://") self.assertEqual(result, (None, None, []))