HiveBrain v1.2.0
Get Started
← Back to all entries
patterncMinor

Sending an SMS message in C

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
messagesendingsms

Problem

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.



        

  • 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
#include
#include

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) lines_read];

if(data)
{
size_t len = strlen(data);
memcpy(ptr, data, len);
upload->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://

Solution

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: 
Subject: Gmail Test
From: syb0rg 
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

Body content

--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
#include
#include
#include
#include

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->to = to;
data->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", "", input, "\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(&rawtime);
struct tm *timeinfo = localtime(&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: \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 & 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)

Code Snippets

typedef struct
 {
     const char* to;
     const char* from;
     const char** email;
     size_t pos;
 } EmailData;
size_t read_callback(char *buffer, size_t size, size_t nitems, void *instream);
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: <CJYf78Vnxbma4wdV1mZuBXHPeEptOzyjH1vLMzLwnhoXsPw@mail.gmail.com>
Subject: Gmail Test
From: syb0rg <person@gmail.com>
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

<div dir="ltr">Body content</div>

--001a11c04f6ca3e68e051d1354cc--
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <curl/curl.h>

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->to = to;
    data->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", "<div dir=\"ltr\">", input, "</div>\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(&rawtime);
    struct tm *timeinfo = localtime(&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: <CJYf78Vnxbma4dV1mZuBpXPeEptOzyjHvLMBzLwnhoXsPw@mail.gmail.com>\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 & 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->email = payload;
    data->pos = 0;
    return data;
}

size_t readCallback(char *ptr, size_t size, size_t nmemb, voi

Context

StackExchange Code Review Q#100445, answer score: 4

Revisions (0)

No revisions yet.