4.4BSD/usr/src/contrib/news/inn/nnrpd/commands.c

Compare this file to the similar file:
Show the results in this format:

/*  $Revision: 1.14 $
**
**  Miscellaneous commands.
*/
#include "nnrpd.h"
#include <time.h>


typedef struct _LISTINFO {
    STRING	File;
    STRING	Items;
    STRING	Format;
} LISTINFO;


STATIC LISTINFO		INFOactive = {
    ACTIVE, "active newsgroups",
    "Newsgroups in form \"group high low flags\""
};
STATIC LISTINFO		INFOactivetimes = {
    ACTIVETIMES, "creation times",
    "Group creations in form \"name time who\""
};
STATIC LISTINFO		INFOdistribs = {
    _PATH_NNRPDIST, "newsgroup distributions",
    "Distributions in form \"area description\""
};
STATIC LISTINFO		INFOdistribpats = {
    _PATH_DISTPATS, "distribution patterns",
    "Default distributions in form \"weight:pattern:value\""
};
STATIC LISTINFO		INFOgroups = {
    NEWSGROUPS, "newsgroup descriptions",
    "Descriptions in form \"group description\""
};
STATIC LISTINFO		INFOschema = {
    _PATH_SCHEMA, "overview schema",
    "Order of fields in overview database"
};


/* ARGSUSED */
FUNCTYPE
CMDauthinfo(ac, av)
    int		ac;
    char	*av[];
{
    static char	User[30];
    static char	Password[30];
    char	accesslist[BIG_BUFFER];

    if (caseEQ(av[1], "user")) {
	(void)strncpy(User, av[2], sizeof User - 1);
	User[sizeof User - 1] = 0;
	Reply("%d PASS required\r\n", NNTP_AUTH_NEXT_VAL);
	return;
    }

    if (!caseEQ(av[1], "pass")) {
	Reply("%d bad authinfo param\r\n", NNTP_BAD_COMMAND_VAL);
	return;
    }
    if (User[0] == '\0') {
	Reply("%d USER required\r\n", NNTP_AUTH_REJECT_VAL);
	return;
    }

    (void)strncpy(Password, av[2], sizeof Password - 1);
    Password[sizeof Password - 1] = 0;

    if (EQ(User, PERMuser) && EQ(Password, PERMpass)) {
	syslog(L_NOTICE, "%s user %s", ClientHost, User);
	Reply("%d Ok\r\n", NNTP_AUTH_OK_VAL);
	PERMneedauth = FALSE;
	PERMauthorized = TRUE;
	return;
    }
    if (PERMinfile((char *)NULL, (char *)NULL, User, Password, accesslist)) {
	PERMspecified = NGgetlist(&PERMlist, accesslist);
	syslog(L_NOTICE, "%s user %s", ClientHost, User);
	Reply("%d Ok\r\n", NNTP_AUTH_OK_VAL);
	PERMneedauth = FALSE;
	PERMauthorized = TRUE;
	return;
    }

    syslog(L_FATAL, "%s bad_auth", ClientHost);
    Reply("%d Authentication error\r\n", NNTP_ACCESS_VAL);
    ExitWithStats(1);
}


/*
**  The "DATE" command.  Part of NNTPv2.
*/
/* ARGSUSED0 */
FUNCTYPE
CMDdate(ac, av)
    int		ac;
    char	*av[];
{
    TIMEINFO	t;
    struct tm	*gmt;

    if (GetTimeInfo(&t) < 0 || (gmt = gmtime(&t.time)) == NULL) {
	Reply("%d Can't get time, %s\r\n", NNTP_TEMPERR_VAL, strerror(errno));
	return;
    }
    Reply("%d %04.4d%02.2d%02.2d%02.2d%02.2d%02.2d\r\n",
	NNTP_DATE_FOLLOWS_VAL,
	gmt->tm_year + 1900, gmt->tm_mon + 1, gmt->tm_mday,
	gmt->tm_hour, gmt->tm_min, gmt->tm_sec);
}


/*
**  List active newsgroups, newsgroup descriptions, and distributions.
*/
/* ARGSUSED0 */
FUNCTYPE
CMDlist(ac, av)
    int			ac;
    char		*av[];
{
    register QIOSTATE	*qp;
    register char	*p;
    register char	*save;
    char		*grplist[2];
    LISTINFO		*lp;

    p = av[1];
    if (p == NULL || caseEQ(p, "active")) {
	if (!GetGroupList()) {
	    syslog(L_NOTICE, "%s cant getgroupslist for list %m", ClientHost);
	    Reply("%d Group update failed. Try later.\r\n", NNTP_TEMPERR_VAL);
	    ExitWithStats(1);
	}
	lp = &INFOactive;
    }
    else if (caseEQ(p, "active.times"))
	lp = &INFOactivetimes;
    else if (caseEQ(p, "distributions"))
	lp = &INFOdistribs;
    else if (caseEQ(p, "distrib.pats"))
	lp = &INFOdistribpats;
    else if (caseEQ(p, "newsgroups"))
	lp = &INFOgroups;
    else if (caseEQ(p, "overview.fmt"))
	lp = &INFOschema;
    else {
	Reply("%s\r\n", NNTP_SYNTAX_USE);
	return;
    }

    if ((qp = QIOopen(lp->File, QIO_BUFFER)) == NULL) {
	syslog(L_ERROR, "%s cant fopen %s %m", ClientHost, lp->File);
	Reply("%d No list of %s available.\r\n", NNTP_TEMPERR_VAL, lp->Items);
	return;
    }

    Reply("%d %s.\r\n", NNTP_LIST_FOLLOWS_VAL, lp->Format);
    if (!PERMspecified && !PERMdefault) {
	/* Optmize for unlikely case of no permissions and FALSE default. */
	(void)QIOclose(qp);
	Printf(".\r\n");
    }

    /* Set up group list terminator. */
    grplist[1] = NULL;

    /* Read lines, ignore long ones. */
    while ((p = QIOread(qp)) != NULL) {
	if (lp == &INFOdistribs || lp == &INFOdistribpats) {
	    Printf("%s\r\n", p);
	    continue;
	}
	if (lp == &INFOschema) {
	    if (*p != '\0' && *p != '#')
		Printf("%s\r\n", p);
	    continue;
	}
	if ((save = strchr(p, ' ')) != NULL)
	    *save = '\0';
	if (PERMspecified) {
	    grplist[0] = p;
	    if (!PERMmatch(PERMdefault, PERMlist, grplist))
		continue;
	}
	if (save != NULL)
	    *save = ' ';
	Printf("%s\r\n", p);
    }
    QIOclose(qp);

    Printf(".\r\n");
}


/*
**  Handle the "mode" command.
*/
/* ARGSUSED */
FUNCTYPE
CMDmode(ac, av)
    int		ac;
    char	*av[];
{
    if (caseEQ(av[1], "reader"))
	Reply("%d %s InterNetNews NNRP server %s ready (%s).\r\n",
	       PERMcanpost ? NNTP_POSTOK_VAL : NNTP_NOPOSTOK_VAL,
	       MyHostName, INNVersion(),
	       PERMcanpost ? "posting ok" : "no posting");
    else
	Reply("%d What?\r\n", NNTP_BAD_COMMAND_VAL);
}


/*
**  Display new newsgroups since a given date and time for specified
**  <distributions>.
*/
FUNCTYPE
CMDnewgroups(ac, av)
    int			ac;
    char		*av[];
{
    static char		USAGE[] =
	"NEWGROUPS yymmdd hhmmss [\"GMT\"] [<distributions>]";
    static char		**distlist;
    register char	*p;
    register char	*q;
    register char	**dp;
    register QIOSTATE	*qp;
    register GROUPENTRY	*gp;
    BOOL		All;
    long		date;
    char		*grplist[2];

    /* Parse the date. */
    date = NNTPtoGMT(av[1], av[2]);
    if (date < 0) {
	Reply("%d Usage: %s\r\n", NNTP_SYNTAX_VAL, USAGE);
	return;
    }
    ac -= 3;
    av += 3;
    if (ac > 0 && caseEQ(*av, "GMT")) {
	av++;
	ac--;
    }
    else
	date = LOCALtoGMT(date);

    if (ac == 0)
	All = TRUE;
    else {
	if (!ParseDistlist(&distlist, *av) < 0) {
	    Reply("%d Bad distribution list %s:\r\n", NNTP_SYNTAX_VAL, *av);
	    return;
	}
	All = FALSE;
    }

    if (!GetGroupList()) {
	syslog(L_NOTICE, "%s cant getgroupslist for list %m", ClientHost);
	Reply("%d Group update failed. Try later.\r\n", NNTP_TEMPERR_VAL);
	ExitWithStats(1);
    }

    if ((qp = QIOopen(ACTIVETIMES, QIO_BUFFER)) == NULL) {
	syslog(L_ERROR, "%s cant fopen %s %m",
	    ClientHost, ACTIVETIMES);
	Reply("%d Cannot open newsgroup date file.\r\n", NNTP_TEMPERR_VAL);
	return;
    }
    Reply("%d New newsgroups follow.\r\n", NNTP_NEWGROUPS_FOLLOWS_VAL);

    /* Read the file, ignoring long lines. */
    while ((p = QIOread(qp)) != NULL) {
	if ((q = strchr(p, ' ')) == NULL)
	    continue;
	*q++ = '\0';
	if (atol(q) < date || (gp = GRPfind(p)) == NULL)
	    continue;

	if (PERMspecified) {
	    grplist[0] = p;
	    grplist[1] = NULL;
	    if (!PERMmatch(PERMdefault, PERMlist, grplist))
		continue;
	}
	else if (!PERMdefault)
	    continue;

	if (!All) {
	    if ((q = strchr(p, '.')) == NULL)
		continue;
	    for (*q = '\0', dp = distlist; *dp; dp++)
		if (EQ(p, *dp)) {
		    *q = '.';
		    break;
		}
	    if (*dp == NULL)
		continue;
	}
	Printf("%s %ld %ld %c%s\r\n",
	    p, (long)gp->High, (long)gp->Low,
	    gp->Flag, gp->Alias ? gp->Alias : "");
    }
    QIOclose(qp);
    Printf(".\r\n");
}


/*
**  Post an article.
*/
/* ARGSUSED */
FUNCTYPE
CMDpost(ac, av)
    int		ac;
    char	*av[];
{
    static char		*article;
    static int		size;
    register char	*p;
    register char	*end;
    register int	longline;
    register READTYPE	r;
    int			i;
    long		l;
    STRING		response;
    char		idbuff[SMBUF];

    if (!PERMcanpost) {
	syslog(L_NOTICE, "%s noperm post without permission", ClientHost);
	Reply("%s\r\n", NNTP_CANTPOST);
	return;
    }

    /* Start at beginning of buffer. */
    if (article == NULL) {
	size = 4096;
	article = NEW(char, size);
    }
    p = article;
    end = &article[size];

    Reply("%d Ok\r\n", NNTP_START_POST_VAL);
    (void)fflush(stdout);

    for (l = 0, longline = 0; ; l++) {
	/* Need more room? */
	if (end - p < ART_LINE_MALLOC) {
	    i = p - article;
	    size += ART_LINE_MALLOC;
	    RENEW(article, char, size);
	    end = &article[size];
	    p = i + article;
	}

	/* Read line, process bad cases. */
	switch (r = READline(p, ART_LINE_LENGTH, DEFAULT_TIMEOUT)) {
	default:
	    syslog(L_ERROR, "%s internal %d in post", ClientHost, r);
	    /* FALLTHROUGH */
	case RTtimeout:
	    syslog(L_ERROR, "%s timeout in post", ClientHost);
	    Printf("%d timeout after %d seconds, closing connection\r\n",
		   NNTP_TEMPERR_VAL, DEFAULT_TIMEOUT);
	    ExitWithStats(1);
	    /* NOTREACHED */
	case RTeof:
	    syslog(L_ERROR, "%s eof in post", ClientHost);
	    ExitWithStats(1);
	    /* NOTREACHED */
	case RTlong:
	    if (longline == 0)
		longline = l + 1;
	    continue;
	case RTok:
	    break;
	}

	/* Process normal text. */
	if (*p != '.') {
	    p += strlen(p);
	    *p++ = '\n';
	    *p = '\0';
	    continue;
	}

	/* Got a leading period; see if it's the terminator. */
	if (p[1] == '\0') {
	    *p = '\0';
	    break;
	}

	/* "Arnold, please copy down over the period for me." */
	while ((p[0] = p[1]) != '\0')
	    p++;
	*p++ = '\n';
	*p = '\0';
    }

    if (longline) {
	syslog(L_NOTICE, "%s toolong in post", ClientHost);
	Printf("%d Line %d too long\r\n", NNTP_POSTFAIL_VAL, longline);
	POSTrejected++;
	return;
    }

    /* Send the article to the server. */
    response = ARTpost(article, idbuff);
    if (response == NULL) {
	syslog(L_NOTICE, "%s post ok %s", ClientHost, idbuff);
	Reply("%s\r\n", NNTP_POSTEDOK);
	POSTreceived++;
    }
    else {
	if ((p = strchr(response, '\r')) != NULL)
	    *p = '\0';
	if ((p = strchr(response, '\n')) != NULL)
	    *p = '\0';
	syslog(L_NOTICE, "%s post failed %s", ClientHost, response);
	Reply("%d %s\r\n", NNTP_POSTFAIL_VAL, response);
	POSTrejected++;
    }
}


/*
**  The "xpath" command.  An uncommon extension.
*/
/* ARGSUSED */
FUNCTYPE
CMDxpath(ac, av)
    int		ac;
    char	*av[];
{
    char	*p;

    if ((p = HISgetent(av[1], TRUE)) == NULL)
	Reply("%d Don't have it\r\n", NNTP_DONTHAVEIT_VAL);
    else
	Reply("%d %s\r\n", NNTP_NOTHING_FOLLOWS_VAL, p);
}