345 lines
13 KiB
C
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
|
|
} |