/// <summary> /// Sets the data port on the server, i.e. sends a PORT /// command /// </summary> /// <param name="ep">local endpoint /// </param> internal virtual void SetDataPort(IPEndPoint ep) { byte[] hostBytes = ep.Address.GetAddressBytes();//BitConverter.GetBytes(ep.Address.Address); if (activeIPAddress != null) { log.Info("Forcing use of fixed IP for PORT command"); hostBytes = activeIPAddress.GetAddressBytes();// BitConverter.GetBytes(activeIPAddress.Address); } // This is a .NET 1.1 API // byte[] hostBytes = ep.Address.GetAddressBytes(); byte[] portBytes = ToByteArray((ushort)ep.Port); // assemble the PORT command string cmd = new StringBuilder("PORT "). Append((short)hostBytes[0]).Append(","). Append((short)hostBytes[1]).Append(","). Append((short)hostBytes[2]).Append(","). Append((short)hostBytes[3]).Append(","). Append((short)portBytes[0]).Append(","). Append((short)portBytes[1]).ToString(); // send command and check reply // CoreFTP returns 250 incorrectly FTPReply reply = SendCommand(cmd); ValidateReply(reply, "200", "250"); }
/// <summary> Checks that the standard 220 reply is returned /// following the initiated connection. Allow 230 as well, some proxy /// servers return it. /// </summary> internal FTPReply ValidateConnection() { FTPReply reply = ReadReply(); ValidateReply(reply, "220", "230"); return(reply); }
/// <summary> /// Validate reply object /// </summary> /// <param name="reply"> reference to reply object /// </param> /// <param name="expectedReplyCode"> expect reply code /// </param> /// <returns> true if valid, false if invalid /// </returns> private bool ValidateReplyCode(FTPReply reply, string expectedReplyCode) { string replyCode = reply.ReplyCode; if (strictReturnCodes) { if (replyCode.Equals(expectedReplyCode)) { return(true); } else { return(false); } } else { // non-strict - match first char if (replyCode[0] == expectedReplyCode[0]) { return(true); } else { return(false); } } }
/// <summary> Validate the response the host has supplied against the /// expected reply. If we get an unexpected reply we throw an /// exception, setting the message to that returned by the /// FTP server /// /// </summary> /// <param name="reply"> reply object /// </param> /// <param name="expectedReplyCode"> expected reply /// </param> /// <returns> reply object /// /// </returns> public virtual FTPReply ValidateReply(FTPReply reply, string expectedReplyCode) { if (ValidateReplyCode(reply, expectedReplyCode)) { return(reply); } // got this far, not recognised throw new FTPException(reply); }
/// <summary> Constructor. Permits setting of reply code /// /// </summary> /// <param name="reply"> reply object /// </param> public FTPException(FTPReply reply) : base(reply.ReplyText) { // extract reply code if possible try { this.replyCode = System.Int32.Parse(reply.ReplyCode); } catch (FormatException) { this.replyCode = -1; } }
/// <summary> /// Validate the response the host has supplied against the /// expected reply. If we get an unexpected reply we throw an /// exception, setting the message to that returned by the /// FTP server /// </summary> /// <param name="reply"> the entire reply string we received /// </param> /// <param name="expectedReplyCode"> the reply we expected to receive /// /// </param> internal virtual FTPReply ValidateReply(string reply, string expectedReplyCode) { FTPReply replyObj = new FTPReply(reply); if (ValidateReplyCode(replyObj, expectedReplyCode)) { return(replyObj); } // if unexpected reply, throw an exception throw new FTPException(replyObj); }
/// <summary> Validate the response the host has supplied against the /// expected reply. If we get an unexpected reply we throw an /// exception, setting the message to that returned by the /// FTP server /// /// </summary> /// <param name="reply"> reply object /// </param> /// <param name="expectedReplyCodes"> array of expected replies /// </param> /// <returns> reply object /// /// </returns> public virtual FTPReply ValidateReply(FTPReply reply, string[] expectedReplyCodes) { for (int i = 0; i < expectedReplyCodes.Length; i++) { if (ValidateReplyCode(reply, expectedReplyCodes[i])) { return(reply); } } // got this far, not recognised throw new FTPException(reply); }
internal void SetDataPort(IPEndPoint ep) { byte[] bytes = BitConverter.GetBytes(ep.Address.Address); if (this.activeIPAddress != null) { this.log.Info("Forcing use of fixed IP for PORT command"); bytes = BitConverter.GetBytes(this.activeIPAddress.Address); } byte[] buffer2 = this.ToByteArray((ushort)ep.Port); string command = new StringBuilder("PORT ").Append((short)bytes[0]).Append(",").Append((short)bytes[1]).Append(",").Append((short)bytes[2]).Append(",").Append((short)bytes[3]).Append(",").Append((short)buffer2[0]).Append(",").Append((short)buffer2[1]).ToString(); FTPReply reply = this.SendCommand(command); this.ValidateReply(reply, new string[] { "200", "250" }); }
protected virtual FTPDataSocket CreateDataSocketPASVInternal() { // PASSIVE command - tells the server to listen for // a connection attempt rather than initiating it FTPReply replyObj = SendCommand("PASV"); ValidateReply(replyObj, "227"); string reply = replyObj.ReplyText; // The reply to PASV is in the form: // 227 Entering Passive Mode (h1,h2,h3,h4,p1,p2). // where h1..h4 are the IP address to connect and // p1,p2 the port number // Example: // 227 Entering Passive Mode (128,3,122,1,15,87). // NOTE: Some FTP servers miss the brackets out completely Regex regEx = new Regex(@"(?<a0>\d{1,3}),(?<a1>\d{1,3}),(?<a2>\d{1,3}),(?<a3>\d{1,3}),(?<p0>\d{1,3}),(?<p1>\d{1,3})"); Match m1 = regEx.Match(reply); // assemble the IP address // we try connecting, so we don't bother checking digits etc string ipAddress = m1.Groups["a0"].Value + "." + m1.Groups["a1"].Value + "." + m1.Groups["a2"].Value + "." + m1.Groups["a3"].Value; log.Debug("Server supplied address=" + ipAddress); // assemble the port number int[] portParts = new int[2]; portParts[0] = Int32.Parse(m1.Groups["p0"].Value); portParts[1] = Int32.Parse(m1.Groups["p1"].Value); int port = (portParts[0] << 8) + portParts[1]; log.Debug("Server supplied port=" + port); string hostIP = ipAddress; log.Debug("autoPassiveIPSubstitution=" + autoPassiveIPSubstitution); log.Debug("remoteAddr=" + (remoteAddr == null ? "null" : remoteAddr.ToString())); if (autoPassiveIPSubstitution && remoteAddr != null) { hostIP = remoteAddr.ToString(); log.Debug("Substituting server supplied IP ({0}) with remote host IP ({1})", ipAddress, hostIP); } // create the socket return(NewPassiveDataSocket(hostIP, port)); }
/// <summary> /// Validate the response the host has supplied against the /// expected reply. If we get an unexpected reply we throw an /// exception, setting the message to that returned by the /// FTP server /// </summary> /// <param name="reply">reply object</param> /// <param name="expectedReplyCodes"> array of expected replies</param> /// <returns>reply object</returns> public virtual FTPReply ValidateReply(FTPReply reply, params string[] expectedReplyCodes) { if ("421" == reply.ReplyCode) { log.Error("Received 421 - throwing exception"); throw new FTPConnectionClosedException(reply.ReplyText); } foreach (string expectedReplyCode in expectedReplyCodes) { if (strictReturnCodes) { if (reply.ReplyCode == expectedReplyCode) { return(reply); } } else { // non-strict - match first char if (reply.ReplyCode[0] == expectedReplyCode[0]) { return(reply); } } } // got this far, not recognised StringBuilder buf = new StringBuilder("["); int i = 0; foreach (string expectedReplyCode in expectedReplyCodes) { buf.Append(expectedReplyCode); if (i + 1 < expectedReplyCodes.Length) { buf.Append(","); } i++; } buf.Append("]"); log.Debug("Expected reply codes = " + buf.ToString() + " (strict=" + strictReturnCodes + ")"); // got this far, not recognised throw new FTPException(reply); }
internal virtual FTPDataSocket CreateDataSocketPASV() { FTPDataSocket socket; bool synchronizePassiveConnections = this.SynchronizePassiveConnections; Mutex mutex = null; if (synchronizePassiveConnections) { mutex = new Mutex(false, PASV_MUTEX_NAME); mutex.WaitOne(); } try { FTPReply reply = this.SendCommand("PASV"); this.ValidateReply(reply, new string[] { "227" }); string replyText = reply.ReplyText; Match match = new Regex(@"(?<a0>\d{1,3}),(?<a1>\d{1,3}),(?<a2>\d{1,3}),(?<a3>\d{1,3}),(?<p0>\d{1,3}),(?<p1>\d{1,3})").Match(replyText); string str2 = match.Groups["a0"].Value + "." + match.Groups["a1"].Value + "." + match.Groups["a2"].Value + "." + match.Groups["a3"].Value; this.log.Debug("Server supplied address=" + str2); int[] numArray = new int[] { int.Parse(match.Groups["p0"].Value), int.Parse(match.Groups["p1"].Value) }; int port = (numArray[0] << 8) + numArray[1]; this.log.Debug("Server supplied port=" + port); string str3 = str2; if (this.autoPassiveIPSubstitution && (this.remoteAddr != null)) { str3 = this.remoteAddr.ToString(); if (this.log.IsEnabledFor(Level.DEBUG)) { this.log.Debug(string.Format("Substituting server supplied IP ({0}) with remote host IP ({1})", str2, str3)); } } socket = this.NewPassiveDataSocket(str3, port); } finally { if (synchronizePassiveConnections && (mutex != null)) { mutex.ReleaseMutex(); mutex.Close(); } } return(socket); }
/// <summary> /// Sets the data port on the server, i.e. sends a PORT /// command /// </summary> /// <param name="ep">local endpoint /// </param> internal void SetDataPort(IPEndPoint ep) { byte[] hostBytes = BitConverter.GetBytes(ep.Address.Address); // This is a .NET 1.1 API // byte[] hostBytes = ep.Address.GetAddressBytes(); byte[] portBytes = ToByteArray((ushort)ep.Port); // assemble the PORT command string cmd = new StringBuilder("PORT "). Append((short)hostBytes[0]).Append(","). Append((short)hostBytes[1]).Append(","). Append((short)hostBytes[2]).Append(","). Append((short)hostBytes[3]).Append(","). Append((short)portBytes[0]).Append(","). Append((short)portBytes[1]).ToString(); // send command and check reply FTPReply reply = SendCommand(cmd); ValidateReply(reply, "200"); }
public virtual FTPReply ValidateReply(FTPReply reply, params string[] expectedReplyCodes) { if ("421" == reply.ReplyCode) { this.log.Error("Received 421 - throwing exception"); throw new FTPConnectionClosedException(reply.ReplyText); } foreach (string str in expectedReplyCodes) { if (this.strictReturnCodes) { if (reply.ReplyCode == str) { return(reply); } } else if (reply.ReplyCode[0] == str[0]) { return(reply); } } StringBuilder builder = new StringBuilder("["); int num2 = 0; foreach (string str2 in expectedReplyCodes) { builder.Append(str2); if ((num2 + 1) < expectedReplyCodes.Length) { builder.Append(","); } num2++; } builder.Append("]"); this.log.Info(string.Concat(new object[] { "Expected reply codes = ", builder.ToString(), " (strict=", this.strictReturnCodes, ")" })); throw new FTPException(reply); }
/// <summary> /// Validate an FTPReply /// </summary> /// <param name="reply">reply object</param> /// <param name="expectedReplyCodes">expected codes</param> internal void ValidateReply(FTPReply reply, string[] expectedReplyCodes) { control.ValidateReply(reply, expectedReplyCodes); }
/// <summary>Quit the FTP session by sending a <c>QUIT</c> command before closing the socket.</summary> public virtual void Quit() { CheckConnection(true); if (fileFactory != null) fileFactory.System = null; try { FTPReply reply = control.SendCommand("QUIT"); lastValidReply = control.ValidateReply(reply, "221", "226"); } finally { CloseDataSocket(); // ensure we clean up the connection control.Logout(); control = null; } }
/// <summary> Get the help text for the specified command /// /// </summary> /// <param name="command"> name of the command to get help on /// </param> /// <returns> help text from the server for the supplied command /// </returns> public virtual string Help(string command) { CheckConnection(true); FTPReply reply = control.SendCommand("HELP " + command); lastValidReply = control.ValidateReply(reply, "211", "214"); return lastValidReply.ReplyText; }
/// <summary> /// Validate reply object /// </summary> /// <param name="reply"> reference to reply object /// </param> /// <param name="expectedReplyCode"> expect reply code /// </param> /// <returns> true if valid, false if invalid /// </returns> private bool ValidateReplyCode(FTPReply reply, string expectedReplyCode) { string replyCode = reply.ReplyCode; if (strictReturnCodes) { if (replyCode.Equals(expectedReplyCode)) return true; else return false; } else { // non-strict - match first char if (replyCode[0] == expectedReplyCode[0]) return true; else return false; } }
/// <summary>Request to the server that the get is set up.</summary> /// <param name="remoteFile">Name of remote file in current directory.</param> private void InitGet(string remoteFile) { CheckConnection(true); // reset the cancel flag cancelTransfer = false; bool close = false; data = null; try { // set up data channel data = control.CreateDataSocket(connectMode); data.Timeout = timeout; // if resume is requested, we must issue REST if (resume) { if (transferType.Equals(FTPTransferType.ASCII)) throw new FTPException("Resume only supported for BINARY transfers"); try { Restart(resumeMarker); } catch (FTPException ex) { resumeMarker = 0; resume = false; log.Warn("REST failed - resume will not be used (" + ex.Message + ")"); } } else resumeMarker = 0; // send the retrieve command FTPReply reply = control.SendCommand("RETR " + remoteFile); // Can get a 125 or a 150 lastValidReply = control.ValidateReply(reply, "125", "150"); } catch (SystemException) { close = true; throw; } catch (FTPException) { close = true; throw; } finally { if (close) { resume = false; resumeMarker = 0; CloseDataSocket(); } } }
/// <summary>Validate that the Put() or get() was successful.</summary> /// <remarks>This method is not for general use. If it is called explicitly after /// a transfer, the connection will hang.</remarks> public void ValidateTransfer() { CheckConnection(true); FTPReply reply = null; Exception ex1 = null; try { reply = control.ReadReply(); } catch (Exception ex) { ex1 = ex; log.Warn("ReadReply failed", ex); } if (cancelTransfer) { if (reply != null) { lastValidReply = reply; } log.Warn("Transfer cancelled"); throw new FTPTransferCancelledException("Transfer cancelled.", lastBytesTransferred); } else if (ex1 != null) throw ex1; lastValidReply = control.ValidateReply(reply, "225", "226", "250"); }
private string[] Dir(string dirname, bool full, LineCallback lineCallback, object state) { CheckConnection(true); try { // set up data channel data = control.CreateDataSocket(connectMode); data.Timeout = timeout; // send the retrieve command string command = full ? "LIST ":"NLST "; if (showHiddenFiles) command += "-a "; if (dirname != null) command += dirname; // some FTP servers bomb out if NLST has whitespace appended command = command.Trim(); FTPReply reply = control.SendCommand(command); // check the control response. wu-ftp returns 550 if the // directory is empty, so we handle 550 appropriately. Similarly // proFTPD returns 450. If dir is empty, some servers return 226 Transfer complete lastValidReply = control.ValidateReply(reply, "125", "226", "150", "450", "550"); // an empty array of files for 450/550 string[] result = new string[0]; // a normal reply ... extract the file list string replyCode = lastValidReply.ReplyCode; if (!replyCode.Equals("450") && !replyCode.Equals("550") && !replyCode.Equals("226")) { // get a character input stream to read data from Encoding enc = controlEncoding == null ? Encoding.ASCII : controlEncoding; ArrayList lines = null; // reset the cancel flag cancelTransfer = false; try { if (enc.Equals(Encoding.ASCII)) { lines = ReadASCIIListingData(dirname, lineCallback, state); } else { lines = ReadListingData(dirname, enc, lineCallback, state); } // check the control response reply = control.ReadReply(); lastValidReply = control.ValidateReply(reply, "226", "250"); } catch (SystemException ex) { ValidateTransferOnError(); log.Error("SystemException in directory listing", ex); throw; } // empty array is default if (!(lines.Count == 0)) { log.Debug("Found " + lines.Count + " listing lines"); result = new string[lines.Count]; lines.CopyTo(result); } else log.Debug("No listing data found"); } else { // throw exception if not a "no files" message or transfer complete string replyText = lastValidReply.ReplyText.ToUpper(); if (!dirEmptyStrings.Matches(replyText) && !transferCompleteStrings.Matches(replyText)) throw new FTPException(reply); } return result; } finally { CloseDataSocket(); } }
internal void ValidateConnection() { FTPReply reply = this.ReadReply(); this.ValidateReply(reply, new string[] { "220", "230" }); }
/// <summary> Validate the response the host has supplied against the /// expected reply. If we get an unexpected reply we throw an /// exception, setting the message to that returned by the /// FTP server /// /// </summary> /// <param name="reply"> the entire reply string we received /// </param> /// <param name="expectedReplyCodes"> array of expected replies /// </param> /// <returns> an object encapsulating the server's reply /// /// </returns> public virtual FTPReply ValidateReply(string reply, string[] expectedReplyCodes) { FTPReply replyObj = new FTPReply(reply); return(ValidateReply(replyObj, expectedReplyCodes)); }
/// <summary> Validate the response the host has supplied against the /// expected reply. If we get an unexpected reply we throw an /// exception, setting the message to that returned by the /// FTP server /// /// </summary> /// <param name="reply"> reply object /// </param> /// <param name="expectedReplyCode"> expected reply /// </param> /// <returns> reply object /// /// </returns> public virtual FTPReply ValidateReply(FTPReply reply, string expectedReplyCode) { if (ValidateReplyCode(reply, expectedReplyCode)) return reply; // got this far, not recognised throw new FTPException(reply); }
/// <summary> /// Request a data socket be created on the /// server, connect to it and return our /// connected socket. /// </summary> /// <returns> connected data socket /// </returns> internal virtual FTPDataSocket CreateDataSocketPASV() { // PASSIVE command - tells the server to listen for // a connection attempt rather than initiating it FTPReply replyObj = SendCommand("PASV"); ValidateReply(replyObj, "227"); string reply = replyObj.ReplyText; // The reply to PASV is in the form: // 227 Entering Passive Mode (h1,h2,h3,h4,p1,p2). // where h1..h4 are the IP address to connect and // p1,p2 the port number // Example: // 227 Entering Passive Mode (128,3,122,1,15,87). // NOTE: PASV command in IBM/Mainframe returns the string // 227 Entering Passive Mode 128,3,122,1,15,87 (missing // brackets) // extract the IP data string from between the brackets int startIP = reply.IndexOf((System.Char) '('); int endIP = reply.IndexOf((System.Char) ')'); // allow for IBM missing brackets around IP address if (startIP < 0 && endIP < 0) { startIP = reply.ToUpper().LastIndexOf("MODE") + 4; endIP = reply.Length; } string ipData = reply.Substring(startIP + 1, (endIP) - (startIP + 1)); int[] parts = new int[6]; int len = ipData.Length; int partCount = 0; StringBuilder buf = new StringBuilder(); // loop thru and examine each char for (int i = 0; i < len && partCount <= 6; i++) { char ch = ipData[i]; if (System.Char.IsDigit(ch)) { buf.Append(ch); } else if (ch != ',') { throw new FTPException("Malformed PASV reply: " + reply); } // get the part if (ch == ',' || i + 1 == len) { // at end or at separator try { parts[partCount++] = System.Int32.Parse(buf.ToString()); buf.Length = 0; } catch (FormatException) { throw new FTPException("Malformed PASV reply: " + reply); } } } // assemble the IP address // we try connecting, so we don't bother checking digits etc string ipAddress = parts[0] + "." + parts[1] + "." + parts[2] + "." + parts[3]; // assemble the port number int port = (parts[4] << 8) + parts[5]; // create the socket return(NewPassiveDataSocket(ipAddress, port)); }
/// <summary> Checks that the standard 220 reply is returned /// following the initiated connection /// </summary> internal void ValidateConnection() { FTPReply reply = ReadReply(); ValidateReply(reply, "220"); }
/// <summary> /// Get the size of a remote file. /// </summary> /// <remarks> /// This is not a standard FTP command, it is defined in "Extensions to FTP", a draft RFC /// (draft-ietf-ftpext-mlst-16.txt). /// </remarks> /// <param name="remoteFile">Name or path of remote file in current directory.</param> /// <returns>Size of file in bytes.</returns> public virtual long Size(string remoteFile) { CheckConnection(true); FTPReply reply = control.SendCommand("SIZE " + remoteFile); lastValidReply = control.ValidateReply(reply, "213"); // parse the reply string . string replyText = lastValidReply.ReplyText; // trim off any trailing characters after a space, e.g. webstar // responds to SIZE with 213 55564 bytes int spacePos = replyText.IndexOf((System.Char) ' '); if (spacePos >= 0) replyText = replyText.Substring(0, (spacePos) - (0)); // parse the reply try { return Int64.Parse(replyText); } catch (FormatException) { throw new FTPException("Failed to parse reply: " + replyText); } }
/// <summary>Set the REST marker so that the next transfer doesn't start at the beginning of the remote file</summary> /// <remarks> /// Issue the RESTart command to the remote server. This indicates the byte /// position that REST is performed at. For put, bytes start at this point, while /// for get, bytes are fetched from this point. /// </remarks> /// <param name="size">the REST param, the mark at which the restart is performed on the remote file. /// For STOR, this is retrieved by SIZE</param> /// <throws>SystemException </throws> /// <throws>FTPException </throws> public void Restart(long size) { FTPReply reply = control.SendCommand("REST " + size); lastValidReply = control.ValidateReply(reply, "350"); }
/// <summary>Rename a file or directory.</summary> /// <param name="from">Name of file or directory to rename.</param> /// <param name="to">Intended name.</param> public virtual void Rename(string from, string to) { CheckConnection(true); FTPReply reply = control.SendCommand("RNFR " + from); lastValidReply = control.ValidateReply(reply, "350"); reply = control.SendCommand("RNTO " + to); lastValidReply = control.ValidateReply(reply, "250"); }
/// <summary>Request the server to set up the put.</summary> /// <param name="remoteFile">Name of remote file in current directory.</param> /// <param name="append"><c>true</c> if appending, <c>false</c> otherwise.</param> private void InitPut(string remoteFile, bool append) { CheckConnection(true); // reset the cancel flag cancelTransfer = false; bool close = false; data = null; try { resumeMarker = 0; // if resume is requested, we must obtain the size of the // remote file if (resume) { if (transferType.Equals(FTPTransferType.ASCII)) throw new FTPException("Resume only supported for BINARY transfers"); try { resumeMarker = Size(remoteFile); } catch (FTPException ex) { resumeMarker = 0; resume = false; log.Warn("SIZE failed '" + remoteFile + "' - resume will not be used (" + ex.Message + ")"); } } // set up data channel data = control.CreateDataSocket(connectMode); data.Timeout = timeout; // issue REST if (resume) { try { Restart(resumeMarker); } catch (FTPException ex) { resumeMarker = 0; resume = false; log.Warn("REST failed - resume will not be used (" + ex.Message + ")"); } } // send the command to store string cmd = append?"APPE ":"STOR "; FTPReply reply = control.SendCommand(cmd + remoteFile); // Can get a 125 or a 150, also allow 350 (for Global eXchange Services server) // JScape returns 151 lastValidReply = control.ValidateReply(reply, "125", "150", "151", "350"); } catch (SystemException) { close = true; throw; } catch (FTPException) { close = true; throw; } finally { if (close) { resume = false; resumeMarker = 0; CloseDataSocket(); } } }
/// <summary>Change the remote working directory to that supplied.</summary> /// <param name="dir">Name of remote directory to change to.</param> public virtual void ChDir(string dir) { CheckConnection(true); FTPReply reply = control.SendCommand("CWD " + dir); lastValidReply = control.ValidateReply(reply, "200", "250"); }
/// <summary>Run a site-specific command on the server.</summary> /// <remarks> /// Support for commands is dependent on the server. /// </remarks> /// <param name="command">The site command to run</param> /// <returns><c>true</c> if command ok, <c>false</c> if command not implemented.</returns> public virtual bool Site(string command) { CheckConnection(true); // send the retrieve command FTPReply reply = control.SendCommand("SITE " + command); // Can get a 200 (ok) or 202 (not impl). Some // FTP servers return 502 (not impl) lastValidReply = control.ValidateReply(reply, "200", "202", "250", "502"); // 250 for leitch media server // return true or false? 200 is ok, 202/502 not // implemented if (reply.ReplyCode.Equals("200")) return true; else return false; }
/// <summary> /// Validate the response the host has supplied against the /// expected reply. If we get an unexpected reply we throw an /// exception, setting the message to that returned by the /// FTP server /// </summary> /// <param name="reply"> the entire reply string we received /// </param> /// <param name="expectedReplyCode"> the reply we expected to receive /// /// </param> internal virtual FTPReply ValidateReply(string reply, string expectedReplyCode) { FTPReply replyObj = new FTPReply(reply); if (ValidateReplyCode(replyObj, expectedReplyCode)) return replyObj; // if unexpected reply, throw an exception throw new FTPException(replyObj); }
/// <summary>Delete the specified remote file.</summary> /// <param name="remoteFile">Name of remote file to delete.</param> public virtual void Delete(string remoteFile) { CheckConnection(true); FTPReply reply = control.SendCommand("DELE " + remoteFile); lastValidReply = control.ValidateReply(reply, "200", "250"); }
/// <summary>Get modification time for a remote file.</summary> /// <param name="remoteFile">Name of remote file.</param> /// <returns>Modification time of file as a <c>DateTime</c>.</returns> public virtual DateTime ModTime(string remoteFile) { CheckConnection(true); FTPReply reply = control.SendCommand("MDTM " + remoteFile); lastValidReply = control.ValidateReply(reply, "213"); // parse the reply string, which returns UTC DateTime ts = DateTime.ParseExact(lastValidReply.ReplyText, modtimeFormats, null, DateTimeStyles.None); // return the equivalent in local time return TimeZone.CurrentTimeZone.ToLocalTime(ts); }
/// <summary>Create the specified remote working directory.</summary> /// <param name="dir">Name of remote directory to create.</param> public virtual void MkDir(string dir) { CheckConnection(true); FTPReply reply = control.SendCommand("MKD " + dir); // some servers return 200,257, technically incorrect but // we cater for it ... lastValidReply = control.ValidateReply(reply, "200", "250", "257"); }
/// <summary>Get the current remote working directory.</summary> /// <returns>The current working directory.</returns> public virtual string Pwd() { CheckConnection(true); FTPReply reply = control.SendCommand("PWD"); lastValidReply = control.ValidateReply(reply, "257"); // get the reply text and extract the dir // listed in quotes, if we can find it. Otherwise // just return the whole reply string string text = lastValidReply.ReplyText; int start = text.IndexOf((System.Char) '"'); int end = text.LastIndexOf((System.Char) '"'); if (start >= 0 && end > start) return text.Substring(start + 1, (end) - (start + 1)); else return text; }
/// <summary>Change the remote working directory to the parent directory.</summary> public virtual void CdUp() { CheckConnection(true); FTPReply reply = control.SendCommand("CDUP"); lastValidReply = control.ValidateReply(reply, "200", "250"); }
/// <summary>Get the type of the OS at the server.</summary> /// <returns>The type of server OS.</returns> public virtual string GetSystem() { CheckConnection(true); FTPReply reply = control.SendCommand("SYST"); lastValidReply = control.ValidateReply(reply, "200", "213", "215", "250"); // added 250 for leitch return lastValidReply.ReplyText; }
/// <summary>Sets the modification time of a remote file.</summary> /// <remarks> /// Although times are passed to the server with second precision, some /// servers may ignore seconds and only provide minute precision. /// May not be supported by some FTP servers. /// </remarks> /// <param name="remoteFile">Name of remote file.</param> /// <param name="modTime">Desired modification-time to set in local time.</param> public virtual void SetModTime(string remoteFile, DateTime modTime) { CheckConnection(true); DateTime univTime = TimeZone.CurrentTimeZone.ToUniversalTime(modTime); string timeStr = univTime.ToString(DEFAULT_TIME_FORMAT); FTPReply reply = control.SendCommand("MFMT " + timeStr + " " + remoteFile); lastValidReply = control.ValidateReply(reply, "213"); }
/// <summary>Login into an account on the FTP server using the user-name and password provided.</summary> /// <remarks>This /// call completes the entire login process. Note that /// <see cref="Connect()"/> must be called first.</remarks> /// <param name="user">User-name.</param> /// <param name="password">Password.</param> public virtual void Login(string user, string password) { CheckConnection(true); FTPReply reply = control.SendCommand("USER " + user); // we allow for a site with no password - 230 and 232 (RFC4217) response lastValidReply = control.ValidateReply(reply, "230", "232", "331"); if (lastValidReply.ReplyCode.Equals("230") || lastValidReply.ReplyCode.Equals("232")) return ; else { Password(password); } }
/// <summary>Get the server supplied features.</summary> /// <returns> /// <c>string</c>-array containing server features, or <c>null</c> if no features or not supported. /// </returns> public virtual string[] Features() { CheckConnection(true); FTPReply reply = control.SendCommand("FEAT"); lastValidReply = control.ValidateReply(reply, "211", "500", "502"); if (lastValidReply.ReplyCode == "211") { string[] features = null; if (lastValidReply.ReplyData != null && lastValidReply.ReplyData.Length > 2) { features = new string[lastValidReply.ReplyData.Length-2]; for (int i = 0; i < lastValidReply.ReplyData.Length-2; i++) features[i] = lastValidReply.ReplyData[i+1].Trim(); } else // no features but command supported { features = new string[0]; } return features; } else throw new FTPException(reply); }
/// <summary> /// Supply the user-name to log into an account on the FTP server. /// Must be followed by the <see cref="Password(string)"/> method. /// Note that <see cref="Connect()"/> must be called first. /// </summary> /// <param name="user">User-name.</param> public virtual void User(string user) { CheckConnection(true); FTPReply reply = control.SendCommand("USER " + user); // we allow for a site with no password - 230 and 232 (RFC4217) response lastValidReply = control.ValidateReply(reply, "230", "232", "331"); }
/// <summary> /// Send a "no operation" message that does nothing, which can /// be called periodically to prevent the connection timing out. /// </summary> public void NoOperation() { CheckConnection(true); FTPReply reply = control.SendCommand("NOOP"); lastValidReply = control.ValidateReply(reply, "200", "250"); // added 250 for leitch }
/// <summary> /// Supplies the password for a previously supplied /// user-name to log into the FTP server. Must be /// preceeded by the <see cref="User(string)"/> method /// </summary> /// <param name="password">Password.</param> public virtual void Password(string password) { CheckConnection(true); FTPReply reply = control.SendCommand("PASS " + password); // we allow for a site with no passwords (202) try { lastValidReply = control.ValidateReply(reply, "230", "202", "332"); } catch (FTPException ex) { throw new FTPAuthenticationException(ex.Message, ex.ReplyCode); } }
/// <summary>Abort the current action.</summary> /// <remarks> /// This does not close the FTP session. /// </remarks> protected virtual void Abort() { CheckConnection(true); FTPReply reply = control.SendCommand("ABOR"); lastValidReply = control.ValidateReply(reply, "426", "226"); }
/// <summary> /// Supply account information string to the server. /// </summary> /// <remarks> /// This can be used for a variety of purposes - for example, the server could /// indicate that a password has expired (by sending 332 in reply to /// PASS) and a new password automatically supplied via ACCT. It /// is up to the server how it uses this string. /// </remarks> /// <param name="accountInfo">account information</param> public virtual void Account(string accountInfo) { CheckConnection(true); FTPReply reply = control.SendCommand("ACCT " + accountInfo); // ok or not implemented try { lastValidReply = control.ValidateReply(reply, "230", "202"); } catch (FTPException ex) { throw new FTPAuthenticationException(ex.Message); } }
public FTPException(FTPReply reply) : base(reply.ReplyText, reply.ReplyCode) { }
/// <summary>Issue arbitrary ftp commands to the FTP server.</summary> /// <param name="command">FTP command to be sent to server.</param> /// <param name="validCodes">Valid return codes for this command.</param> /// <returns>The text returned by the FTP server.</returns> public virtual string Quote(string command, string[] validCodes) { CheckConnection(true); FTPReply reply = control.SendCommand(command); // allow for no validation to be supplied if (validCodes != null && validCodes.Length>0) { lastValidReply = control.ValidateReply(reply, validCodes); } else // not doing any validation { lastValidReply = reply; } return lastValidReply.ReplyText; }