/* * 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 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); 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; 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); 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%ld,%ld\t%s\t%s\t%s\t%s\t%d\t%s\t%s\n", path, (long)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; 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(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); } /* * 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); } 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; if (t) NFGmtMin = t / 60; errno = 0; if (DebugOpt > 1) printf("Opening dnewsfeeds file: %s\n", PatLibExpand(DNewsfeedsPat)); fi = fopen(PatLibExpand(DNewsfeedsPat), "r"); if (fi == NULL) logit(LOG_CRIT, "%s: %s", PatLibExpand(DNewsfeedsPat), strerror(errno)); if (fi == NULL || (fstat(fileno(fi), &st) == 0 && st.st_mtime != NFMTime && !TooNear(st.st_mtime)) || force || NFMTime == 0 ) { char buf[MAXGNAME+256]; 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; int lineNo = 0; int inGroupDef = 0; FILE *altFi = NULL; while (fi && fgets(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 (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; 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); } } } 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; } } 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 (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; 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); 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, "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, "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) { nf->nf_LinkInfo.li_NoCheck = !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, 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; paNode = NULL; psaNode = NULL; pgNode = NULL; psNode = NULL; prNode = NULL; pfNode = NULL; 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; paNode = NULL; psaNode = NULL; pgNode = NULL; psNode = NULL; prNode = NULL; pfNode = NULL; 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) fclose(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); } 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); } /* * 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_MaxParallel = 2; nf->li_NoStream = 0; nf->li_DelayFeed = 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_NoCheck = 0; 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_BindAddress = NULL; nf->li_MaxParallel = -1; nf->li_NoStream = -1; nf->li_DelayFeed = -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_NoCheck = -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_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_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_NoCheck < 0 && gl->li_NoCheck >= 0) nf->li_NoCheck = gl->li_NoCheck; 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); 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); 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, " 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, " NoCheck : %d\n", nf->nf_LinkInfo.li_NoCheck); 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"); }