mutt stable branch with some hacks
at jcs 716 lines 17 kB view raw
1/* mutt - text oriented MIME mail user agent 2 * Copyright (C) 2002 Michael R. Elkins <me@mutt.org> 3 * Copyright (C) 2005-2009 Brendan Cully <brendan@kublai.com> 4 * 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. 9 * 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. 14 * 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. 18 */ 19 20/* This file contains code for direct SMTP delivery of email messages. */ 21 22#if HAVE_CONFIG_H 23#include "config.h" 24#endif 25 26#include "mutt.h" 27#include "mutt_curses.h" 28#include "mutt_socket.h" 29#ifdef USE_SSL 30# include "mutt_ssl.h" 31#endif 32#ifdef USE_SASL 33#include "mutt_sasl.h" 34 35#include <sasl/sasl.h> 36#include <sasl/saslutil.h> 37#endif 38 39#include <netdb.h> 40#include <netinet/in.h> 41#include <stdio.h> 42#include <sys/stat.h> 43 44#define smtp_success(x) ((x)/100 == 2) 45#define smtp_ready 334 46#define smtp_continue 354 47 48#define smtp_err_read -2 49#define smtp_err_write -3 50#define smtp_err_code -4 51 52#define SMTP_PORT 25 53#define SMTPS_PORT 465 54 55#define SMTP_AUTH_SUCCESS 0 56#define SMTP_AUTH_UNAVAIL 1 57#define SMTP_AUTH_FAIL -1 58 59enum { 60 STARTTLS, 61 AUTH, 62 DSN, 63 EIGHTBITMIME, 64 SMTPUTF8, 65 66 CAPMAX 67}; 68 69static int smtp_auth (CONNECTION* conn); 70static int smtp_auth_oauth (CONNECTION* conn); 71#ifdef USE_SASL 72static int smtp_auth_sasl (CONNECTION* conn, const char* mechanisms); 73#endif 74 75static int smtp_fill_account (ACCOUNT* account); 76static int smtp_open (CONNECTION* conn); 77 78static int Esmtp = 0; 79static char* AuthMechs = NULL; 80static unsigned char Capabilities[(CAPMAX + 7)/ 8]; 81 82static int smtp_code (char *buf, size_t len, int *n) 83{ 84 char code[4]; 85 86 if (len < 4) 87 return -1; 88 code[0] = buf[0]; 89 code[1] = buf[1]; 90 code[2] = buf[2]; 91 code[3] = 0; 92 if (mutt_atoi (code, n) < 0) 93 return -1; 94 return 0; 95} 96 97/* Reads a command response from the SMTP server. 98 * Returns: 99 * 0 on success (2xx code) or continue (354 code) 100 * -1 write error, or any other response code 101 */ 102static int 103smtp_get_resp (CONNECTION * conn) 104{ 105 int n; 106 char buf[1024]; 107 108 do 109 { 110 n = mutt_socket_readln (buf, sizeof (buf), conn); 111 if (n < 4) 112 { 113 /* read error, or no response code */ 114 return smtp_err_read; 115 } 116 117 if (!ascii_strncasecmp ("8BITMIME", buf + 4, 8)) 118 mutt_bit_set (Capabilities, EIGHTBITMIME); 119 else if (!ascii_strncasecmp ("AUTH ", buf + 4, 5)) 120 { 121 mutt_bit_set (Capabilities, AUTH); 122 FREE (&AuthMechs); 123 AuthMechs = safe_strdup (buf + 9); 124 } 125 else if (!ascii_strncasecmp ("DSN", buf + 4, 3)) 126 mutt_bit_set (Capabilities, DSN); 127 else if (!ascii_strncasecmp ("STARTTLS", buf + 4, 8)) 128 mutt_bit_set (Capabilities, STARTTLS); 129 else if (!ascii_strncasecmp ("SMTPUTF8", buf + 4, 8)) 130 mutt_bit_set (Capabilities, SMTPUTF8); 131 132 if (smtp_code (buf, n, &n) < 0) 133 return smtp_err_code; 134 135 } while (buf[3] == '-'); 136 137 if (smtp_success (n) || n == smtp_continue) 138 return 0; 139 140 mutt_error (_("SMTP session failed: %s"), buf); 141 return -1; 142} 143 144static int 145smtp_rcpt_to (CONNECTION * conn, const ADDRESS * a) 146{ 147 char buf[1024]; 148 int r; 149 150 while (a) 151 { 152 /* weed out group mailboxes, since those are for display only */ 153 if (!a->mailbox || a->group) 154 { 155 a = a->next; 156 continue; 157 } 158 if (mutt_bit_isset (Capabilities, DSN) && DsnNotify) 159 snprintf (buf, sizeof (buf), "RCPT TO:<%s> NOTIFY=%s\r\n", 160 a->mailbox, DsnNotify); 161 else 162 snprintf (buf, sizeof (buf), "RCPT TO:<%s>\r\n", a->mailbox); 163 if (mutt_socket_write (conn, buf) == -1) 164 return smtp_err_write; 165 if ((r = smtp_get_resp (conn))) 166 return r; 167 a = a->next; 168 } 169 170 return 0; 171} 172 173static int 174smtp_data (CONNECTION * conn, const char *msgfile) 175{ 176 char buf[1024]; 177 FILE *fp = 0; 178 progress_t progress; 179 struct stat st; 180 int r, term = 0; 181 size_t buflen = 0; 182 183 fp = fopen (msgfile, "r"); 184 if (!fp) 185 { 186 mutt_error (_("SMTP session failed: unable to open %s"), msgfile); 187 return -1; 188 } 189 stat (msgfile, &st); 190 unlink (msgfile); 191 mutt_progress_init (&progress, _("Sending message..."), MUTT_PROGRESS_SIZE, 192 NetInc, st.st_size); 193 194 snprintf (buf, sizeof (buf), "DATA\r\n"); 195 if (mutt_socket_write (conn, buf) == -1) 196 { 197 safe_fclose (&fp); 198 return smtp_err_write; 199 } 200 if ((r = smtp_get_resp (conn))) 201 { 202 safe_fclose (&fp); 203 return r; 204 } 205 206 while (fgets (buf, sizeof (buf) - 1, fp)) 207 { 208 buflen = mutt_strlen (buf); 209 term = buflen && buf[buflen-1] == '\n'; 210 if (term && (buflen == 1 || buf[buflen - 2] != '\r')) 211 snprintf (buf + buflen - 1, sizeof (buf) - buflen + 1, "\r\n"); 212 if (buf[0] == '.') 213 { 214 if (mutt_socket_write_d (conn, ".", -1, MUTT_SOCK_LOG_FULL) == -1) 215 { 216 safe_fclose (&fp); 217 return smtp_err_write; 218 } 219 } 220 if (mutt_socket_write_d (conn, buf, -1, MUTT_SOCK_LOG_FULL) == -1) 221 { 222 safe_fclose (&fp); 223 return smtp_err_write; 224 } 225 mutt_progress_update (&progress, ftell (fp), -1); 226 } 227 if (!term && buflen && 228 mutt_socket_write_d (conn, "\r\n", -1, MUTT_SOCK_LOG_FULL) == -1) 229 { 230 safe_fclose (&fp); 231 return smtp_err_write; 232 } 233 safe_fclose (&fp); 234 235 /* terminate the message body */ 236 if (mutt_socket_write (conn, ".\r\n") == -1) 237 return smtp_err_write; 238 239 if ((r = smtp_get_resp (conn))) 240 return r; 241 242 return 0; 243} 244 245 246/* Returns 1 if a contains at least one 8-bit character, 0 if none do. 247 */ 248static int address_uses_unicode(const char *a) 249{ 250 if (!a) 251 return 0; 252 253 while (*a) 254 { 255 if ((unsigned char) *a & (1<<7)) 256 return 1; 257 a++; 258 } 259 260 return 0; 261} 262 263 264/* Returns 1 if any address in a contains at least one 8-bit 265 * character, 0 if none do. 266 */ 267static int addresses_use_unicode(const ADDRESS* a) 268{ 269 while (a) 270 { 271 if (a->mailbox && !a->group && address_uses_unicode(a->mailbox)) 272 return 1; 273 a = a->next; 274 } 275 return 0; 276} 277 278 279int 280mutt_smtp_send (const ADDRESS* from, const ADDRESS* to, const ADDRESS* cc, 281 const ADDRESS* bcc, const char *msgfile, int eightbit) 282{ 283 CONNECTION *conn; 284 ACCOUNT account; 285 const char* envfrom; 286 char buf[1024]; 287 int ret = -1; 288 289 /* it might be better to synthesize an envelope from from user and host 290 * but this condition is most likely arrived at accidentally */ 291 if (EnvFrom) 292 envfrom = EnvFrom->mailbox; 293 else if (from) 294 envfrom = from->mailbox; 295 else 296 { 297 mutt_error (_("No from address given")); 298 return -1; 299 } 300 301 if (smtp_fill_account (&account) < 0) 302 return ret; 303 304 if (!(conn = mutt_conn_find (NULL, &account))) 305 return -1; 306 307 Esmtp = eightbit; 308 309 do 310 { 311 /* send our greeting */ 312 if (( ret = smtp_open (conn))) 313 break; 314 FREE (&AuthMechs); 315 316 /* send the sender's address */ 317 ret = snprintf (buf, sizeof (buf), "MAIL FROM:<%s>", envfrom); 318 if (eightbit && mutt_bit_isset (Capabilities, EIGHTBITMIME)) 319 { 320 safe_strncat (buf, sizeof (buf), " BODY=8BITMIME", 15); 321 ret += 14; 322 } 323 if (DsnReturn && mutt_bit_isset (Capabilities, DSN)) 324 ret += snprintf (buf + ret, sizeof (buf) - ret, " RET=%s", DsnReturn); 325 if (mutt_bit_isset (Capabilities, SMTPUTF8) && 326 (address_uses_unicode(envfrom) || 327 addresses_use_unicode(to) || 328 addresses_use_unicode(cc) || 329 addresses_use_unicode(bcc))) 330 ret += snprintf (buf + ret, sizeof (buf) - ret, " SMTPUTF8"); 331 safe_strncat (buf, sizeof (buf), "\r\n", 3); 332 if (mutt_socket_write (conn, buf) == -1) 333 { 334 ret = smtp_err_write; 335 break; 336 } 337 if ((ret = smtp_get_resp (conn))) 338 break; 339 340 /* send the recipient list */ 341 if ((ret = smtp_rcpt_to (conn, to)) || (ret = smtp_rcpt_to (conn, cc)) 342 || (ret = smtp_rcpt_to (conn, bcc))) 343 break; 344 345 /* send the message data */ 346 if ((ret = smtp_data (conn, msgfile))) 347 break; 348 349 mutt_socket_write (conn, "QUIT\r\n"); 350 351 ret = 0; 352 } 353 while (0); 354 355 if (conn) 356 mutt_socket_close (conn); 357 358 if (ret == smtp_err_read) 359 mutt_error (_("SMTP session failed: read error")); 360 else if (ret == smtp_err_write) 361 mutt_error (_("SMTP session failed: write error")); 362 else if (ret == smtp_err_code) 363 mutt_error (_("Invalid server response")); 364 365 return ret; 366} 367 368static int smtp_fill_account (ACCOUNT* account) 369{ 370 static unsigned short SmtpPort = 0; 371 372 struct servent* service; 373 ciss_url_t url; 374 char* urlstr; 375 376 account->flags = 0; 377 account->port = 0; 378 account->type = MUTT_ACCT_TYPE_SMTP; 379 380 urlstr = safe_strdup (SmtpUrl); 381 url_parse_ciss (&url, urlstr); 382 if ((url.scheme != U_SMTP && url.scheme != U_SMTPS) 383 || mutt_account_fromurl (account, &url) < 0) 384 { 385 FREE (&urlstr); 386 mutt_error (_("Invalid SMTP URL: %s"), SmtpUrl); 387 mutt_sleep (1); 388 return -1; 389 } 390 FREE (&urlstr); 391 392 if (url.scheme == U_SMTPS) 393 account->flags |= MUTT_ACCT_SSL; 394 395 if (!account->port) 396 { 397 if (account->flags & MUTT_ACCT_SSL) 398 account->port = SMTPS_PORT; 399 else 400 { 401 if (!SmtpPort) 402 { 403 service = getservbyname ("smtp", "tcp"); 404 if (service) 405 SmtpPort = ntohs (service->s_port); 406 else 407 SmtpPort = SMTP_PORT; 408 dprint (3, (debugfile, "Using default SMTP port %d\n", SmtpPort)); 409 } 410 account->port = SmtpPort; 411 } 412 } 413 414 return 0; 415} 416 417static int smtp_helo (CONNECTION* conn) 418{ 419 char buf[LONG_STRING]; 420 const char* fqdn; 421 422 memset (Capabilities, 0, sizeof (Capabilities)); 423 424 if (!Esmtp) 425 { 426 /* if TLS or AUTH are requested, use EHLO */ 427 if (conn->account.flags & MUTT_ACCT_USER) 428 Esmtp = 1; 429#ifdef USE_SSL 430 if (option (OPTSSLFORCETLS) || quadoption (OPT_SSLSTARTTLS) != MUTT_NO) 431 Esmtp = 1; 432#endif 433 } 434 435 if (!(fqdn = mutt_fqdn (0))) 436 fqdn = NONULL (Hostname); 437 438 snprintf (buf, sizeof (buf), "%s %s\r\n", Esmtp ? "EHLO" : "HELO", fqdn); 439 /* XXX there should probably be a wrapper in mutt_socket.c that 440 * repeatedly calls conn->write until all data is sent. This 441 * currently doesn't check for a short write. 442 */ 443 if (mutt_socket_write (conn, buf) == -1) 444 return smtp_err_write; 445 return smtp_get_resp (conn); 446} 447 448static int smtp_open (CONNECTION* conn) 449{ 450 int rc; 451 452 if (mutt_socket_open (conn)) 453 return -1; 454 455 /* get greeting string */ 456 if ((rc = smtp_get_resp (conn))) 457 return rc; 458 459 if ((rc = smtp_helo (conn))) 460 return rc; 461 462#ifdef USE_SSL 463 if (conn->ssf) 464 rc = MUTT_NO; 465 else if (option (OPTSSLFORCETLS)) 466 rc = MUTT_YES; 467 else if (mutt_bit_isset (Capabilities, STARTTLS) && 468 (rc = query_quadoption (OPT_SSLSTARTTLS, 469 _("Secure connection with TLS?"))) == -1) 470 return rc; 471 472 if (rc == MUTT_YES) 473 { 474 if (mutt_socket_write (conn, "STARTTLS\r\n") < 0) 475 return smtp_err_write; 476 if ((rc = smtp_get_resp (conn))) 477 return rc; 478 479 if (mutt_ssl_starttls (conn)) 480 { 481 mutt_error (_("Could not negotiate TLS connection")); 482 mutt_sleep (1); 483 return -1; 484 } 485 486 /* re-EHLO to get authentication mechanisms */ 487 if ((rc = smtp_helo (conn))) 488 return rc; 489 } 490#endif 491 492 if (conn->account.flags & MUTT_ACCT_USER) 493 { 494 if (!mutt_bit_isset (Capabilities, AUTH)) 495 { 496 mutt_error (_("SMTP server does not support authentication")); 497 mutt_sleep (1); 498 return -1; 499 } 500 501 return smtp_auth (conn); 502 } 503 504 return 0; 505} 506 507static int smtp_auth (CONNECTION* conn) 508{ 509 int r = SMTP_AUTH_UNAVAIL; 510 511 if (SmtpAuthenticators) 512 { 513 char* methods = safe_strdup (SmtpAuthenticators); 514 char* method; 515 char* delim; 516 517 for (method = methods; method; method = delim) 518 { 519 delim = strchr (method, ':'); 520 if (delim) 521 *delim++ = '\0'; 522 if (! method[0]) 523 continue; 524 525 dprint (2, (debugfile, "smtp_authenticate: Trying method %s\n", method)); 526 527 if (!strcmp (method, "oauthbearer")) 528 { 529 r = smtp_auth_oauth (conn); 530 } 531 else 532 { 533#ifdef USE_SASL 534 r = smtp_auth_sasl (conn, method); 535#else 536 mutt_error (_("SMTP authentication method %s requires SASL"), method); 537 mutt_sleep (1); 538 continue; 539#endif 540 } 541 if (r == SMTP_AUTH_FAIL && delim) 542 { 543 mutt_error (_("%s authentication failed, trying next method"), method); 544 mutt_sleep (1); 545 } 546 else if (r != SMTP_AUTH_UNAVAIL) 547 break; 548 } 549 550 FREE (&methods); 551 } 552 else 553 { 554#ifdef USE_SASL 555 r = smtp_auth_sasl (conn, AuthMechs); 556#else 557 mutt_error (_("SMTP authentication requires SASL")); 558 mutt_sleep (1); 559 r = SMTP_AUTH_UNAVAIL; 560#endif 561 } 562 563 if (r != SMTP_AUTH_SUCCESS) 564 mutt_account_unsetpass (&conn->account); 565 566 if (r == SMTP_AUTH_FAIL) 567 { 568 mutt_error (_("SASL authentication failed")); 569 mutt_sleep (1); 570 } 571 else if (r == SMTP_AUTH_UNAVAIL) 572 { 573 mutt_error (_("No authenticators available")); 574 mutt_sleep (1); 575 } 576 577 return r == SMTP_AUTH_SUCCESS ? 0 : -1; 578} 579 580#ifdef USE_SASL 581static int smtp_auth_sasl (CONNECTION* conn, const char* mechlist) 582{ 583 sasl_conn_t* saslconn; 584 sasl_interact_t* interaction = NULL; 585 const char* mech; 586 const char* data = NULL; 587 unsigned int len; 588 char *buf = NULL; 589 size_t bufsize = 0; 590 int rc, saslrc; 591 592 if (mutt_sasl_client_new (conn, &saslconn) < 0) 593 return SMTP_AUTH_FAIL; 594 595 do 596 { 597 rc = sasl_client_start (saslconn, mechlist, &interaction, &data, &len, &mech); 598 if (rc == SASL_INTERACT) 599 mutt_sasl_interact (interaction); 600 } 601 while (rc == SASL_INTERACT); 602 603 if (rc != SASL_OK && rc != SASL_CONTINUE) 604 { 605 dprint (2, (debugfile, "smtp_auth_sasl: %s unavailable\n", mech)); 606 sasl_dispose (&saslconn); 607 return SMTP_AUTH_UNAVAIL; 608 } 609 610 if (!option(OPTNOCURSES)) 611 mutt_message (_("Authenticating (%s)..."), mech); 612 613 bufsize = ((len * 2) > LONG_STRING) ? (len * 2) : LONG_STRING; 614 buf = safe_malloc (bufsize); 615 616 snprintf (buf, bufsize, "AUTH %s", mech); 617 if (len) 618 { 619 safe_strcat (buf, bufsize, " "); 620 if (sasl_encode64 (data, len, buf + mutt_strlen (buf), 621 bufsize - mutt_strlen (buf), &len) != SASL_OK) 622 { 623 dprint (1, (debugfile, "smtp_auth_sasl: error base64-encoding client response.\n")); 624 goto fail; 625 } 626 } 627 safe_strcat (buf, bufsize, "\r\n"); 628 629 do 630 { 631 if (mutt_socket_write (conn, buf) < 0) 632 goto fail; 633 if ((rc = mutt_socket_readln (buf, bufsize, conn)) < 0) 634 goto fail; 635 if (smtp_code (buf, rc, &rc) < 0) 636 goto fail; 637 638 if (rc != smtp_ready) 639 break; 640 641 if (sasl_decode64 (buf+4, strlen (buf+4), buf, bufsize - 1, &len) != SASL_OK) 642 { 643 dprint (1, (debugfile, "smtp_auth_sasl: error base64-decoding server response.\n")); 644 goto fail; 645 } 646 647 do 648 { 649 saslrc = sasl_client_step (saslconn, buf, len, &interaction, &data, &len); 650 if (saslrc == SASL_INTERACT) 651 mutt_sasl_interact (interaction); 652 } 653 while (saslrc == SASL_INTERACT); 654 655 if (len) 656 { 657 if ((len * 2) > bufsize) 658 { 659 bufsize = len * 2; 660 safe_realloc (&buf, bufsize); 661 } 662 if (sasl_encode64 (data, len, buf, bufsize, &len) != SASL_OK) 663 { 664 dprint (1, (debugfile, "smtp_auth_sasl: error base64-encoding client response.\n")); 665 goto fail; 666 } 667 } 668 strfcpy (buf + len, "\r\n", bufsize - len); 669 } while (rc == smtp_ready && saslrc != SASL_FAIL); 670 671 if (smtp_success (rc)) 672 { 673 mutt_sasl_setup_conn (conn, saslconn); 674 FREE (&buf); 675 return SMTP_AUTH_SUCCESS; 676 } 677 678fail: 679 sasl_dispose (&saslconn); 680 FREE (&buf); 681 return SMTP_AUTH_FAIL; 682} 683#endif /* USE_SASL */ 684 685 686/* smtp_auth_oauth: AUTH=OAUTHBEARER support. See RFC 7628 */ 687static int smtp_auth_oauth (CONNECTION* conn) 688{ 689 char* ibuf = NULL; 690 char* oauthbearer = NULL; 691 int ilen; 692 int rc; 693 694 mutt_message _("Authenticating (OAUTHBEARER)..."); 695 696 /* We get the access token from the smtp_oauth_refresh_command */ 697 oauthbearer = mutt_account_getoauthbearer (&conn->account); 698 if (oauthbearer == NULL) 699 return SMTP_AUTH_FAIL; 700 701 ilen = strlen (oauthbearer) + 30; 702 ibuf = safe_malloc (ilen); 703 704 snprintf (ibuf, ilen, "AUTH OAUTHBEARER %s\r\n", oauthbearer); 705 706 rc = mutt_socket_write (conn, ibuf); 707 FREE (&oauthbearer); 708 FREE (&ibuf); 709 710 if (rc == -1) 711 return SMTP_AUTH_FAIL; 712 if (smtp_get_resp (conn) != 0) 713 return SMTP_AUTH_FAIL; 714 715 return SMTP_AUTH_SUCCESS; 716}