NeoMutt  2021-10-29-33-g41675a
Teaching an old dog new tricks
DOXYGEN
smtp.c
Go to the documentation of this file.
1 
31 /* This file contains code for direct SMTP delivery of email messages. */
32 
33 #include "config.h"
34 #include <netdb.h>
35 #include <netinet/in.h>
36 #include <stdbool.h>
37 #include <stdint.h>
38 #include <stdio.h>
39 #include <string.h>
40 #include <sys/stat.h>
41 #include <unistd.h>
42 #include "mutt/lib.h"
43 #include "address/lib.h"
44 #include "config/lib.h"
45 #include "email/lib.h"
46 #include "conn/lib.h"
47 #include "smtp.h"
48 #include "lib.h"
49 #include "progress/lib.h"
50 #include "question/lib.h"
51 #include "mutt_account.h"
52 #include "mutt_globals.h"
53 #include "mutt_socket.h"
54 #ifdef USE_SASL
55 #include <sasl/sasl.h>
56 #include <sasl/saslutil.h>
57 #include "options.h"
58 #endif
59 
60 #define smtp_success(x) ((x) / 100 == 2)
61 #define SMTP_READY 334
62 #define SMTP_CONTINUE 354
63 
64 #define SMTP_ERR_READ -2
65 #define SMTP_ERR_WRITE -3
66 #define SMTP_ERR_CODE -4
67 
68 #define SMTP_PORT 25
69 #define SMTPS_PORT 465
70 
71 #define SMTP_AUTH_SUCCESS 0
72 #define SMTP_AUTH_UNAVAIL 1
73 #define SMTP_AUTH_FAIL -1
74 
75 // clang-format off
79 typedef uint8_t SmtpCapFlags;
80 #define SMTP_CAP_NO_FLAGS 0
81 #define SMTP_CAP_STARTTLS (1 << 0)
82 #define SMTP_CAP_AUTH (1 << 1)
83 #define SMTP_CAP_DSN (1 << 2)
84 #define SMTP_CAP_EIGHTBITMIME (1 << 3)
85 #define SMTP_CAP_SMTPUTF8 (1 << 4)
86 
87 #define SMTP_CAP_ALL ((1 << 5) - 1)
88 // clang-format on
89 
94 {
95  const char *auth_mechs;
97  struct Connection *conn;
98  struct ConfigSubset *sub;
99  const char *fqdn;
100 };
101 
105 struct SmtpAuth
106 {
113  int (*authenticate)(struct SmtpAccountData *adata, const char *method);
114 
115  const char *method;
117 };
118 
126 static bool valid_smtp_code(char *buf, size_t buflen, int *n)
127 {
128  return (mutt_str_atoi(buf, n) - buf) <= 3;
129 }
130 
137 static int smtp_get_resp(struct SmtpAccountData *adata)
138 {
139  int n;
140  char buf[1024];
141 
142  do
143  {
144  n = mutt_socket_readln(buf, sizeof(buf), adata->conn);
145  if (n < 4)
146  {
147  /* read error, or no response code */
148  return SMTP_ERR_READ;
149  }
150  const char *s = buf + 4; /* Skip the response code and the space/dash */
151  size_t plen;
152 
153  if (mutt_istr_startswith(s, "8BITMIME"))
155  else if ((plen = mutt_istr_startswith(s, "AUTH ")))
156  {
157  adata->capabilities |= SMTP_CAP_AUTH;
158  FREE(&adata->auth_mechs);
159  adata->auth_mechs = mutt_str_dup(s + plen);
160  }
161  else if (mutt_istr_startswith(s, "DSN"))
162  adata->capabilities |= SMTP_CAP_DSN;
163  else if (mutt_istr_startswith(s, "STARTTLS"))
165  else if (mutt_istr_startswith(s, "SMTPUTF8"))
167 
168  if (!valid_smtp_code(buf, n, &n))
169  return SMTP_ERR_CODE;
170 
171  } while (buf[3] == '-');
172 
173  if (smtp_success(n) || (n == SMTP_CONTINUE))
174  return 0;
175 
176  mutt_error(_("SMTP session failed: %s"), buf);
177  return -1;
178 }
179 
187 static int smtp_rcpt_to(struct SmtpAccountData *adata, const struct AddressList *al)
188 {
189  if (!al)
190  return 0;
191 
192  const char *const c_dsn_notify = cs_subset_string(adata->sub, "dsn_notify");
193 
194  struct Address *a = NULL;
195  TAILQ_FOREACH(a, al, entries)
196  {
197  /* weed out group mailboxes, since those are for display only */
198  if (!a->mailbox || a->group)
199  {
200  continue;
201  }
202  char buf[1024];
203  if ((adata->capabilities & SMTP_CAP_DSN) && c_dsn_notify)
204  snprintf(buf, sizeof(buf), "RCPT TO:<%s> NOTIFY=%s\r\n", a->mailbox, c_dsn_notify);
205  else
206  snprintf(buf, sizeof(buf), "RCPT TO:<%s>\r\n", a->mailbox);
207  if (mutt_socket_send(adata->conn, buf) == -1)
208  return SMTP_ERR_WRITE;
209  int rc = smtp_get_resp(adata);
210  if (rc != 0)
211  return rc;
212  }
213 
214  return 0;
215 }
216 
224 static int smtp_data(struct SmtpAccountData *adata, const char *msgfile)
225 {
226  char buf[1024];
227  struct Progress *progress = NULL;
228  int rc = SMTP_ERR_WRITE;
229  int term = 0;
230  size_t buflen = 0;
231 
232  FILE *fp = fopen(msgfile, "r");
233  if (!fp)
234  {
235  mutt_error(_("SMTP session failed: unable to open %s"), msgfile);
236  return -1;
237  }
238  const long size = mutt_file_get_size_fp(fp);
239  if (size == 0)
240  {
241  mutt_file_fclose(&fp);
242  return -1;
243  }
244  unlink(msgfile);
245  progress = progress_new(_("Sending message..."), MUTT_PROGRESS_NET, size);
246 
247  snprintf(buf, sizeof(buf), "DATA\r\n");
248  if (mutt_socket_send(adata->conn, buf) == -1)
249  {
250  mutt_file_fclose(&fp);
251  goto done;
252  }
253  rc = smtp_get_resp(adata);
254  if (rc != 0)
255  {
256  mutt_file_fclose(&fp);
257  goto done;
258  }
259 
260  rc = SMTP_ERR_WRITE;
261  while (fgets(buf, sizeof(buf) - 1, fp))
262  {
263  buflen = mutt_str_len(buf);
264  term = buflen && buf[buflen - 1] == '\n';
265  if (term && ((buflen == 1) || (buf[buflen - 2] != '\r')))
266  snprintf(buf + buflen - 1, sizeof(buf) - buflen + 1, "\r\n");
267  if (buf[0] == '.')
268  {
269  if (mutt_socket_send_d(adata->conn, ".", MUTT_SOCK_LOG_FULL) == -1)
270  {
271  mutt_file_fclose(&fp);
272  goto done;
273  }
274  }
275  if (mutt_socket_send_d(adata->conn, buf, MUTT_SOCK_LOG_FULL) == -1)
276  {
277  mutt_file_fclose(&fp);
278  goto done;
279  }
280  progress_update(progress, ftell(fp), -1);
281  }
282  if (!term && buflen &&
283  (mutt_socket_send_d(adata->conn, "\r\n", MUTT_SOCK_LOG_FULL) == -1))
284  {
285  mutt_file_fclose(&fp);
286  goto done;
287  }
288  mutt_file_fclose(&fp);
289 
290  /* terminate the message body */
291  if (mutt_socket_send(adata->conn, ".\r\n") == -1)
292  goto done;
293 
294  rc = smtp_get_resp(adata);
295 
296 done:
297  progress_free(&progress);
298  return rc;
299 }
300 
304 static const char *smtp_get_field(enum ConnAccountField field, void *gf_data)
305 {
306  struct SmtpAccountData *adata = gf_data;
307  if (!adata)
308  return NULL;
309 
310  switch (field)
311  {
312  case MUTT_CA_LOGIN:
313  case MUTT_CA_USER:
314  {
315  const char *const c_smtp_user = cs_subset_string(adata->sub, "smtp_user");
316  return c_smtp_user;
317  }
318  case MUTT_CA_PASS:
319  {
320  const char *const c_smtp_pass = cs_subset_string(adata->sub, "smtp_pass");
321  return c_smtp_pass;
322  }
323  case MUTT_CA_OAUTH_CMD:
324  {
325  const char *const c_smtp_oauth_refresh_command =
326  cs_subset_string(adata->sub, "smtp_oauth_refresh_command");
327  return c_smtp_oauth_refresh_command;
328  }
329  case MUTT_CA_HOST:
330  default:
331  return NULL;
332  }
333 }
334 
342 static int smtp_fill_account(struct SmtpAccountData *adata, struct ConnAccount *cac)
343 {
344  cac->flags = 0;
345  cac->port = 0;
346  cac->type = MUTT_ACCT_TYPE_SMTP;
347  cac->service = "smtp";
348  cac->get_field = smtp_get_field;
349  cac->gf_data = adata;
350 
351  const char *const c_smtp_url = cs_subset_string(adata->sub, "smtp_url");
352 
353  struct Url *url = url_parse(c_smtp_url);
354  if (!url || ((url->scheme != U_SMTP) && (url->scheme != U_SMTPS)) ||
355  !url->host || (mutt_account_fromurl(cac, url) < 0))
356  {
357  url_free(&url);
358  mutt_error(_("Invalid SMTP URL: %s"), c_smtp_url);
359  return -1;
360  }
361 
362  if (url->scheme == U_SMTPS)
363  cac->flags |= MUTT_ACCT_SSL;
364 
365  if (cac->port == 0)
366  {
367  if (cac->flags & MUTT_ACCT_SSL)
368  cac->port = SMTPS_PORT;
369  else
370  {
371  static unsigned short SmtpPort = 0;
372  if (SmtpPort == 0)
373  {
374  struct servent *service = getservbyname("smtp", "tcp");
375  if (service)
376  SmtpPort = ntohs(service->s_port);
377  else
378  SmtpPort = SMTP_PORT;
379  mutt_debug(LL_DEBUG3, "Using default SMTP port %d\n", SmtpPort);
380  }
381  cac->port = SmtpPort;
382  }
383  }
384 
385  url_free(&url);
386  return 0;
387 }
388 
396 static int smtp_helo(struct SmtpAccountData *adata, bool esmtp)
397 {
399 
400  if (!esmtp)
401  {
402  /* if TLS or AUTH are requested, use EHLO */
403  if (adata->conn->account.flags & MUTT_ACCT_USER)
404  esmtp = true;
405 #ifdef USE_SSL
406  const bool c_ssl_force_tls = cs_subset_bool(adata->sub, "ssl_force_tls");
407  const enum QuadOption c_ssl_starttls =
408  cs_subset_quad(adata->sub, "ssl_starttls");
409 
410  if (c_ssl_force_tls || (c_ssl_starttls != MUTT_NO))
411  esmtp = true;
412 #endif
413  }
414 
415  char buf[1024];
416  snprintf(buf, sizeof(buf), "%s %s\r\n", esmtp ? "EHLO" : "HELO", adata->fqdn);
417  /* XXX there should probably be a wrapper in mutt_socket.c that
418  * repeatedly calls adata->conn->write until all data is sent. This
419  * currently doesn't check for a short write. */
420  if (mutt_socket_send(adata->conn, buf) == -1)
421  return SMTP_ERR_WRITE;
422  return smtp_get_resp(adata);
423 }
424 
425 #ifdef USE_SASL
433 static int smtp_auth_sasl(struct SmtpAccountData *adata, const char *mechlist)
434 {
435  sasl_conn_t *saslconn = NULL;
436  sasl_interact_t *interaction = NULL;
437  const char *mech = NULL;
438  const char *data = NULL;
439  unsigned int len;
440  char *buf = NULL;
441  size_t bufsize = 0;
442  int rc, saslrc;
443 
444  if (mutt_sasl_client_new(adata->conn, &saslconn) < 0)
445  return SMTP_AUTH_FAIL;
446 
447  do
448  {
449  rc = sasl_client_start(saslconn, mechlist, &interaction, &data, &len, &mech);
450  if (rc == SASL_INTERACT)
451  mutt_sasl_interact(interaction);
452  } while (rc == SASL_INTERACT);
453 
454  if ((rc != SASL_OK) && (rc != SASL_CONTINUE))
455  {
456  mutt_debug(LL_DEBUG2, "%s unavailable\n", NONULL(mech));
457  sasl_dispose(&saslconn);
458  return SMTP_AUTH_UNAVAIL;
459  }
460 
461  if (!OptNoCurses)
462  {
463  // L10N: (%s) is the method name, e.g. Anonymous, CRAM-MD5, GSSAPI, SASL
464  mutt_message(_("Authenticating (%s)..."), mech);
465  }
466 
467  bufsize = MAX((len * 2), 1024);
468  buf = mutt_mem_malloc(bufsize);
469 
470  snprintf(buf, bufsize, "AUTH %s", mech);
471  if (len)
472  {
473  mutt_str_cat(buf, bufsize, " ");
474  if (sasl_encode64(data, len, buf + mutt_str_len(buf),
475  bufsize - mutt_str_len(buf), &len) != SASL_OK)
476  {
477  mutt_debug(LL_DEBUG1, "#1 error base64-encoding client response\n");
478  goto fail;
479  }
480  }
481  mutt_str_cat(buf, bufsize, "\r\n");
482 
483  do
484  {
485  if (mutt_socket_send(adata->conn, buf) < 0)
486  goto fail;
487  rc = mutt_socket_readln_d(buf, bufsize, adata->conn, MUTT_SOCK_LOG_FULL);
488  if (rc < 0)
489  goto fail;
490  if (!valid_smtp_code(buf, rc, &rc))
491  goto fail;
492 
493  if (rc != SMTP_READY)
494  break;
495 
496  if (sasl_decode64(buf + 4, strlen(buf + 4), buf, bufsize - 1, &len) != SASL_OK)
497  {
498  mutt_debug(LL_DEBUG1, "error base64-decoding server response\n");
499  goto fail;
500  }
501 
502  do
503  {
504  saslrc = sasl_client_step(saslconn, buf, len, &interaction, &data, &len);
505  if (saslrc == SASL_INTERACT)
506  mutt_sasl_interact(interaction);
507  } while (saslrc == SASL_INTERACT);
508 
509  if (len)
510  {
511  if ((len * 2) > bufsize)
512  {
513  bufsize = len * 2;
514  mutt_mem_realloc(&buf, bufsize);
515  }
516  if (sasl_encode64(data, len, buf, bufsize, &len) != SASL_OK)
517  {
518  mutt_debug(LL_DEBUG1, "#2 error base64-encoding client response\n");
519  goto fail;
520  }
521  }
522  mutt_str_copy(buf + len, "\r\n", bufsize - len);
523  } while (rc == SMTP_READY && saslrc != SASL_FAIL);
524 
525  if (smtp_success(rc))
526  {
527  mutt_sasl_setup_conn(adata->conn, saslconn);
528  FREE(&buf);
529  return SMTP_AUTH_SUCCESS;
530  }
531 
532 fail:
533  sasl_dispose(&saslconn);
534  FREE(&buf);
535  return SMTP_AUTH_FAIL;
536 }
537 #endif
538 
546 static int smtp_auth_oauth_xoauth2(struct SmtpAccountData *adata, const char *method, bool xoauth2)
547 {
548  (void) method; // This is OAUTHBEARER
549  const char *authtype = xoauth2 ? "XOAUTH2" : "OAUTHBEARER";
550 
551  // L10N: (%s) is the method name, e.g. Anonymous, CRAM-MD5, GSSAPI, SASL
552  mutt_message(_("Authenticating (%s)..."), authtype);
553 
554  /* We get the access token from the smtp_oauth_refresh_command */
555  char *oauthbearer = mutt_account_getoauthbearer(&adata->conn->account, xoauth2);
556  if (!oauthbearer)
557  return SMTP_AUTH_FAIL;
558 
559  size_t ilen = strlen(oauthbearer) + 30;
560  char *ibuf = mutt_mem_malloc(ilen);
561  snprintf(ibuf, ilen, "AUTH %s %s\r\n", authtype, oauthbearer);
562 
563  int rc = mutt_socket_send(adata->conn, ibuf);
564  FREE(&oauthbearer);
565  FREE(&ibuf);
566 
567  if (rc == -1)
568  return SMTP_AUTH_FAIL;
569  if (smtp_get_resp(adata) != 0)
570  return SMTP_AUTH_FAIL;
571 
572  return SMTP_AUTH_SUCCESS;
573 }
574 
581 static int smtp_auth_oauth(struct SmtpAccountData *adata, const char *method)
582 {
583  return smtp_auth_oauth_xoauth2(adata, method, false);
584 }
585 
592 static int smtp_auth_xoauth2(struct SmtpAccountData *adata, const char *method)
593 {
594  return smtp_auth_oauth_xoauth2(adata, method, true);
595 }
596 
604 static int smtp_auth_plain(struct SmtpAccountData *adata, const char *method)
605 {
606  (void) method; // This is PLAIN
607 
608  char buf[1024];
609 
610  /* Get username and password. Bail out of any can't be retrieved. */
611  if ((mutt_account_getuser(&adata->conn->account) < 0) ||
612  (mutt_account_getpass(&adata->conn->account) < 0))
613  {
614  goto error;
615  }
616 
617  /* Build the initial client response. */
618  size_t len =
619  mutt_sasl_plain_msg(buf, sizeof(buf), "AUTH PLAIN", adata->conn->account.user,
620  adata->conn->account.user, adata->conn->account.pass);
621 
622  /* Terminate as per SMTP protocol. Bail out if there's no room left. */
623  if (snprintf(buf + len, sizeof(buf) - len, "\r\n") != 2)
624  {
625  goto error;
626  }
627 
628  /* Send request, receive response (with a check for OK code). */
629  if ((mutt_socket_send(adata->conn, buf) < 0) || smtp_get_resp(adata))
630  {
631  goto error;
632  }
633 
634  /* If we got here, auth was successful. */
635  return 0;
636 
637 error:
638  // L10N: %s is the method name, e.g. Anonymous, CRAM-MD5, GSSAPI, SASL
639  mutt_error(_("%s authentication failed"), "SASL");
640  return -1;
641 }
642 
650 static int smtp_auth_login(struct SmtpAccountData *adata, const char *method)
651 {
652  (void) method; // This is LOGIN
653 
654  char b64[1024] = { 0 };
655  char buf[1024] = { 0 };
656 
657  /* Get username and password. Bail out of any can't be retrieved. */
658  if ((mutt_account_getuser(&adata->conn->account) < 0) ||
659  (mutt_account_getpass(&adata->conn->account) < 0))
660  {
661  goto error;
662  }
663 
664  /* Send the AUTH LOGIN request. */
665  if (mutt_socket_send(adata->conn, "AUTH LOGIN\r\n") < 0)
666  {
667  goto error;
668  }
669 
670  /* Read the 334 VXNlcm5hbWU6 challenge ("Username:" base64-encoded) */
671  mutt_socket_readln_d(buf, sizeof(buf), adata->conn, MUTT_SOCK_LOG_FULL);
672  if (!mutt_str_equal(buf, "334 VXNlcm5hbWU6"))
673  {
674  goto error;
675  }
676 
677  /* Send the username */
678  size_t len = snprintf(buf, sizeof(buf), "%s", adata->conn->account.user);
679  mutt_b64_encode(buf, len, b64, sizeof(b64));
680  snprintf(buf, sizeof(buf), "%s\r\n", b64);
681  if (mutt_socket_send(adata->conn, buf) < 0)
682  {
683  goto error;
684  }
685 
686  /* Read the 334 UGFzc3dvcmQ6 challenge ("Password:" base64-encoded) */
687  mutt_socket_readln_d(buf, sizeof(buf), adata->conn, MUTT_SOCK_LOG_FULL);
688  if (!mutt_str_equal(buf, "334 UGFzc3dvcmQ6"))
689  {
690  goto error;
691  }
692 
693  /* Send the password */
694  len = snprintf(buf, sizeof(buf), "%s", adata->conn->account.pass);
695  mutt_b64_encode(buf, len, b64, sizeof(b64));
696  snprintf(buf, sizeof(buf), "%s\r\n", b64);
697  if (mutt_socket_send(adata->conn, buf) < 0)
698  {
699  goto error;
700  }
701 
702  /* Check the final response */
703  if (smtp_get_resp(adata) < 0)
704  {
705  goto error;
706  }
707 
708  /* If we got here, auth was successful. */
709  return 0;
710 
711 error:
712  // L10N: %s is the method name, e.g. Anonymous, CRAM-MD5, GSSAPI, SASL
713  mutt_error(_("%s authentication failed"), "LOGIN");
714  return -1;
715 }
716 
720 static const struct SmtpAuth SmtpAuthenticators[] = {
721  // clang-format off
722  { smtp_auth_oauth, "oauthbearer" },
723  { smtp_auth_xoauth2, "xoauth2" },
724  { smtp_auth_plain, "plain" },
725  { smtp_auth_login, "login" },
726 #ifdef USE_SASL
727  { smtp_auth_sasl, NULL },
728 #endif
729  // clang-format on
730 };
731 
740 bool smtp_auth_is_valid(const char *authenticator)
741 {
742  for (size_t i = 0; i < mutt_array_size(SmtpAuthenticators); i++)
743  {
744  const struct SmtpAuth *auth = &SmtpAuthenticators[i];
745  if (auth->method && mutt_istr_equal(auth->method, authenticator))
746  return true;
747  }
748 
749  return false;
750 }
751 
758 static int smtp_authenticate(struct SmtpAccountData *adata)
759 {
760  int r = SMTP_AUTH_UNAVAIL;
761 
762  const struct Slist *c_smtp_authenticators =
763  cs_subset_slist(adata->sub, "smtp_authenticators");
764  if (c_smtp_authenticators && (c_smtp_authenticators->count > 0))
765  {
766  mutt_debug(LL_DEBUG2, "Trying user-defined smtp_authenticators\n");
767 
768  /* Try user-specified list of authentication methods */
769  struct ListNode *np = NULL;
770  STAILQ_FOREACH(np, &c_smtp_authenticators->head, entries)
771  {
772  mutt_debug(LL_DEBUG2, "Trying method %s\n", np->data);
773 
774  for (size_t i = 0; i < mutt_array_size(SmtpAuthenticators); i++)
775  {
776  const struct SmtpAuth *auth = &SmtpAuthenticators[i];
777  if (!auth->method || mutt_istr_equal(auth->method, np->data))
778  {
779  r = auth->authenticate(adata, np->data);
780  if (r == SMTP_AUTH_SUCCESS)
781  return r;
782  }
783  }
784  }
785  }
786  else
787  {
788  /* Fall back to default: any authenticator */
789  mutt_debug(LL_DEBUG2, "Falling back to smtp_auth_sasl, if using sasl.\n");
790 
791 #ifdef USE_SASL
792  r = smtp_auth_sasl(adata, adata->auth_mechs);
793 #else
794  mutt_error(_("SMTP authentication requires SASL"));
795  r = SMTP_AUTH_UNAVAIL;
796 #endif
797  }
798 
799  if (r != SMTP_AUTH_SUCCESS)
801 
802  if (r == SMTP_AUTH_FAIL)
803  {
804  // L10N: %s is the method name, e.g. Anonymous, CRAM-MD5, GSSAPI, SASL
805  mutt_error(_("%s authentication failed"), "SASL");
806  }
807  else if (r == SMTP_AUTH_UNAVAIL)
808  {
809  mutt_error(_("No authenticators available"));
810  }
811 
812  return (r == SMTP_AUTH_SUCCESS) ? 0 : -1;
813 }
814 
822 static int smtp_open(struct SmtpAccountData *adata, bool esmtp)
823 {
824  int rc;
825 
826  if (mutt_socket_open(adata->conn))
827  return -1;
828 
829  /* get greeting string */
830  rc = smtp_get_resp(adata);
831  if (rc != 0)
832  return rc;
833 
834  rc = smtp_helo(adata, esmtp);
835  if (rc != 0)
836  return rc;
837 
838 #ifdef USE_SSL
839  const bool c_ssl_force_tls = cs_subset_bool(adata->sub, "ssl_force_tls");
840  const enum QuadOption c_ssl_starttls =
841  cs_subset_quad(adata->sub, "ssl_starttls");
842  enum QuadOption ans = MUTT_NO;
843  if (adata->conn->ssf != 0)
844  ans = MUTT_NO;
845  else if (c_ssl_force_tls)
846  ans = MUTT_YES;
847  else if ((adata->capabilities & SMTP_CAP_STARTTLS) &&
848  ((ans = query_quadoption(c_ssl_starttls,
849  _("Secure connection with TLS?"))) == MUTT_ABORT))
850  {
851  return -1;
852  }
853 
854  if (ans == MUTT_YES)
855  {
856  if (mutt_socket_send(adata->conn, "STARTTLS\r\n") < 0)
857  return SMTP_ERR_WRITE;
858  rc = smtp_get_resp(adata);
859  // Clear any data after the STARTTLS acknowledgement
860  mutt_socket_empty(adata->conn);
861  if (rc != 0)
862  return rc;
863 
864  if (mutt_ssl_starttls(adata->conn))
865  {
866  mutt_error(_("Could not negotiate TLS connection"));
867  return -1;
868  }
869 
870  /* re-EHLO to get authentication mechanisms */
871  rc = smtp_helo(adata, esmtp);
872  if (rc != 0)
873  return rc;
874  }
875 #endif
876 
877  if (adata->conn->account.flags & MUTT_ACCT_USER)
878  {
879  if (!(adata->capabilities & SMTP_CAP_AUTH))
880  {
881  mutt_error(_("SMTP server does not support authentication"));
882  return -1;
883  }
884 
885  return smtp_authenticate(adata);
886  }
887 
888  return 0;
889 }
890 
903 int mutt_smtp_send(const struct AddressList *from, const struct AddressList *to,
904  const struct AddressList *cc, const struct AddressList *bcc,
905  const char *msgfile, bool eightbit, struct ConfigSubset *sub)
906 {
907  struct SmtpAccountData adata = { 0 };
908  struct ConnAccount cac = { { 0 } };
909  const char *envfrom = NULL;
910  char buf[1024];
911  int rc = -1;
912 
913  adata.sub = sub;
914  adata.fqdn = mutt_fqdn(false, adata.sub);
915  if (!adata.fqdn)
916  adata.fqdn = NONULL(ShortHostname);
917 
918  const struct Address *c_envelope_from_address =
919  cs_subset_address(adata.sub, "envelope_from_address");
920 
921  /* it might be better to synthesize an envelope from from user and host
922  * but this condition is most likely arrived at accidentally */
923  if (c_envelope_from_address)
924  envfrom = c_envelope_from_address->mailbox;
925  else if (from && !TAILQ_EMPTY(from))
926  envfrom = TAILQ_FIRST(from)->mailbox;
927  else
928  {
929  mutt_error(_("No from address given"));
930  return -1;
931  }
932 
933  if (smtp_fill_account(&adata, &cac) < 0)
934  return rc;
935 
936  adata.conn = mutt_conn_find(&cac);
937  if (!adata.conn)
938  return -1;
939 
940  const char *const c_dsn_return = cs_subset_string(adata.sub, "dsn_return");
941 
942  do
943  {
944  /* send our greeting */
945  rc = smtp_open(&adata, eightbit);
946  if (rc != 0)
947  break;
948  FREE(&adata.auth_mechs);
949 
950  /* send the sender's address */
951  int len = snprintf(buf, sizeof(buf), "MAIL FROM:<%s>", envfrom);
952  if (eightbit && (adata.capabilities & SMTP_CAP_EIGHTBITMIME))
953  {
954  mutt_strn_cat(buf, sizeof(buf), " BODY=8BITMIME", 15);
955  len += 14;
956  }
957  if (c_dsn_return && (adata.capabilities & SMTP_CAP_DSN))
958  len += snprintf(buf + len, sizeof(buf) - len, " RET=%s", c_dsn_return);
959  if ((adata.capabilities & SMTP_CAP_SMTPUTF8) &&
962  {
963  snprintf(buf + len, sizeof(buf) - len, " SMTPUTF8");
964  }
965  mutt_strn_cat(buf, sizeof(buf), "\r\n", 3);
966  if (mutt_socket_send(adata.conn, buf) == -1)
967  {
968  rc = SMTP_ERR_WRITE;
969  break;
970  }
971  rc = smtp_get_resp(&adata);
972  if (rc != 0)
973  break;
974 
975  /* send the recipient list */
976  if ((rc = smtp_rcpt_to(&adata, to)) || (rc = smtp_rcpt_to(&adata, cc)) ||
977  (rc = smtp_rcpt_to(&adata, bcc)))
978  {
979  break;
980  }
981 
982  /* send the message data */
983  rc = smtp_data(&adata, msgfile);
984  if (rc != 0)
985  break;
986 
987  mutt_socket_send(adata.conn, "QUIT\r\n");
988 
989  rc = 0;
990  } while (false);
991 
992  mutt_socket_close(adata.conn);
993  FREE(&adata.conn);
994 
995  if (rc == SMTP_ERR_READ)
996  mutt_error(_("SMTP session failed: read error"));
997  else if (rc == SMTP_ERR_WRITE)
998  mutt_error(_("SMTP session failed: write error"));
999  else if (rc == SMTP_ERR_CODE)
1000  mutt_error(_("Invalid server response"));
1001 
1002  return rc;
1003 }
bool mutt_addrlist_uses_unicode(const struct AddressList *al)
Do any of a list of addresses use Unicode characters.
Definition: address.c:1532
bool mutt_addr_uses_unicode(const char *str)
Does this address use Unicode character.
Definition: address.c:1512
Email Address Handling.
const char * mutt_str_atoi(const char *str, int *dst)
Convert ASCII string to an integer.
Definition: atoi.c:178
size_t mutt_b64_encode(const char *in, size_t inlen, char *out, size_t outlen)
Convert raw bytes to null-terminated base64 string.
Definition: base64.c:88
Convenience wrapper for the config headers.
Connection Library.
int mutt_account_getpass(struct ConnAccount *cac)
Fetch password into ConnAccount, if necessary.
Definition: connaccount.c:111
int mutt_account_getuser(struct ConnAccount *cac)
Retrieve username into ConnAccount, if necessary.
Definition: connaccount.c:48
char * mutt_account_getoauthbearer(struct ConnAccount *cac, bool xoauth2)
Get an OAUTHBEARER/XOAUTH2 token.
Definition: connaccount.c:159
void mutt_account_unsetpass(struct ConnAccount *cac)
Unset ConnAccount's password.
Definition: connaccount.c:141
ConnAccountField
Login credentials.
Definition: connaccount.h:33
@ MUTT_CA_OAUTH_CMD
OAuth refresh command.
Definition: connaccount.h:38
@ MUTT_CA_USER
User name.
Definition: connaccount.h:36
@ MUTT_CA_LOGIN
Login name.
Definition: connaccount.h:35
@ MUTT_CA_HOST
Server name.
Definition: connaccount.h:34
@ MUTT_CA_PASS
Password.
Definition: connaccount.h:37
#define MUTT_ACCT_SSL
Account uses SSL/TLS.
Definition: connaccount.h:47
#define MUTT_ACCT_USER
User field has been set.
Definition: connaccount.h:44
Structs that make up an email.
int mutt_file_fclose(FILE **fp)
Close a FILE handle (and NULL the pointer)
Definition: file.c:153
long mutt_file_get_size_fp(FILE *fp)
Get the size of a file.
Definition: file.c:1544
int mutt_ssl_starttls(struct Connection *conn)
Negotiate TLS over an already opened connection.
Definition: gnutls.c:1167
#define mutt_error(...)
Definition: logging.h:87
#define mutt_message(...)
Definition: logging.h:86
#define mutt_debug(LEVEL,...)
Definition: logging.h:84
const struct Address * cs_subset_address(const struct ConfigSubset *sub, const char *name)
Get an Address config item by name.
Definition: helpers.c:49
const struct Slist * cs_subset_slist(const struct ConfigSubset *sub, const char *name)
Get a string-list config item by name.
Definition: helpers.c:268
enum QuadOption cs_subset_quad(const struct ConfigSubset *sub, const char *name)
Get a quad-value config item by name.
Definition: helpers.c:218
const char * cs_subset_string(const struct ConfigSubset *sub, const char *name)
Get a string config item by name.
Definition: helpers.c:317
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:73
@ LL_DEBUG3
Log at debug level 3.
Definition: logging.h:42
@ LL_DEBUG2
Log at debug level 2.
Definition: logging.h:41
@ LL_DEBUG1
Log at debug level 1.
Definition: logging.h:40
void * mutt_mem_malloc(size_t size)
Allocate memory on the heap.
Definition: memory.c:90
void mutt_mem_realloc(void *ptr, size_t size)
Resize a block of memory on the heap.
Definition: memory.c:114
#define FREE(x)
Definition: memory.h:40
#define MAX(a, b)
Definition: memory.h:30
#define mutt_array_size(x)
Definition: memory.h:33
Convenience wrapper for the library headers.
#define _(a)
Definition: message.h:28
char * mutt_strn_cat(char *d, size_t l, const char *s, size_t sl)
Concatenate two strings.
Definition: string.c:225
bool mutt_istr_equal(const char *a, const char *b)
Compare two strings, ignoring case.
Definition: string.c:727
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:181
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:715
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:475
size_t mutt_str_copy(char *dest, const char *src, size_t dsize)
Copy a string into a buffer (guaranteeing NUL-termination)
Definition: string.c:560
char * mutt_str_cat(char *buf, size_t buflen, const char *s)
Concatenate two strings.
Definition: string.c:196
size_t mutt_istr_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix, ignoring case.
Definition: string.c:170
int mutt_account_fromurl(struct ConnAccount *cac, const struct Url *url)
Fill ConnAccount with information from url.
Definition: mutt_account.c:43
ConnAccount object used by POP and IMAP.
@ MUTT_ACCT_TYPE_SMTP
Smtp Account.
Definition: mutt_account.h:39
Hundreds of global variables to back the user variables.
char * ShortHostname
Short version of the hostname.
Definition: mutt_globals.h:52
struct Connection * mutt_conn_find(const struct ConnAccount *cac)
Find a connection from a list.
Definition: mutt_socket.c:89
NeoMutt connections.
#define MUTT_SOCK_LOG_FULL
Definition: mutt_socket.h:31
#define mutt_socket_readln(buf, buflen, conn)
Definition: mutt_socket.h:36
#define mutt_socket_send(conn, buf)
Definition: mutt_socket.h:37
#define mutt_socket_send_d(conn, buf, dbg)
Definition: mutt_socket.h:38
Handling of global boolean variables.
bool OptNoCurses
(pseudo) when sending in batch mode
Definition: options.h:53
static size_t plen
Length of cached packet.
Definition: pgppacket.c:39
Progress bar.
@ MUTT_PROGRESS_NET
Progress tracks bytes, according to $net_inc
Definition: lib.h:48
void progress_update(struct Progress *progress, size_t pos, int percent)
Update the state of the progress bar.
Definition: progress.c:177
void progress_free(struct Progress **ptr)
Free a Progress Bar.
Definition: progress.c:232
struct Progress * progress_new(const char *msg, enum ProgressType type, size_t size)
Create a new Progress Bar.
Definition: progress.c:252
QuadOption
Possible values for a quad-option.
Definition: quad.h:36
@ MUTT_ABORT
User aborted the question (with Ctrl-G)
Definition: quad.h:37
@ MUTT_NO
User answered 'No', or assume 'No'.
Definition: quad.h:38
@ MUTT_YES
User answered 'Yes', or assume 'Yes'.
Definition: quad.h:39
Ask the user a question.
enum QuadOption query_quadoption(enum QuadOption opt, const char *prompt)
Ask the user a quad-question.
Definition: question.c:349
#define TAILQ_FOREACH(var, head, field)
Definition: queue.h:725
#define STAILQ_FOREACH(var, head, field)
Definition: queue.h:352
#define TAILQ_FIRST(head)
Definition: queue.h:723
#define TAILQ_EMPTY(head)
Definition: queue.h:721
int mutt_sasl_interact(sasl_interact_t *interaction)
Perform an SASL interaction with the user.
Definition: sasl.c:701
int mutt_sasl_client_new(struct Connection *conn, sasl_conn_t **saslconn)
Wrapper for sasl_client_new()
Definition: sasl.c:606
void mutt_sasl_setup_conn(struct Connection *conn, sasl_conn_t *saslconn)
Set up an SASL connection.
Definition: sasl.c:737
size_t mutt_sasl_plain_msg(char *buf, size_t buflen, const char *cmd, const char *authz, const char *user, const char *pass)
Construct a base64 encoded SASL PLAIN message.
Definition: sasl_plain.c:55
const char * mutt_fqdn(bool may_hide_host, const struct ConfigSubset *sub)
Get the Fully-Qualified Domain Name.
Definition: sendlib.c:1194
static int smtp_get_resp(struct SmtpAccountData *adata)
Read a command response from the SMTP server.
Definition: smtp.c:137
#define SMTPS_PORT
Definition: smtp.c:69
#define SMTP_CAP_NO_FLAGS
No flags are set.
Definition: smtp.c:80
#define SMTP_CAP_STARTTLS
Server supports STARTTLS command.
Definition: smtp.c:81
uint8_t SmtpCapFlags
SMTP server capabilities.
Definition: smtp.c:79
static int smtp_authenticate(struct SmtpAccountData *adata)
Authenticate to an SMTP server.
Definition: smtp.c:758
#define SMTP_ERR_READ
Definition: smtp.c:64
bool smtp_auth_is_valid(const char *authenticator)
Check if string is a valid smtp authentication method.
Definition: smtp.c:740
static int smtp_auth_oauth_xoauth2(struct SmtpAccountData *adata, const char *method, bool xoauth2)
Authenticate an SMTP connection using OAUTHBEARER/XOAUTH2.
Definition: smtp.c:546
static const struct SmtpAuth SmtpAuthenticators[]
Accepted authentication methods.
Definition: smtp.c:720
#define SMTP_AUTH_UNAVAIL
Definition: smtp.c:72
static int smtp_helo(struct SmtpAccountData *adata, bool esmtp)
Say hello to an SMTP Server.
Definition: smtp.c:396
#define SMTP_ERR_CODE
Definition: smtp.c:66
#define SMTP_CAP_EIGHTBITMIME
Server supports 8-bit MIME content.
Definition: smtp.c:84
#define smtp_success(x)
Definition: smtp.c:60
#define SMTP_AUTH_FAIL
Definition: smtp.c:73
#define SMTP_CAP_AUTH
Server supports AUTH command.
Definition: smtp.c:82
static int smtp_data(struct SmtpAccountData *adata, const char *msgfile)
Send data to an SMTP server.
Definition: smtp.c:224
#define SMTP_ERR_WRITE
Definition: smtp.c:65
static int smtp_fill_account(struct SmtpAccountData *adata, struct ConnAccount *cac)
Create ConnAccount object from SMTP Url.
Definition: smtp.c:342
#define SMTP_AUTH_SUCCESS
Definition: smtp.c:71
static int smtp_auth_xoauth2(struct SmtpAccountData *adata, const char *method)
Authenticate an SMTP connection using XOAUTH2.
Definition: smtp.c:592
static int smtp_auth_sasl(struct SmtpAccountData *adata, const char *mechlist)
Authenticate using SASL.
Definition: smtp.c:433
#define SMTP_CAP_SMTPUTF8
Server accepts UTF-8 strings.
Definition: smtp.c:85
#define SMTP_CONTINUE
Definition: smtp.c:62
int mutt_smtp_send(const struct AddressList *from, const struct AddressList *to, const struct AddressList *cc, const struct AddressList *bcc, const char *msgfile, bool eightbit, struct ConfigSubset *sub)
Send a message using SMTP.
Definition: smtp.c:903
#define SMTP_CAP_DSN
Server supports Delivery Status Notification.
Definition: smtp.c:83
static int smtp_auth_login(struct SmtpAccountData *adata, const char *method)
Authenticate using plain text.
Definition: smtp.c:650
static int smtp_auth_plain(struct SmtpAccountData *adata, const char *method)
Authenticate using plain text.
Definition: smtp.c:604
static bool valid_smtp_code(char *buf, size_t buflen, int *n)
Is the is a valid SMTP return code?
Definition: smtp.c:126
static int smtp_auth_oauth(struct SmtpAccountData *adata, const char *method)
Authenticate an SMTP connection using OAUTHBEARER.
Definition: smtp.c:581
static int smtp_rcpt_to(struct SmtpAccountData *adata, const struct AddressList *al)
Set the recipient to an Address.
Definition: smtp.c:187
static int smtp_open(struct SmtpAccountData *adata, bool esmtp)
Open an SMTP Connection.
Definition: smtp.c:822
static const char * smtp_get_field(enum ConnAccountField field, void *gf_data)
Get connection login credentials - Implements ConnAccount::get_field()
Definition: smtp.c:304
#define SMTP_PORT
Definition: smtp.c:68
#define SMTP_READY
Definition: smtp.c:61
Send email to an SMTP server.
int mutt_socket_close(struct Connection *conn)
Close a socket.
Definition: socket.c:97
void mutt_socket_empty(struct Connection *conn)
Clear out any queued data.
Definition: socket.c:313
int mutt_socket_open(struct Connection *conn)
Simple wrapper.
Definition: socket.c:76
int mutt_socket_readln_d(char *buf, size_t buflen, struct Connection *conn, int dbg)
Read a line from a socket.
Definition: socket.c:246
Key value store.
#define NONULL(x)
Definition: string2.h:37
An email address.
Definition: address.h:36
bool group
Group mailbox?
Definition: address.h:39
char * mailbox
Mailbox and host address.
Definition: address.h:38
A set of inherited config items.
Definition: subset.h:47
Login details for a remote server.
Definition: connaccount.h:53
char user[128]
Username.
Definition: connaccount.h:56
char pass[256]
Password.
Definition: connaccount.h:57
const char * service
Name of the service, e.g. "imap".
Definition: connaccount.h:61
const char *(* get_field)(enum ConnAccountField field, void *gf_data)
Function to get some login credentials.
Definition: connaccount.h:68
unsigned char type
Connection type, e.g. MUTT_ACCT_TYPE_IMAP.
Definition: connaccount.h:59
MuttAccountFlags flags
Which fields are initialised, e.g. MUTT_ACCT_USER.
Definition: connaccount.h:60
void * gf_data
Private data to pass to get_field()
Definition: connaccount.h:70
unsigned short port
Port to connect to.
Definition: connaccount.h:58
unsigned int ssf
Security strength factor, in bits (see notes)
Definition: connection.h:51
struct ConnAccount account
Account details: username, password, etc.
Definition: connection.h:50
A List node for strings.
Definition: list.h:35
char * data
String.
Definition: list.h:36
A Progress Bar.
Definition: progress.c:49
size_t size
Total expected size.
Definition: progress.c:55
String list.
Definition: slist.h:47
struct ListHead head
List containing values.
Definition: slist.h:48
size_t count
Number of values in list.
Definition: slist.h:49
Server connection data.
Definition: smtp.c:94
const char * fqdn
Fully-qualified domain name.
Definition: smtp.c:99
struct ConfigSubset * sub
Config scope.
Definition: smtp.c:98
struct Connection * conn
Server Connection.
Definition: smtp.c:97
const char * auth_mechs
Allowed authorisation mechanisms.
Definition: smtp.c:95
SmtpCapFlags capabilities
Server capabilities.
Definition: smtp.c:96
SMTP authentication multiplexor.
Definition: smtp.c:106
int(* authenticate)(struct SmtpAccountData *adata, const char *method)
Authenticate an SMTP connection.
Definition: smtp.c:113
const char * method
Name of authentication method supported, NULL means variable.
Definition: smtp.c:115
A parsed URL proto://user:password@host:port/path?a=1&b=2
Definition: url.h:69
char * host
Host.
Definition: url.h:73
enum UrlScheme scheme
Scheme, e.g. U_SMTPS.
Definition: url.h:70
struct Url * url_parse(const char *src)
Fill in Url.
Definition: url.c:234
void url_free(struct Url **ptr)
Free the contents of a URL.
Definition: url.c:123
@ U_SMTPS
Url is smtps://.
Definition: url.h:44
@ U_SMTP
Url is smtp://.
Definition: url.h:43