/// <summary> /// Stops a download if one is running, checks if the donwnloader thread actually stops. /// </summary> /// <returns>true if stopped succesfully</returns> public bool AbortDownloader(int timeOut) { if (CheckIfDownloading()) { OnDccDebugMessage?.Invoke(this, new DCCDebugMessageArgs("File " + CurrentFilePath + " will be deleted after aborting.", "DCC DOWNLOADER")); _shouldAbort = true; int timeout = 0; while (CheckIfDownloading()) { if (timeout > timeOut) { return(false); } timeout++; Thread.Sleep(1); } return(true); } else { return(false); } }
/// <summary> /// Add a new Download to the queue /// </summary> /// <param name="ircData">The irc command.</param> /// <param name="downloadDirectory">The download directory.</param> /// <param name="bot">The bot.</param> /// <param name="packageNumber">The package number.</param> /// <param name="client">The client.</param> /// <returns>The created Download.</returns> public Download StartDownload(string ircData, string downloadDirectory, string bot, string packageNumber, IrcClient client) { var download = new Download(ircData, downloadDirectory, bot, packageNumber, client, _tokenSource.Token); download.OnDccDebugMessage += (sender, args) => OnDccDebugMessage?.Invoke(sender, args); download.OnDccEvent += (sender, args) => OnDccEvent?.Invoke(sender, args); _downloadQueue.TryAdd(download, 0, _tokenSource.Token); return(download); }
/// <summary> /// Starts a downloader by parsing the received message from the irc server on information /// </summary> /// <param name="dccString">message from irc server</param> /// <param name="downloaddir">download directory</param> /// <param name="bot">bot where the file came from</param> /// <param name="pack">pack on bot where the file came from</param> /// <param name="client">irc client used the moment it received the dcc message, used for sending abort messages when download fails unexpectedly</param> public void StartDownloader(string dccString, string downloaddir, string bot, string pack, IrcClient client) { if ((dccString ?? downloaddir ?? bot ?? pack) != null && dccString.Contains("SEND") && !IsDownloading) { NewDccString = dccString; _curDownloadDir = downloaddir; BotName = bot; PackNum = pack; _ircClient = client; //parsing the data for downloader thread UpdateStatus("PARSING"); bool isParsed = ParseData(dccString); //try to set the necesary information for the downloader if (isParsed) { _shouldAbort = false; //start the downloader thread _downloader = new Thread(new ThreadStart(this.Downloader)); _downloader.IsBackground = true; _downloader.Start(); } else { OnDccDebugMessage?.Invoke(this, new DCCDebugMessageArgs( "Can't parse dcc string and start downloader, failed to parse data, removing from que\n", "DCC STARTER")); _ircClient.SendMessageToAll("/msg " + BotName + " xdcc remove " + PackNum); _ircClient.SendMessageToAll("/msg " + BotName + " xdcc cancel"); } } else { if (IsDownloading) { OnDccDebugMessage?.Invoke(this, new DCCDebugMessageArgs("You are already downloading! Ignore SEND request\n", "DCC STARTER")); } else { OnDccDebugMessage?.Invoke(this, new DCCDebugMessageArgs("DCC String does not contain SEND and/or invalid values for parsing! Ignore SEND request\n", "DCC STARTER")); } } }
/// <summary> /// Reverses IP from little endian to big endian or vice versa depending on what succeeds. /// </summary> /// <param name="ip">ip string</param> /// <returns>reversed ip string</returns> private string ReverseIp(string ip) { string[] parts = ip.Trim().Split('.'); if (parts.Length >= 3) { OnDccDebugMessage?.Invoke(this, new DCCDebugMessageArgs("DCCClient: converting ip: " + ip, "DCC IP PARSER")); string NewIP = parts[3] + "." + parts[2] + "." + parts[1] + "." + parts[0]; OnDccDebugMessage?.Invoke(this, new DCCDebugMessageArgs("DCCClient: to: " + NewIP, "DCC IP PARSER")); return(NewIP); } else { OnDccDebugMessage?.Invoke(this, new DCCDebugMessageArgs("DCCClient: converting ip: " + ip, "DCC IP PARSER")); OnDccDebugMessage?.Invoke(this, new DCCDebugMessageArgs("DCCClient: amount of parts: " + parts.Length, "DCC IP PARSER")); return("0.0.0.0"); } }
/// <summary> /// Method ran within downloader thread, starts a connection to the file server, and receives the file accordingly, sends updates using event handler during the download. /// </summary> public void Downloader() { UpdateStatus("WAITING"); Buffer = new List <byte>(); //combining download directory path with filename if (_curDownloadDir != null) { if (_curDownloadDir != string.Empty) { if (_curDownloadDir.Length > 0) { if (!Directory.Exists(_curDownloadDir)) { Directory.CreateDirectory(_curDownloadDir); } } else { _curDownloadDir = Path.Combine(Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location), "Downloads"); if (!Directory.Exists(_curDownloadDir)) { Directory.CreateDirectory(_curDownloadDir); } } } else { _curDownloadDir = Path.Combine(Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location), "Downloads"); if (!Directory.Exists(_curDownloadDir)) { Directory.CreateDirectory(_curDownloadDir); } } } else { _curDownloadDir = Path.Combine(Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location), "Downloads"); if (!Directory.Exists(_curDownloadDir)) { Directory.CreateDirectory(_curDownloadDir); } } string dlDirAndFileName = Path.Combine(_curDownloadDir, NewFileName); CurrentFilePath = dlDirAndFileName; try { if (!File.Exists(dlDirAndFileName)) { OnDccDebugMessage?.Invoke(this, new DCCDebugMessageArgs("File does not exist yet, start connection with: " + NewIP + ":" + NewPortNum + Environment.NewLine, "DCC DOWNLOADER")); Thread.Sleep(500); //start connection with tcp server IPAddress ip = IPAddress.Parse(NewIP); using (TcpClient dltcp = new TcpClient(ip.AddressFamily)) { dltcp.Connect(ip, NewPortNum); using (NetworkStream dlstream = dltcp.GetStream()) { //succesfully connected to tcp server, status is downloading UpdateStatus("DOWNLOADING"); //values to keep track of progress Int64 bytesReceived = 0; Int64 oldBytesReceived = 0; Int64 oneprocent = NewFileSize / 100; DateTime start = DateTime.Now; bool timedOut = false; //values to keep track of download position int count; //to me this buffer size seemed to be the most efficient. byte[] buffer; if (NewFileSize > 1048576) { OnDccDebugMessage?.Invoke(this, new DCCDebugMessageArgs("Big file, big buffer (1 mb) \n ", "DCC DOWNLOADER")); buffer = new byte[16384]; } else if (NewFileSize < 1048576 && NewFileSize > 2048) { OnDccDebugMessage?.Invoke(this, new DCCDebugMessageArgs("Smaller file (< 1 mb), smaller buffer (2 kb) \n ", "DCC DOWNLOADER")); buffer = new byte[2048]; } else if (NewFileSize < 2048 && NewFileSize > 128) { OnDccDebugMessage?.Invoke(this, new DCCDebugMessageArgs("Small file (< 2kb mb), small buffer (128 b) \n ", "DCC DOWNLOADER")); buffer = new byte[128]; } else { OnDccDebugMessage?.Invoke(this, new DCCDebugMessageArgs("Tiny file (< 128 b), tiny buffer (2 b) \n ", "DCC DOWNLOADER")); buffer = new byte[2]; } //create file to write to using (FileStream writeStream = new FileStream(dlDirAndFileName, FileMode.Append, FileAccess.Write, FileShare.Read)) { writeStream.SetLength(NewFileSize); IsDownloading = true; //download while connected and filesize is not reached while (dltcp.Connected && bytesReceived < NewFileSize && !_shouldAbort) { if (_shouldAbort) { dltcp.Close(); dlstream.Dispose(); writeStream.Close(); } //keep track of progress DateTime end = DateTime.Now; if (end.Second != start.Second) { BytesPerSecond = bytesReceived - oldBytesReceived; KBytesPerSecond = (int)(BytesPerSecond / 1024); MBytesPerSecond = (KBytesPerSecond / 1024); oldBytesReceived = bytesReceived; start = DateTime.Now; UpdateStatus("DOWNLOADING"); Buffer.Clear(); } //count bytes received count = dlstream.Read(buffer, 0, buffer.Length); //write to buffer Buffer.AddRange(buffer); //write to file writeStream.Write(buffer, 0, count); //count bytes received bytesReceived += count; Progress = (int)(bytesReceived / oneprocent); } //close all connections and streams (just to be save) dltcp.Close(); dlstream.Dispose(); writeStream.Close(); IsDownloading = false; if (_shouldAbort) { try { OnDccDebugMessage?.Invoke(this, new DCCDebugMessageArgs("Downloader Stopped", "DCC DOWNLOADER")); OnDccDebugMessage?.Invoke(this, new DCCDebugMessageArgs("File " + CurrentFilePath + " will be deleted due to aborting", "DCC DOWNLOADER")); File.Delete(CurrentFilePath); } catch (Exception e) { OnDccDebugMessage?.Invoke(this, new DCCDebugMessageArgs("File " + CurrentFilePath + " probably doesn't exist :X", "DCC DOWNLOADER")); OnDccDebugMessage?.Invoke(this, new DCCDebugMessageArgs(e.ToString(), "DCC DOWNLOADER")); } UpdateStatus("ABORTED"); } else { //consider 95% downloaded as done, files are sequentually downloaded, sometimes download stops early, but the file still is usable if (Progress < 95 && !_shouldAbort) { UpdateStatus("FAILED"); OnDccDebugMessage?.Invoke(this, new DCCDebugMessageArgs("Download stopped at < 95 % finished, deleting file: " + NewFileName + " \n", "DCC DOWNLOADER")); OnDccDebugMessage?.Invoke(this, new DCCDebugMessageArgs("Download stopped at : " + bytesReceived + " bytes, a total of " + Progress + "%", "DCC DOWNLOADER")); File.Delete(dlDirAndFileName); timedOut = false; } else if (timedOut && Progress < 95 && !_shouldAbort) { UpdateStatus("FAILED: TIMED OUT"); OnDccDebugMessage?.Invoke(this, new DCCDebugMessageArgs("Download timed out at < 95 % finished, deleting file: " + NewFileName + " \n", "DCC DOWNLOADER")); OnDccDebugMessage?.Invoke(this, new DCCDebugMessageArgs("Download stopped at : " + bytesReceived + " bytes, a total of " + Progress + "%", "DCC DOWNLOADER")); File.Delete(dlDirAndFileName); timedOut = false; } else if (!_shouldAbort) { //make sure that in the event something happens and the downloader calls delete after finishing, the file will remain where it is. dlDirAndFileName = ""; UpdateStatus("COMPLETED"); } } _shouldAbort = false; } } } } else { OnDccDebugMessage?.Invoke(this, new DCCDebugMessageArgs("File already exists, removing from xdcc que!\n", "DCC DOWNLOADER")); _ircClient.SendMessageToAll("/msg " + BotName + " xdcc remove " + PackNum); _ircClient.SendMessageToAll("/msg " + BotName + " xdcc cancel"); UpdateStatus("FAILED: ALREADY EXISTS"); } OnDccDebugMessage?.Invoke(this, new DCCDebugMessageArgs("File has been downloaded! \n File Location:" + CurrentFilePath, "DCC DOWNLOADER")); } catch (SocketException e) { OnDccDebugMessage?.Invoke(this, new DCCDebugMessageArgs("Something went wrong while downloading the file! Removing from xdcc que to be sure! Error:\n" + e.ToString(), "DCC DOWNLOADER")); _ircClient.SendMessageToAll("/msg " + BotName + " xdcc remove " + PackNum); _ircClient.SendMessageToAll("/msg " + BotName + " xdcc cancel"); UpdateStatus("FAILED: CONNECTING"); } catch (Exception ex) { OnDccDebugMessage?.Invoke(this, new DCCDebugMessageArgs("Something went wrong while downloading the file! Removing from xdcc que to be sure! Error:\n" + ex.ToString(), "DCC DOWNLOADER")); _ircClient.SendMessageToAll("/msg " + BotName + " xdcc remove " + PackNum); _ircClient.SendMessageToAll("/msg " + BotName + " xdcc cancel"); UpdateStatus("FAILED: UNKNOWN"); } IsDownloading = false; }
/// <summary> /// Parses the received DCC string /// </summary> /// <param name="dccString">dcc string</param> /// <returns>returns true if parsing was succesfull, false if failed</returns> private bool ParseData(string dccString) { /* * :_bot PRIVMSG nickname :DCC SEND \"filename\" ip_networkbyteorder port filesize * [email protected] PRIVMSG WeebIRCDev :DCC SEND "[LNS] Death Parade - 02 [BD 720p] [7287AE5C].mkv" 633042523 59538 258271780 * [email protected] PRIVMSG WeebIRCDev :DCC SEND [Coalgirls]_Spirited_Away_(1280x692_Blu-ray_FLAC)_[5805EE6B].mkv 3281692293 35567 10393049211 * :[EWG][email protected] PRIVMSG LittleWeeb_jtokck :DCC SEND The.Good.Doctor.S01E13.Seven.Reasons.1080p.AMZN.WEB-DL.DD+5.1.H.264-QOQ.mkv 2655354388 55000 1821620363 * Ginpa2:DCC SEND "[HorribleSubs] Dies Irae - 05 [480p].mkv" 84036312 35016 153772128 */ dccString = RemoveSpecialCharacters(dccString).Substring(1); OnDccDebugMessage?.Invoke(this, new DCCDebugMessageArgs("DCCClient: DCC STRING: " + dccString, "DCC PARSER")); if (!dccString.Contains(" :DCC")) { BotName = dccString.Split(':')[0]; if (dccString.Contains("\"")) { NewFileName = dccString.Split('"')[1]; OnDccDebugMessage?.Invoke(this, new DCCDebugMessageArgs("DCCClient1: FILENAME PARSED: " + NewFileName, "DCC PARSER")); string[] thaimportantnumbers = dccString.Split('"')[2].Trim().Split(' '); if (thaimportantnumbers[0].Contains(":")) { NewIP = thaimportantnumbers[0]; } else { try { OnDccDebugMessage?.Invoke(this, new DCCDebugMessageArgs("DCCClient1: PARSING FOLLOWING IPBYTES: " + thaimportantnumbers[0], "DCC PARSER")); string ipAddress = UInt64ToIPAddress(Int64.Parse(thaimportantnumbers[0])); NewIP = ipAddress; } catch { return(false); } } OnDccDebugMessage?.Invoke(this, new DCCDebugMessageArgs("DCCClient1: IP PARSED: " + NewIP, "DCC PARSER")); NewPortNum = int.Parse(thaimportantnumbers[1]); NewFileSize = Int64.Parse(thaimportantnumbers[2]); return(true); } else { NewFileName = dccString.Split(' ')[2]; OnDccDebugMessage?.Invoke(this, new DCCDebugMessageArgs("DCCClient2: FILENAME PARSED: " + NewFileName, "DCC PARSER")); if (dccString.Split(' ')[3].Contains(":")) { NewIP = dccString.Split(' ')[3]; } else { try { OnDccDebugMessage?.Invoke(this, new DCCDebugMessageArgs("DCCClient2: PARSING FOLLOWING IPBYTES DIRECTLY: " + dccString.Split(' ')[3], "DCC PARSER")); string ipAddress = UInt64ToIPAddress(Int64.Parse(dccString.Split(' ')[3])); NewIP = ipAddress; } catch { return(false); } } OnDccDebugMessage?.Invoke(this, new DCCDebugMessageArgs("DCCClient2: IP PARSED: " + NewIP, "DCC PARSER")); NewPortNum = int.Parse(dccString.Split(' ')[4]); NewFileSize = Int64.Parse(dccString.Split(' ')[5]); return(true); } } else { BotName = dccString.Split('!')[0]; if (dccString.Contains("\"")) { NewFileName = dccString.Split('"')[1]; OnDccDebugMessage?.Invoke(this, new DCCDebugMessageArgs("DCCClient3: FILENAME PARSED: " + NewFileName, "DCC PARSER")); string[] thaimportantnumbers = dccString.Split('"')[2].Trim().Split(' '); if (thaimportantnumbers[0].Contains(":")) { NewIP = thaimportantnumbers[0]; } else { try { OnDccDebugMessage?.Invoke(this, new DCCDebugMessageArgs("DCCClient3: PARSING FOLLOWING IPBYTES DIRECTLY: " + thaimportantnumbers[0], "DCC PARSER")); string ipAddress = UInt64ToIPAddress(Int64.Parse(thaimportantnumbers[0])); NewIP = ipAddress; } catch { return(false); } } OnDccDebugMessage?.Invoke(this, new DCCDebugMessageArgs("DCCClient3: IP PARSED: " + NewIP, "DCC PARSER")); NewPortNum = int.Parse(thaimportantnumbers[1]); NewFileSize = Int64.Parse(thaimportantnumbers[2]); return(true); } else { NewFileName = dccString.Split(' ')[5]; OnDccDebugMessage?.Invoke(this, new DCCDebugMessageArgs("DCCClient4: FILENAME PARSED: " + NewFileName, "DCC PARSER")); if (dccString.Split(' ')[6].Contains(":")) { NewIP = dccString.Split(' ')[6]; } else { try { OnDccDebugMessage?.Invoke(this, new DCCDebugMessageArgs("DCCClient4: PARSING FOLLOWING IPBYTES DIRECTLY: " + dccString.Split(' ')[6], "DCC PARSER")); string ipAddress = UInt64ToIPAddress(Int64.Parse(dccString.Split(' ')[6])); NewIP = ipAddress; } catch { return(false); } } OnDccDebugMessage?.Invoke(this, new DCCDebugMessageArgs("DCCClient4: IP PARSED: " + NewIP, "DCC PARSER")); NewPortNum = int.Parse(dccString.Split(' ')[7]); NewFileSize = Int64.Parse(dccString.Split(' ')[8]); return(true); } } }