178 lines
6.7 KiB
C++
178 lines
6.7 KiB
C++
#ifndef SMTP_MANAGER_CPP
|
|
#define SMTP_MANAGER_CPP
|
|
|
|
#include <iostream>
|
|
#include <vector>
|
|
#include <iomanip>
|
|
#include <ctime>
|
|
#include <curl/curl.h>
|
|
|
|
#include "emailTemplateCollection.cpp"
|
|
#include "utilities.cpp"
|
|
|
|
|
|
|
|
namespace SMTPManager {
|
|
static const std::string END_OF_LINE = "\r\n";
|
|
static const std::string END_OF_MAIL = "\0";
|
|
|
|
struct payload {
|
|
int lines_read = 0;
|
|
std::vector<std::string> payload_data;
|
|
};
|
|
|
|
/*
|
|
* Generates the Date Time Header Segment
|
|
* Date: {Date Time in RFC 2822 Format} \r\n
|
|
*/
|
|
std::string generateEmailSegmentDateTime() {
|
|
std::time_t time = std::time(nullptr);
|
|
std::tm localTime = *std::localtime(&time);
|
|
std::ostringstream oss;
|
|
oss << "Date: " << std::put_time(&localTime, "%a, %d %b %Y %T %z") << END_OF_LINE;
|
|
return oss.str();
|
|
}
|
|
|
|
/*
|
|
* Generates the Message-ID Header Segment based on the RFC 2822 Format
|
|
* Message-ID: <{SHA256}@{email server address}>\r\n
|
|
*/
|
|
std::string generateEmailSegmentMessageId(const Utilities::config &configuration) {
|
|
return "Message-ID: <" + Utilities::generateRandomHashValueSHA256() + "@" + configuration.emailServerAddress + ">" + END_OF_LINE;
|
|
}
|
|
|
|
/*
|
|
* Generates the Recipient Header Segment
|
|
* To: {Recipient Name} <{email address}>\r\n
|
|
*/
|
|
std::string generateEmailSegmentRecipient(const std::string &recipientName, const std::string &recipientEmail) {
|
|
return "To: " + recipientName + " <" + recipientEmail + ">" + END_OF_LINE;
|
|
}
|
|
|
|
/*
|
|
* Generates the Sender Header Segment
|
|
* From: {Sender Name} <{email address}>\r\n
|
|
*/
|
|
std::string generateEmailSegmentSender(const Utilities::config &configuration) {
|
|
return "From: " + configuration.emailAddressDisplay + " <" + configuration.emailAddress + ">" + END_OF_LINE;
|
|
}
|
|
|
|
/*
|
|
* Generates the Subject Header Segment
|
|
* Subject: {subject}\r\n
|
|
*/
|
|
std::string generateEmailSegmentSubject(const std::string &emailSubject) {
|
|
return "Subject: " + emailSubject + END_OF_LINE;
|
|
}
|
|
|
|
/*
|
|
* Generates the Content-Type Header Segment
|
|
*/
|
|
std::string generateEmailSegmentContentType() {
|
|
return "Content-Type: text/html; charset=UTF-8" + END_OF_LINE;
|
|
}
|
|
|
|
|
|
/*
|
|
* Generates a line for the body
|
|
* {bodyLine}\r\n
|
|
*/
|
|
std::string generateEmailSegmentBody(const std::string &body) {
|
|
return body + END_OF_LINE;
|
|
}
|
|
|
|
void fillEmailPayloadData(struct payload &payload, const Utilities::config &configuration, const std::string &recipientEmail, const std::string &recipientName, const std::string &emailSubject, const std::string &body) {
|
|
payload.payload_data.emplace_back(generateEmailSegmentDateTime());
|
|
payload.payload_data.emplace_back(generateEmailSegmentMessageId(configuration));
|
|
payload.payload_data.emplace_back(generateEmailSegmentRecipient(recipientName, recipientEmail));
|
|
payload.payload_data.emplace_back(generateEmailSegmentSender(configuration));
|
|
payload.payload_data.emplace_back(generateEmailSegmentSubject(emailSubject));
|
|
payload.payload_data.emplace_back(generateEmailSegmentContentType());
|
|
payload.payload_data.emplace_back(END_OF_LINE);
|
|
payload.payload_data.emplace_back(generateEmailSegmentBody(body));
|
|
payload.payload_data.emplace_back(END_OF_MAIL);
|
|
}
|
|
|
|
/*
|
|
* curl read callback function
|
|
*/
|
|
size_t payload_source(void *ptr, size_t size, size_t nmemb, void *userp)
|
|
{
|
|
auto* payload_context = (struct payload*)userp;
|
|
const char *data;
|
|
|
|
if((size == 0) || (nmemb == 0) || ((size*nmemb) < 1)) {
|
|
return 0;
|
|
}
|
|
|
|
data = payload_context->payload_data[payload_context->lines_read].c_str();
|
|
|
|
if(data) {
|
|
size_t len = strlen(data);
|
|
memcpy(ptr, data, len);
|
|
payload_context->lines_read++;
|
|
return len;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Generates the SMTP URL
|
|
* "smtp://{address}:{port}"
|
|
*/
|
|
std::string generateMailServerURL(const Utilities::config &configuration) {
|
|
return "smtp://" + configuration.emailServerAddress + ":" + std::to_string(configuration.emailServerPort);
|
|
}
|
|
|
|
/*
|
|
* Sends an HTML Email using curl
|
|
* returns CURL Error Codes https://curl.se/libcurl/c/libcurl-errors.html
|
|
* 1 = OK
|
|
*/
|
|
int sendEmail(const Utilities::config& configuration, const std::string& recipientEmail, const std::string& emailSubject, const std::string& htmlBody, const std::string& recipientDisplayName = "") {
|
|
struct payload payload;
|
|
std::string recipientName;
|
|
if (!recipientDisplayName.empty())
|
|
recipientName = recipientDisplayName;
|
|
else
|
|
recipientName = "";
|
|
fillEmailPayloadData(payload, configuration, recipientEmail, recipientName, emailSubject, EmailTemplateCollection::HTML_HEADER + htmlBody + EmailTemplateCollection::HTML_FOOTER);
|
|
CURL* curl;
|
|
CURLcode res = CURLE_OK;
|
|
struct curl_slist* recipients = nullptr;
|
|
curl = curl_easy_init();
|
|
if (curl) {
|
|
curl_easy_setopt(curl, CURLOPT_USERNAME, configuration.emailAddress.c_str());
|
|
curl_easy_setopt(curl, CURLOPT_PASSWORD, configuration.emailPassword.c_str());
|
|
curl_easy_setopt(curl, CURLOPT_URL, generateMailServerURL(configuration).c_str());
|
|
curl_easy_setopt(curl, CURLOPT_USE_SSL, (long)CURLUSESSL_ALL);
|
|
curl_easy_setopt(curl, CURLOPT_MAIL_FROM, configuration.emailAddress.c_str());
|
|
recipients = curl_slist_append(recipients, recipientEmail.c_str());
|
|
curl_easy_setopt(curl, CURLOPT_MAIL_RCPT, recipients);
|
|
curl_easy_setopt(curl, CURLOPT_READFUNCTION, payload_source);
|
|
curl_easy_setopt(curl, CURLOPT_READDATA, &payload);
|
|
curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
|
|
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
|
|
res = curl_easy_perform(curl);
|
|
|
|
if (res != CURLE_OK)
|
|
fprintf(stderr, "curl_easy_perform() failed: %s\n",
|
|
curl_easy_strerror(res));
|
|
|
|
curl_slist_free_all(recipients);
|
|
|
|
curl_easy_cleanup(curl);
|
|
}
|
|
return (int)res;
|
|
}
|
|
|
|
/*
|
|
* Sends a Password Reset Email
|
|
* return 1 at success
|
|
*/
|
|
int sendPasswordResetEmail(const Utilities::config& configuration, const std::string& email, const std::string& passwordResetKey) {
|
|
std::string emailContent = EmailTemplateCollection::passwordResetEmail(configuration.domain, email, passwordResetKey);
|
|
return sendEmail(configuration, email, EmailTemplateCollection::PASSWORD_RESET_EMAIL_SUBJECT, emailContent);
|
|
}
|
|
}
|
|
#endif |