SMTP_Session.cs
上传用户:horngjaan
上传日期:2009-12-12
资源大小:2882k
文件大小:32k
- using System;
- using System.IO;
- using System.Net;
- using System.Net.Sockets;
- using System.Threading;
- using System.Collections;
- using System.Text;
- namespace LumiSoft.MailServer.SMTP
- {
- /// <summary>
- /// SMTP Session.
- /// </summary>
- internal class SMTP_Session
- {
- private Socket m_pClientSocket = null; // Referance to client Socket.
- private SMTP_Server m_pSMTP_Server = null; // Referance to SMTP server.
- private string m_SessionID = ""; // Holds session ID.
- private string m_ConnectedIp = ""; // Holds connected computer's IP.
- private string m_ConnectedHostName = ""; // Holds connected computer's name.
- private string m_Reverse_path = ""; // Holds sender's reverse path.
- private Hashtable m_Forward_path = null; // Holds Mail to.
- private bool m_Authenticated = false; // Holds authentication flag.
- private int m_BadCmdCount = 0; // Holds number of bad commands.
- private SMTP_Cmd_Validator m_CmdValidator = null;
- private _LogWriter m_pLogWriter = null;
- // Events
- internal ValidateIPHandler ValidateIPAddress = null;
- internal AuthUserEventHandler AuthUser = null;
- internal ValidateMailFromHandler ValidateMailFrom = null;
- internal ValidateMailToHandler ValidateMailTo = null;
- internal ValidateMailboxSize ValidateMailboxSize = null;
- internal NewMailEventHandler NewMailEvent = null;
- /// <summary>
- /// Default constructor.
- /// </summary>
- /// <param name="clientSocket">Referance to socket.</param>
- /// <param name="server">Referance to SMTP server.</param>
- /// <param name="sessionID">Session ID which is assigned to this session.</param>
- /// <param name="logWriter">Log writer.</param>
- public SMTP_Session(Socket clientSocket,SMTP_Server server,string sessionID,_LogWriter logWriter)
- {
- m_pClientSocket = clientSocket;
- m_pSMTP_Server = server;
- m_SessionID = sessionID;
- m_Forward_path = new Hashtable();
- m_CmdValidator = new SMTP_Cmd_Validator();
- m_pLogWriter = logWriter;
- }
-
- #region function StartProcessing
- /// <summary>
- /// Starts session processing.
- /// </summary>
- public void StartProcessing()
- {
- try
- {
- // Store client ip and host name.
- m_ConnectedIp = Core.ParseIP_from_EndPoint(m_pClientSocket.RemoteEndPoint.ToString());
- m_ConnectedHostName = Core.GetHostName(m_ConnectedIp);
-
- // Check if ip is allowed to connect this computer
- if(OnValidate_IpAddress(m_ConnectedIp)){
-
- // Notify that server is ready
- SendData("220 " + Dns.GetHostName() + " Service readyrn");
-
- //------ Create command loop --------------------------------//
- // Loop while QUIT cmd or Session TimeOut.
- int lastCmdTime = Environment.TickCount;
- string lastCmd = "";
- while(true)
- {
- // If there is any data available, begin command reading.
- if(m_pClientSocket.Available > 0){
- ReadReplyCode replyCode = Core.ReadLineFromSocket(m_pClientSocket,out lastCmd,m_pSMTP_Server.CommandIdleTimeOut);
- if(replyCode == ReadReplyCode.Ok){
- if(SwitchCommand(lastCmd)){
- break;
- }
- }
- //----- There was bad command (usually cmd isn't terminated with '<CRLF>') -------------//
- else{
-
- //---- Check that maximum bad commands count isn't exceeded ---------------//
- if(m_BadCmdCount > m_pSMTP_Server.MaxBadCommands-1){
- SendData("421 Too many bad commands, closing transmission channelrn");
- break;
- }
- m_BadCmdCount++;
- //-------------------------------------------------------------------------//
- switch(replyCode)
- {
- case ReadReplyCode.LenghtExceeded:
- SendData("500 Line too long.rn");
- break;
- case ReadReplyCode.TimeOut:
- SendData("500 Command timeout.rn");
- break;
- case ReadReplyCode.UnKnownError:
- SendData("500 UnKnown Error.rn");
- m_pSMTP_Server.OnSysError(new Exception("ReadReplyCode.UnKnownError"),new System.Diagnostics.StackTrace());
- break;
- }
- }
- //------- End of bad command ------------------------------------------------------------//
- // reset last command time
- lastCmdTime = Environment.TickCount;
- }
- //----- Session timeout stuff ------------------------------------------------//
- if(m_pClientSocket.Available == 0){
- if(Environment.TickCount > lastCmdTime + m_pSMTP_Server.SessionIdleTimeOut){
-
- // Notify for closing
- SendData("421 Session timeout, closing transmission channelrn");
- break;
- }
-
- // Wait 100ms to save CPU, otherwise while loop may take 100% CPU.
- Thread.Sleep(100);
- }
- //---------------------------------------------------------------------------//
- }
- }
- }
- catch(ThreadInterruptedException e){
- }
- catch(Exception x){
- /* RFC 2821 3.9
- NOTE:
- An SMTP server which is forcibly shut down via external means SHOULD
- attempt to send a line containing a 421 response code to the SMTP
- client before exiting. The SMTP client will normally read the 421
- response code after sending its next command.
- */
- if(m_pClientSocket.Connected){
- SendData("421 Service not available, closing transmission channelrn");
- return;
- }
- if(!m_pClientSocket.Connected){
- m_pLogWriter.AddEntry("Connection is aborted by client machine",this.SessionID,m_ConnectedIp,"x");
- return;
- }
- m_pSMTP_Server.OnSysError(x,new System.Diagnostics.StackTrace());
- }
- finally{
- m_pSMTP_Server.RemoveSession(this.SessionID,m_pLogWriter);
- m_pClientSocket.Close();
- // Write logs to log file, if needed
- if(m_pSMTP_Server.LogCommands){
- m_pLogWriter.Flush();
- }
- }
- }
- #endregion
- #region function SwitchCommand
- /// <summary>
- /// Executes SMTP command.
- /// </summary>
- /// <param name="SMTP_commandTxt">Original command text.</param>
- /// <returns>Returns true if must end session(command loop).</returns>
- private bool SwitchCommand(string SMTP_commandTxt)
- {
- try
- {
- //---- Parse command --------------------------------------------------//
- string[] cmdParts = SMTP_commandTxt.TrimStart().Split(new char[]{' '});
- string SMTP_command = cmdParts[0];
- SMTP_command = SMTP_command.ToUpper();
- //---------------------------------------------------------------------//
- //----- loging stuff --------------------------------------------------------------------------//
- if(m_pSMTP_Server.LogCommands){
- string lCmdTxt = SMTP_commandTxt.Replace("rn","<CRLF>");
- m_pLogWriter.AddEntry(lCmdTxt,this.SessionID,m_ConnectedIp,"C");
- }
- //---- End of loging ---------------------------------------------------------------------------//
- switch(SMTP_command)
- {
- case "HELO":
- HELO();
- break;
- case "EHLO":
- EHLO();
- break;
- case "AUTH":
- AUTH(Core.GetArgsText(SMTP_commandTxt,SMTP_command));
- break;
- case "MAIL":
- MAIL(Core.GetArgsText(SMTP_commandTxt,SMTP_command));
- break;
-
- case "RCPT":
- RCPT(Core.GetArgsText(SMTP_commandTxt,SMTP_command));
- break;
- case "DATA":
- DATA(Core.GetArgsText(SMTP_commandTxt,SMTP_command));
- break;
- case "RSET":
- RSET(Core.GetArgsText(SMTP_commandTxt,SMTP_command));
- break;
- case "VRFY":
- VRFY();
- break;
- case "EXPN":
- EXPN();
- break;
- case "HELP":
- HELP();
- break;
- case "NOOP":
- NOOP();
- return true;
-
- case "QUIT":
- QUIT(Core.GetArgsText(SMTP_commandTxt,SMTP_command));
- return true;
-
- default:
- SendData("500 command unrecognizedrn");
- //---- Check that maximum bad commands count isn't exceeded ---------------//
- if(m_BadCmdCount > m_pSMTP_Server.MaxBadCommands-1){
- SendData("421 Too many bad commands, closing transmission channelrn");
- return true;
- }
- m_BadCmdCount++;
- //-------------------------------------------------------------------------//
- // Core.WriteLog(m_ErrorFile,"SMTP SwitchCommand default:" + SMTP_command + " IP:" + m_ConnectedIp);
- break;
-
- }
- }
- catch(Exception x)
- {
- // Connection lost
- if(!m_pClientSocket.Connected){
- return true;
- }
-
- SendData("500 Unkown temp errorrn");
- m_pSMTP_Server.OnSysError(x,new System.Diagnostics.StackTrace());
- }
-
- return false;
- }
- #endregion
- #region function HELO
- private void HELO()
- {
- /* Rfc 2821 4.1.1.1
- These commands, and a "250 OK" reply to one of them, confirm that
- both the SMTP client and the SMTP server are in the initial state,
- that is, there is no transaction in progress and all state tables and
- buffers are cleared.
-
- Syntax:
- "HELO" SP Domain CRLF
- */
- SendData("250 " + Dns.GetHostName() + " Hello [" + m_ConnectedIp + "]rn");
- m_CmdValidator.Helo_ok = true;
- }
- #endregion
- #region function EHLO
- private void EHLO()
- {
- /* Rfc 2821 4.1.1.1
- These commands, and a "250 OK" reply to one of them, confirm that
- both the SMTP client and the SMTP server are in the initial state,
- that is, there is no transaction in progress and all state tables and
- buffers are cleared.
- */
- string reply = "" +
- "250-" + Dns.GetHostName() + " Hello [" + m_ConnectedIp + "]rn" +
- "250-SIZE " + m_pSMTP_Server.MaxMessageSize + "rn" +
- // "250-DSNrn" +
- // "250-HELPrn" +
- "250-AUTH LOGINrn" +
- "250 Okrn";
-
- SendData(reply);
-
- m_CmdValidator.Helo_ok = true;
- }
- #endregion
- #region function AUTH
- private void AUTH(string argsText)
- {
- /* Rfc 2554 AUTH --------------------------------------------------//
- Restrictions:
- After an AUTH command has successfully completed, no more AUTH
- commands may be issued in the same session. After a successful
- AUTH command completes, a server MUST reject any further AUTH
- commands with a 503 reply.
- */
- if(m_Authenticated){
- SendData("503 already authenticatedrn");
- return;
- }
-
-
- //------ Parse parameters -------------------------------------//
- string userName = "";
- string password = "";
- string[] param = argsText.Split(new char[]{' '});
- switch(param[0].ToUpper())
- {
- case "PLAIN":
- SendData("504 Unrecognized authentication type.rn");
- break;
- case "LOGIN":
- #region LOGIN authentication
- //---- AUTH = LOGIN ------------------------------
- // Note: all strings are base64 strings eg. VXNlcm5hbWU6 = UserName.
-
- // Query UserName
- SendData("334 VXNlcm5hbWU6rn");
- string userNameLine = "";
- if(Core.ReadLineFromSocket(m_pClientSocket,out userNameLine,m_pSMTP_Server.CommandIdleTimeOut) == ReadReplyCode.Ok){
- // Encode username from base64
- userName = System.Text.Encoding.ASCII.GetString(Convert.FromBase64String(userNameLine));
- //----- loging stuff --------------------------------------------------------------------------//
- if(m_pSMTP_Server.LogCommands){
- string uLogTxt = userNameLine.Replace("rn","<CRLF>");
- m_pLogWriter.AddEntry(uLogTxt,this.SessionID,m_ConnectedIp,"C");
- }
- //---- End of loging ---------------------------------------------------------------------------//
- // Query Password
- SendData("334 UGFzc3dvcmQ6rn");
- string passwordLine = "";
- if(Core.ReadLineFromSocket(m_pClientSocket,out passwordLine,m_pSMTP_Server.CommandIdleTimeOut) == ReadReplyCode.Ok){
- // Encode password from base64
- password = System.Text.Encoding.ASCII.GetString(Convert.FromBase64String(passwordLine));
-
- //----- loging stuff --------------------------------------------------------------------------//
- if(m_pSMTP_Server.LogCommands){
- string pLogTxt = passwordLine.Replace("rn","<CRLF>");
- m_pLogWriter.AddEntry(pLogTxt,this.SessionID,m_ConnectedIp,"C");
- }
- //---- End of loging ---------------------------------------------------------------------------//
- }
- else{
- SendData("500 Syntax error, command unrecognizedrn");
- }
- }
- else{
- SendData("500 Syntax error, command unrecognizedrn");
- }
-
- if(OnAuthUser(userName,password)){
- SendData("235 Authentication successful.rn");
- m_Authenticated = true;
- }
- else{
- SendData("535 Authentication failedrn");
- }
- #endregion
- break;
- case "DIGEST-MD5":
- SendData("504 Unrecognized authentication type.rn");
- break;
- default:
- SendData("504 Unrecognized authentication type.rn");
- break;
- }
- //-----------------------------------------------------------------//
-
- /* Rfc 2554 AUTH
- Remarks:
- If an AUTH command fails, the server MUST behave the same as if
- the client had not issued the AUTH command.
- */
- }
- #endregion
- #region function MAIL
- private void MAIL(string argsText)
- {
- /* RFC 2821 3.3
- NOTE:
- This command tells the SMTP-receiver that a new mail transaction is
- starting and to reset all its state tables and buffers, including any
- recipients or mail data. The <reverse-path> portion of the first or
- only argument contains the source mailbox (between "<" and ">"
- brackets), which can be used to report errors (see section 4.2 for a
- discussion of error reporting). If accepted, the SMTP server returns
- a 250 OK reply.
-
- MAIL FROM:<reverse-path> [SP <mail-parameters> ] <CRLF>
- reverse-path = "<" [ A-d-l ":" ] Mailbox ">"
- Mailbox = Local-part "@" Domain
-
- Examples:
- C: MAIL FROM:<ned@thor.innosoft.com>
- C: MAIL FROM:<ned@thor.innosoft.com> SIZE=500000
- */
- if(!m_CmdValidator.MayHandle_MAIL){
- if(m_CmdValidator.MailFrom_ok){
- SendData("503 Sender already specifiedrn");
- }
- else{
- SendData("503 Bad sequence of commandsrn");
- }
- return;
- }
- //------ Parse parameters -------------------------------------------------------------------//
- string reverse_path = "";
- string senderEmail = "";
- long messageSize = 0;
- bool isFromParam = false;
- _Parameter[] param = _ParamParser.Parse(argsText,new string[]{"FROM:","SIZE="},new string[]{":","="});
- foreach(_Parameter parameter in param){
- // Possible params:
- // FROM:
- // SIZE=
- switch(parameter.ParamName.ToUpper()) // paramInf[0] because of param syntax: pramName =/: value
- {
- //------ Required paramters -----//
- case "FROM:":
- if(parameter.ParamValue.Length == 0){
- SendData("501 Sender address isn't specified. Syntax:{MAIL FROM:<address> [SIZE=msgSize]}rn");
- return;
- }
- else{
- reverse_path = parameter.ParamValue;
- isFromParam = true;
- }
- break;
- //------ Optional parameters ---------------------//
- case "SIZE=":
- if(parameter.ParamValue.Length == 0){
- SendData("501 Size parameter isn't specified. Syntax:{MAIL FROM:<address> [SIZE=msgSize]}rn");
- return;
- }
- else{
- if(Core.IsNumber(parameter.ParamValue)){
- messageSize = Convert.ToInt64(parameter.ParamValue);
- }
- }
- break;
- default:
- SendData("501 Invalid parameter. Syntax:{MAIL FROM:<address> [SIZE=msgSize]}rn");
- return;
- }
- }
-
- // If required parameter 'FROM:' is missing
- if(!isFromParam){
- SendData("501 Required param FROM: is missing. Syntax:{MAIL FROM:<address> [SIZE=msgSize]}rn");
- return;
- }
- // Parse sender's email address
- senderEmail = Core.ParseEmailFromPath(reverse_path);
- //---------------------------------------------------------------------------------------------//
-
- // Check message size
- if(m_pSMTP_Server.MaxMessageSize > messageSize){
- // Check if sender is ok
- if(OnValidate_MailFrom(reverse_path,senderEmail)){
- SendData("250 OK <" + senderEmail + "> Sender okrn");
- m_Reverse_path = reverse_path;
-
- // See note above
- ResetState();
- m_CmdValidator.MailFrom_ok = true;
- }
- else{
- SendData("550 You are refused to send mail herern");
- }
- }
- else{
- SendData("552 Message exceeds allowed sizern");
- }
- }
- #endregion
- #region function RCPT
- private void RCPT(string argsText)
- {
- /* RFC 2821 4.1.1.3 RCPT
- NOTE:
- This command is used to identify an individual recipient of the mail
- data; multiple recipients are specified by multiple use of this
- command. The argument field contains a forward-path and may contain
- optional parameters.
-
- Relay hosts SHOULD strip or ignore source routes, and
- names MUST NOT be copied into the reverse-path.
-
- Example:
- RCPT TO:<@hosta.int,@jkl.org:userc@d.bar.org>
- will normally be sent directly on to host d.bar.org with envelope
- commands
- RCPT TO:<userc@d.bar.org>
- RCPT TO:<userc@d.bar.org> SIZE=40000
-
- RCPT TO:<forward-path> [ SP <rcpt-parameters> ] <CRLF>
- */
- /* RFC 2821 3.3
- If a RCPT command appears without a previous MAIL command,
- the server MUST return a 503 "Bad sequence of commands" response.
- */
- if(!m_CmdValidator.MayHandle_RCPT){
- SendData("503 Bad sequence of commandsrn");
- return;
- }
- // Check that recipient count isn't exceeded
- if(m_Forward_path.Count > m_pSMTP_Server.MaxRecipients){
- SendData("452 Too many recipientsrn");
- return;
- }
- //------ Parse parameters -------------------------------------------------------------------//
- string forward_path = "";
- string recipientEmail = "";
- long messageSize = 0;
- bool isToParam = false;
- _Parameter[] param = _ParamParser.Parse(argsText,new string[]{"TO:","SIZE="},new string[]{":","="});
- foreach(_Parameter parameter in param){
- // Possible params:
- // TO:
- // SIZE=
- switch(parameter.ParamName.ToUpper()) // paramInf[0] because of param syntax: pramName =/: value
- {
- //------ Required paramters -----//
- case "TO:":
- if(parameter.ParamValue.Length == 0){
- SendData("501 Recipient address isn't specified. Syntax:{RCPT TO:<address> [SIZE=msgSize]}rn");
- return;
- }
- else{
- forward_path = parameter.ParamValue;
- isToParam = true;
- }
- break;
- //------ Optional parameters ---------------------//
- case "SIZE=":
- if(parameter.ParamValue.Length == 0){
- SendData("501 Size parameter isn't specified. Syntax:{RCPT TO:<address> [SIZE=msgSize]}rn");
- return;
- }
- else{
- if(Core.IsNumber(parameter.ParamValue)){
- messageSize = Convert.ToInt64(parameter.ParamValue);
- }
- }
- break;
- default:
- SendData("501 Invalid parameter. Syntax:{RCPT TO:<address> [SIZE=msgSize]}rn");
- return;
- }
- }
-
- // If required parameter 'TO:' is missing
- if(!isToParam){
- SendData("501 Required param TO: is missing. Syntax:<RCPT TO:{address> [SIZE=msgSize]}rn");
- return;
- }
- // Parse recipient's email address
- recipientEmail = Core.ParseEmailFromPath(forward_path);
- //---------------------------------------------------------------------------------------------//
- // Check message size
- if(m_pSMTP_Server.MaxMessageSize > messageSize){
- // Check if email address is ok
- if(OnValidate_MailTo(forward_path,recipientEmail)){
- // Check if mailbox size isn't exceeded
- if(Validate_MailBoxSize(recipientEmail,messageSize)){
- // Store reciptient
- if(!m_Forward_path.Contains(recipientEmail)){
- m_Forward_path.Add(recipientEmail,forward_path);
- }
-
- SendData("250 OK <" + recipientEmail + "> Recipient okrn");
- m_CmdValidator.RcptTo_ok = true;
- }
- else{
- SendData("552 Mailbox size limit exceededrn");
- }
- }
- else{
- SendData("550 <" + recipientEmail + "> No such user herern");
- }
- }
- else{
- SendData("552 Message exceeds allowed sizern");
- }
- }
- #endregion
- #region function DATA
- private void DATA(string argsText)
- {
- /* RFC 2821 4.1.1
- NOTE:
- Several commands (RSET, DATA, QUIT) are specified as not permitting
- parameters. In the absence of specific extensions offered by the
- server and accepted by the client, clients MUST NOT send such
- parameters and servers SHOULD reject commands containing them as
- having invalid syntax.
- */
- if(argsText.Length > 0){
- SendData("500 Syntax error. Syntax:{DATA}rn");
- return;
- }
- /* RFC 2821 4.1.1.4 DATA
- NOTE:
- If accepted, the SMTP server returns a 354 Intermediate reply and
- considers all succeeding lines up to but not including the end of
- mail data indicator to be the message text. When the end of text is
- successfully received and stored the SMTP-receiver sends a 250 OK
- reply.
-
- The mail data is terminated by a line containing only a period, that
- is, the character sequence "<CRLF>.<CRLF>" (see section 4.5.2). This
- is the end of mail data indication.
-
-
- When the SMTP server accepts a message either for relaying or for
- final delivery, it inserts a trace record (also referred to
- interchangeably as a "time stamp line" or "Received" line) at the top
- of the mail data. This trace record indicates the identity of the
- host that sent the message, the identity of the host that received
- the message (and is inserting this time stamp), and the date and time
- the message was received. Relayed messages will have multiple time
- stamp lines. Details for formation of these lines, including their
- syntax, is specified in section 4.4.
-
- */
- /* RFC 2821 DATA
- NOTE:
- If there was no MAIL, or no RCPT, command, or all such commands
- were rejected, the server MAY return a "command out of sequence"
- (503) or "no valid recipients" (554) reply in response to the DATA
- command.
- */
- if(!m_CmdValidator.MayHandle_DATA){
- SendData("503 Bad sequence of commandsrn");
- return;
- }
- // reply: 354 Start mail input
- SendData("354 Start mail input; end with <CRLF>.<CRLF>rn");
-
- //---- Construct server headers -------------------------------------------------------------------//
- byte[] headers = null;
- string header = "Received: from " + this.m_ConnectedHostName + " (" + this.m_ConnectedIp + ")rn";
- header += "tby " + Dns.GetHostName() + " with SMTP; " + Core.GetDateTimeNow() + "rn";
-
- headers = System.Text.Encoding.ASCII.GetBytes(header.ToCharArray());
- //-------------------------------------------------------------------------------------------------//
- MemoryStream reply = null;
- ReadReplyCode replyCode = Core.ReadReplyFromSocket(m_pClientSocket,out reply,headers,m_pSMTP_Server.MaxMessageSize,m_pSMTP_Server.CommandIdleTimeOut,"rn.rn",".rn",false);
- if(replyCode == ReadReplyCode.Ok){
-
- //------- Do period handling and raise store event --------//
- // If line starts with '.', mail client adds additional '.',
- // remove them.
- using(MemoryStream msgStream = Core.DoPeriodHandling(reply,false)){
- reply.Close();
- // Raise NewMail event
- OnNewMail(msgStream);
- // Send ok - got message
- SendData("250 OKrn");
- }
- //----------------------------------------------------------//
-
-
- /* RFC 2821 4.1.1.4 DATA
- NOTE:
- Receipt of the end of mail data indication requires the server to
- process the stored mail transaction information. This processing
- consumes the information in the reverse-path buffer, the forward-path
- buffer, and the mail data buffer, and on the completion of this
- command these buffers are cleared.
- */
- ResetState();
- }
- else{
- if(replyCode == ReadReplyCode.LenghtExceeded){
- SendData("552 Requested mail action aborted: exceeded storage allocationrn");
- }
- else{
- SendData("500 Error mail not terminated with '.'rn");
- }
- }
- }
- #endregion
- #region function RSET
- private void RSET(string argsText)
- {
- /* RFC 2821 4.1.1
- NOTE:
- Several commands (RSET, DATA, QUIT) are specified as not permitting
- parameters. In the absence of specific extensions offered by the
- server and accepted by the client, clients MUST NOT send such
- parameters and servers SHOULD reject commands containing them as
- having invalid syntax.
- */
- if(argsText.Length > 0){
- SendData("500 Syntax error. Syntax:{RSET}rn");
- return;
- }
- /* RFC 2821 4.1.1.5 RESET (RSET)
- NOTE:
- This command specifies that the current mail transaction will be
- aborted. Any stored sender, recipients, and mail data MUST be
- discarded, and all buffers and state tables cleared. The receiver
- MUST send a "250 OK" reply to a RSET command with no arguments.
- */
-
- ResetState();
- SendData("250 OKrn");
- }
- #endregion
- #region function VRFY
- private void VRFY()
- {
- /* RFC 821 VRFY
- Example:
- S: VRFY Lumi
- R: 250 Ivar Lumi <ivx@lumisoft.ee>
-
- S: VRFY lum
- R: 550 String does not match anything.
- */
- // ToDo: Parse user, add new event for cheking user
- // SendData("250 OKrn");
- SendData("502 Command not implementedrn");
- }
- #endregion
- #region function NOOP
- private void NOOP()
- {
- /* RFC 2821 4.1.1.9 NOOP (NOOP)
- NOTE:
- This command does not affect any parameters or previously entered
- commands. It specifies no action other than that the receiver send
- an OK reply.
- */
- SendData("250 OKrn");
- }
- #endregion
- #region function QUIT
- private void QUIT(string argsText)
- {
- /* RFC 2821 4.1.1
- NOTE:
- Several commands (RSET, DATA, QUIT) are specified as not permitting
- parameters. In the absence of specific extensions offered by the
- server and accepted by the client, clients MUST NOT send such
- parameters and servers SHOULD reject commands containing them as
- having invalid syntax.
- */
- if(argsText.Length > 0){
- SendData("500 Syntax error. Syntax:<QUIT>rn");
- return;
- }
- /* RFC 2821 4.1.1.10 QUIT (QUIT)
- NOTE:
- This command specifies that the receiver MUST send an OK reply, and
- then close the transmission channel.
- */
- // reply: 221 - Close transmission cannel
- SendData("221 Service closing transmission channelrn");
- }
- #endregion
- //---- Optional commands
-
- #region function EXPN
- private void EXPN()
- {
- /* RFC 821 EXPN
- NOTE:
- This command asks the receiver to confirm that the argument
- identifies a mailing list, and if so, to return the
- membership of that list. The full name of the users (if
- known) and the fully specified mailboxes are returned in a
- multiline reply.
-
- Example:
- S: EXPN lsAll
- R: 250-ivar lumi <ivx@lumisoft.ee>
- R: 250-<willy@lumisoft.ee>
- R: 250 <kaido@lumisoft.ee>
- */
- // SendData("250 OKrn");
- SendData("502 Command not implementedrn");
- }
- #endregion
- #region function HELP
- private void HELP()
- {
- /* RFC 821 HELP
- NOTE:
- This command causes the receiver to send helpful information
- to the sender of the HELP command. The command may take an
- argument (e.g., any command name) and return more specific
- information as a response.
- */
- // SendData("250 OKrn");
- SendData("502 Command not implementedrn");
- }
- #endregion
- #region function ResetState
- private void ResetState()
- {
- //--- Reset variables
- m_Forward_path.Clear();
- m_Reverse_path = "";
- // m_Authenticated = false; // ??? must clear or not, no info.
- m_CmdValidator.Reset();
- m_CmdValidator.Helo_ok = true;
- }
- #endregion
- #region function SendData
-
- /// <summary>
- /// Sends data to socket.
- /// </summary>
- /// <param name="data">String data wich to send.</param>
- private void SendData(string data)
- {
- Byte[] byte_data = System.Text.Encoding.ASCII.GetBytes(data.ToCharArray());
-
- int nCount = m_pClientSocket.Send(byte_data,byte_data.Length,0);
- if(m_pSMTP_Server.LogCommands){
- data = data.Replace("rn","<CRLF>");
- m_pLogWriter.AddEntry(data,this.SessionID,m_ConnectedIp,"S");
- }
- }
- #endregion
-
- #region Properties Implementation
-
- /// <summary>
- /// Gets session ID.
- /// </summary>
- public string SessionID
- {
- get{ return m_SessionID; }
- }
- #endregion
- #region Events Implementaion
- #region function OnValidate_IpAddress
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="ConnectedIP"></param>
- /// <returns></returns>
- protected virtual bool OnValidate_IpAddress(string ConnectedIP)
- {
- ValidateIP_EventArgs oArg = new ValidateIP_EventArgs(ConnectedIP);
- if(this.ValidateIPAddress != null){
- this.ValidateIPAddress(this, oArg);
- }
- return oArg.Validated;
- }
- #endregion
- #region function OnAuthUser
- /// <summary>
- ///
- /// </summary>
- /// <param name="userName"></param>
- /// <param name="password"></param>
- /// <returns></returns>
- protected bool OnAuthUser(string userName,string password)
- {
- AuthUser_EventArgs oArgs = new AuthUser_EventArgs(m_ConnectedIp,userName,password,"",AuthType.Plain);
- if(this.AuthUser != null){
- this.AuthUser(this,oArgs);
- }
- return oArgs.Validated;
- }
- #endregion
- #region function OnValidate_MailFrom
- /// <summary>
- ///
- /// </summary>
- /// <param name="reverse_path"></param>
- /// <param name="email"></param>
- /// <returns></returns>
- protected virtual bool OnValidate_MailFrom(string reverse_path,string email)
- {
- ValidateSender_EventArgs oArg = new ValidateSender_EventArgs(m_ConnectedIp,email);
- if(this.ValidateMailFrom != null){
- this.ValidateMailFrom(this, oArg);
- }
- return true;
- }
- #endregion
- #region function OnValidate_MailTo
- /// <summary>
- ///
- /// </summary>
- /// <param name="forward_path"></param>
- /// <param name="email"></param>
- /// <returns></returns>
- protected virtual bool OnValidate_MailTo(string forward_path,string email)
- {
- ValidateRecipient_EventArgs oArg = new ValidateRecipient_EventArgs(m_ConnectedIp,email,m_Authenticated);
- if(this.ValidateMailTo != null){
- this.ValidateMailTo(this, oArg);
- }
- return oArg.Validated;
- }
- #endregion
- #region function Validate_MailBoxSize
- /// <summary>
- ///
- /// </summary>
- /// <param name="eAddress"></param>
- /// <param name="messageSize"></param>
- /// <returns></returns>
- protected bool Validate_MailBoxSize(string eAddress,long messageSize)
- {
- ValidateMailboxSize_EventArgs oArgs = new ValidateMailboxSize_EventArgs(eAddress,messageSize);
- if(this.ValidateMailboxSize != null){
- this.ValidateMailboxSize(this,oArgs);
- }
- return oArgs.IsValid;
- }
- #endregion
- #region function OnNewMail
- /// <summary>
- ///
- /// </summary>
- /// <param name="msgStream"></param>
- protected virtual void OnNewMail(MemoryStream msgStream)
- {
- string[] to = new string[m_Forward_path.Count];
- m_Forward_path.Values.CopyTo(to,0);
- // New mail event;
- NewMail_EventArgs oArg = new NewMail_EventArgs(m_ConnectedIp,m_ConnectedHostName,m_Reverse_path,to,msgStream);
- if(this.NewMailEvent != null){
- this.NewMailEvent(this, oArg);
- }
- }
- #endregion
- #endregion
- }
- }