Sending an SMS message in C Recently I've been fiddling with the idea of sending a text message using C.  Looking at a few options, I thought that using libcurl to send an SMTP email using TLS for some security would be the best and easiest option.  Here's what I would like reviewed:


Method: Is there a better/easier way I could be sending an SMS?
Security: Is my current security enough? How could I add more security to make sure my authentication details aren't being snooped on?
SMS Format: Is there a way I could format my payload to look more like what it would if I sent it off using a normal email client?  The normal email client sends something that looks like the image on the left, with my first name being blacked out; the image on the right is what it looks like sent from my program with my email being censored.


&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;


Conversations: With my program, a new conversation is generated each time an SMS is sent, with the numbers at the top (950-9 in this case) being randomly chosen anew each time.  Could an improvement be made to the program so that each new SMS message stays in the same conversation?




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

typedef struct
{
    int lines_read;
} UploadStatus;

size_t payload_source(void *ptr, size_t size, size_t nmemb, void *userp)
{
    if((size == 0) || (nmemb == 0) || ((size * nmemb) &lt; 1)) return 0;

    UploadStatus *upload = (UploadStatus*) userp;
    const char *payload[] =
    {
        "Subject: SMS Test\r\n",
        "\r\n", /* empty line to divide headers from body, see RFC5322 */
        "Test",
        NULL
    };
    const char *data = payload[upload-&gt;lines_read];

    if(data)
    {
        size_t len = strlen(data);
        memcpy(ptr, data, len);
        upload-&gt;lines_read++;
        return len;
    }
    return 0;
}

int main(void)
{
    CURLcode res = CURLE_OK;
    struct curl_slist *recipients = NULL;
    UploadStatus upload_ctx = {0};
    const char *from = "test@gmail.com";
    const char *to = "UrTxtEmail"; // see http://www.emailtextmessages.com/
    CURL *curl = curl_easy_init();

    if(curl)
    {
        // set username and password
        curl_easy_setopt(curl, CURLOPT_USERNAME, from);
        curl_easy_setopt(curl, CURLOPT_PASSWORD, "password");
        curl_easy_setopt(curl, CURLOPT_URL, "smtp://smtp.gmail.com:587/");
        // start with normal connection, and upgrade to TLS using STARTTLS
        curl_easy_setopt(curl, CURLOPT_USE_SSL, (long) CURLUSESSL_ALL);
        curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
        curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
        curl_easy_setopt(curl, CURLOPT_MAIL_FROM, from);
        recipients = curl_slist_append(recipients, to);
        curl_easy_setopt(curl, CURLOPT_MAIL_RCPT, recipients);
        curl_easy_setopt(curl, CURLOPT_READFUNCTION, payload_source);
        curl_easy_setopt(curl, CURLOPT_READDATA, &amp;upload_ctx);
        curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
        // useful for debugging encryped traffic
        curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);

        // send the message
        res = curl_easy_perform(curl);
        if(res != CURLE_OK) fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
        curl_slist_free_all(recipients);
        curl_easy_cleanup(curl);
    }

    return (int) res;
}
A few notes:


First off, your UploadStatus structure only contains one variable, which is somewhat useless IMO.  Instead, I would make some sort of email structure that contains all the necessary parts of an email in order for your program to send it off.  Here's a structure that I came up with to use:

 typedef struct
 {
     const char* to;
     const char* from;
     const char** email;
     size_t pos;
 } EmailData;


You may note that I changed your lines_read to pos and made it a size_t since I doubt you will ever read a negative number of lines.
The payload_source() method looks to be your CURLOPT_READFUNCTION, but it doesn't have the right format.  It should look like this prototype according to the documentation:

size_t read_callback(char *buffer, size_t size, size_t nitems, void *instream);


Also, the current name of your function doesn't really convey to me that it is a callback when reading your code.  To aid readability, I would change it to something such as readCallback().
Right now you have your email hardcoded within your code... something I would definitely look to change to be more dynamic.  I would suggest creating a sort of message constructing function that would fulfill such a purpose, store it in your EmailData struct, and then pass that structure to your callback function as the instream parameter.  
To fix your formatting I would look at the original HTML email that is sent from a Gmail client and then dissect that.  Here is an example of one that I sent off:


MIME-Version: 1.0
Received: by 10.37.194.194 with HTTP; Tue, 11 Aug 2015 18:42:25 -0700 (PDT)
Date: Tue, 11 Aug 2015 20:42:25 -0500
Delivered-To: person@gmail.com
Message-ID: &lt;CJYf78Vnxbma4wdV1mZuBXHPeEptOzyjH1vLMzLwnhoXsPw@mail.gmail.com&gt;
Subject: Gmail Test
From: syb0rg &lt;person@gmail.com&gt;
To: 5555555555@tmomail.net
Content-Type: multipart/alternative; boundary=001a11c04f6ca3e68e051d1354cc

--001a11c04f6ca3e68e051d1354cc
Content-Type: text/plain; charset=UTF-8

Body content

--001a11c04f6ca3e68e051d1354cc
Content-Type: text/html; charset=UTF-8

&lt;div dir="ltr"&gt;Body content&lt;/div&gt;

--001a11c04f6ca3e68e051d1354cc--



The actual dissection of each line of this email is done in my final code with comments




Final Code

Please note that this code leaks memory and doesn't contain a unique ID generation method.

#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;string.h&gt;
#include &lt;time.h&gt;
#include &lt;curl/curl.h&gt;

typedef struct
{
    const char* to;
    const char* from;
    const char** email;
    size_t pos;
} EmailData;

EmailData* initEmailData(EmailData* data, const char* to, const char* from, const char* subject, const char* input)
{
    data-&gt;to = to;
    data-&gt;from = from;

    // the added numbers to the length of the string are the pre-computed
    // lengths of the strings to be concatenated + 1 (for termination char)
    size_t htmlSize = strlen(input) + 24;
    char *html = malloc(htmlSize);
    snprintf(html, htmlSize, "%s%s%s", "&lt;div dir=\"ltr\"&gt;", input, "&lt;/div&gt;\r\n");

    size_t headerSize = strlen(subject) + 12;
    char *header = malloc(1);
    snprintf(header, headerSize, "%s%s%s", "Subject: ", subject, "\r\n");

    size_t receiverSize = strlen(to) + 7;
    char *receiver = malloc(receiverSize);
    snprintf(receiver, receiverSize, "%s%s%s", "To: ", to, "\r\n");

    size_t senderSize = strlen(from) + 9;
    char *sender = malloc(senderSize);
    snprintf(sender, senderSize, "%s%s%s", "From: ", from, "\r\n");

    size_t timeSize = 40;
    char* timeString = malloc(timeSize);
    time_t rawtime = 0;
    time(&amp;rawtime);
    struct tm *timeinfo = localtime(&amp;rawtime);
    strftime (timeString, timeSize, "Date: %a, %d %b %Y %T %z\r\n", timeinfo);

    const char **payload = malloc(sizeof(char*) * 17);
    const char* setup[] =
    {
        // extends the format of email to support more data (vidoes, audio, etc)
        "MIME-Version: 1.0\r\n",
        // when the email was sent
        timeString,
        // who the email is to
        // TODO: multiple recipient support
        receiver,
        // who the email is from
        sender,
        // https://en.wikipedia.org/wiki/Message-ID
        //"Message-ID: &lt;CJYf78Vnxbma4dV1mZuBpXPeEptOzyjHvLMBzLwnhoXsPw@mail.gmail.com&gt;\r\n"
        // email subject, optional
        header,
        // describe the data contained in the body
        // https://tools.ietf.org/html/rfc2046#section-5.1.1
        "Content-Type: multipart/alternative; boundary=border\r\n",
        // empty line to divide headers from body, see RFC5322
        "\r\n",
        // all boundaries must start with two hyphens
        "--border\r\n",
        // let recipient interpret displaying text, send plain text &amp; HTML
        // specify the plain text encoding
        "Content-Type: text/plain; charset=UTF-8\r\n",
        "\r\n",
        input,
        "\r\n",
        "--border\r\n",
        // specify the HTML encoding
        "Content-Type: text/html; charset=UTF-8\r\n",
        "\r\n",
        html,
        "\r\n",
        // final boundary ends with two hyphens as well
        "--border--\r\n",
        NULL
    };
    for (int i = 0; setup[i]; i++)
    {
        *(payload + i) = setup[i];
    }
    data-&gt;email = payload;
    data-&gt;pos = 0;
    return data;
}

size_t readCallback(char *ptr, size_t size, size_t nmemb, void *userp)
{
    if((size == 0) || (nmemb == 0) || ((size * nmemb) &lt; 1)) return 0;

    EmailData *upload = (EmailData*) userp;
    const char *email = upload-&gt;email[upload-&gt;pos];

    if(email)
    {
        size_t len = strlen(email);
        if (len &gt; size * nmemb) return CURL_READFUNC_ABORT;
        memcpy(ptr, email, len);
        upload-&gt;pos++;
        return len;
    }
    return 0;
}

int main(void)
{
    CURLcode res = CURLE_OK;
    struct curl_slist *recipients = NULL;
    const char *from = "person@gmail.com";
    const char *password = "password";
    const char *to = "5555555555@tmomail.net"; // see http://www.emailtextmessages.com/
    EmailData data = {0};
    initEmailData(&amp;data, to, from, "Test Subject", "Test content");
    CURL *curl = curl_easy_init();

    if (curl)
    {
        // set username and password
        curl_easy_setopt(curl, CURLOPT_USERNAME, from);
        curl_easy_setopt(curl, CURLOPT_PASSWORD, password);
        curl_easy_setopt(curl, CURLOPT_URL, "smtp://smtp.gmail.com:587/");
        // start with normal connection, and upgrade to TLS using STARTTLS
        curl_easy_setopt(curl, CURLOPT_USE_SSL, (long) CURLUSESSL_ALL);
        curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
        curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
        curl_easy_setopt(curl, CURLOPT_MAIL_FROM, from);
        recipients = curl_slist_append(recipients, to);
        curl_easy_setopt(curl, CURLOPT_MAIL_RCPT, recipients);
        curl_easy_setopt(curl, CURLOPT_READFUNCTION, readCallback);
        curl_easy_setopt(curl, CURLOPT_READDATA, &amp;data);
        curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
        // useful for debugging encryped traffic
        curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);

        // send the message
        res = curl_easy_perform(curl);
        if(res != CURLE_OK) fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
        curl_slist_free_all(recipients);
        curl_easy_cleanup(curl);
    }

    return (int) res;
}