home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Gold Fish 1
/
GoldFishApril1994_CD2.img
/
d4xx
/
d473
/
cnewssrc
/
cnews_src.lzh
/
relay
/
control.c
< prev
next >
Wrap
C/C++ Source or Header
|
1990-12-30
|
11KB
|
384 lines
/* :ts=4
* Implement the Usenet control messages, as per RFC 1036 (nee 850).
* These are fairly infrequent and can afford to be done by
* separate programs. They are:
*
* cancel message-ID restricted to Sender: else From: or root, in theory
* ihave message-ID-list remotesys generate a sendme from message-ID-list
* sendme message-ID-list remotesys send articles named to remotesys
* (ihave/sendme is semi-documented in the RFCs, kludgey and broken in B2.10.)
*
* newgroup groupname must be Approved:
* rmgroup groupname must be Approved:; allow some local control over this
* sendsys mail to Reply-To: else From:
* senduuname ditto
* version ditto
*
* $Log$
*/
#include <stdio.h>
#include <ctype.h>
#ifdef unix
# include <sys/types.h>
#endif /* unix */
#include "libc.h"
#include "news.h"
#include "case.h"
#include "config.h"
#include "control.h"
#include "headers.h"
#include "article.h"
#include "caches.h"
#include "history.h"
#define NO_FILES ""
#define SUBDIR binfile("ctl") /* holds shell scripts */
#ifdef SLOWCTLMATCH
#define OLDCNTRL "all.all.ctl"
#endif
#define SFXOLDCNTRL ".ctl"
/*
* These are shell meta-characters, except for /, which is included
* since it allows people to escape from the control directory.
*/
#define SHELLMETAS "<>|&;({$=*?[`'\"/"
/* imports from news */
extern statust snufffiles();
extern void ihave(), sendme();
/* forwards */
#ifndef AMIGA
FORWARD statust cancelart();
FORWARD void runctlmsg(), bombctlmsg();
#else
FORWARD statust cancelart();
FORWARD int runctlmsg();
#endif /* AMIGA */
/*
* Implement control message specified in "art".
* Because newgroup and rmgroup may modify the active file, for example,
* we must flush in-core caches to disk first and reload them afterward.
* We handle cancels in this process for speed and dbm access.
* We handle ihave & sendme in this process for dbm access and
* to work around syntax restrictions (<>).
*
* In future, one could pass header values to scripts as arguments or
* in environment, as NEWS* variables, to save time in the scripts.
*/
void
ctlmsg(art)
register struct article *art;
{
register char *inname = art->a_tmpf, *ctlcmd = art->h.h_ctlcmd;
int pid, deadpid;
int wstatus;
static char nmcancel[] = "cancel ";
static char nmihave[] = "ihave ";
static char nmsendme[] = "sendme ";
if (ctlcmd == NULL)
ctlcmd = art->h.h_etctlcmd;
if (ctlcmd == NULL)
return;
if (STREQN(ctlcmd, nmcancel, STRLEN(nmcancel))) {
art->a_status |= cancelart(ctlcmd + STRLEN(nmcancel));
return;
}
if (STREQN(ctlcmd, nmihave, STRLEN(nmihave))) {
ihave(ctlcmd + STRLEN(nmihave), art);
return;
}
if (STREQN(ctlcmd, nmsendme, STRLEN(nmsendme))) {
sendme(ctlcmd + STRLEN(nmsendme), art);
return;
}
/*
* These "other" ctlmsgs are handled through the Amiga-tized
* version of runctlmsg(). Basically I just call Execute()...
*/
#ifndef AMIGA
art->a_status |= synccaches();
(void) fflush(stdout);
(void) fflush(stderr);
pid = fork();
if (pid == 0) /* child? */
runctlmsg(ctlcmd, inname);
else if (pid == -1)
warning("fork failed", "");
/* lint complains about &wstatus on 4.2+BSD; too bad, lint's wrong. */
while ((deadpid = wait(&wstatus)) != pid && deadpid != -1)
;
/* wrong kid returned, fork failed or child screwed up? */
if (deadpid == -1 || pid == -1 || wstatus != 0)
art->a_status |= ST_DROPPED; /* admin got err.msg. by mail */
/* let lazy evaluation load the caches */
#else /* AMIGA */
art->a_status |= synccaches();
(void) fflush(stdout);
(void) fflush(stderr);
if (runctlmsg(ctlcmd, inname))
art->a_status |= ST_DROPPED; /* admin got err.msg. by mail */
#endif /* !AMIGA */
}
STATIC boolean
safecmd(cmd) /* true if it's safe to system(3) cmd */
register char *cmd;
{
register char *s;
for (s = cmd; *s != '\0'; s++)
if (STREQN(s, "..", STRLEN("..")))
return NO;
for (s = SHELLMETAS; *s != '\0'; s++)
if (strchr(cmd, *s) != NULL)
return NO;
return YES;
}
/*
* In theory (RFC 1036 nee 850), we should verify that the user issuing
* the cancel (the Sender: of this article or From: if no Sender) is the
* Sender: or From: of the original article or the local super-user.
*
* In practice, this is a lot of work and since anyone can forge news
* (and thus cancel anything), not worth the effort.
*
* Ignore ST_ACCESS while cancelling an already-seen article since the
* article may have been cancelled before or may have a fake history entry
* because the cancel arrived before the article.
*
* If the article being cancelled has not been seen yet, generate a history
* file entry for the cancelled article in case it arrives after the cancel
* control. The history file entry will cause the cancelled article to be
* rejected as a duplicate.
*/
STATIC statust
cancelart(msgidstr)
char *msgidstr;
{
register char *wsp;
register char *msgid = strsave(msgidstr);
register int idbytes;
register char *wholemsgid = msgid;
register statust status = ST_OKAY;
/* skip leading whitespace in msgid */
while (*msgid != '\0' && isascii(*msgid) && isspace(*msgid))
++msgid;
/* replace trailing whitespace with NULs; `wsp >= msgid' is not safe */
idbytes = strlen(msgid);
for (wsp = msgid + idbytes - 1; idbytes-- > 0 &&
isascii(*wsp) && isspace(*wsp); --wsp)
*wsp = '\0';
printf("Attempting to cancel article %s\n", msgid);
if (alreadyseen(msgid)) {
register char *histent, *filelist;
histent = gethistory(msgid);
if (histent != NULL && (filelist = findfiles(histent)) != NULL) {
status |= snufffiles(filelist) & ~ST_ACCESS;
fprintf(stderr,
" removed files \"%s\"; status 0x%04X\n", filelist, status);
}
} else {
status |= fakehist(msgid, DEFEXP, NO_FILES); /* start log */
(void) putchar('\n'); /* end log line */
}
free(wholemsgid);
return status;
}
/*
* Execute a non-builtin control message by searching $NEWSCTL/bin and
* $NEWSBIN/ctl for the command named by the control message.
* runctlmsg is called from a child of relaynews, so it must always
* call _exit() rather than exit() to avoid flushing stdio buffers.
*
* Enforce at least minimal security: the environment was standardised at
* startup, including PATH and IFS; close non-standard file descriptors;
* reject shell metacharacters in ctlcmd.
*/
#ifdef AMIGA
execute(register char *cmd, char *ctlcmd, char *inname)
{
register int last;
register char *cmdline;
if (last = strcspn(ctlcmd, " \t\n")) {
strncpy(cmd, ctlcmd, last);
cmd[last] = '\0';
} else {
last = strlen(ctlcmd);
strcpy(cmd, ctlcmd);
}
if ((cmdline = envparm(cmd)) == NULL)
sprintf(cmd+last, "<%s %s", inname, ctlcmd+last);
else
sprintf(cmd, cmdline, inname, ctlcmd+last);
return( !Execute(cmd, 0L, 0L) );
}
#endif /* AMIGA */
#ifdef AMIGA
STATIC int runctlmsg(ctlcmd, inname)
#else
STATIC void runctlmsg(ctlcmd, inname) /* child process */
#endif
register char *ctlcmd, *inname;
{
register char *cmd;
register int cmdstat;
#ifdef AMIGA
# define EXIT return
#else
# define EXIT _exit
#endif
#ifndef AMIGA
closeall(1);
if (!safecmd(ctlcmd)) {
(void) fprintf(stderr,
"%s: control `%s' looks unsafe to execute\n", progname, ctlcmd);
(void) fflush(stderr);
_exit(1);
}
cmd = malloc((unsigned) strlen("PATH=") + strlen(ctlfile("bin")) +
strlen(":") + strlen(SUBDIR) + strlen(";") + strlen(ctlcmd) +
strlen(" <") + strlen(inname) + SIZENUL);
if (cmd == NULL) {
warning("can't allocate memory in runctlmsg", "");
(void) fflush(stderr);
_exit(1);
}
(void) strcpy(cmd, "PATH=");
(void) strcat(cmd, ctlfile("bin"));
(void) strcat(cmd, ":");
(void) strcat(cmd, SUBDIR);
(void) strcat(cmd, ";");
(void) strcat(cmd, ctlcmd);
(void) strcat(cmd, " <");
(void) strcat(cmd, inname);
cmdstat = system(cmd);
if (cmdstat != 0)
bombctlmsg(cmd, cmdstat);
#else
if (!safecmd(ctlcmd)) {
(void) fprintf(stderr,
"%s: control `%s' looks unsafe to execute\n", progname, ctlcmd);
EXIT(1);
}
cmd = malloc(strlen(ctlcmd) + STRLEN(" <") + strlen(inname) + 4);
if (cmd == NULL) {
warning("can't allocate memory in runctlmsg", "");
EXIT(1);
}
cmdstat = execute(cmd, ctlcmd, inname);
if (cmdstat != 0) {
char *msg = malloc(strlen(progname) + strlen(cmd) + 60);
sprintf(msg, "%s \"%s: control message `%s' unsuccessful\" %s",
envparm("SENDMAIL"), progname, cmd, newsmaster());
warning("control message `%s' unsuccessful", cmd);
(void) Execute(msg, 0L, 0L);
free(msg);
free(cmd);
EXIT(1);
}
#endif /* AMIGA */
free(cmd);
EXIT(0);
}
#ifndef AMIGA
/*
* Notify the local news administrator by mail that "cmd" failed
* with "cmdstat" status, and _exit with bad status (again avoid stdio
* buffer flushing in the child).
*/
STATIC void
bombctlmsg(cmd, cmdstat)
char *cmd;
int cmdstat;
{
register char *mailcmd;
register FILE *mailf;
mailcmd = malloc((unsigned)strlen("PATH=") + strlen(newspath()) +
strlen(" mail ") + strlen(newsmaster()) + SIZENUL);
if (mailcmd == NULL) {
warning("can't allocate memory in bombctlmsg", "");
(void) fflush(stderr);
_exit(1);
}
(void) sprintf(mailcmd, "PATH=%s mail %s", newspath(), newsmaster());
mailf = popen(mailcmd, "w");
if (mailf == NULL)
mailf = stderr;
(void) fprintf(mailf,
"%s: control message `%s' exited with status 0%o\n",
progname, cmd, cmdstat);
(void) fflush(mailf);
if (mailf != stderr)
(void) pclose(mailf);
free(mailcmd);
_exit(1);
}
#endif /* !AMIGA */
STATIC boolean
oldctl(hdrs) /* true iff ngs match OLDCNTRL */
register struct headers *hdrs;
{
#ifdef SLOWCTLMATCH
return ngmatch(OLDCNTRL, hdrs->h_ngs);
#else
register int ngslen = strlen(hdrs->h_ngs);
if (ngslen < STRLEN(SFXOLDCNTRL)) /* ngs too short */
return NO;
else /* check for .ctl suffix */
/*
* This is more general than RFC 850 specifies, but this
* generality seems harmless. This doesn't work for e.g.
* x.y.ctl,z.q, which is a darn shame, but that's a violation
* of common sense.
*/
return STREQ(&hdrs->h_ngs[ngslen-STRLEN(SFXOLDCNTRL)],
SFXOLDCNTRL);
#endif /* SLOWCTLMATCH */
}
hackoldctl(hdrs) /* Handle the all.all.ctl hack. */
register struct headers *hdrs;
{
if (hdrs->h_ctlcmd == NULL && oldctl(hdrs))
hdrs->h_ctlcmd = strsave(hdrs->h_subj);
}
char *
hackhybrid(line)
register char *line;
{
static char stupersedes[] = "Supersedes:";
static char alsocan[] = "Also-Control: cancel ";
if (CISTREQN(line, stupersedes, STRLEN(stupersedes)))
return str3save(alsocan, "", &line[STRLEN(stupersedes)]);
else
return strsave(line);
}