RFC 28822 compliance changes.

Move Mail Functionality into smtpManager.cpp
This commit is contained in:
Tina_Azure 2023-05-29 15:12:40 +02:00
parent 8102ea41dc
commit 3237b2af38
4 changed files with 40 additions and 59 deletions

View File

@ -4,7 +4,9 @@
#include <string>
namespace EmailTemplateCollection {
const std::string PASSWORD_RESET_EMAIL_SUBJECT = "Password Reset";
static const std::string PASSWORD_RESET_EMAIL_SUBJECT = "Password Reset";
static const std::string HTML_HEADER = "<!DOCTYPE html><html><body>";
static const std::string HTML_FOOTER = "</body></html>";
std::string passwordResetEmail(const std::string& domain, const std::string& email, const std::string& passwordResetKey) {

View File

@ -11,6 +11,7 @@
#include "utilities.cpp"
#include "regularTaskExecution.cpp"
#include "templateConstCollection.cpp"
#include "smtpManager.cpp"
using namespace std;
using namespace TemplateConstCollection;
@ -26,6 +27,10 @@ int main(int argc, char *argv[]) {
return 1;
}
//simple email test, header validation via https://mailheader.org/
//SMTPManager::sendEmail(configuration, "mail@mail.de", "testsubject", "<h2> bluetest<h2><br>blue<br>yellow");
//return 0;
// Create app with Middleware
crow::App<crow::CookieParser> app;
@ -239,7 +244,7 @@ int main(int argc, char *argv[]) {
templateHTML = TEMPLATE_CUSTOMER_FREELANCER_TEMPLATE_REQUEST_FULFILMENT;
pqxx::result resultEmailAddress = Database::executePreparedStatement_SELECT_FREELANCER_EMAIL(databaseConnection, newRequest.freelancerID);
if (!resultEmailAddress.empty())
Utilities::sendEmail(configuration, resultEmailAddress.at(0).at(0).c_str(), "NEW REQUEST", newRequest.toJSONString());
SMTPManager::sendEmail(configuration, resultEmailAddress.at(0).at(0).c_str(), "NEW REQUEST", newRequest.toJSONString());
}
else {
ctx[MUSTACHE_ERROR_UNABLE_TO_CREATE_REQUEST] = true;
@ -355,7 +360,7 @@ int main(int argc, char *argv[]) {
Database::executePreparedStatement_INSERT_FREELANCER_RESET_KEY(databaseConnection, email, passwordResetKey);
if (Utilities::sendPasswordResetEmail(configuration, email, passwordResetKey) == 0)
if (SMTPManager::sendPasswordResetEmail(configuration, email, passwordResetKey) == 1)
ctx["passwordresetmailsent"] = true;
}
ctx["resetemail"] = email;

View File

@ -7,6 +7,7 @@
#include <ctime>
#include <curl/curl.h>
#include "emailTemplateCollection.cpp"
#include "utilities.cpp"
@ -22,16 +23,24 @@ namespace SMTPManager {
/*
* Generates the Date Time Header Segment
* Date: {day}/{month}/{year} {hour}:{minute}:{second} {delta to UTC} \r\n
* 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, "%d/%m/%Y %H:%M:%S %z") << END_OF_LINE;
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
@ -74,6 +83,7 @@ namespace SMTPManager {
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));
@ -115,19 +125,21 @@ namespace SMTPManager {
}
/*
* 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) {
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;
fillEmailPayloadData(payload, configuration, recipientEmail, "tmpString", emailSubject, htmlBody);//todo:change
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;
recipients = curl_slist_append(nullptr, "root@localhost");
struct curl_slist* recipients = nullptr;
curl = curl_easy_init();
if (curl) {
curl_easy_setopt(curl, CURLOPT_USERNAME, configuration.emailAddress.c_str());
@ -153,5 +165,14 @@ namespace SMTPManager {
}
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

View File

@ -15,13 +15,10 @@
#include <pqxx/pqxx>
#include "crow.h"
#include "crow/middlewares/cookie_parser.h"
#include "cpp/opportunisticsecuresmtpclient.hpp"
#include "cpp/htmlmessage.hpp"
#include "emailTemplateCollection.cpp"
#include "templateConstCollection.cpp"
using namespace jed_utils::cpp;
using namespace DatabaseStatementConstCollection;
using namespace TemplateConstCollection;
@ -316,50 +313,6 @@ namespace Utilities {
}
}
/*
* Sends an HTML Email using CPP-SMTPClient-library
* return 0 at success, 1 at client fail, 2 at critical fail.
*/
int sendEmail(const config& configuration, const std::string& destinationEmail, std::string emailSubject, std::string htmlBody){
OpportunisticSecureSMTPClient client(configuration.emailServerAddress, configuration.emailServerPort);
client.setCredentials(Credential(configuration.emailAddress, configuration.emailPassword));
try {
const MessageAddress from(configuration.emailAddress, configuration.emailAddressDisplay);
const auto to = { MessageAddress(destinationEmail) };
const auto subject = std::move(emailSubject);
const auto body = std::move(htmlBody);
const std::vector<MessageAddress> cc = {};
const std::vector<MessageAddress> bcc = {};
const std::vector<Attachment> attachments = {};
HTMLMessage msg(from, to, subject, body, cc, bcc, attachments);
int err_no = client.sendMail(msg);
if (err_no != 0) {
std::cerr << client.getCommunicationLog() << '\n';
std::string errorMessage = OpportunisticSecureSMTPClient::getErrorMessage(err_no);
std::cerr << "An error occurred: " << errorMessage
<< " (error no: " << err_no << ")" << '\n';
return 1;
}
std::cout << client.getCommunicationLog() << '\n';
std::cout << "Operation completed!" << std::endl;
}
catch (std::invalid_argument &err) {
std::cerr << err.what() << std::endl;
return 2;
}
return 0;
}
/*
* Sends a Password Reset Email
* return 0 at success, 1 at client fail, 2 at critical fail.
*/
int sendPasswordResetEmail(const 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);
}
std::string createHashSha512(const std::string& str){
unsigned char hash[SHA512_DIGEST_LENGTH];