1: /* 2: * This software is Copyright (c) 1985 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: * 16: * funcs2 - functions used by both inews and readnews. 17: */ 18: 19: #ifdef SCCSID 20: static char *SccsId = "@(#)funcs2.c 1.13 1/17/86"; 21: #endif /* SCCSID */ 22: 23: #include "params.h" 24: 25: #ifdef SunIII 26: #ifndef INTERNET 27: #define INTERNET 28: #endif /* !INTERNET */ 29: #endif /* SunIII */ 30: 31: /*LINTLIBRARY*/ 32: 33: /* 34: * Get user name and home directory. 35: */ 36: getuser() 37: { 38: static int flag = TRUE; 39: register struct passwd *p; 40: 41: if (flag) { 42: if ((p = getpwuid(uid)) == NULL) 43: xerror("Cannot get user's name"); 44: if ( username == NULL || username[0] == 0) 45: username = AllocCpy(p->pw_name); 46: userhome = AllocCpy(p->pw_dir); 47: flag = FALSE; 48: } 49: (void) strcpy(header.path, username); 50: } 51: 52: static FILE *sysfile; 53: 54: char *fldget(); 55: 56: static int sfline; 57: 58: /* 59: * Open SUBFILE. 60: */ 61: s_openr() 62: { 63: sysfile = xfopen(SUBFILE, "r"); 64: sfline = 0; 65: } 66: 67: /* 68: * Read SUBFILE. 69: */ 70: s_read(sp) 71: register struct srec *sp; 72: { 73: register char *p; 74: register int c; 75: char *e; 76: int chop_spaces = 0; 77: again: 78: p = bfr; 79: /* 80: * Read the SUBFILE (/usr/lib/news/sys) from the current 81: * position to the first unescaped newline. If a newline is 82: * escaped with a backslash (\) continue reading but throw away 83: * the backslash and newline; read the next line skipping spaces 84: * and tabs until the first non-space/tab character, then start 85: * looking for a newline again. Skipping the leading 86: * spaces/tabs after a escaped newline keeps the news groups 87: * together. If a line begins with a newline, just skip it. 88: */ 89: for (e=p+LBUFLEN; p < e && (c=getc(sysfile)) != EOF; p++) { 90: *p = c; 91: if (c == '\n') { 92: sfline++; 93: if (p == bfr || p[-1] != '\\') { 94: p[1] = '\0'; 95: break; 96: } else { 97: chop_spaces++; 98: p -= 2; 99: } 100: } else if (chop_spaces) { 101: if (c == '\t' || c == ' ') 102: p--; 103: else 104: chop_spaces = 0; 105: } 106: } 107: if (c == EOF) { 108: return FALSE; 109: } 110: p = bfr; 111: if (*p == '\n') 112: goto again; /* skip newlines */ 113: if (!nstrip(p)) 114: xerror("SUBFILE (%s) line %d too long.", SUBFILE, sfline); 115: if (*p == '#') 116: goto again; 117: sp->s_xmit[0] = '\0'; 118: sp->s_flags[0] = '\0'; 119: sp->s_nosend = (char *)0; 120: 121: p = fldget(sp->s_name, p); 122: if (*p++ == '\0') 123: xerror("Bad SUBFILE (%s) line %d.", SUBFILE, sfline); 124: /* 125: * A sys file line reading "ME" means the name of the local system. 126: */ 127: if (strcmp(sp->s_name, "ME") == 0) 128: (void) strcpy(sp->s_name, FULLSYSNAME); 129: e = index(sp->s_name, '/'); 130: if (e) { 131: *e++ = '\0'; 132: sp->s_nosend = e; 133: } 134: p = fldget(sp->s_nbuf, p); 135: lcase(sp->s_nbuf); 136: if (*p++ == '\0') 137: return TRUE; 138: 139: p = fldget(sp->s_flags, p); 140: if (*p++ == '\0') 141: return TRUE; 142: 143: (void) fldget(sp->s_xmit, p); 144: return TRUE; 145: } 146: 147: char * 148: fldget(q, p) 149: register char *q, *p; 150: { 151: while (*p && *p != ':') { 152: if (*p == '\\' && p[1]==':') 153: p++; 154: *q++ = *p++; 155: } 156: *q = '\0'; 157: return p; 158: } 159: 160: /* 161: * Find the SUBFILE record for a system. 162: */ 163: s_find(sp, system) 164: register struct srec *sp; 165: char *system; 166: { 167: s_openr(); 168: while (s_read(sp)) 169: if (strncmp(system, sp->s_name, SNLN) == 0) { 170: s_close(); 171: return TRUE; 172: } 173: s_close(); 174: return FALSE; 175: } 176: 177: /* 178: * Close sysfile. 179: */ 180: s_close() 181: { 182: (void) fclose(sysfile); 183: } 184: 185: time_t 186: cgtdate(datestr) 187: char *datestr; 188: { 189: char junk[40],month[40],day[30],tod[60],year[50]; 190: static time_t lasttime; 191: static char lastdatestr[BUFLEN] = ""; 192: 193: if ( lastdatestr[0] && strcmp(datestr, lastdatestr) == 0) 194: return lasttime; 195: lasttime = getdate(datestr, (struct timeb *)NULL); 196: if (lasttime < 0 && 197: sscanf(datestr, "%s %s %s %s %s", junk, month, day, tod, year) == 5) { 198: (void) sprintf(bfr, "%s %s, %s %s", month, day, year, tod); 199: lasttime = getdate(bfr, (struct timeb *)NULL); 200: } 201: strncpy(lastdatestr, datestr, BUFLEN); 202: return lasttime; 203: } 204: 205: lcase(s) 206: register char *s; 207: { 208: register char *ptr; 209: 210: for (ptr = s; *ptr; ptr++) 211: if (isupper(*ptr)) 212: *ptr = tolower(*ptr); 213: } 214: 215: /* 216: * Return a compact representation of the person who posted the given 217: * message. A sender or internet name will be used, otherwise 218: * the last part of the path is used preceded by an optional ".." 219: */ 220: char * 221: tailpath(hp) 222: struct hbuf *hp; 223: { 224: char *p, *r; 225: static char resultbuf[BUFLEN]; 226: char pathbuf[PATHLEN]; 227: char *malloc(); 228: 229: /* 230: * This only happens for articles posted by old news software 231: * in non-internet format. 232: */ 233: resultbuf[0] = '\0'; 234: (void) strncpy(pathbuf, hp->path, PATHLEN); 235: p = index(pathbuf, ' '); 236: if (p) 237: *p = '\0'; /* Chop off trailing " (name)" */ 238: r = rindex(pathbuf, '!'); 239: if (r == 0) { 240: r = pathbuf; 241: } else { 242: while (r > pathbuf && *--r != '!') 243: ; 244: if (r > pathbuf) { 245: r++; 246: (void) strcpy(resultbuf, "..!"); 247: } 248: } 249: (void) strcat(resultbuf, r); 250: return resultbuf; 251: } 252: 253: /* 254: * arpadate is like ctime(3) except that the time is returned in 255: * an acceptable ARPANET time format instead of ctime format. 256: */ 257: char * 258: arpadate(longtime) 259: time_t *longtime; 260: { 261: register char *p, *q, *ud; 262: register int i; 263: static char b[40]; 264: extern struct tm *gmtime(); 265: extern char *asctime(); 266: 267: /* Get current time. This will be used resolve the timezone. */ 268: ud = asctime(gmtime(longtime)); 269: 270: /* Crack the UNIX date line in a singularly unoriginal way. */ 271: q = b; 272: 273: #ifdef notdef 274: /* until every site installs the fix to getdate.y, the day 275: of the week can cause time warps */ 276: p = &ud[0]; /* Mon */ 277: *q++ = *p++; 278: *q++ = *p++; 279: *q++ = *p++; 280: *q++ = ','; *q++ = ' '; 281: #endif 282: 283: p = &ud[8]; /* 16 */ 284: if (*p == ' ') 285: p++; 286: else 287: *q++ = *p++; 288: *q++ = *p++; *q++ = ' '; 289: 290: p = &ud[4]; /* Sep */ 291: *q++ = *p++; *q++ = *p++; *q++ = *p++; *q++ = ' '; 292: 293: p = &ud[22]; /* 1979 */ 294: *q++ = *p++; *q++ = *p++; *q++ = ' '; 295: 296: p = &ud[11]; /* 01:03:52 */ 297: for (i = 8; i > 0; i--) 298: *q++ = *p++; 299: 300: *q++ = ' '; 301: *q++ = 'G'; /* GMT */ 302: *q++ = 'M'; 303: *q++ = 'T'; 304: *q = '\0'; 305: 306: return b; 307: } 308: 309: char * 310: replyname(hptr) 311: struct hbuf *hptr; 312: { 313: register char *ptr; 314: static char tbuf[PATHLEN]; 315: 316: ptr = hptr->path; 317: if (prefix(ptr, FULLSYSNAME) && 318: index(NETCHRS, ptr[strlen(FULLSYSNAME)])) 319: ptr = index(ptr, '!') + 1; 320: #ifdef INTERNET 321: if (hptr->from[0]) 322: ptr = hptr->from; 323: if (hptr->replyto[0]) 324: ptr = hptr->replyto; 325: #endif 326: (void) strcpy(tbuf, ptr); 327: ptr = index(tbuf, '('); 328: if (ptr) { 329: while (ptr[-1] == ' ') 330: ptr--; 331: *ptr = 0; 332: } 333: #ifdef SunIII 334: if (ptr = rindex(tbuf, '.')) { 335: if (prefix(++ptr, "OZ")) { 336: /* some people only allow it in lower case ... */ 337: strcpy(ptr, "oz"); 338: return tbuf; 339: } 340: if (prefix(ptr, "UUCP") || prefix(ptr, "ARPA") || 341: prefix(ptr, "DEC") || prefix(ptr, "CSNET")) { 342: strcat(tbuf, "@munnari.oz"); /* via sun to munnari */ 343: return tbuf; 344: } 345: } 346: /* 347: * must(?) have come from a uucp site, lets look see if path passes 348: * through munnari, and if so delete the fake uucp path after that. 349: */ 350: for (ptr = tbuf ;; ptr++) { 351: if (prefix(ptr, "munnari!")) { 352: strcpy(tbuf, ptr+8); 353: break; 354: } 355: ptr = index(ptr, '!'); 356: if (ptr == (char *)0) 357: break; 358: } 359: /* 360: * now, just send the address we have left to munnari, and 361: * hope that something sensible will be done with it there. 362: * (This works in more cases than you'd think ...) 363: */ 364: strcat(tbuf, "@munnari.oz"); 365: #else /* !SunIII */ 366: #ifndef INTERNET 367: /* 368: * Play games stripping off multiple berknet 369: * addresses (a!b!c:d:e => a!b!d:e) here. 370: */ 371: for (ptr=tbuf; *ptr; ptr++) { 372: register char *ptr2; 373: 374: if (index(NETCHRS, *ptr) && *ptr == ':' && 375: (ptr2=index(ptr+1, ':'))) 376: (void) strcpy(ptr, ptr2); 377: } 378: #endif /* !INTERNET */ 379: #endif /* SunIII */ 380: return tbuf; 381: } 382: 383: #ifdef DBM 384: typedef struct { 385: char *dptr; 386: int dsize; 387: } datum; 388: #endif /* DBM */ 389: 390: /* 391: * Given an article ID, find the line in the history file that mentions it. 392: * Return the text of the line, or NULL if not found. A pointer to a 393: * static area is returned. 394: */ 395: char * 396: findhist(artid) 397: char *artid; 398: { 399: static char lbuf[256]; 400: char oidbuf[BUFSIZ]; 401: FILE *hfp; 402: register char *p; 403: #ifdef DBM 404: datum lhs, rhs; 405: datum fetch(); 406: long fpos; /* We have to use an explicit variable to insure alignment */ 407: #else /* !DBM */ 408: char *histfile(); 409: #endif /* !DBM */ 410: 411: /* Try to understand old artid's as well. Assume .UUCP domain. */ 412: if (artid[0] != '<') { 413: p = index(artid, '.'); 414: if (p) 415: *p++ = '\0'; 416: (void) sprintf(oidbuf, "<%s@%s.UUCP>", p, artid); 417: if (p) 418: *--p = '.'; 419: } else 420: (void) strcpy(oidbuf, artid); 421: lcase(oidbuf); 422: #ifdef DBM 423: initdbm(ARTFILE); 424: lhs.dptr = oidbuf; 425: lhs.dsize = strlen(lhs.dptr) + 1; 426: rhs = fetch(lhs); 427: if (rhs.dptr == NULL) 428: return NULL; 429: hfp = xfopen(ARTFILE, "r"); 430: /* The bcopy is NECESSARY to insure alignment on some machines */ 431: bcopy(rhs.dptr, (char *)&fpos, sizeof (long)); 432: fseek(hfp, fpos, 0); 433: #else /* !DBM */ 434: hfp = xfopen(histfile(oidbuf), "r"); 435: #endif /* !DBM */ 436: while (fgets(lbuf, BUFLEN, hfp) != NULL) { 437: p = index(lbuf, '\t'); 438: if (p == NULL) 439: p = index(lbuf, '\n'); 440: *p = 0; 441: if (strcmp(lbuf, artid) == 0 || strcmp(lbuf, oidbuf) == 0) { 442: (void) fclose(hfp); 443: *p = '\t'; 444: *(lbuf + strlen(lbuf) - 1) = 0; /* zap the \n */ 445: return lbuf; 446: } 447: #ifdef DBM 448: break; 449: #endif /* DBM */ 450: } 451: (void) fclose(hfp); 452: return NULL; 453: } 454: 455: /* 456: * Hunt up the article "artid", and return the newsgroup/artnum 457: * where it can be found. 458: */ 459: char * 460: findfname(artid) 461: char *artid; 462: { 463: char *line, *p, *q; 464: char *findhist(); 465: static char fname[BUFLEN]; 466: 467: line = findhist(artid); 468: if (line) { 469: /* Look for it stored as an article, where it should be */ 470: p = index(line, '\t'); 471: p = index(p+1, '\t'); 472: p++; 473: if (*p) { 474: q = index(p, ' '); 475: if (q) 476: *q = 0; 477: (void) strcpy(fname, p); 478: return fname; 479: } 480: } 481: return NULL; 482: } 483: 484: /* 485: * Hunt up the article "artid", fopen it for read, and return a 486: * file descriptor to it. We look everywhere we can think of. 487: */ 488: FILE * 489: hfopen(artid) 490: char *artid; 491: { 492: char *p; 493: char *findhist(); 494: FILE *rv = NULL; 495: char fname[BUFLEN]; 496: 497: p = findfname(artid); 498: if (p) { 499: (void) strcpy(fname, dirname(p)); 500: rv = fopen(fname, "r"); /* NOT xfopen! */ 501: if (rv == NULL) 502: xerror("Cannot hfopen article %s", artid); 503: } 504: return rv; 505: } 506: 507: #ifdef DBM 508: /* 509: ** Avoid problems of multiple dbminit calls. 510: */ 511: initdbm(name) 512: char *name; 513: { 514: static int called = 0; 515: 516: if (called != 0) 517: return; 518: called = 1; 519: (void) dbminit(name); 520: } 521: #endif 522: 523: #ifndef BSD4_2 524: /* 525: * move n bytes from a to b 526: */ 527: bcopy(a, b, n) 528: register char *a, *b; 529: register n; 530: { 531: while (--n >= 0) 532: *b++ = *a++; 533: } 534: #endif 535: 536: #if !defined(BSD4_2) && !defined(BSD4_1C) 537: rename(from,to) 538: register char *from, *to; 539: { 540: (void) unlink(to); 541: if (link(from, to) < 0) 542: return -1; 543: 544: (void) unlink(from); 545: return 0; 546: } 547: #endif /* !BSD4_2 && ! BSD4_1C */ 548: 549: #ifndef DBM 550: /* 551: ** Generate the appropriate history subfile name 552: */ 553: char * 554: histfile(hline) 555: char *hline; 556: { 557: char *p; 558: char chr; /* least significant digit of article number */ 559: static char subfile[BUFLEN]; 560: 561: p = strchr(hline, '@'); 562: if (p != NULL && p > hline) 563: chr = *(p - 1); 564: else 565: chr = '0'; 566: if (!isdigit(chr)) 567: chr = '0'; 568: sprintf(subfile, "%s.d/%c", ARTFILE, chr); 569: if (access(subfile, 04) < 0) 570: return(ARTFILE); 571: return(subfile); 572: } 573: #endif /* !DBM */