/// <summary> /// Creates a remote directory asynchronously /// </summary> /// <param name="path">The full or relative path to the new remote directory</param> /// <param name="force">Try to create the whole path if the preceding directories do not exist</param> /// <param name="token">The token that can be used to cancel the entire process</param> /// <returns>True if directory was created, false if it was skipped</returns> public async Task <bool> CreateDirectoryAsync(string path, bool force, CancellationToken token = default(CancellationToken)) { // don't verify args as blank/null path is OK //if (path.IsBlank()) // throw new ArgumentException("Required parameter is null or blank.", "path"); LogFunc(nameof(CreateDirectoryAsync), new object[] { path, force }); FtpReply reply; var ftppath = path.GetFtpPath(); // cannot create root or working directory if (ftppath.IsFtpRootDirectory()) { return(false); } // server-specific directory creation if (await FtpServerSpecificHandler.ServerCreateDirectoryAsync(this, path, ftppath, force, token)) { return(true); } path = path.GetFtpPath().TrimEnd('/'); if (force && !await DirectoryExistsAsync(path.GetFtpDirectoryName(), token)) { LogStatus(FtpTraceLevel.Verbose, "Create non-existent parent directory: " + path.GetFtpDirectoryName()); await CreateDirectoryAsync(path.GetFtpDirectoryName(), true, token); } // fix: improve performance by skipping the directory exists check /*else if (await DirectoryExistsAsync(path, token)) { * return false; * }*/ LogStatus(FtpTraceLevel.Verbose, "CreateDirectory " + ftppath); if (!(reply = await ExecuteAsync("MKD " + ftppath, token)).Success) { // if the error indicates the directory already exists, its not an error if (reply.Code == "550") { return(false); } if (reply.Code[0] == '5' && reply.Message.IsKnownError(FtpServerStrings.folderExists)) { return(false); } throw new FtpCommandException(reply); } return(true); }
/// <summary> /// Ensure a relative path is absolute by appending the working dir /// </summary> private string GetAbsolutePath(string path) { if (path == null || path.Trim().Length == 0) { // if path not given, then use working dir var pwd = GetWorkingDirectory(); if (pwd != null && pwd.Trim().Length > 0) { path = pwd; } else { path = "./"; } } // FIX : #153 ensure this check works with unix & windows // FIX : #454 OpenVMS paths can be a single character else if (!path.StartsWith("/") && !(path.Length > 1 && path[1] == ':')) { // if its a server-specific absolute path then don't add base dir if (FtpServerSpecificHandler.IsAbsolutePath(this, path)) { return(path); } // if relative path given then add working dir to calc full path var pwd = GetWorkingDirectory(); if (pwd != null && pwd.Trim().Length > 0) { if (path.StartsWith("./")) { path = path.Remove(0, 2); } path = (pwd + "/" + path).GetFtpPath(); } } return(path); }
/// <summary> /// Populates the capabilities flags based on capabilities /// supported by this server. This method is overridable /// so that new features can be supported /// </summary> /// <param name="reply">The reply object from the FEAT command. The InfoMessages property will /// contain a list of the features the server supported delimited by a new line '\n' character.</param> protected virtual void GetFeatures(FtpReply reply) { FtpServerSpecificHandler.GetFeatures(this, m_capabilities, ref m_hashAlgorithms, reply.InfoMessages.Split('\n')); }
// TODO: add example /// <summary> /// Connect to the server /// </summary> /// <exception cref="ObjectDisposedException">Thrown if this object has been disposed.</exception> public virtual async Task ConnectAsync(CancellationToken token = default(CancellationToken)) { FtpReply reply; LogFunc(nameof(ConnectAsync)); if (IsDisposed) { throw new ObjectDisposedException("This FtpClient object has been disposed. It is no longer accessible."); } if (m_stream == null) { m_stream = new FtpSocketStream(this); m_stream.ValidateCertificate += new FtpSocketStreamSslValidation(FireValidateCertficate); } else { if (IsConnected) { Disconnect(); } } if (Host == null) { throw new FtpException("No host has been specified"); } if (m_capabilities == null) { m_capabilities = new List <FtpCapability>(); } ResetStateFlags(); m_hashAlgorithms = FtpHashAlgorithm.NONE; m_stream.ConnectTimeout = m_connectTimeout; m_stream.SocketPollInterval = m_socketPollInterval; await ConnectAsync(m_stream, token); m_stream.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, m_keepAlive); #if !NO_SSL if (EncryptionMode == FtpEncryptionMode.Implicit) { await m_stream.ActivateEncryptionAsync(Host, m_clientCerts.Count > 0?m_clientCerts : null, m_SslProtocols); } #endif await HandshakeAsync(token); m_serverType = FtpServerSpecificHandler.DetectFtpServer(this, HandshakeReply); if (SendHost) { if (!(reply = await ExecuteAsync("HOST " + (SendHostDomain != null ? SendHostDomain : Host), token)).Success) { throw new FtpException("HOST command failed."); } } #if !NO_SSL // try to upgrade this connection to SSL if supported by the server if (EncryptionMode == FtpEncryptionMode.Explicit || EncryptionMode == FtpEncryptionMode.Auto) { reply = await ExecuteAsync("AUTH TLS", token); if (!reply.Success) { _ConnectionFTPSFailure = true; if (EncryptionMode == FtpEncryptionMode.Explicit) { throw new FtpSecurityNotAvailableException("AUTH TLS command failed."); } } else if (reply.Success) { await m_stream.ActivateEncryptionAsync(Host, m_clientCerts.Count > 0?m_clientCerts : null, m_SslProtocols); } } #endif if (m_credentials != null) { await AuthenticateAsync(token); } // configure the default FTPS settings if (IsEncrypted && DataConnectionEncryption) { if (!(reply = await ExecuteAsync("PBSZ 0", token)).Success) { throw new FtpCommandException(reply); } if (!(reply = await ExecuteAsync("PROT P", token)).Success) { throw new FtpCommandException(reply); } } // if this is a clone these values should have already been loaded // so save some bandwidth and CPU time and skip executing this again. // otherwise clear the capabilities in case connection is reused to // a different server if (!m_isClone && m_checkCapabilities) { m_capabilities.Clear(); } bool assumeCaps = false; if (m_capabilities.IsBlank() && m_checkCapabilities) { if ((reply = await ExecuteAsync("FEAT", token)).Success && reply.InfoMessages != null) { GetFeatures(reply); } else { assumeCaps = true; } } // Enable UTF8 if the encoding is ASCII and UTF8 is supported if (m_textEncodingAutoUTF && m_textEncoding == Encoding.ASCII && HasFeature(FtpCapability.UTF8)) { m_textEncoding = Encoding.UTF8; } LogStatus(FtpTraceLevel.Info, "Text encoding: " + m_textEncoding.ToString()); if (m_textEncoding == Encoding.UTF8) { // If the server supports UTF8 it should already be enabled and this // command should not matter however there are conflicting drafts // about this so we'll just execute it to be safe. if ((reply = await ExecuteAsync("OPTS UTF8 ON", token)).Success) { _ConnectionUTF8Success = true; } } // Get the system type - Needed to auto-detect file listing parser if ((reply = await ExecuteAsync("SYST", token)).Success) { m_systemType = reply.Message; m_serverType = FtpServerSpecificHandler.DetectFtpServerBySyst(this); m_serverOS = FtpServerSpecificHandler.DetectFtpOSBySyst(this); } // Set a FTP server handler if a custom handler has not already been set if (ServerHandler == null) { ServerHandler = FtpServerSpecificHandler.GetServerHandler(m_serverType); } // Assume the system's capabilities if FEAT command not supported by the server if (assumeCaps) { FtpServerSpecificHandler.AssumeCapabilities(this, ServerHandler, m_capabilities, ref m_hashAlgorithms); } #if !NO_SSL && !CORE if (IsEncrypted && PlainTextEncryption) { if (!(reply = await ExecuteAsync("CCC", token)).Success) { throw new FtpSecurityNotAvailableException("Failed to disable encryption with CCC command. Perhaps your server does not support it or is not configured to allow it."); } else { // close the SslStream and send close_notify command to server m_stream.DeactivateEncryption(); // read stale data (server's reply?) await ReadStaleDataAsync(false, true, false, token); } } #endif // Unless a custom list parser has been set, // Detect the listing parser and prefer machine listings over any other type // FIX : #739 prefer using machine listings to fix issues with GetListing and DeleteDirectory if (ListingParser != FtpParser.Custom) { ListingParser = ServerHandler != null?ServerHandler.GetParser() : FtpParser.Auto; if (HasFeature(FtpCapability.MLSD)) { ListingParser = FtpParser.Machine; } } // Create the parser even if the auto-OS detection failed m_listParser.Init(m_serverOS, ListingParser); // FIX : #318 always set the type when we create a new connection ForceSetDataType = true; // Execute server-specific post-connection event if (ServerHandler != null) { await ServerHandler.AfterConnectedAsync(this, token); } }
// TODO: add example /// <summary> /// Connect to the server /// </summary> /// <exception cref="ObjectDisposedException">Thrown if this object has been disposed.</exception> /// <example><code source="..\Examples\Connect.cs" lang="cs" /></example> public virtual async Task ConnectAsync(CancellationToken token = default(CancellationToken)) { FtpReply reply; LogFunc(nameof(ConnectAsync)); if (IsDisposed) { throw new ObjectDisposedException("This FtpClient object has been disposed. It is no longer accessible."); } if (m_stream == null) { m_stream = new FtpSocketStream(m_SslProtocols); m_stream.Client = this; m_stream.ValidateCertificate += new FtpSocketStreamSslValidation(FireValidateCertficate); } else { if (IsConnected) { Disconnect(); } } if (Host == null) { throw new FtpException("No host has been specified"); } if (!IsClone) { m_capabilities = new List <FtpCapability>(); } m_hashAlgorithms = FtpHashAlgorithm.NONE; m_stream.ConnectTimeout = m_connectTimeout; m_stream.SocketPollInterval = m_socketPollInterval; await ConnectAsync(m_stream, token); m_stream.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, m_keepAlive); #if !NO_SSL if (EncryptionMode == FtpEncryptionMode.Implicit) { await m_stream.ActivateEncryptionAsync(Host, m_clientCerts.Count > 0?m_clientCerts : null, m_SslProtocols); } #endif await HandshakeAsync(token); m_serverType = FtpServerSpecificHandler.DetectFtpServer(this, HandshakeReply); if (SendHost) { if (!(reply = await ExecuteAsync("HOST " + (SendHostDomain != null ? SendHostDomain : Host), token)).Success) { throw new FtpException("HOST command failed."); } } #if !NO_SSL if (EncryptionMode == FtpEncryptionMode.Explicit) { if (!(reply = await ExecuteAsync("AUTH TLS", token)).Success) { throw new FtpSecurityNotAvailableException("AUTH TLS command failed."); } await m_stream.ActivateEncryptionAsync(Host, m_clientCerts.Count > 0?m_clientCerts : null, m_SslProtocols); } #endif if (m_credentials != null) { await AuthenticateAsync(token); } if (m_stream.IsEncrypted && DataConnectionEncryption) { if (!(reply = await ExecuteAsync("PBSZ 0", token)).Success) { throw new FtpCommandException(reply); } if (!(reply = await ExecuteAsync("PROT P", token)).Success) { throw new FtpCommandException(reply); } } // if this is a clone these values should have already been loaded // so save some bandwidth and CPU time and skip executing this again. bool assumeCaps = false; if (!IsClone && m_checkCapabilities) { if ((reply = await ExecuteAsync("FEAT", token)).Success && reply.InfoMessages != null) { GetFeatures(reply); } else { assumeCaps = true; } } // Enable UTF8 if the encoding is ASCII and UTF8 is supported if (m_textEncodingAutoUTF && m_textEncoding == Encoding.ASCII && HasFeature(FtpCapability.UTF8)) { m_textEncoding = Encoding.UTF8; } LogStatus(FtpTraceLevel.Info, "Text encoding: " + m_textEncoding.ToString()); if (m_textEncoding == Encoding.UTF8) { // If the server supports UTF8 it should already be enabled and this // command should not matter however there are conflicting drafts // about this so we'll just execute it to be safe. await ExecuteAsync("OPTS UTF8 ON", token); } // Get the system type - Needed to auto-detect file listing parser if ((reply = await ExecuteAsync("SYST", token)).Success) { m_systemType = reply.Message; m_serverType = FtpServerSpecificHandler.DetectFtpServerBySyst(this); m_serverOS = FtpServerSpecificHandler.DetectFtpOSBySyst(this); } // Assume the system's capabilities if FEAT command not supported by the server if (assumeCaps) { FtpServerSpecificHandler.AssumeCapabilities(this, m_capabilities, ref m_hashAlgorithms); } #if !NO_SSL && !CORE if (m_stream.IsEncrypted && PlainTextEncryption) { if (!(reply = await ExecuteAsync("CCC", token)).Success) { throw new FtpSecurityNotAvailableException("Failed to disable encryption with CCC command. Perhaps your server does not support it or is not configured to allow it."); } else { // close the SslStream and send close_notify command to server m_stream.DeactivateEncryption(); // read stale data (server's reply?) await ReadStaleDataAsync(false, true, false, token); } } #endif // Create the parser after OS auto-detection m_listParser.Init(m_serverOS, FtpServerSpecificHandler.GetParserByServerType(this)); }
/// <summary> /// Connect to the server /// </summary> /// <exception cref="ObjectDisposedException">Thrown if this object has been disposed.</exception> /// <example><code source="..\Examples\Connect.cs" lang="cs" /></example> public virtual void Connect() { FtpReply reply; #if !CORE14 lock (m_lock) { #endif LogFunc(nameof(Connect)); if (IsDisposed) { throw new ObjectDisposedException("This FtpClient object has been disposed. It is no longer accessible."); } if (m_stream == null) { m_stream = new FtpSocketStream(m_SslProtocols); m_stream.Client = this; m_stream.ValidateCertificate += new FtpSocketStreamSslValidation(FireValidateCertficate); } else { if (IsConnected) { Disconnect(); } } if (Host == null) { throw new FtpException("No host has been specified"); } if (!IsClone) { m_capabilities = new List <FtpCapability>(); } ResetStateFlags(); m_hashAlgorithms = FtpHashAlgorithm.NONE; m_stream.ConnectTimeout = m_connectTimeout; m_stream.SocketPollInterval = m_socketPollInterval; Connect(m_stream); m_stream.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, m_keepAlive); #if !NO_SSL if (EncryptionMode == FtpEncryptionMode.Implicit) { m_stream.ActivateEncryption(Host, m_clientCerts.Count > 0 ? m_clientCerts : null, m_SslProtocols); } #endif Handshake(); m_serverType = FtpServerSpecificHandler.DetectFtpServer(this, HandshakeReply); if (SendHost) { if (!(reply = Execute("HOST " + (SendHostDomain != null ? SendHostDomain : Host))).Success) { throw new FtpException("HOST command failed."); } } #if !NO_SSL // try to upgrade this connection to SSL if supported by the server if (EncryptionMode == FtpEncryptionMode.Explicit || EncryptionMode == FtpEncryptionMode.Auto) { reply = Execute("AUTH TLS"); if (!reply.Success && EncryptionMode == FtpEncryptionMode.Explicit) { throw new FtpSecurityNotAvailableException("AUTH TLS command failed."); } else if (reply.Success) { m_stream.ActivateEncryption(Host, m_clientCerts.Count > 0 ? m_clientCerts : null, m_SslProtocols); } } #endif if (m_credentials != null) { Authenticate(); } // configure the default FTPS settings if (IsEncrypted && DataConnectionEncryption) { if (!(reply = Execute("PBSZ 0")).Success) { throw new FtpCommandException(reply); } if (!(reply = Execute("PROT P")).Success) { throw new FtpCommandException(reply); } } // if this is a clone these values should have already been loaded // so save some bandwidth and CPU time and skip executing this again. bool assumeCaps = false; if (!IsClone && m_checkCapabilities) { if ((reply = Execute("FEAT")).Success && reply.InfoMessages != null) { GetFeatures(reply); } else { assumeCaps = true; } } // Enable UTF8 if the encoding is ASCII and UTF8 is supported if (m_textEncodingAutoUTF && m_textEncoding == Encoding.ASCII && HasFeature(FtpCapability.UTF8)) { m_textEncoding = Encoding.UTF8; } LogStatus(FtpTraceLevel.Info, "Text encoding: " + m_textEncoding.ToString()); if (m_textEncoding == Encoding.UTF8) { // If the server supports UTF8 it should already be enabled and this // command should not matter however there are conflicting drafts // about this so we'll just execute it to be safe. Execute("OPTS UTF8 ON"); } // Get the system type - Needed to auto-detect file listing parser if ((reply = Execute("SYST")).Success) { m_systemType = reply.Message; m_serverType = FtpServerSpecificHandler.DetectFtpServerBySyst(this); m_serverOS = FtpServerSpecificHandler.DetectFtpOSBySyst(this); } // Set a FTP server handler if a custom handler has not already been set if (ServerHandler == null) { ServerHandler = FtpServerSpecificHandler.GetServerHandler(m_serverType); } // Assume the system's capabilities if FEAT command not supported by the server if (assumeCaps) { FtpServerSpecificHandler.AssumeCapabilities(this, ServerHandler, m_capabilities, ref m_hashAlgorithms); } #if !NO_SSL && !CORE if (IsEncrypted && PlainTextEncryption) { if (!(reply = Execute("CCC")).Success) { throw new FtpSecurityNotAvailableException("Failed to disable encryption with CCC command. Perhaps your server does not support it or is not configured to allow it."); } else { // close the SslStream and send close_notify command to server m_stream.DeactivateEncryption(); // read stale data (server's reply?) ReadStaleData(false, true, false); } } #endif // Create the parser even if the auto-OS detection failed var forcedParser = ServerHandler != null?ServerHandler.GetParser() : FtpParser.Auto; m_listParser.Init(m_serverOS, forcedParser); // FIX : #318 always set the type when we create a new connection ForceSetDataType = true; #if !CORE14 } #endif }
/// <summary> /// Deletes the specified directory and all its contents. /// </summary> /// <param name="path">The full or relative path of the directory to delete</param> /// <param name="deleteContents">If the directory is not empty, remove its contents</param> /// <param name="options">Useful to delete hidden files or dot-files.</param> /// <example><code source="..\Examples\DeleteDirectory.cs" lang="cs" /></example> private void DeleteDirInternal(string path, bool deleteContents, FtpListOption options) { FtpReply reply; var ftppath = path.GetFtpPath(); #if !CORE14 lock (m_lock) { #endif // server-specific directory deletion if (!ftppath.IsFtpRootDirectory()) { if (FtpServerSpecificHandler.ServerDeleteDirectory(this, path, ftppath, deleteContents, options)) { return; } } // DELETE CONTENTS OF THE DIRECTORY if (deleteContents) { // when GetListing is called with recursive option, then it does not // make any sense to call another DeleteDirectory with force flag set. // however this requires always delete files first. var recurse = !WasGetListingRecursive(options); // items that are deeper in directory tree are listed first, // then files will be listed before directories. This matters // only if GetListing was called with recursive option. FtpListItem[] itemList; if (recurse) { itemList = GetListing(path, options); } else { itemList = GetListing(path, options).OrderByDescending(x => x.FullName.Count(c => c.Equals('/'))).ThenBy(x => x.Type).ToArray(); } // delete the item based on the type foreach (var item in itemList) { switch (item.Type) { case FtpFileSystemObjectType.File: DeleteFile(item.FullName); break; case FtpFileSystemObjectType.Directory: DeleteDirInternal(item.FullName, recurse, options); break; default: throw new FtpException("Don't know how to delete object type: " + item.Type); } } } // SKIP DELETING ROOT DIRS // can't delete the working directory and // can't delete the server root. if (ftppath.IsFtpRootDirectory()) { return; } // DELETE ACTUAL DIRECTORY if (!(reply = Execute("RMD " + ftppath)).Success) { throw new FtpCommandException(reply); } #if !CORE14 } #endif }
/// <summary> /// Automatic FTP and FTPS connection negotiation. /// This method tries every possible combination of the FTP connection properties, and returns the list of successful connection profiles. /// You can configure it to stop after finding the first successful profile, or to collect all successful profiles. /// You can then generate code for the profile using the FtpProfile.ToCode method. /// If no successful profiles are found, a blank list is returned. /// </summary> /// <param name="firstOnly">Find all successful profiles (false) or stop after finding the first successful profile (true)</param> /// <param name="cloneConnection">Use a new cloned FtpClient for testing connection profiles (true) or use the source FtpClient (false)</param> /// <returns></returns> public List <FtpProfile> AutoDetect(bool firstOnly = true, bool cloneConnection = true) { var results = new List <FtpProfile>(); #if !CORE14 lock (m_lock) { #endif LogFunc(nameof(AutoDetect), new object[] { firstOnly, cloneConnection }); ValidateAutoDetect(); // get known working connection profile based on the host (if any) var knownProfile = FtpServerSpecificHandler.GetWorkingProfileFromHost(Host, Port); if (knownProfile != null) { results.Add(knownProfile); return(results); } var blacklistedEncryptions = new List <FtpEncryptionMode>(); bool resetPort = m_port == 0; // clone this connection or use this connection var conn = cloneConnection ? CloneConnection() : this; // copy basic props if cloned connection if (cloneConnection) { conn.Host = this.Host; conn.Port = this.Port; conn.Credentials = this.Credentials; } // disconnect if already connected if (conn.IsConnected) { conn.Disconnect(); } // try each encryption mode foreach (var encryption in autoConnectEncryption) { // skip if FTPS was tried and failed if (blacklistedEncryptions.Contains(encryption)) { continue; } // try each SSL protocol foreach (var protocol in autoConnectProtocols) { // skip plain protocols if testing secure FTPS -- disabled because 'None' is recommended by Microsoft /*if (encryption != FtpEncryptionMode.None && protocol == SysSslProtocols.None) { * continue; * }*/ // skip secure protocols if testing plain FTP if (encryption == FtpEncryptionMode.None && protocol != SysSslProtocols.None) { continue; } // reset port so it auto computes based on encryption type if (resetPort) { conn.Port = 0; } // set rolled props conn.EncryptionMode = encryption; conn.SslProtocols = protocol; conn.DataConnectionType = FtpDataConnectionType.AutoPassive; conn.Encoding = Encoding.UTF8; // try to connect var connected = false; var dataConn = FtpDataConnectionType.PASV; try { conn.Connect(); connected = true; // get data connection once connected dataConn = AutoDataConnection(conn); // if non-cloned connection, we want to remain connected if it works if (cloneConnection) { conn.Disconnect(); } } catch (Exception ex) { #if !CORE14 if (ex is AuthenticationException) { throw new FtpInvalidCertificateException(); } #endif // since the connection failed, disconnect and retry conn.Disconnect(); // if server does not support FTPS no point trying encryption again if (IsFtpsFailure(blacklistedEncryptions, encryption, ex)) { goto SkipEncryptionMode; } // catch error "no such host is known" and hard abort if (IsPermanantConnectionFailure(ex)) { if (cloneConnection) { conn.Dispose(); } // rethrow permanent failures so caller can be made aware of it throw; } } // if it worked if (connected) { // if connected by explicit FTPS failed, no point trying encryption again if (IsConnectedButFtpsFailure(blacklistedEncryptions, encryption, conn._ConnectionFTPSFailure)) { } results.Add(new FtpProfile { Host = Host, Credentials = Credentials, Encryption = blacklistedEncryptions.Contains(encryption) ? FtpEncryptionMode.None : encryption, Protocols = protocol, DataConnection = dataConn, Encoding = Encoding.UTF8, EncodingVerified = conn._ConnectionUTF8Success || conn.HasFeature(FtpCapability.UTF8) }); // stop if only 1 wanted if (firstOnly) { goto Exit; } } } SkipEncryptionMode: var skip = true; } Exit: if (cloneConnection) { conn.Dispose(); } #if !CORE14 } #endif return(results); }
/// <summary> /// Automatic FTP and FTPS connection negotiation. /// This method tries every possible combination of the FTP connection properties, and returns the list of successful connection profiles. /// You can configure it to stop after finding the first successful profile, or to collect all successful profiles. /// You can then generate code for the profile using the FtpProfile.ToCode method. /// If no successful profiles are found, a blank list is returned. /// </summary> /// <param name="firstOnly">Find all successful profiles (false) or stop after finding the first successful profile (true)?</param> /// <returns></returns> public List <FtpProfile> AutoDetect(bool firstOnly = true) { var results = new List <FtpProfile>(); #if !CORE14 lock (m_lock) { #endif LogFunc(nameof(AutoDetect), new object[] { firstOnly }); if (IsDisposed) { throw new ObjectDisposedException("This FtpClient object has been disposed. It is no longer accessible."); } if (Host == null) { throw new FtpException("No host has been specified. Please set the 'Host' property before trying to auto connect."); } if (Credentials == null) { throw new FtpException("No username and password has been specified. Please set the 'Credentials' property before trying to auto connect."); } // get known working connection profile based on the host (if any) var knownProfile = FtpServerSpecificHandler.GetWorkingProfileFromHost(Host, Port); if (knownProfile != null) { results.Add(knownProfile); return(results); } // try each encoding encoding: foreach (var encoding in autoConnectEncoding) { // try each encryption mode encryption: foreach (var encryption in autoConnectEncryption) { // try each SSL protocol protocol: foreach (var protocol in autoConnectProtocols) { // skip secure protocols if testing plain FTP if (encryption == FtpEncryptionMode.None && protocol != SysSslProtocols.None) { continue; } // try each data connection type dataType: foreach (var dataType in autoConnectData) { // clone this connection var conn = CloneConnection(); // set basic props conn.Host = Host; conn.Port = Port; conn.Credentials = Credentials; // set rolled props conn.EncryptionMode = encryption; conn.SslProtocols = protocol; conn.DataConnectionType = dataType; conn.Encoding = encoding; // try to connect var connected = false; try { conn.Connect(); connected = true; conn.Dispose(); } catch (Exception ex) { conn.Dispose(); // catch error "no such host is known" and hard abort #if NETFX || CORE2PLUS if (ex is SocketException && ((SocketException)ex).ErrorCode == 11001) { return(results); } #else if (ex is SocketException && ((SocketException)ex).Message.Contains("No such host is known")) { return(results); } #endif // catch error "timed out trying to connect" and hard abort if (ex is TimeoutException) { return(results); } } // if it worked, add the profile if (connected) { results.Add(new FtpProfile { Host = Host, Credentials = Credentials, Encryption = encryption, Protocols = protocol, DataConnection = dataType, Encoding = encoding }); // stop if only 1 wanted if (firstOnly) { return(results); } } } } } } #if !CORE14 } #endif return(results); }