|
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: 32354 (0x7e62) Types: TextFile Names: »ifuncs.c«
└─⟦a0efdde77⟧ Bits:30001252 EUUGD11 Tape, 1987 Spring Conference Helsinki └─⟦this⟧ »EUUGD11/euug-87hel/sec1/news/src/ifuncs.c«
/* * This software is Copyright (c) 1986 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.63 3/23/87"; #endif /* SCCSID */ #include "iparams.h" /*LINTLIBRARY*/ #define AFSIZ 4000 /* size of text in the active file for initial malloc */ /* * 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 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(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; 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 = maynotify && (index(sp->s_flags, 'I') != NULL); 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/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 (sp->s_xmit[0] == '\0') sprintf(sp->s_xmit, "%s/%s%s", BATCHDIR, sp->s_name, appmsgid ? ".ihave" : ""); #ifdef IHCC (void) sprintf(TRANS, "%s/%s/%s", logdir(HOME), BATCHDIR, sp->s_xmit); ofp = fopen(TRANS, "a"); #else /* !IHCC */ ofp = fopen(sp->s_xmit, "a"); #endif /* !IHCC */ if (ofp == NULL) xerror("Cannot append to %s", sp->s_xmit); fprintf(ofp, "%s", appmsgid ? hh.ident : firstbufname); #ifdef MULTICAST while (--mc >= 0) fprintf(ofp, " %s", *sysnames++); #endif /* !MULTICAST */ putc('\n', ofp); (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 { ofp = xfopen(mktemp(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; } #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 */ typedef struct { char *dptr; int dsize; } datum; /* * 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); 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 */ histline[0] = '\0'; addhist(hp->ident); addhist("\t"); #ifdef DEBUG fprintf(stderr,"history returns false\n"); #endif return FALSE; } 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 */ #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. */ p = index(hline, '\t'); if (p) *p = 0; lcase(hline); lhs.dptr = hline; 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; #ifdef VMS int fd; /* 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"); (void) 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 i = creat(tempname, 0666); if (i < 0) xerror("Cannot creat %s: errno %d", tempname, errno); (void) close(i); while (link(tempname, lockname)) { #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); } #ifdef VMS (void) close(fd); #endif (void) unlink(tempname); } 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(isproc) { register FILE * f; register char * cp; register int i, j; register int ngcount, okcount, havealiased; register int pass; char * ngs[sizeof header.nbuf / 2]; char uses[sizeof header.nbuf / 2]; char tbuf[sizeof header.nbuf]; char abuf[BUFLEN]; havealiased = ngcount = 0; is_mod[0] = '\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; 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] = 1; /* it should go in "Newsgroups" line */ ++ngcount; } /* ** Check groups against active file. */ recheck: okcount = 0; rewind(actfp); clearerr(actfp); while (okcount < ngcount && fgets(bfr, BUFLEN, actfp) == bfr) { if ((cp = index(bfr, ' ')) == NULL) continue; /* strange line in input! */ /* newsgroup 12345 12345 X */ /* cp + 01234567890123 */ if (!isproc && cp[13] == 'n') continue; /* can't post to this group! */ *cp = '\0'; for (i = 0; i < ngcount; ++i) if (uses[i] >= 1 && strcmp(bfr, ngs[i]) == 0) { uses[i] = 2; /* it should be localized too */ if (cp[13] == 'm') strcpy(is_mod, bfr); ++okcount; } } #ifdef ALWAYSALIAS okcount = 0; #endif /* ALWAYSALIAS */ /* ** Handle groups absent from active file. */ if (havealiased == 0 && okcount < ngcount) { /* ** See if remaining groups are in our alias list. */ f = xfopen(ALIASES, "r"); while (okcount < ngcount && fscanf(f, "%s %s", abuf, bfr) == 2) for (i = 0; i < ngcount; ++i) { #ifndef ALWAYSALIAS if (uses[i] == 2) continue; #endif /* ALWAYSALIAS */ 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); ngs[i] = AllocCpy(bfr); uses[i] = 2; ++havealiased; ++okcount; } (void) fclose(f); for (i = 0; i < ngcount; ++i) { if (uses[i] == 2) 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 (havealiased) goto recheck; } /* ** Zap duplicates. */ for (i = 0; i < ngcount - 1; ++i) { if (uses[i] == 0) continue; for (j = i + 1; j < ngcount; ++j) { if (uses[j] == 0) continue; if (strcmp(ngs[i], ngs[j]) != 0) continue; logerr("Duplicate %s removed", ngs[j]); if (uses[i] < uses[j]) uses[i] = uses[j]; uses[j] = 0; } } 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; j = strlen(ngs[i]); if (j + 2 > avail) { logerr("Redone Newsgroups too long"); break; } (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]); return nbuf[0] == '\0'; } /* * 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; } #ifdef BATCH /* * 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, 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); 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); 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 *filename; int pid, wpid, exstat; #define CPBFSZ 8192 char buf[CPBFSZ]; filename = 0; 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 (!filename) filename = mktemp("/tmp/unbnewsXXXXXX"); if ((fd = creat(filename, 0666)) < 0) { fprintf(stderr, "rnews: creat of \"%s\" failed", filename); perror(" "); exit(1); } } wc = write(fd, buf, rc); /* write to temp file */ if (wc != rc) { fprintf(stderr, "write of %d to \"%s\" returned %d", rc, filename, wc); perror(" "); exit(1); } 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(filename, "r", infp); 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) { fprintf(stderr, "write of %d to pipe returned %d", rc, wc); perror("rnews: write"); exit(1); } (void) close(piped[0]); (void) close(piped[1]); } while ((wpid = wait(&exstat)) >= 0 && wpid != pid); } while (fgets(cmd, BUFLEN, infp) != NULL); (void) unlink(filename); exit(0);/* all done */ } else { docmd(cmd); xxit(0); } } /* while a batch */ 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) { perror("checkbatch: pipe() failed"); exit(1); } fflush(stdout); while ((pid = vfork()) == -1) { perror("checkbatch: fork failed, waiting"); 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); perror("checkbatch"); xerror("Unable to exec %s to unpack news.", cmd); } 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++; 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); perror(path); xxit(2); } #endif /* BATCH */ /* * 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, 0) < 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(SUBFILE, 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 */ /* 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); }