smtp-client
SMTP Client C Library
smtp.c
Go to the documentation of this file.
1 
25 #if defined(_WIN32) || defined(WIN32)
26 # define SMTP_IS_WINDOWS
27 #endif /* SMTP_IS_WINDOWS */
28 
29 #ifdef SMTP_IS_WINDOWS
30 # include <winsock2.h>
31 # include <ws2tcpip.h>
32 #else /* POSIX */
33 # include <netinet/in.h>
34 # include <sys/select.h>
35 # include <sys/socket.h>
36 # include <netdb.h>
37 # include <unistd.h>
38 #endif /* SMTP_IS_WINDOWS */
39 
40 #include <errno.h>
41 #include <limits.h>
42 #include <signal.h>
43 #include <stdarg.h>
44 #include <string.h>
45 #include <stdlib.h>
46 #include <time.h>
47 
48 #ifdef SMTP_OPENSSL
49 # include <openssl/bio.h>
50 # include <openssl/err.h>
51 # include <openssl/ssl.h>
52 # include <openssl/x509.h>
53 # include <openssl/x509v3.h>
54 #endif /* SMTP_OPENSSL */
55 
59 #define SMTP_INTERNAL_DEFINE
60 
61 #include "smtp.h"
62 
63 /*
64  * The SMTP_TEST converts some library routines into special test seams which
65  * allows the test program to control whether they fail. For example, we can
66  * control when malloc() fails under certain conditions with an out of
67  * memory condition.
68  */
69 #ifdef SMTP_TEST
70 
74 # define SMTP_LINKAGE extern
75 # include "../test/seams.h"
76 #else /* !(SMTP_TEST) */
77 
81 # define SMTP_LINKAGE static
82 #endif /* SMTP_TEST */
83 
88 #define SMTP_GETDELIM_READ_SZ 1000
89 
93 struct smtp_address{
99  char *email;
100 
104  char *name;
105 
110 
114  char pad[4];
115 };
116 
124  char *name;
125 
129  char *b64_data;
130 };
131 
135 struct smtp_header{
139  char *key;
140 
144  char *value;
145 };
146 
150 struct smtp{
154  enum smtp_flag flags;
155 
159  int sock;
160 
164  struct str_getdelimfd gdfd;
165 
170 
174  size_t num_headers;
175 
180 
184  size_t num_address;
185 
190 
195 
202 
208  enum smtp_status_code status_code;
209 
215  int tls_on;
216 
221  const char *cafile;
222 
223 #ifdef SMTP_OPENSSL
224 
227  SSL *tls;
228 
232  SSL_CTX *tls_ctx;
233 
237  BIO *tls_bio;
238 #endif /* SMTP_OPENSSL */
239 };
240 
251 SMTP_LINKAGE int
252 smtp_si_add_size_t(const size_t a,
253  const size_t b,
254  size_t *const result){
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 }
274 
285 SMTP_LINKAGE int
286 smtp_si_sub_size_t(const size_t a,
287  const size_t b,
288  size_t *const result){
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 }
308 
319 SMTP_LINKAGE int
320 smtp_si_mul_size_t(const size_t a,
321  const size_t b,
322  size_t *const result){
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 }
342 
351 static enum smtp_status_code
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 }
367 
381 static long
383  void *buf,
384  size_t count){
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 }
411 
430 static int
431 smtp_str_getdelimfd_search_delim(const char *const buf,
432  size_t buf_len,
433  int delim,
434  size_t *const delim_pos){
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 }
446 
456 SMTP_LINKAGE int
458  size_t copy_len){
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 }
482 
488 SMTP_LINKAGE void
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 }
498 
506 static enum str_getdelim_retcode
510 }
511 
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 }
586 
598 SMTP_LINKAGE char *
599 smtp_stpcpy(char *s1,
600  const char *s2){
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 }
609 
621 SMTP_LINKAGE void *
623  size_t nmemb,
624  size_t size){
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 }
637 
649 SMTP_LINKAGE char *
650 smtp_strdup(const char *s){
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 }
665 
678 SMTP_LINKAGE char *
679 smtp_str_replace(const char *const search,
680  const char *const replace,
681  const char *const s){
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 }
764 
772 static char g_base64_encode_table[] = {
773  'A','B','C','D','E','F','G','H','I','J',
774  'K','L','M','N','O','P','Q','R','S','T',
775  'U','V','W','X','Y','Z',
776  'a','b','c','d','e','f','g','h','i','j',
777  'k','l','m','n','o','p','q','r','s','t',
778  'u','v','w','x','y','z',
779  '0','1','2','3','4','5','6','7','8','9',
780  '+','/'
781 };
782 
791 static void
792 smtp_base64_encode_block(const char *const buf,
793  size_t buf_block_sz,
794  char *const b64){
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 }
813 
824 SMTP_LINKAGE char *
825 smtp_base64_encode(const char *const buf,
826  size_t buflen){
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 }
881 
882 #ifdef SMTP_OPENSSL
883 
893 static signed char
895  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
896  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
897  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
898  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
899  -1, -1, -1,
900  62, /* + */
901  -1, -1, -1,
902  63, /* / */
903  52, 53, 54, 55, 56, 57, 58, 59, 60, 61, /* 0 - 9 */
904  -1, -1, -1, -1, -1, -1, -1,
905  0, 1, 2, 3, 4, 5, 6, 7, 8, 9, /* A - J */
906  10, 11, 12, 13, 14, 15, 16, 17, 18, 19, /* K - T */
907  20, 21, 22, 23, 24, 25, /* U - Z */
908  -1, -1, -1, -1, -1, -1,
909  26, 27, 28, 29, 30, 31, 32, 33, 34, 35, /* a - j */
910  36, 37, 38, 39, 40, 41, 42, 43, 44, 45, /* k - t */
911  46, 47, 48, 49, 50, 51, /* u - z */
912  -1, -1, -1, -1, -1
913 };
914 
923 static size_t
924 smtp_base64_decode_block(const unsigned char *const buf,
925  unsigned char *const decode){
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 }
968 
983 SMTP_LINKAGE size_t
984 smtp_base64_decode(const char *const buf,
985  unsigned char **decode){
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 }
1019 
1030 SMTP_LINKAGE char *
1031 smtp_bin2hex(const unsigned char *const s,
1032  size_t slen){
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 }
1063 #endif /* SMTP_OPENSSL */
1064 
1077 SMTP_LINKAGE size_t
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 }
1098 
1109 SMTP_LINKAGE int
1110 smtp_str_has_nonascii_utf8(const char *const s){
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 }
1122 
1137 SMTP_LINKAGE size_t
1138 smtp_strnlen_utf8(const char *s,
1139  size_t maxlen){
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 }
1159 
1178 SMTP_LINKAGE size_t
1180  unsigned int maxlen){
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 }
1213 
1218 #define SMTP_LINE_MAX 78
1219 
1249 SMTP_LINKAGE char *
1250 smtp_fold_whitespace(const char *const s,
1251  unsigned int maxlen){
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 }
1300 
1311 SMTP_LINKAGE char *
1312 smtp_chunk_split(const char *const s,
1313  size_t chunklen,
1314  const char *const end){
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 }
1373 
1385 SMTP_LINKAGE char *
1387  size_t *bytes_read){
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 }
1426 
1438 SMTP_LINKAGE char *
1439 smtp_file_get_contents(const char *const filename,
1440  size_t *bytes_read){
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 }
1457 
1466 SMTP_LINKAGE int
1467 smtp_parse_cmd_line(char *const line,
1468  struct smtp_command *const cmd){
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 }
1502 
1511 static void
1512 smtp_puts_dbg(struct smtp *const smtp,
1513  const char *const prefix,
1514  const char *const str){
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 }
1536 
1543 static enum str_getdelim_retcode
1544 smtp_getline(struct smtp *const smtp){
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 }
1565 
1573 static int
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 }
1589 
1603 smtp_write(struct smtp *const smtp,
1604  const char *const buf,
1605  size_t len){
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 }
1646 
1654 static enum smtp_status_code
1655 smtp_puts(struct smtp *const smtp,
1656  const char *const s){
1657  return smtp_write(smtp, s, strlen(s));
1658 }
1659 
1668 static enum smtp_status_code
1670  const char *const s){
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 }
1688 
1702 static int
1703 smtp_connect(struct smtp *const smtp,
1704  const char *const server,
1705  const char *const port){
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 }
1758 
1759 #ifdef SMTP_OPENSSL
1760 
1769 static int
1770 smtp_tls_init(struct smtp *const smtp,
1771  const char *const server){
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 }
1857 #endif /* SMTP_OPENSSL */
1858 
1869 static enum smtp_status_code
1870 smtp_ehlo(struct smtp *const smtp){
1871  if(smtp_puts(smtp, "EHLO smtp\r\n") == SMTP_STATUS_OK){
1873  }
1874  return smtp->status_code;
1875 }
1876 
1892 static int
1893 smtp_auth_plain(struct smtp *const smtp,
1894  const char *const user,
1895  const char *const pass){
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 }
1949 
1965 static int
1966 smtp_auth_login(struct smtp *const smtp,
1967  const char *const user,
1968  const char *const pass){
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 }
2017 
2018 #ifdef SMTP_OPENSSL
2019 
2036 static int
2038  const char *const user,
2039  const char *const pass){
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 }
2129 #endif /* SMTP_OPENSSL */
2130 
2137 static void
2139  long seconds){
2140  smtp->timeout_sec = seconds;
2141 }
2142 
2160 static enum smtp_status_code
2162  const char *const server,
2163  enum smtp_connection_security connection_security){
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 }
2209 
2222 #define SMTP_DATE_MAX_SZ (32 + 15)
2223 
2235 SMTP_LINKAGE int
2236 smtp_date_rfc_2822(char *const date){
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 }
2322 
2332 static int
2333 smtp_header_cmp_key(const void *const v1,
2334  const void *const v2){
2335  const char *key;
2336  const struct smtp_header *header2;
2337 
2338  key = v1;
2339  header2 = v2;
2340  return strcmp(key, header2->key);
2341 }
2342 
2351 static int
2352 smtp_header_exists(const struct smtp *const smtp,
2353  const char *const key){
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 }
2363 
2370 #define SMTP_MIME_BOUNDARY_LEN 15
2371 
2385 static void
2386 smtp_gen_mime_boundary(char *const boundary){
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 }
2400 
2410 static enum smtp_status_code
2412  const char *const boundary,
2413  const char *const body_dd){
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 }
2454 
2463 static enum smtp_status_code
2465  const char *const boundary,
2466  const struct smtp_attachment *const attachment){
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 }
2511 
2520 static enum smtp_status_code
2522  const char *const boundary){
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 }
2531 
2542 static enum smtp_status_code
2544  const char *const body_dd){
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 }
2566 
2575 static enum smtp_status_code
2577  const char *const body_dd){
2578  return smtp_puts_terminate(smtp, body_dd);
2579 }
2580 
2588 static enum smtp_status_code
2590  const char *const body){
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 }
2611 
2623 static enum smtp_status_code
2625  const struct smtp_header *const header){
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 }
2660 
2673 static enum smtp_status_code
2675  enum smtp_address_type address_type,
2676  const char *const key){
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 }
2738 
2751 static enum smtp_status_code
2753  const char *const header,
2754  const struct smtp_address *const address){
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 }
2791 
2801 static int
2802 smtp_header_cmp(const void *v1,
2803  const void *v2){
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 }
2811 
2821 SMTP_LINKAGE int
2822 smtp_header_key_validate(const char *const key){
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 }
2841 
2851 SMTP_LINKAGE int
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 }
2866 
2877 SMTP_LINKAGE int
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 }
2891 
2902 SMTP_LINKAGE int
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 }
2915 
2926 SMTP_LINKAGE int
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 }
2940 
2945 #define SMTP_FLAG_INVALID_MEMORY (enum smtp_flag)(0xFFFFFFFF)
2946 
2953 static struct smtp
2955  SMTP_FLAG_INVALID_MEMORY, /* flags */
2956  0, /* sock */
2957  { /* gdfd */
2958  NULL, /* _buf */
2959  0, /* _bufsz */
2960  0, /* _buf_len */
2961  NULL, /* line */
2962  0, /* line_len */
2963  NULL, /* getdelimfd_read */
2964  NULL, /* user_data */
2965  0, /* delim */
2966  {0} /* pad */
2967  }, /* gdfd */
2968  NULL, /* header_list */
2969  0, /* num_headers */
2970  NULL, /* address_list */
2971  0, /* num_address */
2972  NULL, /* attachment_list */
2973  0, /* num_attachment */
2974  0, /* timeout_sec */
2975  SMTP_STATUS_NOMEM, /* smtp_status_code status_code */
2976  0, /* tls_on */
2977  NULL /* cafile */
2978 #ifdef SMTP_OPENSSL
2979  ,
2980  NULL, /* tls */
2981  NULL, /* tls_ctx */
2982  NULL /* tls_bio */
2983 #endif /* SMTP_OPENSSL */
2984 };
2985 
2986 enum smtp_status_code
2987 smtp_open(const char *const server,
2988  const char *const port,
2989  enum smtp_connection_security connection_security,
2990  enum smtp_flag flags,
2991  const char *const cafile,
2992  struct smtp **smtp){
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 }
3024 
3025 enum smtp_status_code
3026 smtp_auth(struct smtp *const smtp,
3027  enum smtp_authentication_method auth_method,
3028  const char *const user,
3029  const char *const pass){
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 }
3060 
3061 enum smtp_status_code
3062 smtp_mail(struct smtp *const smtp,
3063  const char *const body){
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 }
3166 
3167 enum smtp_status_code
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 }
3215 
3216 enum smtp_status_code
3217 smtp_status_code_get(const struct smtp *const smtp){
3218  return smtp->status_code;
3219 }
3220 
3221 enum smtp_status_code
3223  enum smtp_status_code old_status;
3224 
3225  old_status = smtp_status_code_get(smtp);
3227  return old_status;
3228 }
3229 
3230 enum smtp_status_code
3232  enum smtp_status_code status_code){
3233  if((unsigned)status_code >= SMTP_STATUS__LAST){
3235  }
3236  smtp->status_code = status_code;
3237  return status_code;
3238 }
3239 
3240 const char *
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 }
3276 
3277 enum smtp_status_code
3278 smtp_header_add(struct smtp *const smtp,
3279  const char *const key,
3280  const char *const value){
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 
3293  if(value && smtp_header_value_validate(value) < 0){
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 }
3330 
3331 void
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 }
3345 
3346 enum smtp_status_code
3348  enum smtp_address_type type,
3349  const char *const email,
3350  const char *const name){
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 
3359  if(smtp_address_validate_email(email) < 0){
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 }
3399 
3400 void
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 }
3414 
3415 enum smtp_status_code
3417  const char *const name,
3418  const char *const path){
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 }
3437 
3438 enum smtp_status_code
3440  const char *const name,
3441  FILE *fp){
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 }
3460 
3461 enum smtp_status_code
3463  const char *const name,
3464  const void *const data,
3465  size_t datasz){
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 
3475  if(smtp_attachment_validate_name(name) < 0){
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 }
3513 
3514 void
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 }
3528 
SMTP_LINKAGE size_t smtp_strnlen_utf8(const char *s, size_t maxlen)
Definition: smtp.c:1138
static enum smtp_status_code smtp_print_nomime_email(struct smtp *const smtp, const char *const body_dd)
Definition: smtp.c:2576
#define SMTP_GETDELIM_READ_SZ
Definition: smtp.c:88
enum smtp_status_code smtp_status_code_clear(struct smtp *const smtp)
Definition: smtp.c:3222
enum smtp_address_type type
Definition: smtp.c:109
#define SSL_CTX_new
Definition: seams.h:209
struct smtp_address * address_list
Definition: smtp.c:179
static enum str_getdelim_retcode smtp_getline(struct smtp *const smtp)
Definition: smtp.c:1544
static size_t smtp_base64_decode_block(const unsigned char *const buf, unsigned char *const decode)
Definition: smtp.c:924
enum smtp_status_code smtp_close(struct smtp *smtp)
Definition: smtp.c:3168
char * b64_data
Definition: smtp.c:129
#define SSL_read
Definition: seams.h:249
enum smtp_status_code smtp_address_add(struct smtp *const smtp, enum smtp_address_type type, const char *const email, const char *const name)
Definition: smtp.c:3347
enum smtp_result_code code
Definition: smtp.h:531
SMTP_LINKAGE int smtp_parse_cmd_line(char *const line, struct smtp_command *const cmd)
Definition: smtp.c:1467
BIO * tls_bio
Definition: smtp.c:237
#define SSL_do_handshake
Definition: seams.h:217
SMTP_LINKAGE char * smtp_bin2hex(const unsigned char *const s, size_t slen)
Definition: smtp.c:1031
static int smtp_auth_cram_md5(struct smtp *const smtp, const char *const user, const char *const pass)
Definition: smtp.c:2037
#define calloc
Definition: seams.h:73
#define SMTP_FLAG_INVALID_MEMORY
Definition: smtp.c:2945
struct str_getdelimfd gdfd
Definition: smtp.c:164
#define SMTP_MIME_BOUNDARY_LEN
Definition: smtp.c:2370
#define time
Definition: seams.h:281
int tls_on
Definition: smtp.c:215
SSL_CTX * tls_ctx
Definition: smtp.c:232
#define sprintf
Definition: seams.h:265
#define ferror
Definition: seams.h:113
enum smtp_status_code smtp_attachment_add_fp(struct smtp *const smtp, const char *const name, FILE *fp)
Definition: smtp.c:3439
void smtp_address_clear_all(struct smtp *const smtp)
Definition: smtp.c:3401
#define SIZE_MAX
Definition: smtp.h:23
enum smtp_status_code smtp_status_code_get(const struct smtp *const smtp)
Definition: smtp.c:3217
#define SSL_write
Definition: seams.h:257
SMTP_LINKAGE void * smtp_reallocarray(void *ptr, size_t nmemb, size_t size)
Definition: smtp.c:622
#define HMAC
Definition: seams.h:129
static int smtp_header_exists(const struct smtp *const smtp, const char *const key)
Definition: smtp.c:2352
#define SMTP_LINKAGE
Definition: smtp.c:81
SMTP_LINKAGE enum smtp_status_code smtp_write(struct smtp *const smtp, const char *const buf, size_t len)
Definition: smtp.c:1603
SMTP_LINKAGE char * smtp_file_get_contents(const char *const filename, size_t *bytes_read)
Definition: smtp.c:1439
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
SMTP_LINKAGE char * smtp_ffile_get_contents(FILE *stream, size_t *bytes_read)
Definition: smtp.c:1386
#define SMTP_LINE_MAX
Definition: smtp.c:1218
SMTP_LINKAGE int smtp_header_key_validate(const char *const key)
Definition: smtp.c:2822
#define realloc
Definition: seams.h:161
#define ERR_peek_error
Definition: seams.h:97
size_t _buf_len
Definition: smtp.h:587
static enum smtp_status_code smtp_print_header(struct smtp *const smtp, const struct smtp_header *const header)
Definition: smtp.c:2624
#define close
Definition: seams.h:81
struct smtp_attachment * attachment_list
Definition: smtp.c:189
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
enum smtp_status_code smtp_attachment_add_path(struct smtp *const smtp, const char *const name, const char *const path)
Definition: smtp.c:3416
size_t line_len
Definition: smtp.h:597
int more
Definition: smtp.h:539
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
smtp_connection_security
Definition: smtp.h:140
static struct smtp g_smtp_error
Definition: smtp.c:2954
char * key
Definition: smtp.c:139
static void smtp_base64_encode_block(const char *const buf, size_t buf_block_sz, char *const b64)
Definition: smtp.c:792
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
#define SSL_new
Definition: seams.h:241
smtp_result_code
Definition: smtp.h:483
#define recv
Definition: seams.h:169
SMTP_LINKAGE int smtp_address_validate_name(const char *const name)
Definition: smtp.c:2903
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
char * line
Definition: smtp.h:592
SMTP_LINKAGE int smtp_address_validate_email(const char *const email)
Definition: smtp.c:2878
static int smtp_connect(struct smtp *const smtp, const char *const server, const char *const port)
Definition: smtp.c:1703
#define SSL_get_peer_certificate
Definition: seams.h:225
SMTP_LINKAGE char * smtp_base64_encode(const char *const buf, size_t buflen)
Definition: smtp.c:825
char * _buf
Definition: smtp.h:577
void smtp_header_clear_all(struct smtp *const smtp)
Definition: smtp.c:3332
int g_smtp_test_err_si_sub_size_t_ctr
Definition: seams.c:143
#define gmtime_r
Definition: seams.h:121
#define mktime
Definition: seams.h:153
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
char * value
Definition: smtp.c:144
#define socket
Definition: seams.h:193
#define SSL_connect
Definition: seams.h:201
smtp_status_code
Definition: smtp.h:32
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
#define connect
Definition: seams.h:89
enum smtp_status_code smtp_mail(struct smtp *const smtp, const char *const body)
Definition: smtp.c:3062
static void smtp_gen_mime_boundary(char *const boundary)
Definition: smtp.c:2386
struct smtp_header * header_list
Definition: smtp.c:169
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
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_authentication_method
Definition: smtp.h:170
SMTP_LINKAGE int smtp_date_rfc_2822(char *const date)
Definition: smtp.c:2236
long timeout_sec
Definition: smtp.c:201
static int smtp_auth_plain(struct smtp *const smtp, const char *const user, const char *const pass)
Definition: smtp.c:1893
size_t num_headers
Definition: smtp.c:174
SMTP_LINKAGE int smtp_str_has_nonascii_utf8(const char *const s)
Definition: smtp.c:1110
SMTP_LINKAGE size_t smtp_fold_whitespace_get_offset(const char *const s, unsigned int maxlen)
Definition: smtp.c:1179
static void smtp_set_read_timeout(struct smtp *const smtp, long seconds)
Definition: smtp.c:2138
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
#define malloc
Definition: seams.h:145
void smtp_attachment_clear_all(struct smtp *const smtp)
Definition: smtp.c:3515
void * user_data
Definition: smtp.h:615
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)
Definition: smtp.c:2987
static int smtp_auth_login(struct smtp *const smtp, const char *const user, const char *const pass)
Definition: smtp.c:1966
char pad[4]
Definition: smtp.c:114
SMTP_LINKAGE size_t smtp_base64_decode(const char *const buf, unsigned char **decode)
Definition: smtp.c:984
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 size_t smtp_utf8_charlen(char c)
Definition: smtp.c:1078
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_mime_email(struct smtp *const smtp, const char *const body_dd)
Definition: smtp.c:2543
static int smtp_header_cmp_key(const void *const v1, const void *const v2)
Definition: smtp.c:2333
static int smtp_tls_init(struct smtp *const smtp, const char *const server)
Definition: smtp.c:1770
#define X509_check_host
Definition: seams.h:233
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
int g_smtp_test_err_si_add_size_t_ctr
Definition: seams.c:137
#define send
Definition: seams.h:185
enum smtp_status_code status_code
Definition: smtp.c:208
#define strlen
Definition: seams.h:273
SMTP_LINKAGE int smtp_header_value_validate(const char *const value)
Definition: smtp.c:2852
enum smtp_status_code smtp_auth(struct smtp *const smtp, enum smtp_authentication_method auth_method, const char *const user, const char *const pass)
Definition: smtp.c:3026
Definition: smtp.c:150
SMTP client library.
SSL * tls
Definition: smtp.c:227
smtp_flag
Definition: smtp.h:198
SMTP_LINKAGE int smtp_si_mul_size_t(const size_t a, const size_t b, size_t *const result)
Definition: smtp.c:320
int sock
Definition: smtp.c:159
str_getdelim_retcode
Definition: smtp.h:551
SMTP_LINKAGE char * smtp_str_replace(const char *const search, const char *const replace, const char *const s)
Definition: smtp.c:679
SMTP_LINKAGE int smtp_attachment_validate_name(const char *const name)
Definition: smtp.c:2927
#define select
Definition: seams.h:177
char * name
Definition: smtp.c:124
#define localtime_r
Definition: seams.h:137
#define BIO_new_socket
Definition: seams.h:57
static enum smtp_status_code smtp_str_getdelimfd_read_timeout(struct smtp *const smtp)
Definition: smtp.c:352
int g_smtp_test_err_si_mul_size_t_ctr
Definition: seams.c:149
const char * text
Definition: smtp.h:544
int smtp_test_seam_dec_err_ctr(int *const test_err_ctr)
Definition: seams.c:244
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 signed char g_base64_decode_table[]
Definition: smtp.c:894
static char g_base64_encode_table[]
Definition: smtp.c:772
#define fclose
Definition: seams.h:105
#define BIO_should_retry
Definition: seams.h:65
SMTP_LINKAGE char * smtp_fold_whitespace(const char *const s, unsigned int maxlen)
Definition: smtp.c:1250
static int smtp_header_cmp(const void *v1, const void *v2)
Definition: smtp.c:2802
SMTP_LINKAGE enum str_getdelim_retcode smtp_str_getdelimfd(struct str_getdelimfd *const gdfd)
Definition: smtp.c:523
const char * smtp_status_code_errstr(enum smtp_status_code status_code)
Definition: smtp.c:3241
static void smtp_puts_dbg(struct smtp *const smtp, const char *const prefix, const char *const str)
Definition: smtp.c:1512
SMTP_LINKAGE int smtp_str_getdelimfd_set_line_and_buf(struct str_getdelimfd *const gdfd, size_t copy_len)
Definition: smtp.c:457
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
char * name
Definition: smtp.c:104
size_t num_address
Definition: smtp.c:184
static enum smtp_status_code smtp_puts_terminate(struct smtp *const smtp, const char *const s)
Definition: smtp.c:1669
int delim
Definition: smtp.h:620
static enum smtp_status_code smtp_ehlo(struct smtp *const smtp)
Definition: smtp.c:1870
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
#define SMTP_DATE_MAX_SZ
Definition: smtp.c:2222
smtp_address_type
Definition: smtp.h:105
size_t _bufsz
Definition: smtp.h:582
char * email
Definition: smtp.c:99