1: /* 2: * This software is Copyright (c) 1986 by Rick Adams. 3: * 4: * Permission is hereby granted to copy, reproduce, redistribute or 5: * otherwise use this software as long as: there is no monetary 6: * profit gained specifically from the use or reproduction or this 7: * software, it is not sold, rented, traded or otherwise marketed, and 8: * this copyright notice is included prominently in any copy 9: * made. 10: * 11: * The author make no claims as to the fitness or correctness of 12: * this software for any use whatsoever, and it is provided as is. 13: * Any use of this software is at the user's own risk. 14: * 15: * header.c - header functions plus some other goodies 16: */ 17: 18: #ifdef SCCSID 19: static char *SccsId = "@(#)header.c 2.41 3/19/86"; 20: #endif /* SCCSID */ 21: 22: #include <stdio.h> 23: #include "params.h" 24: 25: char *hfgets(); 26: 27: char *news_version = NEWS_VERSION; 28: 29: /* 30: * Read header from file fp into *hp. If wholething is FALSE, 31: * it's an incremental read, otherwise start from scratch. 32: * Return (FILE *) if header okay, else NULL. 33: */ 34: FILE * 35: hread(hp, fp, wholething) 36: register struct hbuf *hp; 37: FILE *fp; 38: int wholething; 39: { 40: register int len; 41: register int i; 42: #ifdef OLD 43: char *p; 44: #endif /* OLD */ 45: 46: if (wholething) { 47: for(i=0;i<NUNREC;i++) 48: if (hp->unrec[i] != NULL) 49: free(hp->unrec[i]); 50: else 51: break; 52: bzero((char *)hp, sizeof (*hp)); 53: for (i=0;i<NUNREC;i++) 54: hp->unrec[i] = NULL; 55: } 56: 57: /* Check that it's a B news style header. */ 58: if (hfgets(bfr, PATHLEN, fp) != NULL && isalpha(bfr[0]) 59: && index(bfr, ':')) 60: if (frmread(fp, hp)) 61: goto strip; 62: 63: if (!nstrip(bfr+1)) 64: return NULL; 65: 66: /* It's not. Try A news (begins with PROTO). */ 67: if (bfr[0] != PROTO) 68: return NULL; 69: #ifndef OLD 70: logerr("Can not process A news format article without OLD defined"); 71: #else /* OLD */ 72: /* Read in an A news format article. */ 73: p = index(bfr+1, '.'); 74: if (p == NULL) { 75: (void) strcpy(hp->ident, bfr+1); 76: return NULL; 77: } 78: *p++ = '\0'; 79: (void) sprintf(hp->ident, "<%s@%s%s>", p, bfr+1, ".UUCP"); 80: 81: /* Newsgroup List */ 82: if (hfgets(hp->nbuf, BUFLEN, fp) == NULL || !nstrip(hp->nbuf)) 83: return NULL; 84: /* source path */ 85: if (hfgets(hp->path, PATHLEN, fp) == NULL || !nstrip(hp->path)) 86: return NULL; 87: /* date */ 88: if (hfgets(hp->subdate, DATELEN, fp) == NULL || !nstrip(hp->subdate)) 89: return NULL; 90: /* title */ 91: if (hfgets(hp->title, BUFLEN, fp) == NULL || !nstrip(hp->title)) 92: return NULL; 93: #endif /* OLD */ 94: 95: strip: /* strip off sys! from front of path. */ 96: if (strncmp(FULLSYSNAME, hp->path, (len = strlen(FULLSYSNAME))) == 0 97: && index(NETCHRS, hp->path[len])) 98: (void) strcpy(hp->path, &(hp->path[len+1])); 99: lcase(hp->nbuf); 100: 101: /* Intuit the From: line if only a path was given. */ 102: if (wholething) { 103: #ifdef OLD 104: if (hp->from[0] == '\0') 105: intuitfrom(hp); 106: else 107: #endif /* OLD */ 108: fixfrom(hp); 109: } 110: 111: return fp; 112: } 113: 114: 115: /* 116: * Get header info from mail-format file. 117: * Return non-zero on success. 118: */ 119: #include <ctype.h> 120: #define FROM 1 121: #define NEWSGROUP 2 122: #define TITLE 3 123: #define SUBMIT 4 124: #define RECEIVE 5 125: #define EXPIRE 6 126: #define ARTICLEID 7 127: #define MESSAGEID 8 128: #define REPLYTO 9 129: #define FOLLOWID 10 130: #define CONTROL 11 131: #define SENDER 12 132: #define FOLLOWTO 13 133: #define PATH 14 134: #define POSTVERSION 15 135: #define RELAYVERSION 16 136: #define DISTRIBUTION 17 137: #define ORGANIZATION 18 138: #define NUMLINES 19 139: #define KEYWORDS 20 140: #define APPROVED 21 141: #define NFID 22 142: #define NFFROM 23 143: #define XREF 24 144: #define SUMMARY 25 145: #define XPATH 26 146: #define OTHER 99 147: 148: char *malloc(); 149: 150: frmread(fp, hp) 151: register FILE *fp; 152: register struct hbuf *hp; 153: { 154: int unreccnt = 0; 155: register int i; 156: long curpos; 157: 158: i = type(bfr); 159: do { 160: curpos = ftell(fp); 161: switch (i) { 162: case PATH: 163: getfield(hp->path, sizeof(hp->path)); 164: break; 165: case FROM: 166: getfield(hp->from, sizeof(hp->from)); 167: break; 168: case NEWSGROUP: 169: getfield(hp->nbuf, sizeof(hp->nbuf)); 170: break; 171: case TITLE: 172: getfield(hp->title, sizeof(hp->title)); 173: break; 174: case SUBMIT: 175: getfield(hp->subdate, sizeof(hp->subdate)); 176: break; 177: case EXPIRE: 178: getfield(hp->expdate, sizeof(hp->expdate)); 179: break; 180: #ifdef OLD 181: case ARTICLEID: 182: /* Believe Message-ID in preference to Article-ID */ 183: if (hp->ident[0] == '\0') { 184: register char *p; 185: char msgb[NAMELEN]; 186: getfield(msgb, sizeof msgb); 187: p = index(msgb, '.'); 188: if (p == NULL) { 189: (void) strcpy(hp->ident, msgb); 190: } else { 191: *p++ = '\0'; 192: (void) sprintf(hp->ident, "<%s@%s%s>", p, msgb, ".UUCP"); 193: } 194: } 195: break; 196: #endif /* OLD */ 197: case MESSAGEID: 198: getfield(hp->ident, sizeof(hp->ident)); 199: break; 200: case REPLYTO: 201: getfield(hp->replyto, sizeof(hp->replyto)); 202: break; 203: case FOLLOWID: 204: getfield(hp->followid, sizeof(hp->followid)); 205: break; 206: case SENDER: 207: getfield(hp->sender, sizeof(hp->sender)); 208: break; 209: case FOLLOWTO: 210: getfield(hp->followto, sizeof(hp->followto)); 211: break; 212: case CONTROL: 213: getfield(hp->ctlmsg, sizeof(hp->ctlmsg)); 214: break; 215: case DISTRIBUTION: 216: getfield(hp->distribution, sizeof(hp->distribution)); 217: break; 218: case ORGANIZATION: 219: getfield(hp->organization, sizeof(hp->organization)); 220: break; 221: case NUMLINES: 222: getfield(hp->numlines, sizeof(hp->numlines)); 223: hp->intnumlines = atoi(hp->numlines); 224: break; 225: case KEYWORDS: 226: getfield(hp->keywords, sizeof(hp->keywords)); 227: break; 228: case APPROVED: 229: getfield(hp->approved, sizeof(hp->approved)); 230: break; 231: case NFID: 232: getfield(hp->nf_id, sizeof(hp->nf_id)); 233: break; 234: case NFFROM: 235: getfield(hp->nf_from, sizeof(hp->nf_from)); 236: break; 237: /* discard these lines */ 238: case XREF: 239: case XPATH: 240: case RELAYVERSION: 241: case POSTVERSION: 242: case RECEIVE: 243: break; 244: case SUMMARY: 245: getfield(hp->summary, sizeof(hp->summary)); 246: break; 247: case OTHER: 248: if (unreccnt < NUNREC) { 249: if ((hp->unrec[unreccnt] = malloc((unsigned)(strlen(bfr) + 1))) != NULL ) { 250: (void) strcpy(hp->unrec[unreccnt], bfr); 251: (void) nstrip(hp->unrec[unreccnt]); 252: unreccnt++; 253: } else 254: xerror("frmread: out of memory"); 255: } 256: break; 257: } 258: } while ((i = type(hfgets(bfr, LBUFLEN, fp))) > 0); 259: 260: if (*bfr != '\n') 261: fseek(fp, curpos, 0); 262: if ((hp->from[0] || hp->path[0]) && hp->subdate[0] && hp->ident[0]) 263: return TRUE; 264: return FALSE; 265: } 266: 267: #ifdef OLD 268: /* 269: * There was no From: line in the message (because it was generated by 270: * an old news program). Guess what it should have been and create it. 271: */ 272: intuitfrom(hp) 273: register struct hbuf *hp; 274: { 275: char *tp; 276: char *user, *host; 277: char *tailpath(), *rindex(); 278: char *at, *dot; 279: char pathbuf[PATHLEN]; 280: char fullname[BUFLEN]; 281: 282: tp = tailpath(hp); 283: user = rindex(tp, '!'); 284: if (user == NULL) 285: user = tp; 286: else 287: *user++ = '\0'; 288: 289: /* Check for an existing Internet address on the end. */ 290: at = index(user, '@'); 291: if (at) { 292: dot = index(at, '.'); 293: if (dot) { 294: (void) strcpy(hp->from, user); 295: return; 296: } 297: /* @ signs are illegal except for the biggie, so */ 298: *at = '%'; 299: } 300: 301: if (tp[0] == '.') 302: host = index(tp, '!') + 1; 303: else if (user == tp) 304: host = FULLSYSNAME; 305: else 306: host = tp; 307: 308: tp = index(host, '@'); 309: if (tp != NULL) 310: *tp = 0; 311: (void) sprintf(hp->from, "%s@%s%s", user, host, MYDOMAIN); 312: 313: skin(pathbuf, fullname, hp->path); /* remove RFC822-style comments */ 314: if (fullname[0] != '\0') { 315: strcat(hp->from, " ("); 316: (void) strcat(hp->from, fullname); 317: strcat(hp->from, ")"); 318: } 319: strcpy(hp->path, pathbuf); /* and stick it back in */ 320: } 321: #endif /* OLD */ 322: 323: /* 324: * Canonicalize the "From:" line into the form 325: * 326: * From: <mail-address> (full-name) 327: * 328: * RFC822 doesn't require the comment to be at the end of the string 329: * like that. 330: */ 331: fixfrom(hp) 332: register struct hbuf *hp; 333: { 334: char frombuf[PATHLEN]; 335: char fullname[BUFLEN]; 336: 337: skin(frombuf, fullname, hp->from); /* remove RFC822-style comments */ 338: if (fullname[0] != '\0') { 339: strcat(frombuf, " ("); 340: strcat(frombuf, fullname); 341: strcat(frombuf, ")"); 342: } 343: strcpy(hp->from, frombuf); /* stick the canonicalized "from" back in */ 344: } 345: 346: skin(name, fullname, hfield) 347: char *name; 348: char *fullname; 349: char *hfield; 350: { 351: register int c; 352: register char *cp, *cp2; 353: char *bufend; 354: int gotlt, parenlev, lastsp; 355: int seenfullname = FALSE; 356: 357: *fullname = '\0'; /* no full name yet */ 358: if (strpbrk(hfield, "(< ") == NULL) { /* include ',' ?? */ 359: strcpy(name, hfield); 360: return; 361: } 362: gotlt = 0; 363: parenlev = 0; 364: lastsp = 0; 365: bufend = name; 366: for (cp = hfield, cp2 = bufend; c = *cp++; ) { 367: switch (c) { 368: case '(': 369: /* 370: * Start of a "comment". 371: * Ignore it, or save it in "fullname" if we haven't 372: * seen a comment yet. 373: */ 374: parenlev++; 375: while ((c = *cp) != 0) { 376: cp++; 377: switch (c) { 378: case '\\': 379: if ((c = *cp) == 0) 380: goto outcm; 381: cp++; 382: break; 383: case '(': 384: parenlev++; 385: break; 386: case ')': 387: parenlev--; 388: if (parenlev == 0) 389: goto outcm; 390: break; 391: } 392: if (!seenfullname) 393: *fullname++ = c; 394: } 395: outcm: 396: parenlev = 0; 397: lastsp = 0; 398: if (!seenfullname) { 399: *fullname = '\0'; 400: seenfullname = TRUE; /* only extract first comment */ 401: } 402: break; 403: 404: case '"': 405: /* 406: * Start of a "quoted-string". 407: * Copy it in its entirety. 408: */ 409: while ((c = *cp) != 0) { 410: cp++; 411: switch (c) { 412: case '\\': 413: if ((c = *cp) == 0) 414: goto outqs; 415: cp++; 416: break; 417: case '"': 418: goto outqs; 419: } 420: *cp2++ = c; 421: } 422: outqs: 423: lastsp = 0; 424: break; 425: 426: case ' ': 427: if (cp[0] == 'a' && cp[1] == 't' && cp[2] == ' ') 428: cp += 3, *cp2++ = '@'; 429: else 430: if (cp[0] == '@' && cp[1] == ' ') 431: cp += 2, *cp2++ = '@'; 432: else 433: lastsp = 1; 434: break; 435: 436: case '<': 437: if (!seenfullname) { 438: *cp2 = '\0'; 439: strcpy(fullname, name); 440: seenfullname = TRUE; 441: } 442: cp2 = bufend; 443: gotlt++; 444: lastsp = 0; 445: break; 446: 447: case '>': 448: if (gotlt) { 449: gotlt = 0; 450: /* 451: * this doesn't seem reasonable, what about (,) 452: * or "," ?? 453: */ 454: while (*cp != ',' && *cp != 0) 455: cp++; 456: if (*cp == 0 ) 457: goto done; 458: *cp2++ = ','; 459: *cp2++ = ' '; 460: bufend = cp2; 461: break; 462: } 463: 464: /* Fall into . . . */ 465: 466: default: 467: if (lastsp) { 468: lastsp = 0; 469: *cp2++ = ' '; 470: } 471: *cp2++ = c; 472: break; 473: } 474: } 475: done: 476: *cp2 = 0; 477: } 478: 479: 480: #ifdef OLD 481: char * 482: oident(ident) 483: char *ident; 484: { 485: char lbuf[BUFLEN]; 486: static char oidbuf[BUFLEN]; 487: register char *p, *q; 488: 489: (void) strcpy(lbuf, ident); 490: p = index(lbuf, '@'); 491: if (p == NULL) 492: return ident; 493: *p++ = '\0'; 494: q = index(p, '.'); 495: if (q == NULL) 496: q = index(p, '>'); 497: if (q) 498: *q++ = '\0'; 499: p[SNLN] = '\0'; 500: (void) sprintf(oidbuf, "%s.%s", p, lbuf+1); 501: return oidbuf; 502: } 503: #endif /* OLD */ 504: 505: /* 506: * Get the given field of a header (char * parm) from bfr, but only 507: * if there's something actually there (after the colon). Don't 508: * bother if we already have an entry for this field. 509: */ 510: getfield(hpfield, size) 511: char *hpfield; 512: int size; 513: { 514: register char *ptr; 515: 516: if (hpfield[0]) 517: return; 518: for (ptr = index(bfr, ':'); isspace(*++ptr); ) 519: ; 520: if (*ptr != '\0') { 521: (void) strncpy(hpfield, ptr, size - 1); 522: (void) nstrip(hpfield); 523: } 524: } 525: 526: 527: #define its(type) (prefix(ptr, type)) 528: type(ptr) 529: register char *ptr; 530: { 531: register char *colon, *space; 532: 533: if (ptr == NULL) 534: return FALSE; 535: if (!isalpha(*ptr)) 536: return FALSE; 537: colon = index(ptr, ':'); 538: space = index(ptr, ' '); 539: if (!colon || space && space < colon) 540: return FALSE; 541: if (its("From: ")) 542: if (index(ptr, '@') || !index(ptr, '!')) 543: return FROM; 544: else 545: return PATH; 546: if (its("Path: ")) 547: return PATH; 548: if (its("Newsgroups: ")) 549: return NEWSGROUP; 550: if (its("Subject: ")) 551: return TITLE; 552: if (its("Date: ")) 553: return SUBMIT; 554: if (its("Date-Received: ")) 555: return RECEIVE; 556: #ifdef OLD 557: if (its("Title: ")) 558: return TITLE; 559: if (its("Posted: ")) 560: return SUBMIT; 561: if (its("Received: ")) 562: return RECEIVE; 563: #endif /* OLD */ 564: if (its("Expires: ")) 565: return EXPIRE; 566: if (its("Article-I.D.: ")) 567: return ARTICLEID; 568: if (its("Message-ID: ")) 569: return MESSAGEID; 570: if (its("Reply-To: ")) 571: return REPLYTO; 572: if (its("References: ")) 573: return FOLLOWID; 574: if (its("Control: ")) 575: return CONTROL; 576: if (its("Sender: ")) 577: return SENDER; 578: if (its("Followup-To: ")) 579: return FOLLOWTO; 580: if (its("Posting-Version: ")) 581: return POSTVERSION; 582: if (its("Relay-Version: ")) 583: return RELAYVERSION; 584: if (its("Distribution: ")) 585: return DISTRIBUTION; 586: if (its("Organization: ")) 587: return ORGANIZATION; 588: if (its("Lines: ")) 589: return NUMLINES; 590: if (its("Summary: ")) 591: return SUMMARY; 592: if (its("Keywords: ")) 593: return KEYWORDS; 594: if (its("Approved: ")) 595: return APPROVED; 596: if (its("Nf-ID: ")) 597: return NFID; 598: if (its("Nf-From: ")) 599: return NFFROM; 600: if (its("Xref: ")) 601: return XREF; 602: if (its("Xpath: ")) 603: return XPATH; 604: return OTHER; 605: } 606: 607: /* 608: * Generate the current version of the news software. 609: */ 610: 611: static char *my_version = NULL; 612: 613: char * 614: genversion() 615: { 616: char rb[BUFLEN]; 617: register char *t; 618: 619: if (my_version == NULL) { 620: #ifdef HIDDENNET 621: if (strcmp(LOCALSYSNAME, FULLSYSNAME)) 622: (void) sprintf(rb, "version %s; site %s.%s%s", 623: news_version, LOCALSYSNAME, FULLSYSNAME, MYDOMAIN); 624: else 625: #endif /* !HIDDENNET */ 626: (void) sprintf(rb, "version %s; site %s%s", 627: news_version, FULLSYSNAME, MYDOMAIN); 628: while (t = index(rb, '\t')) 629: *t = ' '; 630: my_version = malloc((unsigned)strlen(rb) + 1); 631: if (my_version == NULL) 632: xerror("malloc failed for my_version"); 633: (void) strcpy(my_version, rb); 634: } 635: return my_version; 636: } 637: 638: /* 639: * Write header at 'hp' on stream 'fp' in B+ format. Include received date 640: * if wr is 1. Leave off sysname if wr is 2. 641: */ 642: ihwrite(hp, fp, wr) 643: register struct hbuf *hp; 644: register FILE *fp; 645: int wr; 646: { 647: int iu; 648: time_t t; 649: time_t cgtdate(); 650: 651: /* 652: * We're being tricky with Path/From because of upward compatibility 653: * issues. The new version considers From and Path to be separate. 654: * The old one thinks they both mean "Path" but only believes the 655: * first one it sees, so will ignore the second. 656: */ 657: if (prefix(hp->path, FULLSYSNAME) && 658: index(NETCHRS, hp->path[strlen(FULLSYSNAME)])) 659: fprintf(fp, "Path: %s\n", hp->path); 660: else 661: fprintf(fp, "Path: %s!%s\n", FULLSYSNAME, hp->path); 662: if (hp->from[0]) 663: fprintf(fp, "From: %s\n", hp->from); 664: 665: fprintf(fp, "Newsgroups: %s\n", hp->nbuf); 666: fprintf(fp, "Subject: %s\n", hp->title); 667: fprintf(fp, "Message-ID: %s\n", hp->ident); 668: t = cgtdate(hp->subdate); 669: fprintf(fp, "Date: %s\n", arpadate(&t)); 670: #ifdef OLD 671: fprintf(fp, "Article-I.D.: %s\n", oident(hp->ident)); 672: fprintf(fp, "Posted: %s", ctime(&t)); 673: #endif /* OLD */ 674: if (*hp->expdate) 675: fprintf(fp, "Expires: %s\n", hp->expdate); 676: if (*hp->followid) { 677: register char *dp, *cp; 678: 679: dp = cp = hp->followid; 680: while (*cp != '\0') 681: if (*cp == '<' && *(cp + 1) == '>') 682: cp += 2; 683: else 684: *dp++ = *cp++; 685: *dp = '\0'; 686: if (*hp->followid) 687: fprintf(fp, "References: %s\n", hp->followid); 688: } 689: if (*hp->ctlmsg) 690: fprintf(fp, "Control: %s\n", hp->ctlmsg); 691: if (*hp->sender) 692: fprintf(fp, "Sender: %s\n", hp->sender); 693: if (*hp->replyto) 694: fprintf(fp, "Reply-To: %s\n", hp->replyto); 695: if (*hp->followto) 696: fprintf(fp, "Followup-To: %s\n", hp->followto); 697: if (*hp->distribution) 698: fprintf(fp, "Distribution: %s\n", hp->distribution); 699: if (*hp->organization) 700: fprintf(fp, "Organization: %s\n", hp->organization); 701: if (*hp->numlines) 702: fprintf(fp, "Lines: %s\n", hp->numlines); 703: if (*hp->keywords) 704: fprintf(fp, "Keywords: %s\n", hp->keywords); 705: if (*hp->summary) 706: fprintf(fp, "Summary: %s\n", hp->summary); 707: if (*hp->approved) 708: fprintf(fp, "Approved: %s\n", hp->approved); 709: if (*hp->nf_id) 710: fprintf(fp, "Nf-ID: %s\n", hp->nf_id); 711: if (*hp->nf_from) 712: fprintf(fp, "Nf-From: %s\n", hp->nf_from); 713: #ifdef DOXREFS 714: if ( wr ==1 && *hp->xref) 715: fprintf(fp, "Xref: %s\n", hp->xref); 716: #endif /* DOXREFS */ 717: for (iu = 0; iu < NUNREC; iu++) { 718: if (hp->unrec[iu]) 719: fprintf(fp, "%s\n", &hp->unrec[iu][0]); 720: } 721: putc('\n', fp); 722: } 723: 724: 725: #ifndef BSD4_2 726: /* 727: * Set nc bytes, starting at cp, to zero. 728: */ 729: bzero(cp, nc) 730: register char *cp; 731: register int nc; 732: { 733: if (nc > 0) 734: do { 735: *cp++ = 0; 736: } while (--nc); 737: } 738: #endif /* !BSD4_2 */ 739: 740: /* 741: * hfgets is like fgets, but deals with continuation lines. 742: * It also ensures that even if a line that is too long is 743: * received, the remainder of the line is thrown away 744: * instead of treated like a second line. 745: */ 746: char * 747: hfgets(buf, len, fp) 748: char *buf; 749: int len; 750: FILE *fp; 751: { 752: register int c; 753: register int n = 0; 754: register char *cp; 755: 756: cp = buf; 757: while (n < len && (c = getc(fp)) != EOF) { 758: if (c == '\n') 759: break; 760: if (isprint(c) || c == '\b' || c == ' ' || c == '\t') { 761: *cp++ = c; 762: n++; 763: } 764: } 765: if (c == EOF && cp == buf) 766: return NULL; 767: *cp = '\0'; 768: 769: if (c != '\n') { 770: /* Line too long - part read didn't fit into a newline */ 771: while ((c = getc(fp)) != '\n' && c != EOF) 772: ; 773: } else if (cp == buf) { 774: /* Don't look for continuation of blank lines */ 775: *cp++ = '\n'; 776: *cp = '\0'; 777: return buf; 778: } 779: 780: while ((c = getc(fp)) == ' ' || c == '\t') { /* for each cont line */ 781: /* Continuation line. */ 782: if ((n += 2) < len) { 783: *cp++ = '\n'; 784: *cp++ = c; 785: } 786: while ((c = getc(fp)) != '\n' && c != EOF) 787: if ((isprint(c) || c == '\b' || c == ' ' || c == '\t') 788: && n++ < len) 789: *cp++ = c; 790: } 791: if (n >= len - 1) 792: cp = buf + len - 2; 793: *cp++ = '\n'; 794: *cp = '\0'; 795: if (c != EOF) 796: (void) ungetc(c, fp); /* push back first char of next header */ 797: return buf; 798: }