42 void addMissingHeaders(shared_ptr<mail::Email>& email, shared_ptr<mail::EmailAddress>
const& from) {
43 if (!email->headers().count(
"Date")) {
46 if (!email->headers().count(
"From") && !from->address().empty()) {
47 email->headers()[
"From"] = from->get();
50 if (!email->headers().count(
"Message-ID") && !from->address().empty() && (atPos = from->address().find_last_of(
'@')) != string::npos) {
55 clock_gettime(CLOCK_REALTIME, &mtime);
56 base << mtime.tv_sec << mtime.tv_nsec << from->address() << rd();
57 mid <<
'<' <<
crypto::md5(base.str(),
true) <<
'@' << from->address().substr(atPos + 1) <<
'>';
58 email->headers()[
"Message-ID"] = mid.str();
66 std::shared_ptr<mail::Email const> email;
67 std::shared_ptr<mail::EmailAddress const> from;
68 std::vector<mail::EmailAddress> recipients;
69 std::shared_ptr<mail::ReplacementRules> replacementRules;
73struct mail::SmtpMailer::Data {
74 std::string serverDomain;
75 unsigned int serverPort;
76 TlsMode serverTlsMode;
77 bool verifyServerTlsCert;
78 std::string authUsername;
79 std::string authPassword;
80 long connectionTimeout;
81 std::vector<QueueElem> queue;
83 Data(
string serverDomain,
unsigned int serverPort, TlsMode serverTlsMode,
bool verifyServerTlsCert,
84 string authUsername,
string authPassword,
long connectionTimeout) : serverDomain(std::move(serverDomain)),
85 serverPort(serverPort),
86 serverTlsMode(serverTlsMode),
87 verifyServerTlsCert(verifyServerTlsCert),
88 authUsername(std::move(authUsername)),
89 authPassword(std::move(authPassword)),
90 connectionTimeout(connectionTimeout) {}
96 bool verifyServerTlsCert,
string authUsername,
string authPassword,
97 long connectionTimeout) {
98 data = make_unique<Data>(std::move(serverDomain), serverPort, serverTlsMode, verifyServerTlsCert, std::move(authUsername),
99 std::move(authPassword), connectionTimeout);
102void mail::SmtpMailer::setServer(std::string domain,
unsigned int port,
SmtpMailer::TlsMode tlsMode,
bool verifyTlsCert) {
103 data->serverDomain = std::move(domain);
104 data->serverPort = port;
105 data->serverTlsMode = tlsMode;
106 data->verifyServerTlsCert = verifyTlsCert;
109void mail::SmtpMailer::setAuth(std::string username, std::string password) {
110 data->authUsername = std::move(username);
111 data->authPassword = std::move(password);
114void mail::SmtpMailer::setConnectionTimeout(
long timeout) {
115 data->connectionTimeout = timeout;
118void mail::SmtpMailer::enqueue(std::shared_ptr<Email> email,
EmailAddress to, std::shared_ptr<EmailAddress> from,
119 std::shared_ptr<ReplacementRules> replacementRules) {
120 bulkEnqueue(std::move(email), vector<EmailAddress>({std::move(to)}), std::move(from),
121 std::move(replacementRules));
124void mail::SmtpMailer::bulkEnqueue(std::shared_ptr<Email> email, std::vector<EmailAddress> recipients,
125 std::shared_ptr<EmailAddress> from, std::shared_ptr<ReplacementRules> replacementRules) {
126 addMissingHeaders(email, from);
127 data->queue.push_back(QueueElem{.email = std::move(email), .from = std::move(from), .recipients = std::move(recipients), .replacementRules = std::move(replacementRules)});
130void mail::SmtpMailer::clearQueue() {
134void mail::SmtpMailer::processQueue()
const {
138 curl = curl_easy_init();
142 if (!data->authUsername.empty()) {
143 curl_easy_setopt(curl, CURLOPT_USERNAME, data->authUsername.c_str());
144 if (!data->authPassword.empty())
145 curl_easy_setopt(curl, CURLOPT_PASSWORD, data->authPassword.c_str());
150 stringstream curlUrl;
151 if (data->serverTlsMode == TlsMode::SMTPS) {
152 curlUrl <<
"smtps://";
154 curlUrl <<
"smtp://";
156 curlUrl << data->serverDomain <<
":" << data->serverPort;
157 curl_easy_setopt(curl, CURLOPT_URL, curlUrl.str().c_str());
161 if (data->serverTlsMode == TlsMode::REQUIRE_STARTTLS) {
162 curl_easy_setopt(curl, CURLOPT_USE_SSL, (
long) CURLUSESSL_ALL);
163 }
else if (data->serverTlsMode == TlsMode::TRY_STARTTLS) {
164 curl_easy_setopt(curl, CURLOPT_USE_SSL, (
long) CURLUSESSL_TRY);
166 if (data->serverTlsMode != TlsMode::NONE && !data->verifyServerTlsCert) {
167 curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
168 curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
172 FILE* devNull = fopen(
"/dev/null",
"wb");
173 curl_easy_setopt(curl, CURLOPT_STDERR, devNull);
176 curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT_MS, data->connectionTimeout);
179 for (
const auto& mail : data->queue) {
182 curl_easy_setopt(curl, CURLOPT_MAIL_FROM, mail.from->get(
false).c_str());
185 curl_slist* recipients =
nullptr;
186 for (
const auto& to : mail.recipients) {
187 recipients = curl_slist_append(recipients, to.get(
false).c_str());
189 curl_easy_setopt(curl, CURLOPT_MAIL_RCPT, recipients);
192 string payload = mail.email->getRaw(mail.replacementRules);
194 FILE* payloadFile = fmemopen((
void*) payload.c_str(), payload.length(),
"r");
195 curl_easy_setopt(curl, CURLOPT_READDATA, (
void*) payloadFile);
196 curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
199 res = curl_easy_perform(curl);
202 curl_slist_free_all(recipients);
206 if (res != CURLE_OK) {
207 curl_easy_cleanup(curl);
209 string(
"CURL error: ") + curl_easy_strerror(res));
213 curl_easy_cleanup(curl);
Structure representing an email address.
Exception class that can be used by apps to catch errors resulting from nawa function calls.
For establishing a connection to an SMTP server and sending emails.
A bunch of useful cryptographic functions (esp. hashing), acting as a wrapper to C crypto libraries.
#define NAWA_DEFAULT_DESTRUCTOR_IMPL_WITH_NS(Namespace, Class)
std::string md5(std::string const &input, bool hex=true)
std::string makeSmtpTime(time_t time)
Contains useful functions that improve the readability and facilitate maintenance of the NAWA code.