Skip to content

Instantly share code, notes, and snippets.

@puppis42
Created May 27, 2023 23:58
Show Gist options
  • Save puppis42/a122878000d0f76b4ab7d9fed434723f to your computer and use it in GitHub Desktop.
Save puppis42/a122878000d0f76b4ab7d9fed434723f to your computer and use it in GitHub Desktop.
#include <curl/curl.h>
#include <string>
#include <iostream>
#include <vector>
#include <fstream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
using namespace std;
#define MULTI_PERFORM_HANG_TIMEOUT 60 * 1000
static const char* payload_text[] = {
"Subject: SMTPS Example\r\n",
"Date: Mon, 29 Nov 2010 21:54:29 +1100\r\n",
"User-Agent: My eMail Client\r\n",
"MIME-Version: 1.0\r\n",
"Content-Type: multipart/mixed;\r\n",
" boundary=\"------------030203080101020302070708\"\r\n",
"\r\nThis is a multi-part message in MIME format.\r\n",
"--------------030203080101020302070708\r\n",
"Content-Type: text/html; charset=utf-8; format=flowed\r\n", /* text/plain : NO HTML | text/html : HTML */
"Content-Transfer-Encoding: 7bit\r\n",
"\r\n", /* empty line to divide headers from body, see RFC5322 */
"<h1>The body of the message starts here.</h1>\r\n",
"<img height='300' weight='500' src='<url>'/>\r\n",
"\r\n",
"It could be a lot of lines, could be MIME encoded, whatever.\r\n",
"Check RFC5322.\r\n\r\n",
"--------------030203080101020302070708\r\n",
"Content-Type: image/png; charset=utf-8; format=flowed\r\n", //First edit image/gif
"Content-Transfer-Encoding: base64\r\n",
"Content-Disposition: attachment;\r\n",
" filename=\"abc.png\"\r\n",
"\r\n",
//Base64 Data
"[FILE-DATA-TO-BASE64]\r\n",
"--------------030203080101020302070708--\r\n",
NULL
};
//FILE TO BASE64
const char* base64_chars{
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/" };
string base64_encode(unsigned const char* input, unsigned const int len) {
string ret;
size_t i = 0;
unsigned char bytes[3];
unsigned char sextets[4];
while (i <= (len - 3)) {
bytes[0] = *(input++);
bytes[1] = *(input++);
bytes[2] = *(input++);
sextets[0] = (bytes[0] & 0xfc) >> 2; // Cuts last two bits off of first byte
sextets[1] = ((bytes[0] & 0x03) << 4) + ((bytes[1] & 0xf0) >> 4); // Takes last two bits from first byte and adds it to first 4 bits of 2nd byte
sextets[2] = ((bytes[1] & 0x0f) << 2) + ((bytes[2] & 0xc0) >> 6); // Takes last 4 bits of 2nd byte and adds it to first 2 bits of third byte
sextets[3] = bytes[2] & 0x3f; // takes last 6 bits of third byte
for (size_t j = 0; j < 4; ++j) {
ret += base64_chars[sextets[j]];
}
i += 3; // increases to go to third byte
}
if (i != len) {
size_t k = 0;
size_t j = len - i; // Find index of last byte
while (k < j) { // Sets first bytes
bytes[k] = *(input++);
++k;
}
while (j < 3) { // Set last bytes to 0x00
bytes[j] = '\0';
++j;
}
sextets[0] = (bytes[0] & 0xfc) >> 2; // Cuts last two bits off of first byte
sextets[1] = ((bytes[0] & 0x03) << 4) + ((bytes[1] & 0xf0) >> 4); // Takes last two bits from first byte and adds it to first 4 bits of 2nd byte
sextets[2] = ((bytes[1] & 0x0f) << 2) + ((bytes[2] & 0xc0) >> 6); // Takes last 4 bits of 2nd byte and adds it to first 2 bits of third byte
// No last one is needed, because if there were 4, then (i == len) == true
for (j = 0; j < (len - i) + 1; ++j) { // Gets sextets that include data
ret += base64_chars[sextets[j]]; // Appends them to string
}
while ((j++) < 4) // Appends remaining ='s
ret += '=';
}
return ret;
}
string base64_encode_image(const string& path) {
vector<char> temp;
ifstream infile;
infile.open(path, ios::binary); // Open file in binary mode
if (infile.is_open()) {
while (!infile.eof()) {
char c = (char)infile.get();
temp.push_back(c);
}
infile.close();
}
else return "File could not be opened";
string ret(temp.begin(), temp.end() - 1);
ret = base64_encode((unsigned const char*)ret.c_str(), ret.size());
return ret;
}
//SEND MAIL FUNCTION(S)
struct WriteThis {
int counter;
};
static size_t read_callback(void* ptr, size_t size, size_t nmemb, void* userp)
{
struct WriteThis* pooh = (struct WriteThis*)userp;
const char* data;
if (size * nmemb < 1)
return 0;
data = payload_text[pooh->counter];
if (data) {
size_t len = strlen(data);
memcpy(ptr, data, len);
pooh->counter++; /* advance pointer */
return len;
}
return 0; /* no more data left to deliver */
}
static struct timeval tvnow(void)
{
/*
** time() returns the value of time in seconds since the Epoch.
*/
struct timeval now;
now.tv_sec = (long)time(NULL);
now.tv_usec = 0;
return now;
}
static long tvdiff(struct timeval newer, struct timeval older)
{
return (newer.tv_sec - older.tv_sec) * 1000 +
(newer.tv_usec - older.tv_usec) / 1000;
}
int sendMail(string userName, string password, const char* recipient, string smtpServer, string smtpPort) {
CURL* curl;
CURLM* mcurl;
int still_running = 1;
struct timeval mp_start;
struct WriteThis pooh;
struct curl_slist* rcpt_list = NULL;
pooh.counter = 0;
curl_global_init(CURL_GLOBAL_DEFAULT);
curl = curl_easy_init();
if (!curl)
return 1;
mcurl = curl_multi_init();
if (!mcurl)
return 2;
rcpt_list = curl_slist_append(rcpt_list, recipient);
/* more addresses can be added here
rcpt_list = curl_slist_append(rcpt_list, "<others@example.com>");
*/
curl_easy_setopt(curl, CURLOPT_URL, ("smtp://" + smtpServer + ":" + smtpPort).c_str());
curl_easy_setopt(curl, CURLOPT_USERNAME, userName.c_str());
curl_easy_setopt(curl, CURLOPT_PASSWORD, password.c_str());
curl_easy_setopt(curl, CURLOPT_READFUNCTION, read_callback);
curl_easy_setopt(curl, CURLOPT_MAIL_FROM, userName.c_str());
curl_easy_setopt(curl, CURLOPT_MAIL_RCPT, rcpt_list);
curl_easy_setopt(curl, CURLOPT_USE_SSL, (long)CURLUSESSL_ALL);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
curl_easy_setopt(curl, CURLOPT_READDATA, &pooh);
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
curl_easy_setopt(curl, CURLOPT_SSLVERSION, 0L);
curl_easy_setopt(curl, CURLOPT_SSL_SESSIONID_CACHE, 0L);
curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
curl_multi_add_handle(mcurl, curl);
mp_start = tvnow();
/* we start some action by calling perform right away */
curl_multi_perform(mcurl, &still_running);
while (still_running) {
struct timeval timeout;
int rc; /* select() return code */
fd_set fdread;
fd_set fdwrite;
fd_set fdexcep;
int maxfd = -1;
long curl_timeo = -1;
FD_ZERO(&fdread);
FD_ZERO(&fdwrite);
FD_ZERO(&fdexcep);
/* set a suitable timeout to play around with */
timeout.tv_sec = 1;
timeout.tv_usec = 0;
curl_multi_timeout(mcurl, &curl_timeo);
if (curl_timeo >= 0) {
timeout.tv_sec = curl_timeo / 1000;
if (timeout.tv_sec > 1)
timeout.tv_sec = 1;
else
timeout.tv_usec = (curl_timeo % 1000) * 1000;
}
/* get file descriptors from the transfers */
curl_multi_fdset(mcurl, &fdread, &fdwrite, &fdexcep, &maxfd);
/* In a real-world program you OF COURSE check the return code of the
function calls. On success, the value of maxfd is guaranteed to be
greater or equal than -1. We call select(maxfd + 1, ...), specially in
case of (maxfd == -1), we call select(0, ...), which is basically equal
to sleep. */
rc = select(maxfd + 1, &fdread, &fdwrite, &fdexcep, &timeout);
if (tvdiff(tvnow(), mp_start) > MULTI_PERFORM_HANG_TIMEOUT) {
fprintf(stderr, "ABORTING TEST, since it seems "
"that it would have run forever.\n");
break;
return 101;
}
switch (rc) {
case -1:
/* select error */
break;
return 200;
case 0: /* timeout */
default: /* action */
curl_multi_perform(mcurl, &still_running);
break;
}
}
curl_slist_free_all(rcpt_list);
curl_multi_remove_handle(mcurl, curl);
curl_multi_cleanup(mcurl);
curl_easy_cleanup(curl);
curl_global_cleanup();
}
int main(void)
{
string s = base64_encode_image("tst.png");
payload_text[23] = s.c_str(), "\r\n";
int res = sendMail("<mail-addr>", "<password>",
"<mail-addr>", "smtp.gmail.com", "587");
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment