Path: utzoo!utgpu!jarvis.csri.toronto.edu!mailrus!tut.cis.ohio-state.edu!snorkelwacker!usc!samsung!munnari.oz.au!basser!ultima!nick From: nick@ultima.cs.uts.oz (Nick Andrew) Newsgroups: comp.os.minix Subject: News for Minix (part 2b of 12) Keywords: news Message-ID: <16744@ultima.cs.uts.oz> Date: 7 Dec 89 11:50:19 GMT Organization: Comp Sci, NSWIT, Australia Lines: 1369 #! /bin/sh # This is a shell archive. Remove anything before this line, then unpack # it by saving it into a file and typing "sh file". To overwrite existing # files, type "sh file -c". You can also feed this as standard input via # unshar, or by typing "sh 'inews.c' <<'END_OF_FILE' X/* X * This software is Copyright (c) 1986 by Rick Adams. X * X * Permission is hereby granted to copy, reproduce, redistribute or X * otherwise use this software as long as: there is no monetary X * profit gained specifically from the use or reproduction or this X * software, it is not sold, rented, traded or otherwise marketed, and X * this copyright notice is included prominently in any copy X * made. X * X * The author make no claims as to the fitness or correctness of X * this software for any use whatsoever, and it is provided as is. X * Any use of this software is at the user's own risk. X * X * inews - insert, receive, and transmit news articles. X * X */ X X#ifdef SCCSID Xstatic char *SccsId = "@(#)inews.c 2.80 4/10/87"; X#endif /* SCCSID */ X X#include "iparams.h" X X# ifdef LOCKF X# include X# include X X# ifdef F_RDLCK Xstruct flock news_lock; X# endif /* F_RDLCK */ X# endif /* LOCKF */ X X#ifdef BSD4_2 X# include X# include X#else /* !BSD4_2 */ X# include "ndir.h" X# if defined(USG) && !defined(LOCKF) X# include X# endif /* USG */ X#endif /* !BSD4_2 */ X/* local defines for inews */ X X#define OPTION 0 /* pick up an option string */ X#define STRING 1 /* pick up a string of arguments */ X X#define UNKNOWN 0001 /* possible modes for news program */ X#define UNPROC 0002 /* Unprocessed input */ X#define PROC 0004 /* Processed input */ X#define CONTROL 0010 /* Control Message */ X#define CREATENG 0020 /* Create a new newsgroup */ X X#define DONT_SPOOL 0 X#define DO_SPOOL 1 X#define EXPIRE_RUNNING 2 Xint spool_news = DONT_SPOOL; X Xextern char histline[]; Xchar forgedname[NAMELEN]; /* A user specified -f option. */ X/* Fake sys line in case they forget their own system */ Xstruct srec dummy_srec = { "MEMEME", "", "all", "", "" }; X Xchar *Progname = "inews"; /* used by xerror to identify failing program */ X Xstruct { /* options table. */ X char optlet; /* option character. */ X char filchar; /* if to pickup string, fill character. */ X int flag; /* TRUE if have seen this opt. */ X int oldmode; /* OR of legal input modes. */ X int newmode; /* output mode. */ X char *buf; /* string buffer */ X} *optpt, options[] = { /* Xoptlet filchar flag oldmode newmode buf */ X't', ' ', FALSE, UNPROC, UNKNOWN, header.title, X'n', NGDELIM, FALSE, UNPROC, UNKNOWN, header.nbuf, X'd', '\0', FALSE, UNPROC, UNKNOWN, header.distribution, X'e', ' ', FALSE, UNPROC, UNKNOWN, header.expdate, X'p', '\0', FALSE, UNKNOWN|PROC, PROC, filename, X'f', '\0', FALSE, UNPROC, UNKNOWN, forgedname, X'F', ' ', FALSE, UNPROC, UNKNOWN, header.followid, X'c', ' ', FALSE, UNKNOWN,UNKNOWN, header.ctlmsg, X'C', ' ', FALSE, UNKNOWN,CREATENG, header.ctlmsg, X#define hflag options[9].flag X'h', '\0', FALSE, UNPROC, UNKNOWN, filename, X#define oflag options[10].flag X'o', '\0', FALSE, UNPROC, UNKNOWN, header.organization, X#define Mflag options[11].flag X'M', '\0', FALSE, UNPROC, UNKNOWN, filename, X'a', '\0', FALSE, UNPROC, UNKNOWN, header.approved, X'U', '\0', FALSE, PROC, PROC, filename, X#define Sflag options[14].flag X'S', '\0', FALSE, UNKNOWN|PROC, UNPROC, filename, X'x', '\0', FALSE, UNPROC, UNKNOWN, not_here, X'r', '\0', FALSE, UNPROC, UNKNOWN, header.replyto, X'\0', '\0', 0, 0, 0, (char *)NULL X}; X XFILE *mailhdr(); Xextern int errno; X Xstruct timeb Now; X X/* X * Authors: X * Matt Glickman glickman@ucbarpa.Berkeley.ARPA X * Mark Horton mark@cbosgd.UUCP X * Stephen Daniels swd@mcnc.UUCP X * Tom Truscott trt@duke.UUCP X * Rick Adams rick@seismo.CSS.GOV X * IHCC version adapted by: X * Larry Marek larry@ihuxf.UUCP X */ Xmain(argc, argv) Xint argc; Xregister char **argv; X{ X int state; /* which type of argument to pick up */ X int tlen, len; /* temps for string processing routine */ X register char *ptr; /* pointer to rest of buffer */ X int filchar; /* fill character (state = STRING) */ X char *user = NULL, *home = NULL; /* environment temps */ X struct passwd *pw; /* struct for pw lookup */ X struct group *gp; /* struct for group lookup */ X register int i; X FILE *mfd; /* mail file file-descriptor */ X X /* uuxqt doesn't close all it's files */ X for (i = 3; !close(i); i++) X ; X /* set up defaults and initialize. */ X mode = UNKNOWN; X infp = stdin; X pathinit(); X savmask = umask(N_UMASK); /* set up mask */ X ptr = rindex(*argv, '/'); X if (!ptr) X ptr = *argv - 1; X actfp = xfopen(ACTIVE, "r+"); X#ifdef LOCKF X# ifdef F_RDLCK X news_lock.l_type = F_RDLCK; X if (fcntl(fileno(actfp), F_SETLK, &news_lock) < 0) { X# else /* !F_RDLCK */ X if (lockf(fileno(actfp), F_TLOCK, 0) < 0) { X# endif /* !F_RDLCK */ X if (errno != EAGAIN && errno != EACCES) X#else /* !LOCKF */ X#ifdef BSD4_2 X if (flock(fileno(actfp), LOCK_SH|LOCK_NB) < 0) { X if (errno != EWOULDBLOCK) X#else /* !BSD4_2 */ X sprintf(bfr, "%s.lock", ACTIVE); X if (LINK(ACTIVE, bfr) < 0) { X if (errno != EEXIST) X#endif /* V7 */ X#endif /* !BSD4_2 */ X xerror("Can't lock %s: %s", ACTIVE, errmsg(errno)); X spool_news = EXPIRE_RUNNING; X } else { X#ifdef SPOOLNEWS X if (argc > 1 && !strcmp(*(argv+1), "-S")) { X argc--; X argv++; X Sflag = 1; X } else X spool_news = DO_SPOOL; X X#endif /* SPOOLNEWS */ X } X if (spool_news != EXPIRE_RUNNING) { X /* only unlock if we locked */ X#ifdef LOCKF X (void) lockf(fileno(actfp), F_ULOCK, 0); X#else /* !LOCKF */ X#ifdef BSD4_2 X (void) flock(fileno(actfp), LOCK_UN); X#else /* !BSD4_2 */ X (void) UNLINK(bfr); X#endif /* V7 */ X#endif /* !BSD4_2 */ X } else { /* expire is running */ X if (argc > 1 && !strcmp(*(argv+1), "-S")) X exit(42); /* inform rnews -U by exit status */ X } X if (argc > 1 && !strcmp(*(argv+1), "-U")) { X /* can't unspool while things are locked */ X if (spool_news == EXPIRE_RUNNING) X xxit(0); X dounspool(); X /* NOT REACHED */ X } X X if (!strncmp(ptr+1, "rnews", 5)) { X mode = PROC; X if (spool_news != DONT_SPOOL) { X dospool((char *)NULL, FALSE); X /* NOT REACHED */ X } X#ifdef NICENESS X if (nice(0) < NICENESS) X (void) nice(NICENESS); X#endif /* NICENESS */ X } else { X /* it's not rnews, so it must be inews */ X if (argc < 2) X goto usage; X#ifndef SPOOLINEWS X if (spool_news == DO_SPOOL) X spool_news = DONT_SPOOL; X#endif /* SPOOLINEWS */ X } X X state = OPTION; X header.title[0] = header.nbuf[0] = filename[0] = '\0'; X X /* check for existence of special files */ X#ifdef DBM X chkfile(ARTFILE); X#else X chkdir(ARTFILE); X#endif /* DBM */ X chkfile(ACTIVE); X SigTrap = FALSE; /* true if a signal has been caught */ X if (mode != PROC) { X (void) signal(SIGHUP, onsig); X (void) signal(SIGINT, onsig); X } X uid = getuid(); X gid = getgid(); X duid = geteuid(); X dgid = getegid(); X (void) ftime(&Now); X if (uid == 0 && duid == 0) { X /* X * Must go through with this kludge since X * some systems do not honor the setuid bit X * when root invokes a setuid program. X */ X if ((pw = getpwnam(NEWSUSR)) == NULL) X xerror("Cannot get NEWSU pw entry"); X X duid = pw->pw_uid; X if ((gp = getgrnam(NEWSGRP)) == NULL) X xerror("Cannot get NEWSG gr entry"); X dgid = gp->gr_gid; X (void) setgid(dgid); X (void) setuid(duid); X } X X /* X * IHCC forces the use of 'getuser()' to prevent forgery of articles X * by just changing $LOGNAME X */ X#ifndef IHCC X if (isatty(fileno(stderr))) { X if ((user = getenv("USER")) == NULL) X user = getenv("LOGNAME"); X if ((home = getenv("HOME")) == NULL) X home = getenv("LOGDIR"); X } X#endif /* !IHCC */ X if (user == NULL || home == NULL) X getuser(); X else { X if (username == NULL || username[0] == 0) { X username = AllocCpy(user); X } X userhome = AllocCpy(home); X } X getuser(); X X /* loop once per arg. */ X X ++argv; /* skip first arg, which is prog name. */ X X while (--argc) { X if (state == OPTION) { X if (**argv != '-') { X xerror("Bad option string \"%s\"", *argv); X } X while (*++*argv != '\0') { X for (optpt = options; optpt->optlet != '\0'; ++optpt) { X if (optpt->optlet == **argv) X goto found; X } X /* unknown option letter */ Xusage: X fprintf(stderr, "usage: inews -t title"); X fprintf(stderr, " [ -n newsgroups ]"); X fprintf(stderr, " [ -e expiration date ]\n"); X fprintf(stderr, "\t[ -f sender]\n\n"); X xxit(1); X X found:; X if (optpt->flag == TRUE || (mode != UNKNOWN && X (mode&optpt->oldmode) == 0)) { X xerror("Bad %c option", **argv); X } X if (mode == UNKNOWN) X mode = optpt->newmode; X filchar = optpt->filchar; X optpt->flag = TRUE; X state = STRING; X ptr = optpt->buf; X len = BUFLEN; X } X X argv++; /* done with this option arg. */ X X } else { X X /* X * Pick up a piece of a string and put it into X * the appropriate buffer. X */ X if (**argv == '-') { X state = OPTION; X argc++; /* uncount this arg. */ X continue; X } X X if ((tlen = strlen(*argv)) >= len) X xerror("Argument string too long"); X (void) strcpy(ptr, *argv++); X ptr += tlen; X if (*(ptr-1) != filchar) X *ptr++ = filchar; X len -= tlen + 1; X *ptr = '\0'; X } X } X X /* X * ALL of the command line has now been processed. (!) X */ X X if (*filename) { X infp = freopen(filename, "r", stdin); X if (infp == NULL) X xerror("freopen(%s): %s", filename, errmsg(errno)); X } else X infp = stdin; X X tty = isatty(fileno(infp)); X X if (mode == CREATENG) X createng(); X X if (header.ctlmsg[0] != '\0' && header.title[0] == '\0') X (void) strcpy(header.title, header.ctlmsg); X X if (*header.nbuf) { X lcase(header.nbuf); X ptr = index(header.nbuf, '\0'); X if (ptr[-1] == NGDELIM) X *--ptr = '\0'; X } X (void) nstrip(header.title); X (void) nstrip(header.expdate); X (void) nstrip(header.followid); X if (mode != PROC) { X if (hflag) { X header.path[0] = '\0'; X (void) hread(&header, infp, FALSE); X /* there are certain fields we won't let him specify. */ X if (header.from[0]) { X (void) fixfrom(header.from); X if (Sflag && !Mflag && !header.approved[0] & X !header.sender[0]) { X register char *p; X strcpy(bfr, header.from); X p = strpbrk(bfr, "@ !"); X if (p) X *p = '\0'; X if ((pw = getpwnam(bfr)) != NULL) { X uid = pw->pw_uid; X gid = pw->pw_gid; X username = AllocCpy(bfr); X } X } else { X (void) strcpy(forgedname, header.from); X header.from[0] = '\0'; X } X } X if (!header.approved[0]) X Mflag = FALSE; X header.sender[0] = '\0'; X if (header.subdate[0] && cgtdate(header.subdate) < 0) X header.subdate[0] = '\0'; X } X X if (header.ident[0] == '\0') X getident(&header); X X if (forgedname[0]) { X register char *p1; X if (Mflag) X sprintf(header.path, "%s!%s", X PATHSYSNAME, username); X else if (!header.path[0]) { X (void) strcpy(header.path, forgedname); X X if ((p1 = strpbrk(header.path, "@ (<")) != NULL) X *p1 = '\0'; X } X if (!Mflag && !strpbrk(forgedname, "@ (<")) X (void) sprintf(header.from,"%s@%s", X forgedname, FROMSYSNAME); X else X (void) strncpy(header.from, forgedname, BUFLEN); X X (void) sprintf(header.sender, "%s@%s", X username, FROMSYSNAME); X } else { X gensender(&header, username); X } X#ifdef MYORG X if (header.organization[0] == '\0' && !Mflag && X header.sender[0] == '\0') { X strncpy(header.organization, MYORG, BUFLEN); X if (strncmp(header.organization, "Frobozz", 7) == 0) X header.organization[0] = '\0'; X if (ptr = getenv("ORGANIZATION")) X strncpy(header.organization, ptr, BUFLEN); X /* X * Note that the organization can also be turned off by X * setting it to the null string, either in MYORG or X * $ORGANIZATION in the environment. X */ X if (header.organization[0] == '/') { X mfd = fopen(header.organization, "r"); X if (mfd) { X (void) fgets(header.organization, sizeof header.organization, mfd); X (void) fclose(mfd); X } else { X header.organization[0] = '\0'; X logerr("Couldn't open %s", X header.organization); X } X ptr = index(header.organization, '\n'); X if (ptr) X *ptr = '\0'; X } X } X#endif /* MYORG */ X } X X /* Authorize newsgroups. */ X if (mode == PROC) { X checkbatch(); X (void) signal(SIGHUP, SIG_IGN); X (void) signal(SIGINT, SIG_IGN); X (void) signal(SIGQUIT, SIG_IGN); X header.ident[0] = '\0'; X if (hread(&header, infp, TRUE) == NULL) X xerror("%s: Inbound news is garbled", filename); X input(); X } X /* always check history */ X X if (history(&header)) { X log("Duplicate article %s rejected. Path: %s", X header.ident, header.path); X xxit(0); X } X X /* Easy way to make control messages, since all.all.ctl is unblessed */ X if (mode != PROC && prefix(header.title, "cmsg ") && header.ctlmsg[0] == 0) X (void) strcpy(header.ctlmsg, &header.title[5]); X is_ctl = mode != CREATENG && X (ngmatch(header.nbuf, "all.all.ctl,") || header.ctlmsg[0]); X#ifdef DEBUG X fprintf(stderr,"is_ctl set to %d\n", is_ctl); X#endif X X if (mode != CREATENG) { X if (!*header.title) X error("No title, ng %s from %s", header.nbuf, X header.from); X if (!*header.nbuf) X (void) strcpy(header.nbuf, DFLTNG); X } X X if (mode <= UNPROC) { X#ifdef FASCIST X if (uid && uid != ROOTID && fascist(user, header.nbuf)) X xerror("User %s is not authorized to post to newsgroup %s", X user, header.nbuf); X#endif /* FASCIST */ X ctlcheck(); X } X X if (mode == CREATENG) X createng(); X X /* Determine input. */ X if (mode != PROC) X input(); X if (header.intnumlines == 0 && !is_ctl) X error("%s rejected: no text lines", header.ident); X X dates(&header); X X /* Do the actual insertion. */ X insert(); X /* NOTREACHED */ X} X X/* check for existence of file */ Xstatic chkfile(f) Xchar *f; X{ X FILE *mfd; /* mail file file-descriptor */ X char cbuf[BUFLEN]; /* command buffer */ X X if (rwaccess(f)) X return; /* everything is ok */ X mfd = mailhdr((struct hbuf *)NULL, X exists(f) ? "Unwritable files!" : "Missing files!"); X if (mfd == NULL) X return; X putc('\n', mfd); X fprintf(mfd, "System: %s\n\nThere was a problem with %s!!\n", X LOCALSYSNAME, f); X (void) sprintf(cbuf, "touch %s;chmod 666 %s", f, f); X (void) system(cbuf); X if (rwaccess(f)) X fprintf(mfd, "The problem has been taken care of.\n"); X else X fprintf(mfd, "Corrective action failed - check suid bits.\n"); X (void) mclose(mfd); X} X X#ifndef DBM X/* check for existence of directory */ Xstatic chkdir(d) Xchar *d; X{ X FILE *mfd; /* mail file file-descriptor */ X char dir[BUFLEN]; /* holds directory name */ X X sprintf(dir, "%s.d", d); X if (eaccess(dir, 07) == 0) X return; /* everything is ok */ X mfd = mailhdr((struct hbuf *)NULL, X exists(dir) ? "Unwritable directories" : "Missing directories"); X if (mfd == NULL) X return; X putc('\n', mfd); X fprintf(mfd, "System: %s\n\nThere was a problem with %s!\n", X LOCALSYSNAME, dir); X (void) mkdir(dir, 0775); X if (eaccess(dir, 07) == 0) X fprintf(mfd, "The problem has been taken care of.\n"); X else X fprintf(mfd, "Corrective action failed - check suid bits.\n"); X (void) mclose(mfd); X} X X/* X * This version of access checks against effective uid and effective gid X */ Xeaccess(name, mode) Xregister char *name; Xregister int mode; X{ X struct stat statb; X int euserid = geteuid(); X int egroupid = getegid(); X X if (stat(name, &statb) == 0) { X if (euserid == 0) { X if ((statb.st_mode&S_IFMT) != S_IFREG || mode != 1) X return 0; X /* root needs execute permission for someone */ X mode = (S_IEXEC|(S_IEXEC>>3)|(S_IEXEC>>6)); X } X else if (euserid == statb.st_uid) X mode <<= 6; X else if (egroupid == statb.st_gid) X mode <<= 3; X#ifdef BSD4_2 X /* in BSD4_2 you can be in several groups */ X else { X int groups[NGROUPS]; X register int n; X n = getgroups(NGROUPS,groups); X while(--n >= 0) { X if(groups[n] == statb.st_gid) { X mode <<= 3; X break; X } X } X } X#endif /* BSD4_2 */ X X if (statb.st_mode & mode) X return 0; X } X return -1; X} X#endif /* DBM */ X Xdospool(batchcmd, dolhwrite) Xchar *batchcmd; Xint dolhwrite; X{ X register int c; X register FILE *sp; X register struct tm *tp; X time_t t; X char buf[BUFLEN], sfile[BUFLEN]; X extern struct tm *gmtime(); X X (void) sprintf(sfile, "%s/.spXXXXXX", SPOOL); X sp = xfopen(mktemp(sfile), "w"); X if (batchcmd != NULL) { X if (not_here[0] != '\0') X fprintf(sp, "%s -x %s\n", batchcmd, not_here); X else X fprintf(sp, "%s\n", batchcmd); X } else X if (not_here[0] != '\0') X fprintf(sp, "#! inews -x %s -p\n", not_here); X if (dolhwrite) X lhwrite(&header, sp); X while ((c = getc(infp)) != EOF) X putc(c, sp); X fclose(sp); X X (void) time(&t); X tp = gmtime(&t); X /* This file name "has to" be unique (right?) */ X#ifdef USG X (void) sprintf(buf, "%s/.rnews/%2.2d%2.2d%2.2d%2.2d%2.2d%x", X#else X#ifdef VMS X /* Eunice doesn't like dots in directory names */ X (void) sprintf(buf, "%s/+rnews/%02d%02d%02d%02d%02d%x", X#else /* V7 */ X (void) sprintf(buf, "%s/.rnews/%02d%02d%02d%02d%02d%x", X#endif /* V7 */ X#endif /* VMS */ X SPOOL, X tp->tm_year, tp->tm_mon+1, tp->tm_mday, X tp->tm_hour, tp->tm_min, getpid()); X if (LINK(sfile, buf) < 0) { X char dbuf[BUFLEN]; X#ifdef VMS X sprintf(dbuf, "%s/+rnews", SPOOL); X#else /* !VMS */ X sprintf(dbuf, "%s/.rnews", SPOOL); X#endif /* !VMS */ X if (mkdir(dbuf, 0777&~N_UMASK) < 0) X xerror("Cannot mkdir %s: %s", dbuf, errmsg(errno)); X if (LINK(sfile, buf) < 0) X xerror("Cannot link(%s,%s): %s", sfile, buf, X errmsg(errno)); X } X (void) UNLINK(sfile); X xxit(0); X /* NOTREACHED */ X} X X/* X * Create a newsgroup X */ Xcreateng() X{ X register char *cp; X X /* X * Only certain users are allowed to create newsgroups X */ X if (uid != ROOTID && uid != duid && uid) { X logerr("Please contact one of the local netnews people"); X xerror("to create group \"%s\" for you", header.ctlmsg); X } X if (header.distribution[0] == '\0') X#ifdef ORGDISTRIB X strcpy(header.distribution, ORGDISTRIB); X#else /* !ORGDISTRIB */ X strcpy(header.distribution, "local"); X#endif /* !ORGDISTRIB */ X X (void) strcpy(header.nbuf, header.ctlmsg); X if ((cp=index(header.nbuf, ' ')) != NULL) X *cp = '\0'; X X if (header.approved[0] == '\0') X (void) sprintf(header.approved, "%s@%s", X username, FROMSYSNAME); X (void) sprintf(bfr, "%s/inews -n %s.ctl -c newgroup %s -d %s -a \"%s\"", X LIB, header.nbuf, header.ctlmsg, header.distribution, X header.approved); X if (tty) { X printf("Please type in a paragraph describing the new newsgroup.\n"); X printf("End with control D as usual.\n"); X } X printf("%s\n", bfr); X (void) fflush(stdout); X (void) system(bfr); X exit(0); X /*NOTREACHED*/ X} X Xchar firstbufname[BUFLEN]; X/* X * Link ARTICLE into dir for ngname and update active file. X */ Xlong Xlocalize(ngname) Xchar *ngname; X{ X char afline[BUFLEN]; X long ngsize; X long fpos; X int e; X char *cp; X X lock(); X (void) rewind(actfp); clearerr(actfp); X X for(;;) { X fpos = ftell(actfp); X if (fgets(afline, sizeof afline, actfp) == NULL) { X unlock(); X logerr("Can't find \"%s\" in active file", ngname); X return FALSE; /* No such newsgroup locally */ X } X if (prefix(afline, ngname)) { X (void) sscanf(afline, "%s %ld", bfr, &ngsize); X if (strcmp(bfr, ngname) == 0) { X if (ngsize < 0 || ngsize > 99998) { X logerr("found bad ngsize %ld ng %s, setting to 1", ngsize, bfr); X ngsize = 1; X } X break; X } X } X } X for (;;) { X cp = dirname(ngname); X X (void) sprintf(bfr, "%s/%ld", cp, ngsize+1); X#ifdef VMS X /* X * The effect of this code is to store the article in the first X * newsgroup's directory and to put symbolic links elsewhere. X * If this is the first group, firstbufname is not yet filled X * in. It should be portable to other link-less systems. X * epimass!jbuck X */ X if (firstbufname[0]) { X if (vmslink(firstbufname, bfr) == 0) X break; X } else if (rename(ARTICLE, bfr) == 0) X break; X#else /* !VMS */ X if (link(ARTICLE, bfr) == 0) X break; X#endif /* !VMS */ X if (!exists(cp)) X mknewsg(cp, ngname); X#ifdef VMS X if (firstbufname[0]) { X if (vmslink(firstbufname, bfr) == 0) X break; X } else if (rename(ARTICLE, bfr) == 0) X break; X#else /* !VMS */ X if (link(ARTICLE, bfr) == 0) X break; X#endif /* !VMS */ X e = errno; /* keep log from clobbering it */ X log("Cannot install article as %s: %s", bfr, errmsg(errno)); X if (e != EEXIST) { X logerr("Link into %s failed (%s); check dir permissions.", X bfr, errmsg(e)); X unlock(); X return FALSE; X } X ngsize++; X } X X /* X * This works around a bug in the 4.1bsd stdio X * on fseeks to non even offsets in r+w files X */ X if (fpos&1) X (void) rewind(actfp); X X (void) fseek(actfp, fpos, 0); X /* X * Has to be same size as old because of %05d. X * This will overflow with 99999 articles. X */ X fprintf(actfp, "%s %05ld", ngname, ngsize+1); X#if defined(USG) || defined(MG1) X /* X * U G L Y K L U D G E X * This utter piece of tripe is the only way I know of to get X * around the fact that ATT BROKE standard IO in System 5.2. X * Basically, you can't open a file for "r+" and then try and X * write to it. This works on all "real" USGUnix systems, It will X * probably break on some obscure look alike that doesnt use the X * real ATT stdio.h X * Don't blame me, blame ATT. stdio should have already done the X * following line for us, but it doesn't X * also broken in WCW MG-1 42nix 2.0 X */ X actfp->_flag |= _IOWRT; X#endif /* USG */ X (void) fflush(actfp); X if (ferror(actfp)) X xerror("Active file write failed"); X unlock(); X if (firstbufname[0] == '\0') X (void) strcpy(firstbufname, bfr); X (void) sprintf(bfr, "%s/%ld ", ngname, ngsize+1); X addhist(bfr); X return ngsize+1; X} X X/* X * Localize for each newsgroup and broadcast. X */ Xinsert() X{ X register char *ptr; X register FILE *tfp; X register int c; X struct srec srec; /* struct for sys file lookup */ X struct tm *tm, *gmtime(); X int is_invalid = FALSE; X int exitcode = 0; X long now; X#ifdef DOXREFS X register char *nextref = header.xref; X#endif /* DOXREFS */ X X /* Clean up Newsgroups: line */ X if (!is_ctl && mode != CREATENG) X is_invalid = ngfcheck(mode == PROC); X X (void) time(&now); X tm = gmtime(&now); X if (header.expdate[0]) X addhist(" "); X#ifdef USG X sprintf(bfr,"%2.2d/%2.2d/%d %2.2d:%2.2d\t", X#else /* !USG */ X sprintf(bfr,"%02d/%02d/%d %02d:%02d\t", X#endif /* !USG */ X tm->tm_mon+1, tm->tm_mday, tm->tm_year,tm->tm_hour, tm->tm_min); X addhist(bfr); X log("%s %s ng %s subj '%s' from %s", spool_news != DONT_SPOOL X ? "queued" : (mode==PROC ? "received" : "posted"), X header.ident, header.nbuf, header.title, header.from); X X /* Write article to temp file. */ X tfp = xfopen(mktemp(ARTICLE), "w"); X X if (is_invalid) { X logerr("No valid newsgroups found, moved to junk"); X if (localize("junk")) X savehist(histline); X exitcode = 1; X goto writeout; X } X X#ifdef ZAPNOTES X if (strncmp(header.title, "Re: Orphaned Response", 21) == 0) { X logerr("Orphaned Response, moved to junk"); X if (localize("junk")) X savehist(histline); X exitcode = 1; X goto writeout; X } X#endif /* ZAPNOTES */ X X if (time((time_t *)0) > (cgtdate(header.subdate) + HISTEXP) ){ X logerr("Article too old, moved to junk"); X if (localize("junk")) X savehist(histline); X exitcode = 1; X goto writeout; X } X X if (is_mod[0] != '\0' /* one of the groups is moderated */ X && header.approved[0] == '\0') { /* and unapproved */ X struct hbuf mhdr; X FILE *mfd, *mhopen(); X register char *p; X char modadd[BUFLEN], *replyname(); X#ifdef DONTFOWARD X if(mode == PROC) { X logerr("Unapproved article in moderated group %s", X is_mod); X if (localize("junk")) X savehist(histline); X goto writeout; X } X#endif /* DONTFORWARD */ X fprintf(stderr,"%s is moderated and may not be posted to", X is_mod); X fprintf(stderr," directly.\nYour article is being mailed to"); X fprintf(stderr," the moderator who will post it for you.\n"); X /* Let's find a path to the backbone */ X sprintf(bfr, "%s/mailpaths", LIB); X mfd = xfopen(bfr, "r"); X do { X if (fscanf(mfd, "%s %s", bfr, modadd) != 2) X xerror("Can't find backbone in %s/mailpaths", X LIB); X } while (strcmp(bfr, "backbone") != 0 && !ngmatch(is_mod, bfr)); X (void) fclose(mfd); X /* fake a header for mailhdr */ X mhdr.from[0] = '\0'; X mhdr.replyto[0] = '\0'; X p = is_mod; X while (*++p) X if (*p == '.') X *p = '-'; X sprintf(mhdr.path, modadd, is_mod); X mfd = mhopen(&mhdr); X if (mfd == NULL) X xerror("Can't send mail to %s", mhdr.path); X fprintf(mfd, "To: %s\n", replyname(mhdr.path)); X lhwrite(&header, mfd); X putc('\n', mfd); X while ((c = getc(infp)) != EOF) X putc(c, mfd); X mclose(mfd); X log("Article mailed to %s", mhdr.path); X xxit(0); X } X X if (mode != PROC && spool_news != DONT_SPOOL) { X if (spool_news != EXPIRE_RUNNING X && ngmatch(header.nbuf,"to.all.ctl")) X spool_news = DONT_SPOOL; X if (spool_news != DONT_SPOOL) { X fprintf(stderr, X "Your article has been spooled for later processing.\n"); X dospool("#! inews -S -h", TRUE); X /* NOT REACHED */ X } X } X X if (is_ctl) { X exitcode = control(&header); X if (localize("control") && exitcode != 0) X savehist(histline); X } else { X if (s_find(&srec, LOCALPATHSYSNAME) == FALSE) { X logerr("Cannot find my name '%s' in %s", X LOCALPATHSYSNAME, SUBFILE); X srec = dummy_srec; X } X#ifdef DOXREFS X (void) strncpy(nextref, PATHSYSNAME, BUFLEN); X#endif /* DOXREFS */ X for (ptr = nbuf; *ptr;) { X if (ngmatch(ptr,srec.s_nbuf) || index(ptr,'.') == NULL){ X#ifdef DOXREFS X while (*nextref++) X ; X (void) sprintf(--nextref, " %s:%ld", ptr, localize(ptr)); X#else /* !DOXREFS */ X (void) localize(ptr); X#endif /* !DOXREFS */ X } X while (*ptr++) X ; X } X if (firstbufname[0] == '\0') { X logerr("Newsgroups in active, but not sys"); X (void) localize("junk"); X } X } X#ifdef DOXREFS X if (index(header.nbuf, NGDELIM) == NULL) X header.xref[0] = '\0'; X#endif /* DOXREFS */ X Xwriteout: X /* Part 1 of kludge to get around article truncation problem */ X if ( (c=getc(infp)) != EOF) { X ungetc(c, infp); X if (c == ' ' || c == '\t') { X header.intnumlines++; X (void) sprintf(header.numlines, "%d", X header.intnumlines); X } X } X /* End of part 1 */ X if (header.expdate[0] != '\0' && mode != PROC) { X /* Make sure it's fully qualified */ X long t = cgtdate(header.expdate); X strcpy(header.expdate, arpadate(&t)); X } X X lhwrite(&header, tfp); X if ((c = getc(infp)) != EOF) { X /* Part 2 of kludge to get around article truncation problem */ X if (c == ' ' || c == '\t' ) X putc('\n', tfp); X /* End of part 2 */ X ungetc(c, infp); X while (fgets(bfr, BUFLEN, infp) != NULL) X fputs(bfr, tfp); X if (bfr[strlen(bfr)-1] != '\n') X putc('\n',tfp); X } X if (ferror(tfp)) X xerror("Write failed for temp file"); X (void) fclose(tfp); X (void) fclose(infp); X if(exitcode == 0) { X /* article has passed all the checks, so work in background */ X if (mode != PROC) { X int pid; X if ((pid=fork()) < 0) X xerror("Can't fork"); X else if (pid > 0) X _exit(0); X } X#ifdef SIGTTOU X (void) signal(SIGTTOU, SIG_IGN); X#endif /* SIGTTOU */ X savehist(histline); X broadcast(mode==PROC); X } X xxit((mode == PROC && filename[0] == '\0') ? 0 : X (exitcode < 0 ? 0 : exitcode)); X} X Xinput() X{ X register char *cp; X register int c; X register int empty = TRUE; X FILE *tmpfp; X int consec_newlines = 0; X int linecount = 0; X int linserted = 0; X X tmpfp = xfopen(mktemp(INFILE), "w"); X while (!SigTrap && fgets(bfr, BUFLEN, infp) != NULL) { X if (mode == PROC) { /* zap trailing empty lines */ X#ifdef ZAPNOTES X if (empty && bfr[0] == '#' && bfr[2] == ':' X && header.nf_id[0] == '\0' X && header.nf_from[0] == '\0' ) { X (void) strcpy(header.nf_id, bfr); X (void) nstrip(header.nf_id); X (void) fgets(bfr, BUFLEN, infp); X (void) strcpy(header.nf_from, bfr); X (void) nstrip(header.nf_from); X (void) fgets(bfr, BUFLEN, infp); X X if (header.numlines[0]) { X header.intnumlines -= 2; X (void) sprintf(header.numlines, "%d", header.intnumlines); X } X X /* Strip trailing " - (nf)" */ X if ((cp = rindex(header.title, '-')) != NULL X && !strcmp(--cp, " - (nf)")) X *cp = '\0'; X log("Stripped notes header on %s", header.ident); X continue; X } X#endif /* ZAPNOTES */ X if (bfr[0] == '\n' || X /* Bandage for older versions of inews */ X bfr[1] == '\n' && !isascii(bfr[0])) { X consec_newlines++; /* count it, in case */ X continue; /* but don't write it*/ X } X /* foo! a non-empty line. write out all saved lines. */ X while (consec_newlines > 0) { X putc('\n', tmpfp); X consec_newlines--; X linecount++; X } X } X if (mode != PROC && tty && strcmp(bfr, ".\n") == 0) X break; X for (cp = bfr; c = toascii(*cp); cp++) { X if (isprint(c) || isspace(c) || c == '\b') X putc(c, tmpfp); X if (c == '\n') X linecount++; X } X if (bfr[0] == '>') X linserted++; X if (bfr[0] == '<') /* kludge to allow diff's to be posted */ X linserted--; X empty = FALSE; X } X if (*filename) X (void) fclose(infp); X if (mode != PROC && X linecount > LNCNT && linserted > (linecount-linserted)) X error("Article rejected: %s included more text than new text", X username); X X if (mode != PROC && !is_ctl && header.sender[0] == '\0' && !Sflag) { X int siglines = 0; X char sbuf[BUFLEN]; X (void) sprintf(bfr, "%s/%s", userhome, ".signature"); X if (access(bfr, 4) == 0) { X if ((infp = fopen(bfr, "r")) == NULL) { X (void) fprintf(stderr, X "inews: \"%s\" left off (must be readable by \"inews\" owner)\n", bfr); X goto finish; X } X X while (fgets(sbuf, sizeof sbuf, infp) != NULL) X if (++siglines > 4) X break; X if (siglines > 4) X fprintf(stderr,".signature not included (> 4 lines)\n"); X else { X rewind(infp); X fprintf(tmpfp, "-- \n"); /* To separate */ X linecount++; X while ((c = getc(infp)) != EOF) { X putc(c, tmpfp); X if (c == '\n') X linecount++; X } X } X (void) fclose(infp); X } X } X Xfinish: X if (ferror(tmpfp)) X xerror("write failed to temp file"); X (void) fclose(tmpfp); X if (SigTrap) { X if (tty) X fprintf(stderr, "Interrupt\n"); X if (tty && !empty) X fwait(fsubr(newssave, (char *) NULL, (char *) NULL)); X if (!tty) X log("Blown away by an interrupt %d", SigTrap); X xxit(1); X } X if (tty) X fprintf(stderr, "EOT\n"); X fflush(stdout); X infp = fopen(INFILE, "r"); X if (header.numlines[0]) { X /* X * Check line count if there's already one attached to X * the article. Could make this a fatal error - X * throwing it away if it got chopped, in hopes that X * another copy will come in later with a correct X * line count. But that seems a bit much for now. X */ X if (linecount != header.intnumlines) { X if (linecount == 0) X error("%s rejected. linecount expected %d, got 0", header.ident, header.intnumlines); X if (linecount > header.intnumlines || X linecount+consec_newlines < header.intnumlines) X log("linecount expected %d, got %d", header.intnumlines, linecount+consec_newlines); X } X /* adjust count for blank lines we stripped off */ X if (consec_newlines) { X header.intnumlines -= consec_newlines; X if (header.intnumlines < 0 ) X header.intnumlines = 0; /* paranoia */ X (void) sprintf(header.numlines, "%d", header.intnumlines); X } X X } else { X /* Attach a line count to the article. */ X header.intnumlines = linecount; X (void) sprintf(header.numlines, "%d", linecount); X } X} X X/* X * Make the directory for a new newsgroup. ngname should be the X * full pathname of the directory. Do the other stuff too. X * The various games with setuid and chown are to try to make sure X * the directory is owned by NEWSUSR and NEWSGRP, which is tough to X * do if you aren't root. This will work on a UCB system (which allows X * setuid(geteuid()) or a USG system (which allows you to give away files X * you own with chown), otherwise you have to change your kernel to allow X * one of these things or run with your dirs 777 so that it doesn't matter X * who owns them. X */ Xmknewsg(fulldir, ngname) Xchar *fulldir; Xchar *ngname; X{ X if (ngname == NULL || !isalpha(ngname[0])) X xerror("Tried to make illegal newsgroup %s", ngname); X X /* Create the directory */ X mkparents(fulldir); X X if (mkdir(fulldir, 0777) < 0) X xerror("Cannot mkdir %s: %s", fulldir, errmsg(errno)); X X log("make newsgroup %s in dir %s", ngname, fulldir); X} X X/* X * If any parent directories of this dir don't exist, create them. X */ Xmkparents(dname) Xchar *dname; X{ X char buf[200]; X register char *p; X X (void) strcpy(buf, dname); X p = rindex(buf, '/'); X if (p) X *p = '\0'; X if (exists(buf)) X return; X mkparents(buf); X if (mkdir(buf, 0777) < 0) X xerror("Can not mkdir %s: %s", buf, errmsg(errno)); X} X Xcancel() X{ X register FILE *fp; X X log("cancel article %s", filename); X fp = fopen(filename, "r"); X if (fp == NULL) { X log("article %s not found", filename); X return; X } X if (hread(&header, fp, TRUE) == NULL) X error("Article is garbled."); X (void) fclose(fp); X (void) unlink(filename); X} X Xdounspool() X{ X register DIR *dirp; X register struct direct *dir; X register int foundsome; X int pid, status, ret; X char spbuf[BUFLEN]; X#ifdef LOCKF X FILE* LockFd; X#endif /* LOCKF */ X#ifdef VMS X sprintf(spbuf, "%s/+rnews", SPOOL); X#else /* !VMS */ X sprintf(spbuf, "%s/.rnews", SPOOL); X#endif /* !VMS */ X X if (chdir(spbuf) < 0) X xerror("chdir(%s):%s", spbuf, errmsg(errno)); X X dirp = opendir("."); X if (dirp == NULL) /* Boy are things screwed up */ X xerror("opendir can't open .:%s", errmsg(errno)); X#ifdef LOCKF X LockFd = xfopen(SEQFILE, "r+w"); X if (lockf(fileno(LockFd), F_TLOCK, 0) < 0) { X if (errno != EAGAIN && errno != EACCES) X#else /* !LOCKF */ X#ifdef BSD4_2 X if (flock(dirp->dd_fd, LOCK_EX|LOCK_NB) < 0) { X if (errno != EWOULDBLOCK) X#else /* V7 */ X strcat(spbuf, ".lock"); X sprintf(bfr, "%s.tmp", spbuf); X (void) close(creat(bfr, 0666)); X ret = LINK(bfr, spbuf); X status = errno; X (void) UNLINK(bfr); X errno = status; X if (ret < 0) { X if (errno != EEXIST) X#endif /* V7 */ X#endif /* !LOCKF */ X xerror("Can't lock %s: %s", spbuf, errmsg(errno)); X xxit(3); /* another rnews -U is running */ X } X X do { X foundsome = 0; X X while ((dir=readdir(dirp)) != NULL) { X if (dir->d_name[0] == '.') X continue; X if ((pid=vfork()) == -1) X xerror("Can't fork: %s", errmsg(errno)); X if (pid == 0) { X#ifdef IHCC X char bufr[BUFSIZ]; X sprintf(bufr, "%s/%s", logdir(HOME), RNEWS); X execl(bufr, "rnews", "-S", "-p", dir->d_name, X (char *) NULL); X#else /* !IHCC */ X execl(RNEWS, "rnews", "-S", "-p", dir->d_name, X (char *) NULL); X#endif /* !IHCC */ X _exit(1); X } X X while ((ret=wait(&status)) != pid && ret != -1) X /* continue */; X X if (((status>>8)&0177) == 42) { X /* expire has started up, shutdown rnews -U */ X break; X } X X if (status != 0) { X sprintf(bfr, "../%s", dir->d_name); X (void) LINK(dir->d_name, bfr); X logerr("rnews failed, status %d. Batch saved in %s/%s", X status, SPOOL, dir->d_name); X } X (void) unlink(dir->d_name); X foundsome++; X } X rewinddir(dirp); X } while (foundsome); /* keep rereading the directory until it's empty */ X (void) UNLINK(spbuf); X X xxit(0); X} END_OF_FILE if test 34390 -ne `wc -c <'inews.c'`; then echo shar: \"'inews.c'\" unpacked with wrong size! fi # end of 'inews.c' fi echo shar: End of shell archive. exit 0 -- "Zeta Microcomputer Software" ACSnet: nick@ultima.cs.uts.oz UUCP: ...!uunet!munnari!ultima.cs.uts.oz!nick Fidonet: Nick Andrew on 3:713/602 (Zeta)