NeoMutt  2025-12-11-872-g385a04
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
smtp.c File Reference

Send email to an SMTP server. More...

#include "config.h"
#include <arpa/inet.h>
#include <netdb.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <unistd.h>
#include "mutt/lib.h"
#include "address/lib.h"
#include "config/lib.h"
#include "email/lib.h"
#include "core/lib.h"
#include "conn/lib.h"
#include "smtp.h"
#include "progress/lib.h"
#include "question/lib.h"
#include "globals.h"
#include "mutt_socket.h"
#include "muttlib.h"
#include "sendlib.h"
+ Include dependency graph for smtp.c:

Go to the source code of this file.

Data Structures

struct  SmtpAccountData
 Server connection data. More...
 
struct  SmtpAuth
 SMTP authentication multiplexor. More...
 

Macros

#define smtp_success(x)
 Check if SMTP response code indicates success (2xx codes)
 
#define SMTP_READY   334
 SMTP server ready for authentication data.
 
#define SMTP_CONTINUE   354
 SMTP server ready to accept message data.
 
#define SMTP_ERR_READ   -2
 Error reading from server.
 
#define SMTP_ERR_WRITE   -3
 Error writing to server.
 
#define SMTP_ERR_CODE   -4
 Invalid server response code.
 
#define SMTP_PORT   25
 Default SMTP port.
 
#define SMTPS_PORT   465
 Default SMTPS (SMTP over SSL) port.
 
#define SMTP_AUTH_SUCCESS   0
 Authentication completed successfully.
 
#define SMTP_AUTH_UNAVAIL   1
 Authentication method unavailable.
 
#define SMTP_AUTH_FAIL   -1
 Authentication failed.
 
#define SMTP_CAP_ALL   ((1U << 5) - 1)
 All SMTP capability flags.
 

Typedefs

typedef uint8_t SmtpCapFlags
 

Enumerations

enum  SmtpCapFlag {
  SMTP_CAP_NONE = 0 , SMTP_CAP_STARTTLS = 1U << 0 , SMTP_CAP_AUTH = 1U << 1 , SMTP_CAP_DSN = 1U << 2 ,
  SMTP_CAP_EIGHTBITMIME = 1U << 3 , SMTP_CAP_SMTPUTF8 = 1U << 4
}
 SMTP server capabilities. More...
 

Functions

static bool valid_smtp_code (char *buf, int *n)
 Is the is a valid SMTP return code?
 
static int smtp_get_resp (struct SmtpAccountData *adata)
 Read a command response from the SMTP server.
 
static int smtp_rcpt_to (struct SmtpAccountData *adata, const struct AddressList *al)
 Set the recipient to an Address.
 
static int smtp_data (struct SmtpAccountData *adata, const char *msgfile)
 Send data to an SMTP server.
 
static const char * smtp_get_field (enum ConnAccountField field, void *gf_data)
 Get connection login credentials - Implements ConnAccount::get_field() -.
 
static int smtp_fill_account (struct SmtpAccountData *adata, struct ConnAccount *cac)
 Create ConnAccount object from SMTP Url.
 
static int smtp_helo (struct SmtpAccountData *adata, bool esmtp)
 Say hello to an SMTP Server.
 
static int smtp_auth_oauth_xoauth2 (struct SmtpAccountData *adata, const char *method, bool xoauth2)
 Authenticate an SMTP connection using OAUTHBEARER/XOAUTH2.
 
static int smtp_auth_oauth (struct SmtpAccountData *adata, const char *method)
 Authenticate an SMTP connection using OAUTHBEARER - Implements SmtpAuth::authenticate() -.
 
static int smtp_auth_xoauth2 (struct SmtpAccountData *adata, const char *method)
 Authenticate an SMTP connection using XOAUTH2 - Implements SmtpAuth::authenticate() -.
 
static int smtp_auth_plain (struct SmtpAccountData *adata, const char *method)
 Authenticate using plain text - Implements SmtpAuth::authenticate() -.
 
static int smtp_auth_login (struct SmtpAccountData *adata, const char *method)
 Authenticate using plain text - Implements SmtpAuth::authenticate() -.
 
bool smtp_auth_is_valid (const char *authenticator)
 Check if string is a valid smtp authentication method.
 
static int smtp_authenticate (struct SmtpAccountData *adata)
 Authenticate to an SMTP server.
 
static int smtp_open (struct SmtpAccountData *adata, bool esmtp)
 Open an SMTP Connection.
 
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.
 

Variables

static const struct SmtpAuth SmtpAuthenticators []
 Accepted authentication methods.
 

Detailed Description

Send email to an SMTP server.

Authors
  • Richard Russon
  • Pietro Cerutti
  • Yousef Akbar
  • Ryan Kavanagh
  • Alejandro Colomar
  • Anna Figueiredo Gomes
  • Rayford Shireman

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/.

Definition in file smtp.c.

Macro Definition Documentation

◆ smtp_success

#define smtp_success ( x)
Value:
(((x) / 100) == 2)

Check if SMTP response code indicates success (2xx codes)

Definition at line 65 of file smtp.c.

◆ SMTP_READY

#define SMTP_READY   334

SMTP server ready for authentication data.

Definition at line 66 of file smtp.c.

◆ SMTP_CONTINUE

#define SMTP_CONTINUE   354

SMTP server ready to accept message data.

Definition at line 67 of file smtp.c.

◆ SMTP_ERR_READ

#define SMTP_ERR_READ   -2

Error reading from server.

Definition at line 69 of file smtp.c.

◆ SMTP_ERR_WRITE

#define SMTP_ERR_WRITE   -3

Error writing to server.

Definition at line 70 of file smtp.c.

◆ SMTP_ERR_CODE

#define SMTP_ERR_CODE   -4

Invalid server response code.

Definition at line 71 of file smtp.c.

◆ SMTP_PORT

#define SMTP_PORT   25

Default SMTP port.

Definition at line 73 of file smtp.c.

◆ SMTPS_PORT

#define SMTPS_PORT   465

Default SMTPS (SMTP over SSL) port.

Definition at line 74 of file smtp.c.

◆ SMTP_AUTH_SUCCESS

#define SMTP_AUTH_SUCCESS   0

Authentication completed successfully.

Definition at line 76 of file smtp.c.

◆ SMTP_AUTH_UNAVAIL

#define SMTP_AUTH_UNAVAIL   1

Authentication method unavailable.

Definition at line 77 of file smtp.c.

◆ SMTP_AUTH_FAIL

#define SMTP_AUTH_FAIL   -1

Authentication failed.

Definition at line 78 of file smtp.c.

◆ SMTP_CAP_ALL

#define SMTP_CAP_ALL   ((1U << 5) - 1)

All SMTP capability flags.

Definition at line 96 of file smtp.c.

Typedef Documentation

◆ SmtpCapFlags

typedef uint8_t SmtpCapFlags

Definition at line 94 of file smtp.c.

Enumeration Type Documentation

◆ SmtpCapFlag

SMTP server capabilities.

Enumerator
SMTP_CAP_NONE 

No flags are set.

SMTP_CAP_STARTTLS 

Server supports STARTTLS command.

SMTP_CAP_AUTH 

Server supports AUTH command.

SMTP_CAP_DSN 

Server supports Delivery Status Notification.

SMTP_CAP_EIGHTBITMIME 

Server supports 8-bit MIME content.

SMTP_CAP_SMTPUTF8 

Server accepts UTF-8 strings.

Definition at line 83 of file smtp.c.

84{
85 // clang-format off
86 SMTP_CAP_NONE = 0,
87 SMTP_CAP_STARTTLS = 1U << 0,
88 SMTP_CAP_AUTH = 1U << 1,
89 SMTP_CAP_DSN = 1U << 2,
90 SMTP_CAP_EIGHTBITMIME = 1U << 3,
91 SMTP_CAP_SMTPUTF8 = 1U << 4,
92 // clang-format on
93};
@ SMTP_CAP_SMTPUTF8
Server accepts UTF-8 strings.
Definition smtp.c:91
@ SMTP_CAP_EIGHTBITMIME
Server supports 8-bit MIME content.
Definition smtp.c:90
@ SMTP_CAP_DSN
Server supports Delivery Status Notification.
Definition smtp.c:89
@ SMTP_CAP_NONE
No flags are set.
Definition smtp.c:86
@ SMTP_CAP_AUTH
Server supports AUTH command.
Definition smtp.c:88
@ SMTP_CAP_STARTTLS
Server supports STARTTLS command.
Definition smtp.c:87

Function Documentation

◆ valid_smtp_code()

static bool valid_smtp_code ( char * buf,
int * n )
static

Is the is a valid SMTP return code?

Parameters
[in]bufString to check
[out]nNumeric value of code
Return values
trueValid number

Definition at line 135 of file smtp.c.

136{
137 return (mutt_str_atoi(buf, n) - buf) <= 3;
138}
const char * mutt_str_atoi(const char *str, int *dst)
Convert ASCII string to an integer.
Definition atoi.c:191
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ smtp_get_resp()

static int smtp_get_resp ( struct SmtpAccountData * adata)
static

Read a command response from the SMTP server.

Parameters
adataSMTP Account data
Return values
0Success (2xx code) or continue (354 code)
-1Write error, or any other response code

Definition at line 146 of file smtp.c.

147{
148 int n;
149 char buf[1024] = { 0 };
150
151 do
152 {
153 n = mutt_socket_readln(buf, sizeof(buf), adata->conn);
154 if (n < 4)
155 {
156 /* read error, or no response code */
157 return SMTP_ERR_READ;
158 }
159 const char *s = buf + 4; /* Skip the response code and the space/dash */
160 size_t plen;
161
162 if (mutt_istr_startswith(s, "8BITMIME"))
163 {
165 }
166 else if ((plen = mutt_istr_startswith(s, "AUTH ")))
167 {
168 adata->capabilities |= SMTP_CAP_AUTH;
169 FREE(&adata->auth_mechs);
170 adata->auth_mechs = mutt_str_dup(s + plen);
171 }
172 else if (mutt_istr_startswith(s, "DSN"))
173 {
174 adata->capabilities |= SMTP_CAP_DSN;
175 }
176 else if (mutt_istr_startswith(s, "STARTTLS"))
177 {
179 }
180 else if (mutt_istr_startswith(s, "SMTPUTF8"))
181 {
183 }
184
185 if (!valid_smtp_code(buf, &n))
186 return SMTP_ERR_CODE;
187
188 } while (buf[3] == '-');
189
190 if (smtp_success(n) || (n == SMTP_CONTINUE))
191 return 0;
192
193 mutt_error(_("SMTP session failed: %s"), buf);
194 return -1;
195}
#define mutt_error(...)
Definition logging2.h:94
#define FREE(x)
Free memory and set the pointer to NULL.
Definition memory.h:68
#define _(a)
Definition message.h:28
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition string.c:257
size_t mutt_istr_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix, ignoring case.
Definition string.c:246
#define SMTP_ERR_READ
Error reading from server.
Definition smtp.c:69
static bool valid_smtp_code(char *buf, int *n)
Is the is a valid SMTP return code?
Definition smtp.c:135
#define SMTP_ERR_CODE
Invalid server response code.
Definition smtp.c:71
#define smtp_success(x)
Check if SMTP response code indicates success (2xx codes)
Definition smtp.c:65
#define SMTP_CONTINUE
SMTP server ready to accept message data.
Definition smtp.c:67
#define mutt_socket_readln(buf, buflen, conn)
Definition socket.h:55
struct Connection * conn
Server Connection.
Definition smtp.c:105
const char * auth_mechs
Allowed authorisation mechanisms.
Definition smtp.c:103
SmtpCapFlags capabilities
Server capabilities.
Definition smtp.c:104
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ smtp_rcpt_to()

static int smtp_rcpt_to ( struct SmtpAccountData * adata,
const struct AddressList * al )
static

Set the recipient to an Address.

Parameters
adataSMTP Account data
alAddressList to use
Return values
0Success
<0Error, e.g. SMTP_ERR_WRITE

Definition at line 204 of file smtp.c.

205{
206 if (!al)
207 return 0;
208
209 const char *const c_dsn_notify = cs_subset_string(adata->sub, "dsn_notify");
210
211 struct Address *a = NULL;
212 TAILQ_FOREACH(a, al, entries)
213 {
214 /* weed out group mailboxes, since those are for display only */
215 if (!a->mailbox || a->group)
216 {
217 continue;
218 }
219 char buf[1024] = { 0 };
220 if ((adata->capabilities & SMTP_CAP_DSN) && c_dsn_notify)
221 {
222 snprintf(buf, sizeof(buf), "RCPT TO:<%s> NOTIFY=%s\r\n",
223 buf_string(a->mailbox), c_dsn_notify);
224 }
225 else
226 {
227 snprintf(buf, sizeof(buf), "RCPT TO:<%s>\r\n", buf_string(a->mailbox));
228 }
229 if (mutt_socket_send(adata->conn, buf) == -1)
230 return SMTP_ERR_WRITE;
231 int rc = smtp_get_resp(adata);
232 if (rc != 0)
233 {
234 mutt_sleep(2);
235 mutt_error(_("SMTP session failed: cannot add recipient <%s>"),
236 buf_string(a->mailbox));
237 return rc;
238 }
239 }
240
241 return 0;
242}
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
void mutt_sleep(short s)
Sleep for a while.
Definition muttlib.c:787
#define TAILQ_FOREACH(var, head, field)
Definition queue.h:782
static int smtp_get_resp(struct SmtpAccountData *adata)
Read a command response from the SMTP server.
Definition smtp.c:146
#define SMTP_ERR_WRITE
Error writing to server.
Definition smtp.c:70
#define mutt_socket_send(conn, buf)
Definition socket.h:56
An email address.
Definition address.h:35
bool group
Group mailbox?
Definition address.h:38
struct Buffer * mailbox
Mailbox and host address.
Definition address.h:37
struct ConfigSubset * sub
Config scope.
Definition smtp.c:106
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ smtp_data()

static int smtp_data ( struct SmtpAccountData * adata,
const char * msgfile )
static

Send data to an SMTP server.

Parameters
adataSMTP Account data
msgfileFilename containing data
Return values
0Success
<0Error, e.g. SMTP_ERR_WRITE

Definition at line 251 of file smtp.c.

252{
253 char buf[1024] = { 0 };
254 struct Progress *progress = NULL;
255 int rc = SMTP_ERR_WRITE;
256 int term = 0;
257 size_t buflen = 0;
258
259 FILE *fp = mutt_file_fopen(msgfile, "r");
260 if (!fp)
261 {
262 mutt_error(_("SMTP session failed: unable to open %s"), msgfile);
263 return -1;
264 }
265 const long size = mutt_file_get_size_fp(fp);
266 if (size == 0)
267 {
268 mutt_file_fclose(&fp);
269 return -1;
270 }
271 unlink(msgfile);
272 progress = progress_new(MUTT_PROGRESS_NET, size);
273 progress_set_message(progress, _("Sending message..."));
274
275 snprintf(buf, sizeof(buf), "DATA\r\n");
276 if (mutt_socket_send(adata->conn, buf) == -1)
277 {
278 mutt_file_fclose(&fp);
279 goto done;
280 }
281 rc = smtp_get_resp(adata);
282 if (rc != 0)
283 {
284 mutt_file_fclose(&fp);
285 goto done;
286 }
287
288 rc = SMTP_ERR_WRITE;
289 while (fgets(buf, sizeof(buf) - 1, fp))
290 {
291 buflen = mutt_str_len(buf);
292 term = buflen && buf[buflen - 1] == '\n';
293 if (term && ((buflen == 1) || (buf[buflen - 2] != '\r')))
294 snprintf(buf + buflen - 1, sizeof(buf) - buflen + 1, "\r\n");
295 if (buf[0] == '.')
296 {
297 if (mutt_socket_send_d(adata->conn, ".", MUTT_SOCK_LOG_FULL) == -1)
298 {
299 mutt_file_fclose(&fp);
300 goto done;
301 }
302 }
303 if (mutt_socket_send_d(adata->conn, buf, MUTT_SOCK_LOG_FULL) == -1)
304 {
305 mutt_file_fclose(&fp);
306 goto done;
307 }
308 progress_update(progress, MAX(0, ftell(fp)), -1);
309 }
310 if (!term && buflen &&
311 (mutt_socket_send_d(adata->conn, "\r\n", MUTT_SOCK_LOG_FULL) == -1))
312 {
313 mutt_file_fclose(&fp);
314 goto done;
315 }
316 mutt_file_fclose(&fp);
317
318 /* terminate the message body */
319 if (mutt_socket_send(adata->conn, ".\r\n") == -1)
320 goto done;
321
322 rc = smtp_get_resp(adata);
323
324done:
325 progress_free(&progress);
326 return rc;
327}
long mutt_file_get_size_fp(FILE *fp)
Get the size of a file.
Definition file.c:1432
#define mutt_file_fclose(FP)
Definition file.h:144
#define mutt_file_fopen(PATH, MODE)
Definition file.h:143
#define MAX(a, b)
Return the maximum of two values.
Definition memory.h:38
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition string.c:503
@ MUTT_PROGRESS_NET
Progress tracks bytes, according to $net_inc
Definition lib.h:83
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
#define MUTT_SOCK_LOG_FULL
Log everything including full protocol.
Definition socket.h:53
#define mutt_socket_send_d(conn, buf, dbg)
Definition socket.h:57
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ smtp_fill_account()

static int smtp_fill_account ( struct SmtpAccountData * adata,
struct ConnAccount * cac )
static

Create ConnAccount object from SMTP Url.

Parameters
adataSMTP Account data
cacConnAccount to populate
Return values
0Success
-1Error

Definition at line 369 of file smtp.c.

370{
371 cac->flags = 0;
372 cac->port = 0;
374 cac->service = "smtp";
376 cac->gf_data = adata;
377
378 const char *const c_smtp_url = cs_subset_string(adata->sub, "smtp_url");
379
380 struct Url *url = url_parse(c_smtp_url);
381 if (!url || ((url->scheme != U_SMTP) && (url->scheme != U_SMTPS)) ||
382 !url->host || (account_from_url(cac, url) < 0))
383 {
384 url_free(&url);
385 mutt_error(_("Invalid SMTP URL: %s"), c_smtp_url);
386 return -1;
387 }
388
389 if (url->scheme == U_SMTPS)
390 cac->flags |= MUTT_ACCT_SSL;
391
392 if (cac->port == 0)
393 {
394 if (cac->flags & MUTT_ACCT_SSL)
395 {
396 cac->port = SMTPS_PORT;
397 }
398 else
399 {
400 static unsigned short SmtpPort = 0;
401 if (SmtpPort == 0)
402 {
403 struct servent *service = getservbyname("smtp", "tcp");
404 if (service)
405 SmtpPort = ntohs(service->s_port);
406 else
407 SmtpPort = SMTP_PORT;
408 mutt_debug(LL_DEBUG3, "Using default SMTP port %d\n", SmtpPort);
409 }
410 cac->port = SmtpPort;
411 }
412 }
413
414 url_free(&url);
415 return 0;
416}
@ MUTT_ACCT_SSL
Account uses SSL/TLS.
Definition connaccount.h:51
static const char * smtp_get_field(enum ConnAccountField field, void *gf_data)
Get connection login credentials - Implements ConnAccount::get_field() -.
Definition smtp.c:332
#define mutt_debug(LEVEL,...)
Definition logging2.h:91
@ LL_DEBUG3
Log at debug level 3.
Definition logging2.h:47
int account_from_url(struct ConnAccount *cac, const struct Url *url)
Fill ConnAccount with information from url.
@ MUTT_ACCT_TYPE_SMTP
Smtp Account.
#define SMTPS_PORT
Default SMTPS (SMTP over SSL) port.
Definition smtp.c:74
#define SMTP_PORT
Default SMTP port.
Definition smtp.c:73
const char * service
Name of the service, e.g. "imap".
Definition connaccount.h:67
const char *(* get_field)(enum ConnAccountField field, void *gf_data)
Definition connaccount.h:76
unsigned char type
Connection type, e.g. MUTT_ACCT_TYPE_IMAP.
Definition connaccount.h:65
MuttAccountFlags flags
Which fields are initialised, e.g. MUTT_ACCT_USER.
Definition connaccount.h:66
void * gf_data
Private data to pass to get_field()
Definition connaccount.h:78
unsigned short port
Port to connect to.
Definition connaccount.h:64
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:242
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
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ smtp_helo()

static int smtp_helo ( struct SmtpAccountData * adata,
bool esmtp )
static

Say hello to an SMTP Server.

Parameters
adataSMTP Account data
esmtpIf true, use ESMTP
Return values
0Success
<0Error, e.g. SMTP_ERR_WRITE

Definition at line 425 of file smtp.c.

426{
428
429 if (!esmtp)
430 {
431 /* if TLS or AUTH are requested, use EHLO */
432 if (adata->conn->account.flags & MUTT_ACCT_USER)
433 esmtp = true;
434#ifdef USE_SSL
435 const bool c_ssl_force_tls = cs_subset_bool(adata->sub, "ssl_force_tls");
436 const enum QuadOption c_ssl_starttls = cs_subset_quad(adata->sub, "ssl_starttls");
437
438 if (c_ssl_force_tls || (c_ssl_starttls != MUTT_NO))
439 esmtp = true;
440#endif
441 }
442
443 char buf[1024] = { 0 };
444 snprintf(buf, sizeof(buf), "%s %s\r\n", esmtp ? "EHLO" : "HELO", adata->fqdn);
445 /* XXX there should probably be a wrapper in mutt_socket.c that
446 * repeatedly calls adata->conn->write until all data is sent. This
447 * currently doesn't check for a short write. */
448 if (mutt_socket_send(adata->conn, buf) == -1)
449 return SMTP_ERR_WRITE;
450 return smtp_get_resp(adata);
451}
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
@ MUTT_ACCT_USER
User field has been set.
Definition connaccount.h:48
QuadOption
Possible values for a quad-option.
Definition quad.h:36
@ MUTT_NO
User answered 'No', or assume 'No'.
Definition quad.h:38
struct ConnAccount account
Account details: username, password, etc.
Definition connection.h:49
const char * fqdn
Fully-qualified domain name.
Definition smtp.c:107
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ smtp_auth_oauth_xoauth2()

static int smtp_auth_oauth_xoauth2 ( struct SmtpAccountData * adata,
const char * method,
bool xoauth2 )
static

Authenticate an SMTP connection using OAUTHBEARER/XOAUTH2.

Parameters
adataSMTP Account data
methodAuthentication method
xoauth2Use XOAUTH2 token (if true), OAUTHBEARER token otherwise
Return values
numResult, e.g. SMTP_AUTH_SUCCESS

Definition at line 765 of file smtp.c.

766{
767 /* If they did not explicitly request or configure oauth then fail quietly */
768 const char *const c_smtp_oauth_refresh_command = cs_subset_string(NeoMutt->sub, "smtp_oauth_refresh_command");
769 if (!method && !c_smtp_oauth_refresh_command)
770 return SMTP_AUTH_UNAVAIL;
771
772 const char *authtype = xoauth2 ? "XOAUTH2" : "OAUTHBEARER";
773
774 // L10N: (%s) is the method name, e.g. Anonymous, CRAM-MD5, GSSAPI, SASL
775 mutt_message(_("Authenticating (%s)..."), authtype);
776
777 /* We get the access token from the smtp_oauth_refresh_command */
778 char *oauthbearer = mutt_account_getoauthbearer(&adata->conn->account, xoauth2);
779 if (!oauthbearer)
780 return SMTP_AUTH_FAIL;
781
782 char *ibuf = NULL;
783 mutt_str_asprintf(&ibuf, "AUTH %s %s\r\n", authtype, oauthbearer);
784
785 int rc = mutt_socket_send(adata->conn, ibuf);
786 FREE(&oauthbearer);
787 FREE(&ibuf);
788
789 if (rc == -1)
790 return SMTP_AUTH_FAIL;
791 if (smtp_get_resp(adata) != 0)
792 return SMTP_AUTH_FAIL;
793
794 return SMTP_AUTH_SUCCESS;
795}
char * mutt_account_getoauthbearer(struct ConnAccount *cac, bool xoauth2)
Get an OAUTHBEARER/XOAUTH2 token.
#define mutt_message(...)
Definition logging2.h:93
int mutt_str_asprintf(char **strp, const char *fmt,...)
Definition string.c:808
#define SMTP_AUTH_UNAVAIL
Authentication method unavailable.
Definition smtp.c:77
#define SMTP_AUTH_FAIL
Authentication failed.
Definition smtp.c:78
#define SMTP_AUTH_SUCCESS
Authentication completed successfully.
Definition smtp.c:76
Container for Accounts, Notifications.
Definition neomutt.h:41
struct ConfigSubset * sub
Inherited config items.
Definition neomutt.h:49
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ smtp_auth_is_valid()

bool smtp_auth_is_valid ( const char * authenticator)

Check if string is a valid smtp authentication method.

Parameters
authenticatorAuthenticator string to check
Return values
trueArgument is a valid auth method

Validate whether an input string is an accepted smtp authentication method as defined by SmtpAuthenticators.

Definition at line 959 of file smtp.c.

960{
961 for (size_t i = 0; i < countof(SmtpAuthenticators); i++)
962 {
963 const struct SmtpAuth *auth = &SmtpAuthenticators[i];
964 if (auth->method && mutt_istr_equal(auth->method, authenticator))
965 return true;
966 }
967
968 return false;
969}
#define countof(x)
Definition memory.h:49
bool mutt_istr_equal(const char *a, const char *b)
Compare two strings, ignoring case.
Definition string.c:677
static const struct SmtpAuth SmtpAuthenticators[]
Accepted authentication methods.
Definition smtp.c:936
SMTP authentication multiplexor.
Definition smtp.c:114
const char * method
Name of authentication method supported, NULL means variable.
Definition smtp.c:125
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ smtp_authenticate()

static int smtp_authenticate ( struct SmtpAccountData * adata)
static

Authenticate to an SMTP server.

Parameters
adataSMTP Account data
Return values
0Success
<0Error, e.g. SMTP_AUTH_FAIL

Definition at line 977 of file smtp.c.

978{
979 int r = SMTP_AUTH_UNAVAIL;
980
981 const struct Slist *c_smtp_authenticators = cs_subset_slist(adata->sub, "smtp_authenticators");
982 if (c_smtp_authenticators && (c_smtp_authenticators->count > 0))
983 {
984 mutt_debug(LL_DEBUG2, "Trying user-defined smtp_authenticators\n");
985
986 /* Try user-specified list of authentication methods */
987 struct ListNode *np = NULL;
988 STAILQ_FOREACH(np, &c_smtp_authenticators->head, entries)
989 {
990 mutt_debug(LL_DEBUG2, "Trying method %s\n", np->data);
991
992 for (size_t i = 0; i < countof(SmtpAuthenticators); i++)
993 {
994 const struct SmtpAuth *auth = &SmtpAuthenticators[i];
995 if (!auth->method || mutt_istr_equal(auth->method, np->data))
996 {
997 r = auth->authenticate(adata, np->data);
998 if (r == SMTP_AUTH_SUCCESS)
999 return r;
1000 }
1001 }
1002 }
1003 }
1004 else
1005 {
1006 /* Fall back to default: any authenticator */
1007#if defined(USE_SASL_CYRUS)
1008 mutt_debug(LL_DEBUG2, "Falling back to smtp_auth_sasl, if using sasl\n");
1009 r = smtp_auth_sasl(adata, adata->auth_mechs);
1010#elif defined(USE_SASL_GNU)
1011 mutt_debug(LL_DEBUG2, "Falling back to smtp_auth_gsasl, if using gsasl\n");
1012 r = smtp_auth_gsasl(adata, adata->auth_mechs);
1013#else
1014 mutt_debug(LL_DEBUG2, "Falling back to using any authenticator available\n");
1015 /* Try all available authentication methods */
1016 for (size_t i = 0; i < countof(SmtpAuthenticators); i++)
1017 {
1018 const struct SmtpAuth *auth = &SmtpAuthenticators[i];
1019 mutt_debug(LL_DEBUG2, "Trying method %s\n", auth->method ? auth->method : "<variable>");
1020 r = auth->authenticate(adata, auth->method);
1021 if (r == SMTP_AUTH_SUCCESS)
1022 return r;
1023 }
1024#endif
1025 }
1026
1027 if (r != SMTP_AUTH_SUCCESS)
1029
1030 if (r == SMTP_AUTH_FAIL)
1031 {
1032 // L10N: %s is the method name, e.g. Anonymous, CRAM-MD5, GSSAPI, SASL
1033 mutt_error(_("%s authentication failed"), "SASL");
1034 }
1035 else if (r == SMTP_AUTH_UNAVAIL)
1036 {
1037 mutt_error(_("No authenticators available"));
1038 }
1039
1040 return (r == SMTP_AUTH_SUCCESS) ? 0 : -1;
1041}
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
void mutt_account_unsetpass(struct ConnAccount *cac)
Unset ConnAccount's password.
@ LL_DEBUG2
Log at debug level 2.
Definition logging2.h:46
#define STAILQ_FOREACH(var, head, field)
Definition queue.h:390
A List node for strings.
Definition list.h:37
char * data
String.
Definition list.h:38
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
int(* authenticate)(struct SmtpAccountData *adata, const char *method)
Definition smtp.c:123
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ smtp_open()

static int smtp_open ( struct SmtpAccountData * adata,
bool esmtp )
static

Open an SMTP Connection.

Parameters
adataSMTP Account data
esmtpIf true, use ESMTP
Return values
0Success
-1Error

Definition at line 1050 of file smtp.c.

1051{
1052 int rc;
1053
1054 if (mutt_socket_open(adata->conn))
1055 return -1;
1056
1057 const bool force_auth = cs_subset_string(adata->sub, "smtp_user");
1058 esmtp |= force_auth;
1059
1060 /* get greeting string */
1061 rc = smtp_get_resp(adata);
1062 if (rc != 0)
1063 return rc;
1064
1065 rc = smtp_helo(adata, esmtp);
1066 if (rc != 0)
1067 return rc;
1068
1069#ifdef USE_SSL
1070 const bool c_ssl_force_tls = cs_subset_bool(adata->sub, "ssl_force_tls");
1071 enum QuadOption ans = MUTT_NO;
1072 if (adata->conn->ssf != 0)
1073 ans = MUTT_NO;
1074 else if (c_ssl_force_tls)
1075 ans = MUTT_YES;
1076 else if ((adata->capabilities & SMTP_CAP_STARTTLS) &&
1077 ((ans = query_quadoption(_("Secure connection with TLS?"),
1078 adata->sub, "ssl_starttls")) == MUTT_ABORT))
1079 {
1080 return -1;
1081 }
1082
1083 if (ans == MUTT_YES)
1084 {
1085 if (mutt_socket_send(adata->conn, "STARTTLS\r\n") < 0)
1086 return SMTP_ERR_WRITE;
1087 rc = smtp_get_resp(adata);
1088 // Clear any data after the STARTTLS acknowledgement
1089 mutt_socket_empty(adata->conn);
1090 if (rc != 0)
1091 return rc;
1092
1093 if (mutt_ssl_starttls(adata->conn))
1094 {
1095 mutt_error(_("Could not negotiate TLS connection"));
1096 return -1;
1097 }
1098
1099 /* re-EHLO to get authentication mechanisms */
1100 rc = smtp_helo(adata, esmtp);
1101 if (rc != 0)
1102 return rc;
1103 }
1104#endif
1105
1106 if (force_auth || adata->conn->account.flags & MUTT_ACCT_USER)
1107 {
1108 if (!(adata->capabilities & SMTP_CAP_AUTH))
1109 {
1110 mutt_error(_("SMTP server does not support authentication"));
1111 return -1;
1112 }
1113
1114 return smtp_authenticate(adata);
1115 }
1116
1117 return 0;
1118}
int mutt_ssl_starttls(struct Connection *conn)
Negotiate TLS over an already opened connection.
Definition gnutls.c:1172
@ MUTT_ABORT
User aborted the question (with Ctrl-G)
Definition quad.h:37
@ MUTT_YES
User answered 'Yes', or assume 'Yes'.
Definition quad.h:39
enum QuadOption query_quadoption(const char *prompt, struct ConfigSubset *sub, const char *name)
Ask the user a quad-question.
Definition question.c:384
static int smtp_authenticate(struct SmtpAccountData *adata)
Authenticate to an SMTP server.
Definition smtp.c:977
static int smtp_helo(struct SmtpAccountData *adata, bool esmtp)
Say hello to an SMTP Server.
Definition smtp.c:425
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
unsigned int ssf
Security strength factor, in bits (see notes)
Definition connection.h:50
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_smtp_send()

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.

Parameters
fromFrom Address
toTo Address
ccCc Address
bccBcc Address
msgfileMessage to send to the server
eightbitIf true, try for an 8-bit friendly connection
subConfig Subset
Return values
0Success
-1Error

Definition at line 1132 of file smtp.c.

1135{
1136 struct SmtpAccountData adata = { 0 };
1137 struct ConnAccount cac = { { 0 } };
1138 const char *envfrom = NULL;
1139 int rc = -1;
1140
1141 adata.sub = sub;
1142 adata.fqdn = mutt_fqdn(false, adata.sub);
1143 if (!adata.fqdn)
1144 adata.fqdn = NONULL(ShortHostname);
1145
1146 const struct Address *c_envelope_from_address = cs_subset_address(adata.sub, "envelope_from_address");
1147
1148 if (smtp_fill_account(&adata, &cac) < 0)
1149 return rc;
1150
1151 adata.conn = mutt_conn_find(&cac);
1152 if (!adata.conn)
1153 return -1;
1154
1155 /* it might be better to synthesize an envelope from from user and host
1156 * but this condition is most likely arrived at accidentally */
1157 if (c_envelope_from_address)
1158 {
1159 envfrom = buf_string(c_envelope_from_address->mailbox);
1160 }
1161 else if (from && !TAILQ_EMPTY(from))
1162 {
1163 envfrom = buf_string(TAILQ_FIRST(from)->mailbox);
1164 }
1165 else
1166 {
1167 mutt_error(_("No from address given"));
1168 mutt_socket_close(adata.conn);
1169 return -1;
1170 }
1171
1172 const char *const c_dsn_return = cs_subset_string(adata.sub, "dsn_return");
1173
1174 struct Buffer *buf = buf_pool_get();
1175 do
1176 {
1177 /* send our greeting */
1178 rc = smtp_open(&adata, eightbit);
1179 if (rc != 0)
1180 break;
1181 FREE(&adata.auth_mechs);
1182
1183 /* send the sender's address */
1184 buf_printf(buf, "MAIL FROM:<%s>", envfrom);
1185 if (eightbit && (adata.capabilities & SMTP_CAP_EIGHTBITMIME))
1186 buf_addstr(buf, " BODY=8BITMIME");
1187
1188 if (c_dsn_return && (adata.capabilities & SMTP_CAP_DSN))
1189 buf_add_printf(buf, " RET=%s", c_dsn_return);
1190
1191 if ((adata.capabilities & SMTP_CAP_SMTPUTF8) &&
1194 {
1195 buf_addstr(buf, " SMTPUTF8");
1196 }
1197 buf_addstr(buf, "\r\n");
1198 if (mutt_socket_send(adata.conn, buf_string(buf)) == -1)
1199 {
1200 rc = SMTP_ERR_WRITE;
1201 break;
1202 }
1203 rc = smtp_get_resp(&adata);
1204 if (rc != 0)
1205 break;
1206
1207 /* send the recipient list */
1208 if ((rc = smtp_rcpt_to(&adata, to)) || (rc = smtp_rcpt_to(&adata, cc)) ||
1209 (rc = smtp_rcpt_to(&adata, bcc)))
1210 {
1211 break;
1212 }
1213
1214 /* send the message data */
1215 rc = smtp_data(&adata, msgfile);
1216 if (rc != 0)
1217 break;
1218
1219 mutt_socket_send(adata.conn, "QUIT\r\n");
1220
1221 rc = 0;
1222 } while (false);
1223
1224 mutt_socket_close(adata.conn);
1225 FREE(&adata.conn);
1226 FREE(&adata.auth_mechs);
1227
1228 if (rc == SMTP_ERR_READ)
1229 mutt_error(_("SMTP session failed: read error"));
1230 else if (rc == SMTP_ERR_WRITE)
1231 mutt_error(_("SMTP session failed: write error"));
1232 else if (rc == SMTP_ERR_CODE)
1233 mutt_error(_("Invalid server response"));
1234
1235 buf_pool_release(&buf);
1236 return rc;
1237}
bool mutt_addrlist_uses_unicode(const struct AddressList *al)
Do any of a list of addresses use Unicode characters.
Definition address.c:1531
bool mutt_addr_uses_unicode(const char *str)
Does this address use Unicode character.
Definition address.c:1511
const struct Address * cs_subset_address(const struct ConfigSubset *sub, const char *name)
Get an Address config item by name.
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_addstr(struct Buffer *buf, const char *s)
Add a string to a Buffer.
Definition buffer.c:226
char * ShortHostname
Short version of the hostname.
Definition globals.c:36
struct Connection * mutt_conn_find(const struct ConnAccount *cac)
Find a connection from a list.
Definition mutt_socket.c:88
struct Buffer * buf_pool_get(void)
Get a Buffer from the pool.
Definition pool.c:91
void buf_pool_release(struct Buffer **ptr)
Return a Buffer to the pool.
Definition pool.c:111
#define TAILQ_FIRST(head)
Definition queue.h:780
#define TAILQ_EMPTY(head)
Definition queue.h:778
const char * mutt_fqdn(bool may_hide_host, const struct ConfigSubset *sub)
Get the Fully-Qualified Domain Name.
Definition sendlib.c:713
static int smtp_data(struct SmtpAccountData *adata, const char *msgfile)
Send data to an SMTP server.
Definition smtp.c:251
static int smtp_fill_account(struct SmtpAccountData *adata, struct ConnAccount *cac)
Create ConnAccount object from SMTP Url.
Definition smtp.c:369
static int smtp_rcpt_to(struct SmtpAccountData *adata, const struct AddressList *al)
Set the recipient to an Address.
Definition smtp.c:204
static int smtp_open(struct SmtpAccountData *adata, bool esmtp)
Open an SMTP Connection.
Definition smtp.c:1050
int mutt_socket_close(struct Connection *conn)
Close a socket.
Definition socket.c:100
#define NONULL(x)
Definition string2.h:44
String manipulation buffer.
Definition buffer.h:36
Login details for a remote server.
Definition connaccount.h:59
Server connection data.
Definition smtp.c:102
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

Variable Documentation

◆ SmtpAuthenticators

const struct SmtpAuth SmtpAuthenticators[]
static
Initial value:
= {
{ smtp_auth_oauth, "oauthbearer" },
{ smtp_auth_xoauth2, "xoauth2" },
{ smtp_auth_plain, "plain" },
{ smtp_auth_login, "login" },
}
static int smtp_auth_xoauth2(struct SmtpAccountData *adata, const char *method)
Authenticate an SMTP connection using XOAUTH2 - Implements SmtpAuth::authenticate() -.
Definition smtp.c:814
static int smtp_auth_login(struct SmtpAccountData *adata, const char *method)
Authenticate using plain text - Implements SmtpAuth::authenticate() -.
Definition smtp.c:868
static int smtp_auth_plain(struct SmtpAccountData *adata, const char *method)
Authenticate using plain text - Implements SmtpAuth::authenticate() -.
Definition smtp.c:828
static int smtp_auth_oauth(struct SmtpAccountData *adata, const char *method)
Authenticate an SMTP connection using OAUTHBEARER - Implements SmtpAuth::authenticate() -.
Definition smtp.c:803

Accepted authentication methods.

Definition at line 936 of file smtp.c.

936 {
937 // clang-format off
938 { smtp_auth_oauth, "oauthbearer" },
939 { smtp_auth_xoauth2, "xoauth2" },
940 { smtp_auth_plain, "plain" },
941 { smtp_auth_login, "login" },
942#ifdef USE_SASL_CYRUS
943 { smtp_auth_sasl, NULL },
944#endif
945#ifdef USE_SASL_GNU
946 { smtp_auth_gsasl, NULL },
947#endif
948 // clang-format on
949};