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