📄 inetmail.cxx
字号:
/*
* inetmail.cxx
*
* Internet Mail classes.
*
* Portable Windows Library
*
* Copyright (c) 1993-2002 Equivalence Pty. Ltd.
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.0 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
* the License for the specific language governing rights and limitations
* under the License.
*
* The Original Code is Portable Windows Library.
*
* The Initial Developer of the Original Code is Equivalence Pty. Ltd.
*
* Contributor(s): Federico Pinna and Reitek S.p.A. (SASL authentication)
*
* $Log: inetmail.cxx,v $
* Revision 1.30 2004/05/09 07:23:49 rjongbloed
* More work on XMPP, thanks Federico Pinna and Reitek S.p.A.
*
* Revision 1.29 2004/05/02 08:58:15 csoutheren
* Removed warnings when compling without SASL
*
* Revision 1.28 2004/04/28 11:26:43 csoutheren
* Hopefully fixed SASL and SASL2 problems
*
* Revision 1.27 2004/04/26 01:33:20 rjongbloed
* Fixed minor problem with SASL authentication, thanks Federico Pinna, Reitek S.p.A.
*
* Revision 1.26 2004/04/21 00:29:56 csoutheren
* Added SASL authentication to PPOP3Client and PSMTPClient
* Thanks to Federico Pinna and Reitek S.p.A.
*
* Revision 1.25 2004/04/03 06:54:25 rjongbloed
* Many and various changes to support new Visual C++ 2003
*
* Revision 1.24 2003/02/20 00:16:06 craigs
* Changed MIME_Version to MIME-Version
*
* Revision 1.23 2002/12/19 01:35:24 robertj
* Fixed problem with returning incorrect lastWriteLength on translated output.
*
* Revision 1.22 2002/11/06 22:47:25 robertj
* Fixed header comment (copyright etc)
*
* Revision 1.21 2002/01/07 05:26:47 robertj
* Fixed getting scan list of messages, thanks xradish
*
* Revision 1.20 2001/09/28 00:45:27 robertj
* Removed HasKey() as is confusing due to ancestor Contains().
*
* Revision 1.19 2000/11/21 01:49:25 robertj
* Fixed warning on GNU compiler.
*
* Revision 1.18 2000/11/16 07:15:15 robertj
* Fixed problem with not closing off base64 encoding at next MIME part.
*
* Revision 1.17 2000/11/14 08:30:03 robertj
* Fixed bug in closing SMTP client, conditional around wrong way.
*
* Revision 1.16 2000/11/10 01:08:11 robertj
* Added content transfer encoding and automatic base64 translation.
*
* Revision 1.15 2000/11/09 06:01:58 robertj
* Added MIME version and content disposition to RFC822 class.
*
* Revision 1.14 2000/11/09 05:50:23 robertj
* Added RFC822 aware channel class for doing internet mail.
*
* Revision 1.13 2000/06/21 01:01:22 robertj
* AIX port, thanks Wolfgang Platzer (wolfgang.platzer@infonova.at).
*
* Revision 1.12 1998/11/30 04:52:01 robertj
* New directory structure
*
* Revision 1.11 1998/09/23 06:22:18 robertj
* Added open source copyright license.
*
* Revision 1.10 1998/01/26 02:49:20 robertj
* GNU support.
*
* Revision 1.9 1997/07/14 11:47:14 robertj
* Added "const" to numerous variables.
*
* Revision 1.8 1996/12/21 01:24:39 robertj
* Added missing open message to smtp server.
*
* Revision 1.7 1996/09/14 13:18:03 robertj
* Renamed file and changed to be a protocol off new indirect channel to separate
* the protocol from the low level byte transport channel.
*
* Revision 1.6 1996/07/27 04:12:45 robertj
* Redesign and reimplement of mail sockets.
*
* Revision 1.5 1996/06/28 13:22:09 robertj
* Changed SMTP incoming message handler so can tell when started, processing or ended message.
*
* Revision 1.4 1996/05/26 03:46:51 robertj
* Compatibility to GNU 2.7.x
*
* Revision 1.3 1996/03/18 13:33:16 robertj
* Fixed incompatibilities to GNU compiler where PINDEX != int.
*
* Revision 1.2 1996/03/16 04:51:28 robertj
* Changed lastResponseCode to an integer.
* Added ParseReponse() for splitting reponse line into code and info.
*
* Revision 1.1 1996/03/04 12:12:51 robertj
* Initial revision
*
*/
#ifdef __GNUC__
#pragma implementation "inetmail.h"
#endif
#include <ptlib.h>
#include <ptlib/sockets.h>
#include <ptclib/inetmail.h>
#if P_SASL2
#include <ptclib/psasl.h>
#endif
static const PString CRLF = "\r\n";
static const PString CRLFdotCRLF = "\r\n.\r\n";
//////////////////////////////////////////////////////////////////////////////
// PSMTP
static char const * const SMTPCommands[PSMTP::NumCommands] = {
"HELO", "EHLO", "QUIT", "HELP", "NOOP",
"TURN", "RSET", "VRFY", "EXPN", "RCPT",
"MAIL", "SEND", "SAML", "SOML", "DATA",
"AUTH"
};
PSMTP::PSMTP()
: PInternetProtocol("smtp 25", NumCommands, SMTPCommands)
{
}
//////////////////////////////////////////////////////////////////////////////
// PSMTPClient
PSMTPClient::PSMTPClient()
{
haveHello = FALSE;
extendedHello = FALSE;
eightBitMIME = FALSE;
}
PSMTPClient::~PSMTPClient()
{
Close();
}
BOOL PSMTPClient::OnOpen()
{
return ReadResponse() && lastResponseCode/100 == 2;
}
BOOL PSMTPClient::Close()
{
BOOL ok = TRUE;
if (sendingData)
ok = EndMessage();
if (IsOpen() && haveHello) {
SetReadTimeout(60000);
ok = ExecuteCommand(QUIT, "")/100 == 2 && ok;
}
return PInternetProtocol::Close() && ok;
}
#if P_SASL2
BOOL PSMTPClient::LogIn(const PString & username,
const PString & password)
{
PString localHost;
PIPSocket * socket = GetSocket();
if (socket != NULL) {
localHost = socket->GetLocalHostName();
}
if (haveHello)
return FALSE; // Wrong state
if (ExecuteCommand(EHLO, localHost)/100 != 2)
return TRUE; // EHLO not supported, therefore AUTH not supported
haveHello = extendedHello = TRUE;
PStringArray caps = lastResponseInfo.Lines();
PStringArray serverMechs;
PINDEX i, max;
for (i = 0, max = caps.GetSize() ; i < max ; i++)
if (caps[i].Left(5) == "AUTH ") {
serverMechs = caps[i].Mid(5).Tokenise(" ", FALSE);
break;
}
if (serverMechs.GetSize() == 0)
return TRUE; // No mechanisms, no login
PSASLClient auth("smtp", username, username, password);
PStringSet ourMechs;
if (!auth.Init("", ourMechs))
return FALSE;
PString mech;
for (i = 0, max = serverMechs.GetSize() ; i < max ; i++)
if (ourMechs.Contains(serverMechs[i])) {
mech = serverMechs[i];
break;
}
if (mech.IsEmpty())
return TRUE; // No mechanism in common
PString output;
// Ok, let's go...
if (!auth.Start(mech, output))
return FALSE;
if (!output.IsEmpty())
mech = mech + " " + output;
if (ExecuteCommand(AUTH, mech) <= 0)
return FALSE;
PSASLClient::PSASLResult result;
int response;
do {
response = lastResponseCode/100;
if (response == 2)
break;
else if (response != 3)
return FALSE;
result = auth.Negotiate(lastResponseInfo, output);
if (result == PSASLClient::Fail)
return FALSE;
if (!output.IsEmpty()) {
WriteLine(output);
if (!ReadResponse())
return FALSE;
}
} while (result == PSASLClient::Continue);
auth.End();
return TRUE;
}
#else
BOOL PSMTPClient::LogIn(const PString &,
const PString &)
{
return TRUE;
}
#endif
BOOL PSMTPClient::BeginMessage(const PString & from,
const PString & to,
BOOL useEightBitMIME)
{
fromAddress = from;
toNames.RemoveAll();
toNames.AppendString(to);
eightBitMIME = useEightBitMIME;
return _BeginMessage();
}
BOOL PSMTPClient::BeginMessage(const PString & from,
const PStringList & toList,
BOOL useEightBitMIME)
{
fromAddress = from;
toNames = toList;
eightBitMIME = useEightBitMIME;
return _BeginMessage();
}
BOOL PSMTPClient::_BeginMessage()
{
PString localHost;
PString peerHost;
PIPSocket * socket = GetSocket();
if (socket != NULL) {
localHost = socket->GetLocalHostName();
peerHost = socket->GetPeerHostName();
}
if (!haveHello) {
if (ExecuteCommand(EHLO, localHost)/100 == 2)
haveHello = extendedHello = TRUE;
}
if (!haveHello) {
extendedHello = FALSE;
if (eightBitMIME)
return FALSE;
if (ExecuteCommand(HELO, localHost)/100 != 2)
return FALSE;
haveHello = TRUE;
}
if (fromAddress[0] != '"' && fromAddress.Find(' ') != P_MAX_INDEX)
fromAddress = '"' + fromAddress + '"';
if (!localHost && fromAddress.Find('@') == P_MAX_INDEX)
fromAddress += '@' + localHost;
if (ExecuteCommand(MAIL, "FROM:<" + fromAddress + '>')/100 != 2)
return FALSE;
for (PINDEX i = 0; i < toNames.GetSize(); i++) {
if (!peerHost && toNames[i].Find('@') == P_MAX_INDEX)
toNames[i] += '@' + peerHost;
if (ExecuteCommand(RCPT, "TO:<" + toNames[i] + '>')/100 != 2)
return FALSE;
}
if (ExecuteCommand(DATA, PString())/100 != 3)
return FALSE;
stuffingState = StuffIdle;
sendingData = TRUE;
return TRUE;
}
BOOL PSMTPClient::EndMessage()
{
flush();
stuffingState = DontStuff;
sendingData = FALSE;
if (!WriteString(CRLFdotCRLF))
return FALSE;
return ReadResponse() && lastResponseCode/100 == 2;
}
//////////////////////////////////////////////////////////////////////////////
// PSMTPServer
PSMTPServer::PSMTPServer()
{
extendedHello = FALSE;
eightBitMIME = FALSE;
messageBufferSize = 30000;
ServerReset();
}
void PSMTPServer::ServerReset()
{
eightBitMIME = FALSE;
sendCommand = WasMAIL;
fromAddress = PString();
toNames.RemoveAll();
}
BOOL PSMTPServer::OnOpen()
{
return WriteResponse(220, PIPSocket::GetHostName() + "ESMTP server ready");
}
BOOL PSMTPServer::ProcessCommand()
{
PString args;
PINDEX num;
if (!ReadCommand(num, args))
return FALSE;
switch (num) {
case HELO :
OnHELO(args);
break;
case EHLO :
OnEHLO(args);
break;
case QUIT :
OnQUIT();
return FALSE;
case NOOP :
OnNOOP();
break;
case TURN :
OnTURN();
break;
case RSET :
OnRSET();
break;
case VRFY :
OnVRFY(args);
break;
case EXPN :
OnEXPN(args);
break;
case RCPT :
OnRCPT(args);
break;
case MAIL :
OnMAIL(args);
break;
case SEND :
OnSEND(args);
break;
case SAML :
OnSAML(args);
break;
case SOML :
OnSOML(args);
break;
case DATA :
OnDATA();
break;
default :
return OnUnknown(args);
}
return TRUE;
}
void PSMTPServer::OnHELO(const PCaselessString & remoteHost)
{
extendedHello = FALSE;
ServerReset();
PCaselessString peerHost;
PIPSocket * socket = GetSocket();
if (socket != NULL)
peerHost = socket->GetPeerHostName();
PString response = PIPSocket::GetHostName() & "Hello" & peerHost + ", ";
if (remoteHost == peerHost)
response += "pleased to meet you.";
else if (remoteHost.IsEmpty())
response += "why do you wish to remain anonymous?";
else
response += "why do you wish to call yourself \"" + remoteHost + "\"?";
WriteResponse(250, response);
}
void PSMTPServer::OnEHLO(const PCaselessString & remoteHost)
{
extendedHello = TRUE;
ServerReset();
PCaselessString peerHost;
PIPSocket * socket = GetSocket();
if (socket != NULL)
peerHost = socket->GetPeerHostName();
PString response = PIPSocket::GetHostName() & "Hello" & peerHost + ", ";
if (remoteHost == peerHost)
response += ", pleased to meet you.";
else if (remoteHost.IsEmpty())
response += "why do you wish to remain anonymous?";
else
response += "why do you wish to call yourself \"" + remoteHost + "\"?";
response += "\nHELP\nVERB\nONEX\nMULT\nEXPN\nTICK\n8BITMIME\n";
WriteResponse(250, response);
}
void PSMTPServer::OnQUIT()
{
WriteResponse(221, PIPSocket::GetHostName() + " closing connection, goodbye.");
Close();
}
void PSMTPServer::OnHELP()
{
WriteResponse(214, "No help here.");
}
void PSMTPServer::OnNOOP()
{
WriteResponse(250, "Ok");
}
void PSMTPServer::OnTURN()
{
WriteResponse(502, "I don't do that yet. Sorry.");
}
void PSMTPServer::OnRSET()
{
ServerReset();
WriteResponse(250, "Reset state.");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -