inetmail.cxx
上传用户:hzhsqp
上传日期:2007-01-06
资源大小:1600k
文件大小:25k
源码类别:

IP电话/视频会议

开发平台:

Visual C++

  1. /*
  2.  * inetmail.cxx
  3.  *
  4.  * Internet Mail classes.
  5.  *
  6.  * Portable Windows Library
  7.  *
  8.  * Copyright (c) 1993-1998 Equivalence Pty. Ltd.
  9.  *
  10.  * The contents of this file are subject to the Mozilla Public License
  11.  * Version 1.0 (the "License"); you may not use this file except in
  12.  * compliance with the License. You may obtain a copy of the License at
  13.  * http://www.mozilla.org/MPL/
  14.  *
  15.  * Software distributed under the License is distributed on an "AS IS"
  16.  * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
  17.  * the License for the specific language governing rights and limitations
  18.  * under the License.
  19.  *
  20.  * The Original Code is Portable Windows Library.
  21.  *
  22.  * The Initial Developer of the Original Code is Equivalence Pty. Ltd.
  23.  *
  24.  * Portions are Copyright (C) 1993 Free Software Foundation, Inc.
  25.  * All Rights Reserved.
  26.  *
  27.  * Contributor(s): ______________________________________.
  28.  *
  29.  * $Log: inetmail.cxx,v $
  30.  * Revision 1.13  2000/06/21 01:01:22  robertj
  31.  * AIX port, thanks Wolfgang Platzer (wolfgang.platzer@infonova.at).
  32.  *
  33.  * Revision 1.12  1998/11/30 04:52:01  robertj
  34.  * New directory structure
  35.  *
  36.  * Revision 1.11  1998/09/23 06:22:18  robertj
  37.  * Added open source copyright license.
  38.  *
  39.  * Revision 1.10  1998/01/26 02:49:20  robertj
  40.  * GNU support.
  41.  *
  42.  * Revision 1.9  1997/07/14 11:47:14  robertj
  43.  * Added "const" to numerous variables.
  44.  *
  45.  * Revision 1.8  1996/12/21 01:24:39  robertj
  46.  * Added missing open message to smtp server.
  47.  *
  48.  * Revision 1.7  1996/09/14 13:18:03  robertj
  49.  * Renamed file and changed to be a protocol off new indirect channel to separate
  50.  *   the protocol from the low level byte transport channel.
  51.  *
  52.  * Revision 1.6  1996/07/27 04:12:45  robertj
  53.  * Redesign and reimplement of mail sockets.
  54.  *
  55.  * Revision 1.5  1996/06/28 13:22:09  robertj
  56.  * Changed SMTP incoming message handler so can tell when started, processing or ended message.
  57.  *
  58.  * Revision 1.4  1996/05/26 03:46:51  robertj
  59.  * Compatibility to GNU 2.7.x
  60.  *
  61.  * Revision 1.3  1996/03/18 13:33:16  robertj
  62.  * Fixed incompatibilities to GNU compiler where PINDEX != int.
  63.  *
  64.  * Revision 1.2  1996/03/16 04:51:28  robertj
  65.  * Changed lastResponseCode to an integer.
  66.  * Added ParseReponse() for splitting reponse line into code and info.
  67.  *
  68.  * Revision 1.1  1996/03/04 12:12:51  robertj
  69.  * Initial revision
  70.  *
  71.  */
  72. #ifdef __GNUC__
  73. #pragma implementation "inetmail.h"
  74. #endif
  75. #include <ptlib.h>
  76. #include <ptlib/sockets.h>
  77. #include <ptclib/inetmail.h>
  78. static const PString CRLF = "rn";
  79. static const PString CRLFdotCRLF = "rn.rn";
  80. //////////////////////////////////////////////////////////////////////////////
  81. // PSMTP
  82. static char const * const SMTPCommands[PSMTP::NumCommands] = {
  83.   "HELO", "EHLO", "QUIT", "HELP", "NOOP",
  84.   "TURN", "RSET", "VRFY", "EXPN", "RCPT",
  85.   "MAIL", "SEND", "SAML", "SOML", "DATA"
  86. };
  87. PSMTP::PSMTP()
  88.   : PInternetProtocol("smtp 25", NumCommands, SMTPCommands)
  89. {
  90. }
  91. //////////////////////////////////////////////////////////////////////////////
  92. // PSMTPClient
  93. PSMTPClient::PSMTPClient()
  94. {
  95.   haveHello = FALSE;
  96.   extendedHello = FALSE;
  97.   eightBitMIME = FALSE;
  98. }
  99. PSMTPClient::~PSMTPClient()
  100. {
  101.   Close();
  102. }
  103. BOOL PSMTPClient::OnOpen()
  104. {
  105.   return ReadResponse() && lastResponseCode/100 == 2;
  106. }
  107. BOOL PSMTPClient::Close()
  108. {
  109.   BOOL ok = TRUE;
  110.   if (IsOpen() && haveHello) {
  111.     SetReadTimeout(60000);
  112.     ok = ExecuteCommand(QUIT, "") == '2';
  113.   }
  114.   return PInternetProtocol::Close() && ok;
  115. }
  116. BOOL PSMTPClient::BeginMessage(const PString & from,
  117.                                const PString & to,
  118.                                BOOL useEightBitMIME)
  119. {
  120.   fromAddress = from;
  121.   toNames.RemoveAll();
  122.   toNames.AppendString(to);
  123.   eightBitMIME = useEightBitMIME;
  124.   return _BeginMessage();
  125. }
  126. BOOL PSMTPClient::BeginMessage(const PString & from,
  127.                                const PStringList & toList,
  128.                                BOOL useEightBitMIME)
  129. {
  130.   fromAddress = from;
  131.   toNames = toList;
  132.   eightBitMIME = useEightBitMIME;
  133.   return _BeginMessage();
  134. }
  135. BOOL PSMTPClient::_BeginMessage()
  136. {
  137.   PString localHost;
  138.   PString peerHost;
  139.   PIPSocket * socket = GetSocket();
  140.   if (socket != NULL) {
  141.     localHost = socket->GetLocalHostName();
  142.     peerHost = socket->GetPeerHostName();
  143.   }
  144.   if (!haveHello) {
  145.     if (ExecuteCommand(EHLO, localHost) == '2')
  146.       haveHello = extendedHello = TRUE;
  147.   }
  148.   if (!haveHello) {
  149.     extendedHello = FALSE;
  150.     if (eightBitMIME)
  151.       return FALSE;
  152.     if (ExecuteCommand(HELO, localHost) != '2')
  153.       return FALSE;
  154.     haveHello = TRUE;
  155.   }
  156.   if (fromAddress[0] != '"' && fromAddress.Find(' ') != P_MAX_INDEX)
  157.     fromAddress = '"' + fromAddress + '"';
  158.   if (!localHost && fromAddress.Find('@') == P_MAX_INDEX)
  159.     fromAddress += '@' + localHost;
  160.   if (ExecuteCommand(MAIL, "FROM:<" + fromAddress + '>') != '2')
  161.     return FALSE;
  162.   for (PINDEX i = 0; i < toNames.GetSize(); i++) {
  163.     if (!peerHost && toNames[i].Find('@') == P_MAX_INDEX)
  164.       toNames[i] += '@' + peerHost;
  165.     if (ExecuteCommand(RCPT, "TO:<" + toNames[i] + '>') != '2')
  166.       return FALSE;
  167.   }
  168.   if (ExecuteCommand(DATA, PString()) != '3')
  169.     return FALSE;
  170.   stuffingState = StuffIdle;
  171.   return TRUE;
  172. }
  173. BOOL PSMTPClient::EndMessage()
  174. {
  175.   flush();
  176.   stuffingState = DontStuff;
  177.   if (!WriteString(CRLFdotCRLF))
  178.     return FALSE;
  179.   return ReadResponse() && lastResponseCode/100 == 2;
  180. }
  181. //////////////////////////////////////////////////////////////////////////////
  182. // PSMTPServer
  183. PSMTPServer::PSMTPServer()
  184. {
  185.   extendedHello = FALSE;
  186.   eightBitMIME = FALSE;
  187.   messageBufferSize = 30000;
  188.   ServerReset();
  189. }
  190. void PSMTPServer::ServerReset()
  191. {
  192.   eightBitMIME = FALSE;
  193.   sendCommand = WasMAIL;
  194.   fromAddress = PString();
  195.   toNames.RemoveAll();
  196. }
  197. BOOL PSMTPServer::OnOpen()
  198. {
  199.   return WriteResponse(220, PIPSocket::GetHostName() + "ESMTP server ready");
  200. }
  201. BOOL PSMTPServer::ProcessCommand()
  202. {
  203.   PString args;
  204.   PINDEX num;
  205.   if (!ReadCommand(num, args))
  206.     return FALSE;
  207.   switch (num) {
  208.     case HELO :
  209.       OnHELO(args);
  210.       break;
  211.     case EHLO :
  212.       OnEHLO(args);
  213.       break;
  214.     case QUIT :
  215.       OnQUIT();
  216.       return FALSE;
  217.     case NOOP :
  218.       OnNOOP();
  219.       break;
  220.     case TURN :
  221.       OnTURN();
  222.       break;
  223.     case RSET :
  224.       OnRSET();
  225.       break;
  226.     case VRFY :
  227.       OnVRFY(args);
  228.       break;
  229.     case EXPN :
  230.       OnEXPN(args);
  231.       break;
  232.     case RCPT :
  233.       OnRCPT(args);
  234.       break;
  235.     case MAIL :
  236.       OnMAIL(args);
  237.       break;
  238.     case SEND :
  239.       OnSEND(args);
  240.       break;
  241.     case SAML :
  242.       OnSAML(args);
  243.       break;
  244.     case SOML :
  245.       OnSOML(args);
  246.       break;
  247.     case DATA :
  248.       OnDATA();
  249.       break;
  250.     default :
  251.       return OnUnknown(args);
  252.   }
  253.   return TRUE;
  254. }
  255. void PSMTPServer::OnHELO(const PCaselessString & remoteHost)
  256. {
  257.   extendedHello = FALSE;
  258.   ServerReset();
  259.   PCaselessString peerHost;
  260.   PIPSocket * socket = GetSocket();
  261.   if (socket != NULL)
  262.     peerHost = socket->GetPeerHostName();
  263.   PString response = PIPSocket::GetHostName() & "Hello" & peerHost + ", ";
  264.   if (remoteHost == peerHost)
  265.     response += "pleased to meet you.";
  266.   else if (remoteHost.IsEmpty())
  267.     response += "why do you wish to remain anonymous?";
  268.   else
  269.     response += "why do you wish to call yourself "" + remoteHost + ""?";
  270.   WriteResponse(250, response);
  271. }
  272. void PSMTPServer::OnEHLO(const PCaselessString & remoteHost)
  273. {
  274.   extendedHello = TRUE;
  275.   ServerReset();
  276.   PCaselessString peerHost;
  277.   PIPSocket * socket = GetSocket();
  278.   if (socket != NULL)
  279.     peerHost = socket->GetPeerHostName();
  280.   PString response = PIPSocket::GetHostName() & "Hello" & peerHost + ", ";
  281.   if (remoteHost == peerHost)
  282.     response += ", pleased to meet you.";
  283.   else if (remoteHost.IsEmpty())
  284.     response += "why do you wish to remain anonymous?";
  285.   else
  286.     response += "why do you wish to call yourself "" + remoteHost + ""?";
  287.   response += "nHELPnVERBnONEXnMULTnEXPNnTICKn8BITMIMEn";
  288.   WriteResponse(250, response);
  289. }
  290. void PSMTPServer::OnQUIT()
  291. {
  292.   WriteResponse(221, PIPSocket::GetHostName() + " closing connection, goodbye.");
  293.   Close();
  294. }
  295. void PSMTPServer::OnHELP()
  296. {
  297.   WriteResponse(214, "No help here.");
  298. }
  299. void PSMTPServer::OnNOOP()
  300. {
  301.   WriteResponse(250, "Ok");
  302. }
  303. void PSMTPServer::OnTURN()
  304. {
  305.   WriteResponse(502, "I don't do that yet. Sorry.");
  306. }
  307. void PSMTPServer::OnRSET()
  308. {
  309.   ServerReset();
  310.   WriteResponse(250, "Reset state.");
  311. }
  312. void PSMTPServer::OnVRFY(const PCaselessString & name)
  313. {
  314.   PString expandedName;
  315.   switch (LookUpName(name, expandedName)) {
  316.     case AmbiguousUser :
  317.       WriteResponse(553, "User "" + name + "" ambiguous.");
  318.       break;
  319.     case ValidUser :
  320.       WriteResponse(250, expandedName);
  321.       break;
  322.     case UnknownUser :
  323.       WriteResponse(550, "Name "" + name + "" does not match anything.");
  324.       break;
  325.     default :
  326.       WriteResponse(550, "Error verifying user "" + name + "".");
  327.   }
  328. }
  329. void PSMTPServer::OnEXPN(const PCaselessString &)
  330. {
  331.   WriteResponse(502, "I don't do that. Sorry.");
  332. }
  333. static PINDEX ParseMailPath(const PCaselessString & args,
  334.                             const PCaselessString & subCmd,
  335.                             PString & name,
  336.                             PString & domain,
  337.                             PString & forwardList)
  338. {
  339.   PINDEX colon = args.Find(':');
  340.   if (colon == P_MAX_INDEX)
  341.     return 0;
  342.   PCaselessString word = args.Left(colon).Trim();
  343.   if (subCmd != word)
  344.     return 0;
  345.   PINDEX leftAngle = args.Find('<', colon);
  346.   if (leftAngle == P_MAX_INDEX)
  347.     return 0;
  348.   PINDEX finishQuote;
  349.   PINDEX startQuote = args.Find('"', leftAngle);
  350.   if (startQuote == P_MAX_INDEX) {
  351.     colon = args.Find(':', leftAngle);
  352.     if (colon == P_MAX_INDEX)
  353.       colon = leftAngle;
  354.     finishQuote = startQuote = colon+1;
  355.   }
  356.   else {
  357.     finishQuote = args.Find('"', startQuote+1);
  358.     if (finishQuote == P_MAX_INDEX)
  359.       finishQuote = startQuote;
  360.     colon = args.Find(':', leftAngle);
  361.     if (colon > startQuote)
  362.       colon = leftAngle;
  363.   }
  364.   PINDEX rightAngle = args.Find('>', finishQuote);
  365.   if (rightAngle == P_MAX_INDEX)
  366.     return 0;
  367.   PINDEX at = args.Find('@', finishQuote);
  368.   if (at > rightAngle)
  369.     at = rightAngle;
  370.   if (startQuote == finishQuote)
  371.     finishQuote = at;
  372.   name = args(startQuote, finishQuote-1);
  373.   domain = args(at+1, rightAngle-1);
  374.   forwardList = args(leftAngle+1, colon-1);
  375.   return rightAngle+1;
  376. }
  377. void PSMTPServer::OnRCPT(const PCaselessString & recipient)
  378. {
  379.   PCaselessString toName;
  380.   PCaselessString toDomain;
  381.   PCaselessString forwardList;
  382.   if (ParseMailPath(recipient, "to", toName, toDomain, forwardList) == 0)
  383.     WriteResponse(501, "Syntax error.");
  384.   else {
  385.     switch (ForwardDomain(toDomain, forwardList)) {
  386.       case CannotForward :
  387.         WriteResponse(550, "Cannot do forwarding.");
  388.         break;
  389.       case WillForward :
  390.         if (!forwardList)
  391.           forwardList += ":";
  392.         forwardList += toName;
  393.         if (!toDomain)
  394.           forwardList += "@" + toDomain;
  395.         toNames.AppendString(toName);
  396.         toDomains.AppendString(forwardList);
  397.         break;
  398.       case LocalDomain :
  399.       {
  400.         PString expandedName;
  401.         switch (LookUpName(toName, expandedName)) {
  402.           case ValidUser :
  403.             WriteResponse(250, "Recipient " + toName + " Ok");
  404.             toNames.AppendString(toName);
  405.             toDomains.AppendString("");
  406.             break;
  407.           case AmbiguousUser :
  408.             WriteResponse(553, "User ambiguous.");
  409.             break;
  410.           case UnknownUser :
  411.             WriteResponse(550, "User unknown.");
  412.             break;
  413.           default :
  414.             WriteResponse(550, "Error verifying user.");
  415.         }
  416.       }
  417.     }
  418.   }
  419. }
  420. void PSMTPServer::OnMAIL(const PCaselessString & sender)
  421. {
  422.   sendCommand = WasMAIL;
  423.   OnSendMail(sender);
  424. }
  425. void PSMTPServer::OnSEND(const PCaselessString & sender)
  426. {
  427.   sendCommand = WasSEND;
  428.   OnSendMail(sender);
  429. }
  430. void PSMTPServer::OnSAML(const PCaselessString & sender)
  431. {
  432.   sendCommand = WasSAML;
  433.   OnSendMail(sender);
  434. }
  435. void PSMTPServer::OnSOML(const PCaselessString & sender)
  436. {
  437.   sendCommand = WasSOML;
  438.   OnSendMail(sender);
  439. }
  440. void PSMTPServer::OnSendMail(const PCaselessString & sender)
  441. {
  442.   if (!fromAddress) {
  443.     WriteResponse(503, "Sender already specified.");
  444.     return;
  445.   }
  446.   PString fromDomain;
  447.   PINDEX extendedArgPos = ParseMailPath(sender, "from", fromAddress, fromDomain, fromPath);
  448.   if (extendedArgPos == 0 || fromAddress.IsEmpty()) {
  449.     WriteResponse(501, "Syntax error.");
  450.     return;
  451.   }
  452.   fromAddress += fromDomain;
  453.   if (extendedHello) {
  454.     PINDEX equalPos = sender.Find('=', extendedArgPos);
  455.     PCaselessString body = sender(extendedArgPos, equalPos).Trim();
  456.     PCaselessString mime = sender.Mid(equalPos+1).Trim();
  457.     eightBitMIME = (body == "BODY" && mime == "8BITMIME");
  458.   }
  459.   PString response = "Sender " + fromAddress;
  460.   if (eightBitMIME)
  461.     response += " and 8BITMIME";
  462.   WriteResponse(250, response + " Ok");
  463. }
  464. void PSMTPServer::OnDATA()
  465. {
  466.   if (fromAddress.IsEmpty()) {
  467.     WriteResponse(503, "Need a valid MAIL command.");
  468.     return;
  469.   }
  470.   if (toNames.GetSize() == 0) {
  471.     WriteResponse(503, "Need a valid RCPT command.");
  472.     return;
  473.   }
  474.   // Ok, everything is ready to start the message.
  475.   if (!WriteResponse(354,
  476.         eightBitMIME ? "Enter 8BITMIME message, terminate with '<CR><LF>.<CR><LF>'."
  477.                      : "Enter mail, terminate with '.' alone on a line."))
  478.     return;
  479.   endMIMEDetectState = eightBitMIME ? StuffIdle : DontStuff;
  480.   BOOL ok = TRUE;
  481.   BOOL completed = FALSE;
  482.   BOOL starting = TRUE;
  483.   while (ok && !completed) {
  484.     PCharArray buffer;
  485.     if (eightBitMIME)
  486.       ok = OnMIMEData(buffer, completed);
  487.     else
  488.       ok = OnTextData(buffer, completed);
  489.     if (ok) {
  490.       ok = HandleMessage(buffer, starting, completed);
  491.       starting = FALSE;
  492.     }
  493.   }
  494.   if (ok)
  495.     WriteResponse(250, "Message received Ok.");
  496.   else
  497.     WriteResponse(554, "Message storage failed.");
  498. }
  499. BOOL PSMTPServer::OnUnknown(const PCaselessString & command)
  500. {
  501.   WriteResponse(500, "Command "" + command + "" unrecognised.");
  502.   return TRUE;
  503. }
  504. BOOL PSMTPServer::OnTextData(PCharArray & buffer, BOOL & completed)
  505. {
  506.   PString line;
  507.   while (ReadLine(line)) {
  508.     PINDEX len = line.GetLength();
  509.     if (len == 1 && line[0] == '.') {
  510.       completed = TRUE;
  511.       return TRUE;
  512.     }
  513.     PINDEX start = (len > 1 && line[0] == '.' && line[1] == '.') ? 1 : 0;
  514.     PINDEX size = buffer.GetSize();
  515.     len -= start;
  516.     memcpy(buffer.GetPointer(size + len + 2) + size,
  517.            ((const char *)line)+start, len);
  518.     size += len;
  519.     buffer[size++] = 'r';
  520.     buffer[size++] = 'n';
  521.     if (size > messageBufferSize)
  522.       return TRUE;
  523.   }
  524.   return FALSE;
  525. }
  526. BOOL PSMTPServer::OnMIMEData(PCharArray & buffer, BOOL & completed)
  527. {
  528.   PINDEX count = 0;
  529.   int c;
  530.   while ((c = ReadChar()) >= 0) {
  531.     if (count >= buffer.GetSize())
  532.       buffer.SetSize(count + 100);
  533.     switch (endMIMEDetectState) {
  534.       case StuffIdle :
  535.         buffer[count++] = (char)c;
  536.         break;
  537.       case StuffCR :
  538.         endMIMEDetectState = c != 'n' ? StuffIdle : StuffCRLF;
  539.         buffer[count++] = (char)c;
  540.         break;
  541.       case StuffCRLF :
  542.         if (c == '.')
  543.           endMIMEDetectState = StuffCRLFdot;
  544.         else {
  545.           endMIMEDetectState = StuffIdle;
  546.           buffer[count++] = (char)c;
  547.         }
  548.         break;
  549.       case StuffCRLFdot :
  550.         switch (c) {
  551.           case 'r' :
  552.             endMIMEDetectState = StuffCRLFdotCR;
  553.             break;
  554.           case '.' :
  555.             endMIMEDetectState = StuffIdle;
  556.             buffer[count++] = (char)c;
  557.             break;
  558.           default :
  559.             endMIMEDetectState = StuffIdle;
  560.             buffer[count++] = '.';
  561.             buffer[count++] = (char)c;
  562.         }
  563.         break;
  564.       case StuffCRLFdotCR :
  565.         if (c == 'n') {
  566.           completed = TRUE;
  567.           return TRUE;
  568.         }
  569.         buffer[count++] = '.';
  570.         buffer[count++] = 'r';
  571.         buffer[count++] = (char)c;
  572.         endMIMEDetectState = StuffIdle;
  573.       default :
  574.         PAssertAlways("Illegal SMTP state");
  575.     }
  576.     if (count > messageBufferSize) {
  577.       buffer.SetSize(messageBufferSize);
  578.       return TRUE;
  579.     }
  580.   }
  581.   return FALSE;
  582. }
  583. PSMTPServer::ForwardResult PSMTPServer::ForwardDomain(PCaselessString & userDomain,
  584.                                                       PCaselessString & forwardDomainList)
  585. {
  586.   return userDomain.IsEmpty() && forwardDomainList.IsEmpty() ? LocalDomain : CannotForward;
  587. }
  588. PSMTPServer::LookUpResult PSMTPServer::LookUpName(const PCaselessString &,
  589.                                                   PString & expandedName)
  590. {
  591.   expandedName = PString();
  592.   return LookUpError;
  593. }
  594. BOOL PSMTPServer::HandleMessage(PCharArray &, BOOL, BOOL)
  595. {
  596.   return FALSE;
  597. }
  598. //////////////////////////////////////////////////////////////////////////////
  599. // PPOP3
  600. static char const * const POP3Commands[PPOP3::NumCommands] = {
  601.   "USER", "PASS", "QUIT", "RSET", "NOOP", "STAT",
  602.   "LIST", "RETR", "DELE", "APOP", "TOP",  "UIDL"
  603. };
  604. PString PPOP3::okResponse = "+OK";
  605. PString PPOP3::errResponse = "-ERR";
  606. PPOP3::PPOP3()
  607.   : PInternetProtocol("pop3 110", NumCommands, POP3Commands)
  608. {
  609. }
  610. PINDEX PPOP3::ParseResponse(const PString & line)
  611. {
  612.   lastResponseCode = line[0] == '+';
  613.   PINDEX endCode = line.Find(' ');
  614.   if (endCode != P_MAX_INDEX)
  615.     lastResponseInfo = line.Mid(endCode+1);
  616.   else
  617.     lastResponseInfo = PString();
  618.   return 0;
  619. }
  620. //////////////////////////////////////////////////////////////////////////////
  621. // PPOP3Client
  622. PPOP3Client::PPOP3Client()
  623. {
  624.   loggedIn = FALSE;
  625. }
  626. PPOP3Client::~PPOP3Client()
  627. {
  628.   Close();
  629. }
  630. BOOL PPOP3Client::OnOpen()
  631. {
  632.   return ReadResponse() && lastResponseCode > 0;
  633. }
  634. BOOL PPOP3Client::Close()
  635. {
  636.   BOOL ok = TRUE;
  637.   if (IsOpen() && loggedIn) {
  638.     SetReadTimeout(60000);
  639.     ok = ExecuteCommand(QUIT, "") > 0;
  640.   }
  641.   return PInternetProtocol::Close() && ok;
  642. }
  643. BOOL PPOP3Client::LogIn(const PString & username, const PString & password)
  644. {
  645.   if (ExecuteCommand(USER, username) <= 0)
  646.     return FALSE;
  647.   if (ExecuteCommand(PASS, password) <= 0)
  648.     return FALSE;
  649.   loggedIn = TRUE;
  650.   return TRUE;
  651. }
  652. int PPOP3Client::GetMessageCount()
  653. {
  654.   if (ExecuteCommand(STATcmd, "") <= 0)
  655.     return -1;
  656.   return (int)lastResponseInfo.AsInteger();
  657. }
  658. PUnsignedArray PPOP3Client::GetMessageSizes()
  659. {
  660.   PUnsignedArray sizes;
  661.   if (ExecuteCommand(LIST, "") > 0) {
  662.     PString msgInfo;
  663.     while (ReadLine(msgInfo))
  664.       sizes.SetAt((PINDEX)msgInfo.AsInteger()-1,
  665.                   (unsigned)msgInfo.Mid(msgInfo.Find(' ')).AsInteger());
  666.   }
  667.   return sizes;
  668. }
  669. PStringArray PPOP3Client::GetMessageHeaders()
  670. {
  671.   PStringArray headers;
  672.   int count = GetMessageCount();
  673.   for (int msgNum = 1; msgNum <= count; msgNum++) {
  674.     if (ExecuteCommand(TOP, PString(PString::Unsigned,msgNum) + " 0") > 0) {
  675.       PString headerLine;
  676.       while (ReadLine(headerLine, TRUE))
  677.         headers[msgNum-1] += headerLine;
  678.     }
  679.   }
  680.   return headers;
  681. }
  682. BOOL PPOP3Client::BeginMessage(PINDEX messageNumber)
  683. {
  684.   return ExecuteCommand(RETR, PString(PString::Unsigned, messageNumber)) > 0;
  685. }
  686. BOOL PPOP3Client::DeleteMessage(PINDEX messageNumber)
  687. {
  688.   return ExecuteCommand(DELE, PString(PString::Unsigned, messageNumber)) > 0;
  689. }
  690. //////////////////////////////////////////////////////////////////////////////
  691. // PPOP3Server
  692. PPOP3Server::PPOP3Server()
  693. {
  694. }
  695. BOOL PPOP3Server::OnOpen()
  696. {
  697.   return WriteResponse(okResponse, PIPSocket::GetHostName() +
  698.                      " POP3 server ready at " +
  699.                       PTime(PTime::MediumDateTime).AsString());
  700. }
  701. BOOL PPOP3Server::ProcessCommand()
  702. {
  703.   PString args;
  704.   PINDEX num;
  705.   if (!ReadCommand(num, args))
  706.     return FALSE;
  707.   switch (num) {
  708.     case USER :
  709.       OnUSER(args);
  710.       break;
  711.     case PASS :
  712.       OnPASS(args);
  713.       break;
  714.     case QUIT :
  715.       OnQUIT();
  716.       return FALSE;
  717.     case RSET :
  718.       OnRSET();
  719.       break;
  720.     case NOOP :
  721.       OnNOOP();
  722.       break;
  723.     case STATcmd :
  724.       OnSTAT();
  725.       break;
  726.     case LIST :
  727.       OnLIST((PINDEX)args.AsInteger());
  728.       break;
  729.     case RETR :
  730.       OnRETR((PINDEX)args.AsInteger());
  731.       break;
  732.     case DELE :
  733.       OnDELE((PINDEX)args.AsInteger());
  734.       break;
  735.     case TOP :
  736.       if (args.Find(' ') == P_MAX_INDEX)
  737.         WriteResponse(errResponse, "Syntax error");
  738.       else
  739.         OnTOP((PINDEX)args.AsInteger(),
  740.               (PINDEX)args.Mid(args.Find(' ')).AsInteger());
  741.       break;
  742.     case UIDL :
  743.       OnUIDL((PINDEX)args.AsInteger());
  744.       break;
  745.     default :
  746.       return OnUnknown(args);
  747.   }
  748.   return TRUE;
  749. }
  750. void PPOP3Server::OnUSER(const PString & name)
  751. {
  752.   messageSizes.SetSize(0);
  753.   messageIDs.SetSize(0);
  754.   username = name;
  755.   WriteResponse(okResponse, "User name accepted.");
  756. }
  757. void PPOP3Server::OnPASS(const PString & password)
  758. {
  759.   if (username.IsEmpty())
  760.     WriteResponse(errResponse, "No user name specified.");
  761.   else if (HandleOpenMailbox(username, password))
  762.     WriteResponse(okResponse, username + " mail is available.");
  763.   else
  764.     WriteResponse(errResponse, "No access to " + username + " mail.");
  765.   messageDeletions.SetSize(messageIDs.GetSize());
  766. }
  767. void PPOP3Server::OnQUIT()
  768. {
  769.   for (PINDEX i = 0; i < messageDeletions.GetSize(); i++)
  770.     if (messageDeletions[i])
  771.       HandleDeleteMessage(i+1, messageIDs[i]);
  772.   WriteResponse(okResponse, PIPSocket::GetHostName() +
  773.                      " POP3 server signing off at " +
  774.                       PTime(PTime::MediumDateTime).AsString());
  775.   Close();
  776. }
  777. void PPOP3Server::OnRSET()
  778. {
  779.   for (PINDEX i = 0; i < messageDeletions.GetSize(); i++)
  780.     messageDeletions[i] = FALSE;
  781.   WriteResponse(okResponse, "Resetting state.");
  782. }
  783. void PPOP3Server::OnNOOP()
  784. {
  785.   WriteResponse(okResponse, "Doing nothing.");
  786. }
  787. void PPOP3Server::OnSTAT()
  788. {
  789.   DWORD total = 0;
  790.   for (PINDEX i = 0; i < messageSizes.GetSize(); i++)
  791.     total += messageSizes[i];
  792.   WriteResponse(okResponse, psprintf("%u %u", messageSizes.GetSize(), total));
  793. }
  794. void PPOP3Server::OnLIST(PINDEX msg)
  795. {
  796.   if (msg == 0) {
  797.     WriteResponse(okResponse, psprintf("%u messages.", messageSizes.GetSize()));
  798.     for (PINDEX i = 0; i < messageSizes.GetSize(); i++)
  799.       if (!messageDeletions[i])
  800.         WriteLine(psprintf("%u %u", i+1, messageSizes[i]));
  801.     WriteLine(".");
  802.   }
  803.   else if (msg < 1 || msg > messageSizes.GetSize())
  804.     WriteResponse(errResponse, "No such message.");
  805.   else
  806.     WriteResponse(okResponse, psprintf("%u %u", msg, messageSizes[msg-1]));
  807. }
  808. void PPOP3Server::OnRETR(PINDEX msg)
  809. {
  810.   if (msg < 1 || msg > messageDeletions.GetSize())
  811.     WriteResponse(errResponse, "No such message.");
  812.   else {
  813.     WriteResponse(okResponse,
  814.                  PString(PString::Unsigned, messageSizes[msg-1]) + " octets.");
  815.     stuffingState = StuffIdle;
  816.     HandleSendMessage(msg, messageIDs[msg-1], P_MAX_INDEX);
  817.     stuffingState = DontStuff;
  818.     WriteString(CRLFdotCRLF);
  819.   }
  820. }
  821. void PPOP3Server::OnDELE(PINDEX msg)
  822. {
  823.   if (msg < 1 || msg > messageDeletions.GetSize())
  824.     WriteResponse(errResponse, "No such message.");
  825.   else {
  826.     messageDeletions[msg-1] = TRUE;
  827.     WriteResponse(okResponse, "Message marked for deletion.");
  828.   }
  829. }
  830. void PPOP3Server::OnTOP(PINDEX msg, PINDEX count)
  831. {
  832.   if (msg < 1 || msg > messageDeletions.GetSize())
  833.     WriteResponse(errResponse, "No such message.");
  834.   else {
  835.     WriteResponse(okResponse, "Top of message");
  836.     stuffingState = StuffIdle;
  837.     HandleSendMessage(msg, messageIDs[msg-1], count);
  838.     stuffingState = DontStuff;
  839.     WriteString(CRLFdotCRLF);
  840.   }
  841. }
  842. void PPOP3Server::OnUIDL(PINDEX msg)
  843. {
  844.   if (msg == 0) {
  845.     WriteResponse(okResponse,
  846.               PString(PString::Unsigned, messageIDs.GetSize()) + " messages.");
  847.     for (PINDEX i = 0; i < messageIDs.GetSize(); i++)
  848.       if (!messageDeletions[i])
  849.         WriteLine(PString(PString::Unsigned, i+1) & messageIDs[i]);
  850.     WriteLine(".");
  851.   }
  852.   else if (msg < 1 || msg > messageSizes.GetSize())
  853.     WriteResponse(errResponse, "No such message.");
  854.   else
  855.     WriteLine(PString(PString::Unsigned, msg) & messageIDs[msg-1]);
  856. }
  857. BOOL PPOP3Server::OnUnknown(const PCaselessString & command)
  858. {
  859.   WriteResponse(errResponse, "Command "" + command + "" unrecognised.");
  860.   return TRUE;
  861. }
  862. BOOL PPOP3Server::HandleOpenMailbox(const PString &, const PString &)
  863. {
  864.   return FALSE;
  865. }
  866. void PPOP3Server::HandleSendMessage(PINDEX, const PString &, PINDEX)
  867. {
  868. }
  869. void PPOP3Server::HandleDeleteMessage(PINDEX, const PString &)
  870. {
  871. }
  872. // End Of File ///////////////////////////////////////////////////////////////