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