예제 #1
0
        /// <summary>
        /// Sends the specified message to an SMTP server for delivery.
        /// </summary>
        /// <param name="message">An OpaqueMail.MailMessage that contains the message to send.</param>
        public async Task SendAsync(MailMessage message)
        {
            // If the message isn't encoded, do so now.
            if (string.IsNullOrEmpty(message.RawBody))
            {
                message.Prepare();
            }

            // Perform requested S/MIME signing and/or encryption.
            message.SmimePrepare(this);
            string rawBody = message.RawBody;

            // Connect to the SMTP server.
            TcpClient SmtpTcpClient = new TcpClient();

            SmtpTcpClient.Connect(Host, Port);
            Stream SmtpStream = SmtpTcpClient.GetStream();

            // Use stream readers and writers to simplify I/O.
            StreamReader reader = new StreamReader(SmtpStream);
            StreamWriter writer = new StreamWriter(SmtpStream);

            writer.AutoFlush = true;

            // Read the welcome message.
            string response = await reader.ReadLineAsync();

            // Send EHLO and find out server capabilities.
            await writer.WriteLineAsync("EHLO " + Host);

            char[] charBuffer = new char[Constants.SMALLBUFFERSIZE];
            int    bytesRead  = await reader.ReadAsync(charBuffer, 0, Constants.SMALLBUFFERSIZE);

            response = new string(charBuffer, 0, bytesRead);
            if (!response.StartsWith("2"))
            {
                throw new SmtpException("Unable to connect to remote server '" + Host + "'.  Sent 'EHLO' and received '" + response + "'.");
            }

            // Stand up a TLS/SSL stream.
            if (EnableSsl)
            {
                await writer.WriteLineAsync("STARTTLS");

                response = await reader.ReadLineAsync();

                if (!response.StartsWith("2"))
                {
                    throw new SmtpException("Unable to start TLS/SSL protection with '" + Host + "'.  Received '" + response + "'.");
                }

                SmtpStream = new SslStream(SmtpStream);
                ((SslStream)SmtpStream).AuthenticateAsClient(Host, null, SslProtocols, true);

                reader           = new StreamReader(SmtpStream);
                writer           = new StreamWriter(SmtpStream);
                writer.AutoFlush = true;

                await writer.WriteLineAsync("EHLO " + Host);

                bytesRead = await reader.ReadAsync(charBuffer, 0, Constants.SMALLBUFFERSIZE);

                response = new string(charBuffer, 0, bytesRead);
            }

            // Authenticate using the AUTH LOGIN command.
            if (Credentials != null)
            {
                NetworkCredential cred = (NetworkCredential)Credentials;
                await writer.WriteLineAsync("AUTH LOGIN");

                response = await reader.ReadLineAsync();

                if (!response.StartsWith("3"))
                {
                    throw new SmtpException("Unable to authenticate with server '" + Host + "'.  Received '" + response + "'.");
                }
                await writer.WriteLineAsync(Functions.ToBase64String(cred.UserName));

                response = await reader.ReadLineAsync();

                await writer.WriteLineAsync(Functions.ToBase64String(cred.Password));

                response = await reader.ReadLineAsync();

                if (!response.StartsWith("2"))
                {
                    throw new SmtpException("Unable to authenticate with server '" + Host + "'.  Received '" + response + "'.");
                }
            }

            // Build our raw headers block.
            StringBuilder rawHeaders = new StringBuilder(Constants.SMALLSBSIZE);

            // Specify who the message is from.
            rawHeaders.Append(Functions.SpanHeaderLines("From: " + Functions.EncodeMailHeader(Functions.ToMailAddressString(message.From))) + "\r\n");
            await writer.WriteLineAsync("MAIL FROM:<" + message.From.Address + ">");

            response = await reader.ReadLineAsync();

            if (!response.StartsWith("2"))
            {
                throw new SmtpException("Exception communicating with server '" + Host + "'.  Sent 'MAIL FROM' and received '" + response + "'.");
            }

            // Identify all recipients of the message.
            if (message.To.Count > 0)
            {
                rawHeaders.Append(Functions.SpanHeaderLines("To: " + Functions.EncodeMailHeader(message.To.ToString())) + "\r\n");
            }
            foreach (MailAddress address in message.To)
            {
                await writer.WriteLineAsync("RCPT TO:<" + address.Address + ">");

                response = await reader.ReadLineAsync();

                if (!response.StartsWith("2"))
                {
                    throw new SmtpException("Exception communicating with server '" + Host + "'.  Sent 'RCPT TO' and received '" + response + "'.");
                }
            }

            if (message.CC.Count > 0)
            {
                rawHeaders.Append(Functions.SpanHeaderLines("CC: " + Functions.EncodeMailHeader(message.CC.ToString())) + "\r\n");
            }
            foreach (MailAddress address in message.CC)
            {
                await writer.WriteLineAsync("RCPT TO:<" + address.Address + ">");

                response = await reader.ReadLineAsync();

                if (!response.StartsWith("2"))
                {
                    throw new SmtpException("Exception communicating with server '" + Host + "'.  Sent 'RCPT TO' and received '" + response + "'.");
                }
            }

            foreach (MailAddress address in message.Bcc)
            {
                await writer.WriteLineAsync("RCPT TO:<" + address.Address + ">");

                response = await reader.ReadLineAsync();

                if (!response.StartsWith("2"))
                {
                    throw new SmtpException("Exception communicating with server '" + Host + "'.  Sent 'RCPT TO' and received '" + response + "'.");
                }
            }

            // Ensure a content type is set.
            if (string.IsNullOrEmpty(message.ContentType))
            {
                if (Functions.AppearsHTML(message.Body))
                {
                    message.ContentType = "text/html";
                }
                else
                {
                    message.ContentType = "text/plain";
                }
            }
            message.Headers["Content-Type"] = message.ContentType + (!string.IsNullOrEmpty(message.CharSet) ? "; charset=\"" + message.CharSet + "\"" : "");

            // If the body hasn't been processed, handle encoding of extended characters.
            if (string.IsNullOrEmpty(rawBody) && !string.IsNullOrEmpty(message.Body))
            {
                bool extendedCharacterFound = false;
                foreach (char headerCharacter in message.Body.ToCharArray())
                {
                    if (headerCharacter > 127)
                    {
                        extendedCharacterFound = true;
                        break;
                    }
                }

                if (extendedCharacterFound)
                {
                    message.ContentTransferEncoding = "base64";
                    message.Body = Functions.ToBase64String(message.Body);
                }

                rawBody = message.Body;
            }

            if (!string.IsNullOrEmpty(message.ContentTransferEncoding))
            {
                message.Headers["Content-Transfer-Encoding"] = message.ContentTransferEncoding;
            }

            // Send the raw message.
            await writer.WriteLineAsync("DATA");

            response = await reader.ReadLineAsync();

            if (!response.StartsWith("3"))
            {
                throw new SmtpException("Exception communicating with server '" + Host + "'.  Sent 'DATA' and received '" + response + "'.");
            }

            rawHeaders.Append(Functions.SpanHeaderLines("Subject: " + Functions.EncodeMailHeader(message.Subject, 32)) + "\r\n");
            foreach (string rawHeader in message.Headers)
            {
                switch (rawHeader.ToUpper())
                {
                case "BCC":
                case "CC":
                case "FROM":
                case "SUBJECT":
                case "TO":
                    break;

                default:
                    rawHeaders.Append(Functions.SpanHeaderLines(rawHeader + ": " + message.Headers[rawHeader]) + "\r\n");
                    break;
                }
            }

            await writer.WriteAsync(rawHeaders.ToString() + "\r\n" + rawBody + "\r\n.\r\n");

            response = await reader.ReadLineAsync();

            if (!response.StartsWith("2"))
            {
                throw new SmtpException("Exception communicating with server '" + Host + "'.  Sent message and received '" + response + "'.");
            }

            // Clean up this connection.
            await writer.WriteLineAsync("QUIT");

            writer.Dispose();
            reader.Dispose();

            SmtpStream.Dispose();
            SmtpTcpClient.Close();
        }
예제 #2
0
        /// <summary>
        /// Sends the specified message to an SMTP server for delivery without making modifications to the body.
        /// Necessary because the standard SmtpClient.Send() may slightly alter messages, invalidating signatures.
        /// </summary>
        /// <param name="message">An OpaqueMail.MailMessage that contains the message to send.</param>
        private async Task SmimeSendRawAsync(MailMessage message)
        {
            // Connect to the SMTP server.
            TcpClient SmtpTcpClient = new TcpClient();

            SmtpTcpClient.Connect(Host, Port);
            Stream SmtpStream = SmtpTcpClient.GetStream();

            // Use stream readers and writers to simplify I/O.
            StreamReader reader = new StreamReader(SmtpStream);
            StreamWriter writer = new StreamWriter(SmtpStream);

            writer.AutoFlush = true;

            // Read the welcome message.
            string response = await reader.ReadLineAsync();

            // Send EHLO and find out server capabilities.
            await writer.WriteLineAsync("EHLO " + Host);

            char[] charBuffer = new char[Constants.SMALLBUFFERSIZE];
            int    bytesRead  = await reader.ReadAsync(charBuffer, 0, Constants.SMALLBUFFERSIZE);

            response = new string(charBuffer, 0, bytesRead);
            if (!response.StartsWith("2"))
            {
                throw new SmtpException("Unable to connect to remote server '" + Host + "'.  Sent 'EHLO' and received '" + response + "'.");
            }

            // Stand up a TLS/SSL stream.
            if (EnableSsl)
            {
                await writer.WriteLineAsync("STARTTLS");

                response = await reader.ReadLineAsync();

                if (!response.StartsWith("2"))
                {
                    throw new SmtpException("Unable to start TLS/SSL protection with '" + Host + "'.  Received '" + response + "'.");
                }

                SmtpStream = new SslStream(SmtpStream);
                ((SslStream)SmtpStream).AuthenticateAsClient(Host);

                reader           = new StreamReader(SmtpStream);
                writer           = new StreamWriter(SmtpStream);
                writer.AutoFlush = true;
            }

            // Authenticate using the AUTH LOGIN command.
            if (Credentials != null)
            {
                NetworkCredential cred = (NetworkCredential)Credentials;
                await writer.WriteLineAsync("AUTH LOGIN");

                response = await reader.ReadLineAsync();

                if (!response.StartsWith("3"))
                {
                    throw new SmtpException("Unable to authenticate with server '" + Host + "'.  Received '" + response + "'.");
                }
                await writer.WriteLineAsync(Functions.ToBase64String(cred.UserName));

                response = await reader.ReadLineAsync();

                await writer.WriteLineAsync(Functions.ToBase64String(cred.Password));

                response = await reader.ReadLineAsync();

                if (!response.StartsWith("2"))
                {
                    throw new SmtpException("Unable to authenticate with server '" + Host + "'.  Received '" + response + "'.");
                }
            }

            // Build our raw headers block.
            StringBuilder rawHeaders = new StringBuilder(Constants.SMALLSBSIZE);

            // Specify who the message is from.
            rawHeaders.Append(Functions.SpanHeaderLines("From: " + Functions.EncodeMailHeader(Functions.ToMailAddressString(message.From))) + "\r\n");
            await writer.WriteLineAsync("MAIL FROM:<" + message.From.Address + ">");

            response = await reader.ReadLineAsync();

            if (!response.StartsWith("2"))
            {
                throw new SmtpException("Exception communicating with server '" + Host + "'.  Sent 'MAIL FROM' and received '" + response + "'.");
            }

            // Identify all recipients of the message.
            if (message.To.Count > 0)
            {
                rawHeaders.Append(Functions.SpanHeaderLines("To: " + Functions.EncodeMailHeader(Functions.ToMailAddressString(message.To))) + "\r\n");
            }
            foreach (MailAddress address in message.To)
            {
                await writer.WriteLineAsync("RCPT TO:<" + address.Address + ">");

                response = await reader.ReadLineAsync();

                if (!response.StartsWith("2"))
                {
                    throw new SmtpException("Exception communicating with server '" + Host + "'.  Sent 'RCPT TO' and received '" + response + "'.");
                }
            }

            if (message.CC.Count > 0)
            {
                rawHeaders.Append(Functions.SpanHeaderLines("CC: " + Functions.EncodeMailHeader(Functions.ToMailAddressString(message.CC))) + "\r\n");
            }
            foreach (MailAddress address in message.CC)
            {
                await writer.WriteLineAsync("RCPT TO:<" + address.Address + ">");

                response = await reader.ReadLineAsync();

                if (!response.StartsWith("2"))
                {
                    throw new SmtpException("Exception communicating with server '" + Host + "'.  Sent 'RCPT TO' and received '" + response + "'.");
                }
            }

            foreach (MailAddress address in message.Bcc)
            {
                await writer.WriteLineAsync("RCPT TO:<" + address.Address + ">");

                response = await reader.ReadLineAsync();

                if (!response.StartsWith("2"))
                {
                    throw new SmtpException("Exception communicating with server '" + Host + "'.  Sent 'RCPT TO' and received '" + response + "'.");
                }
            }

            // Send the raw message.
            await writer.WriteLineAsync("DATA");

            response = await reader.ReadLineAsync();

            if (!response.StartsWith("3"))
            {
                throw new SmtpException("Exception communicating with server '" + Host + "'.  Sent 'DATA' and received '" + response + "'.");
            }

            // If a read-only mail message is passed in with its raw headers and body, save a few steps by sending that directly.
            if (message is ReadOnlyMailMessage)
            {
                await writer.WriteAsync(((ReadOnlyMailMessage)message).RawHeaders + "\r\n" + ((ReadOnlyMailMessage)message).RawBody + "\r\n.\r\n");
            }
            else
            {
                rawHeaders.Append(Functions.SpanHeaderLines("Subject: " + Functions.EncodeMailHeader(message.Subject)) + "\r\n");
                foreach (string rawHeader in message.Headers)
                {
                    switch (rawHeader.ToUpper())
                    {
                    case "BCC":
                    case "CC":
                    case "FROM":
                    case "SUBJECT":
                    case "TO":
                        break;

                    default:
                        rawHeaders.Append(Functions.SpanHeaderLines(rawHeader + ": " + message.Headers[rawHeader]) + "\r\n");
                        break;
                    }
                }

                await writer.WriteAsync(rawHeaders.ToString() + "\r\n" + message.Body + "\r\n.\r\n");
            }

            response = await reader.ReadLineAsync();

            if (!response.StartsWith("2"))
            {
                throw new SmtpException("Exception communicating with server '" + Host + "'.  Sent message and received '" + response + "'.");
            }

            // Clean up this connection.
            await writer.WriteLineAsync("QUIT");

            writer.Dispose();
            reader.Dispose();

            SmtpStream.Dispose();
            SmtpTcpClient.Close();
        }