SMTP

Smtp interface provides the service to send and receive emails using the Simple Mail Transfer Protocol. To create an ISmtp instance, at first an instance of ISmtpProvider, ISocketProvider (TCP) and ISocketProvider (TLS) must be created. This need to be done by calling CreateSmtpProvider(), CreateTCPSocketProvider() and CreateTLSSocketProvider(tcpSocketProvider). An ISmtpProvider can be used to create multiple ISmtp instances. To read more about the ISocket, please check: ISocket.

When a new TaskSmtpSend is started, the connection with the SMTP server is established using TCP. If the server allows STARTTLS, the connection will be upgraded to TLS. Otherwise TCP will continue to be used.

In addition, the SMTP server for receiving emails supports both UTF-8 and Latin-1 (ISO-8859-1) character encodings. The emails can be configured to utilize either encodings.

File information

Filecommon/interface/smtp.h

Public functions CreateSmtpProvider

Classes ISmtpProvider
ISmtp
ISmtpSend
ISmtpListen
USmtpListen
USmtpReceive
ISmtpReceive

Code Examples Initialize the Provider
Create the ISmtp instance
Send Emails
Receive Emails

Functions

Functions to initialize

class ISmtpProvider * CreateSmtpProvider();
CreateSmtpProvider
Creates an instance of the ISmtpProvider.

Return value

The actual ISmtpProvider instance. That instance can be freed as soon as it no longer is used by calling the C++ delete operator.

Classes

ISmtpProvider

class ISmtpProvider {
public:
    virtual ~ISmtpProvider() {}
    virtual class ISmtp * CreateSmtp(class IIoMux * iomux, class ISocketProvider * tcpSocketProvider, class ISocketProvider * tlsSocketProvider, class IInstanceLog * const log, class IDns * const dns = nullptr) = 0;
};

Overview

This class provides ISmtp instance to manage the task to send emails.

Public functions

CreateSmtpProvider
Returns a new ISmtp instance.

Parameters

class IIoMux * const iomuxThe IIoMux instance the smtp will be registered to.
class ISocketProvider * tcpSocketProviderThe socket provider for the TCP connection.
class ISocketProvider * tlsSocketProviderThe socket provider for the TLS connection.
class IInstanceLog * const logThe IInstanceLog instance used for loging purposes.
class IDns * const dnsA IDns instance that provides DNS resolution.

Return value

An ISmtp instance that can be used to manage the task to send emails.

Remarks

The application should store a pointer to it.

ISmtp

class ISmtp {
public:
    enum ContentTypes {
    ContentTextHtml,
    ContentTextPlain,
    ContentUnknown
    };

public:
    virtual ~ISmtp() {}
    virtual class ISmtpSend * CreateSend(const char *from, const char *fromName, const char *subject, const char *host, const char *server, const char *userSmtp, const char *password) = 0;
    virtual void SendChunk(const char *attachmentChunk, int sChunk, bool lastChunk, dword fileIndex) = 0;
    };

Overview

This class provides ISmtpSend task to send an email.

ContentTypes Enumeration

The ContentTypes enumeration defines the various content types that can be used in email messages. This helps in specifying the format of the content being received in the email.

Enumerators

ContentTextHtml

Represents HTML content in the email body.

ContentTextPlain

Represents plain text content in the email body. This type is used when the email content is in plain text format without any special formatting.

ContentUnknown

Represents an unknown content type. This can be used as a default value or when the content type cannot be determined.

Public functions

CreateSend
Returns a new SMTP Provider.

Parameters

const char * fromThe sender's email address.
const char * fromNameThe sender's name on UTF-8.
const char * subjectThe subject for the email on UTF-8.
const char * hostResolvable DNS name or an IP address literal e.g. [123.255.37.2] of the SMTP client.
const char * serverResolvable DNS name or IP-address of used SMTP server.
const char * userSmtpThe sender's user, if authentication is required.
const char * passwordThe sender's password, if authentication is required.

Return value

An ISmtpSend task that can be used to send an email.
SendChunk
Returns a new SMTP Provider.

Parameters

const char * attachmentChunkThe chunk of the file that will be attached.
int sChunkThe size of the chunk.
bool lastChunkThis indicates if this is the last chunk of the attachment (true) or there are still more (false).
dword fileIndexThe number that identifies the attached file (starting on 0 and ending with the number of files been attached minus 1).

Remarks

This must be called inside TaskProgress if at least one file has been attached to the email.

ISmtpSend

class ISmtpSend : public ITask {
public:
    virtual void AddAttachment(const char *attachmentUrl) = 0;
    virtual void AddTo(const char *rcpt) = 0;
    virtual void AddCc(const char *rcpt) = 0;
    virtual void AddBcc(const char *rcpt) = 0;
    virtual void AddBody(const char *data, const char *format, const char *charset) = 0;
    virtual void Start(class UTask * user) = 0;
};

Overview

This class implements the task to send an email.

Public functions

AddAttachment
Adds a new attachment to the email.

Parameters

const char * attachmentUrlThe filename of the attachment that will be sent.

Remarks

It should be called once per attachment.
AddTo
Adds a new receiver to the recipients list.

Parameters

const char * rcptThe receiver's email address.

Remarks

It should be called at least once before Start(). The email will be sent to every rcpt if they are reachable (for example, it could be unreachable if your SMTP server is on "Relay Access denied" mode). If none of the receivers is reachable, the email will not be sent.
AddCc
Adds a new receiver to the recipients list. The email will be sent as a Carbon Copy to this receiver.

Parameters

const char * rcptThe receiver's email address.

Remarks

It should be called at least once before Start(). The email will be sent to every rcpt if they are reachable (for example, it could be unreachable if your SMTP server is on "Relay Access denied" mode). If none of the receivers is reachable, the email will not be sent.
AddBcc
Adds a new receiver to the recipients list. The email will be sent as a Blind Carbon Copy to this receiver.

Parameters

const char * rcptThe receiver's email address.

Remarks

It should be called at least once before Start(). The email will be sent to every rcpt if they are reachable (for example, it could be unreachable if your SMTP server is on "Relay Access denied" mode). If none of the receivers is reachable, the email will not be sent.
AddBody
Adds the body to the email.

Parameters

const char * dataThe text that will be sent on the body.
const char * formatThe text format i.e., text/html or text/plains.
const char * charsetThe text charset i.e., ISO-8859-1 or UTF-8.

Remarks

This function must be called at least one. If the email should not have a body, this function must still be called with and empty string as body and valid format and charset values.
Start
Starts the task that sends the email.

Parameters

class UTask * userUsed for the callbacks for task completion.

Remarks

Several ISmtpSend tasks can be called at once and the emails will be queued.

ISmtpListen

class ISmtpListen {
public:
    virtual class ISmtpReceive * CreateReceive(const char * user, const char * password, const char * domain) = 0;
    virtual void Shutdown() = 0;
};

Overview

This class manages the incoming SMTP connections.

Public functions

CreateReceive
Creates an ISmtpReceive instance.

Parameters

const char * userThe username of the receiver for authentication.
const char * passwordThe password of the receiver for authentication.
const char * domainThe domain name of the receiver for authentication.

Return value

A pointer to an ISmtpReceive object.
Shutdown
Shuts down the listening socket.

USmtpListen

class USmtpListen {
public:
    virtual void SmtpListenResult(class ISmtpReceive * smtpRecv) = 0;
    virtual void SmtpListenShutdownComplete() = 0;
    virtual class ISmtpReceive * CreateSmtpReceive() = 0;
    };

Overview

This class is used to receive the callbacks from the ISmtpListen instance. So this class provides methods for handling SMTP listening results, including smtp Listen shutdown.

Public functions

SmtpListenResult
Called when an SMTP listen accepts a connection for receiving an incoming email. The given ISmtpReceive instance as a argument makes possible to call the functions of the ISmtpReceive , allowing the connection with ISmtpReceive.

Parameters

class ISmtpReceive * smtpRecvThe ISmtpReceive instance resulting from listening.

Remarks

It is advisable to create an USmtpReceive object within this function to be prepared for incoming email data.
SmtpListenShutdownComplete
Called when the ISmtpListen' shutdown process is complete.
CreateSmtpReceive
Creates a new SMTP receive object. It is reasonable to call CreateReceive function with the username, password and the domain name information respectively within this function. In this way, ISmtpReceive instance will utilize this information for the authentication process.

Return value

A pointer to an ISmtpReceive object.

USmtpReceive

class USmtpListen {
public:
    virtual void SmtpReceiveCancelComplete() = 0;
    virtual void CallRecvBody() = 0;
    virtual void SmtpHeader() = 0;
    virtual void SmtpBody(char * buffer, int length, bool complete) = 0;
    };

Overview

The USmtpReceive class is used to receive the callbacks from the ISmtpReceive class. Accordingly, this class will be implemented by the user-side.

Public functions

SmtpReceiveCancelComplete
This is the final callback triggered when the receiving socket is closed. After this, no more data will be received from the ISmtpReceive class.
CallRecvBody
The ISmtpReceive class processes the incoming data in real time without storing it. When email data is about to be received, this function is called to initiate the process of receiving the email body. Therefore, RecvBody must be called within this function to start the data-receiving process. In other words, CallRecvBody is called when the ISmtpReceive class starts to parse and deliver a new segment of the email (such as an image or a pdf datei), as it parses the data in real time.

Remarks

The RecvAttachment function should not be called additionally if the body and the attachment buffers are the same. The initiation of the email body reception through CallRecvBody will automatically start the process of receiving attachments, if any are present. If the body and the attachment buffers are different, then the RecvAttachment function must be called.
SmtpHeader
Specific header information such as the recipient's and sender's addresses, subject, and date of the email can be retrieved within this function by calling the respective getters "GetFrom", "GetTo", "GetSubject", and "GetDate".
SmtpBody
This function is used by the ISmtpReceive class to deliver email data in parts. It writes the email and attachment data into the provided buffer. It is crucial for the USmtpReceive class to process this data accordingly.

Parameters

char * bufferThe buffer in which the email body is written.
int lengthThe size, in bytes, of the data segment to be received from the ISmtpReceive class.
bool completeIndicates whether the current data segment is the final part of the email body. If "complete" is false, "RecvBody" must be called again to receive subsequent data segments. If "complete" is true, it signifies that the current batch is the last segment of the email body. Note that a true value for "complete" does not imply the absence of remaining attachment data.

Remarks

After the entire email body is received (when complete is true), and if attachment data exists, the ISmtpReceive class will continue to process this data. The "RecvAttachment" function should be called again to receive the remaining attachment data. "Complete" returning true again indicates that the receiving of the attachment data is completed. In the case of attachments, the final length value passed to SmtpBody represents the size of the last chunk of attachment data.

ISmtpReceive

class USmtpListen {
public:
    virtual void Cancel() = 0;
    virtual void Receive() = 0;
    virtual void Accept(USmtpReceive * receive) = 0;
    virtual char * GetTo() = 0;
    virtual char * GetFrom() = 0;
    virtual char * GetSubject() = 0;
    virtual char * GetDate() = 0;
    virtual char * GetFileName() = 0;
    virtual enum ISmtp::ContentTypes GetContentType(const char ** type = 0) = 0;
    virtual void RecvBody(char * mBuffer, int len) = 0;
    virtual void RecvAttachment(char * mBuffer, int len) = 0;
    };

Overview

The ISmtpReceive class is used for SMTP receiving operations.

Public functions

Cancel
Shuts down the socket for receiving operations, so no email data will be received after the cancel is called.
Receive
Initiates the SMTP receive operation, so with this function, the receiving socket starts receiving the data.
Accept
Needs to be called to establish the communication between the USmtpReceive and ISmtpReceive classes. It makes sense to call 'Accept' function once the instance of 'USmtpReceive' is created to set up the connection with the server correctly.

Parameters

USmtpReceive * receiveThe USmtpReceive instance created n the beginning of receiving emails.
GetTo
Retrieves the recipient's address.

Return value

A char* pointer to the recipient's address.
GetFrom
Retrieves the sender's address.

Return value

A char* pointer to the sender's address.
GetSubject
Retrieves the subject of the email.

Return value

A char* pointer to the to the email subject.
GetDate
Retrieves the date of the email.

Return value

A char* pointer to the email date.
GetFileName
Retrieves the file name of the attachment of the email.

Return value

A char* pointer to the attachment name.
GetContentType
The function yields whether the content type of the email is HTML, plain or unknown. If the content type is unknown, then the parameter type will yield the content type as a string.

Return value

enum content types
RecvBody
Receives a portion of the email body. It is triggered by "CallRecvBody" and processes and parses the data in real-time as it is received.

Parameters

char * mBufferThe buffer in which the email body data will be written. This most not be a nullptr.
int lenThe amount of bytes to be received from the ISmtpReceive. Given that the data is to be stored in mBuffer, it is essential for mBuffer to have a size that is at least equal to len.
RecvAttachment
Receives a portion of the email attachment. if there is an attachment in the email, the ISmtpReceive will call CallRecvBody.

Parameters

The number of bytes to be received from the ISmtpReceive service. Since the data is to be stored in mBuffer, mBuffer should be at least as large as len.
char * mBufferThe buffer in which the attachment data will be written. This most not be a nullptr.
int len

Code Examples

Initialize the Provider

#include "common/interface/smtp.h"
...
ISmtpProvider * smtpProvider = CreateSmtpProvider();
...
AppService * service = new AppService(iomux, localSocketProvider, tcpSocketProvider, tlsSocketProvider, ..., smtpProvider, &serviceArgs);
...
delete smtpProvider;

Create the ISmtp instance

app::app(IIoMux * const iomux, ISocketProvider * localSocketProvider, IWebserverPluginProvider * const webserverPluginProvider, IDatabaseProvider * databaseProvider, class Service * service, ISmtpProvider * smtpProvider, AppInstanceArgs * args) :
AppInstance(service, args)
{
    this->smtp = smtpProvider->CreateSmtp(iomux, tcpSocketProvider, tlsSocketProvider, this, "145.253.157.7");
    this->logFlags |= LOG_SMTP; // to show the flags generated by the SMTP class
}
app::~app()
{
    if (webserverPlugin) delete webserverPlugin;
    if (smtp) delete smtp;
}

Send Emails

SendEmail::SendEmail(class App * app): taskSmtp(this, &SendEmail::SmtpSendComplete, &SendEmail::SmtpSendFailed, &SendEmail::SmtpSendProgress)
{        
    ...
}
SendEmail::~SendEmail()
{
    ...
}
void SendEmail::Send()
{
    ISmtpSend * emailTask = appInstance->smtp->CreateSend("sender@innovaphone.com", "SenderName", "Subject", "sophos", "sophos.innovaphone.com", "usersmtp", "password");
    emailTask->AddTo("receiver@innovaphone.com");
    emailTask->AddTo("otherreceiver@innovaphone.com");
    emailTask->AddBody("Body of the email...", "text/html", "ISO-8859-1");
    emailTask->AddAttachment("test.txt");
    emailTask->AddAttachment("Fish.png");
    emailTask->AddAttachment("rfc821.pdf");
    bufferrb = (char*)malloc(sizeof(char) * 4096);
    emailTask->Start(&taskSmtp);
}
void SendEmail::SmtpSendComplete(class TaskSmtpSend * task)
{
    ...
}
void SendEmail::SmtpSendFailed(class TaskSmtpSend * task)
{
    ...
}
void SendEmail::SmtpSendProgress(class TaskSmtpSend * task, dword progress)
{
    //SmtpSendProgress will be called for every attachment added. For each attachment it will be called until lastChunk will be set as true.
    //progress indicates the number of the attachment requested by the task.
    //It starts with "0" for the first filename added.
    
    appInstance->smtp->SendChunk(ReadFiles(progress), len, lastChunk, progress);  
}
char * SendEmail::ReadFiles(dword file) //Example function to get the attachments chunks.
{
    char * path = (char*)"";
    switch (file)
    {
    case 0:
        path = (char*)"/path/test.txt";
        break;
    case 1:
        path = (char*)"/path/Fish.png";
        break;
    case 2:
        path = (char*)"/path/rfc821.pdf";
        break;
    default:
        break;
    }
    if (!opened)
    {
        opened = true;
        pFile = fopen(path, "rb");   
        fseek(pFile, 0, SEEK_END);
        lSize = ftell(pFile);
        rewind(pFile);
        lastChunk = false;
        bytesRead = 0;
    }
    if (!feof(pFile))
    {
        len = 0;
        len = fread(bufferrb, 1, 4096, pFile);
        bytesRead += len;
        if (bytesRead >= lSize) lastChunk = true;
    }
    if (lastChunk)
    {
        opened = false;
        fclose(pFile);
    }
    return bufferrb;
}

Receive Emails

    
// Class ForReceiving
class ForReceiving {
public:
void Start() {
    if (!smtpListen) {
    smtpListen = smtp->CreateListen(this);
    }
}

class ISmtpReceive* CreateSmtpReceive() {
    return smtpListen->CreateReceive("inno-fax", "fax123", "fax2.innovaphone.com");
}

void SmtpListenResult(class ISmtpReceive* smtpReceive) {
    if (!receiveMail) {
    receiveMail = new ReceiveMail(app, database, smtpReceive);
    } else {
    smtpReceive->Cancel();
    }
}

private:
    class ISmtpListen* smtpListen = nullptr;
    class ReceiveMail* receiveMail = nullptr;
    // Other member variables
};

// Class ReceiveMail
class ReceiveMail {
public:
    void Start() {
    smtpReceive->Accept(this);
}

void SmtpHeader() {
    const char* from = smtpReceive->GetFrom();
    const char* to = smtpReceive->GetTo();
}

void CallRecvBody() {
    smtpReceive->RecvBody(buffer, length);
}

void SmtpBody(char* buffer, int length, bool complete) {
    char* dataIn = buffer;
    const char * contentType = 0;
    smtpReceive->GetContentType(&contentType);
    if (contentType && !strcmp(contentType, "application/pdf")) {
    //change the state here
    }
    smtpReceive->RecvBody(buffer, length);
}

private:
    class ISmtpReceive* smtpReceive;
    char buffer[4096];
    // Other member variables 
};