private void ClearInfo() { name = null; comment = null; createdBy = null; creationDate = DateTime.MinValue; isPrivate = false; source = null; infoHash = default(InfoHash); pieceSize = 0; pieceHashes = null; totalSize = 0L; files = null; announces = null; }
private BEncoding.Dictionary Save() { var torrentInfo = new BEncoding.Dictionary(); // Make sure that there is a piece size if (pieceSize == 0) { throw new InvalidOperationException("No piece size has been set yet."); } else if (!MathHelper.IsPowerOfTwo(pieceSize)) { throw new InvalidOperationException("The piece size must be a power of two."); } else if (files == null || files.Length == 0) { throw new InvalidOperationException("No files have been defined yet."); } else if (totalSize == 0L) { throw new InvalidOperationException("The total size of the torrent cannot be 0."); } int pieceCount = (int)((totalSize - 1) / pieceSize) + 1; if (pieceCount != pieceHashes.Length) { throw new InvalidOperationException(string.Format("The calculated number of pieces is {0} while there are {1} piece hashes.", pieceCount, pieceHashes.Length)); } if (announces != null && announces.Length > 0) { if (!string.IsNullOrEmpty(announces[0].Url)) { torrentInfo.Add("announce", announces[0].Url); } var trackerList = new BEncoding.List(); for (int i = 0; i < announces.Length; i++) { var urls = announces[i].Urls; if (urls == null || urls.Length == 0) { continue; } var urlList = new BEncoding.List(); for (int j = 0; j < urls.Length; j++) { if (!string.IsNullOrEmpty(urls[j])) { urlList.Add(urls[j]); } } if (urlList.Count > 0) { trackerList.Add(urlList); } } if (trackerList.Count > 0) { torrentInfo.Add("announce-list", trackerList); } } if (!string.IsNullOrEmpty(comment)) { torrentInfo.Add("comment", comment); } if (!string.IsNullOrEmpty(createdBy)) { torrentInfo.Add("created by", createdBy); } if (creationDate != DateTime.MinValue) { long creationDateTimestamp = TimeHelper.GetUnixTimestampFromDate(creationDate); torrentInfo.Add("creation date", creationDateTimestamp); } // Get the piece hash data byte[] pieceHashData = new byte[20 * pieceCount]; for (int i = 0; i < pieceCount; i++) { Buffer.BlockCopy(pieceHashes[i].Hash, 0, pieceHashData, (i * 20), 20); } var info = new BEncoding.Dictionary(); info.Add("piece length", pieceSize); info.Add("pieces", pieceHashData); info.Add("private", (isPrivate ? 1 : 0)); if (!string.IsNullOrEmpty(source)) { info.Add("source", source); } if (files != null) { if (files.Length == 1) { var fileItem = files[0]; info.Add("length", fileItem.Size); if (fileItem.MD5Hash != null && fileItem.MD5Hash.Length == 16) { string fileMD5HashHex = HexHelper.BytesToHex(fileItem.MD5Hash); info.Add("md5sum", fileMD5HashHex); } info.Add("name", GetFileName(fileItem.Path)); } else if (files.Length > 1) { info.Add("name", name ?? "Unnamed"); var fileList = new BEncoding.List(); for (int i = 0; i < files.Length; i++) { var fileItem = files[i]; var fileDictionary = new BEncoding.Dictionary(); fileDictionary.Add("length", fileItem.Size); if (fileItem.MD5Hash != null && fileItem.MD5Hash.Length == 16) { string fileMD5HashHex = HexHelper.BytesToHex(fileItem.MD5Hash); info.Add("md5sum", fileMD5HashHex); } string[] pathParts = fileItem.Path.Split(new char[] { '/' }); var pathList = new BEncoding.List(pathParts); fileDictionary.Add("path", pathList); fileList.Add(fileDictionary); } info.Add("files", fileList); } } infoHash = ComputeInfoHash(info); torrentInfo.Add("info", info); return(torrentInfo); }
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)); } }