/// <summary> /// Tries to load the torrent info from the specified file path. /// </summary> /// <param name="torrentInfoFilePath">The torrent information file path.</param> /// <param name="torrentInfo">The torrent information.</param> /// <returns> /// True if torrent was loaded successfully, false otherwise. /// </returns> public static bool TryLoad(string torrentInfoFilePath, out TorrentInfo torrentInfo) { torrentInfoFilePath.MustBeValidFilePath(); torrentInfoFilePath.MustFileExist(); return(TryLoad(File.ReadAllBytes(torrentInfoFilePath), out torrentInfo)); }
/// <summary> /// Starts the specified torrent. /// </summary> /// <param name="torrentInfo">The torrent information.</param> public void Start(TorrentInfo torrentInfo) { torrentInfo.CannotBeNull(); TransferManager transfer; Debug.WriteLine($"starting torrent {torrentInfo.InfoHash}"); if (this.IsRunning) { lock (((IDictionary)this.transfers).SyncRoot) { if (!this.transfers.ContainsKey(torrentInfo.InfoHash)) { transfer = new TransferManager(this.ListeningPort, torrentInfo, this.throttlingManager, new PersistenceManager(this.BaseDirectory, torrentInfo.Length, torrentInfo.PieceLength, torrentInfo.PieceHashes, torrentInfo.Files)); transfer.TorrentHashing += this.Transfer_TorrentHashing; transfer.TorrentLeeching += this.Transfer_TorrentLeeching; transfer.TorrentSeeding += this.Transfer_TorrentSeeding; transfer.TorrentStarted += this.Transfer_TorrentStarted; transfer.TorrentStopped += this.Transfer_TorrentStopped; transfer.Start(); this.transfers.Add(torrentInfo.InfoHash, transfer); } else { throw new TorrentClientException($"Torrent {torrentInfo.InfoHash} is already active."); } } } else { throw new TorrentClientException("Torrent client is not running."); } }
/// <summary> /// Initializes a new instance of the <see cref="TransferManager" /> class. /// </summary> /// <param name="listeningPort">The port.</param> /// <param name="torrentInfo">The torrent information.</param> /// <param name="throttlingManager">The throttling manager.</param> /// <param name="persistenceManager">The persistence manager.</param> public TransferManager(int listeningPort, TorrentInfo torrentInfo, ThrottlingManager throttlingManager, PersistenceManager persistenceManager) { listeningPort.MustBeGreaterThanOrEqualTo(IPEndPoint.MinPort); listeningPort.MustBeLessThanOrEqualTo(IPEndPoint.MaxPort); torrentInfo.CannotBeNull(); throttlingManager.CannotBeNull(); persistenceManager.CannotBeNull(); Tracker tracker = null; this.PeerId = "-AB1100-" + "0123456789ABCDEF".Random(24); Debug.WriteLine($"creating torrent manager for torrent {torrentInfo.InfoHash}"); Debug.WriteLine($"local peer id {this.PeerId}"); this.TorrentInfo = torrentInfo; this.throttlingManager = throttlingManager; this.persistenceManager = persistenceManager; // initialize trackers foreach (var trackerUri in torrentInfo.AnnounceList) { if (trackerUri.Scheme == "http" || trackerUri.Scheme == "https") { tracker = new HttpTracker(trackerUri, this.PeerId, torrentInfo.InfoHash, listeningPort); } else if (trackerUri.Scheme == "udp") { tracker = new UdpTracker(trackerUri, this.PeerId, torrentInfo.InfoHash, listeningPort); } if (tracker != null) { tracker.TrackingEvent = TrackingEvent.Started; tracker.Announcing += this.Tracker_Announcing; tracker.Announced += this.Tracker_Announced; tracker.TrackingFailed += this.Tracker_TrackingFailed; tracker.BytesLeftToDownload = this.TorrentInfo.Length - this.Downloaded; tracker.WantedPeerCount = 30; // we can handle 30 peers at a time this.trackers.Add(trackerUri, tracker); } else { // unsupported tracker protocol Debug.WriteLine($"unsupported tracker protocol {trackerUri.Scheme}"); } } }
/// <summary> /// Initializes a new instance of the <see cref="TorrentLeechingEventArgs" /> class. /// </summary> /// <param name="torrentInfo">The torrent information.</param> public TorrentLeechingEventArgs(TorrentInfo torrentInfo) { torrentInfo.CannotBeNull(); this.TorrentInfo = torrentInfo; }
/// <summary> /// Initializes a new instance of the <see cref="TorrentStartedEventArgs" /> class. /// </summary> /// <param name="torrentInfo">The torrent information.</param> public TorrentStartedEventArgs(TorrentInfo torrentInfo) { torrentInfo.CannotBeNull(); this.TorrentInfo = torrentInfo; }
/// <summary> /// Tries to load the torrent info from the specified binary data. /// </summary> /// <param name="data">The data.</param> /// <param name="torrentInfo">The torrent information.</param> /// <returns>True if torrent was loaded successfully, false otherwise.</returns> public static bool TryLoad(byte[] data, out TorrentInfo torrentInfo) { data.CannotBeNullOrEmpty(); BEncodedValue value; BEncodedDictionary general; BEncodedDictionary info; List <TorrentFileInfo> files = new List <TorrentFileInfo>(); long pieceLength; List <string> pieceHashes = new List <string>(); bool isPrivate = false; Uri tmpUri; List <Uri> announceList = new List <Uri>(); DateTime? creationDate = null; string comment = null; string createdBy = null; Encoding encoding = Encoding.ASCII; string filePath; long fileLength = 0; string fileHash; string tmpString; BEncodedString infoKey = new BEncodedString("info"); BEncodedString pieceLengthKey = new BEncodedString("piece length"); BEncodedString piecesKey = new BEncodedString("pieces"); BEncodedString privateKey = new BEncodedString("private"); BEncodedString nameKey = new BEncodedString("name"); BEncodedString lengthKey = new BEncodedString("length"); BEncodedString md5sumKey = new BEncodedString("md5sum"); BEncodedString filesKey = new BEncodedString("files"); BEncodedString pathKey = new BEncodedString("path"); BEncodedString announceKey = new BEncodedString("announce"); BEncodedString announceListKey = new BEncodedString("announce-list"); BEncodedString creationDateKey = new BEncodedString("creation date"); BEncodedString commentKey = new BEncodedString("comment"); BEncodedString createdByKey = new BEncodedString("created by"); BEncodedString encodingKey = new BEncodedString("encoding"); torrentInfo = null; try { value = BEncodedValue.Decode(data); } catch (BEncodingException) { return(false); } if (value is BEncodedDictionary) { general = value as BEncodedDictionary; if (general.ContainsKey(infoKey) && general[infoKey] is BEncodedDictionary) { info = general[infoKey] as BEncodedDictionary; // piece length if (info.ContainsKey(pieceLengthKey) && info[pieceLengthKey] is BEncodedNumber) { pieceLength = info[pieceLengthKey].As <BEncodedNumber>().Number; } else { return(false); } // pieces if (info.ContainsKey(piecesKey) && info[piecesKey] is BEncodedString && info[piecesKey].As <BEncodedString>().TextBytes.Length % 20 == 0) { for (int i = 0; i < info[piecesKey].As <BEncodedString>().TextBytes.Length; i += 20) { byte[] tmpBytes = new byte[20]; Array.Copy(info[piecesKey].As <BEncodedString>().TextBytes, i, tmpBytes, 0, tmpBytes.Length); pieceHashes.Add(tmpBytes.ToHexaDecimalString()); } if (pieceHashes.Count == 0) { return(false); } } else { return(false); } // is private if (info.ContainsKey(privateKey) && info[privateKey] is BEncodedNumber) { isPrivate = info[privateKey].As <BEncodedNumber>().Number == 1; } // files if (info.ContainsKey(nameKey) && info[nameKey] is BEncodedString && info.ContainsKey(lengthKey) && info[lengthKey] is BEncodedNumber) { // single file filePath = info[nameKey].As <BEncodedString>().Text; fileLength = info[lengthKey].As <BEncodedNumber>().Number; if (info.ContainsKey(md5sumKey) && info[md5sumKey] is BEncodedString) { fileHash = info[md5sumKey].As <BEncodedString>().Text; } else { fileHash = null; } files.Add(new TorrentFileInfo(filePath, fileHash, fileLength)); } else if (info.ContainsKey(nameKey) && info[nameKey] is BEncodedString && info.ContainsKey(filesKey) && info[filesKey] is BEncodedList) { tmpString = info[nameKey].As <BEncodedString>().Text; // multi file foreach (var item in info[filesKey].As <BEncodedList>()) { if (item is BEncodedDictionary && item.As <BEncodedDictionary>().ContainsKey(pathKey) && item.As <BEncodedDictionary>()[pathKey] is BEncodedList && item.As <BEncodedDictionary>()[pathKey].As <BEncodedList>().All(x => x is BEncodedString) && item.As <BEncodedDictionary>().ContainsKey(lengthKey) && item.As <BEncodedDictionary>()[lengthKey] is BEncodedNumber) { filePath = Path.Combine(tmpString, Path.Combine(item.As <BEncodedDictionary>()[pathKey].As <BEncodedList>().Select(x => x.As <BEncodedString>().Text).ToArray())); fileLength = item.As <BEncodedDictionary>()[lengthKey].As <BEncodedNumber>().Number; if (item.As <BEncodedDictionary>().ContainsKey(md5sumKey) && item.As <BEncodedDictionary>()[md5sumKey] is BEncodedString) { fileHash = item.As <BEncodedDictionary>()[md5sumKey].As <BEncodedString>().Text; } else { fileHash = null; } files.Add(new TorrentFileInfo(filePath, fileHash, fileLength)); } else { return(false); } } if (files.Count == 0) { return(false); } } else { return(false); } } else { return(false); } // announce if (general.ContainsKey(announceKey) && general[announceKey] is BEncodedString && Uri.TryCreate(general[announceKey].As <BEncodedString>().Text, UriKind.Absolute, out tmpUri)) { announceList.Add(tmpUri); } else { return(false); } // announce list if (general.ContainsKey(announceListKey) && general[announceListKey] is BEncodedList) { foreach (var item in general[announceListKey].As <BEncodedList>()) { if (item is BEncodedList) { foreach (var item2 in item.As <BEncodedList>()) { if (Uri.TryCreate(item2.As <BEncodedString>().Text, UriKind.Absolute, out tmpUri)) { announceList.Add(tmpUri); } } } } announceList = announceList.Select(x => x.AbsoluteUri).Distinct().Select(x => new Uri(x)).ToList(); } // creation adte if (general.ContainsKey(creationDateKey) && general[creationDateKey] is BEncodedNumber) { creationDate = new DateTime(1970, 1, 1, 0, 0, 0, 0).AddSeconds(general[creationDateKey].As <BEncodedNumber>().Number).ToLocalTime(); } // comment if (general.ContainsKey(commentKey) && general[commentKey] is BEncodedString) { comment = general[commentKey].As <BEncodedString>().Text; } // created by if (general.ContainsKey(createdByKey) && general[createdByKey] is BEncodedString) { createdBy = general[createdByKey].As <BEncodedString>().Text; } // encoding if (general.ContainsKey(encodingKey) && general[encodingKey] is BEncodedString) { if (general[encodingKey].As <BEncodedString>().Text == "UTF8") { encoding = Encoding.UTF8; } } torrentInfo = new TorrentInfo( general, info.Encode().CalculateSha1Hash().ToHexaDecimalString(), pieceLength, new ReadOnlyCollection <string>(pieceHashes), isPrivate, new ReadOnlyCollection <Uri>(announceList), creationDate, comment, createdBy, encoding, new ReadOnlyCollection <TorrentFileInfo>(files)); return(true); } else { return(false); } }