📄 art.c
字号:
IOError("logging site"); syslog(L_ERROR, "%s cant write log_site %m", LogName); clearerr(Log); } sp->Sendit = TRUE; sp->Seenit = TRUE; if (sp->Master != NOSITE) Sites[sp->Master].Seenit = TRUE; } if (putc('\n', Log) == EOF || (!BufferedLogs && fflush(Log)) || ferror(Log)) { syslog(L_ERROR, "%s cant write log_end %m", LogName); clearerr(Log); } /* Handle funnel sites. */ for (sp = Sites, i = nSites; --i >= 0; sp++) if (sp->Sendit && sp->Funnel != NOSITE) { sp->Sendit = FALSE; funnel = &Sites[sp->Funnel]; funnel->Sendit = TRUE; if (funnel->FNLwantsnames) { bp = &funnel->FNLnames; p = &bp->Data[bp->Used]; if (bp->Used) { *p++ = ' '; bp->Used++; } bp->Used += strlen(strcpy(p, sp->Name)); } }}/*** Build up the overview data.*/STATIC voidARTmakeoverview(Data) ARTDATA *Data;{ static char SEP[] = "\t"; static char COLONSPACE[] = ": "; static BUFFER Overview; register ARTOVERFIELD *fp; register ARTHEADER *hp; register char *p; register int i; /* Setup. */ if (Overview.Data == NULL) Overview.Data = NEW(char, 1); Data->Overview = &Overview; BUFFset(&Overview, Xref.Data + Xrefbase + 1, Xref.Used - (Xrefbase + 2)); for (i = Overview.Left, p = Overview.Data; --i >= 0; p++) if (*p == '.' || *p == ':') *p = '/'; if (ARTfields == NULL) { /* User error. */ return; } /* Write the data, a field at a time. */ for (fp = ARTfields; fp->Header; fp++) { BUFFappend(&Overview, SEP, STRLEN(SEP)); hp = fp->Header; if (!hp->Found) continue; if (fp->NeedHeader) { BUFFappend(&Overview, hp->Name, hp->Size); BUFFappend(&Overview, COLONSPACE, STRLEN(COLONSPACE)); } i = Overview.Left; BUFFappend(&Overview, hp->Value, hp->Length); for (p = &Overview.Data[i]; i < Overview.Left; p++, i++) if (*p == '\t' || *p == '\n') *p = ' '; }}/*** This routine is the heart of it all. Take a full article, parse it,** file or reject it, feed it to the other sites. Return the NNTP** message to send back.*/STRINGARTpost(cp, Replic, ihave) CHANNEL *cp; BUFFER *Replic; char *ihave;{ static char errNOSPACE[] = NNTP_RESENDIT_NOSPACE; static char errNOHIST[] = NNTP_RESENDIT_NOHIST; static BUFFER Files; static BUFFER Header; static char buff[SPOOLNAMEBUFF]; register char *p; register int i; register int j; register NEWSGROUP *ngp; register NEWSGROUP **ngptr; register int *isp; register SITE *sp; ARTDATA Data; BOOL Approved; BOOL Accepted; BOOL LikeNewgroup; BOOL CrossPosted; BOOL ToGroup; BOOL GroupMissing; BUFFER *article; char linkname[SPOOLNAMEBUFF]; char **groups; char **hops; int hopcount; char **distributions; STRING error; char ControlWord[SMBUF]; int ControlHeader; /* Preliminary clean-ups. */ article = &cp->In; Data.MessageID = ihave; error = ARTclean(article, &Data); /* Fill in other Data fields. */ Data.Poster = HDR(_sender); if (*Data.Poster == '\0') Data.Poster = HDR(_from); Data.Replyto = HDR(_reply_to); if (*Data.Replyto == '\0') Data.Replyto = HDR(_from); hops = ARTparsepath(HDR(_path), &hopcount);#if defined(DO_IPADDR_LOG) Data.Feedsite = RChostname(cp); if (Data.Feedsite == NULL) Data.Feedsite = CHANname(cp);#else Data.Feedsite = hops && hops[0] ? hops[0] : CHANname(cp);#endif /* defined(DO_IPADDRLOG) */ Data.FeedsiteLength = strlen(Data.Feedsite); (void)sprintf(Data.TimeReceived, "%lu", Now.time); Data.TimeReceivedLength = strlen(Data.TimeReceived); /* A duplicate? */ if (error == NULL && HIShavearticle(Data.MessageID)) error = "Duplicate article"; /* Now see if we got an error in the article. */ if (error != NULL) { (void)sprintf(buff, "%d %s", NNTP_REJECTIT_VAL, error); ARTlog(&Data, ART_REJECT, buff); ARTreject(buff, article); return buff; } /* Stash a copy of the Newsgroups header. */ p = HDR(_newsgroups); i = strlen(p); if (Header.Data == NULL) { Header.Size = i; Header.Data = NEW(char, Header.Size + 1); } else if (Header.Size <= i) { Header.Size = i + 16; RENEW(Header.Data, char, Header.Size + 1); } (void)strcpy(Header.Data, p); Data.Newsgroups = Header.Data; Data.NewsgroupsLength = i; /* If we limit what distributions we get, see if we want this one. */ p = HDR(_distribution); distributions = *p ? CommaSplit(p) : NULL; if (distributions) { DISTparse(distributions, &Data); if (ME.Distributions && !DISTwantany(ME.Distributions, distributions)) { (void)sprintf(buff, "%d Unwanted distribution \"%s\"", NNTP_REJECTIT_VAL, MaxLength(distributions[0], distributions[0])); ARTlog(&Data, ART_REJECT, buff); DISPOSE(distributions); ARTreject(buff, article); return buff; } } else { Data.Distribution = "?"; Data.DistributionLength = 1; } /* Clear all groups and sites -- assume nobody gets the article. */ for (i = nGroups, ngp = Groups; --i >= 0; ngp++) ngp->PostCount = 0; for (i = nSites, sp = Sites; --i >= 0; sp++) { sp->Sendit = FALSE; sp->Seenit = FALSE; sp->FNLnames.Used = 0; sp->ng = NULL; } /* Parse the Control or Also-Control header. */ groups = NGsplit(HDR(_newsgroups)); for (i = 0; groups[i] != NULL; i++) continue; Data.Groupcount = i; if (HDR(_control)[0] != '\0') ControlHeader = _control; else if (HDR(_alsocontrol)[0] != '\0') ControlHeader = _alsocontrol; else { ControlHeader = -1; LikeNewgroup = FALSE; } if (ControlHeader >= 0) { /* Nip off the first word into lowercase. */ (void)strncpy(ControlWord, HDR(ControlHeader), sizeof ControlWord); ControlWord[sizeof ControlWord - 1] = '\0'; for (p = ControlWord; *p && !ISWHITE(*p); p++) if (CTYPE(isupper, *p)) *p = tolower(*p); *p = '\0'; LikeNewgroup = EQ(ControlWord, "newgroup") || EQ(ControlWord, "rmgroup"); /* Control messages to "foo.ctl" are treated as if they were * posted to "foo". I should probably apologize for all the * side-effects in the if. */ for (i = 0; (p = groups[i++]) != NULL; ) if ((j = strlen(p) - 4) > 0 && *(p += j) == '.' && p[1] == 'c' && p[2] == 't' && p[3] == 'l') *p = '\0'; } /* Loop over the newsgroups, see which ones we want, and get the * total space needed for the Xref line. At the end of this section * of code, j will have the needed length, the appropriate site * entries will have their Sendit and ng fields set, and GroupPointers * will have pointers to the relevant newsgroups. */ ToGroup = FALSE; p = HDR(_approved); Approved = *p != '\0'; ngptr = GroupPointers; j = 0; for (GroupMissing = Accepted = FALSE; (p = *groups) != NULL; groups++) { if (!RCcanpost(cp, p)) continue; if ((ngp = NGfind(p)) == NULL) { GroupMissing = TRUE; if (LikeNewgroup && Approved) { /* Newgroup/rmgroup being sent to a group that doesn't * exist. Assume it is being sent to the group being * created or removed, nd send the group to all sites that * would or would have had the group if it were created. */ ARTsendthegroup(*groups); Accepted = TRUE; }#if defined(DO_MERGE_TO_GROUPS) /* Try to collapse all "to" newsgroups. */ if (*p != 't' || *++p != 'o' || *++p != '.' || *++p == '\0') continue; ngp = NGfind("to"); ToGroup = TRUE; if ((sp = SITEfind(p)) != NULL) { SITEmark(sp, ngp); }#else continue;#endif /* defined(DO_MERGE_TO_GROUPS) */ } /* Ignore this group? */ if (ngp->Rest[0] == NF_FLAG_IGNORE) continue; /* Basic validity check. */ if (ngp->Rest[0] == NF_FLAG_MODERATED && !Approved) { (void)sprintf(buff, "%d Unapproved for \"%s\"", NNTP_REJECTIT_VAL, ngp->Name); ARTlog(&Data, ART_REJECT, buff); if (distributions) DISPOSE(distributions); ARTreject(buff, article); return buff; } /* Valid group, feed it to that group's sites. */ Accepted = TRUE; for (isp = ngp->Sites, i = ngp->nSites; --i >= 0; isp++) if (*isp >= 0) { sp = &Sites[*isp]; SITEmark(sp, ngp); } /* If it's excluded, don't file it. */ if (ngp->Rest[0] == NF_FLAG_EXCLUDED) continue; /* Expand aliases, mark the article as getting filed in the group. */ if (ngp->Alias != NULL) ngp = ngp->Alias; *ngptr++ = ngp; ngp->PostCount = 0; j += ngp->NameLength + 1 + MAXARTFNAME + 1; } /* Control messages not filed in "to" get filed only in controlname * or control. */ if (ControlHeader >= 0 && Accepted && !ToGroup) { FileGlue(buff, "control", '.', ControlWord); if ((ngp = NGfind(buff)) == NULL) ngp = NGfind(ARTctl); ngp->PostCount = 0; ngptr = GroupPointers; *ngptr++ = ngp; j = ngp->NameLength + 1 + MAXARTFNAME; for (isp = ngp->Sites, i = ngp->nSites; --i >= 0; isp++) if (*isp >= 0) { sp = &Sites[*isp]; SITEmark(sp, ngp); } } /* If !Accepted, then none of the article's newgroups exist in our * active file. Proper action is to drop the article on the floor. * If ngp == GroupPointers, then all the new articles newsgroups are * "j" entries in the active file. In that case, we have to file it * under junk so that downstream feeds can get it. */ if (!Accepted || ngptr == GroupPointers) { if (!Accepted) { (void)sprintf(buff, "%d Unwanted newsgroup \"%s\"", NNTP_REJECTIT_VAL, MaxLength(HDR(_newsgroups), HDR(_newsgroups))); ARTlog(&Data, ART_REJECT, buff);#if defined(DONT_WANT_TRASH)#if defined(DO_REMEMBER_TRASH) if (Mode == OMrunning && !HISwrite(&Data, "")) syslog(L_ERROR, "%s cant write history %s %m", LogName, Data.MessageID);#endif /* defined(DO_REMEMBER_TRASH) */ if (distributions) DISPOSE(distributions); ARTreject(buff, article); return buff;#else /* if !GroupMissing, then all the groups the article was posted * to have a flag of "x" in our active file, and therefore * we should throw the article away: if you have define * DO_WANT_TRASH, then you want all trash except that which * you explicitly excluded in your active file. */ if (!GroupMissing) { if (distributions) DISPOSE(distributions); ARTreject(buff, article); return buff; }#endif /* defined(DONT_WANT_TRASH) */ } ngp = NGfind(ARTjnk); *ngptr++ = ngp; ngp->PostCount = 0; j = STRLEN(ARTjnk) + 1 + MAXARTFNAME; /* Junk can be fed to other sites. */ for (isp = ngp->Sites, i = ngp->nSites; --i >= 0; isp++) if (*isp >= 0) { sp = &Sites[*isp]; SITEmark(sp, ngp); } } *ngptr = NULL; CrossPosted = ngptr > &GroupPointers[1] || AlwaysCrosspost; j++; if (Replic) j = Replic->Used + 1; /* Make sure the Xref buffer has room. */ Xref.Used = Xrefbase; if (Xref.Size <= j + Xrefbase + 2) { Xref.Size = j + Xrefbase + 2; RENEW(Xref.Data, char, Xref.Size + 1); } /* Make sure the filename buffer has room. */ if (Files.Data == NULL) { Files.Size = j; Files.Data = NEW(char, Files.Size + 1); } else if (Files.Size <= j) { Files.Size = j; RENEW(Files.Data, char, Files.Size + 1); } /* Assign article numbers, fill in Xref buffer. */ if (Replic == NULL) ARTassignnumbers(); else ARTreplic(Replic, &CrossPosted); /* Optimize how we place the article on the disk. */ ARTsortfordisk(); /* Now we can file it. */ if (++ICDactivedirty >= ICD_SYNC_COUNT) { ICDwriteactive(); ICDactivedirty = 0; } Data.Name[0] = '\0'; p = Files.Data; *p = '\0'; for (i = 0; (ngp = GroupPointers[i]) != NULL; i++) { if (!ngp->PostCount) continue; ngp->PostCount = 0; if (Data.Name[0] == '\0') { /* Write the article the first time. */ (void)sprintf(Data.Name, "%s/%lu", ngp->Dir, ngp->Filenum); if (ARTwrite(Data.Name, article, &Data, CrossPosted) < 0 && (!MakeSpoolDirectory(ngp->Dir) || ARTwrite(Data.Name, article, &Data, CrossPosted) < 0)) { syslog(L_ERROR, "%s cant write %s %m", LogName, Data.Name); ARTlog(&Data, ART_REJECT, errNOSPACE); if (distributions) DISPOSE(distributions); ARTreject(buff, article); return errNOSPACE; } p += strlen(strcpy(p, Data.Name)); Data.NameLength = strlen(Data.Name); } else { /* Link to the main article. */ (void)sprintf(linkname, "%s/%lu", ngp->Dir, ngp->Filenum); if (link(Data.Name, linkname) < 0 && (!MakeSpoolDirectory(ngp->Dir) || link(Data.Name, linkname) < 0)) {#if defined(DONT_HAVE_SYMLINK) IOError("linking article"); syslog(L_ERROR, "%s cant link %s and %s %m", LogName, Data.Name, linkname); continue;#else /* Try to make a symbolic link to the full pathname. */ FileGlue(buff, SPOOL, '/', Data.Name); if (symlink(buff, linkname) < 0 && (!MakeSpoolDirectory(ngp->Dir) || symlink(buff, linkname) < 0)) { IOError("symlinking article"); syslog(L_ERROR, "%s cant symlink %s and %s %m", LogName, buff, linkname); continue; }#endif /* defined(DONT_HAVE_SYMLINK) */ } *p++ = ' '; p += strlen(strcpy(p, linkname)); } } /* Update history if we didn't get too many I/O errors above. */ if (Mode != OMrunning || !HISwrite(&Data, Files.Data)) { syslog(L_ERROR, "%s cant write history %s %m", LogName, Data.MessageID); ARTlog(&Data, ART_REJECT, errNOHIST); if (distributions) DISPOSE(distributions); ARTreject(buff, article); return errNOHIST; } /* If we just flushed the active (above), now flush history. */ if (ICDactivedirty == 0) HISsync(); /* We wrote the history, so modify it and save it for output. */ for (Data.Replic = Files.Data, p = (char *)Data.Replic; *p; p++) if (*p == ' ') *p = ','; Data.ReplicLength = p - Data.Replic; /* Start logging, then propagate the article. */ ARTlog(&Data, Accepted ? ART_ACCEPT : ART_JUNK, (char *)NULL);#if defined(DO_NNTPLINK_LOG) if (fprintf(Log, " (%s)", Data.Name) == EOF || ferror(Log)) { IOError("logging nntplink"); syslog(L_ERROR, "%s cant write log_nntplink %m", LogName); clearerr(Log); }#endif /* defined(DO_NNTPLINK_LOG) */ ARTpropagate(&Data, hops, hopcount, distributions); if (distributions) DISPOSE(distributions); /* Now that it's been written, process the control message. This has * a small window, if we get a new article before the newgroup message * has been processed. We could pause ourselves here, but it doesn't * seem to be worth it. */ if (Accepted) { if (ControlHeader >= 0) ARTcontrol(&Data, HDR(ControlHeader)); p = HDR(_supersedes); if (*p) ARTcancel(&Data, p, FALSE); } /* If we need the overview data, write it. */ if (NeedOverview) ARTmakeoverview(&Data); /* And finally, send to everyone who should get it */ for (sp = Sites, i = nSites; --i >= 0; sp++) if (sp->Sendit) SITEsend(sp, &Data); return NNTP_TOOKIT;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -