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