/**
* @file
* @brief Test the smtp-client library.
* @author James Humphrey (mail@somnisoft.com)
* @version 0.99
*
* This smtp-client testing framework has 100% branch coverage on POSIX
* systems. It requires a Postfix SMTP server that supports all of the
* connection security and authentication methods. These functional tests
* also require the user to manually check and ensure that the destination
* addresses received all of the test emails.
*
* This software has been placed into the public domain using CC0.
*/
/**
* This POSIX declaration required for the setenv() function.
*/
#define _POSIX_C_SOURCE 200112L
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
/**
* Get access to the @ref smtp_result_code and @ref smtp_command definitions.
*/
#define SMTP_INTERNAL_DEFINE
#include "test.h"
/**
* Temporary file path to use for testing the @ref smtp_file_get_contents
* function.
*/
#define TMP_FILE_PATH "/tmp/test_smtp_file_get_contents.txt"
/**
* Maximum email subject buffer length.
*/
#define SMTP_TEST_SUBJECT_LEN 100
/**
* Maximum email body buffer length.
*/
#define SMTP_TEST_BODY_LEN 1000
/**
* Maximum server name buffer length.
*/
#define SMTP_MAX_SERVER_LEN 255
/**
* Maximum server port buffer length.
*/
#define SMTP_MAX_PORT_LEN 10
/**
* Maximum email buffer length.
*/
#define SMTP_MAX_EMAIL_LEN 255
/**
* Maximum account password buffer length.
*/
#define SMTP_MAX_PASS_LEN 255
/**
* Maximum file attachment name buffer length.
*/
#define SMTP_MAX_ATTACHMENT_NAME_LEN 100
/**
* This default connection security method will get used for most test
* connections with the SMTP server.
*/
#define SMTP_TEST_DEFAULT_CONNECTION_SECURITY SMTP_SECURITY_NONE
/**
* This default authentication method will get used for most test connections
* with the SMTP server.
*/
#define SMTP_TEST_DEFAULT_AUTH_METHOD SMTP_AUTH_PLAIN
/**
* These default flags will get used for most test connections with the
* SMTP server.
*/
#define SMTP_TEST_DEFAULT_FLAGS (SMTP_DEBUG | SMTP_NO_CERT_VERIFY)
/**
* Some unit tests use this for testing encoding or splitting long strings.
*/
#define STR_ALPHABET_LOWERCASE "abcdefghijklmnopqrstuvwxyz"
/**
* Copy a string and guarantee that the destination string has been
* null-terminated based on the given size.
*
* This function has a safer interface than strncpy because it always null
* terminates the destination string and it returns the total number of bytes
* in @p src which makes it easier to determine if the src tried to overflow
* the buffer.
*
* @param[out] dest Destination string buffer.
* @param[in] src Source string buffer.
* @param[in] destsz Number of bytes available in @p dest.
* @return String length of @p src.
*/
static size_t
smtp_strlcpy(char *dest,
const char *src,
size_t destsz){
size_t src_idx;
int found_end;
found_end = 0;
src_idx = 0;
while(*src){
if(!found_end){
if(src_idx >= destsz - 1 || destsz == 0){
dest[src_idx] = '\0';
found_end = 1;
}
else{
dest[src_idx] = *src;
}
}
src_idx += 1;
src += 1;
}
if(!found_end){
dest[src_idx] = '\0';
}
return src_idx;
}
/**
* Duplicate a string only up to a maximum number of bytes.
*
* @param[in] s String to duplicate.
* @param[in] n Maximum number of bytes to copy.
* @retval char* Duplicate of string @p s with at most @p n bytes.
* @retval NULL Memory allocation failure.
*/
static char *
smtp_strndup(const char *s,
size_t n){
char *ns;
size_t newsz;
newsz = sizeof(*ns) * (n + 1);
ns = malloc(newsz);
if(ns){
smtp_strlcpy(ns, s, newsz);
}
return ns;
}
/**
* Holds a list of strings.
*
* Used by a number of utility functions below to store and operate on lists
* of strings.
*/
struct smtp_str_list{
/**
* Number of strings in @p slist.
*/
size_t n;
/**
* List of strings.
*/
char **slist;
};
/**
* Append a string to the string list.
*
* @param[in] slist String list to append to.
* @param[in] s The new string to append to the list.
* @param[in] n Maximum number of bytes to copy in the string.
* @retval 0 Successfully appended the string to the list.
* @retval -1 Memory allocation failure.
*/
static int
smtp_str_list_append(struct smtp_str_list *const slist,
const char *const s,
size_t n){
char **slist_alloc;
char *snew;
if((slist_alloc = realloc(slist->slist,
sizeof(*slist->slist) * (slist->n + 1))) == NULL){
return -1;
}
slist->slist = slist_alloc;
if((snew = smtp_strndup(s, n)) == NULL){
return -1;
}
slist->slist[slist->n] = snew;
slist->n += 1;
return 0;
}
/**
* Free all memory associated to the string list.
*
* @param[in] list The string list to free.
*/
static void
smtp_str_list_free(struct smtp_str_list *const list){
size_t i;
for(i = 0; i < list->n; i++){
free(list->slist[i]);
}
free(list->slist);
list->slist = NULL;
list->n = 0;
}
/**
* Split a string with delimiters into a list.
*
* @param[in] s The string to split.
* @param[in] slen Length of string @p s to split, or -1 to split the
* entire string.
* @param[in] delimiter Split the string at every delimiter location.
* @param[in] limit A positive limit will limit the maximum number of
* split strings to @p limit with the last string
* containing the rest of the string. A value of 0 has
* the same meaning as 1. A negative value will cut off
* the last @p limit strings from the result.
* @param[out] slist
* @retval 0 Successfully split the string and stored the results into
* @p slist.
* @retval -1 Memory allocation failure.
*/
static int
smtp_str_split(const char *const s,
ssize_t slen,
const char *const delimiter,
int limit,
struct smtp_str_list *slist){
int i;
size_t i1;
size_t i2;
size_t delimiter_len;
int split_idx;
memset(slist, 0, sizeof(*slist));
delimiter_len = strlen(delimiter);
if(slen < 0){
slen = strlen(s);
}
split_idx = 0;
for(i1 = 0, i2 = 0; i2 < (size_t)slen; i2++){
if(limit > 0 && limit - 1 <= split_idx){
if(smtp_str_list_append(slist, &s[i1], -1) < 0){
smtp_str_list_free(slist);
return -1;
}
return 0;
}
else if(strncmp(&s[i2], delimiter, delimiter_len) == 0){
if(i2 - i1 == 0 && s[i2] == '\0'){
break;
}
if(smtp_str_list_append(slist, &s[i1], i2 - i1) < 0){
smtp_str_list_free(slist);
return -1;
}
i1 = i2 + delimiter_len;
i2 = i1;
if(strncmp(&s[i2], delimiter, delimiter_len) == 0){
i2 -= 1;
}
split_idx += 1;
}
}
if(smtp_str_list_append(slist, &s[i1], i2 - i1) < 0){
smtp_str_list_free(slist);
return -1;
}
if(limit < 0){
for(i = 0; i < abs(limit); i++){
free(slist->slist[slist->n - i - 1]);
}
slist->n -= i;
}
return 0;
}
/**
* Write bytes to an open file stream.
*
* @param[in] stream The file stream to write bytes to.
* @param[in] data The buffer containing the contents to write to the
* file stream.
* @param[in] datasz Number of bytes in @p data.
* @return Number of bytes written to the file.
*/
static size_t
smtp_ffile_put_contents(FILE *stream,
const void *const data,
ssize_t datasz){
ssize_t bytes_written;
bytes_written = 0;
if(datasz < 0){
bytes_written = strlen(data);
if(fputs(data, stream) == EOF){
bytes_written = 0;
}
}
else{
bytes_written = fwrite(data, 1, datasz, stream);
}
return bytes_written;
}
/**
* Write a byte string to a file.
*
* This interface handles safely opening, writing, and closing the file.
*
* A return of 0 can either indicate an error or it could indicate that the
* string did not have any bytes to write. Check errno for if further details
* required.
*
* @param[in] filename Path to file for writing the contents.
* @param[in] data The buffer contents to write to file.
* @param[in] datasz Number of bytes in @p data, or -1 if @p data consists
* of a null-terminated string.
* @param[in] flags Set to 0 for write mode or O_APPEND for append mode.
* @retval 0 Failed to write any bytes to the file.
* @retval >0 Number of bytes written to file.
*/
static size_t
smtp_file_put_contents(const char *const filename,
const void *const data,
ssize_t datasz,
int flags){
FILE *fp;
size_t bytes_written;
const char *mode;
if(flags == 0){
mode = "w";
}
else if(flags == O_APPEND){
mode = "a";
}
else{
errno = EINVAL;
return 0;
}
if((fp = fopen(filename, mode)) == NULL){
return 0;
}
bytes_written = smtp_ffile_put_contents(fp, data, datasz);
if(fclose(fp) == EOF){
return 0;
}
return bytes_written;
}
/**
* Sleep for number of seconds.
*
* Useful for testing failure scenarios because a timeout will occur after
* too many failed login attempts.
*
* @param[in] seconds The number of seconds to pause execution.
*/
static void
smtp_test_sleep(unsigned int seconds){
fprintf(stderr, "TEST FRAMEWORK: sleeping for %u seconds\n", seconds);
assert(sleep(seconds) == 0);
}
/**
* Test harness for @ref smtp_base64_decode.
*
* @param[in] buf Null-terminated base64 string.
* @param[in] expect_str Decoded binary data.
* @param[in] expect_str_len Length of @p expect_str.
*/
static void
smtp_unit_test_base64_decode(const char *const buf,
const char *const expect_str,
ssize_t expect_str_len){
unsigned char *decode;
ssize_t str_len;
str_len = smtp_base64_decode(buf, &decode);
if(expect_str){
assert(memcmp(decode, expect_str, str_len) == 0);
free(decode);
}
else{ /* NULL */
assert(decode == NULL);
}
assert(str_len == expect_str_len);
}
/**
* Run all test cases for base64 decoding.
*/
static void
smtp_unit_test_all_base64_decode(void){
smtp_unit_test_base64_decode("" , "", 0);
smtp_unit_test_base64_decode("YQ==" , "a", 1);
smtp_unit_test_base64_decode("YWE=" , "aa" , 2);
smtp_unit_test_base64_decode("YWFh" , "aaa" , 3);
smtp_unit_test_base64_decode("YWFhYQ==", "aaaa" , 4);
smtp_unit_test_base64_decode("YWFhYWE=", "aaaaa", 5);
smtp_unit_test_base64_decode("MTIzNDU=", "12345", 5);
smtp_unit_test_base64_decode("YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXo=",
STR_ALPHABET_LOWERCASE,
26);
/* invalid inputs */
smtp_unit_test_base64_decode("AB" , NULL, -1);
smtp_unit_test_base64_decode("^^^^" , NULL, -1);
smtp_unit_test_base64_decode("^^^\xFF", NULL, -1);
g_smtp_test_err_calloc_ctr = 0;
smtp_unit_test_base64_decode("", NULL, -1);
g_smtp_test_err_calloc_ctr = -1;
}
/**
* Test harness for @ref smtp_base64_encode.
*
* @param[in] buf Binary data to encode in base64.
* @param[in] buflen Number of bytes in the @p buf parameter.
* @param[in] expect The expected base64 string that would get returned.
*/
static void
smtp_unit_test_base64_encode(const char *const buf,
ssize_t buflen,
const char *const expect){
char *result;
result = smtp_base64_encode(buf, buflen);
if(expect){
assert(strcmp(result, expect) == 0);
free(result);
}
else{ /* NULL */
assert(result == expect);
}
}
/**
* Run all test cases for base64 encoding.
*/
static void
smtp_unit_test_all_base64_encode(void){
smtp_unit_test_base64_encode("" , -1, "");
smtp_unit_test_base64_encode("a" , -1, "YQ==");
smtp_unit_test_base64_encode("aa" , -1, "YWE=");
smtp_unit_test_base64_encode("aaa" , -1, "YWFh");
smtp_unit_test_base64_encode("aaaa" , -1, "YWFhYQ==");
smtp_unit_test_base64_encode("aaaaa", -1, "YWFhYWE=");
smtp_unit_test_base64_encode("12345", -1, "MTIzNDU=");
smtp_unit_test_base64_encode(STR_ALPHABET_LOWERCASE,
-1,
"YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXo=");
/* binary data */
smtp_unit_test_base64_encode("a\0b\1c", 5, "YQBiAWM=");
smtp_unit_test_base64_encode("a\n\r\4bc", 6, "YQoNBGJj");
/* calloc */
g_smtp_test_err_calloc_ctr = 0;
smtp_unit_test_base64_encode("", -1, NULL);
g_smtp_test_err_calloc_ctr = -1;
}
/**
* Test harness for @ref smtp_bin2hex.
*
* @param[in] s Buffer containing binary data to convert.
* @param[in] slen Number of bytes in @p s.
* @param[in] expect Expected hex string output returned by @ref smtp_bin2hex.
*/
static void
smtp_unit_test_bin2hex(const char *const s,
size_t slen,
const char *const expect){
char *result;
result = smtp_bin2hex((const unsigned char *const)s, slen);
if(expect){
assert(strcmp(result, expect) == 0);
}
else{
assert(result == expect);
}
free(result);
}
/**
* Run all test cases for @ref smtp_bin2hex.
*/
static void
smtp_unit_test_all_bin2hex(void){
smtp_unit_test_bin2hex("" , 0, "" );
smtp_unit_test_bin2hex("0" , 0, "" );
smtp_unit_test_bin2hex("0" , 1, "30" );
smtp_unit_test_bin2hex("1" , 1, "31" );
smtp_unit_test_bin2hex("012345", 6, "303132333435");
smtp_unit_test_bin2hex("012345", 3, "303132" );
smtp_unit_test_bin2hex("000000", 6, "303030303030");
smtp_unit_test_bin2hex(
STR_ALPHABET_LOWERCASE,
26,
"6162636465666768696a6b6c6d6e6f707172737475767778797a");
smtp_unit_test_bin2hex("\xFF", 1, "ff");
smtp_unit_test_bin2hex("\x00", 1, "00");
g_smtp_test_err_malloc_ctr = 0;
smtp_unit_test_bin2hex("", 0, NULL);
g_smtp_test_err_malloc_ctr = -1;
g_smtp_test_err_sprintf_ctr = 0;
g_smtp_test_err_sprintf_rc = -1;
smtp_unit_test_bin2hex("0", 1, NULL);
g_smtp_test_err_sprintf_ctr = -1;
g_smtp_test_err_sprintf_ctr = 0;
g_smtp_test_err_sprintf_rc = 10;
smtp_unit_test_bin2hex("0", 1, NULL);
g_smtp_test_err_sprintf_ctr = -1;
}
/**
* Test harness for @ref smtp_strdup.
*
* @param[in] s String to duplicate.
* @param[in] expect Expected string result.
*/
static void
smtp_unit_test_strdup(const char *const s,
const char *const expect){
char *result;
result = smtp_strdup(s);
if(expect){
assert(strcmp(result, expect) == 0);
free(result);
}
else{ /* NULL */
assert(result == expect);
}
}
/**
* Run all test cases for @ref smtp_strdup.
*/
static void
smtp_unit_test_all_strdup(void){
smtp_unit_test_strdup("", "");
smtp_unit_test_strdup("a", "a");
smtp_unit_test_strdup("ab", "ab");
/* malloc */
g_smtp_test_err_malloc_ctr = 0;
smtp_unit_test_strdup("", NULL);
g_smtp_test_err_malloc_ctr = -1;
}
/**
* Test harness for @ref smtp_str_replace.
*
* @param[in] search Substring to search for in @p s.
* @param[in] replace Replace each instance of the search string with this.
* @param[in] s Null-terminated string to search and replace.
* @param[in] expect Expected result.
*/
static void
smtp_unit_test_str_replace(const char *const search,
const char *const replace,
const char *const s,
const char *const expect){
char *result;
result = smtp_str_replace(search, replace, s);
if(expect){
assert(strcmp(result, expect) == 0);
free(result);
}
else{ /* NULL */
assert(result == expect);
}
}
/**
* Run all tests for @ref smtp_str_replace.
*/
static void
smtp_unit_test_all_str_replace(void){
smtp_unit_test_str_replace("", "", "", "");
smtp_unit_test_str_replace("a", "b", "", "");
smtp_unit_test_str_replace("", "", "a b c", "a b c");
smtp_unit_test_str_replace("a", "", "a b c", " b c");
smtp_unit_test_str_replace("a", "a", "a", "a");
smtp_unit_test_str_replace("a", "b", "a", "b");
smtp_unit_test_str_replace("a", "bc", "a", "bc");
smtp_unit_test_str_replace("a", "b", "abc", "bbc");
smtp_unit_test_str_replace("A", "b", "a", "a");
smtp_unit_test_str_replace("b", "a", "abc", "aac");
smtp_unit_test_str_replace("string", "test", "test string", "test test");
smtp_unit_test_str_replace("a", "b", "a b a", "b b b");
smtp_unit_test_str_replace("a", "b", "a b a", "b b b");
smtp_unit_test_str_replace("a", "b", "a b a b a", "b b b b b");
g_smtp_test_err_realloc_ctr = 0;
smtp_unit_test_str_replace("a", "b", "a b c", NULL);
g_smtp_test_err_realloc_ctr = -1;
g_smtp_test_err_realloc_ctr = 0;
smtp_unit_test_str_replace("b", "a", "a b c", NULL);
g_smtp_test_err_realloc_ctr = -1;
}
/**
* Test harness for @ref smtp_strnlen.
*
* @param[in] s Null-terminated string.
* @param[in] maxlen Do not check more than @p maxlen bytes of string @p s.
* @param[in] expect Expected string length.
*/
static void
smtp_unit_test_strnlen(const char *s,
size_t maxlen,
size_t expect){
size_t slen;
slen = smtp_strnlen(s, maxlen);
assert(slen == expect);
}
/**
* Run all tests for @ref smtp_strnlen.
*/
static void
smtp_unit_test_all_strnlen(void){
smtp_unit_test_strnlen("" , 0, 0);
smtp_unit_test_strnlen("" , 1, 0);
smtp_unit_test_strnlen("a" , 0, 0);
smtp_unit_test_strnlen("a" , 1, 1);
smtp_unit_test_strnlen("a" , 2, 1);
smtp_unit_test_strnlen("ab", 0, 0);
smtp_unit_test_strnlen("ab", 1, 1);
smtp_unit_test_strnlen("ab", 2, 2);
smtp_unit_test_strnlen("ab", 3, 2);
}
/**
* Test harness for @ref smtp_chunk_split.
*
* @param[in] s The string to chunk.
* @param[in] chunklen Number of bytes for each chunk in the string.
* @param[in] end Terminating string placed at the end of each chunk.
* @param[in] expect Expected chunk string.
*/
static void
smtp_unit_test_chunk_split(const char *const s,
int chunklen,
const char *const end,
const char *const expect){
char *result;
result = smtp_chunk_split(s, chunklen, end);
if(expect == NULL){
assert(result == expect);
}
else{
assert(strcmp(result, expect) == 0);
free(result);
}
}
/**
* Run all tests for @ref smtp_chunk_split.
*/
static void
smtp_unit_test_all_chunk_split(void){
smtp_unit_test_chunk_split("", 0, "", NULL);
smtp_unit_test_chunk_split("a", 0, "a", NULL);
smtp_unit_test_chunk_split("", 1, "", "");
smtp_unit_test_chunk_split("", 1, "a", "a");
smtp_unit_test_chunk_split("", 2, "a", "a");
smtp_unit_test_chunk_split("a", 1, "", "a");
smtp_unit_test_chunk_split("abc", 1, "-", "a-b-c-");
smtp_unit_test_chunk_split("abc", 2, "-", "ab-c-");
smtp_unit_test_chunk_split("abc", 3, "-", "abc-");
smtp_unit_test_chunk_split("abcdefghijklmnop",
3,
"-",
"abc-def-ghi-jkl-mno-p-");
smtp_unit_test_chunk_split("abc", 1, "-!@", "a-!@b-!@c-!@");
smtp_unit_test_chunk_split("abcdefghijklmnop",
3,
"-!",
"abc-!def-!ghi-!jkl-!mno-!p-!");
smtp_unit_test_chunk_split("abc", 1, "\r\n", "a\r\nb\r\nc\r\n");
smtp_unit_test_chunk_split(STR_ALPHABET_LOWERCASE,
10,
"\r\n",
"abcdefghij\r\nklmnopqrst\r\nuvwxyz\r\n");
g_smtp_test_err_calloc_ctr = 0;
smtp_unit_test_chunk_split("abc", 1, "-", NULL);
g_smtp_test_err_calloc_ctr = -1;
}
/**
* Test harness for @ref smtp_file_get_contents.
*
* @param[in] s The string to write to the temp file before reading.
* @param[in] nbytes Number of bytes in @p s.
* @param[in] expect Expected string after reading the file.
*/
static void
smtp_unit_test_file_get_contents(const char *const s,
size_t nbytes,
const char *const expect){
char *read_buf;
size_t nbytes_rw;
nbytes_rw = smtp_file_put_contents(TMP_FILE_PATH, s, nbytes, 0);
assert(nbytes_rw == strlen(expect));
read_buf = smtp_file_get_contents(TMP_FILE_PATH, &nbytes_rw);
assert(read_buf);
assert(memcmp(expect, read_buf, strlen(expect)) == 0);
free(read_buf);
}
/**
* Run all tests for @ref smtp_file_get_contents.
*/
static void
smtp_unit_test_all_file_get_contents(void){
const char *test_str;
smtp_unit_test_file_get_contents("", 0, "");
test_str = "test";
smtp_unit_test_file_get_contents(test_str, 0, "");
smtp_unit_test_file_get_contents(test_str, strlen(test_str), test_str);
test_str = "test\nnewline";
smtp_unit_test_file_get_contents(test_str, strlen(test_str), test_str);
test_str = "test";
smtp_unit_test_file_get_contents(test_str, -1, test_str);
test_str = STR_ALPHABET_LOWERCASE;
smtp_unit_test_file_get_contents(test_str, strlen(test_str), test_str);
/* smtp_file_get_contents - fopen */
assert(smtp_file_get_contents("", NULL) == NULL);
/* smtp_file_get_contents - fclose */
g_smtp_test_err_fclose_ctr = 0;
assert(smtp_file_get_contents(TMP_FILE_PATH, NULL) == NULL);
g_smtp_test_err_fclose_ctr = -1;
/* smtp_file_get_contents - realloc */
g_smtp_test_err_realloc_ctr = 0;
assert(smtp_file_get_contents(TMP_FILE_PATH, NULL) == NULL);
g_smtp_test_err_realloc_ctr = -1;
/* smtp_file_get_contents - ferror */
g_smtp_test_err_ferror_ctr = 0;
assert(smtp_file_get_contents(TMP_FILE_PATH, NULL) == NULL);
g_smtp_test_err_ferror_ctr = -1;
}
/**
* Test harness for @ref smtp_parse_cmd_line.
*
* @param[in] line The server response line to parse.
* @param[in] expect_code Expected server response code.
* @param[in] expect_more Set to 1 if more lines will get returned or 0 if
* no more lines.
* @param[in] expect_text Expected text shown after the response code.
*/
static void
smtp_unit_test_parse_cmd_line(const char *const line,
enum smtp_result_code expect_code,
int expect_more,
const char *const expect_text){
char *line_dup;
struct smtp_command cmd;
int rc;
line_dup = smtp_strdup(line);
assert(line_dup);
rc = smtp_parse_cmd_line(line_dup, &cmd);
assert(rc == expect_code);
assert(cmd.code == expect_code);
assert(cmd.more == expect_more);
assert(strcmp(cmd.text, expect_text) == 0);
free(line_dup);
}
/**
* Run all tests for @ref smtp_parse_cmd_line.
*/
static void
smtp_unit_test_all_parse_cmd_line(void){
smtp_unit_test_parse_cmd_line("",
SMTP_INTERNAL_ERROR,
0,
"");
smtp_unit_test_parse_cmd_line("<5",
SMTP_INTERNAL_ERROR,
0,
"<5");
smtp_unit_test_parse_cmd_line("bad text",
SMTP_INTERNAL_ERROR,
0,
"text");
smtp_unit_test_parse_cmd_line("bad-text",
SMTP_INTERNAL_ERROR,
1,
"text");
smtp_unit_test_parse_cmd_line("0x1 text",
SMTP_INTERNAL_ERROR,
0,
"text");
smtp_unit_test_parse_cmd_line("-22 text",
-22,
0,
"text");
smtp_unit_test_parse_cmd_line("-22 text",
-22,
0,
"text");
smtp_unit_test_parse_cmd_line("220 ready",
SMTP_READY,
0,
"ready");
}
/**
* Test harness for @ref smtp_date_rfc_2822.
*
* @param[in] t Force the time() function to return this time_t value.
* @param[in] expect Expected date string.
* @param[in] expect_rc Expected return code.
*/
static void
smtp_unit_test_date_rfc_2822(time_t t,
const char *const expect,
int expect_rc){
char result[SMTP_DATE_MAX_SZ];
int rc;
g_smtp_test_time_custom_ret = 1;
g_smtp_test_time_ret_value = t;
setenv("TZ", "UTC", 1);
rc = smtp_date_rfc_2822(result);
assert(rc == expect_rc);
if(expect_rc == 0){
assert(strcmp(result, expect) == 0);
}
g_smtp_test_time_custom_ret = 0;
g_smtp_test_time_ret_value = 0;
}
/**
* Run all tests for @ref smtp_date_rfc_2822.
*/
static void
smtp_unit_test_all_date_rfc_2822(void){
smtp_unit_test_date_rfc_2822(0, "Thu, 01 Jan 1970 00:00:00 +0000", 0);
smtp_unit_test_date_rfc_2822(60 * 60 * 24 * 2 + 5,
"Sat, 03 Jan 1970 00:00:05 +0000",
0);
smtp_unit_test_date_rfc_2822(-1, NULL, -1);
g_smtp_test_err_localtime_r_ctr = 0;
smtp_unit_test_date_rfc_2822(0, NULL, -1);
g_smtp_test_err_localtime_r_ctr = -1;
g_smtp_test_err_gmtime_r_ctr = 0;
smtp_unit_test_date_rfc_2822(0, NULL, -1);
g_smtp_test_err_gmtime_r_ctr = -1;
g_smtp_test_err_mktime_ctr = 0;
smtp_unit_test_date_rfc_2822(0, NULL, -1);
g_smtp_test_err_mktime_ctr = -1;
g_smtp_test_err_mktime_ctr = 1;
smtp_unit_test_date_rfc_2822(0, NULL, -1);
g_smtp_test_err_mktime_ctr = -1;
g_smtp_test_err_sprintf_ctr = 0;
g_smtp_test_err_sprintf_rc = -1;
smtp_unit_test_date_rfc_2822(0, NULL, -1);
g_smtp_test_err_sprintf_ctr = -1;
}
/**
* Run all tests for @ref smtp_address_validate_email.
*/
static void
smtp_unit_test_all_smtp_address_validate_email(void){
assert(smtp_address_validate_email(STR_ALPHABET_LOWERCASE) == 0);
assert(smtp_address_validate_email("mail@example.com") == 0);
assert(smtp_address_validate_email("īḑȋᵭ") == 0);
assert(smtp_address_validate_email("<abc") == -1);
assert(smtp_address_validate_email("abc>") == -1);
assert(smtp_address_validate_email("\x7f") == -1);
assert(smtp_address_validate_email("a b c") == -1);
}
/**
* Run all tests for @ref smtp_address_validate_name.
*/
static void
smtp_unit_test_all_smtp_address_validate_name(void){
assert(smtp_address_validate_name(STR_ALPHABET_LOWERCASE) == 0);
assert(smtp_address_validate_name("John Doe") == 0);
assert(smtp_address_validate_name("John O'Doe") == 0);
assert(smtp_address_validate_name("īḑȋᵭ") == 0);
assert(smtp_address_validate_name("a\nb\nc") == -1);
assert(smtp_address_validate_name("\"abc") == -1);
assert(smtp_address_validate_name("\x7f") == -1);
}
/**
* Run all tests for @ref smtp_attachment_validate_name.
*/
static void
smtp_unit_test_all_smtp_attachment_validate_name(void){
assert(smtp_attachment_validate_name(STR_ALPHABET_LOWERCASE) == 0);
assert(smtp_attachment_validate_name("a b c") == 0);
assert(smtp_attachment_validate_name("test.txt") == 0);
assert(smtp_attachment_validate_name("īḑȋᵭ") == 0);
assert(smtp_attachment_validate_name("a\nbc") == -1);
assert(smtp_attachment_validate_name("\x7f") == -1);
assert(smtp_attachment_validate_name("a\'bc") == -1);
assert(smtp_attachment_validate_name("a\"bc") == -1);
}
/**
* Run all tests for @ref smtp_header_key_validate.
*/
static void
smtp_unit_test_all_smtp_header_key_validate(void){
assert(smtp_header_key_validate(STR_ALPHABET_LOWERCASE) == 0);
assert(smtp_header_key_validate("īḑȋᵭ") == -1);
assert(smtp_header_key_validate("a b c") == -1);
assert(smtp_header_key_validate("a\xff") == -1);
assert(smtp_header_key_validate("a:b:c") == -1);
assert(smtp_header_key_validate("a\nb\nc") == -1);
}
/**
* Run all tests for @ref smtp_header_value_validate.
*/
static void
smtp_unit_test_all_smtp_header_value_validate(void){
assert(smtp_header_value_validate(STR_ALPHABET_LOWERCASE) == 0);
assert(smtp_header_value_validate("a\tb c") == 0);
assert(smtp_header_value_validate("īḑȋᵭ") == -1);
assert(smtp_header_value_validate("a\nb\nc") == -1);
assert(smtp_header_value_validate("a\xff") == -1);
}
/**
* Test harness for @ref smtp_status_code_errstr.
*
* @param[in] status_code See @ref smtp_status_code.
* @param[in] expect Expected error string.
*/
static void
smtp_unit_test_smtp_status_code_errstr(enum smtp_status_code status_code,
const char *const expect){
const char *result;
result = smtp_status_code_errstr(status_code);
assert(strcmp(result, expect) == 0);
}
/**
* Run all tests for @ref smtp_status_code_errstr.
*/
static void
smtp_unit_test_all_smtp_status_code_errstr(void){
smtp_unit_test_smtp_status_code_errstr(SMTP_STATUS_OK,
"Success");
smtp_unit_test_smtp_status_code_errstr(SMTP_STATUS_NOMEM,
"Memory allocation failed");
smtp_unit_test_smtp_status_code_errstr(-1,
"Unknown error");
smtp_unit_test_smtp_status_code_errstr(99,
"Unknown error");
}
/**
* User data pointer for testing the @ref str_getdelimfd interface.
*/
struct smtp_test_getdelimfd_fp{
/**
* Read from this file which should contain the contents used to test
* the parser.
*/
FILE *fp;
};
/**
* Set to a non-zero value to force an error return value
* in @ref smtp_unit_test_getdelimfd_fp.
*/
static int g_smtp_test_getdelimfd_fp_fail = 0;
/**
* Read function used by the @ref smtp_str_getdelimfd interface.
*
* @param[in] gdfd See @ref str_getdelimfd.
* @param[out] buf Pointer to buffer for storing bytes read.
* @param[in] count Maximum number of bytes to try reading.
* @retval >=0 Number of bytes read.
* @retval -1 Failed to read from the socket.
*/
static ssize_t
smtp_unit_test_getdelimfd_fp(struct str_getdelimfd *const gdfd,
void *buf,
size_t count){
struct smtp_test_getdelimfd_fp *getdelimfd_fp;
size_t bytes_read;
getdelimfd_fp = gdfd->user_data;
bytes_read = fread(buf, sizeof(char), count, getdelimfd_fp->fp);
if(g_smtp_test_getdelimfd_fp_fail){
return -1;
}
return bytes_read;
}
/**
* Test harness for @ref smtp_str_getdelimfd.
*
* @param[in] input_string Test string used in delimeter parsing.
* @param[in] nbytes Number of bytes in @p input_string.
* @param[in] delim Delimiter used to split the string.
* @param[in] expect_rc Expected return code.
* @param[in] expect_pieces Expected list of strings parsed from the file.
*/
static void
smtp_unit_test_str_getdelimfd(const char *const input_string,
size_t nbytes,
int delim,
enum str_getdelim_retcode expect_rc,
const char *expect_pieces, ...){
const char *piece;
enum str_getdelim_retcode rc;
size_t bytes_written;
struct str_getdelimfd gdfd;
struct smtp_test_getdelimfd_fp getdelimfd_fp;
struct smtp_str_list slist;
FILE *fp;
size_t piece_idx;
va_list ap;
memset(&slist, 0, sizeof(slist));
bytes_written = smtp_file_put_contents(TMP_FILE_PATH,
input_string,
nbytes,
0);
assert(bytes_written == nbytes);
memset(&getdelimfd_fp, 0, sizeof(getdelimfd_fp));
fp = fopen(TMP_FILE_PATH, "r");
assert(fp);
getdelimfd_fp.fp = fp;
memset(&gdfd, 0, sizeof(gdfd));
gdfd.delim = delim;
gdfd.getdelimfd_read = smtp_unit_test_getdelimfd_fp;
gdfd.user_data = &getdelimfd_fp;
do{
rc = smtp_str_getdelimfd(&gdfd);
if(expect_rc == STRING_GETDELIMFD_ERROR){
assert(rc == expect_rc);
smtp_str_list_free(&slist);
return;
}
assert(rc != STRING_GETDELIMFD_ERROR);
assert(smtp_str_list_append(&slist, gdfd.line, gdfd.line_len) == 0);
} while (rc != STRING_GETDELIMFD_DONE);
smtp_str_getdelimfd_free(&gdfd);
assert(fclose(fp) == 0);
piece_idx = 0;
piece = expect_pieces;
va_start(ap, expect_pieces);
while (piece){
assert(strcmp(piece, slist.slist[piece_idx]) == 0);
piece_idx += 1;
piece = va_arg(ap, const char *);
}
va_end(ap);
assert(piece_idx == slist.n);
smtp_str_list_free(&slist);
}
/**
* Run all tests for @ref smtp_str_getdelimfd.
*/
static void
smtp_unit_test_all_str_getdelimfd(void){
const char *s;
s = "";
smtp_unit_test_str_getdelimfd(s,
strlen(s),
'\n',
STRING_GETDELIMFD_DONE,
"",
NULL);
s = "a";
smtp_unit_test_str_getdelimfd(s,
strlen(s),
'\n',
STRING_GETDELIMFD_DONE,
"a",
NULL);
s = "\n";
smtp_unit_test_str_getdelimfd(s,
strlen(s),
'\n',
STRING_GETDELIMFD_DONE,
"",
"",
NULL);
s = "a\n";
smtp_unit_test_str_getdelimfd(s,
strlen(s),
'\n',
STRING_GETDELIMFD_DONE,
"a",
"",
NULL);
s = "\na";
smtp_unit_test_str_getdelimfd(s,
strlen(s),
'\n',
STRING_GETDELIMFD_DONE,
"",
"a",
NULL);
s = "test line 1";
smtp_unit_test_str_getdelimfd(s,
strlen(s),
'\n',
STRING_GETDELIMFD_DONE,
"test line 1",
NULL);
s = "test line 1\n";
smtp_unit_test_str_getdelimfd(s,
strlen(s),
'\n',
STRING_GETDELIMFD_DONE,
"test line 1",
"",
NULL);
s = "test line 1\ntest line 2";
smtp_unit_test_str_getdelimfd(s,
strlen(s),
'\n',
STRING_GETDELIMFD_DONE,
"test line 1",
"test line 2",
NULL);
s = "test line 1\ntest line 2\ntest line 3";
smtp_unit_test_str_getdelimfd(s,
strlen(s),
'\n',
STRING_GETDELIMFD_DONE,
"test line 1",
"test line 2",
"test line 3",
NULL);
/* smtp_str_getdelimfd_set_line_and_buf - 2 */
g_smtp_test_err_calloc_ctr = 0;
s = "a";
smtp_unit_test_str_getdelimfd(s,
strlen(s),
'\n',
STRING_GETDELIMFD_ERROR,
NULL);
g_smtp_test_err_calloc_ctr = -1;
/* smtp_str_getdelimfd_set_line_and_buf - 2 */
g_smtp_test_err_calloc_ctr = 0;
s = "a\na";
smtp_unit_test_str_getdelimfd(s,
strlen(s),
'\n',
STRING_GETDELIMFD_ERROR,
NULL);
g_smtp_test_err_calloc_ctr = -1;
/* realloc */
g_smtp_test_err_realloc_ctr = 0;
s = "a";
smtp_unit_test_str_getdelimfd(s,
strlen(s),
'\n',
STRING_GETDELIMFD_ERROR,
NULL);
g_smtp_test_err_realloc_ctr = -1;
/* fread */
g_smtp_test_getdelimfd_fp_fail = 1;
s = "a";
smtp_unit_test_str_getdelimfd(s,
strlen(s),
'\n',
STRING_GETDELIMFD_ERROR,
NULL);
g_smtp_test_getdelimfd_fp_fail = 0;
}
/**
* Unit test functions which do not require an SMTP client context.
*/
static void
smtp_unit_test_all(void){
smtp_unit_test_all_base64_decode();
smtp_unit_test_all_base64_encode();
smtp_unit_test_all_bin2hex();
smtp_unit_test_all_strdup();
smtp_unit_test_all_str_replace();
smtp_unit_test_all_strnlen();
smtp_unit_test_all_chunk_split();
smtp_unit_test_all_file_get_contents();
smtp_unit_test_all_parse_cmd_line();
smtp_unit_test_all_date_rfc_2822();
smtp_unit_test_all_smtp_address_validate_email();
smtp_unit_test_all_smtp_address_validate_name();
smtp_unit_test_all_smtp_attachment_validate_name();
smtp_unit_test_all_smtp_header_key_validate();
smtp_unit_test_all_smtp_header_value_validate();
smtp_unit_test_all_smtp_status_code_errstr();
smtp_unit_test_all_str_getdelimfd();
}
/**
* Stores details from the server configuration file.
*
* Most of this information gets loaded from separate files because the
* files contain sensitive account information which should not get stored
* in a public repository.
*/
struct smtp_test_config{
/**
* SMTP client context.
*/
struct smtp *smtp;
/**
* Email subject line.
*/
char subject[SMTP_TEST_SUBJECT_LEN];
/**
* Email body text.
*/
char body[SMTP_TEST_BODY_LEN];
/*
* The following contains fields that get loaded in from the
* configuration file.
*/
/**
* Server name or IP address.
*/
char server[SMTP_MAX_SERVER_LEN];
/**
* Server port number.
*/
char port[SMTP_MAX_PORT_LEN];
/**
* Server port number for direct TLS connection.
*/
char port_tls[SMTP_MAX_PORT_LEN];
/**
* Account user name.
*/
char user[SMTP_MAX_EMAIL_LEN];
/**
* Account password.
*/
char pass[SMTP_MAX_PASS_LEN];
/**
* The email displayed in the email from section.
*/
char email_from[SMTP_MAX_EMAIL_LEN];
/**
* Email destination address.
*/
char email_to[SMTP_MAX_EMAIL_LEN];
/**
* Email destination address (2nd).
*/
char email_to_2[SMTP_MAX_EMAIL_LEN];
/**
* Email destination address (3rd).
*/
char email_to_3[SMTP_MAX_EMAIL_LEN];
};
/**
* Load server configuration file containing server connection information.
*
* The server connection information contains sensitive information, so we
* need to load it in from a separate configuration file that does not get
* saved in the repository. This stores the parsed results into a
* @ref smtp_test_config data structure.
*
* @param[in] config_path Path to the server configuration file.
* @param[out] config The parsed contents from the configuration file
* gets stored in this data structure.
* @retval 0 Successfully parsed and saved the server configuration into the
* data structure.
* @retval -1 Failed to read file or memory allocation failure.
*/
static int
smtp_test_config_load_from_file(const char *const config_path,
struct smtp_test_config *const config){
char *config_data;
char *config_data_new;
size_t config_data_len;
struct smtp_str_list line_list;
char *line;
int rc;
size_t i;
const char *key;
const char *value;
memset(config, 0, sizeof(*config));
if((config_data = smtp_file_get_contents(config_path,
&config_data_len)) == NULL){
return -1;
}
/* add a null character at end of file data */
if((config_data_new = realloc(config_data, config_data_len + 1)) == NULL){
free(config_data);
return -1;
}
config_data = config_data_new;
config_data[config_data_len] = '\0';
rc = smtp_str_split(config_data, config_data_len, "\n", 0, &line_list);
assert(rc == 0);
free(config_data);
for(i = 0; i < line_list.n; i++){
line = line_list.slist[i];
if(line[0] == '#'){
continue;
}
key = strtok(line, "=");
if(key == NULL){
continue;
}
value = strtok(NULL, "=");
if(value == NULL){
value = "";
}
if(strcmp(key, "server") == 0){
smtp_strlcpy(config->server, value, sizeof(config->server));
}
else if(strcmp(key, "port") == 0){
smtp_strlcpy(config->port, value, sizeof(config->port));
}
else if(strcmp(key, "port_tls") == 0){
smtp_strlcpy(config->port_tls, value, sizeof(config->port_tls));
}
else if(strcmp(key, "user") == 0){
smtp_strlcpy(config->user, value, sizeof(config->user));
}
else if(strcmp(key, "pass") == 0){
smtp_strlcpy(config->pass, value, sizeof(config->pass));
}
else if(strcmp(key, "email_from") == 0){
smtp_strlcpy(config->email_from, value, sizeof(config->email_from));
}
else if(strcmp(key, "email_to") == 0){
smtp_strlcpy(config->email_to, value, sizeof(config->email_to));
}
else if(strcmp(key, "email_to_2") == 0){
smtp_strlcpy(config->email_to, value, sizeof(config->email_to));
}
else if(strcmp(key, "email_to_3") == 0){
smtp_strlcpy(config->email_to, value, sizeof(config->email_to));
}
}
smtp_str_list_free(&line_list);
return 0;
}
/**
* Opens an smtp connection using @ref smtp_open and adds default addresses.
*
* Uses default connection and flag parameters and ensures the return status
* gets set to SMTP_STATUS_OK. Adds the FROM, TO, and CC email addresses
* given in the config file. This function should always succeed.
*
* @param[in] config SMTP server context.
*/
static void
test_smtp_open_default(struct smtp_test_config *const config){
int rc;
rc = smtp_open(config->server,
config->port,
SMTP_TEST_DEFAULT_CONNECTION_SECURITY,
SMTP_TEST_DEFAULT_FLAGS,
&config->smtp);
assert(rc == SMTP_STATUS_OK);
rc = smtp_address_add(config->smtp,
SMTP_ADDRESS_FROM,
config->email_from,
"");
assert(rc == SMTP_STATUS_OK);
rc = smtp_address_add(config->smtp,
SMTP_ADDRESS_TO,
config->email_to,
"Test Email");
assert(rc == SMTP_STATUS_OK);
rc = smtp_address_add(config->smtp,
SMTP_ADDRESS_CC,
config->email_to_2,
"Test Email");
assert(rc == SMTP_STATUS_OK);
}
/**
* Test the @ref smtp_status_code_get function.
*
* @param[in] config SMTP server context.
*/
static void
smtp_func_test_all_status_code_get(struct smtp_test_config *const config){
int rc;
rc = smtp_open(config->server,
config->port,
SMTP_TEST_DEFAULT_CONNECTION_SECURITY,
SMTP_TEST_DEFAULT_FLAGS,
&config->smtp);
assert(rc == SMTP_STATUS_OK);
rc = smtp_status_code_get(config->smtp);
assert(rc == SMTP_STATUS_OK);
smtp_status_code_set(config->smtp, SMTP_STATUS_NOMEM);
rc = smtp_status_code_get(config->smtp);
assert(rc == SMTP_STATUS_NOMEM);
smtp_status_code_set(config->smtp, SMTP_STATUS_OK);
rc = smtp_close(config->smtp);
assert(rc == SMTP_STATUS_OK);
}
/**
* Send a test email with the given parameters.
*
* See @ref smtp_open and @ref smtp_auth for more information about this
* functions parameters.
*
* @param[in] config SMTP server context.
* @param[in] port Server connection port.
* @param[in] connection_security Connection security settings.
* @param[in] flags Miscellaneous configuration flags.
* @param[in] auth_method Authentication method.
* @param[in] subject Email subject line.
* @param[in] body Email body.
*/
static void
smtp_func_test_send_email(struct smtp_test_config *const config,
const char *const port,
enum smtp_connection_security connection_security,
enum smtp_flag flags,
enum smtp_authentication_method auth_method,
const char *const subject,
const char *const body){
int rc;
rc = smtp_open(config->server,
port,
connection_security,
flags,
&config->smtp);
assert(rc == SMTP_STATUS_OK);
rc = smtp_auth(config->smtp, auth_method, config->user, config->pass);
assert(rc == SMTP_STATUS_OK);
rc = smtp_address_add(config->smtp,
SMTP_ADDRESS_FROM,
config->email_from,
NULL);
assert(rc == SMTP_STATUS_OK);
rc = smtp_address_add(config->smtp,
SMTP_ADDRESS_TO,
config->email_to,
"Test Email");
assert(rc == SMTP_STATUS_OK);
rc = smtp_header_add(config->smtp, "Subject", subject);
assert(rc == SMTP_STATUS_OK);
rc = smtp_mail(config->smtp, body);
assert(rc == SMTP_STATUS_OK);
rc = smtp_close(config->smtp);
assert(rc == SMTP_STATUS_OK);
}
/**
* Send a test email with a specific connection security method.
*
* @param[in] config Server connection details.
* @param[in] server_port Server port number to connect to.
* @param[in] con_security See @ref smtp_connection_security.
* @param[in] security_description Description of @p connection_security.
*/
static void
smtp_func_test_connection_security(struct smtp_test_config *const config,
const char *const server_port,
enum smtp_connection_security con_security,
const char *const security_description){
sprintf(config->subject,
"SMTP Test: Connection Security %s",
security_description);
sprintf(config->body,
"Email sent with connection security: %s",
security_description);
smtp_func_test_send_email(config,
server_port,
con_security,
SMTP_TEST_DEFAULT_FLAGS,
SMTP_TEST_DEFAULT_AUTH_METHOD,
config->subject,
config->body);
}
/**
* Run through all types of SMTP connections provided in the
* @ref smtp_connection_security.
*
* @param[in] config Server connection details.
*/
static void
smtp_func_test_all_connection_security(struct smtp_test_config *const config){
smtp_func_test_connection_security(config,
config->port,
SMTP_SECURITY_NONE,
"None");
smtp_func_test_connection_security(config,
config->port,
SMTP_SECURITY_STARTTLS,
"STARTTLS");
smtp_func_test_connection_security(config,
config->port_tls,
SMTP_SECURITY_TLS,
"TLS");
}
/**
* Send a test email with a specific authentication method.
*
* @param[in] config Server connection details.
* @param[in] auth_method See @ref smtp_authentication_method.
* @param[in] auth_description Description of @p auth_method.
*/
static void
smtp_func_test_auth(struct smtp_test_config *const config,
enum smtp_authentication_method auth_method,
const char *const auth_description){
sprintf(config->subject,
"SMTP Test: AUTH %s",
auth_description);
sprintf(config->body,
"Email authenticated using %s",
auth_description);
smtp_func_test_send_email(config,
config->port,
SMTP_TEST_DEFAULT_CONNECTION_SECURITY,
SMTP_TEST_DEFAULT_FLAGS,
auth_method,
config->subject,
config->body);
}
/**
* Run through all types of SMTP authentication methods provided in the
* @ref smtp_authentication_method.
*
* @param[in] config Server connection details.
*/
static void
smtp_func_test_all_auth_methods(struct smtp_test_config *const config){
smtp_func_test_auth(config, SMTP_AUTH_NONE, "NONE");
smtp_func_test_auth(config, SMTP_AUTH_PLAIN, "PLAIN");
smtp_func_test_auth(config, SMTP_AUTH_LOGIN, "LOGIN");
smtp_func_test_auth(config, SMTP_AUTH_CRAM_MD5, "CRAM-MD5");
}
/**
* Test harness for @ref smtp_attachment_add_path.
*
* @param[in] config Server connection details.
* @param[in] name Name of the file to display to the recipient.
* @param[in] path Local file path to use as the attachment.
* @param[in] expect_rc Expected return code for the attachment function
* and every function after that.
*/
static void
smtp_func_test_attachment_path(struct smtp_test_config *const config,
const char *const name,
const char *const path,
int expect_rc){
int rc;
strcpy(config->subject, "SMTP Test: Attachment (file path)");
strcpy(config->body, "This email should contain a pdf attachment");
rc = smtp_open(config->server,
config->port,
SMTP_TEST_DEFAULT_CONNECTION_SECURITY,
SMTP_TEST_DEFAULT_FLAGS,
&config->smtp);
assert(rc == SMTP_STATUS_OK);
rc = smtp_auth(config->smtp,
SMTP_TEST_DEFAULT_AUTH_METHOD,
config->user,
config->pass);
assert(rc == SMTP_STATUS_OK);
rc = smtp_address_add(config->smtp,
SMTP_ADDRESS_FROM,
config->email_from,
NULL);
assert(rc == SMTP_STATUS_OK);
rc = smtp_address_add(config->smtp,
SMTP_ADDRESS_TO,
config->email_to,
"Test Email");
assert(rc == SMTP_STATUS_OK);
rc = smtp_attachment_add_path(config->smtp, name, path);
assert(rc == expect_rc);
rc = smtp_header_add(config->smtp, "Subject", config->subject);
assert(rc == expect_rc);
rc = smtp_mail(config->smtp, config->body);
assert(rc == expect_rc);
rc = smtp_close(config->smtp);
assert(rc == expect_rc);
}
/**
* Test harness for @ref smtp_attachment_add_fp.
*
* @param[in] config Server connection details.
* @param[in] name Name of the file to display to the recipient.
* @param[in] path Local file path to use as the attachment.
* @param[in] expect_rc Expected return code for the attachment function
* and every function after that.
*/
static void
smtp_func_test_attachment_fp(struct smtp_test_config *const config,
const char *const name,
const char *const path,
int expect_rc){
int rc;
FILE *fp;
strcpy(config->subject, "SMTP Test: Attachment (fp)");
strcpy(config->body, "This email should contain a pdf attachment");
fp = fopen(path, "r");
assert(fp != NULL);
rc = smtp_open(config->server,
config->port,
SMTP_TEST_DEFAULT_CONNECTION_SECURITY,
SMTP_TEST_DEFAULT_FLAGS,
&config->smtp);
assert(rc == SMTP_STATUS_OK);
rc = smtp_address_add(config->smtp,
SMTP_ADDRESS_FROM,
config->email_from,
NULL);
assert(rc == SMTP_STATUS_OK);
rc = smtp_address_add(config->smtp,
SMTP_ADDRESS_TO,
config->email_to,
"Test Email");
assert(rc == SMTP_STATUS_OK);
rc = smtp_attachment_add_fp(config->smtp, name, fp);
assert(rc == expect_rc);
rc = smtp_header_add(config->smtp, "Subject", config->subject);
assert(rc == expect_rc);
rc = smtp_mail(config->smtp, config->body);
assert(rc == expect_rc);
rc = smtp_close(config->smtp);
assert(rc == expect_rc);
rc = fclose(fp);
assert(rc == 0);
}
/**
* Test harness for @ref smtp_attachment_add_mem.
*
* @param[in] config Server connection details.
* @param[in] num_attachment Number of attachments to send.
*/
static void
smtp_func_test_attachment_mem(struct smtp_test_config *const config,
size_t num_attachment){
size_t i;
char attachment_name[SMTP_MAX_ATTACHMENT_NAME_LEN];
char attachment_data[100];
int rc;
sprintf(config->subject,
"SMTP Test: Attachments (%u)",
(unsigned)num_attachment);
sprintf(config->body,
"You should have %u attachments in this email. "
"Each attachment should contain the text "
"\"Attachment# <number>\"",
(unsigned)num_attachment);
rc = smtp_open(config->server,
config->port,
SMTP_TEST_DEFAULT_CONNECTION_SECURITY,
SMTP_TEST_DEFAULT_FLAGS,
&config->smtp);
assert(rc == SMTP_STATUS_OK);
rc = smtp_auth(config->smtp,
SMTP_TEST_DEFAULT_AUTH_METHOD,
config->user,
config->pass);
assert(rc == SMTP_STATUS_OK);
rc = smtp_address_add(config->smtp,
SMTP_ADDRESS_FROM,
config->email_from,
NULL);
assert(rc == SMTP_STATUS_OK);
rc = smtp_address_add(config->smtp,
SMTP_ADDRESS_TO,
config->email_to,
"Test Email");
assert(rc == SMTP_STATUS_OK);
for(i = 0; i < num_attachment; i++){
sprintf(attachment_name, "%u.txt", (unsigned)(i + 1));
sprintf(attachment_data, "Attachment# %u", (unsigned)(i + 1));
rc = smtp_attachment_add_mem(config->smtp,
attachment_name,
attachment_data,
-1);
assert(rc == SMTP_STATUS_OK);
}
rc = smtp_header_add(config->smtp, "Subject", config->subject);
assert(rc == SMTP_STATUS_OK);
rc = smtp_mail(config->smtp, config->body);
assert(rc == SMTP_STATUS_OK);
rc = smtp_close(config->smtp);
assert(rc == SMTP_STATUS_OK);
}
/**
* Run all tests for @ref smtp_attachment_add_mem.
*
* @param[in] config Server connection details.
*/
static void
smtp_func_test_all_attachments_mem(struct smtp_test_config *const config){
/* Send one attachment using the mem interface. */
smtp_func_test_attachment_mem(config, 1);
/* Send 10 attachments in one email. */
smtp_func_test_attachment_mem(config, 10);
}
/**
* Run all tests for @ref smtp_attachment_add_path.
*
* @param[in] config Server connection details.
*/
static void
smtp_func_test_all_attachments_path(struct smtp_test_config *const config){
/* Send a PDF test file using the path interface. */
smtp_func_test_attachment_path(config,
"test.pdf",
"test/test.pdf",
SMTP_STATUS_OK);
/* Try to send a file that does not exist. */
smtp_func_test_attachment_path(config,
"noexist.txt",
"test/noexist.txt",
SMTP_STATUS_FILE);
}
/**
* Run all tests for @ref smtp_attachment_add_fp.
*
* @param[in] config Server connection details.
*/
static void
smtp_func_test_all_attachments_fp(struct smtp_test_config *const config){
smtp_func_test_attachment_fp(config,
"test.pdf",
"test/test.pdf",
SMTP_STATUS_OK);
}
/**
* Test different ways of loading file attachments onto an SMTP context.
*
* @param[in] config SMTP server context.
*/
static void
smtp_func_test_all_attachments(struct smtp_test_config *const config){
smtp_func_test_all_attachments_path(config);
smtp_func_test_all_attachments_fp(config);
smtp_func_test_all_attachments_mem(config);
}
/**
* Test multiple ways of sending to different recipients.
*
* @param[in] config SMTP server context.
*/
static void
smtp_func_test_all_address(struct smtp_test_config *const config){
int rc;
rc = smtp_open(config->server,
config->port,
SMTP_TEST_DEFAULT_CONNECTION_SECURITY,
SMTP_TEST_DEFAULT_FLAGS,
&config->smtp);
assert(rc == SMTP_STATUS_OK);
/* Multiple TO addresses. */
smtp_header_clear_all(config->smtp);
rc = smtp_header_add(config->smtp,
"Subject",
"SMTP Test: Multiple TO Addresses");
assert(rc == SMTP_STATUS_OK);
rc = smtp_address_add(config->smtp,
SMTP_ADDRESS_FROM,
config->email_from,
NULL);
assert(rc == SMTP_STATUS_OK);
rc = smtp_address_add(config->smtp,
SMTP_ADDRESS_TO,
config->email_to,
"Test Email");
assert(rc == SMTP_STATUS_OK);
rc = smtp_address_add(config->smtp,
SMTP_ADDRESS_TO,
config->email_to_2,
"Test Email");
assert(rc == SMTP_STATUS_OK);
rc = smtp_mail(config->smtp,
"This email should contain two TO recipients.");
assert(rc == SMTP_STATUS_OK);
/* One BCC address. */
smtp_header_clear_all(config->smtp);
rc = smtp_header_add(config->smtp,
"Subject",
"SMTP Test: BCC Address");
assert(rc == SMTP_STATUS_OK);
rc = smtp_address_add(config->smtp,
SMTP_ADDRESS_FROM,
config->email_from,
NULL);
assert(rc == SMTP_STATUS_OK);
rc = smtp_address_add(config->smtp,
SMTP_ADDRESS_BCC,
config->email_to,
"Test Email");
assert(rc == SMTP_STATUS_OK);
rc = smtp_mail(config->smtp,
"This email should contain one BCC recipient.");
assert(rc == SMTP_STATUS_OK);
/* One TO and one BCC address. */
smtp_header_clear_all(config->smtp);
rc = smtp_header_add(config->smtp,
"Subject",
"SMTP Test: TO and BCC Addresses");
assert(rc == SMTP_STATUS_OK);
rc = smtp_address_add(config->smtp,
SMTP_ADDRESS_FROM,
config->email_from,
NULL);
assert(rc == SMTP_STATUS_OK);
rc = smtp_address_add(config->smtp,
SMTP_ADDRESS_TO,
config->email_to,
"Test Email");
assert(rc == SMTP_STATUS_OK);
rc = smtp_address_add(config->smtp,
SMTP_ADDRESS_BCC,
config->email_to_2,
"Test Email");
assert(rc == SMTP_STATUS_OK);
rc = smtp_mail(config->smtp,
"This email should contain one TO and one BCC recipient.");
assert(rc == SMTP_STATUS_OK);
/* One TO, CC, and BCC addresses. */
smtp_header_clear_all(config->smtp);
rc = smtp_header_add(config->smtp,
"Subject",
"SMTP Test: TO, CC, and BCC Addresses");
assert(rc == SMTP_STATUS_OK);
rc = smtp_address_add(config->smtp,
SMTP_ADDRESS_FROM,
config->email_from,
NULL);
assert(rc == SMTP_STATUS_OK);
rc = smtp_address_add(config->smtp,
SMTP_ADDRESS_TO,
config->email_to,
"Test Email");
assert(rc == SMTP_STATUS_OK);
rc = smtp_address_add(config->smtp,
SMTP_ADDRESS_CC,
config->email_to_2,
"Test Email");
assert(rc == SMTP_STATUS_OK);
rc = smtp_address_add(config->smtp,
SMTP_ADDRESS_BCC,
config->email_to_3,
"Test Email");
assert(rc == SMTP_STATUS_OK);
rc = smtp_mail(config->smtp,
"This email should contain one TO, CC, and BCC recipient.");
assert(rc == SMTP_STATUS_OK);
/* No FROM address. */
smtp_header_clear_all(config->smtp);
rc = smtp_header_add(config->smtp,
"Subject",
"SMTP Test: No FROM address");
assert(rc == SMTP_STATUS_OK);
rc = smtp_address_add(config->smtp,
SMTP_ADDRESS_TO,
config->email_to,
"Test Email");
assert(rc == SMTP_STATUS_OK);
rc = smtp_mail(config->smtp,
"This email should not have a FROM address in the header.");
assert(rc == SMTP_STATUS_OK);
rc = smtp_close(config->smtp);
assert(rc == SMTP_STATUS_OK);
}
/**
* Test scenario where the caller provides a custom date value in the header.
*
* This should override the default date implementation which uses the current
* local date.
*
* @param[in] config SMTP server context.
*/
static void
smtp_func_test_header_custom_date(struct smtp_test_config *const config){
int rc;
rc = smtp_open(config->server,
config->port,
SMTP_TEST_DEFAULT_CONNECTION_SECURITY,
SMTP_TEST_DEFAULT_FLAGS,
&config->smtp);
assert(rc == SMTP_STATUS_OK);
smtp_header_clear_all(config->smtp);
rc = smtp_header_add(config->smtp,
"Subject",
"SMTP Test: Custom Date");
assert(rc == SMTP_STATUS_OK);
rc = smtp_header_add(config->smtp,
"Date",
"Thu, 21 May 1998 05:33:29 -0700");
assert(rc == SMTP_STATUS_OK);
rc = smtp_address_add(config->smtp,
SMTP_ADDRESS_FROM,
config->email_from,
NULL);
assert(rc == SMTP_STATUS_OK);
rc = smtp_address_add(config->smtp,
SMTP_ADDRESS_TO,
config->email_to,
"Test Email");
assert(rc == SMTP_STATUS_OK);
rc = smtp_mail(config->smtp,
"This email should contain a custom date header.");
assert(rc == SMTP_STATUS_OK);
rc = smtp_close(config->smtp);
assert(rc == SMTP_STATUS_OK);
}
/**
* Test scenario where the caller provides a NULL value for a header.
*
* This should prevent that header from generating in the email.
*
* @param[in] config SMTP server context.
*/
static void
smtp_func_test_header_null_no_date(struct smtp_test_config *const config){
int rc;
rc = smtp_open(config->server,
config->port,
SMTP_TEST_DEFAULT_CONNECTION_SECURITY,
SMTP_TEST_DEFAULT_FLAGS,
&config->smtp);
assert(rc == SMTP_STATUS_OK);
smtp_header_clear_all(config->smtp);
rc = smtp_header_add(config->smtp,
"Subject",
"SMTP Test: Null Header (No Date)");
assert(rc == SMTP_STATUS_OK);
rc = smtp_header_add(config->smtp,
"Date",
NULL);
assert(rc == SMTP_STATUS_OK);
rc = smtp_address_add(config->smtp,
SMTP_ADDRESS_FROM,
config->email_from,
NULL);
assert(rc == SMTP_STATUS_OK);
rc = smtp_address_add(config->smtp,
SMTP_ADDRESS_TO,
config->email_to,
"Test Email");
assert(rc == SMTP_STATUS_OK);
rc = smtp_mail(config->smtp,
"This email should not contain a Date header.");
assert(rc == SMTP_STATUS_OK);
rc = smtp_close(config->smtp);
assert(rc == SMTP_STATUS_OK);
}
/**
* Test multiple ways of sending to different headers.
*
* @param[in] config SMTP server context.
*/
static void
smtp_func_test_all_headers(struct smtp_test_config *const config){
smtp_func_test_header_custom_date(config);
smtp_func_test_header_null_no_date(config);
}
/**
* Test failure points in the @ref smtp_open function.
*
* @param[in] config SMTP server context.
*/
static void
test_failure_open(struct smtp_test_config *const config){
int rc;
/* Initial memory allocation failure for the SMTP client context. */
g_smtp_test_err_calloc_ctr = 0;
rc = smtp_open(config->server,
config->port,
SMTP_TEST_DEFAULT_CONNECTION_SECURITY,
SMTP_TEST_DEFAULT_FLAGS,
&config->smtp);
g_smtp_test_err_calloc_ctr = -1;
assert(rc == SMTP_STATUS_NOMEM);
rc = smtp_close(config->smtp);
assert(rc == SMTP_STATUS_NOMEM);
/* Invalid hostname should cause getaddrinfo() to fail. */
rc = smtp_open(NULL,
NULL,
SMTP_TEST_DEFAULT_CONNECTION_SECURITY,
SMTP_TEST_DEFAULT_FLAGS,
&config->smtp);
assert(rc == SMTP_STATUS_CONNECT);
rc = smtp_close(config->smtp);
assert(rc == SMTP_STATUS_CONNECT);
/* socket() function failure. */
g_smtp_test_err_socket_ctr = 0;
rc = smtp_open(config->server,
config->port,
SMTP_TEST_DEFAULT_CONNECTION_SECURITY,
SMTP_TEST_DEFAULT_FLAGS,
&config->smtp);
g_smtp_test_err_socket_ctr = -1;
assert(rc == SMTP_STATUS_CONNECT);
rc = smtp_close(config->smtp);
assert(rc == SMTP_STATUS_CONNECT);
/* connect() function failure. */
g_smtp_test_err_connect_ctr = 0;
rc = smtp_open(config->server,
config->port,
SMTP_TEST_DEFAULT_CONNECTION_SECURITY,
SMTP_TEST_DEFAULT_FLAGS,
&config->smtp);
g_smtp_test_err_connect_ctr = -1;
assert(rc == SMTP_STATUS_CONNECT);
rc = smtp_close(config->smtp);
assert(rc == SMTP_STATUS_CONNECT);
/* SSL_CTX_new() failure. */
g_smtp_test_err_ssl_ctx_new_ctr = 0;
rc = smtp_open(config->server,
config->port,
SMTP_SECURITY_STARTTLS,
SMTP_TEST_DEFAULT_FLAGS,
&config->smtp);
g_smtp_test_err_ssl_ctx_new_ctr = -1;
assert(rc == SMTP_STATUS_HANDSHAKE);
rc = smtp_close(config->smtp);
assert(rc == SMTP_STATUS_HANDSHAKE);
/* ERR_peek_error() failure. */
g_smtp_test_err_err_peek_error_ctr = 0;
rc = smtp_open(config->server,
config->port,
SMTP_SECURITY_STARTTLS,
SMTP_TEST_DEFAULT_FLAGS,
&config->smtp);
g_smtp_test_err_err_peek_error_ctr = -1;
assert(rc == SMTP_STATUS_HANDSHAKE);
rc = smtp_close(config->smtp);
assert(rc == SMTP_STATUS_HANDSHAKE);
/* SSL_new() failure. */
g_smtp_test_err_ssl_new_ctr = 0;
rc = smtp_open(config->server,
config->port,
SMTP_SECURITY_STARTTLS,
SMTP_TEST_DEFAULT_FLAGS,
&config->smtp);
g_smtp_test_err_ssl_new_ctr = -1;
assert(rc == SMTP_STATUS_HANDSHAKE);
rc = smtp_close(config->smtp);
assert(rc == SMTP_STATUS_HANDSHAKE);
/* BIO_new_socket() failure. */
g_smtp_test_err_bio_new_socket_ctr = 0;
rc = smtp_open(config->server,
config->port,
SMTP_SECURITY_STARTTLS,
SMTP_TEST_DEFAULT_FLAGS,
&config->smtp);
g_smtp_test_err_bio_new_socket_ctr = -1;
assert(rc == SMTP_STATUS_HANDSHAKE);
rc = smtp_close(config->smtp);
assert(rc == SMTP_STATUS_HANDSHAKE);
/* SSL_connect() failure. */
g_smtp_test_err_ssl_connect_ctr = 0;
rc = smtp_open(config->server,
config->port,
SMTP_SECURITY_STARTTLS,
SMTP_TEST_DEFAULT_FLAGS,
&config->smtp);
g_smtp_test_err_ssl_connect_ctr = -1;
assert(rc == SMTP_STATUS_HANDSHAKE);
rc = smtp_close(config->smtp);
assert(rc == SMTP_STATUS_HANDSHAKE);
/* SSL_do_handshake() failure. */
g_smtp_test_err_ssl_do_handshake_ctr = 0;
rc = smtp_open(config->server,
config->port,
SMTP_SECURITY_STARTTLS,
SMTP_TEST_DEFAULT_FLAGS,
&config->smtp);
g_smtp_test_err_ssl_do_handshake_ctr = -1;
assert(rc == SMTP_STATUS_HANDSHAKE);
rc = smtp_close(config->smtp);
assert(rc == SMTP_STATUS_HANDSHAKE);
/* SSL_get_peer_certificate() failure. */
/***TODO: verify working*/
g_smtp_test_err_ssl_get_peer_certificate_ctr = 0;
rc = smtp_open(config->server,
config->port,
SMTP_SECURITY_STARTTLS,
SMTP_DEBUG,
&config->smtp);
g_smtp_test_err_ssl_get_peer_certificate_ctr = -1;
assert(rc == SMTP_STATUS_HANDSHAKE);
rc = smtp_close(config->smtp);
assert(rc == SMTP_STATUS_HANDSHAKE);
/*
* Ensure self-signed certificate throws an error. This error will occur by
* default since the test server uses a self-signed certificate.
*/
rc = smtp_open(config->server,
config->port,
SMTP_SECURITY_STARTTLS,
SMTP_DEBUG,
&config->smtp);
assert(rc == SMTP_STATUS_HANDSHAKE);
rc = smtp_close(config->smtp);
assert(rc == SMTP_STATUS_HANDSHAKE);
/*
* TLS failure in @ref smtp_initiate_handshake (1) when using direct
* TLS connection.
*/
g_smtp_test_err_ssl_ctx_new_ctr = 0;
rc = smtp_open(config->server,
config->port,
SMTP_SECURITY_TLS,
SMTP_TEST_DEFAULT_FLAGS,
&config->smtp);
g_smtp_test_err_ssl_ctx_new_ctr = -1;
assert(rc == SMTP_STATUS_HANDSHAKE);
rc = smtp_close(config->smtp);
assert(rc == SMTP_STATUS_HANDSHAKE);
/* @ref smtp_initiate_handshake failure in (2). */
g_smtp_test_err_recv_ctr = 0;
rc = smtp_open(config->server,
config->port,
SMTP_TEST_DEFAULT_CONNECTION_SECURITY,
SMTP_TEST_DEFAULT_FLAGS,
&config->smtp);
g_smtp_test_err_recv_ctr = -1;
assert(rc == SMTP_STATUS_HANDSHAKE);
rc = smtp_close(config->smtp);
assert(rc == SMTP_STATUS_HANDSHAKE);
/* @ref smtp_initiate_handshake failure in (3). */
g_smtp_test_err_send_ctr = 0;
rc = smtp_open(config->server,
config->port,
SMTP_TEST_DEFAULT_CONNECTION_SECURITY,
SMTP_TEST_DEFAULT_FLAGS,
&config->smtp);
g_smtp_test_err_send_ctr = -1;
assert(rc == SMTP_STATUS_HANDSHAKE);
rc = smtp_close(config->smtp);
assert(rc == SMTP_STATUS_HANDSHAKE);
/* @ref smtp_initiate_handshake STARTTLS send failure in (4). */
g_smtp_test_err_send_ctr = 1;
rc = smtp_open(config->server,
config->port,
SMTP_SECURITY_STARTTLS,
SMTP_TEST_DEFAULT_FLAGS,
&config->smtp);
g_smtp_test_err_send_ctr = -1;
assert(rc == SMTP_STATUS_HANDSHAKE);
rc = smtp_close(config->smtp);
assert(rc == SMTP_STATUS_HANDSHAKE);
/* @ref smtp_initiate_handshake failed response to STARTTLS in (4). */
g_smtp_test_err_recv_ctr = 2;
rc = smtp_open(config->server,
config->port,
SMTP_SECURITY_STARTTLS,
SMTP_TEST_DEFAULT_FLAGS,
&config->smtp);
g_smtp_test_err_recv_ctr = -1;
assert(rc == SMTP_STATUS_HANDSHAKE);
rc = smtp_close(config->smtp);
assert(rc == SMTP_STATUS_HANDSHAKE);
/* @ref smtp_initiate_handshake second EHLO in (4). */
g_smtp_test_err_ssl_write_ctr = 0;
rc = smtp_open(config->server,
config->port,
SMTP_SECURITY_STARTTLS,
SMTP_TEST_DEFAULT_FLAGS,
&config->smtp);
g_smtp_test_err_ssl_write_ctr = -1;
assert(rc == SMTP_STATUS_HANDSHAKE);
rc = smtp_close(config->smtp);
assert(rc == SMTP_STATUS_HANDSHAKE);
/* Failure in @ref BIO_should_retry. */
g_smtp_test_err_ssl_read_ctr = 0;
g_smtp_test_err_bio_should_retry_ctr = 0;
rc = smtp_open(config->server,
config->port,
SMTP_SECURITY_STARTTLS,
SMTP_TEST_DEFAULT_FLAGS,
&config->smtp);
g_smtp_test_err_ssl_read_ctr = -1;
g_smtp_test_err_bio_should_retry_ctr = -1;
assert(rc == SMTP_STATUS_HANDSHAKE);
rc = smtp_close(config->smtp);
assert(rc == SMTP_STATUS_HANDSHAKE);
}
/**
* Test different error results in the address functions, including memory
* allocation failures.
*
* @param[in] config SMTP server context.
*/
static void
test_failure_address_add(struct smtp_test_config *const config){
int rc;
rc = smtp_open(config->server,
config->port,
SMTP_TEST_DEFAULT_CONNECTION_SECURITY,
SMTP_TEST_DEFAULT_FLAGS,
&config->smtp);
assert(rc == SMTP_STATUS_OK);
/* Invalid SMTP status code. */
smtp_status_code_set(config->smtp, SMTP_STATUS_NOMEM);
rc = smtp_address_add(config->smtp,
SMTP_ADDRESS_FROM,
config->email_from,
NULL);
assert(rc == SMTP_STATUS_NOMEM);
/* Invalid email address. */
smtp_status_code_set(config->smtp, SMTP_STATUS_OK);
rc = smtp_address_add(config->smtp,
SMTP_ADDRESS_FROM,
"<invalid>",
NULL);
assert(rc == SMTP_STATUS_PARAM);
/* Invalid email name. */
smtp_status_code_set(config->smtp, SMTP_STATUS_OK);
rc = smtp_address_add(config->smtp,
SMTP_ADDRESS_FROM,
config->email_from,
"\"invalid\"");
assert(rc == SMTP_STATUS_PARAM);
/* Memory allocation failed while trying to increase size of address list. */
smtp_status_code_set(config->smtp, SMTP_STATUS_OK);
g_smtp_test_err_realloc_ctr = 0;
rc = smtp_address_add(config->smtp,
SMTP_ADDRESS_FROM,
config->email_from,
NULL);
g_smtp_test_err_realloc_ctr = -1;
assert(rc == SMTP_STATUS_NOMEM);
/* Failed to duplicate email string. */
smtp_status_code_set(config->smtp, SMTP_STATUS_OK);
g_smtp_test_err_malloc_ctr = 0;
rc = smtp_address_add(config->smtp,
SMTP_ADDRESS_FROM,
config->email_from,
"test name");
g_smtp_test_err_malloc_ctr = -1;
assert(rc == SMTP_STATUS_NOMEM);
/* Failed to duplicate name string. */
smtp_status_code_set(config->smtp, SMTP_STATUS_OK);
g_smtp_test_err_malloc_ctr = 1;
rc = smtp_address_add(config->smtp,
SMTP_ADDRESS_FROM,
config->email_from,
"test name");
g_smtp_test_err_malloc_ctr = -1;
assert(rc == SMTP_STATUS_NOMEM);
rc = smtp_close(config->smtp);
assert(rc == SMTP_STATUS_NOMEM);
}
/**
* Test different error results in the attachment functions, including memory
* allocation failures.
*
* @param[in] config SMTP server context.
*/
static void
test_failure_attachment_add(struct smtp_test_config *const config){
int rc;
FILE *fp;
rc = smtp_open(config->server,
config->port,
SMTP_TEST_DEFAULT_CONNECTION_SECURITY,
SMTP_TEST_DEFAULT_FLAGS,
&config->smtp);
assert(rc == SMTP_STATUS_OK);
/* Invalid SMTP status code. */
smtp_status_code_set(config->smtp, SMTP_STATUS_PARAM);
rc = smtp_attachment_add_mem(config->smtp, "valid", "test", -1);
assert(rc == SMTP_STATUS_PARAM);
/* Invalid filename parameter. */
smtp_status_code_set(config->smtp, SMTP_STATUS_OK);
rc = smtp_attachment_add_mem(config->smtp,
"\"invalid\"",
"test",
-1);
assert(rc == SMTP_STATUS_PARAM);
/* Memory allocation failure while increasing the attachment list size. */
smtp_status_code_set(config->smtp, SMTP_STATUS_OK);
g_smtp_test_err_realloc_ctr = 0;
rc = smtp_attachment_add_mem(config->smtp, "valid", "test", -1);
assert(rc == SMTP_STATUS_NOMEM);
g_smtp_test_err_realloc_ctr = -1;
/* Memory allocation failure while using smtp_strdup on file name. */
smtp_status_code_set(config->smtp, SMTP_STATUS_OK);
g_smtp_test_err_malloc_ctr = 0;
rc = smtp_attachment_add_mem(config->smtp, "valid", "test", -1);
assert(rc == SMTP_STATUS_NOMEM);
g_smtp_test_err_malloc_ctr = -1;
/* Memory allocation failure while using smtp_base64_encode. */
smtp_status_code_set(config->smtp, SMTP_STATUS_OK);
g_smtp_test_err_calloc_ctr = 0;
rc = smtp_attachment_add_mem(config->smtp, "valid", "test", -1);
assert(rc == SMTP_STATUS_NOMEM);
g_smtp_test_err_calloc_ctr = -1;
/* Invalid SMTP status code. */
smtp_status_code_set(config->smtp, SMTP_STATUS_PARAM);
rc = smtp_attachment_add_fp(config->smtp, "test", stdin);
assert(rc == SMTP_STATUS_PARAM);
/* @ref smtp_ffile_get_contents memory allocation failure. */
smtp_status_code_set(config->smtp, SMTP_STATUS_OK);
g_smtp_test_err_realloc_ctr = 0;
rc = smtp_attachment_add_fp(config->smtp, "test", stdin);
g_smtp_test_err_realloc_ctr = -1;
assert(rc == SMTP_STATUS_NOMEM);
/* @ref smtp_ffile_get_contents fread error. */
smtp_status_code_set(config->smtp, SMTP_STATUS_OK);
fp = fopen("COPYING", "r");
assert(fp);
g_smtp_test_err_ferror_ctr = 0;
rc = smtp_attachment_add_fp(config->smtp, "test", fp);
g_smtp_test_err_ferror_ctr = -1;
assert(rc == SMTP_STATUS_FILE);
rc = fclose(fp);
assert(rc == 0);
/* @ref smtp_file_get_contents memory allocation failure. */
smtp_status_code_set(config->smtp, SMTP_STATUS_OK);
g_smtp_test_err_realloc_ctr = 0;
rc = smtp_attachment_add_path(config->smtp, "test", "COPYING");
g_smtp_test_err_realloc_ctr = -1;
assert(rc == SMTP_STATUS_NOMEM);
/* Invalid SMTP status code. */
smtp_status_code_set(config->smtp, SMTP_STATUS_PARAM);
rc = smtp_attachment_add_path(config->smtp, "test", "test.txt");
assert(rc == SMTP_STATUS_PARAM);
rc = smtp_close(config->smtp);
assert(rc == SMTP_STATUS_PARAM);
}
/**
* Test different error results in the @ref smtp_header_add function,
* including memory allocation failures.
*
* @param[in] config SMTP server context.
*/
static void
test_failure_header_add(struct smtp_test_config *const config){
int rc;
rc = smtp_open(config->server,
config->port,
SMTP_TEST_DEFAULT_CONNECTION_SECURITY,
SMTP_TEST_DEFAULT_FLAGS,
&config->smtp);
assert(rc == SMTP_STATUS_OK);
/* Invalid SMTP status code. */
smtp_status_code_set(config->smtp, SMTP_STATUS_NOMEM);
rc = smtp_header_add(config->smtp,
"key",
"value");
assert(rc == SMTP_STATUS_NOMEM);
/* Invalid header key. */
smtp_status_code_set(config->smtp, SMTP_STATUS_OK);
rc = smtp_header_add(config->smtp,
"invalid:",
"value");
assert(rc == SMTP_STATUS_PARAM);
/* Invalid header value. */
smtp_status_code_set(config->smtp, SMTP_STATUS_OK);
rc = smtp_header_add(config->smtp,
"key",
"invalid\n");
assert(rc == SMTP_STATUS_PARAM);
/* Memory allocation failure while trying to increase header list size. */
smtp_status_code_set(config->smtp, SMTP_STATUS_OK);
g_smtp_test_err_realloc_ctr = 0;
rc = smtp_header_add(config->smtp,
"key",
"value");
g_smtp_test_err_realloc_ctr = -1;
assert(rc == SMTP_STATUS_NOMEM);
/* Failed to strdup header key. */
smtp_status_code_set(config->smtp, SMTP_STATUS_OK);
g_smtp_test_err_malloc_ctr = 0;
rc = smtp_header_add(config->smtp,
"key",
"value");
g_smtp_test_err_malloc_ctr = -1;
assert(rc == SMTP_STATUS_NOMEM);
/* Failed to strdup header value. */
smtp_status_code_set(config->smtp, SMTP_STATUS_OK);
g_smtp_test_err_malloc_ctr = 1;
rc = smtp_header_add(config->smtp,
"key",
"value");
g_smtp_test_err_malloc_ctr = -1;
assert(rc == SMTP_STATUS_NOMEM);
rc = smtp_close(config->smtp);
assert(rc == SMTP_STATUS_NOMEM);
}
/**
* Test different error results @ref smtp_status_code_set function.
*
* @param[in] config SMTP server context.
*/
static void
test_failure_status_code_set(struct smtp_test_config *const config){
int rc;
rc = smtp_open(config->server,
config->port,
SMTP_TEST_DEFAULT_CONNECTION_SECURITY,
SMTP_TEST_DEFAULT_FLAGS,
&config->smtp);
assert(rc == SMTP_STATUS_OK);
rc = smtp_status_code_set(config->smtp, -1);
assert(rc == SMTP_STATUS_PARAM);
rc = smtp_status_code_set(config->smtp, SMTP_STATUS__LAST);
assert(rc == SMTP_STATUS_PARAM);
rc = smtp_status_code_set(config->smtp, SMTP_STATUS_OK);
assert(rc == SMTP_STATUS_OK);
rc = smtp_close(config->smtp);
assert(rc == SMTP_STATUS_OK);
}
/***TODO: ensure all of these test cases correct */
/**
* Test different error conditions in the @ref smtp_mail function.
*
* @param[in] config SMTP server context.
*/
static void
test_failure_mail(struct smtp_test_config *const config){
int rc;
/* Invalid SMTP client context. */
test_smtp_open_default(config);
smtp_status_code_set(config->smtp, SMTP_STATUS_NOMEM);
rc = smtp_mail(config->smtp, "body");
assert(rc == SMTP_STATUS_NOMEM);
smtp_close(config->smtp);
/*
* Memory allocation failure in the first call to
* @ref smtp_mail_envelope_header.
*/
test_smtp_open_default(config);
g_smtp_test_err_malloc_ctr = 0;
rc = smtp_mail(config->smtp, "body");
g_smtp_test_err_malloc_ctr = -1;
assert(rc == SMTP_STATUS_NOMEM);
smtp_close(config->smtp);
/* Send failure in @ref smtp_mail_envelope_header. */
test_smtp_open_default(config);
g_smtp_test_err_send_ctr = 0;
g_smtp_test_err_ssl_write_ctr = 0;
rc = smtp_mail(config->smtp, "body");
g_smtp_test_err_send_ctr = -1;
g_smtp_test_err_ssl_write_ctr = -1;
assert(rc == SMTP_STATUS_SEND);
smtp_close(config->smtp);
/* Read failure in @ref smtp_mail_envelope_header. */
test_smtp_open_default(config);
g_smtp_test_err_recv_ctr = 0;
g_smtp_test_err_ssl_read_ctr = 0;
rc = smtp_mail(config->smtp, "body");
g_smtp_test_err_recv_ctr = -1;
g_smtp_test_err_ssl_read_ctr = -1;
assert(rc == SMTP_STATUS_RECV);
smtp_close(config->smtp);
/*
* Memory allocation failure in the second call to
* @ref smtp_mail_envelope_header.
*/
test_smtp_open_default(config);
g_smtp_test_err_send_ctr = 1;
g_smtp_test_err_ssl_write_ctr = 1;
rc = smtp_mail(config->smtp, "body");
g_smtp_test_err_send_ctr = -1;
g_smtp_test_err_ssl_write_ctr = -1;
assert(rc == SMTP_STATUS_SEND);
smtp_close(config->smtp);
/* Failed to send DATA command. */
test_smtp_open_default(config);
g_smtp_test_err_send_ctr = 2;
g_smtp_test_err_ssl_write_ctr = 2;
rc = smtp_mail(config->smtp, "body");
g_smtp_test_err_send_ctr = -1;
g_smtp_test_err_ssl_write_ctr = -1;
assert(rc == SMTP_STATUS_SEND);
smtp_close(config->smtp);
/* Failed to generate date string in @ref smtp_date_rfc_2822. */
test_smtp_open_default(config);
g_smtp_test_err_localtime_r_ctr = 0;
rc = smtp_mail(config->smtp, "body");
g_smtp_test_err_localtime_r_ctr = -1;
assert(rc == SMTP_STATUS_DATE);
smtp_close(config->smtp);
/* Failed to add Date header to list using @ref smtp_header_add. */
test_smtp_open_default(config);
g_smtp_test_err_realloc_ctr = 0;
rc = smtp_mail(config->smtp, "body");
g_smtp_test_err_realloc_ctr = -1;
assert(rc == SMTP_STATUS_NOMEM);
smtp_close(config->smtp);
/*
* Failed to add FROM address to header using
* @ref smtp_append_address_to_header.
*/
test_smtp_open_default(config);
g_smtp_test_err_realloc_ctr = 1;
rc = smtp_mail(config->smtp, "body");
g_smtp_test_err_realloc_ctr = -1;
assert(rc == SMTP_STATUS_NOMEM);
smtp_close(config->smtp);
/*
* Failed to add TO address to header using
* @ref smtp_append_address_to_header.
*/
test_smtp_open_default(config);
g_smtp_test_err_realloc_ctr = 2;
rc = smtp_mail(config->smtp, "body");
g_smtp_test_err_realloc_ctr = -1;
assert(rc == SMTP_STATUS_NOMEM);
smtp_close(config->smtp);
/*
* Failed to add CC address to header using
* @ref smtp_append_address_to_header.
*/
test_smtp_open_default(config);
g_smtp_test_err_realloc_ctr = 3;
rc = smtp_mail(config->smtp, "body");
g_smtp_test_err_realloc_ctr = -1;
assert(rc == SMTP_STATUS_NOMEM);
smtp_close(config->smtp);
/* Failed memory allocation in @ref smtp_print_header. */
test_smtp_open_default(config);
g_smtp_test_err_malloc_ctr = 1;
rc = smtp_mail(config->smtp, "body");
g_smtp_test_err_malloc_ctr = -1;
assert(rc == SMTP_STATUS_NOMEM);
smtp_close(config->smtp);
/* Failed @ref smtp_chunk_split in @ref smtp_print_header. */
test_smtp_open_default(config);
g_smtp_test_err_calloc_ctr = 0;
rc = smtp_mail(config->smtp, "body");
g_smtp_test_err_calloc_ctr = -1;
assert(rc == SMTP_STATUS_NOMEM);
smtp_close(config->smtp);
/* Failed @ref smtp_puts in @ref smtp_print_header. */
test_smtp_open_default(config);
g_smtp_test_err_send_ctr = 3;
g_smtp_test_err_ssl_write_ctr = 3;
rc = smtp_mail(config->smtp, "body");
g_smtp_test_err_send_ctr = -1;
g_smtp_test_err_ssl_write_ctr = -1;
assert(rc == SMTP_STATUS_SEND);
smtp_close(config->smtp);
/*
* Failure in @ref smtp_print_mime_email ->
* @ref smtp_print_mime_header_and_body ->
* @ref smtp_str_replace.
*/
test_smtp_open_default(config);
g_smtp_test_err_malloc_ctr = 2;
rc = smtp_mail(config->smtp, "body");
g_smtp_test_err_malloc_ctr = -1;
assert(rc == SMTP_STATUS_SEND);
smtp_close(config->smtp);
/*
* Memory allocation failure in @ref smtp_print_mime_email ->
* @ref smtp_print_mime_header_and_body ->
* @ref smtp_str_replace.
*/
test_smtp_open_default(config);
g_smtp_test_err_malloc_ctr = 3;
rc = smtp_mail(config->smtp, "body");
g_smtp_test_err_malloc_ctr = -1;
assert(rc == SMTP_STATUS_NOMEM);
smtp_close(config->smtp);
/*
* Send failure in @ref smtp_print_mime_email ->
* @ref smtp_print_mime_header_and_body ->
* @ref smtp_puts.
*/
test_smtp_open_default(config);
g_smtp_test_err_send_ctr = 4;
g_smtp_test_err_ssl_write_ctr = 4;
rc = smtp_mail(config->smtp, "body");
g_smtp_test_err_send_ctr = -1;
g_smtp_test_err_ssl_write_ctr = -1;
assert(rc == SMTP_STATUS_SEND);
smtp_close(config->smtp);
/* Memory allocation failure in @ref smtp_print_mime_attachment. */
test_smtp_open_default(config);
g_smtp_test_err_malloc_ctr = 4;
rc = smtp_mail(config->smtp, "body");
g_smtp_test_err_malloc_ctr = -1;
assert(rc == SMTP_STATUS_NOMEM);
smtp_close(config->smtp);
/* Send failure in @ref smtp_print_mime_end. */
test_smtp_open_default(config);
g_smtp_test_err_send_ctr = 5;
g_smtp_test_err_ssl_write_ctr = 5;
rc = smtp_mail(config->smtp, "body");
g_smtp_test_err_send_ctr = -1;
g_smtp_test_err_ssl_write_ctr = -1;
assert(rc == SMTP_STATUS_SEND);
smtp_close(config->smtp);
/* Failed to send end of DATA segment. */
test_smtp_open_default(config);
g_smtp_test_err_send_ctr = 6;
g_smtp_test_err_ssl_write_ctr = 6;
rc = smtp_mail(config->smtp, "body");
g_smtp_test_err_send_ctr = -1;
g_smtp_test_err_ssl_write_ctr = -1;
assert(rc == SMTP_STATUS_SEND);
smtp_close(config->smtp);
/* Invalid server response on DATA termination. */
test_smtp_open_default(config);
g_smtp_test_err_recv_ctr = 1;
g_smtp_test_err_ssl_read_ctr = 1;
rc = smtp_mail(config->smtp, "body");
g_smtp_test_err_recv_ctr = -1;
g_smtp_test_err_ssl_read_ctr = -1;
assert(rc == SMTP_STATUS_SERVER_RESPONSE);
smtp_close(config->smtp);
}
/**
* Test different error conditions in the @ref smtp_close function.
*
* @param[in] config SMTP server context.
*/
static void
test_failure_close(struct smtp_test_config *const config){
int rc;
/* Failed to send the QUIT command. */
rc = smtp_open(config->server,
config->port,
SMTP_TEST_DEFAULT_CONNECTION_SECURITY,
SMTP_TEST_DEFAULT_FLAGS,
&config->smtp);
assert(rc == SMTP_STATUS_OK);
g_smtp_test_err_send_ctr = 0;
g_smtp_test_err_ssl_write_ctr = 0;
rc = smtp_close(config->smtp);
g_smtp_test_err_send_ctr = -1;
g_smtp_test_err_ssl_write_ctr = -1;
assert(rc == SMTP_STATUS_SEND);
/* Failed to close the socket file descriptor. */
rc = smtp_open(config->server,
config->port,
SMTP_TEST_DEFAULT_CONNECTION_SECURITY,
SMTP_TEST_DEFAULT_FLAGS,
&config->smtp);
assert(rc == SMTP_STATUS_OK);
g_smtp_test_err_close_ctr = 0;
rc = smtp_close(config->smtp);
g_smtp_test_err_close_ctr = -1;
assert(rc == SMTP_STATUS_CLOSE);
}
/**
* Test different error results in the auth functions, including memory
* allocation failures and invalid credentials.
*
* @param[in] config SMTP server context.
*/
static void
test_failure_auth(struct smtp_test_config *const config){
int rc;
rc = smtp_open(config->server,
config->port,
SMTP_TEST_DEFAULT_CONNECTION_SECURITY,
SMTP_TEST_DEFAULT_FLAGS,
&config->smtp);
assert(rc == SMTP_STATUS_OK);
smtp_test_sleep(10);
/* Invalid SMTP status code. */
smtp_status_code_set(config->smtp, SMTP_STATUS_NOMEM);
rc = smtp_auth(config->smtp,
SMTP_AUTH_NONE,
config->user,
config->pass);
assert(rc == SMTP_STATUS_NOMEM);
smtp_test_sleep(10);
/* Invalid authentication method. */
smtp_status_code_set(config->smtp, SMTP_STATUS_OK);
rc = smtp_auth(config->smtp,
-1,
config->user,
config->pass);
assert(rc == SMTP_STATUS_PARAM);
smtp_test_sleep(10);
/* PLAIN - Invalid credentials. */
smtp_status_code_set(config->smtp, SMTP_STATUS_OK);
rc = smtp_auth(config->smtp,
SMTP_AUTH_PLAIN,
"invalid",
"invalid");
assert(rc == SMTP_STATUS_AUTH);
smtp_test_sleep(10);
/* PLAIN - Memory allocation failure in (1). */
smtp_status_code_set(config->smtp, SMTP_STATUS_OK);
g_smtp_test_err_malloc_ctr = 0;
rc = smtp_auth(config->smtp,
SMTP_AUTH_PLAIN,
config->user,
config->pass);
g_smtp_test_err_malloc_ctr = -1;
assert(rc == SMTP_STATUS_AUTH);
smtp_test_sleep(10);
/* PLAIN - @ref smtp_base64_encode failure in (2). */
smtp_status_code_set(config->smtp, SMTP_STATUS_OK);
g_smtp_test_err_calloc_ctr = 0;
rc = smtp_auth(config->smtp,
SMTP_AUTH_PLAIN,
config->user,
config->pass);
g_smtp_test_err_calloc_ctr = -1;
assert(rc == SMTP_STATUS_AUTH);
smtp_test_sleep(10);
/* PLAIN - Memory allocation failure in (3). */
smtp_status_code_set(config->smtp, SMTP_STATUS_OK);
g_smtp_test_err_malloc_ctr = 1;
rc = smtp_auth(config->smtp,
SMTP_AUTH_PLAIN,
config->user,
config->pass);
g_smtp_test_err_malloc_ctr = -1;
assert(rc == SMTP_STATUS_AUTH);
smtp_test_sleep(10);
/* PLAIN - @ref smtp_puts failure in (3). */
smtp_status_code_set(config->smtp, SMTP_STATUS_OK);
g_smtp_test_err_send_ctr = 0;
g_smtp_test_err_ssl_write_ctr = 0;
rc = smtp_auth(config->smtp,
SMTP_AUTH_PLAIN,
config->user,
config->pass);
g_smtp_test_err_send_ctr = -1;
g_smtp_test_err_ssl_write_ctr = -1;
assert(rc == SMTP_STATUS_AUTH);
smtp_test_sleep(10);
/* LOGIN - @ref smtp_base64_encode failure in (1). */
smtp_status_code_set(config->smtp, SMTP_STATUS_OK);
g_smtp_test_err_calloc_ctr = 0;
rc = smtp_auth(config->smtp,
SMTP_AUTH_LOGIN,
config->user,
config->pass);
g_smtp_test_err_calloc_ctr = -1;
assert(rc == SMTP_STATUS_AUTH);
smtp_test_sleep(10);
/* LOGIN - Memory allocation failure in (2). */
smtp_status_code_set(config->smtp, SMTP_STATUS_OK);
g_smtp_test_err_malloc_ctr = 0;
rc = smtp_auth(config->smtp,
SMTP_AUTH_LOGIN,
config->user,
config->pass);
g_smtp_test_err_malloc_ctr = -1;
assert(rc == SMTP_STATUS_AUTH);
smtp_test_sleep(10);
/* LOGIN - @ref smtp_puts send failure in (2). */
smtp_status_code_set(config->smtp, SMTP_STATUS_OK);
g_smtp_test_err_send_ctr = 0;
g_smtp_test_err_ssl_write_ctr = 0;
rc = smtp_auth(config->smtp,
SMTP_AUTH_LOGIN,
config->user,
config->pass);
g_smtp_test_err_send_ctr = -1;
g_smtp_test_err_ssl_write_ctr = -1;
assert(rc == SMTP_STATUS_AUTH);
smtp_test_sleep(10);
/* LOGIN - Response read error in (2). */
smtp_status_code_set(config->smtp, SMTP_STATUS_OK);
g_smtp_test_err_recv_ctr = 0;
g_smtp_test_err_ssl_read_ctr = 0;
rc = smtp_auth(config->smtp,
SMTP_AUTH_LOGIN,
config->user,
config->pass);
g_smtp_test_err_recv_ctr = -1;
g_smtp_test_err_ssl_read_ctr = -1;
assert(rc == SMTP_STATUS_AUTH);
smtp_test_sleep(10);
/* LOGIN - @ref smtp_base64_encode failure in (3). */
smtp_status_code_set(config->smtp, SMTP_STATUS_OK);
g_smtp_test_err_calloc_ctr = 2;
rc = smtp_auth(config->smtp,
SMTP_AUTH_LOGIN,
config->user,
config->pass);
g_smtp_test_err_calloc_ctr = -1;
assert(rc == SMTP_STATUS_AUTH);
smtp_test_sleep(10);
/* LOGIN - @ref smtp_puts_terminate failure in (3). */
smtp_status_code_set(config->smtp, SMTP_STATUS_OK);
g_smtp_test_err_send_ctr = 1;
g_smtp_test_err_ssl_write_ctr = 1;
rc = smtp_auth(config->smtp,
SMTP_AUTH_LOGIN,
config->user,
config->pass);
g_smtp_test_err_send_ctr = -1;
g_smtp_test_err_ssl_write_ctr = -1;
assert(rc == SMTP_STATUS_AUTH);
smtp_test_sleep(10);
/* LOGIN - @ref smtp_puts_terminate memory allocation failure in (3). */
smtp_status_code_set(config->smtp, SMTP_STATUS_OK);
g_smtp_test_err_malloc_ctr = 1;
rc = smtp_auth(config->smtp,
SMTP_AUTH_LOGIN,
config->user,
config->pass);
g_smtp_test_err_malloc_ctr = 0;
assert(rc == SMTP_STATUS_AUTH);
smtp_test_sleep(10);
/* LOGIN - Invalid credentials in (3). */
smtp_status_code_set(config->smtp, SMTP_STATUS_OK);
rc = smtp_auth(config->smtp,
SMTP_AUTH_LOGIN,
"invalid",
"invalid");
assert(rc == SMTP_STATUS_AUTH);
smtp_test_sleep(10);
/* CRAM-MD5 (1) @ref smtp_puts failure. */
smtp_status_code_set(config->smtp, SMTP_STATUS_OK);
g_smtp_test_err_send_ctr = 0;
g_smtp_test_err_ssl_write_ctr = 0;
rc = smtp_auth(config->smtp,
SMTP_AUTH_CRAM_MD5,
config->user,
config->pass);
g_smtp_test_err_send_ctr = -1;
g_smtp_test_err_ssl_write_ctr = -1;
assert(rc == SMTP_STATUS_AUTH);
smtp_test_sleep(10);
/* CRAM-MD5 (1) Response read error. */
smtp_status_code_set(config->smtp, SMTP_STATUS_OK);
g_smtp_test_err_recv_ctr = 0;
g_smtp_test_err_ssl_read_ctr = 0;
rc = smtp_auth(config->smtp,
SMTP_AUTH_CRAM_MD5,
config->user,
config->pass);
g_smtp_test_err_recv_ctr = -1;
g_smtp_test_err_ssl_read_ctr = -1;
assert(rc == SMTP_STATUS_AUTH);
smtp_test_sleep(10);
/* CRAM-MD5 (1) Server response bad. */
smtp_status_code_set(config->smtp, SMTP_STATUS_OK);
g_smtp_test_err_recv_ctr = 0;
g_smtp_test_err_ssl_read_ctr = 0;
strcpy(g_smtp_test_err_recv_bytes, "535 authentication failed");
rc = smtp_auth(config->smtp,
SMTP_AUTH_CRAM_MD5,
config->user,
config->pass);
g_smtp_test_err_recv_bytes[0] = '\0';
g_smtp_test_err_recv_ctr = -1;
g_smtp_test_err_ssl_read_ctr = -1;
assert(rc == SMTP_STATUS_AUTH);
smtp_test_sleep(10);
/* CRAM-MD5 (2) @ref smtp_base64_decode failure. */
smtp_status_code_set(config->smtp, SMTP_STATUS_OK);
g_smtp_test_err_calloc_ctr = 0;
rc = smtp_auth(config->smtp,
SMTP_AUTH_CRAM_MD5,
config->user,
config->pass);
g_smtp_test_err_calloc_ctr = -1;
assert(rc == SMTP_STATUS_AUTH);
smtp_test_sleep(10);
/* CRAM-MD5 (3) @ref HMAC failure. */
smtp_status_code_set(config->smtp, SMTP_STATUS_OK);
g_smtp_test_err_hmac_ctr = 0;
rc = smtp_auth(config->smtp,
SMTP_AUTH_CRAM_MD5,
config->user,
config->pass);
g_smtp_test_err_hmac_ctr = -1;
assert(rc == SMTP_STATUS_AUTH);
smtp_test_sleep(10);
/* CRAM-MD5 (4) @ref smtp_bin2hex failure. */
smtp_status_code_set(config->smtp, SMTP_STATUS_OK);
g_smtp_test_err_malloc_ctr = 0;
rc = smtp_auth(config->smtp,
SMTP_AUTH_CRAM_MD5,
config->user,
config->pass);
g_smtp_test_err_malloc_ctr = -1;
assert(rc == SMTP_STATUS_AUTH);
smtp_test_sleep(10);
/* CRAM-MD5 (5) Memory allocation failure. */
smtp_status_code_set(config->smtp, SMTP_STATUS_OK);
g_smtp_test_err_malloc_ctr = 1;
rc = smtp_auth(config->smtp,
SMTP_AUTH_CRAM_MD5,
config->user,
config->pass);
g_smtp_test_err_malloc_ctr = -1;
assert(rc == SMTP_STATUS_AUTH);
smtp_test_sleep(10);
/* CRAM-MD5 (6) @ref smtp_base64_encode failure. */
smtp_status_code_set(config->smtp, SMTP_STATUS_OK);
g_smtp_test_err_calloc_ctr = 1;
rc = smtp_auth(config->smtp,
SMTP_AUTH_CRAM_MD5,
config->user,
config->pass);
g_smtp_test_err_calloc_ctr = -1;
assert(rc == SMTP_STATUS_AUTH);
smtp_test_sleep(10);
/* CRAM-MD5 (7) @ref smtp_puts_terminate failure. */
smtp_status_code_set(config->smtp, SMTP_STATUS_OK);
g_smtp_test_err_send_ctr = 1;
g_smtp_test_err_ssl_write_ctr = 1;
rc = smtp_auth(config->smtp,
SMTP_AUTH_CRAM_MD5,
config->user,
config->pass);
g_smtp_test_err_send_ctr = -1;
g_smtp_test_err_ssl_write_ctr = -1;
assert(rc == SMTP_STATUS_AUTH);
smtp_test_sleep(10);
/* CRAM-MD5 (7) Invalid credentials. */
smtp_status_code_set(config->smtp, SMTP_STATUS_OK);
rc = smtp_auth(config->smtp,
SMTP_AUTH_CRAM_MD5,
"invalid",
"invalid");
assert(rc == SMTP_STATUS_AUTH);
rc = smtp_close(config->smtp);
assert(rc == SMTP_STATUS_AUTH);
smtp_test_sleep(10);
}
/**
* Simulate a timeout when reading server response.
*
* @param[in] config SMTP server context.
*/
static void
test_failure_timeout(struct smtp_test_config *const config){
int rc;
rc = smtp_open(config->server,
config->port,
SMTP_TEST_DEFAULT_CONNECTION_SECURITY,
SMTP_TEST_DEFAULT_FLAGS,
&config->smtp);
assert(rc == SMTP_STATUS_OK);
g_smtp_test_err_select_ctr = 0;
rc = smtp_mail(config->smtp, "body");
g_smtp_test_err_select_ctr = -1;
assert(rc == SMTP_STATUS_RECV);
rc = smtp_close(config->smtp);
assert(rc == SMTP_STATUS_RECV);
}
/**
* Test multiple failure modes when using the high-level interfaces.
*
* @param[in] config SMTP server context.
*/
static void
test_all_failure_modes(struct smtp_test_config *const config){
test_failure_open(config);
test_failure_auth(config);
test_failure_address_add(config);
test_failure_attachment_add(config);
test_failure_header_add(config);
test_failure_status_code_set(config);
test_failure_mail(config);
test_failure_close(config);
test_failure_timeout(config);
}
/**
* Run the functional tests on local postfix server.
*
* This configuration handles most of the functional testing and includes:
* - Failure modes.
* - Different combinations of connection and authentication methods.
* - Multiple attachments.
* - Multiple recipients
*/
static void
smtp_func_test_postfix(void){
struct smtp_test_config config;
int rc;
rc = smtp_test_config_load_from_file("test/config/postfix.txt", &config);
assert(rc == 0);
test_all_failure_modes(&config);
smtp_test_sleep(60);
smtp_func_test_all_status_code_get(&config);
smtp_func_test_all_connection_security(&config);
smtp_func_test_all_auth_methods(&config);
smtp_func_test_all_attachments(&config);
smtp_func_test_all_address(&config);
smtp_func_test_all_headers(&config);
}
/**
* Run the functional tests on the test SMTP gmail account.
*
* This only sends one email using a test gmail account. Most of the tests
* have been designed to work with a local postfix server instance.
*/
static void
smtp_func_test_gmail(void){
struct smtp_test_config config;
int rc;
rc = smtp_test_config_load_from_file("test/config/gmail.txt", &config);
assert(rc == 0);
fprintf(stderr, "SMTP TEST: sending test email using gmail account");
smtp_func_test_send_email(&config,
config.port,
SMTP_SECURITY_STARTTLS,
SMTP_DEBUG,
SMTP_AUTH_PLAIN,
"SMTP Test: gmail",
"test email sent through gmail server");
}
/**
* Run through all functional/integration tests for each test SMTP server.
*/
static void
smtp_func_test_all(void){
smtp_func_test_postfix();
smtp_func_test_gmail();
}
/**
* Configuration flags for the smtp testing framework.
*/
enum smtp_test_flags{
/**
* Only run the unit tests, skipping all functional testing.
*/
SMTP_TEST_UNIT_TESTING_ONLY = 1 << 0
};
/**
* Program configuration parameters.
*/
struct smtp_test{
/**
* See @ref smtp_test_flags.
*/
enum smtp_test_flags flags;
};
/**
* Main testing program entry point for testing the smtp-client library.
*
* This program supports the following options:
* - u - Only run unit tests, skipping the functional testing.
*
* @param[in] argc Number of arguments in @p argv.
* @param[in] argv String array containing the program name and any optional
* parameters described above.
* @retval 0 All tests passed.
* @retval 1 Error.
*/
int main(int argc, char *argv[]){
struct smtp_test smtp_test;
int c;
memset(&smtp_test, 0, sizeof(smtp_test));
while((c = getopt(argc, argv, "u")) != -1){
switch(c){
case 'u':
smtp_test.flags |= SMTP_TEST_UNIT_TESTING_ONLY;
break;
default:
return 1;
}
}
argc -= optind;
argv += optind;
smtp_unit_test_all();
if(!(smtp_test.flags & SMTP_TEST_UNIT_TESTING_ONLY)){
smtp_func_test_all();
}
return 0;
}