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.
Related
I have searched here, but did not find anything that matched. What I have tested with curl.exe and that works fine. I then tried a small test program. Created a commandline Win32 project in VS2017 and copied in the code from simplessl.c. Problem is that no matter what certificates I try I always get CURLE_SSL_CERTPROBLEM. This points to the client certificate being wrong. I even tried with the 2048 bit certificate and key from here: https://fm4dd.com/openssl/certexamples.shtm Not sure how to troubleshoot.
The test program I used:
#include "pch.h"
#include <iostream>
#include <curl/curl.h>
int main()
{
std::cout << "Hello World!\n";
CURL *curl;
CURLcode res;
FILE *headerfile;
const char *pPassphrase = NULL;
static const char *pCertFile = "C:\\Prog\\TestCurl\\2048b-rsa-example-cert.pem";
static const char *pCACertFile = "cacert.pem";
static const char *pHeaderFile = "C:\\Prog\\TestCurl\\requestWith.txt";
const char *pKeyName;
const char *pKeyType;
const char *pEngine;
#ifdef USE_ENGINE
pKeyName = "rsa_test";
pKeyType = "ENG";
pEngine = "chil"; /* for nChiper HSM... */
#else
pKeyName = "C:\\Prog\\TestCurl\\2048b-rsa-example-keypair.pem";
pKeyType = "PEM";
pEngine = NULL;
#endif
headerfile = fopen(pHeaderFile, "wb");
curl_global_init(CURL_GLOBAL_DEFAULT);
curl = curl_easy_init();
if (curl) {
/* what call to write: */
//curl_easy_setopt(curl, CURLOPT_URL, "https://pintatesti.vero.fi/FIS/Return/IIT/Test/GetWithholdingPercentage/v1");
curl_easy_setopt(curl, CURLOPT_URL, "https://www.pedago.fi");
curl_easy_setopt(curl, CURLOPT_HEADERDATA, headerfile);
do { /* dummy loop, just to break out from */
if (pEngine) {
/* use crypto engine */
if (curl_easy_setopt(curl, CURLOPT_SSLENGINE, pEngine) != CURLE_OK) {
/* load the crypto engine */
fprintf(stderr, "can't set crypto engine\n");
break;
}
if (curl_easy_setopt(curl, CURLOPT_SSLENGINE_DEFAULT, 1L) != CURLE_OK) {
/* set the crypto engine as default */
/* only needed for the first time you load
a engine in a curl object... */
fprintf(stderr, "can't set crypto engine as default\n");
break;
}
}
/* cert is stored PEM coded in file... */
/* since PEM is default, we needn't set it for PEM */
curl_easy_setopt(curl, CURLOPT_SSLCERTTYPE, "PEM");
/* set the cert for client authentication */
curl_easy_setopt(curl, CURLOPT_SSLCERT, pCertFile);
/* sorry, for engine we must set the passphrase
(if the key has one...) */
if (pPassphrase)
curl_easy_setopt(curl, CURLOPT_KEYPASSWD, pPassphrase);
/* if we use a key stored in a crypto engine,
we must set the key type to "ENG" */
curl_easy_setopt(curl, CURLOPT_SSLKEYTYPE, pKeyType);
/* set the private key (file or ID in engine) */
curl_easy_setopt(curl, CURLOPT_SSLKEY, pKeyName);
/* set the file with the certs vaildating the server */
//curl_easy_setopt(curl, CURLOPT_CAINFO, pCACertFile);
/* disconnect if we can't validate server's cert */
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
/* Perform the request, res will get the return code */
res = curl_easy_perform(curl);
/* Check for errors */
if (res != CURLE_OK)
fprintf(stderr, "curl_easy_perform() failed: %s\n",
curl_easy_strerror(res));
/* we are done... */
} while (0);
/* always cleanup */
curl_easy_cleanup(curl);
}
curl_global_cleanup();
return 0;
}
Edit: Underlying error I get is " schannel: certificate format compatibility error" and only occurrence I can find is in schannel.c where it compares to a P12 type, but my certs are PEM?
Edit2: So it does work if I import the certificate into the local store, something I would definitely want to avoid! Also curl_easy_setopt(curl, CURLOPT_VERBOSE, 1); appears to be avery good idea! Now getting further.
Edit3: Got it working. Possibly related to: Having trouble sending client certificate in libcurl ssl request, what am I missing? which is from 2019. I would very much prefer to have the certificates in a file though.
Edit4: "libcurl/7.79.1 Schannel WinIDN"
I solved this with a little help from my friends. Libcurl when compiled as default on Windows will default to schannel. To override that you need to build for OpenSSL, thusly: nmake /f Makefile.vc mode=static DEBUG=yes WITH_SSL=static WITH_DEVEL=C:\Prog\OpenSSL-Win32_111L\. The important keywords are WITH_SSL=static or dynamic and WITH_DEVEL= path to where your OpenSSL files live. When built this way you can give file paths for certificate and key, ie: curl_easy_setopt(curl, CURLOPT_SSLCERT, m_Certificate.GetString());
This frees you of the messy certificate handling in Windows and lets you keep the certificates with the app itself.
I try to send an email using the following code :
#include "optimnet_mail.h"
#include <cstring>
struct upload_status {
int lines_read;
};
static size_t payload_source(char *ptr, size_t size, size_t nmemb, void *userp){
char *data = static_cast<char*>(userp);
if((size == 0) || (nmemb == 0) || ((size*nmemb) < 1)) {
return 0;
}
size_t len = strlen(data);
memcpy(ptr, data, len);
return len;
}
std::string mailText(const std::string destination, const std::string content){
const std::string stringResult =
"To: " + destination +
"\r\nFrom: " + FROM +
"\r\nSubject: Solver has finished\r\n" +
"\r\n" + content + "\r\n";
return stringResult;
}
int sendMail(const std::string destination, const std::string content){
const std::string text = mailText(destination, content);
char *data = new char [text.length()+1];
std::strcpy (data,text.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) {
/* Set username and password */
curl_easy_setopt(curl, CURLOPT_USERNAME, FROM);
curl_easy_setopt(curl, CURLOPT_PASSWORD, PASS);
//using smtps fails instantly
curl_easy_setopt(curl, CURLOPT_URL, "smtps://" HOST ":" PORT);
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);
//curl_easy_setopt(curl, CURLOPT_CAINFO, "~/.ssh/known_hosts");
curl_easy_setopt(curl, CURLOPT_MAIL_FROM, FROM);
// Add two recipients
recipients = curl_slist_append(recipients, destination.c_str());
curl_easy_setopt(curl, CURLOPT_MAIL_RCPT, recipients);
//upload data
curl_easy_setopt(curl, CURLOPT_READFUNCTION, payload_source);
curl_easy_setopt(curl, CURLOPT_READDATA, data);
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);
}
delete data;
return (int)res;
}
int main(){
return sendMail(std::string("myemail#mycompany.com"),std::string("Test2"));
}
Unfortunately, executing this code gives me the following log :
* Trying 83.166.143.44:465...
* TCP_NODELAY set
* Connected to mail.infomaniak.ch (83.166.143.44) port 465 (#0)
* successfully set certificate verify locations:
* CAfile: /etc/ssl/certs/ca-certificates.crt
CApath: /etc/ssl/certs
* SSL connection using TLSv1.2 / ECDHE-RSA-AES256-GCM-SHA384
* Server certificate:
* subject: CN=mail.infomaniak.ch
* start date: Feb 17 22:44:47 2021 GMT
* expire date: May 18 22:44:47 2021 GMT
* subjectAltName: host "mail.infomaniak.ch" matched cert's "mail.infomaniak.ch"
* issuer: C=US; O=Let's Encrypt; CN=R3
* SSL certificate verify ok.
< 220 mail.infomaniak.com ESMTP ready
> EHLO conway
< 250-mail.infomaniak.com
< 250-PIPELINING
< 250-SIZE
< 250-ETRN
< 250-ENHANCEDSTATUSCODES
< 250-8BITMIME
< 250-DSN
< 250 AUTH PLAIN LOGIN
> AUTH PLAIN
< 334
> Some hash
< 535 5.7.0 Invalid login or password
* Closing connection 0
curl_easy_perform() failed: Login denied
On the other hand, when using this curl command :
curl --ssl-reqd --url 'smtps://mail.infomaniak.ch' --user 'user#example.com:password' --mail-from 'user#example.com' --mail-rcpt 'myemail#mycompany.com' --upload-file mail.txt, the email is indeed sent.
Is there something I missed in my code? I don't understand why using the exact same login and password would make the authentication fail.
Edit : thanks to rustyx and Sam Varshavchik, I corrected a problem of port and a problem of memory leak.
The problem was that there is a difference between the host, that looks like <myemail#mycompany.com> and the user, that looks like myemail#mycompany.com. This is what was causing authentication failure.
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 am having trouble using the curl library inside a c++ program. I copied the curl folder directly to the Microsoft Visual Studio 12.0\VC\crt\src folder. I am using a test program directly from Curl's website so there should not be any problem there. However it does give me some unresolved external symbols which looks like the library is not loading correctly. I am using visual studio 12.0 express.
`/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel#haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at http://curl.haxx.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
***************************************************************************/
#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. It builds on the smtp-mail.c example to add authentication
* and, more importantly, transport security to protect the authentication
* details from being snooped.
*
* 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 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");
/* 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://mainserver.example.net: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");
/* 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);
/* 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 */
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;
}`
I suppose you are getting linking error unresolved external symbols for functions like curl_global_init and other curl functions.
To get rid of these errors, you have to compile curl library and link .lib with the above test application
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);