/// <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);
        }