/// <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);
        }
示例#2
0
        /// <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);
        }
示例#3
0
 /// <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'));
 }
示例#4
0
        // 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);
            }
        }
示例#5
0
        // 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);
        }