Exemple #1
0
        public void TestArgumentExceptions()
        {
            var credentials = new NetworkCredential("username", "password");

            Assert.Throws <ArgumentNullException> (() => SaslMechanism.Create(null, Encoding.UTF8, credentials));
            Assert.Throws <ArgumentNullException> (() => SaslMechanism.Create("PLAIN", null, credentials));
            Assert.Throws <ArgumentNullException> (() => SaslMechanism.Create("PLAIN", Encoding.UTF8, null));

            Assert.Throws <ArgumentNullException> (() => SaslMechanism.Create(null, credentials));
            Assert.Throws <ArgumentNullException> (() => SaslMechanism.Create("PLAIN", null));

            Assert.Throws <ArgumentNullException> (() => SaslMechanism.IsSupported(null));

            Assert.Throws <ArgumentNullException> (() => SaslMechanism.SaslPrep(null));
        }
Exemple #2
0
        public void TestArgumentExceptions()
        {
            var credentials = new NetworkCredential("username", "password");
            var uri         = new Uri("smtp://localhost");

            Assert.Throws <ArgumentNullException> (() => SaslMechanism.Create(null, uri, Encoding.UTF8, credentials));
            Assert.Throws <ArgumentNullException> (() => SaslMechanism.Create("PLAIN", null, Encoding.UTF8, credentials));
            Assert.Throws <ArgumentNullException> (() => SaslMechanism.Create("PLAIN", uri, null, credentials));
            Assert.Throws <ArgumentNullException> (() => SaslMechanism.Create("PLAIN", uri, Encoding.UTF8, null));

            Assert.Throws <ArgumentNullException> (() => SaslMechanism.Create(null, uri, credentials));
            Assert.Throws <ArgumentNullException> (() => SaslMechanism.Create("PLAIN", null, credentials));
            Assert.Throws <ArgumentNullException> (() => SaslMechanism.Create("PLAIN", uri, null));

            Assert.Throws <ArgumentNullException> (() => SaslMechanism.SaslPrep(null));
        }
        public void TestIsSupported()
        {
            var supported   = new [] { "PLAIN", "LOGIN", "CRAM-MD5", "DIGEST-MD5", "SCRAM-SHA-1", "SCRAM-SHA-256", "NTLM", "XOAUTH2" };
            var unsupported = new [] { "ANONYMOUS", "GSSAPI", "KERBEROS_V4" };
            var credentials = new NetworkCredential("username", "password");
            var uri         = new Uri("smtp://localhost");

            foreach (var mechanism in supported)
            {
                Assert.IsTrue(SaslMechanism.IsSupported(mechanism), mechanism);
                var sasl = SaslMechanism.Create(mechanism, uri, credentials);
                Assert.IsNotNull(sasl, mechanism);
                Assert.AreEqual(mechanism, sasl.MechanismName, "MechanismName");
            }

            foreach (var mechanism in unsupported)
            {
                Assert.IsFalse(SaslMechanism.IsSupported(mechanism), mechanism);
            }
        }
Exemple #4
0
        public void TestIsSupported()
        {
            var supported   = new [] { "PLAIN", "LOGIN", "CRAM-MD5", "DIGEST-MD5", "SCRAM-SHA-1", "SCRAM-SHA-1-PLUS", "SCRAM-SHA-256", "SCRAM-SHA-256-PLUS", "SCRAM-SHA-512", "SCRAM-SHA-512-PLUS", "NTLM", "OAUTHBEARER", "XOAUTH2", "ANONYMOUS" };
            var unsupported = new [] { "EXTERNAL", "GSSAPI", "KERBEROS_V4" };
            var credentials = new NetworkCredential("username", "password");

            foreach (var mechanism in supported)
            {
                Assert.IsTrue(SaslMechanism.IsSupported(mechanism), mechanism);

                var sasl = SaslMechanism.Create(mechanism, credentials);
                Assert.IsNotNull(sasl, mechanism);
                Assert.AreEqual(mechanism, sasl.MechanismName, "MechanismName");

                sasl.Reset();
            }

            foreach (var mechanism in unsupported)
            {
                Assert.IsFalse(SaslMechanism.IsSupported(mechanism), mechanism);
            }
        }
Exemple #5
0
        /// <summary>
        /// Authenticates using the supplied credentials.
        /// </summary>
        /// <remarks>
        /// <para>If the server supports one or more SASL authentication mechanisms, then
        /// the SASL mechanisms that both the client and server support are tried
        /// in order of greatest security to weakest security. Once a SASL
        /// authentication mechanism is found that both client and server support,
        /// the credentials are used to authenticate.</para>
        /// <para>If the server does not support SASL or if no common SASL mechanisms
        /// can be found, then LOGIN command is used as a fallback.</para>
        /// </remarks>
        /// <param name="credentials">The user's credentials.</param>
        /// <param name="cancellationToken">A cancellation token.</param>
        /// <exception cref="System.ArgumentNullException">
        /// <paramref name="credentials"/> is <c>null</c>.
        /// </exception>
        /// <exception cref="System.ObjectDisposedException">
        /// The <see cref="ImapClient"/> has been disposed.
        /// </exception>
        /// <exception cref="System.InvalidOperationException">
        /// The <see cref="ImapClient"/> is not connected or is already authenticated.
        /// </exception>
        /// <exception cref="System.OperationCanceledException">
        /// The operation was canceled via the cancellation token.
        /// </exception>
        /// <exception cref="System.Security.Authentication.AuthenticationException">
        /// Authentication using the supplied credentials has failed.
        /// </exception>
        /// <exception cref="MailKit.Security.SaslException">
        /// A SASL authentication error occurred.
        /// </exception>
        /// <exception cref="System.IO.IOException">
        /// An I/O error occurred.
        /// </exception>
        /// <exception cref="ImapProtocolException">
        /// An IMAP protocol error occurred.
        /// </exception>
        public void Authenticate(ICredentials credentials, CancellationToken cancellationToken)
        {
            CheckDisposed();

            if (!IsConnected)
            {
                throw new InvalidOperationException("The ImapClient must be connected before you can authenticate.");
            }

            if (engine.State >= ImapEngineState.Authenticated)
            {
                throw new InvalidOperationException("The ImapClient is already authenticated.");
            }

            if (credentials == null)
            {
                throw new ArgumentNullException("credentials");
            }


            int capabilitiesVersion = engine.CapabilitiesVersion;
            var uri = new Uri("imap://" + host);
            NetworkCredential cred;
            ImapCommand       ic;

            if ((engine.Capabilities & ImapCapabilities.StartTLS) != 0)
            {
                ic = engine.QueueCommand(cancellationToken, null, "STARTTLS\r\n");

                engine.Wait(ic);

                if (ic.Result == ImapCommandResult.Ok)
                {
                    var tls = new SslStream(engine.Stream, false, ValidateRemoteCertificate);
                    tls.AuthenticateAsClient(host, ClientCertificates, SslProtocols.Tls, true);
                    engine.Stream.Stream = tls;

                    // Query the CAPABILITIES again if the server did not include an
                    // untagged CAPABILITIES response to the STARTTLS command.
                    if (engine.CapabilitiesVersion == 1)
                    {
                        engine.QueryCapabilities(cancellationToken);
                    }
                }
            }
            else if ((engine.Capabilities & ImapCapabilities.Compress) != 0)
            {
                ic = engine.QueueCommand(cancellationToken, null, "COMPRESS DEFLATE\r\n");

                engine.Wait(ic);

                if (ic.Result == ImapCommandResult.Ok)
                {
                    var unzip = new DeflateStream(engine.Stream, CompressionMode.Decompress);
                    var zip   = new DeflateStream(engine.Stream, CompressionMode.Compress);

                    engine.Stream.Stream = new DuplexStream(unzip, zip);

                    // Query the CAPABILITIES again if the server did not include an
                    // untagged CAPABILITIES response to the COMPRESS command.
                    if (engine.CapabilitiesVersion == 1)
                    {
                        engine.QueryCapabilities(cancellationToken);
                    }
                }
            }

            foreach (var authmech in SaslMechanism.AuthMechanismRank)
            {
                if (!engine.AuthenticationMechanisms.Contains(authmech))
                {
                    continue;
                }

                var sasl = SaslMechanism.Create(authmech, uri, credentials);

                cancellationToken.ThrowIfCancellationRequested();

                var command = string.Format("AUTHENTICATE {0}", sasl.MechanismName);
                var ir      = sasl.Challenge(null);

                if ((engine.Capabilities & ImapCapabilities.SaslIR) != 0 && ir != null)
                {
                    command += " " + ir + "\r\n";
                }
                else
                {
                    command += "\r\n";
                    sasl.Reset();
                }

                ic = engine.QueueCommand(cancellationToken, null, command);
                ic.ContinuationHandler = (imap, cmd, text) => {
                    string challenge;

                    if (sasl.IsAuthenticated)
                    {
                        // the server claims we aren't done authenticating, but our SASL mechanism thinks we are...
                        // FIXME: will sending an empty string abort the AUTHENTICATE command?
                        challenge = string.Empty;
                    }
                    else
                    {
                        challenge = sasl.Challenge(text);
                    }

                    cmd.CancellationToken.ThrowIfCancellationRequested();

                    var buf = Encoding.ASCII.GetBytes(challenge + "\r\n");
                    imap.Stream.Write(buf, 0, buf.Length);
                    imap.Stream.Flush();
                };

                engine.Wait(ic);

                if (ic.Result != ImapCommandResult.Ok)
                {
                    continue;
                }

                engine.State = ImapEngineState.Authenticated;

                // Query the CAPABILITIES again if the server did not include an
                // untagged CAPABILITIES response to the AUTHENTICATE command.
                if (engine.CapabilitiesVersion == capabilitiesVersion)
                {
                    engine.QueryCapabilities(cancellationToken);
                }

                engine.QueryNamespaces(cancellationToken);
                engine.QuerySpecialFolders(cancellationToken);
                return;
            }

            if ((Capabilities & ImapCapabilities.LoginDisabled) != 0)
            {
                throw new AuthenticationException();
            }

            // fall back to the classic LOGIN command...
            cred = credentials.GetCredential(uri, "LOGIN");

            ic = engine.QueueCommand(cancellationToken, null, "LOGIN %S %S\r\n", cred.UserName, cred.Password);

            engine.Wait(ic);

            if (ic.Result != ImapCommandResult.Ok)
            {
                throw new AuthenticationException();
            }

            engine.State = ImapEngineState.Authenticated;

            // Query the CAPABILITIES again if the server did not include an
            // untagged CAPABILITIES response to the LOGIN command.
            if (engine.CapabilitiesVersion == capabilitiesVersion)
            {
                engine.QueryCapabilities(cancellationToken);
            }

            engine.QueryNamespaces(cancellationToken);
            engine.QuerySpecialFolders(cancellationToken);
        }
        public void TestArgumentExceptions()
        {
            var           credentials = new NetworkCredential("username", "password");
            var           uri         = new Uri("smtp://localhost");
            SaslMechanism sasl;

            Assert.Throws <ArgumentNullException> (() => new SaslException(null, SaslErrorCode.MissingChallenge, "message"));

            sasl = new SaslMechanismCramMd5(credentials);
            Assert.Throws <ArgumentNullException> (() => new SaslMechanismCramMd5(null, credentials));
            Assert.Throws <ArgumentNullException> (() => new SaslMechanismCramMd5(uri, null));
            Assert.Throws <ArgumentNullException> (() => new SaslMechanismCramMd5(null, "username", "password"));
            Assert.Throws <ArgumentNullException> (() => new SaslMechanismCramMd5(uri, null, "password"));
            Assert.Throws <ArgumentNullException> (() => new SaslMechanismCramMd5(uri, "username", null));
            Assert.Throws <ArgumentNullException> (() => new SaslMechanismCramMd5(null));
            Assert.Throws <ArgumentNullException> (() => new SaslMechanismCramMd5(null, "password"));
            Assert.Throws <ArgumentNullException> (() => new SaslMechanismCramMd5("username", null));
            Assert.Throws <NotSupportedException> (() => sasl.Challenge(null));

            sasl = new SaslMechanismDigestMd5(credentials)
            {
                Uri = uri
            };
            Assert.Throws <ArgumentNullException> (() => new SaslMechanismDigestMd5(null, credentials));
            Assert.Throws <ArgumentNullException> (() => new SaslMechanismDigestMd5(uri, null));
            Assert.Throws <ArgumentNullException> (() => new SaslMechanismDigestMd5(null, "username", "password"));
            Assert.Throws <ArgumentNullException> (() => new SaslMechanismDigestMd5(uri, (string)null, "password"));
            Assert.Throws <ArgumentNullException> (() => new SaslMechanismDigestMd5(uri, "username", null));
            Assert.Throws <ArgumentNullException> (() => new SaslMechanismDigestMd5(null));
            Assert.Throws <ArgumentNullException> (() => new SaslMechanismDigestMd5(null, "password"));
            Assert.Throws <ArgumentNullException> (() => new SaslMechanismDigestMd5("username", null));
            Assert.Throws <NotSupportedException> (() => sasl.Challenge(null));

            sasl = new SaslMechanismLogin(credentials);
            Assert.Throws <ArgumentNullException> (() => new SaslMechanismLogin((Uri)null, Encoding.UTF8, credentials));
            Assert.Throws <ArgumentNullException> (() => new SaslMechanismLogin(uri, null, credentials));
            Assert.Throws <ArgumentNullException> (() => new SaslMechanismLogin(uri, Encoding.UTF8, null));
            Assert.Throws <ArgumentNullException> (() => new SaslMechanismLogin((Uri)null, credentials));
            Assert.Throws <ArgumentNullException> (() => new SaslMechanismLogin(uri, null));
            Assert.Throws <ArgumentNullException> (() => new SaslMechanismLogin((Uri)null, Encoding.UTF8, "username", "password"));
            Assert.Throws <ArgumentNullException> (() => new SaslMechanismLogin(uri, null, "username", "password"));
            Assert.Throws <ArgumentNullException> (() => new SaslMechanismLogin(uri, Encoding.UTF8, null, "password"));
            Assert.Throws <ArgumentNullException> (() => new SaslMechanismLogin(uri, Encoding.UTF8, "username", null));
            Assert.Throws <ArgumentNullException> (() => new SaslMechanismLogin((Uri)null, "username", "password"));
            Assert.Throws <ArgumentNullException> (() => new SaslMechanismLogin(uri, null, "password"));
            Assert.Throws <ArgumentNullException> (() => new SaslMechanismLogin(uri, "username", null));
            Assert.Throws <ArgumentNullException> (() => new SaslMechanismLogin(null, credentials));
            Assert.Throws <ArgumentNullException> (() => new SaslMechanismLogin(Encoding.UTF8, null));
            Assert.Throws <ArgumentNullException> (() => new SaslMechanismLogin(null));
            Assert.Throws <ArgumentNullException> (() => new SaslMechanismLogin((Encoding)null, "username", "password"));
            Assert.Throws <ArgumentNullException> (() => new SaslMechanismLogin(Encoding.UTF8, null, "password"));
            Assert.Throws <ArgumentNullException> (() => new SaslMechanismLogin(Encoding.UTF8, "username", null));
            Assert.Throws <ArgumentNullException> (() => new SaslMechanismLogin(null, "password"));
            Assert.Throws <ArgumentNullException> (() => new SaslMechanismLogin("username", null));
            Assert.Throws <NotSupportedException> (() => sasl.Challenge(null));

            sasl = new SaslMechanismNtlm(credentials);
            Assert.Throws <ArgumentNullException> (() => new SaslMechanismNtlm((Uri)null, credentials));
            Assert.Throws <ArgumentNullException> (() => new SaslMechanismNtlm(uri, null));
            Assert.Throws <ArgumentNullException> (() => new SaslMechanismNtlm((Uri)null, "username", "password"));
            Assert.Throws <ArgumentNullException> (() => new SaslMechanismNtlm(uri, (string)null, "password"));
            Assert.Throws <ArgumentNullException> (() => new SaslMechanismNtlm(uri, "username", null));
            Assert.Throws <ArgumentNullException> (() => new SaslMechanismNtlm(null));
            Assert.Throws <ArgumentNullException> (() => new SaslMechanismNtlm(null, "password"));
            Assert.Throws <ArgumentNullException> (() => new SaslMechanismNtlm("username", null));
            Assert.DoesNotThrow(() => sasl.Challenge(null));

            sasl = new SaslMechanismOAuth2(credentials);
            Assert.Throws <ArgumentNullException> (() => new SaslMechanismOAuth2((Uri)null, credentials));
            Assert.Throws <ArgumentNullException> (() => new SaslMechanismOAuth2(uri, null));
            Assert.Throws <ArgumentNullException> (() => new SaslMechanismOAuth2((Uri)null, "username", "password"));
            Assert.Throws <ArgumentNullException> (() => new SaslMechanismOAuth2(uri, (string)null, "password"));
            Assert.Throws <ArgumentNullException> (() => new SaslMechanismOAuth2(uri, "username", null));
            Assert.Throws <ArgumentNullException> (() => new SaslMechanismOAuth2(null));
            Assert.Throws <ArgumentNullException> (() => new SaslMechanismOAuth2(null, "password"));
            Assert.Throws <ArgumentNullException> (() => new SaslMechanismOAuth2("username", null));
            Assert.DoesNotThrow(() => sasl.Challenge(null));

            sasl = new SaslMechanismPlain(credentials);
            Assert.Throws <ArgumentNullException> (() => new SaslMechanismPlain((Uri)null, Encoding.UTF8, credentials));
            Assert.Throws <ArgumentNullException> (() => new SaslMechanismPlain(uri, null, credentials));
            Assert.Throws <ArgumentNullException> (() => new SaslMechanismPlain(uri, Encoding.UTF8, null));
            Assert.Throws <ArgumentNullException> (() => new SaslMechanismPlain((Uri)null, credentials));
            Assert.Throws <ArgumentNullException> (() => new SaslMechanismPlain(uri, null));
            Assert.Throws <ArgumentNullException> (() => new SaslMechanismPlain((Uri)null, Encoding.UTF8, "username", "password"));
            Assert.Throws <ArgumentNullException> (() => new SaslMechanismPlain(uri, null, "username", "password"));
            Assert.Throws <ArgumentNullException> (() => new SaslMechanismPlain(uri, Encoding.UTF8, null, "password"));
            Assert.Throws <ArgumentNullException> (() => new SaslMechanismPlain(uri, Encoding.UTF8, "username", null));
            Assert.Throws <ArgumentNullException> (() => new SaslMechanismPlain((Uri)null, "username", "password"));
            Assert.Throws <ArgumentNullException> (() => new SaslMechanismPlain(uri, null, "password"));
            Assert.Throws <ArgumentNullException> (() => new SaslMechanismPlain(uri, "username", null));
            Assert.Throws <ArgumentNullException> (() => new SaslMechanismPlain(null, credentials));
            Assert.Throws <ArgumentNullException> (() => new SaslMechanismPlain(Encoding.UTF8, null));
            Assert.Throws <ArgumentNullException> (() => new SaslMechanismPlain(null));
            Assert.Throws <ArgumentNullException> (() => new SaslMechanismPlain((Encoding)null, "username", "password"));
            Assert.Throws <ArgumentNullException> (() => new SaslMechanismPlain(Encoding.UTF8, null, "password"));
            Assert.Throws <ArgumentNullException> (() => new SaslMechanismPlain(Encoding.UTF8, "username", null));
            Assert.Throws <ArgumentNullException> (() => new SaslMechanismPlain(null, "password"));
            Assert.Throws <ArgumentNullException> (() => new SaslMechanismPlain("username", null));
            Assert.DoesNotThrow(() => sasl.Challenge(null));

            sasl = new SaslMechanismScramSha1(credentials);
            Assert.Throws <ArgumentNullException> (() => new SaslMechanismScramSha1(null, credentials));
            Assert.Throws <ArgumentNullException> (() => new SaslMechanismScramSha1(uri, null));
            Assert.Throws <ArgumentNullException> (() => new SaslMechanismScramSha1(null, "username", "password"));
            Assert.Throws <ArgumentNullException> (() => new SaslMechanismScramSha1(uri, (string)null, "password"));
            Assert.Throws <ArgumentNullException> (() => new SaslMechanismScramSha1(uri, "username", null));
            Assert.Throws <ArgumentNullException> (() => new SaslMechanismScramSha1(null));
            Assert.Throws <ArgumentNullException> (() => new SaslMechanismScramSha1((string)null, "password"));
            Assert.Throws <ArgumentNullException> (() => new SaslMechanismScramSha1("username", null));
            Assert.DoesNotThrow(() => sasl.Challenge(null));

            sasl = new SaslMechanismScramSha256(credentials);
            Assert.Throws <ArgumentNullException> (() => new SaslMechanismScramSha256(null, credentials));
            Assert.Throws <ArgumentNullException> (() => new SaslMechanismScramSha256(uri, null));
            Assert.Throws <ArgumentNullException> (() => new SaslMechanismScramSha256(null, "username", "password"));
            Assert.Throws <ArgumentNullException> (() => new SaslMechanismScramSha256(uri, (string)null, "password"));
            Assert.Throws <ArgumentNullException> (() => new SaslMechanismScramSha256(uri, "username", null));
            Assert.Throws <ArgumentNullException> (() => new SaslMechanismScramSha256(null));
            Assert.Throws <ArgumentNullException> (() => new SaslMechanismScramSha256((string)null, "password"));
            Assert.Throws <ArgumentNullException> (() => new SaslMechanismScramSha256("username", null));
            Assert.DoesNotThrow(() => sasl.Challenge(null));

            Assert.Throws <ArgumentNullException> (() => SaslMechanism.Create(null, uri, Encoding.UTF8, credentials));
            Assert.Throws <ArgumentNullException> (() => SaslMechanism.Create("PLAIN", null, Encoding.UTF8, credentials));
            Assert.Throws <ArgumentNullException> (() => SaslMechanism.Create("PLAIN", uri, null, credentials));
            Assert.Throws <ArgumentNullException> (() => SaslMechanism.Create("PLAIN", uri, Encoding.UTF8, null));

            Assert.Throws <ArgumentNullException> (() => SaslMechanism.Create(null, uri, credentials));
            Assert.Throws <ArgumentNullException> (() => SaslMechanism.Create("PLAIN", null, credentials));
            Assert.Throws <ArgumentNullException> (() => SaslMechanism.Create("PLAIN", uri, null));

            Assert.Throws <ArgumentNullException> (() => SaslMechanism.SaslPrep(null));
        }
Exemple #7
0
        /// <summary>
        /// Authenticates using the supplied credentials.
        /// </summary>
        /// <remarks>
        /// <para>If the SMTP server supports authentication, then the SASL mechanisms
        /// that both the client and server support are tried in order of greatest
        /// security to weakest security. Once a SASL authentication mechanism is
        /// found that both client and server support, the credentials are used to
        /// authenticate.</para>
        /// <para>If, on the other hand, authentication is not supported, then
        /// this method simply returns without attempting to authenticate.</para>
        /// </remarks>
        /// <param name="credentials">The user's credentials.</param>
        /// <param name="cancellationToken">A cancellation token.</param>
        /// <exception cref="System.ArgumentNullException">
        /// <paramref name="credentials"/> is <c>null</c>.
        /// </exception>
        /// <exception cref="System.InvalidOperationException">
        /// The <see cref="SmtpClient"/> is not connected or is already authenticated.
        /// </exception>
        /// <exception cref="System.NotSupportedException">
        /// The SMTP server does not support authentication.
        /// </exception>
        /// <exception cref="System.OperationCanceledException">
        /// The operation was canceled via the cancellation token.
        /// </exception>
        /// <exception cref="System.Security.Authentication.AuthenticationException">
        /// Authentication using the supplied credentials has failed.
        /// </exception>
        /// <exception cref="MailKit.Security.SaslException">
        /// A SASL authentication error occurred.
        /// </exception>
        /// <exception cref="System.IO.IOException">
        /// An I/O error occurred.
        /// </exception>
        /// <exception cref="SmtpCommandException">
        /// The SMTP command failed.
        /// </exception>
        /// <exception cref="SmtpProtocolException">
        /// An SMTP protocol error occurred.
        /// </exception>
        public void Authenticate(ICredentials credentials, CancellationToken cancellationToken)
        {
            if (!IsConnected)
            {
                throw new InvalidOperationException("The SmtpClient must be connected before you can authenticate.");
            }

            if (authenticated)
            {
                throw new InvalidOperationException("The SmtpClient is already authenticated.");
            }

            if ((Capabilities & SmtpCapabilities.Authentication) == 0)
            {
                throw new NotSupportedException("The SMTP server does not support authentication.");
            }

            if (credentials == null)
            {
                throw new ArgumentNullException("credentials");
            }

            var          uri = new Uri("smtp://" + host);
            SmtpResponse response;
            string       challenge;
            string       command;

            foreach (var authmech in SaslMechanism.AuthMechanismRank)
            {
                if (!AuthenticationMechanisms.Contains(authmech))
                {
                    continue;
                }

                var sasl = SaslMechanism.Create(authmech, uri, credentials);

                cancellationToken.ThrowIfCancellationRequested();

                // send an initial challenge if the mechanism supports it
                if ((challenge = sasl.Challenge(null)) != null)
                {
                    command = string.Format("AUTH {0} {1}", authmech, challenge);
                }
                else
                {
                    command = string.Format("AUTH {0}", authmech);
                }

                response = SendCommand(command, cancellationToken);

                if (response.StatusCode == SmtpStatusCode.AuthenticationMechanismTooWeak)
                {
                    continue;
                }

                while (!sasl.IsAuthenticated)
                {
                    if (response.StatusCode != SmtpStatusCode.AuthenticationChallenge)
                    {
                        throw new SmtpCommandException(SmtpErrorCode.UnexpectedStatusCode, response.StatusCode, response.Response);
                    }

                    challenge = sasl.Challenge(response.Response);
                    response  = SendCommand(challenge, cancellationToken);
                }

                if (response.StatusCode == SmtpStatusCode.AuthenticationSuccessful)
                {
                    Ehlo(cancellationToken);
                    authenticated = true;
                    return;
                }

                throw new AuthenticationException();
            }

            throw new NotSupportedException("No compatible authentication mechanisms found.");
        }