How to add dynamic mail body using curl in c++ - c++

I am using following code to send a mail but here the
body of mail is statically defined but i want to send some variable
values like username and password so please give me the solution how
to add variable in my mail body.
#define FROM "<sender#example.org>"
#define TO "<addressee#example.net>"
#define CC "<info#example.org>"
static const char *payload_text[] = {
"Date: Mon, 29 Nov 2010 21:54:29 +1100\r\n",
"To: " TO "\r\n",
"From: " FROM "(Example User)\r\n",
"Cc: " CC "(Another example User)\r\n",
"Message-ID: <dcd7cb36-11db-487a-9f3a-e652a9458efd#rfcpedant.example.org>\r\n",
"Subject: SMTP TLS example message\r\n",
"\r\n", /* empty line to divide headers from body, see RFC5322 */
"The body of the message starts here.\r\n",
"\r\n",
"It could be a lot of lines, could be MIME encoded, whatever.\r\n",
"Check RFC5322.\r\n",
NULL
};
struct upload_status {
int lines_read;
};
static size_t payload_source(void *ptr, size_t size, size_t nmemb, void *userp)
{
struct upload_status *upload_ctx = (struct upload_status *)userp;
const char *data;
if((size == 0) || (nmemb == 0) || ((size*nmemb) < 1)) {
return 0;
}
data = payload_text[upload_ctx->lines_read];
if(data) {
size_t len = strlen(data);
memcpy(ptr, data, len);
upload_ctx->lines_read++;
return len;
}
return 0;
}
int main(void)
{
CURL *curl;
CURLcode res = CURLE_OK;
struct curl_slist *recipients = NULL;
struct upload_status upload_ctx;
upload_ctx.lines_read = 0;
curl = curl_easy_init();
if(curl) {
/* Set username and password */
curl_easy_setopt(curl, CURLOPT_USERNAME, "user");
curl_easy_setopt(curl, CURLOPT_PASSWORD, "secret");
curl_easy_setopt(curl, CURLOPT_URL, "smtp://mainserver.example.net:587");
curl_easy_setopt(curl, CURLOPT_USE_SSL, (long)CURLUSESSL_ALL);
curl_easy_setopt(curl, CURLOPT_CAINFO, "/path/to/certificate.pem");
curl_easy_setopt(curl, CURLOPT_MAIL_FROM, FROM);
recipients = curl_slist_append(recipients, TO);
recipients = curl_slist_append(recipients, CC);
curl_easy_setopt(curl, CURLOPT_MAIL_RCPT, recipients);
curl_easy_setopt(curl, CURLOPT_READFUNCTION, payload_source);
curl_easy_setopt(curl, CURLOPT_READDATA, &upload_ctx);
curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
/* Send the message */
res = curl_easy_perform(curl);
/* 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);
/* Always cleanup */
curl_easy_cleanup(curl);
}
return (int)res;
}

Related

Sending an email using curl c++

Im trying to send an email using curl c++, i managed to log in well and when i run the program it works fine, does not throw any error, but the email never comes.
This is my code:
#include <iostream>
#include <curl/curl.h>
static const char *payload_text =
"Date: Mon, 29 Nov 2010 21:54:29 +1100\r\n"
"To: " "mailto" "\r\n"
"From: " "mymail" "\r\n"
"Message-ID: <dcd7cb36-11db-487a-9f3a-e652a9458efd#"
"rfcpedant.example.org>\r\n"
"Subject: SMTP example message\r\n"
"\r\n" /* empty line to divide headers from body, see RFC5322 */
"The body of the message starts here.\r\n"
"\r\n"
"It could be a lot of lines, could be MIME encoded, whatever.\r\n"
"Check RFC5322.\r\n";
size_t read_function(char *buffer, size_t size, size_t nmemb,char *data)
{
size_t len;
if(size == 0 or nmemb == 0)
{
return 0;
}
if(data)
{
len = strlen(data);
memcpy(buffer, data, len);
return len;
}
return 0;
}
int main()
{
CURL *curl;
CURLcode res = CURLE_OK;
const char *data = payload_text;
curl = curl_easy_init();
if(curl)
{
curl_easy_setopt(curl, CURLOPT_USERNAME, "mymail");
curl_easy_setopt(curl, CURLOPT_PASSWORD, "password");
curl_easy_setopt(curl, CURLOPT_URL, "smtp://smtp.gmail.com:587");
curl_easy_setopt(curl, CURLOPT_MAIL_FROM, "my mail");
curl_easy_setopt(curl, CURLOPT_MAIL_RCPT, "mailto");
curl_easy_setopt(curl, CURLOPT_READDATA,payload_text);
curl_easy_setopt(curl, CURLOPT_READFUNCTION, read_function);
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
curl_easy_setopt(curl, CURLOPT_USE_SSL, CURLUSESSL_ALL);
}
res = curl_easy_perform(curl);
if(res != CURLE_OK)
{
fprintf(stderr, "curl_easy_perform() failed: %s\n",
curl_easy_strerror(res));
curl_easy_cleanup(curl);
}
return 0;
}
I think the problem is in the curl options READDATA and READUNCTION.
In the documentation says that you have to pass as an argument to READDATA a data pointer.
const char *data = payload_text; is the data pointer, right?
then READFUNCTION takes as an argument a function which return the size of the data and i think that is what size_t read_function(char *buffer, size_t size, size_t nmemb,char *data) is doing.
I am new in this so any advice would be good for me.
I found this to be a helpful starting point:
https://curl.se/libcurl/c/smtp-mail.html
There are two main problems with your code:
Your read_function didn't keep track of how much of the payload has been read so it would keep giving the same content to libcurl over and over and never signal the end of the message.
You were setting CURLOPT_MAIL_RCPT to a string when in fact it should be a struct curl_slist * because there can be multiple recipients.
Here is a fixed example that I tested on my computer and it worked. Private data at the top of the file was modified before posting.
#define USERNAME "david"
#define PASSWORD "xxxxx"
#define MAILTO "david#example.com"
#define MAILFROM "you#example.com"
#define SMTP "smtp://your.smtp.server.example.com:25"
#include <stdio.h>
#include <curl/curl.h>
const char * payload_text =
"Date: Mon, 29 Nov 2010 21:54:29 +1100\r\n"
"To: " MAILTO "\r\n"
"From: " MAILFROM "\r\n"
"Subject: SMTP example message with libcurl 6\r\n"
"\r\n"
"Hello world!\r\n";
struct ReadData
{
explicit ReadData(const char * str)
{
source = str;
size = strlen(str);
}
const char * source;
size_t size;
};
size_t read_function(char * buffer, size_t size, size_t nitems, ReadData * data)
{
size_t len = size * nitems;
if (len > data->size) { len = data->size; }
memcpy(buffer, data->source, len);
data->source += len;
data->size -= len;
return len;
}
int main()
{
CURL * curl = curl_easy_init();
if (!curl)
{
fprintf(stderr, "curl_easy_init failed\n");
return 1;
}
curl_easy_setopt(curl, CURLOPT_USERNAME, USERNAME);
curl_easy_setopt(curl, CURLOPT_PASSWORD, PASSWORD);
curl_easy_setopt(curl, CURLOPT_URL, SMTP);
curl_easy_setopt(curl, CURLOPT_MAIL_FROM, MAILFROM);
struct curl_slist * rcpt = NULL;
rcpt = curl_slist_append(rcpt, MAILTO);
curl_easy_setopt(curl, CURLOPT_MAIL_RCPT, rcpt);
ReadData data(payload_text);
curl_easy_setopt(curl, CURLOPT_READDATA, &data);
curl_easy_setopt(curl, CURLOPT_READFUNCTION, read_function);
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);
curl_easy_setopt(curl, CURLOPT_UPLOAD, 1);
curl_easy_setopt(curl, CURLOPT_USE_SSL, CURLUSESSL_ALL);
// If your server doesn't have a proper SSL certificate:
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0);
CURLcode res = curl_easy_perform(curl);
if (res != CURLE_OK)
{
fprintf(stderr, "curl_easy_perform() failed: %s\n",
curl_easy_strerror(res));
curl_easy_cleanup(curl);
}
return 0;
}
read_function: you are changing the local pointer buffer that does not affect the byte buffer of a caller. You should copy data to the pointed buffer
memcpy(buffer, data, len);
FYI sizeof(char) is guaranteed to be 1, thus is unneeded.
Another issue - the function never returns 0 that signals all data is sent, it sends the same data again and again. You should return 0 on a second call. Or set the length to the option CURLOPT_POSTFIELDSIZE_LARGE.
See the example smtp-mail.c

how to send an email using libcurl

I'm able to send an email to my gmail successfully, but it's sending an empty message and not able to send contain message , anyway the code I'm using is as follows:
#include <stdio.h>
#include <string.h>
#include <curl/curl.h>
#define FROM "<xxxxxx#gmail.com>"
#define TO "<xxxxx#gmail.com>"
static const char* payload_text[] =
{
"To: " TO "\r\n",
"From: " FROM "\r\n",
"Subject: SMTP TLS example message\r\n",
"Contain: Helllo\r\n",
NULL
};
struct upload_status {
int lines_read;
};
static size_t payload_source(void* ptr, size_t size, size_t nmemb, void* userp)
{
struct upload_status* upload_ctx = (struct upload_status*)userp;
const char* data;
if ((size == 0) || (nmemb == 0) || ((size * nmemb) < 1)) {
return 0;
}
data = payload_text[upload_ctx->lines_read];
if (data) {
size_t len = strlen(data);
memcpy(ptr, data, len);
upload_ctx->lines_read++;
return len;
}
return 0;
}
int main(void)
{
CURL* curl;
CURLcode res = CURLE_OK;
struct curl_slist* recipients = NULL;
struct upload_status upload_ctx;
upload_ctx.lines_read = 0;
curl = curl_easy_init();
if (curl) {
curl_easy_setopt(curl, CURLOPT_USERNAME, "xxxxxx#gmail.com");
curl_easy_setopt(curl, CURLOPT_PASSWORD, "xxxxxxxx");
curl_easy_setopt(curl, CURLOPT_URL, "smtp://smtp.gmail.com:587");
curl_easy_setopt(curl, CURLOPT_USE_SSL, (long)CURLUSESSL_ALL);
curl_easy_setopt(curl, CURLOPT_MAIL_FROM, FROM);
recipients = curl_slist_append(recipients, TO);
curl_easy_setopt(curl, CURLOPT_MAIL_RCPT, recipients);
curl_easy_setopt(curl, CURLOPT_READFUNCTION, payload_source);
curl_easy_setopt(curl, CURLOPT_READDATA, &upload_ctx);
curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
res = curl_easy_perform(curl);
if (res != CURLE_OK)
fprintf(stderr, "curl_easy_perform() failed: %s\n",
curl_easy_strerror(res));
curl_slist_free_all(recipients);
curl_easy_cleanup(curl);
}
return (int)res;
}
How to fix this ? I have no acquaintance with this library,and the only thing I can receive is the subject, Thanks if anyone can help
static const char* payload_text[] =
{
"To: " TO "\r\n",
"From: " FROM "\r\n",
"Subject: SMTP TLS example message\r\n",
"Contain: Helllo\r\n",
NULL
};
should be something like
static const char* payload_text[] =
{
"To: " TO "\r\n",
"From: " FROM "\r\n",
"Subject: SMTP TLS example message\r\n",
"\r\n",
"Helllo\r\n",
NULL
};
That is Contain: is wrong, and there should be a blank line between the headers and the message.
Email format is described here.

send email without using libcurl.dll in c++

I written a simple little code based around libcurl that sends a email to myself:
#include <cstdio>
#include <cstring>
#include <curl/curl.h>
static const char* payload_text[] = {
"To: " "<myemail#web.de>" "\r\n",
"From: " "<myemail#web.de>" " (Example User)\r\n",
"Cc: " "<myemail#web.de>" " (Another example User)\r\n",
"Message-ID: <dcd7cb36-11db-487a-9f3a-e652a9458efd#"
"rfcpedant.example.org>\r\n",
"Subject: Hello\r\n",
"\r\n",
"The body of the message starts here.\r\n",
"\r\n",
"Test.\r\n",
nullptr
};
struct upload_status {
int lines_read;
};
static size_t payload_source(void* ptr, const size_t size, const size_t nmemb, void* userp)
{
auto* const upload_ctx = static_cast<struct upload_status*>(userp);
if (size == 0 || nmemb == 0 || size * nmemb < 1) {
return 0;
}
const auto* const data = payload_text[upload_ctx->lines_read];
if (data) {
const auto len = strlen(data);
memcpy(ptr, data, len);
upload_ctx->lines_read++;
return len;
}
return 0;
}
int _stdcall WinMain(struct HINSTANCE__* hinstance, struct HINSTANCE__* hprevinstance, char* cmdline, int cmdshow)
{
auto res = CURLE_OK;
struct curl_slist* recipients = nullptr;
struct upload_status upload_ctx;
upload_ctx.lines_read = 0;
auto* const curl = curl_easy_init();
if (curl) {
curl_easy_setopt(curl, CURLOPT_USERNAME, "myemail#web.de");
curl_easy_setopt(curl, CURLOPT_PASSWORD, "secret");
curl_easy_setopt(curl, CURLOPT_URL, "smtp://smtp.web.de:587");
curl_easy_setopt(curl, CURLOPT_USE_SSL, static_cast<long>(CURLUSESSL_ALL));
curl_easy_setopt(curl, CURLOPT_MAIL_FROM, "<myemail#web.de>");
recipients = curl_slist_append(recipients, "<myemail#web.de>");
recipients = curl_slist_append(recipients, "<myemail#web.de>");
curl_easy_setopt(curl, CURLOPT_MAIL_RCPT, recipients);
curl_easy_setopt(curl, CURLOPT_READFUNCTION, payload_source);
curl_easy_setopt(curl, CURLOPT_READDATA, &upload_ctx);
curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
res = curl_easy_perform(curl);
if (res != CURLE_OK)
fprintf(stderr, "curl_easy_perform() failed: %s\n",
curl_easy_strerror(res));
curl_slist_free_all(recipients);
curl_easy_cleanup(curl);
}
return static_cast<int>(res);
}
NOTE: thats not my password there in curl_easy_setopt(curl, CURLOPT_PASSWORD, "secret");
The above code works. but i needs libcurl.dll and zlib1.dll
I would like it in a single .exe file
My question is.. Is that possible in c++, i know its possible in c# with using System.Net.Mail or something, or is there are alternative to my code that doesn't require external .dlls

Function that uses define directive

I have this piece of code which sends email via libcurl
#define FROM "<my#gmail.com>"
#define TO "<your#gmail.com>"
static const char *payload_text[] = {
"To: " TO "\r\n",
"From: " FROM "\r\n",
"Subject: Shift\r\n",
"text\r\n",
NULL
};
struct upload_status {
int lines_read;
};
static size_t payload_source(void *ptr, size_t size, size_t nmemb, void *userp)
{
struct upload_status *upload_ctx = (struct upload_status *)userp;
const char *data;
if((size == 0) || (nmemb == 0) || ((size*nmemb) < 1)) {
return 0;
}
data = payload_text[upload_ctx->lines_read];
if(data) {
size_t len = strlen(data);
memcpy(ptr, data, len);
upload_ctx->lines_read++;
return len;
}
return 0;
}
int main(void)
{
CURL *curl;
CURLcode res = CURLE_OK;
struct curl_slist *recipients = NULL;
struct upload_status upload_ctx;
upload_ctx.lines_read = 0;
curl = curl_easy_init();
if(curl) {
curl_easy_setopt(curl, CURLOPT_USERNAME, "my#gmail.com");
curl_easy_setopt(curl, CURLOPT_PASSWORD, "pass");
curl_easy_setopt(curl, CURLOPT_URL, "smtp://smtp.gmail.com:587");
curl_easy_setopt(curl, CURLOPT_USE_SSL, (long)CURLUSESSL_ALL);
curl_easy_setopt(curl, CURLOPT_CAINFO, "google.pem");
curl_easy_setopt(curl, CURLOPT_MAIL_FROM, FROM);
recipients = curl_slist_append(recipients, TO);
//curl_easy_setopt(curl, CURLOPT_FILE, "edgE0DF.tmp");
curl_easy_setopt(curl, CURLOPT_MAIL_RCPT, recipients);
curl_easy_setopt(curl, CURLOPT_READFUNCTION, payload_source);
curl_easy_setopt(curl, CURLOPT_READDATA, &upload_ctx);
curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
res = curl_easy_perform(curl);
if(res != CURLE_OK)
fprintf(stderr, "curl_easy_perform() failed: %s\n",
curl_easy_strerror(res));
curl_slist_free_all(recipients);
curl_easy_cleanup(curl);
}
return 0;
}
My question is: how can I make a function which takes as arguments from and to and change my global FROM and TO? Is this possible? Because, I know that global variables are initialized before other parts of code and this means that my static const char *payload_text[] that uses FROM and TO will be initialized with wrong addresses.

Const char* not assigning properly?

I have the following code:
#define FROM "<example#gmail.com>"
#define TO "<example2#gmail.com>"
const char *payload_text[] = {
"Date: Mon, 29 Nov 2010 21:54:29 +1100\r\n",
"To: " TO "\r\n",
"From: " FROM "(Example User)\r\n",
"Subject: SMTP TLS example message\r\n",
"MIME-Version: 1.0\r\n",
"Content-Type: multipart/mixed; boundary=\"KkK170891tpbkKk__FV_KKKkkkjjwq\"\r\n",
"\r\n",
"This is a multipart message in MIME format.",
"\r\n",
"--KkK170891tpbkKk__FV_KKKkkkjjwq\r\n",
"Content-Type: text/plain",
"\r\n",
"here goes the text message\r\n",
"\r\n",
"--KkK170891tpbkKk__FV_KKKkkkjjwq\r\n",
"Content-Type: image/jpeg; name=\"test.jpg\"\r\n",
"Content-Transfer-Encoding: base64\r\n",
"Content-Disposition: attachment; filename=\"test.jpg\"\r\n",
"\r\n",
NULL, /*19*/
"\r\n",
"--KkK170891tpbkKk__FV_KKKkkkjjwq--\r\n",
NULL
};
struct upload_status {
int lines_read;
};
static size_t payload_source(void *ptr, size_t size, size_t nmemb, void *userp) {
struct upload_status *upload_ctx = (struct upload_status *)userp;
const char *data;
if((size == 0) || (nmemb == 0) || ((size*nmemb) < 1)) {
return 0;
}
data = payload_text[upload_ctx->lines_read];
if(data) {
size_t len = strlen(data);
memcpy(ptr, data, len);
upload_ctx->lines_read++;
return len;
}
return 0;
}
std::string readFileToBase64(const char* filename) {
/* converts binary file to base64 */
}
std::string split76(std::string in) {
int lines = in.length() / 76;
for(int i=0;i<lines;i++) {
in.insert((i+1)*76+i*2, "\r\n");
}
return in;
}
int main(void) {
payload_text[19] = split76(readFileToBase64("C:\\Users\\thrymgjol\\code\\emailtest\\bin\\Release\\test.jpg")).c_str();
CURL *curl;
CURLcode res = CURLE_OK;
struct curl_slist *recipients = NULL;
struct upload_status upload_ctx;
upload_ctx.lines_read = 0;
curl = curl_easy_init();
if(curl) {
curl_easy_setopt(curl, CURLOPT_USERNAME, "example2#gmail.com");
curl_easy_setopt(curl, CURLOPT_PASSWORD, "legitpassword");
curl_easy_setopt(curl, CURLOPT_URL, "smtp://smtp.gmail.com:587");
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_MAIL_FROM, FROM);
recipients = curl_slist_append(recipients, TO);
curl_easy_setopt(curl, CURLOPT_MAIL_RCPT, recipients);
curl_easy_setopt(curl, CURLOPT_READFUNCTION, payload_source);
curl_easy_setopt(curl, CURLOPT_READDATA, &upload_ctx);
curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
res = curl_easy_perform(curl);
if(res != CURLE_OK)
fprintf(stderr, "curl_easy_perform() failed: %s\n",
curl_easy_strerror(res));
curl_slist_free_all(recipients);
curl_easy_cleanup(curl);
}
return (int)res;
}
However, when I compile and run, payload_source() stops reading payload_text after the 18th indice. This completely cuts off my attachment, which I assign to payload_text[19]. Any ideas on why it does this?
The problem is the value returned from split76 is temporary and is destroyed after the assignment has completed. Any attempt to access it after that results in undefined behavior. If you need to store a pointer to the string buffer you can create local std::string to hold it an ensure it lives long enough to be used.
std::string encodedFile(split76(readFileToBase64("C:\\Users\\thrymgjol\\code\\emailtest\\bin\\Release\\test.jpg")));
payload_text[19] = encodedFile.c_str();