Created initial SMTP handlers.

This commit is contained in:
Brad Arant 2019-07-28 19:47:50 -07:00
parent 1751d431c9
commit caa8dd7d90
11 changed files with 608 additions and 12 deletions

View File

@ -2,6 +2,27 @@
<CodeLite_Project Name="BMAMail" Version="10.0.0" InternalType="Console"> <CodeLite_Project Name="BMAMail" Version="10.0.0" InternalType="Console">
<Description/> <Description/>
<Dependencies/> <Dependencies/>
<VirtualDirectory Name="src">
<File Name="main.cpp"/>
<File Name="./BMAIMAPServer.h"/>
<File Name="./BMAIMAPSession.h"/>
<File Name="./BMAPOP3Server.h"/>
<File Name="./BMAPOP3Session.h"/>
<File Name="./BMASMTPServer.h"/>
<File Name="./BMASMTPSession.h"/>
<File Name="__SMTP_HELO.h"/>
<File Name="__SMTP_EHLO.h"/>
<File Name="__SMTP_AUTH.h"/>
<File Name="__SMTP_MAIL.h"/>
<File Name="__SMTP_RCPT.h"/>
<File Name="__SMTP_DATA.h"/>
<File Name="__SMTP_RSET.h"/>
<File Name="__SMTP_VRFY.h"/>
<File Name="__SMTP_NOOP.h"/>
<File Name="__SMTP_QUIT.h"/>
</VirtualDirectory>
<Dependencies Name="Debug"/>
<Dependencies Name="Release"/>
<Settings Type="Executable"> <Settings Type="Executable">
<GlobalSettings> <GlobalSettings>
<Compiler Options="" C_Options="" Assembler=""> <Compiler Options="" C_Options="" Assembler="">
@ -15,8 +36,15 @@
<Configuration Name="Debug" CompilerType="GCC" DebuggerType="GNU gdb debugger" Type="Executable" BuildCmpWithGlobalSettings="append" BuildLnkWithGlobalSettings="append" BuildResWithGlobalSettings="append"> <Configuration Name="Debug" CompilerType="GCC" DebuggerType="GNU gdb debugger" Type="Executable" BuildCmpWithGlobalSettings="append" BuildLnkWithGlobalSettings="append" BuildResWithGlobalSettings="append">
<Compiler Options="-g;-O0;-Wall" C_Options="-g;-O0;-Wall" Assembler="" Required="yes" PreCompiledHeader="" PCHInCommandLine="no" PCHFlags="" PCHFlagsPolicy="0"> <Compiler Options="-g;-O0;-Wall" C_Options="-g;-O0;-Wall" Assembler="" Required="yes" PreCompiledHeader="" PCHInCommandLine="no" PCHFlags="" PCHFlagsPolicy="0">
<IncludePath Value="."/> <IncludePath Value="."/>
<IncludePath Value="../ServerCore"/>
<IncludePath Value="../BMAMySQL"/>
</Compiler> </Compiler>
<Linker Options="" Required="yes"/> <Linker Options="" Required="yes">
<LibraryPath Value="../Debug/ServerCore"/>
<LibraryPath Value="../Debug/BMAMySQL"/>
<Library Value="ServerCore"/>
<Library Value="BMAMySQL"/>
</Linker>
<ResourceCompiler Options="" Required="no"/> <ResourceCompiler Options="" Required="no"/>
<General OutputFile="$(IntermediateDirectory)/$(ProjectName)" IntermediateDirectory="./Debug" Command="./$(ProjectName)" CommandArguments="" UseSeparateDebugArgs="no" DebugArguments="" WorkingDirectory="$(IntermediateDirectory)" PauseExecWhenProcTerminates="yes" IsGUIProgram="no" IsEnabled="yes"/> <General OutputFile="$(IntermediateDirectory)/$(ProjectName)" IntermediateDirectory="./Debug" Command="./$(ProjectName)" CommandArguments="" UseSeparateDebugArgs="no" DebugArguments="" WorkingDirectory="$(IntermediateDirectory)" PauseExecWhenProcTerminates="yes" IsGUIProgram="no" IsEnabled="yes"/>
<BuildSystem Name="Default"/> <BuildSystem Name="Default"/>
@ -92,15 +120,4 @@
</Completion> </Completion>
</Configuration> </Configuration>
</Settings> </Settings>
<VirtualDirectory Name="src">
<File Name="main.cpp"/>
<File Name="./BMAIMAPServer.h"/>
<File Name="./BMAIMAPSession.h"/>
<File Name="./BMAPOP3Server.h"/>
<File Name="./BMAPOP3Session.h"/>
<File Name="./BMASMTPServer.h"/>
<File Name="./BMASMTPSession.h"/>
</VirtualDirectory>
<Dependencies Name="Debug"/>
<Dependencies Name="Release"/>
</CodeLite_Project> </CodeLite_Project>

98
__SMTP_AUTH.h Normal file
View File

@ -0,0 +1,98 @@
#ifndef ____SMTP_AUTH_h__
#define ____SMTP_AUTH_h__
#include "Command.h"
namespace http {
class __SMTP_AUTH : public core::Command {
int processCommand(std::string request, Session *session, std::stringstream &data);
data << "" << std::endl;
//---------------------------------------------------------------------------
// AUTH command request handling.
//---------------------------------------------------------------------------
else if(command(input) == "AUTH") {
if(input.length() > 5) {
string method = input.substr(5);
string userName;
string password;
//--------------------------------
// Check for AUTH LOGIN method.
//--------------------------------
if(method == "LOGIN") {
cout << "334 VXNlcm5hbWU6" << CRLF;
alarm(10);
if(!getline(cin, userName)) {
return -1;
}
alarm(0);
if(userName[userName.length() - 1] == '\r')
userName.erase(userName.length() - 1);
cout << "334 UGFzc3dvcmQ6" << CRLF;
alarm(10);
if(!getline(cin, password)) {
return -1;
}
alarm(0);
if(password[password.length() - 1] == '\r')
password.erase(password.length() - 1);
BASE64 base64;
log.message("Logging in with user '" + base64.decode(userName) + "' using password '" + base64.decode(password) + "'.");
if(authLogin(base64.decode(userName), base64.decode(password))) {
cout << "235 Authentication successful" << CRLF;
log.message("Response: 235 Authentication successful.");
relay = true;
}
else {
cout << "530 Login was unsuccessful." << CRLF;
log.message("Response: 530 Login was unsuccessful.");
}
}
else {
cout << "530 AUTH method not supported." << CRLF;
log.message("Response: 530 AUTH method not supported.");
}
}
else {
}
}
httpRequest.response.addHeader("Content-Type", "script/javascript");
return 0;
}
};
}
#endif

164
__SMTP_DATA.h Normal file
View File

@ -0,0 +1,164 @@
//---------------------------------------------------------------------------
// DATA command request handling.
//---------------------------------------------------------------------------
else if(command(input) == "DATA") {
//--------------------------------------------------------------
// We must have recipients before we can send data.
//--------------------------------------------------------------
if(state == "RCPT") {
//---------------------------------------------------------
// Prompt for client to begin entering mail message data.
//---------------------------------------------------------
cout << "354 Enter the mail message terminated by <CRLF>.<CRLF>" << CRLF;
mailData = "";
//-----------------------------------------------------------
// Receive mail message one line at a time and keep an eye
// out for the terminating period character.
//-----------------------------------------------------------
do {
alarm(120);
if(!getline(cin, input)) {
return -1;
}
if(input[input.length() - 1] == '\r')
input.erase(input.length() - 1);
alarm(0);
if(input != ".") {
//--------------------------------------------------------------
// If there was a period for the first character but it wasn't
// the only character then remove and ignore the first period.
// This is the transparency mode capability.
//--------------------------------------------------------------
if(input[0] == '.') {
mailData += input.substr(1) + CRLF;
}
else {
mailData += input + CRLF;
}
}
} while(input != ".");
//------------------------------------------------------------------
// Run the received message through an external filter program if
// one is configured into the system settings.
//------------------------------------------------------------------
string ID;
if(filterMessage(mailData)) {
if(recipientList != "") {
if(mailData.length() > 0) {
//------------------------------------------------------------------
// We have the message and we have a list of recipients. Send the
// message to the queue since we know everyone in the recipient
// list has passed the test.
//------------------------------------------------------------------
ID = queueMail(sender, recipientList, mailData, clientIP, log, sql, "N");
if(ID != "") {
log.message("Response: 250 OK Queued message " + ID);
//------------------------------
// Tell the client we sent it.
//------------------------------
cout << "250 OK Queued message " << ID << CRLF;
}
else {
log.message("Response: 550 Mail message too big.");
//---------------------------------
// Tell the client it was too big.
//---------------------------------
cout << "550 Mail message too big" << CRLF;
}
}
//------------------------------------------------------------------------
// The mail message is empty so we are going to error out. We don't like
// getting empty messages.
//------------------------------------------------------------------------
else {
log.message("Response: 550 Mail message was empty.");
cout << "550 Mail message was empty" << CRLF;
}
}
else {
log.message("Response: 250 OK Queued message. (We actually discarded it due to empty recipient list)");
//------------------------------
// Tell the client we sent it.
//------------------------------
cout << "250 OK Queued message " << ID << CRLF;
}
}
//----------------------------------------------------
// Error out cause it did not pass the filter test.
//----------------------------------------------------
else {
cout << "550 Message is probably spam" << CRLF;
log.message("Response: 550 Message is probably spam.");
}
//----------------------------
// Return to the READY state.
//----------------------------
state = "READY";
}
//--------------------------------------------------------
// Generate an error cause we are not in the right state.
//--------------------------------------------------------
else {
if(state == "MAIL") {
cout << "503 Please use RCPT first" << CRLF;
log.message("Response: 503 Please use RCPT first.");
}
else {
cout << "503 Please use MAIL first" << CRLF;
log.message("Response: 503 Please use MAIL first.");
}
}
}

25
__SMTP_EHLO.h Normal file
View File

@ -0,0 +1,25 @@
//---------------------------------------------------------------------------
// EHLO command request handling.
//---------------------------------------------------------------------------
else if(command(input) == "EHLO") {
if(input.length() > 5) {
string hostName = input.substr(5);
}
else {
// TODO: Provide error message demanding identity here.
//
}
cout << "250-" << getHostName() << CRLF;
// cout << "250-STARTTLS" << CRLF;
// cout << "250-PIPELINING" << CRLF;
// cout << "250-8BITMIME" << CRLF;
cout << "250-AUTH LOGIN" << CRLF;
cout << "250 HELP" << CRLF;
state = "READY";
}

19
__SMTP_HELO.h Normal file
View File

@ -0,0 +1,19 @@
//---------------------------------------------------------------------------
// HELO command request handling.
//---------------------------------------------------------------------------
if(command(input) == "HELO") {
if(input.length() > 5) {
string hostName = input.substr(5);
}
else {
// TODO: Provide error message demanding identity here.
//
}
cout << "250 " << getHostName() << CRLF;
state = "READY";
}

25
__SMTP_MAIL.h Normal file
View File

@ -0,0 +1,25 @@
//---------------------------------------------------------------------------
// MAIL command request handling.
//---------------------------------------------------------------------------
else if(command(input) == "MAIL") {
sender = cleanEMail(input.substr(5));
//-------------------------------------------------------------
// Verify that the senders domain name resolves to an address
// with an MX record. If not and there is a bounce then we
// will not be able to return the bounce message so it is
// probably a spammer anyway.
//-------------------------------------------------------------
if(verifyDomainMX(domainOnly(sender))) {
cout << "250 OK" << CRLF;
log.message("Response: 250 OK.");
recipientList = "";
state = "MAIL";
}
}

21
__SMTP_NOOP.h Normal file
View File

@ -0,0 +1,21 @@
#ifndef ____SMTP_AUTH_h__
#define ____SMTP_AUTH_h__
#include "Command.h"
namespace http {
class __SMTP_AUTH : public core::Command {
int processCommand(std::string request, Session *session, std::stringstream &data);
data << "" << std::endl;
//---------------------------------------------------------------------------
// NOOP command request handling.
//---------------------------------------------------------------------------
else if(command(input) == "NOOP") {
cout << "250 OK" << CRLF;
log.message("Response: 250 OK.");
}

23
__SMTP_QUIT.h Normal file
View File

@ -0,0 +1,23 @@
#ifndef ____SMTP_AUTH_h__
#define ____SMTP_AUTH_h__
#include "Command.h"
namespace http {
class __SMTP_AUTH : public core::Command {
int processCommand(std::string request, Session *session, std::stringstream &data);
data << "" << std::endl;
//---------------------------------------------------------------------------
// QUIT command request handling.
//---------------------------------------------------------------------------
else if(command(input) == "QUIT") {
cout << "221 " << getHostName() << CRLF;
log.message("Response: 221 " + getHostName() + ".");
state = "QUIT";
break;
}

165
__SMTP_RCPT.h Normal file
View File

@ -0,0 +1,165 @@
#ifndef ____SMTP_AUTH_h__
#define ____SMTP_AUTH_h__
#include "Command.h"
namespace http {
class __SMTP_AUTH : public core::Command {
int processCommand(std::string request, Session *session, std::stringstream &data);
data << "" << std::endl;
//---------------------------------------------------------------------------
// RCPT command request handling.
//---------------------------------------------------------------------------
else if(command(input) == "RCPT") {
if((state == "MAIL") || (state == "RCPT")) {
bool done = false;
//--------------------------------------
// Obtain the recipient's email address.
//--------------------------------------
string recipient = input.substr(5);
//-----------------------------------------------
// Fetch the recipient list for the alias.
//-----------------------------------------------
string aliasRecipients;
if(getAliasList(cleanEMail(recipient), aliasRecipients)) {
//-----------------------------------------------
// If the list is not blank then add the list
// to the recipient list and accept the request.
//-----------------------------------------------
recipientList += aliasRecipients;
cout << "250 OK" << CRLF;
log.message("Response: 250 OK.");
state = "RCPT";
done = true;
}
//-------------------------------------------------
// No alias entry for the mailbox so check if it
// is a hard mailbox.
//-------------------------------------------------
if(!done) {
//--------------------------------------
// Check to see if the user is a local
// address to deliver to.
//--------------------------------------
if(localUser(cleanEMail(recipient))) {
//------------------------------------------
// They're local so lets queue it for them.
//------------------------------------------
recipientList += cleanEMail(recipient) + " ";
cout << "250 OK" << CRLF;
log.message("Response: 250 OK.");
state = "RCPT";
done = true;
}
}
//--------------------------------------------------
// There is no mailbox either so do domain check.
//--------------------------------------------------
if(!done) {
//---------------------------------------------------
// Check to see if the domain is good at least and
// if there is a default mailbox address..
//---------------------------------------------------
string defaultMailbox;
if(localDomain(domainOnly(cleanEMail(recipient)), defaultMailbox)) {
//--------------------------------------------------
// The domain is local so if there is a default
// mailbox then let's send it to the queue with the
// default address attached instead.
//--------------------------------------------------
if(defaultMailbox != "*NONE") {
//---------------------------------------------------------
// If the default mailbox is *DISCARD then we do not want
// to add the mailbox to the recipient list but we still
// want to say we have a mailbox.
//---------------------------------------------------------
if(defaultMailbox != "*DISCARD")
recipientList += defaultMailbox + " ";
cout << "250 OK" << CRLF;
log.message("Response: 250 OK.");
state = "RCPT";
done = true;
}
//--------------------------------------------------
// If there is no default address then we will tell
// the client that the mailbox does not exist.
//--------------------------------------------------
else {
cout << "553 Mailbox does not exist" << CRLF;
log.message("Response: 553 Mailbox does not exist.");
done = true;
}
}
}
//--------------------------------------------------------------
// If we are not done then this is a relay request.
//--------------------------------------------------------------
if (!done) {
//---------------------------------------------------------
// If relaying is enabled for this conversation then
// queue the mail.
//---------------------------------------------------------
if(relay) {
recipientList += cleanEMail(recipient) + " ";
cout << "250 OK" << CRLF;
log.message("Response: 250 OK.");
state = "RCPT";
}
//----------------------------------------
// Otherwise send an error.
//----------------------------------------
else {
cout << "553 Server requires authentication to relay" << CRLF;
log.message("Response: 553 Server requires authentication to relay.");
}
}
}
else {
cout << "503 Please use MAIL first" << CRLF;
log.message("Response: 503 Please use MAIL first.");
}
}

20
__SMTP_RSET.h Normal file
View File

@ -0,0 +1,20 @@
#ifndef ____SMTP_RSET_h__
#define ____SMTP_RSET_h__
#include "Command.h"
namespace http {
class __SMTP_RSET : public core::Command {
int processCommand(std::string request, Session *session, std::stringstream &data);
state = "READY";
data << "250 OK" << CRLF;
return 0;
}
};
}
#endif

19
__SMTP_VRFY.h Normal file
View File

@ -0,0 +1,19 @@
#ifndef ____SMTP_VRFY_h__
#define ____SMTP_VRFY_h__
#include "Command.h"
namespace http {
class __SMTP_VRFY : public core::Command {
int processCommand(std::string request, Session *session, std::stringstream &data) {
data << "252 You must know who the mail is for" << CRLF;
return 0;
}
};
}
#endif