示例#1
0
        /// <summary>
        /// Send the RCPT TO command to the server using <paramref name="rcptTo"/> as parameter.
        /// </summary>
        /// <param name="rcptTo">Email address to use as parameter.</param>
        /// <param name="failedCallback">Action to call if command fails.</param>
        public async Task <bool> ExecRcptToAsync(MailAddress rcptTo, Action <string> failedCallback)
        {
            if (!base.Connected)
            {
                return(false);
            }

            IsActive    = true;
            _LastActive = DateTime.UtcNow;
            await SmtpStream.WriteLineAsync("RCPT TO: <" + rcptTo.Address + ">");

            // If the remote MX doesn't support pipelining then wait and check the response.
            if (!_CanPipeline)
            {
                string response = await SmtpStream.ReadAllLinesAsync();

                if (!response.StartsWith("250"))
                {
                    failedCallback(response);
                }
            }

            _LastActive = DateTime.UtcNow;
            IsActive    = false;

            return(true);
        }
示例#2
0
        /// <summary>
        /// Send the MAIL FROM command to the server using <paramref name="mailFrom"/> as parameter.
        /// </summary>
        /// <param name="mailFrom">Email address to use as parameter.</param>
        /// <param name="failedCallback">Action to call if command fails.</param>
        public async Task <bool> ExecMailFromAsync(MailAddress mailFrom, Action <string> failedCallback)
        {
            if (!base.Connected)
            {
                return(false);
            }

            _LastActive = DateTime.UtcNow;
            IsActive    = true;
            await SmtpStream.WriteLineAsync("MAIL FROM: <" +
                                            (mailFrom == null ? string.Empty : mailFrom.Address) + ">" +
                                            (_DataTransportMime == SmtpTransportMIME._8BitUTF ? " BODY=8BITMIME" : string.Empty));

            // If the remote MX doesn't support pipelining then wait and check the response.
            if (!_CanPipeline)
            {
                string response = await SmtpStream.ReadAllLinesAsync();

                if (!response.StartsWith("250"))
                {
                    failedCallback(response);
                }
            }

            _LastActive = DateTime.UtcNow;
            IsActive    = false;

            return(true);
        }
示例#3
0
        /// <summary>
        /// Send the data to the server
        /// </summary>
        /// <param name="data">Data to send to the server</param>
        /// <param name="failedCallback">Action to call if fails to send.</param>
        private async Task <MantaOutboundClientSendResult> ExecDataAsync(string data)
        {
            await SmtpStream.WriteLineAsync("DATA");

            string response = await SmtpStream.ReadAllLinesAsync(); // Data response or Mail From if pipelining

            // If the remote MX supports pipelining then we need to check the MAIL FROM and RCPT to responses.
            if (_CanPipeline)
            {
                // Check MAIL FROM OK.
                if (!response.StartsWith("250"))
                {
                    await SmtpStream.ReadAllLinesAsync(); // RCPT TO

                    await SmtpStream.ReadAllLinesAsync(); // DATA

                    return(new MantaOutboundClientSendResult(MantaOutboundClientResult.RejectedByRemoteServer, response, _VirtualMta, _MXRecord));
                }


                // Check RCPT TO OK.
                response = await SmtpStream.ReadAllLinesAsync();

                if (!response.StartsWith("250"))
                {
                    await SmtpStream.ReadAllLinesAsync(); // DATA

                    return(new MantaOutboundClientSendResult(MantaOutboundClientResult.RejectedByRemoteServer, response, _VirtualMta, _MXRecord));
                }

                // Get the Data Command response.
                response = await SmtpStream.ReadAllLinesAsync();
            }

            if (!response.StartsWith("354"))
            {
                return(new MantaOutboundClientSendResult(MantaOutboundClientResult.RejectedByRemoteServer, response, _VirtualMta, _MXRecord));
            }

            // Send the message data using the correct transport MIME
            SmtpStream.SetSmtpTransportMIME(_DataTransportMime);
            await SmtpStream.WriteAsync(data, false);

            await SmtpStream.WriteAsync(MtaParameters.NewLine + "." + MtaParameters.NewLine, false);

            // Data done so return to 7-Bit mode.
            SmtpStream.SetSmtpTransportMIME(SmtpTransportMIME._7BitASCII);

            response = await SmtpStream.ReadAllLinesAsync();

            if (!response.StartsWith("250"))
            {
                return(new MantaOutboundClientSendResult(MantaOutboundClientResult.RejectedByRemoteServer, response, _VirtualMta, _MXRecord));
            }

            _MessagesAccepted++;
            return(new MantaOutboundClientSendResult(MantaOutboundClientResult.Success, response, _VirtualMta, _MXRecord));
        }
示例#4
0
 public void TestSeek()
 {
     using (var stream = new SmtpStream(new DummyNetworkStream(), new NullProtocolLogger())) {
         Assert.Throws <NotSupportedException> (() => stream.Seek(0, SeekOrigin.Begin));
         Assert.Throws <NotSupportedException> (() => stream.Position = 500);
         Assert.AreEqual(0, stream.Position);
         Assert.AreEqual(0, stream.Length);
     }
 }
示例#5
0
 public void TestCanReadWriteSeek()
 {
     using (var stream = new SmtpStream(new DummyNetworkStream(), new NullProtocolLogger())) {
         Assert.IsTrue(stream.CanRead);
         Assert.IsTrue(stream.CanWrite);
         Assert.IsFalse(stream.CanSeek);
         Assert.IsTrue(stream.CanTimeout);
     }
 }
示例#6
0
        public void TestRead()
        {
            using (var stream = new SmtpStream(new DummyNetworkStream(), new NullProtocolLogger())) {
                var buffer = new byte[16];

                Assert.Throws <NotImplementedException> (() => stream.Read(buffer, 0, buffer.Length));
                Assert.ThrowsAsync <NotImplementedException> (async() => await stream.ReadAsync(buffer, 0, buffer.Length));
            }
        }
示例#7
0
        public void TestGetSetTimeouts()
        {
            using (var stream = new SmtpStream(new DummyNetworkStream(), new NullProtocolLogger())) {
                stream.ReadTimeout = 5;
                Assert.AreEqual(5, stream.ReadTimeout, "ReadTimeout");

                stream.WriteTimeout = 7;
                Assert.AreEqual(7, stream.WriteTimeout, "WriteTimeout");
            }
        }
示例#8
0
        public void TestReadResponseInvalidResponseCode()
        {
            using (var stream = new SmtpStream(new DummyNetworkStream(), new NullProtocolLogger())) {
                var buffer = Encoding.ASCII.GetBytes("XXX This is an invalid response.\r\n");
                var dummy  = (MemoryStream)stream.Stream;

                dummy.Write(buffer, 0, buffer.Length);
                dummy.Position = 0;

                Assert.Throws <SmtpProtocolException> (() => stream.ReadResponse(CancellationToken.None));
            }
        }
示例#9
0
        public void TestReadResponseMismatchedResponseCodes()
        {
            using (var stream = new SmtpStream(new DummyNetworkStream(), new NullProtocolLogger())) {
                var buffer = Encoding.ASCII.GetBytes("250-This is the first line of a response.\r\n340 And this is a mismatched response code.\r\n");
                var dummy  = (MemoryStream)stream.Stream;

                dummy.Write(buffer, 0, buffer.Length);
                dummy.Position = 0;

                Assert.Throws <SmtpProtocolException> (() => stream.ReadResponse(CancellationToken.None));
            }
        }
示例#10
0
        /// <summary>
        /// Say EHLO/HELO to the server.
        /// Will also check to see if 8BITMIME is supported.
        /// </summary>
        /// <param name="failedCallback">Action to call if hello fail.</param>
        private async Task <MantaOutboundClientSendResult> ExecHeloAsync()
        {
            // We have connected to the MX, Say EHLO.
            await SmtpStream.WriteLineAsync("EHLO " + _VirtualMta.Hostname);

            string response = await SmtpStream.ReadAllLinesAsync();

            if (response.StartsWith("421"))
            {
                return(new MantaOutboundClientSendResult(MantaOutboundClientResult.ServiceNotAvalible, response, _VirtualMta, _MXRecord));
            }

            try
            {
                if (!response.StartsWith("2"))
                {
                    // If server didn't respond with a success code on hello then we should retry with HELO
                    await SmtpStream.WriteLineAsync("HELO " + _VirtualMta.Hostname);

                    response = await SmtpStream.ReadAllLinesAsync();

                    if (!response.StartsWith("250"))
                    {
                        TcpClient.Close();
                        return(new MantaOutboundClientSendResult(MantaOutboundClientResult.ServiceNotAvalible, response, _VirtualMta, _MXRecord));
                    }
                }
                else
                {
                    // Server responded to EHLO
                    // Check to see if it supports 8BITMIME
                    if (response.IndexOf("8BITMIME", StringComparison.OrdinalIgnoreCase) > -1)
                    {
                        _DataTransportMime = SmtpTransportMIME._8BitUTF;
                    }
                    else
                    {
                        _DataTransportMime = SmtpTransportMIME._7BitASCII;
                    }

                    // Check to see if the server supports pipelining
                    _CanPipeline = response.IndexOf("PIPELINING", StringComparison.OrdinalIgnoreCase) > -1;
                }
            }
            catch (IOException)
            {
                // Remote Endpoint Disconnected Mid HELO.
                return(new MantaOutboundClientSendResult(MantaOutboundClientResult.ServiceNotAvalible, response, _VirtualMta, _MXRecord));
            }

            return(new MantaOutboundClientSendResult(MantaOutboundClientResult.Success, null, _VirtualMta, _MXRecord));
        }
示例#11
0
        /// <summary>
        /// Send the RCPT TO command to the server using <paramref name="rcptTo"/> as parameter.
        /// </summary>
        /// <param name="rcptTo">Email address to use as parameter.</param>
        /// <param name="failedCallback">Action to call if command fails.</param>
        private async Task <MantaOutboundClientSendResult> ExecRcptToAsync(MailAddress rcptTo)
        {
            await SmtpStream.WriteLineAsync("RCPT TO: <" + rcptTo.Address + ">");

            // If the remote MX doesn't support pipelining then wait and check the response.
            if (!_CanPipeline)
            {
                string response = await SmtpStream.ReadAllLinesAsync();

                if (!response.StartsWith("250"))
                {
                    return(new MantaOutboundClientSendResult(MantaOutboundClientResult.RejectedByRemoteServer, response, _VirtualMta, _MXRecord));
                }
            }

            return(new MantaOutboundClientSendResult(MantaOutboundClientResult.Success, null, _VirtualMta, _MXRecord));
        }
示例#12
0
        /// <summary>
        /// Send the RSET command to the server.
        /// </summary>
        public async Task <bool> ExecRsetAsync()
        {
            if (!base.Connected)
            {
                Logging.Debug("Cannot RSET connection has been closed.");
                throw new Exception();
            }
            IsActive = true;
            await SmtpStream.WriteLineAsync("RSET");

            await SmtpStream.ReadAllLinesAsync();

            _LastActive = DateTime.UtcNow;
            IsActive    = false;

            return(true);
        }
示例#13
0
        public void TestReadResponseLatin1Fallback()
        {
            const string input    = "250-Wikipédia est un projet d'encyclopédie collective en ligne,\r\n250-universelle, multilingue et fonctionnant sur le principe du wiki.\r\n250-Ce projet vise à offrir un contenu librement réutilisable, objectif\r\n250 et vérifiable, que chacun peut modifier et améliorer.\r\n";
            var          expected = input.Replace("250-", "").Replace("250 ", "").Replace("\r\n", "\n").TrimEnd();

            using (var stream = new SmtpStream(new DummyNetworkStream(), new NullProtocolLogger())) {
                var buffer = Encoding.GetEncoding(28591).GetBytes(input);
                var dummy  = (MemoryStream)stream.Stream;

                dummy.Write(buffer, 0, buffer.Length);
                dummy.Position = 0;

                var response = stream.ReadResponse(CancellationToken.None);

                Assert.AreEqual(250, (int)response.StatusCode);
                Assert.AreEqual(expected, response.Response);
            }
        }
示例#14
0
        /// <summary>
        /// Send the SMTP Quit command to the Server.
        /// </summary>
        private async Task ExecQuitAsync()
        {
            if (TcpClient.Connected)
            {
                try
                {
                    await SmtpStream.WriteLineAsync("QUIT");

                    // Don't read response as don't care.
                    // Close the TCP connection.
                    TcpClient.GetStream().Close();
                    TcpClient.Close();
                }
                catch (ObjectDisposedException)
                {
                    Logging.Debug("SmtpOutboundClient: Tried to quit an already disposed client.");
                }
            }
        }
示例#15
0
        /// <summary>
        /// Send the RSET command to the server.
        /// </summary>
        private async Task <MantaOutboundClientSendResult> ExecRsetAsync()
        {
            if (!await SmtpStream.WriteLineAsync("RSET"))
            {
                throw new ObjectDisposedException("Connection");
            }

            var response = await SmtpStream.ReadAllLinesAsync();

            switch (response[0])
            {
            case '2':
                return(new MantaOutboundClientSendResult(MantaOutboundClientResult.Success, response, _VirtualMta, _MXRecord));

            case '4':
            case '5':
            default:
                return(new MantaOutboundClientSendResult(MantaOutboundClientResult.RejectedByRemoteServer, response, _VirtualMta, _MXRecord));
            }
        }
示例#16
0
        /// <summary>
        /// Send the SMTP Quit command to the Server.
        /// </summary>
        public async Task <bool> ExecQuitAsync()
        {
            if (!base.Connected)
            {
                return(false);
            }

            IsActive = true;
            try
            {
                await SmtpStream.WriteLineAsync("QUIT");

                // Don't read response as don't care.
                // Close the TCP connection.
                base.GetStream().Close();
                base.Close();
            }
            catch (ObjectDisposedException) {
                Logging.Debug("SmtpOutboundClient: Tried to quit an already disposed client.");
            }
            IsActive = false;
            return(true);
        }
示例#17
0
        /// <summary>
        /// Send the MAIL FROM command to the server using <paramref name="mailFrom"/> as parameter.
        /// </summary>
        /// <param name="mailFrom">Email address to use as parameter.</param>
        /// <param name="failedCallback">Action to call if command fails.</param>
        private async Task <MantaOutboundClientSendResult> ExecMailFromAsync(MailAddress mailFrom)
        {
            await SmtpStream.WriteLineAsync("MAIL FROM: <" +
                                            (mailFrom == null ? string.Empty : mailFrom.Address) + ">" +
                                            (_DataTransportMime == SmtpTransportMIME._8BitUTF ? " BODY=8BITMIME" : string.Empty));

            // If the remote MX doesn't support pipelining then wait and check the response.
            if (!_CanPipeline)
            {
                string response = await SmtpStream.ReadAllLinesAsync();

                if (!response.StartsWith("250"))
                {
                    if (response.StartsWith("421"))
                    {
                        return(new MantaOutboundClientSendResult(MantaOutboundClientResult.ServiceNotAvalible, response, _VirtualMta, _MXRecord));
                    }

                    return(new MantaOutboundClientSendResult(MantaOutboundClientResult.RejectedByRemoteServer, response, _VirtualMta, _MXRecord));
                }
            }

            return(new MantaOutboundClientSendResult(MantaOutboundClientResult.Success, null, _VirtualMta, _MXRecord));
        }
示例#18
0
        public void TestReadResponseOver4K()
        {
            string expected;
            string input;

            using (var rng = new RNGCryptoServiceProvider()) {
                var builder = new StringBuilder();
                var buffer  = new byte[72];

                while (builder.Length < 5120)
                {
                    rng.GetBytes(buffer);

                    var base64 = Convert.ToBase64String(buffer);
                    builder.AppendFormat("250-{0}\r\n", base64);
                }

                builder.Append("250 Okay, now we're done.\r\n");
                input = builder.ToString();

                expected = input.Replace("250-", "").Replace("250 ", "").Replace("\r\n", "\n").TrimEnd();
            }

            using (var stream = new SmtpStream(new DummyNetworkStream(), new NullProtocolLogger())) {
                var buffer = Encoding.ASCII.GetBytes(input);
                var dummy  = (MemoryStream)stream.Stream;

                dummy.Write(buffer, 0, buffer.Length);
                dummy.Position = 0;

                var response = stream.ReadResponse(CancellationToken.None);

                Assert.AreEqual(250, (int)response.StatusCode);
                Assert.AreEqual(expected, response.Response);
            }
        }
示例#19
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();
        }
示例#20
0
        public async Task TestWriteAsync()
        {
            using (var stream = new SmtpStream(new DummyNetworkStream(), new NullProtocolLogger())) {
                var     memory = (MemoryStream)stream.Stream;
                var     buffer = new byte[8192];
                var     buf1k  = new byte[1024];
                var     buf4k  = new byte[4096];
                var     buf9k  = new byte[9216];
                byte [] mem;

                using (var rng = new RNGCryptoServiceProvider()) {
                    rng.GetBytes(buf1k);
                    rng.GetBytes(buf4k);
                    rng.GetBytes(buf9k);
                }

                // Test #1: write less than 4K to make sure that SmtpStream buffers it
                await stream.WriteAsync(buf1k, 0, buf1k.Length);

                Assert.AreEqual(0, memory.Length, "#1");

                // Test #2: make sure that flushing the SmtpStream flushes the entire buffer out to the network
                await stream.FlushAsync();

                Assert.AreEqual(buf1k.Length, memory.Length, "#2");
                mem = memory.GetBuffer();
                for (int i = 0; i < buf1k.Length; i++)
                {
                    Assert.AreEqual(buf1k[i], mem[i], "#2 byte[{0}]", i);
                }
                memory.SetLength(0);

                // Test #3: write exactly 4K to make sure it passes through w/o the need to flush
                await stream.WriteAsync(buf4k, 0, buf4k.Length);

                Assert.AreEqual(buf4k.Length, memory.Length, "#3");
                mem = memory.GetBuffer();
                for (int i = 0; i < buf4k.Length; i++)
                {
                    Assert.AreEqual(buf4k[i], mem[i], "#3 byte[{0}]", i);
                }
                memory.SetLength(0);

                // Test #4: write 1k and then write 4k, make sure that only 4k passes thru (last 1k gets buffered)
                await stream.WriteAsync(buf1k, 0, buf1k.Length);

                await stream.WriteAsync(buf4k, 0, buf4k.Length);

                Assert.AreEqual(4096, memory.Length, "#4");
                await stream.FlushAsync();

                Assert.AreEqual(buf1k.Length + buf4k.Length, memory.Length, "#4");
                Array.Copy(buf1k, 0, buffer, 0, buf1k.Length);
                Array.Copy(buf4k, 0, buffer, buf1k.Length, buf4k.Length);
                mem = memory.GetBuffer();
                for (int i = 0; i < buf1k.Length + buf4k.Length; i++)
                {
                    Assert.AreEqual(buffer[i], mem[i], "#4 byte[{0}]", i);
                }
                memory.SetLength(0);

                // Test #5: write 9k and make sure only the first 8k goes thru (last 1k gets buffered)
                await stream.WriteAsync(buf9k, 0, buf9k.Length);

                Assert.AreEqual(8192, memory.Length, "#5");
                await stream.FlushAsync();

                Assert.AreEqual(buf9k.Length, memory.Length, "#5");
                mem = memory.GetBuffer();
                for (int i = 0; i < buf9k.Length; i++)
                {
                    Assert.AreEqual(buf9k[i], mem[i], "#5 byte[{0}]", i);
                }
                memory.SetLength(0);
            }
        }
示例#21
0
 public void TestSetLength()
 {
     using (var stream = new SmtpStream(new DummyNetworkStream(), new NullProtocolLogger())) {
         Assert.Throws <NotSupportedException> (() => stream.SetLength(500));
     }
 }
示例#22
0
        /// <summary>
        /// Send the data to the server
        /// </summary>
        /// <param name="data">Data to send to the server</param>
        /// <param name="failedCallback">Action to call if fails to send.</param>
        public async Task <bool> ExecDataAsync(string data, Action <string> failedCallback, Func <string, Task> successCallbackAsync)
        {
            if (!base.Connected)
            {
                return(false);
            }

            _LastActive = DateTime.UtcNow;
            IsActive    = true;

            await SmtpStream.WriteLineAsync("DATA");

            string response = await SmtpStream.ReadAllLinesAsync();

            // If the remote MX supports pipelining then we need to check the MAIL FROM and RCPT to responses.
            if (_CanPipeline)
            {
                // Check MAIL FROM OK.
                if (!response.StartsWith("250"))
                {
                    failedCallback(response);
                    IsActive = false;
                    return(false);
                }


                // Check RCPT TO OK.
                response = await SmtpStream.ReadAllLinesAsync();

                if (!response.StartsWith("250"))
                {
                    failedCallback(response);
                    IsActive = false;
                    return(false);
                }

                // Get the Data Command response.
                response = await SmtpStream.ReadAllLinesAsync();
            }

            _LastActive = DateTime.UtcNow;

            if (!response.StartsWith("354"))
            {
                failedCallback(response);
                IsActive = false;
                return(false);
            }

            // Increment the data commands as server has responded positiely.
            _DataCommands++;

            // Send the message data using the correct transport MIME
            SmtpStream.SetSmtpTransportMIME(_DataTransportMime);
            await SmtpStream.WriteAsync(data, false);

            await SmtpStream.WriteAsync(MtaParameters.NewLine + "." + MtaParameters.NewLine, false);

            _LastActive = DateTime.UtcNow;

            // Data done so return to 7-Bit mode.
            SmtpStream.SetSmtpTransportMIME(SmtpTransportMIME._7BitASCII);


            response = await SmtpStream.ReadAllLinesAsync();

            _LastActive = DateTime.UtcNow;
            IsActive    = false;


            if (!response.StartsWith("250"))
            {
                failedCallback(response);
            }
            else
            {
                await successCallbackAsync(response);
            }


            // If max messages have been sent quit the connection.
            if (_DataCommands >= OutboundRuleManager.GetMaxMessagesPerConnection(MXRecord, MtaIpAddress))
            {
                ExecQuitAsync().Wait();
            }

            return(true);
        }
示例#23
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();
        }
示例#24
0
        /// <summary>
        /// Say EHLO/HELO to the server.
        /// Will also check to see if 8BITMIME is supported.
        /// </summary>
        /// <param name="failedCallback">Action to call if hello fail.</param>
        public async Task <bool> ExecHeloAsync(Action <string> failedCallback)
        {
            if (!base.Connected)
            {
                return(false);
            }

            _LastActive = DateTime.UtcNow;
            IsActive    = true;

            // We have connected to the MX, Say EHLO.
            _LastActive = DateTime.UtcNow;
            await SmtpStream.WriteLineAsync("EHLO " + MtaIpAddress.Hostname);

            string response = await SmtpStream.ReadAllLinesAsync();

            if (response.StartsWith("421"))
            {
                failedCallback(response);
            }
            _LastActive = DateTime.UtcNow;

            try
            {
                if (!response.StartsWith("2"))
                {
                    // If server didn't respond with a success code on hello then we should retry with HELO
                    await SmtpStream.WriteLineAsync("HELO " + MtaIpAddress.Hostname);

                    response = await SmtpStream.ReadAllLinesAsync();

                    _LastActive = DateTime.UtcNow;
                    if (!response.StartsWith("250"))
                    {
                        failedCallback(response);
                        base.Close();
                    }
                }
                else
                {
                    // Server responded to EHLO
                    // Check to see if it supports 8BITMIME
                    if (response.IndexOf("8BITMIME", StringComparison.OrdinalIgnoreCase) > -1)
                    {
                        _DataTransportMime = SmtpTransportMIME._8BitUTF;
                    }

                    // Check to see if the server supports pipelining
                    if (response.IndexOf("PIPELINING", StringComparison.OrdinalIgnoreCase) > -1)
                    {
                        _CanPipeline = true;
                    }
                }
            }
            catch (IOException)
            {
                // Remote Endpoint Disconnected Mid HELO. Most likly Yahoo throttling.
            }


            _HasHelloed = true;
            _LastActive = DateTime.UtcNow;
            IsActive    = false;
            return(true);
        }