private void CheckMetadata(BencodedString announce, BencodedDictionary info) { if (announce == null || info == null) { throw new TorrentException("Invalid metadata, \'announce\'/\'info\' not of expected type."); } }
private void CheckMetadata(BencodedString announce, BencodedDictionary info) { if (announce == null || info == null) { throw new TorrentException(string.Format("Invalid metadata, 'announce'/'info' not of expected type.")); } }
private void LoadMetadata() { BencodedDictionary metadata = GetMetadata(); var announce = metadata["announce"] as BencodedString; var info = metadata["info"] as BencodedDictionary; CheckMetadata(announce, info); var pieceLength = info["piece length"] as BencodedInteger; List <byte[]> checksumList = GetRawChecksums(info, pieceLength); BencodedString name = GetNameFromInfo(info); List <FileEntry> decodedFiles = DecodeFiles(info, name); if (metadata.ContainsKey("announce-list")) { var announceList = (BencodedList)metadata["announce-list"]; AnnounceList = DecodeAnnounceList(announceList).AsReadOnly(); } AnnounceURL = announce; Checksums = checksumList.AsReadOnly(); Files = decodedFiles.AsReadOnly(); PieceLength = pieceLength; PieceCount = Checksums.Count; Name = name; InfoHash = ComputeInfoHash(info); Announces = CreateAnnouces(AnnounceURL, AnnounceList); TotalLength = Files.Sum(f => f.Length); }
/// <summary> /// Creates a .torrent file with the given data. /// </summary> /// <param name="name">The name of the file.</param> /// <param name="inputFiles">A List'string containing all the files to be added to the .torrent file.</param> /// <param name="filesDir">The root directory of all the files in the .torrent file.</param> /// <param name="announce">The announce URL of the tracker, in which the .torrent file will be uploaded to.</param> /// <param name="announceList">A list of announce URLs.</param> /// <param name="pieceLength">The number of bytes in each piece.</param> /// <param name="pieces">A list of strings consisting of the concatenation of all 20-byte SHA1 hash values.</param> /// <returns>A string representig the content of the .torrent file.</returns> public static string Create(string name, List <FileEntry> inputFiles, string filesDir, string announce, List <string> announceList, int pieceLength, List <byte[]> pieces) { var clientVersion = Global.Instance.Version; var res = new BencodedDictionary(); res.Add("announce", new BencodedString(announce)); var alist = new BencodedList(); announceList.ForEach(a => alist.Add(new BencodedList { new BencodedString(a) })); res.Add("announce-list", alist); res.Add("created by", new BencodedString("rtTorrent/" + clientVersion)); res.Add("creation date", new BencodedInteger(GetUnixTime())); res.Add("encoding", new BencodedString("UTF-8")); var info = new BencodedDictionary(); info.Add("piece length", new BencodedInteger(pieceLength)); var piecesString = new StringBuilder(); pieces.ForEach(piece => piece.ForEach(b => piecesString.Append((char)b))); info.Add("pieces", new BencodedString(piecesString.ToString())); //"1" - the client MUST publish its presence to get other peers ONLY via the trackers explicitly described in the metainfo file //"0" - the client may obtain peer from other means, e.g. PEX peer exchange, dht. Here, "private" may be read as "no external peer source". //info.Add("private", new BencodedInteger(0)); if (inputFiles.Count == 1) { info.Add("name", new BencodedString(name)); info.Add("length", new BencodedInteger(inputFiles[0].Length)); //MD5 hash of the file should be added here. The BitTorrent protocol specifies it as NOT needed. } else { info.Add("name", new BencodedString(filesDir)); var files = new BencodedList(); foreach (var inputFile in inputFiles) { var aFile = new BencodedDictionary(); aFile.Add("length", new BencodedInteger(inputFile.Length)); var filePath = new BencodedList(); inputFile.Name.Split(Path.DirectorySeparatorChar).ForEach( dir => filePath.Add(new BencodedString(dir))); aFile.Add("path", filePath); files.Add(aFile); } info.Add("files", files); } res.Add("info", info); return(res.ToString()); }
private byte[] ComputeInfoHash(BencodedDictionary info) { var hasher = SHA1.Create(); var bencoded = info.ToBencodedString(); var bytes = bencoded.Select(c => (byte)c).ToArray(); var hash = hasher.ComputeHash(bytes); return(hash); }
private BencodedString GetNameFromInfo(BencodedDictionary info) { var name = info["name"] as BencodedString; if (name == null) { throw new TorrentException("Invalid metadata in file, \'name\' not of expected type."); } return(name); }
private FileEntry CreateTorrentFile(BencodedDictionary file) { var filePathList = (file["path"] as BencodedList).Select(s => (string)(s as BencodedString)).ToArray(); var fileLength = file["length"] as BencodedInteger; CheckFileProperties(filePathList, fileLength); var filePath = Path.Combine(filePathList); var torrentFile = new FileEntry(filePath, fileLength); return(torrentFile); }
private List <byte[]> GetRawChecksums(BencodedDictionary info, BencodedInteger pieceLength) { var rawChecksums = (info["pieces"] as BencodedString).Select(c => (byte)c).ToArray(); if (pieceLength == null || rawChecksums == null || rawChecksums.Length % ChecksumSize != 0) { throw new TorrentException( "Invalid metadata, \'piece length\'/\'pieces\' not of expected type, or invalid length of \'pieces\'."); } var slicedChecksums = rawChecksums.Batch(ChecksumSize).Select(e => e.ToArray()); return(slicedChecksums.ToList()); }
private void CheckMetadata(BencodedDictionary metadata) { if (metadata == null) { throw new TorrentException("Invalid metadata."); } if (!metadata.ContainsKey("announce")) { throw new TorrentException("Invalid metadata, \'announce\' not found."); } if (!metadata.ContainsKey("info")) { throw new TorrentException("Invalid metadata, \'info\' not found."); } }
private List <FileEntry> DecodeFiles(BencodedDictionary info, BencodedString name) { var decodedFiles = new List <FileEntry>(); if (info.ContainsKey("files")) { var files = info["files"] as BencodedList; CheckInfoFiles(files); foreach (BencodedDictionary file in files) { var torrentFile = CreateTorrentFile(file); decodedFiles.Add(torrentFile); } } else { var fileLength = info["length"] as BencodedInteger; CheckFileLength(fileLength); decodedFiles.Add(new FileEntry(name, fileLength)); } return(decodedFiles); }
private BencodedString GetNameFromInfo(BencodedDictionary info) { var name = info["name"] as BencodedString; if (name == null) throw new TorrentException(string.Format("Invalid metadata in file, 'name' not of expected type.")); return name; }
private byte[] HashBencodeDictionary(BencodedDictionary bencodedDictionary) { return(new SHA1Managed().ComputeHash(Encoding.GetEncoding(28591).GetBytes(bencodedDictionary.RawValue))); }
/// <summary> /// Creates a .torrent file with the given data. /// </summary> /// <param name="name">The name of the file.</param> /// <param name="inputFiles">A List'string containing all the files to be added to the .torrent file.</param> /// <param name="filesDir">The root directory of all the files in the .torrent file.</param> /// <param name="announce">The announce URL of the tracker, in which the .torrent file will be uploaded to.</param> /// <param name="announceList">A list of announce URLs.</param> /// <param name="pieceLength">The number of bytes in each piece.</param> /// <param name="pieces">A list of strings consisting of the concatenation of all 20-byte SHA1 hash values.</param> /// <returns>A string representig the content of the .torrent file.</returns> public static string Create(string name, List<FileEntry> inputFiles, string filesDir, string announce, List<string> announceList, int pieceLength, List<byte[]> pieces) { Contract.Requires(name != null); Contract.Requires(inputFiles != null); Contract.Requires(inputFiles.Count >= 1); Contract.Requires(announce != null); Contract.Requires(pieces != null); var clientVersion = Global.Instance.Version; var res = new BencodedDictionary(); res.Add("announce", new BencodedString(announce)); var alist = new BencodedList(); announceList.ForEach(a => alist.Add(new BencodedList {new BencodedString(a)})); res.Add("announce-list", alist); res.Add("created by", new BencodedString("rtTorrent/" + clientVersion)); res.Add("creation date", new BencodedInteger(GetUnixTime())); res.Add("encoding", new BencodedString("UTF-8")); var info = new BencodedDictionary(); info.Add("piece length", new BencodedInteger(pieceLength)); var piecesString = new StringBuilder(); pieces.ForEach(piece => piece.ForEach(b => piecesString.Append((char) b))); info.Add("pieces", new BencodedString(piecesString.ToString())); //"1" - the client MUST publish its presence to get other peers ONLY via the trackers explicitly described in the metainfo file //"0" - the client may obtain peer from other means, e.g. PEX peer exchange, dht. Here, "private" may be read as "no external peer source". //info.Add("private", new BencodedInteger(0)); if (inputFiles.Count == 1) { info.Add("name", new BencodedString(name)); info.Add("length", new BencodedInteger(inputFiles[0].Length)); //MD5 hash of the file should be added here. The BitTorrent protocol specifies it as NOT needed. } else { info.Add("name", new BencodedString(filesDir)); var files = new BencodedList(); foreach (FileEntry inputFile in inputFiles) { var aFile = new BencodedDictionary(); aFile.Add("length", new BencodedInteger(inputFile.Length)); var filePath = new BencodedList(); inputFile.Name.Split(Path.DirectorySeparatorChar).ForEach( dir => filePath.Add(new BencodedString(dir))); aFile.Add("path", filePath); files.Add(aFile); } info.Add("files", files); } res.Add("info", info); return res.ToString(); }
private void CheckMetadata(BencodedString announce, BencodedDictionary info) { if (announce == null || info == null) throw new TorrentException(string.Format("Invalid metadata, 'announce'/'info' not of expected type.")); }
private void CheckMetadata(BencodedDictionary metadata) { if (metadata == null) throw new TorrentException(string.Format("Invalid metadata.")); if (!metadata.ContainsKey("announce")) throw new TorrentException(string.Format("Invalid metadata, 'announce' not found.")); if (!metadata.ContainsKey("info")) throw new TorrentException(string.Format("Invalid metadata, 'info' not found.")); }
private byte[] ComputeInfoHash(BencodedDictionary info) { SHA1 hasher = SHA1.Create(); string bencoded = info.ToBencodedString(); byte[] bytes = bencoded.Select(c => (byte) c).ToArray(); byte[] hash = hasher.ComputeHash(bytes); return hash; }
public static IPEndPoint FromBencoded(BencodedDictionary peer) { return new IPEndPoint(IPAddress.Parse((BencodedString) peer["ip"]), (ushort) (BencodedInteger) peer["port"]); }
private FileEntry CreateTorrentFile(BencodedDictionary file) { string[] filePathList = (file["path"] as BencodedList).Select(s => (string) (s as BencodedString)).ToArray(); var fileLength = file["length"] as BencodedInteger; CheckFileProperties(filePathList, fileLength); string filePath = Path.Combine(filePathList); var torrentFile = new FileEntry(filePath, fileLength); return torrentFile; }
private List<FileEntry> DecodeFiles(BencodedDictionary info, BencodedString name) { var decodedFiles = new List<FileEntry>(); if (info.ContainsKey("files")) { var files = info["files"] as BencodedList; CheckInfoFiles(files); foreach (BencodedDictionary file in files) { FileEntry torrentFile = CreateTorrentFile(file); decodedFiles.Add(torrentFile); } } else { var fileLength = info["length"] as BencodedInteger; CheckFileLength(fileLength); decodedFiles.Add(new FileEntry(name, fileLength)); } return decodedFiles; }
public static IPEndPoint FromBencoded(BencodedDictionary peer) { return(new IPEndPoint(IPAddress.Parse((BencodedString)peer["ip"]), (ushort)(BencodedInteger)peer["port"])); }
private List<byte[]> GetRawChecksums(BencodedDictionary info, BencodedInteger pieceLength) { byte[] rawChecksums = (info["pieces"] as BencodedString).Select(c=>(byte)c).ToArray(); if (pieceLength == null || rawChecksums == null || rawChecksums.Length%ChecksumSize != 0) throw new TorrentException( string.Format( "Invalid metadata, 'piece length'/'pieces' not of expected type, or invalid length of 'pieces'.")); IEnumerable<byte[]> slicedChecksums = rawChecksums.Batch(ChecksumSize).Select(e => e.ToArray()); return slicedChecksums.ToList(); }