hunting/utils/Servers/src/smtp_servers.c
2024-02-28 03:49:11 -08:00

345 lines
13 KiB
C

/*
* 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 <curl/curl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
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[] =
// "<html><body>\r\n"
// "<p>This is the inline <b>HTML</b> message of the email.</p>"
// "<br />\r\n"
// "<p>It could be a lot of HTML data that would be displayed by "
// "email viewers able to handle HTML.</p>"
// "</body></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
}