home *** CD-ROM | disk | FTP | other *** search
/ Complete Linux / Complete Linux.iso / docs / system / mail / delivery / deliver.tz / deliver / dfile.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-12-07  |  11.8 KB  |  622 lines

  1. /* $Header: dfile.c,v 2.7 90/03/06 12:21:12 chip Exp $
  2.  *
  3.  * Filter destination(s) through delivery file(s).
  4.  *
  5.  * $Log:    dfile.c,v $
  6.  * Revision 2.7  90/03/06  12:21:12  chip
  7.  * Move logging into log.c and address parsing into addr.c.
  8.  * New: error delivery file for messages that fail.
  9.  * Major rearrangement of delivery file code.
  10.  * 
  11.  * Revision 2.6  90/02/23  16:35:54  chip
  12.  * \Fix problems determining legality of user references.
  13.  * 
  14.  * Revision 2.5  90/02/23  14:16:44  chip
  15.  * Support "#!" in delivery files.
  16.  * Support "user|program" and "user?error" from delivery files.
  17.  * Improve debugging and error message formatting.
  18.  * Rearrange code for clarity.
  19.  * 
  20.  * Revision 2.4  89/11/10  12:23:52  network
  21.  * Be more selective about trying to deliver to MBX_UNDEL.
  22.  * 
  23.  * Revision 2.3  89/11/01  12:31:11  network
  24.  * Use the new exists() function.
  25.  * 
  26.  * Revision 2.2  89/09/29  18:17:59  network
  27.  * Save message when delivery file produces no output,
  28.  * unless delivery file output the "DROP" string.
  29.  * Don't recopy temp files for sys and post-user delfiles.
  30.  * 
  31.  * Revision 2.1  89/06/09  12:25:24  network
  32.  * Update RCS revisions.
  33.  * 
  34.  * Revision 1.10  89/06/09  12:23:49  network
  35.  * Baseline for 2.0 release.
  36.  * 
  37.  */
  38.  
  39. #include "deliver.h"
  40. #include <sys/stat.h>
  41.  
  42. /*----------------------------------------------------------------------
  43.  * Filter all valid destinations through the system delivery file.
  44.  * Return 1 (executed), 0 (not executed), -1 (no attempt made).
  45.  */
  46.  
  47. static    int    sys_ac;
  48. static    char    **sys_av;
  49.  
  50. char **
  51. sys_args(pac)
  52. int    *pac;
  53. {
  54.     char    **fav;
  55.     int    fac, a;
  56.  
  57.     fav = (char **) zalloc(sys_ac * sizeof(char **));
  58.     fac = 0;
  59.  
  60.     for (a = 0; a < sys_ac; ++a)
  61.     {
  62.         char    *addr;
  63.  
  64.         addr = sys_av[a];
  65.  
  66.         /* Note invalid address(es); report them later. */
  67.  
  68.         if (!addr_clean(addr))
  69.             (void) dest(addr, CL_USER, (char *) NULL);
  70.  
  71.         /* Note non-user address(es); report them later. */
  72.  
  73.         else if (addr_class(addr) != CL_USER)
  74.             (void) addr_dest(addr, (CONTEXT *)NULL);
  75.  
  76.         /* Let the system delivery file handle the rest. */
  77.  
  78.         else
  79.             fav[fac++] = addr;
  80.     }
  81.  
  82.     /*
  83.      * If there were any good names found, let loose the delivery
  84.      * file.  Note the meaning of "good" is "well-formed", not "valid".
  85.      * Thus the system delivery file has control over the handling of
  86.      * all local deliveries, not just those to valid users.
  87.      */
  88.  
  89.     if (fac <= 0)
  90.     {
  91.         free((char *) fav);
  92.         fav = NULL;
  93.     }
  94.  
  95.     *pac = fac;
  96.     return fav;
  97. }
  98.  
  99. sys_dfile(ac, av)
  100. int     ac;
  101. char    **av;
  102. {
  103.     sys_ac = ac;
  104.     sys_av = av;
  105.     return glob_dfile("system", sys_deliver, sys_args, FALSE);
  106. }
  107.  
  108. /*----------------------------------------------------------------------
  109.  * Filter some undelivered destinations through the post-user
  110.  * delivery file.
  111.  * Return 1 (executed), 0 (not executed), -1 (no attempt made).
  112.  */
  113.  
  114. post_choose(d)
  115. DEST    *d;
  116. {
  117.     if ((d->d_class == CL_USER || d->d_class == CL_UUCP)
  118.      && (d->d_state == ST_WORKING
  119.          || (d->d_state == ST_ERROR && d->d_error == E_NSUSER)))
  120.     {
  121.         d->d_state = ST_HOLD;
  122.         return TRUE;
  123.     }
  124.  
  125.     return FALSE;
  126. }
  127.  
  128. char **
  129. post_args(pac)
  130. int    *pac;
  131. {
  132.     return choose_args(pac, post_choose);
  133. }
  134.  
  135. post_dfile()
  136. {
  137.     return glob_dfile("post-user", post_deliver, post_args, FALSE);
  138. }
  139.  
  140. /*----------------------------------------------------------------------
  141.  * Filter broken (but well-formed) destinations through the error
  142.  * delivery file.
  143.  * Return 1 (executed), 0 (not executed), -1 (no attempt made).
  144.  */
  145.  
  146. err_choose(d)
  147. DEST    *d;
  148. {
  149.     return (d->d_state == ST_ERROR);
  150. }
  151.  
  152. char **
  153. err_args(pac)
  154. int    *pac;
  155. {
  156.     return choose_args(pac, err_choose);
  157. }
  158.  
  159. err_dfile()
  160. {
  161.     return glob_dfile("error", err_deliver, err_args, TRUE);
  162. }
  163.  
  164. /*----------------------------------------------------------------------
  165.  * Execute a global delivery file given description of delivery file,
  166.  * its path, a function to get arguments, and a boolean to indicate whether
  167.  * a lack of output is a normal condition.
  168.  * Return 1 (executed), 0 (not executed), -1 (no attempt made).
  169.  */
  170.  
  171. glob_dfile(desc, dfile, args, silent_ok)
  172. char    *desc;
  173. char    *dfile;
  174. char    **(*args)();
  175. int    silent_ok;
  176. {
  177.     char    **fav;
  178.     int    fac;
  179.  
  180.     /*
  181.      * If the delivery file is missing, forget it.
  182.      */
  183.  
  184.     if (!exists(relpath(eff_ct->ct_home, dfile)))
  185.     {
  186.         if (verbose)
  187.             message("no %s delivery file\n", desc);
  188.         return -1;
  189.     }
  190.  
  191.     /*
  192.      * If we've been asked not to run delivery files, forget it.
  193.      */
  194.  
  195.     if (!rundfiles)
  196.     {
  197.         if (verbose)
  198.             message("%s delivery file disabled\n", desc);
  199.         return -1;
  200.     }
  201.  
  202.     /*
  203.      * Now we can get the argument list.
  204.      * If the list is empty, that's all she wrote.
  205.      */
  206.  
  207.     if ((fav = (*args)(&fac)) == NULL)
  208.         return 0;
  209.  
  210.     /*
  211.      * "Just do it."
  212.      * If we get nothing back from the delivery file,
  213.      * and if silence is not supposed to be permitted,
  214.      * put the message in the "undelivered" mailbox.
  215.      */
  216.  
  217.     if (run_dfile(eff_ct, dfile, fac, fav, (DEST *)NULL) <= 0
  218.         && !silent_ok)
  219.     {
  220.         if (verbose)
  221.             message("%s delivery file: no output\n", desc);
  222.         dest_undel(eff_ct->ct_name);
  223.     }
  224.  
  225.     /*
  226.      * The argument function allocated the arguments; we free them.
  227.      */
  228.  
  229.     free((char *) fav);
  230.  
  231.     /*
  232.      * Return "done".
  233.      */
  234.  
  235.     return 1;
  236. }
  237.  
  238. /*----------------------------------------------------------------------
  239.  * Generate a delivery file argument list.
  240.  * We do this by getting an array of all destinations,
  241.  * then keeping only the ones we want.
  242.  */
  243.  
  244. char **
  245. choose_args(pac, choose)
  246. int    *pac;
  247. int    (*choose)();
  248. {
  249.     DEST    *d;
  250.     char    **fav;
  251.     int     count, fac;
  252.  
  253.     if ((count = dest_count()) <= 0)
  254.         return NULL;
  255.  
  256.     fav = (char **) zalloc(count * sizeof(char *));
  257.     fac = 0;
  258.  
  259.     for (d = first_dest(); d; d = next_dest(d))
  260.     {
  261.         if ((*choose)(d))
  262.             fav[fac++] = d->d_name;
  263.     }
  264.  
  265.     if (fac <= 0)
  266.     {
  267.         free((char *) fav);
  268.         fav = NULL;
  269.     }
  270.  
  271.     *pac = fac;
  272.     return fav;
  273. }
  274.  
  275. /*----------------------------------------------------------------------
  276.  * Filter all user destinations through their local delivery files.
  277.  * Return 1 (some executed), 0 (none executed), -1 (no attempt made).
  278.  */
  279.  
  280. user_dfiles()
  281. {
  282.     DEST    *d;
  283.     int     nfound, ret;
  284.  
  285.     /*
  286.      * If we've been asked not to run delivery files, forget it.
  287.      */
  288.  
  289.     if (!rundfiles)
  290.     {
  291.         if (verbose)
  292.             message("user delivery files disabled\n");
  293.         return -1;
  294.     }
  295.  
  296.     /*
  297.      * Continue to loop through all addresses until no destination
  298.      * that needs expanding can be found.
  299.      */
  300.  
  301.     ret = 0;
  302.     do {
  303.         nfound = 0;
  304.         for (d = first_dest(); d; d = next_dest(d))
  305.         {
  306.             if (d->d_class == CL_USER
  307.              && d->d_state == ST_WORKING
  308.              && !d->d_dfdone)
  309.             {
  310.                 u_dfile(d);
  311.                 d->d_dfdone = TRUE;
  312.                 ret = 1;
  313.             }
  314.         }
  315.     } while (nfound > 0);
  316.  
  317.     return ret;
  318. }
  319.  
  320. /*----------------------------------------------------------------------
  321.  * Run the user delivery file (if any) for the specified destination.
  322.  */
  323.  
  324. u_dfile(d)
  325. DEST    *d;
  326. {
  327.     struct stat st;
  328.     CONTEXT *ct;
  329.     char    *s, *udel_path;
  330.     int    n;
  331.  
  332.     if ((ct = name_context(d->d_name)) == NULL)
  333.     {
  334.         dest_err(d, E_CTLOST);
  335.         return;
  336.     }
  337.  
  338.     /*
  339.      * If user's home directory is missing, forget it.
  340.      * If user's home directory is writable to the world,
  341.      * executing the delivery file would allow a security breach!
  342.      * Thanks to Jon Zeeff for this hint...
  343.      */
  344.  
  345.     if (stat(ct->ct_home, &st) == -1
  346.      || (st.st_mode & S_IFMT) != S_IFDIR)
  347.     {
  348.         if (verbose)
  349.             message("user %s: home directory %s is missing!\n",
  350.                 ct->ct_name, ct->ct_home);
  351.         return;
  352.     }
  353.  
  354.     if (st.st_mode & 02)
  355.     {
  356.         if (verbose)
  357.             message("user %s: home directory is writable to the world!\n",
  358.                 ct->ct_name);
  359.         return;
  360.     }
  361.  
  362.     /*
  363.      * If there is no delivery file to execute, just return.
  364.      */
  365.  
  366.     s = relpath(ct->ct_home, user_deliver);
  367.     if (!exists(s))
  368.     {
  369.         if (verbose)
  370.             message("%s has no delivery file\n", d->d_name);
  371.         return;
  372.     }
  373.     udel_path = copystr(s);
  374.  
  375.     /*
  376.      * Put this destination on hold.
  377.      * It will be ignored unless it's named by a delivery file.
  378.      */
  379.  
  380.     d->d_state = ST_HOLD;
  381.  
  382.     /*
  383.      * Time to run the file!
  384.      * If we get nothing back from the user delivery file,
  385.      * put the message in the user's "undelivered" mailbox--
  386.      * or ours, depending on the kind of failure.
  387.      */
  388.  
  389.     n = run_dfile(ct, udel_path, 1, &d->d_name, d);
  390.     if (n <= 0)
  391.     {
  392.         if (verbose)
  393.             message("u_dfile: no output\n");
  394.         dest_undel((n == 0 ? ct : eff_ct)->ct_name);
  395.     }
  396.  
  397.     free(udel_path);
  398. }
  399.  
  400. /*----------------------------------------------------------------------
  401.  * Execute a delivery file (global or user).
  402.  * Return the count of valid destinations we got back from it.
  403.  * If delivering to MBX_UNDEL is possible, errors return zero.
  404.  * Otherwise, errors return -1.
  405.  */
  406.  
  407. int
  408. run_dfile(ct, dfile, fac, fav, d)
  409. CONTEXT *ct;
  410. char    *dfile;
  411. int     fac;
  412. char    **fav;
  413. DEST    *d;
  414. {
  415.     FILE    *fp;
  416.     char    **av;
  417.     int    ac, a, fd, linecount;
  418.     static  char    buf[BUFSIZ];
  419.  
  420.     if (!ct)
  421.         return -1;
  422.  
  423.     if (! ok_context(eff_uid, real_uid, real_gid, ct))
  424.     {
  425.         if (d)
  426.             dest_err(d, E_CTPERM);
  427.         else
  428.             message("No permissions to run as %s\n", ct->ct_name);
  429.  
  430.         return -1;
  431.     }
  432.  
  433.     /*
  434.      * We trust the superuser not to stomp on the temp files.
  435.      * Other users get copies of the temp files.
  436.      */
  437.  
  438.     if (ct->ct_uid == 0)
  439.     {
  440.         /* We trust the superuser; don't bother copying again */
  441.  
  442.         if (dont_copy() < 0)
  443.             return -1;
  444.     }
  445.     else
  446.     {
  447.         /* Copy the temp files again */
  448.  
  449.         if (copy_again() < 0)
  450.             return -1;
  451.  
  452.         /* Allow the given user to own and read the copies */
  453.  
  454.         if (give_temps(ct) < 0)
  455.             return -1;
  456.     }
  457.  
  458.     /* Produce the arguments in the form we need. */
  459.  
  460.     av = (char **) zalloc((fac + 4) * sizeof(char *));
  461.     ac = 0;
  462.  
  463.     /* Process the "#!" hack. */
  464.  
  465.     if ((fd = open(dfile, O_RDONLY)) != -1)
  466.     {
  467.         char    *p;
  468.         int    rd;
  469.         static char hashbang[64]; /* arbitrary */
  470.  
  471.         rd = read(fd, hashbang, sizeof(hashbang) - 1);
  472.         (void) close(fd);
  473.         hashbang[rd > 0 ? rd : 0] = 0;
  474.  
  475.         if ((p = strchr(hashbang, '\n')) != NULL
  476.          && hashbang[0] == '#'
  477.          && hashbang[1] == '!')
  478.         {
  479.             *p = 0;
  480.  
  481.             /* Interpreter. */
  482.  
  483.             p = hashbang + 2;
  484.             while (isspace(*p))
  485.                 ++p;
  486.             av[ac++] = p;
  487.             while (*p && !isspace(*p))
  488.                 ++p;
  489.             if (*p)
  490.                 *p++ = 0;
  491.  
  492.             /* Only one argument; sorry. */
  493.  
  494.             while (isspace(*p))
  495.                 ++p;
  496.             if (*p)
  497.                 av[ac++] = p;
  498.         }
  499.     }
  500.  
  501.     /*
  502.      * If no "#!" found, use the default shell.
  503.      * Then add the delivery file, address(es), and a NULL.
  504.      */
  505.  
  506.     if (ac == 0)
  507.         av[ac++] = shell;
  508.     av[ac++] = dfile;
  509.     for (a = 0; a < fac; ++a)
  510.         av[ac++] = fav[a];
  511.     av[ac] = NULL;
  512.  
  513.     /* Here we go! */
  514.  
  515.     if (verbose)
  516.         message("Processing delivery file as %s\n", ct->ct_name);
  517.  
  518.     fp = ct_fopenv(ct, av[0], av, "r");
  519.  
  520.     /* We don't need the argument vector any more. */
  521.  
  522.     free((char *) av);
  523.  
  524.     /* If something went wrong, bail out now. */
  525.  
  526.     if (fp == NULL)
  527.     {
  528.         error("can't execute delivery file as %s\n", ct->ct_name);
  529.         return 0;
  530.     }
  531.  
  532.     /*
  533.      * Read the standard output of the delivery file.
  534.      */
  535.  
  536.     linecount = 0;
  537.  
  538.     while (fgets(buf, GETSIZE(buf), fp) != NULL)
  539.     {
  540.         DEST    *nd;
  541.         char    *p;
  542.  
  543.         if ((p = strchr(buf, '\n')) != NULL)
  544.             *p = 0;
  545.         else
  546.         {
  547.             int c;
  548.  
  549.             while ((c = fgetc(fp)) != '\n' && c != EOF)
  550.                 ; /* keep reading */
  551.  
  552.             error("invalid line from delivery file: '%s'\n", buf);
  553.             continue;
  554.         }
  555.  
  556.         /* Debugging message: display input line. */
  557.  
  558.         if (verbose)
  559.             message("\t'%s'\n", buf);
  560.  
  561.         /* Okay-to-drop directive is a special case. */
  562.  
  563.         if (strcmp(buf, DFILE_DROP) == 0)
  564.         {
  565.             message("\tDelivery file says OK to drop\n");
  566.             ++linecount;
  567.             continue;
  568.         }
  569.  
  570.         /* Parse destination; if none, look for next line. */
  571.  
  572.         if ((nd = addr_dest(buf, ct)) == NULL)
  573.             continue;
  574.  
  575.         /* We got some output; remember that. */
  576.  
  577.         ++linecount;
  578.  
  579.         /* If destination was on hold, it's not so anymore. */
  580.  
  581.         if (nd->d_state == ST_HOLD)
  582.             nd->d_state = ST_WORKING;
  583.     }
  584.  
  585.     (void) ct_fclose(fp);
  586.  
  587.     return linecount;
  588. }
  589.  
  590. /*----------------------------------------------------------------------
  591.  * Make the temp files readable in the given context.
  592.  * This is needed because the temps are readable by owner only.
  593.  */
  594.  
  595. int
  596. give_temps(ct)
  597. CONTEXT *ct;
  598. {
  599.     int     err, t;
  600.  
  601.     if (!ct)
  602.         return -1;
  603.  
  604.     err = 0;
  605.     for (t = T_HDRCOPY; t <= T_BODYCOPY; ++t)
  606.     {
  607.         if (chmod(tfile[t], 0600) == -1)
  608.         {
  609.             syserr("can't chmod %s", tfile[t]);
  610.             ++err;
  611.         }
  612.         if (chown(tfile[t], ct->ct_uid, ct->ct_gid) == -1)
  613.         {
  614.             syserr("can't chown %s to %d/%d",
  615.                 tfile[t], ct->ct_uid, ct->ct_gid);
  616.             ++err;
  617.         }
  618.     }
  619.  
  620.     return err ? -1 : 0;
  621. }
  622.