Example #1
0
        private async Task <MailKit.IMailTransport> CreateTransport()
        {
            SecureSocketOptions secureSocketOptions = SecureSocketOptions.SslOnConnect;

            MailKit.Net.Smtp.SmtpClient smtpClient = new MailKit.Net.Smtp.SmtpClient();
            smtpClient.ServerCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) => true;
            try {
                await smtpClient.ConnectAsync(_Settings.SmtpMailServer, _Settings.SmtpPort, secureSocketOptions);

                var authMech = smtpClient.AuthenticationMechanisms;
                if (smtpClient.IsConnected)
                {
                    await smtpClient.AuthenticateAsync(_Settings.SenderLogin, _Settings.SenderPassword);
                }
                if (!smtpClient.IsAuthenticated)
                {
                    return(smtpClient);
                }
            }
            catch (AggregateException e) {
                _Logger?.LogError(e, e.Message);
                throw;
            }
            return(smtpClient);
        }
        public override void LoadSetting(string fieldName, string fieldValue)
        {
            base.LoadSetting(fieldName, fieldValue);
            switch (fieldName.ToLower())
            {
            case "mailhost":
                MailHost = fieldValue; break;

            case "username":
                UserName = fieldValue; break;

            case "userpassword":
                UserPassword = fieldValue; break;

            case "mailboxname":
                MailboxName = fieldValue; break;

            case "clienttype":
                ClientType = fieldValue; break;

            case "encryption":
                Encryption = (SecureSocketOptions)Enum.Parse(typeof(SecureSocketOptions), fieldValue); break;

            case "query":
                Query = fieldValue; break;

            case "includesubfolders":
                IncludeSubfolders = Convert.ToBoolean(fieldValue); break;

            case "unreadmailsonly":
                UnreadMailsOnly = Convert.ToBoolean(fieldValue); break;
            }
        }
Example #3
0
        public static async Task ReconnectAsync(string host, int port, SecureSocketOptions options)
        {
            // Note: for demo purposes, we're ignoring SSL validation errors (don't do this in production code)
            Client.ServerCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) => true;

            await Client.ConnectAsync(host, port, options);

            try {
                await Client.AuthenticateAsync(Credentials);
            } catch {
                MessageBox.Show("Failed to Authenticate to server. If you are using GMail, then you probably " +
                                "need to go into your GMail settings to enable \"less secure apps\" in order " +
                                "to get this demo to work.\n\n" +
                                "For a real Mail application, you'll want to add support for obtaining the " +
                                "user's OAuth2 credentials to prevent the need for user's to enable this, but " +
                                "that is beyond the scope of this demo.",
                                "Authentication Error");
                throw;
            }

            if (Client.Capabilities.HasFlag(ImapCapabilities.UTF8Accept))
            {
                await Client.EnableUTF8Async();
            }

            CurrentTask = Task.FromResult(true);
        }
Example #4
0
        public void SendMessage(MMailMessage message)
        {
            var smtpClient = new MailKit.Net.Smtp.SmtpClient();


            if (options.IgnoreSSLError)
            {
                smtpClient.ServerCertificateValidationCallback += (sender, certificate, chain, errors) => true;
            }

            SecureSocketOptions secOpts = SecureSocketOptions.Auto;

            if (!options.UseSSL)
            {
                secOpts = SecureSocketOptions.None;
            }

            smtpClient.Connect(options.SMTPServer, options.SMTPServerPort, secOpts);


            smtpClient.AuthenticationMechanisms.Remove("XOAUTH2");

            if (options.Credentials != null)
            {
                smtpClient.Authenticate(options.Credentials);
            }


            smtpClient.Send(message);
            smtpClient.Disconnect(true);
        }
Example #5
0
        public async Task SendEmailAsync(string email, string subject, string body)
        {
            var message = new MimeMessage();

            message.From.Add(new MailboxAddress(_smtpSettings.SenderName, _smtpSettings.SenderEmail));
            message.To.Add(MailboxAddress.Parse(email));
            message.Subject = subject;

            message.Body = new TextPart(TextFormat.Html)
            {
                Text = body
            };

            using (var smtp = new SmtpClient())
            {
                smtp.ServerCertificateValidationCallback = (s, c, h, e) => true;

                smtp.MessageSent += (sender, a) =>
                {
                };

                SecureSocketOptions secureSocketOptions = SecureSocketOptions.Auto;
                Enum.TryParse(_smtpSettings.SecureSocketOptions, out secureSocketOptions);

                await smtp.ConnectAsync(_smtpSettings.Server, _smtpSettings.Port, secureSocketOptions);

                await smtp.AuthenticateAsync(_smtpSettings.Username, _smtpSettings.Password);

                await smtp.SendAsync(message);

                await smtp.DisconnectAsync(true);
            }
        }
Example #6
0
 public override void Connect(Stream stream, string host, int port = 0, SecureSocketOptions options = SecureSocketOptions.Auto,
                              CancellationToken cancellationToken  = new CancellationToken())
 {
     if (ConnectException != null)
     {
         throw ConnectException;
     }
 }
Example #7
0
 public MailSender(SmtpConfiguration configuration)
 {
     // TODO check params
     this.Credentials     = new NetworkCredential(configuration.Username, configuration.Password);
     this.Host            = configuration.Host;
     this.Port            = configuration.Port;
     this.SecurityOptions = configuration.SecureSocketOptions;
 }
Example #8
0
 public MailSender(ICredentials credentials, string host, int port, SecureSocketOptions securityOptions)
 {
     // TODO check params
     this.Credentials     = credentials;
     this.Host            = host;
     this.Port            = port;
     this.SecurityOptions = securityOptions;
 }
Example #9
0
 public void OverrideWith(SmtpConfiguration other)
 {
     this.Host                = other.Host;
     this.Port                = other.Port;
     this.Username            = other.Username;
     this.Password            = other.Password;
     this.SecureSocketOptions = other.SecureSocketOptions;
 }
Example #10
0
 public MailSender(string host, ushort port, SecureSocketOptions options, string?username, string?password)
 {
     this.host     = host;
     this.port     = port;
     this.options  = options;
     this.username = username;
     this.password = password;
 }
Example #11
0
 public IdleEmailService(IEmailConfiguration emailConfiguration, IKudoService kudoService)
 {
     _emailConfiguration = emailConfiguration;
     _kudoService        = kudoService;
     sslOptions          = SecureSocketOptions.Auto;
     messages            = new List <IMessageSummary>();
     cancel = new CancellationTokenSource();
     client = new ImapClient(new ProtocolLogger(Console.OpenStandardError()));
 }
 public EMailController(string mailServer, int port, SecureSocketOptions secureOptions = SecureSocketOptions.Auto, bool authenticated = false, string userName = null, string password = null)
 {
     _mailServer     = mailServer;
     _mailServerPort = port;
     _secureOptions  = secureOptions;
     _authenticated  = authenticated;
     _username       = userName;
     _password       = password;
 }
Example #13
0
        public override Task ConnectAsync(Stream stream, string host, int port = 0, SecureSocketOptions options = SecureSocketOptions.Auto,
                                          CancellationToken cancellationToken  = new CancellationToken())
        {
            if (ConnectException != null)
            {
                throw ConnectException;
            }

            return(Task.CompletedTask);
        }
Example #14
0
 public IdleClient(string host, int port, SecureSocketOptions sslOptions, string username, string password)
 {
     this.client     = new ImapClient(new ProtocolLogger(Console.OpenStandardError()));
     this.messages   = new List <IMessageSummary> ();
     this.cancel     = new CancellationTokenSource();
     this.sslOptions = sslOptions;
     this.username   = username;
     this.password   = password;
     this.host       = host;
     this.port       = port;
 }
Example #15
0
    public Configuration(string host, int port, int timeoutInMilliseconds, string?userName, string?password, string?secureSocketOption)
    {
        Host = host;
        Port = port;
        TimeoutInMilliseconds = timeoutInMilliseconds;
        Credential            = new Credential(userName, password);

        SecureSocketOption = Enum.TryParse <SecureSocketOptions>(secureSocketOption, out var secureSocketOptionEnum)
            ? secureSocketOptionEnum
            : SecureSocketOptions.StartTlsWhenAvailable;
    }
Example #16
0
        public static SmtpClient Client(string host = "localhost", int port = 9025, SecureSocketOptions options = SecureSocketOptions.Auto)
        {
            var client = new SmtpClient();

            client.Connected += (sender, args) =>
            {
            };

            client.Connect("localhost", 9025, options);

            return(client);
        }
Example #17
0
        public void Logon(InternetMailProfile profile)
        {
            SecureSocketOptions options = SecureSocketOptions.Auto;

            _client.Timeout = profile.Timeout * 1000;
            _client.ServerCertificateValidationCallback = (s, c, h, e) => true;
            _client.Connect(profile.SmtpServerAddress, profile.GetSmtpPort(), options);

            if (profile.SmtpUser != "")
            {
                _client.Authenticate(profile.SmtpUser, profile.SmtpPassword);
            }
        }
Example #18
0
        public MailWorker(ICredentials credentials, string server, int prt)
        {
            Credentials             = credentials;
            host                    = server;
            port                    = prt;
            Client.Disconnected    += OnClientDisconnected;
            Client.Connected       += Client_Connected;
            Client.Alert           += Client_Alert;
            Client.MetadataChanged += Client_MetadataChanged;

            options = SecureSocketOptions.SslOnConnect;
            cancel  = new CancellationTokenSource();
        }
        public SmtpClient CreateSmtpClient(NetworkCredential credentials, string host, int port, SecureSocketOptions options, int timeout)
        {
            var client = new SmtpClient {
                Timeout = timeout
            };

            this.credentials   = credentials;
            this.client        = client;
            this.host          = host;
            this.port          = port;
            this.socketOptions = options;
            ConnectClient(this.client);
            return(this.client);
        }
        // string proSender = "*****@*****.**";

        public IdleClient(string host, int port, SecureSocketOptions sslOptions, string username, string password, string transperfectEmail, string transperfectPass)
        {
            logWriter              = new LogWriter("Start IdleClient ...");
            client                 = new ImapClient(new ProtocolLogger(Console.OpenStandardError()));
            messages               = new List <IMessageSummary>();
            cancel                 = new CancellationTokenSource();
            this.sslOptions        = sslOptions;
            this.username          = username;
            this.password          = password;
            this.transperfectEmail = transperfectEmail;
            this.transperfectPass  = transperfectPass;
            this.host              = host;
            this.port              = port;

            autoRun = new AutoRun(this.username, this.transperfectEmail, this.transperfectPass);
        }
Example #21
0
        public static async Task ReconnectAsync(string host, int port, SecureSocketOptions options)
        {
            // Note: for demo purposes, we're ignoring SSL validation errors (don't do this in production code)
            Client.ServerCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) => true;

            await Client.ConnectAsync(host, port, options);

            await Client.AuthenticateAsync(Credentials);

            if (Client.Capabilities.HasFlag(ImapCapabilities.UTF8Accept))
            {
                await Client.EnableUTF8Async();
            }

            CurrentTask = Task.FromResult(true);
        }
Example #22
0
        public SmtpEmailTransport(SmtpOptions options)
        {
            _options = options;

            var sender = options.SenderAddress ?? DefaultSender;

            _sender = new MailboxAddress(options.SenderName, sender);

            _socketOptions = options.UseEncryption ? SecureSocketOptions.StartTls : SecureSocketOptions.None;

            Uri senderUri;

            if (Uri.TryCreate($"mailto:{sender}", UriKind.Absolute, out senderUri))
            {
                _localDomain = senderUri.Host;
            }
        }
Example #23
0
        private MailSendResult SendViaMailKit(MimeMessage message)
        {
            MailSendResult sendResult = new MailSendResult();

            if (!message.From.Any())
            {
                message.From.Add(new MailboxAddress(_settingsService.GetSettings <string>("SystemEmailSenderName"), _settingsService.GetSettings <string>("SystemEmailAddress")));
            }
            if (AppSettingsHelper.EmailBlindCarbonCopyEnabled)
            {
                string bcc = AppSettingsHelper.EmailBlindCarbonCopyAddress;
                message.Bcc.Add(new MailboxAddress(bcc));
            }
            string host = _settingsService.GetSettings <string>("EmailSmtpUrl");
            int    port = _settingsService.GetSettings <int>("EmailSmtpPort");
            SecureSocketOptions socketSecurityOption = _settingsService.GetSettings <bool>("SmtpSslEnabled") ? SecureSocketOptions.SslOnConnect : SecureSocketOptions.Auto;
            string username = _settingsService.GetSettings <string>("EmailLogin");
            string password = _settingsService.GetSettings <string>("EmailPassword");

            try
            {
                Task.Run(async() =>
                {
                    using (var smtp = new MailKit.Net.Smtp.SmtpClient())
                    {
                        await smtp.ConnectAsync(host, port, socketSecurityOption);
                        await smtp.AuthenticateAsync(username, password);
                        await smtp.SendAsync(message); //отправка
                        sendResult.Success = true;
                        await smtp.DisconnectAsync(true);
                    }
                }).Wait();
            }
            catch (Exception e)
            {
                _logService.LogError(e);
                sendResult.Success  = false;
                sendResult.ErrorMsg = e.Message;
            }
            return(sendResult);
        }
Example #24
0
        /// <summary>
        /// Asynchronously establish a connection to the specified mail server.
        /// </summary>
        /// <remarks>
        /// Asynchronously establishes a connection to the specified mail server.
        /// </remarks>
        /// <returns>An asynchronous task context.</returns>
        /// <param name="host">The host name to connect to.</param>
        /// <param name="port">The port to connect to. If the specified port is <c>0</c>, then the default port will be used.</param>
        /// <param name="options">The secure socket options to when connecting.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <exception cref="System.ArgumentNullException">
        /// <paramref name="host"/> is <c>null</c>.
        /// </exception>
        /// <exception cref="System.ArgumentOutOfRangeException">
        /// <paramref name="port"/> is not between <c>0</c> and <c>65535</c>.
        /// </exception>
        /// <exception cref="System.ArgumentException">
        /// The <paramref name="host"/> is a zero-length string.
        /// </exception>
        /// <exception cref="System.ObjectDisposedException">
        /// The <see cref="MailService"/> has been disposed.
        /// </exception>
        /// <exception cref="System.InvalidOperationException">
        /// The <see cref="MailService"/> is already connected.
        /// </exception>
        /// <exception cref="System.OperationCanceledException">
        /// The operation was canceled via the cancellation token.
        /// </exception>
        /// <exception cref="System.IO.IOException">
        /// An I/O error occurred.
        /// </exception>
        /// <exception cref="ProtocolException">
        /// A protocol error occurred.
        /// </exception>
        public virtual Task ConnectAsync(string host, int port = 0, SecureSocketOptions options = SecureSocketOptions.Auto, CancellationToken cancellationToken = default(CancellationToken))
        {
            if (host == null)
            {
                throw new ArgumentNullException("host");
            }

            if (host.Length == 0)
            {
                throw new ArgumentException("The host name cannot be empty.", "host");
            }

            if (port < 0 || port > 65535)
            {
                throw new ArgumentOutOfRangeException("port");
            }

            return(Task.Factory.StartNew(() => {
                lock (SyncRoot) {
                    Connect(host, port, options, cancellationToken);
                }
            }, cancellationToken, TaskCreationOptions.None, TaskScheduler.Default));
        }
            async Task ConnectAsync(string host, int port, SecureSocketOptions options, bool doAsync, CancellationToken cancellationToken)
            {
                using (var socket = await ConnectSocket(host, port, doAsync, cancellationToken).ConfigureAwait(false)) {
                    hostName = host;

                    var ssl = new SslStream(new NetworkStream(socket, false), false, ValidateRemoteCertificate);

                    try {
                        if (doAsync)
                        {
                            await ssl.AuthenticateAsClientAsync(host, ClientCertificates, SslProtocols, CheckCertificateRevocation).ConfigureAwait(false);
                        }
                        else
                        {
                            ssl.AuthenticateAsClient(host, ClientCertificates, SslProtocols, CheckCertificateRevocation);
                        }
                    } catch (Exception ex) {
                        ssl.Dispose();

                        throw SslHandshakeException.Create(this, ex, false, "HTTP", host, port, 443, 80);
                    }
                }
            }
Example #26
0
        private MailManager()
        {
            try
            {
                var smtpConfig = ConfigManager.Configuration.GetSection("SMTP");
                Host = smtpConfig.GetValue <string>("Host");
                Port = smtpConfig.GetValue <int>("Port");
                Ssl  = smtpConfig.GetValue <bool>("SSL") ? SecureSocketOptions.StartTls : SecureSocketOptions.None;

                _smtp = new SmtpClient();
                _smtp.Connect(Host, Port, Ssl);

                _senderThread = new Thread(SendMails)
                {
                    Name     = "Portunus Mail Dispatcher Thread",
                    Priority = ThreadPriority.BelowNormal
                };
            }
            catch (Exception ex)
            {
                _log.Error(ex, "Could not initialize mail sender.");
                throw; // cannot operate without mail sending
            }
        }
Example #27
0
        public override INotifier Create(ILogger logger, IConfigurationSection section)
        {
            using (logger.BeginScope(nameof(EmailFactory)))
            {
                logger.LogInformation("Processing Email Config");

                SecureSocketOptions socketOptions = GetSecureSocketOptions(logger, section);
                string destination = section.GetValue <string>("Destination");
                string host        = section.GetValue <string>("Host");
                int    port        = section.GetValue <int>("Port", 25);
                string username    = section.GetValue <string>("Username");
                string password    = section.GetValue <string>("Password");

                return(new Email()
                {
                    Destination = destination,
                    Username = username,
                    Password = password,
                    Host = host,
                    SocketOptions = socketOptions,
                    Port = port
                });
            }
        }
 public Task ConnectAsync(string host, int port = 0, SecureSocketOptions options = SecureSocketOptions.Auto,
     CancellationToken cancellationToken = new CancellationToken())
 {
     return _imapClient.ConnectAsync(host, port, options, cancellationToken);
 }
Example #29
0
 protected override void OnDisconnected(string host, int port, SecureSocketOptions options, bool requested)
 {
     base.OnDisconnected(host, port, options, requested);
 }
Example #30
0
 protected override void OnConnected(string host, int port, SecureSocketOptions options)
 {
     base.OnConnected(host, port, options);
 }
 public void Connect(Socket socket, string host, int port = 0,
     SecureSocketOptions options = SecureSocketOptions.Auto,
     CancellationToken cancellationToken = new CancellationToken())
 {
     _imapClient.Connect(socket, host, port, options, cancellationToken);
 }
Example #32
0
		/// <summary>
		/// Asynchronously establish a connection to the specified mail server.
		/// </summary>
		/// <remarks>
		/// Asynchronously establishes a connection to the specified mail server.
		/// </remarks>
		/// <returns>An asynchronous task context.</returns>
		/// <param name="host">The host name to connect to.</param>
		/// <param name="port">The port to connect to. If the specified port is <c>0</c>, then the default port will be used.</param>
		/// <param name="options">The secure socket options to when connecting.</param>
		/// <param name="cancellationToken">The cancellation token.</param>
		/// <exception cref="System.ArgumentNullException">
		/// <paramref name="host"/> is <c>null</c>.
		/// </exception>
		/// <exception cref="System.ArgumentOutOfRangeException">
		/// <paramref name="port"/> is not between <c>0</c> and <c>65535</c>.
		/// </exception>
		/// <exception cref="System.ArgumentException">
		/// The <paramref name="host"/> is a zero-length string.
		/// </exception>
		/// <exception cref="System.ObjectDisposedException">
		/// The <see cref="MailService"/> has been disposed.
		/// </exception>
		/// <exception cref="System.InvalidOperationException">
		/// The <see cref="MailService"/> is already connected.
		/// </exception>
		/// <exception cref="System.OperationCanceledException">
		/// The operation was canceled via the cancellation token.
		/// </exception>
		/// <exception cref="System.IO.IOException">
		/// An I/O error occurred.
		/// </exception>
		/// <exception cref="ProtocolException">
		/// A protocol error occurred.
		/// </exception>
		public virtual Task ConnectAsync (string host, int port = 0, SecureSocketOptions options = SecureSocketOptions.Auto, CancellationToken cancellationToken = default (CancellationToken))
		{
			if (host == null)
				throw new ArgumentNullException ("host");

			if (host.Length == 0)
				throw new ArgumentException ("The host name cannot be empty.", "host");

			if (port < 0 || port > 65535)
				throw new ArgumentOutOfRangeException ("port");

			return Task.Factory.StartNew (() => {
				lock (SyncRoot) {
					Connect (host, port, options, cancellationToken);
				}
			}, cancellationToken, TaskCreationOptions.None, TaskScheduler.Default);
		}
Example #33
0
		/// <summary>
		/// Establish a connection to the specified mail server.
		/// </summary>
		/// <remarks>
		/// Establishes a connection to the specified mail server.
		/// </remarks>
		/// <example>
		/// <code language="c#" source="Examples\SmtpExamples.cs" region="SendMessage"/>
		/// </example>
		/// <param name="host">The host name to connect to.</param>
		/// <param name="port">The port to connect to. If the specified port is <c>0</c>, then the default port will be used.</param>
		/// <param name="options">The secure socket options to when connecting.</param>
		/// <param name="cancellationToken">The cancellation token.</param>
		/// <exception cref="System.ArgumentNullException">
		/// <paramref name="host"/> is <c>null</c>.
		/// </exception>
		/// <exception cref="System.ArgumentOutOfRangeException">
		/// <paramref name="port"/> is not between <c>0</c> and <c>65535</c>.
		/// </exception>
		/// <exception cref="System.ArgumentException">
		/// The <paramref name="host"/> is a zero-length string.
		/// </exception>
		/// <exception cref="System.ObjectDisposedException">
		/// The <see cref="MailService"/> has been disposed.
		/// </exception>
		/// <exception cref="System.InvalidOperationException">
		/// The <see cref="MailService"/> is already connected.
		/// </exception>
		/// <exception cref="System.OperationCanceledException">
		/// The operation was canceled via the cancellation token.
		/// </exception>
		/// <exception cref="System.IO.IOException">
		/// An I/O error occurred.
		/// </exception>
		/// <exception cref="ProtocolException">
		/// A protocol error occurred.
		/// </exception>
		public abstract void Connect (string host, int port = 0, SecureSocketOptions options = SecureSocketOptions.Auto, CancellationToken cancellationToken = default (CancellationToken));
 public EmailSendService(string userName, string password, string host, int port = 25, SecureSocketOptions secureSocketOptions = SecureSocketOptions.None)
 {
     _smtpClient = new();
     _smtpClient.Connect(host, port, secureSocketOptions);
     _smtpClient.Authenticate(userName, password);
 }
Example #35
0
		/// <summary>
		/// Establish a connection to the specified IMAP server.
		/// </summary>
		/// <remarks>
		/// <para>Establishes a connection to the specified IMAP or IMAP/S server.</para>
		/// <para>If the <paramref name="port"/> has a value of <c>0</c>, then the
		/// <paramref name="options"/> parameter is used to determine the default port to
		/// connect to. The default port used with <see cref="SecureSocketOptions.SslOnConnect"/>
		/// is <c>993</c>. All other values will use a default port of <c>143</c>.</para>
		/// <para>If the <paramref name="options"/> has a value of
		/// <see cref="SecureSocketOptions.Auto"/>, then the <paramref name="port"/> is used
		/// to determine the default security options. If the <paramref name="port"/> has a value
		/// of <c>993</c>, then the default options used will be
		/// <see cref="SecureSocketOptions.SslOnConnect"/>. All other values will use
		/// <see cref="SecureSocketOptions.StartTlsWhenAvailable"/>.</para>
		/// <para>Once a connection is established, properties such as
		/// <see cref="AuthenticationMechanisms"/> and <see cref="Capabilities"/> will be
		/// populated.</para>
		/// </remarks>
		/// <example>
		/// <code language="c#" source="Examples\ImapExamples.cs" region="DownloadMessages"/>
		/// </example>
		/// <param name="host">The host name to connect to.</param>
		/// <param name="port">The port to connect to. If the specified port is <c>0</c>, then the default port will be used.</param>
		/// <param name="options">The secure socket options to when connecting.</param>
		/// <param name="cancellationToken">The cancellation token.</param>
		/// <exception cref="System.ArgumentNullException">
		/// <paramref name="host"/> is <c>null</c>.
		/// </exception>
		/// <exception cref="System.ArgumentOutOfRangeException">
		/// <paramref name="port"/> is not between <c>0</c> and <c>65535</c>.
		/// </exception>
		/// <exception cref="System.ArgumentException">
		/// The <paramref name="host"/> is a zero-length string.
		/// </exception>
		/// <exception cref="System.ObjectDisposedException">
		/// The <see cref="ImapClient"/> has been disposed.
		/// </exception>
		/// <exception cref="System.InvalidOperationException">
		/// The <see cref="ImapClient"/> is already connected.
		/// </exception>
		/// <exception cref="System.NotSupportedException">
		/// <paramref name="options"/> was set to
		/// <see cref="MailKit.Security.SecureSocketOptions.StartTls"/>
		/// and the IMAP server does not support the STARTTLS extension.
		/// </exception>
		/// <exception cref="System.OperationCanceledException">
		/// The operation was canceled via the cancellation token.
		/// </exception>
		/// <exception cref="System.IO.IOException">
		/// An I/O error occurred.
		/// </exception>
		/// <exception cref="ImapProtocolException">
		/// An IMAP protocol error occurred.
		/// </exception>
		public override void Connect (string host, int port = 0, SecureSocketOptions options = SecureSocketOptions.Auto, CancellationToken cancellationToken = default (CancellationToken))
		{
			if (host == null)
				throw new ArgumentNullException ("host");

			if (host.Length == 0)
				throw new ArgumentException ("The host name cannot be empty.", "host");

			if (port < 0 || port > 65535)
				throw new ArgumentOutOfRangeException ("port");

			CheckDisposed ();

			if (IsConnected)
				throw new InvalidOperationException ("The ImapClient is already connected.");

			Stream stream;
			bool starttls;
			Uri uri;

			ComputeDefaultValues (host, ref port, ref options, out uri, out starttls);

#if !NETFX_CORE
			var ipAddresses = Dns.GetHostAddresses (host);
			Socket socket = null;

			for (int i = 0; i < ipAddresses.Length; i++) {
				socket = new Socket (ipAddresses[i].AddressFamily, SocketType.Stream, ProtocolType.Tcp);

				try {
					cancellationToken.ThrowIfCancellationRequested ();
					socket.Connect (ipAddresses[i], port);
					break;
				} catch (OperationCanceledException) {
					socket.Dispose ();
					throw;
				} catch {
					socket.Dispose ();

					if (i + 1 == ipAddresses.Length)
						throw;
				}
			}

			if (socket == null)
				throw new IOException (string.Format ("Failed to resolve host: {0}", host));
			
			engine.Uri = uri;

			if (options == SecureSocketOptions.SslOnConnect) {
				var ssl = new SslStream (new NetworkStream (socket, true), false, ValidateRemoteCertificate);
				ssl.AuthenticateAsClient (host, ClientCertificates, DefaultSslProtocols, true);
				stream = ssl;
			} else {
				stream = new NetworkStream (socket, true);
			}
#else
			var protection = options == SecureSocketOptions.SslOnConnect ? SocketProtectionLevel.Tls12 : SocketProtectionLevel.PlainSocket;
			socket = new StreamSocket ();

			try {
				cancellationToken.ThrowIfCancellationRequested ();
				socket.ConnectAsync (new HostName (host), port.ToString (), protection)
					.AsTask (cancellationToken)
					.GetAwaiter ()
					.GetResult ();
			} catch {
				socket.Dispose ();
				socket = null;
				throw;
			}

			stream = new DuplexStream (socket.InputStream.AsStreamForRead (0), socket.OutputStream.AsStreamForWrite (0));
			engine.Uri = uri;
#endif

			if (stream.CanTimeout) {
				stream.WriteTimeout = timeout;
				stream.ReadTimeout = timeout;
			}

			ProtocolLogger.LogConnect (uri);

			engine.Connect (new ImapStream (stream, socket, ProtocolLogger), cancellationToken);

			try {
				// Only query the CAPABILITIES if the greeting didn't include them.
				if (engine.CapabilitiesVersion == 0)
					engine.QueryCapabilities (cancellationToken);
				
				if (options == SecureSocketOptions.StartTls && (engine.Capabilities & ImapCapabilities.StartTLS) == 0)
					throw new NotSupportedException ("The IMAP server does not support the STARTTLS extension.");
				
				if (starttls && (engine.Capabilities & ImapCapabilities.StartTLS) != 0) {
					var ic = engine.QueueCommand (cancellationToken, null, "STARTTLS\r\n");

					engine.Wait (ic);

					if (ic.Response == ImapCommandResponse.Ok) {
#if !NETFX_CORE
						var tls = new SslStream (stream, false, ValidateRemoteCertificate);
						tls.AuthenticateAsClient (host, ClientCertificates, DefaultSslProtocols, true);
						engine.Stream.Stream = tls;
#else
						socket.UpgradeToSslAsync (SocketProtectionLevel.Tls12, new HostName (host))
							.AsTask (cancellationToken)
							.GetAwaiter ()
							.GetResult ();
#endif

						// 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 (options == SecureSocketOptions.StartTls) {
						throw ImapCommandException.Create ("STARTTLS", ic);
					}
				}
			} catch {
				engine.Disconnect ();
				throw;
			}

			engine.Disconnected += OnEngineDisconnected;
			OnConnected ();
		}
Example #36
0
		/// <summary>
		/// Establish a connection to the specified SMTP or SMTP/S server using the provided socket.
		/// </summary>
		/// <remarks>
		/// <para>Establishes a connection to the specified SMTP or SMTP/S server.</para>
		/// <para>If the <paramref name="port"/> has a value of <c>0</c>, then the
		/// <paramref name="options"/> parameter is used to determine the default port to
		/// connect to. The default port used with <see cref="SecureSocketOptions.SslOnConnect"/>
		/// is <c>465</c>. All other values will use a default port of <c>25</c>.</para>
		/// <para>If the <paramref name="options"/> has a value of
		/// <see cref="SecureSocketOptions.Auto"/>, then the <paramref name="port"/> is used
		/// to determine the default security options. If the <paramref name="port"/> has a value
		/// of <c>465</c>, then the default options used will be
		/// <see cref="SecureSocketOptions.SslOnConnect"/>. All other values will use
		/// <see cref="SecureSocketOptions.StartTlsWhenAvailable"/>.</para>
		/// <para>Once a connection is established, properties such as
		/// <see cref="AuthenticationMechanisms"/> and <see cref="Capabilities"/> will be
		/// populated.</para>
		/// <para>Note: The connection established by any of the
		/// <a href="Overload_MailKit_Net_Smtp_SmtpClient_Connect.htm">Connect</a>
		/// methods may be re-used if an application wishes to send multiple messages
		/// to the same SMTP server. Since connecting and authenticating can be expensive
		/// operations, re-using a connection can significantly improve performance when
		/// sending a large number of messages to the same SMTP server over a short
		/// period of time.</para>
		/// </remarks>
		/// <param name="socket">The socket to use for the connection.</param>
		/// <param name="host">The host name to connect to.</param>
		/// <param name="port">The port to connect to. If the specified port is <c>0</c>, then the default port will be used.</param>
		/// <param name="options">The secure socket options to when connecting.</param>
		/// <param name="cancellationToken">The cancellation token.</param>
		/// <exception cref="System.ArgumentNullException">
		/// <para><paramref name="socket"/> is <c>null</c>.</para>
		/// <para>-or-</para>
		/// <para><paramref name="host"/> is <c>null</c>.</para>
		/// </exception>
		/// <exception cref="System.ArgumentOutOfRangeException">
		/// <paramref name="port"/> is not between <c>0</c> and <c>65535</c>.
		/// </exception>
		/// <exception cref="System.ArgumentException">
		/// <para><paramref name="socket"/> is not connected.</para>
		/// <para>-or-</para>
		/// The <paramref name="host"/> is a zero-length string.
		/// </exception>
		/// <exception cref="System.ObjectDisposedException">
		/// The <see cref="SmtpClient"/> has been disposed.
		/// </exception>
		/// <exception cref="System.InvalidOperationException">
		/// The <see cref="SmtpClient"/> is already connected.
		/// </exception>
		/// <exception cref="System.NotSupportedException">
		/// <paramref name="options"/> was set to
		/// <see cref="MailKit.Security.SecureSocketOptions.StartTls"/>
		/// and the SMTP server does not support the STARTTLS extension.
		/// </exception>
		/// <exception cref="System.OperationCanceledException">
		/// The operation was canceled.
		/// </exception>
		/// <exception cref="System.IO.IOException">
		/// An I/O error occurred.
		/// </exception>
		/// <exception cref="SmtpCommandException">
		/// An SMTP command failed.
		/// </exception>
		/// <exception cref="SmtpProtocolException">
		/// An SMTP protocol error occurred.
		/// </exception>
		public void Connect (Socket socket, string host, int port = 0, SecureSocketOptions options = SecureSocketOptions.Auto, CancellationToken cancellationToken = default (CancellationToken))
		{
			if (socket == null)
				throw new ArgumentNullException ("socket");

			if (!socket.Connected)
				throw new ArgumentException ("The socket is not connected.", "socket");

			if (host == null)
				throw new ArgumentNullException ("host");

			if (host.Length == 0)
				throw new ArgumentException ("The host name cannot be empty.", "host");

			if (port < 0 || port > 65535)
				throw new ArgumentOutOfRangeException ("port");

			CheckDisposed ();

			if (IsConnected)
				throw new InvalidOperationException ("The SmtpClient is already connected.");

			capabilities = SmtpCapabilities.None;
			AuthenticationMechanisms.Clear ();
			MaxSize = 0;

			SmtpResponse response;
			Stream stream;
			bool starttls;
			Uri uri;

			ComputeDefaultValues (host, ref port, ref options, out uri, out starttls);

			this.host = host;

			if (options == SecureSocketOptions.SslOnConnect) {
				var ssl = new SslStream (new NetworkStream (socket, true), false, ValidateRemoteCertificate);
				ssl.AuthenticateAsClient (host, ClientCertificates, SslProtocols, true);
				stream = ssl;
			} else {
				stream = new NetworkStream (socket, true);
			}

			if (stream.CanTimeout) {
				stream.WriteTimeout = timeout;
				stream.ReadTimeout = timeout;
			}

			ProtocolLogger.LogConnect (uri);

			Stream = new SmtpStream (stream, socket, ProtocolLogger);

			try {
				// read the greeting
				response = Stream.ReadResponse (cancellationToken);

				if (response.StatusCode != SmtpStatusCode.ServiceReady)
					throw new SmtpCommandException (SmtpErrorCode.UnexpectedStatusCode, response.StatusCode, response.Response);

				// Send EHLO and get a list of supported extensions
				Ehlo (cancellationToken);

				if (options == SecureSocketOptions.StartTls && (capabilities & SmtpCapabilities.StartTLS) == 0)
					throw new NotSupportedException ("The SMTP server does not support the STARTTLS extension.");

				if (starttls && (capabilities & SmtpCapabilities.StartTLS) != 0) {
					response = SendCommand ("STARTTLS", cancellationToken);
					if (response.StatusCode != SmtpStatusCode.ServiceReady)
						throw new SmtpCommandException (SmtpErrorCode.UnexpectedStatusCode, response.StatusCode, response.Response);

					var tls = new SslStream (stream, false, ValidateRemoteCertificate);
					tls.AuthenticateAsClient (host, ClientCertificates, SslProtocols, true);
					Stream.Stream = tls;

					// Send EHLO again and get the new list of supported extensions
					Ehlo (cancellationToken);
				}

				connected = true;
			} catch {
				Stream.Dispose ();
				Stream = null;
				throw;
			}

			OnConnected ();
		}
Example #37
0
		/// <summary>
		/// Establishes a connection to the specified SMTP or SMTP/S server.
		/// </summary>
		/// <remarks>
		/// <para>Establishes a connection to the specified SMTP or SMTP/S server.</para>
		/// <para>If the <paramref name="port"/> has a value of <c>0</c>, then the
		/// <paramref name="options"/> parameter is used to determine the default port to
		/// connect to. The default port used with <see cref="SecureSocketOptions.SslOnConnect"/>
		/// is <c>465</c>. All other values will use a default port of <c>25</c>.</para>
		/// <para>If the <paramref name="options"/> has a value of
		/// <see cref="SecureSocketOptions.Auto"/>, then the <paramref name="port"/> is used
		/// to determine the default security options. If the <paramref name="port"/> has a value
		/// of <c>465</c>, then the default options used will be
		/// <see cref="SecureSocketOptions.SslOnConnect"/>. All other values will use
		/// <see cref="SecureSocketOptions.StartTlsWhenAvailable"/>.</para>
		/// <para>Once a connection is established, properties such as
		/// <see cref="AuthenticationMechanisms"/> and <see cref="Capabilities"/> will be
		/// populated.</para>
		/// <para>Note: The connection established by any of the
		/// <a href="Overload_MailKit_Net_Smtp_SmtpClient_Connect.htm">Connect</a>
		/// methods may be re-used if an application wishes to send multiple messages
		/// to the same SMTP server. Since connecting and authenticating can be expensive
		/// operations, re-using a connection can significantly improve performance when
		/// sending a large number of messages to the same SMTP server over a short
		/// period of time.</para>
		/// </remarks>
		/// <example>
		/// <code language="c#" source="Examples\SmtpExamples.cs" region="SendMessage"/>
		/// </example>
		/// <param name="host">The host name to connect to.</param>
		/// <param name="port">The port to connect to. If the specified port is <c>0</c>, then the default port will be used.</param>
		/// <param name="options">The secure socket options to when connecting.</param>
		/// <param name="cancellationToken">The cancellation token.</param>
		/// <exception cref="System.ArgumentNullException">
		/// <paramref name="host"/> is <c>null</c>.
		/// </exception>
		/// <exception cref="System.ArgumentOutOfRangeException">
		/// <paramref name="port"/> is not between <c>0</c> and <c>65535</c>.
		/// </exception>
		/// <exception cref="System.ArgumentException">
		/// The <paramref name="host"/> is a zero-length string.
		/// </exception>
		/// <exception cref="System.ObjectDisposedException">
		/// The <see cref="SmtpClient"/> has been disposed.
		/// </exception>
		/// <exception cref="System.InvalidOperationException">
		/// The <see cref="SmtpClient"/> is already connected.
		/// </exception>
		/// <exception cref="System.NotSupportedException">
		/// <paramref name="options"/> was set to
		/// <see cref="MailKit.Security.SecureSocketOptions.StartTls"/>
		/// and the SMTP server does not support the STARTTLS extension.
		/// </exception>
		/// <exception cref="System.OperationCanceledException">
		/// The operation was canceled.
		/// </exception>
		/// <exception cref="System.IO.IOException">
		/// An I/O error occurred.
		/// </exception>
		/// <exception cref="SmtpCommandException">
		/// An SMTP command failed.
		/// </exception>
		/// <exception cref="SmtpProtocolException">
		/// An SMTP protocol error occurred.
		/// </exception>
		public override void Connect (string host, int port = 0, SecureSocketOptions options = SecureSocketOptions.Auto, CancellationToken cancellationToken = default (CancellationToken))
		{
			if (host == null)
				throw new ArgumentNullException ("host");

			if (host.Length == 0)
				throw new ArgumentException ("The host name cannot be empty.", "host");

			if (port < 0 || port > 65535)
				throw new ArgumentOutOfRangeException ("port");
			
			CheckDisposed ();

			if (IsConnected)
				throw new InvalidOperationException ("The SmtpClient is already connected.");

			capabilities = SmtpCapabilities.None;
			AuthenticationMechanisms.Clear ();
			MaxSize = 0;

			SmtpResponse response;
			Stream stream;
			bool starttls;
			Uri uri;

			ComputeDefaultValues (host, ref port, ref options, out uri, out starttls);

#if !NETFX_CORE
			var ipAddresses = Dns.GetHostAddresses (host);
			Socket socket = null;

			for (int i = 0; i < ipAddresses.Length; i++) {
				socket = new Socket (ipAddresses[i].AddressFamily, SocketType.Stream, ProtocolType.Tcp);

				try {
					cancellationToken.ThrowIfCancellationRequested ();

					if (LocalEndPoint != null)
						socket.Bind (LocalEndPoint);

					socket.Connect (ipAddresses[i], port);
					break;
				} catch (OperationCanceledException) {
					socket.Dispose ();
					socket = null;
					throw;
				} catch {
					socket.Dispose ();
					socket = null;

					if (i + 1 == ipAddresses.Length)
						throw;
				}
			}

			if (socket == null)
				throw new IOException (string.Format ("Failed to resolve host: {0}", host));

			this.host = host;

			if (options == SecureSocketOptions.SslOnConnect) {
				var ssl = new SslStream (new NetworkStream (socket, true), false, ValidateRemoteCertificate);
				ssl.AuthenticateAsClient (host, ClientCertificates, SslProtocols, true);
				stream = ssl;
			} else {
				stream = new NetworkStream (socket, true);
			}
#else
			var protection = options == SecureSocketOptions.SslOnConnect ? SocketProtectionLevel.Tls12 : SocketProtectionLevel.PlainSocket;
			var socket = new StreamSocket ();

			try {
				cancellationToken.ThrowIfCancellationRequested ();
				socket.ConnectAsync (new HostName (host), port.ToString (), protection)
					.AsTask (cancellationToken)
					.GetAwaiter ()
					.GetResult ();
			} catch {
				socket.Dispose ();
				throw;
			}

			stream = new DuplexStream (socket.InputStream.AsStreamForRead (0), socket.OutputStream.AsStreamForWrite (0));
#endif

			if (stream.CanTimeout) {
				stream.WriteTimeout = timeout;
				stream.ReadTimeout = timeout;
			}

			ProtocolLogger.LogConnect (uri);

			Stream = new SmtpStream (stream, socket, ProtocolLogger);

			try {
				// read the greeting
				response = Stream.ReadResponse (cancellationToken);

				if (response.StatusCode != SmtpStatusCode.ServiceReady)
					throw new SmtpCommandException (SmtpErrorCode.UnexpectedStatusCode, response.StatusCode, response.Response);

				// Send EHLO and get a list of supported extensions
				Ehlo (cancellationToken);

				if (options == SecureSocketOptions.StartTls && (capabilities & SmtpCapabilities.StartTLS) == 0)
					throw new NotSupportedException ("The SMTP server does not support the STARTTLS extension.");

				if (starttls && (capabilities & SmtpCapabilities.StartTLS) != 0) {
					response = SendCommand ("STARTTLS", cancellationToken);
					if (response.StatusCode != SmtpStatusCode.ServiceReady)
						throw new SmtpCommandException (SmtpErrorCode.UnexpectedStatusCode, response.StatusCode, response.Response);

#if !NETFX_CORE
					var tls = new SslStream (stream, false, ValidateRemoteCertificate);
					tls.AuthenticateAsClient (host, ClientCertificates, SslProtocols, true);
					Stream.Stream = tls;
#else
					socket.UpgradeToSslAsync (SocketProtectionLevel.Tls12, new HostName (host))
						.AsTask (cancellationToken)
						.GetAwaiter ()
						.GetResult ();
#endif

					// Send EHLO again and get the new list of supported extensions
					Ehlo (cancellationToken);
				}

				connected = true;
			} catch {
				Stream.Dispose ();
				Stream = null;
				throw;
			}

			OnConnected ();
		}
Example #38
0
		static void ComputeDefaultValues (string host, ref int port, ref SecureSocketOptions options, out Uri uri, out bool starttls)
		{
			switch (options) {
			default:
				if (port == 0)
					port = 25;
				break;
			case SecureSocketOptions.Auto:
				switch (port) {
				case 0: port = 25; goto default;
				case 465: options = SecureSocketOptions.SslOnConnect; break;
				default: options = SecureSocketOptions.StartTlsWhenAvailable; break;
				}
				break;
			case SecureSocketOptions.SslOnConnect:
				if (port == 0)
					port = 465;
				break;
			}

			switch (options) {
			case SecureSocketOptions.StartTlsWhenAvailable:
				uri = new Uri ("smtp://" + host + ":" + port + "/?starttls=when-available");
				starttls = true;
				break;
			case SecureSocketOptions.StartTls:
				uri = new Uri ("smtp://" + host + ":" + port + "/?starttls=always");
				starttls = true;
				break;
			case SecureSocketOptions.SslOnConnect:
				uri = new Uri ("smtps://" + host + ":" + port);
				starttls = false;
				break;
			default:
				uri = new Uri ("smtp://" + host + ":" + port);
				starttls = false;
				break;
			}
		}
Example #39
0
		/// <summary>
		/// Establish a connection to the specified IMAP or IMAP/S server using the provided socket.
		/// </summary>
		/// <remarks>
		/// <para>Establishes a connection to the specified IMAP or IMAP/S server using
		/// the provided socket.</para>
		/// <para>If the <paramref name="port"/> has a value of <c>0</c>, then the
		/// <paramref name="options"/> parameter is used to determine the default port to
		/// connect to. The default port used with <see cref="SecureSocketOptions.SslOnConnect"/>
		/// is <c>993</c>. All other values will use a default port of <c>143</c>.</para>
		/// <para>If the <paramref name="options"/> has a value of
		/// <see cref="SecureSocketOptions.Auto"/>, then the <paramref name="port"/> is used
		/// to determine the default security options. If the <paramref name="port"/> has a value
		/// of <c>993</c>, then the default options used will be
		/// <see cref="SecureSocketOptions.SslOnConnect"/>. All other values will use
		/// <see cref="SecureSocketOptions.StartTlsWhenAvailable"/>.</para>
		/// <para>Once a connection is established, properties such as
		/// <see cref="AuthenticationMechanisms"/> and <see cref="Capabilities"/> will be
		/// populated.</para>
		/// </remarks>
		/// <param name="socket">The socket to use for the connection.</param>
		/// <param name="host">The host name to connect to.</param>
		/// <param name="port">The port to connect to. If the specified port is <c>0</c>, then the default port will be used.</param>
		/// <param name="options">The secure socket options to when connecting.</param>
		/// <param name="cancellationToken">The cancellation token.</param>
		/// <exception cref="System.ArgumentNullException">
		/// <para><paramref name="socket"/> is <c>null</c>.</para>
		/// <para>-or-</para>
		/// <para><paramref name="host"/> is <c>null</c>.</para>
		/// </exception>
		/// <exception cref="System.ArgumentOutOfRangeException">
		/// <paramref name="port"/> is not between <c>0</c> and <c>65535</c>.
		/// </exception>
		/// <exception cref="System.ArgumentException">
		/// <para><paramref name="socket"/> is not connected.</para>
		/// <para>-or-</para>
		/// The <paramref name="host"/> is a zero-length string.
		/// </exception>
		/// <exception cref="System.ObjectDisposedException">
		/// The <see cref="ImapClient"/> has been disposed.
		/// </exception>
		/// <exception cref="System.InvalidOperationException">
		/// The <see cref="ImapClient"/> is already connected.
		/// </exception>
		/// <exception cref="System.NotSupportedException">
		/// <paramref name="options"/> was set to
		/// <see cref="MailKit.Security.SecureSocketOptions.StartTls"/>
		/// and the IMAP server does not support the STARTTLS extension.
		/// </exception>
		/// <exception cref="System.OperationCanceledException">
		/// The operation was canceled via the cancellation token.
		/// </exception>
		/// <exception cref="System.IO.IOException">
		/// An I/O error occurred.
		/// </exception>
		/// <exception cref="ImapProtocolException">
		/// An IMAP protocol error occurred.
		/// </exception>
		public void Connect (Socket socket, string host, int port = 0, SecureSocketOptions options = SecureSocketOptions.Auto, CancellationToken cancellationToken = default (CancellationToken))
		{
			if (socket == null)
				throw new ArgumentNullException ("socket");

			if (!socket.Connected)
				throw new ArgumentException ("The socket is not connected.", "socket");

			if (host == null)
				throw new ArgumentNullException ("host");

			if (host.Length == 0)
				throw new ArgumentException ("The host name cannot be empty.", "host");

			if (port < 0 || port > 65535)
				throw new ArgumentOutOfRangeException ("port");

			CheckDisposed ();

			if (IsConnected)
				throw new InvalidOperationException ("The ImapClient is already connected.");
			
			Stream stream;
			bool starttls;
			Uri uri;

			ComputeDefaultValues (host, ref port, ref options, out uri, out starttls);

			engine.Uri = uri;

			if (options == SecureSocketOptions.SslOnConnect) {
				var ssl = new SslStream (new NetworkStream (socket, true), false, ValidateRemoteCertificate);
				ssl.AuthenticateAsClient (host, ClientCertificates, DefaultSslProtocols, true);
				stream = ssl;
			} else {
				stream = new NetworkStream (socket, true);
			}

			if (stream.CanTimeout) {
				stream.WriteTimeout = timeout;
				stream.ReadTimeout = timeout;
			}

			ProtocolLogger.LogConnect (uri);

			engine.Connect (new ImapStream (stream, socket, ProtocolLogger), cancellationToken);

			try {
				// Only query the CAPABILITIES if the greeting didn't include them.
				if (engine.CapabilitiesVersion == 0)
					engine.QueryCapabilities (cancellationToken);
				
				if (options == SecureSocketOptions.StartTls && (engine.Capabilities & ImapCapabilities.StartTLS) == 0)
					throw new NotSupportedException ("The IMAP server does not support the STARTTLS extension.");
				
				if (starttls && (engine.Capabilities & ImapCapabilities.StartTLS) != 0) {
					var ic = engine.QueueCommand (cancellationToken, null, "STARTTLS\r\n");

					engine.Wait (ic);

					if (ic.Response == ImapCommandResponse.Ok) {
						var tls = new SslStream (stream, false, ValidateRemoteCertificate);
						tls.AuthenticateAsClient (host, ClientCertificates, DefaultSslProtocols, 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 (options == SecureSocketOptions.StartTls) {
						throw ImapCommandException.Create ("STARTTLS", ic);
					}
				}
			} catch {
				engine.Disconnect ();
				throw;
			}

			engine.Disconnected += OnEngineDisconnected;
			OnConnected ();
		}