1 #ifndef lint
2 static char sccsid[] = "@(#)$Id: newnews.c,v 1.27 1994/11/01 06:08:21 sob Exp sob $";
3 #endif
4
5 #include "common.h"
6 #include "date.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 * Made RFC977/Y2K compliant via changes in dtol() - see time.c - and
21 * slightly simplified the comparison between incoming date and history
22 * file. Fixed(?) the local time offset. D. Glover 29/11/99
23 *
24 */
25
26 void
newnews(argc,argv)27 newnews(argc, argv)
28 register int argc;
29 char *argv[];
30 {
31 register char *cp, *ngp;
32 char *key;
33 char datebuf[32];
34 char line[MAXBUFLEN];
35 char **distlist, **histlist;
36 static char **nglist;
37 int distcount, ngcount, histcount;
38 int all;
39 FILE *fp;
40 long date;
41 long dtol();
42 char *ltod();
43 #ifdef USGHIST
44 FILE *tmplst;
45 int i;
46 char *tmpfile;
47 #endif
48
49 if (argc < 4) {
50 printf("%d Usage: NEWNEWS newsgroups yymmdd hhmmss [\"GMT\"] [<distributions>].\r\n",
51 ERR_CMDSYN);
52 (void) fflush(stdout);
53 return;
54 }
55
56 if (!canread) {
57 printf("%d You do not have permission to read. sorry.\r\n",
58 ERR_ACCESS);
59 (void) fflush(stdout);
60 return;
61 }
62
63 #ifdef LOG
64 sprintf(line, "%s newnews %s %s %s %s %s",
65 hostname,
66 argv[1],
67 argv[2],
68 argv[3],
69 (argc >= 5 && *argv[4] == 'G') ? "GMT" : "local",
70 (argc >= 5 && *argv[argc-1] == '<') ? argv[argc-1] : "none");
71 syslog(LOG_INFO, line);
72 #endif
73
74 all = (argv[1][0] == '*' && argv[1][1] == '\0');
75 if (!all) {
76 ngcount = get_nglist(&nglist, argv[1]);
77 if (ngcount == 0) {
78 printf("%d Bogus newsgroup specifier: %s\r\n",
79 ERR_CMDSYN, argv[1]);
80 (void) fflush(stdout);
81 return;
82 }
83 }
84
85 /* YYMMDD HHMMSS */
86 if (strlen(argv[2]) != 6 || strlen(argv[3]) != 6) {
87 printf("%d Date/time must be in form YYMMDD HHMMSS.\r\n",
88 ERR_CMDSYN);
89 (void) fflush(stdout);
90 return;
91 }
92
93 (void) strcpy(datebuf, argv[2]);
94 (void) strcat(datebuf, argv[3]);
95
96 argc -= 4;
97 argv += 4;
98
99 /* Originally the "key" for searching was obtained here and the localtime
100 * offset wasn't applied when doing the actual comparison... DG 29/11/99
101 */
102 date = dtol(datebuf); /* local time here */
103 if (date < 0) {
104 printf("%d Invalid date specification.\r\n",ERR_CMDSYN);
105 (void) fflush(stdout);
106 return;
107 }
108
109 /* REMOVED apparently unwanted call to gmt_to_local()
110 * and tidied up handling of GMT/distributions DG 29/11/99
111 */
112 if (argc > 0 && !strcasecmp(*argv, "GMT")) {
113 ++argv;
114 --argc;
115 }
116 else {
117 /* now we convert from local to GMT since this is what history */
118 /* file in News 2.11 expects */
119 date = local_to_gmt(date);
120 }
121 strcpy(datebuf, ltod(date));
122 key = datebuf;
123 distcount = 0;
124 if (argc > 0) {
125 distcount = get_distlist(&distlist, *argv);
126 if (distcount < 0) {
127 printf("%d Bad distribution list: %s\r\n", ERR_CMDSYN,
128 *argv);
129 (void) fflush(stdout);
130 return;
131 }
132 }
133
134 #ifdef USGHIST
135 if ((tmpfile = mktemp("/tmp/listXXXXXX")) == NULL ||
136 (tmplst = fopen(tmpfile, "w+")) == NULL) {
137 printf("%d Cannot process history file.\r\n", ERR_FAULT);
138 (void) fflush(stdout);
139 return;
140 }
141
142 for (i = 0; i < 9; i++) {
143 sprintf(historyfile, "%s.d/%d", HISTORY_FILE, i);
144 #endif
145
146 fp = fopen(historyfile, "r");
147 if (fp == NULL) {
148 #ifdef SYSLOG
149 syslog(LOG_ERR, "newnews: fopen %s: %m", historyfile);
150 #endif
151 #ifndef USGHIST
152 printf("%d Cannot open history file.\r\n", ERR_FAULT);
153 (void) fflush(stdout);
154 return;
155 #else
156 continue;
157 #endif
158 }
159
160 #ifndef USGHIST
161 printf("%d New news by message id follows\r\n", OK_NEWNEWS);
162 #endif
163
164 if (seekuntil(fp, key, line, sizeof (line)) < 0) {
165 #ifndef USGHIST
166 printf(".\r\n");
167 (void) fflush(stdout);
168 #endif
169 (void) fclose(fp);
170 #ifndef USGHIST
171 return;
172 #else
173 continue;
174 #endif
175 }
176
177 /*
178 * History file looks like:
179 *
180 * <1569@emory.UUCP> 01/22/86 09:19 net.micro.att/899 ucb.general/2545
181 * ^--tab ^--tab ^--space ^sp\0
182 * Sometimes the newsgroups are missing; we try to be robust and
183 * ignore such bogosity. We tackle this by our usual parse routine,
184 * and break the list of articles in the history file into an argv
185 * array with one newsgroup per entry.
186 */
187
188 do {
189 if ((cp = index(line, '\t')) == NULL)
190 continue;
191
192 if ((ngp = index(cp+1, '\t')) == NULL) /* 2nd tab */
193 continue;
194 ++ngp; /* Points at newsgroup list */
195 if (*ngp == '\n')
196 continue;
197 histcount = get_histlist(&histlist, ngp);
198 if (histcount == 0)
199 continue;
200
201 /*
202 * For each newsgroup on this line in the history
203 * file, check it against the newsgroup names we're given.
204 * If it matches, then see if we're hacking distributions.
205 * If so, open the file and match the distribution line.
206 */
207
208 if (!all)
209 if (!ngmatch(restreql, 0, nglist, ngcount,
210 histlist, histcount))
211 continue;
212
213 if (distcount)
214 if (!distmatch(distlist, distcount, histlist, histcount))
215 continue;
216
217 *cp = '\0';
218 #ifdef USGHIST
219 fputs(line, tmplst);
220 fputc('\n', tmplst);
221 #else
222 putline(line);
223 #endif
224 #ifdef LOG
225 nn_told++;
226 #endif
227 } while (fgets(line, sizeof(line), fp) != NULL);
228
229 #ifndef USGHIST
230 putline(".");
231 (void) fflush(stdout);
232 #endif
233 (void) fclose(fp);
234 #ifdef USGHIST
235 }
236 printf("%d New news by message id follows\r\n", OK_NEWNEWS);
237 rewind(tmplst);
238 while (fgets(line, sizeof(line), tmplst) != NULL)
239 if (line[0] == '<') putline(line);
240 putline(".");
241 (void) fflush(stdout);
242 (void) fclose(tmplst);
243 (void) unlink(tmpfile);
244 #endif
245 }
246
247
248 /*
249 * seekuntil -- seek through the history file looking for
250 * a line with date later than "akey". Get that line, and return.
251 *
252 * Parameters: "fp" is the active file.
253 * "akey" is the date, in form YYYYMMDDHHMMSS
254 * "line" is storage for the first line we find.
255 *
256 * Returns: -1 on error, 0 otherwise.
257 *
258 * Side effects: Seeks in history file, modifies line.
259 */
260
261 int
seekuntil(fp,akey,line,linesize)262 seekuntil(fp, akey, line, linesize)
263 FILE *fp;
264 char *akey;
265 char *line;
266 int linesize;
267 {
268 char datetime[32];
269 register int c;
270 register long top, bot, mid;
271
272 bot = 0;
273 (void) fseek(fp, 0L, 2);
274 top = ftell(fp);
275 for(;;) {
276 mid = (top+bot)/2;
277 (void) fseek(fp, mid, 0);
278 do {
279 c = getc(fp);
280 mid++;
281 } while (c != EOF && c!='\n');
282 if (!getword(fp, datetime, line, linesize)) {
283 return (-1);
284 }
285 switch (compare(akey, datetime)) {
286 case -2:
287 case -1:
288 case 0:
289 if (top <= mid)
290 break;
291 top = mid;
292 continue;
293 case 1:
294 case 2:
295 bot = mid;
296 continue;
297 }
298 break;
299 }
300 (void) fseek(fp, bot, 0);
301 while(ftell(fp) < top) {
302 if (!getword(fp, datetime, line, linesize)) {
303 return (-1);
304 }
305 switch(compare(akey, datetime)) {
306 case -2:
307 case -1:
308 case 0:
309 break;
310 case 1:
311 case 2:
312 continue;
313 }
314 break;
315 }
316
317 return (0);
318 }
319
320 int
compare(s,t)321 compare(s, t)
322 register char *s, *t;
323 {
324 for (; *s == *t; s++, t++)
325 if (*s == 0)
326 return(0);
327 return (*s == 0 ? -1:
328 *t == 0 ? 1:
329 *s < *t ? -2:
330 2);
331 }
332
333 /*
334 * Combined B and C news version of getword.
335 */
336 int
getword(fp,w,line,linesize)337 getword(fp, w, line, linesize)
338 FILE *fp;
339 register char *w;
340 char *line;
341 int linesize;
342 {
343 register char *cp;
344 long qz = 0;
345 if (fgets(line, linesize, fp) == NULL)
346 return (0);
347 w[0] = '\0'; /* in case of bad format */
348 if ((cp = index(line, '\t')) != NULL) { /* find 2nd field */
349 register char *endp;
350 while (*cp == ' ' || *cp == '\t') cp++;
351 /* skip any leading spaces or tabs */
352 endp = index(cp, '~'); /* end of date-received */
353 /* This will fail for B news */
354 if (endp == NULL)
355 endp = index(cp, '\t'); /* end of expiry */
356 /* This will fail if article */
357 /* has expired */
358 if (endp == NULL) return(1); /* nothing is returned */
359
360 (void) strncpy(w, cp, endp - cp);
361 w[endp - cp] = '\0';
362 if (index(w,'/') != NULL){ /* old B news format */
363 /*
364 * The following gross hack is present because the old history file date
365 * format is braindamaged. They like "mm/dd/yy hh:mm", which is useless
366 * for relative comparisons of dates using something like atoi() or
367 * strcmp. So, this changes their format into yyyymmddhhmm. Sigh.
368 *
369 * Modified to use 4 digit years, just in case anyone is still using B news
370 * but not tested here. DG 29/11/99
371 *
372 * 01234567890123 ("x" for cp[x])
373 * mm/dd/yy hh:mm (their lousy representation)
374 * yymmddhhmmss (our good one)
375 * 012345678901 ("x" for w[x])
376 */
377 w[0] = cp[6]; /* Years */
378 w[1] = cp[7];
379 w[2] = cp[0]; /* Months */
380 w[3] = cp[1];
381 w[4] = cp[3]; /* Days */
382 w[5] = cp[4];
383 w[6] = cp[9]; /* Hours */
384 w[7] = cp[10];
385 w[8] = cp[12]; /* Minutes */
386 w[9] = cp[13];
387 w[10] = '0'; /* Seconds are faked */
388 w[11] = '0';
389 w[12] = '\0';
390
391 /* We now have YYMMDDHHMMSS - use dtol() to get something sensible for the
392 * century and then use ltod() below to make it compatible with the way
393 * the search is done. Really it would be much better to compare everything
394 * as time_t, but this means changing seekuntil() and all the underlying stuff.
395 * Maybe for a subsequent release if anyone is bothered? DG 29/11/99
396 */
397 qz = dtol(w);
398 }
399
400 else /* convert new format to yyyymmddhhmmss */
401 {
402 qz =atol(w);
403 }
404 }
405 strcpy(w,ltod(qz));
406 return (1);
407 }
408
409 /*
410 * distmatch -- see if a file matches a set of distributions.
411 * We have to do this by (yech!) opening the file, finding
412 * the Distribution: line, if it has one, and seeing if the
413 * things match.
414 *
415 * Parameters: "distlist" is the distribution list
416 * we want.
417 * "distcount" is the count of distributions in it.
418 * "grouplist" is the list of groups (articles)
419 * for this line of the history file. Note that
420 * this isn't quite a filename.
421 * "groupcount" is the count of groups in it.
422 *
423 * Returns: 1 if the article is in the given distribution.
424 * 0 otherwise.
425 */
426
427 int
distmatch(distlist,distcount,grouplist,groupcount)428 distmatch(distlist, distcount, grouplist, groupcount)
429 char *distlist[];
430 int distcount;
431 char *grouplist[];
432 int groupcount;
433 {
434 register char c;
435 register char *cp;
436 register FILE *fp;
437 register int i, j;
438 char buf[MAXBUFLEN];
439
440 (void) strcpy(buf, spooldir);
441 (void) strcat(buf, "/");
442 (void) strcat(buf, grouplist[0]);
443
444 for (cp = buf; *cp; cp++)
445 if (*cp == '.')
446 *cp = '/';
447
448 fp = fopen(buf, "r");
449 if (fp == NULL) {
450 #ifdef SYSLOG
451 syslog(LOG_ERR, "distmatch: fopen %s: %m", buf);
452 #endif
453 return (0);
454 }
455
456 while (fgets(buf, sizeof (buf), fp) != NULL) {
457 if ((c = buf[0]) == '\n') /* End of header */
458 break;
459 if (c != 'd' && c != 'D')
460 continue;
461 cp = index(cp + 1, '\n');
462 if (cp)
463 *cp = '\0';
464 cp = index(buf, ':');
465 if (cp == NULL)
466 continue;
467 *cp = '\0';
468 if (!strcasecmp(buf, "distribution")) {
469 for (i = 0; i < distcount; ++i) {
470 if (!strcasecmp(cp + 2, distlist[i])) {
471 (void) fclose(fp);
472 return (1);
473 }
474 }
475 (void) fclose(fp);
476 return (0);
477 }
478 }
479
480 (void) fclose(fp);
481
482 /*
483 * We've finished the header with no distribution field.
484 * So we'll assume that the distribution is the characters
485 * up to the first dot in the newsgroup name.
486 */
487
488 for (i = 0; i < groupcount; i++) {
489 cp = index(grouplist[i], '.');
490 if (cp)
491 *cp = '\0';
492 for (j = 0; j < distcount; j++)
493 if (!strcasecmp(grouplist[i], distlist[j]))
494 return (1);
495 }
496
497 return (0);
498 }
499
500
501 /*
502 * get_histlist -- return a nicely set up array of newsgroups
503 * (actually, net.foo.bar/article_num) along with a count.
504 *
505 * Parameters: "array" is storage for our array,
506 * set to point at some static data.
507 * "list" is the history file newsgroup list.
508 *
509 * Returns: Number of group specs found.
510 *
511 * Side effects: Changes static data area.
512 * Also puts null bytes in "list"
513 *
514 */
515
516 int
get_histlist(array,list)517 get_histlist(array, list)
518 char ***array;
519 register char *list;
520 {
521 register int histcount = 0;
522 static int nalloc = 0;
523 static char **hist_list = (char **) NULL;
524
525 if (nalloc == 0)
526 hist_list = (char **) malloc(((nalloc = 10) + 1)*
527 sizeof(char *));
528
529 while (1) {
530 for (; *list == ' ' || *list == '\t'; list++);
531
532 if (*list == '\0' || *list == '\n') break;
533
534 if (histcount >= nalloc)
535 hist_list = (char **) realloc((char *) hist_list,
536 ((nalloc += 10) + 1)*
537 sizeof(char *));
538
539 if (hist_list == (char **) NULL) {
540 fprintf(stderr, "get_histlist: Out of memory!\n");
541 return(0);
542 }
543
544 hist_list[histcount++] = list;
545
546 for (; *list && *list != ' ' && *list != '\t' && *list != '\n';
547 list++);
548
549 if (*list) *(list++) = '\0';
550 }
551
552 hist_list[histcount] = (char *) NULL;
553
554 *array = hist_list;
555 return (histcount);
556 }
557
558
559 /*
560 * get_nglist -- return a nicely set up array of newsgroups
561 * along with a count, when given an NNTP-spec newsgroup list
562 * in the form ng1,ng2,ng...
563 *
564 * Parameters: "array" is storage for our array,
565 * set to point at some static data.
566 * "list" is the NNTP newsgroup list.
567 *
568 * Returns: Number of group specs found.
569 *
570 * Side effects: Changes static data area.
571 */
572
573 int
get_nglist(array,list)574 get_nglist(array, list)
575 char ***array;
576 char *list;
577 {
578 register char *cp;
579 register int ngcount;
580
581 for (cp = list; *cp != '\0'; ++cp)
582 if (*cp == ',')
583 *cp = ' ';
584
585 ngcount = parsit(list, array);
586
587 return (ngcount);
588 }
589