1: /* 2: ** Get header info from mail-format file. 3: ** Return non-zero on success. 4: */ 5: 6: #include "defs.h" 7: #include <ctype.h> 8: #include <sys/types.h> 9: #include <stdio.h> 10: #include "header.h" 11: #include "llist.h" 12: 13: #ifndef isblank 14: #define isblank(c) ((c) == ' ' || (c) == '\t') 15: #endif 16: 17: char *hfgets(); 18: char *arpadate(); 19: char *errmsg(); 20: char *strpbrk(); 21: time_t cgtdate(); 22: extern char *index(); 23: extern unsigned strlen(); 24: extern time_t time(); 25: 26: #define FROM 1 27: #define NEWSGROUP 2 28: #define TITLE 3 29: #define SUBMIT 4 30: #define RECEIVE 5 31: #define EXPIRE 6 32: #define ARTICLEID 7 33: #define MESSAGEID 8 34: #define REPLYTO 9 35: #define FOLLOWID 10 36: #define CONTROL 11 37: #define SENDER 12 38: #define FOLLOWTO 13 39: #define PATH 14 40: #define POSTVERSION 15 41: #define RELAYVERSION 16 42: #define DISTRIBUTION 17 43: #define ORGANIZATION 18 44: #define NUMLINES 19 45: #define KEYWORDS 20 46: #define APPROVED 21 47: #define NFID 22 48: #define NFFROM 23 49: #define XREF 24 50: #define SUMMARY 25 51: #define FULLNAME 26 52: #define OTHER 99 53: 54: /* 55: ** This is the list of headers we recognize. 56: ** All others get stripped before they get to inews. 57: */ 58: struct htype { 59: char *hname; 60: int hid; 61: } htype[] = { 62: {"Approved:", APPROVED}, 63: {"Article-I.D.:", ARTICLEID}, 64: {"Control:", CONTROL}, 65: {"Date-Received:", RECEIVE}, 66: {"Date:", SUBMIT}, 67: {"Distribution:", DISTRIBUTION}, 68: {"Expires:", EXPIRE}, 69: {"Followup-To:", FOLLOWTO}, 70: {"From:", FROM}, 71: /* {"Full-Name:", FULLNAME}, */ 72: {"In-Reply-To:", FOLLOWID}, 73: {"Keywords:", KEYWORDS}, 74: {"Lines:", NUMLINES}, 75: {"Message-ID:", MESSAGEID}, 76: {"Newsgroups:", NEWSGROUP}, 77: {"Nf-From:", NFFROM}, 78: {"Nf-ID:", NFID}, 79: {"Organization:", ORGANIZATION}, 80: {"Path:", PATH}, 81: {"Posted:", SUBMIT}, 82: {"Posting-Version:", POSTVERSION}, 83: /* {"Received:", RECEIVE}, a bad name w.r.t. RFC822 */ 84: {"References:", FOLLOWID}, 85: {"Relay-Version:", RELAYVERSION}, 86: {"Reply-To:", REPLYTO}, 87: {"Sender:", SENDER}, 88: {"Subject:", TITLE}, 89: {"Summary:", SUMMARY}, 90: {"Title:", TITLE}, 91: {"Xref:", XREF}, 92: {(char *)NULL, NULL} 93: }; 94: 95: char *malloc(); 96: 97: rfc822read(hp, fp, bfr) 98: register struct hbuf *hp; 99: register FILE *fp; 100: char *bfr; 101: { 102: register int i = type(bfr); 103: long curpos; 104: 105: do { 106: curpos = ftell(fp); 107: switch (i) { 108: case FROM: 109: getfield(bfr, hp->from, sizeof(hp->from)); 110: break; 111: case NEWSGROUP: 112: getfield(bfr, hp->nbuf, sizeof(hp->nbuf)); 113: break; 114: case TITLE: 115: getfield(bfr, hp->title, sizeof(hp->title)); 116: break; 117: case SUBMIT: 118: getfield(bfr, hp->subdate, sizeof(hp->subdate)); 119: break; 120: case EXPIRE: 121: getfield(bfr, hp->expdate, sizeof(hp->expdate)); 122: break; 123: case MESSAGEID: 124: getfield(bfr, hp->ident, sizeof(hp->ident)); 125: break; 126: case REPLYTO: 127: getfield(bfr, hp->replyto, sizeof(hp->replyto)); 128: break; 129: case FOLLOWID: 130: getfield(bfr, hp->followid, sizeof(hp->followid)); 131: break; 132: case SENDER: 133: getfield(bfr, hp->sender, sizeof(hp->sender)); 134: break; 135: case FOLLOWTO: 136: getfield(bfr, hp->followto, sizeof(hp->followto)); 137: break; 138: case CONTROL: 139: getfield(bfr, hp->ctlmsg, sizeof(hp->ctlmsg)); 140: break; 141: case DISTRIBUTION: 142: getfield(bfr, hp->distribution, sizeof(hp->distribution)); 143: break; 144: case ORGANIZATION: 145: getfield(bfr, hp->organization, sizeof(hp->organization)); 146: break; 147: case KEYWORDS: 148: getfield(bfr, hp->keywords, sizeof(hp->keywords)); 149: break; 150: case APPROVED: 151: getfield(bfr, hp->approved, sizeof(hp->approved)); 152: break; 153: case SUMMARY: 154: getfield(bfr, hp->summary, sizeof(hp->summary)); 155: break; 156: } 157: } while ((i = type(hfgets(bfr, LBUFLEN, fp))) > 0); 158: 159: if (*bfr != '\n') 160: fseek(fp, curpos, 0); 161: return(TRUE); 162: } 163: 164: #define its(type) (prefix(ptr, type)) 165: 166: type(ptr) 167: register char *ptr; 168: { 169: register struct htype *hp; 170: register char *colon, *space; 171: static int lasthdr = FALSE; /* for continuation headers */ 172: 173: /* 174: ** some consistency checks (i.e. is this really a header line?) 175: */ 176: if ((ptr == NULL) || !isascii(*ptr) || (*ptr == '\n')) 177: return(lasthdr = FALSE); 178: 179: if (isblank(*ptr)) /* continuation line? */ 180: return(lasthdr); 181: 182: colon = index(ptr, ':'); 183: space = index(ptr, ' '); 184: if (!colon || space && space < colon) 185: return(lasthdr = FALSE); 186: 187: for(hp = htype; hp->hname; hp++) { 188: if (its(hp->hname)) 189: return(lasthdr = hp->hid); 190: } 191: return(lasthdr = OTHER); 192: } 193: 194: /* 195: ** Get the contents of the field of the header line, appending it, 196: ** with a space delimeter if it's a continuation line. 197: ** If there is already something in the header storage, skip this 198: ** header line and the continuations. 199: */ 200: getfield(src, dest, size) 201: register char *src, *dest; 202: int size; /* of dest (total bytes) */ 203: { 204: static int skip = FALSE; /* skip the continuation lines */ 205: 206: if (src == (char *)NULL || dest == (char *)NULL) 207: return(FALSE); 208: 209: if (isblank(*src)) { /* continuation line? */ 210: if (skip) 211: return(TRUE); 212: if ((size -= strlen(dest)) <= 0) /* any space left? */ 213: return(FALSE); 214: while(*src && isblank(*src)) /* eat whitespace */ 215: src++; 216: *--src = ' '; /* backup & add one */ 217: (void) strncat(dest, src, size - 1); /* append to hdr */ 218: } else { 219: skip = FALSE; 220: if (*dest) /* already got one? */ 221: return(skip = TRUE); /* skip continuation */ 222: if ((src = index(src, ':')) == (char *)NULL) /* impossible */ 223: return(FALSE); 224: src++; /* skip colon */ 225: while(*src && isblank(*src)) /* eat whitespace */ 226: src++; 227: (void) strncpy(dest, src, size - 1); 228: } 229: (void) nstrip(dest); 230: return(TRUE); 231: } 232: 233: /* 234: ** Write out an RFC822 header, paying no attention to line limits. 235: ** Ideally, we should do continuations in here... 236: */ 237: rfc822write(hp, fp) 238: register struct hbuf *hp; 239: register FILE *fp; 240: { 241: time_t t; 242: 243: if (hp->path[0]) 244: fprintf(fp, "Path: %s\n", hp->path); 245: if (hp->from[0]) 246: fprintf(fp, "From: %s\n", hp->from); 247: if (hp->nbuf[0]) 248: fprintf(fp, "Newsgroups: %s\n", hp->nbuf); 249: if (hp->title[0]) 250: fprintf(fp, "Subject: %s\n", hp->title); 251: if (hp->ident[0]) 252: fprintf(fp, "Message-ID: %s\n", hp->ident); 253: if (hp->subdate[0]) 254: t = cgtdate(hp->subdate); 255: else 256: time(&t); 257: fprintf(fp, "Date: %s\n", arpadate(&t)); 258: if (*hp->expdate) 259: fprintf(fp, "Expires: %s\n", hp->expdate); 260: if (*hp->followid) 261: fprintf(fp, "References: %s\n", hp->followid); 262: if (*hp->ctlmsg) 263: fprintf(fp, "Control: %s\n", hp->ctlmsg); 264: if (*hp->sender) 265: fprintf(fp, "Sender: %s\n", hp->sender); 266: if (*hp->replyto) 267: fprintf(fp, "Reply-To: %s\n", hp->replyto); 268: if (*hp->followto) 269: fprintf(fp, "Followup-To: %s\n", hp->followto); 270: if (*hp->distribution) 271: fprintf(fp, "Distribution: %s\n", hp->distribution); 272: if (*hp->organization) 273: fprintf(fp, "Organization: %s\n", hp->organization); 274: if (*hp->keywords) 275: fprintf(fp, "Keywords: %s\n", hp->keywords); 276: if (*hp->summary) 277: fprintf(fp, "Summary: %s\n", hp->summary); 278: if (*hp->approved) 279: fprintf(fp, "Approved: %s\n", hp->approved); 280: putc('\n', fp); 281: return(!ferror(fp)); 282: } 283: 284: /* 285: ** strip leading and trailing spaces 286: */ 287: char * 288: sp_strip(s) 289: register char *s; 290: { 291: register char *cp; 292: 293: if (s == NULL) 294: return(NULL); 295: 296: if (*s == '\0') 297: return(s); 298: 299: cp = &s[strlen(s) - 1]; 300: while(cp > s && isspace(*cp)) 301: cp--; 302: 303: *++cp = '\0'; /* zap trailing spaces */ 304: 305: for(cp = s; *cp && isspace(*cp); cp++) 306: continue; 307: 308: return(cp); /* return pointer to first non-space */ 309: } 310: 311: /* 312: ** crack an RFC822 from header field into address and fullname. 313: */ 314: crackfrom(addr,name,field) 315: char *addr, *name, *field; 316: { 317: char commbuf[LBUFLEN]; 318: char addrbuf[LBUFLEN]; 319: register char *p; 320: register char *ap = addrbuf; 321: register char *np; 322: short commfound = 0, comment = 0; 323: short addrfound = 0, address = 0; 324: struct llist comm, *cp = &comm; 325: 326: *name = '\0'; /* just make sure */ 327: *addr = '\0'; 328: 329: if ((p = field) == NULL) 330: return(FALSE); 331: 332: while(*p && isspace(*p)) /* eat leading white space */ 333: p++; 334: 335: while(*p) { 336: switch(*p) { 337: case '(': 338: if (comment == 0) { 339: np = commbuf; 340: *np = '\0'; 341: } 342: comment++; 343: break; 344: case ')': 345: if (comment > 0 && --comment == 0) { 346: *np++ = *p++; /* note incr; skip `)' */ 347: *np++ = '\0'; 348: if ((cp = l_alloc(cp, commbuf, strlen(commbuf) + 1)) == NULL) { 349: if (commfound) 350: l_free(comm); 351: return(FALSE); 352: } 353: commfound++; 354: np = NULL; 355: continue; 356: } 357: break; 358: case '<': 359: if (address) { 360: if (commfound) 361: l_free(comm); 362: return(FALSE); /* AWK! Abort! */ 363: } 364: if (!comment) { 365: address++; 366: *ap = '\0'; 367: ap = addr; 368: } 369: break; 370: case '>': 371: if (!comment && address) { 372: address--; 373: addrfound++; 374: *ap = '\0'; 375: ap = &addrbuf[strlen(addrbuf)]; 376: p++; /* skip the `>' */ 377: } 378: break; 379: } 380: 381: if (comment) { 382: *np++ = *p; 383: } else if (address) { 384: if (*p != '<') 385: *ap++ = *p; 386: } else { 387: *ap++ = *p; 388: } 389: if (*p) 390: p++; 391: else 392: break; 393: } 394: 395: *ap++ = '\0'; 396: 397: if (addrfound) { 398: (void) strcpy(name, sp_strip(addrbuf)); 399: (void) strcpy(addr, strcpy(commbuf, sp_strip(addr))); 400: } else { 401: (void) strcpy(addr, sp_strip(addrbuf)); 402: *name = '\0'; 403: } 404: /* 405: ** Just to be sure that we got the full name, 406: ** we'll take all of the comments! 407: */ 408: if (commfound) { 409: register int len; 410: register int flag = (*name != '\0' ? TRUE : FALSE); 411: 412: for(cp = &comm; cp->l_item; cp = cp->l_next) { 413: if (flag) 414: (void)strcat(name, ", "); 415: flag = TRUE; 416: if (cp->l_item[cp->l_len - 2] == ')') 417: cp->l_item[cp->l_len - 2] = '\0'; 418: (void)strcat(name, sp_strip(&cp->l_item[1])); 419: } 420: l_free(comm); 421: } 422: return(TRUE); 423: } 424: 425: /* 426: ** Check on the validity of an RFC822 message-id. 427: ** Check for enclosing `<>', an `@', and a `.' after 428: ** the `@'. Also make sure that everything is ASCII, 429: ** and non-control characters. 430: */ 431: msgid_ok(id) 432: register char *id; 433: { 434: register atdot = FALSE; 435: register closure = FALSE; 436: 437: if (id == NULL) 438: return(FALSE); /* don't waste my time! */ 439: 440: if (*id != '<') 441: return(FALSE); 442: 443: /* skip the first `<', cause we check for more */ 444: for(id++; *id; id++) { 445: switch(*id) { 446: case '<': 447: return(FALSE); /* we've already got one */ 448: case '>': 449: closure = TRUE; 450: goto end; /* djikstra is a wimp! */ 451: break; 452: case '.': 453: case '@': /* should be a domain spec */ 454: atdot = TRUE; 455: break; 456: default: 457: if (!isascii(*id) || iscntrl(*id) || isspace(*id)) 458: return(FALSE); /* quit immediately */ 459: break; 460: } 461: } 462: end: 463: return(atdot && closure); 464: } 465: 466: /* 467: ** hfgets is like fgets, but deals with continuation lines. 468: ** It also ensures that even if a line that is too long is 469: ** received, the remainder of the line is thrown away 470: ** instead of treated like a second line. 471: */ 472: char * 473: hfgets(buf, len, fp) 474: char *buf; 475: int len; 476: FILE *fp; 477: { 478: register int c; 479: register int n = 0; 480: register char *cp; 481: 482: cp = buf; 483: while (n < len && (c = getc(fp)) != EOF) { 484: if (c == '\n') 485: break; 486: if (!iscntrl(c) || c == '\b' || c == '\t') { 487: *cp++ = c; 488: n++; 489: } 490: } 491: if (c == EOF && cp == buf) 492: return NULL; 493: *cp = '\0'; 494: 495: if (c != '\n') { 496: /* Line too long - part read didn't fit into a newline */ 497: while ((c = getc(fp)) != '\n' && c != EOF) 498: ; 499: } else if (cp == buf) { 500: /* Don't look for continuation of blank lines */ 501: *cp++ = '\n'; 502: *cp = '\0'; 503: return buf; 504: } 505: 506: while ((c = getc(fp)) == ' ' || c == '\t') { /* for each cont line */ 507: /* Continuation line. */ 508: if ((n += 2) < len) { 509: *cp++ = '\n'; 510: *cp++ = c; 511: } 512: while ((c = getc(fp)) != '\n' && c != EOF) 513: if ((!iscntrl(c) || c == '\b' || c == '\t') && n++ < len) 514: *cp++ = c; 515: } 516: if (n >= len - 1) 517: cp = buf + len - 2; 518: *cp++ = '\n'; 519: *cp = '\0'; 520: if (c != EOF) 521: (void) ungetc(c, fp); /* push back first char of next header */ 522: return buf; 523: } 524: 525: /* 526: * arpadate is like ctime(3) except that the time is returned in 527: * an acceptable ARPANET time format instead of ctime format. 528: */ 529: char * 530: arpadate(longtime) 531: time_t *longtime; 532: { 533: register char *p, *q, *ud; 534: register int i; 535: static char b[40]; 536: extern struct tm *gmtime(); 537: extern char *asctime(); 538: 539: /* Get current time. This will be used resolve the timezone. */ 540: ud = asctime(gmtime(longtime)); 541: 542: /* Crack the UNIX date line in a singularly unoriginal way. */ 543: q = b; 544: 545: #ifdef notdef 546: /* until every site installs the fix to getdate.y, the day 547: of the week can cause time warps */ 548: p = &ud[0]; /* Mon */ 549: *q++ = *p++; 550: *q++ = *p++; 551: *q++ = *p++; 552: *q++ = ','; *q++ = ' '; 553: #endif 554: 555: p = &ud[8]; /* 16 */ 556: if (*p == ' ') 557: p++; 558: else 559: *q++ = *p++; 560: *q++ = *p++; *q++ = ' '; 561: 562: p = &ud[4]; /* Sep */ 563: *q++ = *p++; *q++ = *p++; *q++ = *p++; *q++ = ' '; 564: 565: p = &ud[22]; /* 1979 */ 566: *q++ = *p++; *q++ = *p++; *q++ = ' '; 567: 568: p = &ud[11]; /* 01:03:52 */ 569: for (i = 8; i > 0; i--) 570: *q++ = *p++; 571: 572: *q++ = ' '; 573: *q++ = 'G'; /* GMT */ 574: *q++ = 'M'; 575: *q++ = 'T'; 576: *q = '\0'; 577: 578: return b; 579: } 580: 581: time_t 582: cgtdate(datestr) 583: char *datestr; 584: { 585: char junk[40], month[40], day[30], tod[60], year[50], buf[BUFLEN]; 586: static time_t lasttime; 587: static char lastdatestr[BUFLEN] = ""; 588: extern time_t getdate(); 589: 590: if (lastdatestr[0] && strcmp(datestr, lastdatestr) == 0) 591: return(lasttime); 592: lasttime = getdate(datestr, (struct timeb *)NULL); 593: if (lasttime < 0 && 594: sscanf(datestr, "%s %s %s %s %s", junk, month, day, tod, year) == 5) { 595: (void) sprintf(buf, "%s %s, %s %s", month, day, year, tod); 596: lasttime = getdate(buf, (struct timeb *)NULL); 597: } 598: strncpy(lastdatestr, datestr, BUFLEN); 599: return(lasttime); 600: } 601: 602: char * 603: errmsg(code) 604: int code; 605: { 606: extern int sys_nerr; 607: extern char *sys_errlist[]; 608: static char ebuf[6+5+1]; 609: 610: if (code > sys_nerr) { 611: (void) sprintf(ebuf, "Error %d", code); 612: return ebuf; 613: } else 614: return sys_errlist[code]; 615: } 616: 617: /* 618: * Strip trailing newlines, blanks, and tabs from 's'. 619: * Return TRUE if newline was found, else FALSE. 620: */ 621: nstrip(s) 622: register char *s; 623: { 624: register char *p; 625: register int rc; 626: 627: rc = FALSE; 628: p = s; 629: while (*p) 630: if (*p++ == '\n') 631: rc = TRUE; 632: while (--p >= s && (*p == '\n' || *p == ' ' || *p == '\t')); 633: *++p = '\0'; 634: return rc; 635: } 636: 637: prefix(full, pref) 638: register char *full, *pref; 639: { 640: register char fc, pc; 641: 642: while ((pc = *pref++) != '\0') { 643: fc = *full++; 644: if (isupper(fc)) 645: fc = tolower(fc); 646: if (isupper(pc)) 647: pc = tolower(pc); 648: if (fc != pc) 649: return FALSE; 650: } 651: return TRUE; 652: } 653: 654: #ifndef USG 655: char * 656: strpbrk(str, chars) 657: register char *str, *chars; 658: { 659: register char *cp; 660: 661: do { 662: cp = chars - 1; 663: while (*++cp) { 664: if (*str == *cp) 665: return str; 666: } 667: } while (*str++); 668: return NULL; 669: } 670: #endif /* !USG */