NeoMutt  2022-04-29-249-gaae397
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 <unistd.h>
41#include "mutt/lib.h"
42#include "address/lib.h"
43#include "config/lib.h"
44#include "email/lib.h"
45#include "conn/lib.h"
46#include "smtp.h"
47#include "lib.h"
48#include "progress/lib.h"
49#include "question/lib.h"
50#include "mutt_account.h"
51#include "mutt_globals.h"
52#include "mutt_socket.h"
53#ifdef USE_SASL_GNU
54#include <gsasl.h>
55#include "options.h"
56#endif
57#ifdef USE_SASL_CYRUS
58#include <sasl/sasl.h>
59#include <sasl/saslutil.h>
60#include "options.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
90#define SMTP_CAP_ALL ((1 << 5) - 1)
91// clang-format on
92
97{
98 const char *auth_mechs;
100 struct Connection *conn;
102 const char *fqdn;
103};
104
109{
116 int (*authenticate)(struct SmtpAccountData *adata, const char *method);
117
118 const char *method;
120};
121
129static bool valid_smtp_code(char *buf, size_t buflen, 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"))
158 else if ((plen = mutt_istr_startswith(s, "AUTH ")))
159 {
160 adata->capabilities |= SMTP_CAP_AUTH;
161 FREE(&adata->auth_mechs);
162 adata->auth_mechs = mutt_str_dup(s + plen);
163 }
164 else if (mutt_istr_startswith(s, "DSN"))
165 adata->capabilities |= SMTP_CAP_DSN;
166 else if (mutt_istr_startswith(s, "STARTTLS"))
168 else if (mutt_istr_startswith(s, "SMTPUTF8"))
170
171 if (!valid_smtp_code(buf, n, &n))
172 return SMTP_ERR_CODE;
173
174 } while (buf[3] == '-');
175
176 if (smtp_success(n) || (n == SMTP_CONTINUE))
177 return 0;
178
179 mutt_error(_("SMTP session failed: %s"), buf);
180 return -1;
181}
182
190static int smtp_rcpt_to(struct SmtpAccountData *adata, const struct AddressList *al)
191{
192 if (!al)
193 return 0;
194
195 const char *const c_dsn_notify = cs_subset_string(adata->sub, "dsn_notify");
196
197 struct Address *a = NULL;
198 TAILQ_FOREACH(a, al, entries)
199 {
200 /* weed out group mailboxes, since those are for display only */
201 if (!a->mailbox || a->group)
202 {
203 continue;
204 }
205 char buf[1024] = { 0 };
206 if ((adata->capabilities & SMTP_CAP_DSN) && c_dsn_notify)
207 snprintf(buf, sizeof(buf), "RCPT TO:<%s> NOTIFY=%s\r\n", a->mailbox, c_dsn_notify);
208 else
209 snprintf(buf, sizeof(buf), "RCPT TO:<%s>\r\n", a->mailbox);
210 if (mutt_socket_send(adata->conn, buf) == -1)
211 return SMTP_ERR_WRITE;
212 int rc = smtp_get_resp(adata);
213 if (rc != 0)
214 return rc;
215 }
216
217 return 0;
218}
219
227static int smtp_data(struct SmtpAccountData *adata, const char *msgfile)
228{
229 char buf[1024] = { 0 };
230 struct Progress *progress = NULL;
231 int rc = SMTP_ERR_WRITE;
232 int term = 0;
233 size_t buflen = 0;
234
235 FILE *fp = fopen(msgfile, "r");
236 if (!fp)
237 {
238 mutt_error(_("SMTP session failed: unable to open %s"), msgfile);
239 return -1;
240 }
241 const long size = mutt_file_get_size_fp(fp);
242 if (size == 0)
243 {
244 mutt_file_fclose(&fp);
245 return -1;
246 }
247 unlink(msgfile);
248 progress = progress_new(_("Sending message..."), MUTT_PROGRESS_NET, size);
249
250 snprintf(buf, sizeof(buf), "DATA\r\n");
251 if (mutt_socket_send(adata->conn, buf) == -1)
252 {
253 mutt_file_fclose(&fp);
254 goto done;
255 }
256 rc = smtp_get_resp(adata);
257 if (rc != 0)
258 {
259 mutt_file_fclose(&fp);
260 goto done;
261 }
262
263 rc = SMTP_ERR_WRITE;
264 while (fgets(buf, sizeof(buf) - 1, fp))
265 {
266 buflen = mutt_str_len(buf);
267 term = buflen && buf[buflen - 1] == '\n';
268 if (term && ((buflen == 1) || (buf[buflen - 2] != '\r')))
269 snprintf(buf + buflen - 1, sizeof(buf) - buflen + 1, "\r\n");
270 if (buf[0] == '.')
271 {
272 if (mutt_socket_send_d(adata->conn, ".", MUTT_SOCK_LOG_FULL) == -1)
273 {
274 mutt_file_fclose(&fp);
275 goto done;
276 }
277 }
278 if (mutt_socket_send_d(adata->conn, buf, MUTT_SOCK_LOG_FULL) == -1)
279 {
280 mutt_file_fclose(&fp);
281 goto done;
282 }
283 progress_update(progress, MAX(0, ftell(fp)), -1);
284 }
285 if (!term && buflen &&
286 (mutt_socket_send_d(adata->conn, "\r\n", MUTT_SOCK_LOG_FULL) == -1))
287 {
288 mutt_file_fclose(&fp);
289 goto done;
290 }
291 mutt_file_fclose(&fp);
292
293 /* terminate the message body */
294 if (mutt_socket_send(adata->conn, ".\r\n") == -1)
295 goto done;
296
297 rc = smtp_get_resp(adata);
298
299done:
300 progress_free(&progress);
301 return rc;
302}
303
307static const char *smtp_get_field(enum ConnAccountField field, void *gf_data)
308{
309 struct SmtpAccountData *adata = gf_data;
310 if (!adata)
311 return NULL;
312
313 switch (field)
314 {
315 case MUTT_CA_LOGIN:
316 case MUTT_CA_USER:
317 {
318 const char *const c_smtp_user = cs_subset_string(adata->sub, "smtp_user");
319 return c_smtp_user;
320 }
321 case MUTT_CA_PASS:
322 {
323 const char *const c_smtp_pass = cs_subset_string(adata->sub, "smtp_pass");
324 return c_smtp_pass;
325 }
327 {
328 const char *const c_smtp_oauth_refresh_command = cs_subset_string(adata->sub, "smtp_oauth_refresh_command");
329 return c_smtp_oauth_refresh_command;
330 }
331 case MUTT_CA_HOST:
332 default:
333 return NULL;
334 }
335}
336
344static int smtp_fill_account(struct SmtpAccountData *adata, struct ConnAccount *cac)
345{
346 cac->flags = 0;
347 cac->port = 0;
349 cac->service = "smtp";
351 cac->gf_data = adata;
352
353 const char *const c_smtp_url = cs_subset_string(adata->sub, "smtp_url");
354
355 struct Url *url = url_parse(c_smtp_url);
356 if (!url || ((url->scheme != U_SMTP) && (url->scheme != U_SMTPS)) ||
357 !url->host || (mutt_account_fromurl(cac, url) < 0))
358 {
359 url_free(&url);
360 mutt_error(_("Invalid SMTP URL: %s"), c_smtp_url);
361 return -1;
362 }
363
364 if (url->scheme == U_SMTPS)
365 cac->flags |= MUTT_ACCT_SSL;
366
367 if (cac->port == 0)
368 {
369 if (cac->flags & MUTT_ACCT_SSL)
370 cac->port = SMTPS_PORT;
371 else
372 {
373 static unsigned short SmtpPort = 0;
374 if (SmtpPort == 0)
375 {
376 struct servent *service = getservbyname("smtp", "tcp");
377 if (service)
378 SmtpPort = ntohs(service->s_port);
379 else
380 SmtpPort = SMTP_PORT;
381 mutt_debug(LL_DEBUG3, "Using default SMTP port %d\n", SmtpPort);
382 }
383 cac->port = SmtpPort;
384 }
385 }
386
387 url_free(&url);
388 return 0;
389}
390
398static int smtp_helo(struct SmtpAccountData *adata, bool esmtp)
399{
401
402 if (!esmtp)
403 {
404 /* if TLS or AUTH are requested, use EHLO */
405 if (adata->conn->account.flags & MUTT_ACCT_USER)
406 esmtp = true;
407#ifdef USE_SSL
408 const bool c_ssl_force_tls = cs_subset_bool(adata->sub, "ssl_force_tls");
409 const enum QuadOption c_ssl_starttls = cs_subset_quad(adata->sub, "ssl_starttls");
410
411 if (c_ssl_force_tls || (c_ssl_starttls != MUTT_NO))
412 esmtp = true;
413#endif
414 }
415
416 char buf[1024] = { 0 };
417 snprintf(buf, sizeof(buf), "%s %s\r\n", esmtp ? "EHLO" : "HELO", adata->fqdn);
418 /* XXX there should probably be a wrapper in mutt_socket.c that
419 * repeatedly calls adata->conn->write until all data is sent. This
420 * currently doesn't check for a short write. */
421 if (mutt_socket_send(adata->conn, buf) == -1)
422 return SMTP_ERR_WRITE;
423 return smtp_get_resp(adata);
424}
425
426#ifdef USE_SASL_GNU
439static int smtp_code(const char *str, size_t len, int *n)
440{
441 char code[4];
442
443 if (len < 4)
444 return false;
445 code[0] = str[0];
446 code[1] = str[1];
447 code[2] = str[2];
448 code[3] = 0;
449
450 const char *end = mutt_str_atoi(code, n);
451 if (!end || (*end != '\0'))
452 return false;
453 return true;
454}
455
467static int smtp_get_auth_response(struct Connection *conn, struct Buffer *input_buf,
468 int *smtp_rc, struct Buffer *response_buf)
469{
470 mutt_buffer_reset(response_buf);
471 do
472 {
473 if (mutt_socket_buffer_readln(input_buf, conn) < 0)
474 return -1;
475 if (!smtp_code(mutt_buffer_string(input_buf),
476 mutt_buffer_len(input_buf) + 1 /* number of bytes */, smtp_rc))
477 {
478 return -1;
479 }
480
481 if (*smtp_rc != SMTP_READY)
482 break;
483
484 const char *smtp_response = mutt_buffer_string(input_buf) + 3;
485 if (*smtp_response)
486 {
487 smtp_response++;
488 mutt_buffer_addstr(response_buf, smtp_response);
489 }
490 } while (mutt_buffer_string(input_buf)[3] == '-');
491
492 return 0;
493}
494
502static int smtp_auth_gsasl(struct SmtpAccountData *adata, const char *mechlist)
503{
504 Gsasl_session *gsasl_session = NULL;
505 struct Buffer *input_buf = NULL, *output_buf = NULL, *smtp_response_buf = NULL;
506 int rc = SMTP_AUTH_FAIL, gsasl_rc = GSASL_OK, smtp_rc;
507
508 const char *chosen_mech = mutt_gsasl_get_mech(mechlist, adata->auth_mechs);
509 if (!chosen_mech)
510 {
511 mutt_debug(LL_DEBUG2, "returned no usable mech\n");
512 return SMTP_AUTH_UNAVAIL;
513 }
514
515 mutt_debug(LL_DEBUG2, "using mech %s\n", chosen_mech);
516
517 if (mutt_gsasl_client_new(adata->conn, chosen_mech, &gsasl_session) < 0)
518 {
519 mutt_debug(LL_DEBUG1, "Error allocating GSASL connection.\n");
520 return SMTP_AUTH_UNAVAIL;
521 }
522
523 if (!OptNoCurses)
524 mutt_message(_("Authenticating (%s)..."), chosen_mech);
525
526 input_buf = mutt_buffer_pool_get();
527 output_buf = mutt_buffer_pool_get();
528 smtp_response_buf = mutt_buffer_pool_get();
529
530 mutt_buffer_printf(output_buf, "AUTH %s", chosen_mech);
531
532 /* Work around broken SMTP servers. See Debian #1010658.
533 * The msmtp source also forces IR for PLAIN because the author
534 * encountered difficulties with a server requiring it. */
535 if (mutt_str_equal(chosen_mech, "PLAIN"))
536 {
537 char *gsasl_step_output = NULL;
538 gsasl_rc = gsasl_step64(gsasl_session, "", &gsasl_step_output);
539 if (gsasl_rc != GSASL_NEEDS_MORE && gsasl_rc != GSASL_OK)
540 {
541 mutt_debug(LL_DEBUG1, "gsasl_step64() failed (%d): %s\n", gsasl_rc,
542 gsasl_strerror(gsasl_rc));
543 goto fail;
544 }
545
546 mutt_buffer_addch(output_buf, ' ');
547 mutt_buffer_addstr(output_buf, gsasl_step_output);
548 gsasl_free(gsasl_step_output);
549 }
550
551 mutt_buffer_addstr(output_buf, "\r\n");
552
553 do
554 {
555 if (mutt_socket_send(adata->conn, mutt_buffer_string(output_buf)) < 0)
556 goto fail;
557
558 if (smtp_get_auth_response(adata->conn, input_buf, &smtp_rc, smtp_response_buf) < 0)
559 goto fail;
560
561 if (smtp_rc != SMTP_READY)
562 break;
563
564 char *gsasl_step_output = NULL;
565 gsasl_rc = gsasl_step64(gsasl_session, mutt_buffer_string(smtp_response_buf),
566 &gsasl_step_output);
567 if ((gsasl_rc == GSASL_NEEDS_MORE) || (gsasl_rc == GSASL_OK))
568 {
569 mutt_buffer_strcpy(output_buf, gsasl_step_output);
570 mutt_buffer_addstr(output_buf, "\r\n");
571 gsasl_free(gsasl_step_output);
572 }
573 else
574 {
575 mutt_debug(LL_DEBUG1, "gsasl_step64() failed (%d): %s\n", gsasl_rc,
576 gsasl_strerror(gsasl_rc));
577 }
578 } while ((gsasl_rc == GSASL_NEEDS_MORE) || (gsasl_rc == GSASL_OK));
579
580 if (smtp_rc == SMTP_READY)
581 {
582 mutt_socket_send(adata->conn, "*\r\n");
583 goto fail;
584 }
585
586 if (smtp_success(smtp_rc) && (gsasl_rc == GSASL_OK))
588
589fail:
590 mutt_buffer_pool_release(&input_buf);
591 mutt_buffer_pool_release(&output_buf);
592 mutt_buffer_pool_release(&smtp_response_buf);
593 mutt_gsasl_client_finish(&gsasl_session);
594
595 if (rc == SMTP_AUTH_FAIL)
596 mutt_debug(LL_DEBUG2, "%s failed\n", chosen_mech);
597
598 return rc;
599}
600#endif
601
602#ifdef USE_SASL_CYRUS
610static int smtp_auth_sasl(struct SmtpAccountData *adata, const char *mechlist)
611{
612 sasl_conn_t *saslconn = NULL;
613 sasl_interact_t *interaction = NULL;
614 const char *mech = NULL;
615 const char *data = NULL;
616 unsigned int len;
617 char *buf = NULL;
618 size_t bufsize = 0;
619 int rc, saslrc;
620
621 if (mutt_sasl_client_new(adata->conn, &saslconn) < 0)
622 return SMTP_AUTH_FAIL;
623
624 do
625 {
626 rc = sasl_client_start(saslconn, mechlist, &interaction, &data, &len, &mech);
627 if (rc == SASL_INTERACT)
628 mutt_sasl_interact(interaction);
629 } while (rc == SASL_INTERACT);
630
631 if ((rc != SASL_OK) && (rc != SASL_CONTINUE))
632 {
633 mutt_debug(LL_DEBUG2, "%s unavailable\n", NONULL(mech));
634 sasl_dispose(&saslconn);
635 return SMTP_AUTH_UNAVAIL;
636 }
637
638 if (!OptNoCurses)
639 {
640 // L10N: (%s) is the method name, e.g. Anonymous, CRAM-MD5, GSSAPI, SASL
641 mutt_message(_("Authenticating (%s)..."), mech);
642 }
643
644 bufsize = MAX((len * 2), 1024);
645 buf = mutt_mem_malloc(bufsize);
646
647 snprintf(buf, bufsize, "AUTH %s", mech);
648 if (len)
649 {
650 mutt_str_cat(buf, bufsize, " ");
651 if (sasl_encode64(data, len, buf + mutt_str_len(buf),
652 bufsize - mutt_str_len(buf), &len) != SASL_OK)
653 {
654 mutt_debug(LL_DEBUG1, "#1 error base64-encoding client response\n");
655 goto fail;
656 }
657 }
658 mutt_str_cat(buf, bufsize, "\r\n");
659
660 do
661 {
662 if (mutt_socket_send(adata->conn, buf) < 0)
663 goto fail;
664 rc = mutt_socket_readln_d(buf, bufsize, adata->conn, MUTT_SOCK_LOG_FULL);
665 if (rc < 0)
666 goto fail;
667 if (!valid_smtp_code(buf, rc, &rc))
668 goto fail;
669
670 if (rc != SMTP_READY)
671 break;
672
673 if (sasl_decode64(buf + 4, strlen(buf + 4), buf, bufsize - 1, &len) != SASL_OK)
674 {
675 mutt_debug(LL_DEBUG1, "error base64-decoding server response\n");
676 goto fail;
677 }
678
679 do
680 {
681 saslrc = sasl_client_step(saslconn, buf, len, &interaction, &data, &len);
682 if (saslrc == SASL_INTERACT)
683 mutt_sasl_interact(interaction);
684 } while (saslrc == SASL_INTERACT);
685
686 if (len)
687 {
688 if ((len * 2) > bufsize)
689 {
690 bufsize = len * 2;
691 mutt_mem_realloc(&buf, bufsize);
692 }
693 if (sasl_encode64(data, len, buf, bufsize, &len) != SASL_OK)
694 {
695 mutt_debug(LL_DEBUG1, "#2 error base64-encoding client response\n");
696 goto fail;
697 }
698 }
699 mutt_str_copy(buf + len, "\r\n", bufsize - len);
700 } while (rc == SMTP_READY && saslrc != SASL_FAIL);
701
702 if (smtp_success(rc))
703 {
704 mutt_sasl_setup_conn(adata->conn, saslconn);
705 FREE(&buf);
706 return SMTP_AUTH_SUCCESS;
707 }
708
709fail:
710 sasl_dispose(&saslconn);
711 FREE(&buf);
712 return SMTP_AUTH_FAIL;
713}
714#endif
715
723static int smtp_auth_oauth_xoauth2(struct SmtpAccountData *adata, const char *method, bool xoauth2)
724{
725 (void) method; // This is OAUTHBEARER
726 const char *authtype = xoauth2 ? "XOAUTH2" : "OAUTHBEARER";
727
728 // L10N: (%s) is the method name, e.g. Anonymous, CRAM-MD5, GSSAPI, SASL
729 mutt_message(_("Authenticating (%s)..."), authtype);
730
731 /* We get the access token from the smtp_oauth_refresh_command */
732 char *oauthbearer = mutt_account_getoauthbearer(&adata->conn->account, xoauth2);
733 if (!oauthbearer)
734 return SMTP_AUTH_FAIL;
735
736 size_t ilen = strlen(oauthbearer) + 30;
737 char *ibuf = mutt_mem_malloc(ilen);
738 snprintf(ibuf, ilen, "AUTH %s %s\r\n", authtype, oauthbearer);
739
740 int rc = mutt_socket_send(adata->conn, ibuf);
741 FREE(&oauthbearer);
742 FREE(&ibuf);
743
744 if (rc == -1)
745 return SMTP_AUTH_FAIL;
746 if (smtp_get_resp(adata) != 0)
747 return SMTP_AUTH_FAIL;
748
749 return SMTP_AUTH_SUCCESS;
750}
751
758static int smtp_auth_oauth(struct SmtpAccountData *adata, const char *method)
759{
760 return smtp_auth_oauth_xoauth2(adata, method, false);
761}
762
769static int smtp_auth_xoauth2(struct SmtpAccountData *adata, const char *method)
770{
771 return smtp_auth_oauth_xoauth2(adata, method, true);
772}
773
781static int smtp_auth_plain(struct SmtpAccountData *adata, const char *method)
782{
783 (void) method; // This is PLAIN
784
785 char buf[1024] = { 0 };
786
787 /* Get username and password. Bail out of any can't be retrieved. */
788 if ((mutt_account_getuser(&adata->conn->account) < 0) ||
789 (mutt_account_getpass(&adata->conn->account) < 0))
790 {
791 goto error;
792 }
793
794 /* Build the initial client response. */
795 size_t len = mutt_sasl_plain_msg(buf, sizeof(buf), "AUTH PLAIN",
796 adata->conn->account.user,
797 adata->conn->account.user,
798 adata->conn->account.pass);
799
800 /* Terminate as per SMTP protocol. Bail out if there's no room left. */
801 if (snprintf(buf + len, sizeof(buf) - len, "\r\n") != 2)
802 {
803 goto error;
804 }
805
806 /* Send request, receive response (with a check for OK code). */
807 if ((mutt_socket_send(adata->conn, buf) < 0) || smtp_get_resp(adata))
808 {
809 goto error;
810 }
811
812 /* If we got here, auth was successful. */
813 return 0;
814
815error:
816 // L10N: %s is the method name, e.g. Anonymous, CRAM-MD5, GSSAPI, SASL
817 mutt_error(_("%s authentication failed"), "SASL");
818 return -1;
819}
820
828static int smtp_auth_login(struct SmtpAccountData *adata, const char *method)
829{
830 (void) method; // This is LOGIN
831
832 char b64[1024] = { 0 };
833 char buf[1024] = { 0 };
834
835 /* Get username and password. Bail out of any can't be retrieved. */
836 if ((mutt_account_getuser(&adata->conn->account) < 0) ||
837 (mutt_account_getpass(&adata->conn->account) < 0))
838 {
839 goto error;
840 }
841
842 /* Send the AUTH LOGIN request. */
843 if (mutt_socket_send(adata->conn, "AUTH LOGIN\r\n") < 0)
844 {
845 goto error;
846 }
847
848 /* Read the 334 VXNlcm5hbWU6 challenge ("Username:" base64-encoded) */
849 mutt_socket_readln_d(buf, sizeof(buf), adata->conn, MUTT_SOCK_LOG_FULL);
850 if (!mutt_str_equal(buf, "334 VXNlcm5hbWU6"))
851 {
852 goto error;
853 }
854
855 /* Send the username */
856 size_t len = snprintf(buf, sizeof(buf), "%s", adata->conn->account.user);
857 mutt_b64_encode(buf, len, b64, sizeof(b64));
858 snprintf(buf, sizeof(buf), "%s\r\n", b64);
859 if (mutt_socket_send(adata->conn, buf) < 0)
860 {
861 goto error;
862 }
863
864 /* Read the 334 UGFzc3dvcmQ6 challenge ("Password:" base64-encoded) */
865 mutt_socket_readln_d(buf, sizeof(buf), adata->conn, MUTT_SOCK_LOG_FULL);
866 if (!mutt_str_equal(buf, "334 UGFzc3dvcmQ6"))
867 {
868 goto error;
869 }
870
871 /* Send the password */
872 len = snprintf(buf, sizeof(buf), "%s", adata->conn->account.pass);
873 mutt_b64_encode(buf, len, b64, sizeof(b64));
874 snprintf(buf, sizeof(buf), "%s\r\n", b64);
875 if (mutt_socket_send(adata->conn, buf) < 0)
876 {
877 goto error;
878 }
879
880 /* Check the final response */
881 if (smtp_get_resp(adata) < 0)
882 {
883 goto error;
884 }
885
886 /* If we got here, auth was successful. */
887 return 0;
888
889error:
890 // L10N: %s is the method name, e.g. Anonymous, CRAM-MD5, GSSAPI, SASL
891 mutt_error(_("%s authentication failed"), "LOGIN");
892 return -1;
893}
894
898static const struct SmtpAuth SmtpAuthenticators[] = {
899 // clang-format off
900 { smtp_auth_oauth, "oauthbearer" },
901 { smtp_auth_xoauth2, "xoauth2" },
902 { smtp_auth_plain, "plain" },
903 { smtp_auth_login, "login" },
904#ifdef USE_SASL_CYRUS
905 { smtp_auth_sasl, NULL },
906#endif
907#ifdef USE_SASL_GNU
908 { smtp_auth_gsasl, NULL },
909#endif
910 // clang-format on
911};
912
921bool smtp_auth_is_valid(const char *authenticator)
922{
923 for (size_t i = 0; i < mutt_array_size(SmtpAuthenticators); i++)
924 {
925 const struct SmtpAuth *auth = &SmtpAuthenticators[i];
926 if (auth->method && mutt_istr_equal(auth->method, authenticator))
927 return true;
928 }
929
930 return false;
931}
932
939static int smtp_authenticate(struct SmtpAccountData *adata)
940{
941 int r = SMTP_AUTH_UNAVAIL;
942
943 const struct Slist *c_smtp_authenticators = cs_subset_slist(adata->sub, "smtp_authenticators");
944 if (c_smtp_authenticators && (c_smtp_authenticators->count > 0))
945 {
946 mutt_debug(LL_DEBUG2, "Trying user-defined smtp_authenticators\n");
947
948 /* Try user-specified list of authentication methods */
949 struct ListNode *np = NULL;
950 STAILQ_FOREACH(np, &c_smtp_authenticators->head, entries)
951 {
952 mutt_debug(LL_DEBUG2, "Trying method %s\n", np->data);
953
954 for (size_t i = 0; i < mutt_array_size(SmtpAuthenticators); i++)
955 {
956 const struct SmtpAuth *auth = &SmtpAuthenticators[i];
957 if (!auth->method || mutt_istr_equal(auth->method, np->data))
958 {
959 r = auth->authenticate(adata, np->data);
960 if (r == SMTP_AUTH_SUCCESS)
961 return r;
962 }
963 }
964 }
965 }
966 else
967 {
968 /* Fall back to default: any authenticator */
969 mutt_debug(LL_DEBUG2, "Falling back to smtp_auth_sasl, if using sasl.\n");
970
971#if defined(USE_SASL_CYRUS)
972 r = smtp_auth_sasl(adata, adata->auth_mechs);
973#elif defined(USE_SASL_GNU)
974 r = smtp_auth_gsasl(adata, adata->auth_mechs);
975#else
976 mutt_error(_("SMTP authentication requires SASL"));
978#endif
979 }
980
981 if (r != SMTP_AUTH_SUCCESS)
983
984 if (r == SMTP_AUTH_FAIL)
985 {
986 // L10N: %s is the method name, e.g. Anonymous, CRAM-MD5, GSSAPI, SASL
987 mutt_error(_("%s authentication failed"), "SASL");
988 }
989 else if (r == SMTP_AUTH_UNAVAIL)
990 {
991 mutt_error(_("No authenticators available"));
992 }
993
994 return (r == SMTP_AUTH_SUCCESS) ? 0 : -1;
995}
996
1004static int smtp_open(struct SmtpAccountData *adata, bool esmtp)
1005{
1006 int rc;
1007
1008 if (mutt_socket_open(adata->conn))
1009 return -1;
1010
1011 const bool force_auth = cs_subset_string(adata->sub, "smtp_user");
1012 esmtp |= force_auth;
1013
1014 /* get greeting string */
1015 rc = smtp_get_resp(adata);
1016 if (rc != 0)
1017 return rc;
1018
1019 rc = smtp_helo(adata, esmtp);
1020 if (rc != 0)
1021 return rc;
1022
1023#ifdef USE_SSL
1024 const bool c_ssl_force_tls = cs_subset_bool(adata->sub, "ssl_force_tls");
1025 const enum QuadOption c_ssl_starttls = cs_subset_quad(adata->sub, "ssl_starttls");
1026 enum QuadOption ans = MUTT_NO;
1027 if (adata->conn->ssf != 0)
1028 ans = MUTT_NO;
1029 else if (c_ssl_force_tls)
1030 ans = MUTT_YES;
1031 else if ((adata->capabilities & SMTP_CAP_STARTTLS) &&
1032 ((ans = query_quadoption(c_ssl_starttls, _("Secure connection with TLS?"))) == MUTT_ABORT))
1033 {
1034 return -1;
1035 }
1036
1037 if (ans == MUTT_YES)
1038 {
1039 if (mutt_socket_send(adata->conn, "STARTTLS\r\n") < 0)
1040 return SMTP_ERR_WRITE;
1041 rc = smtp_get_resp(adata);
1042 // Clear any data after the STARTTLS acknowledgement
1043 mutt_socket_empty(adata->conn);
1044 if (rc != 0)
1045 return rc;
1046
1047 if (mutt_ssl_starttls(adata->conn))
1048 {
1049 mutt_error(_("Could not negotiate TLS connection"));
1050 return -1;
1051 }
1052
1053 /* re-EHLO to get authentication mechanisms */
1054 rc = smtp_helo(adata, esmtp);
1055 if (rc != 0)
1056 return rc;
1057 }
1058#endif
1059
1060 if (force_auth || adata->conn->account.flags & MUTT_ACCT_USER)
1061 {
1062 if (!(adata->capabilities & SMTP_CAP_AUTH))
1063 {
1064 mutt_error(_("SMTP server does not support authentication"));
1065 return -1;
1066 }
1067
1068 return smtp_authenticate(adata);
1069 }
1070
1071 return 0;
1072}
1073
1086int mutt_smtp_send(const struct AddressList *from, const struct AddressList *to,
1087 const struct AddressList *cc, const struct AddressList *bcc,
1088 const char *msgfile, bool eightbit, struct ConfigSubset *sub)
1089{
1090 struct SmtpAccountData adata = { 0 };
1091 struct ConnAccount cac = { { 0 } };
1092 const char *envfrom = NULL;
1093 char buf[1024] = { 0 };
1094 int rc = -1;
1095
1096 adata.sub = sub;
1097 adata.fqdn = mutt_fqdn(false, adata.sub);
1098 if (!adata.fqdn)
1099 adata.fqdn = NONULL(ShortHostname);
1100
1101 const struct Address *c_envelope_from_address = cs_subset_address(adata.sub, "envelope_from_address");
1102
1103 /* it might be better to synthesize an envelope from from user and host
1104 * but this condition is most likely arrived at accidentally */
1105 if (c_envelope_from_address)
1106 envfrom = c_envelope_from_address->mailbox;
1107 else if (from && !TAILQ_EMPTY(from))
1108 envfrom = TAILQ_FIRST(from)->mailbox;
1109 else
1110 {
1111 mutt_error(_("No from address given"));
1112 return -1;
1113 }
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 const char *const c_dsn_return = cs_subset_string(adata.sub, "dsn_return");
1123
1124 do
1125 {
1126 /* send our greeting */
1127 rc = smtp_open(&adata, eightbit);
1128 if (rc != 0)
1129 break;
1130 FREE(&adata.auth_mechs);
1131
1132 /* send the sender's address */
1133 int len = snprintf(buf, sizeof(buf), "MAIL FROM:<%s>", envfrom);
1134 if (eightbit && (adata.capabilities & SMTP_CAP_EIGHTBITMIME))
1135 {
1136 mutt_strn_cat(buf, sizeof(buf), " BODY=8BITMIME", 15);
1137 len += 14;
1138 }
1139 if (c_dsn_return && (adata.capabilities & SMTP_CAP_DSN))
1140 len += snprintf(buf + len, sizeof(buf) - len, " RET=%s", c_dsn_return);
1141 if ((adata.capabilities & SMTP_CAP_SMTPUTF8) &&
1144 {
1145 snprintf(buf + len, sizeof(buf) - len, " SMTPUTF8");
1146 }
1147 mutt_strn_cat(buf, sizeof(buf), "\r\n", 3);
1148 if (mutt_socket_send(adata.conn, buf) == -1)
1149 {
1150 rc = SMTP_ERR_WRITE;
1151 break;
1152 }
1153 rc = smtp_get_resp(&adata);
1154 if (rc != 0)
1155 break;
1156
1157 /* send the recipient list */
1158 if ((rc = smtp_rcpt_to(&adata, to)) || (rc = smtp_rcpt_to(&adata, cc)) ||
1159 (rc = smtp_rcpt_to(&adata, bcc)))
1160 {
1161 break;
1162 }
1163
1164 /* send the message data */
1165 rc = smtp_data(&adata, msgfile);
1166 if (rc != 0)
1167 break;
1168
1169 mutt_socket_send(adata.conn, "QUIT\r\n");
1170
1171 rc = 0;
1172 } while (false);
1173
1174 mutt_socket_close(adata.conn);
1175 FREE(&adata.conn);
1176
1177 if (rc == SMTP_ERR_READ)
1178 mutt_error(_("SMTP session failed: read error"));
1179 else if (rc == SMTP_ERR_WRITE)
1180 mutt_error(_("SMTP session failed: write error"));
1181 else if (rc == SMTP_ERR_CODE)
1182 mutt_error(_("Invalid server response"));
1183
1184 return rc;
1185}
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
size_t mutt_buffer_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition: buffer.c:327
size_t mutt_buffer_len(const struct Buffer *buf)
Calculate the length of a Buffer.
Definition: buffer.c:371
size_t mutt_buffer_addch(struct Buffer *buf, char c)
Add a single character to a Buffer.
Definition: buffer.c:248
size_t mutt_buffer_addstr(struct Buffer *buf, const char *s)
Add a string to a Buffer.
Definition: buffer.c:233
int mutt_buffer_printf(struct Buffer *buf, const char *fmt,...)
Format a string overwriting a Buffer.
Definition: buffer.c:168
void mutt_buffer_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition: buffer.c:85
static const char * mutt_buffer_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition: buffer.h:77
const char * cs_subset_string(const struct ConfigSubset *sub, const char *name)
Get a string config item by name.
Definition: helpers.c:317
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 struct Address * cs_subset_address(const struct ConfigSubset *sub, const char *name)
Get an Address config item by name.
Definition: helpers.c:49
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:73
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:50
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
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:1569
int mutt_ssl_starttls(struct Connection *conn)
Negotiate TLS over an already opened connection.
Definition: gnutls.c:1143
#define mutt_error(...)
Definition: logging.h:87
#define mutt_message(...)
Definition: logging.h:86
#define mutt_debug(LEVEL,...)
Definition: logging.h:84
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: 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:43
#define MAX(a, b)
Definition: memory.h:30
#define mutt_array_size(x)
Definition: memory.h:36
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:819
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:250
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:807
char * mutt_strn_cat(char *d, size_t l, const char *s, size_t sl)
Concatenate two strings.
Definition: string.c:294
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:567
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:652
size_t mutt_istr_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix, ignoring case.
Definition: string.c:239
char * mutt_str_cat(char *buf, size_t buflen, const char *s)
Concatenate two strings.
Definition: string.c:265
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:50
struct Connection * mutt_conn_find(const struct ConnAccount *cac)
Find a connection from a list.
Definition: mutt_socket.c:89
NeoMutt connections.
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
void mutt_buffer_pool_release(struct Buffer **pbuf)
Free a Buffer from the pool.
Definition: pool.c:112
struct Buffer * mutt_buffer_pool_get(void)
Get a Buffer from the pool.
Definition: pool.c:101
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:86
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:118
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:386
#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:694
int mutt_sasl_client_new(struct Connection *conn, sasl_conn_t **saslconn)
Wrapper for sasl_client_new()
Definition: sasl.c:599
void mutt_sasl_setup_conn(struct Connection *conn, sasl_conn_t *saslconn)
Set up an SASL connection.
Definition: sasl.c:731
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:698
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:939
#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:921
static int smtp_auth_oauth_xoauth2(struct SmtpAccountData *adata, const char *method, bool xoauth2)
Authenticate an SMTP connection using OAUTHBEARER/XOAUTH2.
Definition: smtp.c:723
static const struct SmtpAuth SmtpAuthenticators[]
Accepted authentication methods.
Definition: smtp.c:898
#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:398
#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:227
#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:344
#define SMTP_AUTH_SUCCESS
Definition: smtp.c:74
static const char * smtp_get_field(enum ConnAccountField field, void *gf_data)
Get connection login credentials - Implements ConnAccount::get_field()
Definition: smtp.c:307
static int smtp_auth_xoauth2(struct SmtpAccountData *adata, const char *method)
Authenticate an SMTP connection using XOAUTH2.
Definition: smtp.c:769
#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:1086
#define SMTP_CAP_DSN
Server supports Delivery Status Notification.
Definition: smtp.c:86
static int smtp_auth_login(struct SmtpAccountData *adata, const char *method)
Authenticate using plain text.
Definition: smtp.c:828
static int smtp_auth_plain(struct SmtpAccountData *adata, const char *method)
Authenticate using plain text.
Definition: smtp.c:781
static bool valid_smtp_code(char *buf, size_t buflen, int *n)
Is the is a valid SMTP return code?
Definition: smtp.c:129
static int smtp_auth_oauth(struct SmtpAccountData *adata, const char *method)
Authenticate an SMTP connection using OAUTHBEARER.
Definition: smtp.c:758
static int smtp_rcpt_to(struct SmtpAccountData *adata, const struct AddressList *al)
Set the recipient to an Address.
Definition: smtp.c:190
static int smtp_open(struct SmtpAccountData *adata, bool esmtp)
Open an SMTP Connection.
Definition: smtp.c:1004
#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:98
void mutt_socket_empty(struct Connection *conn)
Clear out any queued data.
Definition: socket.c:314
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:247
#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
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
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
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:97
const char * fqdn
Fully-qualified domain name.
Definition: smtp.c:102
struct ConfigSubset * sub
Config scope.
Definition: smtp.c:101
struct Connection * conn
Server Connection.
Definition: smtp.c:100
const char * auth_mechs
Allowed authorisation mechanisms.
Definition: smtp.c:98
SmtpCapFlags capabilities
Server capabilities.
Definition: smtp.c:99
SMTP authentication multiplexor.
Definition: smtp.c:109
int(* authenticate)(struct SmtpAccountData *adata, const char *method)
Authenticate an SMTP connection.
Definition: smtp.c:116
const char * method
Name of authentication method supported, NULL means variable.
Definition: smtp.c:118
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