1: #ifndef lint 2: static char *sccsid = "@(#)newnews.c 1.19 (Berkeley) 2/6/88"; 3: #endif 4: 5: #include "common.h" 6: #include "time.h" 7: 8: #ifdef LOG 9: int nn_told = 0; 10: int nn_took = 0; 11: #endif 12: 13: 14: /* 15: * NEWNEWS newsgroups date time ["GMT"] [<distributions>] 16: * 17: * Return the message-id's of any news articles past 18: * a certain date and time, within the specified distributions. 19: * 20: */ 21: 22: newnews(argc, argv) 23: register int argc; 24: char *argv[]; 25: { 26: register char *cp, *ngp; 27: char *key; 28: char datebuf[32]; 29: char line[MAXBUFLEN]; 30: char **distlist, **histlist; 31: static char **nglist; 32: int distcount, ngcount, histcount; 33: int all; 34: FILE *fp; 35: long date; 36: long dtol(); 37: char *ltod(); 38: #ifdef USG 39: FILE *tmplst; 40: int i; 41: char *tmpfile; 42: #endif USG 43: 44: if (argc < 4) { 45: printf("%d Usage: NEWNEWS newsgroups yymmdd hhmmss [\"GMT\"] [<distributions>].\r\n", 46: ERR_CMDSYN); 47: (void) fflush(stdout); 48: return; 49: } 50: 51: #ifdef LOG 52: sprintf(line, "%s newnews %s %s %s %s %s", 53: hostname, 54: argv[1], 55: argv[2], 56: argv[3], 57: (argc >= 5 && *argv[4] == 'G') ? "GMT" : "local", 58: (argc >= 5 && *argv[argc-1] == '<') ? argv[argc-1] : "none"); 59: syslog(LOG_INFO, line); 60: #endif 61: 62: all = (argv[1][0] == '*' && argv[1][1] == '\0'); 63: if (!all) { 64: ngcount = get_nglist(&nglist, argv[1]); 65: if (ngcount == 0) { 66: printf("%d Bogus newsgroup specifier: %s\r\n", 67: ERR_CMDSYN, argv[1]); 68: (void) fflush(stdout); 69: return; 70: } 71: } 72: 73: /* YYMMDD HHMMSS */ 74: if (strlen(argv[2]) != 6 || strlen(argv[3]) != 6) { 75: printf("%d Date/time must be in form YYMMDD HHMMSS.\r\n", 76: ERR_CMDSYN); 77: (void) fflush(stdout); 78: return; 79: } 80: 81: (void) strcpy(datebuf, argv[2]); 82: (void) strcat(datebuf, argv[3]); 83: 84: argc -= 4; 85: argv += 4; 86: 87: /* 88: * Flame on. The history file is not stored in GMT, but 89: * in local time. So we have to convert GMT to local time 90: * if we're given GMT, otherwise we need only chop off the 91: * the seconds. Such braindamage. 92: */ 93: 94: key = datebuf; /* Unless they specify GMT */ 95: 96: if (argc > 0) { 97: if (!strcasecmp(*argv, "GMT")) { /* Which we handle here */ 98: date = dtol(datebuf); 99: if (date < 0) { 100: printf("%d Invalid date specification.\r\n", 101: ERR_CMDSYN); 102: (void) fflush(stdout); 103: return; 104: } 105: date = gmt_to_local(date); 106: key = ltod(date); 107: ++argv; 108: --argc; 109: } 110: } 111: 112: /* So, key now points to the local time, but we need to zap secs */ 113: 114: key[10] = '\0'; 115: 116: distcount = 0; 117: if (argc > 0) { 118: distcount = get_distlist(&distlist, *argv); 119: if (distcount < 0) { 120: printf("%d Bad distribution list: %s\r\n", ERR_CMDSYN, 121: *argv); 122: (void) fflush(stdout); 123: return; 124: } 125: } 126: 127: #ifdef USG 128: if ((tmpfile = mktemp("/tmp/listXXXXXX")) == NULL || 129: (tmplst = fopen(tmpfile, "w+")) == NULL) { 130: printf("%d Cannot process history file.\r\n", ERR_FAULT); 131: (void) fflush(stdout); 132: return; 133: } 134: 135: for (i = 0; i < 9; i++) { 136: sprintf(historyfile, "%s.d/%d", HISTORY_FILE, i); 137: #endif USG 138: 139: fp = fopen(historyfile, "r"); 140: if (fp == NULL) { 141: #ifdef SYSLOG 142: syslog(LOG_ERR, "newnews: fopen %s: %m", historyfile); 143: #endif 144: #ifndef USG 145: printf("%d Cannot open history file.\r\n", ERR_FAULT); 146: (void) fflush(stdout); 147: return; 148: #else USG 149: continue; 150: #endif USG 151: } 152: 153: #ifndef USG 154: printf("%d New news by message id follows.\r\n", OK_NEWNEWS); 155: #endif not USG 156: 157: if (seekuntil(fp, key, line, sizeof (line)) < 0) { 158: #ifndef USG 159: printf(".\r\n"); 160: (void) fflush(stdout); 161: #endif not USG 162: (void) fclose(fp); 163: #ifndef USG 164: return; 165: #else USG 166: continue; 167: #endif USG 168: } 169: 170: /* 171: * History file looks like: 172: * 173: * <1569@emory.UUCP> 01/22/86 09:19 net.micro.att/899 ucb.general/2545 174: * ^--tab ^--tab ^--space ^sp\0 175: * Sometimes the newsgroups are missing; we try to be robust and 176: * ignore such bogosity. We tackle this by our usual parse routine, 177: * and break the list of articles in the history file into an argv 178: * array with one newsgroup per entry. 179: */ 180: 181: do { 182: if ((cp = index(line, '\t')) == NULL) 183: continue; 184: 185: if ((ngp = index(cp+1, '\t')) == NULL) /* 2nd tab */ 186: continue; 187: ++ngp; /* Points at newsgroup list */ 188: if (*ngp == '\n') 189: continue; 190: histcount = get_histlist(&histlist, ngp); 191: if (histcount == 0) 192: continue; 193: 194: /* 195: * For each newsgroup on this line in the history 196: * file, check it against the newsgroup names we're given. 197: * If it matches, then see if we're hacking distributions. 198: * If so, open the file and match the distribution line. 199: */ 200: 201: if (!all) 202: if (!ngmatch(restreql, 0, nglist, ngcount, 203: histlist, histcount)) 204: continue; 205: 206: if (distcount) 207: if (!distmatch(distlist, distcount, histlist, histcount)) 208: continue; 209: 210: *cp = '\0'; 211: #ifdef USG 212: fputs(line, tmplst); 213: fputc('\n', tmplst); 214: #else not USG 215: putline(line); 216: #endif not USG 217: #ifdef LOG 218: nn_told++; 219: #endif 220: } while (fgets(line, sizeof(line), fp) != NULL); 221: 222: #ifndef USG 223: putchar('.'); 224: putchar('\r'); 225: putchar('\n'); 226: (void) fflush(stdout); 227: #endif 228: (void) fclose(fp); 229: #ifdef USG 230: } 231: printf("%d New news by message id follows.\r\n", OK_NEWNEWS); 232: rewind(tmplst); 233: while (fgets(line, sizeof(line), tmplst) != NULL) 234: putline(line); 235: putchar('.'); 236: putchar('\r'); 237: putchar('\n'); 238: (void) fflush(stdout); 239: (void) fclose(tmplst); 240: (void) unlink(tmpfile); 241: #endif USG 242: } 243: 244: 245: /* 246: * seekuntil -- seek through the history file looking for 247: * a line with date "key". Get that line, and return. 248: * 249: * Parameters: "fp" is the active file. 250: * "key" is the date, in form YYMMDDHHMM (no SS) 251: * "line" is storage for the first line we find. 252: * 253: * Returns: -1 on error, 0 otherwise. 254: * 255: * Side effects: Seeks in history file, modifies line. 256: */ 257: 258: seekuntil(fp, key, line, linesize) 259: FILE *fp; 260: char *key; 261: char *line; 262: int linesize; 263: { 264: char datetime[32]; 265: register int c; 266: register long top, bot, mid; 267: 268: bot = 0; 269: (void) fseek(fp, 0L, 2); 270: top = ftell(fp); 271: for(;;) { 272: mid = (top+bot)/2; 273: (void) fseek(fp, mid, 0); 274: do { 275: c = getc(fp); 276: mid++; 277: } while (c != EOF && c!='\n'); 278: if (!getword(fp, datetime, line, linesize)) { 279: return (-1); 280: } 281: switch (compare(key, datetime)) { 282: case -2: 283: case -1: 284: case 0: 285: if (top <= mid) 286: break; 287: top = mid; 288: continue; 289: case 1: 290: case 2: 291: bot = mid; 292: continue; 293: } 294: break; 295: } 296: (void) fseek(fp, bot, 0); 297: while(ftell(fp) < top) { 298: if (!getword(fp, datetime, line, linesize)) { 299: return (-1); 300: } 301: switch(compare(key, datetime)) { 302: case -2: 303: case -1: 304: case 0: 305: break; 306: case 1: 307: case 2: 308: continue; 309: } 310: break; 311: } 312: 313: return (0); 314: } 315: 316: 317: compare(s, t) 318: register char *s, *t; 319: { 320: for (; *s == *t; s++, t++) 321: if (*s == 0) 322: return(0); 323: return (*s == 0 ? -1: 324: *t == 0 ? 1: 325: *s < *t ? -2: 326: 2); 327: } 328: 329: 330: getword(fp, w, line, linesize) 331: FILE *fp; 332: register char *w; 333: char *line; 334: int linesize; 335: { 336: register char *cp; 337: 338: if (fgets(line, linesize, fp) == NULL) 339: return (0); 340: if (cp = index(line, '\t')) { 341: /* 342: * The following gross hack is present because the history file date 343: * format is braindamaged. They like "mm/dd/yy hh:mm", which is useless 344: * for relative comparisons of dates using something like atoi() or 345: * strcmp. So, this changes their format into yymmddhhmm. Sigh. 346: * 347: * 12345678901234 ("x" for cp[x]) 348: * mm/dd/yy hh:mm (their lousy representation) 349: * yymmddhhmm (our good one) 350: * 0123456789 ("x" for w[x]) 351: */ 352: *cp = '\0'; 353: (void) strncpy(w, cp+1, 15); 354: w[0] = cp[7]; /* Years */ 355: w[1] = cp[8]; 356: w[2] = cp[1]; /* Months */ 357: w[3] = cp[2]; 358: w[4] = cp[4]; /* Days */ 359: w[5] = cp[5]; 360: w[6] = cp[10]; /* Hours */ 361: w[7] = cp[11]; 362: w[8] = cp[13]; /* Minutes */ 363: w[9] = cp[14]; 364: w[10] = '\0'; 365: } else 366: w[0] = '\0'; 367: return (1); 368: } 369: 370: 371: /* 372: * distmatch -- see if a file matches a set of distributions. 373: * We have to do this by (yech!) opening the file, finding 374: * the Distribution: line, if it has one, and seeing if the 375: * things match. 376: * 377: * Parameters: "distlist" is the distribution list 378: * we want. 379: * "distcount" is the count of distributions in it. 380: * "grouplist" is the list of groups (articles) 381: * for this line of the history file. Note that 382: * this isn't quite a filename. 383: * "groupcount" is the count of groups in it. 384: * 385: * Returns: 1 if the article is in the given distribution. 386: * 0 otherwise. 387: */ 388: 389: distmatch(distlist, distcount, grouplist, groupcount) 390: char *distlist[]; 391: int distcount; 392: char *grouplist[]; 393: int groupcount; 394: { 395: register char c; 396: register char *cp; 397: register FILE *fp; 398: register int i, j; 399: char buf[MAXBUFLEN]; 400: 401: (void) strcpy(buf, spooldir); 402: (void) strcat(buf, "/"); 403: (void) strcat(buf, grouplist[0]); 404: 405: for (cp = buf; *cp; cp++) 406: if (*cp == '.') 407: *cp = '/'; 408: 409: fp = fopen(buf, "r"); 410: if (fp == NULL) { 411: #ifdef SYSLOG 412: syslog(LOG_ERR, "distmatch: fopen %s: %m", buf); 413: #endif 414: return (0); 415: } 416: 417: while (fgets(buf, sizeof (buf), fp) != NULL) { 418: if ((c = buf[0]) == '\n') /* End of header */ 419: break; 420: if (c != 'd' && c != 'D') 421: continue; 422: cp = index(cp + 1, '\n'); 423: if (cp) 424: *cp = '\0'; 425: cp = index(buf, ':'); 426: if (cp == NULL) 427: continue; 428: *cp = '\0'; 429: if (!strcasecmp(buf, "distribution")) { 430: for (i = 0; i < distcount; ++i) { 431: if (!strcasecmp(cp + 2, distlist[i])) { 432: (void) fclose(fp); 433: return (1); 434: } 435: } 436: (void) fclose(fp); 437: return (0); 438: } 439: } 440: 441: (void) fclose(fp); 442: 443: /* 444: * We've finished the header with no distribution field. 445: * So we'll assume that the distribution is the characters 446: * up to the first dot in the newsgroup name. 447: */ 448: 449: for (i = 0; i < groupcount; i++) { 450: cp = index(grouplist[i], '.'); 451: if (cp) 452: *cp = '\0'; 453: for (j = 0; j < distcount; j++) 454: if (!strcasecmp(grouplist[i], distlist[j])) 455: return (1); 456: } 457: 458: return (0); 459: } 460: 461: 462: /* 463: * get_histlist -- return a nicely set up array of newsgroups 464: * (actually, net.foo.bar/article_num) along with a count. 465: * 466: * Parameters: "array" is storage for our array, 467: * set to point at some static data. 468: * "list" is the history file newsgroup list. 469: * 470: * Returns: Number of group specs found. 471: * 472: * Side effects: Changes static data area. 473: */ 474: 475: get_histlist(array, list) 476: char ***array; 477: char *list; 478: { 479: register int histcount; 480: register char *cp; 481: static char **hist_list = (char **) NULL; 482: 483: cp = index(list, '\n'); 484: if (cp) 485: *cp-- = '\0'; 486: histcount = parsit(list, &hist_list); 487: *array = hist_list; 488: return (histcount); 489: } 490: 491: 492: /* 493: * get_nglist -- return a nicely set up array of newsgroups 494: * along with a count, when given an NNTP-spec newsgroup list 495: * in the form ng1,ng2,ng... 496: * 497: * Parameters: "array" is storage for our array, 498: * set to point at some static data. 499: * "list" is the NNTP newsgroup list. 500: * 501: * Returns: Number of group specs found. 502: * 503: * Side effects: Changes static data area. 504: */ 505: 506: get_nglist(array, list) 507: char ***array; 508: char *list; 509: { 510: register char *cp; 511: register int ngcount; 512: 513: for (cp = list; *cp != '\0'; ++cp) 514: if (*cp == ',') 515: *cp = ' '; 516: 517: ngcount = parsit(list, array); 518: 519: return (ngcount); 520: }