"""Inspect a torrent or demonstrate unchanged infohash and damaged piece detection.""" import argparse import json from pathlib import Path import sys from metainfo import decode, encode, inspect, make_metainfo, verify def describe(raw): meta, infohash = inspect(raw) info = meta[b"info"] print(json.dumps({ "announce": meta[b"announce"].decode(), "name": info[b"name"].decode(), "length": info[b"length"], "piece length": info[b"piece length"], "pieces (SHA-1)": [info[b"pieces"][i:i + 20].hex() for i in range(0, len(info[b"pieces"]), 20)], "infohash (raw bencoded info)": infohash, }, indent=2)) def demo(directory): directory.mkdir(parents=True, exist_ok=True) data = bytes(range(256)) * 1024 original = encode(make_metainfo("lesson.bin", data)) meta, original_info = decode(original) changed = dict(meta) changed[b"announce"] = b"http://127.0.0.1:6970/announce" changed[b"comment"] = b"A different comment" modified = encode(changed) assert decode(modified)[1] == original_info damaged = bytearray(data) damaged[65536 + 17] ^= 1 for name, content in [("lesson.bin", data), ("lesson.torrent", original), ("changed.torrent", modified), ("damaged.bin", damaged)]: (directory / name).write_bytes(content) describe(original) print("Original infohash:", inspect(original)[1]) print("Changed infohash:", inspect(modified)[1]) assert inspect(original)[1] == inspect(modified)[1] good, _ = verify(original, data) bad, indices = verify(original, damaged) assert good and not bad and indices == [1] print("PASS: changing announce/comment preserved infohash") print("PASS: original file verified; damaged file rejected, piece index:", indices) print("Files:", directory) def main(): parser = argparse.ArgumentParser(description=__doc__) subs = parser.add_subparsers(dest="command", required=True) p = subs.add_parser("demo") p.add_argument("--directory", type=Path, default=Path(".local/metadata")) p = subs.add_parser("inspect") p.add_argument("torrent", type=Path) p = subs.add_parser("verify") p.add_argument("torrent", type=Path) p.add_argument("file", type=Path) args = parser.parse_args() if args.command == "demo": demo(args.directory) elif args.command == "inspect": describe(args.torrent.read_bytes()) else: ok, bad = verify(args.torrent.read_bytes(), args.file.read_bytes()) print("OK" if ok else "FAILED", "bad pieces:", bad) return 0 if ok else 1 return 0 if __name__ == "__main__": sys.exit(main())