/*
* LIB/NEWSFEED.C
*
* (c)Copyright 1997-1998, Matthew Dillon, All Rights Reserved. Refer to
* the COPYRIGHT file in the base directory of this distribution
* for specific rights granted.
*
* NOTE: hlabel, once found, may not be changed.
*/
#include "defs.h"
Prototype int FeedAdd(const char *msgid, time_t t, History *h, const char *nglist, const char *npath, const char *dist, int headerOnly, const char *artType, const char *cSize);
Prototype int FeedWrite(int logIt, int (*callback)(const char *hlabel, const char *msgid, const char *path, const char *offsize, int plfo, int headOnly, const char *artType, const char *cSize), const char *msgid, const char *path, const char *offsize, const char *nglist, const char *npath, const char *dist, const char *headOnly, const char *artType, int spamArt, const char *cSize);
Prototype void FeedFlush(void);
Prototype void LoadNewsFeed(time_t t, int force, const char *hlabel);
Prototype void TouchNewsFeed(void);
Prototype int FeedValid(const char *hlabel, int *pcount);
Prototype int IsFiltered(const char *hlabel, const char *nglist);
Prototype int IsDelayed(const char *hlabel);
Prototype int FeedQuerySpam(const char *hlabel, const char *hostInfo);
Prototype int PathElmMatches(const char *hlabel, const char *p, int bytes, int *pidx);
Prototype int CommonElmMatches(const char *common, const char *p, int bytes);
Prototype int FeedInDelay(const char *hlabel);
Prototype void FeedGetThrottle(const char *hlabel, int *delay, int *lines);
Prototype int FeedReadOnly(const char *hlabel);
Prototype int FeedWhereIs(const char *hlabel);
Prototype int FeedPriority(const char *hlabel);
Prototype int FeedPrecommitReject(const char *hlabel);
Prototype LabelList *FeedLinkLabelList(void);
Prototype NewslinkInfo *FeedLinkInfo(const char *hlabel);
Prototype int FeedFilter(char *hlabel, const char *nglist, const char *npath, const char *dist, const char *artType, int bytes);
Prototype int FeedSpam(int which, const char *nglist, const char *npath, const char *dist, const char *artType, int bytes);
Prototype void DumpAllFeedInfo(FILE *fo);
Prototype void DumpFeedInfo(FILE *fo, char *label);
Prototype void FeedGetMaxInboundRate(const char *hlabel, int *bps);
Prototype int ArtHashIsFiltered(const char *msgid);
#if USE_OFFER_FILTER
Prototype int OfferIsFiltered(const char *hlabel, const char *msgid);
#endif /* USE_OFFER_FILTER */
Prototype FILE *FeedFo;
Prototype int FeedDebug;
/*
* NewsFeed - a newsfeeds label or group.
*
* For most integer options, 0 is the default (usually off), -1 is
* off, and +1 is on. Other values are also possible depending on the
* option.
*/
typedef struct NewsFeed {
struct NewsFeed *nf_Next;
char *nf_Label;
struct Node *nf_PathAliasBase;
struct Node *nf_SpamPathAliasBase;
struct Node *nf_GroupAcceptBase;
struct Node *nf_FilterBase;
struct Node *nf_SpamBase;
struct Node *nf_RequireGroupsBase;
#if USE_OFFER_FILTER
struct Node *nf_OfferFilterBase;
#endif /* USE_OFFER_FILTER */
int nf_MaxCrossPost; /* 0=unlimited */
int nf_MinCrossPost; /* 0=unlimited */
int nf_MaxArtSize; /* 0=unlimited */
int nf_MinArtSize; /* 0=unlimited */
int nf_MaxPathLen; /* maximum outgoing path len */
int nf_MinPathLen; /* minimum outgoing path len */
int nf_MaxConnect; /* max connections */
char *nf_MaxInboundRate; /* maximum B/s per conn */
char *nf_Dist; /* distribution */
char *nf_NoDist; /* !distribution */
char nf_PerLineFlushOpt;
char nf_NoMisMatch;
char nf_SpamFeedOpt;
char nf_Resolved;
int nf_ThrottleDelay;
int nf_ThrottleLines;
int nf_ReadOnly;
int nf_WhereIs;
int nf_IncomingPriority; /* nice() value of process */
int nf_PrecommitReject; /* reject precommit hits */
HashFeed nf_HashFeed; /* hashfeed */
ArtTypeList *nf_ArtTypes; /* What type of articles */
/*
* The following struct is only used by dspoolout and doutq
*/
struct NewslinkInfo nf_LinkInfo;
} NewsFeed;
#define MAXRECURSION 16
#define RTF_ENABLED 0x01 /* enable realtime feed */
#define RTF_NOBATCH 0x02 /* do not generate batch */
NewsFeed *NFBase; /* label base */
NewsFeed *GRBase; /* groupref base */
NewsFeed *NFCache; /* Most recently used label */
NewsFeed *NFGlob; /* The GLOBAL label */
NewsFeed *ISBase; /* Internal SPAM label */
NewsFeed *ESBase; /* External SPAM label */
NewsFeed *IFBase; /* Incoming Filter label */
char NFBuf[16384];
FILE *FeedFo = NULL;
MemPool *NFMemPool = NULL;
const char *SaveHLabel = NULL;
int FeedDebug = 0;
int UseSpamAlias = 0;
static int RecurCount = 0;
static int RecurWarn = 0;
int feedHashFeedOK(HashFeed *hashfeed, int article);
int feedQueryPaths(NewsFeed *feed, const char *npath, int size, int artType);
int feedQuerySpamPaths(NewsFeed *feed, const char *npath, int size, const char * msgid);
int feedQueryGroups(NewsFeed *feed, const char *nglist);
int feedQueryDists(NewsFeed *feed, const char *dist);
int filterRequireGroups(Node *node, const char *nglist);
int filterQueryGroups(NewsFeed *feed, const char *nglist);
int feedPathQuery(NewsFeed *feed, const char *path);
int feedSpamPathQuery(NewsFeed *feed, const char *path);
int feedDistQuery(const char *item, int len, const char *list);
int feedSpamFeedOK(int feed, int article);
#if USE_OFFER_FILTER
int feedOfferFilterQuery(NewsFeed *feed, const char *msgid);
#endif /* USE_OFFER_FILTER */
void resolveGroupList(const char *label, Node *scan);
void AltFeed(NewsFeed *nf, FILE *fi);
int TooNear(time_t t);
void setLinkGLOBAL(NewslinkInfo *nf);
void setLinkStart(NewslinkInfo *nf);
void setLinkDefaults(NewslinkInfo *nf, NewslinkInfo *gl);
void resolveDefaults(NewsFeed *nf, Node *no);
int cbWildCmpStopWhenFound(Node *node, const void *data, int *pabort, int def);
int cbWildCmpNoStop(Node *node, const void *data, int *pabort, int def);
int cbFeedGroupQuery(Node *node, const void *data, int *pabort, int def);
int cbFilterRequireGroups(Node *node, const void *data, int *pabort, int def);
int recursiveScan(NewsFeed *feed, int off, const void *data, int (*callback)(Node *node, const void *data, int *pabort, int def), int def);
/*
* FEEDADD() - given message-id, history file data, newsgroup list, pathlist,
* and article size, commit the article to outgoing feeds.
*
* NOTE: none of the string objects is allowed to contain a tab,
* this is checked prior to the call.
*
* This ia used in the slave diablos and essentially performs an RPC
* call to FeedWrite in the master diablo.
*/
int
FeedAdd(const char *msgid, time_t t, History *h, const char *nglist, const char *npath, const char *dist, int headerOnly, const char *artType, const char *cSize)
{
int r = 0;
LoadNewsFeed(t, 0, NULL);
if (FeedFo) {
char path[256];
ArticleFileName(path, sizeof(path), h, ARTFILE_FILE_REL);
fprintf(FeedFo, "SOUT\t%s\t%lld,%ld\t%s\t%s\t%s\t%s\t%d\t%s\t%s\n",
path, (off_t)h->boffset, (long)h->bsize, msgid, nglist, dist, npath,
headerOnly, artType, cSize
);
fflush(FeedFo); /* simplify the parent reader on the pipe */
if (ferror(FeedFo)) {
logit(LOG_CRIT, "lost backchannel to master server");
r = -1;
}
}
return(r);
}
/*
* FeedWrite()
* Used in the master and called after the receipt of a SOUT in the
* control pipe.
*/
int
FeedWrite(int logIt,
int (*callback)(const char *hlabel, const char *msgid, const char *path, const char *offsize, int plfo, int headOnly, const char *artType, const char *cSize),
const char *msgid,
const char *path,
const char *offsize,
const char *nglist,
const char *npath,
const char *dist,
const char *headOnly,
const char *artType,
int spamArt,
const char *cSize
) {
NewsFeed *nf;
int r = 0;
int bytes = 0;
char linebuf[4096];
int linesiz, siz;
char *ptr;
int arthash = quickhash(msgid);
if ((ptr = strchr(npath, '!'))) {
snprintf(linebuf, ptr - npath + 1, "%s", npath);
} else {
snprintf(linebuf, sizeof(linebuf), "%s", npath);
}
linesiz = strlen(linebuf);
if (offsize && ((ptr = strchr(offsize, ',')))) {
ptr++;
} else {
*ptr = '?';
}
siz = strlen(msgid) + strlen(ptr) + strlen(artType) + 4;
if (cSize != NULL && strlen(cSize) > 1)
siz += strlen(cSize);
if (linesiz + siz < sizeof(linebuf) - 1) {
strcat(linebuf, " + ");
strcat(linebuf, msgid);
strcat(linebuf, " ");
strcat(linebuf, ptr);
if (cSize != NULL && strlen(cSize) > 1) {
strcat(linebuf, ":");
strcat(linebuf, cSize);
}
strcat(linebuf, " ");
strcat(linebuf, artType);
linesiz += siz;
}
{
char *p;
if ((p = strchr(offsize, ',')) != NULL)
bytes = strtol(p + 1, NULL, 0);
}
for (nf = NFBase; nf; nf = nf->nf_Next) {
if (FeedDebug)
printf(">>SCAN:%s\n", nf->nf_Label);
if (UseSpamAlias && feedQuerySpamPaths(nf, npath, bytes, msgid))
break;
if (feedQueryPaths(nf, npath, bytes, strtol(artType, NULL, 16)) == 0 &&
feedQueryGroups(nf, nglist) == 0 &&
feedSpamFeedOK(nf->nf_SpamFeedOpt, spamArt) == 0 &&
feedQueryDists(nf, dist) == 0 &&
feedHashFeedOK(&nf->nf_HashFeed, arthash) == 0
) {
siz = strlen(nf->nf_Label) + 1;
if (linesiz + siz < sizeof(linebuf) - 1) {
strcat(linebuf, " ");
strcat(linebuf, nf->nf_Label);
linesiz += siz;
}
r += callback(nf->nf_Label, msgid, path, offsize, nf->nf_PerLineFlushOpt, strtol(headOnly, NULL, 10), artType, cSize);
}
}
/* logit(LOG_INFO, "%s", linebuf); */
if (logIt)
LogIncoming("%s%s%s", linebuf, "", "");
return(r);
}
void
FeedFlush(void)
{
/* NewsFeed *nf;*/
/*
if (FeedFo)
fflush(FeedFo);
*/
}
int
FeedValid(const char *hlabel, int *pcount)
{
NewsFeed *nf;
if ((nf = NFCache) == NULL) {
for (nf = NFBase; nf; nf = nf->nf_Next) {
if (strcmp(hlabel, nf->nf_Label) == 0)
break;
}
NFCache = nf;
}
if (nf == NULL)
return(FEED_MISSINGLABEL);
if (nf->nf_MaxConnect && *pcount > nf->nf_MaxConnect) {
*pcount = nf->nf_MaxConnect;
return(FEED_MAXCONNECT);
}
return(0);
}
int
feedHashFeedOK(HashFeed *hashfeed, int article)
{
if (!hashfeed->hf_Mod)
return(0);
return(!HashFeedMatch(hashfeed, article));
}
int
ArtHashIsFiltered(const char *msgid)
{
if(IFBase && IFBase->nf_HashFeed.hf_Mod) {
return(!HashFeedMatch(&(IFBase->nf_HashFeed),quickhash(msgid)));
}
return(0);
}
/*
* IsFiltered() - return 0 if the article should be scrapped because
* one of the gropus is filtered out, -1 otherwise.
*/
int
IsFiltered(const char *hlabel, const char *nglist)
{
NewsFeed *nf;
int r;
if ((nf = NFCache) == NULL) {
for (nf = NFBase; nf; nf = nf->nf_Next) {
if (strcmp(hlabel, nf->nf_Label) == 0)
break;
}
NFCache = nf;
}
if (nf == NULL) {
r = 0;
} else {
r = filterQueryGroups(nf, nglist);
}
if (DebugOpt > 1)
printf("IsFiltered: %d (%s,%s)\n", r, hlabel, nglist);
return(r);
}
/*
* IsDelayed() - return 1 if the feed is delayed
*/
int
IsDelayed(const char *hlabel)
{
NewsFeed *nf;
for (nf = NFBase; nf; nf = nf->nf_Next) {
if (strcmp(hlabel, nf->nf_Label) == 0)
break;
}
if (nf == NULL)
return(0);
return(nf->nf_LinkInfo.li_DelayFeed != 0);
}
/*
* Return 0 if the feed does NOT have an alias for an element in the path
* AND the article is not too large, otherwise return -1.
*/
int
feedQueryPaths(NewsFeed *feed, const char *npath, int size, int artType)
{
const char *p;
int cnt = 0;
if (feed->nf_MaxArtSize && size > feed->nf_MaxArtSize)
return(-1);
if (feed->nf_MinArtSize && size <= feed->nf_MinArtSize)
return(-1);
/*
* If we don't want any art types, then return
* If arttype is unknown and we don't want the default, then return
* If artype is known, but doesn't match the feed request, then return
*/
if (ArtTypeMatch(artType, feed->nf_ArtTypes) == 0)
return(-1);
while (*npath == ' ' || *npath == '\t')
++npath;
for (p = npath; p; p = strchr(p, '!')) {
char pat[MAXGNAME];
int l;
++cnt;
if (*p == '!')
++p;
for (l = 0; l < MAXGNAME - 1 && p[l] && p[l] != '!' && p[l] != ' ' && p[l] != '\t' && p[l] != '\n' && p[l] != '\r'; ++l)
;
strncpy(pat, p, l);
pat[l] = 0;
if (feedPathQuery(feed, pat) == 0)
break;
}
if (p == NULL) {
/*
* no path aliases matched, we are ok... unless MaxPathLen
* or MinPathLen is set.
*/
if (feed->nf_MaxPathLen && feed->nf_MaxPathLen < cnt)
return(-1);
if (feed->nf_MinPathLen && feed->nf_MinPathLen > cnt)
return(-1);
return(0);
}
/*
* path alias found, skip this feed
*/
return(-1);
}
/*
* Return 0 if the feed does NOT have a spamalias for either the user or
* last site in the path, otherwise return -1.
*/
int
feedQuerySpamPaths(NewsFeed *feed, const char *npath, int size, const char *msgid)
{
const char *p;
char *q;
char *r;
char pat[MAXGNAME];
if (*npath == 0)
return(0);
p = npath;
q = 0;
r = 0;
while (*p && (p = strchr(p, '!')) != NULL) { /* Walk to end of path */
p++;
r = q;
q = (char *)p;
}
if (r == NULL)
r = (char *)npath;
strcpy(pat, r);
r = strchr(pat,'!');
if (r != NULL) {
*r = 0;
r++;
} else
r = pat;
if (DebugOpt > 4)
logit(LOG_DEBUG, "%s: Spam test, '%s' '%s' '%s'",
PatLibExpand(DNewsfeedsPat), npath, pat, r);
/*
At this point, r should point at the username at the end of the
path and pat should point at the sitename the user is at,
assuming the Path: syntax was not bogus
*/
if (feedSpamPathQuery(feed, pat) == 0) {
logit(LOG_DEBUG, "Spam Path A detect, %s %s", msgid, pat);
return(-1);
}
if (feedSpamPathQuery(feed, r) == 0) {
logit(LOG_DEBUG, "Spam Path B detect, %s %s", msgid, r);
return(-1);
}
return(0);
}
/*
* Return 0 if the feed has a group match against a group in the group list
* Return -1 if there is no match
*/
int
feedQueryGroups(NewsFeed *feed, const char *nglist)
{
const char *p;
int r = -1;
int count = 0;
if (FeedDebug)
printf(">>QUERY:%s:%s\n", feed->nf_Label, nglist);
while (*nglist == ' ' || *nglist == '\t')
++nglist;
for (p = nglist; p && r != -2; p = strchr(p, ',')) {
int l;
int nr;
char group[MAXGNAME];
if (*p == ',')
++p;
for (l = 0; l < MAXGNAME - 1 && p[l] && p[l] != ',' && p[l] != ' ' && p[l] != '\t' && p[l] != '\n' && p[l] != '\r'; ++l)
;
/*
* r:
* 0 feed based on group
* -1 do not feed based on group
* -2 do not feed based on group if group appears at all
*/
strncpy(group, p, l);
group[l] = 0;
nr = recursiveScan(feed, offsetof(NewsFeed, nf_GroupAcceptBase), group, cbFeedGroupQuery, -1);
++count;
if (nr != -1) {
r = nr;
}
}
if (r >= 0 && feed->nf_MaxCrossPost && count > feed->nf_MaxCrossPost)
r = -1;
if (r >= 0 && feed->nf_MinCrossPost && count < feed->nf_MinCrossPost)
r = -1;
if (r >= 0 && recursiveScan(feed, offsetof(NewsFeed, nf_RequireGroupsBase), nglist, cbFilterRequireGroups, 0) < 0)
r = -2;
return(r);
}
/*
* Check whether a distribution is valid. Return 0 if so, -1 if we should
* drop the article.
*
* Original by larso@ifi.uio.no (=?iso-8859-1?Q?Lars_Henrik_B=F8ler_Olafsen?=),
* this routine rewritten by Matt.
*
* The algorithm works like this:
*
* If no feed distributions defined, Pass.
*
* If positive feed distributions defined, require that we match at
* least one of them to return 0, else we return -1. But if no
* positive feed distributions have been defined we will return 0 unless
* a negative feed distribution match occurs.
*
* If negative feed distributions defined and we match *any* of them,
* we dump, even if we had other positive matches. I.e. negative rules
* have precedence.
*
* Individual distributions cannot be more then 30 characters long.
*/
int
feedQueryDists(NewsFeed *feed, const char *dist)
{
int r = 0;
if (feed->nf_Dist != NULL || feed->nf_NoDist != NULL) {
/*
* If match distributions exist, the default return value is -1
* and we MUST match something.
*/
int i = 0;
if (feed->nf_Dist != NULL)
r = -1;
while (dist[i]) {
int j;
i += strspn(dist + i, " \t\r\n,"); /* skip ws */
j = strcspn(dist + i, " \t\r\n,"); /* parse distribution */
if (j) {
/*
* &dist[i] for j characters
*
* If we find a match in nodist, that's it.
*/
if (feedDistQuery(dist + i, j, feed->nf_NoDist) == 0) {
r = -1;
break;
}
/*
* If we find a match in dist, set r = 0, but a nodist match
* later on can still dump us.
*/
if (feedDistQuery(dist + i, j, feed->nf_Dist) == 0) {
r = 0;
}
}
i += j;
/*
* after skipping past the distribution string, the next character
* MUST be a comma if we are going to have more distributions, with
* no white space. If someone puts whitespace here they're bozos
* anyway.
*/
if (dist[i] != ',')
break;
}
}
return(r);
}
int
feedDistQuery(const char *item, int len, const char *list)
{
int i = 0;
int r = -1;
if (list) {
while (list[i]) {
int j = strcspn(list + i, ",");
if (j == len && strncasecmp(item, list + i, j) == 0) {
r = 0;
break;
}
i += j;
if (list[i] != ',')
break;
++i;
}
}
return(r);
}
/*
* cbFilterRequireGroups()
*
* If no requiregroups nodes specified, allow any group. Otherwise
* group must be in requiregroups list. abort if def > 0. For the
* recursion, if someone else sets def > 0, we are already done.
*/
int
cbFilterRequireGroups(Node *node, const void *data, int *pabort, int def)
{
const char *nglist = data;
if (def <= 0) {
/*
* 0 -> -1, indicates that we have at leaset one requiregroup
* so the default has changed.. we MUST find the group.
*/
const char *p;
def = -1;
while (*nglist == ' ' || *nglist == '\t')
++nglist;
for (p = nglist; p; p = strchr(p, ',')) {
int l;
char group[MAXGNAME];
if (*p == ',')
++p;
for (l = 0; l < MAXGNAME - 1 && p[l] && p[l] != ',' && p[l] != ' ' && p[l] != '\t' && p[l] != '\n' && p[l] != '\r'; ++l)
;
strncpy(group, p, l);
group[l] = 0;
if (WildCmp(node->no_Name, group) == 0) {
def = 1;
break;
}
}
}
if (def > 0)
*pabort = 1;
return(def);
}
/*
* FeedQuerySpam() - scan addspam/delspam filters on NNTP-Posting-Host:
*
* 0 - could be either, use normal spam filter
* -1 - definitely spam
* +1 - definitely not spam
*/
int
FeedQuerySpam(const char *hlabel, const char *hostInfo)
{
NewsFeed *feed;
int r = 0;
if ((feed = NFCache) == NULL) {
for (feed = NFBase; feed; feed = feed->nf_Next) {
if (strcmp(hlabel, feed->nf_Label) == 0)
break;
}
NFCache = feed;
}
if (feed) {
r = recursiveScan(
feed,
offsetof(NewsFeed, nf_SpamBase),
hostInfo,
cbWildCmpNoStop,
r
);
}
/*
* My directives are weird. 'delspam' means 'not spam' but has a node
* value of -1. 'addspam' means 'spam' but has a node value of +1.
*/
if (r)
r = -r;
return(r);
}
/*
* PathElmMatches() - match first path element against aliases.
* Return -1 on failure, 0 on success. Set
* *pidx to the length of the first path element.
*/
int
PathElmMatches(const char *hlabel, const char *p, int bytes, int *pidx)
{
NewsFeed *feed;
int i;
int r = 0;
if ((feed = NFCache) == NULL) {
for (feed = NFBase; feed; feed = feed->nf_Next) {
if (strcmp(hlabel, feed->nf_Label) == 0)
break;
}
NFCache = feed;
}
for (i = 0; i < bytes && p[i] != '!' && p[i] != '\n' && p[i] != '\r'; ++i)
;
*pidx = i;
if (feed && feed->nf_NoMisMatch <= 0) {
char buf[256];
if (i >= sizeof(buf))
i = sizeof(buf) - 1;
bcopy(p, buf, i);
buf[i] = 0;
r = recursiveScan(
feed,
offsetof(NewsFeed, nf_PathAliasBase),
buf,
cbWildCmpStopWhenFound,
-1
);
}
return(r);
}
/*
* Return 0 if the common path element exists in the given
* path string.
*/
int
CommonElmMatches(const char *common, const char *p, int bytes)
{
int l = strlen(common);
while (bytes >= l) {
if (strncmp(common, p, l) == 0 &&
(bytes == l || p[l] == '!' || p[l] == '\n' || p[l] == '\r')
) {
return(0);
}
while (bytes && *p != '!' && *p != '\n' && *p != '\r') {
--bytes;
++p;
}
if (bytes && *p == '!') {
--bytes;
++p;
}
}
return(-1);
}
/*
* Returns how long the feed in quesiton should be delayed before allowing
* a connection.
*/
int
FeedInDelay(const char *hlabel)
{
NewsFeed *feed;
if ((feed = NFCache) == NULL) {
for (feed = NFBase; feed; feed = feed->nf_Next) {
if (strcmp(hlabel, feed->nf_Label) == 0)
break;
}
NFCache = feed;
}
if (feed)
return feed->nf_LinkInfo.li_DelayInFeed;
else
return 0;
}
/*
* Write the throttling delay and line count into the appropriate
* memory locations.
*/
void
FeedGetThrottle(const char *hlabel, int *delay, int *lines)
{
NewsFeed *feed;
if ((feed = NFCache) == NULL) {
for (feed = NFBase; feed; feed = feed->nf_Next) {
if (strcmp(hlabel, feed->nf_Label) == 0)
break;
}
NFCache = feed;
}
if (feed) {
*delay = feed->nf_ThrottleDelay;
*lines = feed->nf_ThrottleLines;
} else {
*delay = 0;
*lines = 0;
}
}
/*
* Return 1 if the feed is read-only (i.e., no IHAVE/CHECK/TAKETHIS),
* 0 otherwise.
*/
int
FeedReadOnly(const char *hlabel)
{
NewsFeed *feed;
if ((feed = NFCache) == NULL) {
for (feed = NFBase; feed; feed = feed->nf_Next) {
if (strcmp(hlabel, feed->nf_Label) == 0)
break;
}
NFCache = feed;
}
if (feed)
return feed->nf_ReadOnly;
else
return 0;
}
/*
* Return 1 if the feed is allowed to use use the 'whereis' command
* 0 otherwise.
*/
int
FeedWhereIs(const char *hlabel)
{
NewsFeed *feed;
if ((feed = NFCache) == NULL) {
for (feed = NFBase; feed; feed = feed->nf_Next) {
if (strcmp(hlabel, feed->nf_Label) == 0)
break;
}
NFCache = feed;
}
if (feed)
return feed->nf_WhereIs;
else
return 0;
}
/*
* Return the incoming priority setting
*/
int
FeedPriority(const char *hlabel)
{
NewsFeed *feed;
if ((feed = NFCache) == NULL) {
for (feed = NFBase; feed; feed = feed->nf_Next) {
if (strcmp(hlabel, feed->nf_Label) == 0)
break;
}
NFCache = feed;
}
if (feed)
return feed->nf_IncomingPriority;
else
return 0;
}
/*
* Return 1 if we need to reject precommit hits for this incoming feed
* 0 otherwise.
*/
int
FeedPrecommitReject(const char *hlabel)
{
NewsFeed *feed;
if ((feed = NFCache) == NULL) {
for (feed = NFBase; feed; feed = feed->nf_Next) {
if (strcmp(hlabel, feed->nf_Label) == 0)
break;
}
NFCache = feed;
}
if (feed)
return feed->nf_PrecommitReject;
else
return 0;
}
/*
* filterQueryGroups()
*
* 0 if no filter commands matched
* -1 if last matching filter command was 'nofilter'
* +1 if last matching filter command was 'filter'
*
* If one group returns -1 and another returns +1, we always return +1.
*/
int
filterQueryGroups(NewsFeed *feed, const char *nglist)
{
const char *p;
int r = 0;
while (*nglist == ' ' || *nglist == '\t')
++nglist;
for (p = nglist; p; p = strchr(p, ',')) {
int l;
char group[MAXGNAME];
if (*p == ',')
++p;
for (l = 0; l < MAXGNAME - 1 && p[l] && p[l] != ',' && p[l] != ' ' && p[l] != '\t' && p[l] != '\n' && p[l] != '\r'; ++l)
;
strncpy(group, p, l);
group[l] = 0;
r = recursiveScan(
feed,
offsetof(NewsFeed, nf_FilterBase),
group,
cbWildCmpNoStop,
r
);
if (r > 0)
break;
}
return(r);
}
/*
* cbFeedGroupQuery()
*
* -2 do not feed ANY group if this group appears in the newsgroups
* line.
*
* -1 do not feed this group
*
* 0 feed this group
*
*/
int
cbFeedGroupQuery(Node *node, const void *data, int *pabort, int def)
{
const char *group = data;
if (WildCmp(node->no_Name, group) == 0) {
if (node->no_Value < 0) {
def = node->no_Value; /* -1 or -2 */
} else if (node->no_Value > 0) {
def = 0; /* +1 */
}
}
if (FeedDebug)
printf(">>CMP:%s:%s:%d:%d\n", node->no_Name, group, node->no_Value, def);
return(def);
}
/*
* Return 0 if we get a match
*/
int
feedSpamPathQuery(NewsFeed *feed, const char *path)
{
int r = -1;
if (feed) {
r = recursiveScan(
feed,
offsetof(NewsFeed, nf_SpamPathAliasBase),
path,
cbWildCmpStopWhenFound,
r
);
}
return(r);
}
/*
* Return 0 if we get a match
*/
int
feedPathQuery(NewsFeed *feed, const char *path)
{
int r = -1;
if (feed) {
r = recursiveScan(
feed,
offsetof(NewsFeed, nf_PathAliasBase),
path,
cbWildCmpStopWhenFound,
r
);
}
return(r);
}
#if USE_OFFER_FILTER
/*
* OfferIsFiltered() - return 0 if the article should be refused because
* the Message-ID is filtered out, -1 otherwise.
*/
int
OfferIsFiltered(const char *hlabel, const char *msgid)
{
NewsFeed *nf;
int r;
if ((nf = NFCache) == NULL) {
for (nf = NFBase; nf; nf = nf->nf_Next) {
if (strcmp(hlabel, nf->nf_Label) == 0)
break;
}
NFCache = nf;
}
if (nf == NULL) {
r = 0;
} else {
r = feedOfferFilterQuery(nf, msgid);
}
if (DebugOpt > 1)
printf("OfferIsFiltered: %d (%s,%s)\n", r, hlabel, msgid);
return(r);
}
/*
* Return non-0 if we get a match
*/
int
feedOfferFilterQuery(NewsFeed *feed, const char *msgid)
{
int r = 0;
if (feed) {
r = recursiveScan(
feed,
offsetof(NewsFeed, nf_OfferFilterBase),
msgid,
cbWildCmpNoStop,
r
);
}
return(r);
}
#endif /* USE_OFFER_FILTER */
time_t NFGmtMin = (time_t)-1;
time_t NFMTime = 0;
void
TouchNewsFeed(void)
{
struct utimbuf ut;
time(&ut.actime);
ut.modtime = ut.actime;
utime(PatLibExpand(DNewsfeedsPat), &ut);
}
/*
* LoadNewsFeed() - [re]load dnewsfeeds file
*/
void
LoadNewsFeed(time_t t, int force, const char *hlabel)
{
/*
* check for dnewsfeeds file modified once a minute
*/
if (hlabel != (void *)-1)
SaveHLabel = hlabel;
if (force || t == 0 || t / 60 != NFGmtMin) {
struct stat st = { 0 };
FILE *fi = NULL;
if (t)
NFGmtMin = t / 60;
errno = 0;
if ((istat(0, &st) == 0 && st.st_mtime != NFMTime && !TooNear(st.st_mtime)) ||
force ||
NFMTime == 0
) {
char buf[MAXGNAME+256];
if (DebugOpt > 1)
printf("Opening dnewsfeeds file: %s\n", PatLibExpand(DNewsfeedsPat));
fi = iopen(PatLibExpand(DNewsfeedsPat), "r");
if (fi == NULL)
logit(LOG_CRIT, "%s: %s", PatLibExpand(DNewsfeedsPat), strerror(errno));
NFMTime = st.st_mtime; /* may be 0 if file failed to open */
/*
* flush existing feed information
*/
if (DebugOpt) {
printf("Reloading dnewsfeeds file hlabel=%s\n",
((hlabel) ? hlabel : "?")
);
}
FeedFlush();
/*
* free up feed structures
*/
freePool(&NFMemPool);
NFCache = NULL;
NFGlob = NULL;
NFBase = NULL;
IFBase = NULL;
GRBase = NULL;
ISBase = NULL;
ESBase = NULL;
UseSpamAlias = 0;
/*
* Reset MaxPerRemote if it wasn't specified on the command line.
* 0 = disabled
* -1 = disabled but there may be per-feed limits
* +N = some global limit
*/
if (SaveHLabel == NULL && DOpts.MaxPerRemote == -1)
DOpts.MaxPerRemote = 0;
/*
* load up new feed structures
*/
{
NewsFeed *nf = NULL;
Node **paNode = NULL;
Node **psaNode = NULL;
Node **pgNode = NULL;
Node **psNode = NULL;
Node **pfNode = NULL;
Node **prNode = NULL;
#if USE_OFFER_FILTER
Node **pofNode = NULL;
#endif /* USE_OFFER_FILTER */
int lineNo = 0;
int inGroupDef = 0;
FILE *altFi = NULL;
while (fi && igets(buf, sizeof(buf), fi) != NULL) {
char *s1 = strtok(buf, " \t\n");
char *s2 = (s1) ? strtok(NULL, " \t\n") : NULL;
int err = 1;
++lineNo;
if (s1 == NULL || *s1 == '#')
continue;
if (strcmp(s1, "label") == 0) {
if (nf) {
logit(LOG_CRIT,
"Newsfeed config line %d, no end before new label!\n",
lineNo
);
if (pfNode) {
*pfNode = NULL;
pfNode = NULL;
}
if (paNode) {
*paNode = NULL;
paNode = NULL;
}
if (psaNode) {
*psaNode = NULL;
psaNode = NULL;
}
if (pgNode) {
*pgNode = NULL;
pgNode = NULL;
}
if (psNode) {
*psNode = NULL;
psNode = NULL;
}
if (prNode) {
*prNode = NULL;
prNode = NULL;
}
#if USE_OFFER_FILTER
if (pofNode) {
*pofNode = NULL;
pofNode = NULL;
}
#endif /* USE_OFFER_FILTER */
if (nf) {
if (inGroupDef) {
nf->nf_Next = GRBase;
GRBase = nf;
} else if (strcmp(nf->nf_Label, "ISPAM") != 0 &&
strcmp(nf->nf_Label, "ESPAM") != 0 &&
strcmp(nf->nf_Label, "IFILTER") != 0) {
nf->nf_Next = NFBase;
NFBase = nf;
}
if (altFi) {
AltFeed(nf, altFi);
fclose(altFi);
altFi = NULL;
}
}
nf = NULL;
}
inGroupDef = 0;
/*
* If we are loading a particular label, it must exist.
*/
if (s2 &&
(SaveHLabel == NULL ||
strcmp(s2, SaveHLabel) == 0 ||
strcmp(s2, "GLOBAL") == 0 ||
strcmp(s2, "ISPAM") == 0 ||
strcmp(s2, "ESPAM") == 0 ||
strcmp(s2, "IFILTER") == 0
)) {
char path[256];
nf = zalloc(&NFMemPool, sizeof(NewsFeed) + strlen(s2) + 1);
nf->nf_Label = (char *)(nf + 1);
nf->nf_ArtTypes = NULL;
strcpy(nf->nf_Label, s2);
pfNode = &nf->nf_FilterBase;
paNode = &nf->nf_PathAliasBase;
psaNode = &nf->nf_SpamPathAliasBase;
pgNode = &nf->nf_GroupAcceptBase;
psNode = &nf->nf_SpamBase;
prNode = &nf->nf_RequireGroupsBase;
#if USE_OFFER_FILTER
pofNode = &nf->nf_OfferFilterBase;
#endif /* USE_OFFER_FILTER */
setLinkStart(&nf->nf_LinkInfo);
snprintf(path, sizeof(path), "%s/%s", PatExpand(FeedsHomePat), s2);
altFi = fopen(path, "r");
err = 0;
if (strcmp(nf->nf_Label, "GLOBAL") == 0) {
NFGlob = nf;
setLinkGLOBAL(&nf->nf_LinkInfo);
} else {
char *gl = "GLOBAL";
if (strcmp(s2, "ISPAM") == 0)
ISBase = nf;
else if (strcmp(s2, "ESPAM") == 0)
ESBase = nf;
else if (strcmp(s2, "IFILTER") == 0)
IFBase = nf;
MakeNodeAppList(&pgNode, &NFMemPool, gl, 2);
MakeNodeAppList(&paNode, &NFMemPool, gl, 2);
MakeNodeAppList(&psNode, &NFMemPool, gl, 2);
MakeNodeAppList(&prNode, &NFMemPool, gl, 2);
MakeNodeAppList(&pfNode, &NFMemPool, gl, 2);
#if USE_OFFER_FILTER
MakeNodeAppList(&pofNode, &NFMemPool, gl, 2);
#endif /* USE_OFFER_FILTER */
}
}
} else if (strcmp(s1, "alias") == 0) {
if (nf && s2) {
(void)MakeNodeAppList(&paNode, &NFMemPool, s2, 0);
err = 0;
}
} else if (strcmp(s1, "spamalias") == 0) {
if (nf && s2) {
(void)MakeNodeAppList(&psaNode, &NFMemPool, s2, 0);
err = 0;
UseSpamAlias = 1;
}
} else if (strcmp(s1, "adddist") == 0) {
if (nf && s2) {
zappendStr(&NFMemPool, &nf->nf_Dist, ",", s2);
err = 0;
}
} else if (strcmp(s1, "deldist") == 0) {
if (nf && s2) {
zappendStr(&NFMemPool, &nf->nf_NoDist, ",", s2);
err = 0;
}
} else if (strcmp(s1, "rtflush") == 0) {
if (nf) {
nf->nf_PerLineFlushOpt = enabled(s2);
err = 0;
}
} else if (strcmp(s1, "nortflush") == 0) {
if (nf) {
nf->nf_PerLineFlushOpt = -1;
err = 0;
}
} else if (strcmp(s1, "nospam") == 0) {
if (nf) {
nf->nf_SpamFeedOpt = enabled(s2);
err = 0;
}
} else if (strcmp(s1, "onlyspam") == 0) {
if (nf) {
nf->nf_SpamFeedOpt = 2;
err = 0;
}
} else if (strcmp(s1, "nomismatch") == 0) {
if (nf) {
nf->nf_NoMisMatch = enabled(s2);
err = 0;
}
} else if (strcmp(s1, "domismatch") == 0) {
if (nf) {
nf->nf_NoMisMatch = -1;
err = 0;
}
} else if (strcmp(s1, "filter") == 0) {
if (nf && s2) {
(void)MakeNodeAppList(&pfNode, &NFMemPool, s2, 1);
err = 0;
}
} else if (strcmp(s1, "nofilter") == 0) {
if (nf && s2) {
(void)MakeNodeAppList(&pfNode, &NFMemPool, s2, -1);
err = 0;
}
#if USE_OFFER_FILTER
} else if (strcmp(s1, "offerfilter") == 0) {
if (nf && s2) {
(void)MakeNodeAppList(&pofNode, &NFMemPool, s2, 1);
err = 0;
}
} else if (strcmp(s1, "noofferfilter") == 0) {
if (nf && s2) {
(void)MakeNodeAppList(&pofNode, &NFMemPool, s2, 0);
err = 0;
}
#endif /* USE_OFFER_FILTER */
} else if (strcmp(s1, "maxconnect") == 0) {
if (nf && s2) {
nf->nf_MaxConnect = strtol(s2, NULL, 0);
err = 0;
if (nf->nf_MaxConnect && DOpts.MaxPerRemote == 0)
DOpts.MaxPerRemote = -1;
}
} else if (strcmp(s1, "maxinboundrate") == 0) {
if (nf && s2) {
nf->nf_MaxInboundRate = zallocStr(&NFMemPool, s2);
err = 0;
}
} else if (strcmp(s1, "mincross") == 0) {
if (nf && s2) {
nf->nf_MinCrossPost = strtol(s2, NULL, 0);
err = 0;
}
} else if (strcmp(s1, "maxcross") == 0) {
if (nf && s2) {
nf->nf_MaxCrossPost = strtol(s2, NULL, 0);
err = 0;
}
} else if (strcmp(s1, "minpath") == 0) {
if (nf && s2) {
nf->nf_MinPathLen = strtol(s2, NULL, 0);
err = 0;
}
} else if (strcmp(s1, "maxpath") == 0) {
if (nf && s2) {
nf->nf_MaxPathLen = strtol(s2, NULL, 0);
err = 0;
}
} else if (strcmp(s1, "minsize") == 0) {
if (nf && s2) {
nf->nf_MinArtSize = bsizetol(s2);
err = 0;
}
} else if (strcmp(s1, "maxsize") == 0) {
if (nf && s2) {
nf->nf_MaxArtSize = bsizetol(s2);
err = 0;
}
} else if (strcmp(s1, "groupdef") == 0) {
if (nf) {
logit(LOG_CRIT,
"Newsfeed config line %d, no end before new groupdef!\n",
lineNo
);
if (pfNode) {
*pfNode = NULL;
pfNode = NULL;
}
if (paNode) {
*paNode = NULL;
paNode = NULL;
}
if (psaNode) {
*psaNode = NULL;
psaNode = NULL;
}
if (pgNode) {
*pgNode = NULL;
pgNode = NULL;
}
if (psNode) {
*psNode = NULL;
psNode = NULL;
}
if (prNode) {
*prNode = NULL;
prNode = NULL;
}
#if USE_OFFER_FILTER
if (pofNode) {
*pofNode = NULL;
pofNode = NULL;
}
#endif /* USE_OFFER_FILTER */
if (inGroupDef) {
nf->nf_Next = GRBase;
GRBase = nf;
} else if (strcmp(nf->nf_Label, "ISPAM") != 0 &&
strcmp(nf->nf_Label, "ESPAM") != 0 &&
strcmp(nf->nf_Label, "IFILTER") != 0) {
nf->nf_Next = NFBase;
NFBase = nf;
}
if (altFi) {
AltFeed(nf, altFi);
fclose(altFi);
altFi = NULL;
}
nf = NULL;
}
inGroupDef = 1;
if (s2) {
nf = zalloc(&NFMemPool, sizeof(NewsFeed) + strlen(s2) + 1);
nf->nf_Label = (char *)(nf + 1);
nf->nf_ArtTypes = NULL;
strcpy(nf->nf_Label, s2);
pfNode = &nf->nf_FilterBase;
paNode = &nf->nf_PathAliasBase;
psaNode = &nf->nf_SpamPathAliasBase;
pgNode = &nf->nf_GroupAcceptBase;
psNode = &nf->nf_SpamBase;
prNode = &nf->nf_RequireGroupsBase;
#if USE_OFFER_FILTER
pofNode = &nf->nf_OfferFilterBase;
#endif /* USE_OFFER_FILTER */
err = 0;
}
} else if (strcmp(s1, "groupref") == 0) {
if (nf && s2) {
(void)MakeNodeAppList(&pgNode, &NFMemPool, s2, 2);
(void)MakeNodeAppList(&paNode, &NFMemPool, s2, 2);
(void)MakeNodeAppList(&psaNode, &NFMemPool, s2, 2);
(void)MakeNodeAppList(&psNode, &NFMemPool, s2, 2);
(void)MakeNodeAppList(&prNode, &NFMemPool, s2, 2);
(void)MakeNodeAppList(&pfNode, &NFMemPool, s2, 2);
#if USE_OFFER_FILTER
(void)MakeNodeAppList(&pofNode, &NFMemPool, s2, 2);
#endif /* USE_OFFER_FILTER */
err = 0;
}
} else if (strcmp(s1, "delgroupany") == 0) {
if (pgNode && s2) {
(void)MakeNodeAppList(&pgNode, &NFMemPool, s2, -2);
err = 0;
}
} else if (strcmp(s1, "delgroup") == 0) {
if (pgNode && s2) {
(void)MakeNodeAppList(&pgNode, &NFMemPool, s2, -1);
err = 0;
}
} else if (strcmp(s1, "requiregroup") == 0) {
if (prNode && s2) {
(void)MakeNodeAppList(&prNode, &NFMemPool, s2, 1);
err = 0;
}
} else if (strcmp(s1, "addgroup") == 0) {
if (pgNode && s2) {
(void)MakeNodeAppList(&pgNode, &NFMemPool, s2, 1);
err = 0;
}
} else if (strcmp(s1, "delspam") == 0) {
if (psNode && s2) {
(void)MakeNodeAppList(&psNode, &NFMemPool, s2, -1);
err = 0;
}
} else if (strcmp(s1, "addspam") == 0) {
if (psNode && s2) {
(void)MakeNodeAppList(&psNode, &NFMemPool, s2, 1);
err = 0;
}
} else if (strcmp(s1, "throttle_delay") == 0) {
if (nf && s2) {
nf->nf_ThrottleDelay = strtol(s2, NULL, 0);
err = 0;
}
} else if (strcmp(s1, "throttle_lines") == 0) {
if (nf && s2) {
nf->nf_ThrottleLines = strtol(s2, NULL, 0);
err = 0;
}
} else if (strcmp(s1, "readonly") == 0) {
if (nf) {
nf->nf_ReadOnly = enabled(s2);
err = 0;
}
} else if (strcmp(s1, "allow_readonly") == 0) {
if (nf) {
logit(LOG_CRIT,
"Newsfeed config line %d, allow_readonly ignored, use R flag in dserver.hosts\n",
lineNo
);
err = 0;
}
} else if (strcmp(s1, "whereis") == 0) {
if (nf) {
nf->nf_WhereIs = enabled(s2);
err = 0;
}
} else if (strcmp(s1, "incomingpriority") == 0) {
if (nf) {
nf->nf_IncomingPriority = enabled(s2);
err = 0;
}
} else if (strcmp(s1, "precomreject") == 0) {
if (nf) {
nf->nf_PrecommitReject = enabled(s2);
err = 0;
}
} else if (strcmp(s1, "noprecomreject") == 0) {
if (nf) {
nf->nf_PrecommitReject = -1;
err = 0;
}
} else if (strcmp(s1, "startdelay") == 0) {
if (nf) {
nf->nf_LinkInfo.li_StartDelay = atoi(s2);
err = 0;
}
} else if (strcmp(s1, "delayfeed") == 0) {
if (nf) {
nf->nf_LinkInfo.li_DelayFeed = atoi(s2);
err = 0;
}
} else if (strcmp(s1, "delayinfeed") == 0) {
if (nf) {
nf->nf_LinkInfo.li_DelayInFeed = atoi(s2);
err = 0;
}
} else if (strcmp(s1, "transmitbuf") == 0) {
if (nf) {
nf->nf_LinkInfo.li_TransmitBuf = atoi(s2);
err = 0;
}
} else if (strcmp(s1, "receivebuf") == 0) {
if (nf) {
nf->nf_LinkInfo.li_ReceiveBuf = atoi(s2);
err = 0;
}
} else if (strcmp(s1, "setqos") == 0) {
if (nf) {
nf->nf_LinkInfo.li_QoS = strtol(s2,NULL,0); /* both hex & decimal */
err = 0;
}
} else if (strcmp(s1, "maxparallel") == 0) {
if (nf) {
nf->nf_LinkInfo.li_MaxParallel = atoi(s2);
err = 0;
}
} else if (strcmp(s1, "stream") == 0) {
if (nf) {
nf->nf_LinkInfo.li_NoStream = !enabled(s2);
err = 0;
}
} else if (strcmp(s1, "realtime") == 0) {
if (nf && s2 != NULL) {
if (strcmp(s2, "flush") == 0) {
nf->nf_PerLineFlushOpt = 1;
nf->nf_LinkInfo.li_RealTime = 1;
} else if (strcmp(s2, "notify") == 0) {
nf->nf_PerLineFlushOpt = 1;
nf->nf_LinkInfo.li_RealTime = 1;
nf->nf_LinkInfo.li_Notify = 1;
} else {
nf->nf_LinkInfo.li_RealTime = enabled(s2);
}
err = 0;
}
} else if (strcmp(s1, "notify") == 0) {
if (nf) {
nf->nf_LinkInfo.li_Notify = enabled(s2);
err = 0;
}
} else if ((strcmp(s1, "maxqueuefile") == 0) ||
(strcmp(s1, "maxqueue") == 0)) {
if (nf) {
nf->nf_LinkInfo.li_MaxQueueFile = atoi(s2);
err = 0;
}
} else if (strcmp(s1, "headfeed") == 0) {
if (nf) {
nf->nf_LinkInfo.li_HeadFeed = enabled(s2);
err = 0;
}
} else if (strcmp(s1, "preservebytes") == 0 ||
strcmp(s1, "ignorebytes") == 0) {
if (nf) {
nf->nf_LinkInfo.li_PreserveBytes = enabled(s2);
err = 0;
}
} else if (strcmp(s1, "genlines") == 0) {
if (nf) {
nf->nf_LinkInfo.li_GenLines = enabled(s2);
err = 0;
}
} else if (strcmp(s1, "check") == 0) {
if (nf && s2) {
if (strcmp(s2, "nortcheck") == 0) {
nf->nf_LinkInfo.li_Check = 2;
} else {
nf->nf_LinkInfo.li_Check = enabled(s2);
}
err = 0;
}
} else if (strcmp(s1, "compress") == 0) {
if (nf) {
#ifndef USE_ZLIB
logit(LOG_NOTICE, "%s: compression not enabled - ignoring compress option",
PatLibExpand(DNewsfeedsPat));
#endif
nf->nf_LinkInfo.li_Compress = atoi(s2);
err = 0;
}
} else if (strcmp(s1, "maxstream") == 0) {
if (nf) {
nf->nf_LinkInfo.li_MaxStream = atoi(s2);
err = 0;
}
} else if (strcmp(s1, "queueskip") == 0) {
if (nf) {
nf->nf_LinkInfo.li_QueueSkip = atoi(s2);
err = 0;
}
} else if (strcmp(s1, "articlestat") == 0) {
if (nf) {
nf->nf_LinkInfo.li_ArticleStat = enabled(s2);
err = 0;
}
} else if (strcmp(s1, "priority") == 0) {
if (nf) {
nf->nf_LinkInfo.li_Priority = atoi(s2);
err = 0;
}
} else if (strcmp(s1, "port") == 0) {
if (nf) {
nf->nf_LinkInfo.li_Port = atoi(s2);
err = 0;
}
} else if (strcmp(s1, "hostname") == 0) {
if (nf && s2) {
nf->nf_LinkInfo.li_HostName = zallocStr(&NFMemPool, s2);
err = 0;
}
} else if (strcmp(s1, "bindaddress") == 0) {
if (nf && s2) {
nf->nf_LinkInfo.li_BindAddress = zallocStr(&NFMemPool, SanitiseAddr(s2));
err = 0;
}
} else if (strcmp(s1, "logarts") == 0) {
if (nf && s2) {
zappendStr(&NFMemPool, &nf->nf_LinkInfo.li_LogArts, ",", s2);
err = 0;
}
} else if (strcmp(s1, "hours") == 0) {
if (nf && s2) {
zappendStr(&NFMemPool, &nf->nf_LinkInfo.li_Hours, ",", s2);
err = 0;
}
} else if (strcmp(s1, "arttypes") == 0) {
if (nf && s2) {
char *p = s2;
ArtTypeList *atp = NULL;
ArtTypeList *tatp;
while ((p = strsep(&s2, " \t:,")) != NULL) {
tatp = zalloc(&NFMemPool, sizeof(ArtTypeList));
tatp->negate = 0;
if (*p == '!') {
tatp->negate = 1;
p++;
}
tatp->arttype = ArtTypeConv(p);
tatp->next = NULL;
if (atp != NULL)
atp->next = tatp;
atp = tatp;
if (nf->nf_ArtTypes == NULL)
nf->nf_ArtTypes = atp;
}
err = 0;
}
} else if (strcmp(s1, "hashfeed") == 0) {
if (nf && s2) {
char *p;
err = 0;
if ((p = strchr(s2, '-')) != NULL) {
nf->nf_HashFeed.hf_Begin = strtol(s2, NULL, 0);
nf->nf_HashFeed.hf_End = strtol(++p, NULL, 0);
if ((p = strchr(s2, '/')) != NULL)
nf->nf_HashFeed.hf_Mod = strtol(++p, NULL, 0);
} else {
nf->nf_HashFeed.hf_Begin = strtol(s2, NULL, 0);
nf->nf_HashFeed.hf_End = nf->nf_HashFeed.hf_Begin;
if ((p = strchr(s2, '/')) != NULL)
nf->nf_HashFeed.hf_Mod = strtol(++p, NULL, 0);
}
}
} else if (strcmp(s1, "inhost") == 0) {
/* Used in lib/hostauth.c */
err = 0;
} else if (strcmp(s1, "host") == 0) {
/* Also used in lib/hostauth.c */
if (nf && s2) {
nf->nf_LinkInfo.li_HostName = zallocStr(&NFMemPool, s2);
(void)MakeNodeAppList(&paNode, &NFMemPool, s2, 0);
err = 0;
}
} else if (strcmp(s1, "end") == 0) {
if (nf) {
*paNode = NULL;
*psaNode = NULL;
*pgNode = NULL;
*psNode = NULL;
*prNode = NULL;
*pfNode = NULL;
#if USE_OFFER_FILTER
*pofNode = NULL;
#endif /* USE_OFFER_FILTER */
paNode = NULL;
psaNode = NULL;
pgNode = NULL;
psNode = NULL;
prNode = NULL;
pfNode = NULL;
#if USE_OFFER_FILTER
pofNode = NULL;
#endif /* USE_OFFER_FILTER */
if (inGroupDef ||
strcmp(nf->nf_Label, "GLOBAL") == 0) {
nf->nf_Next = GRBase;
GRBase = nf;
} else if (strcmp(nf->nf_Label, "ISPAM") != 0 &&
strcmp(nf->nf_Label, "ESPAM") != 0 &&
strcmp(nf->nf_Label, "IFILTER") != 0) {
nf->nf_Next = NFBase;
NFBase = nf;
}
if (altFi) {
AltFeed(nf, altFi);
fclose(altFi);
altFi = NULL;
}
nf = NULL;
err = 0;
}
} else {
logit(LOG_CRIT,
"Newsfeed config line %d, unknown command\n",
lineNo
);
err = 0;
}
/*
* deal with errors inside active labels (nf != NULL) or
* general errors when parsing the entire file (hlabel == NULL)
*/
if (err && (nf != NULL || SaveHLabel == NULL)) {
logit(LOG_ERR, "Newsfeed config line %d, command in unexpected position or command requires argument", lineNo);
}
}
if (nf) {
*paNode = NULL;
*psaNode = NULL;
*pgNode = NULL;
*psNode = NULL;
*prNode = NULL;
*pfNode = NULL;
#if USE_OFFER_FILTER
*pofNode = NULL;
#endif /* USE_OFFER_FILTER */
paNode = NULL;
psaNode = NULL;
pgNode = NULL;
psNode = NULL;
prNode = NULL;
pfNode = NULL;
#if USE_OFFER_FILTER
pofNode = NULL;
#endif /* USE_OFFER_FILTER */
if (inGroupDef) {
nf->nf_Next = GRBase;
GRBase = nf;
} else if (strcmp(nf->nf_Label, "ISPAM") != 0 &&
strcmp(nf->nf_Label, "ESPAM") != 0 &&
strcmp(nf->nf_Label, "IFILTER") != 0) {
nf->nf_Next = NFBase;
NFBase = nf;
}
nf = NULL;
}
if (altFi) {
fclose(altFi);
altFi = NULL;
}
}
}
if (fi)
iclose(fi);
/*
* Resolve NewsFeed references after the fact. This allows
* grouplist definitions to put after newsfeed definitions.
*/
{
NewsFeed *nf;
for (nf = NFBase; nf; nf = nf->nf_Next) {
resolveGroupList(nf->nf_Label, nf->nf_PathAliasBase);
resolveGroupList(nf->nf_Label, nf->nf_SpamPathAliasBase);
resolveGroupList(nf->nf_Label, nf->nf_GroupAcceptBase);
resolveGroupList(nf->nf_Label, nf->nf_FilterBase);
resolveGroupList(nf->nf_Label, nf->nf_SpamBase);
resolveGroupList(nf->nf_Label, nf->nf_RequireGroupsBase);
#if USE_OFFER_FILTER
resolveGroupList(nf->nf_Label, nf->nf_OfferFilterBase);
#endif /* USE_OFFER_FILTER */
}
for (nf = GRBase; nf; nf = nf->nf_Next) {
resolveGroupList(nf->nf_Label, nf->nf_PathAliasBase);
resolveGroupList(nf->nf_Label, nf->nf_SpamPathAliasBase);
resolveGroupList(nf->nf_Label, nf->nf_GroupAcceptBase);
resolveGroupList(nf->nf_Label, nf->nf_FilterBase);
resolveGroupList(nf->nf_Label, nf->nf_SpamBase);
resolveGroupList(nf->nf_Label, nf->nf_RequireGroupsBase);
#if USE_OFFER_FILTER
resolveGroupList(nf->nf_Label, nf->nf_OfferFilterBase);
#endif /* USE_OFFER_FILTER */
}
/*
* This recursively resolves defaults for all groupdef's, we
* can scan any of our lists to find the groups so we use the
* one that is most likely to be short.
*
* We must also resolve global defaults in
*/
for (nf = NFBase; nf; nf = nf->nf_Next) {
resolveDefaults(nf, nf->nf_FilterBase);
if (NFGlob)
resolveDefaults(nf, NFGlob->nf_FilterBase);
}
/*
* This isn't really necessary, we should have resolved them
* all above, but if we use groups for something else in the
* future we need to make sure the recursive grouprefs are
* resolved.
*/
for (nf = GRBase; nf; nf = nf->nf_Next) {
resolveDefaults(nf, nf->nf_FilterBase);
}
}
}
}
void
resolveGroupList(const char *label, Node *scan)
{
for (; scan; scan = scan->no_Next) {
if (scan->no_Value == 2) {
NewsFeed *gl;
for (gl = GRBase; gl; gl = gl->nf_Next) {
if (strcmp(scan->no_Name, gl->nf_Label) == 0) {
scan->no_Data = gl;
break;
}
}
if (gl == NULL) {
logit(LOG_CRIT, "Error: grouplist %s does not exist (from %s)\n", scan->no_Name, label);
}
}
}
}
void
AltFeed(NewsFeed *nf, FILE *fi)
{
char buf[MAXGNAME+256];
Node **pgNode = &nf->nf_GroupAcceptBase;
Node **psNode = &nf->nf_SpamBase;
Node **prNode = &nf->nf_RequireGroupsBase;
while (*pgNode)
pgNode = &(*pgNode)->no_Next;
while (*psNode)
psNode = &(*psNode)->no_Next;
while (fgets(buf, sizeof(buf), fi) != NULL) {
char *s1 = strtok(buf, " \t\n");
char *s2 = strtok(NULL, " \t\n");
if (s1 == NULL)
continue;
if (s2 == NULL)
continue;
if (strcmp(s1, "addgroup") == 0) {
(void)MakeNodeAppList(&pgNode, &NFMemPool, s2, 1);
} else if (strcmp(s1, "delgroup") == 0) {
(void)MakeNodeAppList(&pgNode, &NFMemPool, s2, -1);
} else if (strcmp(s1, "requiregroup") == 0) {
(void)MakeNodeAppList(&prNode, &NFMemPool, s2, 1);
} else if (strcmp(s1, "addspam") == 0) {
(void)MakeNodeAppList(&psNode, &NFMemPool, s2, 1);
} else if (strcmp(s1, "delspam") == 0) {
(void)MakeNodeAppList(&psNode, &NFMemPool, s2, -1);
} else if (strcmp(s1, "delgroupany") == 0) {
(void)MakeNodeAppList(&pgNode, &NFMemPool, s2, -2);
} else if (strcmp(s1, "maxsize") == 0) {
nf->nf_MaxArtSize = bsizetol(s2);
} else if (strcmp(s1, "minsize") == 0) {
nf->nf_MinArtSize = bsizetol(s2);
}
}
*pgNode = NULL;
*psNode = NULL;
*prNode = NULL;
}
int
TooNear(time_t t)
{
time_t now = time(NULL);
int32 dt = (int32)(now - t);
if (dt > -10 && dt < 10)
return(1);
return(0);
}
int
feedSpamFeedOK(int feed, int article)
{
if (! feed) {
return(0);
}
if (feed == 1) {
if (article == 1) {
return(1);
}
return(0);
}
if (feed == 2) {
if (article == 0) {
return(1);
}
return(0);
}
return(0);
}
/*
* Set the starting values for GLOBAL - these can be overwritten
*/
void
setLinkGLOBAL(NewslinkInfo *nf)
{
nf->li_Port = 119;
nf->li_StartDelay = 0;
nf->li_TransmitBuf = 0;
nf->li_ReceiveBuf = 0;
nf->li_QoS = 0;
nf->li_MaxParallel = 2;
nf->li_NoStream = 0;
nf->li_DelayFeed = 0;
nf->li_DelayInFeed = 0;
nf->li_RealTime = 0;
nf->li_Notify = 0;
nf->li_MaxQueueFile = 0;
nf->li_HeadFeed = 0;
nf->li_PreserveBytes = 0;
nf->li_GenLines = 0;
nf->li_Check = 1;
nf->li_Compress = 0;
nf->li_QueueSkip = 0;
nf->li_ArticleStat = 0;
nf->li_MaxStream = MAXSTREAM;
nf->li_Priority = 0;
nf->li_LogArts = NULL;
nf->li_Hours = NULL;
}
/*
* Set the initial values for each newsfeed link entry. These will
* all get overwritten by the GLOBAL or user specified entries.
*
* They are set to -1, because we need to use 0
*/
void
setLinkStart(NewslinkInfo *nf)
{
nf->li_Port = -1;
nf->li_StartDelay = -1;
nf->li_TransmitBuf = -1;
nf->li_ReceiveBuf = -1;
nf->li_QoS = -1;
nf->li_BindAddress = NULL;
nf->li_MaxParallel = -1;
nf->li_NoStream = -1;
nf->li_DelayFeed = -1;
nf->li_DelayInFeed = -1;
nf->li_RealTime = -1;
nf->li_Notify = -1;
nf->li_MaxQueueFile = -1;
nf->li_HeadFeed = -1;
nf->li_PreserveBytes = -1;
nf->li_GenLines = -1;
nf->li_Check = -1;
nf->li_Compress = -1;
nf->li_QueueSkip = -1;
nf->li_ArticleStat = -1;
nf->li_MaxStream = -1;
nf->li_Priority = -1;
nf->li_LogArts = NULL;
nf->li_Hours = NULL;
}
void
setLinkDefaults(NewslinkInfo *nf, NewslinkInfo *gl)
{
if (nf->li_Port < 0 && gl->li_Port >= 0)
nf->li_Port = gl->li_Port;
if (nf->li_StartDelay < 0 && gl->li_StartDelay >= 0)
nf->li_StartDelay = gl->li_StartDelay;
if (nf->li_TransmitBuf < 0 && gl->li_TransmitBuf >= 0)
nf->li_TransmitBuf = gl->li_TransmitBuf;
if (nf->li_ReceiveBuf < 0 && gl->li_ReceiveBuf >= 0)
nf->li_ReceiveBuf = gl->li_ReceiveBuf;
if (nf->li_QoS < 0 && gl->li_QoS >= 0)
nf->li_QoS = gl->li_QoS;
if (!nf->li_BindAddress && gl->li_BindAddress)
nf->li_BindAddress = gl->li_BindAddress;
if (nf->li_MaxParallel < 0 && gl->li_MaxParallel >= 0)
nf->li_MaxParallel = gl->li_MaxParallel;
if (nf->li_NoStream < 0 && gl->li_NoStream >= 0)
nf->li_NoStream = gl->li_NoStream;
if (nf->li_DelayFeed < 0 && gl->li_DelayFeed >= 0)
nf->li_DelayFeed = gl->li_DelayFeed;
if (nf->li_DelayInFeed < 0 && gl->li_DelayInFeed >= 0)
nf->li_DelayInFeed = gl->li_DelayInFeed;
if (nf->li_RealTime < 0 && gl->li_RealTime >= 0)
nf->li_RealTime = gl->li_RealTime;
if (nf->li_Notify < 0 && gl->li_Notify >= 0)
nf->li_Notify = gl->li_Notify;
if (nf->li_MaxQueueFile < 0 && gl->li_MaxQueueFile >= 0)
nf->li_MaxQueueFile = gl->li_MaxQueueFile;
if (nf->li_HeadFeed < 0 && gl->li_HeadFeed >= 0)
nf->li_HeadFeed = gl->li_HeadFeed;
if (nf->li_PreserveBytes < 0 && gl->li_PreserveBytes >= 0)
nf->li_PreserveBytes = gl->li_PreserveBytes;
if (nf->li_GenLines < 0 && gl->li_GenLines >= 0)
nf->li_GenLines = gl->li_GenLines;
if (nf->li_Check < 0 && gl->li_Check >= 0)
nf->li_Check = gl->li_Check;
if (nf->li_Compress < 0 && gl->li_Compress >= 0)
nf->li_Compress = gl->li_Compress;
if (nf->li_QueueSkip < 0 && gl->li_QueueSkip >= 0)
nf->li_QueueSkip = gl->li_QueueSkip;
if (nf->li_ArticleStat < 0 && gl->li_ArticleStat >= 0)
nf->li_ArticleStat = gl->li_ArticleStat;
if (nf->li_MaxStream < 0 && gl->li_MaxStream >= 0)
nf->li_MaxStream = gl->li_MaxStream;
if (nf->li_Priority < 0 && gl->li_Priority >= 0)
nf->li_Priority = gl->li_Priority;
if (!nf->li_LogArts && gl->li_LogArts)
nf->li_LogArts = gl->li_LogArts;
if (!nf->li_Hours && gl->li_Hours)
nf->li_Hours = gl->li_Hours;
}
/*
* resolveDefaults() - resolve groupref recursion for simple defaults.
*
* We have to recurse our resolution. This code resolves simple
* defaults: Distribution patterns, integer values, and so forth.
*/
void
resolveDefaults(NewsFeed *nf, Node *no)
{
nf->nf_Resolved = 1;
while (no) {
if (no->no_Value == 2 && no->no_Data != NULL) {
NewsFeed *gl = no->no_Data;
if (gl->nf_Resolved == 0)
resolveDefaults(gl, gl->nf_FilterBase);
if (!nf->nf_MaxCrossPost && gl->nf_MaxCrossPost)
nf->nf_MaxCrossPost = gl->nf_MaxCrossPost;
if (!nf->nf_MinCrossPost && gl->nf_MinCrossPost)
nf->nf_MinCrossPost = gl->nf_MinCrossPost;
if (!nf->nf_MaxArtSize && gl->nf_MaxArtSize)
nf->nf_MaxArtSize = gl->nf_MaxArtSize;
if (!nf->nf_MinArtSize && gl->nf_MinArtSize)
nf->nf_MinArtSize = gl->nf_MinArtSize;
if (!nf->nf_MaxPathLen && gl->nf_MaxPathLen)
nf->nf_MaxPathLen = gl->nf_MaxPathLen;
if (!nf->nf_MinPathLen && gl->nf_MinPathLen)
nf->nf_MinPathLen = gl->nf_MinPathLen;
if (!nf->nf_MaxConnect && gl->nf_MaxConnect)
nf->nf_MaxConnect = gl->nf_MaxConnect;
if (!nf->nf_MaxInboundRate && gl->nf_MaxInboundRate)
nf->nf_MaxInboundRate = zallocStr(&NFMemPool, gl->nf_MaxInboundRate);
if (nf->nf_ArtTypes == NULL && gl->nf_ArtTypes)
nf->nf_ArtTypes = gl->nf_ArtTypes;
if (nf->nf_Dist == NULL && gl->nf_Dist)
zappendStr(&NFMemPool, &nf->nf_Dist, NULL, gl->nf_Dist);
if (nf->nf_NoDist == NULL && gl->nf_NoDist)
zappendStr(&NFMemPool, &nf->nf_NoDist, NULL, gl->nf_NoDist);
if (!nf->nf_PerLineFlushOpt && gl->nf_PerLineFlushOpt)
nf->nf_PerLineFlushOpt = gl->nf_PerLineFlushOpt;
if (!nf->nf_NoMisMatch && gl->nf_NoMisMatch)
nf->nf_NoMisMatch = gl->nf_NoMisMatch;
if (!nf->nf_SpamFeedOpt && gl->nf_SpamFeedOpt)
nf->nf_SpamFeedOpt = gl->nf_SpamFeedOpt;
if (!nf->nf_ThrottleDelay && gl->nf_ThrottleDelay)
nf->nf_ThrottleDelay = gl->nf_ThrottleDelay;
if (!nf->nf_ThrottleLines && gl->nf_ThrottleLines)
nf->nf_ThrottleLines = gl->nf_ThrottleLines;
if (!nf->nf_ReadOnly && gl->nf_ReadOnly)
nf->nf_ReadOnly = gl->nf_ReadOnly;
if (!nf->nf_WhereIs && gl->nf_WhereIs)
nf->nf_WhereIs = gl->nf_WhereIs;
if (!nf->nf_IncomingPriority && gl->nf_IncomingPriority)
nf->nf_IncomingPriority = gl->nf_IncomingPriority;
if (!nf->nf_PrecommitReject && gl->nf_PrecommitReject)
nf->nf_PrecommitReject = gl->nf_PrecommitReject;
setLinkDefaults(&nf->nf_LinkInfo, &gl->nf_LinkInfo);
}
no = no->no_Next;
}
}
int
recursiveScan(NewsFeed *feed, int off, const void *data, int (*callback)(Node *node, const void *data, int *pabort, int def), int def)
{
int r = def;
int abortMe = 0;
Node *node;
if (NFGlob && NFGlob != feed)
r = recursiveScan(NFGlob, off, data, callback, r);
for (node = *(Node **)((char *)feed + off); node; node = node->no_Next) {
if (node->no_Value == 2) {
if (++RecurCount == MAXRECURSION) {
if (RecurWarn == 0) {
RecurWarn = 1;
logit(LOG_EMERG, "Infinite recursion in dnewsfeeds file!");
}
} else {
if (node->no_Data != NULL)
r = recursiveScan(node->no_Data, off, data, callback, r);
}
--RecurCount;
} else {
r = callback(node, data, &abortMe, r);
}
if (abortMe)
break;
}
return(r);
}
int
cbWildCmpStopWhenFound(Node *node, const void *data, int *pabort, int def)
{
if (WildCmp(node->no_Name, (char *)data) == 0) {
def = node->no_Value;
*pabort = 1;
}
return(def);
}
int
cbWildCmpNoStop(Node *node, const void *data, int *pabort, int def)
{
if (WildCmp(node->no_Name, (char *)data) == 0) {
def = node->no_Value;
}
return(def);
}
LabelList *
FeedLinkLabelList(void)
{
NewsFeed *feed;
LabelList *ll = NULL;
LabelList *tl;
LabelList *ptl = NULL;
if (NFBase) {
for (feed = NFBase; feed; feed = feed->nf_Next) {
if (feed && feed->nf_LinkInfo.li_HostName) {
tl = zalloc(&NFMemPool, sizeof(LabelList));
tl->label = feed->nf_Label;
if (ptl)
ptl->next = tl;
ptl = tl;
if (ll == NULL)
ll = tl;
}
}
}
return(ll);
}
NewslinkInfo *
FeedLinkInfo(const char *hlabel)
{
NewsFeed *feed;
for (feed = NFBase; feed; feed = feed->nf_Next) {
if (strcmp(hlabel, feed->nf_Label) == 0)
break;
}
if (feed)
return(&feed->nf_LinkInfo);
else
return NULL;
}
int
FeedFilter(char *hlabel, const char *nglist, const char *npath, const char *dist, const char *artType, int bytes)
{
NewsFeed *nf = NULL;
nf = IFBase;
if (nf == NULL)
return(0);
if (feedQueryPaths(nf, npath, bytes, strtol(artType, NULL, 16)) == 0 &&
feedQueryGroups(nf, nglist) == 0 &&
feedQueryDists(nf, dist) == 0) {
return(1);
}
return(0);
}
int
FeedSpam(int which, const char *nglist, const char *npath, const char *dist, const char *artType, int bytes)
{
NewsFeed *nf = NULL;
if (which == 1)
nf = ISBase;
else if (which == 2)
nf = ESBase;
if (nf == NULL)
return(0);
if (feedQueryPaths(nf, npath, bytes, strtol(artType, NULL, 16)) == 0 &&
feedQueryGroups(nf, nglist) == 0 &&
feedQueryDists(nf, dist) == 0) {
return(1);
}
return(0);
}
/*
* Write the current max inbound rate into *bps
*/
void
FeedGetMaxInboundRate(const char *hlabel, int *bps)
{
NewsFeed *feed;
if ((feed = NFCache) == NULL) {
for (feed = NFBase; feed; feed = feed->nf_Next) {
if (strcmp(hlabel, feed->nf_Label) == 0)
break;
}
NFCache = feed;
}
if (! feed || ! feed->nf_MaxInboundRate || ! *(feed->nf_MaxInboundRate)) {
*bps = 0;
} else {
char *ptr = feed->nf_MaxInboundRate;
char *p2;
time_t now;
struct tm *tm;
/* Check for simple 24/7 rate limit - no colon */
if (! (p2 = strchr(ptr, ':'))) {
*bps = atoi(ptr);
} else {
/* It's a more complex rate limit */
int hours[24], bpstmp, hourtmp, hourendtmp, i;
/* Default is no limit for a given hour */
for (i = 0; i < 24; i++) {
hours[i] = 0;
}
/*
* Now parse a string of the format
* speed:hr-hr,hr;speed:hr,hr,hr-hr;etc
*
* I really, really, really, really hate writing character
* string parsers. But it's fast (I hope) and correct (I
* think). JG200103131808
*/
while (*ptr) {
/* We might be sitting on a colon from a previous iteration */
while (*ptr == ';') {
ptr++;
}
/* "speed:" */
bpstmp = atoi(ptr);
while (*ptr && *ptr != ':') {
ptr++;
}
ptr++;
/* Process hour range(s) */
while (*ptr && *ptr != ';') {
p2 = ptr;
while (*ptr && *ptr >= '0' && *ptr <= '9') {
ptr++;
}
if (ptr == p2) {
logit(LOG_ERR, "Newsfeed entry %s having trouble making sense of MaxInboundRate expression %s (A%02x)", hlabel, feed->nf_MaxInboundRate, *ptr);
}
hourtmp = atoi(p2);
if (*ptr == ',' || *ptr == ';' || ! *ptr) {
if (hourtmp < 0 || hourtmp > 23) {
logit(LOG_ERR, "Newsfeed entry %s having trouble making sense of MaxInboundRate expression %s (B%d)", hlabel, feed->nf_MaxInboundRate, hourtmp);
} else {
hours[hourtmp] = bpstmp;
}
if (*ptr != ';') {
ptr++;
}
} else if (*ptr == '-') {
/* Move past dash */
ptr++;
p2 = ptr;
/* Scan past second number */
while (*ptr && *ptr >= '0' && *ptr <= '9') {
ptr++;
}
if (ptr == p2) {
logit(LOG_ERR, "Newsfeed entry %s having trouble making sense of MaxInboundRate expression %s (C%02x)", hlabel, feed->nf_MaxInboundRate, *ptr);
}
hourendtmp = atoi(p2);
if (hourtmp < 0 || hourtmp > 23 || hourendtmp < 0 || hourendtmp > 23 || hourtmp >= hourendtmp) {
logit(LOG_ERR, "Newsfeed entry %s having trouble making sense of MaxInboundRate expression %s (D%d/%d)", hlabel, feed->nf_MaxInboundRate, hourtmp, hourendtmp);
} else {
for (i = hourtmp; i <= hourendtmp; i++) {
hours[i] = bpstmp;
}
}
if (*ptr != ';') {
ptr++;
}
} else {
logit(LOG_ERR, "Newsfeed entry %s having trouble making sense of MaxInboundRate expression %s (E%02x)", hlabel, feed->nf_MaxInboundRate, *ptr);
ptr++;
}
}
}
/*
* What an incredible amount of putzing around that was.
* Now we ask the system what hour (0-23) it is, and set *bps
* accordingly
*/
time(&now);
tm = localtime(&now);
*bps = hours[tm->tm_hour];
}
}
}
void
DumpAllFeedInfo(FILE *fo)
{
NewsFeed *nf = NULL;
fprintf(fo, "** GLOBAL **\n");
DumpFeedInfo(fo, NFGlob->nf_Label);
DumpFeedInfo(fo, "ISPAM");
DumpFeedInfo(fo, "ESPAM");
DumpFeedInfo(fo, "IFILTER");
fprintf(fo, "** GROUPREFS **\n");
for (nf = GRBase; nf; nf = nf->nf_Next) {
DumpFeedInfo(fo, nf->nf_Label);
}
fprintf(fo, "** LABELS **\n");
for (nf = NFBase; nf; nf = nf->nf_Next) {
DumpFeedInfo(fo, nf->nf_Label);
}
}
void
DumpFeedInfo(FILE *fo, char *label)
{
NewsFeed *nf = NULL;
if (strcmp(label, "ISPAM") == 0)
nf = ISBase;
else if (strcmp(label, "ESPAM") == 0)
nf = ESBase;
else if (strcmp(label, "IFILTER") == 0)
nf = IFBase;
else for (nf = NFBase; nf; nf = nf->nf_Next) {
if (strcmp(label, nf->nf_Label) == 0)
break;
}
if (nf == NULL)
for (nf = GRBase; nf; nf = nf->nf_Next)
if (strcmp(label, nf->nf_Label) == 0)
break;
if (nf == NULL)
return;
fprintf(fo, "label %s\n", nf->nf_Label);
fprintf(fo, " MaxCrossPost : %d\n", nf->nf_MaxCrossPost);
fprintf(fo, " MinCrossPost : %d\n", nf->nf_MinCrossPost);
fprintf(fo, " MaxArtSize : %d\n", nf->nf_MaxArtSize);
fprintf(fo, " MinArtSize : %d\n", nf->nf_MinArtSize);
fprintf(fo, " MaxPathLen : %d\n", nf->nf_MaxPathLen);
fprintf(fo, " MinPathLen : %d\n", nf->nf_MinPathLen);
fprintf(fo, " MaxConnect : %d\n", nf->nf_MaxConnect);
fprintf(fo, " MaxInboundRate : %s\n", nf->nf_MaxInboundRate ?
nf->nf_MaxInboundRate : "-1");
fprintf(fo, " Dist : %s\n", nf->nf_Dist ? nf->nf_Dist : "NONE");
fprintf(fo, " NoDist : %s\n", nf->nf_NoDist ? nf->nf_NoDist : "NONE");
fprintf(fo, " PerLineFlushOpt: %d\n", nf->nf_PerLineFlushOpt);
fprintf(fo, " NoMisMatch : %d\n", nf->nf_NoMisMatch);
fprintf(fo, " SpamFeedOpt : %d\n", nf->nf_SpamFeedOpt);
fprintf(fo, " Resolved : %d\n", nf->nf_Resolved);
fprintf(fo, " ThrottleDelay : %d\n", nf->nf_ThrottleDelay);
fprintf(fo, " ThrottleLines : %d\n", nf->nf_ThrottleLines);
fprintf(fo, " ReadOnly : %d\n", nf->nf_ReadOnly);
fprintf(fo, " WhereIs : %d\n", nf->nf_WhereIs);
fprintf(fo, " HashFeed : %d-%d/%d\n", nf->nf_HashFeed.hf_Begin,
nf->nf_HashFeed.hf_End,
nf->nf_HashFeed.hf_Mod);
fprintf(fo, " IncomingPriority: %d\n", nf->nf_IncomingPriority);
fprintf(fo, " PrecommitReject: %d\n", nf->nf_PrecommitReject);
#if USE_OFFER_FILTER
fprintf(fo, " OfferFilter : ");
{
Node *n;
for (n = nf->nf_OfferFilterBase; n != NULL; n = n->no_Next)
fprintf(fo, "%s%s,",
(n->no_Value == 1) ? "" : /* not filtered */
(n->no_Value == 0) ? "!" : "?",
n->no_Name);
fprintf(fo, "\n");
}
#endif /* USE_OFFER_FILTER */
fprintf(fo, " ArtTypes : ");
{
ArtTypeList *at;
for (at = nf->nf_ArtTypes; at != NULL; at = at->next) {
fprintf(fo, "%s%08x,", at->negate ? "!": "", at->arttype);
if (at->next != NULL)
fprintf(fo, ",");
}
fprintf(fo, "\n");
}
fprintf(fo, " GroupAccept : ");
{
Node *n;
for (n = nf->nf_GroupAcceptBase; n != NULL; n = n->no_Next)
fprintf(fo, "%s%s,",
(n->no_Value == -2) ? "@" :
(n->no_Value == -1) ? "!" :
(n->no_Value == 0) ? "" : "?",
n->no_Name);
fprintf(fo, "\n");
}
fprintf(fo, " Hostname : %s\n", nf->nf_LinkInfo.li_HostName ?
nf->nf_LinkInfo.li_HostName : "NONE");
fprintf(fo, " Port : %d\n", nf->nf_LinkInfo.li_Port);
fprintf(fo, " StartDelay : %d\n", nf->nf_LinkInfo.li_StartDelay);
fprintf(fo, " Headfeed : %d\n", nf->nf_LinkInfo.li_HeadFeed);
fprintf(fo, " TransmitBuf : %d\n", nf->nf_LinkInfo.li_TransmitBuf);
fprintf(fo, " ReceiveBuf : %d\n", nf->nf_LinkInfo.li_ReceiveBuf);
#ifdef IP_TOS
fprintf(fo, " QoS : %d\n", nf->nf_LinkInfo.li_QoS);
#endif
fprintf(fo, " BindAddress : %s\n", nf->nf_LinkInfo.li_BindAddress ?
nf->nf_LinkInfo.li_BindAddress : "ALL");
fprintf(fo, " MaxParallel : %d\n", nf->nf_LinkInfo.li_MaxParallel);
fprintf(fo, " NoStream : %d\n", nf->nf_LinkInfo.li_NoStream);
fprintf(fo, " DelayFeed : %d\n", nf->nf_LinkInfo.li_DelayFeed);
fprintf(fo, " DelayInFeed : %d\n", nf->nf_LinkInfo.li_DelayInFeed);
fprintf(fo, " RealTime : %d\n", nf->nf_LinkInfo.li_RealTime);
fprintf(fo, " Notify : %d\n", nf->nf_LinkInfo.li_Notify);
fprintf(fo, " MaxQueueFile : %d\n", nf->nf_LinkInfo.li_MaxQueueFile);
fprintf(fo, " PreserveBytes : %d\n", nf->nf_LinkInfo.li_PreserveBytes);
fprintf(fo, " GenLines : %d\n", nf->nf_LinkInfo.li_GenLines);
fprintf(fo, " Check : %d\n", nf->nf_LinkInfo.li_Check);
fprintf(fo, " MaxStream : %d\n", nf->nf_LinkInfo.li_MaxStream);
fprintf(fo, " Priority : %d\n", nf->nf_LinkInfo.li_Priority);
fprintf(fo, " Compress : %d\n", nf->nf_LinkInfo.li_Compress);
fprintf(fo, " QueueSkip : %d\n", nf->nf_LinkInfo.li_QueueSkip);
fprintf(fo, " ArticleStat : %d\n", nf->nf_LinkInfo.li_ArticleStat);
fprintf(fo, " Logarts : %s\n", nf->nf_LinkInfo.li_LogArts ?
nf->nf_LinkInfo.li_LogArts : "NONE");
fprintf(fo, " Hours : %s\n", nf->nf_LinkInfo.li_Hours ?
nf->nf_LinkInfo.li_Hours : "ALL");
fprintf(fo, "end\n");
fprintf(fo, "#------------------------------------------------------------\n");
}
syntax highlighted by Code2HTML, v. 0.9.1