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