|
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: 42311 (0xa547) Types: TextFile Names: »inews.c«
└─⟦9ae75bfbd⟧ Bits:30007242 EUUGD3: Starter Kit └─⟦373604645⟧ »EurOpenD3/news/bnews.2.11/src.tar.Z« └─⟦3beb569ac⟧ └─⟦this⟧ »src/inews.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. * * inews - insert, receive, and transmit news articles. * */ #ifdef SCCSID static char *SccsId = "@(#)inews.c 2.93 10/29/89"; #endif /* SCCSID */ #include "iparams.h" # ifdef LOCKF # include <unistd.h> # include <fcntl.h> # if defined(F_RDLCK) && defined(F_SETLK) && !defined(HP9K5) struct flock news_lock; # endif /* F_RDLCK && F_SETLK && !HP9K5 */ # endif /* LOCKF */ #ifdef BSD4_2 # include <sys/file.h> #else /* !BSD4_2 */ # if defined(USG) && !defined(LOCKF) # include <fcntl.h> # endif /* USG */ #endif /* !BSD4_2 */ /* local defines for inews */ #define OPTION 0 /* pick up an option string */ #define STRING 1 /* pick up a string of arguments */ #define UNKNOWN 0001 /* possible modes for news program */ #define UNPROC 0002 /* Unprocessed input */ #define PROC 0004 /* Processed input */ #define CONTROL 0010 /* Control Message */ #define CREATENG 0020 /* Create a new newsgroup */ #define DONT_SPOOL 0 #define DO_SPOOL 1 #define EXPIRE_RUNNING 2 int spool_news = DONT_SPOOL; static char *artlinks[32]; static int linkcount; extern char histline[]; char forgedname[NAMELEN]; /* A user specified -f option. */ /* Fake sys line in case they forget their own system */ char *Progname = "inews"; /* used by xerror to identify failing program */ struct { /* options table. */ char optlet; /* option character. */ char filchar; /* if to pickup string, fill character. */ int flag; /* TRUE if have seen this opt. */ int oldmode; /* OR of legal input modes. */ int newmode; /* output mode. */ char *buf; /* string buffer */ } *optpt, options[] = { /* optlet filchar flag oldmode newmode buf */ 't', ' ', FALSE, UNPROC, UNKNOWN, header.title, 'n', NGDELIM, FALSE, UNPROC, UNKNOWN, header.nbuf, 'd', '\0', FALSE, UNPROC, UNKNOWN, header.distribution, 'e', ' ', FALSE, UNPROC, UNKNOWN, header.expdate, 'p', '\0', FALSE, UNKNOWN|PROC, PROC, filename, 'f', '\0', FALSE, UNPROC, UNKNOWN, forgedname, 'F', ' ', FALSE, UNPROC, UNKNOWN, header.followid, 'c', ' ', FALSE, UNKNOWN,UNKNOWN, header.ctlmsg, #define COPT 'C' COPT, ' ', FALSE, UNKNOWN,CREATENG, header.ctlmsg, #define hflag options[9].flag 'h', '\0', FALSE, UNPROC, UNKNOWN, filename, #define oflag options[10].flag 'o', '\0', FALSE, UNPROC, UNKNOWN, header.organization, #define Mflag options[11].flag 'M', '\0', FALSE, UNPROC, UNKNOWN, filename, 'a', '\0', FALSE, UNPROC, UNKNOWN, header.approved, 'U', '\0', FALSE, PROC, PROC, filename, #define Sflag options[14].flag 'S', '\0', FALSE, UNKNOWN|PROC, UNPROC, filename, 'x', '\0', FALSE, UNPROC, UNKNOWN, not_here, 'r', '\0', FALSE, UNPROC, UNKNOWN, header.replyto, #define vflag options[17].flag 'v', '\0', FALSE, UNPROC, UNKNOWN, filename, '\0', '\0', 0, 0, 0, (char *)NULL }; FILE *mailhdr(); extern FILE *controlmail; extern int errno, xxit(); char *infpbuf = NULL; struct timeb Now; /* * Authors: * Matt Glickman glickman@ucbarpa.Berkeley.ARPA * Mark Horton mark@cbosgd.UUCP * Stephen Daniels swd@mcnc.UUCP * Tom Truscott trt@duke.UUCP * Rick Adams rick@seismo.CSS.GOV * IHCC version adapted by: * Larry Marek larry@ihuxf.UUCP */ main(argc, argv) int argc; register char **argv; { int state; /* which type of argument to pick up */ int tlen, len; /* temps for string processing routine */ register char *ptr; /* pointer to rest of buffer */ int filchar; /* fill character (state = STRING) */ char *user = NULL, *home = NULL; /* environment temps */ struct passwd *pw; /* struct for pw lookup */ struct group *gp; /* struct for group lookup */ register int i; FILE *mfd; /* mail file file-descriptor */ #if !defined(LOCKF) && !defined(BSD4_2) struct stat stbuf; #endif /* !LOCKF && ! BSD4_2 */ /* uuxqt doesn't close all its files */ for (i = 3; !close(i); i++) ; /* set up defaults and initialize. */ mode = UNKNOWN; infp = stdin; pathinit(); savmask = umask(N_UMASK); /* set up mask */ ptr = rindex(*argv, '/'); if (!ptr) ptr = *argv - 1; #ifdef NFSCLIENT actfp = xfopen(ACTIVE, "r"); #else /* !NFSCLIENT */ actfp = xfopen(ACTIVE, "r+"); #ifdef LOCKF # if defined(F_RDLCK) && defined(F_SETLK) && !defined(HP9K5) news_lock.l_type = F_RDLCK; if (fcntl(fileno(actfp), F_SETLK, &news_lock) < 0) { # else /* !(F_RDLCK && F_SETLK && !HP9K5) */ if (lockf(fileno(actfp), F_TLOCK, 0L) < 0) { # endif /* !(F_RDLCK && F_SETLK && !HP9K5) */ if (errno != EAGAIN && errno != EACCES) #else /* !LOCKF */ #ifdef BSD4_2 if (flock(fileno(actfp), LOCK_SH|LOCK_NB) < 0) { if (errno != EWOULDBLOCK) #else /* !BSD4_2 */ sprintf(bfr, "%s.lock", ACTIVE); /* assume a dead lock if the active file is over 12 hours old */ if (LINK(ACTIVE, bfr) < 0 && (errno != EEXIST || (stat(bfr, &stbuf) == 0 && (time((char *)0) - stbuf.st_mtime) < DAYS/2))) { if (errno != EEXIST) #endif /* V7 */ #endif /* !BSD4_2 */ xerror("Can't lock %s: %s", ACTIVE, errmsg(errno)); spool_news = EXPIRE_RUNNING; } else { #ifdef SPOOLNEWS if (argc > 1 && !STRCMP(*(argv+1), "-S")) { argc--; argv++; Sflag = 1; } else spool_news = DO_SPOOL; #endif /* SPOOLNEWS */ } if (spool_news != EXPIRE_RUNNING) { /* only unlock if we locked */ #ifdef LOCKF (void) lockf(fileno(actfp), F_ULOCK, 0L); #else /* !LOCKF */ #ifdef BSD4_2 (void) flock(fileno(actfp), LOCK_UN); #else /* !BSD4_2 */ (void) UNLINK(bfr); #endif /* V7 */ #endif /* !BSD4_2 */ } else { /* expire is running */ if (argc > 1 && !STRCMP(*(argv+1), "-S")) exit(42); /* inform rnews -U by exit status */ } (void) signal(SIGTERM, xxit); if (argc > 1 && !STRCMP(*(argv+1), "-U")) { /* can't unspool while things are locked */ if (spool_news == EXPIRE_RUNNING) xxit(0); dounspool(); /* NOT REACHED */ } #endif /* !NFSCLIENT */ if (!STRNCMP(ptr+1, "rnews", 5)) { #ifndef NFSCLIENT mode = PROC; if (spool_news != DONT_SPOOL) { dospool((char *)NULL, FALSE); /* NOT REACHED */ } #else /* NFSCLIENT */ mfd = mailhdr((struct hbuf *)NULL, "Improper use of INEWS"); if (mfd != NULL) { fprintf(mfd,"System: %s\n\nINEWS is running improperly as RNEWS on slave NFS site by user %s.\n", LOCALSYSNAME, username); (void) mclose(mfd); exit(1); } #endif /* NFSCLIENT */ #ifdef NICENESS if ((i=nice(0)) < NICENESS) (void) nice(NICENESS-i); #endif /* NICENESS */ } else { /* it's not rnews, so it must be inews */ if (argc < 2) goto usage; #ifndef SPOOLINEWS if (spool_news == DO_SPOOL) spool_news = DONT_SPOOL; #endif /* SPOOLINEWS */ } #ifdef MINFREE if (space()) { /* check disk space */ spool_news = DO_SPOOL; logerr("Out of space in %s.", SPOOLDIR); } #endif /* MINFREE */ state = OPTION; header.title[0] = header.nbuf[0] = filename[0] = '\0'; /* check for existence of special files */ #ifdef DBM chkfile(ARTFILE); #else chkdir(ARTFILE); #endif /* DBM */ chkfile(ACTIVE); SigTrap = FALSE; /* true if a signal has been caught */ if (mode != PROC) { (void) signal(SIGHUP, onsig); (void) signal(SIGINT, onsig); } /* * Catch "filesize exceeded" signals on 4.2BSD systems * - the history files may exceed this limit. */ #ifdef SIGXFSZ (void) signal(SIGXFSZ, SIG_IGN); #endif /* SIGXFSZ */ uid = getuid(); gid = getgid(); duid = geteuid(); dgid = getegid(); (void) ftime(&Now); #ifndef NFSCLIENT if (uid == 0 && duid == 0) { #else /* NFSCLIENT */ if (duid == 0) { #endif /* NFSCLIENT */ /* * Must go through with this kludge since * some systems do not honor the setuid bit * when root invokes a setuid program. * * On NFS slave systems, inews is setuid to ROOT. This allows * inews to setuid/gid (real and effective) to NEWSUSR and * NEWSGRP respectively. This *must* happen so that the "rsh" * program will run as user NEWSUSR and not as the user running * inews. "rsh" runs as the "real" user even when the program * calling it is setuid. */ if ((pw = getpwnam(NEWSUSR)) == NULL) xerror("Cannot get NEWSU pw entry"); duid = pw->pw_uid; if ((gp = getgrnam(NEWSGRP)) == NULL) xerror("Cannot get NEWSG gr entry"); dgid = gp->gr_gid; (void) setgid(dgid); (void) setuid(duid); } /* * Force the use of 'getuser()' to prevent forgery of articles * by just changing $LOGNAME */ if (isatty(fileno(stderr)) #ifdef DOGETUSER && uid == 0 /* allow root to set name in any case */ #endif /* DOGETUSER */ ) { if ((user = getenv("USER")) == NULL) user = getenv("LOGNAME"); if ((home = getenv("HOME")) == NULL) home = getenv("LOGDIR"); } if (user == NULL || home == NULL) getuser(); else { if (STRCMP(username, "Unknown") == 0 || username[0] == 0) { username = AllocCpy(user); } userhome = AllocCpy(home); } getuser(); /* loop once per arg. */ ++argv; /* skip first arg, which is prog name. */ while (--argc) { if (state == OPTION) { if (**argv != '-') { xerror("Bad option string \"%s\"", *argv); } while (*++*argv != '\0') { for (optpt = options; optpt->optlet != '\0'; ++optpt) { if (optpt->optlet == **argv) goto found; } /* unknown option letter */ usage: fprintf(stderr, "usage: inews -t title"); fprintf(stderr, " [ -n newsgroups ]"); fprintf(stderr, " [ -e expiration date ]\n"); fprintf(stderr, "\t[ -f sender]\n\n"); xxit(1); found:; #ifdef NFSCLIENT if (optpt->optlet == COPT) { fprintf(stderr, "Cannot create new newsgroups from an NFS slave system.\n\n"); xxit(1); } #endif /* NFSCLIENT */ if (optpt->flag == TRUE || (mode != UNKNOWN && (mode&optpt->oldmode) == 0)) { xerror("Bad %c option", **argv); } if (mode == UNKNOWN) mode = optpt->newmode; filchar = optpt->filchar; optpt->flag = TRUE; state = STRING; ptr = optpt->buf; len = BUFLEN; } argv++; /* done with this option arg. */ } else { /* * Pick up a piece of a string and put it into * the appropriate buffer. */ if (**argv == '-') { state = OPTION; argc++; /* uncount this arg. */ continue; } if ((tlen = strlen(*argv)) >= len) xerror("Argument string too long"); (void) strcpy(ptr, *argv++); ptr += tlen; if (*(ptr-1) != filchar) *ptr++ = filchar; len -= tlen + 1; *ptr = '\0'; } } /* * ALL of the command line has now been processed. (!) */ if (*filename) { infp = freopen(filename, "r", stdin); if (infp == NULL) xerror("freopen(%s): %s", filename, errmsg(errno)); } else infp = stdin; tty = isatty(fileno(infp)); #ifndef NFSCLIENT if (mode == CREATENG) createng(); #endif /* !NFSCLIENT */ if (header.ctlmsg[0] != '\0' && header.title[0] == '\0') (void) strcpy(header.title, header.ctlmsg); if (*header.nbuf) { lcase(header.nbuf); ptr = index(header.nbuf, '\0'); if (ptr[-1] == NGDELIM) *--ptr = '\0'; } (void) nstrip(header.title); (void) nstrip(header.expdate); (void) nstrip(header.followid); if (mode != PROC) { if (hflag) { header.path[0] = '\0'; (void) hread(&header, infp, FALSE); /* there are certain fields we won't let him specify. */ if (header.from[0]) { (void) fixfrom(&header); if (Sflag && !Mflag && !header.approved[0] & !header.sender[0]) { register char *p; strcpy(bfr, header.from); p = strpbrk(bfr, "@ !"); if (p) *p = '\0'; if ((pw = getpwnam(bfr)) != NULL) { uid = pw->pw_uid; gid = pw->pw_gid; username = AllocCpy(bfr); } } else { (void) strcpy(forgedname, header.from); header.from[0] = '\0'; } } if (!header.approved[0]) Mflag = FALSE; header.sender[0] = '\0'; if (header.subdate[0] && cgtdate(header.subdate) < 0) header.subdate[0] = '\0'; } if (header.ident[0] == '\0') getident(&header); if (forgedname[0]) { register char *p1; if (Mflag) sprintf(header.path, "%s!%s", PATHSYSNAME, username); else if (!header.path[0]) { if ((p1 = rindex(forgedname, '@')) == NULL) (void) strcpy(header.path, forgedname); else { *p1 = '\0'; (void) sprintf(header.path, "%s!%s", p1+1, forgedname); *p1 = '@'; } if ((p1 = strpbrk(header.path, " (<")) != NULL) *p1 = '\0'; } if (!Mflag && !strpbrk(forgedname, "@ (<")) (void) sprintf(header.from,"%s@%s", forgedname, FROMSYSNAME); else (void) strncpy(header.from, forgedname, BUFLEN); (void) sprintf(header.sender, "%s@%s", username, FROMSYSNAME); } else { gensender(&header, username); } #ifdef MYORG if (header.organization[0] == '\0' && !Mflag && header.sender[0] == '\0') { strncpy(header.organization, MYORG, BUFLEN); if (STRNCMP(header.organization, "Frobozz", 7) == 0) header.organization[0] = '\0'; if (ptr = getenv("ORGANIZATION")) strncpy(header.organization, ptr, BUFLEN); /* * Note that the organization can also be turned off by * setting it to the null string, either in MYORG or * $ORGANIZATION in the environment. */ if (header.organization[0] == '/') { mfd = fopen(header.organization, "r"); if (mfd) { (void) fgets(header.organization, sizeof header.organization, mfd); (void) fclose(mfd); } else { logerr("Couldn't open %s", header.organization); header.organization[0] = '\0'; } ptr = index(header.organization, '\n'); if (ptr) *ptr = '\0'; } } #endif /* MYORG */ } /* Authorize newsgroups. */ if (mode == PROC) { #ifdef NFSCLIENT mfd = mailhdr((struct hbuf *)NULL, "Improper use of INEWS"); if (mfd != NULL) { fprintf(mfd,"System: %s\n\nINEWS is running improperly as RNEWS on slave NFS site by user %s.\n", LOCALSYSNAME, username); (void) mclose(mfd); exit(1); } #else /* !NFSCLIENT */ checkbatch(); if (infpbuf == NULL) { /* make sure do buffered reads */ infpbuf = malloc((unsigned)BUFSIZ); if (infpbuf != NULL) setbuf(infp, infpbuf); } (void) signal(SIGHUP, SIG_IGN); (void) signal(SIGINT, SIG_IGN); (void) signal(SIGQUIT, SIG_IGN); header.ident[0] = '\0'; if (hread(&header, infp, TRUE) == NULL) xerror("%s: Inbound news is garbled", filename); input(bfr[0] != '\n'); } /* always check history */ if (history(&header)) { log("Duplicate article %s rejected. Path: %s", header.ident, header.path); xxit(0); #endif /* !NFSCLIENT */ } /* Easy way to make control messages, since all.all.ctl is unblessed */ if (mode != PROC && PREFIX(header.title, "cmsg ") && header.ctlmsg[0] == 0) (void) strcpy(header.ctlmsg, &header.title[5]); is_ctl = mode != CREATENG && (ngmatch(header.nbuf, "all.all.ctl,") || header.ctlmsg[0]); #ifdef DEBUG fprintf(stderr,"is_ctl set to %d\n", is_ctl); #endif if (mode != CREATENG) { if (!*header.title) error("No title, ng %s from %s", header.nbuf, header.from); if (!*header.nbuf) (void) strcpy(header.nbuf, DFLTNG); } if (mode <= UNPROC) { #ifdef FASCIST if (uid && uid != ROOTID && fascist(username, header.nbuf)) xerror("User %s is not authorized to post to newsgroup %s", username, header.nbuf); #endif /* FASCIST */ #ifndef NFSCLIENT ctlcheck(); #endif /* !NFSCLIENT */ } #ifndef NFSCLIENT if (mode == CREATENG) createng(); #endif /* !NFSCLIENT */ /* Determine input. */ if (mode != PROC) input(FALSE); if (header.intnumlines == 0 && !is_ctl) error("%s rejected: no text lines", header.ident); dates(&header); /* Do the actual insertion. */ insert(); /* NOTREACHED */ } /* check for existence of file */ static chkfile(f) char *f; { FILE *mfd; /* mail file file-descriptor */ char cbuf[BUFLEN]; /* command buffer */ #ifndef NFSCLIENT if (rwaccess(f)) return; /* everything is ok */ mfd = mailhdr((struct hbuf *)NULL, exists(f) ? "Unwritable files!" : "Missing files!"); #else /* NFSCLIENT */ if (exists(f)) return; /* everything is ok */ mfd = mailhdr((struct hbuf *)NULL, "Missing files!"); #endif /* NFSCLIENT */ if (mfd == NULL) return; putc('\n', mfd); fprintf(mfd, "System: %s\n\nThere was a problem with %s!!\n", LOCALSYSNAME, f); #ifndef NFSCLIENT (void) sprintf(cbuf, "touch %s;chmod 666 %s", f, f); (void) system(cbuf); if (rwaccess(f)) fprintf(mfd, "The problem has been taken care of.\n"); else fprintf(mfd, "Corrective action failed - check suid bits.\n"); #else /* NFSCLIENT */ fprintf(mfd, "Corrective action must take place on \"%s\", the master NFS news system.\n", NFSSYSNAME); #endif /* NFSCLIENT */ (void) mclose(mfd); } #ifndef DBM /* check for existence of directory */ static chkdir(d) char *d; { FILE *mfd; /* mail file file-descriptor */ char dir[BUFLEN]; /* holds directory name */ sprintf(dir, "%s.d", d); if (eaccess(dir, 07) == 0) return; /* everything is ok */ mfd = mailhdr((struct hbuf *)NULL, exists(dir) ? "Unwritable directories" : "Missing directories"); if (mfd == NULL) return; putc('\n', mfd); fprintf(mfd, "System: %s\n\nThere was a problem with %s!\n", LOCALSYSNAME, dir); #ifndef NFSCLIENT (void) mkdir(dir, 0775); if (eaccess(dir, 07) == 0) fprintf(mfd, "The problem has been taken care of.\n"); else fprintf(mfd, "Corrective action failed - check suid bits.\n"); #else /* NFSCLIENT */ fprintf(mfd, "Corrective action must take place on \"%s\", the master NFS news system.\n", NFSSYSNAME); #endif /* NFSCLIENT */ (void) mclose(mfd); } /* * This version of access checks against effective uid and effective gid */ eaccess(name, mode) register char *name; register int mode; { struct stat statb; int euserid = geteuid(); int egroupid = getegid(); if (stat(name, &statb) == 0) { if (euserid == 0) { if ((statb.st_mode&S_IFMT) != S_IFREG || mode != 1) return 0; /* root needs execute permission for someone */ mode = (S_IEXEC|(S_IEXEC>>3)|(S_IEXEC>>6)); } else if (euserid == statb.st_uid) mode <<= 6; else if (egroupid == statb.st_gid) mode <<= 3; #ifdef BSD4_2 /* in BSD4_2 you can be in several groups */ else { int groups[NGROUPS]; register int n; n = getgroups(NGROUPS,groups); while(--n >= 0) { if(groups[n] == statb.st_gid) { mode <<= 3; break; } } } #endif /* BSD4_2 */ if (statb.st_mode & mode) return 0; } return -1; } #endif /* DBM */ #ifndef NFSCLIENT dospool(batchcmd, dolhwrite) char *batchcmd; int dolhwrite; { register int c; register FILE *sp; register struct tm *tp; time_t t; char buf[BUFLEN], sfile[BUFLEN]; extern struct tm *gmtime(); (void) sprintf(sfile, "%s/.tmp/spXXXXXX", SPOOL); MKTEMP(sfile); sp = xfopen(sfile, "w"); if (batchcmd != NULL) { if (not_here[0] != '\0') fprintf(sp, "%s -x %s\n", batchcmd, not_here); else fprintf(sp, "%s\n", batchcmd); } else if (not_here[0] != '\0') fprintf(sp, "#! inews -x %s -p\n", not_here); if (dolhwrite) lhwrite(&header, sp); while ((c = getc(infp)) != EOF) putc(c, sp); fclose(sp); (void) time(&t); tp = gmtime(&t); /* This file name "has to" be unique (right?) */ #ifdef USG (void) sprintf(buf, "%s/.rnews/%2.2d%2.2d%2.2d%2.2d%2.2d%x", #else #ifdef VMS /* Eunice doesn't like dots in directory names */ (void) sprintf(buf, "%s/+rnews/%02d%02d%02d%02d%02d%x", #else /* V7 */ (void) sprintf(buf, "%s/.rnews/%02d%02d%02d%02d%02d%x", #endif /* V7 */ #endif /* VMS */ SPOOL, tp->tm_year, tp->tm_mon+1, tp->tm_mday, tp->tm_hour, tp->tm_min, getpid()); #ifdef IHCC log("Spooling %s into %s", header.ident, (rindex(buf,'/') + 1)); #endif /* IHCC */ if (LINK(sfile, buf) < 0) { char dbuf[BUFLEN]; #ifdef VMS sprintf(dbuf, "%s/+rnews", SPOOL); #else /* !VMS */ sprintf(dbuf, "%s/.rnews", SPOOL); #endif /* !VMS */ if (mkdir(dbuf, 0777&~N_UMASK) < 0) xerror("Cannot mkdir %s: %s", dbuf, errmsg(errno)); if (LINK(sfile, buf) < 0) xerror("Cannot link(%s,%s): %s", sfile, buf, errmsg(errno)); } (void) UNLINK(sfile); xxit(0); /* NOTREACHED */ } /* * Create a newsgroup */ createng() { register char *cp; /* * Only certain users are allowed to create newsgroups */ if (uid != ROOTID && uid != duid && uid) { logerr("Please contact one of the local netnews people"); xerror("to create group \"%s\" for you", header.ctlmsg); } if (header.distribution[0] == '\0') #ifdef ORGDISTRIB strcpy(header.distribution, ORGDISTRIB); #else /* !ORGDISTRIB */ strcpy(header.distribution, "local"); #endif /* !ORGDISTRIB */ (void) strcpy(header.nbuf, header.ctlmsg); if ((cp=index(header.nbuf, ' ')) != NULL) *cp = '\0'; if (header.approved[0] == '\0') (void) sprintf(header.approved, "%s@%s", username, FROMSYSNAME); (void) sprintf(bfr, "%s/inews -n %s.ctl -c newgroup %s -d %s -a \"%s\"", LIB, header.nbuf, header.ctlmsg, header.distribution, header.approved); if (tty) { printf("Please type in a paragraph describing the new newsgroup.\n"); printf("End with control D as usual.\n"); } printf("%s\n", bfr); (void) fflush(stdout); (void) system(bfr); exit(0); /*NOTREACHED*/ } char firstbufname[BUFLEN]; /* * Link ARTICLE into dir for ngname and update active file. */ long localize(ngname) char *ngname; { char afline[BUFLEN]; long ngsize; long fpos; int e; char *cp; lock(); (void) rewind(actfp); clearerr(actfp); for(;;) { fpos = ftell(actfp); if (fgets(afline, sizeof afline, actfp) == NULL) { unlock(); logerr("Can't find \"%s\" in active file", ngname); return FALSE; /* No such newsgroup locally */ } if (PREFIX(afline, ngname)) { (void) sscanf(afline, "%s %ld", bfr, &ngsize); if (STRCMP(bfr, ngname) == 0) { if (ngsize < 0 || ngsize > 9999998L) { logerr("found bad ngsize %ld ng %s, setting to 1", ngsize, bfr); ngsize = 1; } break; } } } for (;;) { cp = dirname(ngname); (void) sprintf(bfr, "%s/%ld", cp, ngsize+1); #ifdef VMS /* * The effect of this code is to store the article in the first * newsgroup's directory and to put symbolic links elsewhere. * If this is the first group, firstbufname is not yet filled * in. It should be portable to other link-less systems. * epimass!jbuck */ if (firstbufname[0]) { if (vmslink(firstbufname, bfr) == 0) break; } else if (rename(ARTICLE, bfr) == 0) break; #else /* !VMS */ if (link(ARTICLE, bfr) == 0) break; #endif /* !VMS */ if (!exists(cp)) mknewsg(cp, ngname); #ifdef VMS if (firstbufname[0]) { if (vmslink(firstbufname, bfr) == 0) break; } else if (rename(ARTICLE, bfr) == 0) break; #else /* !VMS */ if (link(ARTICLE, bfr) == 0) break; #endif /* !VMS */ e = errno; /* keep log from clobbering it */ log("Cannot install article as %s: %s", bfr, errmsg(errno)); if (e != EEXIST) { logerr("Link into %s failed (%s); check dir permissions.", bfr, errmsg(e)); unlock(); return FALSE; } ngsize++; } /* * Keep track of destination file names so we can delete * the article files if the write-out fails. */ cp = malloc(strlen(bfr)+1); (void) strcpy(cp, bfr); artlinks[linkcount++] = cp; /* * This works around a bug in the 4.1bsd stdio * on fseeks to non even offsets in r+w files */ if (fpos&1) (void) rewind(actfp); (void) fseek(actfp, fpos, 0); /* * Has to be same size as old because of %07d. * This will overflow with 9,999,999 articles. */ fprintf(actfp, "%s %07ld", ngname, ngsize+1); #if defined(USG) || defined(MG1) /* * U G L Y K L U D G E * This utter piece of tripe is the only way I know of to get * around the fact that ATT BROKE standard IO in System 5.2. * Basically, you can't open a file for "r+" and then try and * write to it. This works on all "real" USGUnix systems, It will * probably break on some obscure look alike that doesnt use the * real ATT stdio.h * Don't blame me, blame ATT. stdio should have already done the * following line for us, but it doesn't * also broken in WCW MG-1 42nix 2.0 */ actfp->_flag |= _IOWRT; #endif /* USG */ (void) fflush(actfp); if (ferror(actfp)) xerror("Active file write failed"); unlock(); if (firstbufname[0] == '\0') (void) strcpy(firstbufname, bfr); (void) sprintf(bfr, "%s/%ld ", ngname, ngsize+1); addhist(bfr); return ngsize+1; } #endif /* !NFSCLIENT */ /* * Localize for each newsgroup and broadcast. */ insert() { register char *ptr; register FILE *tfp; register int c; struct srec srec; /* struct for sys file lookup */ int is_invalid = FALSE; int junked = FALSE; int exitcode = 0; long now; #ifdef DOXREFS register char *nextref = header.xref; #endif /* DOXREFS */ /* Clean up Newsgroups: line */ if (!is_ctl && mode != CREATENG) { #ifdef MODFILEONLY if (mode <= UNPROC) header.approved[0] = '\0'; #endif /* MODFILEONLY */ is_invalid = ngfcheck(username, mode == PROC, header.approved[0] != '\0'); } sprintf(bfr, "%ld", time(&now)); addhist(bfr); if (header.expdate[0]) { sprintf(bfr,"~%ld",(long) cgtdate(header.expdate)); addhist(bfr); } addhist("\t"); log("%s %s ng %s subj '%s' from %s", spool_news != DONT_SPOOL ? "queued" : (mode==PROC ? "received" : "posted"), header.ident, header.nbuf, header.title, header.from); /* Write article to temp file. */ MKTEMP(ARTICLE); tfp = xfopen(ARTICLE, "w"); linkcount = 0; #ifndef NFSCLIENT if (is_invalid) { logerr("%s: No valid newsgroups found, moved to junk", header.nbuf); if (localize("junk")) { savehist(histline); junked = TRUE; } exitcode = 1; goto writeout; } #ifdef ZAPNOTES if (STRNCMP(header.title, "Re: Orphaned Response", 21) == 0) { logerr("Orphaned Response, moved to junk"); if (localize("junk")) { savehist(histline); /* junked = TRUE; */ } exitcode = 1; goto writeout; } #endif /* ZAPNOTES */ if (time((time_t *)0) > (cgtdate(header.subdate) + HISTEXP) ){ logerr("Article too old, moved to junk"); if (localize("junk")) { savehist(histline); junked = TRUE; } exitcode = 1; goto writeout; } #endif /* !NFSCLIENT */ if (is_mod[0] != '\0' /* one of the groups is moderated */ && header.approved[0] == '\0') { /* and unapproved */ if (is_mod_file_okay) { (void) sprintf(header.approved, "%s@%s", username, FROMSYSNAME); } else { struct hbuf mhdr; FILE *mfd, *mhopen(); register char *p; char modadd[BUFLEN], *replyname(); #ifndef NFSCLIENT #ifdef DONTFOWARD if(mode == PROC) { logerr("Unapproved article in moderated group %s", is_mod); if (localize("junk")) savehist(histline); goto writeout; } #endif /* DONTFORWARD */ #endif /* !NFSCLIENT */ fprintf(stderr,"%s is moderated and may not ", is_mod); fprintf(stderr,"be posted to directly.\nYour "); fprintf(stderr, "article is being mailed to the "); fprintf(stderr, "moderator who will post it for "); fprintf(stderr, "you.\n"); /* Let's find a path to the backbone */ sprintf(bfr, "%s/mailpaths", LIB); mfd = xfopen(bfr, "r"); do { if (fscanf(mfd, "%s %s", bfr, modadd) != 2) xerror("Can't find backbone in %s/mailpaths", LIB); } while (STRCMP(bfr, "backbone") != 0 && !ngmatch(is_mod, bfr)); (void) fclose(mfd); /* fake a header for mailhdr */ mhdr.from[0] = '\0'; mhdr.replyto[0] = '\0'; p = is_mod; while (*++p) if (*p == '.') *p = '-'; sprintf(mhdr.path, modadd, is_mod); mfd = mhopen(&mhdr); if (mfd == NULL) xerror("Can't send mail to %s", mhdr.path); fprintf(mfd, "To: %s\n", replyname(&mhdr)); lhwrite(&header, mfd); putc('\n', mfd); while ((c = getc(infp)) != EOF) putc(c, mfd); mclose(mfd); log("Article mailed to %s", mhdr.path); xxit(0); } } #ifndef NFSCLIENT if (mode != PROC && spool_news != DONT_SPOOL) { if (spool_news != EXPIRE_RUNNING && ngmatch(header.nbuf,"to.all.ctl")) spool_news = DONT_SPOOL; if (spool_news != DONT_SPOOL) { fprintf(stderr, "Your article has been spooled for later processing.\n"); dospool("#! inews -S -h", TRUE); /* NOT REACHED */ } } #endif /* !NFSCLIENT */ if (is_ctl) { exitcode = control(&header); #ifndef NFSCLIENT if (exitcode != 0) savehist(histline); else localize("control"); } else { if (s_find(&srec, LOCALPATHSYSNAME) == FALSE) { logerr("Cannot find my name '%s' in %s", LOCALPATHSYSNAME, SUBFILE); strcpy(srec.s_name, "ME-dummy"); srec.s_nosend = NULL; strcpy(srec.s_nbuf, "all"); srec.s_flags[0] = '\0'; srec.s_xmit[0] = '\0'; } #ifdef DOXREFS (void) strncpy(nextref, PATHSYSNAME, BUFLEN); #endif /* DOXREFS */ for (ptr = nbuf; *ptr;) { if (ngmatch(ptr,srec.s_nbuf) || index(ptr,'.') == NULL){ #ifdef DOXREFS while (*nextref++) ; (void) sprintf(--nextref, " %s:%ld", ptr, localize(ptr)); #else /* !DOXREFS */ (void) localize(ptr); #endif /* !DOXREFS */ } while (*ptr++) ; } if (firstbufname[0] == '\0') { logerr("Newsgroups in active, but not sys"); (void) localize("junk"); } #endif /* !NFSCLIENT */ } #ifdef DOXREFS if (index(header.nbuf, NGDELIM) == NULL) header.xref[0] = '\0'; #endif /* DOXREFS */ writeout: /* Part 1 of kludge to get around article truncation problem */ if ( (c=getc(infp)) != EOF) { ungetc(c, infp); if (c == ' ' || c == '\t') { header.intnumlines++; (void) sprintf(header.numlines, "%d", header.intnumlines); } } /* End of part 1 */ if (header.expdate[0] != '\0' && mode != PROC) { /* Make sure it's fully qualified */ long t = cgtdate(header.expdate); strcpy(header.expdate, arpadate(&t)); } lhwrite(&header, tfp); if ((c = getc(infp)) != EOF) { /* Part 2 of kludge to get around article truncation problem */ if (c == ' ' || c == '\t' ) { putc('\n', tfp); if (controlmail) putc('\n', controlmail); } /* End of part 2 */ ungetc(c, infp); while (fgets(bfr, BUFLEN, infp) != NULL) { fputs(bfr, tfp); if (controlmail) fputs(bfr, controlmail); } if (bfr[strlen(bfr)-1] != '\n') { putc('\n', tfp); if (controlmail) putc('\n', controlmail); } } if (controlmail) (void) mclose(controlmail); controlmail = NULL; fflush(tfp); if (ferror(tfp)) { (void) UNLINK(ARTICLE); for (c = 0; c < linkcount; c++) (void) UNLINK(artlinks[c]); xerror("Write failed for temp file"); } else { for (c = 0; c < linkcount; c++) free(artlinks[c]); } (void) fclose(tfp); (void) fclose(infp); if (infpbuf) { (void) free(infpbuf); infpbuf = NULL; } if(exitcode == 0) { if (vflag) { printf("%s\n", header.ident); fflush(stdout); } /* article has passed all the checks, so work in background */ if (mode != PROC) { #ifndef NFSCLIENT int pid; if ((pid=fork()) < 0) xerror("Can't fork"); else if (pid > 0) _exit(0); } #ifdef SIGTTOU (void) signal(SIGTTOU, SIG_IGN); #endif /* SIGTTOU */ savehist(histline); if (header.supersedes[0] != '\0') { char *av[2]; av[0] = "cancel"; av[1] = header.supersedes; c_cancel(2, av); } broadcast(mode==PROC); #else /* NFSCLIENT */ int status; char command[LBUFLEN]; (void) sprintf(command, NFSCMDFORMAT, NFSCMDARGS); status = system(command); (void) unlink(ARTICLE); exit(status); } #endif /* NFSCLIENT */ } xxit((mode == PROC && filename[0] == '\0') ? 0 : (exitcode < 0 || junked ? 0 : exitcode)); } input(usegunk) { register char *cp; register int c; register int empty = TRUE; FILE *tmpfp; int consec_newlines = 0; int linecount = 0; int linserted = 0; MKTEMP(INFILE); tmpfp = xfopen(INFILE, "w"); for ( ; ; ) { if (SigTrap) break; if (usegunk) usegunk = FALSE; else if (fgets(bfr, BUFLEN, infp) != bfr) break; #ifndef NFSCLIENT if (mode == PROC) { /* zap trailing empty lines */ #ifdef ZAPNOTES if (empty && bfr[0] == '#' && bfr[2] == ':' && header.nf_id[0] == '\0' && header.nf_from[0] == '\0' ) { (void) strcpy(header.nf_id, bfr); (void) nstrip(header.nf_id); (void) fgets(bfr, BUFLEN, infp); (void) strcpy(header.nf_from, bfr); (void) nstrip(header.nf_from); (void) fgets(bfr, BUFLEN, infp); if (header.numlines[0]) { header.intnumlines -= 2; (void) sprintf(header.numlines, "%d", header.intnumlines); } /* Strip trailing " - (nf)" */ if ((cp = rindex(header.title, '-')) != NULL && !STRCMP(--cp, " - (nf)")) *cp = '\0'; log("Stripped notes header on %s", header.ident); continue; } #endif /* ZAPNOTES */ if (bfr[0] == '\n' || /* Bandage for older versions of inews */ bfr[1] == '\n' && !isascii(bfr[0])) { consec_newlines++; /* count it, in case */ continue; /* but don't write it*/ } /* foo! a non-empty line. write out all saved lines. */ while (consec_newlines > 0) { putc('\n', tmpfp); consec_newlines--; linecount++; } } #endif /* !NFSCLIENT */ if (mode != PROC && tty && STRCMP(bfr, ".\n") == 0) break; for (cp = bfr; c = toascii(*cp); cp++) { if (isprint(c) || isspace(c) || c == '\b') putc(c, tmpfp); if (c == '\n') linecount++; } if (bfr[0] == '>') linserted++; if (bfr[0] == '<') /* kludge to allow diff's to be posted */ linserted--; empty = FALSE; } if (*filename) { (void) fclose(infp); if (infpbuf) { (void) free(infpbuf); infpbuf = NULL; } } if (mode != PROC && index(header.nbuf,',') != NULL && strlen(header.followto) == 0) { /* * they didn't give us a followup-to * so we'll make one for them */ char *comma; /* just use the first newsgroup in the Newsgroups: line */ strcpy(header.followto, header.nbuf); comma = index(header.followto, ','); if (comma != NULL) { /* this should always be true! */ *comma = '\0'; } } if (mode != PROC && linecount > LNCNT && linserted > (linecount-linserted)) error("Article rejected: %s included more text than new text", username); if (mode != PROC && !is_ctl && header.sender[0] == '\0' && !Sflag) { int siglines = 0; char sbuf[BUFLEN]; (void) sprintf(bfr, "%s/%s", userhome, ".signature"); if (access(bfr, 4) == 0) { if ((infp = fopen(bfr, "r")) == NULL) { (void) fprintf(stderr, "inews: \"%s\" left off (must be readable by \"inews\" owner)\n", bfr); goto finish; } while (fgets(sbuf, sizeof sbuf, infp) != NULL) if (++siglines > 4) break; else if (index(sbuf, '\n') == 0) { fprintf(stderr, ".signature not included (long or unterminated line)\n"); siglines = -1; break; } if (siglines > 4) fprintf(stderr,".signature not included (> 4 lines)\n"); else if (siglines == 0) fprintf(stderr,".signature not included (empty)\n"); else if (siglines > 0) { rewind(infp); fprintf(tmpfp, "-- \n"); /* To separate */ linecount++; while ((c = getc(infp)) != EOF) { putc(c, tmpfp); if (c == '\n') linecount++; } } (void) fclose(infp); } } finish: if (ferror(tmpfp)) xerror("write failed to temp file"); (void) fclose(tmpfp); if (SigTrap) { if (tty) fprintf(stderr, "Interrupt\n"); if (tty && !empty) fwait(fsubr(newssave, (char *) NULL, (char *) NULL)); if (!tty) log("Blown away by an interrupt %d", SigTrap); xxit(1); } if (tty) fprintf(stderr, "EOT\n"); fflush(stdout); infp = fopen(INFILE, "r"); if (header.numlines[0]) { /* * Check line count if there's already one attached to * the article. Could make this a fatal error - * throwing it away if it got chopped, in hopes that * another copy will come in later with a correct * line count. But that seems a bit much for now. */ if (linecount != header.intnumlines) { if (linecount == 0) error("%s rejected. linecount expected %d, got 0", header.ident, header.intnumlines); if (linecount > header.intnumlines || linecount+consec_newlines < header.intnumlines) log("linecount expected %d, got %d", header.intnumlines, linecount+consec_newlines); } /* adjust count for blank lines we stripped off */ if (consec_newlines) { header.intnumlines -= consec_newlines; if (header.intnumlines < 0 ) header.intnumlines = 0; /* paranoia */ (void) sprintf(header.numlines, "%d", header.intnumlines); } } else { /* Attach a line count to the article. */ header.intnumlines = linecount; (void) sprintf(header.numlines, "%d", linecount); } } #ifndef NFSCLIENT /* * Make the directory for a new newsgroup. ngname should be the * full pathname of the directory. Do the other stuff too. * The various games with setuid and chown are to try to make sure * the directory is owned by NEWSUSR and NEWSGRP, which is tough to * do if you aren't root. This will work on a UCB system (which allows * setuid(geteuid()) or a USG system (which allows you to give away files * you own with chown), otherwise you have to change your kernel to allow * one of these things or run with your dirs 777 so that it doesn't matter * who owns them. */ mknewsg(fulldir, ngname) char *fulldir; char *ngname; { #ifdef USG register char *p; char parent[200]; char sysbuf[200]; struct stat sbuf; #endif /* USG */ if (ngname == NULL || !isalpha(ngname[0])) xerror("Tried to make illegal newsgroup %s", ngname); #ifdef USG /* * If the parent is 755 the setuid(getuid) * will fail, and since mkdir is suid, and our real uid is random, * the mkdir will fail. So we have to temporarily chmod it to 777. */ (void) strcpy(parent, fulldir); while (p = rindex(parent, '/')) { *p = '\0'; if (stat(parent, &sbuf) == 0) { (void) chmod(parent, 0777); break; } } #endif /* USG */ /* Create the directory */ mkparents(fulldir); if (mkdir(fulldir, 0777) < 0) xerror("Cannot mkdir %s: %s", fulldir, errmsg(errno)); #ifdef USG /* * Give away the directories we just created which were assigned * our real uid. */ (void) setuid(uid); (void) chown(fulldir, duid, dgid); (void) strcpy(sysbuf, fulldir); while (p = rindex(sysbuf, '/')) { *p = '\0'; /* stop when get to last known good parent */ if (STRCMP(sysbuf, parent) == 0) break; (void) chown(sysbuf, duid, dgid); } (void) setuid(duid); (void) chmod(parent, (int)sbuf.st_mode); /* put it back */ #endif /* USG */ log("make newsgroup %s in dir %s", ngname, fulldir); } /* * If any parent directories of this dir don't exist, create them. */ mkparents(dname) char *dname; { char buf[200]; register char *p; (void) strcpy(buf, dname); p = rindex(buf, '/'); if (p) *p = '\0'; if (exists(buf)) return; mkparents(buf); if (mkdir(buf, 0777) < 0) xerror("Can not mkdir %s: %s", buf, errmsg(errno)); } dounspool() { register DIR *dirp; register DIRECTORY_STRUCT *dir; register int foundsome; int pid, status, ret; char spbuf[BUFLEN]; #ifdef LOCKF FILE* LockFd; #endif /* LOCKF */ #if !defined(LOCKF) && !defined(BSD4_2) struct stat stbuf; #endif /* !LOCKF && ! BSD4_2 */ #ifdef VMS sprintf(spbuf, "%s/+rnews", SPOOL); #else /* !VMS */ sprintf(spbuf, "%s/.rnews", SPOOL); #endif /* !VMS */ if (chdir(spbuf) < 0) xerror("chdir(%s):%s", spbuf, errmsg(errno)); dirp = opendir("."); if (dirp == NULL) /* Boy are things screwed up */ xerror("opendir can't open .:%s", errmsg(errno)); #ifdef LOCKF LockFd = xfopen(SEQFILE, "r+w"); /* The lseek is kludge for systems with mandatory locking */ lseek(fileno(LockFd), 512L, 0); if (lockf(fileno(LockFd), F_TLOCK, 0L) < 0) { if (errno != EAGAIN && errno != EACCES) #else /* !LOCKF */ #ifdef BSD4_2 if (flock(dirp->dd_fd, LOCK_EX|LOCK_NB) < 0) { if (errno != EWOULDBLOCK) #else /* V7 */ strcat(spbuf, ".lock"); sprintf(bfr, "%s.tmp", spbuf); (void) close(creat(bfr, 0666)); ret = LINK(bfr, spbuf); status = errno; (void) UNLINK(bfr); errno = status; /* assume a dead lock if the active file is over 12 hours old */ if (ret < 0 && (errno != EEXIST || (stat(bfr, &stbuf) == 0 && (time((char *)0) - stbuf.st_mtime) < DAYS/2))) { if (errno != EEXIST) #endif /* V7 */ #endif /* !LOCKF */ xerror("Can't lock %s: %s", spbuf, errmsg(errno)); xxit(3); /* another rnews -U is running */ } do { foundsome = 0; while ((dir=readdir(dirp)) != NULL) { if (dir->d_name[0] == '.') continue; #ifdef IHCC log("Unspooling from %s", dir->d_name); #endif /* IHCC */ if ((pid=vfork()) == -1) xerror("Can't fork: %s", errmsg(errno)); if (pid == 0) { #ifdef LOGDIR char bufr[BUFSIZ]; sprintf(bufr, "%s/%s", logdir(HOME), RNEWS); execl(bufr, "rnews", "-S", "-p", dir->d_name, (char *) NULL); #else /* !LOGDIR */ execl(RNEWS, "rnews", "-S", "-p", dir->d_name, (char *) NULL); #endif /* !LOGDIR */ _exit(1); } while ((ret=wait(&status)) != pid && ret != -1) /* continue */; if (((status>>8)&0177) == 42) { /* expire has started up, shutdown rnews -U */ break; } if (status != 0) { sprintf(bfr, "../.bad/%s", dir->d_name); (void) LINK(dir->d_name, bfr); logerr("rnews failed, status %ld. Batch saved in %s/.bad/%s", (long)status, SPOOL, dir->d_name); } (void) unlink(dir->d_name); foundsome++; } rewinddir(dirp); } while (foundsome); /* keep rereading the directory until it's empty */ #ifndef LOCKF #ifndef BSD4_2 (void) UNLINK(spbuf); #endif #endif xxit(0); } #endif /* !NFSCLIENT */ #ifdef MINFREE #include <ustat.h> /* * determine if there is enough free space on the device * return 0 if there is and * anything appropriate if there is not * written by Stan Barber (sob@bcm.tmc.edu) */ space() { struct stat file; struct ustat device; if (stat(SPOOLDIR,&file)) return 1; /* can't stat spool */ if (ustat(file.st_dev, &device)) return 1; /* can't stat the device */ if(device.f_tfree < MINFREE) return 1; return 0; } #else int space() { /* I'll figure this out for BSD some other time */ return(0); } #endif /* MINFREE */