patterncMinor
Sending an SMS message in C
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:
```
#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://
- 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
You may note that I changed your
-
The
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
-
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
-
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:
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)
-
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, voiContext
StackExchange Code Review Q#100445, answer score: 4
Revisions (0)
No revisions yet.