NeoMutt  2022-04-29-215-gc12b98
Teaching an old dog new tricks
DOXYGEN
smtp.c File Reference

Send email to an SMTP server. More...

#include "config.h"
#include <netdb.h>
#include <netinet/in.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include "mutt/lib.h"
#include "address/lib.h"
#include "config/lib.h"
#include "email/lib.h"
#include "conn/lib.h"
#include "smtp.h"
#include "lib.h"
#include "progress/lib.h"
#include "question/lib.h"
#include "mutt_account.h"
#include "mutt_globals.h"
#include "mutt_socket.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)   ((x) / 100 == 2)
 
#define SMTP_READY   334
 
#define SMTP_CONTINUE   354
 
#define SMTP_ERR_READ   -2
 
#define SMTP_ERR_WRITE   -3
 
#define SMTP_ERR_CODE   -4
 
#define SMTP_PORT   25
 
#define SMTPS_PORT   465
 
#define SMTP_AUTH_SUCCESS   0
 
#define SMTP_AUTH_UNAVAIL   1
 
#define SMTP_AUTH_FAIL   -1
 
#define SMTP_CAP_NO_FLAGS   0
 No flags are set. More...
 
#define SMTP_CAP_STARTTLS   (1 << 0)
 Server supports STARTTLS command. More...
 
#define SMTP_CAP_AUTH   (1 << 1)
 Server supports AUTH command. More...
 
#define SMTP_CAP_DSN   (1 << 2)
 Server supports Delivery Status Notification. More...
 
#define SMTP_CAP_EIGHTBITMIME   (1 << 3)
 Server supports 8-bit MIME content. More...
 
#define SMTP_CAP_SMTPUTF8   (1 << 4)
 Server accepts UTF-8 strings. More...
 
#define SMTP_CAP_ALL   ((1 << 5) - 1)
 

Typedefs

typedef uint8_t SmtpCapFlags
 SMTP server capabilities. More...
 

Functions

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

Variables

static const struct SmtpAuth SmtpAuthenticators []
 Accepted authentication methods. More...
 

Detailed Description

Send email to an SMTP server.

Authors
  • Michael R. Elkins
  • Brendan Cully
  • Pietro Cerutti

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)    ((x) / 100 == 2)

Definition at line 59 of file smtp.c.

◆ SMTP_READY

#define SMTP_READY   334

Definition at line 60 of file smtp.c.

◆ SMTP_CONTINUE

#define SMTP_CONTINUE   354

Definition at line 61 of file smtp.c.

◆ SMTP_ERR_READ

#define SMTP_ERR_READ   -2

Definition at line 63 of file smtp.c.

◆ SMTP_ERR_WRITE

#define SMTP_ERR_WRITE   -3

Definition at line 64 of file smtp.c.

◆ SMTP_ERR_CODE

#define SMTP_ERR_CODE   -4

Definition at line 65 of file smtp.c.

◆ SMTP_PORT

#define SMTP_PORT   25

Definition at line 67 of file smtp.c.

◆ SMTPS_PORT

#define SMTPS_PORT   465

Definition at line 68 of file smtp.c.

◆ SMTP_AUTH_SUCCESS

#define SMTP_AUTH_SUCCESS   0

Definition at line 70 of file smtp.c.

◆ SMTP_AUTH_UNAVAIL

#define SMTP_AUTH_UNAVAIL   1

Definition at line 71 of file smtp.c.

◆ SMTP_AUTH_FAIL

#define SMTP_AUTH_FAIL   -1

Definition at line 72 of file smtp.c.

◆ SMTP_CAP_NO_FLAGS

#define SMTP_CAP_NO_FLAGS   0

No flags are set.

Definition at line 79 of file smtp.c.

◆ SMTP_CAP_STARTTLS

#define SMTP_CAP_STARTTLS   (1 << 0)

Server supports STARTTLS command.

Definition at line 80 of file smtp.c.

◆ SMTP_CAP_AUTH

#define SMTP_CAP_AUTH   (1 << 1)

Server supports AUTH command.

Definition at line 81 of file smtp.c.

◆ SMTP_CAP_DSN

#define SMTP_CAP_DSN   (1 << 2)

Server supports Delivery Status Notification.

Definition at line 82 of file smtp.c.

◆ SMTP_CAP_EIGHTBITMIME

#define SMTP_CAP_EIGHTBITMIME   (1 << 3)

Server supports 8-bit MIME content.

Definition at line 83 of file smtp.c.

◆ SMTP_CAP_SMTPUTF8

#define SMTP_CAP_SMTPUTF8   (1 << 4)

Server accepts UTF-8 strings.

Definition at line 84 of file smtp.c.

◆ SMTP_CAP_ALL

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

Definition at line 86 of file smtp.c.

Typedef Documentation

◆ SmtpCapFlags

typedef uint8_t SmtpCapFlags

SMTP server capabilities.

Flags, e.g. SMTP_CAP_STARTTLS

Definition at line 78 of file smtp.c.

Function Documentation

◆ valid_smtp_code()

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

Is the is a valid SMTP return code?

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

Definition at line 125 of file smtp.c.

126{
127 return (mutt_str_atoi(buf, n) - buf) <= 3;
128}
const char * mutt_str_atoi(const char *str, int *dst)
Convert ASCII string to an integer.
Definition: atoi.c:178
+ 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 136 of file smtp.c.

137{
138 int n;
139 char buf[1024] = { 0 };
140
141 do
142 {
143 n = mutt_socket_readln(buf, sizeof(buf), adata->conn);
144 if (n < 4)
145 {
146 /* read error, or no response code */
147 return SMTP_ERR_READ;
148 }
149 const char *s = buf + 4; /* Skip the response code and the space/dash */
150 size_t plen;
151
152 if (mutt_istr_startswith(s, "8BITMIME"))
154 else if ((plen = mutt_istr_startswith(s, "AUTH ")))
155 {
156 adata->capabilities |= SMTP_CAP_AUTH;
157 FREE(&adata->auth_mechs);
158 adata->auth_mechs = mutt_str_dup(s + plen);
159 }
160 else if (mutt_istr_startswith(s, "DSN"))
161 adata->capabilities |= SMTP_CAP_DSN;
162 else if (mutt_istr_startswith(s, "STARTTLS"))
164 else if (mutt_istr_startswith(s, "SMTPUTF8"))
166
167 if (!valid_smtp_code(buf, n, &n))
168 return SMTP_ERR_CODE;
169
170 } while (buf[3] == '-');
171
172 if (smtp_success(n) || (n == SMTP_CONTINUE))
173 return 0;
174
175 mutt_error(_("SMTP session failed: %s"), buf);
176 return -1;
177}
#define mutt_error(...)
Definition: logging.h:87
#define FREE(x)
Definition: memory.h:43
#define _(a)
Definition: message.h:28
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:250
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
static size_t plen
Length of cached packet.
Definition: pgppacket.c:39
#define SMTP_CAP_STARTTLS
Server supports STARTTLS command.
Definition: smtp.c:80
#define SMTP_ERR_READ
Definition: smtp.c:63
#define SMTP_ERR_CODE
Definition: smtp.c:65
#define SMTP_CAP_EIGHTBITMIME
Server supports 8-bit MIME content.
Definition: smtp.c:83
#define smtp_success(x)
Definition: smtp.c:59
#define SMTP_CAP_AUTH
Server supports AUTH command.
Definition: smtp.c:81
#define SMTP_CAP_SMTPUTF8
Server accepts UTF-8 strings.
Definition: smtp.c:84
#define SMTP_CONTINUE
Definition: smtp.c:61
#define SMTP_CAP_DSN
Server supports Delivery Status Notification.
Definition: smtp.c:82
static bool valid_smtp_code(char *buf, size_t buflen, int *n)
Is the is a valid SMTP return code?
Definition: smtp.c:125
#define mutt_socket_readln(buf, buflen, conn)
Definition: socket.h:58
struct Connection * conn
Server Connection.
Definition: smtp.c:96
const char * auth_mechs
Allowed authorisation mechanisms.
Definition: smtp.c:94
SmtpCapFlags capabilities
Server capabilities.
Definition: smtp.c:95
+ 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 186 of file smtp.c.

187{
188 if (!al)
189 return 0;
190
191 const char *const c_dsn_notify = cs_subset_string(adata->sub, "dsn_notify");
192
193 struct Address *a = NULL;
194 TAILQ_FOREACH(a, al, entries)
195 {
196 /* weed out group mailboxes, since those are for display only */
197 if (!a->mailbox || a->group)
198 {
199 continue;
200 }
201 char buf[1024] = { 0 };
202 if ((adata->capabilities & SMTP_CAP_DSN) && c_dsn_notify)
203 snprintf(buf, sizeof(buf), "RCPT TO:<%s> NOTIFY=%s\r\n", a->mailbox, c_dsn_notify);
204 else
205 snprintf(buf, sizeof(buf), "RCPT TO:<%s>\r\n", a->mailbox);
206 if (mutt_socket_send(adata->conn, buf) == -1)
207 return SMTP_ERR_WRITE;
208 int rc = smtp_get_resp(adata);
209 if (rc != 0)
210 return rc;
211 }
212
213 return 0;
214}
const char * cs_subset_string(const struct ConfigSubset *sub, const char *name)
Get a string config item by name.
Definition: helpers.c:317
#define TAILQ_FOREACH(var, head, field)
Definition: queue.h:725
static int smtp_get_resp(struct SmtpAccountData *adata)
Read a command response from the SMTP server.
Definition: smtp.c:136
#define SMTP_ERR_WRITE
Definition: smtp.c:64
#define mutt_socket_send(conn, buf)
Definition: socket.h:59
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
struct ConfigSubset * sub
Config scope.
Definition: smtp.c:97
+ 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 223 of file smtp.c.

224{
225 char buf[1024] = { 0 };
226 struct Progress *progress = NULL;
227 int rc = SMTP_ERR_WRITE;
228 int term = 0;
229 size_t buflen = 0;
230
231 FILE *fp = fopen(msgfile, "r");
232 if (!fp)
233 {
234 mutt_error(_("SMTP session failed: unable to open %s"), msgfile);
235 return -1;
236 }
237 const long size = mutt_file_get_size_fp(fp);
238 if (size == 0)
239 {
240 mutt_file_fclose(&fp);
241 return -1;
242 }
243 unlink(msgfile);
244 progress = progress_new(_("Sending message..."), MUTT_PROGRESS_NET, size);
245
246 snprintf(buf, sizeof(buf), "DATA\r\n");
247 if (mutt_socket_send(adata->conn, buf) == -1)
248 {
249 mutt_file_fclose(&fp);
250 goto done;
251 }
252 rc = smtp_get_resp(adata);
253 if (rc != 0)
254 {
255 mutt_file_fclose(&fp);
256 goto done;
257 }
258
259 rc = SMTP_ERR_WRITE;
260 while (fgets(buf, sizeof(buf) - 1, fp))
261 {
262 buflen = mutt_str_len(buf);
263 term = buflen && buf[buflen - 1] == '\n';
264 if (term && ((buflen == 1) || (buf[buflen - 2] != '\r')))
265 snprintf(buf + buflen - 1, sizeof(buf) - buflen + 1, "\r\n");
266 if (buf[0] == '.')
267 {
268 if (mutt_socket_send_d(adata->conn, ".", MUTT_SOCK_LOG_FULL) == -1)
269 {
270 mutt_file_fclose(&fp);
271 goto done;
272 }
273 }
274 if (mutt_socket_send_d(adata->conn, buf, MUTT_SOCK_LOG_FULL) == -1)
275 {
276 mutt_file_fclose(&fp);
277 goto done;
278 }
279 progress_update(progress, MAX(0, ftell(fp)), -1);
280 }
281 if (!term && buflen &&
282 (mutt_socket_send_d(adata->conn, "\r\n", MUTT_SOCK_LOG_FULL) == -1))
283 {
284 mutt_file_fclose(&fp);
285 goto done;
286 }
287 mutt_file_fclose(&fp);
288
289 /* terminate the message body */
290 if (mutt_socket_send(adata->conn, ".\r\n") == -1)
291 goto done;
292
293 rc = smtp_get_resp(adata);
294
295done:
296 progress_free(&progress);
297 return rc;
298}
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
#define MAX(a, b)
Definition: memory.h:30
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:567
@ 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
#define MUTT_SOCK_LOG_FULL
Definition: socket.h:56
#define mutt_socket_send_d(conn, buf, dbg)
Definition: socket.h:60
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ smtp_get_field()

static const char * smtp_get_field ( enum ConnAccountField  field,
void *  gf_data 
)
static

Get connection login credentials - Implements ConnAccount::get_field()

Definition at line 303 of file smtp.c.

304{
305 struct SmtpAccountData *adata = gf_data;
306 if (!adata)
307 return NULL;
308
309 switch (field)
310 {
311 case MUTT_CA_LOGIN:
312 case MUTT_CA_USER:
313 {
314 const char *const c_smtp_user = cs_subset_string(adata->sub, "smtp_user");
315 return c_smtp_user;
316 }
317 case MUTT_CA_PASS:
318 {
319 const char *const c_smtp_pass = cs_subset_string(adata->sub, "smtp_pass");
320 return c_smtp_pass;
321 }
323 {
324 const char *const c_smtp_oauth_refresh_command = cs_subset_string(adata->sub, "smtp_oauth_refresh_command");
325 return c_smtp_oauth_refresh_command;
326 }
327 case MUTT_CA_HOST:
328 default:
329 return NULL;
330 }
331}
@ 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
Server connection data.
Definition: smtp.c:93
+ 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 340 of file smtp.c.

341{
342 cac->flags = 0;
343 cac->port = 0;
345 cac->service = "smtp";
347 cac->gf_data = adata;
348
349 const char *const c_smtp_url = cs_subset_string(adata->sub, "smtp_url");
350
351 struct Url *url = url_parse(c_smtp_url);
352 if (!url || ((url->scheme != U_SMTP) && (url->scheme != U_SMTPS)) ||
353 !url->host || (mutt_account_fromurl(cac, url) < 0))
354 {
355 url_free(&url);
356 mutt_error(_("Invalid SMTP URL: %s"), c_smtp_url);
357 return -1;
358 }
359
360 if (url->scheme == U_SMTPS)
361 cac->flags |= MUTT_ACCT_SSL;
362
363 if (cac->port == 0)
364 {
365 if (cac->flags & MUTT_ACCT_SSL)
366 cac->port = SMTPS_PORT;
367 else
368 {
369 static unsigned short SmtpPort = 0;
370 if (SmtpPort == 0)
371 {
372 struct servent *service = getservbyname("smtp", "tcp");
373 if (service)
374 SmtpPort = ntohs(service->s_port);
375 else
376 SmtpPort = SMTP_PORT;
377 mutt_debug(LL_DEBUG3, "Using default SMTP port %d\n", SmtpPort);
378 }
379 cac->port = SmtpPort;
380 }
381 }
382
383 url_free(&url);
384 return 0;
385}
#define MUTT_ACCT_SSL
Account uses SSL/TLS.
Definition: connaccount.h:47
#define mutt_debug(LEVEL,...)
Definition: logging.h:84
@ LL_DEBUG3
Log at debug level 3.
Definition: logging.h:42
int mutt_account_fromurl(struct ConnAccount *cac, const struct Url *url)
Fill ConnAccount with information from url.
Definition: mutt_account.c:43
@ MUTT_ACCT_TYPE_SMTP
Smtp Account.
Definition: mutt_account.h:39
#define SMTPS_PORT
Definition: smtp.c:68
static const char * smtp_get_field(enum ConnAccountField field, void *gf_data)
Get connection login credentials - Implements ConnAccount::get_field()
Definition: smtp.c:303
#define SMTP_PORT
Definition: smtp.c:67
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
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
+ 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 394 of file smtp.c.

395{
397
398 if (!esmtp)
399 {
400 /* if TLS or AUTH are requested, use EHLO */
401 if (adata->conn->account.flags & MUTT_ACCT_USER)
402 esmtp = true;
403#ifdef USE_SSL
404 const bool c_ssl_force_tls = cs_subset_bool(adata->sub, "ssl_force_tls");
405 const enum QuadOption c_ssl_starttls = cs_subset_quad(adata->sub, "ssl_starttls");
406
407 if (c_ssl_force_tls || (c_ssl_starttls != MUTT_NO))
408 esmtp = true;
409#endif
410 }
411
412 char buf[1024] = { 0 };
413 snprintf(buf, sizeof(buf), "%s %s\r\n", esmtp ? "EHLO" : "HELO", adata->fqdn);
414 /* XXX there should probably be a wrapper in mutt_socket.c that
415 * repeatedly calls adata->conn->write until all data is sent. This
416 * currently doesn't check for a short write. */
417 if (mutt_socket_send(adata->conn, buf) == -1)
418 return SMTP_ERR_WRITE;
419 return smtp_get_resp(adata);
420}
enum QuadOption cs_subset_quad(const struct ConfigSubset *sub, const char *name)
Get a quad-value config item by name.
Definition: helpers.c:218
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:73
#define MUTT_ACCT_USER
User field has been set.
Definition: connaccount.h:44
QuadOption
Possible values for a quad-option.
Definition: quad.h:36
@ MUTT_NO
User answered 'No', or assume 'No'.
Definition: quad.h:38
#define SMTP_CAP_NO_FLAGS
No flags are set.
Definition: smtp.c:79
struct ConnAccount account
Account details: username, password, etc.
Definition: connection.h:50
const char * fqdn
Fully-qualified domain name.
Definition: smtp.c:98
+ 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 (not used)
xoauth2Use XOAUTH2 token (if true), OAUTHBEARER token otherwise
Return values
numResult, e.g. SMTP_AUTH_SUCCESS

Definition at line 543 of file smtp.c.

544{
545 (void) method; // This is OAUTHBEARER
546 const char *authtype = xoauth2 ? "XOAUTH2" : "OAUTHBEARER";
547
548 // L10N: (%s) is the method name, e.g. Anonymous, CRAM-MD5, GSSAPI, SASL
549 mutt_message(_("Authenticating (%s)..."), authtype);
550
551 /* We get the access token from the smtp_oauth_refresh_command */
552 char *oauthbearer = mutt_account_getoauthbearer(&adata->conn->account, xoauth2);
553 if (!oauthbearer)
554 return SMTP_AUTH_FAIL;
555
556 size_t ilen = strlen(oauthbearer) + 30;
557 char *ibuf = mutt_mem_malloc(ilen);
558 snprintf(ibuf, ilen, "AUTH %s %s\r\n", authtype, oauthbearer);
559
560 int rc = mutt_socket_send(adata->conn, ibuf);
561 FREE(&oauthbearer);
562 FREE(&ibuf);
563
564 if (rc == -1)
565 return SMTP_AUTH_FAIL;
566 if (smtp_get_resp(adata) != 0)
567 return SMTP_AUTH_FAIL;
568
569 return SMTP_AUTH_SUCCESS;
570}
char * mutt_account_getoauthbearer(struct ConnAccount *cac, bool xoauth2)
Get an OAUTHBEARER/XOAUTH2 token.
Definition: connaccount.c:195
#define mutt_message(...)
Definition: logging.h:86
void * mutt_mem_malloc(size_t size)
Allocate memory on the heap.
Definition: memory.c:90
#define SMTP_AUTH_FAIL
Definition: smtp.c:72
#define SMTP_AUTH_SUCCESS
Definition: smtp.c:70
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ smtp_auth_oauth()

static int smtp_auth_oauth ( struct SmtpAccountData adata,
const char *  method 
)
static

Authenticate an SMTP connection using OAUTHBEARER.

Parameters
adataSMTP Account data
methodAuthentication method (not used)
Return values
numResult, e.g. SMTP_AUTH_SUCCESS

Definition at line 578 of file smtp.c.

579{
580 return smtp_auth_oauth_xoauth2(adata, method, false);
581}
static int smtp_auth_oauth_xoauth2(struct SmtpAccountData *adata, const char *method, bool xoauth2)
Authenticate an SMTP connection using OAUTHBEARER/XOAUTH2.
Definition: smtp.c:543
+ Here is the call graph for this function:

◆ smtp_auth_xoauth2()

static int smtp_auth_xoauth2 ( struct SmtpAccountData adata,
const char *  method 
)
static

Authenticate an SMTP connection using XOAUTH2.

Parameters
adataSMTP Account data
methodAuthentication method (not used)
Return values
numResult, e.g. SMTP_AUTH_SUCCESS

Definition at line 589 of file smtp.c.

590{
591 return smtp_auth_oauth_xoauth2(adata, method, true);
592}
+ Here is the call graph for this function:

◆ smtp_auth_plain()

static int smtp_auth_plain ( struct SmtpAccountData adata,
const char *  method 
)
static

Authenticate using plain text.

Parameters
adataSMTP Account data
methodAuthentication method (not used)
Return values
0Success
<0Error, e.g. SMTP_AUTH_FAIL

Definition at line 601 of file smtp.c.

602{
603 (void) method; // This is PLAIN
604
605 char buf[1024] = { 0 };
606
607 /* Get username and password. Bail out of any can't be retrieved. */
608 if ((mutt_account_getuser(&adata->conn->account) < 0) ||
609 (mutt_account_getpass(&adata->conn->account) < 0))
610 {
611 goto error;
612 }
613
614 /* Build the initial client response. */
615 size_t len = mutt_sasl_plain_msg(buf, sizeof(buf), "AUTH PLAIN",
616 adata->conn->account.user,
617 adata->conn->account.user,
618 adata->conn->account.pass);
619
620 /* Terminate as per SMTP protocol. Bail out if there's no room left. */
621 if (snprintf(buf + len, sizeof(buf) - len, "\r\n") != 2)
622 {
623 goto error;
624 }
625
626 /* Send request, receive response (with a check for OK code). */
627 if ((mutt_socket_send(adata->conn, buf) < 0) || smtp_get_resp(adata))
628 {
629 goto error;
630 }
631
632 /* If we got here, auth was successful. */
633 return 0;
634
635error:
636 // L10N: %s is the method name, e.g. Anonymous, CRAM-MD5, GSSAPI, SASL
637 mutt_error(_("%s authentication failed"), "SASL");
638 return -1;
639}
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
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
char user[128]
Username.
Definition: connaccount.h:56
char pass[256]
Password.
Definition: connaccount.h:57
+ Here is the call graph for this function:

◆ smtp_auth_login()

static int smtp_auth_login ( struct SmtpAccountData adata,
const char *  method 
)
static

Authenticate using plain text.

Parameters
adataSMTP Account data
methodAuthentication method (not used)
Return values
0Success
<0Error, e.g. SMTP_AUTH_FAIL

Definition at line 648 of file smtp.c.

649{
650 (void) method; // This is LOGIN
651
652 char b64[1024] = { 0 };
653 char buf[1024] = { 0 };
654
655 /* Get username and password. Bail out of any can't be retrieved. */
656 if ((mutt_account_getuser(&adata->conn->account) < 0) ||
657 (mutt_account_getpass(&adata->conn->account) < 0))
658 {
659 goto error;
660 }
661
662 /* Send the AUTH LOGIN request. */
663 if (mutt_socket_send(adata->conn, "AUTH LOGIN\r\n") < 0)
664 {
665 goto error;
666 }
667
668 /* Read the 334 VXNlcm5hbWU6 challenge ("Username:" base64-encoded) */
669 mutt_socket_readln_d(buf, sizeof(buf), adata->conn, MUTT_SOCK_LOG_FULL);
670 if (!mutt_str_equal(buf, "334 VXNlcm5hbWU6"))
671 {
672 goto error;
673 }
674
675 /* Send the username */
676 size_t len = snprintf(buf, sizeof(buf), "%s", adata->conn->account.user);
677 mutt_b64_encode(buf, len, b64, sizeof(b64));
678 snprintf(buf, sizeof(buf), "%s\r\n", b64);
679 if (mutt_socket_send(adata->conn, buf) < 0)
680 {
681 goto error;
682 }
683
684 /* Read the 334 UGFzc3dvcmQ6 challenge ("Password:" base64-encoded) */
685 mutt_socket_readln_d(buf, sizeof(buf), adata->conn, MUTT_SOCK_LOG_FULL);
686 if (!mutt_str_equal(buf, "334 UGFzc3dvcmQ6"))
687 {
688 goto error;
689 }
690
691 /* Send the password */
692 len = snprintf(buf, sizeof(buf), "%s", adata->conn->account.pass);
693 mutt_b64_encode(buf, len, b64, sizeof(b64));
694 snprintf(buf, sizeof(buf), "%s\r\n", b64);
695 if (mutt_socket_send(adata->conn, buf) < 0)
696 {
697 goto error;
698 }
699
700 /* Check the final response */
701 if (smtp_get_resp(adata) < 0)
702 {
703 goto error;
704 }
705
706 /* If we got here, auth was successful. */
707 return 0;
708
709error:
710 // L10N: %s is the method name, e.g. Anonymous, CRAM-MD5, GSSAPI, SASL
711 mutt_error(_("%s authentication failed"), "LOGIN");
712 return -1;
713}
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
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:807
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
+ Here is the call 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 738 of file smtp.c.

739{
740 for (size_t i = 0; i < mutt_array_size(SmtpAuthenticators); i++)
741 {
742 const struct SmtpAuth *auth = &SmtpAuthenticators[i];
743 if (auth->method && mutt_istr_equal(auth->method, authenticator))
744 return true;
745 }
746
747 return false;
748}
#define mutt_array_size(x)
Definition: memory.h:36
bool mutt_istr_equal(const char *a, const char *b)
Compare two strings, ignoring case.
Definition: string.c:819
static const struct SmtpAuth SmtpAuthenticators[]
Accepted authentication methods.
Definition: smtp.c:718
SMTP authentication multiplexor.
Definition: smtp.c:105
const char * method
Name of authentication method supported, NULL means variable.
Definition: smtp.c:114
+ 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 756 of file smtp.c.

757{
758 int r = SMTP_AUTH_UNAVAIL;
759
760 const struct Slist *c_smtp_authenticators = cs_subset_slist(adata->sub, "smtp_authenticators");
761 if (c_smtp_authenticators && (c_smtp_authenticators->count > 0))
762 {
763 mutt_debug(LL_DEBUG2, "Trying user-defined smtp_authenticators\n");
764
765 /* Try user-specified list of authentication methods */
766 struct ListNode *np = NULL;
767 STAILQ_FOREACH(np, &c_smtp_authenticators->head, entries)
768 {
769 mutt_debug(LL_DEBUG2, "Trying method %s\n", np->data);
770
771 for (size_t i = 0; i < mutt_array_size(SmtpAuthenticators); i++)
772 {
773 const struct SmtpAuth *auth = &SmtpAuthenticators[i];
774 if (!auth->method || mutt_istr_equal(auth->method, np->data))
775 {
776 r = auth->authenticate(adata, np->data);
777 if (r == SMTP_AUTH_SUCCESS)
778 return r;
779 }
780 }
781 }
782 }
783 else
784 {
785 /* Fall back to default: any authenticator */
786 mutt_debug(LL_DEBUG2, "Falling back to smtp_auth_sasl, if using sasl.\n");
787
788#ifdef USE_SASL_CYRUS
789 r = smtp_auth_sasl(adata, adata->auth_mechs);
790#else
791 mutt_error(_("SMTP authentication requires SASL"));
793#endif
794 }
795
796 if (r != SMTP_AUTH_SUCCESS)
798
799 if (r == SMTP_AUTH_FAIL)
800 {
801 // L10N: %s is the method name, e.g. Anonymous, CRAM-MD5, GSSAPI, SASL
802 mutt_error(_("%s authentication failed"), "SASL");
803 }
804 else if (r == SMTP_AUTH_UNAVAIL)
805 {
806 mutt_error(_("No authenticators available"));
807 }
808
809 return (r == SMTP_AUTH_SUCCESS) ? 0 : -1;
810}
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
void mutt_account_unsetpass(struct ConnAccount *cac)
Unset ConnAccount's password.
Definition: connaccount.c:177
@ LL_DEBUG2
Log at debug level 2.
Definition: logging.h:41
#define STAILQ_FOREACH(var, head, field)
Definition: queue.h:352
#define SMTP_AUTH_UNAVAIL
Definition: smtp.c:71
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
int(* authenticate)(struct SmtpAccountData *adata, const char *method)
Authenticate an SMTP connection.
Definition: smtp.c:112
+ 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 819 of file smtp.c.

820{
821 int rc;
822
823 if (mutt_socket_open(adata->conn))
824 return -1;
825
826 const bool force_auth = cs_subset_string(adata->sub, "smtp_user");
827 esmtp |= force_auth;
828
829 /* get greeting string */
830 rc = smtp_get_resp(adata);
831 if (rc != 0)
832 return rc;
833
834 rc = smtp_helo(adata, esmtp);
835 if (rc != 0)
836 return rc;
837
838#ifdef USE_SSL
839 const bool c_ssl_force_tls = cs_subset_bool(adata->sub, "ssl_force_tls");
840 const enum QuadOption c_ssl_starttls = cs_subset_quad(adata->sub, "ssl_starttls");
841 enum QuadOption ans = MUTT_NO;
842 if (adata->conn->ssf != 0)
843 ans = MUTT_NO;
844 else if (c_ssl_force_tls)
845 ans = MUTT_YES;
846 else if ((adata->capabilities & SMTP_CAP_STARTTLS) &&
847 ((ans = query_quadoption(c_ssl_starttls, _("Secure connection with TLS?"))) == MUTT_ABORT))
848 {
849 return -1;
850 }
851
852 if (ans == MUTT_YES)
853 {
854 if (mutt_socket_send(adata->conn, "STARTTLS\r\n") < 0)
855 return SMTP_ERR_WRITE;
856 rc = smtp_get_resp(adata);
857 // Clear any data after the STARTTLS acknowledgement
858 mutt_socket_empty(adata->conn);
859 if (rc != 0)
860 return rc;
861
862 if (mutt_ssl_starttls(adata->conn))
863 {
864 mutt_error(_("Could not negotiate TLS connection"));
865 return -1;
866 }
867
868 /* re-EHLO to get authentication mechanisms */
869 rc = smtp_helo(adata, esmtp);
870 if (rc != 0)
871 return rc;
872 }
873#endif
874
875 if (force_auth || adata->conn->account.flags & MUTT_ACCT_USER)
876 {
877 if (!(adata->capabilities & SMTP_CAP_AUTH))
878 {
879 mutt_error(_("SMTP server does not support authentication"));
880 return -1;
881 }
882
883 return smtp_authenticate(adata);
884 }
885
886 return 0;
887}
int mutt_ssl_starttls(struct Connection *conn)
Negotiate TLS over an already opened connection.
Definition: gnutls.c:1143
@ 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(enum QuadOption opt, const char *prompt)
Ask the user a quad-question.
Definition: question.c:386
static int smtp_authenticate(struct SmtpAccountData *adata)
Authenticate to an SMTP server.
Definition: smtp.c:756
static int smtp_helo(struct SmtpAccountData *adata, bool esmtp)
Say hello to an SMTP Server.
Definition: smtp.c:394
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
unsigned int ssf
Security strength factor, in bits (see notes)
Definition: connection.h:51
+ 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 901 of file smtp.c.

904{
905 struct SmtpAccountData adata = { 0 };
906 struct ConnAccount cac = { { 0 } };
907 const char *envfrom = NULL;
908 char buf[1024] = { 0 };
909 int rc = -1;
910
911 adata.sub = sub;
912 adata.fqdn = mutt_fqdn(false, adata.sub);
913 if (!adata.fqdn)
914 adata.fqdn = NONULL(ShortHostname);
915
916 const struct Address *c_envelope_from_address = cs_subset_address(adata.sub, "envelope_from_address");
917
918 /* it might be better to synthesize an envelope from from user and host
919 * but this condition is most likely arrived at accidentally */
920 if (c_envelope_from_address)
921 envfrom = c_envelope_from_address->mailbox;
922 else if (from && !TAILQ_EMPTY(from))
923 envfrom = TAILQ_FIRST(from)->mailbox;
924 else
925 {
926 mutt_error(_("No from address given"));
927 return -1;
928 }
929
930 if (smtp_fill_account(&adata, &cac) < 0)
931 return rc;
932
933 adata.conn = mutt_conn_find(&cac);
934 if (!adata.conn)
935 return -1;
936
937 const char *const c_dsn_return = cs_subset_string(adata.sub, "dsn_return");
938
939 do
940 {
941 /* send our greeting */
942 rc = smtp_open(&adata, eightbit);
943 if (rc != 0)
944 break;
945 FREE(&adata.auth_mechs);
946
947 /* send the sender's address */
948 int len = snprintf(buf, sizeof(buf), "MAIL FROM:<%s>", envfrom);
949 if (eightbit && (adata.capabilities & SMTP_CAP_EIGHTBITMIME))
950 {
951 mutt_strn_cat(buf, sizeof(buf), " BODY=8BITMIME", 15);
952 len += 14;
953 }
954 if (c_dsn_return && (adata.capabilities & SMTP_CAP_DSN))
955 len += snprintf(buf + len, sizeof(buf) - len, " RET=%s", c_dsn_return);
956 if ((adata.capabilities & SMTP_CAP_SMTPUTF8) &&
959 {
960 snprintf(buf + len, sizeof(buf) - len, " SMTPUTF8");
961 }
962 mutt_strn_cat(buf, sizeof(buf), "\r\n", 3);
963 if (mutt_socket_send(adata.conn, buf) == -1)
964 {
965 rc = SMTP_ERR_WRITE;
966 break;
967 }
968 rc = smtp_get_resp(&adata);
969 if (rc != 0)
970 break;
971
972 /* send the recipient list */
973 if ((rc = smtp_rcpt_to(&adata, to)) || (rc = smtp_rcpt_to(&adata, cc)) ||
974 (rc = smtp_rcpt_to(&adata, bcc)))
975 {
976 break;
977 }
978
979 /* send the message data */
980 rc = smtp_data(&adata, msgfile);
981 if (rc != 0)
982 break;
983
984 mutt_socket_send(adata.conn, "QUIT\r\n");
985
986 rc = 0;
987 } while (false);
988
989 mutt_socket_close(adata.conn);
990 FREE(&adata.conn);
991
992 if (rc == SMTP_ERR_READ)
993 mutt_error(_("SMTP session failed: read error"));
994 else if (rc == SMTP_ERR_WRITE)
995 mutt_error(_("SMTP session failed: write error"));
996 else if (rc == SMTP_ERR_CODE)
997 mutt_error(_("Invalid server response"));
998
999 return rc;
1000}
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
const struct Address * cs_subset_address(const struct ConfigSubset *sub, const char *name)
Get an Address config item by name.
Definition: helpers.c:49
char * mutt_strn_cat(char *d, size_t l, const char *s, size_t sl)
Concatenate two strings.
Definition: string.c:294
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
#define TAILQ_FIRST(head)
Definition: queue.h:723
#define TAILQ_EMPTY(head)
Definition: queue.h:721
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_data(struct SmtpAccountData *adata, const char *msgfile)
Send data to an SMTP server.
Definition: smtp.c:223
static int smtp_fill_account(struct SmtpAccountData *adata, struct ConnAccount *cac)
Create ConnAccount object from SMTP Url.
Definition: smtp.c:340
static int smtp_rcpt_to(struct SmtpAccountData *adata, const struct AddressList *al)
Set the recipient to an Address.
Definition: smtp.c:186
static int smtp_open(struct SmtpAccountData *adata, bool esmtp)
Open an SMTP Connection.
Definition: smtp.c:819
int mutt_socket_close(struct Connection *conn)
Close a socket.
Definition: socket.c:98
#define NONULL(x)
Definition: string2.h:37
Login details for a remote server.
Definition: connaccount.h:53
+ 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.
Definition: smtp.c:589
static int smtp_auth_login(struct SmtpAccountData *adata, const char *method)
Authenticate using plain text.
Definition: smtp.c:648
static int smtp_auth_plain(struct SmtpAccountData *adata, const char *method)
Authenticate using plain text.
Definition: smtp.c:601
static int smtp_auth_oauth(struct SmtpAccountData *adata, const char *method)
Authenticate an SMTP connection using OAUTHBEARER.
Definition: smtp.c:578

Accepted authentication methods.

Definition at line 718 of file smtp.c.