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