#ifndef lint
static char sccsid[] = "@(#)$Id: newnews.c,v 1.27 1994/11/01 06:08:21 sob Exp sob $";
#endif
#include "common.h"
#include "date.h"
#ifdef LOG
int nn_told = 0;
int nn_took = 0;
#endif
/*
* NEWNEWS newsgroups date time ["GMT"] [<distributions>]
*
* Return the message-id's of any news articles past
* a certain date and time, within the specified distributions.
*
* Made RFC977/Y2K compliant via changes in dtol() - see time.c - and
* slightly simplified the comparison between incoming date and history
* file. Fixed(?) the local time offset. D. Glover 29/11/99
*
*/
void
newnews(argc, argv)
register int argc;
char *argv[];
{
register char *cp, *ngp;
char *key;
char datebuf[32];
char line[MAXBUFLEN];
char **distlist, **histlist;
static char **nglist;
int distcount, ngcount, histcount;
int all;
FILE *fp;
long date;
long dtol();
char *ltod();
#ifdef USGHIST
FILE *tmplst;
int i;
char *tmpfile;
#endif
if (argc < 4) {
printf("%d Usage: NEWNEWS newsgroups yymmdd hhmmss [\"GMT\"] [<distributions>].\r\n",
ERR_CMDSYN);
(void) fflush(stdout);
return;
}
if (!canread) {
printf("%d You do not have permission to read. sorry.\r\n",
ERR_ACCESS);
(void) fflush(stdout);
return;
}
#ifdef LOG
sprintf(line, "%s newnews %s %s %s %s %s",
hostname,
argv[1],
argv[2],
argv[3],
(argc >= 5 && *argv[4] == 'G') ? "GMT" : "local",
(argc >= 5 && *argv[argc-1] == '<') ? argv[argc-1] : "none");
syslog(LOG_INFO, line);
#endif
all = (argv[1][0] == '*' && argv[1][1] == '\0');
if (!all) {
ngcount = get_nglist(&nglist, argv[1]);
if (ngcount == 0) {
printf("%d Bogus newsgroup specifier: %s\r\n",
ERR_CMDSYN, argv[1]);
(void) fflush(stdout);
return;
}
}
/* YYMMDD HHMMSS */
if (strlen(argv[2]) != 6 || strlen(argv[3]) != 6) {
printf("%d Date/time must be in form YYMMDD HHMMSS.\r\n",
ERR_CMDSYN);
(void) fflush(stdout);
return;
}
(void) strcpy(datebuf, argv[2]);
(void) strcat(datebuf, argv[3]);
argc -= 4;
argv += 4;
/* Originally the "key" for searching was obtained here and the localtime
* offset wasn't applied when doing the actual comparison... DG 29/11/99
*/
date = dtol(datebuf); /* local time here */
if (date < 0) {
printf("%d Invalid date specification.\r\n",ERR_CMDSYN);
(void) fflush(stdout);
return;
}
/* REMOVED apparently unwanted call to gmt_to_local()
* and tidied up handling of GMT/distributions DG 29/11/99
*/
if (argc > 0 && !strcasecmp(*argv, "GMT")) {
++argv;
--argc;
}
else {
/* now we convert from local to GMT since this is what history */
/* file in News 2.11 expects */
date = local_to_gmt(date);
}
strcpy(datebuf, ltod(date));
key = datebuf;
distcount = 0;
if (argc > 0) {
distcount = get_distlist(&distlist, *argv);
if (distcount < 0) {
printf("%d Bad distribution list: %s\r\n", ERR_CMDSYN,
*argv);
(void) fflush(stdout);
return;
}
}
#ifdef USGHIST
if ((tmpfile = mktemp("/tmp/listXXXXXX")) == NULL ||
(tmplst = fopen(tmpfile, "w+")) == NULL) {
printf("%d Cannot process history file.\r\n", ERR_FAULT);
(void) fflush(stdout);
return;
}
for (i = 0; i < 9; i++) {
sprintf(historyfile, "%s.d/%d", HISTORY_FILE, i);
#endif
fp = fopen(historyfile, "r");
if (fp == NULL) {
#ifdef SYSLOG
syslog(LOG_ERR, "newnews: fopen %s: %m", historyfile);
#endif
#ifndef USGHIST
printf("%d Cannot open history file.\r\n", ERR_FAULT);
(void) fflush(stdout);
return;
#else
continue;
#endif
}
#ifndef USGHIST
printf("%d New news by message id follows\r\n", OK_NEWNEWS);
#endif
if (seekuntil(fp, key, line, sizeof (line)) < 0) {
#ifndef USGHIST
printf(".\r\n");
(void) fflush(stdout);
#endif
(void) fclose(fp);
#ifndef USGHIST
return;
#else
continue;
#endif
}
/*
* History file looks like:
*
* <1569@emory.UUCP> 01/22/86 09:19 net.micro.att/899 ucb.general/2545
* ^--tab ^--tab ^--space ^sp\0
* Sometimes the newsgroups are missing; we try to be robust and
* ignore such bogosity. We tackle this by our usual parse routine,
* and break the list of articles in the history file into an argv
* array with one newsgroup per entry.
*/
do {
if ((cp = index(line, '\t')) == NULL)
continue;
if ((ngp = index(cp+1, '\t')) == NULL) /* 2nd tab */
continue;
++ngp; /* Points at newsgroup list */
if (*ngp == '\n')
continue;
histcount = get_histlist(&histlist, ngp);
if (histcount == 0)
continue;
/*
* For each newsgroup on this line in the history
* file, check it against the newsgroup names we're given.
* If it matches, then see if we're hacking distributions.
* If so, open the file and match the distribution line.
*/
if (!all)
if (!ngmatch(restreql, 0, nglist, ngcount,
histlist, histcount))
continue;
if (distcount)
if (!distmatch(distlist, distcount, histlist, histcount))
continue;
*cp = '\0';
#ifdef USGHIST
fputs(line, tmplst);
fputc('\n', tmplst);
#else
putline(line);
#endif
#ifdef LOG
nn_told++;
#endif
} while (fgets(line, sizeof(line), fp) != NULL);
#ifndef USGHIST
putline(".");
(void) fflush(stdout);
#endif
(void) fclose(fp);
#ifdef USGHIST
}
printf("%d New news by message id follows\r\n", OK_NEWNEWS);
rewind(tmplst);
while (fgets(line, sizeof(line), tmplst) != NULL)
if (line[0] == '<') putline(line);
putline(".");
(void) fflush(stdout);
(void) fclose(tmplst);
(void) unlink(tmpfile);
#endif
}
/*
* seekuntil -- seek through the history file looking for
* a line with date later than "akey". Get that line, and return.
*
* Parameters: "fp" is the active file.
* "akey" is the date, in form YYYYMMDDHHMMSS
* "line" is storage for the first line we find.
*
* Returns: -1 on error, 0 otherwise.
*
* Side effects: Seeks in history file, modifies line.
*/
int
seekuntil(fp, akey, line, linesize)
FILE *fp;
char *akey;
char *line;
int linesize;
{
char datetime[32];
register int c;
register long top, bot, mid;
bot = 0;
(void) fseek(fp, 0L, 2);
top = ftell(fp);
for(;;) {
mid = (top+bot)/2;
(void) fseek(fp, mid, 0);
do {
c = getc(fp);
mid++;
} while (c != EOF && c!='\n');
if (!getword(fp, datetime, line, linesize)) {
return (-1);
}
switch (compare(akey, datetime)) {
case -2:
case -1:
case 0:
if (top <= mid)
break;
top = mid;
continue;
case 1:
case 2:
bot = mid;
continue;
}
break;
}
(void) fseek(fp, bot, 0);
while(ftell(fp) < top) {
if (!getword(fp, datetime, line, linesize)) {
return (-1);
}
switch(compare(akey, datetime)) {
case -2:
case -1:
case 0:
break;
case 1:
case 2:
continue;
}
break;
}
return (0);
}
int
compare(s, t)
register char *s, *t;
{
for (; *s == *t; s++, t++)
if (*s == 0)
return(0);
return (*s == 0 ? -1:
*t == 0 ? 1:
*s < *t ? -2:
2);
}
/*
* Combined B and C news version of getword.
*/
int
getword(fp, w, line, linesize)
FILE *fp;
register char *w;
char *line;
int linesize;
{
register char *cp;
extern char *index();
long qz;
if (fgets(line, linesize, fp) == NULL)
return (0);
w[0] = '\0'; /* in case of bad format */
if ((cp = index(line, '\t')) != NULL) { /* find 2nd field */
register char *endp;
while (*cp == ' ' || *cp == '\t') cp++;
/* skip any leading spaces or tabs */
endp = index(cp, '~'); /* end of date-received */
/* This will fail for B news */
if (endp == NULL)
endp = index(cp, '\t'); /* end of expiry */
/* This will fail if article */
/* has expired */
if (endp == NULL) return(1); /* nothing is returned */
(void) strncpy(w, cp, endp - cp);
w[endp - cp] = '\0';
if (index(w,'/') != NULL){ /* old B news format */
/*
* The following gross hack is present because the old history file date
* format is braindamaged. They like "mm/dd/yy hh:mm", which is useless
* for relative comparisons of dates using something like atoi() or
* strcmp. So, this changes their format into yyyymmddhhmm. Sigh.
*
* Modified to use 4 digit years, just in case anyone is still using B news
* but not tested here. DG 29/11/99
*
* 01234567890123 ("x" for cp[x])
* mm/dd/yy hh:mm (their lousy representation)
* yymmddhhmmss (our good one)
* 012345678901 ("x" for w[x])
*/
w[0] = cp[6]; /* Years */
w[1] = cp[7];
w[2] = cp[0]; /* Months */
w[3] = cp[1];
w[4] = cp[3]; /* Days */
w[5] = cp[4];
w[6] = cp[9]; /* Hours */
w[7] = cp[10];
w[8] = cp[12]; /* Minutes */
w[9] = cp[13];
w[10] = '0'; /* Seconds are faked */
w[11] = '0';
w[12] = '\0';
/* We now have YYMMDDHHMMSS - use dtol() to get something sensible for the
* century and then use ltod() below to make it compatible with the way
* the search is done. Really it would be much better to compare everything
* as time_t, but this means changing seekuntil() and all the underlying stuff.
* Maybe for a subsequent release if anyone is bothered? DG 29/11/99
*/
qz = dtol(w);
}
else /* convert new format to yyyymmddhhmmss */
{
qz =atol(w);
}
}
strcpy(w,ltod(qz));
return (1);
}
/*
* distmatch -- see if a file matches a set of distributions.
* We have to do this by (yech!) opening the file, finding
* the Distribution: line, if it has one, and seeing if the
* things match.
*
* Parameters: "distlist" is the distribution list
* we want.
* "distcount" is the count of distributions in it.
* "grouplist" is the list of groups (articles)
* for this line of the history file. Note that
* this isn't quite a filename.
* "groupcount" is the count of groups in it.
*
* Returns: 1 if the article is in the given distribution.
* 0 otherwise.
*/
int
distmatch(distlist, distcount, grouplist, groupcount)
char *distlist[];
int distcount;
char *grouplist[];
int groupcount;
{
register char c;
register char *cp;
register FILE *fp;
register int i, j;
char buf[MAXBUFLEN];
(void) strcpy(buf, spooldir);
(void) strcat(buf, "/");
(void) strcat(buf, grouplist[0]);
for (cp = buf; *cp; cp++)
if (*cp == '.')
*cp = '/';
fp = fopen(buf, "r");
if (fp == NULL) {
#ifdef SYSLOG
syslog(LOG_ERR, "distmatch: fopen %s: %m", buf);
#endif
return (0);
}
while (fgets(buf, sizeof (buf), fp) != NULL) {
if ((c = buf[0]) == '\n') /* End of header */
break;
if (c != 'd' && c != 'D')
continue;
cp = index(cp + 1, '\n');
if (cp)
*cp = '\0';
cp = index(buf, ':');
if (cp == NULL)
continue;
*cp = '\0';
if (!strcasecmp(buf, "distribution")) {
for (i = 0; i < distcount; ++i) {
if (!strcasecmp(cp + 2, distlist[i])) {
(void) fclose(fp);
return (1);
}
}
(void) fclose(fp);
return (0);
}
}
(void) fclose(fp);
/*
* We've finished the header with no distribution field.
* So we'll assume that the distribution is the characters
* up to the first dot in the newsgroup name.
*/
for (i = 0; i < groupcount; i++) {
cp = index(grouplist[i], '.');
if (cp)
*cp = '\0';
for (j = 0; j < distcount; j++)
if (!strcasecmp(grouplist[i], distlist[j]))
return (1);
}
return (0);
}
/*
* get_histlist -- return a nicely set up array of newsgroups
* (actually, net.foo.bar/article_num) along with a count.
*
* Parameters: "array" is storage for our array,
* set to point at some static data.
* "list" is the history file newsgroup list.
*
* Returns: Number of group specs found.
*
* Side effects: Changes static data area.
* Also puts null bytes in "list"
*
*/
int
get_histlist(array, list)
char ***array;
register char *list;
{
register int histcount = 0;
static int nalloc = 0;
static char **hist_list = (char **) NULL;
if (nalloc == 0)
hist_list = (char **) malloc(((nalloc = 10) + 1)*
sizeof(char *));
while (1) {
for (; *list == ' ' || *list == '\t'; list++);
if (*list == '\0' || *list == '\n') break;
if (histcount >= nalloc)
hist_list = (char **) realloc((char *) hist_list,
((nalloc += 10) + 1)*
sizeof(char *));
if (hist_list == (char **) NULL) {
fprintf(stderr, "get_histlist: Out of memory!\n");
return(0);
}
hist_list[histcount++] = list;
for (; *list && *list != ' ' && *list != '\t' && *list != '\n';
list++);
if (*list) *(list++) = '\0';
}
hist_list[histcount] = (char *) NULL;
*array = hist_list;
return (histcount);
}
/*
* get_nglist -- return a nicely set up array of newsgroups
* along with a count, when given an NNTP-spec newsgroup list
* in the form ng1,ng2,ng...
*
* Parameters: "array" is storage for our array,
* set to point at some static data.
* "list" is the NNTP newsgroup list.
*
* Returns: Number of group specs found.
*
* Side effects: Changes static data area.
*/
int
get_nglist(array, list)
char ***array;
char *list;
{
register char *cp;
register int ngcount;
for (cp = list; *cp != '\0'; ++cp)
if (*cp == ',')
*cp = ' ';
ngcount = parsit(list, array);
return (ngcount);
}
syntax highlighted by Code2HTML, v. 0.9.1