/* * Copyright (c) 2023 Fancy Code. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "smtp_servers.h" #include "ILog.h" #include "curl_serve.h" #include #include #include #include struct upload_status { char *payload_text; size_t bytes_read; }; // static size_t payload_source(char *ptr, size_t size, size_t nmemb, void *userp) // { // struct upload_status *upload_ctx = (struct upload_status *)userp; // const char *data; // size_t room = size * nmemb; // if ((size == 0) || (nmemb == 0) || ((size * nmemb) < 1)) // { // return 0; // } // data = &upload_ctx->payload_text[upload_ctx->bytes_read]; // if (data) // { // size_t len = strlen(data); // if (room < len) // len = room; // memcpy(ptr, data, len); // upload_ctx->bytes_read += len; // return len; // } // return 0; // } // static const char inline_html[] = // "\r\n" // "

This is the inline HTML message of the email.

" // "
\r\n" // "

It could be a lot of HTML data that would be displayed by " // "email viewers able to handle HTML.

" // "\r\n"; #define SEND_EMAIL_HEADERS_ARRAY_LENGTH 5 #define SEND_EMAIL_HEADERS_TEXT_LENGTH 1024 #define DATE "Date: " #define TO "To: " #define FROM "From: " #define SUBJECT "Subject: " #define HEADER_END "\r\n" enum HEADERS_NAME { HEADERS_NAME_DATE = 0, HEADERS_NAME_TO, HEADERS_NAME_FROM, HEADERS_NAME_SUBJECT, HEADERS_NAME_END }; static void headers_make(char **headers, ServerSmtp *param) { int textLength = strlen(DATE) + strlen(param->date) + 1; char *text = (char *)malloc(textLength); if (text) { memset(text, 0, textLength); snprintf(text, textLength, "%s%s", DATE, param->date); headers[HEADERS_NAME_DATE] = text; LogInfo("HEADERS_NAME_DATE:%s\n", text); } textLength = strlen(TO) + strlen(param->to) + 1; text = (char *)malloc(textLength); if (text) { memset(text, 0, textLength); snprintf(text, textLength, "%s%s", TO, param->to); headers[HEADERS_NAME_TO] = text; LogInfo("HEADERS_NAME_TO:%s\n", text); } textLength = strlen(FROM) + strlen(param->from) + strlen(param->userName) + strlen("()") + 1; text = (char *)malloc(textLength); if (text) { memset(text, 0, textLength); snprintf(text, textLength, "%s%s(%s)", FROM, param->from, param->userName); headers[HEADERS_NAME_FROM] = text; LogInfo("HEADERS_NAME_FROM:%s\n", text); } textLength = strlen(SUBJECT) + strlen(param->subject) + 1; text = (char *)malloc(textLength); if (text) { memset(text, 0, textLength); snprintf(text, textLength, "%s%s", SUBJECT, param->subject); headers[HEADERS_NAME_SUBJECT] = text; LogInfo("HEADERS_NAME_SUBJECT:%s\n", text); } } static void headers_free(char **headers) { char **cpp = headers; for (int i = 0; i < SEND_EMAIL_HEADERS_ARRAY_LENGTH; i++) { if (*cpp) { free(*cpp); headers[i] = NULL; } cpp++; } } void SmtpServersSendEmail(ServerSmtp *param) { if (!param) { LogError("null pointer.\n"); return; } char *headers_text[SEND_EMAIL_HEADERS_ARRAY_LENGTH] = {NULL}; headers_make(headers_text, param); CURL *curl; CURLcode res = CURLE_OK; curl = CurlEasyMake(); if (curl) { struct curl_slist *headers = NULL; struct curl_slist *recipients = NULL; struct curl_slist *slist = NULL; curl_mime *mime; curl_mime *alt; curl_mimepart *part; const char **cpp; const char **attachment; /* This is the URL for your mailserver */ curl_easy_setopt(curl, CURLOPT_URL, param->url); /* Set the username and password */ curl_easy_setopt(curl, CURLOPT_USERNAME, param->userName); curl_easy_setopt(curl, CURLOPT_PASSWORD, param->password); /* Note that this option is not strictly required, omitting it will result * in libcurl sending the MAIL FROM command with empty sender data. All * autoresponses should have an empty reverse-path, and should be directed * to the address in the reverse-path which triggered them. Otherwise, * they could cause an endless loop. See RFC 5321 Section 4.5.5 for more * details. */ curl_easy_setopt(curl, CURLOPT_MAIL_FROM, param->from); /* Add two recipients, in this particular case they correspond to the * To: and Cc: addressees in the header, but they could be any kind of * recipient. */ recipients = curl_slist_append(recipients, param->to); // recipients = curl_slist_append(recipients, CC); curl_easy_setopt(curl, CURLOPT_MAIL_RCPT, recipients); /* Build and set the message header list. */ for (cpp = (const char **)headers_text; *cpp; cpp++) { headers = curl_slist_append(headers, *cpp); } curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); /* Build the mime message. */ mime = curl_mime_init(curl); /* The inline part is an alternative proposing the html and the text versions of the email. */ alt = curl_mime_init(curl); /* HTML message. */ // part = curl_mime_addpart(alt); // curl_mime_data(part, inline_html, CURL_ZERO_TERMINATED); // curl_mime_type(part, "text/html"); /* Text message. */ if (param->inlineText) { part = curl_mime_addpart(alt); curl_mime_data(part, param->inlineText, CURL_ZERO_TERMINATED); curl_mime_type(part, "text/plain"); } /* Create the inline part. */ part = curl_mime_addpart(mime); curl_mime_subparts(part, alt); curl_mime_type(part, "multipart/alternative"); slist = curl_slist_append(NULL, "Content-Disposition: inline"); curl_mime_headers(part, slist, 1); /* Add the attachment. */ if (param->attachment) { for (attachment = (const char **)param->attachment; *attachment; attachment++) { part = curl_mime_addpart(mime); curl_mime_filedata(part, *attachment); } curl_easy_setopt(curl, CURLOPT_MIMEPOST, mime); } /* Send the message */ res = curl_easy_perform(curl); param->code = res; /* Check for errors */ if (res != CURLE_OK) fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res)); /* Free lists. */ curl_slist_free_all(recipients); if (headers) curl_slist_free_all(headers); /* curl will not send the QUIT command until you call cleanup, so you * should be able to re-use this connection for additional messages * (setting CURLOPT_MAIL_FROM and CURLOPT_MAIL_RCPT as required, and * calling curl_easy_perform() again. It may not be a good idea to keep * the connection open for a very long time though (more than a few * minutes may result in the server timing out the connection), and you do * want to clean up in the end. */ curl_easy_cleanup(curl); /* Free multipart message. */ curl_mime_free(mime); headers_free(headers_text); } } void SmtpServersSendEmailOnlyText(ServerSmtp *param) { #if 0 // only for test. if (!param) { LogError("null pointer.\n"); return; } int payloadTextLength = strlen(DATE HEADER_END) + strlen(TO HEADER_END) + strlen(FROM HEADER_END) + strlen(SUBJECT HEADER_END) + strlen(param->date) + strlen(param->to) + strlen(param->from) + strlen(param->subject); if (param->inlineText) { payloadTextLength += strlen(param->inlineText); } char *payload_text = (char *)malloc(payloadTextLength); if (!payload_text) { LogError("null pointer.\n"); return; } snprintf(payload_text, payloadTextLength, "%s%s%s" "%s%s%s" "%s%s%s" "%s%s%s" "%s", DATE, param->date, HEADER_END, TO, param->to, HEADER_END, FROM, param->from, HEADER_END, SUBJECT, param->subject, HEADER_END HEADER_END, param->inlineText); // LogInfo("payload_text =\n%s\n", payload_text); CURL *curl; CURLcode res = CURLE_OK; struct curl_slist *recipients = NULL; struct upload_status upload_ctx = {0}; upload_ctx.payload_text = payload_text; curl = CurlEasyMake(); if (curl) { curl_mime *mime; curl_mime *alt; curl_mimepart *part; struct curl_slist *slist = NULL; /* This is the URL for your mailserver */ curl_easy_setopt(curl, CURLOPT_URL, param->url); /* Set the username and password */ curl_easy_setopt(curl, CURLOPT_USERNAME, param->userName); curl_easy_setopt(curl, CURLOPT_PASSWORD, param->password); /* Note that this option is not strictly required, omitting it will result * in libcurl sending the MAIL FROM command with empty sender data. All * autoresponses should have an empty reverse-path, and should be directed * to the address in the reverse-path which triggered them. Otherwise, * they could cause an endless loop. See RFC 5321 Section 4.5.5 for more * details. */ curl_easy_setopt(curl, CURLOPT_MAIL_FROM, param->from); /* Add two recipients, in this particular case they correspond to the * To: and Cc: addressees in the header, but they could be any kind of * recipient. */ recipients = curl_slist_append(recipients, param->to); // recipients = curl_slist_append(recipients, CC_ADDR); curl_easy_setopt(curl, CURLOPT_MAIL_RCPT, recipients); /* We are using a callback function to specify the payload (the headers and * body of the message). You could just use the CURLOPT_READDATA option to * specify a FILE pointer to read from. */ curl_easy_setopt(curl, CURLOPT_READFUNCTION, payload_source); curl_easy_setopt(curl, CURLOPT_READDATA, &upload_ctx); curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L); // /* Build the mime message. */ // mime = curl_mime_init(curl); // /* Create the inline part. */ // part = curl_mime_addpart(mime); // curl_mime_subparts(part, alt); // curl_mime_type(part, "multipart/alternative"); // slist = curl_slist_append(NULL, "Content-Disposition: inline"); // curl_mime_headers(part, slist, 1); // curl_mime_filedata(part, "./Makefile"); // curl_easy_setopt(curl, CURLOPT_MIMEPOST, mime); /* Send the message */ res = curl_easy_perform(curl); param->code = res; /* Check for errors */ if (res != CURLE_OK) fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res)); /* Free the list of recipients */ curl_slist_free_all(recipients); /* curl will not send the QUIT command until you call cleanup, so you * should be able to re-use this connection for additional messages * (setting CURLOPT_MAIL_FROM and CURLOPT_MAIL_RCPT as required, and * calling curl_easy_perform() again. It may not be a good idea to keep * the connection open for a very long time though (more than a few * minutes may result in the server timing out the connection), and you do * want to clean up in the end. */ curl_easy_cleanup(curl); /* Free multipart message. */ curl_mime_free(mime); free(payload_text); } #endif }