📄 inetmail.cxx
字号:
}
void PSMTPServer::OnVRFY(const PCaselessString & name)
{
PString expandedName;
switch (LookUpName(name, expandedName)) {
case AmbiguousUser :
WriteResponse(553, "User \"" + name + "\" ambiguous.");
break;
case ValidUser :
WriteResponse(250, expandedName);
break;
case UnknownUser :
WriteResponse(550, "Name \"" + name + "\" does not match anything.");
break;
default :
WriteResponse(550, "Error verifying user \"" + name + "\".");
}
}
void PSMTPServer::OnEXPN(const PCaselessString &)
{
WriteResponse(502, "I don't do that. Sorry.");
}
static PINDEX ParseMailPath(const PCaselessString & args,
const PCaselessString & subCmd,
PString & name,
PString & domain,
PString & forwardList)
{
PINDEX colon = args.Find(':');
if (colon == P_MAX_INDEX)
return 0;
PCaselessString word = args.Left(colon).Trim();
if (subCmd != word)
return 0;
PINDEX leftAngle = args.Find('<', colon);
if (leftAngle == P_MAX_INDEX)
return 0;
PINDEX finishQuote;
PINDEX startQuote = args.Find('"', leftAngle);
if (startQuote == P_MAX_INDEX) {
colon = args.Find(':', leftAngle);
if (colon == P_MAX_INDEX)
colon = leftAngle;
finishQuote = startQuote = colon+1;
}
else {
finishQuote = args.Find('"', startQuote+1);
if (finishQuote == P_MAX_INDEX)
finishQuote = startQuote;
colon = args.Find(':', leftAngle);
if (colon > startQuote)
colon = leftAngle;
}
PINDEX rightAngle = args.Find('>', finishQuote);
if (rightAngle == P_MAX_INDEX)
return 0;
PINDEX at = args.Find('@', finishQuote);
if (at > rightAngle)
at = rightAngle;
if (startQuote == finishQuote)
finishQuote = at;
name = args(startQuote, finishQuote-1);
domain = args(at+1, rightAngle-1);
forwardList = args(leftAngle+1, colon-1);
return rightAngle+1;
}
void PSMTPServer::OnRCPT(const PCaselessString & recipient)
{
PCaselessString toName;
PCaselessString toDomain;
PCaselessString forwardList;
if (ParseMailPath(recipient, "to", toName, toDomain, forwardList) == 0)
WriteResponse(501, "Syntax error.");
else {
switch (ForwardDomain(toDomain, forwardList)) {
case CannotForward :
WriteResponse(550, "Cannot do forwarding.");
break;
case WillForward :
if (!forwardList)
forwardList += ":";
forwardList += toName;
if (!toDomain)
forwardList += "@" + toDomain;
toNames.AppendString(toName);
toDomains.AppendString(forwardList);
break;
case LocalDomain :
{
PString expandedName;
switch (LookUpName(toName, expandedName)) {
case ValidUser :
WriteResponse(250, "Recipient " + toName + " Ok");
toNames.AppendString(toName);
toDomains.AppendString("");
break;
case AmbiguousUser :
WriteResponse(553, "User ambiguous.");
break;
case UnknownUser :
WriteResponse(550, "User unknown.");
break;
default :
WriteResponse(550, "Error verifying user.");
}
}
}
}
}
void PSMTPServer::OnMAIL(const PCaselessString & sender)
{
sendCommand = WasMAIL;
OnSendMail(sender);
}
void PSMTPServer::OnSEND(const PCaselessString & sender)
{
sendCommand = WasSEND;
OnSendMail(sender);
}
void PSMTPServer::OnSAML(const PCaselessString & sender)
{
sendCommand = WasSAML;
OnSendMail(sender);
}
void PSMTPServer::OnSOML(const PCaselessString & sender)
{
sendCommand = WasSOML;
OnSendMail(sender);
}
void PSMTPServer::OnSendMail(const PCaselessString & sender)
{
if (!fromAddress) {
WriteResponse(503, "Sender already specified.");
return;
}
PString fromDomain;
PINDEX extendedArgPos = ParseMailPath(sender, "from", fromAddress, fromDomain, fromPath);
if (extendedArgPos == 0 || fromAddress.IsEmpty()) {
WriteResponse(501, "Syntax error.");
return;
}
fromAddress += fromDomain;
if (extendedHello) {
PINDEX equalPos = sender.Find('=', extendedArgPos);
PCaselessString body = sender(extendedArgPos, equalPos).Trim();
PCaselessString mime = sender.Mid(equalPos+1).Trim();
eightBitMIME = (body == "BODY" && mime == "8BITMIME");
}
PString response = "Sender " + fromAddress;
if (eightBitMIME)
response += " and 8BITMIME";
WriteResponse(250, response + " Ok");
}
void PSMTPServer::OnDATA()
{
if (fromAddress.IsEmpty()) {
WriteResponse(503, "Need a valid MAIL command.");
return;
}
if (toNames.GetSize() == 0) {
WriteResponse(503, "Need a valid RCPT command.");
return;
}
// Ok, everything is ready to start the message.
if (!WriteResponse(354,
eightBitMIME ? "Enter 8BITMIME message, terminate with '<CR><LF>.<CR><LF>'."
: "Enter mail, terminate with '.' alone on a line."))
return;
endMIMEDetectState = eightBitMIME ? StuffIdle : DontStuff;
BOOL ok = TRUE;
BOOL completed = FALSE;
BOOL starting = TRUE;
while (ok && !completed) {
PCharArray buffer;
if (eightBitMIME)
ok = OnMIMEData(buffer, completed);
else
ok = OnTextData(buffer, completed);
if (ok) {
ok = HandleMessage(buffer, starting, completed);
starting = FALSE;
}
}
if (ok)
WriteResponse(250, "Message received Ok.");
else
WriteResponse(554, "Message storage failed.");
}
BOOL PSMTPServer::OnUnknown(const PCaselessString & command)
{
WriteResponse(500, "Command \"" + command + "\" unrecognised.");
return TRUE;
}
BOOL PSMTPServer::OnTextData(PCharArray & buffer, BOOL & completed)
{
PString line;
while (ReadLine(line)) {
PINDEX len = line.GetLength();
if (len == 1 && line[0] == '.') {
completed = TRUE;
return TRUE;
}
PINDEX start = (len > 1 && line[0] == '.' && line[1] == '.') ? 1 : 0;
PINDEX size = buffer.GetSize();
len -= start;
memcpy(buffer.GetPointer(size + len + 2) + size,
((const char *)line)+start, len);
size += len;
buffer[size++] = '\r';
buffer[size++] = '\n';
if (size > messageBufferSize)
return TRUE;
}
return FALSE;
}
BOOL PSMTPServer::OnMIMEData(PCharArray & buffer, BOOL & completed)
{
PINDEX count = 0;
int c;
while ((c = ReadChar()) >= 0) {
if (count >= buffer.GetSize())
buffer.SetSize(count + 100);
switch (endMIMEDetectState) {
case StuffIdle :
buffer[count++] = (char)c;
break;
case StuffCR :
endMIMEDetectState = c != '\n' ? StuffIdle : StuffCRLF;
buffer[count++] = (char)c;
break;
case StuffCRLF :
if (c == '.')
endMIMEDetectState = StuffCRLFdot;
else {
endMIMEDetectState = StuffIdle;
buffer[count++] = (char)c;
}
break;
case StuffCRLFdot :
switch (c) {
case '\r' :
endMIMEDetectState = StuffCRLFdotCR;
break;
case '.' :
endMIMEDetectState = StuffIdle;
buffer[count++] = (char)c;
break;
default :
endMIMEDetectState = StuffIdle;
buffer[count++] = '.';
buffer[count++] = (char)c;
}
break;
case StuffCRLFdotCR :
if (c == '\n') {
completed = TRUE;
return TRUE;
}
buffer[count++] = '.';
buffer[count++] = '\r';
buffer[count++] = (char)c;
endMIMEDetectState = StuffIdle;
default :
PAssertAlways("Illegal SMTP state");
}
if (count > messageBufferSize) {
buffer.SetSize(messageBufferSize);
return TRUE;
}
}
return FALSE;
}
PSMTPServer::ForwardResult PSMTPServer::ForwardDomain(PCaselessString & userDomain,
PCaselessString & forwardDomainList)
{
return userDomain.IsEmpty() && forwardDomainList.IsEmpty() ? LocalDomain : CannotForward;
}
PSMTPServer::LookUpResult PSMTPServer::LookUpName(const PCaselessString &,
PString & expandedName)
{
expandedName = PString();
return LookUpError;
}
BOOL PSMTPServer::HandleMessage(PCharArray &, BOOL, BOOL)
{
return FALSE;
}
//////////////////////////////////////////////////////////////////////////////
// PPOP3
static char const * const POP3Commands[PPOP3::NumCommands] = {
"USER", "PASS", "QUIT", "RSET", "NOOP", "STAT",
"LIST", "RETR", "DELE", "APOP", "TOP", "UIDL",
"AUTH"
};
PString PPOP3::okResponse = "+OK";
PString PPOP3::errResponse = "-ERR";
PPOP3::PPOP3()
: PInternetProtocol("pop3 110", NumCommands, POP3Commands)
{
}
PINDEX PPOP3::ParseResponse(const PString & line)
{
lastResponseCode = line[0] == '+';
PINDEX endCode = line.Find(' ');
if (endCode != P_MAX_INDEX)
lastResponseInfo = line.Mid(endCode+1);
else
lastResponseInfo = PString();
return 0;
}
//////////////////////////////////////////////////////////////////////////////
// PPOP3Client
PPOP3Client::PPOP3Client()
{
loggedIn = FALSE;
}
PPOP3Client::~PPOP3Client()
{
Close();
}
BOOL PPOP3Client::OnOpen()
{
if (!ReadResponse() || lastResponseCode <= 0)
return FALSE;
// APOP login command supported?
PINDEX i = lastResponseInfo.FindRegEx("<.*@.*>");
if (i != P_MAX_INDEX)
apopBanner = lastResponseInfo.Mid(i);
return TRUE;
}
BOOL PPOP3Client::Close()
{
BOOL ok = TRUE;
if (IsOpen() && loggedIn) {
SetReadTimeout(60000);
ok = ExecuteCommand(QUIT, "") > 0;
}
return PInternetProtocol::Close() && ok;
}
BOOL PPOP3Client::LogIn(const PString & username, const PString & password, int options)
{
#if P_SASL2
PString mech;
PSASLClient auth("pop", username, username, password);
if ((options & UseSASL) && ExecuteCommand(AUTH, "") > 0) {
PStringSet serverMechs;
while (ReadLine(mech) && mech[0] != '.')
serverMechs.Include(mech);
mech = PString::Empty();
PStringSet ourMechs;
if (auth.Init("", ourMechs)) {
if (!(options & AllowClearTextSASL)) {
ourMechs.Exclude("PLAIN");
ourMechs.Exclude("LOGIN");
}
for (PINDEX i = 0, max = serverMechs.GetSize() ; i < max ; i++)
if (ourMechs.Contains(serverMechs.GetKeyAt(i))) {
mech = serverMechs.GetKeyAt(i);
break;
}
}
}
PString output;
if ((options & UseSASL) && !mech.IsEmpty() && auth.Start(mech, output)) {
if (ExecuteCommand(AUTH, mech) <= 0)
return FALSE;
PSASLClient::PSASLResult result;
do {
result = auth.Negotiate(lastResponseInfo, output);
if (result == PSASLClient::Fail)
return FALSE;
if (!output.IsEmpty()) {
WriteLine(output);
if (!ReadResponse() || !lastResponseCode)
return FALSE;
}
} while (result == PSASLClient::Continue);
auth.End();
}
else {
#endif
if (!apopBanner.IsEmpty()) { // let's try with APOP
PMessageDigest::Result bin_digest;
PMessageDigest5::Encode(apopBanner + password, bin_digest);
PString digest;
const BYTE * data = bin_digest.GetPointer();
for (PINDEX i = 0, max = bin_digest.GetSize(); i < max ; i++)
digest.sprintf("%02x", (unsigned)data[i]);
if (ExecuteCommand(APOP, username + " " + digest) > 0)
return loggedIn = TRUE;
}
// No SASL and APOP didn't work for us
// If we really have to, we'll go with the plain old USER/PASS scheme
if (!(options & AllowUserPass))
return FALSE;
if (ExecuteCommand(USER, username) <= 0)
return FALSE;
if (ExecuteCommand(PASS, password) <= 0)
return FALSE;
#if P_SASL2
}
#endif
loggedIn = TRUE;
return TRUE;
}
int PPOP3Client::GetMessageCount()
{
if (ExecuteCommand(STATcmd, "") <= 0)
return -1;
return (int)lastResponseInfo.AsInteger();
}
PUnsignedArray PPOP3Client::GetMessageSizes()
{
PUnsignedArray sizes;
if (ExecuteCommand(LIST, "") > 0) {
PString msgInfo;
while (ReadLine(msgInfo) && isdigit(msgInfo[0]))
sizes.SetAt((PINDEX)msgInfo.AsInteger()-1,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -