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