0

Im trying to send an email using curl c++, i managed to log in well and when i run the program it works fine, does not throw any error, but the email never comes.

This is my code:

#include <iostream>
#include <curl/curl.h>

static const char *payload_text =
  "Date: Mon, 29 Nov 2010 21:54:29 +1100\r\n"
  "To: " "mailto" "\r\n"
  "From: " "mymail" "\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";

size_t read_function(char *buffer, size_t size, size_t nmemb,char *data)
{
    size_t len;
    if(size == 0 or nmemb == 0)
    {
        return 0;
    }
    if(data)
    {
        len = strlen(data);
        memcpy(buffer, data, len);
        return len;
    }
    return 0;  
}

int main()
{
    CURL *curl;
    CURLcode res = CURLE_OK;
    const char *data = payload_text;
 
    curl = curl_easy_init();
    if(curl) 
    {
        curl_easy_setopt(curl, CURLOPT_USERNAME, "mymail"); 
        curl_easy_setopt(curl, CURLOPT_PASSWORD, "password");
        curl_easy_setopt(curl, CURLOPT_URL, "smtp://smtp.gmail.com:587");
        curl_easy_setopt(curl, CURLOPT_MAIL_FROM, "my mail");
        curl_easy_setopt(curl, CURLOPT_MAIL_RCPT, "mailto");
        curl_easy_setopt(curl, CURLOPT_READDATA,payload_text);
        curl_easy_setopt(curl, CURLOPT_READFUNCTION, read_function);
        curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
        curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
        curl_easy_setopt(curl, CURLOPT_USE_SSL, CURLUSESSL_ALL);
    }
    res = curl_easy_perform(curl);
    
    if(res != CURLE_OK)
    {
        fprintf(stderr, "curl_easy_perform() failed: %s\n",
        curl_easy_strerror(res));
        curl_easy_cleanup(curl);
    }
    return 0;
}

I think the problem is in the curl options READDATA and READUNCTION. In the documentation says that you have to pass as an argument to READDATA a data pointer. const char *data = payload_text; is the data pointer, right?

then READFUNCTION takes as an argument a function which return the size of the data and i think that is what size_t read_function(char *buffer, size_t size, size_t nmemb,char *data) is doing.

I am new in this so any advice would be good for me.

4
  • Well, not sure, if that could rather a problem with your network configuration and firewalls Commented Jun 16, 2022 at 14:29
  • i dont think it is a problem with that, if that was the case it would throw an error Commented Jun 16, 2022 at 14:37
  • You need to fix the read_function and also look up tutorials on how to set CURLOPT_MAIL_RCPT: it is a "slist" since there can be multiple recipients, not just a pointer to a string. This is probably a good starting point: curl.se/libcurl/c/smtp-mail.html Commented Jun 16, 2022 at 16:22
  • In the old days sending e-mail like this easy (and this resulted in spam farms). Nowadays, mail servers are highly locked down and only take mail from trusted sources. You may be able to send to a local mail server you run (but if that mail server can forward the mail may be another question). Commented Jun 16, 2022 at 18:31

2 Answers 2

2

I found this to be a helpful starting point: https://curl.se/libcurl/c/smtp-mail.html

There are two main problems with your code:

  1. Your read_function didn't keep track of how much of the payload has been read so it would keep giving the same content to libcurl over and over and never signal the end of the message.
  2. You were setting CURLOPT_MAIL_RCPT to a string when in fact it should be a struct curl_slist * because there can be multiple recipients.

Here is a fixed example that I tested on my computer and it worked. Private data at the top of the file was modified before posting.

#define USERNAME "david"
#define PASSWORD "xxxxx"
#define MAILTO "[email protected]"
#define MAILFROM "[email protected]"
#define SMTP "smtp://your.smtp.server.example.com:25"

#include <stdio.h>
#include <curl/curl.h>

const char * payload_text =
  "Date: Mon, 29 Nov 2010 21:54:29 +1100\r\n"
  "To: " MAILTO "\r\n"
  "From: " MAILFROM "\r\n"
  "Subject: SMTP example message with libcurl 6\r\n"
  "\r\n"
  "Hello world!\r\n";

struct ReadData
{
  explicit ReadData(const char * str)
  {
    source = str;
    size = strlen(str);
  }

  const char * source;
  size_t size;
};

size_t read_function(char * buffer, size_t size, size_t nitems, ReadData * data)
{
  size_t len = size * nitems;
  if (len > data->size) { len = data->size; }
  memcpy(buffer, data->source, len);
  data->source += len;
  data->size -= len;
  return len;
}

int main()
{
  CURL * curl = curl_easy_init();
  if (!curl)
  {
    fprintf(stderr, "curl_easy_init failed\n");
    return 1;
  }

  curl_easy_setopt(curl, CURLOPT_USERNAME, USERNAME);
  curl_easy_setopt(curl, CURLOPT_PASSWORD, PASSWORD);
  curl_easy_setopt(curl, CURLOPT_URL, SMTP);
  curl_easy_setopt(curl, CURLOPT_MAIL_FROM, MAILFROM);

  struct curl_slist * rcpt = NULL;
  rcpt = curl_slist_append(rcpt, MAILTO);
  curl_easy_setopt(curl, CURLOPT_MAIL_RCPT, rcpt);

  ReadData data(payload_text);
  curl_easy_setopt(curl, CURLOPT_READDATA, &data);
  curl_easy_setopt(curl, CURLOPT_READFUNCTION, read_function);

  curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);
  curl_easy_setopt(curl, CURLOPT_UPLOAD, 1);
  curl_easy_setopt(curl, CURLOPT_USE_SSL, CURLUSESSL_ALL);

  // If your server doesn't have a proper SSL certificate:
  curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0);

  CURLcode res = curl_easy_perform(curl);
  if (res != CURLE_OK)
  {
    fprintf(stderr, "curl_easy_perform() failed: %s\n",
      curl_easy_strerror(res));
    curl_easy_cleanup(curl);
  }
  return 0;
}
Sign up to request clarification or add additional context in comments.

5 Comments

I wonder if the C++ standard library has a class that would be suitable for me to use so I wouldn't have to invent the ReadData class. Basically something like a std::string but where it is efficient to remove data from the beginning. Or something that refers to a position within a string and knows where the string ends.
std::deque or std::stringstream are suitable for such purposes. The first one allows efficiently to remove data from the beginning, the second one just tracks read data.
thanks, this worked but i dont understand why you do data->source += len; data->size -= len; source is a const char and you are adding up a size_t?
This is my way or removing len bytes from the beginning of the string that ReadData describes. My ReadData object is not const so I can modify it. I must remove these bytes because they were already transferred to libcurl and I don't want to transfer them again. If you are programming in C++ you should learn how pointer arithmetic works (adding a pointer to an integer).
Also note that source is not a const char, it is a pointer to a const char, also written as const char *. Speaking precisely when dealing with code is important.
1

read_function: you are changing the local pointer buffer that does not affect the byte buffer of a caller. You should copy data to the pointed buffer

memcpy(buffer, data, len);

FYI sizeof(char) is guaranteed to be 1, thus is unneeded.

Another issue - the function never returns 0 that signals all data is sent, it sends the same data again and again. You should return 0 on a second call. Or set the length to the option CURLOPT_POSTFIELDSIZE_LARGE.

See the example smtp-mail.c

8 Comments

I updated the code but it still doesnt work
This is another issue. See the update.
i did what you said, still doesnt work, updated the code again.
No, you did not, still return len; infinitely. See the read function example in the reference.
i changed the port to 465, now the error is * response reading failed * Closing connection 0 curl_easy_perform() failed: Failure when receiving data from the peer .
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.