Reputation: 56
I've been building a small test program that uses libcurl to send an email. The ultimate goal is to implement this as a remote notification system for a larger piece of software. I've mostly used this and this as guides.
The program works up until the callback function. Debugger shows curl_callback
is receiving the 'string' TESTMESSAGE.email
properly. Then calls curl_callback
two more times (three total) and hangs.
From my understanding, curl uses the callback function to iterate through the 'string', reading each char. Based on what I am seeing in the debugger, this appears to not be the case.
I'm thinking the issue lies in how I am passing my message to curl, but I don't have a good enough understanding of how curl operates to verify this. The many examples and forums I've looked at seem to all contradict one another as to the best way to prepare and pass the message.
Note: curl_global_init()
is used because the functions developed here will be implemented into a multi-threaded program.
/* Define message structures */
typedef struct emailMsg {
const char *toEmail;
const char *toName;
const char *fromEmail;
const char *fromName;
const char *subject;
const char *body;
const char **email;
size_t pos;
} emailMsg;
int sendEmail(emailMsg *data);
static size_t curl_callback(void *buffer, size_t size, size_t nmemb, void *instream);
char *payload[6];
source.c
#include <header.h>
int main(void){
curl_global_init(CURL_GLOBAL_NOTHING);
struct emailMsg TESTMESSAGE;
/* Definitions */
TESTMESSAGE.toEmail = "<[email protected]>";
TESTMESSAGE.toName = "toName";
TESTMESSAGE.fromEmail = "<[email protected]>";
TESTMESSAGE.fromName = "fromName";
TESTMESSAGE.subject = "Test Message";
TESTMESSAGE.body = "THIS IS A TEST MESSAGE.\r\n"
"\r\n"
"DISREGARD IT AND HAVE A NICE DAY!";
sendEmail(&TESTMESSAGE);
return(0);
}
int sendEmail(emailMsg *data){
size_t bodySize = strlen(data->body) + 3;
char *body = malloc(bodySize);
snprintf(body, bodySize, "%s%s", data->body, "\r\n");
size_t subjectSize = strlen(data->subject) + 12;
char *subject = malloc(subjectSize);
snprintf(subject, subjectSize, "%s%s%s", "Subject: ", data->subject, "\r\n");
size_t receiverSize = strlen(data->toName) + strlen(data->toEmail) + 10;
char *receiver = malloc(receiverSize);
snprintf(receiver, receiverSize, "%s%s (%s)%s", "To: ", data->toEmail, data->toName, "\r\n");
size_t senderSize = strlen(data->fromEmail) + strlen(data->fromName) + 12;
char *sender = malloc(senderSize);
snprintf(sender, senderSize, "%s%s (%s)%s", "From: ", data->fromEmail, data->fromName, "\r\n");
size_t timeSize = 40;
char *timeString = malloc(timeSize);
time_t rawtime = 0;
time(&rawtime);
struct tm *timeinfo = localtime(&rawtime);
strftime (timeString, timeSize, "Date: %a, %d %b %Y %T %z\r\n", timeinfo);
/* Create payload (string) */
size_t total_length = timeSize + receiverSize + senderSize + subjectSize + bodySize + 1;
char *payload = (char*)malloc(total_length);
memset(payload,0,strlen(payload));
strcat(payload, timeString);
strcat(payload, receiver);
strcat(payload, sender);
strcat(payload, subject);
strcat(payload, body);
strcat(payload, "\0");
data->email = &payload;
CURL *curl;
CURLcode res = CURLE_OK;
struct curl_slist *recipients = NULL;
data->pos = 0;
curl = curl_easy_init();
if (curl){
curl_easy_setopt(curl, CURLOPT_URL, "smtp://somemta");
curl_easy_setopt(curl, CURLOPT_MAIL_FROM, data->fromEmail);
recipients = curl_slist_append(recipients, data->toEmail);
curl_easy_setopt(curl, CURLOPT_MAIL_RCPT, recipients);
curl_easy_setopt(curl, CURLOPT_READFUNCTION, curl_callback);
curl_easy_setopt(curl, CURLOPT_READDATA, data);
curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
/* useful for debugging encryped traffic */
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 receipients list and cleanup */
curl_slist_free_all(recipients);
curl_easy_cleanup(curl);
};
return((int)res);
}
static size_t curl_callback(void *buffer, size_t size, size_t nmemb, void *instream){
/*
* Pass N bytes of data to libcurl for the transfer requested.
* This is done 'size' by 'size'.
*
* Parameters:
* void *buffer: Pointer to the buffer
* size_t size: Size of each item
* size_t nmemb: Number of items
* void *instream: Pointer address of data
*/
/* Check for invalid arguments */
if((size == 0) || (nmemb == 0) || ((size * nmemb) < 1)){
return(0);
};
emailMsg *upload = (emailMsg*)instream;
const char *email = upload->email[upload->pos];
if(email){
size_t len = strlen(email);
if(len > size * nmemb){
return(CURL_READFUNC_ABORT);
};
memcpy(buffer, email, len);
upload->pos++;
return(len);
};
return(0);
}
Thanks to nephtes for helping point out the flaw in my logic and some other issues with the program. Your solution helped me realize that by concatenating the strings into one, I was removing the NULL element that CURL was looking for to properly terminate the upload. The working source is below.
header.h#define MTA "somemta"
/* Define message structures */
typedef struct emailMsg {
const char *toEmail;
const char *toName;
const char *fromEmail;
const char *fromName;
const char *subject;
const char *body;
size_t pos;
char *email[];
} emailMsg;
int sendEmail(emailMsg *data);
static size_t curl_callback(void *buffer, size_t size, size_t nmemb, void *instream);
char *payload;
source.c
#include <header.h>
int main(void){
curl_global_init(CURL_GLOBAL_NOTHING
struct emailMsg TESTMESSAGE;
/* Definitions */
TESTMESSAGE.toEmail = "<[email protected]>";
TESTMESSAGE.toName = "toName";
TESTMESSAGE.fromEmail = "<[email protected]>";
TESTMESSAGE.fromName = "fromName";
TESTMESSAGE.subject = "Test Message";
TESTMESSAGE.body = "THIS IS A TEST MESSAGE.\r\n"
"\r\n"
"DISREGARD IT AND HAVE A NICE DAY!";
/* Process and send message */
sendEmail(&TESTMESSAGE);
/* Return zero for success */
return(0);
}
int sendEmail(emailMsg *data){
int i;
size_t sizes[6];
/* Date */
/* Calculate string size */
sizes[0] = 39;
/* Allocate memory (+1 for NULL) */
data->email[0] = malloc(sizes[0] + 1);
/* Compose string */
time_t rawtime = 0;
time(&rawtime);
struct tm *timeinfo = localtime(&rawtime);
strftime (data->email[0], sizes[0] + 1, "Date: %a, %d %b %Y %T %z\r\n", timeinfo);
/* Receiver */
/* Calculate string size */
sizes[1] = strlen(data->toName) + strlen(data->toEmail) + 9;
/* Allocate memory (+1 for NULL) */
data->email[1] = malloc(sizes[1] + 1);
/* Compose string */
snprintf(data->email[1], sizes[1] + 1, "%s%s (%s)%s", "To: ", data->toEmail, data->toName, "\r\n");
/* Sender */
/* Calculate string size */
sizes[2] = strlen(data->fromEmail) + strlen(data->fromName) + 11;
/* Allocate memory (+1 for NULL) */
data->email[2] = malloc(sizes[2] + 1);
/* Compose string */
snprintf(data->email[2], sizes[2] + 1, "%s%s (%s)%s", "From: ", data->fromEmail, data->fromName, "\r\n");
/* Subject */
/* Calculate string size */
sizes[3] = strlen(data->subject) + 11;
/* Allocate memory (+1 for NULL) */
data->email[3] = malloc(sizes[3] + 1);
/* Compose string */
snprintf(data->email[3], sizes[3] + 1, "%s%s%s", "Subject: ", data->subject, "\r\n");
/* Body */
/* Calculate string size */
sizes[4] = strlen(data->body) + 2;
/* Allocate memory (+1 for NULL) */
data->email[4] = malloc(sizes[4] + 1);
/* Compose string */
snprintf(data->email[4], sizes[4] + 1, "%s%s", data->body, "\r\n");
/* NULL terminated */
sizes[5] = 1;
data->email[5] = "";
/* Reset counter */
data->pos = 0;
printf("%s", data->email);
/* CURL initialization */
CURL *curl;
CURLcode res = CURLE_OK;
struct curl_slist *recipients = NULL;
curl = curl_easy_init();
if (curl){
curl_easy_setopt(curl, CURLOPT_URL, MTA);
curl_easy_setopt(curl, CURLOPT_MAIL_FROM, data->fromEmail);
recipients = curl_slist_append(recipients, data->toEmail);
curl_easy_setopt(curl, CURLOPT_MAIL_RCPT, recipients);
curl_easy_setopt(curl, CURLOPT_READFUNCTION, curl_callback);
curl_easy_setopt(curl, CURLOPT_READDATA, data);
curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
/* useful for debugging encryped traffic */
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
/* send the message */
res = curl_easy_perform(curl);
/* Free receipients list and cleanup */
curl_slist_free_all(recipients);
curl_easy_cleanup(curl);
};
/* Return CURL response */
return((int)res);
}
static size_t curl_callback(void *buffer, size_t size, size_t nmemb, void *instream){
/*
* Pass N bytes of data to libcurl for the transfer requested.
* This is done 'size' by 'size'.
*
* Parameters:
* void *buffer: Pointer to the buffer
* size_t size: Size of each item
* size_t nmemb: Number of items
* void *instream: Pointer address of data
*/
/* Check for invalid arguments */
if((size == 0) || (nmemb == 0) || ((size * nmemb) < 1)){
return(0);
};
/* Assign instream to local structure */
emailMsg *upload = (emailMsg*)instream;
/* Assign current position to local variable */
const char *email = upload->email[upload->pos];
/* Run until NULL reached */
if(email){
/* Find length of string */
size_t len = strlen(email);
/* len > expected message */
if(len > size * nmemb){
return(CURL_READFUNC_ABORT);
};
/* Copy to buffer */
memcpy(buffer, email, len);
/* Increment */
upload->pos++;
/* Return the number of bytes written */
return(len);
};
/* Signify end of upload */
return(0);
}
Upvotes: 1
Views: 934
Reputation: 1319
Here's what you're doing before curl_easy_perform()
char *payload = (char*)malloc(total_length);
data->email = &payload;
And here's what you're doing in the callback:
const char *email = upload->email[upload->pos]
The problem is that upload->email
(aka data->email
) is not an array, yet you're treating it like one. Index 0 will be fine, but anything else will point to some random memory location.
What you need to do is make email
a simple char*
buffer instead of char**
, and use upload->pos
as a position index into that buffer, incrementing its value as you copy data to the buffer provided in the callback argument.
Upvotes: 1