smtp-client
SMTP Client C Library
smtp.c File Reference

SMTP client library. More...

#include <netinet/in.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <netdb.h>
#include <unistd.h>
#include <errno.h>
#include <limits.h>
#include <signal.h>
#include <stdarg.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <openssl/bio.h>
#include <openssl/err.h>
#include <openssl/ssl.h>
#include <openssl/x509.h>
#include <openssl/x509v3.h>
#include "smtp.h"
+ Include dependency graph for smtp.c:

Go to the source code of this file.

Data Structures

struct  smtp_address
 
struct  smtp_attachment
 
struct  smtp_header
 
struct  smtp
 

Macros

#define SMTP_INTERNAL_DEFINE
 
#define SMTP_LINKAGE   static
 
#define SMTP_GETDELIM_READ_SZ   1000
 
#define SMTP_LINE_MAX   78
 
#define SMTP_DATE_MAX_SZ   (32 + 15)
 
#define SMTP_MIME_BOUNDARY_LEN   15
 
#define SMTP_FLAG_INVALID_MEMORY   (enum smtp_flag)(0xFFFFFFFF)
 

Functions

SMTP_LINKAGE int smtp_si_add_size_t (const size_t a, const size_t b, size_t *const result)
 
SMTP_LINKAGE int smtp_si_sub_size_t (const size_t a, const size_t b, size_t *const result)
 
SMTP_LINKAGE int smtp_si_mul_size_t (const size_t a, const size_t b, size_t *const result)
 
static enum smtp_status_code smtp_str_getdelimfd_read_timeout (struct smtp *const smtp)
 
static long smtp_str_getdelimfd_read (struct str_getdelimfd *const gdfd, void *buf, size_t count)
 
static int smtp_str_getdelimfd_search_delim (const char *const buf, size_t buf_len, int delim, size_t *const delim_pos)
 
SMTP_LINKAGE int smtp_str_getdelimfd_set_line_and_buf (struct str_getdelimfd *const gdfd, size_t copy_len)
 
SMTP_LINKAGE void smtp_str_getdelimfd_free (struct str_getdelimfd *const gdfd)
 
static enum str_getdelim_retcode smtp_str_getdelimfd_throw_error (struct str_getdelimfd *const gdfd)
 
SMTP_LINKAGE enum str_getdelim_retcode smtp_str_getdelimfd (struct str_getdelimfd *const gdfd)
 
SMTP_LINKAGE char * smtp_stpcpy (char *s1, const char *s2)
 
SMTP_LINKAGE void * smtp_reallocarray (void *ptr, size_t nmemb, size_t size)
 
SMTP_LINKAGE char * smtp_strdup (const char *s)
 
SMTP_LINKAGE char * smtp_str_replace (const char *const search, const char *const replace, const char *const s)
 
static void smtp_base64_encode_block (const char *const buf, size_t buf_block_sz, char *const b64)
 
SMTP_LINKAGE char * smtp_base64_encode (const char *const buf, size_t buflen)
 
static size_t smtp_base64_decode_block (const unsigned char *const buf, unsigned char *const decode)
 
SMTP_LINKAGE size_t smtp_base64_decode (const char *const buf, unsigned char **decode)
 
SMTP_LINKAGE char * smtp_bin2hex (const unsigned char *const s, size_t slen)
 
SMTP_LINKAGE size_t smtp_utf8_charlen (char c)
 
SMTP_LINKAGE int smtp_str_has_nonascii_utf8 (const char *const s)
 
SMTP_LINKAGE size_t smtp_strnlen_utf8 (const char *s, size_t maxlen)
 
SMTP_LINKAGE size_t smtp_fold_whitespace_get_offset (const char *const s, unsigned int maxlen)
 
SMTP_LINKAGE char * smtp_fold_whitespace (const char *const s, unsigned int maxlen)
 
SMTP_LINKAGE char * smtp_chunk_split (const char *const s, size_t chunklen, const char *const end)
 
SMTP_LINKAGE char * smtp_ffile_get_contents (FILE *stream, size_t *bytes_read)
 
SMTP_LINKAGE char * smtp_file_get_contents (const char *const filename, size_t *bytes_read)
 
SMTP_LINKAGE int smtp_parse_cmd_line (char *const line, struct smtp_command *const cmd)
 
static void smtp_puts_dbg (struct smtp *const smtp, const char *const prefix, const char *const str)
 
static enum str_getdelim_retcode smtp_getline (struct smtp *const smtp)
 
static int smtp_read_and_parse_code (struct smtp *const smtp)
 
SMTP_LINKAGE enum smtp_status_code smtp_write (struct smtp *const smtp, const char *const buf, size_t len)
 
static enum smtp_status_code smtp_puts (struct smtp *const smtp, const char *const s)
 
static enum smtp_status_code smtp_puts_terminate (struct smtp *const smtp, const char *const s)
 
static int smtp_connect (struct smtp *const smtp, const char *const server, const char *const port)
 
static int smtp_tls_init (struct smtp *const smtp, const char *const server)
 
static enum smtp_status_code smtp_ehlo (struct smtp *const smtp)
 
static int smtp_auth_plain (struct smtp *const smtp, const char *const user, const char *const pass)
 
static int smtp_auth_login (struct smtp *const smtp, const char *const user, const char *const pass)
 
static int smtp_auth_cram_md5 (struct smtp *const smtp, const char *const user, const char *const pass)
 
static void smtp_set_read_timeout (struct smtp *const smtp, long seconds)
 
static enum smtp_status_code smtp_initiate_handshake (struct smtp *const smtp, const char *const server, enum smtp_connection_security connection_security)
 
SMTP_LINKAGE int smtp_date_rfc_2822 (char *const date)
 
static int smtp_header_cmp_key (const void *const v1, const void *const v2)
 
static int smtp_header_exists (const struct smtp *const smtp, const char *const key)
 
static void smtp_gen_mime_boundary (char *const boundary)
 
static enum smtp_status_code smtp_print_mime_header_and_body (struct smtp *const smtp, const char *const boundary, const char *const body_dd)
 
static enum smtp_status_code smtp_print_mime_attachment (struct smtp *const smtp, const char *const boundary, const struct smtp_attachment *const attachment)
 
static enum smtp_status_code smtp_print_mime_end (struct smtp *const smtp, const char *const boundary)
 
static enum smtp_status_code smtp_print_mime_email (struct smtp *const smtp, const char *const body_dd)
 
static enum smtp_status_code smtp_print_nomime_email (struct smtp *const smtp, const char *const body_dd)
 
static enum smtp_status_code smtp_print_email (struct smtp *const smtp, const char *const body)
 
static enum smtp_status_code smtp_print_header (struct smtp *const smtp, const struct smtp_header *const header)
 
static enum smtp_status_code smtp_append_address_to_header (struct smtp *const smtp, enum smtp_address_type address_type, const char *const key)
 
static enum smtp_status_code smtp_mail_envelope_header (struct smtp *const smtp, const char *const header, const struct smtp_address *const address)
 
static int smtp_header_cmp (const void *v1, const void *v2)
 
SMTP_LINKAGE int smtp_header_key_validate (const char *const key)
 
SMTP_LINKAGE int smtp_header_value_validate (const char *const value)
 
SMTP_LINKAGE int smtp_address_validate_email (const char *const email)
 
SMTP_LINKAGE int smtp_address_validate_name (const char *const name)
 
SMTP_LINKAGE int smtp_attachment_validate_name (const char *const name)
 
enum smtp_status_code smtp_open (const char *const server, const char *const port, enum smtp_connection_security connection_security, enum smtp_flag flags, const char *const cafile, struct smtp **smtp)
 
enum smtp_status_code smtp_auth (struct smtp *const smtp, enum smtp_authentication_method auth_method, const char *const user, const char *const pass)
 
enum smtp_status_code smtp_mail (struct smtp *const smtp, const char *const body)
 
enum smtp_status_code smtp_close (struct smtp *smtp)
 
enum smtp_status_code smtp_status_code_get (const struct smtp *const smtp)
 
enum smtp_status_code smtp_status_code_clear (struct smtp *const smtp)
 
enum smtp_status_code smtp_status_code_set (struct smtp *const smtp, enum smtp_status_code status_code)
 
const char * smtp_status_code_errstr (enum smtp_status_code status_code)
 
enum smtp_status_code smtp_header_add (struct smtp *const smtp, const char *const key, const char *const value)
 
void smtp_header_clear_all (struct smtp *const smtp)
 
enum smtp_status_code smtp_address_add (struct smtp *const smtp, enum smtp_address_type type, const char *const email, const char *const name)
 
void smtp_address_clear_all (struct smtp *const smtp)
 
enum smtp_status_code smtp_attachment_add_path (struct smtp *const smtp, const char *const name, const char *const path)
 
enum smtp_status_code smtp_attachment_add_fp (struct smtp *const smtp, const char *const name, FILE *fp)
 
enum smtp_status_code smtp_attachment_add_mem (struct smtp *const smtp, const char *const name, const void *const data, size_t datasz)
 
void smtp_attachment_clear_all (struct smtp *const smtp)
 

Variables

static char g_base64_encode_table []
 
static signed char g_base64_decode_table []
 
static struct smtp g_smtp_error
 

Detailed Description

SMTP client library.

Author
James Humphrey (mail@.nosp@m.somn.nosp@m.isoft.nosp@m..com)
Version
1.00

This SMTP client library allows the user to send emails to an SMTP server. The user can include custom headers and MIME attachments.

This software has been placed into the public domain using CC0.

Definition in file smtp.c.

Macro Definition Documentation

◆ SMTP_DATE_MAX_SZ

#define SMTP_DATE_MAX_SZ   (32 + 15)

Maximum size of an RFC 2822 date string.

Thu, 21 May 1998 05:33:29 -0700
12345678901234567890123456789012
        10        20        30 32 (bytes)

Add more bytes to the 32 maximum size to silence compiler warning on the computed UTF offset.

Definition at line 2222 of file smtp.c.

Referenced by smtp_date_rfc_2822(), and smtp_mail().

◆ SMTP_FLAG_INVALID_MEMORY

#define SMTP_FLAG_INVALID_MEMORY   (enum smtp_flag)(0xFFFFFFFF)

Special flag value for the SMTP context used to determine if the initial memory allocation failed to create the context.

Definition at line 2945 of file smtp.c.

Referenced by smtp_close().

◆ SMTP_GETDELIM_READ_SZ

#define SMTP_GETDELIM_READ_SZ   1000

Increment the read buffer size by this amount if the delimiter has not been found.

Definition at line 88 of file smtp.c.

Referenced by smtp_str_getdelimfd().

◆ SMTP_INTERNAL_DEFINE

#define SMTP_INTERNAL_DEFINE

Get access to the smtp_result_code and smtp_command definitions.

Definition at line 59 of file smtp.c.

◆ SMTP_LINE_MAX

#define SMTP_LINE_MAX   78

Email header lines should have no more than 78 characters and must not be more than 998 characters.

Definition at line 1218 of file smtp.c.

Referenced by smtp_attachment_add_mem(), and smtp_print_header().

◆ SMTP_LINKAGE

◆ SMTP_MIME_BOUNDARY_LEN

#define SMTP_MIME_BOUNDARY_LEN   15

Minimum length of buffer required to hold the MIME boundary test: mimeXXXXXXXXXX 123456789012345 1 10 15 bytes

Definition at line 2370 of file smtp.c.

Referenced by smtp_gen_mime_boundary(), smtp_print_mime_attachment(), smtp_print_mime_email(), and smtp_print_mime_end().

Function Documentation

◆ smtp_address_add()

enum smtp_status_code smtp_address_add ( struct smtp *const  smtp,
enum smtp_address_type  type,
const char *const  email,
const char *const  name 
)

Add a FROM, TO, CC, or BCC address destination to this SMTP context.

Note
Some SMTP servers may reject over 100 recipients.
Parameters
[in]smtpSMTP client context.
[in]typeSee smtp_address_type.
[in]emailThe email address of the party. Must consist only of printable characters excluding the angle brackets (<) and (>).
[in]nameName or description of the party. Must consist only of printable characters, excluding the quote characters. If set to NULL or empty string, no name will get associated with this email.
Returns
See smtp_status_code.

Definition at line 3347 of file smtp.c.

References smtp::address_list, smtp_address::email, smtp_address::name, smtp::num_address, smtp_address_validate_email(), smtp_address_validate_name(), smtp_reallocarray(), smtp_si_add_size_t(), smtp_status_code_set(), SMTP_STATUS_NOMEM, SMTP_STATUS_OK, SMTP_STATUS_PARAM, smtp_strdup(), smtp::status_code, and smtp_address::type.

Referenced by SMTPMail::address_add(), mailx_send(), smtp_address_add_check(), and test_nossl_smtp().

3350  {
3351  struct smtp_address *new_address_list;
3352  struct smtp_address *new_address;
3353  size_t num_address_inc;
3354 
3355  if(smtp->status_code != SMTP_STATUS_OK){
3356  return smtp->status_code;
3357  }
3358 
3361  }
3362 
3363  if(name && smtp_address_validate_name(name) < 0){
3365  }
3366 
3367  if(smtp_si_add_size_t(smtp->num_address, 1, &num_address_inc)){
3369  }
3370 
3371  new_address_list = smtp_reallocarray(smtp->address_list,
3372  num_address_inc,
3373  sizeof(*new_address_list));
3374  if(new_address_list == NULL){
3376  }
3377  new_address = &new_address_list[smtp->num_address];
3378 
3379  smtp->address_list = new_address_list;
3380 
3381  new_address->type = type;
3382  new_address->email = smtp_strdup(email);
3383  if(name){
3384  new_address->name = smtp_strdup(name);
3385  }
3386  else{
3387  new_address->name = NULL;
3388  }
3389  if(new_address->email == NULL ||
3390  (new_address->name == NULL && name)){
3391  free(new_address->email);
3392  free(new_address->name);
3394  }
3395  smtp->num_address = num_address_inc;
3396 
3397  return smtp->status_code;
3398 }
enum smtp_address_type type
Definition: smtp.c:109
struct smtp_address * address_list
Definition: smtp.c:179
SMTP_LINKAGE void * smtp_reallocarray(void *ptr, size_t nmemb, size_t size)
Definition: smtp.c:622
SMTP_LINKAGE int smtp_address_validate_name(const char *const name)
Definition: smtp.c:2903
SMTP_LINKAGE int smtp_address_validate_email(const char *const email)
Definition: smtp.c:2878
enum smtp_status_code smtp_status_code_set(struct smtp *const smtp, enum smtp_status_code status_code)
Definition: smtp.c:3231
enum smtp_status_code status_code
Definition: smtp.c:208
SMTP_LINKAGE int smtp_si_add_size_t(const size_t a, const size_t b, size_t *const result)
Definition: smtp.c:252
SMTP_LINKAGE char * smtp_strdup(const char *s)
Definition: smtp.c:650
char * name
Definition: smtp.c:104
size_t num_address
Definition: smtp.c:184
char * email
Definition: smtp.c:99
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ smtp_address_clear_all()

void smtp_address_clear_all ( struct smtp *const  smtp)

Free all memory related to the address list.

Parameters
[in]smtpSMTP client context.

Definition at line 3401 of file smtp.c.

References smtp::address_list, smtp_address::email, smtp_address::name, and smtp::num_address.

Referenced by SMTPMail::address_clear_all(), smtp_close(), smtp_func_test_all_address(), and smtp_func_test_all_names().

3401  {
3402  size_t i;
3403  struct smtp_address *address;
3404 
3405  for(i = 0; i < smtp->num_address; i++){
3406  address = &smtp->address_list[i];
3407  free(address->email);
3408  free(address->name);
3409  }
3410  free(smtp->address_list);
3411  smtp->address_list = NULL;
3412  smtp->num_address = 0;
3413 }
struct smtp_address * address_list
Definition: smtp.c:179
char * name
Definition: smtp.c:104
size_t num_address
Definition: smtp.c:184
char * email
Definition: smtp.c:99
+ Here is the caller graph for this function:

◆ smtp_address_validate_email()

SMTP_LINKAGE int smtp_address_validate_email ( const char *const  email)

Validate characters in the email address.

The email address must consist only of printable characters excluding the angle brackets (<) and (>).

Parameters
[in]emailThe email address of the party.
Return values
0Successful validation.
-1Failed to validate.

Definition at line 2878 of file smtp.c.

References SMTP_LINKAGE.

Referenced by smtp_address_add(), and smtp_unit_test_all_smtp_address_validate_email().

2878  {
2879  size_t i;
2880  unsigned char uc;
2881 
2882  for(i = 0; email[i]; i++){
2883  uc = (unsigned char)email[i];
2884  if(uc <= ' ' || uc == 127 ||
2885  uc == '<' || uc == '>'){
2886  return -1;
2887  }
2888  }
2889  return 0;
2890 }
char * email
Definition: smtp.c:99
+ Here is the caller graph for this function:

◆ smtp_address_validate_name()

SMTP_LINKAGE int smtp_address_validate_name ( const char *const  name)

Validate characters in the email name.

Email user name must consist only of printable characters, excluding the double quote character.

Parameters
[in]nameEmail name to validate.
Return values
0Successful validation.
-1Failed to validate.

Definition at line 2903 of file smtp.c.

References SMTP_LINKAGE.

Referenced by smtp_address_add(), and smtp_unit_test_all_smtp_address_validate_name().

2903  {
2904  size_t i;
2905  unsigned char uc;
2906 
2907  for(i = 0; name[i]; i++){
2908  uc = (unsigned char)name[i];
2909  if(uc < ' ' || uc == 127 || uc == '\"'){
2910  return -1;
2911  }
2912  }
2913  return 0;
2914 }
char * name
Definition: smtp.c:104
+ Here is the caller graph for this function:

◆ smtp_append_address_to_header()

static enum smtp_status_code smtp_append_address_to_header ( struct smtp *const  smtp,
enum smtp_address_type  address_type,
const char *const  key 
)
static

Take a FROM, TO, and CC address and add it into the email header list.

The following example shows what the final header might look like when the client sends an email to two CC addresses: Cc: mail1@example.com, mail2@example.com

Parameters
[in]smtpSMTP client context.
[in]address_typeSee smtp_address_type.
[in]keyHeader key value, for example, To From Cc.
Returns
See smtp_status_code.

Definition at line 2674 of file smtp.c.

References smtp::address_list, smtp_address::email, smtp_address::name, smtp::num_address, realloc, smtp_header_add(), smtp_si_add_size_t(), smtp_status_code_set(), SMTP_STATUS_NOMEM, smtp_stpcpy(), smtp::status_code, strlen, and smtp_address::type.

Referenced by smtp_mail().

2676  {
2677  size_t i;
2678  size_t num_address_in_header;
2679  size_t header_value_sz;
2680  size_t name_slen;
2681  size_t email_slen;
2682  size_t concat_len;
2683  struct smtp_address *address;
2684  char *header_value;
2685  char *header_value_new;
2686  char *concat;
2687 
2688  num_address_in_header = 0;
2689  header_value_sz = 0;
2690  header_value = NULL;
2691  concat_len = 0;
2692  for(i = 0; i < smtp->num_address; i++){
2693  address = &smtp->address_list[i];
2694  if(address->type == address_type){
2695  name_slen = 0;
2696  if(address->name){
2697  name_slen = strlen(address->name);
2698  }
2699  email_slen = strlen(address->email);
2700 
2701  /*
2702  * ', "' NAME '" <' EMAIL > \0
2703  * header_value_sz += 3 + name_slen + 3 + email_slen + 1 + 1
2704  */
2705  if(smtp_si_add_size_t(header_value_sz, name_slen, &header_value_sz) ||
2706  smtp_si_add_size_t(header_value_sz, email_slen, &header_value_sz) ||
2707  smtp_si_add_size_t(header_value_sz, 3 + 3 + 1 + 1, &header_value_sz)||
2708  (header_value_new = realloc(header_value,
2709  header_value_sz)) == NULL){
2710  free(header_value);
2712  }
2713  header_value = header_value_new;
2714  concat = header_value + concat_len;
2715  if(num_address_in_header > 0){
2716  concat = smtp_stpcpy(concat, ", ");
2717  }
2718 
2719  if(name_slen){
2720  concat = smtp_stpcpy(concat, "\"");
2721  concat = smtp_stpcpy(concat, address->name);
2722  concat = smtp_stpcpy(concat, "\" ");
2723  }
2724  concat = smtp_stpcpy(concat, "<");
2725  concat = smtp_stpcpy(concat, address->email);
2726  concat = smtp_stpcpy(concat, ">");
2727  num_address_in_header += 1;
2728  concat_len = (size_t)(concat - header_value);
2729  }
2730  }
2731 
2732  if(header_value){
2733  smtp_header_add(smtp, key, header_value);
2734  free(header_value);
2735  }
2736  return smtp->status_code;
2737 }
enum smtp_address_type type
Definition: smtp.c:109
struct smtp_address * address_list
Definition: smtp.c:179
#define realloc
Definition: seams.h:161
enum smtp_status_code smtp_header_add(struct smtp *const smtp, const char *const key, const char *const value)
Definition: smtp.c:3278
enum smtp_status_code smtp_status_code_set(struct smtp *const smtp, enum smtp_status_code status_code)
Definition: smtp.c:3231
enum smtp_status_code status_code
Definition: smtp.c:208
#define strlen
Definition: seams.h:273
SMTP_LINKAGE char * smtp_stpcpy(char *s1, const char *s2)
Definition: smtp.c:599
SMTP_LINKAGE int smtp_si_add_size_t(const size_t a, const size_t b, size_t *const result)
Definition: smtp.c:252
char * name
Definition: smtp.c:104
size_t num_address
Definition: smtp.c:184
char * email
Definition: smtp.c:99
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ smtp_attachment_add_fp()

enum smtp_status_code smtp_attachment_add_fp ( struct smtp *const  smtp,
const char *const  name,
FILE *  fp 
)

Add an attachment using a file pointer.

See smtp_attachment_add_mem for more details.

Parameters
[in]smtpSMTP client context.
[in]nameFilename of the attachment shown to recipients. Must consist only of printable ASCII characters, excluding the quote characters (') and (").
[in]fpFile pointer already opened by the caller.
Returns
See smtp_status_code.

Definition at line 3439 of file smtp.c.

References smtp_attachment_add_mem(), smtp_ffile_get_contents(), smtp_status_code_set(), SMTP_STATUS_FILE, SMTP_STATUS_NOMEM, SMTP_STATUS_OK, and smtp::status_code.

Referenced by SMTPMail::attachment_add_fp(), smtp_func_test_attachment_fp(), and test_failure_attachment_add().

3441  {
3442  char *data;
3443  size_t bytes_read;
3444 
3445  if(smtp->status_code != SMTP_STATUS_OK){
3446  return smtp->status_code;
3447  }
3448 
3449  errno = 0;
3450  if((data = smtp_ffile_get_contents(fp, &bytes_read)) == NULL){
3451  if(errno == ENOMEM){
3453  }
3454  return smtp_status_code_set(smtp, SMTP_STATUS_FILE);
3455  }
3456  smtp_attachment_add_mem(smtp, name, data, bytes_read);
3457  free(data);
3458  return smtp->status_code;
3459 }
SMTP_LINKAGE char * smtp_ffile_get_contents(FILE *stream, size_t *bytes_read)
Definition: smtp.c:1386
enum smtp_status_code smtp_attachment_add_mem(struct smtp *const smtp, const char *const name, const void *const data, size_t datasz)
Definition: smtp.c:3462
enum smtp_status_code smtp_status_code_set(struct smtp *const smtp, enum smtp_status_code status_code)
Definition: smtp.c:3231
enum smtp_status_code status_code
Definition: smtp.c:208
char * name
Definition: smtp.c:104
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ smtp_attachment_add_mem()

enum smtp_status_code smtp_attachment_add_mem ( struct smtp *const  smtp,
const char *const  name,
const void *const  data,
size_t  datasz 
)

Add a MIME attachment to this SMTP context with the data retrieved from memory.

The attachment data will get base64 encoded before sending to the server.

Parameters
[in]smtpSMTP client context.
[in]nameFilename of the attachment shown to recipients. Must consist only of printable ASCII characters, excluding the quote characters (') and (").
[in]dataRaw attachment data stored in memory.
[in]dataszNumber of bytes in data, or -1 if data null-terminated.
Returns
See smtp_status_code.

Definition at line 3462 of file smtp.c.

References smtp::attachment_list, smtp_attachment::b64_data, smtp_attachment::name, smtp::num_attachment, SIZE_MAX, smtp_attachment_validate_name(), smtp_base64_encode(), smtp_chunk_split(), SMTP_LINE_MAX, smtp_reallocarray(), smtp_si_add_size_t(), smtp_status_code_set(), SMTP_STATUS_NOMEM, SMTP_STATUS_OK, SMTP_STATUS_PARAM, smtp_strdup(), smtp::status_code, and strlen.

Referenced by SMTPMail::attachment_add_mem(), smtp_attachment_add_fp(), smtp_attachment_add_path(), smtp_func_test_attachment_long_text(), smtp_func_test_attachment_mem(), test_failure_attachment_add(), and test_smtp_open_default().

3465  {
3466  size_t num_attachment_inc;
3467  struct smtp_attachment *new_attachment_list;
3468  struct smtp_attachment *new_attachment;
3469  char *b64_encode;
3470 
3471  if(smtp->status_code != SMTP_STATUS_OK){
3472  return smtp->status_code;
3473  }
3474 
3477  }
3478 
3479  if(datasz == SIZE_MAX){
3480  datasz = strlen(data);
3481  }
3482 
3483  if(smtp_si_add_size_t(smtp->num_attachment, 1, &num_attachment_inc) ||
3484  (new_attachment_list = smtp_reallocarray(
3485  smtp->attachment_list,
3486  num_attachment_inc,
3487  sizeof(*new_attachment_list))) == NULL){
3489  }
3490  smtp->attachment_list = new_attachment_list;
3491  new_attachment = &new_attachment_list[smtp->num_attachment];
3492 
3493  new_attachment->name = smtp_strdup(name);
3494  b64_encode = smtp_base64_encode(data, datasz);
3495  if(new_attachment->name == NULL || b64_encode == NULL){
3496  free(new_attachment->name);
3497  free(b64_encode);
3499  }
3500 
3501  new_attachment->b64_data = smtp_chunk_split(b64_encode,
3502  SMTP_LINE_MAX,
3503  "\r\n");
3504  free(b64_encode);
3505  if(new_attachment->b64_data == NULL){
3506  free(new_attachment->name);
3508  }
3509 
3510  smtp->num_attachment = num_attachment_inc;
3511  return smtp->status_code;
3512 }
char * b64_data
Definition: smtp.c:129
#define SIZE_MAX
Definition: smtp.h:23
SMTP_LINKAGE void * smtp_reallocarray(void *ptr, size_t nmemb, size_t size)
Definition: smtp.c:622
#define SMTP_LINE_MAX
Definition: smtp.c:1218
struct smtp_attachment * attachment_list
Definition: smtp.c:189
SMTP_LINKAGE char * smtp_base64_encode(const char *const buf, size_t buflen)
Definition: smtp.c:825
size_t num_attachment
Definition: smtp.c:194
enum smtp_status_code smtp_status_code_set(struct smtp *const smtp, enum smtp_status_code status_code)
Definition: smtp.c:3231
enum smtp_status_code status_code
Definition: smtp.c:208
#define strlen
Definition: seams.h:273
SMTP_LINKAGE int smtp_attachment_validate_name(const char *const name)
Definition: smtp.c:2927
char * name
Definition: smtp.c:124
SMTP_LINKAGE int smtp_si_add_size_t(const size_t a, const size_t b, size_t *const result)
Definition: smtp.c:252
SMTP_LINKAGE char * smtp_chunk_split(const char *const s, size_t chunklen, const char *const end)
Definition: smtp.c:1312
SMTP_LINKAGE char * smtp_strdup(const char *s)
Definition: smtp.c:650
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ smtp_attachment_add_path()

enum smtp_status_code smtp_attachment_add_path ( struct smtp *const  smtp,
const char *const  name,
const char *const  path 
)

Add a file attachment from a path.

See smtp_attachment_add_mem for more details.

Parameters
[in]smtpSMTP client context.
[in]nameFilename of the attachment shown to recipients. Must consist only of printable ASCII characters, excluding the quote characters (') and (").
[in]pathPath to file.
Returns
See smtp_status_code.

Definition at line 3416 of file smtp.c.

References smtp_attachment_add_mem(), smtp_file_get_contents(), smtp_status_code_set(), SMTP_STATUS_FILE, SMTP_STATUS_NOMEM, SMTP_STATUS_OK, and smtp::status_code.

Referenced by SMTPMail::attachment_add_path(), mailx_send(), smtp_func_test_attachment_path(), smtp_func_test_attachment_pdf(), smtp_func_test_gmail_attachment(), and test_failure_attachment_add().

3418  {
3419  char *data;
3420  size_t bytes_read;
3421 
3422  if(smtp->status_code != SMTP_STATUS_OK){
3423  return smtp->status_code;
3424  }
3425 
3426  errno = 0;
3427  if((data = smtp_file_get_contents(path, &bytes_read)) == NULL){
3428  if(errno == ENOMEM){
3430  }
3431  return smtp_status_code_set(smtp, SMTP_STATUS_FILE);
3432  }
3433  smtp_attachment_add_mem(smtp, name, data, bytes_read);
3434  free(data);
3435  return smtp->status_code;
3436 }
SMTP_LINKAGE char * smtp_file_get_contents(const char *const filename, size_t *bytes_read)
Definition: smtp.c:1439
enum smtp_status_code smtp_attachment_add_mem(struct smtp *const smtp, const char *const name, const void *const data, size_t datasz)
Definition: smtp.c:3462
enum smtp_status_code smtp_status_code_set(struct smtp *const smtp, enum smtp_status_code status_code)
Definition: smtp.c:3231
enum smtp_status_code status_code
Definition: smtp.c:208
char * name
Definition: smtp.c:124
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ smtp_attachment_clear_all()

void smtp_attachment_clear_all ( struct smtp *const  smtp)

Remove all attachments from the SMTP client context.

Parameters
[in]smtpSMTP client context.

Definition at line 3515 of file smtp.c.

References smtp::attachment_list, smtp_attachment::b64_data, smtp_attachment::name, and smtp::num_attachment.

Referenced by SMTPMail::attachment_clear_all(), and smtp_close().

3515  {
3516  size_t i;
3517  struct smtp_attachment *attachment;
3518 
3519  for(i = 0; i < smtp->num_attachment; i++){
3520  attachment = &smtp->attachment_list[i];
3521  free(attachment->name);
3522  free(attachment->b64_data);
3523  }
3524  free(smtp->attachment_list);
3525  smtp->attachment_list = NULL;
3526  smtp->num_attachment = 0;
3527 }
char * b64_data
Definition: smtp.c:129
struct smtp_attachment * attachment_list
Definition: smtp.c:189
size_t num_attachment
Definition: smtp.c:194
char * name
Definition: smtp.c:124
+ Here is the caller graph for this function:

◆ smtp_attachment_validate_name()

SMTP_LINKAGE int smtp_attachment_validate_name ( const char *const  name)

Validate characters in the attachment file name.

Must consist only of printable characters or the space character ( ), and excluding the quote characters (') and (").

Parameters
[in]nameFilename of the attachment shown to recipients.
Return values
0Successful validation.
-1Failed to validate.

Definition at line 2927 of file smtp.c.

Referenced by smtp_attachment_add_mem(), and smtp_unit_test_all_smtp_attachment_validate_name().

2927  {
2928  size_t i;
2929  unsigned char uc;
2930 
2931  for(i = 0; name[i]; i++){
2932  uc = (unsigned char)name[i];
2933  if(uc < ' ' || uc == 127 ||
2934  uc == '\'' || uc == '\"'){
2935  return -1;
2936  }
2937  }
2938  return 0;
2939 }
char * name
Definition: smtp.c:124
+ Here is the caller graph for this function:

◆ smtp_auth()

enum smtp_status_code smtp_auth ( struct smtp *const  smtp,
enum smtp_authentication_method  auth_method,
const char *const  user,
const char *const  pass 
)

Authenticate the user using one of the methods listed in smtp_authentication_method.

Parameters
[in]smtpSMTP client context.
[in]auth_methodSee smtp_authentication_method.
[in]userSMTP user name.
[in]passSMTP password.
Returns
See smtp_status_code.

Definition at line 3026 of file smtp.c.

References SMTP_AUTH_CRAM_MD5, smtp_auth_cram_md5(), SMTP_AUTH_LOGIN, smtp_auth_login(), SMTP_AUTH_NONE, SMTP_AUTH_PLAIN, smtp_auth_plain(), SMTP_STATUS_AUTH, smtp_status_code_set(), SMTP_STATUS_OK, SMTP_STATUS_PARAM, and smtp::status_code.

Referenced by SMTPMail::auth(), mailx_send(), and smtp_auth_check().

3029  {
3030  int auth_rc;
3031 
3032  if(smtp->status_code != SMTP_STATUS_OK){
3033  return smtp->status_code;
3034  }
3035 
3036  if(auth_method == SMTP_AUTH_PLAIN){
3037  auth_rc = smtp_auth_plain(smtp, user, pass);
3038  }
3039  else if(auth_method == SMTP_AUTH_LOGIN){
3040  auth_rc = smtp_auth_login(smtp, user, pass);
3041  }
3042 #ifdef SMTP_OPENSSL
3043  else if(auth_method == SMTP_AUTH_CRAM_MD5){
3044  auth_rc = smtp_auth_cram_md5(smtp, user, pass);
3045  }
3046 #endif /* SMTP_OPENSSL */
3047  else if(auth_method == SMTP_AUTH_NONE){
3048  auth_rc = 0;
3049  }
3050  else{
3052  }
3053 
3054  if(auth_rc < 0){
3055  return smtp_status_code_set(smtp, SMTP_STATUS_AUTH);
3056  }
3057 
3058  return smtp->status_code;
3059 }
static int smtp_auth_cram_md5(struct smtp *const smtp, const char *const user, const char *const pass)
Definition: smtp.c:2037
static int smtp_auth_plain(struct smtp *const smtp, const char *const user, const char *const pass)
Definition: smtp.c:1893
static int smtp_auth_login(struct smtp *const smtp, const char *const user, const char *const pass)
Definition: smtp.c:1966
enum smtp_status_code smtp_status_code_set(struct smtp *const smtp, enum smtp_status_code status_code)
Definition: smtp.c:3231
enum smtp_status_code status_code
Definition: smtp.c:208
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ smtp_auth_cram_md5()

static int smtp_auth_cram_md5 ( struct smtp *const  smtp,
const char *const  user,
const char *const  pass 
)
static

Authenticate using the CRAM-MD5 method.

  1. Send "AUTH CRAM-MD5<CR><NL>" to the server.
  2. Decode the base64 challenge response from the server.
  3. Do an MD5 HMAC on (2) using the account password as the key.
  4. Convert the binary data in (3) to lowercase hex characters.
  5. Construct the string: "<user> <(4)>".
  6. Encode (5) into base64 format.
  7. Send the final string from (6) to the server and check the response.
Parameters
[in]smtpSMTP client context.
[in]userSMTP account user name.
[in]passSMTP account password.
Return values
0Successfully authenticated.
-1Failed to authenticate.

Definition at line 2037 of file smtp.c.

References smtp::gdfd, HMAC, str_getdelimfd::line, malloc, SIZE_MAX, SMTP_AUTH_CONTINUE, SMTP_AUTH_SUCCESS, smtp_base64_decode(), smtp_base64_encode(), smtp_bin2hex(), smtp_getline(), smtp_parse_cmd_line(), smtp_puts(), smtp_puts_terminate(), smtp_read_and_parse_code(), smtp_si_add_size_t(), SMTP_STATUS_OK, smtp_stpcpy(), smtp::status_code, STRING_GETDELIMFD_ERROR, strlen, and smtp_command::text.

Referenced by smtp_auth().

2039  {
2040  struct smtp_command cmd;
2041  unsigned char *challenge_decoded;
2042  size_t challenge_decoded_len;
2043  const char *key;
2044  int key_len;
2045  unsigned char hmac[EVP_MAX_MD_SIZE];
2046  unsigned int hmac_len;
2047  unsigned char *hmac_ret;
2048  char *challenge_hex;
2049  size_t user_len;
2050  size_t challenge_hex_len;
2051  char *auth_concat;
2052  char *concat;
2053  size_t auth_concat_len;
2054  char *b64_auth;
2055 
2056  /* (1) */
2057  if(smtp_puts(smtp, "AUTH CRAM-MD5\r\n") != SMTP_STATUS_OK){
2058  return -1;
2059  }
2061  return -1;
2062  }
2063  if(smtp_parse_cmd_line(smtp->gdfd.line, &cmd) != SMTP_AUTH_CONTINUE){
2064  return -1;
2065  }
2066 
2067  /* (2) */
2068  challenge_decoded_len = smtp_base64_decode(cmd.text,
2069  &challenge_decoded);
2070  if(challenge_decoded_len == SIZE_MAX){
2071  return -1;
2072  }
2073 
2074  /* (3) */
2075  key = pass;
2076  key_len = (int)strlen(pass);/*********************cast*/
2077  hmac_ret = HMAC(EVP_md5(),
2078  key,
2079  key_len,
2080  challenge_decoded,
2081  challenge_decoded_len,
2082  hmac,
2083  &hmac_len);
2084  free(challenge_decoded);
2085  if(hmac_ret == NULL){
2086  return -1;
2087  }
2088 
2089  /* (4) */
2090  challenge_hex = smtp_bin2hex(hmac, hmac_len);
2091  if(challenge_hex == NULL){
2092  return -1;
2093  }
2094 
2095  /* (5) */
2096  user_len = strlen(user);
2097  challenge_hex_len = strlen(challenge_hex);
2098  /* auth_concat_len = user_len + 1 + challenge_hex_len + 1 */
2099  if(smtp_si_add_size_t(user_len, challenge_hex_len, &auth_concat_len) ||
2100  smtp_si_add_size_t(auth_concat_len, 2, &auth_concat_len) ||
2101  (auth_concat = malloc(auth_concat_len)) == NULL){
2102  free(challenge_hex);
2103  return -1;
2104  }
2105  concat = smtp_stpcpy(auth_concat, user);
2106  concat = smtp_stpcpy(concat, " ");
2107  smtp_stpcpy(concat, challenge_hex);
2108  free(challenge_hex);
2109 
2110  /* (6) */
2111  b64_auth = smtp_base64_encode(auth_concat, auth_concat_len - 1);
2112  free(auth_concat);
2113  if(b64_auth == NULL){
2114  return -1;
2115  }
2116 
2117  /* (7) */
2118  smtp_puts_terminate(smtp, b64_auth);
2119  free(b64_auth);
2120  if(smtp->status_code != SMTP_STATUS_OK){
2121  return -1;
2122  }
2123 
2125  return -1;
2126  }
2127  return 0;
2128 }
static enum str_getdelim_retcode smtp_getline(struct smtp *const smtp)
Definition: smtp.c:1544
SMTP_LINKAGE int smtp_parse_cmd_line(char *const line, struct smtp_command *const cmd)
Definition: smtp.c:1467
SMTP_LINKAGE char * smtp_bin2hex(const unsigned char *const s, size_t slen)
Definition: smtp.c:1031
struct str_getdelimfd gdfd
Definition: smtp.c:164
#define SIZE_MAX
Definition: smtp.h:23
#define HMAC
Definition: seams.h:129
static enum smtp_status_code smtp_puts(struct smtp *const smtp, const char *const s)
Definition: smtp.c:1655
static int smtp_read_and_parse_code(struct smtp *const smtp)
Definition: smtp.c:1574
char * line
Definition: smtp.h:592
SMTP_LINKAGE char * smtp_base64_encode(const char *const buf, size_t buflen)
Definition: smtp.c:825
#define malloc
Definition: seams.h:145
SMTP_LINKAGE size_t smtp_base64_decode(const char *const buf, unsigned char **decode)
Definition: smtp.c:984
enum smtp_status_code status_code
Definition: smtp.c:208
#define strlen
Definition: seams.h:273
SMTP_LINKAGE char * smtp_stpcpy(char *s1, const char *s2)
Definition: smtp.c:599
SMTP_LINKAGE int smtp_si_add_size_t(const size_t a, const size_t b, size_t *const result)
Definition: smtp.c:252
static enum smtp_status_code smtp_puts_terminate(struct smtp *const smtp, const char *const s)
Definition: smtp.c:1669
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ smtp_auth_login()

static int smtp_auth_login ( struct smtp *const  smtp,
const char *const  user,
const char *const  pass 
)
static

Authenticate using the LOGIN method.

  1. Base64 encode the user name.
  2. Send string from (1) as part of the login: "AUTH LOGIN <b64_username><CR><NL>".
  3. Base64 encode the password and send that by itself on a separate line: "<b64_password><CR><NL>".
Parameters
[in]smtpSMTP client context.
[in]userSMTP account user name.
[in]passSMTP account password.
Return values
0Successfully authenticated.
-1Failed to authenticate.

Definition at line 1966 of file smtp.c.

References malloc, SIZE_MAX, SMTP_AUTH_CONTINUE, SMTP_AUTH_SUCCESS, smtp_base64_encode(), smtp_puts(), smtp_puts_terminate(), smtp_read_and_parse_code(), smtp_si_add_size_t(), SMTP_STATUS_OK, smtp_stpcpy(), smtp::status_code, and strlen.

Referenced by smtp_auth().

1968  {
1969  char *b64_user;
1970  char *b64_pass;
1971  size_t b64_user_len;
1972  char *login_str;
1973  char *concat;
1974 
1975  /* (1) */
1976  if((b64_user = smtp_base64_encode(user, SIZE_MAX)) == NULL){
1977  return -1;
1978  }
1979 
1980  /* (2) */
1981  b64_user_len = strlen(b64_user);
1982  if(smtp_si_add_size_t(b64_user_len, 14, &b64_user_len) ||
1983  (login_str = malloc(b64_user_len)) == NULL){
1984  free(b64_user);
1985  return -1;
1986  }
1987  concat = smtp_stpcpy(login_str, "AUTH LOGIN ");
1988  concat = smtp_stpcpy(concat, b64_user);
1989  smtp_stpcpy(concat, "\r\n");
1990 
1991  free(b64_user);
1992  smtp_puts(smtp, login_str);
1993  free(login_str);
1994  if(smtp->status_code != SMTP_STATUS_OK){
1995  return -1;
1996  }
1997 
1999  return -1;
2000  }
2001 
2002  /* (3) */
2003  if((b64_pass = smtp_base64_encode(pass, SIZE_MAX)) == NULL){
2004  return -1;
2005  }
2006  smtp_puts_terminate(smtp, b64_pass);
2007  free(b64_pass);
2008  if(smtp->status_code != SMTP_STATUS_OK){
2009  return -1;
2010  }
2011 
2013  return -1;
2014  }
2015  return 0;
2016 }
#define SIZE_MAX
Definition: smtp.h:23
static enum smtp_status_code smtp_puts(struct smtp *const smtp, const char *const s)
Definition: smtp.c:1655
static int smtp_read_and_parse_code(struct smtp *const smtp)
Definition: smtp.c:1574
SMTP_LINKAGE char * smtp_base64_encode(const char *const buf, size_t buflen)
Definition: smtp.c:825
#define malloc
Definition: seams.h:145
enum smtp_status_code status_code
Definition: smtp.c:208
#define strlen
Definition: seams.h:273
SMTP_LINKAGE char * smtp_stpcpy(char *s1, const char *s2)
Definition: smtp.c:599
SMTP_LINKAGE int smtp_si_add_size_t(const size_t a, const size_t b, size_t *const result)
Definition: smtp.c:252
static enum smtp_status_code smtp_puts_terminate(struct smtp *const smtp, const char *const s)
Definition: smtp.c:1669
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ smtp_auth_plain()

static int smtp_auth_plain ( struct smtp *const  smtp,
const char *const  user,
const char *const  pass 
)
static

Authenticate using the PLAIN method.

  1. Set the text to the following format: "\0<user>\0<password>", or as shown in the format string: "\0%s\0%s", email, password.
  2. Base64 encode the text from (1).
  3. Send the constructed auth text from (2) to the server: "AUTH PLAIN <b64><CR><NL>".
Parameters
[in]smtpSMTP client context.
[in]userSMTP account user name.
[in]passSMTP account password.
Return values
0Successfully authenticated.
-1Failed to authenticate.

Definition at line 1893 of file smtp.c.

References malloc, SMTP_AUTH_SUCCESS, smtp_base64_encode(), smtp_puts(), smtp_read_and_parse_code(), smtp_si_add_size_t(), SMTP_STATUS_OK, smtp_stpcpy(), smtp::status_code, and strlen.

Referenced by smtp_auth().

1895  {
1896  size_t user_len;
1897  size_t pass_len;
1898  char *login_str;
1899  size_t login_len;
1900  char *login_b64;
1901  size_t login_b64_len;
1902  char *login_send;
1903  char *concat;
1904 
1905  /* (1) */
1906  user_len = strlen(user);
1907  pass_len = strlen(pass);
1908  /* login_len = 1 + user_len + 1 + pass_len */
1909  if(smtp_si_add_size_t(user_len, pass_len, &login_len) ||
1910  smtp_si_add_size_t(login_len, 2, &login_len) ||
1911  (login_str = malloc(login_len)) == NULL){
1912  return -1;
1913  }
1914  login_str[0] = '\0';
1915  memcpy(&login_str[1], user, user_len);
1916  login_str[1 + user_len] = '\0';
1917  memcpy(&login_str[1 + user_len + 1], pass, pass_len);
1918 
1919  /* (2) */
1920  login_b64 = smtp_base64_encode(login_str, login_len);
1921  free(login_str);
1922  if(login_b64 == NULL){
1923  return -1;
1924  }
1925 
1926  /* (3) */
1927  login_b64_len = strlen(login_b64);
1928  if(smtp_si_add_size_t(login_b64_len, 14, &login_b64_len) ||
1929  (login_send = malloc(login_b64_len)) == NULL){
1930  free(login_b64);
1931  return -1;
1932  }
1933  concat = smtp_stpcpy(login_send, "AUTH PLAIN ");
1934  concat = smtp_stpcpy(concat, login_b64);
1935  smtp_stpcpy(concat, "\r\n");
1936 
1937  free(login_b64);
1938  smtp_puts(smtp, login_send);
1939  free(login_send);
1940  if(smtp->status_code != SMTP_STATUS_OK){
1941  return -1;
1942  }
1943 
1945  return -1;
1946  }
1947  return 0;
1948 }
static enum smtp_status_code smtp_puts(struct smtp *const smtp, const char *const s)
Definition: smtp.c:1655
static int smtp_read_and_parse_code(struct smtp *const smtp)
Definition: smtp.c:1574
SMTP_LINKAGE char * smtp_base64_encode(const char *const buf, size_t buflen)
Definition: smtp.c:825
#define malloc
Definition: seams.h:145
enum smtp_status_code status_code
Definition: smtp.c:208
#define strlen
Definition: seams.h:273
SMTP_LINKAGE char * smtp_stpcpy(char *s1, const char *s2)
Definition: smtp.c:599
SMTP_LINKAGE int smtp_si_add_size_t(const size_t a, const size_t b, size_t *const result)
Definition: smtp.c:252
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ smtp_base64_decode()

SMTP_LINKAGE size_t smtp_base64_decode ( const char *const  buf,
unsigned char **  decode 
)

Decode a base64 string.

The decode parameter will get dynamically allocated by this function if it successfully completes. Therefore, the caller must free the decode parameter after use.

Parameters
[in]bufNull-terminated base64 string.
[out]decodePointer to buffer which will get dynamically allocated and will contain the decoded binary data. This parameter will get set to NULL if the memory allocation fails.
Return values
>=0Length of the data stored in the decode parameter.
-1Memory allocation failure or invalid base64 byte sequences.

Definition at line 984 of file smtp.c.

References calloc, SIZE_MAX, smtp_base64_decode_block(), SMTP_LINKAGE, smtp_si_add_size_t(), and strlen.

Referenced by smtp_auth_cram_md5(), and smtp_unit_test_base64_decode().

985  {
986  size_t buf_len;
987  size_t buf_len_inc;
988  size_t buf_i;
989  unsigned char *b64_decode;
990  size_t decode_len;
991  size_t decode_block_len;
992 
993  *decode = NULL;
994 
995  buf_len = strlen(buf);
996  if(buf_len % 4 != 0){
997  return SIZE_MAX;
998  }
999 
1000  if(smtp_si_add_size_t(buf_len, 1, &buf_len_inc) ||
1001  (b64_decode = calloc(1, buf_len_inc)) == NULL){
1002  return SIZE_MAX;
1003  }
1004 
1005  decode_len = 0;
1006  for(buf_i = 0; buf_i < buf_len; buf_i += 4){
1007  decode_block_len = smtp_base64_decode_block(
1008  (const unsigned char*)&buf[buf_i],
1009  &b64_decode[decode_len]);
1010  if(decode_block_len == 0){
1011  free(b64_decode);
1012  return SIZE_MAX;
1013  }
1014  decode_len += decode_block_len;
1015  }
1016  *decode = b64_decode;
1017  return decode_len;
1018 }
static size_t smtp_base64_decode_block(const unsigned char *const buf, unsigned char *const decode)
Definition: smtp.c:924
#define calloc
Definition: seams.h:73
#define SIZE_MAX
Definition: smtp.h:23
#define strlen
Definition: seams.h:273
SMTP_LINKAGE int smtp_si_add_size_t(const size_t a, const size_t b, size_t *const result)
Definition: smtp.c:252
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ smtp_base64_decode_block()

static size_t smtp_base64_decode_block ( const unsigned char *const  buf,
unsigned char *const  decode 
)
static

Decodes a base64 block of up to four bytes at a time.

Parameters
[in]bufBuffer containing bytes to decode.
[out]decodeBuffer for storing base64 decoded bytes.
Return values
>0Length of the decoded block.
0If the block contains invalid base64 data.

Definition at line 924 of file smtp.c.

References g_base64_decode_table, and SMTP_LINKAGE.

Referenced by smtp_base64_decode().

925  {
926  size_t decode_block_len;
927  size_t i;
928  signed char decode_table[4];
929  unsigned char outb[3];
930 
931  decode_block_len = 0;
932  for(i = 0; i < 4; i++){
933  if(buf[i] == '='){
934  decode_table[i] = 0;
935  continue;
936  }
937  decode_table[i] = g_base64_decode_table[buf[i]];
938  if(decode_table[i] < 0){
939  return 0;
940  }
941  }
942 
943  outb[0] = ((decode_table[0] << 2) & 0xFC) | ((decode_table[1] >> 4) & 0x03);
944  outb[1] = ((decode_table[1] << 4) & 0xF0) | ((decode_table[2] >> 2) & 0x0F);
945  outb[2] = ((decode_table[2] << 6) & 0xC0) | ((decode_table[3] ) & 0x3F);
946 
947  decode[0] = outb[0];
948  decode_block_len += 1;
949 
950  if(buf[2] == '='){
951  decode[1] = '\0';
952  }
953  else{
954  decode[1] = outb[1];
955  decode_block_len += 1;
956  }
957 
958  if(buf[3] == '='){
959  decode[2] = '\0';
960  }
961  else{
962  decode[2] = outb[2];
963  decode_block_len += 1;
964  }
965 
966  return decode_block_len;
967 }
static signed char g_base64_decode_table[]
Definition: smtp.c:894
+ Here is the caller graph for this function:

◆ smtp_base64_encode()

SMTP_LINKAGE char* smtp_base64_encode ( const char *const  buf,
size_t  buflen 
)

Encode binary data into a base64 string.

Parameters
[in]bufBinary data to encode in base64.
[in]buflenNumber of bytes in the buf parameter, or -1 if null-terminated.
Return values
char*Dynamically allocated base64 encoded string. The caller must free this string when finished.
NULLMemory allocation failure.

Definition at line 825 of file smtp.c.

References calloc, SIZE_MAX, smtp_base64_encode_block(), smtp_si_mul_size_t(), and strlen.

Referenced by smtp_attachment_add_mem(), smtp_auth_cram_md5(), smtp_auth_login(), smtp_auth_plain(), and smtp_unit_test_base64_encode().

826  {
827  char *b64;
828  size_t b64_sz;
829  size_t buf_i;
830  size_t b64_i;
831  size_t remaining_block_sz;
832  size_t buf_block_sz;
833 
834  if(buflen == SIZE_MAX){
835  buflen = strlen(buf);
836  }
837 
838  /*
839  * base64 size expands by 33%
840  * +1 to round integer division up
841  * +2 for '=' padding
842  * +1 null terminator
843  */
844  if(smtp_si_mul_size_t(buflen, 4, NULL)){
845  return NULL;
846  }
847  b64_sz = (4 * buflen / 3) + 1 + 2 + 1;
848  if((b64 = calloc(1, b64_sz)) == NULL){
849  return NULL;
850  }
851 
852  if(buflen == 0){
853  return b64;
854  }
855 
856  buf_i = 0;
857  b64_i = 0;
858  remaining_block_sz = buflen;
859  while(remaining_block_sz > 0){
860  if(remaining_block_sz >= 3){
861  buf_block_sz = 3;
862  }
863  else{
864  buf_block_sz = remaining_block_sz;
865  }
866 
867  smtp_base64_encode_block(&buf[buf_i], buf_block_sz, &b64[b64_i]);
868 
869  /*
870  * Do not need to check for wrapping because these values restricted to
871  * range of b64_sz, which has already been checked for wrapping above.
872  */
873  buf_i += 3;
874  b64_i += 4;
875 
876  remaining_block_sz -= buf_block_sz;
877  }
878 
879  return b64;
880 }
#define calloc
Definition: seams.h:73
#define SIZE_MAX
Definition: smtp.h:23
static void smtp_base64_encode_block(const char *const buf, size_t buf_block_sz, char *const b64)
Definition: smtp.c:792
#define strlen
Definition: seams.h:273
SMTP_LINKAGE int smtp_si_mul_size_t(const size_t a, const size_t b, size_t *const result)
Definition: smtp.c:320
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ smtp_base64_encode_block()

static void smtp_base64_encode_block ( const char *const  buf,
size_t  buf_block_sz,
char *const  b64 
)
static

Encode a single block of binary data into base64.

Parameters
[in]bufBuffer with data to encode.
[in]buf_block_szNumber of bytes in buf to encode (min 1, max 3).
[out]b64Pointer to buffer with at least 4 bytes for storing the base64 encoded result.

Definition at line 792 of file smtp.c.

References g_base64_encode_table, and SMTP_LINKAGE.

Referenced by smtp_base64_encode().

794  {
795  unsigned char inb[3] = {0};
796  unsigned char in_idx[4] = {0};
797  char outb[5] = {'=', '=', '=', '=', '\0'};
798  size_t i;
799 
800  memcpy(inb, buf, buf_block_sz);
801 
802  in_idx[0] = ((inb[0] >> 2)) & 0x3F;
803  in_idx[1] = ((inb[0] << 4) | ((inb[1] >> 4) & 0xF)) & 0x3F;
804  in_idx[2] = ((inb[1] << 2) | ((inb[2] >> 6) & 0x3)) & 0x3F;
805  in_idx[3] = ((inb[2] )) & 0x3F;
806  for(i = 0; i < 4; i++){
807  if(i < buf_block_sz + 1){
808  outb[i] = g_base64_encode_table[in_idx[i]];
809  }
810  b64[i] = outb[i];
811  }
812 }
static char g_base64_encode_table[]
Definition: smtp.c:772
+ Here is the caller graph for this function:

◆ smtp_bin2hex()

SMTP_LINKAGE char* smtp_bin2hex ( const unsigned char *const  s,
size_t  slen 
)

Convert binary data to lowercase hexadecimal representation.

Parameters
[in]sBuffer containing binary data to convert.
[in]slenNumber of bytes in s.
Return values
char*Dynamically allocated string consisting of a hexadecimal representation of binary data in s. The caller must free this memory when finished.
NULLMemory allocation or encoding error.

Definition at line 1031 of file smtp.c.

References malloc, SMTP_LINKAGE, smtp_si_add_size_t(), smtp_si_mul_size_t(), and sprintf.

Referenced by smtp_auth_cram_md5(), and smtp_unit_test_bin2hex().

1032  {
1033  char *snew;
1034  size_t alloc_sz;
1035  size_t i;
1036  size_t j;
1037  unsigned hex;
1038  int rc;
1039 
1040  /* alloc_sz = slen * 2 + 1 */
1041  if(smtp_si_mul_size_t(slen, 2, &alloc_sz) ||
1042  smtp_si_add_size_t(alloc_sz, 1, &alloc_sz)){
1043  return NULL;
1044  }
1045  if((snew = malloc(alloc_sz)) == NULL){
1046  return NULL;
1047  }
1048 
1049  j = 0;
1050  for(i = 0; i < slen; i++){
1051  hex = s[i];
1052  rc = sprintf(&snew[j], "%02x", hex);
1053  if(rc < 0 || (size_t)rc >= 3){
1054  free(snew);
1055  return NULL;
1056  }
1057  j += 2;
1058  }
1059  snew[j] = '\0';
1060 
1061  return snew;
1062 }
#define sprintf
Definition: seams.h:265
#define malloc
Definition: seams.h:145
SMTP_LINKAGE int smtp_si_mul_size_t(const size_t a, const size_t b, size_t *const result)
Definition: smtp.c:320
SMTP_LINKAGE int smtp_si_add_size_t(const size_t a, const size_t b, size_t *const result)
Definition: smtp.c:252
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ smtp_chunk_split()

SMTP_LINKAGE char* smtp_chunk_split ( const char *const  s,
size_t  chunklen,
const char *const  end 
)

Splits a string into smaller chunks separated by a terminating string.

Parameters
[in]sThe string to chunk.
[in]chunklenNumber of bytes for each chunk in the string.
[in]endTerminating string placed at the end of each chunk.
Return values
char*Pointer to an allocated string with the contents split into separate chunks. The caller must free this memory when done.
NULLMemory allocation failure.

Definition at line 1312 of file smtp.c.

References calloc, SIZE_MAX, SMTP_LINKAGE, smtp_si_add_size_t(), smtp_si_mul_size_t(), smtp_strdup(), smtp_strnlen_utf8(), and strlen.

Referenced by smtp_attachment_add_mem(), and smtp_unit_test_chunk_split().

1314  {
1315  char *snew;
1316  size_t bodylen;
1317  size_t bodylen_inc;
1318  size_t endlen;
1319  size_t endlen_inc;
1320  size_t snewlen;
1321  size_t chunk_i;
1322  size_t snew_i;
1323  size_t body_i;
1324  size_t body_copy_len;
1325 
1326  if(chunklen < 1){
1327  errno = EINVAL;
1328  return NULL;
1329  }
1330 
1331  bodylen = strlen(s);
1332  endlen = strlen(end);
1333 
1334  if(bodylen < 1){
1335  return smtp_strdup(end);
1336  }
1337 
1338  /*
1339  * \0
1340  * snewlen = bodylen + (endlen + 1) * (bodylen / chunklen + 1) + 1
1341  */
1342  if(smtp_si_add_size_t(endlen, 1, &endlen_inc) ||
1343  smtp_si_add_size_t(bodylen, 1, &bodylen_inc) ||
1344  smtp_si_mul_size_t(endlen_inc, bodylen / chunklen + 1, &snewlen) ||
1345  smtp_si_add_size_t(snewlen, bodylen_inc, &snewlen) ||
1346  (snew = calloc(1, snewlen)) == NULL){
1347  return NULL;
1348  }
1349 
1350  body_i = 0;
1351  snew_i = 0;
1352  for(chunk_i = 0; chunk_i < bodylen / chunklen + 1; chunk_i++){
1353  body_copy_len = smtp_strnlen_utf8(&s[body_i], chunklen);
1354  if(body_copy_len == SIZE_MAX){
1355  free(snew);
1356  return NULL;
1357  }
1358  memcpy(&snew[snew_i], &s[body_i], body_copy_len);
1359  snew_i += body_copy_len;
1360  if(s[body_i] == '\0'){
1361  snew_i += 1;
1362  }
1363  body_i += body_copy_len;
1364 
1365  if(endlen > 0){
1366  memcpy(&snew[snew_i], end, endlen);
1367  }
1368  snew_i += endlen;
1369  }
1370 
1371  return snew;
1372 }
SMTP_LINKAGE size_t smtp_strnlen_utf8(const char *s, size_t maxlen)
Definition: smtp.c:1138
#define calloc
Definition: seams.h:73
#define SIZE_MAX
Definition: smtp.h:23
#define strlen
Definition: seams.h:273
SMTP_LINKAGE int smtp_si_mul_size_t(const size_t a, const size_t b, size_t *const result)
Definition: smtp.c:320
SMTP_LINKAGE int smtp_si_add_size_t(const size_t a, const size_t b, size_t *const result)
Definition: smtp.c:252
SMTP_LINKAGE char * smtp_strdup(const char *s)
Definition: smtp.c:650
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ smtp_close()

enum smtp_status_code smtp_close ( struct smtp smtp)

Close the SMTP connection and frees all resources held by the SMTP context.

Parameters
[in]smtpSMTP client context.
Returns
See smtp_status_code.

Definition at line 3168 of file smtp.c.

References close, smtp::flags, smtp::gdfd, smtp_address_clear_all(), smtp_attachment_clear_all(), SMTP_FLAG_INVALID_MEMORY, smtp_header_clear_all(), smtp_puts(), SMTP_STATUS_CLOSE, smtp_status_code_set(), SMTP_STATUS_OK, smtp_str_getdelimfd_free(), smtp::sock, smtp::status_code, smtp::tls, smtp::tls_ctx, and smtp::tls_on.

Referenced by SMTPMail::close(), mailx_send(), smtp_close_check(), smtp_func_test_server_secureserver(), and test_nossl_smtp().

3168  {
3169  enum smtp_status_code status_code;
3170 
3171  status_code = smtp->status_code;
3172 
3173  if(smtp->flags == SMTP_FLAG_INVALID_MEMORY){
3174  return status_code;
3175  }
3176 
3177  if(smtp->sock != -1){
3178  /*
3179  * Do not error out if this fails because we still need to free the
3180  * SMTP client resources.
3181  */
3182  smtp->status_code = SMTP_STATUS_OK;
3183  smtp_puts(smtp, "QUIT\r\n");
3184 
3185 #ifdef SMTP_OPENSSL
3186  if(smtp->tls_on){
3187  SSL_free(smtp->tls);
3188  SSL_CTX_free(smtp->tls_ctx);
3189  }
3190 #endif /* SMTP_OPENSSL */
3191 
3192 #ifdef SMTP_IS_WINDOWS
3193  closesocket(smtp->sock);
3194  WSACleanup();
3195 #else /* POSIX */
3196  if(close(smtp->sock) < 0){
3197  if(smtp->status_code == SMTP_STATUS_OK){
3199  }
3200  }
3201 #endif /* SMTP_IS_WINDOWS */
3202  }
3203 
3205  smtp_header_clear_all(smtp);
3206  smtp_address_clear_all(smtp);
3208  if(status_code == SMTP_STATUS_OK){
3209  status_code = smtp->status_code;
3210  }
3211  free(smtp);
3212 
3213  return status_code;
3214 }
#define SMTP_FLAG_INVALID_MEMORY
Definition: smtp.c:2945
struct str_getdelimfd gdfd
Definition: smtp.c:164
int tls_on
Definition: smtp.c:215
SSL_CTX * tls_ctx
Definition: smtp.c:232
void smtp_address_clear_all(struct smtp *const smtp)
Definition: smtp.c:3401
enum smtp_flag flags
Definition: smtp.c:154
#define close
Definition: seams.h:81
SMTP_LINKAGE void smtp_str_getdelimfd_free(struct str_getdelimfd *const gdfd)
Definition: smtp.c:489
static enum smtp_status_code smtp_puts(struct smtp *const smtp, const char *const s)
Definition: smtp.c:1655
void smtp_header_clear_all(struct smtp *const smtp)
Definition: smtp.c:3332
smtp_status_code
Definition: smtp.h:32
void smtp_attachment_clear_all(struct smtp *const smtp)
Definition: smtp.c:3515
enum smtp_status_code smtp_status_code_set(struct smtp *const smtp, enum smtp_status_code status_code)
Definition: smtp.c:3231
enum smtp_status_code status_code
Definition: smtp.c:208
SSL * tls
Definition: smtp.c:227
int sock
Definition: smtp.c:159
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ smtp_connect()

static int smtp_connect ( struct smtp *const  smtp,
const char *const  server,
const char *const  port 
)
static

Connect to the server using a standard TCP socket.

This function handles the server name lookup to get an IP address for the server, and then to connect to that IP using a normal TCP connection.

Parameters
[in]smtpSMTP client context.
[in]serverMail server name or IP address.
[in]portMail server port number.
Return values
0Successfully connected to server.
-1Failed to connect to server.

Definition at line 1703 of file smtp.c.

References close, connect, smtp::sock, and socket.

Referenced by smtp_open().

1705  {
1706  struct addrinfo hints;
1707  struct addrinfo *res0;
1708  struct addrinfo *res;
1709 
1710  /*
1711  * Windows requires initializing the socket library before we call any
1712  * socket functions.
1713  */
1714 #ifdef SMTP_IS_WINDOWS
1715  /* Windows global network socket data structure. */
1716  WSADATA wsa_data;
1717  if(WSAStartup(MAKEWORD(2, 2), &wsa_data) != 0){
1718  return -1;
1719  }
1720 #endif /* SMTP_IS_WINDOWS */
1721 
1722  memset(&hints, 0, sizeof(hints));
1723  hints.ai_family = AF_UNSPEC;
1724  hints.ai_socktype = SOCK_STREAM;
1725  hints.ai_flags = 0;
1726  hints.ai_protocol = IPPROTO_TCP;
1727 
1728  if(getaddrinfo(server, port, &hints, &res0) != 0){
1729  return -1;
1730  }
1731 
1732  for(res = res0; res; res = res->ai_next){
1733  smtp->sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
1734  if(smtp->sock < 0){
1735  continue;
1736  }
1737 
1738  if(connect(smtp->sock, res->ai_addr, res->ai_addrlen) < 0){
1739 #ifdef SMTP_IS_WINDOWS
1740  closesocket(smtp->sock);
1741 #else /* POSIX */
1742  close(smtp->sock);
1743 #endif /* SMTP_IS_WINDOWS */
1744  smtp->sock = -1;
1745  }
1746  else{
1747  break;
1748  }
1749  }
1750 
1751  freeaddrinfo(res0);
1752  if(smtp->sock < 0){
1753  return -1;
1754  }
1755 
1756  return 0;
1757 }
#define close
Definition: seams.h:81
#define socket
Definition: seams.h:193
#define connect
Definition: seams.h:89
int sock
Definition: smtp.c:159
+ Here is the caller graph for this function:

◆ smtp_date_rfc_2822()

SMTP_LINKAGE int smtp_date_rfc_2822 ( char *const  date)

Convert the time into an RFC 2822 formatted string.

Example date format: Thu, 21 May 1998 05:33:29 -0700

Parameters
[out]dateBuffer that has at least SMTP_DATE_MAX_SZ bytes.
Return values
0Successfully copied the current date to the buffer.
-1Failed to establish the current date or an output format error occurred.

Definition at line 2236 of file smtp.c.

References gmtime_r, localtime_r, mktime, SMTP_DATE_MAX_SZ, sprintf, and time.

Referenced by smtp_mail(), and smtp_unit_test_date_rfc_2822().

2236  {
2237  time_t t;
2238  time_t t_local;
2239  time_t t_utc;
2240  struct tm tm_local;
2241  struct tm tm_utc;
2242  long offset_utc;
2243  double diff_local_utc;
2244  int rc;
2245 
2246  const char weekday_abbreviation[7][4] = {
2247  "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
2248  };
2249 
2250  const char month_abbreviation[12][4] = {
2251  "Jan", "Feb", "Mar", "Apr", "May", "Jun",
2252  "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
2253  };
2254 
2255  if((t = time(NULL)) == (time_t)(-1)){
2256  return -1;
2257  }
2258 
2259 #ifdef SMTP_IS_WINDOWS
2260  if(localtime_s(&tm_local, &t) ||
2261  gmtime_s(&tm_utc, &t)){
2262  return -1;
2263  }
2264 #else /* POSIX */
2265  /* Not defined if system does not have localtime_r or gmtime_r. */
2266 # ifdef SMTP_TIME_NO_REENTRANT
2267  struct tm *tm;
2268 
2269  /* localtime() not thread-safe. */
2270  if((tm = localtime(&t)) == NULL){
2271  return -1;
2272  }
2273  memcpy(&tm_local, tm, sizeof(tm_local));
2274 
2275  /* gmtime() not thread-safe. */
2276  if((tm = gmtime(&t)) == NULL){
2277  return -1;
2278  }
2279  memcpy(&tm_utc, tm, sizeof(tm_utc));
2280 # else /* Reentrant versions: localtime_r() and gmtime_r(). */
2281  if(localtime_r(&t, &tm_local) == NULL ||
2282  gmtime_r(&t, &tm_utc) == NULL){
2283  return -1;
2284  }
2285 # endif /* SMTP_TIME_NO_REENTRANT */
2286 #endif /* SMTP_IS_WINDOWS */
2287 
2288  if((t_local = mktime(&tm_local)) == (time_t)(-1)){
2289  return -1;
2290  }
2291 
2292  if((t_utc = mktime(&tm_utc)) == (time_t)(-1)){
2293  return -1;
2294  }
2295 
2296  /*
2297  * After computing the offset, it will contain a maximum of 4 digits.
2298  * For example, PST time zone will have an offset of -800 which will get
2299  * formatted as -0800 in the sprintf call below.
2300  */
2301  diff_local_utc = difftime(t_local, t_utc);
2302  offset_utc = (long)diff_local_utc;
2303  offset_utc = offset_utc / 60 / 60 * 100;
2304 
2305  rc = sprintf(date,
2306  "%s, %02d %s %d %02d:%02d:%02d %0+5ld",
2307  weekday_abbreviation[tm_local.tm_wday],
2308  tm_local.tm_mday,
2309  month_abbreviation[tm_local.tm_mon],
2310  tm_local.tm_year + 1900,
2311  tm_local.tm_hour,
2312  tm_local.tm_min,
2313  tm_local.tm_sec, /* 0 - 60 (leap second) */
2314  offset_utc);
2315 
2316  if(rc + 1 != SMTP_DATE_MAX_SZ - 15){ /* See @ref SMTP_DATE_MAX_SZ for -5. */
2317  return -1;
2318  }
2319 
2320  return 0;
2321 }
#define time
Definition: seams.h:281
#define sprintf
Definition: seams.h:265
#define gmtime_r
Definition: seams.h:121
#define mktime
Definition: seams.h:153
#define localtime_r
Definition: seams.h:137
#define SMTP_DATE_MAX_SZ
Definition: smtp.c:2222
+ Here is the caller graph for this function:

◆ smtp_ehlo()

static enum smtp_status_code smtp_ehlo ( struct smtp *const  smtp)
static

Send the EHLO command and parse through the responses.

Ignores all of the server extensions that get returned. If a server doesn't support an extension we need, then we should receive an error later on when we try to use that extension.

Parameters
[in]smtpSMTP client context.
Returns
See smtp_status_code.

Definition at line 1870 of file smtp.c.

References smtp_puts(), smtp_read_and_parse_code(), SMTP_STATUS_OK, and smtp::status_code.

Referenced by smtp_initiate_handshake().

1870  {
1871  if(smtp_puts(smtp, "EHLO smtp\r\n") == SMTP_STATUS_OK){
1873  }
1874  return smtp->status_code;
1875 }
static enum smtp_status_code smtp_puts(struct smtp *const smtp, const char *const s)
Definition: smtp.c:1655
static int smtp_read_and_parse_code(struct smtp *const smtp)
Definition: smtp.c:1574
enum smtp_status_code status_code
Definition: smtp.c:208
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ smtp_ffile_get_contents()

SMTP_LINKAGE char* smtp_ffile_get_contents ( FILE *  stream,
size_t *  bytes_read 
)

Read the entire contents of a file stream and store the data into a dynamically allocated buffer.

Parameters
[in]streamFile stream already opened by the caller.
[out]bytes_readNumber of bytes stored in the return buffer.
Return values
char*A dynamically allocated buffer which contains the entire contents of stream. The caller must free this memory when done.
NULLMemory allocation or file read error.

Definition at line 1386 of file smtp.c.

References ferror, realloc, SMTP_LINKAGE, and smtp_si_add_size_t().

Referenced by smtp_attachment_add_fp(), and smtp_file_get_contents().

1387  {
1388  char *read_buf;
1389  size_t bufsz;
1390  size_t bufsz_inc;
1391  char *new_buf;
1392  size_t bytes_read_loop;
1393  const size_t BUFSZ_INCREMENT = 512;
1394 
1395  read_buf = NULL;
1396  bufsz = 0;
1397 
1398  if(bytes_read){
1399  *bytes_read = 0;
1400  }
1401 
1402  do{
1403  if(smtp_si_add_size_t(bufsz, BUFSZ_INCREMENT, &bufsz_inc) ||
1404  (new_buf = realloc(read_buf, bufsz_inc)) == NULL){
1405  free(read_buf);
1406  return NULL;
1407  }
1408  read_buf = new_buf;
1409  bufsz = bufsz_inc;
1410 
1411  bytes_read_loop = fread(&read_buf[bufsz - BUFSZ_INCREMENT],
1412  sizeof(char),
1413  BUFSZ_INCREMENT,
1414  stream);
1415  if(bytes_read){
1416  *bytes_read += bytes_read_loop;
1417  }
1418  if(ferror(stream)){
1419  free(read_buf);
1420  return NULL;
1421  }
1422  } while(!feof(stream));
1423 
1424  return read_buf;
1425 }
#define ferror
Definition: seams.h:113
#define realloc
Definition: seams.h:161
SMTP_LINKAGE int smtp_si_add_size_t(const size_t a, const size_t b, size_t *const result)
Definition: smtp.c:252
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ smtp_file_get_contents()

SMTP_LINKAGE char* smtp_file_get_contents ( const char *const  filename,
size_t *  bytes_read 
)

Read the entire contents of a file from a given path, and store the data into a dynamically allocated buffer.

Parameters
[in]filenamePath of file to open and read from.
[out]bytes_readNumber of bytes stored in the return buffer.
Return values
char*A dynamically allocated buffer which has the contents of the file at filename. The caller must free this memory when done.
NULLMemory allocation or file read error.

Definition at line 1439 of file smtp.c.

References fclose, smtp_ffile_get_contents(), and SMTP_LINKAGE.

Referenced by smtp_attachment_add_path(), smtp_test_config_load_from_file(), smtp_unit_test_all_file_get_contents(), and smtp_unit_test_file_get_contents().

1440  {
1441  FILE *fp;
1442  char *read_buf;
1443 
1444  if((fp = fopen(filename, "rb")) == NULL){
1445  return NULL;
1446  }
1447 
1448  read_buf = smtp_ffile_get_contents(fp, bytes_read);
1449 
1450  if(fclose(fp) == EOF){
1451  free(read_buf);
1452  read_buf = NULL;
1453  }
1454 
1455  return read_buf;
1456 }
SMTP_LINKAGE char * smtp_ffile_get_contents(FILE *stream, size_t *bytes_read)
Definition: smtp.c:1386
#define fclose
Definition: seams.h:105
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ smtp_fold_whitespace()

SMTP_LINKAGE char* smtp_fold_whitespace ( const char *const  s,
unsigned int  maxlen 
)

Fold a line at whitespace characters.

This function tries to keep the total number of characters per line under maxlen, but does not guarantee this. For really long text with no whitespace, the line will still extend beyond maxlen and possibly beyond the RFC limit as defined in SMTP_LINE_MAX. This is by design and intended to keep the algorithm simpler to implement. Users sending long headers with no space characters should not assume that will work, but modern email systems may correctly process those headers anyways.

Lines get folded by adding a [CR][LF] and then two space characters on the beginning of the next line. For example, this Subject line:

Subject: Email[WS][WS]Header

Would get folded like this (assuming a small maxlen):

Subject: Email[WS][CR][LF] [WS][WS]Header

Parameters
[in]sString to fold.
[in]maxlenNumber of bytes for each line in the string (soft limit). The minimum value of this parameter is 3 and it will get forced to 3 if the provided value is less.
Return values
char*Pointer to an allocated string with the contents split into separate lines. The caller must free this memory when done.
NULLMemory allocation failed.

Definition at line 1250 of file smtp.c.

References realloc, smtp_fold_whitespace_get_offset(), SMTP_LINKAGE, smtp_si_add_size_t(), and strlen.

Referenced by smtp_print_header(), and smtp_unit_test_fold_whitespace().

1251  {
1252  const char *const SMTP_LINE_FOLD_STR = "\r\n ";
1253  size_t end_slen;
1254  size_t s_i;
1255  size_t buf_i;
1256  size_t bufsz;
1257  size_t ws_offset;
1258  char *buf;
1259  char *buf_new;
1260 
1261  if(maxlen < 3){
1262  maxlen = 3;
1263  }
1264 
1265  end_slen = strlen(SMTP_LINE_FOLD_STR);
1266 
1267  s_i = 0;
1268  buf_i = 0;
1269  bufsz = 0;
1270  buf = NULL;
1271 
1272  while(1){
1273  ws_offset = smtp_fold_whitespace_get_offset(&s[s_i], maxlen - 2);
1274 
1275  /* bufsz += ws_offset + end_slen + 1 */
1276  if(smtp_si_add_size_t(bufsz, ws_offset, &bufsz) ||
1277  smtp_si_add_size_t(bufsz, end_slen, &bufsz) ||
1278  smtp_si_add_size_t(bufsz, 1, &bufsz) ||
1279  (buf_new = realloc(buf, bufsz)) == NULL){
1280  free(buf);
1281  return NULL;
1282  }
1283  buf = buf_new;
1284  memcpy(&buf[buf_i], &s[s_i], ws_offset);
1285  buf[buf_i + ws_offset] = '\0';
1286 
1287  if(s[s_i + ws_offset] == '\0'){
1288  break;
1289  }
1290 
1291  buf_i += ws_offset;
1292  strcat(&buf[buf_i], SMTP_LINE_FOLD_STR);
1293  buf_i += end_slen;
1294 
1295  /* WS */
1296  s_i += ws_offset + 1;
1297  }
1298  return buf;
1299 }
#define realloc
Definition: seams.h:161
SMTP_LINKAGE size_t smtp_fold_whitespace_get_offset(const char *const s, unsigned int maxlen)
Definition: smtp.c:1179
#define strlen
Definition: seams.h:273
SMTP_LINKAGE int smtp_si_add_size_t(const size_t a, const size_t b, size_t *const result)
Definition: smtp.c:252
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ smtp_fold_whitespace_get_offset()

SMTP_LINKAGE size_t smtp_fold_whitespace_get_offset ( const char *const  s,
unsigned int  maxlen 
)

Get the offset of the next whitespace block to process folding.

If a string does not have whitespace before maxlen, then the index will get returned past maxlen. Also returns the index of NULL character if that fits within the next block. The caller must check for the NULL index to indicate the last block. It will skip past any leading whitespace even if that means going over maxlen.

Examples: smtp_fold_whitespace_get_offset ("Subject: Test WS", 1/2/8/9/10/13) -> 8 smtp_fold_whitespace_get_offset ("Subject: Test WS", 14/15) -> 13 smtp_fold_whitespace_get_offset ("Subject: Test WS", 17/18) -> 16

Parameters
[in]sString to get offset from.
[in]maxlenNumber of bytes for each line in the string (soft limit).
Returns
Index in s.

Definition at line 1179 of file smtp.c.

Referenced by smtp_fold_whitespace(), and smtp_unit_test_fold_whitespace_get_offset().

1180  {
1181  size_t i;
1182  size_t offset_i;
1183 
1184  i = 0;
1185  offset_i = 0;
1186 
1187  while(s[i] == ' ' || s[i] == '\t'){
1188  i += 1;
1189  }
1190 
1191  while(s[i]){
1192  if(s[i] == ' ' || s[i] == '\t'){
1193  do{
1194  i += 1;
1195  } while(s[i] == ' ' || s[i] == '\t');
1196  i -= 1;
1197  if(i < maxlen || !offset_i){
1198  offset_i = i;
1199  }
1200  else{
1201  break;
1202  }
1203  }
1204  i += 1;
1205  }
1206 
1207  if(!offset_i || i < maxlen){
1208  offset_i = i;
1209  }
1210 
1211  return offset_i;
1212 }
+ Here is the caller graph for this function:

◆ smtp_gen_mime_boundary()

static void smtp_gen_mime_boundary ( char *const  boundary)
static

Generate the MIME boundary text field and store it in a user-supplied buffer.

For example: mimeXXXXXXXXXX where each X gets set to a pseudo-random uppercase ASCII character.

This uses a simple pseudo-random number generator since we only care about preventing accidental boundary collisions.

Parameters
[out]boundaryBuffer that has at least SMTP_MIME_BOUNDARY_LEN bytes.

Definition at line 2386 of file smtp.c.

References SMTP_MIME_BOUNDARY_LEN, and time.

Referenced by smtp_print_mime_email().

2386  {
2387  size_t i;
2388  unsigned int seed;
2389 
2390  seed = (unsigned int)time(NULL);
2391  srand(seed);
2392 
2393  strcpy(boundary, "mime");
2394  for(i = 4; i < SMTP_MIME_BOUNDARY_LEN - 1; i++){
2395  /* Modulo bias okay since we only need to prevent accidental collision. */
2396  boundary[i] = rand() % 26 + 'A';
2397  }
2398  boundary[SMTP_MIME_BOUNDARY_LEN - 1] = '\0';
2399 }
#define SMTP_MIME_BOUNDARY_LEN
Definition: smtp.c:2370
#define time
Definition: seams.h:281
+ Here is the caller graph for this function:

◆ smtp_getline()

static enum str_getdelim_retcode smtp_getline ( struct smtp *const  smtp)
static

Read a server response line.

Parameters
[in]smtpSMTP client context.
Returns
See str_getdelim_retcode.

Definition at line 1544 of file smtp.c.

References smtp::gdfd, str_getdelimfd::line, str_getdelimfd::line_len, smtp_puts_dbg(), smtp_status_code_set(), SMTP_STATUS_NOMEM, SMTP_STATUS_RECV, smtp_str_getdelimfd(), and STRING_GETDELIMFD_ERROR.

Referenced by smtp_auth_cram_md5(), smtp_initiate_handshake(), and smtp_read_and_parse_code().

1544  {
1545  enum str_getdelim_retcode rc;
1546 
1547  errno = 0;
1548  rc = smtp_str_getdelimfd(&smtp->gdfd);
1549  if(errno == ENOMEM){
1551  return rc;
1552  }
1553  else if(rc == STRING_GETDELIMFD_ERROR){
1555  return STRING_GETDELIMFD_ERROR;
1556  }
1557 
1558  if(smtp->gdfd.line_len > 0){
1559  /* Remove the carriage-return character ('\r'). */
1560  smtp->gdfd.line[smtp->gdfd.line_len - 1] = '\0';
1561  smtp_puts_dbg(smtp, "Server", smtp->gdfd.line);
1562  }
1563  return rc;
1564 }
struct str_getdelimfd gdfd
Definition: smtp.c:164
size_t line_len
Definition: smtp.h:597
char * line
Definition: smtp.h:592
enum smtp_status_code smtp_status_code_set(struct smtp *const smtp, enum smtp_status_code status_code)
Definition: smtp.c:3231
str_getdelim_retcode
Definition: smtp.h:551
SMTP_LINKAGE enum str_getdelim_retcode smtp_str_getdelimfd(struct str_getdelimfd *const gdfd)
Definition: smtp.c:523
static void smtp_puts_dbg(struct smtp *const smtp, const char *const prefix, const char *const str)
Definition: smtp.c:1512
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ smtp_header_add()

enum smtp_status_code smtp_header_add ( struct smtp *const  smtp,
const char *const  key,
const char *const  value 
)

Add a key/value header to the header list in the SMTP context.

If adding a header with an existing key, this will insert instead of replacing the existing header. See smtp_header_clear_all.

See smtp_mail when overriding the default 'Content-Type' header.

Parameters
[in]smtpSMTP client context.
[in]keyKey name for new header. It must consist only of printable US-ASCII characters except colon.
[in]valueValue for new header. It must consist only of printable US-ASCII, space, or horizontal tab. If set to NULL, this will prevent the header from printing out.
Returns
See smtp_status_code.

Definition at line 3278 of file smtp.c.

References smtp::header_list, smtp_header::key, smtp::num_headers, smtp_header_cmp(), smtp_header_key_validate(), smtp_header_value_validate(), smtp_reallocarray(), smtp_si_add_size_t(), smtp_status_code_set(), SMTP_STATUS_NOMEM, SMTP_STATUS_OK, SMTP_STATUS_PARAM, smtp_strdup(), smtp::status_code, and smtp_header::value.

Referenced by SMTPMail::header_add(), mailx_send(), smtp_append_address_to_header(), smtp_header_add_check(), smtp_mail(), and test_nossl_smtp().

3280  {
3281  struct smtp_header *new_header_list;
3282  struct smtp_header *new_header;
3283  size_t num_headers_inc;
3284 
3285  if(smtp->status_code != SMTP_STATUS_OK){
3286  return smtp->status_code;
3287  }
3288 
3289  if(smtp_header_key_validate(key) < 0){
3291  }
3292 
3295  }
3296 
3297  if(smtp_si_add_size_t(smtp->num_headers, 1, &num_headers_inc) ||
3298  (new_header_list = smtp_reallocarray(
3299  smtp->header_list,
3300  num_headers_inc,
3301  sizeof(*smtp->header_list))) == NULL){
3303  }
3304  smtp->header_list = new_header_list;
3305  new_header = &smtp->header_list[smtp->num_headers];
3306 
3307  new_header->key = smtp_strdup(key);
3308  if(value){
3309  new_header->value = smtp_strdup(value);
3310  }
3311  else{
3312  new_header->value = NULL;
3313  }
3314  if(new_header->key == NULL ||
3315  (new_header->value == NULL && value)){
3316  free(new_header->key);
3317  free(new_header->value);
3319  }
3320 
3321  smtp->num_headers = num_headers_inc;
3322 
3323  qsort(smtp->header_list,
3324  smtp->num_headers,
3325  sizeof(*smtp->header_list),
3326  smtp_header_cmp);
3327 
3328  return smtp->status_code;
3329 }
SMTP_LINKAGE void * smtp_reallocarray(void *ptr, size_t nmemb, size_t size)
Definition: smtp.c:622
SMTP_LINKAGE int smtp_header_key_validate(const char *const key)
Definition: smtp.c:2822
char * key
Definition: smtp.c:139
char * value
Definition: smtp.c:144
struct smtp_header * header_list
Definition: smtp.c:169
size_t num_headers
Definition: smtp.c:174
enum smtp_status_code smtp_status_code_set(struct smtp *const smtp, enum smtp_status_code status_code)
Definition: smtp.c:3231
enum smtp_status_code status_code
Definition: smtp.c:208
SMTP_LINKAGE int smtp_header_value_validate(const char *const value)
Definition: smtp.c:2852
SMTP_LINKAGE int smtp_si_add_size_t(const size_t a, const size_t b, size_t *const result)
Definition: smtp.c:252
static int smtp_header_cmp(const void *v1, const void *v2)
Definition: smtp.c:2802
SMTP_LINKAGE char * smtp_strdup(const char *s)
Definition: smtp.c:650
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ smtp_header_clear_all()

void smtp_header_clear_all ( struct smtp *const  smtp)

Free all memory related to email headers.

Parameters
[in]smtpSMTP client context.

Definition at line 3332 of file smtp.c.

References smtp::header_list, smtp_header::key, smtp::num_headers, and smtp_header::value.

Referenced by SMTPMail::header_clear_all(), smtp_close(), smtp_func_test_all_address(), smtp_func_test_all_names(), smtp_func_test_all_nodebug(), smtp_func_test_header_custom_date(), smtp_func_test_header_long(), smtp_func_test_header_null_no_date(), smtp_func_test_html(), and smtp_func_test_html_with_plaintext().

3332  {
3333  size_t i;
3334  struct smtp_header *header;
3335 
3336  for(i = 0; i < smtp->num_headers; i++){
3337  header = &smtp->header_list[i];
3338  free(header->key);
3339  free(header->value);
3340  }
3341  free(smtp->header_list);
3342  smtp->header_list = NULL;
3343  smtp->num_headers = 0;
3344 }
char * key
Definition: smtp.c:139
char * value
Definition: smtp.c:144
struct smtp_header * header_list
Definition: smtp.c:169
size_t num_headers
Definition: smtp.c:174
+ Here is the caller graph for this function:

◆ smtp_header_cmp()

static int smtp_header_cmp ( const void *  v1,
const void *  v2 
)
static

Comparison function for qsort which sorts headers alphabetically based on the key.

Parameters
[in]v1The first smtp_header to compare.
[in]v2The second smtp_header to compare.
Return values
0If the keys match.
!0If the keys do not match.

Definition at line 2802 of file smtp.c.

References smtp_header::key, and SMTP_LINKAGE.

Referenced by smtp_header_add().

2803  {
2804  const struct smtp_header *header1;
2805  const struct smtp_header *header2;
2806 
2807  header1 = v1;
2808  header2 = v2;
2809  return strcmp(header1->key, header2->key);
2810 }
char * key
Definition: smtp.c:139
+ Here is the caller graph for this function:

◆ smtp_header_cmp_key()

static int smtp_header_cmp_key ( const void *const  v1,
const void *const  v2 
)
static

Search function used by bsearch, allowing the caller to check for headers with existing keys.

Parameters
v1String to search for in the list.
v2The smtp_header to compare.
Return values
0If the keys match.
!0If the keys do not match.

Definition at line 2333 of file smtp.c.

References smtp_header::key.

Referenced by smtp_header_exists().

2334  {
2335  const char *key;
2336  const struct smtp_header *header2;
2337 
2338  key = v1;
2339  header2 = v2;
2340  return strcmp(key, header2->key);
2341 }
char * key
Definition: smtp.c:139
+ Here is the caller graph for this function:

◆ smtp_header_exists()

static int smtp_header_exists ( const struct smtp *const  smtp,
const char *const  key 
)
static

Determine if the header key has already been defined in this context.

Parameters
[in]smtpSMTP client context.
[in]keyHeader key value to search for.
Return values
1If the header already exists in this context.
0If the header does not exist in this context.

Definition at line 2352 of file smtp.c.

References smtp::header_list, smtp::num_headers, and smtp_header_cmp_key().

Referenced by smtp_mail(), and smtp_print_email().

2353  {
2354  if(bsearch(key,
2355  smtp->header_list,
2356  smtp->num_headers,
2357  sizeof(*smtp->header_list),
2358  smtp_header_cmp_key) == NULL){
2359  return 0;
2360  }
2361  return 1;
2362 }
char * key
Definition: smtp.c:139
struct smtp_header * header_list
Definition: smtp.c:169
size_t num_headers
Definition: smtp.c:174
static int smtp_header_cmp_key(const void *const v1, const void *const v2)
Definition: smtp.c:2333
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ smtp_header_key_validate()

SMTP_LINKAGE int smtp_header_key_validate ( const char *const  key)

Validate characters in the email header key.

Must consist only of printable US-ASCII characters except colon.

Parameters
[in]keyHeader key to validate.
Return values
0Successful validation.
-1Failed to validate.

Definition at line 2822 of file smtp.c.

References SMTP_LINKAGE, and strlen.

Referenced by smtp_header_add(), and smtp_unit_test_all_smtp_header_key_validate().

2822  {
2823  unsigned char uc;
2824  size_t i;
2825  size_t keylen;
2826 
2827  keylen = strlen(key);
2828  if(keylen < 1){
2829  return -1;
2830  }
2831 
2832  for(i = 0; i < keylen; i++){
2833  uc = (unsigned char)key[i];
2834  if(uc <= ' ' || uc > 126 || uc == ':'){
2835  return -1;
2836  }
2837  }
2838 
2839  return 0;
2840 }
char * key
Definition: smtp.c:139
#define strlen
Definition: seams.h:273
+ Here is the caller graph for this function:

◆ smtp_header_value_validate()

SMTP_LINKAGE int smtp_header_value_validate ( const char *const  value)

Validate characters in the email header contents.

Must consist only of printable character, space, or horizontal tab.

Parameters
[in]valueHeader value to validate.
Return values
0Successful validation.
-1Failed to validate.

Definition at line 2852 of file smtp.c.

References SMTP_LINKAGE.

Referenced by smtp_header_add(), and smtp_unit_test_all_smtp_header_value_validate().

2852  {
2853  size_t i;
2854  unsigned char uc;
2855 
2856  for(i = 0; value[i]; i++){
2857  uc = (unsigned char)value[i];
2858  if((uc < ' ' || uc > 126) &&
2859  uc != '\t' &&
2860  uc < 0x80){ /* Allow UTF-8 byte sequence. */
2861  return -1;
2862  }
2863  }
2864  return 0;
2865 }
char * value
Definition: smtp.c:144
+ Here is the caller graph for this function:

◆ smtp_initiate_handshake()

static enum smtp_status_code smtp_initiate_handshake ( struct smtp *const  smtp,
const char *const  server,
enum smtp_connection_security  connection_security 
)
static

Perform a handshake with the SMTP server which includes optionally setting up TLS and sending the EHLO greeting.

At this point, the client has already connected to the SMTP server through its socket connection. In this function, the client will:

  1. Optionally convert the connection to TLS (SMTP_SECURITY_TLS).
  2. Read the initial server greeting.
  3. Send an EHLO to the server.
  4. Optionally initiate STARTTLS and resend the EHLO (SMTP_SECURITY_STARTTLS).
Parameters
[in]smtpSMTP client context.
[in]serverServer name or IP address.
[in]connection_securitySee smtp_connection_security.
Returns
See smtp_status_code.

Definition at line 2161 of file smtp.c.

References smtp_ehlo(), smtp_getline(), smtp_puts(), smtp_read_and_parse_code(), SMTP_READY, SMTP_SECURITY_STARTTLS, SMTP_SECURITY_TLS, smtp_set_read_timeout(), smtp_status_code_set(), SMTP_STATUS_HANDSHAKE, SMTP_STATUS_OK, smtp_tls_init(), smtp::status_code, and STRING_GETDELIMFD_ERROR.

Referenced by smtp_open().

2163  {
2164  /* Eliminate unused warnings if not using SMTP_OPENSSL. */
2165  (void)server;
2166  (void)connection_security;
2167 
2168  /* (1) */
2169 #ifdef SMTP_OPENSSL
2170  if(connection_security == SMTP_SECURITY_TLS){
2171  if(smtp_tls_init(smtp, server) < 0){
2173  }
2174  }
2175 #endif /* SMTP_OPENSSL */
2176 
2177  /* (2) */
2178  /* Get initial 220 message - 5 minute timeout. */
2179  smtp_set_read_timeout(smtp, 60 * 5);
2181  return smtp->status_code;
2182  }
2183 
2184  /* (3) */
2185  if(smtp_ehlo(smtp) != SMTP_STATUS_OK){
2186  return smtp->status_code;
2187  }
2188 
2189  /* (4) */
2190 #ifdef SMTP_OPENSSL
2191  if(connection_security == SMTP_SECURITY_STARTTLS){
2192  if(smtp_puts(smtp, "STARTTLS\r\n") != SMTP_STATUS_OK){
2193  return smtp->status_code;
2194  }
2195  if(smtp_read_and_parse_code(smtp) != SMTP_READY){
2197  }
2198  if(smtp_tls_init(smtp, server) < 0){
2200  }
2201  if(smtp_ehlo(smtp) != SMTP_STATUS_OK){
2202  return smtp->status_code;
2203  }
2204  }
2205 #endif /* SMTP_OPENSSL */
2206 
2207  return smtp->status_code;
2208 }
static enum str_getdelim_retcode smtp_getline(struct smtp *const smtp)
Definition: smtp.c:1544
static enum smtp_status_code smtp_puts(struct smtp *const smtp, const char *const s)
Definition: smtp.c:1655
static int smtp_read_and_parse_code(struct smtp *const smtp)
Definition: smtp.c:1574
static void smtp_set_read_timeout(struct smtp *const smtp, long seconds)
Definition: smtp.c:2138
static int smtp_tls_init(struct smtp *const smtp, const char *const server)
Definition: smtp.c:1770
enum smtp_status_code smtp_status_code_set(struct smtp *const smtp, enum smtp_status_code status_code)
Definition: smtp.c:3231
enum smtp_status_code status_code
Definition: smtp.c:208
static enum smtp_status_code smtp_ehlo(struct smtp *const smtp)
Definition: smtp.c:1870
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ smtp_mail()

enum smtp_status_code smtp_mail ( struct smtp *const  smtp,
const char *const  body 
)

Sends an email using the addresses, attachments, and headers defined in the current SMTP context.

The caller must call the smtp_open function prior to this.

The 'Date' header will automatically get generated here if it hasn't already been set using smtp_header_add.

If the application overrides the default 'Content-Type' header, then this function will output the body as raw data just below the email headers, and it will not output the attachments added using the smtp_attachment_add_* functions. In other words, the application must create its own MIME sections (if needed) when overriding the 'Content-Type' header.

Parameters
[in]smtpSMTP client context.
[in]bodyNull-terminated string to send in the email body.
Returns
See smtp_status_code.

Definition at line 3062 of file smtp.c.

References smtp::address_list, smtp::header_list, smtp::num_address, smtp::num_headers, SMTP_ADDRESS_CC, SMTP_ADDRESS_FROM, SMTP_ADDRESS_TO, smtp_append_address_to_header(), SMTP_BEGIN_MAIL, SMTP_DATE_MAX_SZ, smtp_date_rfc_2822(), SMTP_DONE, smtp_header_add(), smtp_header_exists(), smtp_mail_envelope_header(), smtp_print_email(), smtp_print_header(), smtp_puts(), smtp_read_and_parse_code(), smtp_set_read_timeout(), smtp_status_code_set(), SMTP_STATUS_DATE, SMTP_STATUS_OK, SMTP_STATUS_PARAM, SMTP_STATUS_SERVER_RESPONSE, smtp::status_code, and smtp_address::type.

Referenced by SMTPMail::mail(), mailx_send(), smtp_mail_check(), and test_nossl_smtp().

3063  {
3064  size_t i;
3065  int has_mail_from;
3066  struct smtp_address *address;
3067  char date[SMTP_DATE_MAX_SZ];
3068 
3069  if(smtp->status_code != SMTP_STATUS_OK){
3070  return smtp->status_code;
3071  }
3072 
3073  /* MAIL timeout 5 minutes. */
3074  smtp_set_read_timeout(smtp, 60 * 5);
3075  has_mail_from = 0;
3076  for(i = 0; i < smtp->num_address; i++){
3077  address = &smtp->address_list[i];
3078  if(address->type == SMTP_ADDRESS_FROM){
3079  if(smtp_mail_envelope_header(smtp,
3080  "MAIL FROM",
3081  address) != SMTP_STATUS_OK){
3082  return smtp->status_code;
3083  }
3084  has_mail_from = 1;
3085  break;
3086  }
3087  }
3088 
3089  if(!has_mail_from){
3091  }
3092 
3093  /* RCPT timeout 5 minutes. */
3094  smtp_set_read_timeout(smtp, 60 * 5);
3095 
3096  for(i = 0; i < smtp->num_address; i++){
3097  address = &smtp->address_list[i];
3098  if(address->type != SMTP_ADDRESS_FROM){
3099  if(smtp_mail_envelope_header(smtp,
3100  "RCPT TO",
3101  address) != SMTP_STATUS_OK){
3102  return smtp->status_code;
3103  }
3104  }
3105  }
3106 
3107  /* DATA timeout 2 minutes. */
3108  smtp_set_read_timeout(smtp, 60 * 2);
3109 
3110  if(smtp_puts(smtp, "DATA\r\n") != SMTP_STATUS_OK){
3111  return smtp->status_code;
3112  }
3113 
3114  /* 354 response to DATA must get returned before we can send the message. */
3117  }
3118 
3119  if(!smtp_header_exists(smtp, "Date")){
3120  if(smtp_date_rfc_2822(date) < 0){
3121  return smtp_status_code_set(smtp, SMTP_STATUS_DATE);
3122  }
3123  if(smtp_header_add(smtp, "Date", date) != SMTP_STATUS_OK){
3124  return smtp->status_code;
3125  }
3126  }
3127 
3128  /* DATA block timeout 3 minutes. */
3129  smtp_set_read_timeout(smtp, 60 * 3);
3130 
3133  "From") != SMTP_STATUS_OK ||
3136  "To") != SMTP_STATUS_OK ||
3139  "Cc") != SMTP_STATUS_OK){
3140  return smtp->status_code;
3141  }
3142 
3143  for(i = 0; i < smtp->num_headers; i++){
3144  if(smtp_print_header(smtp, &smtp->header_list[i]) != SMTP_STATUS_OK){
3145  return smtp->status_code;
3146  }
3147  }
3148 
3149  if(smtp_print_email(smtp, body)){
3150  return smtp->status_code;
3151  }
3152 
3153  /* End of DATA segment. */
3154  if(smtp_puts(smtp, ".\r\n") != SMTP_STATUS_OK){
3155  return smtp->status_code;
3156  }
3157 
3158  /* DATA termination timeout 250 return code - 10 minutes. */
3159  smtp_set_read_timeout(smtp, 60 * 10);
3160  if(smtp_read_and_parse_code(smtp) != SMTP_DONE){
3162  }
3163 
3164  return smtp->status_code;
3165 }
enum smtp_address_type type
Definition: smtp.c:109
struct smtp_address * address_list
Definition: smtp.c:179
static int smtp_header_exists(const struct smtp *const smtp, const char *const key)
Definition: smtp.c:2352
static enum smtp_status_code smtp_print_header(struct smtp *const smtp, const struct smtp_header *const header)
Definition: smtp.c:2624
static enum smtp_status_code smtp_puts(struct smtp *const smtp, const char *const s)
Definition: smtp.c:1655
static int smtp_read_and_parse_code(struct smtp *const smtp)
Definition: smtp.c:1574
static enum smtp_status_code smtp_mail_envelope_header(struct smtp *const smtp, const char *const header, const struct smtp_address *const address)
Definition: smtp.c:2752
struct smtp_header * header_list
Definition: smtp.c:169
static enum smtp_status_code smtp_append_address_to_header(struct smtp *const smtp, enum smtp_address_type address_type, const char *const key)
Definition: smtp.c:2674
SMTP_LINKAGE int smtp_date_rfc_2822(char *const date)
Definition: smtp.c:2236
size_t num_headers
Definition: smtp.c:174
static void smtp_set_read_timeout(struct smtp *const smtp, long seconds)
Definition: smtp.c:2138
enum smtp_status_code smtp_header_add(struct smtp *const smtp, const char *const key, const char *const value)
Definition: smtp.c:3278
static enum smtp_status_code smtp_print_email(struct smtp *const smtp, const char *const body)
Definition: smtp.c:2589
enum smtp_status_code smtp_status_code_set(struct smtp *const smtp, enum smtp_status_code status_code)
Definition: smtp.c:3231
enum smtp_status_code status_code
Definition: smtp.c:208
size_t num_address
Definition: smtp.c:184
#define SMTP_DATE_MAX_SZ
Definition: smtp.c:2222
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ smtp_mail_envelope_header()

static enum smtp_status_code smtp_mail_envelope_header ( struct smtp *const  smtp,
const char *const  header,
const struct smtp_address *const  address 
)
static

Send envelope MAIL FROM or RCPT TO header address.

Examples: MAIL FROM:<mail@example.com> RCPT TO:<mail@example.com>

Parameters
[in]smtpSMTP client context.
[in]headerEither "MAIL FROM" or "RCPT TO".
[in]addressSee smtp_address -> email field.
Returns
See smtp_status_code.

Definition at line 2752 of file smtp.c.

References smtp_address::email, malloc, smtp_puts(), smtp_read_and_parse_code(), smtp_si_add_size_t(), smtp_status_code_set(), SMTP_STATUS_NOMEM, SMTP_STATUS_OK, smtp_stpcpy(), smtp_str_has_nonascii_utf8(), smtp::status_code, and strlen.

Referenced by smtp_mail().

2754  {
2755  const char *const SMTPUTF8 = " SMTPUTF8";
2756  const size_t SMTPUTF8_LEN = strlen(SMTPUTF8);
2757  size_t email_len;
2758  size_t bufsz;
2759  char *envelope_address;
2760  char *concat;
2761  const char *smtputf8_opt;
2762 
2763  email_len = strlen(address->email);
2764 
2765  /* bufsz = 14 + email_len + SMTPUTF8_LEN + 1 */
2766  if(smtp_si_add_size_t(email_len, SMTPUTF8_LEN + 14 + 1, &bufsz) ||
2767  (envelope_address = malloc(bufsz)) == NULL){
2769  }
2770 
2771  smtputf8_opt = "";
2772  if(smtp_str_has_nonascii_utf8(address->email)){
2773  smtputf8_opt = SMTPUTF8;
2774  }
2775 
2776  concat = smtp_stpcpy(envelope_address, header);
2777  concat = smtp_stpcpy(concat, ":<");
2778  concat = smtp_stpcpy(concat, address->email);
2779  concat = smtp_stpcpy(concat, ">");
2780  concat = smtp_stpcpy(concat, smtputf8_opt);
2781  smtp_stpcpy(concat, "\r\n");
2782  smtp_puts(smtp, envelope_address);
2783  free(envelope_address);
2784 
2785  if(smtp->status_code != SMTP_STATUS_OK){
2786  return smtp->status_code;
2787  }
2789  return smtp->status_code;
2790 }
static enum smtp_status_code smtp_puts(struct smtp *const smtp, const char *const s)
Definition: smtp.c:1655
static int smtp_read_and_parse_code(struct smtp *const smtp)
Definition: smtp.c:1574
SMTP_LINKAGE int smtp_str_has_nonascii_utf8(const char *const s)
Definition: smtp.c:1110
#define malloc
Definition: seams.h:145
enum smtp_status_code smtp_status_code_set(struct smtp *const smtp, enum smtp_status_code status_code)
Definition: smtp.c:3231
enum smtp_status_code status_code
Definition: smtp.c:208
#define strlen
Definition: seams.h:273
SMTP_LINKAGE char * smtp_stpcpy(char *s1, const char *s2)
Definition: smtp.c:599
SMTP_LINKAGE int smtp_si_add_size_t(const size_t a, const size_t b, size_t *const result)
Definition: smtp.c:252
char * email
Definition: smtp.c:99
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ smtp_open()

enum smtp_status_code smtp_open ( const char *const  server,
const char *const  port,
enum smtp_connection_security  connection_security,
enum smtp_flag  flags,
const char *const  cafile,
struct smtp **  smtp 
)

Open a connection to an SMTP server and return the context.

After successfully connecting and performing a handshake with the SMTP server, this will return a valid SMTP client context that the application can use in the other library functions.

This always returns a valid SMTP client context even if the server connection or memory allocation fails. In this scenario, the error status will continue to propagate to future library calls for the SMTP context while in this failure mode.

This function will ignore the SIGPIPE signal. Applications that require a handler for that signal should set it up after calling this function.

Parameters
[in]serverServer name or IP address.
[in]portServer port number.
[in]connection_securitySee smtp_connection_security.
[in]flagsSee smtp_flag.
[in]cafilePath to certificate file, or NULL to use certificates in the default path.
[out]smtpPointer to a new SMTP context. When finished, the caller must free this context using smtp_close.
Returns
See smtp_status_code.

Definition at line 2987 of file smtp.c.

References smtp::cafile, calloc, str_getdelimfd::delim, smtp::flags, g_smtp_error, smtp::gdfd, str_getdelimfd::getdelimfd_read, smtp_connect(), smtp_initiate_handshake(), smtp_status_code_get(), smtp_status_code_set(), SMTP_STATUS_CONNECT, SMTP_STATUS_HANDSHAKE, SMTP_STATUS_OK, smtp_str_getdelimfd_read(), smtp::sock, smtp::status_code, and str_getdelimfd::user_data.

Referenced by mailx_send(), SMTPMail::open(), smtp_func_test_all_address(), smtp_func_test_all_body(), smtp_func_test_all_names(), smtp_func_test_all_nodebug(), smtp_func_test_all_status_code_get(), smtp_func_test_all_write(), smtp_func_test_attachment_fp(), smtp_func_test_attachment_long_text(), smtp_func_test_attachment_mem(), smtp_func_test_attachment_path(), smtp_func_test_attachment_pdf(), smtp_func_test_gmail_attachment(), smtp_func_test_header_custom_date(), smtp_func_test_header_long(), smtp_func_test_header_null_no_date(), smtp_func_test_html(), smtp_func_test_html_with_plaintext(), smtp_func_test_send_email(), smtp_func_test_server_secureserver(), test_failure_address_add(), test_failure_attachment_add(), test_failure_close(), test_failure_header_add(), test_failure_misc(), test_failure_open(), test_failure_status_code_set(), test_failure_timeout(), test_nossl_smtp(), and test_smtp_open_default().

2992  {
2993  struct smtp *snew;
2994 
2995  if((snew = calloc(1, sizeof(**smtp))) == NULL){
2996  *smtp = &g_smtp_error;
2997  return smtp_status_code_get(*smtp);
2998  }
2999  *smtp = snew;
3000 
3001  snew->flags = flags;
3002  snew->sock = -1;
3003  snew->gdfd.delim = '\n';
3005  snew->gdfd.user_data = snew;
3006  snew->cafile = cafile;
3007 
3008 #ifndef SMTP_IS_WINDOWS
3009  signal(SIGPIPE, SIG_IGN);
3010 #endif /* !(SMTP_IS_WINDOWS) */
3011 
3012  if(smtp_connect(snew, server, port) < 0){
3014  }
3015 
3016  if(smtp_initiate_handshake(snew,
3017  server,
3018  connection_security) != SMTP_STATUS_OK){
3020  }
3021 
3022  return snew->status_code;
3023 }
#define calloc
Definition: seams.h:73
struct str_getdelimfd gdfd
Definition: smtp.c:164
enum smtp_status_code smtp_status_code_get(const struct smtp *const smtp)
Definition: smtp.c:3217
enum smtp_flag flags
Definition: smtp.c:154
static long smtp_str_getdelimfd_read(struct str_getdelimfd *const gdfd, void *buf, size_t count)
Definition: smtp.c:382
static struct smtp g_smtp_error
Definition: smtp.c:2954
static int smtp_connect(struct smtp *const smtp, const char *const server, const char *const port)
Definition: smtp.c:1703
static enum smtp_status_code smtp_initiate_handshake(struct smtp *const smtp, const char *const server, enum smtp_connection_security connection_security)
Definition: smtp.c:2161
const char * cafile
Definition: smtp.c:221
void * user_data
Definition: smtp.h:615
enum smtp_status_code smtp_status_code_set(struct smtp *const smtp, enum smtp_status_code status_code)
Definition: smtp.c:3231
enum smtp_status_code status_code
Definition: smtp.c:208
int sock
Definition: smtp.c:159
int delim
Definition: smtp.h:620
long(* getdelimfd_read)(struct str_getdelimfd *const gdfd, void *buf, size_t count)
Definition: smtp.h:608
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ smtp_parse_cmd_line()

SMTP_LINKAGE int smtp_parse_cmd_line ( char *const  line,
struct smtp_command *const  cmd 
)

Parse a server response line into the smtp_command data structure.

Parameters
[in]lineServer response string.
[out]cmdStructure containing the server response data broken up into its separate components.
Returns
See smtp_result_code.

Definition at line 1467 of file smtp.c.

References smtp_command::code, smtp_command::more, SMTP_BEGIN_MAIL, SMTP_INTERNAL_ERROR, strlen, and smtp_command::text.

Referenced by smtp_auth_cram_md5(), smtp_read_and_parse_code(), and smtp_unit_test_parse_cmd_line().

1468  {
1469  char *ep;
1470  char code_str[4];
1471  size_t line_len;
1472  unsigned long int ulcode;
1473 
1474  line_len = strlen(line);
1475  if(line_len < 5){
1476  cmd->code = SMTP_INTERNAL_ERROR;
1477  cmd->more = 0;
1478  cmd->text = line;
1479  return cmd->code;
1480  }
1481 
1482  cmd->text = &line[4];
1483 
1484  memcpy(code_str, line, 3);
1485  code_str[3] = '\0';
1486  ulcode = strtoul(code_str, &ep, 10);
1487  if(*ep != '\0' || ulcode > SMTP_BEGIN_MAIL){
1488  cmd->code = SMTP_INTERNAL_ERROR;
1489  }
1490  else{
1491  cmd->code = (enum smtp_result_code)ulcode;
1492  }
1493 
1494  if(line[3] == '-'){
1495  cmd->more = 1;
1496  }
1497  else{
1498  cmd->more = 0;
1499  }
1500  return cmd->code;
1501 }
enum smtp_result_code code
Definition: smtp.h:531
int more
Definition: smtp.h:539
smtp_result_code
Definition: smtp.h:483
#define strlen
Definition: seams.h:273
const char * text
Definition: smtp.h:544
+ Here is the caller graph for this function:

◆ smtp_print_email()

static enum smtp_status_code smtp_print_email ( struct smtp *const  smtp,
const char *const  body 
)
static

Send the email body to the mail server.

Parameters
[in,out]smtpSMTP client context.
[in]bodyEmail body text.
Returns
See smtp_status_code.

Definition at line 2589 of file smtp.c.

References smtp_header_exists(), smtp_print_mime_email(), smtp_print_nomime_email(), smtp_status_code_set(), SMTP_STATUS_NOMEM, smtp_str_replace(), and smtp::status_code.

Referenced by smtp_mail().

2590  {
2591  char *body_double_dot;
2592 
2593  /*
2594  * Insert an extra dot for each line that begins with a dot. This will
2595  * prevent data in the body parameter from prematurely ending the DATA
2596  * segment.
2597  */
2598  if((body_double_dot = smtp_str_replace("\n.", "\n..", body)) == NULL){
2600  }
2601 
2602  if(smtp_header_exists(smtp, "Content-Type")){
2603  smtp_print_nomime_email(smtp, body_double_dot);
2604  }
2605  else{
2606  smtp_print_mime_email(smtp, body_double_dot);
2607  }
2608  free(body_double_dot);
2609  return smtp->status_code;
2610 }
static enum smtp_status_code smtp_print_nomime_email(struct smtp *const smtp, const char *const body_dd)
Definition: smtp.c:2576
static int smtp_header_exists(const struct smtp *const smtp, const char *const key)
Definition: smtp.c:2352
static enum smtp_status_code smtp_print_mime_email(struct smtp *const smtp, const char *const body_dd)
Definition: smtp.c:2543
enum smtp_status_code smtp_status_code_set(struct smtp *const smtp, enum smtp_status_code status_code)
Definition: smtp.c:3231
enum smtp_status_code status_code
Definition: smtp.c:208
SMTP_LINKAGE char * smtp_str_replace(const char *const search, const char *const replace, const char *const s)
Definition: smtp.c:679
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ smtp_print_header()

static enum smtp_status_code smtp_print_header ( struct smtp *const  smtp,
const struct smtp_header *const  header 
)
static

Convert a header into an RFC 5322 formatted string and send it to the SMTP server.

This will adding proper line wrapping and indentation for long header lines.

Parameters
[in]smtpSMTP client context.
[in]headerSee smtp_header.
Returns
See smtp_status_code.

Definition at line 2624 of file smtp.c.

References smtp_header::key, malloc, smtp_fold_whitespace(), SMTP_LINE_MAX, smtp_puts_terminate(), smtp_si_add_size_t(), smtp_status_code_set(), SMTP_STATUS_NOMEM, smtp_stpcpy(), smtp::status_code, strlen, and smtp_header::value.

Referenced by smtp_mail().

2625  {
2626  size_t key_len;
2627  size_t value_len;
2628  size_t concat_len;
2629  char *header_concat;
2630  char *concat;
2631  char *header_fmt;
2632 
2633  if(header->value == NULL){
2634  return smtp->status_code;
2635  }
2636 
2637  key_len = strlen(header->key);
2638  value_len = strlen(header->value);
2639 
2640  /* concat_len = key_len + 2 + value_len + 1 */
2641  if(smtp_si_add_size_t(key_len, value_len, &concat_len) ||
2642  smtp_si_add_size_t(concat_len, 3, &concat_len) ||
2643  (header_concat = malloc(concat_len)) == NULL){
2645  }
2646  concat = smtp_stpcpy(header_concat, header->key);
2647  concat = smtp_stpcpy(concat, ": ");
2648  smtp_stpcpy(concat, header->value);
2649 
2650  header_fmt = smtp_fold_whitespace(header_concat, SMTP_LINE_MAX);
2651  free(header_concat);
2652  if(header_fmt == NULL){
2654  }
2655 
2656  smtp_puts_terminate(smtp, header_fmt);
2657  free(header_fmt);
2658  return smtp->status_code;
2659 }
#define SMTP_LINE_MAX
Definition: smtp.c:1218
char * key
Definition: smtp.c:139
char * value
Definition: smtp.c:144
#define malloc
Definition: seams.h:145
enum smtp_status_code smtp_status_code_set(struct smtp *const smtp, enum smtp_status_code status_code)
Definition: smtp.c:3231
enum smtp_status_code status_code
Definition: smtp.c:208
#define strlen
Definition: seams.h:273
SMTP_LINKAGE char * smtp_stpcpy(char *s1, const char *s2)
Definition: smtp.c:599
SMTP_LINKAGE int smtp_si_add_size_t(const size_t a, const size_t b, size_t *const result)
Definition: smtp.c:252
SMTP_LINKAGE char * smtp_fold_whitespace(const char *const s, unsigned int maxlen)
Definition: smtp.c:1250
static enum smtp_status_code smtp_puts_terminate(struct smtp *const smtp, const char *const s)
Definition: smtp.c:1669
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ smtp_print_mime_attachment()

static enum smtp_status_code smtp_print_mime_attachment ( struct smtp *const  smtp,
const char *const  boundary,
const struct smtp_attachment *const  attachment 
)
static

Print a MIME section containing an attachment.

Parameters
[in]smtpSMTP client context.
[in]boundaryMIME boundary text.
[in]attachmentSee smtp_attachment.
Returns
See smtp_status_code.

Definition at line 2464 of file smtp.c.

References smtp_attachment::b64_data, malloc, smtp_attachment::name, SMTP_MIME_BOUNDARY_LEN, smtp_puts(), smtp_si_add_size_t(), smtp_status_code_set(), SMTP_STATUS_NOMEM, smtp_stpcpy(), smtp::status_code, and strlen.

Referenced by smtp_print_mime_email().

2466  {
2467  /* Buffer size for the static MIME text used below. */
2468  const size_t MIME_TEXT_BUFSZ = 1000;
2469  size_t name_len;
2470  size_t b64_data_len;
2471  size_t bufsz;
2472  char *mime_attach_text;
2473  char *concat;
2474 
2475  name_len = strlen(attachment->name);
2476  b64_data_len = strlen(attachment->b64_data);
2477  /*
2478  * bufsz = SMTP_MIME_BOUNDARY_LEN + name_len + b64_data_len + MIME_TEXT_BUFSZ
2479  */
2480  if(smtp_si_add_size_t(name_len, b64_data_len, &bufsz) ||
2481  smtp_si_add_size_t(bufsz,
2482  SMTP_MIME_BOUNDARY_LEN + MIME_TEXT_BUFSZ,
2483  &bufsz) ||
2484  (mime_attach_text = malloc(bufsz)) == NULL){
2486  }
2487 
2488  concat = smtp_stpcpy(mime_attach_text,
2489  "--");
2490  concat = smtp_stpcpy(concat,
2491  boundary);
2492  concat = smtp_stpcpy(concat,
2493  "\r\n"
2494  "Content-Type: application/octet-stream\r\n"
2495  "Content-Disposition: attachment; filename=\"");
2496  concat = smtp_stpcpy(concat,
2497  attachment->name);
2498  concat = smtp_stpcpy(concat,
2499  "\"\r\n"
2500  "Content-Transfer-Encoding: base64\r\n"
2501  "\r\n");
2502  concat = smtp_stpcpy(concat,
2503  attachment->b64_data);
2504  smtp_stpcpy(concat,
2505  "\r\n"
2506  "\r\n");
2507  smtp_puts(smtp, mime_attach_text);
2508  free(mime_attach_text);
2509  return smtp->status_code;
2510 }
char * b64_data
Definition: smtp.c:129
#define SMTP_MIME_BOUNDARY_LEN
Definition: smtp.c:2370
static enum smtp_status_code smtp_puts(struct smtp *const smtp, const char *const s)
Definition: smtp.c:1655
#define malloc
Definition: seams.h:145
enum smtp_status_code smtp_status_code_set(struct smtp *const smtp, enum smtp_status_code status_code)
Definition: smtp.c:3231
enum smtp_status_code status_code
Definition: smtp.c:208
#define strlen
Definition: seams.h:273
char * name
Definition: smtp.c:124
SMTP_LINKAGE char * smtp_stpcpy(char *s1, const char *s2)
Definition: smtp.c:599
SMTP_LINKAGE int smtp_si_add_size_t(const size_t a, const size_t b, size_t *const result)
Definition: smtp.c:252
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ smtp_print_mime_email()

static enum smtp_status_code smtp_print_mime_email ( struct smtp *const  smtp,
const char *const  body_dd 
)
static

Send the main email body to the SMTP server.

This includes the MIME sections for the email body and attachments.

Parameters
[in]smtpSMTP client context.
[in]body_ddEmail body with double dots added at the beginning of each line.
Returns
See smtp_status_code.

Definition at line 2543 of file smtp.c.

References smtp::attachment_list, smtp::num_attachment, smtp_gen_mime_boundary(), SMTP_MIME_BOUNDARY_LEN, smtp_print_mime_attachment(), smtp_print_mime_end(), smtp_print_mime_header_and_body(), SMTP_STATUS_OK, and smtp::status_code.

Referenced by smtp_print_email().

2544  {
2545  char boundary[SMTP_MIME_BOUNDARY_LEN];
2546  size_t i;
2547  struct smtp_attachment *attachment;
2548 
2549  smtp_gen_mime_boundary(boundary);
2550 
2551  if(smtp_print_mime_header_and_body(smtp, boundary, body_dd) != SMTP_STATUS_OK){
2552  return smtp->status_code;
2553  }
2554 
2555  for(i = 0; i < smtp->num_attachment; i++){
2556  attachment = &smtp->attachment_list[i];
2558  boundary,
2559  attachment) != SMTP_STATUS_OK){
2560  return smtp->status_code;
2561  }
2562  }
2563 
2564  return smtp_print_mime_end(smtp, boundary);
2565 }
#define SMTP_MIME_BOUNDARY_LEN
Definition: smtp.c:2370
struct smtp_attachment * attachment_list
Definition: smtp.c:189
static enum smtp_status_code smtp_print_mime_attachment(struct smtp *const smtp, const char *const boundary, const struct smtp_attachment *const attachment)
Definition: smtp.c:2464
static enum smtp_status_code smtp_print_mime_header_and_body(struct smtp *const smtp, const char *const boundary, const char *const body_dd)
Definition: smtp.c:2411
static void smtp_gen_mime_boundary(char *const boundary)
Definition: smtp.c:2386
static enum smtp_status_code smtp_print_mime_end(struct smtp *const smtp, const char *const boundary)
Definition: smtp.c:2521
size_t num_attachment
Definition: smtp.c:194
enum smtp_status_code status_code
Definition: smtp.c:208
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ smtp_print_mime_end()

static enum smtp_status_code smtp_print_mime_end ( struct smtp *const  smtp,
const char *const  boundary 
)
static

Prints double hyphen on both sides of the MIME boundary which indicates the end of the MIME sections.

Parameters
[in]smtpSMTP client context.
[in]boundaryMIME boundary text.
Returns
See smtp_status_code and smtp_puts.

Definition at line 2521 of file smtp.c.

References SMTP_MIME_BOUNDARY_LEN, smtp_puts(), and smtp_stpcpy().

Referenced by smtp_print_mime_email().

2522  {
2523  char *concat;
2524  char mime_end[2 + SMTP_MIME_BOUNDARY_LEN + 4 + 1];
2525 
2526  concat = smtp_stpcpy(mime_end, "--");
2527  concat = smtp_stpcpy(concat, boundary);
2528  smtp_stpcpy(concat, "--\r\n");
2529  return smtp_puts(smtp, mime_end);
2530 }
#define SMTP_MIME_BOUNDARY_LEN
Definition: smtp.c:2370
static enum smtp_status_code smtp_puts(struct smtp *const smtp, const char *const s)
Definition: smtp.c:1655
SMTP_LINKAGE char * smtp_stpcpy(char *s1, const char *s2)
Definition: smtp.c:599
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ smtp_print_mime_header_and_body()

static enum smtp_status_code smtp_print_mime_header_and_body ( struct smtp *const  smtp,
const char *const  boundary,
const char *const  body_dd 
)
static

Print the MIME header and the MIME section containing the email body.

Parameters
[in]smtpSMTP client context.
[in]boundaryMIME boundary text.
[in]body_ddEmail body with double dots added at the beginning of each line.
Returns
See smtp_status_code.

Definition at line 2411 of file smtp.c.

References malloc, smtp_puts(), smtp_si_add_size_t(), smtp_status_code_set(), SMTP_STATUS_NOMEM, smtp_stpcpy(), smtp::status_code, and strlen.

Referenced by smtp_print_mime_email().

2413  {
2414  /* Buffer size for the static MIME text used below. */
2415  const size_t MIME_TEXT_BUFSZ = 1000;
2416  size_t data_double_dot_len;
2417  char *data_header_and_body;
2418  char *concat;
2419 
2420  data_double_dot_len = strlen(body_dd);
2421  if(smtp_si_add_size_t(data_double_dot_len,
2422  MIME_TEXT_BUFSZ,
2423  &data_double_dot_len) ||
2424  (data_header_and_body = malloc(data_double_dot_len)) == NULL){
2426  }
2427 
2428  concat = smtp_stpcpy(data_header_and_body,
2429  "MIME-Version: 1.0\r\n"
2430  "Content-Type: multipart/mixed; boundary=");
2431  concat = smtp_stpcpy(concat,
2432  boundary);
2433  concat = smtp_stpcpy(concat,
2434  "\r\n"
2435  "\r\n"
2436  "Multipart MIME message.\r\n"
2437  "--");
2438  concat = smtp_stpcpy(concat,
2439  boundary);
2440  concat = smtp_stpcpy(concat,
2441  "\r\n"
2442  "Content-Type: text/plain; charset=\"UTF-8\"\r\n"
2443  "\r\n");
2444  concat = smtp_stpcpy(concat,
2445  body_dd);
2446  smtp_stpcpy(concat,
2447  "\r\n"
2448  "\r\n");
2449 
2450  smtp_puts(smtp, data_header_and_body);
2451  free(data_header_and_body);
2452  return smtp->status_code;
2453 }
static enum smtp_status_code smtp_puts(struct smtp *const smtp, const char *const s)
Definition: smtp.c:1655
#define malloc
Definition: seams.h:145
enum smtp_status_code smtp_status_code_set(struct smtp *const smtp, enum smtp_status_code status_code)
Definition: smtp.c:3231
enum smtp_status_code status_code
Definition: smtp.c:208
#define strlen
Definition: seams.h:273
SMTP_LINKAGE char * smtp_stpcpy(char *s1, const char *s2)
Definition: smtp.c:599
SMTP_LINKAGE int smtp_si_add_size_t(const size_t a, const size_t b, size_t *const result)
Definition: smtp.c:252
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ smtp_print_nomime_email()

static enum smtp_status_code smtp_print_nomime_email ( struct smtp *const  smtp,
const char *const  body_dd 
)
static

Print the email data provided by the user without MIME formatting.

Parameters
[in,out]smtpSMTP client context.
[in]body_ddEmail body with double dots added at the beginning of each line.
Returns
See smtp_status_code.

Definition at line 2576 of file smtp.c.

References smtp_puts_terminate().

Referenced by smtp_print_email().

2577  {
2578  return smtp_puts_terminate(smtp, body_dd);
2579 }
static enum smtp_status_code smtp_puts_terminate(struct smtp *const smtp, const char *const s)
Definition: smtp.c:1669
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ smtp_puts()

static enum smtp_status_code smtp_puts ( struct smtp *const  smtp,
const char *const  s 
)
static

Send a null-terminated string to the SMTP server.

Parameters
[in]smtpSMTP client context.
[in]sNull-terminated string to send to the SMTP server.
Returns
See smtp_status_code and smtp_write.

Definition at line 1655 of file smtp.c.

References smtp_write(), and strlen.

Referenced by smtp_auth_cram_md5(), smtp_auth_login(), smtp_auth_plain(), smtp_close(), smtp_ehlo(), smtp_initiate_handshake(), smtp_mail(), smtp_mail_envelope_header(), smtp_print_mime_attachment(), smtp_print_mime_end(), smtp_print_mime_header_and_body(), and smtp_puts_terminate().

1656  {
1657  return smtp_write(smtp, s, strlen(s));
1658 }
SMTP_LINKAGE enum smtp_status_code smtp_write(struct smtp *const smtp, const char *const buf, size_t len)
Definition: smtp.c:1603
#define strlen
Definition: seams.h:273
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ smtp_puts_dbg()

static void smtp_puts_dbg ( struct smtp *const  smtp,
const char *const  prefix,
const char *const  str 
)
static

Prints communication between the client and server to stderr only if the debug flag has been set.

Parameters
[in]smtpSMTP client context.
[in]prefixPrint this prefix before the main debug line text.
[in]strDebug text to print out.

Definition at line 1512 of file smtp.c.

References smtp::flags, SMTP_DEBUG, and smtp_strdup().

Referenced by smtp_getline(), and smtp_write().

1514  {
1515  char *sdup;
1516  size_t i;
1517 
1518  if(smtp->flags & SMTP_DEBUG){
1519  if((sdup = smtp_strdup(str)) == NULL){
1520  return;
1521  }
1522 
1523  /* Remove carriage return and newline when printing to stderr. */
1524  for(i = 0; sdup[i]; i++){
1525  if(sdup[i] == '\r' || sdup[i] == '\n'){
1526  sdup[i] = ' ';
1527  }
1528  }
1529 
1530  if(fprintf(stderr, "[smtp %s]: %s\n", prefix, sdup) < 0){
1531  /* Do not care if this fails. */
1532  }
1533  free(sdup);
1534  }
1535 }
enum smtp_flag flags
Definition: smtp.c:154
SMTP_LINKAGE char * smtp_strdup(const char *s)
Definition: smtp.c:650
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ smtp_puts_terminate()

static enum smtp_status_code smtp_puts_terminate ( struct smtp *const  smtp,
const char *const  s 
)
static

Same as smtp_puts except this function also appends the line terminating carriage return and newline bytes at the end of the string.

Parameters
[in]smtpSMTP client context.
[in]sNull-terminated string to send to the SMTP server.
Returns
See smtp_status_code and smtp_puts.

Definition at line 1669 of file smtp.c.

References malloc, smtp_puts(), smtp_si_add_size_t(), smtp_status_code_set(), SMTP_STATUS_NOMEM, smtp_stpcpy(), and strlen.

Referenced by smtp_auth_cram_md5(), smtp_auth_login(), smtp_print_header(), and smtp_print_nomime_email().

1670  {
1671  enum smtp_status_code rc;
1672  char *line;
1673  char *concat;
1674  size_t slen;
1675  size_t allocsz;
1676 
1677  slen = strlen(s);
1678  if(smtp_si_add_size_t(slen, 3, &allocsz) ||
1679  (line = malloc(allocsz)) == NULL){
1681  }
1682  concat = smtp_stpcpy(line, s);
1683  smtp_stpcpy(concat, "\r\n");
1684  rc = smtp_puts(smtp, line);
1685  free(line);
1686  return rc;
1687 }
static enum smtp_status_code smtp_puts(struct smtp *const smtp, const char *const s)
Definition: smtp.c:1655
smtp_status_code
Definition: smtp.h:32
#define malloc
Definition: seams.h:145
enum smtp_status_code smtp_status_code_set(struct smtp *const smtp, enum smtp_status_code status_code)
Definition: smtp.c:3231
#define strlen
Definition: seams.h:273
SMTP_LINKAGE char * smtp_stpcpy(char *s1, const char *s2)
Definition: smtp.c:599
SMTP_LINKAGE int smtp_si_add_size_t(const size_t a, const size_t b, size_t *const result)
Definition: smtp.c:252
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ smtp_read_and_parse_code()

static int smtp_read_and_parse_code ( struct smtp *const  smtp)
static

Loop through all of the server response lines until the last line, and then return the status code from the last response line.

Parameters
[in]smtpSMTP client context.
Returns
See smtp_result_code.

Definition at line 1574 of file smtp.c.

References smtp_command::code, smtp::gdfd, str_getdelimfd::line, smtp_command::more, smtp_getline(), SMTP_INTERNAL_ERROR, SMTP_LINKAGE, smtp_parse_cmd_line(), STRING_GETDELIMFD_DONE, and STRING_GETDELIMFD_ERROR.

Referenced by smtp_auth_cram_md5(), smtp_auth_login(), smtp_auth_plain(), smtp_ehlo(), smtp_initiate_handshake(), smtp_mail(), and smtp_mail_envelope_header().

1574  {
1575  struct smtp_command cmd;
1576  enum str_getdelim_retcode rc;
1577 
1578  do{
1579  rc = smtp_getline(smtp);
1580  if(rc == STRING_GETDELIMFD_ERROR){
1581  return SMTP_INTERNAL_ERROR;
1582  }
1583 
1584  smtp_parse_cmd_line(smtp->gdfd.line, &cmd);
1585  }while (rc != STRING_GETDELIMFD_DONE && cmd.more);
1586 
1587  return cmd.code;
1588 }
static enum str_getdelim_retcode smtp_getline(struct smtp *const smtp)
Definition: smtp.c:1544
SMTP_LINKAGE int smtp_parse_cmd_line(char *const line, struct smtp_command *const cmd)
Definition: smtp.c:1467
struct str_getdelimfd gdfd
Definition: smtp.c:164
char * line
Definition: smtp.h:592
str_getdelim_retcode
Definition: smtp.h:551
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ smtp_reallocarray()

SMTP_LINKAGE void* smtp_reallocarray ( void *  ptr,
size_t  nmemb,
size_t  size 
)

Reallocate memory with unsigned wrapping checks.

Parameters
[in]ptrExisting allocation buffer, or NULL when allocating a new buffer.
[in]nmembNumber of elements to allocate.
[in]sizeSize of each element in nmemb.
Return values
void*Pointer to a reallocated buffer containing nmemb * size bytes.
NULLFailed to reallocate memory.

Definition at line 622 of file smtp.c.

References realloc, SMTP_LINKAGE, and smtp_si_mul_size_t().

Referenced by smtp_address_add(), smtp_attachment_add_mem(), smtp_header_add(), and smtp_unit_test_reallocarray().

624  {
625  void *alloc;
626  size_t size_mul;
627 
628  if(smtp_si_mul_size_t(nmemb, size, &size_mul)){
629  alloc = NULL;
630  errno = ENOMEM;
631  }
632  else{
633  alloc = realloc(ptr, size_mul);
634  }
635  return alloc;
636 }
#define realloc
Definition: seams.h:161
SMTP_LINKAGE int smtp_si_mul_size_t(const size_t a, const size_t b, size_t *const result)
Definition: smtp.c:320
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ smtp_set_read_timeout()

static void smtp_set_read_timeout ( struct smtp *const  smtp,
long  seconds 
)
static

Set the timeout for the next socket read operation.

Parameters
[in]smtpSMTP client context.
[in]secondsTimeout in seconds.

Definition at line 2138 of file smtp.c.

References smtp::timeout_sec.

Referenced by smtp_initiate_handshake(), and smtp_mail().

2139  {
2140  smtp->timeout_sec = seconds;
2141 }
long timeout_sec
Definition: smtp.c:201
+ Here is the caller graph for this function:

◆ smtp_si_add_size_t()

SMTP_LINKAGE int smtp_si_add_size_t ( const size_t  a,
const size_t  b,
size_t *const  result 
)

Check if adding a size_t value will cause a wrap.

Parameters
[in]aAdd this value with b.
[in]bAdd this value with a.
[out]resultSave the addition to this buffer. Does not perform the addition if set to NULL.
Return values
1Value wrapped.
0Value did not wrap.

Definition at line 252 of file smtp.c.

References g_smtp_test_err_si_add_size_t_ctr, SIZE_MAX, SMTP_LINKAGE, and smtp_test_seam_dec_err_ctr().

Referenced by smtp_address_add(), smtp_append_address_to_header(), smtp_attachment_add_mem(), smtp_auth_cram_md5(), smtp_auth_login(), smtp_auth_plain(), smtp_base64_decode(), smtp_bin2hex(), smtp_chunk_split(), smtp_ffile_get_contents(), smtp_fold_whitespace(), smtp_header_add(), smtp_mail_envelope_header(), smtp_print_header(), smtp_print_mime_attachment(), smtp_print_mime_header_and_body(), smtp_puts_terminate(), smtp_str_getdelimfd(), smtp_str_getdelimfd_set_line_and_buf(), smtp_str_replace(), smtp_strdup(), and smtp_unit_test_all_si().

254  {
255  int wraps;
256 
257 #ifdef SMTP_TEST
259  return 1;
260  }
261 #endif /* SMTP_TEST */
262 
263  if(SIZE_MAX - a < b){
264  wraps = 1;
265  }
266  else{
267  wraps = 0;
268  }
269  if(result){
270  *result = a + b;
271  }
272  return wraps;
273 }
#define SIZE_MAX
Definition: smtp.h:23
int g_smtp_test_err_si_add_size_t_ctr
Definition: seams.c:137
int smtp_test_seam_dec_err_ctr(int *const test_err_ctr)
Definition: seams.c:244
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ smtp_si_mul_size_t()

SMTP_LINKAGE int smtp_si_mul_size_t ( const size_t  a,
const size_t  b,
size_t *const  result 
)

Check if multiplying a size_t value will cause a wrap.

Parameters
[in]aMultiply this value with b.
[in]bMultiply this value with a.
[out]resultSave the multiplication to this buffer. Does not perform the multiplication if set to NULL.
Return values
1Value wrapped.
0Value did not wrap.

Definition at line 320 of file smtp.c.

References g_smtp_test_err_si_mul_size_t_ctr, SIZE_MAX, and smtp_test_seam_dec_err_ctr().

Referenced by smtp_base64_encode(), smtp_bin2hex(), smtp_chunk_split(), smtp_reallocarray(), and smtp_unit_test_all_si().

322  {
323  int wraps;
324 
325 #ifdef SMTP_TEST
327  return 1;
328  }
329 #endif /* SMTP_TEST */
330 
331  if(b != 0 && a > SIZE_MAX / b){
332  wraps = 1;
333  }
334  else{
335  wraps = 0;
336  }
337  if(result){
338  *result = a * b;
339  }
340  return wraps;
341 }
#define SIZE_MAX
Definition: smtp.h:23
int g_smtp_test_err_si_mul_size_t_ctr
Definition: seams.c:149
int smtp_test_seam_dec_err_ctr(int *const test_err_ctr)
Definition: seams.c:244
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ smtp_si_sub_size_t()

SMTP_LINKAGE int smtp_si_sub_size_t ( const size_t  a,
const size_t  b,
size_t *const  result 
)

Check if subtracting a size_t value will cause wrap.

Parameters
[in]aSubtract this value by b.
[in]bSubtract this value from a.
[out]resultSave the subtraction to this buffer. Does not perform the subtraction if set to NULL.
Return values
1Value wrapped.
0Value did not wrap.

Definition at line 286 of file smtp.c.

References g_smtp_test_err_si_sub_size_t_ctr, SMTP_LINKAGE, and smtp_test_seam_dec_err_ctr().

Referenced by smtp_str_getdelimfd(), smtp_str_getdelimfd_set_line_and_buf(), and smtp_unit_test_all_si().

288  {
289  int wraps;
290 
291 #ifdef SMTP_TEST
293  return 1;
294  }
295 #endif /* SMTP_TEST */
296 
297  if(a < b){
298  wraps = 1;
299  }
300  else{
301  wraps = 0;
302  }
303  if(result){
304  *result = a - b;
305  }
306  return wraps;
307 }
int g_smtp_test_err_si_sub_size_t_ctr
Definition: seams.c:143
int smtp_test_seam_dec_err_ctr(int *const test_err_ctr)
Definition: seams.c:244
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ smtp_status_code_clear()

enum smtp_status_code smtp_status_code_clear ( struct smtp *const  smtp)

Clear the current error code set in the SMTP client context.

Parameters
[in,out]smtpSMTP client context.
Returns
Previous error code before clearing.

Definition at line 3222 of file smtp.c.

References smtp_status_code_get(), smtp_status_code_set(), and SMTP_STATUS_OK.

Referenced by smtp_func_test_all_address(), smtp_func_test_all_status_code_get(), test_failure_address_add(), test_failure_attachment_add(), test_failure_header_add(), and test_failure_status_code_set().

3222  {
3223  enum smtp_status_code old_status;
3224 
3225  old_status = smtp_status_code_get(smtp);
3227  return old_status;
3228 }
enum smtp_status_code smtp_status_code_get(const struct smtp *const smtp)
Definition: smtp.c:3217
smtp_status_code
Definition: smtp.h:32
enum smtp_status_code smtp_status_code_set(struct smtp *const smtp, enum smtp_status_code status_code)
Definition: smtp.c:3231
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ smtp_status_code_errstr()

const char* smtp_status_code_errstr ( enum smtp_status_code  status_code)

Convert a standard SMTP client status code to a descriptive string.

Parameters
[in]status_codeStatus code returned from one of the other library functions.
Returns
String containing a description of the status_code. The caller must not free or modify this string.

Definition at line 3241 of file smtp.c.

References SMTP_STATUS__LAST.

Referenced by mailx_send(), smtp_unit_test_smtp_status_code_errstr(), and SMTPMailException::what().

3241  {
3242  const char *const status_code_err_str[] = {
3243  /* SMTP_STATUS_OK */
3244  "Success",
3245  /* SMTP_STATUS_NOMEM */
3246  "Memory allocation failed",
3247  /* SMTP_STATUS_CONNECT */
3248  "Failed to connect to the mail server",
3249  /* SMTP_STATUS_HANDSHAKE */
3250  "Failed to handshake or negotiate a TLS connection with the server",
3251  /* SMTP_STATUS_AUTH */
3252  "Failed to authenticate with the given credentials",
3253  /* SMTP_STATUS_SEND */
3254  "Failed to send bytes to the server",
3255  /* SMTP_STATUS_RECV */
3256  "Failed to receive bytes from the server",
3257  /* SMTP_STATUS_CLOSE */
3258  "Failed to properly close a connection",
3259  /* SMTP_STATUS_SERVER_RESPONSE */
3260  "SMTP server sent back an unexpected status code",
3261  /* SMTP_STATUS_PARAM */
3262  "Invalid parameter",
3263  /* SMTP_STATUS_FILE */
3264  "Failed to read or open a local file",
3265  /* SMTP_STATUS_DATE */
3266  "Failed to get the local date and time",
3267  /* SMTP_STATUS__LAST */
3268  "Unknown error"
3269  };
3270 
3271  if((unsigned)status_code > SMTP_STATUS__LAST){
3272  status_code = SMTP_STATUS__LAST;
3273  }
3274  return status_code_err_str[status_code];
3275 }
+ Here is the caller graph for this function:

◆ smtp_status_code_get()

enum smtp_status_code smtp_status_code_get ( const struct smtp *const  smtp)

Get the current status/error code.

Parameters
[in]smtpSMTP client context.
Returns
See smtp_status_code.

Definition at line 3217 of file smtp.c.

References smtp::status_code.

Referenced by smtp_func_test_all_status_code_get(), smtp_open(), smtp_status_code_clear(), SMTPMail::status_code_get(), and test_failure_status_code_set().

3217  {
3218  return smtp->status_code;
3219 }
enum smtp_status_code status_code
Definition: smtp.c:208
+ Here is the caller graph for this function:

◆ smtp_status_code_set()

enum smtp_status_code smtp_status_code_set ( struct smtp *const  smtp,
enum smtp_status_code  new_status_code 
)

Set the error status of the SMTP client context and return the same code.

This allows the caller to clear an error status to SMTP_STATUS_OK so that previous errors will stop propagating. However, this will only work correctly for clearing the SMTP_STATUS_PARAM and SMTP_STATUS_FILE errors. Do not use this to clear any other error codes.

Deprecated:
Use smtp_status_code_clear instead.
Parameters
[in]smtpSMTP client context.
[in]new_status_codeSee smtp_status_code.
Returns
See smtp_status_code.

Definition at line 3231 of file smtp.c.

References SMTP_STATUS__LAST, SMTP_STATUS_PARAM, and smtp::status_code.

Referenced by smtp_address_add(), smtp_append_address_to_header(), smtp_attachment_add_fp(), smtp_attachment_add_mem(), smtp_attachment_add_path(), smtp_auth(), smtp_close(), smtp_func_test_all_status_code_get(), smtp_getline(), smtp_header_add(), smtp_initiate_handshake(), smtp_mail(), smtp_mail_envelope_header(), smtp_open(), smtp_print_email(), smtp_print_header(), smtp_print_mime_attachment(), smtp_print_mime_header_and_body(), smtp_puts_terminate(), smtp_status_code_clear(), smtp_str_getdelimfd_read_timeout(), smtp_write(), SMTPMail::status_code_set(), test_failure_address_add(), test_failure_attachment_add(), test_failure_auth(), test_failure_header_add(), test_failure_mail(), and test_failure_status_code_set().

3232  {
3233  if((unsigned)status_code >= SMTP_STATUS__LAST){
3235  }
3236  smtp->status_code = status_code;
3237  return status_code;
3238 }
enum smtp_status_code smtp_status_code_set(struct smtp *const smtp, enum smtp_status_code status_code)
Definition: smtp.c:3231
enum smtp_status_code status_code
Definition: smtp.c:208
+ Here is the caller graph for this function:

◆ smtp_stpcpy()

SMTP_LINKAGE char* smtp_stpcpy ( char *  s1,
const char *  s2 
)

Copy a string and get the pointer to the end of the copied buffer.

This function behaves similar to POSIX stpcpy(), useful for concatenating multiple strings onto a buffer. It always adds a null-terminated byte at the end of the string.

Parameters
[in]s1Destination buffer.
[in]s2Null-terminated source string to copy to s1.
Returns
Pointer to location in s1 after the last copied byte.

Definition at line 599 of file smtp.c.

References SMTP_LINKAGE.

Referenced by smtp_append_address_to_header(), smtp_auth_cram_md5(), smtp_auth_login(), smtp_auth_plain(), smtp_mail_envelope_header(), smtp_print_header(), smtp_print_mime_attachment(), smtp_print_mime_end(), smtp_print_mime_header_and_body(), smtp_puts_terminate(), and smtp_unit_test_stpcpy().

600  {
601  size_t i;
602 
603  i = 0;
604  do{
605  s1[i] = s2[i];
606  } while(s2[i++] != '\0');
607  return &s1[i-1];
608 }
+ Here is the caller graph for this function:

◆ smtp_str_getdelimfd()

SMTP_LINKAGE enum str_getdelim_retcode smtp_str_getdelimfd ( struct str_getdelimfd *const  gdfd)

Read and parse a delimited string using a custom socket read function.

This interface handles all of the logic for expanding the buffer, parsing the delimiter in the buffer, and returning each "line" to the caller for handling.

Parameters
[in]gdfdSee str_getdelimfd.
Returns
See str_getdelim_retcode.

Definition at line 523 of file smtp.c.

References str_getdelimfd::_buf, str_getdelimfd::_buf_len, str_getdelimfd::_bufsz, str_getdelimfd::delim, str_getdelimfd::getdelimfd_read, realloc, SMTP_GETDELIM_READ_SZ, SMTP_LINKAGE, smtp_si_add_size_t(), smtp_si_sub_size_t(), smtp_str_getdelimfd_search_delim(), smtp_str_getdelimfd_set_line_and_buf(), smtp_str_getdelimfd_throw_error(), STRING_GETDELIMFD_DONE, STRING_GETDELIMFD_ERROR, and STRING_GETDELIMFD_NEXT.

Referenced by smtp_getline(), and smtp_unit_test_str_getdelimfd().

523  {
524  size_t delim_pos;
525  long bytes_read;
526  void *read_buf_ptr;
527  char *buf_new;
528  size_t buf_sz_remaining;
529  size_t buf_sz_new;
530 
531  if(gdfd->getdelimfd_read == NULL){
533  }
534 
535  bytes_read = -1;
536 
537  while(1){
539  gdfd->_buf_len,
540  gdfd->delim,
541  &delim_pos)){
542  if(smtp_str_getdelimfd_set_line_and_buf(gdfd, delim_pos) < 0){
543  return smtp_str_getdelimfd_throw_error(gdfd);
544  }
545  return STRING_GETDELIMFD_NEXT;
546  }else if(bytes_read == 0){
547  if(smtp_str_getdelimfd_set_line_and_buf(gdfd, gdfd->_buf_len) < 0){
548  return smtp_str_getdelimfd_throw_error(gdfd);
549  }
550  return STRING_GETDELIMFD_DONE;
551  }
552 
553  if(smtp_si_sub_size_t(gdfd->_bufsz, gdfd->_buf_len, &buf_sz_remaining)){
554  return smtp_str_getdelimfd_throw_error(gdfd);
555  }
556 
557  if(buf_sz_remaining < SMTP_GETDELIM_READ_SZ){
558  if(smtp_si_add_size_t(buf_sz_remaining,
560  &buf_sz_new)){
561  return smtp_str_getdelimfd_throw_error(gdfd);
562  }
563  buf_new = realloc(gdfd->_buf, buf_sz_new);
564  if(buf_new == NULL){
565  return smtp_str_getdelimfd_throw_error(gdfd);
566  }
567  gdfd->_buf = buf_new;
568  gdfd->_bufsz = buf_sz_new;
569  }
570 
571  if(smtp_si_add_size_t((size_t)gdfd->_buf, gdfd->_buf_len, NULL)){
572  return smtp_str_getdelimfd_throw_error(gdfd);
573  }
574  read_buf_ptr = gdfd->_buf + gdfd->_buf_len;
575  bytes_read = (*gdfd->getdelimfd_read)(gdfd,
576  read_buf_ptr,
578  if(bytes_read < 0 ||
580  (size_t)bytes_read,
581  &gdfd->_buf_len)){
582  return smtp_str_getdelimfd_throw_error(gdfd);
583  }
584  }
585 }
#define SMTP_GETDELIM_READ_SZ
Definition: smtp.c:88
#define realloc
Definition: seams.h:161
size_t _buf_len
Definition: smtp.h:587
static int smtp_str_getdelimfd_search_delim(const char *const buf, size_t buf_len, int delim, size_t *const delim_pos)
Definition: smtp.c:431
char * _buf
Definition: smtp.h:577
SMTP_LINKAGE int smtp_si_sub_size_t(const size_t a, const size_t b, size_t *const result)
Definition: smtp.c:286
SMTP_LINKAGE int smtp_si_add_size_t(const size_t a, const size_t b, size_t *const result)
Definition: smtp.c:252
SMTP_LINKAGE int smtp_str_getdelimfd_set_line_and_buf(struct str_getdelimfd *const gdfd, size_t copy_len)
Definition: smtp.c:457
int delim
Definition: smtp.h:620
static enum str_getdelim_retcode smtp_str_getdelimfd_throw_error(struct str_getdelimfd *const gdfd)
Definition: smtp.c:507
long(* getdelimfd_read)(struct str_getdelimfd *const gdfd, void *buf, size_t count)
Definition: smtp.h:608
size_t _bufsz
Definition: smtp.h:582
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ smtp_str_getdelimfd_free()

SMTP_LINKAGE void smtp_str_getdelimfd_free ( struct str_getdelimfd *const  gdfd)

Free memory in the str_getdelimfd data structure.

Parameters
[in]gdfdFrees memory stored in this socket parsing structure.

Definition at line 489 of file smtp.c.

References str_getdelimfd::_buf, str_getdelimfd::_buf_len, str_getdelimfd::_bufsz, str_getdelimfd::line, and str_getdelimfd::line_len.

Referenced by smtp_close(), smtp_str_getdelimfd_throw_error(), and smtp_unit_test_str_getdelimfd().

489  {
490  free(gdfd->_buf);
491  free(gdfd->line);
492  gdfd->_buf = NULL;
493  gdfd->_bufsz = 0;
494  gdfd->_buf_len = 0;
495  gdfd->line = NULL;
496  gdfd->line_len = 0;
497 }
size_t _buf_len
Definition: smtp.h:587
size_t line_len
Definition: smtp.h:597
char * line
Definition: smtp.h:592
char * _buf
Definition: smtp.h:577
size_t _bufsz
Definition: smtp.h:582
+ Here is the caller graph for this function:

◆ smtp_str_getdelimfd_read()

static long smtp_str_getdelimfd_read ( struct str_getdelimfd *const  gdfd,
void *  buf,
size_t  count 
)
static

This function gets called by the smtp_str_getdelimfd interface when it needs to read in more data.

It reads using either the plain socket connection if encryption not enabled, or it reads using OpenSSL if it has an active TLS connection.

Parameters
[in]gdfdSee str_getdelimfd.
[out]bufPointer to buffer for storing bytes read.
[in]countMaximum number of bytes to try reading.
Return values
>=0Number of bytes read.
-1Failed to read from the socket.

Definition at line 382 of file smtp.c.

References BIO_should_retry, recv, SMTP_STATUS_OK, smtp_str_getdelimfd_read_timeout(), smtp::sock, SSL_read, smtp::tls, smtp::tls_bio, smtp::tls_on, and str_getdelimfd::user_data.

Referenced by smtp_open().

384  {
385  struct smtp *smtp;
386  long bytes_read;
387 
388  smtp = gdfd->user_data;
389 
391  return -1;
392  }
393 
394  bytes_read = 0;
395  if(smtp->tls_on){
396 #ifdef SMTP_OPENSSL
397  do{
398  /*
399  * Count will never have a value greater than SMTP_GETDELIM_READ_SZ,
400  * so we can safely convert this to an int.
401  */
402  bytes_read = SSL_read(smtp->tls, buf, (int)count);
403  } while(bytes_read <= 0 && BIO_should_retry(smtp->tls_bio));
404 #endif /* SMTP_OPENSSL */
405  }
406  else{
407  bytes_read = recv(smtp->sock, buf, count, 0);
408  }
409  return bytes_read;
410 }
#define SSL_read
Definition: seams.h:249
BIO * tls_bio
Definition: smtp.c:237
int tls_on
Definition: smtp.c:215
#define recv
Definition: seams.h:169
void * user_data
Definition: smtp.h:615
Definition: smtp.c:150
SSL * tls
Definition: smtp.c:227
int sock
Definition: smtp.c:159
static enum smtp_status_code smtp_str_getdelimfd_read_timeout(struct smtp *const smtp)
Definition: smtp.c:352
#define BIO_should_retry
Definition: seams.h:65
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ smtp_str_getdelimfd_read_timeout()

static enum smtp_status_code smtp_str_getdelimfd_read_timeout ( struct smtp *const  smtp)
static

Wait until more data has been made available on the socket read end.

Parameters
[in]smtpSMTP client context.
Return values
SMTP_STATUS_OKIf data available to read on the socket.
SMTP_STATUS_RECVIf the connection times out before any data appears on the socket.

Definition at line 352 of file smtp.c.

References select, smtp_status_code_set(), SMTP_STATUS_OK, SMTP_STATUS_RECV, smtp::sock, and smtp::timeout_sec.

Referenced by smtp_str_getdelimfd_read().

352  {
353  fd_set readfds;
354  struct timeval timeout;
355  int sel_rc;
356 
357  FD_ZERO(&readfds);
358  FD_SET(smtp->sock, &readfds);
359  timeout.tv_sec = smtp->timeout_sec;
360  timeout.tv_usec = 0;
361  sel_rc = select(smtp->sock + 1, &readfds, NULL, NULL, &timeout);
362  if(sel_rc < 1){
364  }
365  return SMTP_STATUS_OK;
366 }
long timeout_sec
Definition: smtp.c:201
enum smtp_status_code smtp_status_code_set(struct smtp *const smtp, enum smtp_status_code status_code)
Definition: smtp.c:3231
int sock
Definition: smtp.c:159
#define select
Definition: seams.h:177
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ smtp_str_getdelimfd_search_delim()

static int smtp_str_getdelimfd_search_delim ( const char *const  buf,
size_t  buf_len,
int  delim,
size_t *const  delim_pos 
)
static

Find and return the location of the delimiter character in the search buffer.

This function gets used by the main socket parsing function which continually reads from the socket and expands the buffer until it encounters the expected delimiter. This function provides the logic to check for the delimiter character in order to simplify the code in the main parse function.

Parameters
[in]bufSearch buffer used to find the delimiter.
[in]buf_lenNumber of bytes to search for in buf.
[in]delimThe delimiter to search for in buf.
[out]delim_posIf delimiter found in buf, return the delimiter position in this parameter.
Return values
1If the delimiter character found.
0If the delimiter character not found.

Definition at line 431 of file smtp.c.

References SMTP_LINKAGE.

Referenced by smtp_str_getdelimfd().

434  {
435  size_t i;
436 
437  *delim_pos = 0;
438  for(i = 0; i < buf_len; i++){
439  if(buf[i] == delim){
440  *delim_pos = i;
441  return 1;
442  }
443  }
444  return 0;
445 }
+ Here is the caller graph for this function:

◆ smtp_str_getdelimfd_set_line_and_buf()

SMTP_LINKAGE int smtp_str_getdelimfd_set_line_and_buf ( struct str_getdelimfd *const  gdfd,
size_t  copy_len 
)

Set the internal line buffer to the number of bytes specified.

Parameters
[in]gdfdSee str_getdelimfd.
[in]copy_lenNumber of bytes to copy to the internal line buffer.
Return values
0Successfully allocated and copied data over to the new line buffer.
-1Failed to allocate memory for the new line buffer.

Definition at line 457 of file smtp.c.

References str_getdelimfd::_buf, str_getdelimfd::_buf_len, calloc, str_getdelimfd::line, str_getdelimfd::line_len, SMTP_LINKAGE, smtp_si_add_size_t(), and smtp_si_sub_size_t().

Referenced by smtp_str_getdelimfd(), and smtp_unit_test_getdelimfd_set_line_and_buf().

458  {
459  size_t copy_len_inc;
460  size_t nbytes_to_shift;
461  size_t new_buf_len;
462 
463  if(gdfd->line){
464  free(gdfd->line);
465  gdfd->line = NULL;
466  }
467 
468  if(smtp_si_add_size_t(copy_len, 1, &copy_len_inc) ||
469  smtp_si_add_size_t((size_t)gdfd->_buf, copy_len_inc, NULL) ||
470  smtp_si_sub_size_t(gdfd->_buf_len, copy_len, &nbytes_to_shift) ||
471  (gdfd->line = calloc(1, copy_len_inc)) == NULL){
472  return -1;
473  }
474  memcpy(gdfd->line, gdfd->_buf, copy_len);
475  gdfd->line_len = copy_len;
476  memmove(gdfd->_buf, gdfd->_buf + copy_len_inc, nbytes_to_shift);
477  if(smtp_si_sub_size_t(nbytes_to_shift, 1, &new_buf_len) == 0){
478  gdfd->_buf_len = new_buf_len;
479  }
480  return 0;
481 }
#define calloc
Definition: seams.h:73
size_t _buf_len
Definition: smtp.h:587
size_t line_len
Definition: smtp.h:597
char * line
Definition: smtp.h:592
char * _buf
Definition: smtp.h:577
SMTP_LINKAGE int smtp_si_sub_size_t(const size_t a, const size_t b, size_t *const result)
Definition: smtp.c:286
SMTP_LINKAGE int smtp_si_add_size_t(const size_t a, const size_t b, size_t *const result)
Definition: smtp.c:252
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ smtp_str_getdelimfd_throw_error()

static enum str_getdelim_retcode smtp_str_getdelimfd_throw_error ( struct str_getdelimfd *const  gdfd)
static

Free the str_getdelimfd and return the STRING_GETDELIMFD_ERROR error code.

Parameters
[in]gdfdSee str_getdelimfd.
Returns
See str_getdelim_retcode.

Definition at line 507 of file smtp.c.

References SMTP_LINKAGE, smtp_str_getdelimfd_free(), and STRING_GETDELIMFD_ERROR.

Referenced by smtp_str_getdelimfd().

507  {
510 }
SMTP_LINKAGE void smtp_str_getdelimfd_free(struct str_getdelimfd *const gdfd)
Definition: smtp.c:489
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ smtp_str_has_nonascii_utf8()

SMTP_LINKAGE int smtp_str_has_nonascii_utf8 ( const char *const  s)

Check if a string contains non-ASCII UTF-8 characters.

Uses the simple algorithm from smtp_utf8_charlen to check for non-ASCII UTF-8 characters.

Parameters
[in]sUTF-8 string.
Return values
1String contains non-ASCII UTF-8 characters.
0String contains only ASCII characters.

Definition at line 1110 of file smtp.c.

References SMTP_LINKAGE, and smtp_utf8_charlen().

Referenced by smtp_mail_envelope_header(), and smtp_unit_test_all_smtp_str_has_nonascii_utf8().

1110  {
1111  size_t i;
1112  size_t charlen;
1113 
1114  for(i = 0; s[i]; i++){
1115  charlen = smtp_utf8_charlen(s[i]);
1116  if(charlen != 1){
1117  return 1;
1118  }
1119  }
1120  return 0;
1121 }
SMTP_LINKAGE size_t smtp_utf8_charlen(char c)
Definition: smtp.c:1078
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ smtp_str_replace()

SMTP_LINKAGE char* smtp_str_replace ( const char *const  search,
const char *const  replace,
const char *const  s 
)

Search for all substrings in a string and replace each instance with a replacement string.

Parameters
[in]searchSubstring to search for in s.
[in]replaceReplace each instance of the search string with this.
[in]sNull-terminated string to search and replace.
Return values
char*A dynamically allocated string with the replaced instances as described above. The caller must free the allocated memory when finished.
NULLMemory allocation failure.

Definition at line 679 of file smtp.c.

References realloc, smtp_si_add_size_t(), smtp_strdup(), and strlen.

Referenced by smtp_print_email(), and smtp_unit_test_str_replace().

681  {
682  size_t search_len;
683  size_t replace_len;
684  size_t replace_len_inc;
685  size_t slen;
686  size_t slen_inc;
687  size_t s_idx;
688  size_t snew_len;
689  size_t snew_len_inc;
690  size_t snew_sz;
691  size_t snew_sz_dup;
692  size_t snew_sz_plus_slen;
693  size_t snew_replace_len_inc;
694  char *snew;
695  char *stmp;
696 
697  search_len = strlen(search);
698  replace_len = strlen(replace);
699  slen = strlen(s);
700  s_idx = 0;
701  snew = NULL;
702  snew_len = 0;
703  snew_sz = 0;
704 
705  if(smtp_si_add_size_t(replace_len, 1, &replace_len_inc) ||
706  smtp_si_add_size_t(slen, 1, &slen_inc)){
707  return NULL;
708  }
709 
710  if(s[0] == '\0'){
711  return smtp_strdup("");
712  }
713  else if(search_len < 1){
714  return smtp_strdup(s);
715  }
716 
717  while(s[s_idx]){
718  if(smtp_si_add_size_t(snew_len, 1, &snew_len_inc) ||
719  smtp_si_add_size_t(snew_sz, snew_sz, &snew_sz_dup) ||
720  smtp_si_add_size_t(snew_sz, slen, &snew_sz_plus_slen)){
721  free(snew);
722  return NULL;
723  }
724 
725  if(strncmp(&s[s_idx], search, search_len) == 0){
726  if(smtp_si_add_size_t(snew_len, replace_len_inc, &snew_replace_len_inc)){
727  free(snew);
728  return NULL;
729  }
730  if(snew_replace_len_inc >= snew_sz){
731  /* snew_sz += snew_sz + slen + replace_len + 1 */
732  if(smtp_si_add_size_t(snew_sz_dup, slen_inc, &snew_sz) ||
733  smtp_si_add_size_t(snew_sz, replace_len, &snew_sz) ||
734  (stmp = realloc(snew, snew_sz)) == NULL){
735  free(snew);
736  return NULL;
737  }
738  snew = stmp;
739  }
740  memcpy(&snew[snew_len], replace, replace_len);
741  snew_len += replace_len;
742  s_idx += search_len;
743  }
744  else{
745  if(snew_len_inc >= snew_sz){
746  /* snew_sz += snew_sz + slen + snew_len + 1 */
747  if(smtp_si_add_size_t(snew_sz_dup, slen, &snew_sz) ||
748  smtp_si_add_size_t(snew_sz, snew_len_inc, &snew_sz) ||
749  (stmp = realloc(snew, snew_sz)) == NULL){
750  free(snew);
751  return NULL;
752  }
753  snew = stmp;
754  }
755  snew[snew_len] = s[s_idx];
756  s_idx += 1;
757  snew_len = snew_len_inc;
758  }
759  }
760  snew[snew_len] = '\0';
761 
762  return snew;
763 }
#define realloc
Definition: seams.h:161
#define strlen
Definition: seams.h:273
SMTP_LINKAGE int smtp_si_add_size_t(const size_t a, const size_t b, size_t *const result)
Definition: smtp.c:252
SMTP_LINKAGE char * smtp_strdup(const char *s)
Definition: smtp.c:650
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ smtp_strdup()

SMTP_LINKAGE char* smtp_strdup ( const char *  s)

Copy a string into a new dynamically allocated buffer.

Returns a dynamically allocated string, with the same contents as the input string. The caller must free the returned string when finished.

Parameters
[in]sString to duplicate.
Return values
char*Pointer to a new dynamically allocated string duplicated from s.
NULLFailed to allocate memory for the new duplicate string.

Definition at line 650 of file smtp.c.

References malloc, SMTP_LINKAGE, smtp_si_add_size_t(), and strlen.

Referenced by smtp_address_add(), smtp_attachment_add_mem(), smtp_chunk_split(), smtp_header_add(), smtp_puts_dbg(), smtp_str_repeat(), smtp_str_replace(), smtp_unit_test_parse_cmd_line(), and smtp_unit_test_strdup().

650  {
651  char *dup;
652  size_t dup_len;
653  size_t slen;
654 
655  slen = strlen(s);
656  if(smtp_si_add_size_t(slen, 1, &dup_len)){
657  dup = NULL;
658  errno = ENOMEM;
659  }
660  else if((dup = malloc(dup_len)) != NULL){
661  memcpy(dup, s, dup_len);
662  }
663  return dup;
664 }
#define malloc
Definition: seams.h:145
#define strlen
Definition: seams.h:273
SMTP_LINKAGE int smtp_si_add_size_t(const size_t a, const size_t b, size_t *const result)
Definition: smtp.c:252
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ smtp_strnlen_utf8()

SMTP_LINKAGE size_t smtp_strnlen_utf8 ( const char *  s,
size_t  maxlen 
)

Get the number of bytes in a UTF-8 string, or a shorter count if the string exceeds a maximum specified length.

See maxlen for more information on multi-byte parsing.

Parameters
[in]sNull-terminated UTF-8 string.
[in]maxlenDo not check more than maxlen bytes of string s except if in the middle of a multi-byte character.
Return values
strlen(s)If length of s has less bytes than maxlen or the same number of bytes as maxlen. See maxlen for more details.
maxlenIf length of s has more bytes than maxlen.
-1If s contains an invalid UTF-8 byte sequence.

Definition at line 1138 of file smtp.c.

References SIZE_MAX, SMTP_LINKAGE, and smtp_utf8_charlen().

Referenced by smtp_chunk_split(), and smtp_unit_test_strnlen_utf8().

1139  {
1140  size_t i;
1141  size_t utf8_i;
1142  size_t utf8_len;
1143 
1144  for(i = 0; *s && i < maxlen; i += utf8_len){
1145  utf8_len = smtp_utf8_charlen(*s);
1146  if(utf8_len == 0){
1147  return SIZE_MAX;
1148  }
1149 
1150  for(utf8_i = 0; utf8_i < utf8_len; utf8_i++){
1151  if(!*s){
1152  return SIZE_MAX;
1153  }
1154  s += 1;
1155  }
1156  }
1157  return i;
1158 }
#define SIZE_MAX
Definition: smtp.h:23
SMTP_LINKAGE size_t smtp_utf8_charlen(char c)
Definition: smtp.c:1078
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ smtp_tls_init()

static int smtp_tls_init ( struct smtp *const  smtp,
const char *const  server 
)
static

Initialize the TLS library and establish a TLS handshake with the server over the existing socket connection.

Parameters
[in]smtpSMTP client context.
[in]serverServer name or IP address.
Return values
0Successfully established a TLS connection with the server.
-1Failed to establish a TLS connection with the server.

Definition at line 1770 of file smtp.c.

References BIO_new_socket, smtp::cafile, ERR_peek_error, smtp::flags, SMTP_NO_CERT_VERIFY, smtp::sock, SSL_connect, SSL_CTX_new, SSL_do_handshake, SSL_get_peer_certificate, SSL_new, smtp::tls, smtp::tls_bio, smtp::tls_ctx, smtp::tls_on, and X509_check_host.

Referenced by smtp_initiate_handshake().

1771  {
1772  X509 *X509_cert_peer;
1773 
1774  /* Do not need to check the return value since this always returns 1. */
1775  SSL_library_init();
1776 
1777  SSL_load_error_strings();
1778  ERR_load_BIO_strings();
1779  OpenSSL_add_all_algorithms();
1780 
1781  if((smtp->tls_ctx = SSL_CTX_new(SSLv23_client_method())) == NULL){
1782  return -1;
1783  }
1784 
1785  /* Disable SSLv2, SSLv3, and TLSv1.0. */
1786  SSL_CTX_set_options(smtp->tls_ctx,
1787  SSL_OP_NO_SSLv2 |
1788  SSL_OP_NO_SSLv3 |
1789  SSL_OP_NO_TLSv1);
1790 
1791  SSL_CTX_set_mode(smtp->tls_ctx, SSL_MODE_AUTO_RETRY);
1792  if((smtp->flags & SMTP_NO_CERT_VERIFY) == 0){
1793  SSL_CTX_set_verify(smtp->tls_ctx, SSL_VERIFY_PEER, NULL);
1794  }
1795 
1796  /*
1797  * Set the path to the user-provided CA file or use the default cert paths
1798  * if not provided.
1799  */
1800  if(smtp->cafile){
1801  if(SSL_CTX_load_verify_locations(smtp->tls_ctx, smtp->cafile, NULL) != 1){
1802  SSL_CTX_free(smtp->tls_ctx);
1803  return -1;
1804  }
1805  }
1806  else{
1807  X509_STORE_set_default_paths(SSL_CTX_get_cert_store(smtp->tls_ctx));
1808  if(ERR_peek_error() != 0){
1809  SSL_CTX_free(smtp->tls_ctx);
1810  return -1;
1811  }
1812  }
1813 
1814  if((smtp->tls = SSL_new(smtp->tls_ctx)) == NULL){
1815  SSL_CTX_free(smtp->tls_ctx);
1816  return -1;
1817  }
1818 
1819  if((smtp->tls_bio = BIO_new_socket(smtp->sock, 0)) == NULL){
1820  SSL_CTX_free(smtp->tls_ctx);
1821  SSL_free(smtp->tls);
1822  return -1;
1823  }
1824 
1825  SSL_set_bio(smtp->tls, smtp->tls_bio, smtp->tls_bio);
1826  SSL_set_connect_state(smtp->tls);
1827  if(SSL_connect(smtp->tls) != 1){
1828  SSL_CTX_free(smtp->tls_ctx);
1829  SSL_free(smtp->tls);
1830  return -1;
1831  }
1832 
1833  if(SSL_do_handshake(smtp->tls) != 1){
1834  SSL_CTX_free(smtp->tls_ctx);
1835  SSL_free(smtp->tls);
1836  return -1;
1837  }
1838 
1839  /* Verify matching subject in certificate. */
1840  if((smtp->flags & SMTP_NO_CERT_VERIFY) == 0){
1841  if((X509_cert_peer = SSL_get_peer_certificate(smtp->tls)) == NULL){
1842  SSL_CTX_free(smtp->tls_ctx);
1843  SSL_free(smtp->tls);
1844  return -1;
1845  }
1846  if(X509_check_host(X509_cert_peer, server, 0, 0, NULL) != 1){
1847  SSL_CTX_free(smtp->tls_ctx);
1848  SSL_free(smtp->tls);
1849  return -1;
1850  }
1851  X509_free(X509_cert_peer);
1852  }
1853 
1854  smtp->tls_on = 1;
1855  return 0;
1856 }
#define SSL_CTX_new
Definition: seams.h:209
BIO * tls_bio
Definition: smtp.c:237
#define SSL_do_handshake
Definition: seams.h:217
int tls_on
Definition: smtp.c:215
SSL_CTX * tls_ctx
Definition: smtp.c:232
enum smtp_flag flags
Definition: smtp.c:154
#define ERR_peek_error
Definition: seams.h:97
#define SSL_new
Definition: seams.h:241
#define SSL_get_peer_certificate
Definition: seams.h:225
#define SSL_connect
Definition: seams.h:201
const char * cafile
Definition: smtp.c:221
#define X509_check_host
Definition: seams.h:233
SSL * tls
Definition: smtp.c:227
int sock
Definition: smtp.c:159
#define BIO_new_socket
Definition: seams.h:57
+ Here is the caller graph for this function:

◆ smtp_utf8_charlen()

SMTP_LINKAGE size_t smtp_utf8_charlen ( char  c)

Get the length in bytes of a UTF-8 character.

This consists of a very simple check and assumes the user provides a valid UTF-8 byte sequence. It gets the length from the first byte in the sequence and does not validate any other bytes in the character sequence or any other bits in the first byte of the character sequence.

Parameters
[in]cThe first byte in a valid UTF-8 character sequence.
Return values
>0Number of bytes for the current UTF-8 character sequence.
-1Invalid byte sequence.

Definition at line 1078 of file smtp.c.

References SMTP_LINKAGE.

Referenced by smtp_str_has_nonascii_utf8(), smtp_strnlen_utf8(), and smtp_unit_test_all_smtp_utf8_charlen().

1078  {
1079  unsigned char uc;
1080 
1081  uc = (unsigned char)c;
1082  if((uc & 0x80) == 0){ /* 0XXXXXXX */
1083  return 1;
1084  }
1085  else if((uc & 0xE0) == 0xC0){ /* 110XXXXX */
1086  return 2;
1087  }
1088  else if((uc & 0xF0) == 0xE0){ /* 1110XXXX */
1089  return 3;
1090  }
1091  else if((uc & 0xF8) == 0xF0){ /* 11110XXX */
1092  return 4;
1093  }
1094  else{ /* invalid */
1095  return 0;
1096  }
1097 }
+ Here is the caller graph for this function:

◆ smtp_write()

SMTP_LINKAGE enum smtp_status_code smtp_write ( struct smtp *const  smtp,
const char *const  buf,
size_t  len 
)

Send data to the SMTP server.

Writes a buffer of length len into either the unencrypted TCP socket or the TLS encrypted socket, depending on the current underlying mode of the socket.

Parameters
[in]smtpSMTP client context.
[in]bufData to send to the SMTP server.
[in]lenNumber of bytes in buf.
Returns
See smtp_status_code.

Definition at line 1603 of file smtp.c.

References send, smtp_puts_dbg(), smtp_status_code_set(), SMTP_STATUS_SEND, smtp::sock, SSL_write, smtp::status_code, smtp::tls, and smtp::tls_on.

Referenced by smtp_puts(), and test_failure_misc().

1605  {
1606  size_t bytes_to_send;
1607  long bytes_sent;
1608  const char *buf_offset;
1609  int ssl_bytes_to_send;
1610 
1611  smtp_puts_dbg(smtp, "Client", buf);
1612 
1613  bytes_to_send = len;
1614  buf_offset = buf;
1615  while(bytes_to_send){
1616  if(bytes_to_send > INT_MAX){
1617  return smtp_status_code_set(smtp, SMTP_STATUS_SEND);
1618  }
1619 
1620  if(smtp->tls_on){
1621 #ifdef SMTP_OPENSSL
1622  /* bytes_to_send <= INT_MAX */
1623  ssl_bytes_to_send = (int)bytes_to_send;
1624  bytes_sent = SSL_write(smtp->tls, buf_offset, ssl_bytes_to_send);
1625  if(bytes_sent <= 0){
1626  return smtp_status_code_set(smtp, SMTP_STATUS_SEND);
1627  }
1628 #else /* !(SMTP_OPENSSL) */
1629  /* unreachable */
1630  bytes_sent = 0;
1631  (void)ssl_bytes_to_send;
1632 #endif /* SMTP_OPENSSL */
1633  }
1634  else{
1635  bytes_sent = send(smtp->sock, buf_offset, bytes_to_send, 0);
1636  if(bytes_sent < 0){
1637  return smtp_status_code_set(smtp, SMTP_STATUS_SEND);
1638  }
1639  }
1640  bytes_to_send -= (size_t)bytes_sent;
1641  buf_offset += bytes_sent;
1642  }
1643 
1644  return smtp->status_code;
1645 }
int tls_on
Definition: smtp.c:215
#define SSL_write
Definition: seams.h:257
enum smtp_status_code smtp_status_code_set(struct smtp *const smtp, enum smtp_status_code status_code)
Definition: smtp.c:3231
#define send
Definition: seams.h:185
enum smtp_status_code status_code
Definition: smtp.c:208
SSL * tls
Definition: smtp.c:227
int sock
Definition: smtp.c:159
static void smtp_puts_dbg(struct smtp *const smtp, const char *const prefix, const char *const str)
Definition: smtp.c:1512
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

Variable Documentation

◆ g_base64_decode_table

signed char g_base64_decode_table[]
static
Initial value:
= {
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1,
62,
-1, -1, -1,
63,
52, 53, 54, 55, 56, 57, 58, 59, 60, 61,
-1, -1, -1, -1, -1, -1, -1,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
20, 21, 22, 23, 24, 25,
-1, -1, -1, -1, -1, -1,
26, 27, 28, 29, 30, 31, 32, 33, 34, 35,
36, 37, 38, 39, 40, 41, 42, 43, 44, 45,
46, 47, 48, 49, 50, 51,
-1, -1, -1, -1, -1
}

Lookup table used to decode base64 data.

For base64 encoding, every six bits have been encoded using only the ASCII characters from g_base64_encode_table. This table has entries which allow the reversal of that process. It has 128 entries which map over to the index value from the encoding table. If an indexing result ends up with -1 during the decoding process, then that indicates an invalid base64 character in the encoded data.

Definition at line 894 of file smtp.c.

Referenced by smtp_base64_decode_block().

◆ g_base64_encode_table

char g_base64_encode_table[]
static
Initial value:
= {
'A','B','C','D','E','F','G','H','I','J',
'K','L','M','N','O','P','Q','R','S','T',
'U','V','W','X','Y','Z',
'a','b','c','d','e','f','g','h','i','j',
'k','l','m','n','o','p','q','r','s','t',
'u','v','w','x','y','z',
'0','1','2','3','4','5','6','7','8','9',
'+','/'
}

Lookup table used to encode data into base64.

Base64 encoding takes six bits of data and encodes those bits using this table. Since 2^6 = 64, this array has 64 entries which maps directly from the 6 bit value into the corresponding array value.

Definition at line 772 of file smtp.c.

Referenced by smtp_base64_encode_block().

◆ g_smtp_error

struct smtp g_smtp_error
static

This error structure used for the single error case where we cannot initially allocate memory. This makes it easier to propagate any error codes when calling the other header functions because the caller will always get a valid SMTP structure returned.

Definition at line 2954 of file smtp.c.

Referenced by smtp_open().