home *** CD-ROM | disk | FTP | other *** search
/ Gold Fish 1 / GoldFishApril1994_CD2.img / d4xx / d473 / cnewssrc / cnews_src.lzh / relay / control.c < prev    next >
C/C++ Source or Header  |  1990-12-30  |  11KB  |  384 lines

  1. /* :ts=4
  2.  * Implement the Usenet control messages, as per RFC 1036 (nee 850).
  3.  * These are fairly infrequent and can afford to be done by
  4.  * separate programs.  They are:
  5.  *
  6.  * cancel message-ID    restricted to Sender: else From: or root, in theory
  7.  * ihave message-ID-list remotesys    generate a sendme from message-ID-list
  8.  * sendme message-ID-list remotesys    send articles named to remotesys
  9.  * (ihave/sendme is semi-documented in the RFCs, kludgey and broken in B2.10.)
  10.  *
  11.  * newgroup groupname    must be Approved:
  12.  * rmgroup groupname    must be Approved:; allow some local control over this
  13.  * sendsys        mail to Reply-To: else From:
  14.  * senduuname        ditto
  15.  * version        ditto
  16.  *
  17.  *    $Log$
  18.  */
  19.  
  20. #include <stdio.h>
  21. #include <ctype.h>
  22. #ifdef unix
  23. # include <sys/types.h>
  24. #endif /* unix */
  25.  
  26. #include "libc.h"
  27. #include "news.h"
  28. #include "case.h"
  29. #include "config.h"
  30. #include "control.h"
  31. #include "headers.h"
  32. #include "article.h"
  33. #include "caches.h"
  34. #include "history.h"
  35.  
  36. #define NO_FILES ""
  37. #define SUBDIR binfile("ctl")        /* holds shell scripts */
  38. #ifdef SLOWCTLMATCH
  39. #define OLDCNTRL "all.all.ctl"
  40. #endif
  41. #define SFXOLDCNTRL ".ctl"
  42.  
  43. /*
  44.  * These are shell meta-characters, except for /, which is included
  45.  * since it allows people to escape from the control directory.
  46.  */
  47. #define SHELLMETAS "<>|&;({$=*?[`'\"/"
  48.  
  49. /* imports from news */
  50. extern statust snufffiles(); 
  51. extern void ihave(), sendme();
  52.  
  53. /* forwards */
  54. #ifndef AMIGA
  55.     FORWARD statust cancelart();
  56.     FORWARD void runctlmsg(), bombctlmsg();
  57. #else
  58.     FORWARD statust cancelart();
  59.     FORWARD int runctlmsg();
  60. #endif /* AMIGA */
  61.  
  62. /*
  63.  * Implement control message specified in "art".
  64.  * Because newgroup and rmgroup may modify the active file, for example,
  65.  * we must flush in-core caches to disk first and reload them afterward.
  66.  * We handle cancels in this process for speed and dbm access.
  67.  * We handle ihave & sendme in this process for dbm access and
  68.  * to work around syntax restrictions (<>).
  69.  *
  70.  * In future, one could pass header values to scripts as arguments or
  71.  * in environment, as NEWS* variables, to save time in the scripts.
  72.  */
  73. void
  74. ctlmsg(art)
  75. register struct article *art;
  76. {
  77.     register char *inname = art->a_tmpf, *ctlcmd = art->h.h_ctlcmd;
  78.     int pid, deadpid;
  79.     int wstatus;
  80.     static char nmcancel[] = "cancel ";
  81.     static char nmihave[] = "ihave ";
  82.     static char nmsendme[] = "sendme ";
  83.  
  84.     if (ctlcmd == NULL)
  85.         ctlcmd = art->h.h_etctlcmd;
  86.     if (ctlcmd == NULL)
  87.         return;
  88.     if (STREQN(ctlcmd, nmcancel, STRLEN(nmcancel))) {
  89.         art->a_status |= cancelart(ctlcmd + STRLEN(nmcancel));
  90.         return;
  91.     }
  92.     if (STREQN(ctlcmd, nmihave, STRLEN(nmihave))) {
  93.         ihave(ctlcmd + STRLEN(nmihave), art);
  94.         return;
  95.     }
  96.     if (STREQN(ctlcmd, nmsendme, STRLEN(nmsendme))) {
  97.         sendme(ctlcmd + STRLEN(nmsendme), art);
  98.         return;
  99.     }
  100.     /*
  101.      *    These "other" ctlmsgs are handled through the Amiga-tized
  102.      *    version of runctlmsg().  Basically I just call Execute()...
  103.      */
  104. #ifndef AMIGA
  105.     art->a_status |= synccaches();
  106.     (void) fflush(stdout);
  107.     (void) fflush(stderr);
  108.  
  109.     pid = fork();
  110.     if (pid == 0)                /* child? */
  111.         runctlmsg(ctlcmd, inname);
  112.     else if (pid == -1)
  113.         warning("fork failed", "");
  114.  
  115.     /* lint complains about &wstatus on 4.2+BSD; too bad, lint's wrong. */
  116.     while ((deadpid = wait(&wstatus)) != pid && deadpid != -1)
  117.         ;
  118.  
  119.     /* wrong kid returned, fork failed or child screwed up? */
  120.     if (deadpid == -1 || pid == -1 || wstatus != 0)
  121.         art->a_status |= ST_DROPPED;    /* admin got err.msg. by mail */
  122.     /* let lazy evaluation load the caches */
  123. #else /* AMIGA */
  124.     art->a_status |= synccaches();
  125.     (void) fflush(stdout);
  126.     (void) fflush(stderr);
  127.  
  128.     if (runctlmsg(ctlcmd, inname))
  129.         art->a_status |= ST_DROPPED;    /* admin got err.msg. by mail */
  130. #endif /* !AMIGA */
  131. }
  132.  
  133. STATIC boolean
  134. safecmd(cmd)            /* true if it's safe to system(3) cmd */
  135. register char *cmd;
  136. {
  137.     register char *s;
  138.  
  139.     for (s = cmd; *s != '\0'; s++)
  140.         if (STREQN(s, "..", STRLEN("..")))
  141.             return NO;
  142.     for (s = SHELLMETAS; *s != '\0'; s++)
  143.         if (strchr(cmd, *s) != NULL)
  144.             return NO;
  145.     return YES;
  146. }
  147.  
  148. /*
  149.  * In theory (RFC 1036 nee 850), we should verify that the user issuing
  150.  * the cancel (the Sender: of this article or From: if no Sender) is the
  151.  * Sender: or From: of the original article or the local super-user.
  152.  *
  153.  * In practice, this is a lot of work and since anyone can forge news
  154.  * (and thus cancel anything), not worth the effort.
  155.  *
  156.  * Ignore ST_ACCESS while cancelling an already-seen article since the
  157.  * article may have been cancelled before or may have a fake history entry
  158.  * because the cancel arrived before the article.
  159.  *
  160.  * If the article being cancelled has not been seen yet, generate a history
  161.  * file entry for the cancelled article in case it arrives after the cancel
  162.  * control.  The history file entry will cause the cancelled article to be
  163.  * rejected as a duplicate.
  164.  */
  165. STATIC statust
  166. cancelart(msgidstr)
  167. char *msgidstr;
  168. {
  169.     register char *wsp;
  170.     register char *msgid = strsave(msgidstr);
  171.     register int idbytes;
  172.     register char *wholemsgid = msgid;
  173.     register statust status = ST_OKAY;
  174.  
  175.     /* skip leading whitespace in msgid */
  176.     while (*msgid != '\0' && isascii(*msgid) && isspace(*msgid))
  177.         ++msgid;
  178.     /* replace trailing whitespace with NULs; `wsp >= msgid' is not safe */
  179.     idbytes = strlen(msgid);
  180.     for (wsp = msgid + idbytes - 1; idbytes-- > 0 &&
  181.         isascii(*wsp) && isspace(*wsp); --wsp)
  182.         *wsp = '\0';
  183.  
  184.     printf("Attempting to cancel article %s\n", msgid);
  185.     if (alreadyseen(msgid)) {
  186.         register char *histent, *filelist;
  187.  
  188.         histent = gethistory(msgid);
  189.         if (histent != NULL && (filelist = findfiles(histent)) != NULL) {
  190.             status |= snufffiles(filelist) & ~ST_ACCESS;
  191.             fprintf(stderr,
  192.                 "  removed files \"%s\"; status 0x%04X\n", filelist, status);
  193.         }
  194.     } else {
  195.         status |= fakehist(msgid, DEFEXP, NO_FILES);    /* start log */
  196.         (void) putchar('\n');        /* end log line */
  197.     }
  198.     free(wholemsgid);
  199.     return status;
  200. }
  201.  
  202. /*
  203.  * Execute a non-builtin control message by searching $NEWSCTL/bin and
  204.  * $NEWSBIN/ctl for the command named by the control message.
  205.  * runctlmsg is called from a child of relaynews, so it must always
  206.  * call _exit() rather than exit() to avoid flushing stdio buffers.
  207.  *
  208.  * Enforce at least minimal security: the environment was standardised at
  209.  * startup, including PATH and IFS; close non-standard file descriptors;
  210.  * reject shell metacharacters in ctlcmd.
  211.  */
  212.  
  213. #ifdef AMIGA
  214. execute(register char *cmd, char *ctlcmd, char *inname)
  215. {
  216.     register int last;
  217.     register char *cmdline;
  218.  
  219.     if (last = strcspn(ctlcmd, " \t\n")) {
  220.         strncpy(cmd, ctlcmd, last);
  221.         cmd[last] = '\0';
  222.     } else {
  223.         last = strlen(ctlcmd);
  224.         strcpy(cmd, ctlcmd);
  225.     }
  226.     if ((cmdline = envparm(cmd)) == NULL)
  227.         sprintf(cmd+last, "<%s %s", inname, ctlcmd+last);
  228.     else
  229.         sprintf(cmd, cmdline, inname, ctlcmd+last);
  230.     return( !Execute(cmd, 0L, 0L) );
  231. }
  232. #endif /* AMIGA */
  233.  
  234.  
  235. #ifdef AMIGA
  236.     STATIC int runctlmsg(ctlcmd, inname)
  237. #else
  238.     STATIC void runctlmsg(ctlcmd, inname)            /* child process */
  239. #endif
  240. register char *ctlcmd, *inname;
  241. {
  242.     register char *cmd;
  243.     register int cmdstat;
  244.  
  245. #ifdef AMIGA
  246. # define EXIT    return
  247. #else
  248. # define EXIT    _exit
  249. #endif
  250.  
  251. #ifndef AMIGA
  252.     closeall(1);
  253.     if (!safecmd(ctlcmd)) {
  254.         (void) fprintf(stderr,
  255.             "%s: control `%s' looks unsafe to execute\n", progname, ctlcmd);
  256.         (void) fflush(stderr);
  257.         _exit(1);
  258.     }
  259.     cmd = malloc((unsigned) strlen("PATH=") + strlen(ctlfile("bin")) +
  260.         strlen(":") + strlen(SUBDIR) + strlen(";") + strlen(ctlcmd) +
  261.         strlen(" <") + strlen(inname) + SIZENUL);
  262.     if (cmd == NULL) {
  263.         warning("can't allocate memory in runctlmsg", "");
  264.         (void) fflush(stderr);
  265.         _exit(1);
  266.     }
  267.     (void) strcpy(cmd, "PATH=");
  268.     (void) strcat(cmd, ctlfile("bin"));
  269.     (void) strcat(cmd, ":");
  270.     (void) strcat(cmd, SUBDIR);
  271.     (void) strcat(cmd, ";");
  272.     (void) strcat(cmd, ctlcmd);
  273.     (void) strcat(cmd, " <");
  274.     (void) strcat(cmd, inname);
  275.  
  276.     cmdstat = system(cmd);
  277.     if (cmdstat != 0)
  278.         bombctlmsg(cmd, cmdstat);
  279. #else
  280.     if (!safecmd(ctlcmd)) {
  281.         (void) fprintf(stderr,
  282.             "%s: control `%s' looks unsafe to execute\n", progname, ctlcmd);
  283.         EXIT(1);
  284.     }
  285.     cmd = malloc(strlen(ctlcmd) + STRLEN(" <") + strlen(inname) + 4);
  286.     if (cmd == NULL) {
  287.         warning("can't allocate memory in runctlmsg", "");
  288.         EXIT(1);
  289.     }
  290.     cmdstat = execute(cmd, ctlcmd, inname);
  291.     if (cmdstat != 0) {
  292.         char *msg = malloc(strlen(progname) + strlen(cmd) + 60);
  293.         sprintf(msg, "%s \"%s: control message `%s' unsuccessful\" %s",
  294.             envparm("SENDMAIL"), progname, cmd, newsmaster());
  295.         warning("control message `%s' unsuccessful", cmd);
  296.         (void) Execute(msg, 0L, 0L);
  297.         free(msg);
  298.         free(cmd);
  299.         EXIT(1);
  300.     }
  301. #endif /* AMIGA */
  302.     free(cmd);
  303.     EXIT(0);
  304. }
  305.  
  306. #ifndef AMIGA
  307. /*
  308.  * Notify the local news administrator by mail that "cmd" failed
  309.  * with "cmdstat" status, and _exit with bad status (again avoid stdio
  310.  * buffer flushing in the child).
  311.  */
  312. STATIC void
  313. bombctlmsg(cmd, cmdstat)
  314. char *cmd;
  315. int cmdstat;
  316. {
  317.     register char *mailcmd;
  318.     register FILE *mailf;
  319.     
  320.     mailcmd = malloc((unsigned)strlen("PATH=") + strlen(newspath()) +
  321.         strlen(" mail ") + strlen(newsmaster()) + SIZENUL);
  322.     if (mailcmd == NULL) {
  323.         warning("can't allocate memory in bombctlmsg", "");
  324.         (void) fflush(stderr);
  325.         _exit(1);
  326.     }
  327.     (void) sprintf(mailcmd, "PATH=%s mail %s", newspath(), newsmaster());
  328.     mailf = popen(mailcmd, "w");
  329.     if (mailf == NULL)
  330.         mailf = stderr;
  331.     (void) fprintf(mailf,
  332.         "%s: control message `%s' exited with status 0%o\n",
  333.         progname, cmd, cmdstat);
  334.     (void) fflush(mailf);
  335.     if (mailf != stderr)
  336.         (void) pclose(mailf);
  337.     free(mailcmd);
  338.     _exit(1);
  339. }
  340. #endif /* !AMIGA */
  341.  
  342. STATIC boolean
  343. oldctl(hdrs)                /* true iff ngs match OLDCNTRL */
  344. register struct headers *hdrs;
  345. {
  346. #ifdef SLOWCTLMATCH
  347.     return ngmatch(OLDCNTRL, hdrs->h_ngs);
  348. #else
  349.     register int ngslen = strlen(hdrs->h_ngs);
  350.  
  351.     if (ngslen < STRLEN(SFXOLDCNTRL))    /* ngs too short */
  352.         return NO;
  353.     else                    /* check for .ctl suffix */
  354.         /*
  355.          * This is more general than RFC 850 specifies, but this
  356.          * generality seems harmless.  This doesn't work for e.g.
  357.          * x.y.ctl,z.q, which is a darn shame, but that's a violation
  358.          * of common sense.
  359.          */
  360.         return STREQ(&hdrs->h_ngs[ngslen-STRLEN(SFXOLDCNTRL)],
  361.             SFXOLDCNTRL);
  362. #endif                        /* SLOWCTLMATCH */
  363. }
  364.  
  365. hackoldctl(hdrs)            /* Handle the all.all.ctl hack. */
  366. register struct headers *hdrs;
  367. {
  368.     if (hdrs->h_ctlcmd == NULL && oldctl(hdrs))
  369.         hdrs->h_ctlcmd = strsave(hdrs->h_subj);
  370. }
  371.  
  372. char *
  373. hackhybrid(line)
  374. register char *line;
  375. {
  376.     static char stupersedes[] = "Supersedes:";
  377.     static char alsocan[] =     "Also-Control: cancel ";
  378.  
  379.     if (CISTREQN(line, stupersedes, STRLEN(stupersedes)))
  380.         return str3save(alsocan, "", &line[STRLEN(stupersedes)]);
  381.     else
  382.         return strsave(line);
  383. }
  384.