private ScrapeResponse HandleScrapeResponse(BEncoding.Dictionary info) { BEncoding.Dictionary files; if (!info.TryGetDictionary("files", out files)) { return(null); } var torrentInfos = new List <ScrapeResponse.TorrentInfo>(files.Count); foreach (var torrentInfo in files) { var torrentInfoDict = (torrentInfo.Value as BEncoding.Dictionary); if (torrentInfoDict == null) { continue; } long completeCount, incompleteCount, downloadedCount; string torrentName; if (!torrentInfoDict.TryGetInteger("complete", out completeCount)) { completeCount = 0; } if (!torrentInfoDict.TryGetInteger("incomplete", out incompleteCount)) { incompleteCount = 0; } if (!torrentInfoDict.TryGetInteger("downloaded", out downloadedCount)) { downloadedCount = 0; } if (!torrentInfoDict.TryGetString("name", out torrentName)) { torrentName = null; } byte[] infoHashBytes = Encoding.UTF8.GetBytes(torrentInfo.Key); // TODO: Is this okay to do? var infoHash = new InfoHash(infoHashBytes); torrentInfos.Add(new ScrapeResponse.TorrentInfo(infoHash, (int)completeCount, (int)incompleteCount, (int)downloadedCount, torrentName)); } return(new ScrapeResponse(this, torrentInfos.ToArray())); }
private void Load(BEncoding.Dictionary torrentInfo) { ClearInfo(); BEncoding.List announceList; if (torrentInfo.TryGetList("announce-list", out announceList)) { int announceCount = announceList.Count; var newAnnounceList = new List <AnnounceItem>(announceCount); for (int i = 0; i < announceCount; i++) { var urlList = (announceList[i] as BEncoding.List); if (urlList != null) { int urlCount = urlList.Count; var newUrlList = new List <string>(urlCount); for (int j = 0; j < urlCount; j++) { string url = urlList.GetString(j); if (!string.IsNullOrEmpty(url)) { newUrlList.Add(url); } } if (newUrlList.Count > 0) { newAnnounceList.Add(new AnnounceItem(newUrlList.ToArray())); } } } announces = newAnnounceList.ToArray(); } else { string announceUrl; if (torrentInfo.TryGetString("announce", out announceUrl)) { announces = new AnnounceItem[] { new AnnounceItem(announceUrl) }; } } long creationDateTimestamp; if (!torrentInfo.TryGetString("comment", out comment)) { comment = null; } if (!torrentInfo.TryGetString("created by", out createdBy)) { createdBy = null; } if (torrentInfo.TryGetInteger("creation date", out creationDateTimestamp)) { creationDate = TimeHelper.GetDateFromUnixTimestamp(creationDateTimestamp); } else { creationDate = DateTime.MinValue; } BEncoding.Dictionary info; if (!torrentInfo.TryGetDictionary("info", out info)) { throw new InvalidDataException("The info dictionary is missing in the torrent meta-data."); } infoHash = ComputeInfoHash(info); long pieceSize; if (!info.TryGetInteger("piece length", out pieceSize)) { throw new InvalidDataException("The piece length is missing in the torrent meta-data. But it is required for all torrents."); } byte[] pieceHashData; if (!info.TryGetByteArray("pieces", out pieceHashData)) { throw new InvalidDataException("The piece hashes are missing in the torrent meta-data. But it is required for all torrents."); } else if ((pieceHashData.Length % 20) != 0) { throw new InvalidDataException("The piece hashes are invalid in the torrent meta-data. It must be a multiple of 20 (for SHA1 hashes)."); } int pieceCount = (pieceHashData.Length / 20); pieceHashes = new PieceHash[pieceCount]; for (int i = 0; i < pieceCount; i++) { byte[] pieceHashBytes = new byte[20]; Buffer.BlockCopy(pieceHashData, (i * 20), pieceHashBytes, 0, 20); pieceHashes[i] = new PieceHash(pieceHashBytes); } long isPrivate; if (info.TryGetInteger("private", out isPrivate) && isPrivate == 1) { this.isPrivate = true; } if (!info.TryGetString("source", out source)) { source = null; } if (!info.TryGetString("name", out name) || string.IsNullOrEmpty(name)) { throw new InvalidDataException("The name string is missing in the torrent meta-data. But it is required for all torrents."); } this.pieceSize = (int)pieceSize; BEncoding.List fileList; if (info.TryGetList("files", out fileList)) { int fileCount = fileList.Count; files = new FileItem[fileCount]; for (int i = 0; i < fileCount; i++) { var fileItem = fileList[i] as BEncoding.Dictionary; if (fileItem == null) { throw new InvalidDataException("The format of the torrent meta-data is invalid. Expected a dictionary of file information."); } long fileSize; if (!fileItem.TryGetInteger("length", out fileSize)) { throw new InvalidDataException("The format of the torrent meta-data is invalid. Expected a file size."); } string fileMD5HashHex; byte[] fileMD5Hash = null; if (fileItem.TryGetString("md5sum", out fileMD5HashHex) && fileMD5HashHex.Length == 32) { fileMD5Hash = HexHelper.HexToBytes(fileMD5HashHex); } BEncoding.List pathList; if (!fileItem.TryGetList("path", out pathList)) { throw new InvalidDataException("The format of the torrent meta-data is invalid. Expected a file path."); } int pathPartCount = pathList.Count; string[] pathParts = new string[pathPartCount]; for (int j = 0; j < pathPartCount; j++) { string pathPart = pathList.GetString(j); if (string.IsNullOrEmpty(pathPart)) { throw new InvalidDataException("The format of the torrent meta-data is invalid. Expected a file path."); } pathParts[j] = pathPart; } string filePath = string.Join("/", pathParts); files[i] = new FileItem(filePath, fileSize, fileMD5Hash); } } else { long fileSize; if (!info.TryGetInteger("length", out fileSize)) { throw new InvalidDataException("The file length is missing in the torrent meta-data. But it is required for single-file torrents."); } string fileMD5HashHex; byte[] fileMD5Hash = null; if (info.TryGetString("md5sum", out fileMD5HashHex) && fileMD5HashHex.Length == 32) { fileMD5Hash = HexHelper.HexToBytes(fileMD5HashHex); } files = new FileItem[] { new FileItem(name, fileSize, fileMD5Hash) }; } totalSize = ComputeTotalSize(); int expectedPieceCount = (int)((totalSize - 1) / pieceSize) + 1; if (expectedPieceCount != pieceHashes.Length) { throw new InvalidOperationException(string.Format("The calculated number of pieces is {0} while there are {1} piece hashes.", expectedPieceCount, pieceHashes.Length)); } }