1 /* 2 * Copyright (c) 1983 Eric P. Allman 3 * Copyright (c) 1988, 1993 4 * The Regents of the University of California. All rights reserved. 5 * 6 * %sccs.include.redist.c% 7 */ 8 9 # include "sendmail.h" 10 11 #ifndef lint 12 #ifdef SMTP 13 static char sccsid[] = "@(#)usersmtp.c 8.11 (Berkeley) 10/15/93 (with SMTP)"; 14 #else 15 static char sccsid[] = "@(#)usersmtp.c 8.11 (Berkeley) 10/15/93 (without SMTP)"; 16 #endif 17 #endif /* not lint */ 18 19 # include <sysexits.h> 20 # include <errno.h> 21 22 # ifdef SMTP 23 24 /* 25 ** USERSMTP -- run SMTP protocol from the user end. 26 ** 27 ** This protocol is described in RFC821. 28 */ 29 30 #define REPLYTYPE(r) ((r) / 100) /* first digit of reply code */ 31 #define REPLYCLASS(r) (((r) / 10) % 10) /* second digit of reply code */ 32 #define SMTPCLOSING 421 /* "Service Shutting Down" */ 33 34 char SmtpMsgBuffer[MAXLINE]; /* buffer for commands */ 35 char SmtpReplyBuffer[MAXLINE]; /* buffer for replies */ 36 char SmtpError[MAXLINE] = ""; /* save failure error messages */ 37 int SmtpPid; /* pid of mailer */ 38 bool SmtpNeedIntro; /* need "while talking" in transcript */ 39 40 #ifdef __STDC__ 41 extern smtpmessage(char *f, MAILER *m, MCI *mci, ...); 42 #endif 43 /* 44 ** SMTPINIT -- initialize SMTP. 45 ** 46 ** Opens the connection and sends the initial protocol. 47 ** 48 ** Parameters: 49 ** m -- mailer to create connection to. 50 ** pvp -- pointer to parameter vector to pass to 51 ** the mailer. 52 ** 53 ** Returns: 54 ** none. 55 ** 56 ** Side Effects: 57 ** creates connection and sends initial protocol. 58 */ 59 60 smtpinit(m, mci, e) 61 struct mailer *m; 62 register MCI *mci; 63 ENVELOPE *e; 64 { 65 register int r; 66 register char *p; 67 extern void esmtp_check(); 68 extern void helo_options(); 69 70 if (tTd(18, 1)) 71 { 72 printf("smtpinit "); 73 mci_dump(mci); 74 } 75 76 /* 77 ** Open the connection to the mailer. 78 */ 79 80 SmtpError[0] = '\0'; 81 CurHostName = mci->mci_host; /* XXX UGLY XXX */ 82 SmtpNeedIntro = TRUE; 83 switch (mci->mci_state) 84 { 85 case MCIS_ACTIVE: 86 /* need to clear old information */ 87 smtprset(m, mci, e); 88 /* fall through */ 89 90 case MCIS_OPEN: 91 return; 92 93 case MCIS_ERROR: 94 case MCIS_SSD: 95 /* shouldn't happen */ 96 smtpquit(m, mci, e); 97 /* fall through */ 98 99 case MCIS_CLOSED: 100 syserr("451 smtpinit: state CLOSED"); 101 return; 102 103 case MCIS_OPENING: 104 break; 105 } 106 107 mci->mci_state = MCIS_OPENING; 108 109 /* 110 ** Get the greeting message. 111 ** This should appear spontaneously. Give it five minutes to 112 ** happen. 113 */ 114 115 SmtpPhase = mci->mci_phase = "client greeting"; 116 setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase); 117 r = reply(m, mci, e, TimeOuts.to_initial, esmtp_check); 118 if (r < 0 || REPLYTYPE(r) != 2) 119 goto tempfail1; 120 121 /* 122 ** Send the HELO command. 123 ** My mother taught me to always introduce myself. 124 */ 125 126 if (bitnset(M_ESMTP, m->m_flags)) 127 mci->mci_flags |= MCIF_ESMTP; 128 129 tryhelo: 130 if (bitset(MCIF_ESMTP, mci->mci_flags)) 131 { 132 smtpmessage("EHLO %s", m, mci, MyHostName); 133 SmtpPhase = mci->mci_phase = "client EHLO"; 134 } 135 else 136 { 137 smtpmessage("HELO %s", m, mci, MyHostName); 138 SmtpPhase = mci->mci_phase = "client HELO"; 139 } 140 setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase); 141 r = reply(m, mci, e, TimeOuts.to_helo, helo_options); 142 if (r < 0) 143 goto tempfail1; 144 else if (REPLYTYPE(r) == 5) 145 { 146 if (bitset(MCIF_ESMTP, mci->mci_flags)) 147 { 148 /* try old SMTP instead */ 149 mci->mci_flags &= ~MCIF_ESMTP; 150 goto tryhelo; 151 } 152 goto unavailable; 153 } 154 else if (REPLYTYPE(r) != 2) 155 goto tempfail1; 156 157 /* 158 ** Check to see if we actually ended up talking to ourself. 159 ** This means we didn't know about an alias or MX, or we managed 160 ** to connect to an echo server. 161 ** 162 ** If this code remains at all, "CheckLoopBack" should be 163 ** a mailer flag. This is a MAYBENEXTRELEASE feature. 164 */ 165 166 p = strchr(&SmtpReplyBuffer[4], ' '); 167 if (p != NULL) 168 *p = '\0'; 169 if (CheckLoopBack && strcasecmp(&SmtpReplyBuffer[4], MyHostName) == 0) 170 { 171 syserr("553 %s config error: mail loops back to myself", 172 MyHostName); 173 mci->mci_exitstat = EX_CONFIG; 174 mci->mci_errno = 0; 175 smtpquit(m, mci, e); 176 return; 177 } 178 179 /* 180 ** If this is expected to be another sendmail, send some internal 181 ** commands. 182 */ 183 184 if (bitnset(M_INTERNAL, m->m_flags)) 185 { 186 /* tell it to be verbose */ 187 smtpmessage("VERB", m, mci); 188 r = reply(m, mci, e, TimeOuts.to_miscshort, NULL); 189 if (r < 0) 190 goto tempfail2; 191 } 192 193 mci->mci_state = MCIS_OPEN; 194 return; 195 196 tempfail1: 197 tempfail2: 198 mci->mci_exitstat = EX_TEMPFAIL; 199 if (mci->mci_errno == 0) 200 mci->mci_errno = errno; 201 if (mci->mci_state != MCIS_CLOSED) 202 smtpquit(m, mci, e); 203 return; 204 205 unavailable: 206 mci->mci_exitstat = EX_UNAVAILABLE; 207 mci->mci_errno = errno; 208 smtpquit(m, mci, e); 209 return; 210 } 211 /* 212 ** ESMTP_CHECK -- check to see if this implementation likes ESMTP protocol 213 ** 214 ** 215 ** Parameters: 216 ** line -- the response line. 217 ** m -- the mailer. 218 ** mci -- the mailer connection info. 219 ** e -- the envelope. 220 ** 221 ** Returns: 222 ** none. 223 */ 224 225 void 226 esmtp_check(line, m, mci, e) 227 char *line; 228 MAILER *m; 229 register MCI *mci; 230 ENVELOPE *e; 231 { 232 if (strlen(line) < 5) 233 return; 234 line += 4; 235 if (strncmp(line, "ESMTP ", 6) == 0) 236 mci->mci_flags |= MCIF_ESMTP; 237 } 238 /* 239 ** HELO_OPTIONS -- process the options on a HELO line. 240 ** 241 ** Parameters: 242 ** line -- the response line. 243 ** m -- the mailer. 244 ** mci -- the mailer connection info. 245 ** e -- the envelope. 246 ** 247 ** Returns: 248 ** none. 249 */ 250 251 void 252 helo_options(line, m, mci, e) 253 char *line; 254 MAILER *m; 255 register MCI *mci; 256 ENVELOPE *e; 257 { 258 register char *p; 259 260 if (strlen(line) < 5) 261 return; 262 line += 4; 263 p = strchr(line, ' '); 264 if (p != NULL) 265 *p++ = '\0'; 266 if (strcasecmp(line, "size") == 0) 267 { 268 mci->mci_flags |= MCIF_SIZE; 269 if (p != NULL) 270 mci->mci_maxsize = atol(p); 271 } 272 else if (strcasecmp(line, "8bitmime") == 0) 273 mci->mci_flags |= MCIF_8BITMIME; 274 else if (strcasecmp(line, "expn") == 0) 275 mci->mci_flags |= MCIF_EXPN; 276 } 277 /* 278 ** SMTPMAILFROM -- send MAIL command 279 ** 280 ** Parameters: 281 ** m -- the mailer. 282 ** mci -- the mailer connection structure. 283 ** e -- the envelope (including the sender to specify). 284 */ 285 286 smtpmailfrom(m, mci, e) 287 struct mailer *m; 288 MCI *mci; 289 ENVELOPE *e; 290 { 291 int r; 292 char buf[MAXNAME]; 293 char optbuf[MAXLINE]; 294 295 if (tTd(18, 2)) 296 printf("smtpmailfrom: CurHost=%s\n", CurHostName); 297 298 /* set up appropriate options to include */ 299 if (bitset(MCIF_SIZE, mci->mci_flags) && e->e_msgsize > 0) 300 sprintf(optbuf, " SIZE=%ld", e->e_msgsize); 301 else 302 strcpy(optbuf, ""); 303 304 /* 305 ** Send the MAIL command. 306 ** Designates the sender. 307 */ 308 309 mci->mci_state = MCIS_ACTIVE; 310 311 if (bitset(EF_RESPONSE, e->e_flags) && 312 !bitnset(M_NO_NULL_FROM, m->m_flags)) 313 (void) strcpy(buf, ""); 314 else 315 expand("\201g", buf, &buf[sizeof buf - 1], e); 316 if (e->e_from.q_mailer == LocalMailer || 317 !bitnset(M_FROMPATH, m->m_flags)) 318 { 319 smtpmessage("MAIL From:<%s>%s", m, mci, buf, optbuf); 320 } 321 else 322 { 323 smtpmessage("MAIL From:<@%s%c%s>%s", m, mci, MyHostName, 324 buf[0] == '@' ? ',' : ':', buf, optbuf); 325 } 326 SmtpPhase = mci->mci_phase = "client MAIL"; 327 setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase); 328 r = reply(m, mci, e, TimeOuts.to_mail, NULL); 329 if (r < 0 || REPLYTYPE(r) == 4) 330 { 331 mci->mci_exitstat = EX_TEMPFAIL; 332 mci->mci_errno = errno; 333 smtpquit(m, mci, e); 334 return EX_TEMPFAIL; 335 } 336 else if (r == 250) 337 { 338 mci->mci_exitstat = EX_OK; 339 return EX_OK; 340 } 341 else if (r == 552) 342 { 343 /* signal service unavailable */ 344 mci->mci_exitstat = EX_UNAVAILABLE; 345 smtpquit(m, mci, e); 346 return EX_UNAVAILABLE; 347 } 348 349 #ifdef LOG 350 if (LogLevel > 1) 351 { 352 syslog(LOG_CRIT, "%s: SMTP MAIL protocol error: %s", 353 e->e_id, SmtpReplyBuffer); 354 } 355 #endif 356 357 /* protocol error -- close up */ 358 smtpquit(m, mci, e); 359 mci->mci_exitstat = EX_PROTOCOL; 360 return EX_PROTOCOL; 361 } 362 /* 363 ** SMTPRCPT -- designate recipient. 364 ** 365 ** Parameters: 366 ** to -- address of recipient. 367 ** m -- the mailer we are sending to. 368 ** mci -- the connection info for this transaction. 369 ** e -- the envelope for this transaction. 370 ** 371 ** Returns: 372 ** exit status corresponding to recipient status. 373 ** 374 ** Side Effects: 375 ** Sends the mail via SMTP. 376 */ 377 378 smtprcpt(to, m, mci, e) 379 ADDRESS *to; 380 register MAILER *m; 381 MCI *mci; 382 ENVELOPE *e; 383 { 384 register int r; 385 386 smtpmessage("RCPT To:<%s>", m, mci, to->q_user); 387 388 SmtpPhase = mci->mci_phase = "client RCPT"; 389 setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase); 390 r = reply(m, mci, e, TimeOuts.to_rcpt, NULL); 391 if (r < 0 || REPLYTYPE(r) == 4) 392 return (EX_TEMPFAIL); 393 else if (REPLYTYPE(r) == 2) 394 return (EX_OK); 395 else if (r == 550 || r == 551 || r == 553) 396 return (EX_NOUSER); 397 else if (r == 552 || r == 554) 398 return (EX_UNAVAILABLE); 399 400 #ifdef LOG 401 if (LogLevel > 1) 402 { 403 syslog(LOG_CRIT, "%s: SMTP RCPT protocol error: %s", 404 e->e_id, SmtpReplyBuffer); 405 } 406 #endif 407 408 return (EX_PROTOCOL); 409 } 410 /* 411 ** SMTPDATA -- send the data and clean up the transaction. 412 ** 413 ** Parameters: 414 ** m -- mailer being sent to. 415 ** e -- the envelope for this message. 416 ** 417 ** Returns: 418 ** exit status corresponding to DATA command. 419 ** 420 ** Side Effects: 421 ** none. 422 */ 423 424 static jmp_buf CtxDataTimeout; 425 static int datatimeout(); 426 427 smtpdata(m, mci, e) 428 struct mailer *m; 429 register MCI *mci; 430 register ENVELOPE *e; 431 { 432 register int r; 433 register EVENT *ev; 434 time_t timeout; 435 436 /* 437 ** Send the data. 438 ** First send the command and check that it is ok. 439 ** Then send the data. 440 ** Follow it up with a dot to terminate. 441 ** Finally get the results of the transaction. 442 */ 443 444 /* send the command and check ok to proceed */ 445 smtpmessage("DATA", m, mci); 446 SmtpPhase = mci->mci_phase = "client DATA 354"; 447 setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase); 448 r = reply(m, mci, e, TimeOuts.to_datainit, NULL); 449 if (r < 0 || REPLYTYPE(r) == 4) 450 { 451 smtpquit(m, mci, e); 452 return (EX_TEMPFAIL); 453 } 454 else if (r == 554) 455 { 456 smtprset(m, mci, e); 457 return (EX_UNAVAILABLE); 458 } 459 else if (r != 354) 460 { 461 #ifdef LOG 462 if (LogLevel > 1) 463 { 464 syslog(LOG_CRIT, "%s: SMTP DATA-1 protocol error: %s", 465 e->e_id, SmtpReplyBuffer); 466 } 467 #endif 468 smtprset(m, mci, e); 469 return (EX_PROTOCOL); 470 } 471 472 /* 473 ** Set timeout around data writes. Make it at least large 474 ** enough for DNS timeouts on all recipients plus some fudge 475 ** factor. The main thing is that it should not be infinite. 476 */ 477 478 if (setjmp(CtxDataTimeout) != 0) 479 { 480 mci->mci_errno = errno; 481 mci->mci_exitstat = EX_TEMPFAIL; 482 mci->mci_state = MCIS_ERROR; 483 syserr("451 timeout writing message to %s", mci->mci_host); 484 smtpquit(m, mci, e); 485 return EX_TEMPFAIL; 486 } 487 488 timeout = e->e_msgsize / 16; 489 if (timeout < (time_t) 60) 490 timeout = (time_t) 60; 491 timeout += e->e_nrcpts * 90; 492 ev = setevent(timeout, datatimeout, 0); 493 494 /* now output the actual message */ 495 (*e->e_puthdr)(mci->mci_out, m, e); 496 putline("\n", mci->mci_out, m); 497 (*e->e_putbody)(mci->mci_out, m, e, NULL); 498 499 clrevent(ev); 500 501 if (ferror(mci->mci_out)) 502 { 503 /* error during processing -- don't send the dot */ 504 mci->mci_errno = EIO; 505 mci->mci_exitstat = EX_IOERR; 506 mci->mci_state = MCIS_ERROR; 507 smtpquit(m, mci, e); 508 return EX_IOERR; 509 } 510 511 /* terminate the message */ 512 fprintf(mci->mci_out, ".%s", m->m_eol); 513 if (TrafficLogFile != NULL) 514 fprintf(TrafficLogFile, "%05d >>> .\n", getpid()); 515 if (Verbose) 516 nmessage(">>> ."); 517 518 /* check for the results of the transaction */ 519 SmtpPhase = mci->mci_phase = "client DATA 250"; 520 setproctitle("%s %s: %s", e->e_id, CurHostName, mci->mci_phase); 521 r = reply(m, mci, e, TimeOuts.to_datafinal, NULL); 522 if (r < 0) 523 { 524 smtpquit(m, mci, e); 525 return (EX_TEMPFAIL); 526 } 527 mci->mci_state = MCIS_OPEN; 528 e->e_statmsg = newstr(&SmtpReplyBuffer[4]); 529 if (REPLYTYPE(r) == 4) 530 return (EX_TEMPFAIL); 531 else if (r == 250) 532 return (EX_OK); 533 else if (r == 552 || r == 554) 534 return (EX_UNAVAILABLE); 535 #ifdef LOG 536 if (LogLevel > 1) 537 { 538 syslog(LOG_CRIT, "%s: SMTP DATA-2 protocol error: %s", 539 e->e_id, SmtpReplyBuffer); 540 } 541 #endif 542 return (EX_PROTOCOL); 543 } 544 545 546 static int 547 datatimeout() 548 { 549 longjmp(CtxDataTimeout, 1); 550 } 551 /* 552 ** SMTPQUIT -- close the SMTP connection. 553 ** 554 ** Parameters: 555 ** m -- a pointer to the mailer. 556 ** 557 ** Returns: 558 ** none. 559 ** 560 ** Side Effects: 561 ** sends the final protocol and closes the connection. 562 */ 563 564 smtpquit(m, mci, e) 565 register MAILER *m; 566 register MCI *mci; 567 ENVELOPE *e; 568 { 569 int i; 570 571 /* send the quit message if we haven't gotten I/O error */ 572 if (mci->mci_state != MCIS_ERROR) 573 { 574 SmtpPhase = "client QUIT"; 575 smtpmessage("QUIT", m, mci); 576 (void) reply(m, mci, e, TimeOuts.to_quit, NULL); 577 if (mci->mci_state == MCIS_CLOSED) 578 return; 579 } 580 581 /* now actually close the connection and pick up the zombie */ 582 i = endmailer(mci, e, m->m_argv); 583 if (i != EX_OK) 584 syserr("451 smtpquit %s: stat %d", m->m_argv[0], i); 585 } 586 /* 587 ** SMTPRSET -- send a RSET (reset) command 588 */ 589 590 smtprset(m, mci, e) 591 register MAILER *m; 592 register MCI *mci; 593 ENVELOPE *e; 594 { 595 int r; 596 597 SmtpPhase = "client RSET"; 598 smtpmessage("RSET", m, mci); 599 r = reply(m, mci, e, TimeOuts.to_rset, NULL); 600 if (r < 0) 601 mci->mci_state = MCIS_ERROR; 602 else if (REPLYTYPE(r) == 2) 603 { 604 mci->mci_state = MCIS_OPEN; 605 return; 606 } 607 smtpquit(m, mci, e); 608 } 609 /* 610 ** SMTPPROBE -- check the connection state 611 */ 612 613 smtpprobe(mci) 614 register MCI *mci; 615 { 616 int r; 617 MAILER *m = mci->mci_mailer; 618 extern ENVELOPE BlankEnvelope; 619 ENVELOPE *e = &BlankEnvelope; 620 621 SmtpPhase = "client probe"; 622 smtpmessage("RSET", m, mci); 623 r = reply(m, mci, e, TimeOuts.to_miscshort, NULL); 624 if (r < 0 || REPLYTYPE(r) != 2) 625 smtpquit(m, mci, e); 626 return r; 627 } 628 /* 629 ** REPLY -- read arpanet reply 630 ** 631 ** Parameters: 632 ** m -- the mailer we are reading the reply from. 633 ** mci -- the mailer connection info structure. 634 ** e -- the current envelope. 635 ** timeout -- the timeout for reads. 636 ** pfunc -- processing function for second and subsequent 637 ** lines of response -- if null, no special 638 ** processing is done. 639 ** 640 ** Returns: 641 ** reply code it reads. 642 ** 643 ** Side Effects: 644 ** flushes the mail file. 645 */ 646 647 reply(m, mci, e, timeout, pfunc) 648 MAILER *m; 649 MCI *mci; 650 ENVELOPE *e; 651 time_t timeout; 652 void (*pfunc)(); 653 { 654 register char *bufp; 655 register int r; 656 bool firstline = TRUE; 657 char junkbuf[MAXLINE]; 658 659 if (mci->mci_out != NULL) 660 (void) fflush(mci->mci_out); 661 662 if (tTd(18, 1)) 663 printf("reply\n"); 664 665 /* 666 ** Read the input line, being careful not to hang. 667 */ 668 669 for (bufp = SmtpReplyBuffer;; bufp = junkbuf) 670 { 671 register char *p; 672 extern time_t curtime(); 673 674 /* actually do the read */ 675 if (e->e_xfp != NULL) 676 (void) fflush(e->e_xfp); /* for debugging */ 677 678 /* if we are in the process of closing just give the code */ 679 if (mci->mci_state == MCIS_CLOSED) 680 return (SMTPCLOSING); 681 682 if (mci->mci_out != NULL) 683 fflush(mci->mci_out); 684 685 /* get the line from the other side */ 686 p = sfgets(bufp, MAXLINE, mci->mci_in, timeout, SmtpPhase); 687 mci->mci_lastuse = curtime(); 688 689 if (p == NULL) 690 { 691 bool oldholderrs; 692 extern char MsgBuf[]; /* err.c */ 693 694 /* if the remote end closed early, fake an error */ 695 if (errno == 0) 696 # ifdef ECONNRESET 697 errno = ECONNRESET; 698 # else /* ECONNRESET */ 699 errno = EPIPE; 700 # endif /* ECONNRESET */ 701 702 mci->mci_errno = errno; 703 mci->mci_exitstat = EX_TEMPFAIL; 704 oldholderrs = HoldErrs; 705 HoldErrs = TRUE; 706 usrerr("451 reply: read error from %s", mci->mci_host); 707 708 /* if debugging, pause so we can see state */ 709 if (tTd(18, 100)) 710 pause(); 711 mci->mci_state = MCIS_ERROR; 712 smtpquit(m, mci, e); 713 #ifdef XDEBUG 714 { 715 char wbuf[MAXLINE]; 716 char *p = wbuf; 717 if (e->e_to != NULL) 718 { 719 sprintf(p, "%s... ", e->e_to); 720 p += strlen(p); 721 } 722 sprintf(p, "reply(%s) during %s", 723 mci->mci_host, SmtpPhase); 724 checkfd012(wbuf); 725 } 726 #endif 727 HoldErrs = oldholderrs; 728 return (-1); 729 } 730 fixcrlf(bufp, TRUE); 731 732 /* EHLO failure is not a real error */ 733 if (e->e_xfp != NULL && (bufp[0] == '4' || 734 (bufp[0] == '5' && strncmp(SmtpMsgBuffer, "EHLO", 4) != 0))) 735 { 736 /* serious error -- log the previous command */ 737 if (SmtpNeedIntro) 738 { 739 /* inform user who we are chatting with */ 740 fprintf(CurEnv->e_xfp, 741 "... while talking to %s:\n", 742 CurHostName); 743 SmtpNeedIntro = FALSE; 744 } 745 if (SmtpMsgBuffer[0] != '\0') 746 fprintf(e->e_xfp, ">>> %s\n", SmtpMsgBuffer); 747 SmtpMsgBuffer[0] = '\0'; 748 749 /* now log the message as from the other side */ 750 fprintf(e->e_xfp, "<<< %s\n", bufp); 751 } 752 753 /* display the input for verbose mode */ 754 if (Verbose) 755 nmessage("050 %s", bufp); 756 757 /* process the line */ 758 if (pfunc != NULL && !firstline) 759 (*pfunc)(bufp, m, mci, e); 760 761 firstline = FALSE; 762 763 /* if continuation is required, we can go on */ 764 if (bufp[3] == '-') 765 continue; 766 767 /* ignore improperly formated input */ 768 if (!(isascii(bufp[0]) && isdigit(bufp[0]))) 769 continue; 770 771 /* decode the reply code */ 772 r = atoi(bufp); 773 774 /* extra semantics: 0xx codes are "informational" */ 775 if (r >= 100) 776 break; 777 } 778 779 /* 780 ** Now look at SmtpReplyBuffer -- only care about the first 781 ** line of the response from here on out. 782 */ 783 784 /* save temporary failure messages for posterity */ 785 if (SmtpReplyBuffer[0] == '4' && SmtpError[0] == '\0') 786 (void) strcpy(SmtpError, SmtpReplyBuffer); 787 788 /* reply code 421 is "Service Shutting Down" */ 789 if (r == SMTPCLOSING && mci->mci_state != MCIS_SSD) 790 { 791 /* send the quit protocol */ 792 mci->mci_state = MCIS_SSD; 793 smtpquit(m, mci, e); 794 } 795 796 return (r); 797 } 798 /* 799 ** SMTPMESSAGE -- send message to server 800 ** 801 ** Parameters: 802 ** f -- format 803 ** m -- the mailer to control formatting. 804 ** a, b, c -- parameters 805 ** 806 ** Returns: 807 ** none. 808 ** 809 ** Side Effects: 810 ** writes message to mci->mci_out. 811 */ 812 813 /*VARARGS1*/ 814 #ifdef __STDC__ 815 smtpmessage(char *f, MAILER *m, MCI *mci, ...) 816 #else 817 smtpmessage(f, m, mci, va_alist) 818 char *f; 819 MAILER *m; 820 MCI *mci; 821 va_dcl 822 #endif 823 { 824 VA_LOCAL_DECL 825 826 VA_START(mci); 827 (void) vsprintf(SmtpMsgBuffer, f, ap); 828 VA_END; 829 830 if (tTd(18, 1) || Verbose) 831 nmessage(">>> %s", SmtpMsgBuffer); 832 if (TrafficLogFile != NULL) 833 fprintf(TrafficLogFile, "%05d >>> %s\n", getpid(), SmtpMsgBuffer); 834 if (mci->mci_out != NULL) 835 { 836 fprintf(mci->mci_out, "%s%s", SmtpMsgBuffer, 837 m == NULL ? "\r\n" : m->m_eol); 838 } 839 else if (tTd(18, 1)) 840 { 841 printf("smtpmessage: NULL mci_out\n"); 842 } 843 } 844 845 # endif /* SMTP */ 846