//
// File: hzMailer.h
//
// Legal Notice: This file is part of the HadronZoo C++ Class Library.
//
// Copyright 2025 HadronZoo Project (http://www.hadronzoo.com)
//
// The HadronZoo C++ Class Library is free software: You can redistribute it, and/or modify it under the terms of the GNU Lesser General Public License, as published by the Free
// Software Foundation, either version 3 of the License, or any later version.
//
// The HadronZoo C++ Class Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
// A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License along with the HadronZoo C++ Class Library. If not, see http://www.gnu.org/licenses.
//
//
// Prototypes for the HadronZoo email facilities
//
#ifndef hzMailer_h
#define hzMailer_h
#include "hzMimetype.h"
#include "hzIpaddr.h"
#include "hzEmaddr.h"
#include "hzTmplList.h"
#include "hzTmplVect.h"
#include "hzTmplSet.h"
#include "hzTmplMapS.h"
enum SMTPCode
{
// Category: Internet
//
// All possible SMTP return codes
SMTP_NOOP = 0, // Returned by GetSMTPCode() in error
// Initial Connection Errors
SMTP_CONN_FAIL = 101, // The server is unable to connect
SMTP_CONN_REFUSED = 111, // Connection refused or inability to open an SMTP stream
// Success
SMTP_STATUS_NS = 200, // Non standard system status message or help reply
SMTP_STATUS = 211, // System status, or system help reply
SMTP_HELP = 214, // Help message (this reply is useful only to the human user)
SMTP_READY = 220, // Service ready
SMTP_QUIT = 221, // Service closing transmission channel
SMTP_GO_AHEAD = 235, // Tells client to go ahead
SMTP_OK = 250, // Requested mail action okay, completed
SMTP_NOTLOCAL = 251, // User not local; will forward to
SMTP_NOVRFY = 252, // Cannot VRFY user, but will accept message and attempt delivery
// Directives to send
SMTP_LOGINFO = 334, // Login info, either username or password
SMTP_SENDDATA = 354, // Start mail input; end with <CRLF>.<CRLF>
// Temporary Errors
SMTP_TIMEOUT = 420, // General connection timeout issue
SMTP_UNAVAILABLE = 421, // Service not available,
SMTP_MBOX_FULL = 422, // The recipients mailbox has exceeded its storage limit
SMTP_DISK_FULL = 431, // Not enough space on the disk - Expected to be temporary.
SMTP_MBOX_HALT = 432, // Recipients incoming mail queue has been stopped
SMTP_TIMEOUT_PX = 441, // The recipients server is not responding
SMTP_CONN_XMIT = 442, // The connection was dropped during the transmission.
SMTP_CONN_HOPS = 446, // The maximum hop count was exceeded for the message
SMTP_TIMEOUT_NS = 447, // Non standard timeout: Some SMTP servers will send this if they have decided to time out the client (for being too slow).
SMTP_ROUTE = 449, // Routing error
SMTP_MBOXBUSY = 450, // Requested mail action not taken: mailbox unavailable (E.g., mailbox busy)
SMTP_PROCERROR = 451, // Requested action aborted: local error in processing
SMTP_NOSPACE = 452, // Action not taken: no space left
SMTP_CLIENT_ERR = 471, // Some SMTP servers return this in respose to errors on the part of the connecting client (as they see it).
// Permanent Errors
SMTP_BADCOMMAND = 500, // Syntax error, command unrecognized (This may include errors such as cmd line too long)
SMTP_BADSYNTAX = 501, // Syntax error in parameters or arguments
SMTP_NOCOMMAND = 502, // Command not implemented
SMTP_BAD_CMD_SEQ = 503, // Bad sequence of commands
SMTP_BAD_PARAM = 504, // Command parameter not implemented
SMTP_BAD_SNDR_ADDR = 510, // Bad email address
SMTP_BAD_RCPT_ADDR = 511, // Bad email address
SMTP_BAD_HOST = 512, // Host server for the recipients domain name cannot be found in DNS
SMTP_BAD_ADDR_TYPE = 513, // Address type is incorrect
SMTP_MSG_SIZE = 523, // Size of your mail exceeds the server limits
SMTP_BAD_SERVER = 530, // Authentication problem
SMTP_BAD_SENDER = 541, // The recipient address rejected your message
SMTP_NACK = 550, // Non-existent email address
SMTP_TRYOTHER = 551, // User not local or address invalid.
SMTP_MBOXFULL = 552, // Requested mail action aborted: exceeded storage allocation
SMTP_BADNAME = 553, // Mailbox name not allowed (E.g., mailbox syntax incorrect)
SMTP_FAILED = 554, // Transaction failed (Or in the case of a connection-opening response, No SMTP service)
SMTP_INVALID = 999 // Not a 3 digit entity
} ;
/*
** Email item
*/
enum hzContentType
{
// Category: Internet
//
// The hzContentType enumeration refers to the MIME type of files and includes MIME types used in constructing emails
HZ_CONTENT_TYPE_UNDEFINED, // Content type not defined
// For body email parts
HZ_CONTENT_TYPE_TEXT_PLAIN, // Content is in plain text and there are no further levels
HZ_CONTENT_TYPE_TEXT_HTML, // Content is in HTML and there are no further levels
HZ_CONTENT_TYPE_IMAGE_GIF, // Content is an image (gif)
HZ_CONTENT_TYPE_IMAGE_JPEG, // Content is an image (jpeg)
HZ_CONTENT_TYPE_IMAGE_PNG, // Content is an image (png)
HZ_CONTENT_TYPE_IMAGE_ICO, // Content is an image (ico)
HZ_CONTENT_TYPE_SCRIPT_JS, // Content is a java script
HZ_CONTENT_TYPE_APP_MSWORD, // Content is an application file (ms-word document)
HZ_CONTENT_TYPE_APP_MSEXCEL, // Content is an application file (ms-excel document)
HZ_CONTENT_TYPE_APP_PDF, // Content is an application file (pdf)
HZ_CONTENT_TYPE_APP_TAR, // Content is an application file (tar)
HZ_CONTENT_TYPE_APP_GZIP, // Content is an application file (gzipped)
HZ_CONTENT_TYPE_APP_ZIP, // Content is an application file (zipped)
// For headers only
HZ_CONTENT_TYPE_MULTI_ALTERNATIVE, // Content is presented more than once in different parts with each part representing a different format (eg text and HTML).
HZ_CONTENT_TYPE_MULTI_MIXED, // The content is comprised of parts that may be dissimilar in format
HZ_CONTENT_TYPE_MULTI_RELATED, // The content is comprised of related parts (only applies to email root part)
} ;
enum hzContentEncoding
{
// Category: Internet
//
// The hzContentEncoding enumeration describes what form of text encoding is being used (in emails inter alia)
HZ_CONTENT_ENCODE_UNDEFINED, // Content encoding not defined
HZ_CONTENT_ENCODE_7BIT, // Content is not encoded but chars are limited to lower ASCII
HZ_CONTENT_ENCODE_8BIT, // Content is not encoded and chars may be upper ASCII
HZ_CONTENT_ENCODE_BINARY, // Content is not encoded and chars may be anything
HZ_CONTENT_ENCODE_BASE64, // Content is encoded as base64
HZ_CONTENT_ENCODE_QUOTE_PRINT, // Content is encoded as 'quoted-prinatble' meaning that chars ? and = are escaped
} ;
class hzEmpart
{
// Category: Internet
//
// Component of an IMF message (email)
public:
hzChain m_Content ; // The content of the email part
hzString m_Filename ; // Filename
hzString m_ContType ; // Type of content (eg text/plain)
hzContentEncoding m_Encoding ; // Method of encoding (eg base64)
hzEmpart (void)
{
m_Encoding = HZ_CONTENT_ENCODE_UNDEFINED ;
}
~hzEmpart (void)
{
Clear() ;
}
void Clear (void)
{
m_Encoding = HZ_CONTENT_ENCODE_UNDEFINED ;
m_ContType.Clear() ;
m_Content.Clear() ;
m_Filename.Clear() ;
}
hzEmpart& operator= (const hzEmpart& op)
{
m_Content = op.m_Content ;
m_Filename = op.m_Filename ;
m_ContType = op.m_ContType ;
m_Encoding = op.m_Encoding ;
return *this ;
}
} ;
class hzEmail
{
// Category: Internet
//
// hzEmail is a single email message container, used to compose email messages prior to sending, and for deconstruction of recieved email messages into their component parts.
//
// In the send case, the hzEmail instance starts out empty and the message is built in a step by step process. The sender is set, recipients are added one by one, the subject
// is set, the text body is added, the HTML body is added (optional), then attachments (if any) are added one by one. Finally hzEmail::Compose() is called to assemble an IMF
// message, ready for SMTP relay.
//
// In the recv case, hzEmail::Import() processes an IMF message to populate the set of headers and parts, thereby availing these to the application.
//
// Note that four hzEmail members are used for email addresses related to the sender, three of which are set by SMTP headers in the IMF message as follows:-
//
// m_AddrReturn is set by Return-path;
// m_AddrReply is set by Reply-To;
// m_AddrFrom is set by From.
//
// The forth, m_AddrRelay is set at the start of an SMTP client session (message incoming). It is used to set m_AddrFrom if the relayed IMF lacks the From: header, but will be
// ignored otherwise.
//
// m_AddrFrom is the definative sender address. HadronZoo::Epistula stores the m_AddrFrom value as the sender in its message repository, and in the webmail service, this value
// appears in the folder listings. In the construction of an outgoing message, m_AddrFrom is set by hzEmail::SetSender(). In the subsequent relay session, the m_AddrFrom value
// is used as the initiator address. m_AddrReply and m_AddrReturn are set separately. m_AddrReply can be NULL but if m_AddrReturn is NULL, it will assume the m_AddrFrom value.
struct _efile
{
// Attachment description
hzString m_Filepath ; // Full pathname of file as given by AddAttachment() or as supplied in an incoming message
hzMimetype m_eType ; // File type
} ;
hzList <hzEmaddr> m_Recipients ; // List of main recipient(s)
hzList <hzEmaddr> m_CC ; // List of cc recipient(s)
hzList <hzEmaddr> m_BCC ; // List of bcc recipient(s)
hzList <_efile> m_SendAttach ; // List of file attachments (added by Attach() as part of forming an outgoing email)
// Support functions
hzEcode _part_process (chIter& zi, const hzString& mark, uint32_t nLevel) ;
public:
hzList <hzEmpart> m_Attach ; // List of message parts that are attached files
hzList <hzEmpart> m_Inline ; // List of message parts (inline)
hzVect <hzPair> m_Hdrs ; // Message headers: Note this is only filled by Import. Composure of an outgoing message does not populate this list.
hzChain m_Final ; // Fully composed email (IMF)
hzChain m_Text ; // Body of email (Text part)
hzChain m_Html ; // Body of email (Html part)
hzChain m_Err ; // For error reporting (e.g. failed import)
hzXDate m_Date ; // Date of email (recv only)
hzEmaddr m_AddrRelay ; // Sender address as stated at the outset of the SMTP client session. In outgoing messages, this is always the same as m_AddrFrom.
hzEmaddr m_AddrReturn ; // Return address (Return-path header)
hzEmaddr m_AddrReply ; // Address given in the ReplyTo header
hzEmaddr m_AddrFrom ; // Authorative address of sender, as given by From header
hzEmaddr m_AddrTo ; // Address of primary recipient
hzString m_Id ; // Mail id (typically as set by first SMTP server to handle it)
hzString m_DomainOrig ; // Domain part of m_AddrRelay (see below). Note for the message to be accepted, the domain part of m_AddrReturn, m_AddrReply, and m_AddrFrom must concur.
hzString m_RealReply ; // Real name given in Reply-To header (if any)
hzString m_RealFrom ; // Real name of sender
hzString m_RealTo ; // Real name of recipient (rarely used)
hzString m_Subject ; // Subject
hzIpaddr m_SenderIP ; // Sender IP address
hzContentType m_ContType ; // Content type
hzContentEncoding m_Encoding ; // Content encoding
hzEmail (void)
{
m_ContType = HZ_CONTENT_TYPE_UNDEFINED ;
m_Encoding = HZ_CONTENT_ENCODE_UNDEFINED ;
}
~hzEmail (void)
{
Clear() ;
}
void Clear (void) ;
uint32_t CountRecipientsStd (void) const { return m_Recipients.Count() ; }
uint32_t CountRecipientsCC (void) const { return m_CC.Count() ; }
uint32_t CountRecipientsBC (void) const { return m_BCC.Count() ; }
uint32_t CountRecipientsAll (void) const { return m_Recipients.Count() + m_CC.Count() + m_BCC.Count() ; }
// Functions to compile and send an email
hzEcode SetSender (hzEmaddr& sender) { m_AddrFrom = sender ; return m_AddrFrom? E_OK : E_NODATA ; }
hzEcode SetSender (const char* cpSender, const char* cpRealname) ;
hzEcode AddRecipient (hzEmaddr& recipient) ;
hzEcode AddRecipientCC (hzEmaddr& recipient) ;
hzEcode AddRecipientBCC (hzEmaddr& recipient) ;
hzEcode AddRecipient (const char* cpRecipient) ;
hzEcode AddRecipientCC (const char* cpRecipient) ;
hzEcode AddRecipientBCC (const char* cpRecipient) ;
hzEcode SetSubject (const char* cpSublect) ;
hzEcode AddBody (hzChain& Z) ;
hzEcode AddBody (hzString& S) ;
hzEcode AddBody (const char* cpText) ;
hzEcode AddAttachment (const char* cpDir, const char* cpFilename, hzMimetype eType) ;
hzEcode AddAttachment (const char* cpFilepath, hzMimetype eType) ;
hzEcode AddAttachment (const char* cpFilepath) ;
hzEcode Compose (void) ;
hzEcode SendSmtp (const char* cpMailserver, const char* username, const char* password) ;
hzEcode SendEpistula (hzChain& report) ;
// Functions to load a recived email and extract headers and parts from it
hzEcode Import (const hzChain& Z, bool bHead = false) ;
// Get functions
hzEmaddr& GetSender (void) { return m_AddrFrom ; }
hzString& GetSenderReal (void) { return m_RealFrom ; }
hzString& GetSubject (void) { return m_Subject ; }
hzList<hzEmaddr>& GetRecipientsStd (void) { return m_Recipients ; }
hzList<hzEmaddr>& GetRecipientsCC (void) { return m_CC ; }
hzList<hzEmaddr>& GetRecipientsBCC (void) { return m_BCC ; }
} ;
class hzPop3Acc
{
// Category: Internet
//
// POP3 Account.
//
// The hzPop3Acc (POP3 account) class enables applications to operate as a POP3 email client and recieve email messages from a POP3 server. Each hzPop3Acc
// instance comprises a single username and password pair and so can only access a single 'mailbox' or 'email account'. What constitutes a mailbox or email
// account is strictly a matter for the POP3 server. Some POP3 servers such as Epistula, have user centric mailboxes that can span multiple recipient email
// addresses. Other POP3 servers have address centric mailboxes which will only contain email messages for a single recipient email address.
//
// hzPop3Acc specifies a single file into which all emails for the account are aggregated and maintains a set of email ids for lookups and to avoid repeat
// downloads. The email messages themselves are available to the application as populated hzEmail instances.
hzSet<hzString> m_Already ; // IDs of already downloaded messages
hzString m_Server ; // Hostname for the account
hzString m_Username ; // Username for the account
hzString m_Password ; // Password for the account
hzString m_Repos ; // Pathname of repository
public:
hzChain m_Error ; // For error messages arising in a POP3 session
hzPop3Acc (void) {}
~hzPop3Acc (void) {}
hzEcode Init (hzString Server, hzString Username, hzString Password, hzString Repos) ;
uint32_t Count (void) const { return m_Already.Count() ; }
// Collect (download) emails
hzEcode Collect (hzVect<hzString>& messages) ;
// Fetch a downloaded email or header
hzEcode GetEmail (hzEmail& em, hzString& mailId) ;
} ;
/*
** Prototypes
*/
void CreateMessageID (hzString& mailId, const hzDomain& domain) ;
#endif // hzMailer_h