curl / Mailing Lists / curl-library / Single Mail

curl-library

Re: Sending an email with an image via libcurl

From: Nicholas Chambers <nchambers_at_spookyinternet.com>
Date: Sun, 26 Feb 2017 01:29:48 -0800

Thanks Dan. I've made the changes you suggested. Good call about the date. I'll make sure to fix that. (Also, I am wanting it inline, so I removed that). In the actual code, I'll have it send the image in a better way like what you recommended, but will leave it alone for now since this program is more focused around the image. I tried running it again, and am still seeing the behavior. I tried removing the \r\n's from the image base64 encoding, but still no dice. I verified the image is valid by decoding my base64 encoded string and got back the image I was expecting. New code is attached.

---- On Sun, 26 Feb 2017 00:59:51 -0800 Dan Fandrich &lt;dan_at_coneharvesters.com&gt; wrote ----

On Sat, Feb 25, 2017 at 10:48:33PM -0800, Nicholas Chambers wrote:

&gt; Hello all! I'm writing a little thing which involves emailing an image after $x

&gt; amount of time. To make sure the code works, I wanted to put it in a separate

&gt; program and test it. Currently the code I have is attached as mail.c, and the

&gt; image I am trying to send is purple.jpeg. The email sends, and I get it in my

&gt; inbox, but the image fails to load. Could someone help me figure out why?

&gt;

&gt; Cheers,

&gt; Nicholas Chambers

&gt;

&gt;

&gt;

&gt; #include &lt;stdio.h&gt;

&gt; #include &lt;string.h&gt;

&gt; #include &lt;curl/curl.h&gt;

&gt;

&gt; #define FROM "&lt;dilbertd_at_spookyinternet.com&gt;"

&gt; #define TO "&lt;nchambers_at_spookyinternet.com&gt;"

&gt;

&gt; static const char *payload_text[] = {

&gt; "To: " TO "\r\n",

&gt; "From: " FROM "\r\n",

&gt; "Subject: SMTPS Example\r\n",

&gt; "Date: Mon, 29 Nov 2010 21:54:29 +1100\r\n",

Naturally, you'll want to set this dynamically, since an old date will get lost

in people's in-boxes and old dates are used by some systems as a spam indicator.

&gt; "User-Agent: My eMail Client\r\n",

&gt; "MIME-Version: 1.0\r\n",

&gt; "Content-Type: multipart/mixed;\r\n",

&gt; " boundary=\"------------030203080101020302070708\"\r\n",

&gt; "\r\nThis is a multi-part message in MIME format.\r\n",

&gt; "--------------030203080101020302070708\r\n",

&gt; "Content-Type: text/plain; charset=utf-8; format=flowed\r\n",

&gt; "Content-Transfer-Encoding: 7bit\r\n",

&gt; "\r\n", /* empty line to divide headers from body, see RFC5322 */

&gt; "The body of the message starts here.\r\n",

&gt; "\r\n",

&gt; "--------------030203080101020302070708\r\n",

&gt; "Content-Type: text/jpeg; charset=utf-8; format=flowed\r\n",

You want image/jpeg here. And charset and format are meaningless for that type.

&gt; "Content-Disposition: attachment;\r\n",

If you use this, then the image won't be shown inline in most e-mail clients.

Maybe that's what you want and maybe not.

&gt; " filename=\"purple.jpeg\"\r\n",

&gt; "\r\n",

&gt; "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAYEBQYFBAYGBQYHBwYIC\r\n",

This header is only needed for the data: URL scheme. It's not a part of

image/jpeg data and needs to be removed.

[...]

&gt; "XPUfCNuI+MfCPjKXqB7Q8Z4Z4p4YF9OgqVKlfSEqVKlRlh8J6R8Z6x8Ohj0h4Q8IeEDAEMoEr9J+\r\n",

&gt; "jv1cR5jGMIbhxOJ2nP6f//Z\r\n",

&gt; "--------------030203080101020302070708--\r\n",

&gt; NULL

&gt; };

&gt;

&gt; struct upload_status {

&gt; int lines_read;

&gt; };

&gt;

&gt; static size_t payload_source(void *ptr, size_t size, size_t nmemb, void *userp)

&gt; {

&gt; struct upload_status *upload_ctx = (struct upload_status *)userp;

&gt; const char *data;

&gt;

&gt; if((size == 0) || (nmemb == 0) || ((size*nmemb) &lt; 1)) {

&gt; return 0;

&gt; }

&gt;

&gt; data = payload_text[upload_ctx-&gt;lines_read];

&gt;

&gt; if(data) {

&gt; size_t len = strlen(data);

&gt; memcpy(ptr, data, len);

&gt; upload_ctx-&gt;lines_read++;

Sending data line-by-line works, but it's pretty inefficient. You could likely

send the entirety of the message in a single callback. Rather than storing the

message as an array of strings, I'd store it as a single string and send

(size*nmemb) bytes of that string each time until it's all sent.

&gt;

&gt; return len;

&gt; }

&gt;

&gt; return 0;

&gt; }

&gt;

&gt; int main(void)

&gt; {

&gt; CURL *curl;

&gt; CURLcode res = CURLE_OK;

&gt; struct curl_slist *recipients = NULL;

&gt; struct upload_status upload_ctx;

&gt;

&gt; upload_ctx.lines_read = 0;

&gt;

&gt; curl = curl_easy_init();

&gt; if(curl) {

&gt; /* This is the URL for your mailserver */

&gt; curl_easy_setopt(curl, CURLOPT_URL, "smtp://localhost:25");

&gt;

&gt; /* Note that this option isn't strictly required, omitting it will result

&gt; * in libcurl sending the MAIL FROM command with empty sender data. All

&gt; * autoresponses should have an empty reverse-path, and should be directed

&gt; * to the address in the reverse-path which triggered them. Otherwise,

&gt; * they could cause an endless loop. See RFC 5321 Section 4.5.5 for more

&gt; * details.

&gt; */

&gt; curl_easy_setopt(curl, CURLOPT_MAIL_FROM, FROM);

&gt;

&gt; /* Add two recipients, in this particular case they correspond to the

&gt; * To: and Cc: addressees in the header, but they could be any kind of

&gt; * recipient. */

&gt; recipients = curl_slist_append(recipients, TO);

&gt; curl_easy_setopt(curl, CURLOPT_MAIL_RCPT, recipients);

&gt;

&gt; /* We're using a callback function to specify the payload (the headers and

&gt; * body of the message). You could just use the CURLOPT_READDATA option to

&gt; * specify a FILE pointer to read from. */

&gt; curl_easy_setopt(curl, CURLOPT_READFUNCTION, payload_source);

&gt; curl_easy_setopt(curl, CURLOPT_READDATA, &amp;upload_ctx);

&gt; curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);

&gt;

&gt; /* Send the message */

&gt; res = curl_easy_perform(curl);

&gt;

&gt; /* Check for errors */

&gt; if(res != CURLE_OK)

&gt; fprintf(stderr, "curl_easy_perform() failed: %s\n",

&gt; curl_easy_strerror(res));

&gt;

&gt; /* Free the list of recipients */

&gt; curl_slist_free_all(recipients);

&gt;

&gt; /* curl won't send the QUIT command until you call cleanup, so you should

&gt; * be able to re-use this connection for additional messages (setting

&gt; * CURLOPT_MAIL_FROM and CURLOPT_MAIL_RCPT as required, and calling

&gt; * curl_easy_perform() again. It may not be a good idea to keep the

&gt; * connection open for a very long time though (more than a few minutes

&gt; * may result in the server timing out the connection), and you do want to

&gt; * clean up in the end.

&gt; */

&gt; curl_easy_cleanup(curl);

&gt; }

&gt;

&gt; return (int)res;

&gt; }

-------------------------------------------------------------------

Unsubscribe: https://cool.haxx.se/list/listinfo/curl-library

Etiquette: https://curl.haxx.se/mail/etiquette.html

-------------------------------------------------------------------
Unsubscribe: https://cool.haxx.se/list/listinfo/curl-library
Etiquette: https://curl.haxx.se/mail/etiquette.html

  • application/octet-stream attachment: mail.c
Received on 2017-02-26