|
DataMuseum.dkPresents historical artifacts from the history of: DKUUG/EUUG Conference tapes |
This is an automatic "excavation" of a thematic subset of
See our Wiki for more about DKUUG/EUUG Conference tapes Excavated with: AutoArchaeologist - Free & Open Source Software. |
top - metrics - downloadIndex: T i
Length: 40008 (0x9c48) Types: TextFile Names: »ifuncs.c«
└─⟦9ae75bfbd⟧ Bits:30007242 EUUGD3: Starter Kit └─⟦373604645⟧ »EurOpenD3/news/bnews.2.11/src.tar.Z« └─⟦3beb569ac⟧ └─⟦this⟧ »src/ifuncs.c«
/* * This software is Copyright 1986, 1989 by Rick Adams. * * Permission is hereby granted to copy, reproduce, redistribute or * otherwise use this software as long as: there is no monetary * profit gained specifically from the use or reproduction or this * software, it is not sold, rented, traded or otherwise marketed, and * this copyright notice is included prominently in any copy * made. * * The author make no claims as to the fitness or correctness of * this software for any use whatsoever, and it is provided as is. * Any use of this software is at the user's own risk. * * ifuncs - functions used by inews. */ #ifdef SCCSID static char *SccsId = "@(#)ifuncs.c 2.78 10/29/89"; #endif /* SCCSID */ #include "iparams.h" #if defined(BSD4_2) || defined(USG) #include <fcntl.h> #endif /* BSD || USG */ extern long localize(); extern char *infpbuf; /*LINTLIBRARY*/ /* * Transmit this article to all interested systems. */ #ifdef u370 static struct srec srec; #endif /* u370 */ static struct hbuf h, hh; #ifdef MULTICAST #define MAXMCAST 20 #define MAXMCS 10 struct multicast { char mc_name[SBUFLEN]; /* "multi-cast" name */ short mc_syscnt; char mc_tosys[MAXMCAST][SBUFLEN]; } mcast[MAXMCS]; static int mccount; #endif /* MULTICAST */ long lseek(); #ifndef DBM char *histfile(); #endif /* !DBM */ #ifdef VMS /* * For VMS/Eunice there are no links: article was moved to firstbufname * before broadcast is reached. So we read it from there. */ extern char firstbufname[]; #endif #ifndef NFSCLIENT #ifndef GENERICPATH /*ARGSUSED*/ #endif /* !GENERICPATH */ broadcast(is_rnews) int is_rnews; { register char *hptr; register char *sptr; register FILE *fp; #ifndef u370 struct srec srec; #endif char sentbuf[LBUFLEN]; int nsent = 0; char *sentsys; #ifdef GENERICPATH int len; #endif /* GENERICPATH */ /* h is a local copy of the header we can scribble on */ #ifdef VMS fp = xfopen (firstbufname, "r"); #else fp = xfopen(ARTICLE, "r"); #endif if (hread(&h, fp, TRUE) == NULL) xerror("Cannot reread article"); (void) fclose(fp); (void) strcpy(sentbuf, h.ident); (void) strcat(sentbuf, " sent to "); sentsys = index(sentbuf, '\0'); nsent = 0; /* break path into list of systems. */ hptr = h.path; #ifdef GENERICPATH if (!is_rnews && STRNCMP(PATHSYSNAME, h.path, (len = strlen(PATHSYSNAME))) == 0 && index(NETCHRS, h.path[len])) (void) strcpy(h.path, &(h.path[len+1])); #endif /* GENERICPATH */ sptr = hptr = h.path; while ((hptr=strpbrk(hptr, NETCHRS)) != NULL) { *hptr++ = '\0'; sptr = hptr; } *sptr = '\0'; #ifdef MULTICAST mccount = 0; #endif /* MULTICAST */ /* loop once per system. */ s_openr(); while (s_read(&srec)) { char *dist = h.distribution; if (STRNCMP(srec.s_name, LOCALPATHSYSNAME, SNLN) == 0) continue; if (sptr = srec.s_nosend) { while (*sptr) { while (*sptr && *sptr != ',') sptr++; if (*sptr == ',') *sptr++ = '\0'; } *++sptr = '\0'; } hptr = h.path; while (*hptr != '\0') { if (STRNCMP(srec.s_name, hptr, SNLN) == 0) goto contin; if (sptr = srec.s_nosend) { while (*sptr != '\0') { if (STRNCMP(sptr, hptr, SNLN) == 0) goto contin; while (*sptr++) ; } } while (*hptr++ != '\0') ; } if (!ngmatch(h.nbuf, srec.s_nbuf)) continue; if (*dist == '\0') dist = "world"; if (!ngmatch(dist, srec.s_nbuf) && !ngmatch(srec.s_nbuf, dist)) continue; if (nsent) { hptr = sentsys; while ((sptr = index(hptr, ',')) != NULL) { *sptr = '\0'; if (STRCMP(hptr, srec.s_name) == 0) { *sptr = ','; goto contin; } *sptr++ = ','; for (hptr = sptr; isspace(*hptr); hptr++) ; } if (STRCMP(hptr, srec.s_name) == 0) continue; } /* now we've found a system to send this article to */ #ifdef MULTICAST if (index(srec.s_flags, 'M')) { /* do a "multi-cast" transmit */ register struct multicast *m; if (strlen(srec.s_name) >= SBUFLEN || strlen(srec.s_xmit) >= SBUFLEN) xerror("system name too long for multicast"); for (m = mcast; m < &mcast[mccount]; m++) if (STRCMP(srec.s_xmit, m->mc_name) == 0) break; if (m >= &mcast[MAXMCS]) xerror("Too many multicasts"); if (m == &mcast[mccount]) { mccount++; m->mc_syscnt = 0; strcpy(m->mc_name, srec.s_xmit); } if (m->mc_syscnt >= MAXMCAST) xerror("Too many systems for multicast"); strcpy(m->mc_tosys[m->mc_syscnt++], srec.s_name); } else { register struct multicast *m; register char **yptr; char *sysptrs[MAXMCAST]; int mc; mc = 0; for (m = mcast; m < &mcast[mccount]; m++) if (STRCMP(m->mc_name, srec.s_name) == 0) { yptr = sysptrs; while (mc < m->mc_syscnt) *yptr++ = m->mc_tosys[mc++]; break; } #ifdef VMS if (!transmit(&srec, xfopen(firstbufname,"r"), #else /* !VMS */ if (!transmit(&srec, xfopen(ARTICLE,"r"), #endif /* !VMS */ (STRNCMP(h.nbuf, "to.", 3) != 0), sysptrs, mc)) continue; } #else /* !MULTICAST */ #ifdef VMS if (!transmit(&srec, xfopen(firstbufname, "r"), #else /* !VMS */ if (!transmit(&srec, xfopen(ARTICLE, "r"), #endif /* !VMS */ (STRNCMP(h.nbuf, "to.", 3) != 0), (char **) NULL, FALSE)) continue; #endif /* !MULTICAST */ if (nsent) (void) strcat(sentbuf, ", "); (void) strcat(sentbuf, srec.s_name); nsent++; contin:; } if (nsent) log("%s", sentbuf); s_close(); } /* * Transmit file to system. */ #define PROC 0004 #ifndef MULTICAST /* ARGSUSED */ #endif /* !MULTICAST */ transmit(sp, ifp, maynotify, sysnames, mc) register struct srec *sp; register FILE *ifp; int maynotify; char **sysnames; int mc; { register FILE *ofp; register int c; register char *ptr, *size_ptr; char TRANS[BUFLEN]; char *argv[20]; register int pid; extern char firstbufname[]; /* A: afmt: the other machine runs an A news, so we xmit in A format */ int afmt = (index(sp->s_flags, 'A') != NULL); /* B: use B format (this is the default - don't use this letter elsewise). */ /* F: append name to file */ int appfile = (index(sp->s_flags, 'F') != NULL); /* L: local: don't send the article unless it was generated locally */ int local = ((ptr = index(sp->s_flags, 'L')) != NULL); /* H: interpolate history line into command, use existing file */ int history = (index(sp->s_flags, 'H') != NULL); /* m: moderated: only send if group is moderated */ int sendifmoderated = (index(sp->s_flags, 'm') != NULL); /* u: unmoderated: only send if group is unmoderated */ int sendifunmoderated = (index(sp->s_flags, 'u') != NULL); /* M: multi-cast: this is taken care of above, but don't reuse flag */ #ifdef MULTICAST /* O: multi-cast only, don't send article if not multicast hosts */ int multisend = (index(sp->s_flags, 'O') != NULL); #endif /* MULTICAST */ /* N: notify: don't send the article, just tell him we have it */ int notify = maynotify && (index(sp->s_flags, 'N') != NULL); /* S: noshell: don't fork a shell to execute the xmit command */ int noshell = (index(sp->s_flags, 'S') != NULL); /* U: useexist: use the -c option to uux to use the existing copy */ int useexist = (index(sp->s_flags, 'U') != NULL); /* I: append messageid to file. implies F flag */ int appmsgid = (index(sp->s_flags, 'I') != NULL); if (appmsgid && !appfile && !maynotify) { appmsgid = FALSE; appfile = TRUE; } /* allow specification based on size */ if ((size_ptr = strpbrk(sp->s_flags, "<>")) != NULL) { struct stat stbuf; if (fstat(fileno(ifp), &stbuf) == 0 && ( (*size_ptr == '>' && stbuf.st_size < atol(&size_ptr[1])) || (*size_ptr == '<' && stbuf.st_size > atol(&size_ptr[1])) )) { fclose(ifp); return FALSE; } } if (notify) appfile = appmsgid = FALSE; if (local && mode == PROC) { local = 0; while (isdigit(*++ptr)) local = local * 10 + *ptr - '0'; for (ptr = h.path; *ptr != '\0' && local >= 0; local--) while (*ptr++ != '\0') ; if (local < 0) { (void) fclose(ifp); return FALSE; } } /* ** Do not transmit to system specified in -x flag. */ if (not_here[0] && STRCMP(not_here, sp->s_name) == 0) { (void) fclose(ifp); return FALSE; } #ifdef DEBUG printf("Transmitting to '%s'\n", sp->s_name); #endif /* DEBUG */ #ifdef MULTICAST if (multisend && mc == 0) { (void) fclose(ifp); return FALSE; } #endif /* MULTICAST */ if ((sendifmoderated && is_mod[0] == '\0') || (sendifunmoderated && is_mod[0] != '\0')) { fclose(ifp); return FALSE; } if (appmsgid || (!appfile && !useexist && !history)) { if (!hread(&hh, ifp, TRUE)) { logerr("Bad header, not transmitting %s re %s to %s", hh.ident, hh.title, sp->s_name); (void) fclose(ifp); return FALSE; } if (hh.nbuf[0] == '\0') { fprintf(stderr, "Article not subscribed to by %s\n", sp->s_name); (void) fclose(ifp); return FALSE; } (void) sprintf(TRANS, "%s/.tmp/trXXXXXX", SPOOL); } if (notify) { char oldid[50]; (void) sprintf(hh.title, "ihave %s %s", hh.ident, PATHSYSNAME); (void) strcpy(hh.ctlmsg, hh.title); (void) strcpy(hh.numlines, "0"); (void) sprintf(hh.nbuf, "to.%s.ctl", sp->s_name); (void) strcpy(oldid, hh.ident); getident(&hh); log("tell %s about %s, notif. id %s", sp->s_name, oldid, hh.ident); } if (appfile || appmsgid) { if (firstbufname[0] == '\0') { extern char histline[]; localize("junk"); savehist(histline); xerror("No file name to xmit from"); } /* if filename is an absolute pathname, use it * if no filename given, tack system name onto BATCHDIR * otherwise, tack filename onto BATCHDIR * * also, tack ".ihave" onto ihave format files * in BATCHDIR (should this be done for all files?) */ if (sp->s_xmit[0] != '/') { if (sp->s_xmit[0] == '\0') (void) strcpy(TRANS, sp->s_name); else (void) strcpy(TRANS, sp->s_xmit); #ifdef LOGDIR sprintf(sp->s_xmit, "%s/%s/%s%s", logdir(HOME), #else /* !LOGDIR */ sprintf(sp->s_xmit, "%s/%s%s", #endif /* !LOGDIR */ BATCHDIR, TRANS, (appmsgid && !appfile) ? ".ihave" : ""); } ofp = fopen(sp->s_xmit, "a"); if (ofp == NULL) xerror("Cannot append to %s", sp->s_xmit); #if defined(F_SETFL) && !defined(MULTICAST) (void) fcntl(fileno(ofp), F_SETFL, O_APPEND); #endif /* F_SETFL */ #ifdef MULTICAST if (appfile && appmsgid) fprintf(ofp, "%s\t%s", firstbufname, hh.ident); else fprintf(ofp, "%s", appmsgid ? hh.ident : firstbufname); while (--mc >= 0) fprintf(ofp, " %s", *sysnames++); putc('\n', ofp); #else /* !MULTICAST */ if (appfile && appmsgid) fprintf(ofp, "%s\t%s\n", firstbufname, hh.ident); else fprintf(ofp, "%s\n", appmsgid ? hh.ident : firstbufname); #endif /* !MULTICAST */ (void) fclose(ofp); (void) fclose(ifp); return TRUE; } else if (useexist) { if (firstbufname[0] == '\0') xerror("No file name to xmit from"); if (*sp->s_xmit == '\0') #ifdef UXMIT (void) sprintf(bfr, UXMIT, sp->s_name, firstbufname); #else xerror("UXMIT not defined for U flag"); #endif else #ifdef MULTICAST makeargs(bfr, sp->s_xmit, firstbufname, sysnames, mc); #else (void) sprintf(bfr, sp->s_xmit, firstbufname); #endif (void) fclose(ifp); } else if (history) { extern char histline[]; if (*sp->s_xmit == '\0') xerror("no xmit command with H flag"); #ifdef MULTICAST makeargs(bfr, sp->s_xmit, histline, sysnames, mc); #else (void) sprintf(bfr, sp->s_xmit, histline); #endif } else { MKTEMP(TRANS); ofp = xfopen(TRANS, "w"); if (afmt) { #ifdef OLD fprintf(ofp, "A%s\n%s\n%s!%s\n%s\n%s\n", oident(hh.ident), hh.nbuf, PATHSYSNAME, hh.path, hh.subdate, hh.title); #else /* !OLD */ logerr("Must have OLD defined to use A flag for xmit"); return FALSE; #endif /* !OLD */ } else hwrite(&hh, ofp); if (!notify) while ((c = getc(ifp)) != EOF) putc(c, ofp); if (ferror(ofp)) xerror("write failed on transmit"); (void) fclose(ifp); (void) fclose(ofp); if (*sp->s_xmit == '\0') (void) sprintf(bfr, DFTXMIT, sp->s_name, TRANS); else #ifdef MULTICAST makeargs(bfr, sp->s_xmit, TRANS, sysnames, mc); #else /* !MULTICAST */ (void) sprintf(bfr, sp->s_xmit, TRANS); #endif /* !MULTICAST */ } /* At this point, the command to be executed is in bfr. */ if (noshell) { if (pid = vfork()) fwait(pid); else { (void) close(0); (void) open(TRANS, 0); ptr = bfr; for (pid = 0; pid < 19; pid++) { while (isspace(*ptr)) *ptr++ = 0; argv[pid] = ptr; while (!isspace(*++ptr) && *ptr) ; if (!*ptr) break; } argv[++pid] = 0; (void) setgid(gid); (void) setuid(uid); execvp(argv[0], argv); xerror("Can't execv %s", argv[0]); } } else { if (!history && sp->s_xmit[0] && !index(bfr, '<')) { char newcmd[LBUFLEN]; (void) sprintf(newcmd, "(%s) <%s", bfr, useexist ? firstbufname : TRANS); system(newcmd); } else system(bfr); } if (!appfile && !useexist && !history) (void) unlink(TRANS); (void) fclose(ifp); return TRUE; } #endif /* !NFSCLIENT */ #ifdef MULTICAST makeargs(buf, cmd, arg2, sysargs, sac) char *buf; char *cmd; char *arg2; register char **sysargs; int sac; { register char *p = cmd; register char *q; register ac = 0; register char *b = buf; q = p; do { if (q = index(q, ' ')) *q = '\0'; if (index(p, '%')) { switch (++ac) { case 1: while (--sac >= 0) { sprintf(b, p, *sysargs++); b = index(b, '\0'); } break; case 2: sprintf(b, p, arg2); b = index(b, '\0'); break; default: if (q) *q = ' '; xerror("badly formed command: %s", cmd); } } else { strcpy(b, p); b = index(b, '\0'); } if (q) { *q = ' '; p = q; while (isspace(*q)) q++; } } while (q != NULL); } #endif /* MULTICAST */ /* * Return TRUE if we have seen this file before, else FALSE. */ history(hp) struct hbuf *hp; { #ifdef DBM datum lhs, rhs; datum fetch(); #else /* !DBM */ register FILE *hfp; register char *p; #endif /* !DBM */ char lcident[BUFLEN]; extern char histline[]; #ifdef DEBUG fprintf(stderr,"history(%s)\n", hp->ident); #endif /* DEBUG */ /* * Make the article ID case insensitive. */ (void) strcpy(lcident, hp->ident); lcase(lcident); #ifndef NFSCLIENT idlock(lcident); #ifdef DBM initdbm(ARTFILE); lhs.dptr = lcident; lhs.dsize = strlen(lhs.dptr) + 1; rhs = fetch(lhs); if (rhs.dptr) { idunlock(); return(TRUE); } #else /* !DBM */ hfp = xfopen(histfile(lcident), "r"); while (fgets(bfr, BUFLEN, hfp) != NULL) { p = index(bfr, '\t'); if (p == NULL) p = index(bfr, '\n'); if (p != NULL) /* can happen if nulls in file */ *p = 0; lcase(bfr); if (STRCMP(bfr, lcident) == 0) { (void) fclose(hfp); idunlock(); #ifdef DEBUG fprintf(stderr,"history returns true\n"); #endif /* DEBUG */ return TRUE; } } (void) fclose(hfp); #endif /* !DBM */ #endif /* !NFSCLIENT */ histline[0] = '\0'; addhist(hp->ident); addhist("\t"); #ifndef NFSCLIENT #ifdef DEBUG fprintf(stderr,"history returns false\n"); #endif return FALSE; #else /* NFSCLIENT */ return TRUE; #endif /* NFSCLIENT */ } char histline[PATHLEN]; addhist(msg) char *msg; { (void) strcat(histline, msg); } savehist(hline) char *hline; { register FILE *hfp; register char *p; #ifdef DBM long fpos; #endif /* !DBM */ char tmphline[PATHLEN]; #ifndef DBM if (STRCMP((p = histfile(hline)), ARTFILE) != 0) { /* If the history subfile is accessible */ if ((hfp = xfopen(p, "a")) != NULL ) { /* If we can append */ fprintf(hfp, "%s\n", hline); /* Append */ (void) fclose(hfp); } else logerr("Unable to append to %s: %s", p, errmsg(errno)); } else #endif /* !DBM */ { hfp = xfopen(ARTFILE, "a"); (void) fseek(hfp, 0L, 2); /* Unisoft 5.1 doesn't seek to EOF on 'a' */ #ifdef DBM fpos = ftell(hfp); #endif /* !DBM */ fprintf(hfp, "%s\n", hline); (void) fclose(hfp); } #ifdef DBM { datum lhs, rhs; /* We assume that history has already been called, calling dbminit. */ strcpy(tmphline,hline); p = index(tmphline, '\t'); if (p) *p = 0; lcase(tmphline); lhs.dptr = tmphline; lhs.dsize = strlen(lhs.dptr) + 1; rhs.dptr = (char *)&fpos; rhs.dsize = sizeof fpos; store(lhs, rhs); } #endif /* DBM */ idunlock(); } /* * Save partial news. */ /* ARGSUSED */ newssave(fd, dummy) FILE *fd; char *dummy; { register FILE *tofd, *fromfd; char sfname[BUFLEN]; register int c; time_t tim; if (fd == NULL) fromfd = xfopen(INFILE, "r"); else fromfd = fd; (void) umask(savmask); (void) setgid(gid); (void) setuid(uid); (void) sprintf(sfname, "%s/%s", userhome, PARTIAL); if ((tofd = fopen(sfname, "a")) == NULL) xerror("Cannot save partial news in %s", sfname); (void) time(&tim); fprintf(tofd, "----- News saved at %s\n", arpadate(&tim)); while ((c = getc(fromfd)) != EOF) putc(c, tofd); (void) fclose(fromfd); (void) fclose(tofd); printf("News saved in %s\n", sfname); xxit(1); } /* * Handle dates in header. */ dates(hp) struct hbuf *hp; { time_t edt; if (*hp->subdate) { if (cgtdate(hp->subdate) < 0) { error("Cannot parse submittal date '%s'", hp->subdate); } } else { (void) time(&edt); (void) strcpy(hp->subdate, arpadate(&edt)); } } #define LOCKSIZE 128 char lockname[LOCKSIZE]; idlock(str) char *str; { register int i; register char *cp, *scp; char tempname[LOCKSIZE]; time_t now; struct stat sbuf; extern int errno; int fd; #ifdef VMS /* * The name here is because of the peculiar properties of version * numbers in Eunice. We eliminate any troublesome characters also. */ (void) sprintf(lockname, "/tmp/%.10s.l.1", str); for (cp = lockname; *cp; cp++) if (*cp == '/' || *cp == '[' || *cp == ']') *cp = '.'; while ((fd = creat(lockname, 0444)) < 0) { #else /* !VMS */ (void) strcpy(tempname, "/tmp/LTMP.XXXXXX"); MKTEMP(tempname); (void) strcpy(lockname, "/tmp/L"); i = strlen(lockname); cp = &lockname[i]; scp = str - 1; while (i++ < LOCKSIZE && *++scp != '\0') if (*scp == '/') /* slash screws up the open */ *cp++ = '.'; else *cp++ = *scp; *cp = '\0'; #ifdef FOURTEENMAX lockname[5 /* /tmp/ */ + 14] = '\0'; #endif #ifndef O_EXCL (void) strcpy(tempname, "/tmp/LTMP.XXXXXX"); MKTEMP(tempname); fd = creat(tempname, 0666); if (fd < 0) xerror("Cannot creat %s: errno %d", tempname, errno); while (link(tempname, lockname)) { #else /* O_EXCL */ while ((fd=open(lockname, O_EXCL|O_CREAT, 0)) < 0) { #endif /* O_EXCL */ #endif /* !VMS */ (void) time(&now); if (stat(lockname, &sbuf) < 0) xerror("Directory permission problem in /tmp"); if (sbuf.st_mtime + 10*60 < now) { (void) unlink(lockname); logerr("Article %s locked up", str); break; } log("waiting on lock for %s", lockname); sleep((unsigned)60); } (void) close(fd); #ifndef O_EXCL (void) unlink(tempname); #endif } idunlock() { (void) unlink(lockname); } /* * Put a unique name into header.ident. */ getident(hp) struct hbuf *hp; { long seqn; register FILE *fp; extern char *mydomain(); lock(); fp = xfopen(SEQFILE, "r"); (void) fgets(bfr, BUFLEN, fp); (void) fclose(fp); seqn = atol(bfr) + 1; /* * For Eunice, this breaks if SEQFILE is not in Unix format. */ fp = xfopen(SEQFILE, "r+w"); fprintf(fp, "%ld\n", seqn); (void) fclose(fp); unlock(); (void) sprintf(hp->ident, "<%ld@%s>", seqn, LOCALSYSNAME); } /* * Check that header.nbuf contains only valid newsgroup names; * exit with error if not valid. */ ngfcheck(user, isproc, is_mod_init) char *user; { register FILE * f; register char * cp; register int i, j; register int ngcount, okcount, dorecheck; register int pass; char * ngs[sizeof header.nbuf / 2]; char * ngsbug[sizeof header.nbuf / 2]; char uses[sizeof header.nbuf / 2]; char tbuf[sizeof header.nbuf]; char abuf[BUFLEN]; /* uses values ** 0 - haven't seen the newsgroup name anyplace ** 1 - write newsgroup name back into Newsgroup: line ** 2 - exact newsgroup name found in active or aliases file ** 3 - newsgroup name found as prefix in bugs file (but not #2) ** 4 - (2) plus name in bugs file */ #define NGUNSEEN 0 #define NGOK 1 #define NGALIAS 2 #define NGBUGS 3 #define NGABUGS 4 ngcount = 0; /* ** Split header.nbuf into constituent newsgroups. ** Zap "local" newsgroups of articles from remote sites. */ cp = tbuf; (void) strcpy(cp, header.nbuf); for ( ; ; ) { while (*cp == NGDELIM || *cp == ' ') ++cp; if (*cp == '\0') break; ngs[ngcount] = cp; ngsbug[ngcount] = (char *) NULL; uses[ngcount] = NGUNSEEN; do { ++cp; } while (*cp != '\0' && *cp != NGDELIM && *cp != ' '); if (*cp != '\0') *cp++ = '\0'; /* ** Check for local only distribution on incoming ** newsgroups. This might occur if someone posted to ** general,net.unix */ if (isproc && index(ngs[ngcount], '.') == NULL && index(header.nbuf, '.') != NULL) { logerr("Local group %s removed", ngs[ngcount]); continue; } uses[ngcount] = NGOK; /* it should go in "Newsgroups" line */ ++ngcount; } /* ** Check groups against active file. */ recheck: dorecheck = okcount = 0; is_mod[0] = '\0'; is_mod_file_okay = is_mod_init; rewind(actfp); clearerr(actfp); while (okcount < ngcount && fgets(bfr, BUFLEN, actfp) == bfr) { if ((cp = index(bfr, ' ')) == NULL) continue; /* strange line in input! */ *cp = '\0'; for (i = 0; i < ngcount; ++i) if (STRCMP(bfr, ngs[i]) == NGUNSEEN) { /* localize? */ /* newsgroup 1234567 1234567 X optional-fname */ /* cp + 01234567890123456789 */ if (cp[18] == ' ' && cp[19] != '\0') { int ok2post = 0; char buf2[BUFLEN], pbuf[MBUFLEN]; register char *cp2, *cp3; /* check for private groups * only sites listed in the file * specified can post */ cp2 = index(&cp[19], '\n'); if (cp2) *cp2 = '\0'; cp2 = pbuf; cp3 = header.path; while (*cp3 &&index(NETCHRS, *cp3) == 0) *cp2++ = *cp3++; *cp2 = '\0'; f = xfopen(&cp[19], "r"); while (!ok2post) { register int c; cp2 = buf2; while ((c=getc(f)) != EOF) { if (c == '\n') { *cp2 = '\0'; break; } *cp2++ = c; } if (c == EOF) break; if (STRCMP(pbuf, buf2) == 0) { ok2post = 1; break; } } (void) fclose(f); if (!ok2post) { logerr("%s is not authorized to post to %s", pbuf, ngs[i]); return TRUE; } } if (!isproc && cp[17] == 'n') { uses[i] = NGOK; } else { if (uses[i] < NGALIAS) uses[i] = NGALIAS; if (cp[17] == 'm') { strcpy(is_mod, bfr); if (!is_mod_file_okay) is_mod_file_okay = mod_file_okay(user,bfr); } ++okcount; } } } /* ** See what groups we can find in the bugs file */ if ((f = fopen(BUGFILE, "r")) != NULL) { while (fgets(bfr, BUFLEN, f) == bfr) { if (bfr[0] == '#') continue; cp = index(bfr, '\n'); *cp = '.'; for (i = 0; i < ngcount; ++i) { register int bfrlen = strlen(bfr); register int ngslen = strlen(ngs[i]); if (uses[i] == NGBUGS || uses[i] == NGABUGS) continue; if (PREFIX(ngs[i], bfr) || (bfrlen-1 == ngslen && !STRNCMP(ngs[i], bfr, ngslen))) { if (uses[i] == NGALIAS) uses[i] = NGABUGS; else { bfr[bfrlen-1] = '\0'; cp = "Bug group %s -> %s [ok]"; log(cp, ngs[i], bfr); if (ngsbug[i] == (char *) NULL) ngsbug[i] = ngs[i]; ngs[i] = AllocCpy(bfr); bfr[bfrlen-1] = '.'; uses[i] = NGBUGS; okcount++; dorecheck++; } } } } (void) fclose(f); } #ifdef ALWAYSALIAS okcount = 0; #endif /* ALWAYSALIAS */ /* ** Handle groups absent from active and bug_groups files. */ if (okcount < ngcount) { /* ** See if remaining groups are in our alias list. */ f = xfopen(ALIASES, "r"); while (okcount < ngcount && fscanf(f, "%s %s%*[^\n]", abuf, bfr) == 2) { if (abuf[0] == '#') continue; for (i = 0; i < ngcount; ++i) { #ifndef ALWAYSALIAS if (uses[i] > NGOK && uses[i] != NGBUGS) continue; #endif /* ALWAYSALIAS */ cp = (char *) NULL; if (uses[i] == NGBUGS) if (STRCMP(ngsbug[i], abuf) == 0) cp = "Aliased newsgroup %s to %s"; if (cp == (char *) NULL) { if (STRCMP(ngs[i], abuf) != 0) continue; if (isproc) cp = "Aliased newsgroup %s to %s"; else cp = "Please change %s to %s"; } logerr(cp, abuf, bfr); if (uses[i] == NGBUGS) { free(ngs[i]); ngsbug[i] = (char *) NULL; } ngs[i] = AllocCpy(bfr); ++dorecheck; #ifdef ALWAYSALIAS if (uses[i] != NGBUGS) #endif /* ALWAYSALIAS */ ++okcount; uses[i] = NGALIAS; } } (void) fclose(f); for (i = 0; i < ngcount; ++i) { if (uses[i] > NGOK) continue; if (isproc) log("Unknown newsgroup %s not localized", ngs[i]); else logerr("Unknown newsgroup %s", ngs[i]); #ifdef ALWAYSALIAS ++okcount; /* so we know to exit below */ } if (!isproc && okcount > 0) #else /* !ALWAYSALIAS */ } if (!isproc) #endif /* !ALWAYSALIAS */ newssave(infp, (char *) NULL); } /* * Unfortunately, if you alias an unmoderated group to a * moderated group, you must recheck the active file to see * if the new group is moderated. Rude but necessary. */ if (dorecheck) goto recheck; /* ** Zap duplicates. */ for (i = 0; i < ngcount - 1; ++i) { if (uses[i] == NGUNSEEN) continue; for (j = i + 1; ((j < ngcount) && (uses[i] != NGUNSEEN)); ++j){ register int kill = -1; register int keep = -1; if (uses[j] == NGUNSEEN) continue; if (uses[i] == NGABUGS || uses[j] == NGABUGS) { register int k = i; register int l = j; if (uses[i] == NGABUGS && uses[j] == NGABUGS) { if (strlen(ngs[j]) < strlen(ngs[i])) { k = j; l = i; } } else if (uses[i] == NGABUGS) { k = j; l = i; } strcpy(bfr, ngs[k]); strcat(bfr, "."); if (PREFIX(ngs[l], bfr)) { kill = k; keep = l; if (uses[k] != NGBUGS || ngsbug[l] == (char *) NULL) logerr("Duplicate %s removed", ngs[k]); } } if (kill < 0) { if (STRCMP(ngs[i], ngs[j]) != 0) continue; keep = i; kill = j; if (uses[j] != NGBUGS) logerr("Duplicate %s removed", ngs[j]); if (uses[i] < uses[j]) uses[i] = uses[j]; } if (kill >= 0) { if (uses[kill] == NGBUGS) { if (ngsbug[keep] == (char *) NULL) ngsbug[keep] = ngsbug[kill]; else if (ngsbug[kill] != (char*) NULL){ sprintf(bfr, "%s,%s", ngsbug[keep], ngsbug[kill]); if (ngsbug[keep] < tbuf || ngsbug[keep] > &tbuf[sizeof tbuf - 1]) free(ngsbug[keep]); ngsbug[keep] = AllocCpy(bfr); } } uses[kill] = NGUNSEEN; } } } for (pass = 1; pass <= 2; ++pass) { register int avail; if (pass == 1) { /* ** Rewrite header.nbuf. */ cp = header.nbuf; avail = sizeof header.nbuf; } else { /* ** Fill in nbuf. */ cp = nbuf; avail = sizeof nbuf; } for (i = 0; i < ngcount; ++i) { if (uses[i] < pass) continue; if (pass == 1) j = strlen(ngsbug[i] == (char *) NULL ? ngs[i] : ngsbug[i]); else j = strlen(ngs[i]); if (j + 2 > avail) { logerr("Redone Newsgroups too long"); break; } if (pass == 1) (void) strcpy(cp, ngsbug[i] == (char *) NULL ? ngs[i] : ngsbug[i]); else (void) strcpy(cp, ngs[i]); cp += j; *cp++ = (pass == 1) ? NGDELIM : '\0'; avail -= (j + 1); } if (pass == 1) { if (cp == header.nbuf) *cp = '\0'; else *(cp - 1) = '\0'; } else *cp = '\0'; } /* ** Free aliases. */ for (i = 0; i < ngcount; ++i) { if (ngs[i] < tbuf || ngs[i] > &tbuf[sizeof tbuf - 1]) free(ngs[i]); if (ngsbug[i] != NULL && (ngsbug[i] < tbuf || ngsbug[i] > &tbuf[sizeof tbuf - 1])) free(ngsbug[i]); } return nbuf[0] == '\0'; #undef NGUNSEEN #undef NGOK #undef NGALIAS #undef NGBUGS #undef NGABUGS } /* Check $LIB/moderators to see if user is listed as a known moderator * of the newsgroup in ngname..... return TRUE if user is ok. */ mod_file_okay(user, ngname) char *user, *ngname; { FILE *mfd; char *grplist = NULL; char *p, *getgrplist(); int ret = FALSE; char mfn[BUFLEN], mgrp[BUFLEN], mlist[LBUFLEN]; sprintf(mfn, "%s/%s", LIB, "moderators"); mfd = fopen(mfn, "r"); if (mfd == NULL) return FALSE; while ((!ret) && fscanf(mfd, "%[^:]:%s\n", mgrp, mlist) != EOF) { if (mgrp[0] == '#') continue; if (!STRCMP(ngname, mgrp)) { while (*(p = ((p = rindex(mlist, ',')) ? p : mlist )) && (ret == FALSE)) { if (*p == ',') *p++ = '\0'; if (*p == '\\') { *p++ = '\0'; if (!grplist) grplist = getgrplist(user); if (ngmatch(p,grplist)) ret = TRUE; } else if (!STRCMP(p, user)) ret = TRUE; *p = '\0'; } } } fclose(mfd); return ret; } /* * Figure out who posted the article (which is locally entered). * The results are placed in the header structure hp. */ gensender(hp, logname) struct hbuf *hp; char *logname; { register char *fn, *p; char buf[BUFLEN]; char *fullname(), *getenv(); int fd, n; extern char *mydomain(); if ((fn = getenv("NAME")) == NULL) { (void) sprintf(buf, "%s/%s", userhome, ".name"); if ((fd = open(buf, 0)) >= 0) { n = read(fd, buf, sizeof buf); (void) close(fd); if (n > 0 && buf[0] >= 'A') { for (p = fn = buf; *p; p++) if (*p < ' ') *p = '\0'; } } } if (fn == NULL) fn = fullname(logname); (void) sprintf(hp->path, "%s", logname); (void) sprintf(hp->from, "%s@%s (%s)", logname, FROMSYSNAME, fn); } /* * Trap interrupts. */ onsig(n) int n; { static int numsigs = 0; /* * Most UNIX systems reset caught signals to SIG_DFL. * This bad design requires that the trap be set again here. * Unfortunately, if the signal recurs before the trap is set, * the program will die, possibly leaving the lock in place. */ if (++numsigs > 100) { xerror("inews ran away looping on signal %d", n); } (void) signal(n, onsig); SigTrap = n; } /* * If the stdin begins with "#" the input is some kind of batch. if * the first line is: * #!cunbatch * or * #!c7unbatch * then fork off a pipe to do the either a * "compress -d" * or a * "decode | compress -d" * and check their output for more batch headers. They probably * contain a batch format that looks like this: * #! rnews 1234 * article with 1234 chars * #! rnews 4321 * article with 4321 chars * If so, then for each article, copy the indicated number of chars into * a temp file, fork a copy of ourselves, make its input the temp file, * and allow the copy to process the article. This avoids an exec of * rnews for each article. */ checkbatch() { int c; setbuf(infp, (char *)NULL); while ((c = getc(infp)) == '#') { /* some kind of batch, investigate further */ int i; char cmd[BUFLEN]; cmd[0] = c; fgets(cmd + 1, BUFLEN, infp); if (strncmp(cmd, "#! cunbatch", 11) == 0) { (void) sprintf(cmd, "%s/compress", LIB); input_pipe(cmd, "compress", "-d", (char *) 0); setbuf(infp, (char *)NULL); continue; /* look for the #! rnews */ } else if (strncmp(cmd, "#! c7unbatch", 12) == 0) { (void) sprintf(cmd, "%s/decode | %s/compress -d", LIB, LIB); input_pipe("/bin/sh", "news-unpack", "-c", cmd); setbuf(infp, (char *)NULL); continue; /* look for the #! rnews */ } else if (strncmp(cmd, "#! rnews ", 9) == 0 || strncmp(cmd, "! rnews ", 8) == 0) { /* instead of execing unbatch do it ourselves */ register int fd, rc, wc; int piped[2]; register long size, asize; char *tfilename; int pid, wpid, exstat; #define CPBFSZ 8192 char buf[CPBFSZ]; tfilename = 0; infpbuf = malloc((unsigned)BUFSIZ); if (infpbuf != NULL) setbuf(infp, infpbuf); do { while (STRNCMP(cmd, "#! rnews ", 9)) { fprintf(stderr, "out of sync, skipping %s\n", cmd); if (fgets(cmd, BUFLEN, infp) == NULL) exit(0); } asize = atol(cmd + 9); if (asize <= 0) xerror("checkbatch: bad batch count %ld", asize); fd = -1; size = asize; do { if (size > CPBFSZ) rc = CPBFSZ; else rc = size; rc = fread(buf, 1, rc, infp); if (rc <= 0) break; if (fd < 0) { if (rc == asize) break; /* fits in buffer */ if (!tfilename) { tfilename = "/tmp/unbnewsXXXXXX"; MKTEMP(tfilename); } if ((fd = creat(tfilename, 0666)) < 0) { xerror("rnews: creat of \"%s\" failed: %s", tfilename,errmsg(errno)); } } wc = write(fd, buf, rc); /* write to temp file */ if (wc != rc) { xerror("write of %d to \"%s\" returned %d: %s", rc, tfilename, wc, errmsg(errno)); } size -= rc; } while (size > 0); if (fd >= 0) (void) close(fd); /* * If we got a truncated batch, don't process * the last article; it will probably be * received again. */ if ((rc < asize) && (size > 0)) break; /* * This differs from the old unbatcher in * that we don't exec rnews, mainly because * we ARE rnews. Instead we fork off a copy * of ourselves for each article and allow it * to process. */ if (rc == asize) { /* * article fits in buffer, use a pipe * instead of a temporary file. */ if (pipe(piped) != 0) xerror("checkbatch: pipe() failed"); } while ((pid = fork()) == -1) { fprintf(stderr, "fork failed, waiting...\r\n"); sleep(60); } if (pid == 0) { if (rc == asize) { /* article fits in buffer * make the output of the * pipe for STDIN */ (void) fclose(infp); /* redundant but why not */ (void) close(0); if ((i = dup(piped[0])) != 0) xerror("dup() returned %d, should be 0", i); (void) close(piped[0]); (void) close(piped[1]); infp = fdopen(0, "r"); } else /* supstitute temp file as * input */ freopen(tfilename, "r", infp); setbuf(infp, (char *) NULL); (void) free(infpbuf); infpbuf = NULL; return; /* from checkbatch as if * normal article */ } /* parent of fork */ if (rc == asize) { /* article fits in buffer */ wc = write(piped[1], buf, rc); if (wc != rc) { xerror("write of %d to pipe returned %d: %s", rc, wc, errmsg(errno)); } (void) close(piped[0]); (void) close(piped[1]); } while ((wpid = wait(&exstat)) >= 0 && wpid != pid); if (tfilename) (void) unlink(tfilename); } while (fgets(cmd, BUFLEN, infp) != NULL); exit(0);/* all done */ } else { docmd(cmd); xxit(0); } } /* while a batch */ infpbuf = malloc((unsigned)BUFSIZ); if (infpbuf != NULL) setbuf(infp, infpbuf); if (c != EOF) (void) ungetc(c, infp); clearerr(infp); } /* * The input requires some processing so fork and exec the indicated command * with its output piped to our input. */ static input_pipe(cmd, arg0, arg1, arg2) char *cmd, *arg0, *arg1, *arg2; { int i, pid; int piped[2]; if (pipe(piped) != 0) { xerror("checkbatch: pipe() failed: %s",errmsg(errno)); } fflush(stdout); while ((pid = vfork()) == -1) { logerr("checkbatch: fork failed, waiting: %s",errmsg(errno)); sleep(60); } if (pid == 0) { /* child process */ /* * setup a pipe such that the exec'ed process will read our * input file and write to the pipe */ (void) close(1); if ((i = dup(piped[1])) != 1) xerror("dup() returned %d, should be 1", i); (void) close(piped[0]); (void) close(piped[1]); execl(cmd, arg0, arg1, arg2, (char *) 0); xerror("Unable to exec \"%s\" to unpack news: %s", cmd, errmsg(errno)); } else { /* parent process */ /* make the output of the pipe for STDIN */ (void) fclose(infp); (void) close(0); if ((i = dup(piped[0])) != 0) xerror("dup() returned %d, should be 0", i); (void) close(piped[0]); (void) close(piped[1]); /* * there should be a way to clear any buffered input and just * replace file descriptor 0 but I can't find a portable way. */ infp = fdopen(0, "r"); } } #define MAXARGS 32 docmd(p) register char *p; { char *args[MAXARGS]; register char **ap = args; char path[BUFSIZ]; char *rindex(), *cp; while (*p && !isspace(*p)) /* skip leading #! crud */ p++; while (isspace(*p)) p++; if (strncmp(p, "inews", 5) != 0) { #ifdef ALLOW_LIB_EXECS log("Processing incoming batch with command \"%s\"", p); #else /* !ALLOW_LIB_EXECS */ xerror("Attempt to execute news batch with command \"%s\"", p); #endif /* !ALLOW_LIB_EXECS */ } while (*p != '\0') { *ap++ = p; if (ap >= &args[MAXARGS]) { logerr("inews: unbatch: Too many args to %s", args[0]); exit(2); } while (*p && !isspace(*p)) p++; if (*p) *p++ = '\0'; while (isspace(*p)) p++; } *ap = (char *)0; if (ap == args) { logerr("inews: unbatch: no command to execute"); exit(2); } /* strip off any leading pathname in case someone gets tricky */ cp = rindex(args[0], '/'); if (cp++ == NULL) cp = args[0]; # ifdef HOME sprintf(path, "%s/%s/%s", logdir(HOME), LIBDIR, cp); # else /* !HOME */ sprintf(path, "%s/%s", LIBDIR, cp); # endif /* HOME */ /* * "path" is absolute, no searching is needed, we use * 'execvp' solely so that sh scripts will be handled */ (void) execvp(path, args); logerr("Unable to exec \"%s\" to unpack news: %s", path, errmsg(errno)); xxit(2); } /* * Exit and cleanup. */ xxit(status) int status; { (void) unlink(INFILE); (void) unlink(ARTICLE); while (lockcount > 0) unlock(); idunlock(); exit(status); } rwaccess(fname) char *fname; { int fd; fd = open(fname, 2); if (fd < 0) return 0; (void) close(fd); return 1; } exists(fname) char *fname; { int fd; fd = open(fname, 0); if (fd < 0) return 0; (void) close(fd); return 1; } int lockcount = 0; /* no. of times we've called lock */ #ifdef VMS #define SUBLOCK "/tmp/netnews.lck.1" /* * Newsystem locking. * These routines are different for VMS because we can not * effectively simulate links, and VMS supports multiple * version numbers of files */ lock() { register int i; register int fd; if (lockcount++ == 0) { i = DEADTIME; while ((fd = creat(SUBLOCK, 0444)) < 0) { if (--i < 0) { (void) unlink(SUBLOCK); logerr("News system locked up"); } if (i < -3) xerror("Unable to unlock news system"); sleep((unsigned)1); } (void) close(fd); } } unlock() { if (--lockcount == 0) (void) unlink(SUBLOCK); } #else /* !VMS */ /* * Newsystem locking. */ #if defined(BSD4_2) || defined(LOCKF) #ifdef LOCKF #include <unistd.h> #else /* !LOCKF */ #include <sys/file.h> #endif /* !LOCKF */ static int LockFd = -1; lock() { LockFd = open(SUBFILE, 2); if (LockFd < 0) logerr("Can't open(\"%s\", 2) to lock", SUBFILE); /* This will sleep until the other program releases the lock */ /* We may need to alarm out of this, but I don't think so */ #ifdef LOCKF if (lockf(LockFd, F_LOCK, 0L) < 0) #else if (flock(LockFd, LOCK_EX) < 0) #endif xerror("Can't get lock on %s: %s", SUBFILE, errmsg(errno)); } unlock() { (void) close(LockFd); } #else /* !BSD4_2 */ lock() { register int i; extern int errno; if (lockcount++ == 0) { i = DEADTIME; while (link(SEQFILE, LOCKFILE)) { if (errno != EEXIST) break; if (--i < 0) xerror("News system locked up"); sleep((unsigned)1); } } } unlock() { if (--lockcount == 0) (void) unlink(LOCKFILE); } #endif /* !BSD4_2 */ #endif /* !VMS */ #ifdef NFSCLIENT #define PROC 0004 #endif /* NFSCLIENT */ /* VARARGS1 */ error(message, arg1, arg2, arg3) char *message; long arg1, arg2, arg3; { char buffer[LBUFLEN]; fflush(stdout); (void) sprintf(buffer, message, arg1, arg2, arg3); logerr(buffer); xxit(mode == PROC ? 0 : 1); }