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