
/*
 *  FORWARDNEWS.C
 *
 *  (C)Copyright 1990, Mark R. Rinfret, All Rights Reserved
 *
 *  Author:	Mark R. Rinfret
 *
 *  This module is an extension to the UUCP-D rnews program. It provides a
 *  primitive mechanism for forwarding news articles to other sites.
 */

#include "news.h"
#include <errno.h>
#include <ctype.h>
#include <log.h>

IDENT(".01");

#define iswhite(c) ( (c == ' ') || (c == '\t') )

typedef struct FwdPat {
    struct FwdPat   *nextPattern;
    char	    *pattern;
} FwdPat;

typedef struct ForwardList {
    struct ForwardList	*nextSystem;
    char		*sysName;
    FwdPat		*firstPattern;
    FwdPat		*lastPattern;
} ForwardList;

static ForwardList	*firstSystem;
static ForwardList	*lastSystem;
static char		inString[256];

static char		*DupString(const char *s);
static ForwardList	*NewForwardList(const char *name);
static FwdPat		*NewPattern(const char *s);
static char		*NextPattern(char *in, char *out, int *length);
static void		ParseSystem(FILE *sysFile, char *firstLine,
				    ForwardList *system);
static int		wildcmp(const char *wild, const char *name);

/*
 *  FUNCTION
 *	DupString - make a copy of a string.
 *
 *  SYNOPSIS
 *	static char *DupString(const char *s);
 *
 *  DESCRIPTION
 *	DupString creates a dynamically-allocated copy of string <s>.
 *	It returns a pointer to the string or NULL if it fails.
 */

static char *
DupString(const char *s)
{
    char    *p;
	p = malloc(strlen(s) + 1);
	if (p) {
		strcpy(p, s);
	}
	return p;
}

/*
 *  FUNCTION
 *	ForwardArticle - forward article to selected sites.
 *
 *  SYNOPSIS
 *	void ForwardArticle(const char *groupName, const char *articleName, const char *path);
 *
 *  DESCRIPTION
 *	This function forwards the article specified by <articleName>
 *	to each site which requests articles for the <groupName>.
 *
 *	The article is not forwarded to any machine which appears in the
 *	Path: header.
 */

void
ForwardArticle(const char *groupName, const char *articleName, char *path)
{
    extern void     _SetWildStack(long n);

    FILE	    *batchFile;
    static char     batchFileName[256];
    FwdPat	    *patternNode;
    ForwardList     *system;

#ifdef TEST
    printf("ForwardArticle: group = '%s'\n", groupName);
#endif

    for (system = firstSystem; system; system = system->nextSystem) {
#ifdef TEST
	printf("ForwardArticle: checking system '%s'\n",
	       system->sysName);
	if (system->firstPattern == NULL) {
	    printf("ForwardArticle: empty pattern list?\n");
	}
#endif
	for (patternNode = system->firstPattern; patternNode; patternNode = patternNode->nextPattern) {
	    if (wildcmp(patternNode->pattern, groupName) == 0)
		continue;

	    /*
	     *	make sure system does not appear in path (allows us to forward
	     *	both downstream and upstream)
	     */

	    sprintf(batchFileName, "%s!", system->sysName);

	    if (path[0] == 0 || strstr(path, batchFileName)) {
		ulog(1, "will not forward %s to %s", articleName, system->sysName);
		continue;
	    }

#ifdef TEST
	    printf("ForwardArticle: got a match!\n");
#endif
	    strcpy(batchFileName, MakeConfigPath(UUSPOOL, "batch/"));
	    strcat(batchFileName, system->sysName);
	    LockFile(batchFileName);
	    batchFile = fopen(batchFileName, "a");
	    if (!batchFile) {
		fprintf(stderr,"rnews: Can't open batch file '%s'!\n",
			batchFileName);
		exit(1);
	    }
	    fprintf(batchFile,"%s\n", articleName);
	    fclose(batchFile);
	    UnLockFile(batchFileName);
	    break;
	}
    }
}

/*
 *  FUNCTION
 *	GetForwardingInfo - build list of systems to receive news.
 *
 *  SYNOPSIS
 *	int GetForwardingInfo(void);
 *
 *  DESCRIPTION
 *	This function initializes the ForwardNews package, building news
 *	forwarding lists for sites to  receive news from our machine.
 *
 *	The file UULIB/sys defines the article selection criteria for each
 *	machine. Files named UUSPOOL/batch/<system> will be updated to contain
 *	article lists which are then batched by a news batching program.
 *
 *	If this function is successful, it will return zero. Otherwise, -1
 *	will be returned.
 */

int
GetForwardingInfo(void)
{
    char	c;
    char	*s1, *s2;
    int 	status = 0;
    FILE	*sysFile = NULL;
    char	sysName[9];
    ForwardList *sysNode = NULL;

    sysFile = fopen(MakeConfigPath(UULIB,"sys"), "r");

    if (!sysFile)
	return(0);

    while (fgets(inString, sizeof(inString), sysFile)) {

	if (*inString)
	    inString[strlen(inString)-1] = '\0';

	if ((c = *inString) == '\0' || c == '#')
	    continue;

	if (isalpha(c)) {               /* System name? */
	    s1 = strchr(inString, ':');
	    if (!s1) {
bad_line:
		fprintf(stderr,"Bad sys line ignored:\n%s\n", inString);
		continue;
	    }
	    if (s1 - inString > 8)
		goto bad_line;
	    *s1 = '\0';
	    sysNode = NewForwardList(inString);

	    if (!sysNode) {
		status = -1;
		goto done;
	    }
#ifdef TEST
	    printf("System: '%s'\n", inString);
#endif
	    if (firstSystem == NULL)
		firstSystem = sysNode;
	    else
		lastSystem->nextSystem = sysNode;
	    lastSystem = sysNode;
	    ++s1;
	    ParseSystem(sysFile, s1, sysNode);
	} else {
	    goto bad_line;
	}
    }

done:
    return status;
}

static ForwardList *
NewForwardList(const char *name)
{
    ForwardList     *list;

    list = calloc(sizeof(struct ForwardList), 1);
    if (!list) {
no_mem:
	fprintf(stderr,"rnews: out of memory!\n");
	exit(1);
    }

    if ( ! (list->sysName = DupString(name))) goto no_mem;

    return list;
}

static FwdPat *
NewPattern(const char *s)
{
    FwdPat	*pNode;

    pNode = calloc(sizeof(struct FwdPat), 1);
    if (! pNode) {
no_mem:
	fprintf(stderr,"rnews: out of memory!");
	exit(1);
    }

#ifdef undef
    if (! ( pNode->pattern = _ParseWild(s, strlen(s) ) ) ) goto no_mem;
#endif
    pNode->pattern = DupString(s);

    return pNode;
}

static char *
NextPattern(char *in, char *out, int *length)
{
    char    c;
    char    *s1 = out;

    if (*in == ',') ++in;
    while (iswhite(*in)) ++in;

    *length = 0;
    while ( (c = *in) ) {
	if ( (c == '\\') || (c == ',') || (c == '\n') ) break;
	++in;				/* Advance beyond current char. */
	if (! iswhite(c) ) {
	    *s1++ = c;
	    ++*length;
	}
    }
    *s1 = '\0';                         /* Terminate with a NULL. */
    return in;
}

void
ParseSystem(FILE *sysFile, char *firstLine, ForwardList *system)
{
    int 	    length;
    char	    pattern[80];
    int 	    patternLength;
    FwdPat	    *pNode;
    char	    *s1;
    char	    *theLine = firstLine;

    while (! feof(sysFile) ) {
	s1 = theLine;
	length = strlen(s1);
	if (! length) break;
	if (s1[length-1] == '\n') {     /* Delete trailing newline. */
	    s1[--length] = '\0';
	}
	if (*s1 == '#') {
	    s1 = s1 + (length - 1);     /* Point to last character. */
	} else do {
	    s1 = NextPattern(s1, pattern, &patternLength);
	    if (patternLength) {
#ifdef TEST
		printf("ParseSystem - next pattern: '%s'\n", pattern);
#endif
		pNode = NewPattern(pattern);
		/* Add this to our forward-linked list. */
		if (system->firstPattern == NULL)
		    system->firstPattern = pNode;
		else
		    system->lastPattern->nextPattern = pNode;
		system->lastPattern = pNode;
	    }
	} while (*s1 == ',');
	if (*s1 != '\\') break;
	fgets(inString, sizeof(inString), sysFile);
	theLine = inString;
    }
}


/*  FUNCTION
	wildcmp - compare a wild card name with a normal name.

    SYNOPSIS
	int wildcmp(const char *wild, const char *name);

    DESCRIPTION
	Argument <wild>, a string containing wild card characters, is
	compared against <name>, a string containing no wild card
	characters. If the strings match, a one (1) is returned.
	Otherwise, a zero (0) is returned.

    AUTHOR
	Matt Dillon
 */

#define MAXB   8

int
wildcmp(const char *wild, const char *name)
{
    register char  *w = wild;
    register char  *n = name;
    char *back[MAXB][2];
    register char   s1,s2;
    int     bi = 0;

    while (*n || *w){
	switch (*w) {
	case '*':
	    if (bi == MAXB) {
#ifdef DEBUG
		printf("Too many levels of '*'\n");
#endif
		return (0);
	    }
	    back[bi][0]= w;
	    back[bi][1]= n;
	    ++bi;
	    ++w;
	    continue;
	goback:
	    --bi;
	    while (bi >= 0 && *back[bi][1] == '\0')
		--bi;
	    if (bi < 0)
		return (0);
	    w = back[bi][0] + 1;
	    n = ++back[bi][1];
	    ++bi;
	    continue;
	case '?':
	    if (!*n){
		if (bi)
		    goto goback;
		return (0);
	    }
	    break;
	default:
	    s1 = (*n >= 'A' && *n <= 'Z')?*n - 'A' + 'a' :*n;
	    s2 = (*w >= 'A' && *w <= 'Z')?*w - 'A' + 'a' :*w;
	    if (s1 != s2){
		if (bi)
		    goto goback;
		return (0);
	    }
	    break;
	}
	if (*n)
	    ++n;
	if (*w)
	    ++w;
    }
    return (1);
}


#ifdef TEST
main(int argc, char **argv)
{
    if (GetForwardingInfo() == 0) {
	ForwardArticle("comp.sys.amiga", "test1");
	ForwardArticle("unix-pc.sources", "test2");
	ForwardArticle("no-such-group", "test3");
    }
    else
	fprintf(stderr,"GetForwardingInfo() failed.\n");
}
#endif /* TEST */

