NeoMutt  2024-12-12-19-ge4b57e
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
smtp.c
Go to the documentation of this file.
1
35/* This file contains code for direct SMTP delivery of email messages. */
36
37#include "config.h"
38#include <arpa/inet.h>
39#include <netdb.h>
40#include <stdbool.h>
41#include <stdint.h>
42#include <stdio.h>
43#include <unistd.h>
44#include "mutt/lib.h"
45#include "address/lib.h"
46#include "config/lib.h"
47#include "email/lib.h"
48#include "core/lib.h"
49#include "conn/lib.h"
50#include "smtp.h"
51#include "progress/lib.h"
52#include "question/lib.h"
53#include "globals.h"
54#include "mutt_socket.h"
55#include "sendlib.h"
56#ifdef USE_SASL_GNU
57#include <gsasl.h>
58#endif
59#ifdef USE_SASL_CYRUS
60#include <sasl/sasl.h>
61#endif
62
63#define smtp_success(x) (((x) / 100) == 2)
64#define SMTP_READY 334
65#define SMTP_CONTINUE 354
66
67#define SMTP_ERR_READ -2
68#define SMTP_ERR_WRITE -3
69#define SMTP_ERR_CODE -4
70
71#define SMTP_PORT 25
72#define SMTPS_PORT 465
73
74#define SMTP_AUTH_SUCCESS 0
75#define SMTP_AUTH_UNAVAIL 1
76#define SMTP_AUTH_FAIL -1
77
78// clang-format off
82typedef uint8_t SmtpCapFlags;
83#define SMTP_CAP_NO_FLAGS 0
84#define SMTP_CAP_STARTTLS (1 << 0)
85#define SMTP_CAP_AUTH (1 << 1)
86#define SMTP_CAP_DSN (1 << 2)
87#define SMTP_CAP_EIGHTBITMIME (1 << 3)
88#define SMTP_CAP_SMTPUTF8 (1 << 4)
89#define SMTP_CAP_ALL ((1 << 5) - 1)
90// clang-format on
91
96{
97 const char *auth_mechs;
99 struct Connection *conn;
101 const char *fqdn;
102};
103
108{
117 int (*authenticate)(struct SmtpAccountData *adata, const char *method);
118
119 const char *method;
121};
122
129static bool valid_smtp_code(char *buf, int *n)
130{
131 return (mutt_str_atoi(buf, n) - buf) <= 3;
132}
133
140static int smtp_get_resp(struct SmtpAccountData *adata)
141{
142 int n;
143 char buf[1024] = { 0 };
144
145 do
146 {
147 n = mutt_socket_readln(buf, sizeof(buf), adata->conn);
148 if (n < 4)
149 {
150 /* read error, or no response code */
151 return SMTP_ERR_READ;
152 }
153 const char *s = buf + 4; /* Skip the response code and the space/dash */
154 size_t plen;
155
156 if (mutt_istr_startswith(s, "8BITMIME"))
157 {
159 }
160 else if ((plen = mutt_istr_startswith(s, "AUTH ")))
161 {
162 adata->capabilities |= SMTP_CAP_AUTH;
163 FREE(&adata->auth_mechs);
164 adata->auth_mechs = mutt_str_dup(s + plen);
165 }
166 else if (mutt_istr_startswith(s, "DSN"))
167 {
168 adata->capabilities |= SMTP_CAP_DSN;
169 }
170 else if (mutt_istr_startswith(s, "STARTTLS"))
171 {
173 }
174 else if (mutt_istr_startswith(s, "SMTPUTF8"))
175 {
177 }
178
179 if (!valid_smtp_code(buf, &n))
180 return SMTP_ERR_CODE;
181
182 } while (buf[3] == '-');
183
184 if (smtp_success(n) || (n == SMTP_CONTINUE))
185 return 0;
186
187 mutt_error(_("SMTP session failed: %s"), buf);
188 return -1;
189}
190
198static int smtp_rcpt_to(struct SmtpAccountData *adata, const struct AddressList *al)
199{
200 if (!al)
201 return 0;
202
203 const char *const c_dsn_notify = cs_subset_string(adata->sub, "dsn_notify");
204
205 struct Address *a = NULL;
206 TAILQ_FOREACH(a, al, entries)
207 {
208 /* weed out group mailboxes, since those are for display only */
209 if (!a->mailbox || a->group)
210 {
211 continue;
212 }
213 char buf[1024] = { 0 };
214 if ((adata->capabilities & SMTP_CAP_DSN) && c_dsn_notify)
215 {
216 snprintf(buf, sizeof(buf), "RCPT TO:<%s> NOTIFY=%s\r\n",
217 buf_string(a->mailbox), c_dsn_notify);
218 }
219 else
220 {
221 snprintf(buf, sizeof(buf), "RCPT TO:<%s>\r\n", buf_string(a->mailbox));
222 }
223 if (mutt_socket_send(adata->conn, buf) == -1)
224 return SMTP_ERR_WRITE;
225 int rc = smtp_get_resp(adata);
226 if (rc != 0)
227 return rc;
228 }
229
230 return 0;
231}
232
240static int smtp_data(struct SmtpAccountData *adata, const char *msgfile)
241{
242 char buf[1024] = { 0 };
243 struct Progress *progress = NULL;
244 int rc = SMTP_ERR_WRITE;
245 int term = 0;
246 size_t buflen = 0;
247
248 FILE *fp = mutt_file_fopen(msgfile, "r");
249 if (!fp)
250 {
251 mutt_error(_("SMTP session failed: unable to open %s"), msgfile);
252 return -1;
253 }
254 const long size = mutt_file_get_size_fp(fp);
255 if (size == 0)
256 {
257 mutt_file_fclose(&fp);
258 return -1;
259 }
260 unlink(msgfile);
261 progress = progress_new(MUTT_PROGRESS_NET, size);
262 progress_set_message(progress, _("Sending message..."));
263
264 snprintf(buf, sizeof(buf), "DATA\r\n");
265 if (mutt_socket_send(adata->conn, buf) == -1)
266 {
267 mutt_file_fclose(&fp);
268 goto done;
269 }
270 rc = smtp_get_resp(adata);
271 if (rc != 0)
272 {
273 mutt_file_fclose(&fp);
274 goto done;
275 }
276
277 rc = SMTP_ERR_WRITE;
278 while (fgets(buf, sizeof(buf) - 1, fp))
279 {
280 buflen = mutt_str_len(buf);
281 term = buflen && buf[buflen - 1] == '\n';
282 if (term && ((buflen == 1) || (buf[buflen - 2] != '\r')))
283 snprintf(buf + buflen - 1, sizeof(buf) - buflen + 1, "\r\n");
284 if (buf[0] == '.')
285 {
286 if (mutt_socket_send_d(adata->conn, ".", MUTT_SOCK_LOG_FULL) == -1)
287 {
288 mutt_file_fclose(&fp);
289 goto done;
290 }
291 }
292 if (mutt_socket_send_d(adata->conn, buf, MUTT_SOCK_LOG_FULL) == -1)
293 {
294 mutt_file_fclose(&fp);
295 goto done;
296 }
297 progress_update(progress, MAX(0, ftell(fp)), -1);
298 }
299 if (!term && buflen &&
300 (mutt_socket_send_d(adata->conn, "\r\n", MUTT_SOCK_LOG_FULL) == -1))
301 {
302 mutt_file_fclose(&fp);
303 goto done;
304 }
305 mutt_file_fclose(&fp);
306
307 /* terminate the message body */
308 if (mutt_socket_send(adata->conn, ".\r\n") == -1)
309 goto done;
310
311 rc = smtp_get_resp(adata);
312
313done:
314 progress_free(&progress);
315 return rc;
316}
317
321static const char *smtp_get_field(enum ConnAccountField field, void *gf_data)
322{
323 struct SmtpAccountData *adata = gf_data;
324 if (!adata)
325 return NULL;
326
327 switch (field)
328 {
329 case MUTT_CA_LOGIN:
330 case MUTT_CA_USER:
331 {
332 const char *const c_smtp_user = cs_subset_string(adata->sub, "smtp_user");
333 return c_smtp_user;
334 }
335 case MUTT_CA_PASS:
336 {
337 const char *const c_smtp_pass = cs_subset_string(adata->sub, "smtp_pass");
338 return c_smtp_pass;
339 }
341 {
342 const char *const c_smtp_oauth_refresh_command = cs_subset_string(adata->sub, "smtp_oauth_refresh_command");
343 return c_smtp_oauth_refresh_command;
344 }
345 case MUTT_CA_HOST:
346 default:
347 return NULL;
348 }
349}
350
358static int smtp_fill_account(struct SmtpAccountData *adata, struct ConnAccount *cac)
359{
360 cac->flags = 0;
361 cac->port = 0;
363 cac->service = "smtp";
365 cac->gf_data = adata;
366
367 const char *const c_smtp_url = cs_subset_string(adata->sub, "smtp_url");
368
369 struct Url *url = url_parse(c_smtp_url);
370 if (!url || ((url->scheme != U_SMTP) && (url->scheme != U_SMTPS)) ||
371 !url->host || (account_from_url(cac, url) < 0))
372 {
373 url_free(&url);
374 mutt_error(_("Invalid SMTP URL: %s"), c_smtp_url);
375 return -1;
376 }
377
378 if (url->scheme == U_SMTPS)
379 cac->flags |= MUTT_ACCT_SSL;
380
381 if (cac->port == 0)
382 {
383 if (cac->flags & MUTT_ACCT_SSL)
384 {
385 cac->port = SMTPS_PORT;
386 }
387 else
388 {
389 static unsigned short SmtpPort = 0;
390 if (SmtpPort == 0)
391 {
392 struct servent *service = getservbyname("smtp", "tcp");
393 if (service)
394 SmtpPort = ntohs(service->s_port);
395 else
396 SmtpPort = SMTP_PORT;
397 mutt_debug(LL_DEBUG3, "Using default SMTP port %d\n", SmtpPort);
398 }
399 cac->port = SmtpPort;
400 }
401 }
402
403 url_free(&url);
404 return 0;
405}
406
414static int smtp_helo(struct SmtpAccountData *adata, bool esmtp)
415{
417
418 if (!esmtp)
419 {
420 /* if TLS or AUTH are requested, use EHLO */
421 if (adata->conn->account.flags & MUTT_ACCT_USER)
422 esmtp = true;
423#ifdef USE_SSL
424 const bool c_ssl_force_tls = cs_subset_bool(adata->sub, "ssl_force_tls");
425 const enum QuadOption c_ssl_starttls = cs_subset_quad(adata->sub, "ssl_starttls");
426
427 if (c_ssl_force_tls || (c_ssl_starttls != MUTT_NO))
428 esmtp = true;
429#endif
430 }
431
432 char buf[1024] = { 0 };
433 snprintf(buf, sizeof(buf), "%s %s\r\n", esmtp ? "EHLO" : "HELO", adata->fqdn);
434 /* XXX there should probably be a wrapper in mutt_socket.c that
435 * repeatedly calls adata->conn->write until all data is sent. This
436 * currently doesn't check for a short write. */
437 if (mutt_socket_send(adata->conn, buf) == -1)
438 return SMTP_ERR_WRITE;
439 return smtp_get_resp(adata);
440}
441
442#if defined(USE_SASL_CYRUS) || defined(USE_SASL_GNU)
449static int smtp_code(const struct Buffer *buf, int *n)
450{
451 if (buf_len(buf) < 3)
452 return false;
453
454 char code[4] = { 0 };
455 const char *str = buf_string(buf);
456
457 code[0] = str[0];
458 code[1] = str[1];
459 code[2] = str[2];
460 code[3] = 0;
461
462 const char *end = mutt_str_atoi(code, n);
463 if (!end || (*end != '\0'))
464 return false;
465 return true;
466}
467
479static int smtp_get_auth_response(struct Connection *conn, struct Buffer *input_buf,
480 int *smtp_rc, struct Buffer *response_buf)
481{
482 buf_reset(response_buf);
483 do
484 {
485 if (mutt_socket_buffer_readln(input_buf, conn) < 0)
486 return -1;
487 if (!smtp_code(input_buf, smtp_rc))
488 {
489 return -1;
490 }
491
492 if (*smtp_rc != SMTP_READY)
493 break;
494
495 const char *smtp_response = input_buf->data + 3;
496 if (*smtp_response)
497 {
498 smtp_response++;
499 buf_addstr(response_buf, smtp_response);
500 }
501 } while (input_buf->data[3] == '-');
502
503 return 0;
504}
505#endif
506
507#ifdef USE_SASL_GNU
515static int smtp_auth_gsasl(struct SmtpAccountData *adata, const char *mechlist)
516{
517 Gsasl_session *gsasl_session = NULL;
518 struct Buffer *input_buf = NULL, *output_buf = NULL, *smtp_response_buf = NULL;
519 int rc = SMTP_AUTH_FAIL, gsasl_rc = GSASL_OK, smtp_rc;
520
521 const char *chosen_mech = mutt_gsasl_get_mech(mechlist, adata->auth_mechs);
522 if (!chosen_mech)
523 {
524 mutt_debug(LL_DEBUG2, "returned no usable mech\n");
525 return SMTP_AUTH_UNAVAIL;
526 }
527
528 mutt_debug(LL_DEBUG2, "using mech %s\n", chosen_mech);
529
530 if (mutt_gsasl_client_new(adata->conn, chosen_mech, &gsasl_session) < 0)
531 {
532 mutt_debug(LL_DEBUG1, "Error allocating GSASL connection\n");
533 return SMTP_AUTH_UNAVAIL;
534 }
535
536 if (!OptNoCurses)
537 mutt_message(_("Authenticating (%s)..."), chosen_mech);
538
539 input_buf = buf_pool_get();
540 output_buf = buf_pool_get();
541 smtp_response_buf = buf_pool_get();
542
543 buf_printf(output_buf, "AUTH %s", chosen_mech);
544
545 /* Work around broken SMTP servers. See Debian #1010658.
546 * The msmtp source also forces IR for PLAIN because the author
547 * encountered difficulties with a server requiring it. */
548 if (mutt_str_equal(chosen_mech, "PLAIN"))
549 {
550 char *gsasl_step_output = NULL;
551 gsasl_rc = gsasl_step64(gsasl_session, "", &gsasl_step_output);
552 if (gsasl_rc != GSASL_NEEDS_MORE && gsasl_rc != GSASL_OK)
553 {
554 mutt_debug(LL_DEBUG1, "gsasl_step64() failed (%d): %s\n", gsasl_rc,
555 gsasl_strerror(gsasl_rc));
556 goto fail;
557 }
558
559 buf_addch(output_buf, ' ');
560 buf_addstr(output_buf, gsasl_step_output);
561 gsasl_free(gsasl_step_output);
562 }
563
564 buf_addstr(output_buf, "\r\n");
565
566 do
567 {
568 if (mutt_socket_send(adata->conn, buf_string(output_buf)) < 0)
569 goto fail;
570
571 if (smtp_get_auth_response(adata->conn, input_buf, &smtp_rc, smtp_response_buf) < 0)
572 goto fail;
573
574 if (smtp_rc != SMTP_READY)
575 break;
576
577 char *gsasl_step_output = NULL;
578 gsasl_rc = gsasl_step64(gsasl_session, buf_string(smtp_response_buf), &gsasl_step_output);
579 if ((gsasl_rc == GSASL_NEEDS_MORE) || (gsasl_rc == GSASL_OK))
580 {
581 buf_strcpy(output_buf, gsasl_step_output);
582 buf_addstr(output_buf, "\r\n");
583 gsasl_free(gsasl_step_output);
584 }
585 else
586 {
587 mutt_debug(LL_DEBUG1, "gsasl_step64() failed (%d): %s\n", gsasl_rc,
588 gsasl_strerror(gsasl_rc));
589 }
590 } while ((gsasl_rc == GSASL_NEEDS_MORE) || (gsasl_rc == GSASL_OK));
591
592 if (smtp_rc == SMTP_READY)
593 {
594 mutt_socket_send(adata->conn, "*\r\n");
595 goto fail;
596 }
597
598 if (smtp_success(smtp_rc) && (gsasl_rc == GSASL_OK))
600
601fail:
602 buf_pool_release(&input_buf);
603 buf_pool_release(&output_buf);
604 buf_pool_release(&smtp_response_buf);
605 mutt_gsasl_client_finish(&gsasl_session);
606
607 if (rc == SMTP_AUTH_FAIL)
608 mutt_debug(LL_DEBUG2, "%s failed\n", chosen_mech);
609
610 return rc;
611}
612#endif
613
614#ifdef USE_SASL_CYRUS
622static int smtp_auth_sasl(struct SmtpAccountData *adata, const char *mechlist)
623{
624 sasl_conn_t *saslconn = NULL;
625 sasl_interact_t *interaction = NULL;
626 const char *mech = NULL;
627 const char *data = NULL;
628 unsigned int data_len = 0;
629 struct Buffer *temp_buf = NULL;
630 struct Buffer *output_buf = NULL;
631 struct Buffer *smtp_response_buf = NULL;
632 int rc = SMTP_AUTH_FAIL;
633 int rc_sasl;
634 int rc_smtp;
635
636 if (mutt_sasl_client_new(adata->conn, &saslconn) < 0)
637 return SMTP_AUTH_FAIL;
638
639 do
640 {
641 rc_sasl = sasl_client_start(saslconn, mechlist, &interaction, &data, &data_len, &mech);
642 if (rc_sasl == SASL_INTERACT)
643 mutt_sasl_interact(interaction);
644 } while (rc_sasl == SASL_INTERACT);
645
646 if ((rc_sasl != SASL_OK) && (rc_sasl != SASL_CONTINUE))
647 {
648 mutt_debug(LL_DEBUG2, "%s unavailable\n", NONULL(mech));
649 sasl_dispose(&saslconn);
650 return SMTP_AUTH_UNAVAIL;
651 }
652
653 if (!OptNoCurses)
654 {
655 // L10N: (%s) is the method name, e.g. Anonymous, CRAM-MD5, GSSAPI, SASL
656 mutt_message(_("Authenticating (%s)..."), mech);
657 }
658
659 temp_buf = buf_pool_get();
660 output_buf = buf_pool_get();
661 smtp_response_buf = buf_pool_get();
662
663 buf_printf(output_buf, "AUTH %s", mech);
664 if (data_len > 0)
665 {
666 buf_addch(output_buf, ' ');
667 mutt_b64_buffer_encode(temp_buf, data, data_len);
668 buf_addstr(output_buf, buf_string(temp_buf));
669 }
670 buf_addstr(output_buf, "\r\n");
671
672 do
673 {
674 if (mutt_socket_send(adata->conn, buf_string(output_buf)) < 0)
675 goto fail;
676
677 if (smtp_get_auth_response(adata->conn, temp_buf, &rc_smtp, smtp_response_buf) < 0)
678 goto fail;
679
680 if (rc_smtp != SMTP_READY)
681 break;
682
683 if (mutt_b64_buffer_decode(temp_buf, buf_string(smtp_response_buf)) < 0)
684 {
685 mutt_debug(LL_DEBUG1, "error base64-decoding server response\n");
686 goto fail;
687 }
688
689 do
690 {
691 rc_sasl = sasl_client_step(saslconn, buf_string(temp_buf), buf_len(temp_buf),
692 &interaction, &data, &data_len);
693 if (rc_sasl == SASL_INTERACT)
694 mutt_sasl_interact(interaction);
695 } while (rc_sasl == SASL_INTERACT);
696
697 if (data_len > 0)
698 mutt_b64_buffer_encode(output_buf, data, data_len);
699 else
700 buf_reset(output_buf);
701
702 buf_addstr(output_buf, "\r\n");
703 } while (rc_sasl != SASL_FAIL);
704
705 if (smtp_success(rc_smtp))
706 {
707 mutt_sasl_setup_conn(adata->conn, saslconn);
709 }
710 else
711 {
712 if (rc_smtp == SMTP_READY)
713 mutt_socket_send(adata->conn, "*\r\n");
714 sasl_dispose(&saslconn);
715 }
716
717fail:
718 buf_pool_release(&temp_buf);
719 buf_pool_release(&output_buf);
720 buf_pool_release(&smtp_response_buf);
721 return rc;
722}
723#endif
724
732static int smtp_auth_oauth_xoauth2(struct SmtpAccountData *adata, const char *method, bool xoauth2)
733{
734 /* If they did not explicitly request or configure oauth then fail quietly */
735 const char *const c_smtp_oauth_refresh_command = cs_subset_string(NeoMutt->sub, "smtp_oauth_refresh_command");
736 if (!method && !c_smtp_oauth_refresh_command)
737 return SMTP_AUTH_UNAVAIL;
738
739 const char *authtype = xoauth2 ? "XOAUTH2" : "OAUTHBEARER";
740
741 // L10N: (%s) is the method name, e.g. Anonymous, CRAM-MD5, GSSAPI, SASL
742 mutt_message(_("Authenticating (%s)..."), authtype);
743
744 /* We get the access token from the smtp_oauth_refresh_command */
745 char *oauthbearer = mutt_account_getoauthbearer(&adata->conn->account, xoauth2);
746 if (!oauthbearer)
747 return SMTP_AUTH_FAIL;
748
749 char *ibuf = NULL;
750 mutt_str_asprintf(&ibuf, "AUTH %s %s\r\n", authtype, oauthbearer);
751
752 int rc = mutt_socket_send(adata->conn, ibuf);
753 FREE(&oauthbearer);
754 FREE(&ibuf);
755
756 if (rc == -1)
757 return SMTP_AUTH_FAIL;
758 if (smtp_get_resp(adata) != 0)
759 return SMTP_AUTH_FAIL;
760
761 return SMTP_AUTH_SUCCESS;
762}
763
770static int smtp_auth_oauth(struct SmtpAccountData *adata, const char *method)
771{
772 return smtp_auth_oauth_xoauth2(adata, method, false);
773}
774
781static int smtp_auth_xoauth2(struct SmtpAccountData *adata, const char *method)
782{
783 return smtp_auth_oauth_xoauth2(adata, method, true);
784}
785
795static int smtp_auth_plain(struct SmtpAccountData *adata, const char *method)
796{
797 struct Buffer *buf = NULL;
798 struct ConnAccount *cac = &adata->conn->account;
799 int rc = -1;
800
801 /* Get username and password. Bail out of any can't be retrieved. */
802 if ((mutt_account_getuser(cac) < 0) || (mutt_account_getpass(cac) < 0))
803 goto error;
804
805 /* Build the initial client response. */
806 buf = buf_pool_get();
807 mutt_sasl_plain_msg(buf, "AUTH PLAIN", cac->user, cac->user, cac->pass);
808 buf_add_printf(buf, "\r\n");
809
810 /* Send request, receive response (with a check for OK code). */
811 if ((mutt_socket_send(adata->conn, buf_string(buf)) < 0) || smtp_get_resp(adata))
812 goto error;
813
814 rc = 0; // Auth was successful
815
816error:
817 if (rc != 0)
818 {
819 // L10N: %s is the method name, e.g. Anonymous, CRAM-MD5, GSSAPI, SASL
820 mutt_error(_("%s authentication failed"), "SASL");
821 }
822 buf_pool_release(&buf);
823 return rc;
824}
825
835static int smtp_auth_login(struct SmtpAccountData *adata, const char *method)
836{
837 char b64[1024] = { 0 };
838 char buf[1026] = { 0 };
839
840 /* Get username and password. Bail out of any can't be retrieved. */
841 if ((mutt_account_getuser(&adata->conn->account) < 0) ||
842 (mutt_account_getpass(&adata->conn->account) < 0))
843 {
844 goto error;
845 }
846
847 /* Send the AUTH LOGIN request. */
848 if (mutt_socket_send(adata->conn, "AUTH LOGIN\r\n") < 0)
849 {
850 goto error;
851 }
852
853 /* Read the 334 VXNlcm5hbWU6 challenge ("Username:" base64-encoded) */
854 int rc = mutt_socket_readln_d(buf, sizeof(buf), adata->conn, MUTT_SOCK_LOG_FULL);
855 if ((rc < 0) || !mutt_str_equal(buf, "334 VXNlcm5hbWU6"))
856 {
857 goto error;
858 }
859
860 /* Send the username */
861 size_t len = snprintf(buf, sizeof(buf), "%s", adata->conn->account.user);
862 mutt_b64_encode(buf, len, b64, sizeof(b64));
863 snprintf(buf, sizeof(buf), "%s\r\n", b64);
864 if (mutt_socket_send(adata->conn, buf) < 0)
865 {
866 goto error;
867 }
868
869 /* Read the 334 UGFzc3dvcmQ6 challenge ("Password:" base64-encoded) */
870 rc = mutt_socket_readln_d(buf, sizeof(buf), adata->conn, MUTT_SOCK_LOG_FULL);
871 if ((rc < 0) || !mutt_str_equal(buf, "334 UGFzc3dvcmQ6"))
872 {
873 goto error;
874 }
875
876 /* Send the password */
877 len = snprintf(buf, sizeof(buf), "%s", adata->conn->account.pass);
878 mutt_b64_encode(buf, len, b64, sizeof(b64));
879 snprintf(buf, sizeof(buf), "%s\r\n", b64);
880 if (mutt_socket_send(adata->conn, buf) < 0)
881 {
882 goto error;
883 }
884
885 /* Check the final response */
886 if (smtp_get_resp(adata) < 0)
887 {
888 goto error;
889 }
890
891 /* If we got here, auth was successful. */
892 return 0;
893
894error:
895 // L10N: %s is the method name, e.g. Anonymous, CRAM-MD5, GSSAPI, SASL
896 mutt_error(_("%s authentication failed"), "LOGIN");
897 return -1;
898}
899
903static const struct SmtpAuth SmtpAuthenticators[] = {
904 // clang-format off
905 { smtp_auth_oauth, "oauthbearer" },
906 { smtp_auth_xoauth2, "xoauth2" },
907 { smtp_auth_plain, "plain" },
908 { smtp_auth_login, "login" },
909#ifdef USE_SASL_CYRUS
910 { smtp_auth_sasl, NULL },
911#endif
912#ifdef USE_SASL_GNU
913 { smtp_auth_gsasl, NULL },
914#endif
915 // clang-format on
916};
917
926bool smtp_auth_is_valid(const char *authenticator)
927{
928 for (size_t i = 0; i < mutt_array_size(SmtpAuthenticators); i++)
929 {
930 const struct SmtpAuth *auth = &SmtpAuthenticators[i];
931 if (auth->method && mutt_istr_equal(auth->method, authenticator))
932 return true;
933 }
934
935 return false;
936}
937
944static int smtp_authenticate(struct SmtpAccountData *adata)
945{
946 int r = SMTP_AUTH_UNAVAIL;
947
948 const struct Slist *c_smtp_authenticators = cs_subset_slist(adata->sub, "smtp_authenticators");
949 if (c_smtp_authenticators && (c_smtp_authenticators->count > 0))
950 {
951 mutt_debug(LL_DEBUG2, "Trying user-defined smtp_authenticators\n");
952
953 /* Try user-specified list of authentication methods */
954 struct ListNode *np = NULL;
955 STAILQ_FOREACH(np, &c_smtp_authenticators->head, entries)
956 {
957 mutt_debug(LL_DEBUG2, "Trying method %s\n", np->data);
958
959 for (size_t i = 0; i < mutt_array_size(SmtpAuthenticators); i++)
960 {
961 const struct SmtpAuth *auth = &SmtpAuthenticators[i];
962 if (!auth->method || mutt_istr_equal(auth->method, np->data))
963 {
964 r = auth->authenticate(adata, np->data);
965 if (r == SMTP_AUTH_SUCCESS)
966 return r;
967 }
968 }
969 }
970 }
971 else
972 {
973 /* Fall back to default: any authenticator */
974#if defined(USE_SASL_CYRUS)
975 mutt_debug(LL_DEBUG2, "Falling back to smtp_auth_sasl, if using sasl\n");
976 r = smtp_auth_sasl(adata, adata->auth_mechs);
977#elif defined(USE_SASL_GNU)
978 mutt_debug(LL_DEBUG2, "Falling back to smtp_auth_gsasl, if using gsasl\n");
979 r = smtp_auth_gsasl(adata, adata->auth_mechs);
980#else
981 mutt_debug(LL_DEBUG2, "Falling back to using any authenticator available\n");
982 /* Try all available authentication methods */
983 for (size_t i = 0; i < mutt_array_size(SmtpAuthenticators); i++)
984 {
985 const struct SmtpAuth *auth = &SmtpAuthenticators[i];
986 mutt_debug(LL_DEBUG2, "Trying method %s\n", auth->method ? auth->method : "<variable>");
987 r = auth->authenticate(adata, auth->method);
988 if (r == SMTP_AUTH_SUCCESS)
989 return r;
990 }
991#endif
992 }
993
994 if (r != SMTP_AUTH_SUCCESS)
996
997 if (r == SMTP_AUTH_FAIL)
998 {
999 // L10N: %s is the method name, e.g. Anonymous, CRAM-MD5, GSSAPI, SASL
1000 mutt_error(_("%s authentication failed"), "SASL");
1001 }
1002 else if (r == SMTP_AUTH_UNAVAIL)
1003 {
1004 mutt_error(_("No authenticators available"));
1005 }
1006
1007 return (r == SMTP_AUTH_SUCCESS) ? 0 : -1;
1008}
1009
1017static int smtp_open(struct SmtpAccountData *adata, bool esmtp)
1018{
1019 int rc;
1020
1021 if (mutt_socket_open(adata->conn))
1022 return -1;
1023
1024 const bool force_auth = cs_subset_string(adata->sub, "smtp_user");
1025 esmtp |= force_auth;
1026
1027 /* get greeting string */
1028 rc = smtp_get_resp(adata);
1029 if (rc != 0)
1030 return rc;
1031
1032 rc = smtp_helo(adata, esmtp);
1033 if (rc != 0)
1034 return rc;
1035
1036#ifdef USE_SSL
1037 const bool c_ssl_force_tls = cs_subset_bool(adata->sub, "ssl_force_tls");
1038 enum QuadOption ans = MUTT_NO;
1039 if (adata->conn->ssf != 0)
1040 ans = MUTT_NO;
1041 else if (c_ssl_force_tls)
1042 ans = MUTT_YES;
1043 else if ((adata->capabilities & SMTP_CAP_STARTTLS) &&
1044 ((ans = query_quadoption(_("Secure connection with TLS?"),
1045 adata->sub, "ssl_starttls")) == MUTT_ABORT))
1046 {
1047 return -1;
1048 }
1049
1050 if (ans == MUTT_YES)
1051 {
1052 if (mutt_socket_send(adata->conn, "STARTTLS\r\n") < 0)
1053 return SMTP_ERR_WRITE;
1054 rc = smtp_get_resp(adata);
1055 // Clear any data after the STARTTLS acknowledgement
1056 mutt_socket_empty(adata->conn);
1057 if (rc != 0)
1058 return rc;
1059
1060 if (mutt_ssl_starttls(adata->conn))
1061 {
1062 mutt_error(_("Could not negotiate TLS connection"));
1063 return -1;
1064 }
1065
1066 /* re-EHLO to get authentication mechanisms */
1067 rc = smtp_helo(adata, esmtp);
1068 if (rc != 0)
1069 return rc;
1070 }
1071#endif
1072
1073 if (force_auth || adata->conn->account.flags & MUTT_ACCT_USER)
1074 {
1075 if (!(adata->capabilities & SMTP_CAP_AUTH))
1076 {
1077 mutt_error(_("SMTP server does not support authentication"));
1078 return -1;
1079 }
1080
1081 return smtp_authenticate(adata);
1082 }
1083
1084 return 0;
1085}
1086
1099int mutt_smtp_send(const struct AddressList *from, const struct AddressList *to,
1100 const struct AddressList *cc, const struct AddressList *bcc,
1101 const char *msgfile, bool eightbit, struct ConfigSubset *sub)
1102{
1103 struct SmtpAccountData adata = { 0 };
1104 struct ConnAccount cac = { { 0 } };
1105 const char *envfrom = NULL;
1106 int rc = -1;
1107
1108 adata.sub = sub;
1109 adata.fqdn = mutt_fqdn(false, adata.sub);
1110 if (!adata.fqdn)
1111 adata.fqdn = NONULL(ShortHostname);
1112
1113 const struct Address *c_envelope_from_address = cs_subset_address(adata.sub, "envelope_from_address");
1114
1115 if (smtp_fill_account(&adata, &cac) < 0)
1116 return rc;
1117
1118 adata.conn = mutt_conn_find(&cac);
1119 if (!adata.conn)
1120 return -1;
1121
1122 /* it might be better to synthesize an envelope from from user and host
1123 * but this condition is most likely arrived at accidentally */
1124 if (c_envelope_from_address)
1125 {
1126 envfrom = buf_string(c_envelope_from_address->mailbox);
1127 }
1128 else if (from && !TAILQ_EMPTY(from))
1129 {
1130 envfrom = buf_string(TAILQ_FIRST(from)->mailbox);
1131 }
1132 else
1133 {
1134 mutt_error(_("No from address given"));
1135 mutt_socket_close(adata.conn);
1136 return -1;
1137 }
1138
1139 const char *const c_dsn_return = cs_subset_string(adata.sub, "dsn_return");
1140
1141 struct Buffer *buf = buf_pool_get();
1142 do
1143 {
1144 /* send our greeting */
1145 rc = smtp_open(&adata, eightbit);
1146 if (rc != 0)
1147 break;
1148 FREE(&adata.auth_mechs);
1149
1150 /* send the sender's address */
1151 buf_printf(buf, "MAIL FROM:<%s>", envfrom);
1152 if (eightbit && (adata.capabilities & SMTP_CAP_EIGHTBITMIME))
1153 buf_addstr(buf, " BODY=8BITMIME");
1154
1155 if (c_dsn_return && (adata.capabilities & SMTP_CAP_DSN))
1156 buf_add_printf(buf, " RET=%s", c_dsn_return);
1157
1158 if ((adata.capabilities & SMTP_CAP_SMTPUTF8) &&
1161 {
1162 buf_addstr(buf, " SMTPUTF8");
1163 }
1164 buf_addstr(buf, "\r\n");
1165 if (mutt_socket_send(adata.conn, buf_string(buf)) == -1)
1166 {
1167 rc = SMTP_ERR_WRITE;
1168 break;
1169 }
1170 rc = smtp_get_resp(&adata);
1171 if (rc != 0)
1172 break;
1173
1174 /* send the recipient list */
1175 if ((rc = smtp_rcpt_to(&adata, to)) || (rc = smtp_rcpt_to(&adata, cc)) ||
1176 (rc = smtp_rcpt_to(&adata, bcc)))
1177 {
1178 break;
1179 }
1180
1181 /* send the message data */
1182 rc = smtp_data(&adata, msgfile);
1183 if (rc != 0)
1184 break;
1185
1186 mutt_socket_send(adata.conn, "QUIT\r\n");
1187
1188 rc = 0;
1189 } while (false);
1190
1191 mutt_socket_close(adata.conn);
1192 FREE(&adata.conn);
1193
1194 if (rc == SMTP_ERR_READ)
1195 mutt_error(_("SMTP session failed: read error"));
1196 else if (rc == SMTP_ERR_WRITE)
1197 mutt_error(_("SMTP session failed: write error"));
1198 else if (rc == SMTP_ERR_CODE)
1199 mutt_error(_("Invalid server response"));
1200
1201 buf_pool_release(&buf);
1202 return rc;
1203}
bool mutt_addrlist_uses_unicode(const struct AddressList *al)
Do any of a list of addresses use Unicode characters.
Definition: address.c:1522
bool mutt_addr_uses_unicode(const char *str)
Does this address use Unicode character.
Definition: address.c:1502
const struct Address * cs_subset_address(const struct ConfigSubset *sub, const char *name)
Get an Address config item by name.
Definition: config_type.c:272
Email Address Handling.
const char * mutt_str_atoi(const char *str, int *dst)
Convert ASCII string to an integer.
Definition: atoi.c:192
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:87
size_t mutt_b64_buffer_encode(struct Buffer *buf, const char *in, size_t len)
Convert raw bytes to null-terminated base64 string.
Definition: base64.c:198
int mutt_b64_buffer_decode(struct Buffer *buf, const char *in)
Convert null-terminated base64 string to raw bytes.
Definition: base64.c:216
int buf_printf(struct Buffer *buf, const char *fmt,...)
Format a string overwriting a Buffer.
Definition: buffer.c:161
int buf_add_printf(struct Buffer *buf, const char *fmt,...)
Format a string appending a Buffer.
Definition: buffer.c:204
size_t buf_len(const struct Buffer *buf)
Calculate the length of a Buffer.
Definition: buffer.c:491
void buf_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition: buffer.c:76
size_t buf_addch(struct Buffer *buf, char c)
Add a single character to a Buffer.
Definition: buffer.c:241
size_t buf_addstr(struct Buffer *buf, const char *s)
Add a string to a Buffer.
Definition: buffer.c:226
size_t buf_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition: buffer.c:395
static const char * buf_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition: buffer.h:96
const char * cs_subset_string(const struct ConfigSubset *sub, const char *name)
Get a string config item by name.
Definition: helpers.c:291
const struct Slist * cs_subset_slist(const struct ConfigSubset *sub, const char *name)
Get a string-list config item by name.
Definition: helpers.c:242
enum QuadOption cs_subset_quad(const struct ConfigSubset *sub, const char *name)
Get a quad-value config item by name.
Definition: helpers.c:192
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:47
Convenience wrapper for the config headers.
Connection Library.
int mutt_account_getpass(struct ConnAccount *cac)
Fetch password into ConnAccount, if necessary.
Definition: connaccount.c:130
int mutt_account_getuser(struct ConnAccount *cac)
Retrieve username into ConnAccount, if necessary.
Definition: connaccount.c:51
void mutt_account_unsetpass(struct ConnAccount *cac)
Unset ConnAccount's password.
Definition: connaccount.c:177
char * mutt_account_getoauthbearer(struct ConnAccount *cac, bool xoauth2)
Get an OAUTHBEARER/XOAUTH2 token.
Definition: connaccount.c:195
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
Convenience wrapper for the core headers.
Structs that make up an email.
long mutt_file_get_size_fp(FILE *fp)
Get the size of a file.
Definition: file.c:1537
#define mutt_file_fclose(FP)
Definition: file.h:138
#define mutt_file_fopen(PATH, MODE)
Definition: file.h:137
char * ShortHostname
Short version of the hostname.
Definition: globals.c:38
bool OptNoCurses
(pseudo) when sending in batch mode
Definition: globals.c:69
int mutt_ssl_starttls(struct Connection *conn)
Negotiate TLS over an already opened connection.
Definition: gnutls.c:1146
static const char * smtp_get_field(enum ConnAccountField field, void *gf_data)
Get connection login credentials - Implements ConnAccount::get_field() -.
Definition: smtp.c:321
#define mutt_error(...)
Definition: logging2.h:92
#define mutt_message(...)
Definition: logging2.h:91
#define mutt_debug(LEVEL,...)
Definition: logging2.h:89
static int smtp_auth_xoauth2(struct SmtpAccountData *adata, const char *method)
Authenticate an SMTP connection using XOAUTH2 - Implements SmtpAuth::authenticate() -.
Definition: smtp.c:781
static int smtp_auth_login(struct SmtpAccountData *adata, const char *method)
Authenticate using plain text - Implements SmtpAuth::authenticate() -.
Definition: smtp.c:835
static int smtp_auth_plain(struct SmtpAccountData *adata, const char *method)
Authenticate using plain text - Implements SmtpAuth::authenticate() -.
Definition: smtp.c:795
static int smtp_auth_oauth(struct SmtpAccountData *adata, const char *method)
Authenticate an SMTP connection using OAUTHBEARER - Implements SmtpAuth::authenticate() -.
Definition: smtp.c:770
const char * mutt_gsasl_get_mech(const char *requested_mech, const char *server_mechlist)
Pick a connection mechanism.
Definition: gsasl.c:164
int mutt_gsasl_client_new(struct Connection *conn, const char *mech, Gsasl_session **sctx)
Create a new GNU SASL client.
Definition: gsasl.c:199
void mutt_gsasl_client_finish(Gsasl_session **sctx)
Free a GNU SASL client.
Definition: gsasl.c:220
@ LL_DEBUG3
Log at debug level 3.
Definition: logging2.h:45
@ LL_DEBUG2
Log at debug level 2.
Definition: logging2.h:44
@ LL_DEBUG1
Log at debug level 1.
Definition: logging2.h:43
#define FREE(x)
Definition: memory.h:55
#define MAX(a, b)
Definition: memory.h:31
#define mutt_array_size(x)
Definition: memory.h:38
Convenience wrapper for the library headers.
#define _(a)
Definition: message.h:28
bool mutt_istr_equal(const char *a, const char *b)
Compare two strings, ignoring case.
Definition: string.c:672
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:253
int mutt_str_asprintf(char **strp, const char *fmt,...)
Definition: string.c:803
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:660
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:496
size_t mutt_istr_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix, ignoring case.
Definition: string.c:242
int account_from_url(struct ConnAccount *cac, const struct Url *url)
Fill ConnAccount with information from url.
Definition: mutt_account.c:44
@ MUTT_ACCT_TYPE_SMTP
Smtp Account.
Definition: mutt_account.h:38
struct Connection * mutt_conn_find(const struct ConnAccount *cac)
Find a connection from a list.
Definition: mutt_socket.c:89
NeoMutt connections.
struct Buffer * buf_pool_get(void)
Get a Buffer from the pool.
Definition: pool.c:82
void buf_pool_release(struct Buffer **ptr)
Return a Buffer to the pool.
Definition: pool.c:96
Progress Bar.
@ MUTT_PROGRESS_NET
Progress tracks bytes, according to $net_inc
Definition: lib.h:82
struct Progress * progress_new(enum ProgressType type, size_t size)
Create a new Progress Bar.
Definition: progress.c:139
void progress_free(struct Progress **ptr)
Free a Progress Bar.
Definition: progress.c:110
void progress_set_message(struct Progress *progress, const char *fmt,...) __attribute__((__format__(__printf__
bool progress_update(struct Progress *progress, size_t pos, int percent)
Update the state of the progress bar.
Definition: progress.c:80
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(const char *prompt, struct ConfigSubset *sub, const char *name)
Ask the user a quad-question.
Definition: question.c:379
#define TAILQ_FOREACH(var, head, field)
Definition: queue.h:743
#define STAILQ_FOREACH(var, head, field)
Definition: queue.h:352
#define TAILQ_FIRST(head)
Definition: queue.h:741
#define TAILQ_EMPTY(head)
Definition: queue.h:739
int mutt_sasl_interact(sasl_interact_t *interaction)
Perform an SASL interaction with the user.
Definition: sasl.c:704
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:741
size_t mutt_sasl_plain_msg(struct Buffer *buf, const char *cmd, const char *authz, const char *user, const char *pass)
Construct a base64 encoded SASL PLAIN message.
Definition: sasl_plain.c:50
const char * mutt_fqdn(bool may_hide_host, const struct ConfigSubset *sub)
Get the Fully-Qualified Domain Name.
Definition: sendlib.c:707
Miscellaneous functions for sending an email.
static int smtp_get_resp(struct SmtpAccountData *adata)
Read a command response from the SMTP server.
Definition: smtp.c:140
#define SMTPS_PORT
Definition: smtp.c:72
#define SMTP_CAP_NO_FLAGS
No flags are set.
Definition: smtp.c:83
#define SMTP_CAP_STARTTLS
Server supports STARTTLS command.
Definition: smtp.c:84
uint8_t SmtpCapFlags
SMTP server capabilities.
Definition: smtp.c:82
static int smtp_authenticate(struct SmtpAccountData *adata)
Authenticate to an SMTP server.
Definition: smtp.c:944
#define SMTP_ERR_READ
Definition: smtp.c:67
bool smtp_auth_is_valid(const char *authenticator)
Check if string is a valid smtp authentication method.
Definition: smtp.c:926
static int smtp_auth_oauth_xoauth2(struct SmtpAccountData *adata, const char *method, bool xoauth2)
Authenticate an SMTP connection using OAUTHBEARER/XOAUTH2.
Definition: smtp.c:732
static const struct SmtpAuth SmtpAuthenticators[]
Accepted authentication methods.
Definition: smtp.c:903
static bool valid_smtp_code(char *buf, int *n)
Is the is a valid SMTP return code?
Definition: smtp.c:129
#define SMTP_AUTH_UNAVAIL
Definition: smtp.c:75
static int smtp_helo(struct SmtpAccountData *adata, bool esmtp)
Say hello to an SMTP Server.
Definition: smtp.c:414
#define SMTP_ERR_CODE
Definition: smtp.c:69
#define SMTP_CAP_EIGHTBITMIME
Server supports 8-bit MIME content.
Definition: smtp.c:87
#define smtp_success(x)
Definition: smtp.c:63
#define SMTP_AUTH_FAIL
Definition: smtp.c:76
#define SMTP_CAP_AUTH
Server supports AUTH command.
Definition: smtp.c:85
static int smtp_data(struct SmtpAccountData *adata, const char *msgfile)
Send data to an SMTP server.
Definition: smtp.c:240
#define SMTP_ERR_WRITE
Definition: smtp.c:68
static int smtp_fill_account(struct SmtpAccountData *adata, struct ConnAccount *cac)
Create ConnAccount object from SMTP Url.
Definition: smtp.c:358
#define SMTP_AUTH_SUCCESS
Definition: smtp.c:74
#define SMTP_CAP_SMTPUTF8
Server accepts UTF-8 strings.
Definition: smtp.c:88
#define SMTP_CONTINUE
Definition: smtp.c:65
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:1099
#define SMTP_CAP_DSN
Server supports Delivery Status Notification.
Definition: smtp.c:86
static int smtp_rcpt_to(struct SmtpAccountData *adata, const struct AddressList *al)
Set the recipient to an Address.
Definition: smtp.c:198
static int smtp_open(struct SmtpAccountData *adata, bool esmtp)
Open an SMTP Connection.
Definition: smtp.c:1017
#define SMTP_PORT
Definition: smtp.c:71
#define SMTP_READY
Definition: smtp.c:64
Send email to an SMTP server.
int mutt_socket_close(struct Connection *conn)
Close a socket.
Definition: socket.c:100
void mutt_socket_empty(struct Connection *conn)
Clear out any queued data.
Definition: socket.c:306
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:238
#define MUTT_SOCK_LOG_FULL
Definition: socket.h:54
#define mutt_socket_readln(buf, buflen, conn)
Definition: socket.h:56
#define mutt_socket_send(conn, buf)
Definition: socket.h:57
#define mutt_socket_buffer_readln(buf, conn)
Definition: socket.h:61
#define mutt_socket_send_d(conn, buf, dbg)
Definition: socket.h:58
#define NONULL(x)
Definition: string2.h:37
An email address.
Definition: address.h:36
bool group
Group mailbox?
Definition: address.h:39
struct Buffer * mailbox
Mailbox and host address.
Definition: address.h:38
String manipulation buffer.
Definition: buffer.h:36
char * data
Pointer to data.
Definition: buffer.h:37
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)
Definition: connaccount.h:70
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:72
unsigned short port
Port to connect to.
Definition: connaccount.h:58
unsigned int ssf
Security strength factor, in bits (see notes)
Definition: connection.h:50
struct ConnAccount account
Account details: username, password, etc.
Definition: connection.h:49
A List node for strings.
Definition: list.h:37
char * data
String.
Definition: list.h:38
Container for Accounts, Notifications.
Definition: neomutt.h:42
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:46
String list.
Definition: slist.h:37
struct ListHead head
List containing values.
Definition: slist.h:38
size_t count
Number of values in list.
Definition: slist.h:39
Server connection data.
Definition: smtp.c:96
const char * fqdn
Fully-qualified domain name.
Definition: smtp.c:101
struct ConfigSubset * sub
Config scope.
Definition: smtp.c:100
struct Connection * conn
Server Connection.
Definition: smtp.c:99
const char * auth_mechs
Allowed authorisation mechanisms.
Definition: smtp.c:97
SmtpCapFlags capabilities
Server capabilities.
Definition: smtp.c:98
SMTP authentication multiplexor.
Definition: smtp.c:108
int(* authenticate)(struct SmtpAccountData *adata, const char *method)
Definition: smtp.c:117
const char * method
Name of authentication method supported, NULL means variable.
Definition: smtp.c:119
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:239
void url_free(struct Url **ptr)
Free the contents of a URL.
Definition: url.c:124
@ U_SMTPS
Url is smtps://.
Definition: url.h:44
@ U_SMTP
Url is smtp://.
Definition: url.h:43