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