c++emaillibcurltwsspdlog

How to make libcurl work with other web connected libraries


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.


Solution

  • 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;
        }
    
    }