private AnnounceResponse HandleAnnounceResponse(BEncoding.Dictionary info) { string failureReason, warningMessage; if (info.TryGetString("failure reason", out failureReason)) { this.failureMessage = failureReason; } if (info.TryGetString("warning message", out warningMessage)) { this.warningMessage = warningMessage; } long completeCount, incompleteCount, downloadedCount, interval, minInterval; if (info.TryGetInteger("complete", out completeCount)) { this.completeCount = (int)completeCount; } if (info.TryGetInteger("incomplete", out incompleteCount)) { this.incompleteCount = (int)incompleteCount; } if (info.TryGetInteger("downloaded", out downloadedCount)) { this.downloadedCount = (int)downloadedCount; } if (info.TryGetInteger("interval", out interval)) { this.interval = TimeSpan.FromSeconds(interval); } if (info.TryGetInteger("min interval", out minInterval)) { this.minInterval = TimeSpan.FromSeconds(minInterval); if (this.interval < this.minInterval) { this.interval = this.minInterval; } } string trackerID; if (info.TryGetString("tracker id", out trackerID)) { this.trackerID = trackerID; } PeerInfo[] peerInfos = null; byte[] compactPeerBytes; BEncoding.List peerList; if (info.TryGetByteArray("peers", out compactPeerBytes)) { peerInfos = DecodePeers(compactPeerBytes); } else if (info.TryGetList("peers", out peerList)) { peerInfos = DecodePeers(peerList); } // Decode IPv6 peers if (info.TryGetByteArray("peers6", out compactPeerBytes)) { var peerInfosV6 = DecodePeers(compactPeerBytes); if (peerInfosV6 != null) { if (peerInfos != null && peerInfos.Length > 0) { peerInfos = peerInfos.Concat(peerInfosV6).ToArray(); } else { peerInfos = peerInfosV6; } } } return(new AnnounceResponse(this, failureReason, warningMessage, peerInfos)); }
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)); } }