1: /*
   2: **  Sendmail
   3: **  Copyright (c) 1983  Eric P. Allman
   4: **  Berkeley, California
   5: **
   6: **  Copyright (c) 1983 Regents of the University of California.
   7: **  All rights reserved.  The Berkeley software License Agreement
   8: **  specifies the terms and conditions for redistribution.
   9: */
  10: 
  11: #if !defined(lint) && !defined(NOSCCS)
  12: static char SccsId[] = "@(#)deliver.c	5.10.1 (2.11BSD GTE) 3/6/95";
  13: #endif
  14: 
  15: # include <signal.h>
  16: # include <errno.h>
  17: # include "sendmail.h"
  18: # include <sys/stat.h>
  19: # include <netdb.h>
  20: 
  21: /*
  22: **  DELIVER -- Deliver a message to a list of addresses.
  23: **
  24: **	This routine delivers to everyone on the same host as the
  25: **	user on the head of the list.  It is clever about mailers
  26: **	that don't handle multiple users.  It is NOT guaranteed
  27: **	that it will deliver to all these addresses however -- so
  28: **	deliver should be called once for each address on the
  29: **	list.
  30: **
  31: **	Parameters:
  32: **		e -- the envelope to deliver.
  33: **		firstto -- head of the address list to deliver to.
  34: **
  35: **	Returns:
  36: **		zero -- successfully delivered.
  37: **		else -- some failure, see ExitStat for more info.
  38: **
  39: **	Side Effects:
  40: **		The standard input is passed off to someone.
  41: */
  42: 
  43: deliver(e, firstto)
  44:     register ENVELOPE *e;
  45:     ADDRESS *firstto;
  46: {
  47:     char *host;         /* host being sent to */
  48:     char *user;         /* user being sent to */
  49:     char **pvp;
  50:     register char **mvp;
  51:     register char *p;
  52:     register MAILER *m;     /* mailer for this recipient */
  53:     ADDRESS *ctladdr;
  54:     register ADDRESS *to = firstto;
  55:     bool clever = FALSE;        /* running user smtp to this mailer */
  56:     ADDRESS *tochain = NULL;    /* chain of users in this mailer call */
  57:     register int rcode;     /* response code */
  58:     char *pv[MAXPV+1];
  59:     char tobuf[MAXLINE-50];     /* text line of to people */
  60:     char buf[MAXNAME];
  61:     char tfrombuf[MAXNAME];     /* translated from person */
  62:     extern bool checkcompat();
  63:     extern ADDRESS *getctladdr();
  64:     extern char *remotename();
  65: 
  66:     errno = 0;
  67:     if (bitset(QDONTSEND, to->q_flags))
  68:         return (0);
  69: 
  70:     m = to->q_mailer;
  71:     host = to->q_host;
  72: 
  73: # ifdef DEBUG
  74:     if (tTd(10, 1))
  75:         printf("\n--deliver, mailer=%d, host=`%s', first user=`%s'\n",
  76:             m->m_mno, host, to->q_user);
  77: # endif DEBUG
  78: 
  79:     /*
  80: 	**  If this mailer is expensive, and if we don't want to make
  81: 	**  connections now, just mark these addresses and return.
  82: 	**	This is useful if we want to batch connections to
  83: 	**	reduce load.  This will cause the messages to be
  84: 	**	queued up, and a daemon will come along to send the
  85: 	**	messages later.
  86: 	**		This should be on a per-mailer basis.
  87: 	*/
  88: 
  89:     if (NoConnect && !QueueRun && bitnset(M_EXPENSIVE, m->m_flags) &&
  90:         !Verbose)
  91:     {
  92:         for (; to != NULL; to = to->q_next)
  93:         {
  94:             if (bitset(QDONTSEND, to->q_flags) || to->q_mailer != m)
  95:                 continue;
  96:             to->q_flags |= QQUEUEUP|QDONTSEND;
  97:             e->e_to = to->q_paddr;
  98:             message(Arpa_Info, "queued");
  99:             if (LogLevel > 4)
 100:                 logdelivery("queued");
 101:         }
 102:         e->e_to = NULL;
 103:         return (0);
 104:     }
 105: 
 106:     /*
 107: 	**  Do initial argv setup.
 108: 	**	Insert the mailer name.  Notice that $x expansion is
 109: 	**	NOT done on the mailer name.  Then, if the mailer has
 110: 	**	a picky -f flag, we insert it as appropriate.  This
 111: 	**	code does not check for 'pv' overflow; this places a
 112: 	**	manifest lower limit of 4 for MAXPV.
 113: 	**		The from address rewrite is expected to make
 114: 	**		the address relative to the other end.
 115: 	*/
 116: 
 117:     /* rewrite from address, using rewriting rules */
 118:     expand("\001f", buf, &buf[sizeof buf - 1], e);
 119:     (void) strcpy(tfrombuf, remotename(buf, m, TRUE, TRUE));
 120: 
 121:     define('g', tfrombuf, e);       /* translated sender address */
 122:     define('h', host, e);           /* to host */
 123:     Errors = 0;
 124:     pvp = pv;
 125:     *pvp++ = m->m_argv[0];
 126: 
 127:     /* insert -f or -r flag as appropriate */
 128:     if (FromFlag && (bitnset(M_FOPT, m->m_flags) || bitnset(M_ROPT, m->m_flags)))
 129:     {
 130:         if (bitnset(M_FOPT, m->m_flags))
 131:             *pvp++ = "-f";
 132:         else
 133:             *pvp++ = "-r";
 134:         expand("\001g", buf, &buf[sizeof buf - 1], e);
 135:         *pvp++ = newstr(buf);
 136:     }
 137: 
 138:     /*
 139: 	**  Append the other fixed parts of the argv.  These run
 140: 	**  up to the first entry containing "$u".  There can only
 141: 	**  be one of these, and there are only a few more slots
 142: 	**  in the pv after it.
 143: 	*/
 144: 
 145:     for (mvp = m->m_argv; (p = *++mvp) != NULL; )
 146:     {
 147:         while ((p = index(p, '\001')) != NULL)
 148:             if (*++p == 'u')
 149:                 break;
 150:         if (p != NULL)
 151:             break;
 152: 
 153:         /* this entry is safe -- go ahead and process it */
 154:         expand(*mvp, buf, &buf[sizeof buf - 1], e);
 155:         *pvp++ = newstr(buf);
 156:         if (pvp >= &pv[MAXPV - 3])
 157:         {
 158:             syserr("Too many parameters to %s before $u", pv[0]);
 159:             return (-1);
 160:         }
 161:     }
 162: 
 163:     /*
 164: 	**  If we have no substitution for the user name in the argument
 165: 	**  list, we know that we must supply the names otherwise -- and
 166: 	**  SMTP is the answer!!
 167: 	*/
 168: 
 169:     if (*mvp == NULL)
 170:     {
 171:         /* running SMTP */
 172: # ifdef SMTP
 173:         clever = TRUE;
 174:         *pvp = NULL;
 175: # else SMTP
 176:         /* oops!  we don't implement SMTP */
 177:         syserr("SMTP style mailer");
 178:         return (EX_SOFTWARE);
 179: # endif SMTP
 180:     }
 181: 
 182:     /*
 183: 	**  At this point *mvp points to the argument with $u.  We
 184: 	**  run through our address list and append all the addresses
 185: 	**  we can.  If we run out of space, do not fret!  We can
 186: 	**  always send another copy later.
 187: 	*/
 188: 
 189:     tobuf[0] = '\0';
 190:     e->e_to = tobuf;
 191:     ctladdr = NULL;
 192:     for (; to != NULL; to = to->q_next)
 193:     {
 194:         /* avoid sending multiple recipients to dumb mailers */
 195:         if (tobuf[0] != '\0' && !bitnset(M_MUSER, m->m_flags))
 196:             break;
 197: 
 198:         /* if already sent or not for this host, don't send */
 199:         if (bitset(QDONTSEND, to->q_flags) ||
 200:             strcmp(to->q_host, host) != 0 ||
 201:             to->q_mailer != firstto->q_mailer)
 202:             continue;
 203: 
 204:         /* avoid overflowing tobuf */
 205:         if ((int)sizeof tobuf - (strlen(to->q_paddr) + strlen(tobuf) + 2) < 0)
 206:             break;
 207: 
 208: # ifdef DEBUG
 209:         if (tTd(10, 1))
 210:         {
 211:             printf("\nsend to ");
 212:             printaddr(to, FALSE);
 213:         }
 214: # endif DEBUG
 215: 
 216:         /* compute effective uid/gid when sending */
 217:         if (to->q_mailer == ProgMailer)
 218:             ctladdr = getctladdr(to);
 219: 
 220:         user = to->q_user;
 221:         e->e_to = to->q_paddr;
 222:         to->q_flags |= QDONTSEND;
 223: 
 224:         /*
 225: 		**  Check to see that these people are allowed to
 226: 		**  talk to each other.
 227: 		*/
 228: 
 229:         if (m->m_maxsize != 0 && e->e_msgsize > m->m_maxsize)
 230:         {
 231:             usrerr("Message is too large; %ld bytes max", m->m_maxsize);
 232:             NoReturn = TRUE;
 233:             giveresponse(EX_UNAVAILABLE, m, e);
 234:             continue;
 235:         }
 236:         if (!checkcompat(to))
 237:         {
 238:             giveresponse(EX_UNAVAILABLE, m, e);
 239:             continue;
 240:         }
 241: 
 242:         /*
 243: 		**  Strip quote bits from names if the mailer is dumb
 244: 		**	about them.
 245: 		*/
 246: 
 247:         if (bitnset(M_STRIPQ, m->m_flags))
 248:         {
 249:             stripquotes(user, TRUE);
 250:             stripquotes(host, TRUE);
 251:         }
 252:         else
 253:         {
 254:             stripquotes(user, FALSE);
 255:             stripquotes(host, FALSE);
 256:         }
 257: 
 258:         /* hack attack -- delivermail compatibility */
 259:         if (m == ProgMailer && *user == '|')
 260:             user++;
 261: 
 262:         /*
 263: 		**  If an error message has already been given, don't
 264: 		**	bother to send to this address.
 265: 		**
 266: 		**	>>>>>>>>>> This clause assumes that the local mailer
 267: 		**	>> NOTE >> cannot do any further aliasing; that
 268: 		**	>>>>>>>>>> function is subsumed by sendmail.
 269: 		*/
 270: 
 271:         if (bitset(QBADADDR|QQUEUEUP, to->q_flags))
 272:             continue;
 273: 
 274:         /* save statistics.... */
 275:         markstats(e, to);
 276: 
 277:         /*
 278: 		**  See if this user name is "special".
 279: 		**	If the user name has a slash in it, assume that this
 280: 		**	is a file -- send it off without further ado.  Note
 281: 		**	that this type of addresses is not processed along
 282: 		**	with the others, so we fudge on the To person.
 283: 		*/
 284: 
 285:         if (m == LocalMailer)
 286:         {
 287:             if (user[0] == '/')
 288:             {
 289:                 rcode = mailfile(user, getctladdr(to));
 290:                 giveresponse(rcode, m, e);
 291:                 continue;
 292:             }
 293:         }
 294: 
 295:         /*
 296: 		**  Address is verified -- add this user to mailer
 297: 		**  argv, and add it to the print list of recipients.
 298: 		*/
 299: 
 300:         /* link together the chain of recipients */
 301:         to->q_tchain = tochain;
 302:         tochain = to;
 303: 
 304:         /* create list of users for error messages */
 305:         (void) strcat(tobuf, ",");
 306:         (void) strcat(tobuf, to->q_paddr);
 307:         define('u', user, e);       /* to user */
 308:         define('z', to->q_home, e); /* user's home */
 309: 
 310:         /*
 311: 		**  Expand out this user into argument list.
 312: 		*/
 313: 
 314:         if (!clever)
 315:         {
 316:             expand(*mvp, buf, &buf[sizeof buf - 1], e);
 317:             *pvp++ = newstr(buf);
 318:             if (pvp >= &pv[MAXPV - 2])
 319:             {
 320:                 /* allow some space for trailing parms */
 321:                 break;
 322:             }
 323:         }
 324:     }
 325: 
 326:     /* see if any addresses still exist */
 327:     if (tobuf[0] == '\0')
 328:     {
 329:         define('g', (char *) NULL, e);
 330:         return (0);
 331:     }
 332: 
 333:     /* print out messages as full list */
 334:     e->e_to = tobuf + 1;
 335: 
 336:     /*
 337: 	**  Fill out any parameters after the $u parameter.
 338: 	*/
 339: 
 340:     while (!clever && *++mvp != NULL)
 341:     {
 342:         expand(*mvp, buf, &buf[sizeof buf - 1], e);
 343:         *pvp++ = newstr(buf);
 344:         if (pvp >= &pv[MAXPV])
 345:             syserr("deliver: pv overflow after $u for %s", pv[0]);
 346:     }
 347:     *pvp++ = NULL;
 348: 
 349:     /*
 350: 	**  Call the mailer.
 351: 	**	The argument vector gets built, pipes
 352: 	**	are created as necessary, and we fork & exec as
 353: 	**	appropriate.
 354: 	**	If we are running SMTP, we just need to clean up.
 355: 	*/
 356: 
 357:     message(Arpa_Info, "Connecting to %s.%s...", host, m->m_name);
 358: 
 359:     if (ctladdr == NULL)
 360:         ctladdr = &e->e_from;
 361: # ifdef SMTP
 362:     if (clever)
 363:     {
 364:         /* send the initial SMTP protocol */
 365:         rcode = smtpinit(m, pv);
 366: 
 367:         if (rcode == EX_OK)
 368:         {
 369:             /* send the recipient list */
 370:             tobuf[0] = '\0';
 371:             for (to = tochain; to != NULL; to = to->q_tchain)
 372:             {
 373:                 int i;
 374: 
 375:                 e->e_to = to->q_paddr;
 376:                 i = smtprcpt(to, m);
 377:                 if (i != EX_OK)
 378:                 {
 379:                     markfailure(e, to, i);
 380:                     giveresponse(i, m, e);
 381:                 }
 382:                 else
 383:                 {
 384:                     (void) strcat(tobuf, ",");
 385:                     (void) strcat(tobuf, to->q_paddr);
 386:                 }
 387:             }
 388: 
 389:             /* now send the data */
 390:             if (tobuf[0] == '\0')
 391:                 e->e_to = NULL;
 392:             else
 393:             {
 394:                 e->e_to = tobuf + 1;
 395:                 rcode = smtpdata(m, e);
 396:             }
 397: 
 398:             /* now close the connection */
 399:             smtpquit(m);
 400:         }
 401:     }
 402:     else
 403: # endif SMTP
 404:         rcode = sendoff(e, m, pv, ctladdr);
 405: 
 406:     /*
 407: 	**  Do final status disposal.
 408: 	**	We check for something in tobuf for the SMTP case.
 409: 	**	If we got a temporary failure, arrange to queue the
 410: 	**		addressees.
 411: 	*/
 412: 
 413:     if (tobuf[0] != '\0')
 414:         giveresponse(rcode, m, e);
 415:     if (rcode != EX_OK)
 416:     {
 417:         for (to = tochain; to != NULL; to = to->q_tchain)
 418:             markfailure(e, to, rcode);
 419:     }
 420: 
 421:     errno = 0;
 422:     define('g', (char *) NULL, e);
 423:     return (rcode);
 424: }
 425: /*
 426: **  MARKFAILURE -- mark a failure on a specific address.
 427: **
 428: **	Parameters:
 429: **		e -- the envelope we are sending.
 430: **		q -- the address to mark.
 431: **		rcode -- the code signifying the particular failure.
 432: **
 433: **	Returns:
 434: **		none.
 435: **
 436: **	Side Effects:
 437: **		marks the address (and possibly the envelope) with the
 438: **			failure so that an error will be returned or
 439: **			the message will be queued, as appropriate.
 440: */
 441: 
 442: markfailure(e, q, rcode)
 443:     register ENVELOPE *e;
 444:     register ADDRESS *q;
 445:     int rcode;
 446: {
 447:     if (rcode == EX_OK)
 448:         return;
 449:     else if (rcode != EX_TEMPFAIL)
 450:         q->q_flags |= QBADADDR;
 451:     else if (curtime() > e->e_ctime + TimeOut)
 452:     {
 453:         extern char *pintvl();
 454:         char buf[MAXLINE];
 455: 
 456:         if (!bitset(EF_TIMEOUT, e->e_flags))
 457:         {
 458:             (void) sprintf(buf, "Cannot send message for %s",
 459:                 pintvl(TimeOut, FALSE));
 460:             if (e->e_message != NULL)
 461:                 free(e->e_message);
 462:             e->e_message = newstr(buf);
 463:             message(Arpa_Info, buf);
 464:         }
 465:         q->q_flags |= QBADADDR;
 466:         e->e_flags |= EF_TIMEOUT;
 467:     }
 468:     else
 469:         q->q_flags |= QQUEUEUP;
 470: }
 471: /*
 472: **  DOFORK -- do a fork, retrying a couple of times on failure.
 473: **
 474: **	This MUST be a macro, since after a vfork we are running
 475: **	two processes on the same stack!!!
 476: **
 477: **	Parameters:
 478: **		none.
 479: **
 480: **	Returns:
 481: **		From a macro???  You've got to be kidding!
 482: **
 483: **	Side Effects:
 484: **		Modifies the ==> LOCAL <== variable 'pid', leaving:
 485: **			pid of child in parent, zero in child.
 486: **			-1 on unrecoverable error.
 487: **
 488: **	Notes:
 489: **		I'm awfully sorry this looks so awful.  That's
 490: **		vfork for you.....
 491: */
 492: 
 493: # define NFORKTRIES 5
 494: # ifdef VMUNIX
 495: # define XFORK  vfork
 496: # else VMUNIX
 497: # define XFORK  fork
 498: # endif VMUNIX
 499: 
 500: # define DOFORK(fORKfN) \
 501: {\
 502:     register int i;\
 503: \
 504:     for (i = NFORKTRIES; --i >= 0; )\
 505:     {\
 506:         pid = fORKfN();\
 507:         if (pid >= 0)\
 508:             break;\
 509:         if (i > 0)\
 510:             sleep((unsigned) NFORKTRIES - i);\
 511:     }\
 512: }
 513: /*
 514: **  DOFORK -- simple fork interface to DOFORK.
 515: **
 516: **	Parameters:
 517: **		none.
 518: **
 519: **	Returns:
 520: **		pid of child in parent.
 521: **		zero in child.
 522: **		-1 on error.
 523: **
 524: **	Side Effects:
 525: **		returns twice, once in parent and once in child.
 526: */
 527: 
 528: dofork()
 529: {
 530:     register int pid;
 531: 
 532:     DOFORK(fork);
 533:     return (pid);
 534: }
 535: /*
 536: **  SENDOFF -- send off call to mailer & collect response.
 537: **
 538: **	Parameters:
 539: **		e -- the envelope to mail.
 540: **		m -- mailer descriptor.
 541: **		pvp -- parameter vector to send to it.
 542: **		ctladdr -- an address pointer controlling the
 543: **			user/groupid etc. of the mailer.
 544: **
 545: **	Returns:
 546: **		exit status of mailer.
 547: **
 548: **	Side Effects:
 549: **		none.
 550: */
 551: 
 552: sendoff(e, m, pvp, ctladdr)
 553:     register ENVELOPE *e;
 554:     MAILER *m;
 555:     char **pvp;
 556:     ADDRESS *ctladdr;
 557: {
 558:     auto FILE *mfile;
 559:     auto FILE *rfile;
 560:     register int i;
 561:     int pid;
 562: 
 563:     /*
 564: 	**  Create connection to mailer.
 565: 	*/
 566: 
 567:     pid = openmailer(m, pvp, ctladdr, FALSE, &mfile, &rfile);
 568:     if (pid < 0)
 569:         return (-1);
 570: 
 571:     /*
 572: 	**  Format and send message.
 573: 	*/
 574: 
 575:     putfromline(mfile, m);
 576:     (*e->e_puthdr)(mfile, m, e);
 577:     putline("\n", mfile, m);
 578:     (*e->e_putbody)(mfile, m, e);
 579:     (void) fclose(mfile);
 580: 
 581:     i = endmailer(pid, pvp[0]);
 582: 
 583:     /* arrange a return receipt if requested */
 584:     if (e->e_receiptto != NULL && bitnset(M_LOCAL, m->m_flags))
 585:     {
 586:         e->e_flags |= EF_SENDRECEIPT;
 587:         /* do we want to send back more info? */
 588:     }
 589: 
 590:     return (i);
 591: }
 592: /*
 593: **  ENDMAILER -- Wait for mailer to terminate.
 594: **
 595: **	We should never get fatal errors (e.g., segmentation
 596: **	violation), so we report those specially.  For other
 597: **	errors, we choose a status message (into statmsg),
 598: **	and if it represents an error, we print it.
 599: **
 600: **	Parameters:
 601: **		pid -- pid of mailer.
 602: **		name -- name of mailer (for error messages).
 603: **
 604: **	Returns:
 605: **		exit code of mailer.
 606: **
 607: **	Side Effects:
 608: **		none.
 609: */
 610: 
 611: endmailer(pid, name)
 612:     int pid;
 613:     char *name;
 614: {
 615:     int st;
 616: 
 617:     /* in the IPC case there is nothing to wait for */
 618:     if (pid == 0)
 619:         return (EX_OK);
 620: 
 621:     /* wait for the mailer process to die and collect status */
 622:     st = waitfor(pid);
 623:     if (st == -1)
 624:     {
 625:         syserr("endmailer %s: wait", name);
 626:         return (EX_SOFTWARE);
 627:     }
 628: 
 629:     /* see if it died a horrid death */
 630:     if ((st & 0377) != 0)
 631:     {
 632:         syserr("mailer %s died with signal %o", name, st);
 633:         ExitStat = EX_TEMPFAIL;
 634:         return (EX_TEMPFAIL);
 635:     }
 636: 
 637:     /* normal death -- return status */
 638:     st = (st >> 8) & 0377;
 639:     return (st);
 640: }
 641: /*
 642: **  OPENMAILER -- open connection to mailer.
 643: **
 644: **	Parameters:
 645: **		m -- mailer descriptor.
 646: **		pvp -- parameter vector to pass to mailer.
 647: **		ctladdr -- controlling address for user.
 648: **		clever -- create a full duplex connection.
 649: **		pmfile -- pointer to mfile (to mailer) connection.
 650: **		prfile -- pointer to rfile (from mailer) connection.
 651: **
 652: **	Returns:
 653: **		pid of mailer ( > 0 ).
 654: **		-1 on error.
 655: **		zero on an IPC connection.
 656: **
 657: **	Side Effects:
 658: **		creates a mailer in a subprocess.
 659: */
 660: 
 661: openmailer(m, pvp, ctladdr, clever, pmfile, prfile)
 662:     MAILER *m;
 663:     char **pvp;
 664:     ADDRESS *ctladdr;
 665:     bool clever;
 666:     FILE **pmfile;
 667:     FILE **prfile;
 668: {
 669:     int pid;
 670:     int mpvect[2];
 671:     int rpvect[2];
 672:     FILE *mfile;
 673:     FILE *rfile;
 674:     extern FILE *fdopen();
 675: 
 676: # ifdef DEBUG
 677:     if (tTd(11, 1))
 678:     {
 679:         printf("openmailer:");
 680:         printav(pvp);
 681:     }
 682: # endif DEBUG
 683:     errno = 0;
 684: 
 685:     CurHostName = m->m_mailer;
 686: 
 687:     /*
 688: 	**  Deal with the special case of mail handled through an IPC
 689: 	**  connection.
 690: 	**	In this case we don't actually fork.  We must be
 691: 	**	running SMTP for this to work.  We will return a
 692: 	**	zero pid to indicate that we are running IPC.
 693: 	**  We also handle a debug version that just talks to stdin/out.
 694: 	*/
 695: 
 696: #ifdef DEBUG
 697:     /* check for Local Person Communication -- not for mortals!!! */
 698:     if (strcmp(m->m_mailer, "[LPC]") == 0)
 699:     {
 700:         *pmfile = stdout;
 701:         *prfile = stdin;
 702:         return (0);
 703:     }
 704: #endif DEBUG
 705: 
 706:     if (strcmp(m->m_mailer, "[IPC]") == 0)
 707:     {
 708: #ifdef HOSTINFO
 709:         register STAB *st;
 710:         extern STAB *stab();
 711: #endif HOSTINFO
 712: #ifdef DAEMON
 713:         register int i;
 714:         register u_short port;
 715: 
 716:         CurHostName = pvp[1];
 717:         if (!clever)
 718:             syserr("non-clever IPC");
 719:         if (pvp[2] != NULL)
 720:             port = atoi(pvp[2]);
 721:         else
 722:             port = 0;
 723: #ifdef HOSTINFO
 724:         /* see if we have already determined that this host is fried */
 725:         st = stab(pvp[1], ST_HOST, ST_FIND);
 726:         if (st == NULL || st->s_host.ho_exitstat == EX_OK)
 727:             i = makeconnection(pvp[1], port, pmfile, prfile);
 728:         else
 729:         {
 730:             i = st->s_host.ho_exitstat;
 731:             errno = st->s_host.ho_errno;
 732:         }
 733: #else HOSTINFO
 734:         i = makeconnection(pvp[1], port, pmfile, prfile);
 735: #endif HOSTINFO
 736:         if (i != EX_OK)
 737:         {
 738: #ifdef HOSTINFO
 739:             /* enter status of this host */
 740:             if (st == NULL)
 741:                 st = stab(pvp[1], ST_HOST, ST_ENTER);
 742:             st->s_host.ho_exitstat = i;
 743:             st->s_host.ho_errno = errno;
 744: #endif HOSTINFO
 745:             ExitStat = i;
 746:             return (-1);
 747:         }
 748:         else
 749:             return (0);
 750: #else DAEMON
 751:         syserr("openmailer: no IPC");
 752:         return (-1);
 753: #endif DAEMON
 754:     }
 755: 
 756:     /* create a pipe to shove the mail through */
 757:     if (pipe(mpvect) < 0)
 758:     {
 759:         syserr("openmailer: pipe (to mailer)");
 760:         return (-1);
 761:     }
 762: 
 763: #ifdef SMTP
 764:     /* if this mailer speaks smtp, create a return pipe */
 765:     if (clever && pipe(rpvect) < 0)
 766:     {
 767:         syserr("openmailer: pipe (from mailer)");
 768:         (void) close(mpvect[0]);
 769:         (void) close(mpvect[1]);
 770:         return (-1);
 771:     }
 772: #endif SMTP
 773: 
 774:     /*
 775: 	**  Actually fork the mailer process.
 776: 	**	DOFORK is clever about retrying.
 777: 	**
 778: 	**	Dispose of SIGCHLD signal catchers that may be laying
 779: 	**	around so that endmail will get it.
 780: 	*/
 781: 
 782:     if (CurEnv->e_xfp != NULL)
 783:         (void) fflush(CurEnv->e_xfp);       /* for debugging */
 784:     (void) fflush(stdout);
 785: # ifdef SIGCHLD
 786:     (void) signal(SIGCHLD, SIG_DFL);
 787: # endif SIGCHLD
 788:     DOFORK(XFORK);
 789:     /* pid is set by DOFORK */
 790:     if (pid < 0)
 791:     {
 792:         /* failure */
 793:         syserr("openmailer: cannot fork");
 794:         (void) close(mpvect[0]);
 795:         (void) close(mpvect[1]);
 796: #ifdef SMTP
 797:         if (clever)
 798:         {
 799:             (void) close(rpvect[0]);
 800:             (void) close(rpvect[1]);
 801:         }
 802: #endif SMTP
 803:         return (-1);
 804:     }
 805:     else if (pid == 0)
 806:     {
 807:         int i;
 808:         extern int DtableSize;
 809: 
 810:         /* child -- set up input & exec mailer */
 811:         /* make diagnostic output be standard output */
 812:         (void) signal(SIGINT, SIG_IGN);
 813:         (void) signal(SIGHUP, SIG_IGN);
 814:         (void) signal(SIGTERM, SIG_DFL);
 815: 
 816:         /* arrange to filter standard & diag output of command */
 817:         if (clever)
 818:         {
 819:             (void) close(rpvect[0]);
 820:             (void) close(1);
 821:             (void) dup(rpvect[1]);
 822:             (void) close(rpvect[1]);
 823:         }
 824:         else if (OpMode == MD_SMTP || HoldErrs)
 825:         {
 826:             /* put mailer output in transcript */
 827:             (void) close(1);
 828:             (void) dup(fileno(CurEnv->e_xfp));
 829:         }
 830:         (void) close(2);
 831:         (void) dup(1);
 832: 
 833:         /* arrange to get standard input */
 834:         (void) close(mpvect[1]);
 835:         (void) close(0);
 836:         if (dup(mpvect[0]) < 0)
 837:         {
 838:             syserr("Cannot dup to zero!");
 839:             _exit(EX_OSERR);
 840:         }
 841:         (void) close(mpvect[0]);
 842:         if (!bitnset(M_RESTR, m->m_flags))
 843:         {
 844:             if (ctladdr == NULL || ctladdr->q_uid == 0)
 845:             {
 846:                 (void) setgid(DefGid);
 847:                 (void) setuid(DefUid);
 848:             }
 849:             else
 850:             {
 851:                 (void) setgid(ctladdr->q_gid);
 852:                 (void) setuid(ctladdr->q_uid);
 853:             }
 854:         }
 855: 
 856:         /* arrange for all the files to be closed */
 857:         for (i = 3; i < DtableSize; i++)
 858: #ifdef FIOCLEX
 859:             (void) ioctl(i, FIOCLEX, 0);
 860: #else FIOCLEX
 861:             (void) close(i);
 862: #endif FIOCLEX
 863: 
 864:         /* try to execute the mailer */
 865:         execve(m->m_mailer, pvp, UserEnviron);
 866: 
 867: #ifdef FIOCLEX
 868:         syserr("Cannot exec %s", m->m_mailer);
 869: #else FIOCLEX
 870:         printf("Cannot exec '%s' errno=%d\n", m->m_mailer, errno);
 871:         (void) fflush(stdout);
 872: #endif FIOCLEX
 873:         if (m == LocalMailer || errno == EIO || errno == EAGAIN ||
 874:             errno == ENOMEM || errno == EPROCLIM)
 875:             _exit(EX_TEMPFAIL);
 876:         else
 877:             _exit(EX_UNAVAILABLE);
 878:     }
 879: 
 880:     /*
 881: 	**  Set up return value.
 882: 	*/
 883: 
 884:     (void) close(mpvect[0]);
 885:     mfile = fdopen(mpvect[1], "w");
 886:     if (clever)
 887:     {
 888:         (void) close(rpvect[1]);
 889:         rfile = fdopen(rpvect[0], "r");
 890:     }
 891: 
 892:     *pmfile = mfile;
 893:     *prfile = rfile;
 894: 
 895:     return (pid);
 896: }
 897: /*
 898: **  GIVERESPONSE -- Interpret an error response from a mailer
 899: **
 900: **	Parameters:
 901: **		stat -- the status code from the mailer (high byte
 902: **			only; core dumps must have been taken care of
 903: **			already).
 904: **		m -- the mailer descriptor for this mailer.
 905: **
 906: **	Returns:
 907: **		none.
 908: **
 909: **	Side Effects:
 910: **		Errors may be incremented.
 911: **		ExitStat may be set.
 912: */
 913: 
 914: giveresponse(stat, m, e)
 915:     int stat;
 916:     register MAILER *m;
 917:     ENVELOPE *e;
 918: {
 919:     register char *statmsg;
 920:     extern char *SysExMsg[];
 921:     register int i;
 922:     extern int N_SysEx, h_errno;
 923:     char buf[MAXLINE];
 924: 
 925: #ifdef lint
 926:     if (m == NULL)
 927:         return;
 928: #endif lint
 929: 
 930:     /*
 931: 	**  Compute status message from code.
 932: 	*/
 933: 
 934:     i = stat - EX__BASE;
 935:     if (stat == 0)
 936:         statmsg = "250 Sent";
 937:     else if (i < 0 || i > N_SysEx)
 938:     {
 939:         (void) sprintf(buf, "554 unknown mailer error %d", stat);
 940:         stat = EX_UNAVAILABLE;
 941:         statmsg = buf;
 942:     }
 943:     else if (stat == EX_TEMPFAIL)
 944:     {
 945:         (void) strcpy(buf, SysExMsg[i]);
 946:         if (h_errno == TRY_AGAIN)
 947:         {
 948:             extern char *errstring();
 949: 
 950:             statmsg = errstring(h_errno+MAX_ERRNO);
 951:         }
 952:         else
 953:         {
 954:             if (errno != 0)
 955:             {
 956:                 extern char *errstring();
 957: 
 958:                 statmsg = errstring(errno);
 959:             }
 960:             else
 961:             {
 962: #ifdef SMTP
 963:                 extern char SmtpError[];
 964: 
 965:                 statmsg = SmtpError;
 966: #else SMTP
 967:                 statmsg = NULL;
 968: #endif SMTP
 969:             }
 970:         }
 971:         if (statmsg != NULL && statmsg[0] != '\0')
 972:         {
 973:             (void) strcat(buf, ": ");
 974:             (void) strcat(buf, statmsg);
 975:         }
 976:         statmsg = buf;
 977:     }
 978:     else
 979:     {
 980:         statmsg = SysExMsg[i];
 981:     }
 982: 
 983:     /*
 984: 	**  Print the message as appropriate
 985: 	*/
 986: 
 987:     if (stat == EX_OK || stat == EX_TEMPFAIL)
 988:         message(Arpa_Info, &statmsg[4]);
 989:     else
 990:     {
 991:         char    mbuf[8];
 992: 
 993:         Errors++;
 994:         sprintf(mbuf, "%.3s %%s", statmsg);
 995:         usrerr(mbuf, &statmsg[4]);
 996:     }
 997: 
 998:     /*
 999: 	**  Final cleanup.
1000: 	**	Log a record of the transaction.  Compute the new
1001: 	**	ExitStat -- if we already had an error, stick with
1002: 	**	that.
1003: 	*/
1004: 
1005:     if (LogLevel > ((stat == 0 || stat == EX_TEMPFAIL) ? 3 : 2))
1006:         logdelivery(&statmsg[4]);
1007: 
1008:     if (stat != EX_TEMPFAIL)
1009:         setstat(stat);
1010:     if (stat != EX_OK)
1011:     {
1012:         if (e->e_message != NULL)
1013:             free(e->e_message);
1014:         e->e_message = newstr(&statmsg[4]);
1015:     }
1016:     errno = 0;
1017:     h_errno = 0;
1018: }
1019: /*
1020: **  LOGDELIVERY -- log the delivery in the system log
1021: **
1022: **	Parameters:
1023: **		stat -- the message to print for the status
1024: **
1025: **	Returns:
1026: **		none
1027: **
1028: **	Side Effects:
1029: **		none
1030: */
1031: 
1032: logdelivery(stat)
1033:     char *stat;
1034: {
1035:     extern char *pintvl();
1036: 
1037: # ifdef LOG
1038:     syslog(LOG_INFO, "%s: to=%s, delay=%s, stat=%s", CurEnv->e_id,
1039:            CurEnv->e_to, pintvl(curtime() - CurEnv->e_ctime, TRUE), stat);
1040: # endif LOG
1041: }
1042: /*
1043: **  PUTFROMLINE -- output a UNIX-style from line (or whatever)
1044: **
1045: **	This can be made an arbitrary message separator by changing $l
1046: **
1047: **	One of the ugliest hacks seen by human eyes is contained herein:
1048: **	UUCP wants those stupid "remote from <host>" lines.  Why oh why
1049: **	does a well-meaning programmer such as myself have to deal with
1050: **	this kind of antique garbage????
1051: **
1052: **	Parameters:
1053: **		fp -- the file to output to.
1054: **		m -- the mailer describing this entry.
1055: **
1056: **	Returns:
1057: **		none
1058: **
1059: **	Side Effects:
1060: **		outputs some text to fp.
1061: */
1062: 
1063: putfromline(fp, m)
1064:     register FILE *fp;
1065:     register MAILER *m;
1066: {
1067:     char *template = "\001l\n";
1068:     char buf[MAXLINE];
1069: 
1070:     if (bitnset(M_NHDR, m->m_flags))
1071:         return;
1072: 
1073: # ifdef UGLYUUCP
1074:     if (bitnset(M_UGLYUUCP, m->m_flags))
1075:     {
1076:         char *bang;
1077:         char xbuf[MAXLINE];
1078: 
1079:         expand("\001g", buf, &buf[sizeof buf - 1], CurEnv);
1080:         bang = index(buf, '!');
1081:         if (bang == NULL)
1082:             syserr("No ! in UUCP! (%s)", buf);
1083:         else
1084:         {
1085:             *bang++ = '\0';
1086:             (void) sprintf(xbuf, "From %s  \001d remote from %s\n", bang, buf);
1087:             template = xbuf;
1088:         }
1089:     }
1090: # endif UGLYUUCP
1091:     expand(template, buf, &buf[sizeof buf - 1], CurEnv);
1092:     putline(buf, fp, m);
1093: }
1094: /*
1095: **  PUTBODY -- put the body of a message.
1096: **
1097: **	Parameters:
1098: **		fp -- file to output onto.
1099: **		m -- a mailer descriptor to control output format.
1100: **		e -- the envelope to put out.
1101: **
1102: **	Returns:
1103: **		none.
1104: **
1105: **	Side Effects:
1106: **		The message is written onto fp.
1107: */
1108: 
1109: putbody(fp, m, e)
1110:     FILE *fp;
1111:     MAILER *m;
1112:     register ENVELOPE *e;
1113: {
1114:     char buf[MAXLINE];
1115: 
1116:     /*
1117: 	**  Output the body of the message
1118: 	*/
1119: 
1120:     if (e->e_dfp == NULL)
1121:     {
1122:         if (e->e_df != NULL)
1123:         {
1124:             e->e_dfp = fopen(e->e_df, "r");
1125:             if (e->e_dfp == NULL)
1126:                 syserr("Cannot open %s", e->e_df);
1127:         }
1128:         else
1129:             putline("<<< No Message Collected >>>", fp, m);
1130:     }
1131:     if (e->e_dfp != NULL)
1132:     {
1133:         rewind(e->e_dfp);
1134:         while (!ferror(fp) && fgets(buf, sizeof buf, e->e_dfp) != NULL)
1135:         {
1136:             if (buf[0] == 'F' && bitnset(M_ESCFROM, m->m_flags) &&
1137:                 strncmp(buf, "From", 4) == 0)
1138:                 (void) putc('>', fp);
1139:             putline(buf, fp, m);
1140:         }
1141: 
1142:         if (ferror(e->e_dfp))
1143:         {
1144:             syserr("putbody: read error");
1145:             ExitStat = EX_IOERR;
1146:         }
1147:     }
1148: 
1149:     (void) fflush(fp);
1150:     if (ferror(fp) && errno != EPIPE)
1151:     {
1152:         syserr("putbody: write error");
1153:         ExitStat = EX_IOERR;
1154:     }
1155:     errno = 0;
1156: }
1157: /*
1158: **  MAILFILE -- Send a message to a file.
1159: **
1160: **	If the file has the setuid/setgid bits set, but NO execute
1161: **	bits, sendmail will try to become the owner of that file
1162: **	rather than the real user.  Obviously, this only works if
1163: **	sendmail runs as root.
1164: **
1165: **	This could be done as a subordinate mailer, except that it
1166: **	is used implicitly to save messages in ~/dead.letter.  We
1167: **	view this as being sufficiently important as to include it
1168: **	here.  For example, if the system is dying, we shouldn't have
1169: **	to create another process plus some pipes to save the message.
1170: **
1171: **	Parameters:
1172: **		filename -- the name of the file to send to.
1173: **		ctladdr -- the controlling address header -- includes
1174: **			the userid/groupid to be when sending.
1175: **
1176: **	Returns:
1177: **		The exit code associated with the operation.
1178: **
1179: **	Side Effects:
1180: **		none.
1181: */
1182: 
1183: mailfile(filename, ctladdr)
1184:     char *filename;
1185:     ADDRESS *ctladdr;
1186: {
1187:     register FILE *f;
1188:     register int pid;
1189: 
1190:     /*
1191: 	**  Fork so we can change permissions here.
1192: 	**	Note that we MUST use fork, not vfork, because of
1193: 	**	the complications of calling subroutines, etc.
1194: 	*/
1195: 
1196:     DOFORK(fork);
1197: 
1198:     if (pid < 0)
1199:         return (EX_OSERR);
1200:     else if (pid == 0)
1201:     {
1202:         /* child -- actually write to file */
1203:         struct stat stb;
1204: 
1205:         (void) signal(SIGINT, SIG_DFL);
1206:         (void) signal(SIGHUP, SIG_DFL);
1207:         (void) signal(SIGTERM, SIG_DFL);
1208:         (void) umask(OldUmask);
1209:         if (stat(filename, &stb) < 0)
1210:         {
1211:             errno = 0;
1212:             stb.st_mode = 0666;
1213:         }
1214:         if (bitset(0111, stb.st_mode))
1215:             exit(EX_CANTCREAT);
1216:         if (ctladdr == NULL)
1217:             ctladdr = &CurEnv->e_from;
1218:         if (!bitset(S_ISGID, stb.st_mode) || setgid(stb.st_gid) < 0)
1219:         {
1220:             if (ctladdr->q_uid == 0)
1221:                 (void) setgid(DefGid);
1222:             else
1223:                 (void) setgid(ctladdr->q_gid);
1224:         }
1225:         if (!bitset(S_ISUID, stb.st_mode) || setuid(stb.st_uid) < 0)
1226:         {
1227:             if (ctladdr->q_uid == 0)
1228:                 (void) setuid(DefUid);
1229:             else
1230:                 (void) setuid(ctladdr->q_uid);
1231:         }
1232:         f = dfopen(filename, "a");
1233:         if (f == NULL)
1234:             exit(EX_CANTCREAT);
1235: 
1236:         putfromline(f, ProgMailer);
1237:         (*CurEnv->e_puthdr)(f, ProgMailer, CurEnv);
1238:         putline("\n", f, ProgMailer);
1239:         (*CurEnv->e_putbody)(f, ProgMailer, CurEnv);
1240:         putline("\n", f, ProgMailer);
1241:         (void) fclose(f);
1242:         (void) fflush(stdout);
1243: 
1244:         /* reset ISUID & ISGID bits for paranoid systems */
1245:         (void) chmod(filename, (int) stb.st_mode);
1246:         exit(EX_OK);
1247:         /*NOTREACHED*/
1248:     }
1249:     else
1250:     {
1251:         /* parent -- wait for exit status */
1252:         int st;
1253: 
1254:         st = waitfor(pid);
1255:         if ((st & 0377) != 0)
1256:             return (EX_UNAVAILABLE);
1257:         else
1258:             return ((st >> 8) & 0377);
1259:     }
1260: }
1261: /*
1262: **  SENDALL -- actually send all the messages.
1263: **
1264: **	Parameters:
1265: **		e -- the envelope to send.
1266: **		mode -- the delivery mode to use.  If SM_DEFAULT, use
1267: **			the current SendMode.
1268: **
1269: **	Returns:
1270: **		none.
1271: **
1272: **	Side Effects:
1273: **		Scans the send lists and sends everything it finds.
1274: **		Delivers any appropriate error messages.
1275: **		If we are running in a non-interactive mode, takes the
1276: **			appropriate action.
1277: */
1278: 
1279: sendall(e, mode)
1280:     ENVELOPE *e;
1281:     char mode;
1282: {
1283:     register ADDRESS *q;
1284:     bool oldverbose;
1285:     int pid;
1286:     bool announcequeueup;
1287: 
1288:     /* determine actual delivery mode */
1289:     if (mode == SM_DEFAULT)
1290:     {
1291:         if (shouldqueue(e->e_msgpriority))
1292:             mode = SM_QUEUE;
1293:         announcequeueup = mode == SendMode;
1294:     }
1295:     else
1296:         announcequeueup = FALSE;
1297: 
1298: #ifdef DEBUG
1299:     if (tTd(13, 1))
1300:     {
1301:         printf("\nSENDALL: mode %c, sendqueue:\n", mode);
1302:         printaddr(e->e_sendqueue, TRUE);
1303:     }
1304: #endif DEBUG
1305: 
1306:     /*
1307: 	**  Do any preprocessing necessary for the mode we are running.
1308: 	**	Check to make sure the hop count is reasonable.
1309: 	**	Delete sends to the sender in mailing lists.
1310: 	*/
1311: 
1312:     CurEnv = e;
1313: 
1314:     if (e->e_hopcount > MAXHOP)
1315:     {
1316:         errno = 0;
1317:         queueup(e, TRUE, announcequeueup);
1318:         e->e_flags |= EF_FATALERRS|EF_CLRQUEUE;
1319:         syserr("too many hops (%d max): from %s via %s, to %s",
1320:             MAXHOP, e->e_from.q_paddr,
1321:             RealHostName == NULL ? "localhost" : RealHostName,
1322:             e->e_sendqueue->q_paddr);
1323:         return;
1324:     }
1325: 
1326:     if (!MeToo)
1327:     {
1328:         extern ADDRESS *recipient();
1329: 
1330:         e->e_from.q_flags |= QDONTSEND;
1331:         (void) recipient(&e->e_from, &e->e_sendqueue);
1332:     }
1333: 
1334: # ifdef QUEUE
1335:     if ((mode == SM_QUEUE || mode == SM_FORK ||
1336:          (mode != SM_VERIFY && SuperSafe)) &&
1337:         !bitset(EF_INQUEUE, e->e_flags))
1338:         queueup(e, TRUE, announcequeueup);
1339: #endif QUEUE
1340: 
1341:     oldverbose = Verbose;
1342:     switch (mode)
1343:     {
1344:       case SM_VERIFY:
1345:         Verbose = TRUE;
1346:         break;
1347: 
1348:       case SM_QUEUE:
1349:         e->e_flags |= EF_INQUEUE|EF_KEEPQUEUE;
1350:         return;
1351: 
1352:       case SM_FORK:
1353:         if (e->e_xfp != NULL)
1354:             (void) fflush(e->e_xfp);
1355:         pid = fork();
1356:         if (pid < 0)
1357:         {
1358:             mode = SM_DELIVER;
1359:             break;
1360:         }
1361:         else if (pid > 0)
1362:         {
1363:             /* be sure we leave the temp files to our child */
1364:             e->e_id = e->e_df = NULL;
1365:             return;
1366:         }
1367: 
1368:         /* double fork to avoid zombies */
1369:         if (fork() > 0)
1370:             exit(EX_OK);
1371: 
1372:         /* be sure we are immune from the terminal */
1373:         disconnect(FALSE);
1374: 
1375:         break;
1376:     }
1377: 
1378:     /*
1379: 	**  Run through the list and send everything.
1380: 	*/
1381: 
1382:     for (q = e->e_sendqueue; q != NULL; q = q->q_next)
1383:     {
1384:         if (mode == SM_VERIFY)
1385:         {
1386:             e->e_to = q->q_paddr;
1387:             if (!bitset(QDONTSEND|QBADADDR, q->q_flags))
1388:                 message(Arpa_Info, "deliverable");
1389:         }
1390:         else
1391:             (void) deliver(e, q);
1392:     }
1393:     Verbose = oldverbose;
1394: 
1395:     /*
1396: 	**  Now run through and check for errors.
1397: 	*/
1398: 
1399:     if (mode == SM_VERIFY)
1400:         return;
1401: 
1402:     for (q = e->e_sendqueue; q != NULL; q = q->q_next)
1403:     {
1404:         register ADDRESS *qq;
1405: 
1406: # ifdef DEBUG
1407:         if (tTd(13, 3))
1408:         {
1409:             printf("Checking ");
1410:             printaddr(q, FALSE);
1411:         }
1412: # endif DEBUG
1413: 
1414:         /* only send errors if the message failed */
1415:         if (!bitset(QBADADDR, q->q_flags))
1416:             continue;
1417: 
1418:         /* we have an address that failed -- find the parent */
1419:         for (qq = q; qq != NULL; qq = qq->q_alias)
1420:         {
1421:             char obuf[MAXNAME + 6];
1422:             extern char *aliaslookup();
1423: 
1424:             /* we can only have owners for local addresses */
1425:             if (!bitnset(M_LOCAL, qq->q_mailer->m_flags))
1426:                 continue;
1427: 
1428:             /* see if the owner list exists */
1429:             (void) strcpy(obuf, "owner-");
1430:             if (strncmp(qq->q_user, "owner-", 6) == 0)
1431:                 (void) strcat(obuf, "owner");
1432:             else
1433:                 (void) strcat(obuf, qq->q_user);
1434:             if (aliaslookup(obuf) == NULL)
1435:                 continue;
1436: 
1437: # ifdef DEBUG
1438:             if (tTd(13, 4))
1439:                 printf("Errors to %s\n", obuf);
1440: # endif DEBUG
1441: 
1442:             /* owner list exists -- add it to the error queue */
1443:             sendtolist(obuf, (ADDRESS *) NULL, &e->e_errorqueue);
1444:             ErrorMode = EM_MAIL;
1445:             break;
1446:         }
1447: 
1448:         /* if we did not find an owner, send to the sender */
1449:         if (qq == NULL && bitset(QBADADDR, q->q_flags))
1450:             sendtolist(e->e_from.q_paddr, qq, &e->e_errorqueue);
1451:     }
1452: 
1453:     if (mode == SM_FORK)
1454:         finis();
1455: }

Defined functions

deliver defined in line 43; used 2 times
endmailer defined in line 611; used 2 times
giveresponse defined in line 914; used 7 times
logdelivery defined in line 1032; used 3 times
mailfile defined in line 1183; used 1 times
markfailure defined in line 442; used 2 times
openmailer defined in line 661; used 2 times
putfromline defined in line 1063; used 3 times
sendoff defined in line 552; used 1 times

Defined variables

SccsId defined in line 12; never used

Defined macros

DOFORK defined in line 500; used 3 times
NFORKTRIES defined in line 493; used 2 times
XFORK defined in line 497; used 1 times
Last modified: 1995-03-07
Generated: 2016-12-26
Generated by src2html V0.67
page hit count: 2124
Valid CSS Valid XHTML 1.0 Strict