Esempio n. 1
0
        void Ehlo(CancellationToken cancellationToken)
        {
            SmtpResponse response;

            response = SendEhlo (true, cancellationToken);

            // Some SMTP servers do not accept an EHLO after authentication (despite the rfc saying it is required).
            if (response.StatusCode == SmtpStatusCode.BadCommandSequence && capabilities != SmtpCapabilities.None)
                return;

            if (response.StatusCode != SmtpStatusCode.Ok) {
                // Try sending HELO instead...
                response = SendEhlo (false, cancellationToken);
                if (response.StatusCode != SmtpStatusCode.Ok)
                    throw new SmtpCommandException (SmtpErrorCode.UnexpectedStatusCode, response.StatusCode, response.Response);
            } else {
                // Clear the extensions
                capabilities = SmtpCapabilities.None;
                authmechs.Clear();
                MaxSize = 0;

                var lines = response.Response.Split ('\n');
                for (int i = 0; i < lines.Length; i++) {
                    var capability = lines[i].Trim ();

                    if (capability.StartsWith ("AUTH", StringComparison.Ordinal)) {
                        int index = 4;

                        capabilities |= SmtpCapabilities.Authentication;

                        if (index < capability.Length && capability[index] == '=')
                            index++;

                        var mechanisms = capability.Substring (index);
                        foreach (var mechanism in mechanisms.Split (new [] { ' ' }, StringSplitOptions.RemoveEmptyEntries))
                            authmechs.Add (mechanism);
                    } else if (capability.StartsWith ("SIZE", StringComparison.Ordinal)) {
                        int index = 4;
                        uint size;

                        capabilities |= SmtpCapabilities.Size;

                        while (index < capability.Length && char.IsWhiteSpace (capability[index]))
                            index++;

                        if (uint.TryParse (capability.Substring (index), out size))
                            MaxSize = size;
                    } else if (capability == "BINARYMIME") {
                        capabilities |= SmtpCapabilities.BinaryMime;
                    } else if (capability == "CHUNKING") {
                        capabilities |= SmtpCapabilities.Chunking;
                    } else if (capability == "ENHANCEDSTATUSCODES") {
                        capabilities |= SmtpCapabilities.EnhancedStatusCodes;
                    } else if (capability == "8BITMIME") {
                        capabilities |= SmtpCapabilities.EightBitMime;
                    } else if (capability == "PIPELINING") {
                        capabilities |= SmtpCapabilities.Pipelining;
                    } else if (capability == "STARTTLS") {
                        capabilities |= SmtpCapabilities.StartTLS;
                    } else if (capability == "SMTPUTF8") {
                        capabilities |= SmtpCapabilities.UTF8;
                    }
                }
            }
        }
Esempio n. 2
0
        /// <summary>
        /// Establishes a connection to the specified SMTP server.
        /// </summary>
        /// <remarks>
        /// <para>Establishes a connection to an SMTP or SMTP/S server. If the schema
        /// in the uri is "smtp", a clear-text connection is made and defaults to using
        /// port 25 if no port is specified in the URI. However, if the schema in the
        /// uri is "smtps", an SSL connection is made using the
        /// <see cref="ClientCertificates"/> and defaults to port 465 unless a port
        /// is specified in the URI.</para>
        /// <para>It should be noted that when using a clear-text SMTP connection,
        /// if the server advertizes support for the STARTTLS extension, the client
        /// will automatically switch into TLS mode before authenticating unless the
        /// <paramref name="uri"/> contains a query string to disable it.</para>
        /// If a successful connection is made, the <see cref="AuthenticationMechanisms"/>
        /// and <see cref="Capabilities"/> properties will be populated.
        /// </remarks>
        /// <param name="uri">The server URI. The <see cref="System.Uri.Scheme"/> should either
        /// be "smtp" to make a clear-text connection or "smtps" to make an SSL connection.</param>
        /// <param name="cancellationToken">A cancellation token.</param>
        /// <exception cref="System.ArgumentNullException">
        /// <para>The <paramref name="uri"/> is <c>null</c>.</para>
        /// </exception>
        /// <exception cref="System.ArgumentException">
        /// The <paramref name="uri"/> is not an absolute URI.
        /// </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.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(Uri uri, CancellationToken cancellationToken = default (CancellationToken))
        {
            CheckDisposed ();

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

            if (!uri.IsAbsoluteUri)
                throw new ArgumentException ("The uri must be absolute.", "uri");

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

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

            var smtps = uri.Scheme.ToLowerInvariant () == "smtps";
            var port = uri.Port > 0 ? uri.Port : (smtps ? 465 : 25);
            var query = uri.ParsedQuery ();
            SmtpResponse response = null;
            string value;

            var starttls = !smtps && (!query.TryGetValue ("starttls", out value) || Convert.ToBoolean (value));

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

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

                cancellationToken.ThrowIfCancellationRequested ();

                try {
                    socket.Connect (ipAddresses[i], port);
                    localEndPoint = socket.LocalEndPoint;
                    break;
                } catch {
                    if (i + 1 == ipAddresses.Length)
                        throw;
                }
            }

            if (smtps) {
                var ssl = new SslStream (new NetworkStream (socket, true), false, ValidateRemoteCertificate);
                ssl.AuthenticateAsClient (uri.Host, ClientCertificates, SslProtocols.Default, true);
                stream = ssl;
            } else {
                stream = new NetworkStream (socket, true);
            }
            #else
            socket = new StreamSocket ();

            cancellationToken.ThrowIfCancellationRequested ();
            socket.ConnectAsync (new HostName (uri.DnsSafeHost), port.ToString (), smtps ? SocketProtectionLevel.Tls12 : SocketProtectionLevel.PlainSocket)
                .AsTask (cancellationToken)
                .GetAwaiter ()
                .GetResult ();

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

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

            logger.LogConnect (uri);

            try {
                // read the greeting
                response = 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 (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 (uri.Host, ClientCertificates, SslProtocols.Tls, true);
                    stream = tls;
            #else
                    socket.UpgradeToSslAsync (SocketProtectionLevel.Tls12, new HostName (uri.DnsSafeHost))
                        .AsTask (cancellationToken)
                        .GetAwaiter ()
                        .GetResult ();
            #endif

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

                IsConnected = true;
            } catch {
                stream.Dispose ();
                stream = null;
                throw;
            }
        }
Esempio n. 3
0
        internal void ReplayConnect(string hostName, Stream replayStream, CancellationToken cancellationToken)
        {
            CheckDisposed ();

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

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

            #if !NETFX_CORE
            localEndPoint = new IPEndPoint (IPAddress.Loopback, 25);
            #endif
            capabilities = SmtpCapabilities.None;
            stream = replayStream;
            authmechs.Clear ();
            host = hostName;
            MaxSize = 0;

            try {
                // read the greeting
                var response = 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);

                IsConnected = true;
            } catch {
                stream.Dispose ();
                stream = null;
                throw;
            }
        }
Esempio n. 4
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 ();
		}
Esempio n. 5
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 ();
		}
Esempio n. 6
0
		internal void ReplayConnect (string hostName, Stream replayStream, CancellationToken cancellationToken = default (CancellationToken))
		{
			CheckDisposed ();

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

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

			Stream = new SmtpStream (replayStream, null, ProtocolLogger);
			capabilities = SmtpCapabilities.None;
			AuthenticationMechanisms.Clear ();
			host = hostName;
			MaxSize = 0;

			try {
				// read the greeting
				var 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);

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

			OnConnected ();
		}
Esempio n. 7
0
			public ContentTransferEncodingVisitor (SmtpCapabilities capabilities)
			{
				Capabilities = capabilities;
			}
Esempio n. 8
0
		void Disconnect ()
		{
			capabilities = SmtpCapabilities.None;
			authenticated = false;
			connected = false;
			secure = false;
			host = null;

			if (Stream != null) {
				Stream.Dispose ();
				Stream = null;
			}

			OnDisconnected ();
		}
Esempio n. 9
0
        public Task <MailSentStatus> Send(EMailEnvelop EMailEnvelop,
                                          Byte NumberOfRetries = 3,
                                          Boolean AutoStart    = true)
        {
            var SendMailTask = new Task <MailSentStatus>(() => {
                lock (this) {
                    switch (Connect())
                    {
                    case Sockets.TCP.TCPConnectResult.InvalidDomainName:
                        return(MailSentStatus.failed);

                    case Sockets.TCP.TCPConnectResult.NoIPAddressFound:
                        return(MailSentStatus.failed);

                    case Sockets.TCP.TCPConnectResult.UnknownError:
                        return(MailSentStatus.failed);

                    case Sockets.TCP.TCPConnectResult.Ok:

                        // 220 mail.ahzf.de ESMTP Postfix (Debian/GNU)
                        var LoginResponse = this.ReadSMTPResponse();
                        if (LoginResponse.StatusCode != SMTPStatusCode.ServiceReady)
                        {
                            throw new SMTPClientException("SMTP login error: " + LoginResponse.ToString());
                        }

                        switch (LoginResponse.StatusCode)
                        {
                        case SMTPStatusCode.ServiceReady:

                            #region Send EHLO

                            var EHLOResponses = SendCommandAndWaits("EHLO " + LocalDomain);

                            // 250-mail.ahzf.de
                            // 250-PIPELINING
                            // 250-SIZE 30720000
                            // 250-VRFY
                            // 250-ETRN
                            // 250-STARTTLS
                            // 250-AUTH LOGIN DIGEST-MD5 PLAIN CRAM-MD5
                            // 250-AUTH=LOGIN DIGEST-MD5 PLAIN CRAM-MD5
                            // 250-ENHANCEDSTATUSCODES
                            // 250-8BITMIME
                            // 250 DSN

                            if (EHLOResponses.Any(v => v.StatusCode != SMTPStatusCode.Ok))
                            {
                                var Error = EHLOResponses.Where(v => v.StatusCode != SMTPStatusCode.Ok).
                                            FirstOrDefault();

                                if (Error.StatusCode != SMTPStatusCode.Ok)
                                {
                                    throw new SMTPClientException("SMTP EHLO command error: " + Error.ToString());
                                }
                            }

                            #endregion

                            #region Check for STARTTLS

                            if (UseTLS == TLSUsage.STARTTLS)
                            {
                                if (EHLOResponses.Any(v => v.Response == "STARTTLS"))
                                {
                                    var StartTLSResponse = SendCommandAndWait("STARTTLS");

                                    if (StartTLSResponse.StatusCode == SMTPStatusCode.ServiceReady)
                                    {
                                        EnableTLS();
                                    }
                                }

                                else
                                {
                                    throw new Exception("TLS is not supported by the SMTP server!");
                                }

                                // Send EHLO again in order to get the new list of supported extensions!
                                EHLOResponses = SendCommandAndWaits("EHLO " + LocalDomain);
                            }

                            #endregion

                            #region Analyze EHLO responses and set SMTP capabilities

                            // 250-mail.ahzf.de
                            // 250-PIPELINING
                            // 250-SIZE 30720000
                            // 250-VRFY
                            // 250-ETRN
                            // 250-STARTTLS
                            // 250-AUTH LOGIN DIGEST-MD5 PLAIN CRAM-MD5
                            // 250-AUTH=LOGIN DIGEST-MD5 PLAIN CRAM-MD5
                            // 250-ENHANCEDSTATUSCODES
                            // 250-8BITMIME
                            // 250 DSN

                            var MailServerName = EHLOResponses.FirstOrDefault();

                            EHLOResponses.Skip(1).ForEach(v => {
                                #region PIPELINING

                                if (v.Response == "PIPELINING")
                                {
                                    Capabilities |= SmtpCapabilities.Pipelining;
                                }

                                #endregion

                                #region SIZE

                                else if (v.Response.StartsWith("SIZE "))
                                {
                                    Capabilities |= SmtpCapabilities.Size;

                                    if (!UInt64.TryParse(v.Response.Substring(5), out _MaxMailSize))
                                    {
                                        throw new Exception("Invalid SIZE capability!");
                                    }
                                }

                                #endregion

                                //   else if (v.Response == "VRFY")
                                //       Capabilities |= SmtpCapabilities.;

                                //   else if (v.Response == "ETRN")
                                //       Capabilities |= SmtpCapabilities.;

                                #region STARTTLS

                                if (v.Response == "STARTTLS")
                                {
                                    Capabilities |= SmtpCapabilities.StartTLS;
                                }

                                #endregion

                                #region AUTH

                                else if (v.Response.StartsWith("AUTH "))
                                {
                                    Capabilities |= SmtpCapabilities.Authentication;

                                    SMTPAuthMethods ParsedAuthMethod;
                                    var AuthType    = v.Response.Substring(4, 1);
                                    var AuthMethods = v.Response.Substring(5).Split(' ');

                                    // GMail: "AUTH LOGIN PLAIN XOAUTH XOAUTH2 PLAIN-CLIENTTOKEN"
                                    foreach (var AuthMethod in AuthMethods)
                                    {
                                        if (Enum.TryParse <SMTPAuthMethods>(AuthMethod.Replace('-', '_'), true, out ParsedAuthMethod))
                                        {
                                            if (AuthType == " ")
                                            {
                                                _AuthMethods |= ParsedAuthMethod;
                                            }
                                        }
                                        else
                                        {
                                            _UnknownAuthMethods.Add(AuthMethod);
                                        }
                                    }
                                }

                                #endregion

                                #region ENHANCEDSTATUSCODES

                                if (v.Response == "ENHANCEDSTATUSCODES")
                                {
                                    Capabilities |= SmtpCapabilities.EnhancedStatusCodes;
                                }

                                #endregion

                                #region 8BITMIME

                                if (v.Response == "8BITMIME")
                                {
                                    Capabilities |= SmtpCapabilities.EightBitMime;
                                }

                                #endregion

                                #region DSN

                                if (v.Response == "DSN")
                                {
                                    Capabilities |= SmtpCapabilities.Dsn;
                                }

                                #endregion

                                #region BINARYMIME

                                if (v.Response == "BINARYMIME")
                                {
                                    Capabilities |= SmtpCapabilities.BinaryMime;
                                }

                                #endregion

                                #region CHUNKING

                                if (v.Response == "CHUNKING")
                                {
                                    Capabilities |= SmtpCapabilities.Chunking;
                                }

                                #endregion

                                #region UTF8

                                if (v.Response == "UTF8")
                                {
                                    Capabilities |= SmtpCapabilities.UTF8;
                                }

                                #endregion
                            });

                            #endregion

                            #region Auth PLAIN...

                            if (_AuthMethods.HasFlag(SMTPAuthMethods.PLAIN))
                            {
                                var AuthPLAINResponse = SendCommandAndWait("AUTH PLAIN " +
                                                                           Convert.ToBase64String(ByteZero.
                                                                                                  Concat(_Login.ToUTF8Bytes()).
                                                                                                  Concat(ByteZero).
                                                                                                  Concat(_Password.ToUTF8Bytes()).
                                                                                                  ToArray()));
                            }

                            #endregion

                            #region ...or Auth LOGIN...

                            else if (_AuthMethods.HasFlag(SMTPAuthMethods.LOGIN))
                            {
                                var AuthLOGIN1Response = SendCommandAndWait("AUTH LOGIN");
                                var AuthLOGIN2Response = SendCommandAndWait(Convert.ToBase64String(_Login.ToUTF8Bytes()));
                                var AuthLOGIN3Response = SendCommandAndWait(Convert.ToBase64String(_Password.ToUTF8Bytes()));
                            }

                            #endregion

                            #region ...or AUTH CRAM-MD5

                            else if (_AuthMethods.HasFlag(SMTPAuthMethods.CRAM_MD5))
                            {
                                var AuthCRAMMD5Response = SendCommandAndWait("AUTH CRAM-MD5");

                                if (AuthCRAMMD5Response.StatusCode == SMTPStatusCode.AuthenticationChallenge)
                                {
                                    var aa = Ext2.CRAM_MD5("<*****@*****.**>",
                                                           "tim", "tanstaaftanstaaf");
                                    var a2 = Convert.ToBase64String(aa) == "dGltIGI5MTNhNjAyYzdlZGE3YTQ5NWI0ZTZlNzMzNGQzODkw";


                                    var bb = Ext2.CRAM_MD5("<*****@*****.**>",
                                                           "alice", "wonderland");
                                    var b2 = Convert.ToBase64String(bb) == "YWxpY2UgNjRiMmE0M2MxZjZlZDY4MDZhOTgwOTE0ZTIzZTc1ZjA=";

                                    var cc = Ext2.CRAM_MD5("<*****@*****.**>", "ahzf", "ahzf2305!");

                                    var zz = Ext2.CRAM_MD5(Convert.FromBase64String(AuthCRAMMD5Response.Response).ToUTF8String(),
                                                           _Login, _Password);

                                    var AuthPLAINResponse = SendCommandAndWait(Convert.ToBase64String(zz));
                                }
                            }

                            #endregion

                            #region MAIL FROM:

                            foreach (var MailFrom in EMailEnvelop.MailFrom)
                            {
                                // MAIL FROM:<*****@*****.**>
                                /// 250 2.1.0 Ok
                                var MailFromCommand = "MAIL FROM: <" + MailFrom.Address.ToString() + ">";

                                if (Capabilities.HasFlag(SmtpCapabilities.EightBitMime))
                                {
                                    MailFromCommand += " BODY=8BITMIME";
                                }
                                else if (Capabilities.HasFlag(SmtpCapabilities.BinaryMime))
                                {
                                    MailFromCommand += " BODY=BINARYMIME";
                                }

                                var _MailFromResponse = SendCommandAndWait(MailFromCommand);
                                if (_MailFromResponse.StatusCode != SMTPStatusCode.Ok)
                                {
                                    throw new SMTPClientException("SMTP MAIL FROM command error: " + _MailFromResponse.ToString());
                                }
                            }

                            #endregion

                            #region RCPT TO(s):

                            // RCPT TO:<*****@*****.**>
                            /// 250 2.1.5 Ok
                            EMailEnvelop.RcptTo.ForEach(Rcpt => {
                                var _RcptToResponse = SendCommandAndWait("RCPT TO: <" + Rcpt.Address.ToString() + ">");

                                switch (_RcptToResponse.StatusCode)
                                {
                                case SMTPStatusCode.UserNotLocalWillForward:
                                case SMTPStatusCode.Ok:
                                    break;

                                case SMTPStatusCode.UserNotLocalTryAlternatePath:
                                case SMTPStatusCode.MailboxNameNotAllowed:
                                case SMTPStatusCode.MailboxUnavailable:
                                case SMTPStatusCode.MailboxBusy:
                                //    throw new SmtpCommandException(SmtpErrorCode.RecipientNotAccepted, _RcptToResponse.StatusCode, mailbox, _RcptToResponse.Response);

                                case SMTPStatusCode.AuthenticationRequired:
                                    throw new UnauthorizedAccessException(_RcptToResponse.Response);

                                    //default:
                                    //    throw new SmtpCommandException(SmtpErrorCode.UnexpectedStatusCode, _RcptToResponse.StatusCode, _RcptToResponse.Response);
                                }

                                //Debug.WriteLine(_RcptToResponse);
                            });

                            #endregion

                            #region Mail DATA

                            // The encoded MIME text lines must not be longer than 76 characters!

                            /// 354 End data with <CR><LF>.<CR><LF>
                            var _DataResponse = SendCommandAndWait("DATA");
                            if (_DataResponse.StatusCode != SMTPStatusCode.StartMailInput)
                            {
                                throw new SMTPClientException("SMTP DATA command error: " + _DataResponse.ToString());
                            }

                            // Send e-mail headers...
                            if (EMailEnvelop.Mail != null)
                            {
                                EMailEnvelop.Mail.
                                Header.
                                Select(header => header.Key + ": " + header.Value).
                                ForEach(line => SendCommand(line));

                                //SendCommand("Message-Id: <" + (EMailEnvelop.Mail.MessageId != null
                                //                                    ? EMailEnvelop.Mail.MessageId.ToString()
                                //                                    : GenerateMessageId(EMailEnvelop.Mail, RemoteHost).ToString()) + ">");

                                SendCommand("");

                                // Send e-mail body(parts)...
                                //if (EMailEnvelop.Mail.MailBody != null)
                                //{
                                EMailEnvelop.Mail.Body.ToText(false).ForEach(line => SendCommand(line));
                                SendCommand("");
                                //}
                            }

                            else if (EMailEnvelop.Mail.ToText != null)
                            {
                                EMailEnvelop.Mail.ToText.ForEach(line => SendCommand(line));
                                SendCommand("");
                            }

                            #endregion

                            #region End-of-DATA

                            /// .
                            /// 250 2.0.0 Ok: queued as 83398728027
                            var _FinishedResponse = SendCommandAndWait(".");
                            if (_FinishedResponse.StatusCode != SMTPStatusCode.Ok)
                            {
                                throw new SMTPClientException("SMTP DATA '.' command error: " + _FinishedResponse.ToString());
                            }

                            #endregion

                            #region QUIT

                            /// QUIT
                            /// 221 2.0.0 Bye
                            var _QuitResponse = SendCommandAndWait("QUIT");
                            if (_QuitResponse.StatusCode != SMTPStatusCode.ServiceClosingTransmissionChannel)
                            {
                                throw new SMTPClientException("SMTP QUIT command error: " + _QuitResponse.ToString());
                            }

                            #endregion

                            break;
                        }

                        return(MailSentStatus.ok);

                    default:
                        return(MailSentStatus.failed);
                    }
                }
            });

            if (AutoStart)
            {
                SendMailTask.Start();
            }

            return(SendMailTask);
        }
Esempio n. 10
0
        public Task<MailSentStatus> Send(EMailEnvelop  EMailEnvelop,
                                         Byte          NumberOfRetries  = 3,
                                         Boolean       AutoStart        = true)
        {
            var SendMailTask = new Task<MailSentStatus>(() => {

                lock (this) {

                switch (Connect())
                {

                    case Sockets.TCP.TCPConnectResult.InvalidDomainName:
                        return MailSentStatus.failed;

                    case Sockets.TCP.TCPConnectResult.NoIPAddressFound:
                        return MailSentStatus.failed;

                    case Sockets.TCP.TCPConnectResult.UnknownError:
                        return MailSentStatus.failed;

                    case Sockets.TCP.TCPConnectResult.Ok:

                        // 220 mail.ahzf.de ESMTP Postfix (Debian/GNU)
                        var LoginResponse = this.ReadSMTPResponse();
                        if (LoginResponse.StatusCode != SMTPStatusCode.ServiceReady)
                            throw new SMTPClientException("SMTP login error: " + LoginResponse.ToString());

                        switch (LoginResponse.StatusCode)
                        {

                            case SMTPStatusCode.ServiceReady:

                            #region Send EHLO

                            var EHLOResponses = SendCommandAndWaits("EHLO " + LocalDomain);

                            // 250-mail.ahzf.de
                            // 250-PIPELINING
                            // 250-SIZE 30720000
                            // 250-VRFY
                            // 250-ETRN
                            // 250-STARTTLS
                            // 250-AUTH LOGIN DIGEST-MD5 PLAIN CRAM-MD5
                            // 250-AUTH=LOGIN DIGEST-MD5 PLAIN CRAM-MD5
                            // 250-ENHANCEDSTATUSCODES
                            // 250-8BITMIME
                            // 250 DSN

                            if (EHLOResponses.Any(v => v.StatusCode != SMTPStatusCode.Ok))
                            {

                                var Error = EHLOResponses.Where(v => v.StatusCode != SMTPStatusCode.Ok).
                                                          FirstOrDefault();

                                if (Error.StatusCode != SMTPStatusCode.Ok)
                                    throw new SMTPClientException("SMTP EHLO command error: " + Error.ToString());

                            }

                            #endregion

                            #region Check for STARTTLS

                            if (UseTLS == TLSUsage.STARTTLS)
                            {

                                if (EHLOResponses.Any(v => v.Response == "STARTTLS"))
                                {

                                    var StartTLSResponse = SendCommandAndWait("STARTTLS");

                                    if (StartTLSResponse.StatusCode == SMTPStatusCode.ServiceReady)
                                        EnableTLS();

                                }

                                else
                                    throw new Exception("TLS is not supported by the SMTP server!");

                                // Send EHLO again in order to get the new list of supported extensions!
                                EHLOResponses = SendCommandAndWaits("EHLO " + LocalDomain);

                            }

                            #endregion

                            #region Analyze EHLO responses and set SMTP capabilities

                            // 250-mail.ahzf.de
                            // 250-PIPELINING
                            // 250-SIZE 30720000
                            // 250-VRFY
                            // 250-ETRN
                            // 250-STARTTLS
                            // 250-AUTH LOGIN DIGEST-MD5 PLAIN CRAM-MD5
                            // 250-AUTH=LOGIN DIGEST-MD5 PLAIN CRAM-MD5
                            // 250-ENHANCEDSTATUSCODES
                            // 250-8BITMIME
                            // 250 DSN

                            var MailServerName = EHLOResponses.FirstOrDefault();

                            EHLOResponses.Skip(1).ForEach(v => {

                                #region PIPELINING

                                if      (v.Response == "PIPELINING")
                                    Capabilities |= SmtpCapabilities.Pipelining;

                                #endregion

                                #region SIZE

                                else if (v.Response.StartsWith("SIZE "))
                                {

                                    Capabilities |= SmtpCapabilities.Size;

                                    if (!UInt64.TryParse(v.Response.Substring(5), out _MaxMailSize))
                                        throw new Exception("Invalid SIZE capability!");

                                }

                                #endregion

                             //   else if (v.Response == "VRFY")
                             //       Capabilities |= SmtpCapabilities.;

                             //   else if (v.Response == "ETRN")
                             //       Capabilities |= SmtpCapabilities.;

                                #region STARTTLS

                                if (v.Response == "STARTTLS")
                                    Capabilities |= SmtpCapabilities.StartTLS;

                                #endregion

                                #region AUTH

                                else if (v.Response.StartsWith("AUTH "))
                                {

                                    Capabilities |= SmtpCapabilities.Authentication;

                                    SMTPAuthMethods ParsedAuthMethod;
                                    var AuthType            = v.Response.Substring(4, 1);
                                    var AuthMethods         = v.Response.Substring(5).Split(' ');

                                    // GMail: "AUTH LOGIN PLAIN XOAUTH XOAUTH2 PLAIN-CLIENTTOKEN"
                                    foreach (var AuthMethod in AuthMethods)
                                    {

                                        if (Enum.TryParse<SMTPAuthMethods>(AuthMethod.Replace('-', '_'), true, out ParsedAuthMethod))
                                        {
                                            if (AuthType == " ")
                                                _AuthMethods |= ParsedAuthMethod;
                                        }
                                        else
                                            _UnknownAuthMethods.Add(AuthMethod);

                                    }

                                }

                                #endregion

                                #region ENHANCEDSTATUSCODES

                                if (v.Response == "ENHANCEDSTATUSCODES")
                                    Capabilities |= SmtpCapabilities.EnhancedStatusCodes;

                                #endregion

                                #region 8BITMIME

                                if (v.Response == "8BITMIME")
                                    Capabilities |= SmtpCapabilities.EightBitMime;

                                #endregion

                                #region DSN

                                if (v.Response == "DSN")
                                    Capabilities |= SmtpCapabilities.Dsn;

                                #endregion

                                #region BINARYMIME

                                if (v.Response == "BINARYMIME")
                                    Capabilities |= SmtpCapabilities.BinaryMime;

                                #endregion

                                #region CHUNKING

                                if (v.Response == "CHUNKING")
                                    Capabilities |= SmtpCapabilities.Chunking;

                                #endregion

                                #region UTF8

                                if (v.Response == "UTF8")
                                    Capabilities |= SmtpCapabilities.UTF8;

                                #endregion

                            });

                            #endregion

                            #region Auth PLAIN...

                            if (_AuthMethods.HasFlag(SMTPAuthMethods.PLAIN))
                            {

                                var AuthPLAINResponse  = SendCommandAndWait("AUTH PLAIN " +
                                                                            Convert.ToBase64String(ByteZero.
                                                                                                   Concat(_Login.   ToUTF8Bytes()).
                                                                                                   Concat(ByteZero).
                                                                                                   Concat(_Password.ToUTF8Bytes()).
                                                                                                   ToArray()));

                            }

                            #endregion

                            #region ...or Auth LOGIN...

                            else if (_AuthMethods.HasFlag(SMTPAuthMethods.LOGIN))
                            {

                                var AuthLOGIN1Response  = SendCommandAndWait("AUTH LOGIN");
                                var AuthLOGIN2Response  = SendCommandAndWait(Convert.ToBase64String(_Login.   ToUTF8Bytes()));
                                var AuthLOGIN3Response  = SendCommandAndWait(Convert.ToBase64String(_Password.ToUTF8Bytes()));

                            }

                            #endregion

                            #region ...or AUTH CRAM-MD5

                            else if (_AuthMethods.HasFlag(SMTPAuthMethods.CRAM_MD5))
                            {

                                var AuthCRAMMD5Response = SendCommandAndWait("AUTH CRAM-MD5");

                                if (AuthCRAMMD5Response.StatusCode == SMTPStatusCode.AuthenticationChallenge)
                                {

                                    var aa = Ext2.CRAM_MD5("<*****@*****.**>",
                                                           "tim", "tanstaaftanstaaf");
                                    var a2 = Convert.ToBase64String(aa) == "dGltIGI5MTNhNjAyYzdlZGE3YTQ5NWI0ZTZlNzMzNGQzODkw";

                                    var bb = Ext2.CRAM_MD5("<*****@*****.**>",
                                                           "alice", "wonderland");
                                    var b2 = Convert.ToBase64String(bb) == "YWxpY2UgNjRiMmE0M2MxZjZlZDY4MDZhOTgwOTE0ZTIzZTc1ZjA=";

                                    var cc = Ext2.CRAM_MD5("<*****@*****.**>", "ahzf", "ahzf2305!");

                                    var zz = Ext2.CRAM_MD5(Convert.FromBase64String(AuthCRAMMD5Response.Response).ToUTF8String(),
                                                           _Login, _Password);

                                    var AuthPLAINResponse = SendCommandAndWait(Convert.ToBase64String(zz));

                                }

                            }

                            #endregion

                            #region MAIL FROM:

                            foreach (var MailFrom in EMailEnvelop.MailFrom)
                            {

                                // MAIL FROM:<*****@*****.**>
                                /// 250 2.1.0 Ok
                                var MailFromCommand = "MAIL FROM: <" + MailFrom.Address.ToString() + ">";

                                if      (Capabilities.HasFlag(SmtpCapabilities.EightBitMime))
                                    MailFromCommand += " BODY=8BITMIME";
                                else if (Capabilities.HasFlag(SmtpCapabilities.BinaryMime))
                                    MailFromCommand += " BODY=BINARYMIME";

                                var _MailFromResponse = SendCommandAndWait(MailFromCommand);
                                if (_MailFromResponse.StatusCode != SMTPStatusCode.Ok)
                                    throw new SMTPClientException("SMTP MAIL FROM command error: " + _MailFromResponse.ToString());

                            }

                            #endregion

                            #region RCPT TO(s):

                            // RCPT TO:<*****@*****.**>
                            /// 250 2.1.5 Ok
                            EMailEnvelop.RcptTo.ForEach(Rcpt => {

                                var _RcptToResponse = SendCommandAndWait("RCPT TO: <" + Rcpt.Address.ToString() + ">");

                                switch (_RcptToResponse.StatusCode)
                                {

                                    case SMTPStatusCode.UserNotLocalWillForward:
                                    case SMTPStatusCode.Ok:
                                        break;

                                    case SMTPStatusCode.UserNotLocalTryAlternatePath:
                                    case SMTPStatusCode.MailboxNameNotAllowed:
                                    case SMTPStatusCode.MailboxUnavailable:
                                    case SMTPStatusCode.MailboxBusy:
                                    //    throw new SmtpCommandException(SmtpErrorCode.RecipientNotAccepted, _RcptToResponse.StatusCode, mailbox, _RcptToResponse.Response);

                                    case SMTPStatusCode.AuthenticationRequired:
                                        throw new UnauthorizedAccessException(_RcptToResponse.Response);

                                    //default:
                                    //    throw new SmtpCommandException(SmtpErrorCode.UnexpectedStatusCode, _RcptToResponse.StatusCode, _RcptToResponse.Response);

                                }

                                //Debug.WriteLine(_RcptToResponse);

                            });

                            #endregion

                            #region Mail DATA

                            // The encoded MIME text lines must not be longer than 76 characters!

                            /// 354 End data with <CR><LF>.<CR><LF>
                            var _DataResponse = SendCommandAndWait("DATA");
                            if (_DataResponse.StatusCode != SMTPStatusCode.StartMailInput)
                                throw new SMTPClientException("SMTP DATA command error: " + _DataResponse.ToString());

                            // Send e-mail headers...
                            if (EMailEnvelop.Mail != null)
                            {

                                EMailEnvelop.Mail.
                                             Header.
                                             Select(header => header.Key + ": " + header.Value).
                                             ForEach(line => SendCommand(line));

                                //SendCommand("Message-Id: <" + (EMailEnvelop.Mail.MessageId != null
                                //                                    ? EMailEnvelop.Mail.MessageId.ToString()
                                //                                    : GenerateMessageId(EMailEnvelop.Mail, RemoteHost).ToString()) + ">");

                                SendCommand("");

                                // Send e-mail body(parts)...
                                //if (EMailEnvelop.Mail.MailBody != null)
                                //{
                                    EMailEnvelop.Mail.Body.ToText(false).ForEach(line => SendCommand(line));
                                    SendCommand("");
                                //}

                            }

                            else if (EMailEnvelop.Mail.ToText != null)
                            {
                                EMailEnvelop.Mail.ToText.ForEach(line => SendCommand(line));
                                SendCommand("");
                            }

                            #endregion

                            #region End-of-DATA

                            /// .
                            /// 250 2.0.0 Ok: queued as 83398728027
                            var _FinishedResponse = SendCommandAndWait(".");
                            if (_FinishedResponse.StatusCode != SMTPStatusCode.Ok)
                                throw new SMTPClientException("SMTP DATA '.' command error: " + _FinishedResponse.ToString());

                            #endregion

                            #region QUIT

                            /// QUIT
                            /// 221 2.0.0 Bye
                            var _QuitResponse     = SendCommandAndWait("QUIT");
                            if (_QuitResponse.StatusCode != SMTPStatusCode.ServiceClosingTransmissionChannel)
                                throw new SMTPClientException("SMTP QUIT command error: " + _QuitResponse.ToString());

                            #endregion

                            break;

                        }

                        return MailSentStatus.ok;

                    default:
                        return MailSentStatus.failed;

                }}

            });

            if (AutoStart)
                SendMailTask.Start();

            return SendMailTask;
        }