1 /* mutt - text oriented MIME mail user agent
2 * Copyright (C) 2002 Michael R. Elkins <me@mutt.org>
3 * Copyright (C) 2005-9 Brendan Cully <brendan@kublai.com>
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
20 /* This file contains code for direct SMTP delivery of email messages. */
27 #include "mutt_curses.h"
28 #include "mutt_socket.h"
30 # include "mutt_ssl.h"
33 #include "mutt_sasl.h"
35 #include <sasl/sasl.h>
36 #include <sasl/saslutil.h>
40 #include <netinet/in.h>
44 #define smtp_success(x) ((x)/100 == 2)
45 #define smtp_ready 334
46 #define smtp_continue 354
48 #define smtp_err_read -2
49 #define smtp_err_write -3
52 #define SMTPS_PORT 465
54 #define SMTP_AUTH_SUCCESS 0
55 #define SMTP_AUTH_UNAVAIL 1
56 #define SMTP_AUTH_FAIL -1
68 static int smtp_auth (CONNECTION* conn);
69 static int smtp_auth_sasl (CONNECTION* conn, const char* mechanisms);
72 static int smtp_fill_account (ACCOUNT* account);
73 static int smtp_open (CONNECTION* conn);
76 static char* AuthMechs = NULL;
77 static unsigned char Capabilities[(CAPMAX + 7)/ 8];
79 /* Reads a command response from the SMTP server.
81 * 0 on success (2xx code) or continue (354 code)
82 * -1 write error, or any other response code
85 smtp_get_resp (CONNECTION * conn)
91 n = mutt_socket_readln (buf, sizeof (buf), conn);
93 /* read error, or no response code */
97 if (!ascii_strncasecmp ("8BITMIME", buf + 4, 8))
98 mutt_bit_set (Capabilities, EIGHTBITMIME);
99 else if (!ascii_strncasecmp ("AUTH ", buf + 4, 5))
101 mutt_bit_set (Capabilities, AUTH);
103 AuthMechs = safe_strdup (buf + 9);
105 else if (!ascii_strncasecmp ("DSN", buf + 4, 3))
106 mutt_bit_set (Capabilities, DSN);
107 else if (!ascii_strncasecmp ("STARTTLS", buf + 4, 8))
108 mutt_bit_set (Capabilities, STARTTLS);
111 } while (buf[3] == '-');
113 if (smtp_success (n) || n == smtp_continue)
116 mutt_error (_("SMTP session failed: %s"), buf);
121 smtp_rcpt_to (CONNECTION * conn, const ADDRESS * a)
128 if (mutt_bit_isset (Capabilities, DSN) && DsnNotify)
129 snprintf (buf, sizeof (buf), "RCPT TO:<%s> NOTIFY=%s\r\n",
130 a->mailbox, DsnNotify);
132 snprintf (buf, sizeof (buf), "RCPT TO:<%s>\r\n", a->mailbox);
133 if (mutt_socket_write (conn, buf) == -1)
134 return smtp_err_write;
135 if ((r = smtp_get_resp (conn)))
144 smtp_data (CONNECTION * conn, const char *msgfile)
153 fp = fopen (msgfile, "r");
156 mutt_error (_("SMTP session failed: unable to open %s"), msgfile);
161 mutt_progress_init (&progress, _("Sending message..."), M_PROGRESS_SIZE,
164 snprintf (buf, sizeof (buf), "DATA\r\n");
165 if (mutt_socket_write (conn, buf) == -1)
168 return smtp_err_write;
170 if ((r = smtp_get_resp (conn)))
176 while (fgets (buf, sizeof (buf) - 1, fp))
178 buflen = mutt_strlen (buf);
179 if (buflen && buf[buflen-1] == '\n'
180 && (buflen == 1 || buf[buflen - 2] != '\r'))
181 snprintf (buf + buflen - 1, sizeof (buf) - buflen + 1, "\r\n");
184 if (mutt_socket_write_d (conn, ".", -1, M_SOCK_LOG_FULL) == -1)
187 return smtp_err_write;
190 if (mutt_socket_write_d (conn, buf, -1, M_SOCK_LOG_FULL) == -1)
193 return smtp_err_write;
196 mutt_progress_update (&progress, ftell (fp), -1);
200 /* terminate the message body */
201 if (mutt_socket_write (conn, ".\r\n") == -1)
202 return smtp_err_write;
204 if ((r = smtp_get_resp (conn)))
211 mutt_smtp_send (const ADDRESS* from, const ADDRESS* to, const ADDRESS* cc,
212 const ADDRESS* bcc, const char *msgfile, int eightbit)
220 /* it might be better to synthesize an envelope from from user and host
221 * but this condition is most likely arrived at accidentally */
223 envfrom = EnvFrom->mailbox;
225 envfrom = from->mailbox;
228 mutt_error (_("No from address given"));
232 if (smtp_fill_account (&account) < 0)
235 if (!(conn = mutt_conn_find (NULL, &account)))
242 /* send our greeting */
243 if (( ret = smtp_open (conn)))
247 /* send the sender's address */
248 ret = snprintf (buf, sizeof (buf), "MAIL FROM:<%s>", envfrom);
249 if (eightbit && mutt_bit_isset (Capabilities, EIGHTBITMIME))
251 safe_strncat (buf, sizeof (buf), " BODY=8BITMIME", 15);
254 if (DsnReturn && mutt_bit_isset (Capabilities, DSN))
255 ret += snprintf (buf + ret, sizeof (buf) - ret, " RET=%s", DsnReturn);
256 safe_strncat (buf, sizeof (buf), "\r\n", 3);
257 if (mutt_socket_write (conn, buf) == -1)
259 ret = smtp_err_write;
262 if ((ret = smtp_get_resp (conn)))
265 /* send the recipient list */
266 if ((ret = smtp_rcpt_to (conn, to)) || (ret = smtp_rcpt_to (conn, cc))
267 || (ret = smtp_rcpt_to (conn, bcc)))
270 /* send the message data */
271 if ((ret = smtp_data (conn, msgfile)))
274 mutt_socket_write (conn, "QUIT\r\n");
281 mutt_socket_close (conn);
283 if (ret == smtp_err_read)
284 mutt_error (_("SMTP session failed: read error"));
285 else if (ret == smtp_err_write)
286 mutt_error (_("SMTP session failed: write error"));
291 static int smtp_fill_account (ACCOUNT* account)
293 static unsigned short SmtpPort = 0;
295 struct servent* service;
301 account->type = M_ACCT_TYPE_SMTP;
303 urlstr = safe_strdup (SmtpUrl);
304 url_parse_ciss (&url, urlstr);
305 if ((url.scheme != U_SMTP && url.scheme != U_SMTPS)
306 || mutt_account_fromurl (account, &url) < 0)
309 mutt_error (_("Invalid SMTP URL: %s"), SmtpUrl);
315 if (url.scheme == U_SMTPS)
316 account->flags |= M_ACCT_SSL;
320 if (account->flags & M_ACCT_SSL)
321 account->port = SMTPS_PORT;
326 service = getservbyname ("smtp", "tcp");
328 SmtpPort = ntohs (service->s_port);
330 SmtpPort = SMTP_PORT;
331 dprint (3, (debugfile, "Using default SMTP port %d\n", SmtpPort));
333 account->port = SmtpPort;
340 static int smtp_helo (CONNECTION* conn)
342 char buf[LONG_STRING];
345 memset (Capabilities, 0, sizeof (Capabilities));
349 /* if TLS or AUTH are requested, use EHLO */
350 if (conn->account.flags & M_ACCT_USER)
353 if (option (OPTSSLFORCETLS) || quadoption (OPT_SSLSTARTTLS) != M_NO)
358 if(!(fqdn = mutt_fqdn (0)))
359 fqdn = NONULL (Hostname);
361 snprintf (buf, sizeof (buf), "%s %s\r\n", Esmtp ? "EHLO" : "HELO", fqdn);
362 /* XXX there should probably be a wrapper in mutt_socket.c that
363 * repeatedly calls conn->write until all data is sent. This
364 * currently doesn't check for a short write.
366 if (mutt_socket_write (conn, buf) == -1)
367 return smtp_err_write;
368 return smtp_get_resp (conn);
371 static int smtp_open (CONNECTION* conn)
375 if (mutt_socket_open (conn))
378 /* get greeting string */
379 if ((rc = smtp_get_resp (conn)))
382 if ((rc = smtp_helo (conn)))
388 else if (option (OPTSSLFORCETLS))
390 else if (mutt_bit_isset (Capabilities, STARTTLS) &&
391 (rc = query_quadoption (OPT_SSLSTARTTLS,
392 _("Secure connection with TLS?"))) == -1)
397 if (mutt_socket_write (conn, "STARTTLS\r\n") < 0)
398 return smtp_err_write;
399 if ((rc = smtp_get_resp (conn)))
402 if (mutt_ssl_starttls (conn))
404 mutt_error (_("Could not negotiate TLS connection"));
409 /* re-EHLO to get authentication mechanisms */
410 if ((rc = smtp_helo (conn)))
415 if (conn->account.flags & M_ACCT_USER)
417 if (!mutt_bit_isset (Capabilities, AUTH))
419 mutt_error (_("SMTP server does not support authentication"));
425 return smtp_auth (conn);
427 mutt_error (_("SMTP authentication requires SASL"));
430 #endif /* USE_SASL */
437 static int smtp_auth (CONNECTION* conn)
439 int r = SMTP_AUTH_UNAVAIL;
441 if (SmtpAuthenticators && *SmtpAuthenticators)
443 char* methods = safe_strdup (SmtpAuthenticators);
447 for (method = methods; method; method = delim)
449 delim = strchr (method, ':');
455 dprint (2, (debugfile, "smtp_authenticate: Trying method %s\n", method));
457 r = smtp_auth_sasl (conn, method);
459 if (r == SMTP_AUTH_FAIL && delim)
461 mutt_error (_("%s authentication failed, trying next method"), method);
464 else if (r != SMTP_AUTH_UNAVAIL)
471 r = smtp_auth_sasl (conn, AuthMechs);
473 if (r != SMTP_AUTH_SUCCESS)
474 mutt_account_unsetpass (&conn->account);
476 if (r == SMTP_AUTH_FAIL)
478 mutt_error (_("SASL authentication failed"));
481 else if (r == SMTP_AUTH_UNAVAIL)
483 mutt_error (_("No authenticators available"));
487 return r == SMTP_AUTH_SUCCESS ? 0 : -1;
490 static int smtp_auth_sasl (CONNECTION* conn, const char* mechlist)
492 sasl_conn_t* saslconn;
493 sasl_interact_t* interaction = NULL;
495 const char* data = NULL;
497 char buf[HUGE_STRING];
500 if (mutt_sasl_client_new (conn, &saslconn) < 0)
501 return SMTP_AUTH_FAIL;
505 rc = sasl_client_start (saslconn, mechlist, &interaction, &data, &len, &mech);
506 if (rc == SASL_INTERACT)
507 mutt_sasl_interact (interaction);
509 while (rc == SASL_INTERACT);
511 if (rc != SASL_OK && rc != SASL_CONTINUE)
513 dprint (2, (debugfile, "smtp_auth_sasl: %s unavailable\n", mech));
514 sasl_dispose (&saslconn);
515 return SMTP_AUTH_UNAVAIL;
518 if (!option(OPTNOCURSES))
519 mutt_message (_("Authenticating (%s)..."), mech);
521 snprintf (buf, sizeof (buf), "AUTH %s", mech);
524 safe_strcat (buf, sizeof (buf), " ");
525 if (sasl_encode64 (data, len, buf + mutt_strlen (buf),
526 sizeof (buf) - mutt_strlen (buf), &len) != SASL_OK)
528 dprint (1, (debugfile, "smtp_auth_sasl: error base64-encoding client response.\n"));
532 safe_strcat (buf, sizeof (buf), "\r\n");
535 if (mutt_socket_write (conn, buf) < 0)
537 if (mutt_socket_readln (buf, sizeof (buf), conn) < 0)
541 if (rc != smtp_ready)
544 if (sasl_decode64 (buf+4, strlen (buf+4), buf, sizeof (buf), &len) != SASL_OK)
546 dprint (1, (debugfile, "smtp_auth_sasl: error base64-decoding server response.\n"));
552 saslrc = sasl_client_step (saslconn, buf, len, &interaction, &data, &len);
553 if (saslrc == SASL_INTERACT)
554 mutt_sasl_interact (interaction);
556 while (saslrc == SASL_INTERACT);
560 if (sasl_encode64 (data, len, buf, sizeof (buf), &len) != SASL_OK)
562 dprint (1, (debugfile, "smtp_auth_sasl: error base64-encoding client response.\n"));
566 strfcpy (buf + len, "\r\n", sizeof (buf) - len);
567 } while (rc == smtp_ready);
569 if (smtp_success (rc))
571 mutt_sasl_setup_conn (conn, saslconn);
572 return SMTP_AUTH_SUCCESS;
576 sasl_dispose (&saslconn);
577 return SMTP_AUTH_FAIL;
579 #endif /* USE_SASL */