/// <summary>
        /// Default constructor.
        /// </summary>
        /// <param name="session">Owner SMTP server session.</param>
        /// <param name="reply">SMTP server reply.</param>
        /// <exception cref="ArgumentNullException">Is raised when <b>session</b> or <b>reply</b> is null reference.</exception>
        public SMTP_e_Started(SMTP_Session session, SMTP_Reply reply)
        {
            if (session == null)
            {
                throw new ArgumentNullException("session");
            }
            if (reply == null)
            {
                throw new ArgumentNullException("reply");
            }

            m_pSession = session;
            m_pReply   = reply;
        }
Пример #2
0
        /// <summary>
        /// Default constructor.
        /// </summary>
        /// <param name="session">Owner SMTP server session.</param>
        /// <param name="reply">SMTP server reply.</param>
        /// <exception cref="ArgumentNullException">Is raised when <b>session</b> or <b>reply</b> is null reference.</exception>
        public SMTP_e_Started(SMTP_Session session, SMTP_Reply reply)
        {
            if (session == null)
            {
                throw new ArgumentNullException("session");
            }
            if (reply == null)
            {
                throw new ArgumentNullException("reply");
            }

            m_pSession = session;
            m_pReply = reply;
        }
        /// <summary>
        /// Default constructor.
        /// </summary>
        /// <param name="session">Owner SMTP server session.</param>
        /// <param name="stream">Message stream.</param>
        /// <param name="reply">SMTP server reply.</param>
        /// <exception cref="ArgumentNullException">Is raised when <b>session</b>, <b>stream</b> or <b>reply</b> is null reference.</exception>
        public SMTP_e_MessageStored(SMTP_Session session, Stream stream, SMTP_Reply reply)
        {
            if (session == null)
            {
                throw new ArgumentNullException("session");
            }
            if (stream == null)
            {
                throw new ArgumentNullException("stream");
            }
            if (reply == null)
            {
                throw new ArgumentNullException("reply");
            }

            m_pSession = session;
            m_pStream = stream;
            m_pReply = reply;
        }
Пример #4
0
        /// <summary>
        /// Default constructor.
        /// </summary>
        /// <param name="session">Owner SMTP server session.</param>
        /// <param name="to">RCPT TO: value.</param>
        /// <param name="reply">SMTP server reply.</param>
        /// <exception cref="ArgumentNullException">Is raised when <b>session</b>, <b>to</b> or <b>reply</b> is null reference.</exception>
        public SMTP_e_RcptTo(SMTP_Session session, SMTP_RcptTo to, SMTP_Reply reply)
        {
            if (session == null)
            {
                throw new ArgumentNullException("session");
            }
            if (to == null)
            {
                throw new ArgumentNullException("from");
            }
            if (reply == null)
            {
                throw new ArgumentNullException("reply");
            }

            m_pSession = session;
            m_pRcptTo  = to;
            m_pReply   = reply;
        }
Пример #5
0
        /// <summary>
        /// Default constructor.
        /// </summary>
        /// <param name="session">Owner SMTP server session.</param>
        /// <param name="from">MAIL FROM: value.</param>
        /// <param name="reply">SMTP server reply.</param>
        /// <exception cref="ArgumentNullException">Is raised when <b>session</b>, <b>from</b> or <b>reply</b> is null reference.</exception>
        public SMTP_e_MailFrom(SMTP_Session session, SMTP_MailFrom from, SMTP_Reply reply)
        {
            if (session == null)
            {
                throw new ArgumentNullException("session");
            }
            if (from == null)
            {
                throw new ArgumentNullException("from");
            }
            if (reply == null)
            {
                throw new ArgumentNullException("reply");
            }

            m_pSession = session;
            m_pMailFrom = from;
            m_pReply = reply;
        }
        /// <summary>
        /// Default constructor.
        /// </summary>
        /// <param name="session">Owner SMTP server session.</param>
        /// <param name="stream">Message stream.</param>
        /// <param name="reply">SMTP server reply.</param>
        /// <exception cref="ArgumentNullException">Is raised when <b>session</b>, <b>stream</b> or <b>reply</b> is null reference.</exception>
        public SMTP_e_MessageStored(SMTP_Session session, Stream stream, SMTP_Reply reply)
        {
            if (session == null)
            {
                throw new ArgumentNullException("session");
            }
            if (stream == null)
            {
                throw new ArgumentNullException("stream");
            }
            if (reply == null)
            {
                throw new ArgumentNullException("reply");
            }

            m_pSession = session;
            m_pStream  = stream;
            m_pReply   = reply;
        }
Пример #7
0
        /// <summary>
        /// Default constructor.
        /// </summary>
        /// <param name="session">Owner SMTP server session.</param>
        /// <param name="to">RCPT TO: value.</param>
        /// <param name="reply">SMTP server reply.</param>
        /// <exception cref="ArgumentNullException">Is raised when <b>session</b>, <b>to</b> or <b>reply</b> is null reference.</exception>
        public SMTP_e_RcptTo(SMTP_Session session, SMTP_RcptTo to, SMTP_Reply reply)
        {
            if (session == null)
            {
                throw new ArgumentNullException("session");
            }
            if (to == null)
            {
                throw new ArgumentNullException("from");
            }
            if (reply == null)
            {
                throw new ArgumentNullException("reply");
            }

            m_pSession = session;
            m_pRcptTo = to;
            m_pReply = reply;
        }
Пример #8
0
        /// <summary>
        /// Default constructor.
        /// </summary>
        /// <param name="session">Owner SMTP server session.</param>
        /// <param name="from">MAIL FROM: value.</param>
        /// <param name="reply">SMTP server reply.</param>
        /// <exception cref="ArgumentNullException">Is raised when <b>session</b>, <b>from</b> or <b>reply</b> is null reference.</exception>
        public SMTP_e_MailFrom(SMTP_Session session, SMTP_MailFrom from, SMTP_Reply reply)
        {
            if (session == null)
            {
                throw new ArgumentNullException("session");
            }
            if (from == null)
            {
                throw new ArgumentNullException("from");
            }
            if (reply == null)
            {
                throw new ArgumentNullException("reply");
            }

            m_pSession  = session;
            m_pMailFrom = from;
            m_pReply    = reply;
        }
Пример #9
0
        /// <summary>
        /// Default constructor.
        /// </summary>
        /// <param name="session">Owner SMTP server session.</param>
        /// <param name="domain">Ehlo/Helo domain name.</param>
        /// <param name="reply">SMTP server reply.</param>
        /// <exception cref="ArgumentNullException">Is raised when <b>session</b>, <b>domain</b> or <b>reply</b> is null reference.</exception>
        /// <exception cref="ArgumentException">Is raised when any of the arguments has invalid value.</exception>
        public SMTP_e_Ehlo(SMTP_Session session, string domain, SMTP_Reply reply)
        {
            if (session == null)
            {
                throw new ArgumentNullException("session");
            }
            if (domain == null)
            {
                throw new ArgumentNullException("domain");
            }
            if (domain == string.Empty)
            {
                throw new ArgumentException("Argument 'domain' value must be sepcified.", "domain");
            }
            if (reply == null)
            {
                throw new ArgumentNullException("reply");
            }

            m_pSession = session;
            m_Domain = domain;
            m_pReply = reply;
        }
Пример #10
0
        /// <summary>
        /// Default constructor.
        /// </summary>
        /// <param name="session">Owner SMTP server session.</param>
        /// <param name="domain">Ehlo/Helo domain name.</param>
        /// <param name="reply">SMTP server reply.</param>
        /// <exception cref="ArgumentNullException">Is raised when <b>session</b>, <b>domain</b> or <b>reply</b> is null reference.</exception>
        /// <exception cref="ArgumentException">Is raised when any of the arguments has invalid value.</exception>
        public SMTP_e_Ehlo(SMTP_Session session, string domain, SMTP_Reply reply)
        {
            if (session == null)
            {
                throw new ArgumentNullException("session");
            }
            if (domain == null)
            {
                throw new ArgumentNullException("domain");
            }
            if (domain == string.Empty)
            {
                throw new ArgumentException("Argument 'domain' value must be sepcified.", "domain");
            }
            if (reply == null)
            {
                throw new ArgumentNullException("reply");
            }

            m_pSession = session;
            m_Domain   = domain;
            m_pReply   = reply;
        }
Пример #11
0
        private void EHLO(string cmdText)
        {
            // RFC 5321 3.1.
            if (m_SessionRejected)
            {
                WriteLine("503 bad sequence of commands: Session rejected.");
                return;
            }

            /* RFC 5321 4.1.1.1.
                ehlo           = "EHLO" SP ( Domain / address-literal ) CRLF

                ehlo-ok-rsp    = ( "250" SP Domain [ SP ehlo-greet ] CRLF )
                                 / ( "250-" Domain [ SP ehlo-greet ] CRLF
                                 *( "250-" ehlo-line CRLF )
                                 "250" SP ehlo-line CRLF )

                ehlo-greet     = 1*(%d0-9 / %d11-12 / %d14-127)
                                 ; string of any characters other than CR or LF

                ehlo-line      = ehlo-keyword *( SP ehlo-param )

                ehlo-keyword   = (ALPHA / DIGIT) *(ALPHA / DIGIT / "-")
                                ; additional syntax of ehlo-params depends on ehlo-keyword

                ehlo-param     = 1*(%d33-126)
                                ; any CHAR excluding <SP> and all control characters (US-ASCII 0-31 and 127 inclusive)
            */
            if (string.IsNullOrEmpty(cmdText) || cmdText.Split(' ').Length != 1)
            {
                WriteLine("501 Syntax error, syntax: \"EHLO\" SP hostname CRLF");
                return;
            }

            List<string> ehloLines = new List<string>();
            ehloLines.Add(Net_Utils.GetLocalHostName(LocalHostName));
            if (Server.Extentions.Contains(SMTP_ServiceExtensions.PIPELINING))
            {
                ehloLines.Add(SMTP_ServiceExtensions.PIPELINING);
            }
            if (Server.Extentions.Contains(SMTP_ServiceExtensions.SIZE))
            {
                ehloLines.Add(SMTP_ServiceExtensions.SIZE + " " + Server.MaxMessageSize);
            }
            if (Server.Extentions.Contains(SMTP_ServiceExtensions.STARTTLS) && !IsSecureConnection &&
                Certificate != null)
            {
                ehloLines.Add(SMTP_ServiceExtensions.STARTTLS);
            }
            if (Server.Extentions.Contains(SMTP_ServiceExtensions._8BITMIME))
            {
                ehloLines.Add(SMTP_ServiceExtensions._8BITMIME);
            }
            if (Server.Extentions.Contains(SMTP_ServiceExtensions.BINARYMIME))
            {
                ehloLines.Add(SMTP_ServiceExtensions.BINARYMIME);
            }
            if (Server.Extentions.Contains(SMTP_ServiceExtensions.CHUNKING))
            {
                ehloLines.Add(SMTP_ServiceExtensions.CHUNKING);
            }
            if (Server.Extentions.Contains(SMTP_ServiceExtensions.DSN))
            {
                ehloLines.Add(SMTP_ServiceExtensions.DSN);
            }

            StringBuilder sasl = new StringBuilder();
            foreach (AUTH_SASL_ServerMechanism authMechanism in Authentications.Values)
            {
                if (!authMechanism.RequireSSL || (authMechanism.RequireSSL && IsSecureConnection))
                {
                    sasl.Append(authMechanism.Name + " ");
                }
            }
            if (sasl.Length > 0)
            {
                ehloLines.Add(SMTP_ServiceExtensions.AUTH + " " + sasl.ToString().Trim());
            }

            SMTP_Reply reply = new SMTP_Reply(250, ehloLines.ToArray());

            reply = OnEhlo(cmdText, reply);

            // EHLO accepted.
            if (reply.ReplyCode < 300)
            {
                m_EhloHost = cmdText;

                /* RFC 5321 4.1.4.
                    An EHLO command MAY be issued by a client later in the session.  If
                    it is issued after the session begins and the EHLO command is
                    acceptable to the SMTP server, the SMTP server MUST clear all buffers
                    and reset the state exactly as if a RSET command had been issued.  In
                    other words, the sequence of RSET followed immediately by EHLO is
                    redundant, but not harmful other than in the performance cost of
                    executing unnecessary commands.
                */
                Reset();
            }

            WriteLine(reply.ToString());
        }
Пример #12
0
        private bool BDAT(string cmdText)
        {
            // RFC 5321 3.1.
            if (m_SessionRejected)
            {
                WriteLine("503 bad sequence of commands: Session rejected.");
                return true;
            }
            // RFC 5321 4.1.4.
            if (string.IsNullOrEmpty(m_EhloHost))
            {
                WriteLine("503 Bad sequence of commands: send EHLO/HELO first.");
                return true;
            }
            // RFC 5321 4.1.4.
            if (m_pFrom == null)
            {
                WriteLine("503 Bad sequence of commands: send 'MAIL FROM:' first.");
                return true;
            }
            // RFC 5321 4.1.4.
            if (m_pTo.Count == 0)
            {
                WriteLine("503 Bad sequence of commands: send 'RCPT TO:' first.");
                return true;
            }

            /* RFC 3030 2
				The BDAT verb takes two arguments.The first argument indicates the length, 
                in octets, of the binary data chunk. The second optional argument indicates 
                that the data chunk	is the last.
				
				The message data is sent immediately after the trailing <CR>
				<LF> of the BDAT command line.  Once the receiver-SMTP receives the
				specified number of octets, it will return a 250 reply code.

				The optional LAST parameter on the BDAT command indicates that this
				is the last chunk of message data to be sent.  The last BDAT command
				MAY have a byte-count of zero indicating there is no additional data
				to be sent.  Any BDAT command sent after the BDAT LAST is illegal and
				MUST be replied to with a 503 "Bad sequence of commands" reply code.
				The state resulting from this error is indeterminate.  A RSET command
				MUST be sent to clear the transaction before continuing.
				
				A 250 response MUST be sent to each successful BDAT data block within
				a mail transaction.

				bdat-cmd   ::= "BDAT" SP chunk-size [ SP end-marker ] CR LF
				chunk-size ::= 1*DIGIT
				end-marker ::= "LAST"
			*/

            DateTime startTime = DateTime.Now;

            int chunkSize = 0;
            bool last = false;
            string[] args = cmdText.Split(' ');
            if (cmdText == string.Empty || args.Length > 2)
            {
                WriteLine("501 Syntax error, syntax: \"BDAT\" SP chunk-size [SP \"LAST\"] CRLF");
                return true;
            }
            if (!int.TryParse(args[0], out chunkSize))
            {
                WriteLine(
                    "501 Syntax error(chunk-size must be integer), syntax: \"BDAT\" SP chunk-size [SP \"LAST\"] CRLF");
                return true;
            }
            if (args.Length == 2)
            {
                if (args[1].ToUpperInvariant() != "LAST")
                {
                    WriteLine("501 Syntax error, syntax: \"BDAT\" SP chunk-size [SP \"LAST\"] CRLF");
                    return true;
                }
                last = true;
            }

            // First BDAT block in transaction.
            if (m_pMessageStream == null)
            {
                m_pMessageStream = OnGetMessageStream();
                if (m_pMessageStream == null)
                {
                    m_pMessageStream = new MemoryStream();
                }
                // RFC 5321.4.4 trace info.
                byte[] recevived = CreateReceivedHeader();
                m_pMessageStream.Write(recevived, 0, recevived.Length);
            }

            Stream storeStream = m_pMessageStream;
            // Maximum allowed message size exceeded.
            if ((m_BDatReadedCount + chunkSize) > Server.MaxMessageSize)
            {
                storeStream = new JunkingStream();
            }

            // Read data block.
            TcpStream.BeginReadFixedCount(storeStream,
                                          chunkSize,
                                          delegate(IAsyncResult ar)
                                              {
                                                  try
                                                  {
                                                      TcpStream.EndReadFixedCount(ar);

                                                      m_BDatReadedCount += chunkSize;

                                                      // Maximum allowed message size exceeded.
                                                      if (m_BDatReadedCount > Server.MaxMessageSize)
                                                      {
                                                          WriteLine("552 Too much mail data.");

                                                          OnMessageStoringCanceled();
                                                      }
                                                      else
                                                      {
                                                          SMTP_Reply reply = new SMTP_Reply(250,
                                                                                            chunkSize +
                                                                                            " bytes received in " +
                                                                                            (DateTime.Now -
                                                                                             startTime).
                                                                                                TotalSeconds.
                                                                                                ToString("f2") +
                                                                                            " seconds.");

                                                          if (last)
                                                          {
                                                              reply = OnMessageStoringCompleted(reply);
                                                          }

                                                          WriteLine(reply.ToString());
                                                      }

                                                      if (last)
                                                      {
                                                          // Accoring RFC 3030, client should send RSET and we must wait it and reject transaction commands.
                                                          // If we reset internally, then all works as specified. 
                                                          Reset();
                                                      }
                                                  }
                                                  catch (Exception x)
                                                  {
                                                      OnError(x);
                                                  }

                                                  BeginReadCmd();
                                              },
                                          null);

            return false;
        }
Пример #13
0
        /// <summary>
        /// Raises <b>Started</b> event.
        /// </summary>
        /// <param name="reply">Default SMTP server reply.</param>
        /// <returns>Returns SMTP server reply what must be sent to the connected client.</returns>
        private SMTP_Reply OnStarted(SMTP_Reply reply)
        {
            if (Started != null)
            {
                SMTP_e_Started eArgs = new SMTP_e_Started(this, reply);
                Started(this, eArgs);

                return eArgs.Reply;
            }

            return reply;
        }
Пример #14
0
        /// <summary>
        /// Raises <b>Ehlo</b> event.
        /// </summary>
        /// <param name="domain">Ehlo/Helo domain.</param>
        /// <param name="reply">Default SMTP server reply.</param>
        /// <returns>Returns SMTP server reply what must be sent to the connected client.</returns>
        private SMTP_Reply OnEhlo(string domain, SMTP_Reply reply)
        {
            if (Ehlo != null)
            {
                SMTP_e_Ehlo eArgs = new SMTP_e_Ehlo(this, domain, reply);
                Ehlo(this, eArgs);

                return eArgs.Reply;
            }

            return reply;
        }
Пример #15
0
        /// <summary>
        /// Raises <b>MailFrom</b> event.
        /// </summary>
        /// <param name="from">MAIL FROM: value.</param>
        /// <param name="reply">Default SMTP server reply.</param>
        /// <returns>Returns SMTP server reply what must be sent to the connected client.</returns>
        private SMTP_Reply OnMailFrom(SMTP_MailFrom from, SMTP_Reply reply)
        {
            if (MailFrom != null)
            {
                SMTP_e_MailFrom eArgs = new SMTP_e_MailFrom(this, from, reply);
                MailFrom(this, eArgs);

                return eArgs.Reply;
            }

            return reply;
        }
Пример #16
0
        private void MAIL(string cmdText)
        {
            // RFC 5321 3.1.
            if (m_SessionRejected)
            {
                WriteLine("503 bad sequence of commands: Session rejected.");
                return;
            }
            // RFC 5321 4.1.4.
            if (string.IsNullOrEmpty(m_EhloHost))
            {
                WriteLine("503 Bad sequence of commands: send EHLO/HELO first.");
                return;
            }
            // RFC 5321 4.1.4.
            if (m_pFrom != null)
            {
                WriteLine("503 Bad sequence of commands: nested MAIL command.");
                return;
            }
            // RFC 3030 BDAT.
            if (m_pMessageStream != null)
            {
                WriteLine("503 Bad sequence of commands: BDAT command is pending.");
                return;
            }
            if (Server.MaxTransactions != 0 && m_Transactions >= Server.MaxTransactions)
            {
                WriteLine("503 Bad sequence of commands: Maximum allowed mail transactions exceeded.");
                return;
            }

            /* RFC 5321 4.1.1.2.
                mail            = "MAIL FROM:" Reverse-path [SP Mail-parameters] CRLF
              
                Mail-parameters = esmtp-param *(SP esmtp-param)

                esmtp-param     = esmtp-keyword ["=" esmtp-value]

                esmtp-keyword   = (ALPHA / DIGIT) *(ALPHA / DIGIT / "-")

                esmtp-value     = 1*(%d33-60 / %d62-126)
                                  ; any CHAR excluding "=", SP, and control
                                  ; characters.  If this string is an email address,
                                  ; i.e., a Mailbox, then the "xtext" syntax [32] SHOULD be used.
              
                Reverse-path   = Path / "<>"
                Path           = "<" [ A-d-l ":" ] Mailbox ">"
              
               4.1.1.11.
                If the server SMTP does not recognize or cannot implement one or more
                of the parameters associated with a particular MAIL FROM or RCPT TO
                command, it will return code 555.
            */

            if (cmdText.ToUpper().StartsWith("FROM:"))
            {
                // Remove FROM: from command text.
                cmdText = cmdText.Substring(5).Trim();
            }
            else
            {
                WriteLine(
                    "501 Syntax error, syntax: \"MAIL FROM:\" \"<\" address \">\" / \"<>\" [SP Mail-parameters] CRLF");
                return;
            }

            string address = "";
            int size = -1;
            string body = null;
            string ret = null;
            string envID = null;

            // Mailbox not between <>.
            if (!cmdText.StartsWith("<") || cmdText.IndexOf('>') == -1)
            {
                WriteLine(
                    "501 Syntax error, syntax: \"MAIL FROM:\" \"<\" address \">\" / \"<>\" [SP Mail-parameters] CRLF");
                return;
            }
                // Parse mailbox.
            else
            {
                address = cmdText.Substring(1, cmdText.IndexOf('>') - 1).Trim();
                cmdText = cmdText.Substring(cmdText.IndexOf('>') + 1).Trim();
            }

            #region Parse parameters

            string[] parameters = string.IsNullOrEmpty(cmdText) ? new string[0] : cmdText.Split(' ');
            foreach (string parameter in parameters)
            {
                string[] name_value = parameter.Split(new[] {'='}, 2);

                // SIZE
                if (Server.Extentions.Contains(SMTP_ServiceExtensions.SIZE) &&
                    name_value[0].ToUpper() == "SIZE")
                {
                    // RFC 1870.
                    //  size-value ::= 1*20DIGIT
                    if (name_value.Length == 1)
                    {
                        WriteLine("501 Syntax error: SIZE parameter value must be specified.");
                        return;
                    }
                    if (!int.TryParse(name_value[1], out size))
                    {
                        WriteLine("501 Syntax error: SIZE parameter value must be integer.");
                        return;
                    }

                    // Message size exceeds maximum allowed message size.
                    if (size > Server.MaxMessageSize)
                    {
                        WriteLine("552 Message exceeds fixed maximum message size.");
                        return;
                    }
                }
                    // BODY
                else if (Server.Extentions.Contains(SMTP_ServiceExtensions._8BITMIME) &&
                         name_value[0].ToUpper() == "BODY")
                {
                    // RFC 1652.
                    //  body-value ::= "7BIT" / "8BITMIME" / "BINARYMIME"
                    //
                    // BINARYMIME - defined in RFC 3030.
                    if (name_value.Length == 1)
                    {
                        WriteLine("501 Syntax error: BODY parameter value must be specified.");
                        return;
                    }
                    if (name_value[1].ToUpper() != "7BIT" && name_value[1].ToUpper() != "8BITMIME" &&
                        name_value[1].ToUpper() != "BINARYMIME")
                    {
                        WriteLine(
                            "501 Syntax error: BODY parameter value must be \"7BIT\",\"8BITMIME\" or \"BINARYMIME\".");
                        return;
                    }
                    body = name_value[1].ToUpper();
                }
                    // RET
                else if (Server.Extentions.Contains(SMTP_ServiceExtensions.DSN) &&
                         name_value[0].ToUpper() == "RET")
                {
                    // RFC 1891 5.3.
                    //  ret-value = "FULL" / "HDRS"
                    if (name_value.Length == 1)
                    {
                        WriteLine("501 Syntax error: RET parameter value must be specified.");
                        return;
                    }
                    if (name_value[1].ToUpper() != "FULL" && name_value[1].ToUpper() != "HDRS")
                    {
                        WriteLine(
                            "501 Syntax error: RET parameter value must be \"FULL\" or \"HDRS\".");
                        return;
                    }
                    ret = name_value[1].ToUpper();
                }
                    // ENVID
                else if (Server.Extentions.Contains(SMTP_ServiceExtensions.DSN) &&
                         name_value[0].ToUpper() == "ENVID")
                {
                    if (name_value.Length == 1)
                    {
                        WriteLine("501 Syntax error: ENVID parameter value must be specified.");
                        return;
                    }

                    envID = name_value[1].ToUpper();
                }
                    // Unsupported parameter.
                else
                {
                    WriteLine("555 Unsupported parameter: " + parameter);
                    return;
                }
            }

            #endregion

            SMTP_MailFrom from = new SMTP_MailFrom(address, size, body, ret, envID);
            SMTP_Reply reply = new SMTP_Reply(250, "OK.");

            reply = OnMailFrom(from, reply);

            // MAIL accepted.
            if (reply.ReplyCode < 300)
            {
                m_pFrom = from;
                m_Transactions++;
            }

            WriteLine(reply.ToString());
        }
Пример #17
0
        private void HELO(string cmdText)
        {
            // RFC 5321 3.1.
            if (m_SessionRejected)
            {
                WriteLine("503 bad sequence of commands: Session rejected.");
                return;
            }

            /* RFC 5321 4.1.1.1.
                helo     = "HELO" SP Domain CRLF
            
                response = "250" SP Domain [ SP ehlo-greet ] CRLF
            */
            if (string.IsNullOrEmpty(cmdText) || cmdText.Split(' ').Length != 1)
            {
                WriteLine("501 Syntax error, syntax: \"HELO\" SP hostname CRLF");
                return;
            }

            SMTP_Reply reply = new SMTP_Reply(250, Net_Utils.GetLocalHostName(LocalHostName));

            reply = OnEhlo(cmdText, reply);

            // HELO accepted.
            if (reply.ReplyCode < 300)
            {
                m_EhloHost = cmdText;

                /* RFC 5321 4.1.4.
                    An EHLO command MAY be issued by a client later in the session.  If
                    it is issued after the session begins and the EHLO command is
                    acceptable to the SMTP server, the SMTP server MUST clear all buffers
                    and reset the state exactly as if a RSET command had been issued.  In
                    other words, the sequence of RSET followed immediately by EHLO is
                    redundant, but not harmful other than in the performance cost of
                    executing unnecessary commands.
                */
                Reset();
            }

            WriteLine(reply.ToString());
        }
Пример #18
0
        /// <summary>
        /// Completes DATA command.
        /// </summary>
        /// <param name="startTime">Time DATA command started.</param>
        /// <param name="op">Read period-terminated opeartion.</param>
        private void DATA_End(DateTime startTime, SmartStream.ReadPeriodTerminatedAsyncOP op)
        {
            try
            {
                if (op.Error != null)
                {
                    if (op.Error is LineSizeExceededException)
                    {
                        WriteLine("500 Line too long.");
                    }
                    else if (op.Error is DataSizeExceededException)
                    {
                        WriteLine("552 Too much mail data.");
                    }
                    else
                    {
                        OnError(op.Error);
                    }

                    OnMessageStoringCanceled();
                }
                else
                {
                    SMTP_Reply reply = new SMTP_Reply(250,
                                                      "DATA completed in " +
                                                      (DateTime.Now - startTime).TotalSeconds.ToString("f2") +
                                                      " seconds.");

                    reply = OnMessageStoringCompleted(reply);

                    WriteLine(reply.ToString());
                }
            }
            catch (Exception x)
            {
                OnError(x);
            }

            Reset();
            BeginReadCmd();
        }
Пример #19
0
        /// <summary>
        /// Raises <b>MessageStoringCompleted</b> event.
        /// </summary>
        /// <param name="reply">Default SMTP server reply.</param>
        /// <returns>Returns SMTP server reply what must be sent to the connected client.</returns>
        private SMTP_Reply OnMessageStoringCompleted(SMTP_Reply reply)
        {
            if (MessageStoringCompleted != null)
            {
                SMTP_e_MessageStored eArgs = new SMTP_e_MessageStored(this, m_pMessageStream, reply);
                MessageStoringCompleted(this, eArgs);

                return eArgs.Reply;
            }

            return reply;
        }
Пример #20
0
        /// <summary>
        /// Raises <b>RcptTo</b> event.
        /// </summary>
        /// <param name="to">RCPT TO: value.</param>
        /// <param name="reply">Default SMTP server reply.</param>
        /// <returns>Returns SMTP server reply what must be sent to the connected client.</returns>
        private SMTP_Reply OnRcptTo(SMTP_RcptTo to, SMTP_Reply reply)
        {
            if (RcptTo != null)
            {
                SMTP_e_RcptTo eArgs = new SMTP_e_RcptTo(this, to, reply);
                RcptTo(this, eArgs);

                return eArgs.Reply;
            }

            return reply;
        }
Пример #21
0
        /// <summary>
        /// Starts session processing.
        /// </summary>
        protected internal override void Start()
        {
            base.Start();

            /* RFC 5321 3.1.
                The SMTP protocol allows a server to formally reject a mail session
                while still allowing the initial connection as follows: a 554
                response MAY be given in the initial connection opening message
                instead of the 220.  A server taking this approach MUST still wait
                for the client to send a QUIT (see Section 4.1.1.10) before closing
                the connection and SHOULD respond to any intervening commands with
                "503 bad sequence of commands".  Since an attempt to make an SMTP
                connection to such a system is probably in error, a server returning
                a 554 response on connection opening SHOULD provide enough
                information in the reply text to facilitate debugging of the sending
                system.
            */

            try
            {
                SMTP_Reply reply = null;
                if (string.IsNullOrEmpty(Server.GreetingText))
                {
                    reply = new SMTP_Reply(220,
                                           "<" + Net_Utils.GetLocalHostName(LocalHostName) +
                                           "> Simple Mail Transfer Service Ready.");
                }
                else
                {
                    reply = new SMTP_Reply(220, Server.GreetingText);
                }

                reply = OnStarted(reply);

                WriteLine(reply.ToString());

                // Setup rejected flag, so we respond "503 bad sequence of commands" any command except QUIT.
                if (reply.ReplyCode >= 300)
                {
                    m_SessionRejected = true;
                }

                BeginReadCmd();
            }
            catch (Exception x)
            {
                OnError(x);
            }
        }
Пример #22
0
        private void RCPT(string cmdText)
        {
            // RFC 5321 3.1.
            if (m_SessionRejected)
            {
                WriteLine("503 bad sequence of commands: Session rejected.");
                return;
            }
            // RFC 5321 4.1.4.
            if (string.IsNullOrEmpty(m_EhloHost))
            {
                WriteLine("503 Bad sequence of commands: send EHLO/HELO first.");
                return;
            }
            // RFC 5321 4.1.4.
            if (m_pFrom == null)
            {
                WriteLine("503 Bad sequence of commands: send 'MAIL FROM:' first.");
                return;
            }
            // RFC 3030 BDAT.
            if (m_pMessageStream != null)
            {
                WriteLine("503 Bad sequence of commands: BDAT command is pending.");
                return;
            }

            /* RFC 5321 4.1.1.3.
                rcpt = "RCPT TO:" ( "<Postmaster@" Domain ">" / "<Postmaster>" /  Forward-path ) [SP Rcpt-parameters] CRLF
              
                Rcpt-parameters = esmtp-param *(SP esmtp-param)

                esmtp-param     = esmtp-keyword ["=" esmtp-value]

                esmtp-keyword   = (ALPHA / DIGIT) *(ALPHA / DIGIT / "-")

                esmtp-value     = 1*(%d33-60 / %d62-126)
                                  ; any CHAR excluding "=", SP, and control
                                  ; characters.  If this string is an email address,
                                  ; i.e., a Mailbox, then the "xtext" syntax [32] SHOULD be used.

                    Note that, in a departure from the usual rules for local-parts, the "Postmaster" string shown above is
                    treated as case-insensitive.
             
                Forward-path   = Path
                Path           = "<" [ A-d-l ":" ] Mailbox ">"
              
               4.1.1.11.
                If the server SMTP does not recognize or cannot implement one or more
                of the parameters associated with a particular MAIL FROM or RCPT TO
                command, it will return code 555.
            */

            if (cmdText.ToUpper().StartsWith("TO:"))
            {
                // Remove TO: from command text.
                cmdText = cmdText.Substring(3).Trim();
            }
            else
            {
                WriteLine(
                    "501 Syntax error, syntax: \"RCPT TO:\" \"<\" address \">\" [SP Rcpt-parameters] CRLF");
                return;
            }

            string address = "";
            SMTP_Notify notify = SMTP_Notify.NotSpecified;
            string orcpt = null;

            // Mailbox not between <>.
            if (!cmdText.StartsWith("<") || cmdText.IndexOf('>') == -1)
            {
                WriteLine(
                    "501 Syntax error, syntax: \"RCPT TO:\" \"<\" address \">\" [SP Rcpt-parameters] CRLF");
                return;
            }
                // Parse mailbox.
            else
            {
                address = cmdText.Substring(1, cmdText.IndexOf('>') - 1).Trim();
                cmdText = cmdText.Substring(cmdText.IndexOf('>') + 1).Trim();
            }
            if (address == string.Empty)
            {
                WriteLine(
                    "501 Syntax error('address' value must be specified), syntax: \"RCPT TO:\" \"<\" address \">\" [SP Rcpt-parameters] CRLF");
                return;
            }

            #region Parse parameters

            string[] parameters = string.IsNullOrEmpty(cmdText) ? new string[0] : cmdText.Split(' ');
            foreach (string parameter in parameters)
            {
                string[] name_value = parameter.Split(new[] {'='}, 2);

                // NOTIFY
                if (Server.Extentions.Contains(SMTP_ServiceExtensions.DSN) &&
                    name_value[0].ToUpper() == "NOTIFY")
                {
                    /* RFC 1891 5.1.
                        notify-esmtp-value  = "NEVER" / 1#notify-list-element
                        notify-list-element = "SUCCESS" / "FAILURE" / "DELAY"
                      
                        a. Multiple notify-list-elements, separated by commas, MAY appear in a
                           NOTIFY parameter; however, the NEVER keyword MUST appear by itself.
                    */
                    if (name_value.Length == 1)
                    {
                        WriteLine("501 Syntax error: NOTIFY parameter value must be specified.");
                        return;
                    }
                    string[] notifyItems = name_value[1].ToUpper().Split(',');
                    foreach (string notifyItem in notifyItems)
                    {
                        if (notifyItem.Trim().ToUpper() == "NEVER")
                        {
                            notify |= SMTP_Notify.Never;
                        }
                        else if (notifyItem.Trim().ToUpper() == "SUCCESS")
                        {
                            notify |= SMTP_Notify.Success;
                        }
                        else if (notifyItem.Trim().ToUpper() == "FAILURE")
                        {
                            notify |= SMTP_Notify.Failure;
                        }
                        else if (notifyItem.Trim().ToUpper() == "DELAY")
                        {
                            notify |= SMTP_Notify.Delay;
                        }
                            // Invalid or not supported notify item.
                        else
                        {
                            WriteLine("501 Syntax error: Not supported NOTIFY parameter value '" + notifyItem +
                                      "'.");
                            return;
                        }
                    }
                }
                    // ORCPT
                else if (Server.Extentions.Contains(SMTP_ServiceExtensions.DSN) &&
                         name_value[0].ToUpper() == "ORCPT")
                {
                    if (name_value.Length == 1)
                    {
                        WriteLine("501 Syntax error: ORCPT parameter value must be specified.");
                        return;
                    }
                    orcpt = name_value[1].ToUpper();
                }
                    // Unsupported parameter.
                else
                {
                    WriteLine("555 Unsupported parameter: " + parameter);
                }
            }

            #endregion

            // Maximum allowed recipients exceeded.
            if (m_pTo.Count >= Server.MaxRecipients)
            {
                WriteLine("452 Too many recipients");
                return;
            }

            SMTP_RcptTo to = new SMTP_RcptTo(address, notify, orcpt);
            SMTP_Reply reply = new SMTP_Reply(250, "OK.");

            reply = OnRcptTo(to, reply);

            // RCPT accepted.
            if (reply.ReplyCode < 300)
            {
                if (!m_pTo.ContainsKey(address.ToLower()))
                {
                    m_pTo.Add(address.ToLower(), to);
                }
            }

            WriteLine(reply.ToString());
        }