I have a c++ libcurl sending emails while TWS API is running. TWS API connects on port 4002. Periodically I send an email with libcurl and it holds the contens of some of the data sent from TWS API. I’m not sure what’s causing the overlap. This is my first time using libcurl.
string sender = "senderEmail@gmail.com";
string password = "password!";
string receiver1 = "otheremail@gmail.com";
string receiver2 = "5551234567@msg.fi.google.com";
string latestEmailSubject = "default Subject";
string latestEmailBody = "email Body";
size_t writeCallback(void* contents, size_t size, size_t nmemb, void* userp) {
// Simply discard the response for simplicity in this example
return size * nmemb;
}
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;
}
cout << "sending" << endl;
string senderLine = ("From: " + sender + "\r\n");
string receiverLine = "To: " + receiver1 + "\r\n";
string ccLine = "Cc: " + receiver2 + "\r\n";
string subjectLine = "Subject: " + latestEmailSubject + "\r\n";
string bodyLine = latestEmailBody + "\r\n";
string footerLine = "Sent from Accelerated Investing at " + StockThread::TimeToString(system_clock::now()) + "\r\n";
static const char* payload_text[] = {
"Date: Mon, 29 Nov 2023 21:54:29 +1100\r\n",
receiverLine.c_str(),
senderLine.c_str(),
ccLine.c_str(),
"Message-ID: <dcd7cb36-11db-487a-9f3a-e652a9458efd@"
"rfcpedant.example.org>\r\n",
subjectLine.c_str(),
"\r\n",
bodyLine.c_str(),
"\r\n",
footerLine.c_str(),
//"Check RFC5322.\r\n",
NULL
};
data = payload_text[upload_ctx->lines_read];
if (data) {
size_t len = strlen(data);
memcpy(ptr, data, len);
upload_ctx->lines_read++;
return len;
}
cout << "end of payload_source()" << endl;
return 0;
}
void Strategy::SendEmail(string subject, string message)
{
//Solution 1 from here: https://www.codeease.net/programming/c%2b%2b/how-to-send-email-in-c++-program
//followup: https://stackoverflow.com/questions/54778300/using-libcurl-to-send-an-email-in-c
CURL* curl = curl_easy_init();
struct upload_status upload_ctx;
upload_ctx.lines_read = 0;
if (curl)
{
latestEmailSubject = subject;
latestEmailBody = message;
// Set the SMTP server details
curl_easy_setopt(curl, CURLOPT_URL, "smtps://smtp.gmail.com:465");
curl_easy_setopt(curl, CURLOPT_USERNAME, "senderEmail@gmail.com");
//generated password copied from gmail
//go here to change them: https://myaccount.google.com/u/1/apppasswords?pli=1&rapt=AEjHL4M_GxOrC43lsW14JrCVFuwH5SuOBPthkSLYpA090srtteYJgTSiaN4WsvReFBGlCQKQJFRGZbGYhPOQ02dt0qBMD-T6WX-QBmcb6BqqpVRVhXWJyCc
curl_easy_setopt(curl, CURLOPT_PASSWORD, "1234 5678 90ab cdef");
// Set the sender's email address
curl_easy_setopt(curl, CURLOPT_MAIL_FROM, sender);
// Set the recipient's email address
struct curl_slist* recipients = NULL;
recipients = curl_slist_append(recipients, receiver1.c_str());
recipients = curl_slist_append(recipients, receiver2.c_str());
curl_easy_setopt(curl, CURLOPT_MAIL_RCPT, recipients);
// Set the email subject
// Set the email body
curl_easy_setopt(curl, CURLOPT_READFUNCTION, payload_source);
curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
curl_easy_setopt(curl, CURLOPT_READDATA, &upload_ctx);
// Set the callback function to handle the server response
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeCallback);
cout << "sending:" << endl;
cout << "sending subject:" << subject << endl;
cout << "sending body:" << message << endl;
// Perform the email sending operation
CURLcode res = curl_easy_perform(curl);
// Check for errors
if (res != CURLE_OK)
{
std::cerr << "curl_easy_perform() failed: " << curl_easy_strerror(res) << std::endl;
}
// Cleanup
curl_slist_free_all(recipients);
curl_easy_cleanup(curl);
cout << "end of SendEmail()" << endl;
}
}
The setup for TWS API is here:
const char* host = argc > 1 ? argv[1] : "";
int port = argc > 2 ? atoi(argv[2]) : 0;
if (port <= 0)
port = 4002;
string connectOptions = "";
int clientId = 0;
unsigned attempt = 0;
for (;;) {
Strategy client;
EClientSocket* socketpointer = client.m_pClient;
client.runningLiveTest = testOnlineOrders || exportingOnlineData;
if (!client.isConnected())
{
client.connect(host, port, clientId);
}
…
It also seems to send some string in a vector about to be sent to spdlog.
The issue was caused by dangling pointers in payload_source(). Thanks to @Retired_Ninja for the tip. A cleaner way to do this is this:
string sender = "senderEmail@gmail.com";
string password = "password!";
string receiver1 = "otheremail@gmail.com";
string receiver2 = "5551234567@msg.fi.google.com";
size_t writeCallback(void* contents, size_t size, size_t nmemb, void* userp) {
// Simply discard the response for simplicity in this example
return size * nmemb;
}
struct upload_status {
int lines_read;
size_t bytes_read;
vector<string> emailLines;
};
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;
size_t room = size * nmemb;
if ((size == 0) || (nmemb == 0) || ((size * nmemb) < 1)) {
return 0;
}
cout << "sending" << endl;
data = upload_ctx->emailLines[upload_ctx->lines_read].c_str();
if (data && upload_ctx->lines_read < upload_ctx->emailLines.size()) {
size_t len = strlen(data);
if (room < len)
len = room;
memcpy(ptr, data, len);
upload_ctx->lines_read++;
upload_ctx->bytes_read += len;
return len;
}
cout << "end of payload_source()" << endl;
return 0;
}
void Strategy::SendEmail(string subject, string message)
{
//Solution 1 from here: https://www.codeease.net/programming/c%2b%2b/how-to-send-email-in-c++-program
//followup: https://stackoverflow.com/questions/54778300/using-libcurl-to-send-an-email-in-c
CURL* curl = curl_easy_init();
struct upload_status upload_ctx;
upload_ctx.lines_read = 0;
if (curl)
{
string senderLine = ("From: " + sender + "\r\n");
string receiverLine = "To: " + receiver1 + "\r\n";
string ccLine = "Cc: " + receiver2 + "\r\n";
string subjectLine = "Subject: " + subject + "\r\n";
string bodyLine = message + "\r\n";
string footerLine = "Sent from Accelerated Investing at " + StockThread::TimeToString(system_clock::now()) + "\r\n";
upload_ctx.emailLines = {
"Date: Mon, 29 Nov 2010 21:54:29 +1100\r\n",
receiverLine,
senderLine,
ccLine,
"Message-ID: <dcd7cb36-11db-487a-9f3a-e652a9458efd@rfcpedant.example.org>\r\n",
subjectLine,
"\r\n",
bodyLine,
"\r\n",
footerLine
//"Check RFC5322.\r\n",
};
// Set the SMTP server details
curl_easy_setopt(curl, CURLOPT_URL, "smtps://smtp.gmail.com:465");
curl_easy_setopt(curl, CURLOPT_USERNAME, "senderEmail@gmail.com");
//generated password copied from gmail
//go here to change them: https://myaccount.google.com/u/1/apppasswords?pli=1&rapt=AEjHL4M_GxOrC43lsW14JrCVFuwH5SuOBPthkSLYpA090srtteYJgTSiaN4WsvReFBGlCQKQJFRGZbGYhPOQ02dt0qBMD-T6WX-QBmcb6BqqpVRVhXWJyCc
curl_easy_setopt(curl, CURLOPT_PASSWORD, "1234 5678 90ab cdef");
// Set the sender's email address
curl_easy_setopt(curl, CURLOPT_MAIL_FROM, sender);
// Set the recipient's email address
struct curl_slist* recipients = NULL;
recipients = curl_slist_append(recipients, receiver1.c_str());
recipients = curl_slist_append(recipients, receiver2.c_str());
curl_easy_setopt(curl, CURLOPT_MAIL_RCPT, recipients);
// Set the email subject
// Set the email body
curl_easy_setopt(curl, CURLOPT_READFUNCTION, payload_source);
curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
curl_easy_setopt(curl, CURLOPT_READDATA, &upload_ctx);
// Set the callback function to handle the server response
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeCallback);
cout << "sending:" << endl;
cout << "sending subject:" << subject << endl;
cout << "sending body:" << message << endl;
// Perform the email sending operation
CURLcode res = curl_easy_perform(curl);
// Check for errors
if (res != CURLE_OK)
{
std::cerr << "curl_easy_perform() failed: " << curl_easy_strerror(res) << std::endl;
}
// Cleanup
curl_slist_free_all(recipients);
curl_easy_cleanup(curl);
cout << "end of SendEmail()" << endl;
}
}