From d9eec48d7c6d5d1cc44a8b7222de847ac87e4c62 Mon Sep 17 00:00:00 2001 From: Tina_Azure <-> Date: Thu, 25 May 2023 19:51:13 +0200 Subject: [PATCH] WIP:base implementation of replacement mail manager based on libcurl --- src/smtpManager.cpp | 157 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 157 insertions(+) create mode 100644 src/smtpManager.cpp diff --git a/src/smtpManager.cpp b/src/smtpManager.cpp new file mode 100644 index 0000000..a70eab9 --- /dev/null +++ b/src/smtpManager.cpp @@ -0,0 +1,157 @@ +#ifndef SMTP_MANAGER_CPP +#define SMTP_MANAGER_CPP + +#include +#include +#include +#include +#include + +#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 payload_data; + }; + + /* + * Generates the Date Time Header Segment + * Date: {day}/{month}/{year} {hour}:{minute}:{second} {delta to UTC} \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, "%d/%m/%Y %H:%M:%S %z") << END_OF_LINE; + return oss.str(); + } + + /* + * 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(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); + } + + /* + * todo:for some inexplicable reason the sent email contains an empty CC: field which needs some examination + * todo:validate header https://mailheader.org/show.cgi + * Sends an HTML Email using curl + * returns CURL Error Codes https://curl.se/libcurl/c/libcurl-errors.html + * 1 = OK + */ + int sendEmailNew(const Utilities::config &configuration, const std::string &recipientEmail, const std::string &emailSubject, const std::string &htmlBody) { + struct payload payload; + fillEmailPayloadData(payload, configuration, recipientEmail, "tmpString", emailSubject, htmlBody);//todo:change + CURL* curl; + CURLcode res = CURLE_OK; + struct curl_slist* recipients; + recipients = curl_slist_append(nullptr, "root@localhost"); + 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; + } +} +#endif \ No newline at end of file