curl / Mailing Lists / curl-library / Single Mail

curl-library

Re: Sending an email with an image via libcurl

From: Dan Fandrich <dan_at_coneharvesters.com>
Date: Sun, 26 Feb 2017 09:59:51 +0100

On Sat, Feb 25, 2017 at 10:48:33PM -0800, Nicholas Chambers wrote:
> Hello all! I'm writing a little thing which involves emailing an image after $x
> amount of time. To make sure the code works, I wanted to put it in a separate
> program and test it. Currently the code I have is attached as mail.c, and the
> image I am trying to send is purple.jpeg. The email sends, and I get it in my
> inbox, but the image fails to load. Could someone help me figure out why?
>
> Cheers,
> Nicholas Chambers
>
>
>

> #include <stdio.h>
> #include <string.h>
> #include <curl/curl.h>
>
> #define FROM "<dilbertd_at_spookyinternet.com>"
> #define TO "<nchambers_at_spookyinternet.com>"
>
> static const char *payload_text[] = {
> "To: " TO "\r\n",
> "From: " FROM "\r\n",
> "Subject: SMTPS Example\r\n",
> "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.

> "User-Agent: My eMail Client\r\n",
> "MIME-Version: 1.0\r\n",
> "Content-Type: multipart/mixed;\r\n",
> " boundary=\"------------030203080101020302070708\"\r\n",
> "\r\nThis is a multi-part message in MIME format.\r\n",
> "--------------030203080101020302070708\r\n",
> "Content-Type: text/plain; charset=utf-8; format=flowed\r\n",
> "Content-Transfer-Encoding: 7bit\r\n",
> "\r\n", /* empty line to divide headers from body, see RFC5322 */
> "The body of the message starts here.\r\n",
> "\r\n",
> "--------------030203080101020302070708\r\n",
> "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.

> "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.

> " filename=\"purple.jpeg\"\r\n",
> "\r\n",
> "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.

[...]
> "XPUfCNuI+MfCPjKXqB7Q8Z4Z4p4YF9OgqVKlfSEqVKlRlh8J6R8Z6x8Ohj0h4Q8IeEDAEMoEr9J+\r\n",
> "jv1cR5jGMIbhxOJ2nP6f//Z\r\n",
> "--------------030203080101020302070708--\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++;

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.

>
> 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://localhost:25");
>
> /* 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);
> 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;
> }
-------------------------------------------------------------------
Unsubscribe: https://cool.haxx.se/list/listinfo/curl-library
Etiquette: https://curl.haxx.se/mail/etiquette.html
Received on 2017-02-26