# 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 e-mails supports both UTF-8 and Latin-1 (ISO-8859-1) character encodings. The E-mails can be configured to utilize either encodings.

## File information

File | common/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 iomux | The IIoMux instance the smtp will be registered to.
  class ISocketProvider * tcpSocketProvider | The socket provider for the TCP connection.
  class ISocketProvider * tlsSocketProvider | The socket provider for the TLS connection.
  class IInstanceLog * const log | The IInstanceLog instance used for loging purposes.
  class IDns * const dns | A 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 * from | The sender's email address.
  const char * fromName | The sender's name on UTF-8.
  const char * subject | The subject for the email on UTF-8.
  const char * host | Resolvable DNS name or an IP address literal e.g. [123.255.37.2] of the SMTP client.
  const char * server | Resolvable DNS name or IP-address of used SMTP server.
  const char * userSmtp | The sender's user, if authentication is required.
  const char * password | The 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 * attachmentChunk | The chunk of the file that will be attached.
  int sChunk | The size of the chunk.
  bool lastChunk | This indicates if this is the last chunk of the attachment (true) or there are still more (false).
  dword fileIndex | The 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 * attachmentUrl | The 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 * rcpt | The 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 * rcpt | The 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 * rcpt | The 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 * data | The text that will be sent on the body.
  const char * format | The text format i.e., text/html or text/plains.
  const char * charset | The 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 * user | Used 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 * user | The username of the receiver for authentication.
  const char * password | The password of the receiver for authentication.
  const char * domain | The 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 e-mail. The given ISmtpReceive instance as a argument makes possible to call the functions of the ISmtpReceive , allowing the connection with ISmtpReceive.

  #### Parameters

  class ISmtpReceive * smtpRecv | The ISmtpReceive instance resulting from listening.

  #### Remarks

  It is advisable to create an USmtpReceive object within this function to be prepared for incoming e-mail 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 e-mail data is about to be received, this function is called to initiate the process of receiving the e-mail 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 e-mail and attachment data into the provided buffer. It is crucial for the USmtpReceive class to process this data accordingly.

  #### Parameters

  char * buffer | The buffer in which the e-mail body is written.
  int length | The size, in bytes, of the data segment to be received from the ISmtpReceive class.
  bool complete | Indicates whether the current data segment is the final part of the e-mail 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 e-mail body. Note that a true value for "complete" does not imply the absence of remaining attachment data.

  #### Remarks

  After the entire e-mail 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 const char * GetXForwardedFor() = 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 e-mail 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 * receive | The USmtpReceive instance created n the beginning of receiving e-mails.

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 e-mail 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 e-mail.

  #### Return value

  A char* pointer to the attachment name.
GetContentType
  The function yields whether the content type of the e-mail 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
GetXForwardedFor
  If the mail was sent over an innovaphone reverse proxy, this function returns the client IP address, otherwise a nullptr. You must not free the return value.

  #### Return value

  const char *
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 * mBuffer | The buffer in which the e-mail body data will be written. This most not be a nullptr.
  int len | The 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 e-mail attachment. if there is an attachment in the e-mail, the ISmtpReceive will call CallRecvBody.

  #### Parameters

  char * mBuffer | The buffer in which the attachment data will be written. This most not be a nullptr.
  int lenThe 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. |

# 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
    };