POP3_Session.cs
上传用户:horngjaan
上传日期:2009-12-12
资源大小:2882k
文件大小:29k
源码类别:

Email服务器

开发平台:

C#

  1. using System;
  2. using System.IO;
  3. using System.ComponentModel;
  4. using System.Collections;
  5. using System.Diagnostics;
  6. using System.Net;
  7. using System.Net.Sockets;
  8. using System.Threading;
  9. using System.Windows.Forms;
  10. using LumiSoft.MailServer;
  11. namespace LumiSoft.MailServer.POP3
  12. {
  13. /// <summary>
  14. /// POP3 Session.
  15. /// </summary>
  16. internal class POP3_Session
  17. {
  18. private Socket        m_pClientSocket  = null;  // Referance to client Socket.
  19. private POP3_Server   m_pPOP3_Server   = null;  // Referance to POP3 server.
  20. private string        m_SessionID      = "";    // Holds session ID.
  21. private string        m_ConnectedIp    = "";    // Holds conneted computers IP.
  22. private string        m_UserName       = "";    // Holds loggedIn UserName.
  23. private string        m_Password       = "";    // Holds loggedIn Password.
  24. private bool          m_Authenticated  = false; // Holds authentication flag.
  25. private string        m_MD5_prefix     = "";    // Session MD5 prefix for APOP command
  26. private int           m_BadCmdCount    = 0;     // Holds number of bad commands.
  27. private POP3_Messages m_POP3_Messages  = null;
  28. private _LogWriter    m_pLogWriter     = null;
  29. // Events
  30. internal ValidateIPHandler      ValidateIPAddress  = null;
  31. internal AuthUserEventHandler   AuthUser           = null;
  32. internal GetMessagesInfoHandler GetMessgesList     = null;
  33. internal GetMessageHandler      GetMailEvent       = null;
  34. internal DeleteMessageHandler   DeleteMessageEvent = null;
  35. internal GetTopLinesHandler     GetTopLines        = null;
  36. /// <summary>
  37. /// Default constructor.
  38. /// </summary>
  39. /// <param name="clientSocket">Referance to socket.</param>
  40. /// <param name="server">Referance to POP3 server.</param>
  41. /// <param name="sessionID">Session ID which is assigned to this session.</param>
  42. /// <param name="logWriter">Log writer.</param>
  43. public POP3_Session(Socket clientSocket,POP3_Server server,string sessionID,_LogWriter logWriter)
  44. {
  45. m_pClientSocket = clientSocket;
  46. m_pPOP3_Server  = server;
  47. m_SessionID     = sessionID;
  48. m_POP3_Messages = new POP3_Messages();
  49. m_pLogWriter    = logWriter;
  50. }
  51. #region function StartProcessing
  52. /// <summary>
  53. /// Starts POP3 Session processing.
  54. /// </summary>
  55. public void StartProcessing()
  56. {
  57. try
  58. {
  59. // Store client ip 
  60. m_ConnectedIp = Core.ParseIP_from_EndPoint(m_pClientSocket.RemoteEndPoint.ToString());
  61. // Check if ip is allowed to connect this computer
  62. if(OnValidate_IpAddress(m_ConnectedIp)){
  63. // Notify that server is ready
  64. m_MD5_prefix = "<" + Guid.NewGuid().ToString().ToLower() + ">";
  65. SendData("+OK POP3 server ready " + m_MD5_prefix + "rn");
  66. // Create message loop
  67. int lastCmdTime = Environment.TickCount;
  68. string lastCmd  = "";
  69. while(true)
  70. {
  71. if(m_pClientSocket.Available > 0){
  72. ReadReplyCode replyCode = Core.ReadLineFromSocket(m_pClientSocket,out lastCmd,m_pPOP3_Server.CommandIdleTimeOut);
  73. if(replyCode == ReadReplyCode.Ok){
  74. if(SwitchCommand(lastCmd)){
  75. break;
  76. }
  77. }
  78. //----- There was bad command (usually cmd isn't terminated with '<CRLF>') -------------//
  79. else{
  80. //---- Check that maximum bad commands count isn't exceeded ---------------//
  81. if(m_BadCmdCount > m_pPOP3_Server.MaxBadCommands-1){
  82. SendData("-ERR Too many bad commandsrn");
  83. break;
  84. }
  85. m_BadCmdCount++;
  86. //-------------------------------------------------------------------------//
  87. switch(replyCode)
  88. {
  89. case ReadReplyCode.LenghtExceeded:
  90. SendData("-ERR Line too long.rn");
  91. break;
  92. case ReadReplyCode.TimeOut:
  93. SendData("-ERR Command timeout.rn");
  94. break;
  95. case ReadReplyCode.UnKnownError:
  96. SendData("-ERR UnKnown Error.rn");
  97. m_pPOP3_Server.OnSysError(new Exception("ReadReplyCode.UnKnownError"),new System.Diagnostics.StackTrace());
  98. break;
  99. }
  100. }
  101. //------- End of bad command ------------------------------------------------------------//
  102. // reset last command time
  103. lastCmdTime = Environment.TickCount;
  104. }
  105. //----- Session timeout stuff ------------------------------------------------//
  106. if(m_pClientSocket.Available == 0){
  107. if(Environment.TickCount > lastCmdTime + m_pPOP3_Server.SessionIdleTimeOut){
  108. // Notify for closing
  109. SendData("-ERR Session timeout, OK POP3 server signing offrn");
  110. break;
  111. }
  112. // Wait 100ms to save CPU, otherwise while loop may take 100% CPU. 
  113. Thread.Sleep(100);
  114. }
  115. //-----------------------------------------------------------------------------//
  116. }
  117. }
  118. }
  119. catch(ThreadInterruptedException x){
  120. }
  121. catch(Exception x){
  122. if(!m_pClientSocket.Connected){
  123. m_pLogWriter.AddEntry("Connection is aborted by client machine",this.SessionID,m_ConnectedIp,"x");
  124. return;
  125. }
  126. m_pPOP3_Server.OnSysError(x,new System.Diagnostics.StackTrace());
  127. }
  128. finally{
  129. m_pPOP3_Server.RemoveSession(this.SessionID,m_pLogWriter);
  130. m_pClientSocket.Close();
  131. // Write logs to log file, if needed
  132. if(m_pPOP3_Server.LogCommands){
  133. m_pLogWriter.Flush();
  134. }
  135. }
  136. }
  137. #endregion
  138. #region function SwitchCommand
  139. /// <summary>
  140. /// Parses and executes POP3 commmand.
  141. /// </summary>
  142. /// <param name="POP3_commandTxt">POP3 command text.</param>
  143. /// <returns>Returns true,if session must be terminated.</returns>
  144. private bool SwitchCommand(string POP3_commandTxt)
  145. {
  146. try
  147. {
  148. //---- Parse command --------------------------------------------------//
  149. string[] cmdParts = POP3_commandTxt.TrimStart().Split(new char[]{' '});
  150. string POP3_command = cmdParts[0];
  151.    POP3_command = POP3_command.ToUpper();
  152. //---------------------------------------------------------------------//
  153. //----- loging stuff --------------------------------------------------------------------------//
  154. if(m_pPOP3_Server.LogCommands){
  155. string lCmdTxt = POP3_commandTxt.Replace("rn","<CRLF>");
  156. m_pLogWriter.AddEntry(lCmdTxt,this.SessionID,m_ConnectedIp,"C");
  157. }
  158. //---- End of loging ---------------------------------------------------------------------------//
  159. switch(POP3_command)
  160. {
  161. case "USER":
  162. USER(Core.GetArgsText(POP3_commandTxt,"USER"));
  163. break;
  164. case "PASS":
  165. PASS(Core.GetArgsText(POP3_commandTxt,"PASS"));
  166. break;
  167. case "STAT":
  168. STAT();
  169. break;
  170. case "LIST":
  171. LIST(Core.GetArgsText(POP3_commandTxt,"LIST"));
  172. break;
  173. case "RETR":
  174. RETR(Core.GetArgsText(POP3_commandTxt,"RETR"));
  175. break;
  176. case "DELE":
  177. DELE(Core.GetArgsText(POP3_commandTxt,"DELE"));
  178. break;
  179. case "NOOP":
  180. NOOP();
  181. break;
  182. case "RSET":
  183. RSET();
  184. break;
  185. case "QUIT":
  186. QUIT();
  187. return true;
  188. //----- Optional commands ----- //
  189. case "UIDL":
  190. UIDL(Core.GetArgsText(POP3_commandTxt,"UIDL"));
  191. break;
  192. case "APOP":
  193. APOP(Core.GetArgsText(POP3_commandTxt,"APOP"));
  194. break;
  195. case "TOP":
  196. TOP(Core.GetArgsText(POP3_commandTxt,"TOP"));
  197. break;
  198. default:
  199. SendData("-ERR Invalid commandrn");
  200. //---- Check that maximum bad commands count isn't exceeded ---------------//
  201. if(m_BadCmdCount > m_pPOP3_Server.MaxBadCommands-1){
  202. SendData("421 Too many bad commands, closing transmission channelrn");
  203. return true;
  204. }
  205. m_BadCmdCount++;
  206. //-------------------------------------------------------------------------//
  207. break;
  208. }
  209. }
  210. catch(Exception x)
  211. {
  212. // Connection lost
  213. if(!m_pClientSocket.Connected){
  214. return true;
  215. }
  216. SendData("-ERR Unkown temp errorrn");
  217. m_pPOP3_Server.OnSysError(x,new System.Diagnostics.StackTrace());
  218. }
  219. return false;
  220. }
  221. #endregion
  222. #region function USER
  223. private void USER(string argsText)
  224. {
  225. /* RFC 1939 7. USER
  226. Arguments:
  227. a string identifying a mailbox (required), which is of
  228. significance ONLY to the server
  229. NOTE:
  230. If the POP3 server responds with a positive
  231. status indicator ("+OK"), then the client may issue
  232. either the PASS command to complete the authentication,
  233. or the QUIT command to terminate the POP3 session.
  234.  
  235. */
  236. if(m_Authenticated){
  237. SendData("-ERR You are already authenticatedrn");
  238. return;
  239. }
  240. if(m_UserName.Length > 0){
  241. SendData("-ERR username is already specified, please specify passwordrn");
  242. return;
  243. }
  244. string[] param = argsText.Split(new char[]{' '});
  245. // There must be only one parameter - userName
  246. if(argsText.Length > 0 && param.Length == 1){
  247. string userName = param[0];
  248. // Check if user isn't logged in already
  249. if(!m_pPOP3_Server.IsUserLoggedIn(userName)){
  250. SendData("+OK User:'" + userName + "' ok rn");
  251. m_UserName = userName;
  252. }
  253. else{
  254. SendData("-ERR User:'" + userName + "' already logged inrn");
  255. }
  256. }
  257. else{
  258. SendData("-ERR Syntax error. Syntax:{USER username}rn");
  259. }
  260. }
  261. #endregion
  262. #region function PASS
  263. private void PASS(string argsText)
  264. {
  265. /* RFC 7. PASS
  266. Arguments:
  267. a server/mailbox-specific password (required)
  268. Restrictions:
  269. may only be given in the AUTHORIZATION state immediately
  270. after a successful USER command
  271. NOTE:
  272. When the client issues the PASS command, the POP3 server
  273. uses the argument pair from the USER and PASS commands to
  274. determine if the client should be given access to the
  275. appropriate maildrop.
  276. Possible Responses:
  277. +OK maildrop locked and ready
  278. -ERR invalid password
  279. -ERR unable to lock maildrop
  280. */
  281. if(m_Authenticated){
  282. SendData("-ERR You are already authenticatedrn");
  283. return;
  284. }
  285. if(m_UserName.Length == 0){
  286. SendData("-ERR please specify username firstrn");
  287. return;
  288. }
  289. string[] param = argsText.Split(new char[]{' '});
  290. // There may be only one parameter - password
  291. if(param.Length == 1){
  292. string password = param[0];
  293. // Authenticate user
  294. if(OnAuthUser(m_UserName,password,"",AuthType.Plain)){
  295. SendData("+OK Password okrn");
  296. m_Password = password;
  297. m_Authenticated = true;
  298. // Get user messages info.
  299. OnGetMessagesInfo();
  300. }
  301. else{
  302. SendData("-ERR UserName or Password is incorrectrn");
  303. m_UserName = ""; // Reset userName !!!
  304. }
  305. }
  306. else{
  307. SendData("-ERR Syntax error. Syntax:{PASS userName}rn");
  308. }
  309. }
  310. #endregion
  311. #region function STAT
  312. private void STAT()
  313. {
  314. /* RFC 1939 5. STAT
  315. NOTE:
  316. The positive response consists of "+OK" followed by a single
  317. space, the number of messages in the maildrop, a single
  318. space, and the size of the maildrop in octets.
  319. Note that messages marked as deleted are not counted in
  320. either total.
  321.  
  322. Example:
  323. C: STAT
  324. S: +OK 2 320
  325. */
  326. if(!m_Authenticated){
  327. SendData("-ERR You must authenticate first.");
  328. return;
  329. }
  330. SendData("+OK " + m_POP3_Messages.Count.ToString() + " " + m_POP3_Messages.GetTotalMessagesSize() + "rn");
  331. }
  332. #endregion
  333. #region function LIST
  334. private void LIST(string argsText)
  335. {
  336. /* RFC 1939 5. LIST
  337. Arguments:
  338. a message-number (optional), which, if present, may NOT
  339. refer to a message marked as deleted
  340.  
  341. NOTE:
  342. If an argument was given and the POP3 server issues a
  343. positive response with a line containing information for
  344. that message.
  345. If no argument was given and the POP3 server issues a
  346. positive response, then the response given is multi-line.
  347. Note that messages marked as deleted are not listed.
  348. Examples:
  349. C: LIST
  350. S: +OK 2 messages (320 octets)
  351. S: 1 120
  352. S: 2 200
  353. S: .
  354. ...
  355. C: LIST 2
  356. S: +OK 2 200
  357. ...
  358. C: LIST 3
  359. S: -ERR no such message, only 2 messages in maildrop
  360.  
  361. */
  362. if(!m_Authenticated){
  363. SendData("-ERR You must authenticate first.");
  364. return;
  365. }
  366. string[] param = argsText.Split(new char[]{' '});
  367. // Argument isn't specified, multiline response.
  368. if(argsText.Length == 0){
  369. SendData("+OK " + m_POP3_Messages.Count.ToString() + " messagesrn");
  370. // Send message number and size for each message
  371. foreach(POP3_Message msg in m_POP3_Messages.ActiveMessages){
  372. SendData(msg.MessageNr.ToString() + " " + msg.MessageSize + "rn");
  373. }
  374. // ".<CRLF>" - means end of list
  375. SendData(".rn");
  376. }
  377. else{
  378. // If parameters specified,there may be only one parameter - messageNr
  379. if(param.Length == 1){
  380. // Check if messageNr is valid
  381. if(Core.IsNumber(param[0])){
  382. int messageNr = Convert.ToInt32(param[0]);
  383. if(m_POP3_Messages.MessageExists(messageNr)){
  384. POP3_Message msg = m_POP3_Messages[messageNr];
  385. SendData("+OK " + messageNr.ToString() + " " + msg.MessageSize + "rn");
  386. }
  387. else{
  388. SendData("-ERR no such message, or marked for deletionrn");
  389. }
  390. }
  391. else{
  392. SendData("-ERR message-number is invalidrn");
  393. }
  394. }
  395. else{
  396. SendData("-ERR Syntax error. Syntax:{LIST [messageNr]}rn");
  397. }
  398. }
  399. }
  400. #endregion
  401. #region function RETR
  402. private void RETR(string argsText)
  403. {
  404. /* RFC 1939 5. RETR
  405. Arguments:
  406. a message-number (required) which may NOT refer to a
  407. message marked as deleted
  408.  
  409. NOTE:
  410. If the POP3 server issues a positive response, then the
  411. response given is multi-line.  After the initial +OK, the
  412. POP3 server sends the message corresponding to the given
  413. message-number, being careful to byte-stuff the termination
  414. character (as with all multi-line responses).
  415. Example:
  416. C: RETR 1
  417. S: +OK 120 octets
  418. S: <the POP3 server sends the entire message here>
  419. S: .
  420. */
  421. if(!m_Authenticated){
  422. SendData("-ERR You must authenticate first.");
  423. return;
  424. }
  425. string[] param = argsText.Split(new char[]{' '});
  426. // There must be only one parameter - messageNr
  427. if(argsText.Length > 0 && param.Length == 1){
  428. // Check if messageNr is valid
  429. if(Core.IsNumber(param[0])){
  430. int messageNr = Convert.ToInt32(param[0]);
  431. if(m_POP3_Messages.MessageExists(messageNr)){
  432. POP3_Message msg = m_POP3_Messages[messageNr];
  433. SendData("+OK " + msg.MessageSize + " octetsrn");
  434. // Raise Event, request message
  435. byte[] message = OnGetMail(msg.MessageID);
  436. if(message != null){
  437. //------- Do period handling and send message -----------------------//
  438. // If line starts with '.', add additional '.'.(Read rfc for more info)
  439. using(MemoryStream msgStrm = Core.DoPeriodHandling(message,true)){
  440. // Send message to client
  441. if(msgStrm != null){
  442. SendData(msgStrm);
  443. }
  444. }
  445. //-------------------------------------------------------------------//
  446. }
  447. // "."<CRLF> - means end of message
  448. SendData(".rn");
  449. }
  450. else{
  451. SendData("-ERR no such messagern");
  452. }
  453. }
  454. else{
  455. SendData("-ERR message-number is invalidrn");
  456. }
  457. }
  458. else{
  459. SendData("-ERR Syntax error. Syntax:{RETR messageNr}rn");
  460. }
  461. }
  462. #endregion
  463. #region function DELE
  464. private void DELE(string argsText)
  465. {
  466. /* RFC 1939 5. DELE
  467. Arguments:
  468. a message-number (required) which may NOT refer to a
  469. message marked as deleted
  470.  
  471. NOTE:
  472. The POP3 server marks the message as deleted.  Any future
  473. reference to the message-number associated with the message
  474. in a POP3 command generates an error.  The POP3 server does
  475. not actually delete the message until the POP3 session
  476. enters the UPDATE state.
  477. */
  478. if(!m_Authenticated){
  479. SendData("-ERR You must authenticate first.");
  480. return;
  481. }
  482. string[] param = argsText.Split(new char[]{' '});
  483. // There must be only one parameter - messageNr
  484. if(argsText.Length > 0 && param.Length == 1){
  485. // Check if messageNr is valid
  486. if(Core.IsNumber(param[0])){
  487. int nr = Convert.ToInt32(param[0]);
  488. if(m_POP3_Messages.MessageExists(nr)){
  489. POP3_Message msg = m_POP3_Messages[nr];
  490. msg.MarkedForDelete = true;
  491. SendData("+OK marked for deletern");
  492. }
  493. else{
  494. SendData("-ERR no such messagern");
  495. }
  496. }
  497. else{
  498. SendData("-ERR message-number is invalidrn");
  499. }
  500. }
  501. else{
  502. SendData("-ERR Syntax error. Syntax:{DELE messageNr}rn");
  503. }
  504. }
  505. #endregion
  506. #region function NOOP
  507. private void NOOP()
  508. {
  509. /* RFC 1939 5. NOOP
  510. NOTE:
  511. The POP3 server does nothing, it merely replies with a
  512. positive response.
  513. */
  514. if(!m_Authenticated){
  515. SendData("-ERR You must authenticate first.");
  516. return;
  517. }
  518. SendData("+OKrn");
  519. }
  520. #endregion
  521. #region function RSET
  522. private void RSET()
  523. {
  524. /* RFC 1939 5. RSET
  525. Discussion:
  526. If any messages have been marked as deleted by the POP3
  527. server, they are unmarked.  The POP3 server then replies
  528. with a positive response.
  529. */
  530. if(!m_Authenticated){
  531. SendData("-ERR You must authenticate first.");
  532. return;
  533. }
  534. Reset();
  535. SendData("+OKrn");
  536. }
  537. #endregion
  538. #region function QUIT
  539. private void QUIT()
  540. {
  541. /* RFC 1939 6. QUIT
  542. NOTE:
  543. The POP3 server removes all messages marked as deleted
  544. from the maildrop and replies as to the status of this
  545. operation.  If there is an error, such as a resource
  546. shortage, encountered while removing messages, the
  547. maildrop may result in having some or none of the messages
  548. marked as deleted be removed.  In no case may the server
  549. remove any messages not marked as deleted.
  550. Whether the removal was successful or not, the server
  551. then releases any exclusive-access lock on the maildrop
  552. and closes the TCP connection.
  553. */
  554. Update();
  555. SendData("+OK POP3 server signing offrn");
  556. }
  557. #endregion
  558. //--- Optional commands
  559. #region function TOP
  560. private void TOP(string argsText)
  561. {
  562. /* RFC 1939 7. TOP
  563. Arguments:
  564. a message-number (required) which may NOT refer to to a
  565. message marked as deleted, and a non-negative number
  566. of lines (required)
  567. NOTE:
  568. If the POP3 server issues a positive response, then the
  569. response given is multi-line.  After the initial +OK, the
  570. POP3 server sends the headers of the message, the blank
  571. line separating the headers from the body, and then the
  572. number of lines of the indicated message's body, being
  573. careful to byte-stuff the termination character (as with
  574. all multi-line responses).
  575. Examples:
  576. C: TOP 1 10
  577. S: +OK
  578. S: <the POP3 server sends the headers of the
  579. message, a blank line, and the first 10 lines
  580. of the body of the message>
  581. S: .
  582.                 ...
  583. C: TOP 100 3
  584. S: -ERR no such message
  585.  
  586. */
  587. string[] param = argsText.Split(new char[]{' '});
  588. // There must be at two parameters - messageNr and nrLines
  589. if(param.Length == 2){
  590. // Check if messageNr and nrLines is valid
  591. if(Core.IsNumber(param[0]) && Core.IsNumber(param[1])){
  592. int messageNr = Convert.ToInt32(param[0]);
  593. if(m_POP3_Messages.MessageExists(messageNr)){
  594. POP3_Message msg = m_POP3_Messages[messageNr];
  595. byte[] lines = OnGetTopLines(msg.MessageID,Convert.ToInt32(param[1]));
  596. if(lines != null){
  597. SendData("+OKrn");
  598. SendData(lines);
  599. SendData(".rn");
  600. }
  601. else{
  602. SendData("-ERR no such messagern");
  603. }
  604. }
  605. else{
  606. SendData("-ERR no such messagern");
  607. }
  608. }
  609. else{
  610. SendData("-ERR message-number or number of lines is invalidrn");
  611. }
  612. }
  613. else{
  614. SendData("-ERR Syntax error. Syntax:{TOP messageNr nrLines}rn");
  615. }
  616. }
  617. #endregion
  618. #region function UIDL
  619. private void UIDL(string argsText)
  620. {
  621. /* RFC 1939 UIDL [msg]
  622. Arguments:
  623.     a message-number (optional), which, if present, may NOT
  624. refer to a message marked as deleted
  625. NOTE:
  626. If an argument was given and the POP3 server issues a positive
  627. response with a line containing information for that message.
  628. If no argument was given and the POP3 server issues a positive
  629. response, then the response given is multi-line.  After the
  630. initial +OK, for each message in the maildrop, the POP3 server
  631. responds with a line containing information for that message.
  632. Examples:
  633. C: UIDL
  634. S: +OK
  635. S: 1 whqtswO00WBw418f9t5JxYwZ
  636. S: 2 QhdPYR:00WBw1Ph7x7
  637. S: .
  638. ...
  639. C: UIDL 2
  640. S: +OK 2 QhdPYR:00WBw1Ph7x7
  641. ...
  642. C: UIDL 3
  643. S: -ERR no such message
  644. */
  645. string[] param = argsText.Split(new char[]{' '});
  646. // Argument isn't specified, multiline response.
  647. if(argsText.Length == 0){
  648. SendData("+OKrn");
  649. // List all messages UID's
  650. foreach(POP3_Message msg in m_POP3_Messages.ActiveMessages){
  651. SendData("+OK " + msg.MessageNr.ToString() + " " + msg.MessageID + "rn");
  652. }
  653. SendData(".rn");
  654. }
  655. else{
  656. // If parameters specified,there may be only one parameter - messageID
  657. if(param.Length == 1){
  658. // Check if messageNr is valid
  659. if(Core.IsNumber(param[0])){
  660. int messageNr = Convert.ToInt32(param[0]);
  661. if(m_POP3_Messages.MessageExists(messageNr)){
  662. POP3_Message msg = m_POP3_Messages[messageNr];
  663. SendData("+OK " + messageNr.ToString() + " " + msg.MessageID + "rn");
  664. }
  665. else{
  666. SendData("-ERR no such messagern");
  667. }
  668. }
  669. else{
  670. SendData("-ERR message-number is invalidrn");
  671. }
  672. }
  673. else{
  674. SendData("-ERR Syntax error. Syntax:{UIDL [messageNr]}rn");
  675. }
  676. }
  677. }
  678. #endregion
  679. #region function APOP
  680. private void APOP(string argsText)
  681. {
  682. /* RFC 1939 7. APOP
  683. Arguments:
  684. a string identifying a mailbox and a MD5 digest string
  685. (both required)
  686. NOTE:
  687. A POP3 server which implements the APOP command will
  688. include a timestamp in its banner greeting.  The syntax of
  689. the timestamp corresponds to the `msg-id' in [RFC822], and
  690. MUST be different each time the POP3 server issues a banner
  691. greeting.
  692. Examples:
  693. S: +OK POP3 server ready <1896.697170952@dbc.mtview.ca.us>
  694. C: APOP mrose c4c9334bac560ecc979e58001b3e22fb
  695. S: +OK maildrop has 1 message (369 octets)
  696. In this example, the shared  secret  is  the  string  `tan-
  697. staaf'.  Hence, the MD5 algorithm is applied to the string
  698. <1896.697170952@dbc.mtview.ca.us>tanstaaf
  699.  
  700. which produces a digest value of
  701.             c4c9334bac560ecc979e58001b3e22fb
  702.  
  703. */
  704. string[] param = argsText.Split(new char[]{' '});
  705. // There must be two params
  706. if(param.Length == 2){
  707. string userName   = param[0];
  708. string md5HexHash = param[1];
  709. // Authenticate user
  710. if(OnAuthUser(userName,md5HexHash,m_MD5_prefix,AuthType.APOP)){
  711. m_UserName = userName;
  712. SendData("+OK authentication was successfulrn");
  713. }
  714. else{
  715. SendData("-ERR authentication failedrn");
  716. }
  717. }
  718. else{
  719. SendData("-ERR syntax error. Syntax:{APOP userName md5HexHash}rn");
  720. }
  721. }
  722. #endregion
  723. #region function AUTH ???
  724. private void AUTH()
  725. {
  726. }
  727. #endregion
  728. #region function Reset
  729. private void Reset()
  730. {
  731. /* RFC 1939 5. RSET
  732. Discussion:
  733. If any messages have been marked as deleted by the POP3
  734. server, they are unmarked.
  735. */
  736. m_POP3_Messages.ResetDeleteFlags();
  737. }
  738. #endregion
  739. #region function Update
  740. private void Update()
  741. {
  742. /* RFC 1939 6.
  743. NOTE:
  744. When the client issues the QUIT command from the TRANSACTION state,
  745. the POP3 session enters the UPDATE state.  (Note that if the client
  746. issues the QUIT command from the AUTHORIZATION state, the POP3
  747. session terminates but does NOT enter the UPDATE state.)
  748. If a session terminates for some reason other than a client-issued
  749. QUIT command, the POP3 session does NOT enter the UPDATE state and
  750. MUST not remove any messages from the maildrop.
  751. */
  752. if(m_Authenticated){
  753. // Delete all message which are marked for deletion ---//
  754. foreach(POP3_Message msg in m_POP3_Messages.Messages){
  755. if(msg.MarkedForDelete){
  756. OnDeleteMessage(msg.MessageID);
  757. }
  758. }
  759. //-----------------------------------------------------//
  760. }
  761. }
  762. #endregion
  763. #region function SendData (3)
  764. private void SendData(string data)
  765. {
  766. Byte[] byte_data = System.Text.Encoding.ASCII.GetBytes(data.ToCharArray());
  767. int size = m_pClientSocket.Send(byte_data,byte_data.Length,0);
  768. if(m_pPOP3_Server.LogCommands){
  769. string reply = System.Text.Encoding.ASCII.GetString(byte_data);
  770. reply = reply.Replace("rn","<CRLF>");
  771. m_pLogWriter.AddEntry(reply,this.SessionID,m_ConnectedIp,"S");
  772. }
  773. }
  774. private void SendData(byte[] data)
  775. {
  776. using(MemoryStream strm = new MemoryStream(data)){
  777. SendData(strm);
  778. }
  779. }
  780. private void SendData(MemoryStream strm)
  781. {
  782. //---- split message to blocks -------------------------------//
  783. long totalSent = 0;
  784. while(strm.Position < strm.Length){
  785. int blockSize = 4024;
  786. byte[] dataBuf = new byte[blockSize];
  787. int nCount = strm.Read(dataBuf,0,blockSize);
  788. m_pClientSocket.Send(dataBuf,nCount,SocketFlags.None);
  789. totalSent += nCount;
  790. }
  791. //-------------------------------------------------------------//
  792. if(m_pPOP3_Server.LogCommands){
  793. m_pLogWriter.AddEntry("big binary " + strm.Length.ToString() + " bytes'",this.SessionID,m_ConnectedIp,"S");
  794. }
  795. }
  796. #endregion
  797. #region Properties Implementation
  798. /// <summary>
  799. /// Gets session ID.
  800. /// </summary>
  801. public string SessionID
  802. {
  803. get{ return m_SessionID; }
  804. }
  805. /// <summary>
  806. /// Gets userName.
  807. /// </summary>
  808. public string UserName
  809. {
  810. get{ return m_UserName; }
  811. }
  812. #endregion
  813. #region Events Implementation
  814. #region function OnValidate_IpAddress
  815. // ---- Events implementation -------------------------------//
  816. protected virtual bool OnValidate_IpAddress(string ConnectedIP) 
  817. {
  818. ValidateIP_EventArgs oArg = new ValidateIP_EventArgs(ConnectedIP);
  819. if(this.ValidateIPAddress != null){
  820. this.ValidateIPAddress(this, oArg);
  821. }
  822. return oArg.Validated;
  823. }
  824. #endregion
  825. #region function OnAuthUser
  826. /// <summary>
  827. /// 
  828. /// </summary>
  829. /// <param name="userName"></param>
  830. /// <param name="passwData"></param>
  831. /// <param name="data"></param>
  832. /// <param name="authType"></param>
  833. /// <returns></returns>
  834. protected virtual bool OnAuthUser(string userName,string passwData,string data,AuthType authType) 
  835. {
  836. AuthUser_EventArgs oArg = new AuthUser_EventArgs(m_ConnectedIp,userName,passwData,data,authType);
  837. if(this.AuthUser != null){
  838. this.AuthUser(this,oArg);
  839. }
  840. return oArg.Validated;
  841. }
  842. #endregion
  843. #region function OnGetMessagesInfo
  844. protected virtual void OnGetMessagesInfo() 
  845. {
  846. GetMessagesInfo_EventArgs oArg = new GetMessagesInfo_EventArgs(m_POP3_Messages,m_UserName);
  847. if(this.GetMessgesList != null){
  848. this.GetMessgesList(this, oArg);
  849. }
  850. }
  851. #endregion
  852. #region function OnGetMail
  853. protected virtual byte[] OnGetMail(string messageID) 
  854. {
  855. GetMessage_EventArgs oArg = new GetMessage_EventArgs(messageID,m_UserName);
  856. if(this.GetMailEvent != null){
  857. this.GetMailEvent(this, oArg);
  858. }
  859. return oArg.Message;
  860. }
  861. #endregion
  862. #region function OnDeleteMessage
  863. protected virtual bool OnDeleteMessage(string messageID) 
  864. {
  865. DeleteMessage_EventArgs oArg = new DeleteMessage_EventArgs(messageID,m_UserName);
  866. if(this.DeleteMessageEvent != null){
  867. this.DeleteMessageEvent(this, oArg);
  868. }
  869. return true;
  870. }
  871. #endregion
  872. #region function OnGetTopLines
  873. protected byte[] OnGetTopLines(string messageID,int nLines)
  874. {
  875. GetTopLines_Eventargs oArgs = new GetTopLines_Eventargs(m_UserName,messageID,nLines);
  876. if(this.GetTopLines != null){
  877. this.GetTopLines(this,oArgs);
  878. }
  879. return oArgs.LinesData;
  880. }
  881. #endregion
  882. #endregion
  883. }
  884. }