1: /*
   2:  * Copyright (c) 1988 Regents of the University of California.
   3:  * All rights reserved.
   4:  *
   5:  * Redistribution and use in source and binary forms are permitted
   6:  * provided that this notice is preserved and that due credit is given
   7:  * to the University of California at Berkeley. The name of the University
   8:  * may not be used to endorse or promote products derived from this
   9:  * software without specific prior written permission. This software
  10:  * is provided ``as is'' without express or implied warranty.
  11:  *
  12:  *  Sendmail
  13:  *  Copyright (c) 1983  Eric P. Allman
  14:  *  Berkeley, California
  15:  */
  16: 
  17: #if !defined(lint) && !defined(NOSCCS)
  18: static char sccsid[] = "@(#)headers.c	5.9 (Berkeley) 3/13/88";
  19: #endif /* not lint */
  20: 
  21: # include <errno.h>
  22: # include "sendmail.h"
  23: 
  24: /*
  25: **  CHOMPHEADER -- process and save a header line.
  26: **
  27: **	Called by collect and by readcf to deal with header lines.
  28: **
  29: **	Parameters:
  30: **		line -- header as a text line.
  31: **		def -- if set, this is a default value.
  32: **
  33: **	Returns:
  34: **		flags for this header.
  35: **
  36: **	Side Effects:
  37: **		The header is saved on the header list.
  38: **		Contents of 'line' are destroyed.
  39: */
  40: 
  41: chompheader(line, def)
  42:     char *line;
  43:     bool def;
  44: {
  45:     register char *p;
  46:     register HDR *h;
  47:     HDR **hp;
  48:     char *fname;
  49:     char *fvalue;
  50:     struct hdrinfo *hi;
  51:     bool cond = FALSE;
  52:     BITMAP mopts;
  53:     extern char *crackaddr();
  54: 
  55: # ifdef DEBUG
  56:     if (tTd(31, 6))
  57:         printf("chompheader: %s\n", line);
  58: # endif DEBUG
  59: 
  60:     /* strip off options */
  61:     clrbitmap(mopts);
  62:     p = line;
  63:     if (*p == '?')
  64:     {
  65:         /* have some */
  66:         register char *q = index(p + 1, *p);
  67: 
  68:         if (q != NULL)
  69:         {
  70:             *q++ = '\0';
  71:             while (*++p != '\0')
  72:                 setbitn(*p, mopts);
  73:             p = q;
  74:         }
  75:         else
  76:             syserr("chompheader: syntax error, line \"%s\"", line);
  77:         cond = TRUE;
  78:     }
  79: 
  80:     /* find canonical name */
  81:     fname = p;
  82:     p = index(p, ':');
  83:     if (p == NULL)
  84:     {
  85:         syserr("chompheader: syntax error, line \"%s\"", line);
  86:         return (0);
  87:     }
  88:     fvalue = &p[1];
  89:     while (isspace(*--p))
  90:         continue;
  91:     *++p = '\0';
  92:     makelower(fname);
  93: 
  94:     /* strip field value on front */
  95:     if (*fvalue == ' ')
  96:         fvalue++;
  97: 
  98:     /* see if it is a known type */
  99:     for (hi = HdrInfo; hi->hi_field != NULL; hi++)
 100:     {
 101:         if (strcmp(hi->hi_field, fname) == 0)
 102:             break;
 103:     }
 104: 
 105:     /* see if this is a resent message */
 106:     if (!def && bitset(H_RESENT, hi->hi_flags))
 107:         CurEnv->e_flags |= EF_RESENT;
 108: 
 109:     /* if this means "end of header" quit now */
 110:     if (bitset(H_EOH, hi->hi_flags))
 111:         return (hi->hi_flags);
 112: 
 113:     /* drop explicit From: if same as what we would generate -- for MH */
 114:     p = "resent-from";
 115:     if (!bitset(EF_RESENT, CurEnv->e_flags))
 116:         p += 7;
 117:     if (!def && !QueueRun && strcmp(fname, p) == 0)
 118:     {
 119:         if (CurEnv->e_from.q_paddr != NULL &&
 120:             strcmp(fvalue, CurEnv->e_from.q_paddr) == 0)
 121:             return (hi->hi_flags);
 122:     }
 123: 
 124:     /* delete default value for this header */
 125:     for (hp = &CurEnv->e_header; (h = *hp) != NULL; hp = &h->h_link)
 126:     {
 127:         if (strcmp(fname, h->h_field) == 0 &&
 128:             bitset(H_DEFAULT, h->h_flags) &&
 129:             !bitset(H_FORCE, h->h_flags))
 130:             h->h_value = NULL;
 131:     }
 132: 
 133:     /* create a new node */
 134:     h = (HDR *) xalloc(sizeof *h);
 135:     h->h_field = newstr(fname);
 136:     h->h_value = NULL;
 137:     h->h_link = NULL;
 138:     bcopy((char *) mopts, (char *) h->h_mflags, sizeof mopts);
 139:     *hp = h;
 140:     h->h_flags = hi->hi_flags;
 141:     if (def)
 142:         h->h_flags |= H_DEFAULT;
 143:     if (cond)
 144:         h->h_flags |= H_CHECK;
 145:     if (h->h_value != NULL)
 146:         free((char *) h->h_value);
 147:     h->h_value = newstr(fvalue);
 148: 
 149:     /* hack to see if this is a new format message */
 150:     if (!def && bitset(H_RCPT|H_FROM, h->h_flags) &&
 151:         (index(fvalue, ',') != NULL || index(fvalue, '(') != NULL ||
 152:          index(fvalue, '<') != NULL || index(fvalue, ';') != NULL))
 153:     {
 154:         CurEnv->e_flags &= ~EF_OLDSTYLE;
 155:     }
 156: 
 157:     return (h->h_flags);
 158: }
 159: /*
 160: **  ADDHEADER -- add a header entry to the end of the queue.
 161: **
 162: **	This bypasses the special checking of chompheader.
 163: **
 164: **	Parameters:
 165: **		field -- the name of the header field.
 166: **		value -- the value of the field.  It must be lower-cased.
 167: **		e -- the envelope to add them to.
 168: **
 169: **	Returns:
 170: **		none.
 171: **
 172: **	Side Effects:
 173: **		adds the field on the list of headers for this envelope.
 174: */
 175: 
 176: addheader(field, value, e)
 177:     char *field;
 178:     char *value;
 179:     ENVELOPE *e;
 180: {
 181:     register HDR *h;
 182:     register struct hdrinfo *hi;
 183:     HDR **hp;
 184: 
 185:     /* find info struct */
 186:     for (hi = HdrInfo; hi->hi_field != NULL; hi++)
 187:     {
 188:         if (strcmp(field, hi->hi_field) == 0)
 189:             break;
 190:     }
 191: 
 192:     /* find current place in list -- keep back pointer? */
 193:     for (hp = &e->e_header; (h = *hp) != NULL; hp = &h->h_link)
 194:     {
 195:         if (strcmp(field, h->h_field) == 0)
 196:             break;
 197:     }
 198: 
 199:     /* allocate space for new header */
 200:     h = (HDR *) xalloc(sizeof *h);
 201:     h->h_field = field;
 202:     h->h_value = newstr(value);
 203:     h->h_link = *hp;
 204:     h->h_flags = hi->hi_flags | H_DEFAULT;
 205:     clrbitmap(h->h_mflags);
 206:     *hp = h;
 207: }
 208: /*
 209: **  HVALUE -- return value of a header.
 210: **
 211: **	Only "real" fields (i.e., ones that have not been supplied
 212: **	as a default) are used.
 213: **
 214: **	Parameters:
 215: **		field -- the field name.
 216: **
 217: **	Returns:
 218: **		pointer to the value part.
 219: **		NULL if not found.
 220: **
 221: **	Side Effects:
 222: **		none.
 223: */
 224: 
 225: char *
 226: hvalue(field)
 227:     char *field;
 228: {
 229:     register HDR *h;
 230: 
 231:     for (h = CurEnv->e_header; h != NULL; h = h->h_link)
 232:     {
 233:         if (!bitset(H_DEFAULT, h->h_flags) && strcmp(h->h_field, field) == 0)
 234:             return (h->h_value);
 235:     }
 236:     return (NULL);
 237: }
 238: /*
 239: **  ISHEADER -- predicate telling if argument is a header.
 240: **
 241: **	A line is a header if it has a single word followed by
 242: **	optional white space followed by a colon.
 243: **
 244: **	Parameters:
 245: **		s -- string to check for possible headerness.
 246: **
 247: **	Returns:
 248: **		TRUE if s is a header.
 249: **		FALSE otherwise.
 250: **
 251: **	Side Effects:
 252: **		none.
 253: */
 254: 
 255: bool
 256: isheader(s)
 257:     register char *s;
 258: {
 259:     while (*s > ' ' && *s != ':' && *s != '\0')
 260:         s++;
 261: 
 262:     /* following technically violates RFC822 */
 263:     while (isspace(*s))
 264:         s++;
 265: 
 266:     return (*s == ':');
 267: }
 268: /*
 269: **  EATHEADER -- run through the stored header and extract info.
 270: **
 271: **	Parameters:
 272: **		e -- the envelope to process.
 273: **
 274: **	Returns:
 275: **		none.
 276: **
 277: **	Side Effects:
 278: **		Sets a bunch of global variables from information
 279: **			in the collected header.
 280: **		Aborts the message if the hop count is exceeded.
 281: */
 282: 
 283: eatheader(e)
 284:     register ENVELOPE *e;
 285: {
 286:     register HDR *h;
 287:     register char *p;
 288:     int hopcnt = 0;
 289: 
 290: #ifdef DEBUG
 291:     if (tTd(32, 1))
 292:         printf("----- collected header -----\n");
 293: #endif DEBUG
 294:     for (h = e->e_header; h != NULL; h = h->h_link)
 295:     {
 296: #ifdef DEBUG
 297:         extern char *capitalize();
 298: 
 299:         if (tTd(32, 1))
 300:             printf("%s: %s\n", capitalize(h->h_field), h->h_value);
 301: #endif DEBUG
 302:         /* count the number of times it has been processed */
 303:         if (bitset(H_TRACE, h->h_flags))
 304:             hopcnt++;
 305: 
 306:         /* send to this person if we so desire */
 307:         if (GrabTo && bitset(H_RCPT, h->h_flags) &&
 308:             !bitset(H_DEFAULT, h->h_flags) &&
 309:             (!bitset(EF_RESENT, CurEnv->e_flags) || bitset(H_RESENT, h->h_flags)))
 310:         {
 311:             sendtolist(h->h_value, (ADDRESS *) NULL, &CurEnv->e_sendqueue);
 312:         }
 313: 
 314:         /* log the message-id */
 315: #ifdef LOG
 316:         if (!QueueRun && LogLevel > 8 && h->h_value != NULL &&
 317:             strcmp(h->h_field, "message-id") == 0)
 318:         {
 319:             char buf[MAXNAME];
 320: 
 321:             p = h->h_value;
 322:             if (bitset(H_DEFAULT, h->h_flags))
 323:             {
 324:                 expand(p, buf, &buf[sizeof buf], e);
 325:                 p = buf;
 326:             }
 327:             syslog(LOG_INFO, "%s: message-id=%s", e->e_id, p);
 328:         }
 329: #endif LOG
 330:     }
 331: #ifdef DEBUG
 332:     if (tTd(32, 1))
 333:         printf("----------------------------\n");
 334: #endif DEBUG
 335: 
 336:     /* store hop count */
 337:     if (hopcnt > e->e_hopcount)
 338:         e->e_hopcount = hopcnt;
 339: 
 340:     /* message priority */
 341:     p = hvalue("precedence");
 342:     if (p != NULL)
 343:         e->e_class = priencode(p);
 344:     if (!QueueRun)
 345:         e->e_msgpriority = e->e_msgsize
 346:                  - e->e_class * WkClassFact
 347:                  + e->e_nrcpts * WkRecipFact;
 348: 
 349:     /* return receipt to */
 350:     p = hvalue("return-receipt-to");
 351:     if (p != NULL)
 352:         e->e_receiptto = p;
 353: 
 354:     /* errors to */
 355:     p = hvalue("errors-to");
 356:     if (p != NULL)
 357:         sendtolist(p, (ADDRESS *) NULL, &e->e_errorqueue);
 358: 
 359:     /* from person */
 360:     if (OpMode == MD_ARPAFTP)
 361:     {
 362:         register struct hdrinfo *hi = HdrInfo;
 363: 
 364:         for (p = NULL; p == NULL && hi->hi_field != NULL; hi++)
 365:         {
 366:             if (bitset(H_FROM, hi->hi_flags))
 367:                 p = hvalue(hi->hi_field);
 368:         }
 369:         if (p != NULL)
 370:             setsender(p);
 371:     }
 372: 
 373:     /* full name of from person */
 374:     p = hvalue("full-name");
 375:     if (p != NULL)
 376:         define('x', p, e);
 377: 
 378:     /* date message originated */
 379:     p = hvalue("posted-date");
 380:     if (p == NULL)
 381:         p = hvalue("date");
 382:     if (p != NULL)
 383:     {
 384:         define('a', p, e);
 385:         /* we don't have a good way to do canonical conversion ....
 386: 		define('d', newstr(arpatounix(p)), e);
 387: 		.... so we will ignore the problem for the time being */
 388:     }
 389: 
 390:     /*
 391: 	**  Log collection information.
 392: 	*/
 393: 
 394: # ifdef LOG
 395:     if (!QueueRun && LogLevel > 1)
 396:     {
 397:         syslog(LOG_INFO, "%s: from=%s, size=%ld, class=%d\n",
 398:                CurEnv->e_id, CurEnv->e_from.q_paddr, CurEnv->e_msgsize,
 399:                CurEnv->e_class);
 400:     }
 401: # endif LOG
 402: }
 403: /*
 404: **  PRIENCODE -- encode external priority names into internal values.
 405: **
 406: **	Parameters:
 407: **		p -- priority in ascii.
 408: **
 409: **	Returns:
 410: **		priority as a numeric level.
 411: **
 412: **	Side Effects:
 413: **		none.
 414: */
 415: 
 416: priencode(p)
 417:     char *p;
 418: {
 419:     register int i;
 420: 
 421:     for (i = 0; i < NumPriorities; i++)
 422:     {
 423:         if (!strcasecmp(p, Priorities[i].pri_name))
 424:             return (Priorities[i].pri_val);
 425:     }
 426: 
 427:     /* unknown priority */
 428:     return (0);
 429: }
 430: /*
 431: **  CRACKADDR -- parse an address and turn it into a macro
 432: **
 433: **	This doesn't actually parse the address -- it just extracts
 434: **	it and replaces it with "$g".  The parse is totally ad hoc
 435: **	and isn't even guaranteed to leave something syntactically
 436: **	identical to what it started with.  However, it does leave
 437: **	something semantically identical.
 438: **
 439: **	The process is kind of strange.  There are a number of
 440: **	interesting cases:
 441: **		1.  comment <address> comment	==> comment <$g> comment
 442: **		2.  address			==> address
 443: **		3.  address (comment)		==> $g (comment)
 444: **		4.  (comment) address		==> (comment) $g
 445: **	And then there are the hard cases....
 446: **		5.  add (comment) ress		==> $g (comment)
 447: **		6.  comment <address (comment)>	==> comment <$g (comment)>
 448: **		7.    .... etc ....
 449: **
 450: **	Parameters:
 451: **		addr -- the address to be cracked.
 452: **
 453: **	Returns:
 454: **		a pointer to the new version.
 455: **
 456: **	Side Effects:
 457: **		none.
 458: **
 459: **	Warning:
 460: **		The return value is saved in local storage and should
 461: **		be copied if it is to be reused.
 462: */
 463: 
 464: char *
 465: crackaddr(addr)
 466:     register char *addr;
 467: {
 468:     register char *p;
 469:     register int i;
 470:     static char buf[MAXNAME];
 471:     char *rhs;
 472:     bool gotaddr;
 473:     register char *bp;
 474: 
 475: # ifdef DEBUG
 476:     if (tTd(33, 1))
 477:         printf("crackaddr(%s)\n", addr);
 478: # endif DEBUG
 479: 
 480:     (void) strcpy(buf, "");
 481:     rhs = NULL;
 482: 
 483:     /* strip leading spaces */
 484:     while (*addr != '\0' && isspace(*addr))
 485:         addr++;
 486: 
 487:     /*
 488: 	**  See if we have anything in angle brackets.  If so, that is
 489: 	**  the address part, and the rest is the comment.
 490: 	*/
 491: 
 492:     p = index(addr, '<');
 493:     if (p != NULL)
 494:     {
 495:         /* copy the beginning of the addr field to the buffer */
 496:         *p = '\0';
 497:         (void) strcpy(buf, addr);
 498:         (void) strcat(buf, "<");
 499:         *p++ = '<';
 500: 
 501:         /* skip spaces */
 502:         while (isspace(*p))
 503:             p++;
 504: 
 505:         /* find the matching right angle bracket */
 506:         addr = p;
 507:         for (i = 0; *p != '\0'; p++)
 508:         {
 509:             switch (*p)
 510:             {
 511:               case '<':
 512:                 i++;
 513:                 break;
 514: 
 515:               case '>':
 516:                 i--;
 517:                 break;
 518:             }
 519:             if (i < 0)
 520:                 break;
 521:         }
 522: 
 523:         /* p now points to the closing quote (or a null byte) */
 524:         if (*p != '\0')
 525:         {
 526:             /* make rhs point to the extra stuff at the end */
 527:             rhs = p;
 528:             *p++ = '\0';
 529:         }
 530:     }
 531: 
 532:     /*
 533: 	**  Now parse the real address part.  "addr" points to the (null
 534: 	**  terminated) version of what we are inerested in; rhs points
 535: 	**  to the extra stuff at the end of the line, if any.
 536: 	*/
 537: 
 538:     p = addr;
 539: 
 540:     /* now strip out comments */
 541:     bp = &buf[strlen(buf)];
 542:     gotaddr = FALSE;
 543:     for (; *p != '\0'; p++)
 544:     {
 545:         if (*p == '(')
 546:         {
 547:             /* copy to matching close paren */
 548:             *bp++ = *p++;
 549:             for (i = 0; *p != '\0'; p++)
 550:             {
 551:                 *bp++ = *p;
 552:                 switch (*p)
 553:                 {
 554:                   case '(':
 555:                     i++;
 556:                     break;
 557: 
 558:                   case ')':
 559:                     i--;
 560:                     break;
 561:                 }
 562:                 if (i < 0)
 563:                     break;
 564:             }
 565:             continue;
 566:         }
 567: 
 568:         /*
 569: 		**  If this is the first "real" character we have seen,
 570: 		**  then we put the "$g" in the buffer now.
 571: 		*/
 572: 
 573:         if (isspace(*p))
 574:             *bp++ = *p;
 575:         else if (!gotaddr)
 576:         {
 577:             (void) strcpy(bp, "\001g");
 578:             bp += 2;
 579:             gotaddr = TRUE;
 580:         }
 581:     }
 582: 
 583:     /* hack, hack.... strip trailing blanks */
 584:     do
 585:     {
 586:         *bp-- = '\0';
 587:     } while (isspace(*bp));
 588:     bp++;
 589: 
 590:     /* put any right hand side back on */
 591:     if (rhs != NULL)
 592:     {
 593:         *rhs = '>';
 594:         (void) strcpy(bp, rhs);
 595:     }
 596: 
 597: # ifdef DEBUG
 598:     if (tTd(33, 1))
 599:         printf("crackaddr=>`%s'\n", buf);
 600: # endif DEBUG
 601: 
 602:     return (buf);
 603: }
 604: /*
 605: **  PUTHEADER -- put the header part of a message from the in-core copy
 606: **
 607: **	Parameters:
 608: **		fp -- file to put it on.
 609: **		m -- mailer to use.
 610: **		e -- envelope to use.
 611: **
 612: **	Returns:
 613: **		none.
 614: **
 615: **	Side Effects:
 616: **		none.
 617: */
 618: 
 619: putheader(fp, m, e)
 620:     register FILE *fp;
 621:     register MAILER *m;
 622:     register ENVELOPE *e;
 623: {
 624:     char buf[MAXLINE];
 625:     register HDR *h;
 626:     extern char *arpadate();
 627:     extern char *capitalize();
 628:     char obuf[MAXLINE];
 629: 
 630:     for (h = e->e_header; h != NULL; h = h->h_link)
 631:     {
 632:         register char *p;
 633:         extern bool bitintersect();
 634: 
 635:         if (bitset(H_CHECK|H_ACHECK, h->h_flags) &&
 636:             !bitintersect(h->h_mflags, m->m_flags))
 637:             continue;
 638: 
 639:         /* handle Resent-... headers specially */
 640:         if (bitset(H_RESENT, h->h_flags) && !bitset(EF_RESENT, e->e_flags))
 641:             continue;
 642: 
 643:         p = h->h_value;
 644:         if (bitset(H_DEFAULT, h->h_flags))
 645:         {
 646:             /* macro expand value if generated internally */
 647:             expand(p, buf, &buf[sizeof buf], e);
 648:             p = buf;
 649:             if (p == NULL || *p == '\0')
 650:                 continue;
 651:         }
 652: 
 653:         if (bitset(H_FROM|H_RCPT, h->h_flags))
 654:         {
 655:             /* address field */
 656:             bool oldstyle = bitset(EF_OLDSTYLE, e->e_flags);
 657: 
 658:             if (bitset(H_FROM, h->h_flags))
 659:                 oldstyle = FALSE;
 660:             commaize(h, p, fp, oldstyle, m);
 661:         }
 662:         else
 663:         {
 664:             /* vanilla header line */
 665:             register char *nlp;
 666: 
 667:             (void) sprintf(obuf, "%s: ", capitalize(h->h_field));
 668:             while ((nlp = index(p, '\n')) != NULL)
 669:             {
 670:                 *nlp = '\0';
 671:                 (void) strcat(obuf, p);
 672:                 *nlp = '\n';
 673:                 putline(obuf, fp, m);
 674:                 p = ++nlp;
 675:                 obuf[0] = '\0';
 676:             }
 677:             (void) strcat(obuf, p);
 678:             putline(obuf, fp, m);
 679:         }
 680:     }
 681: }
 682: /*
 683: **  COMMAIZE -- output a header field, making a comma-translated list.
 684: **
 685: **	Parameters:
 686: **		h -- the header field to output.
 687: **		p -- the value to put in it.
 688: **		fp -- file to put it to.
 689: **		oldstyle -- TRUE if this is an old style header.
 690: **		m -- a pointer to the mailer descriptor.  If NULL,
 691: **			don't transform the name at all.
 692: **
 693: **	Returns:
 694: **		none.
 695: **
 696: **	Side Effects:
 697: **		outputs "p" to file "fp".
 698: */
 699: 
 700: commaize(h, p, fp, oldstyle, m)
 701:     register HDR *h;
 702:     register char *p;
 703:     FILE *fp;
 704:     bool oldstyle;
 705:     register MAILER *m;
 706: {
 707:     register char *obp;
 708:     int opos;
 709:     bool firstone = TRUE;
 710:     char obuf[MAXLINE + 3];
 711: 
 712:     /*
 713: 	**  Output the address list translated by the
 714: 	**  mailer and with commas.
 715: 	*/
 716: 
 717: # ifdef DEBUG
 718:     if (tTd(14, 2))
 719:         printf("commaize(%s: %s)\n", h->h_field, p);
 720: # endif DEBUG
 721: 
 722:     obp = obuf;
 723:     (void) sprintf(obp, "%s: ", capitalize(h->h_field));
 724:     opos = strlen(h->h_field) + 2;
 725:     obp += opos;
 726: 
 727:     /*
 728: 	**  Run through the list of values.
 729: 	*/
 730: 
 731:     while (*p != '\0')
 732:     {
 733:         register char *name;
 734:         char savechar;
 735:         extern char *remotename();
 736:         extern char *DelimChar;     /* defined in prescan */
 737: 
 738:         /*
 739: 		**  Find the end of the name.  New style names
 740: 		**  end with a comma, old style names end with
 741: 		**  a space character.  However, spaces do not
 742: 		**  necessarily delimit an old-style name -- at
 743: 		**  signs mean keep going.
 744: 		*/
 745: 
 746:         /* find end of name */
 747:         while (isspace(*p) || *p == ',')
 748:             p++;
 749:         name = p;
 750:         for (;;)
 751:         {
 752:             char *oldp;
 753:             char pvpbuf[PSBUFSIZE];
 754:             extern bool isatword();
 755:             extern char **prescan();
 756: 
 757:             (void) prescan(p, oldstyle ? ' ' : ',', pvpbuf);
 758:             p = DelimChar;
 759: 
 760:             /* look to see if we have an at sign */
 761:             oldp = p;
 762:             while (*p != '\0' && isspace(*p))
 763:                 p++;
 764: 
 765:             if (*p != '@' && !isatword(p))
 766:             {
 767:                 p = oldp;
 768:                 break;
 769:             }
 770:             p += *p == '@' ? 1 : 2;
 771:             while (*p != '\0' && isspace(*p))
 772:                 p++;
 773:         }
 774:         /* at the end of one complete name */
 775: 
 776:         /* strip off trailing white space */
 777:         while (p >= name && (isspace(*p) || *p == ',' || *p == '\0'))
 778:             p--;
 779:         if (++p == name)
 780:             continue;
 781:         savechar = *p;
 782:         *p = '\0';
 783: 
 784:         /* translate the name to be relative */
 785:         name = remotename(name, m, bitset(H_FROM, h->h_flags), FALSE);
 786:         if (*name == '\0')
 787:         {
 788:             *p = savechar;
 789:             continue;
 790:         }
 791: 
 792:         /* output the name with nice formatting */
 793:         opos += qstrlen(name);
 794:         if (!firstone)
 795:             opos += 2;
 796:         if (opos > 78 && !firstone)
 797:         {
 798:             (void) strcpy(obp, ",\n");
 799:             putline(obuf, fp, m);
 800:             obp = obuf;
 801:             (void) sprintf(obp, "        ");
 802:             opos = strlen(obp);
 803:             obp += opos;
 804:             opos += qstrlen(name);
 805:         }
 806:         else if (!firstone)
 807:         {
 808:             (void) sprintf(obp, ", ");
 809:             obp += 2;
 810:         }
 811: 
 812:         /* strip off quote bits as we output */
 813:         while (*name != '\0' && obp < &obuf[MAXLINE])
 814:         {
 815:             if (bitset(0200, *name))
 816:                 *obp++ = '\\';
 817:             *obp++ = *name++ & ~0200;
 818:         }
 819:         firstone = FALSE;
 820:         *p = savechar;
 821:     }
 822:     (void) strcpy(obp, "\n");
 823:     putline(obuf, fp, m);
 824: }
 825: /*
 826: **  ISATWORD -- tell if the word we are pointing to is "at".
 827: **
 828: **	Parameters:
 829: **		p -- word to check.
 830: **
 831: **	Returns:
 832: **		TRUE -- if p is the word at.
 833: **		FALSE -- otherwise.
 834: **
 835: **	Side Effects:
 836: **		none.
 837: */
 838: 
 839: bool
 840: isatword(p)
 841:     register char *p;
 842: {
 843:     extern char lower();
 844: 
 845:     if (lower(p[0]) == 'a' && lower(p[1]) == 't' &&
 846:         p[2] != '\0' && isspace(p[2]))
 847:         return (TRUE);
 848:     return (FALSE);
 849: }

Defined functions

commaize defined in line 700; used 2 times
crackaddr defined in line 464; used 3 times
hvalue defined in line 225; used 12 times
isatword defined in line 839; used 2 times
isheader defined in line 255; used 2 times
priencode defined in line 416; used 1 times

Defined variables

sccsid defined in line 18; never used
Last modified: 1988-09-14
Generated: 2016-12-26
Generated by src2html V0.67
page hit count: 4603
Valid CSS Valid XHTML 1.0 Strict