/* * newsspool - copy incoming news into incoming directory * * The -i option relies on the parent setting (and exporting) $PATH. * * -Log- */ #include #include #include #include #include #include "fixerrno.h" #include "libc.h" #include "news.h" #include "config.h" #ifndef lint static char RCSid[] = "$Header$"; #endif #ifndef MAXTRIES #define MAXTRIES 100 /* limit on attempts to make links */ #endif int debug = 0; char *progname; extern void error(), exit(); #ifdef UTZOOERR extern char *mkprogname(); #else #define mkprogname(a) (a) #endif char buf[BUFSIZ*16]; /* try to get a batch in a few gulps */ int immed = 0; /* try an immediate newsrun? */ char *suffix = ".t"; /* suffix for filename, default is plain text */ char grade[3] = ""; /* 3 = digit, period, NUL */ void process(); FILE *outopen(); void outclose(); extern time_t time(); char *outname(); /* - main - parse arguments and handle options */ main(argc, argv) int argc; char *argv[]; { int c; int errflg = 0; FILE *in; struct stat statbuf; extern int optind; extern char *optarg; extern FILE *efopen(); void process(); progname = mkprogname(argv[0]); while ((c = getopt(argc, argv, "ig:d")) != EOF) switch (c) { case 'i': /* try immediate newsrun */ immed++; break; case 'g': /* grade */ if (strchr("0123456789", *optarg) == NULL) error("invalid grade `%s'", optarg); sprintf(grade, "%c.", *optarg); break; case 'd': /* Debugging. */ debug++; setbuf(stderr, (char *)NULL); break; case '?': default: errflg++; break; } if (errflg) { fprintf(stderr, "usage: %s [file] ...\n", progname); exit(2); } /* probe to get unprivileged() called if necessary */ (void) ctlfile((char *)NULL); /* mktemp() uses access(2) [ARGH!] so minimize chances of trouble */ (void) setgid(getegid()); (void) setuid(geteuid()); (void) umask(newsumask()); if (optind >= argc) process(stdin, "stdin"); else for (; optind < argc; optind++) if (STREQ(argv[optind], "-")) process(stdin, "-"); else { in = efopen(argv[optind], "r"); if (fstat(fileno(in), &statbuf) < 0) error("can't fstat `%s'", argv[optind]); process(in, argv[optind]); (void) fclose(in); } if (immed) { /* execlp because shell files may not be directly execable */ execlp(binfile("input/newsrun"), "newsrun", (char *)NULL); error("attempt to run newsrun failed!", ""); } exit(0); } /* * process - process input file */ /* ARGSUSED */ void process(in, inname) FILE *in; char *inname; { register int count; register int firstblock; FILE *out; register char *p; register int n; char *name; register int wrotesome = 0; name = outname(); out = outopen(name); /* do the copying */ firstblock = 1; while ((count = fread(buf, sizeof(char), sizeof(buf), in)) > 0) { if (firstblock) { n = cunskip(buf, count); p = buf + n; count -= n; firstblock = 0; } else p = buf; if (count > 0) { n = fwrite(p, sizeof(char), count, out); if (n != count) error("write error in output to `%s'", name); wrotesome = 1; } } outclose(out, name, wrotesome); } /* - outname - construct name for the temporary output file */ char * outname() { register char *p; p = strsave(fullartfile("in.coming/nspool.XXXXXX")); mktemp(p); return(p); } /* - outopen - acquire an output file */ FILE * outopen(name) char *name; { FILE *f; f = fopen(name, "w"); if (f == NULL) error("unable to create temporary `%s'", name); if (debug) fprintf(stderr, "output into %s\n", name); return(f); } /* - outclose - close output file, moving it to the right place * * Names are based on the current time in hopes of keeping input in order. */ void outclose(f, tmpname, wrotesome) FILE *f; char *tmpname; int wrotesome; /* did anything actually get written to it? */ { register char *p; register char *name; register int ntries; time_t now; if (nfclose(f) == EOF) error("close error on file `%s'", tmpname); if (!wrotesome) { (void) unlink(tmpname); return; } if (!mkinperm(tmpname, grade, suffix)) error("couldn't move %s into the in.coming queue", tmpname); if (debug) fprintf(stderr, "succeeded\n"); } /* - cunskip - inspect block for silly #! cunbatch headers, classify input */ int /* number of chars at start to skip */ cunskip(bufp, count) char *bufp; int count; { static char goop[] = "cunbatch"; # define GOOPLEN (sizeof(goop)-1) /* strlen(goop) */ static char suf[] = ".Z"; static char goop7[] = "c7unbatch"; # define GOOP7LEN (sizeof(goop7)-1) /* strlen(goop7) */ static char suf7[] = ".7"; static char comp[] = "\037\235"; /* compress's magic no. */ register char *p; register int nleft; # define MINCBATCH 5 /* one character, compressed */ nleft = count; p = bufp; if (nleft < 2) /* no room for a header */ return(0); if (p[0] == comp[0] && p[1] == comp[1]) { /* compressed */ if (nleft < MINCBATCH) return(count); suffix = suf; return(0); } if (*p++ != '#' || *p++ != '!') /* doesn't start with #! */ return(0); nleft -= 2; /* skip space */ while (nleft > 0 && (*p == ' ' || *p == '\t')) { p++; nleft--; } /* recognize headers (the +1s ensure room for the newline) */ if (nleft >= GOOPLEN+1 && STREQN(p, goop, GOOPLEN)) { p += GOOPLEN; nleft -= GOOPLEN; suffix = suf; } else if (nleft >= GOOP7LEN+1 && STREQN(p, goop7, GOOP7LEN)) { p += GOOP7LEN; nleft -= GOOP7LEN; suffix = suf7; } else /* no header */ return(0); /* skip more space */ while (nleft > 0 && (*p == ' ' || *p == '\t')) { p++; nleft--; } if (nleft == 0 || *p++ != '\n') /* didn't end properly */ return(0); if (nleft < MINCBATCH) /* null batch */ return(count); return(p - bufp); } /* - unprivileged - drop setuidness if configuration is overridden */ void unprivileged(reason) char *reason; { setgid(getgid()); setuid(getuid()); fprintf(stderr, "%s: renouncing setuid due to nonstandard `%s' in environment\n", progname, reason); }