public static TorrentMetadata ReadTorrentFile(this Stream stream) { stream = stream ?? throw new ArgumentNullException(nameof(stream)); try { var parser = new BencodeParser(); var dict = parser.Parse <BDictionary>(stream); var torrent = new TorrentMetadata(); foreach (var item in dict) { var readableKey = item.Key.ToString(); if (Parsers.TryGetValue(readableKey, out Action <IBObject, TorrentMetadata> action)) { action(item.Value, torrent); } else { torrent.Extensions.Add(readableKey, item.Value); } } return(torrent); } catch (Exception e) { throw new DataMisalignedException("Torrent was not recognized", e); } }
private static void ExtractAnounceList(IBObject obj, TorrentMetadata file) { var lst = (BList)obj; foreach (var item in lst) { var subList = (BList)item; foreach (var subItem in subList) { file.AnnounceList.Add(new Uri(subItem.ToString())); } } }
public static bool ValidatePiece(this Stream piece, int index, TorrentMetadata meta) { meta = meta ?? throw new ArgumentNullException(nameof(meta)); piece = piece ?? throw new ArgumentNullException(nameof(piece)); if (index < 0 || index >= meta.SHAs.Count) { throw new IndexOutOfRangeException(nameof(index)); } using (var calc = SHA1.Create()) { var chunked = new ReadonlyChunkStream(piece, meta.PieceLength); var hash = calc.ComputeHash(chunked); return(hash.SequenceEqual(meta.SHAs[index])); } }
private static void ExtractInfo(IBObject obj, TorrentMetadata file) { var dict = (BDictionary)obj; bool?singleFile = null; foreach (var item in dict) { var key = item.Key.ToString(); switch (key) { case "length": if (singleFile == false) { throw new DataMisalignedException("Torrent already have key \"files\" key"); } singleFile = true; var fname = string.Empty; if (dict.TryGetValue("name", out IBObject nameVal)) { fname = ((BString)nameVal).ToString(); } file.Files.Add(fname, (BNumber)item.Value); break; case "name": break; case "files": if (singleFile == true) { throw new DataMisalignedException("Torrent already have \"length\" key"); } singleFile = false; var dicLst = (BList)item.Value; foreach (var dicListItem in dicLst) { var sdic = (BDictionary)dicListItem; file.Files.Add(((BString)sdic["path"]).ToString(), (BNumber)sdic["length"]); } break; case "piece length": file.PieceLength = (BNumber)item.Value; break; case "pieces": var shas = (BString)item.Value; if (shas.Value.Count % 20 > 0) { throw new DataMisalignedException("SHA1 hash section is invalid"); } var arr = shas.Value.ToArray(); for (var i = 0; i < shas.Value.Count / 20; i++) { var chunk = new byte[20]; Array.Copy(arr, i * 20, chunk, 0, 20); file.SHAs.Add(chunk); } break; default: file.Extensions.Add($"info.{item.Key}", item.Value); break; } } if (singleFile == null) { throw new DataMisalignedException("Information about torrent contents was not found"); } }
public static Task <IEnumerable <int> > ValidateFileAsync(this Stream stream, TorrentMetadata meta) { stream = stream ?? throw new ArgumentNullException(nameof(stream)); meta = meta ?? throw new ArgumentNullException(nameof(meta)); return(Task.Run(() => { var currentIndex = 0; var lstIndexes = new List <int>(); while (stream.Position != stream.Length && currentIndex < meta.SHAs.Count) { if (stream.ValidatePiece(currentIndex, meta) == false) { lstIndexes.Add(currentIndex); } currentIndex++; } return (IEnumerable <int>)lstIndexes; })); }