i am trying to implement a quite specialized email-client using libcurl (7.66) for sending the email.
Sadly, it always crashes on calling curl_easy_perform(curl), no matter what i try whatsoever.
Currently in the process_mail callback i have commented everything out; just returning 0. I am pretty shure, this should be enough for at least not crasing the app ...
The real goal would be having the mail as a string in the memory and just copy it over to the stream.
I really appreciate any kind of help ... i have quite a delay because of this issue.
Here is the header-file:
#ifndef MAILMESSAGE_H
#define MAILMESSAGE_H
#include <QObject>
#include <QDebug>
#include <QFile>
#include <QIODevice>
#include <QByteArray>
#include <curl/curl.h>
#include <stdio.h>
class MailMessage : public QObject
{
Q_OBJECT
public:
explicit MailMessage(QObject *parent = nullptr);
void setSMTPPort(QString smtp_port);
void setSMTPAddress(QString smtp_address);
void setSMTPUser(QString smtp_user);
void setSMTPPassword(QString smtp_password);
void addTo(QString address_to);
void setFrom(QString address_from);
void setSubject(QString subject);
void setAlternativeText(QString text);
void setHTML(QString html);
void addAttachment(QString attachment_path, QString filename);
void generateMessage();
// return POSIX-style: 0: ok; 1: error
int sendMail();
QString message;
int message_size;
const char* message_char;
QString smtp_port;
QString smtp_address;
QString smtp_user;
QString smtp_password;
//QStringList addresses_to;
QString address_to;
QString address_from;
QString subject;
QString payload_text;
QString payload_html;
QStringList attachments_base64;
double upload_speed;
double upload_time;
protected:
static size_t process_mail(void *ptr, size_t size, size_t nmemb, void *userp);
signals:
public slots:
};
#endif // MAILMESSAGE_H
And the relevant parts of the implementation:
void MailMessage::generateMessage()
{
this->message = //"To: "+ this->address_to +"\r\n"
"From: "+ this->address_from +"\r\n"
"Subject: "+ this->subject +"\r\n"
"Content-Type: multipart/mixed; boundary=MixedBoundary"
"\r\n"
"--MixedBoundary"
"Content-Type: multipart/alternative; boundary=AlternativeBoundary"
"\r\n"
"--AlternativeBoundary"
"Content-Type: text/plain; charset=\"utf-8\""
"\r\n"+ this->payload_text +"\r\n"
"--AlternativeBoundary"
"Content-Type: text/html; charset=\"utf-8\""
"\r\n"+ this->payload_html +"\r\n"
"--AlternativeBoundary--"
"\r\n"
"--MixedBoundary";
/*
"Content-Type: application/pdf; name=\"HelloWorld.pdf\""
"Content-Transfer-Encoding: base64"
"Content-Disposition: attachment; filename=\"HelloWorld.pdf\"";
*/
this->message.append("\r\n"
"--MixedBoundary--");
QByteArray array = this->message.toLocal8Bit();
this->message_char = array.data();
this->message_size = sizeof(this->message_char);
}
size_t MailMessage::process_mail(void *ptr, size_t size, size_t nmemb, void *userprocess)
{
size_t retcode = 0;
/*
MailMessage *self = reinterpret_cast<MailMessage*>(userprocess);
if (sizeof(self->message_char) == 0)
return retcode;
retcode = (size * nmemb >= sizeof(self->message_char)) ? sizeof(self->message_char) : size * nmemb;
self->message_size -= retcode;
memcpy(ptr, self->message_char, retcode);
self->message_char += retcode;
*/
return retcode;
}
int MailMessage::sendMail()
{
//qDebug() << this->message;
CURLcode res = CURLE_OK;
CURL *curl = curl_easy_init();
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
// CURLUSESSL_ALL, CURLUSESSL_NONE, CURLUSESSL_TRY, CURLUSESSL_CONTROL
curl_easy_setopt(curl, CURLOPT_USE_SSL, CURLUSESSL_ALL);
curl_easy_setopt(curl, CURLOPT_URL, this->smtp_address.toStdString().c_str());
curl_easy_setopt(curl, CURLOPT_USERNAME, this->smtp_user.toStdString().c_str());
curl_easy_setopt(curl, CURLOPT_PASSWORD, this->smtp_password.toStdString().c_str());
curl_easy_setopt(curl, CURLOPT_MAIL_FROM, this->address_from.toStdString().c_str());
curl_easy_setopt(curl, CURLOPT_MAIL_RCPT, this->address_to.toStdString().c_str());
curl_easy_setopt(curl, CURLOPT_READFUNCTION, &MailMessage::process_mail);
curl_easy_setopt(curl, CURLOPT_READDATA, this);
/* 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));
}
else
{
curl_easy_getinfo(curl, CURLINFO_SPEED_UPLOAD, &upload_speed);
curl_easy_getinfo(curl, CURLINFO_TOTAL_TIME, &upload_time);
qDebug() << "Speed: "+QString::number(upload_speed) + " Time: "+QString::number(upload_time);
}
//curl_slist_free_all(this->address_to.toStdString().c_str());
curl_easy_cleanup(curl);
return 0;
}
And finally some excerpt of the console output:
* start date: May 8 08:01:21 2019 GMT
* expire date: May 13 23:59:59 2021 GMT
* subjectAltName: host "mail.gmx.net" matched cert's "mail.gmx.net"
* issuer: C=DE; O=T-Systems International GmbH; OU=T-Systems Trust Center; ST=Nordrhein Westfalen; postalCode=57250; L=Netphen; street=Untere Industriestr. 20; CN=TeleSec ServerPass Extended Validation Class 3 CA
* SSL certificate verify ok.
> EHLO cf-19
* SMTP 0x5572580cd700 state change from UPGRADETLS to EHLO
< 250-gmx.com Hello cf-19 [2.202.204.204]
* Curl_pp_readresp_ 55 bytes of trailing server response left
< 250-8BITMIME* Curl_pp_readresp_ 41 bytes of trailing server response left
< 250-AUTH LOGIN PLAIN
* Curl_pp_readresp_ 19 bytes of trailing server response left
< 250 SIZE 69920427
> AUTH PLAIN
* SASL 0x5572580cd780 state change from STOP to PLAIN
* SMTP 0x5572580cd700 state change from EHLO to AUTH
< 334
> AHNhbXVlbF9iZWNrZXJAZ214LmRlADI4OTZhY2Q2
* SASL 0x5572580cd780 state change from PLAIN to FINAL
< 235 Authentication succeeded
* SASL 0x5572580cd780 state change from FINAL to STOP
* SMTP 0x5572580cd700 state change from AUTH to STOP
* STATE: PROTOCONNECT => DO handle 0x5572580d0678; line 1701 (connection #0)
* DO phase starts
> MAIL FROM:<xxxxxxx#xxx.xx>
* SMTP 0x5572580cd700 state change from STOP to MAIL
* STATE: DO => DOING handle 0x5572580d0678; line 1743 (connection #0)
< 250 Requested mail action okay, completed
03:37:55: The program has unexpectedly finished.
03:37:55: The process was ended forcefully.
QByteArray array = this->message.toLocal8Bit();
This creates a local QByteArray object, a local variable in the generateMessage() method. When generateMessage() returns, this object and everything in it will, of course, be destroyed just like any other function local variable. That's how local function variables work in C++.
this->message_char = array.data();
this->message_size = sizeof(this->message_char);
This saves a pointer to array's internal buffer in message_char. This also saves sizeof(this->message_char) in message_size. Since message_char is a const char *, this will always save sizeof(const char *), which will typically be 4 or 8 (depending on your operating system), in message_size, no matter how big the array is. That's already an obvious fail at this point, but not the ultimate fail that crashes the shown code.
The ultimate fail is that, as I've explained earlier, as soon as this function returns -- which is right about now (since this is the last statement in this function) -- array and all of its contents gets immediately destroyed. The aforementioned pointer to array's internal buffer will become a dangling pointer, to the internal contents of an object that no longer exists, and any further attempts to dereference this pointer results in undefined behavior, and a likely crash.
This is the obvious reason for your crash: improper usage of dangling pointers to destroyed objects. Additionally contributory factor is improper usage of sizeof (in the event your message's actual size happens to be less than the size of a pointer on your operating system).
Related
I'm following a libcurl sample code and trying to use it to send an email to myself, but there's a few things I'm not quite understanding...
#include <stdio.h>
#include <string.h>
#include <curl/curl.h>
/*
* For an SMTP example using the multi interface please see smtp-multi.c.
*/
/* The libcurl options want plain addresses, the viewable headers in the mail
* can very well get a full name as well.
*/
#define FROM_ADDR "<sender#example.org>"
#define TO_ADDR "<addressee#example.net>"
#define CC_ADDR "<info#example.org>"
#define FROM_MAIL "Sender Person " FROM_ADDR
#define TO_MAIL "A Receiver " TO_ADDR
#define CC_MAIL "John CC Smith " CC_ADDR
static const char *payload_text[] = {
"Date: Mon, 29 Nov 2010 21:54:29 +1100\r\n",
"To: " TO_MAIL "\r\n",
"From: " FROM_MAIL "\r\n",
"Cc: " CC_MAIL "\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",
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) {
/* This is the URL for your mailserver */
curl_easy_setopt(curl, CURLOPT_URL, "smtp://mail.example.com");
/* Note that this option isn't 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, FROM_ADDR);
/* 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, TO_ADDR);
recipients = curl_slist_append(recipients, CC_ADDR);
curl_easy_setopt(curl, CURLOPT_MAIL_RCPT, recipients);
/* We're 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);
/* 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);
/* curl won't 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);
}
return (int)res;
}
Now it looks like for the first curl_easy line, I'd need to change it to
curl_easy_setopt(curl, CURLOPT_URL, "smtp.gmail.com");
If I was using gmail, right? And since this is for outgoing mail, I don't need to worry about what the user is using as long as my outgoing mail address is a gmail address?
Going a little out of order...
recipients = curl_slist_append(recipients, useremail);
In this case, it would be my email. All I need to do is replace this with the receiving email correct? Well what about the sender?
curl_easy_setopt(curl, CURLOPT_MAIL_FROM, myemail);
I'm guessing that likewise, I would need to change this to my email. But it feels like it's missing something. Surely I can't just tell this program to send an email with my address without some sort of authorization, right? But I don't see anywhere on the code where I would need to supply my password (obviously I wouldn't want it hard coded, but that's an issue for later), so how would this work?
For the time being, these are the only lines I would need to change (besides a specific message), right? Am I going about this the right way? Is there more I would need to do for this to work? And how does the authorization work?
EDIT: I have made updates based on what I think would work, as well as the suggestion given for a username and password. I currently have this error:
curl_easy_perform() failed: Failed sending data to the peer
Unfortunately, it's not too specific on what went wrong. This is the new version:
#include <stdio.h>
#include <string.h>
#include <curl/curl.h>
/*
* For an SMTP example using the multi interface please see smtp-multi.c.
*/
/* The libcurl options want plain addresses, the viewable headers in the mail
* can very well get a full name as well.
*/
#define FROM_ADDR "<sender#example.org>"
#define TO_ADDR "<addressee#example.net>"
#define CC_ADDR "<info#example.org>"
#define FROM_MAIL "Sender Person " FROM_ADDR
#define TO_MAIL "A Receiver " TO_ADDR
#define CC_MAIL "John CC Smith " CC_ADDR
static const char *payload_text[] = {
"Date: Mon, 29 Nov 2010 21:54:29 +1100\r\n",
"To: " TO_MAIL "\r\n",
"From: " FROM_MAIL "\r\n",
"Cc: " CC_MAIL "\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",
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) {
/* This is the URL for your mailserver */
curl_easy_setopt(curl, CURLOPT_URL, "smtp.gmail.com");
/* Note that this option isn't 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, "myemail#gmail.com");
/* 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, "myemail#gmail.com");
recipients = curl_slist_append(recipients, CC_ADDR);
curl_easy_setopt(curl, CURLOPT_USERNAME, "myemail#gmail.com");
curl_easy_setopt(curl, CURLOPT_PASSWORD, "mypassword");
curl_easy_setopt(curl, CURLOPT_MAIL_RCPT, recipients);
/* We're 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);
/* 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);
/* curl won't 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);
}
return (int)res;
}
I haven't filled in CC_MAIL, but is this truly necessary? The actual name of the personal is generally automatically filled out by Google when you send an email.
You can set it below
curl_easy_setopt(curl, CURLOPT_USERNAME, USERNAME);
curl_easy_setopt(curl, CURLOPT_PASSWORD, PASSWORD);
Please refer to the example on curl (tag: curl-7_34_0)
https://github.com/curl/curl/blob/curl-7_34_0/docs/examples/smtp-multi.c
Recommended reference smtp-tls.c, using TLS
https://github.com/curl/curl/blob/master/docs/examples/smtp-tls.c
I am also confused whether we have to use that "<" sign or not while defining that
#define CC_ADDR "<info#example.org>"
section for eg.
Do I have to use #define CC_ADDR "anything" or I have to use #define CC_ADDR "<anything>"
I have one information regarding writing the server mail, we can use smtp.gmail.com:587 and it will work. Note that : used here is used to specify the specific port address to the mail server.
I'm trying to play with libcurl SMTP and everything works fine with this example:
http://curl.haxx.se/libcurl/c/smtp-mail.html
#include <stdio.h>
#include <string.h>
#include <curl/curl.h>
/* This is a simple example showing how to send mail using libcurl's SMTP
* capabilities. For an exmaple of using the multi interface please see
* smtp-multi.c.
*
* Note that this example requires libcurl 7.20.0 or above.
*/
#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 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) {
/* This is the URL for your mailserver */
curl_easy_setopt(curl, CURLOPT_URL, "smtp://mail.example.com");
/* Note that this option isn't 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, 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, TO);
recipients = curl_slist_append(recipients, CC);
curl_easy_setopt(curl, CURLOPT_MAIL_RCPT, recipients);
/* We're 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);
/* 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);
/* curl won't 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);
}
return (int)res;
}
but if I greatly increases a payload function, I get an error:
read function returned funny value
Segmentation fault
How could I solve this problem?
The following code is to send a plain text message
rather than a email message. but the basic ideas are the same.
did you set the curl write function option to point to a callback function?
curl_easy_setopt(curlHandle, CURLOPT_WRITEFUNCTION, &CurlWriteFunction);
//////////////////
where the CurlWriteFunction() would look similar to the following:
//////////////////
size_t CurlWriteFunction(void *ptr, size_t size, size_t nmemb, void *stream)
{
memset(CurlMsgStr, 0, MAX_MSG_STR);
phpCurlError = FALSE;
if(((char *)ptr)[0] == '1' || ((char *)ptr)[1] == '1')
{
phpCurlError = FALSE;
sprintf(CurlMsgStr,"Record sent to URL OK...[%c%c]\n",
((char *)ptr)[0],((char *)ptr)[1]);
}
else if(((char *)ptr)[0] == '0' || ((char *)ptr)[1] == '0')
{
phpCurlError = TRUE;
sprintf(CurlMsgStr,"Record sent to URL FAILED...[%c%c]\n",
((char *)ptr)[0],((char *)ptr)[1]);
}
else
{
phpCurlError = TRUE;
sprintf(CurlMsgStr,"UNKNOWN URL RETURN VALUE...[%c%c]\n",
((char *)ptr)[0],((char *)ptr)[1]);
}
return (nmemb);
} // CurlWriteFunction()
////////////////////
Then send the curl message with something like this:
////////////////////
void sendViaCurl(char *str)
{
#if 0 // eliminate heap thrashing of 8k malloc/free
char* postData = NULL;
#else
static char* postData = NULL;
#endif
CURLcode curlErr;
long curlRespCode = 0;
char recNameStr[MAX_STRING_LENGTH];
if(curlHandle)
{
// Specify the POST data
if(postData == NULL) // then first time through
{
postData = malloc(MAX_RESPONSE_LENGTH * 2);
}
memset(postData,0,MAX_RESPONSE_LENGTH * 2);
sprintf(postData, "%s", str);
curl_easy_setopt(curlHandle, CURLOPT_POSTFIELDS, postData);
// Execute the POST, response goes to STDOUT
curlErr = curl_easy_perform(curlHandle);
// Report any CURL errors
if(curlErr)
{
log_error("CURL ERROR -- %s\n", curl_easy_strerror(curlErr));
}
// Get info on the POST data transfer to URL
curl_easy_getinfo(curlHandle, CURLINFO_RESPONSE_CODE, &curlRespCode);
// log_error( "CURL RESPONSE CODE -- %ld\n", curlRespCode);
if(phpCurlError)
{
log_error( " %s rec rejected by DB -- %s\n", recNameStr, str);
curlRecRejected = TRUE;
}
else
{
log_error( "%s [%s] rec accepted by DB...\n",recNameStr, str);
curlRecRejected = FALSE;
}
}
else
{
log_error( "NOT ABLE TO GET CURL HANDLE...RECORD NOT SENT\n");
curlRecRejected = TRUE;
}
#if 0 // eliminate thrashing of heap with 8k malloc/free
if(postData)
{
free(postData);
postData = NULL;
}
#endif
return;
} // sendViaCurl()
I'm currently experiencing some problems and was hoping I could gain some insight as to what is going on by posting it here. I want to write a simple C++ application (or C) to send an email to a gmail account. I am in a win32 (xp sp2) environment and I am using dev C++ as my compiler. I've included the curl libraries and the application seems to compile fine, however it does not send the email nor does it display errors. Here is the code that I found that I am using to do this:
#include <stdio.h>
#include <string.h>
#include <curl/curl.h>
#include <conio.h>
#include <iostream>
/* This is a simple example showing how to send mail using libcurl's SMTP
* capabilities. It builds on the simplesmtp.c example, adding some
* authentication and transport security.
*/
#define FROM "<email#email.com>"
#define TO "<email#gmail.com>"
#define CC "<email#gmail.com>"
using namespace std;
static const char *payload_text[]={
"Date: Mon, 29 Nov 2010 21:54:29 +1100\n",
"To: " TO "\n",
"From: " FROM "(Example User)\n",
"Cc: " CC "(Another example User)\n",
"Message-ID: <dcd7cb36-11db-487a-9f3a-e652a9458efd#rfcpedant.example.org>\n",
"Subject: SMTP TLS example message\n",
"\n", /* empty line to divide headers from body, see RFC5322 */
"The body of the message starts here.\n",
"\n",
"It could be a lot of lines, could be MIME encoded, whatever.\n",
"Check RFC5322.\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)
{
//cout << "starting";
//_getch();
CURL *curl;
CURLcode res;
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_VERBOSE, 1);
/* This is the URL for your mailserver. Note the use of port 587 here,
* instead of the normal SMTP port (25). Port 587 is commonly used for
* secure mail submission (see RFC4403), but you should use whatever
* matches your server configuration. */
curl_easy_setopt(curl, CURLOPT_URL, "smtp://smtp.gmail.com:587");
/* In this example, we'll start with a plain text connection, and upgrade
* to Transport Layer Security (TLS) using the STARTTLS command. Be careful
* of using CURLUSESSL_TRY here, because if TLS upgrade fails, the transfer
* will continue anyway - see the security discussion in the libcurl
* tutorial for more details. */
curl_easy_setopt(curl, CURLOPT_USE_SSL, (long)CURLUSESSL_ALL);
/* If your server doesn't have a valid certificate, then you can disable
* part of the Transport Layer Security protection by setting the
* CURLOPT_SSL_VERIFYPEER and CURLOPT_SSL_VERIFYHOST options to 0 (false).
* curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
* curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
* That is, in general, a bad idea. It is still better than sending your
* authentication details in plain text though.
* Instead, you should get the issuer certificate (or the host certificate
* if the certificate is self-signed) and add it to the set of certificates
* that are known to libcurl using CURLOPT_CAINFO and/or CURLOPT_CAPATH. See
* docs/SSLCERTS for more information.
*/
//curl_easy_setopt(curl, CURLOPT_CAINFO, "/path/to/certificate.pem");
/* A common reason for requiring transport security is to protect
* authentication details (user names and passwords) from being "snooped"
* on the network. Here is how the user name and password are provided: */
curl_easy_setopt(curl, CURLOPT_USERNAME, "username");
curl_easy_setopt(curl, CURLOPT_PASSWORD, "password");
/* value for envelope reverse-path */
curl_easy_setopt(curl, CURLOPT_MAIL_FROM, 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, TO);
recipients = curl_slist_append(recipients, CC);
curl_easy_setopt(curl, CURLOPT_MAIL_RCPT, recipients);
/* In this case, we're using a callback function to specify the data. 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);
/* Since the traffic will be encrypted, it is very useful to turn on debug
* information within libcurl to see what is happening during the transfer.
*/
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
/* send the message (including headers) */
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 and clean up */
curl_slist_free_all(recipients);
curl_easy_cleanup(curl);
_getch();
}
return 0;
}
When I compile and run this, I see a black screen for a milisecond or so, then nothing, no email sent, no errors, etc. I thought by adding the 'curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);' (as reccomended per libcurl's website) would display output, but nothing. So I added the '_getch()' line at the end before the return, and still the window is dissappearing.
I also tried using 'system("pause")' before the final return to no avail either. Currently is appears I need to get the readable output to further debug the problem so I can continue on and get this code to send emails.
I have also tried changing the port to 465 to no avail either.
Anyone have any idea what's going on here? Thank you for your time.
I am using the following code for smtp:
When it invokes sendEmail, the output will display in the terminal. How can I send it to dev/null or not output it? Running in background doesn't help.
Partial Output:
* About to connect() to smtp.gmail.com port 587 (#0)
* Trying 173.194.79.109... * connected
< 220 mx.google.com ESMTP nv6sm10641402pbc.42
> EHLO ubuntu
< 250-mx.google.com at your service, [222.164.82.97]
< 250-SIZE 35882577
< 250-8BITMIME
< 250-STARTTLS
< 250 ENHANCEDSTATUSCODES
> STARTTLS
< 220 2.0.0 Ready to start TLS
* successfully set certificate verify locations:
* CAfile: none
Code:
#include <stdio.h>
#include <string>
#include <cstring>
#include <curl/curl.h>
using namespace std;
/* This is a simple example showing how to send mail using libcurl's SMTP
* capabilities. It builds on the simplesmtp.c example, adding some
* authentication and transport security.
*/
#define FROM "<asktheprogramer2#gmail.com>"
#define CC "<asktheprogramer2#gmail.com>"
string ToAddress,strSubject, strMessage;
struct upload_status {
int lines_read;
};
static size_t payload_source(void *ptr, size_t size, size_t nmemb, void *userp)
{
const char *payload_text[]={ToAddress.c_str(),"From: " FROM "\n","Cc: " CC "\n",strSubject.c_str(),"\n", /* empty line to divide headers from body, see RFC5322 */
strMessage.c_str(),NULL};
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;
}
void sendMail(string strRecipent, string strAubject, string strBessage){
ToAddress="To: ";
ToAddress+=strRecipent;
ToAddress+="\n";
strSubject="Subject: ";
strSubject+=strAubject;
strSubject+="\n";
strMessage=strBessage;
CURL *curl;
CURLcode res;
struct curl_slist *recipients = NULL;
struct upload_status upload_ctx;
upload_ctx.lines_read = 0;
curl = curl_easy_init();
if (curl) {
/* This is the URL for your mailserver. Note the use of port 587 here,
* instead of the normal SMTP port (25). Port 587 is commonly used for
* secure mail submission (see RFC4403), but you should use whatever
* matches your server configuration. */
curl_easy_setopt(curl, CURLOPT_URL, "smtp://smtp.gmail.com:500");
/* In this example, we'll start with a plain text connection, and upgrade
* to Transport Layer Security (TLS) using the STARTTLS command. Be careful
* of using CURLUSESSL_TRY here, because if TLS upgrade fails, the transfer
* will continue anyway - see the security discussion in the libcurl
* tutorial for more details. */
curl_easy_setopt(curl, CURLOPT_USE_SSL, (long)CURLUSESSL_ALL);
/* If your server doesn't have a valid certificate, then you can disable
* part of the Transport Layer Security protection by setting the
* CURLOPT_SSL_VERIFYPEER and CURLOPT_SSL_VERIFYHOST options to 0 (false).
* curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
* curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
* That is, in general, a bad idea. It is still better than sending your
* authentication details in plain text though.
* Instead, you should get the issuer certificate (or the host certificate
* if the certificate is self-signed) and add it to the set of certificates
* that are known to libcurl using CURLOPT_CAINFO and/or CURLOPT_CAPATH. See
* docs/SSLCERTS for more information.
*/
//curl_easy_setopt(curl, CURLOPT_CAINFO, "/path/to/certificate.pem");
/* A common reason for requiring transport security is to protect
* authentication details (user names and passwords) from being "snooped"
* on the network. Here is how the user name and password are provided: */
curl_easy_setopt(curl, CURLOPT_USERNAME, "sim.dssprojects#gmail.com");
curl_easy_setopt(curl, CURLOPT_PASSWORD, "SIMpassword");
/* value for envelope reverse-path */
curl_easy_setopt(curl, CURLOPT_MAIL_FROM, 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, strRecipent.c_str());
recipients = curl_slist_append(recipients, CC);
curl_easy_setopt(curl, CURLOPT_MAIL_RCPT, recipients);
/* In this case, we're using a callback function to specify the data. 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);
/* Since the traffic will be encrypted, it is very useful to turn on debug
* information within libcurl to see what is happening during the transfer.
*/
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
/* send the message (including headers) */
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 and clean up */
curl_slist_free_all(recipients);
curl_easy_cleanup(curl);
}
}
int main(void)
{
int pid = fork();
if (pid == 0)
{
for(int i=0;i<2;i++)
{
sleep(10);
sendMail("<asktheprogramer#gmail.com>","ABE Reminder","You have an appt in 15 mins");
}
return 0;
}
else
{
sleep(30);
}
return 0;
}
A possibility would be to redirect the standard output and standard error. I am unsure if there is an equivalent mechanism for achieving this via C++ streams but you could use freopen():
freopen("stdout.txt", "w", stdout);
freopen("stderr.txt", "w", stderr);
You can redirect the output of your program when running it. Like this (assuming *nix environment):
./<yourprogram> > /dev/null
for the standard output.
./<yourprogram> &> /dev/null
for standard and error outputs.
Delete or comment out this line from the code:
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
I'm working on a program which will download lyrics from sites like AZLyrics. I'm using libcurl.
It's my code
lyricsDownloader.cpp
#include "lyricsDownloader.h"
#include <curl/curl.h>
#include <cstring>
#include <iostream>
#define DEBUG 1
/////////////////////////////////////////////////////////////////////////////
size_t lyricsDownloader::write_data_to_var(char *ptr, size_t size, size_t nmemb, void *userdata) // this function is a static member function
{
ostringstream * stream = (ostringstream*) userdata;
size_t count = size * nmemb;
stream->write(ptr, count);
return count;
}
string AZLyricsDownloader::toProviderCode() const
{ /*this creates an url*/ }
CURLcode AZLyricsDownloader::download()
{
CURL * handle;
CURLcode err;
ostringstream buff;
handle = curl_easy_init();
if (! handle) return static_cast<CURLcode>(-1);
// set verbose if debug on
curl_easy_setopt( handle, CURLOPT_VERBOSE, DEBUG );
curl_easy_setopt( handle, CURLOPT_URL, toProviderCode().c_str() ); // set the download url to the generated one
curl_easy_setopt(handle, CURLOPT_WRITEDATA, &buff);
curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, &AZLyricsDownloader::write_data_to_var);
err = curl_easy_perform(handle); // The segfault should be somewhere here - after calling the function but before it ends
cerr << "cleanup\n";
curl_easy_cleanup(handle);
// copy the contents to text variable
lyrics = buff.str();
return err;
}
main.cpp
#include <QString>
#include <QTextEdit>
#include <iostream>
#include "lyricsDownloader.h"
int main(int argc, char *argv[])
{
AZLyricsDownloader dl(argv[1], argv[2]);
dl.perform();
QTextEdit qtexted(QString::fromStdString(dl.lyrics));
cout << qPrintable(qtexted.toPlainText());
return 0;
}
When running
./maelyrica Anthrax Madhouse
I'm getting this logged from curl
* About to connect() to azlyrics.com port 80 (#0)
* Trying 174.142.163.250... * connected
* Connected to azlyrics.com (174.142.163.250) port 80 (#0)
> GET /lyrics/anthrax/madhouse.html HTTP/1.1
Host: azlyrics.com
Accept: */*
< HTTP/1.1 301 Moved Permanently
< Server: nginx/1.0.12
< Date: Thu, 05 Jul 2012 16:59:21 GMT
< Content-Type: text/html
< Content-Length: 185
< Connection: keep-alive
< Location: http://www.azlyrics.com/lyrics/anthrax/madhouse.html
<
Segmentation fault
Strangely, the file is there. The same error is displayed when there's no such page (redirect to azlyrics.com mainpage)
What am I doing wrong?
Thanks in advance
EDIT: I made the function for writing data static, but this changes nothing.
Even wget seems to have problems
$ wget http://www.azlyrics.com/lyrics/anthrax/madhouse.html
--2012-07-06 10:36:05-- http://www.azlyrics.com/lyrics/anthrax/madhouse.html
Resolving www.azlyrics.com... 174.142.163.250
Connecting to www.azlyrics.com|174.142.163.250|:80... connected.
HTTP request sent, awaiting response... No data received.
Retrying.
Why does opening the page in a browser work and wget/curl not?
EDIT2: After adding this:
curl_easy_setopt(handle, CURLOPT_FOLLOWLOCATION, 1);
And making the function static everything's OK.
Your code
curl_easy_setopt(handle,CURLOPT_WRITEFUNCTION,&AZLyricsDownloader::write_data_to_var);
and the following quote from the documentation from libcurl
There's basically only one thing to keep in mind when using C++
instead of C when interfacing libcurl:
The callbacks CANNOT be non-static class member functions
Example C++ code:
class AClass { static size_t write_data(void *ptr, size_t size, size_t nmemb, void* ourpointer) { /* do what you want with the data */ } }
could be the source of your problem as your function is not a static member. Even if not you are breaking this rule.
This may not solve your problem but given the amount of code you have posted in your example, that was the first thing that immediately came to mind and it is worth changing this as recommended by libcurl. If it does not solve your problem I would suggest identifying the error you are getting in more detail so that you can pose a more specific question next time (with a lot less code displayed).