home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1993 July / Internet Tools.iso / RockRidge / mail / smail-3.1.28 / src / queue.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-09-20  |  31.0 KB  |  1,261 lines

  1. /* @(#)src/queue.c    1.22 9/20/92 12:55:03 */
  2.  
  3. /*
  4.  *    Copyright (C) 1987, 1988 Ronald S. Karr and Landon Curt Noll
  5.  *    Copyright (C) 1992  Ronald S. Karr
  6.  * 
  7.  * See the file COPYING, distributed with smail, for restriction
  8.  * and warranty information.
  9.  */
  10.  
  11. /*
  12.  * queue.c:
  13.  *    operations on the queue, such as reading them and writing to
  14.  *    the queue through the spooling functions, and scanning for work.
  15.  *
  16.  *    external functions: queue_message, read_message, write_body,
  17.  *                log_incoming, check_grade, scan_queue,
  18.  *                swallow_smtp
  19.  */
  20. #include <stdio.h>
  21. #include <ctype.h>
  22. #include <pwd.h>
  23. #include <grp.h>
  24. #include "defs.h"
  25. #include "smail.h"
  26. #include "spool.h"
  27. #include "addr.h"
  28. #include "main.h"
  29. #include "log.h"
  30. #include "field.h"
  31. #include "dys.h"
  32. #include "exitcodes.h"
  33. #ifndef DEPEND
  34. # include "debug.h"
  35. # include "extern.h"
  36. #endif
  37.  
  38. /* variables local to this file */
  39. static long start_msg;            /* where message body begins */
  40. long msg_body_size;            /* size of message body */
  41.  
  42. #define MAXUSER 16            /* maximum length of user name */
  43.  
  44. /* variables exported from this file */
  45. int msg_grade;                /* grade level for this message */
  46.  
  47. /* functions local to this file */
  48. static int queue_envelope();
  49. static int queue_message_internal();
  50. static int queue_message_with_dots();
  51. static int queue_message_simple();
  52. static int fixup_login_user();
  53. static int put_line();
  54. static int read_from_lines();
  55. static char *forward_token();
  56. static char *backward_token();
  57. static void finish_sender();
  58. static int queue_compare();
  59.  
  60. char *getenv();
  61.  
  62.  
  63. /*
  64.  * queue_message - spool a message.
  65.  *
  66.  * spool a message from a stdio file, possibly obeying the hidden-dot
  67.  * protocol from the input.  It will always turn cr/lf into just a
  68.  * newline, while never requiring this to be at the end of lines.
  69.  *
  70.  * If a caller wishes to log any errors that occured during spooling,
  71.  * it must call log_spool_errors.
  72.  *
  73.  * return SUCCEED or FAIL.
  74.  */
  75. int
  76. queue_message(f, dusage, list, error)
  77.     register FILE *f;            /* input file */
  78.     enum dot_usage dusage;        /* how to treat dots */
  79.     struct addr *list;            /* list of recipient addr structures */
  80.     char **error;            /* return error message here */
  81. {
  82.     DEBUG(DBG_QUEUE_HI, "queue_message called\n");
  83.     /* create the spool file */
  84.     if (creat_spool() == FAIL) {
  85.     *error = "spool file creat error";
  86.     if (dusage == SMTP_DOTS) {
  87.         (void) swallow_smtp(f);
  88.     }
  89.     return FAIL;
  90.     }
  91.     errno = 0;
  92.     *error = NULL;
  93.     /* fill it with stuff */
  94.     if (queue_envelope(list) == FAIL ||
  95.     queue_message_internal(f, dusage, error) == FAIL ||
  96.     write_spool() == FAIL ||
  97.     fixup_login_user() == FAIL)
  98.     {
  99.     if (*error == NULL) {
  100.         *error = "spool file write error";
  101.     }
  102.     unlink_spool();
  103.     return FAIL;
  104.     }
  105. #ifdef HAVE_FSYNC
  106.     if (fsync(spoolfile) < 0) {
  107.     *error = "fsync write failure";
  108.     unlink_spool();
  109.     return FAIL;
  110.     }
  111. #endif
  112.  
  113.     return SUCCEED;
  114. }
  115.  
  116. /*
  117.  * swallow_smtp - read and throw away SMTP message contents
  118.  *
  119.  * Read lines up to a line containing a single '.'.  Do not do
  120.  * anything with these lines.  Return SUCCEED if we successfully read
  121.  * up to a line containing only a single '.'.  Return FAIL otherwise.
  122.  *
  123.  * This is used to read in a complete message from an SMTP transaction
  124.  * which will not be using because of some error.
  125.  */
  126. int
  127. swallow_smtp(f)
  128.     register FILE *f;            /* input file */
  129. {
  130.     register int c;
  131.  
  132.     for (;;) {
  133.     if ((c = getc(f)) == EOF) return FAIL;
  134.     if (c == '.') {
  135.         if ((c = getc(f)) == EOF) return FAIL;
  136.         if (c == '\n') {
  137.         return SUCCEED;
  138.         }
  139.         if (c == '\r') {
  140.         if ((c = getc(f)) == EOF) return FAIL;
  141.         if (c == '\n') {
  142.             return SUCCEED;
  143.         }
  144.         }
  145.     }
  146.     while (c != '\n') {
  147.         if ((c = getc(f)) == EOF) return FAIL;
  148.     }
  149.     }
  150. }
  151.  
  152. /*
  153.  * queue_envelope - write out header filler and arguments for spool file
  154.  *
  155.  * save some space for the login username, which is computed last, and
  156.  * write out any arguments, to the spool file.  The form used is one smail
  157.  * command argument per line, allowing the use of process_args() in main
  158.  * to decode the envelope information.
  159.  */
  160. static int
  161. queue_envelope(list)
  162.     struct addr *list;            /* input recipient list */
  163. {
  164.     register struct addr *cur;        /* current recipient addr to process */
  165.     char *p;                /* temp */
  166.     char buf[50];            /* temp storage */
  167.  
  168.     if (local_sender) {
  169.     if (put_line(local_sender) == EOF)
  170.         return FAIL;
  171.     }
  172.     else {
  173.     (void) memset(buf, ' ', MAXUSER);
  174.     buf[MAXUSER] = '\0';
  175.     if (put_line(buf) == EOF)
  176.         return FAIL;
  177.     }
  178.  
  179.     /*
  180.      * write out the real uid and effective gid, as numbers.  Validation
  181.      * of trusted_users will be done from the ids stored here.
  182.      */
  183.     (void) sprintf(buf, "%d %d", real_uid, prog_egid);
  184.     if (put_line(buf) == EOF) {
  185.     return FAIL;
  186.     }
  187.  
  188.     /*
  189.      * write out important state information in the form of command arguments.
  190.      */
  191.     if (extract_addresses) {
  192.     /* signal that addresses are to be taken from the header */
  193.     if (put_line("-t") == EOF) {
  194.         return FAIL;
  195.     }
  196.     }
  197.  
  198.     /* tell which form of error recover is being used */
  199.     switch (error_processing) {
  200.     case MAIL_BACK:
  201.     p = "-oem";
  202.     break;
  203.     case WRITE_BACK:
  204.     p = "-oew";
  205.     break;
  206.     case TERMINAL:
  207.     p = "-oep";
  208.     break;
  209.     default:
  210.     p = "-oeq";
  211.     break;
  212.     }
  213.     if (put_line(p) == EOF) {
  214.     return FAIL;
  215.     }
  216.     if (dont_deliver) {
  217.     put_line("-N");
  218.     }
  219.     if (hop_count >= 0) {
  220.     (void) sprintf(buf, "-h%d", hop_count);
  221.     if (put_line(buf) == EOF) {
  222.         return FAIL;
  223.     }
  224.     }
  225.     if (! do_aliasing) {
  226.     if (put_line("-n") == EOF) {
  227.         return FAIL;
  228.     }
  229.     }
  230.     if (me_too) {
  231.     if (put_line("-om") == EOF) {
  232.         return FAIL;
  233.     }
  234.     }
  235.  
  236.     /*
  237.      * write the sender's full name, if one was given explicitly.  If it was
  238.      * not given explicitly, we would not yet know what it is.
  239.      */
  240.     if (sender_name) {
  241.     if (put_line("-F") == FAIL ||
  242.         put_line(sender_name) == FAIL)
  243.     {
  244.         return FAIL;
  245.     }
  246.     }
  247.  
  248.     /*
  249.      * write the sender address, if one was given explicitly.  If it was
  250.      * not given explicitly, we would not yet know what it is.
  251.      */
  252.     if (sender) {
  253.     if (put_line("-f") == FAIL ||
  254.         put_line(sender) == FAIL)
  255.     {
  256.         return FAIL;
  257.     }
  258.     }
  259.  
  260.     /*
  261.      * write the sending host, host address and sending protocol, if known
  262.      */
  263.     if (sender_host) {
  264.     if (put_line("-oMs") == FAIL ||
  265.         put_line(sender_host) == FAIL)
  266.     {
  267.         return FAIL;
  268.     }
  269.     }
  270.     if (sender_host_addr) {
  271.     if (put_line("-oMa") == FAIL ||
  272.         put_line(sender_host_addr) == FAIL)
  273.     {
  274.         return FAIL;
  275.     }
  276.     }
  277.     if (sender_proto) {
  278.     if (put_line("-oMr") == FAIL ||
  279.         put_line(sender_proto) == FAIL)
  280.     {
  281.         return FAIL;
  282.     }
  283.     }
  284.  
  285.     /*
  286.      * write out the invoked program, too
  287.      */
  288.     if (put_line("-oMP") == FAIL ||
  289.     put_line(program) == FAIL)
  290.     {
  291.     return FAIL;
  292.     }
  293.  
  294.     /*
  295.      * write out all of the recipient addresses.  If the -t flag was given,
  296.      * this is the list of addresses that are explicitly not recipients.
  297.      */
  298.     for (cur = list; cur; cur = cur->succ) {
  299.     if (put_line(cur->in_addr) == FAIL) {
  300.         return FAIL;
  301.     }
  302.     }
  303.  
  304.     /* envelope ends in one blank line */
  305.     if (PUTSPOOL('\n') == EOF) {
  306.     return FAIL;
  307.     }
  308.  
  309.     return SUCCEED;
  310. }
  311.  
  312.  
  313. /*
  314.  * queue_message_internal - read the message into a spool file.
  315.  *
  316.  * Call either queue_message_with_dots() or queue_message_simple() to
  317.  * spool a message to a spool file.
  318.  */
  319. static int
  320. queue_message_internal(f, dusage, error)
  321.     FILE *f;
  322.     enum dot_usage dusage;
  323.     char **error;            /* optionally store error here */
  324. {
  325.     if (dusage == NO_DOT_PROTOCOL) {
  326.     return queue_message_simple(f);
  327.     }
  328.     return queue_message_with_dots(f, dusage, error);
  329. }
  330.  
  331. /*
  332.  * queue_message_with_dots - read in a message and write it to the
  333.  *                 spool file, with a dot protocol.
  334.  *
  335.  * Read in a message using one of the dot protocols (HIDDEN_DOTS,
  336.  * DOT_ENDS_MESSAGE or SMTP_DOTS).  All of these protocols end a
  337.  * message when a dot is found on a line by itself.  The HIDDEN_DOTS
  338.  * protocol drops an initial dot from a line that contains other data.
  339.  * The SMTP_DOTS protocol is like HIDDEN_DOTS except that a message
  340.  * must be terminated by a '.', with an encouter of EOF being an
  341.  * error.  Also, with SMTP_DOTS, the entire message is read from the
  342.  * input even if a write error occured to the spool file.
  343.  *
  344.  * All of these protocols treat a CR/LF sequence as equivalent to a
  345.  * single newline.
  346.  *
  347.  * return SUCCEED or FAIL.
  348.  */
  349. static int
  350. queue_message_with_dots(f, dusage, error)
  351.     register FILE *f;            /* input file */
  352.     enum dot_usage dusage;        /* how to treat dots */
  353.     char **error;            /* optionally store error here */
  354. {
  355.     register int c;
  356.     register int put_success = SUCCEED;    /* FAIL if PUTSPOOL fails */
  357.  
  358.     for (;;) {
  359.     if ((c = getc(f)) == EOF) break;
  360.     if (c == '.') {
  361.         if ((c = getc(f)) == EOF) break;
  362.         if (c == '\n') {
  363.         return put_success;
  364.         }
  365.         if (c == '\r') {
  366.         if ((c = getc(f)) == EOF) break;
  367.         if (c == '\n') {
  368.             return put_success;
  369.         }
  370.         if ((put_success == SUCCEED &&
  371.              dusage == DOT_ENDS_MESSAGE &&
  372.              PUTSPOOL('.') == FAIL) ||
  373.             PUTSPOOL('\r') == FAIL)
  374.         {
  375.             if (dusage != SMTP_DOTS) {
  376.             return FAIL;
  377.             }
  378.             put_success = FAIL;
  379.         }
  380.         } else {
  381.         if (put_success == SUCCEED &&
  382.             (dusage == DOT_ENDS_MESSAGE && PUTSPOOL('.') == FAIL))
  383.         {
  384.             put_success = FAIL;
  385.         }
  386.         }
  387.     }
  388.     for (;;) {
  389.         while (c != '\n' && c != '\r') {
  390.         if (put_success == SUCCEED && PUTSPOOL(c) == FAIL) {
  391.             put_success = FAIL;
  392.         }
  393.         if ((c = getc(f)) == EOF) goto read_eof;
  394.         }
  395.         if (c == '\r') {
  396.         if ((c = getc(f)) == EOF) goto read_eof;
  397.         if (c != '\n') {
  398.             if (put_success == SUCCEED && PUTSPOOL('\r') == FAIL) {
  399.             put_success = FAIL;
  400.             }
  401.             continue;
  402.         }
  403.         }
  404.         if (put_success == SUCCEED && PUTSPOOL(c) == FAIL) {
  405.         put_success = FAIL;
  406.         }
  407.         break;
  408.     }
  409.     }
  410.  
  411. read_eof:
  412.     if (dusage != SMTP_DOTS) {
  413.     return put_success;
  414.     }
  415.     *error = "Unexpected end of file";
  416.     return FAIL;
  417. }
  418.  
  419. /*
  420.  * queue_message_simple - read in a message with no dot protocol.
  421.  *
  422.  * Read in a message without any specific protocol.  No CR/LF mapping
  423.  * is done and the end of message is given only with an EOF.
  424.  *
  425.  * Return SUCCEED or FAIL.
  426.  */
  427. static int
  428. queue_message_simple(f)
  429.     register FILE *f;
  430. {
  431.     register int c;
  432.  
  433.     while ((c = getc(f)) != EOF) {
  434.     if (PUTSPOOL(c) == FAIL) {
  435.         return FAIL;
  436.     }
  437.     }
  438.     return SUCCEED;
  439. }
  440.  
  441.  
  442. /*
  443.  * fixup_login_user - insert the login username at the start of the spoolfile
  444.  *
  445.  * to get the message into the spool file as fast as possible, the login
  446.  * name of the user that ran smail is computed last and filled after
  447.  * everything else is through.
  448.  *
  449.  * This is ugly, but it can take a significant amount of time to compute
  450.  * the real user, and it is better to get the spool file written quickly,
  451.  * thus narrowing the window of system crash vulnerability.
  452.  */
  453. static int
  454. fixup_login_user()
  455. {
  456.     register int i;
  457.     extern long lseek();
  458.     struct passwd *pw;
  459.  
  460.     if (local_sender)
  461.     return SUCCEED;
  462.  
  463.     compute_local_sender();
  464.  
  465.     /*
  466.      * truncate to 16 chars, if for some reason we got a
  467.      * larger user name
  468.      */
  469.     i = (int)strlen(local_sender);
  470.     if (i > MAXUSER)
  471.     i = MAXUSER;
  472.  
  473.     /*
  474.      * write the name to disk
  475.      */
  476.     (void) lseek(spoolfile, 1L, 0);
  477.     if (write(spoolfile, local_sender, i) < i) {
  478.     return FAIL;
  479.     }
  480.  
  481.     if (msg_foffset == 0) {
  482.     /*
  483.      * if the current incore spool file region is at the
  484.      * beginning of the file, make a copy there too.
  485.      */
  486.     (void) memcpy(msg_buf, local_sender, i);
  487.     }
  488.  
  489.     return SUCCEED;
  490. }
  491.  
  492. /*
  493.  * put_line - write a complete line to the spool file
  494.  *
  495.  * the string passed is assumed to be an atomic argument for smail, which
  496.  * should end in a newline.  To guard against newlines in the text of the
  497.  * string to end an argument, newline is encoded as \n and \ is encoded as
  498.  * \\.
  499.  */
  500. static int
  501. put_line(s)
  502.     register char *s;
  503. {
  504.     if (PUTSPOOL('!') == EOF)
  505.     return FAIL;
  506.     while (*s) {
  507.     if (*s == '\\') {
  508.         /* encode \ as \\ and newline as \n */
  509.         if (PUTSPOOL('\\') == EOF || PUTSPOOL('\\') == EOF) {
  510.         return FAIL;
  511.         }
  512.     } else if (*s == '\n') {
  513.         if (PUTSPOOL('\\') == EOF || PUTSPOOL('n') == EOF) {
  514.         return FAIL;
  515.         }
  516.     } else {
  517.         if (PUTSPOOL(*s) == EOF) {
  518.         return FAIL;
  519.         }
  520.     }
  521.  
  522.     s++;
  523.     }
  524.  
  525.     if (PUTSPOOL('\n') == EOF) {
  526.     return FAIL;
  527.     }
  528.  
  529.     return SUCCEED;
  530. }
  531.  
  532.  
  533. /*
  534.  * read_message - scan through an open spool file
  535.  *
  536.  * read through the current open spool file to grab From_ and Header:
  537.  * information.  Putting the sender address in the sender variable,
  538.  * and storing the header information in the headers variable as
  539.  * side effects.
  540.  *
  541.  * return the argument vector in *argv
  542.  *
  543.  * return NULL on failure.  The reason for failure will be logged
  544.  * in the system log file and the spool file should be closed and
  545.  * ignored for now.
  546.  */
  547. char **
  548. read_message()
  549. {
  550.     int a = 32;                /* vectors allocated thus far */
  551.     int i;                /* temp */
  552.     register int c;            /* character from spool file */
  553.     register char **argv;        /* computed argument vector */
  554.     char *p;
  555.     char ls[MAXUSER + 1];        /* local sender from file */
  556.     int just_trust = TRUE;        /* true if no "trusted" variables */
  557.     int first;
  558.  
  559.     DEBUG(DBG_QUEUE_HI, "read_message called\n");
  560.     /* seek to the beginning of the region */
  561.     if (seek_spool(0L) == FAIL) {
  562.     return NULL;
  563.     }
  564.  
  565.     /* get the login username from the spool file */
  566.     i = 0;
  567.     first = 1;
  568.     while ((c = GETSPOOL()) != EOF && c != READ_FAIL &&
  569.        c != '\n' && c != ' ' && i < MAXUSER) {
  570.     if (c != '!' || ! first)
  571.         ls[i++] = c;
  572.     first = 0;
  573.     }
  574.     /* read to the end of the first line */
  575.     while (c != '\n' && c != EOF && c != READ_FAIL) {
  576.     c = GETSPOOL();
  577.     }
  578.     ls[i] = '\0';
  579.  
  580.     /* read the real uid and effective gid under which the mailer was exec'd */
  581.     if (c != READ_FAIL && c != EOF) {
  582.     char buf[50];            /* temp input buffer */
  583.     char *p = buf;            /* temp for scanning buf */
  584.  
  585.     while ((c = GETSPOOL()) != '\n' && c != EOF && c != READ_FAIL) {
  586.         *p++ = c;
  587.     }
  588.     *p = '\0';
  589.     p = buf;
  590.     if (*p == '!')
  591.         p++;
  592.     (void) sscanf(p, "%d %d", &real_uid, &prog_egid);
  593.     }
  594.     if (c == READ_FAIL) {
  595.     write_log(LOG_SYS|LOG_PANIC, "read failed: %s/%s: %s",
  596.           spool_dir, input_spool_fn, strerrno());
  597.     return NULL;
  598.     }
  599.     if (c == EOF) {
  600.     write_log(LOG_SYS, "unexpected end of file: %s/%s",
  601.           spool_dir, input_spool_fn);
  602.     }
  603.  
  604.     /*
  605.      * determine if the login user is a trusted user, using the real uid.
  606.      */
  607.     p = NULL;
  608.     if (trusted && trusted[0]) {
  609.     just_trust = FALSE;
  610.  
  611.     /* trusted string a colon separated list */
  612.     for (p = strcolon(trusted); p; p = strcolon((char *)NULL)) {
  613.         struct passwd *pw = getpwbyname(p);
  614.         if (pw && real_uid == pw->pw_uid) {
  615.         break;
  616.         }
  617.     }
  618.     }
  619.  
  620.     /*
  621.      * trusted group string can also flag invoker as trusted
  622.      */
  623.     if (p == NULL && trusted_groups && trusted_groups[0]) {
  624.     just_trust = FALSE;
  625.  
  626.     for (p = strcolon(trusted_groups); p; p = strcolon((char *)NULL)) {
  627.         struct group *gr = getgrbyname(p);
  628.         if (gr && prog_egid == gr->gr_gid) {
  629.         break;
  630.         }
  631.     }
  632.     }
  633.  
  634.     if (p == NULL && !just_trust) {
  635.     /* user is not verified, sender is the login user */
  636.     DEBUG1(DBG_QUEUE_MID, "sender is not trusted\n", real_uid);
  637.     sender = xmalloc(strlen(ls) + 1);
  638.     strcpy(sender, ls);
  639.     islocal = TRUE;
  640.     sender_is_trusted = FALSE;
  641.     }
  642.  
  643.     /*
  644.      * if sender was already specified and the originator of the
  645.      * message is trusted to supply a sender, see if the sender name
  646.      * is in local form.
  647.      */
  648.     if (sender && sender_is_trusted) {
  649.     char *error;            /* temp to store error message */
  650.  
  651.     islocal = parse_address(sender, (char **)0, &error, (int *)0) == LOCAL;
  652.     }
  653.  
  654.     /*
  655.      * read the argument vectors stored in the spool file
  656.      */
  657.     DEBUG1(DBG_QUEUE_HI, "read arg vectors from spool file (at %d)\n",
  658.        tell_spool());
  659.     argv = (char **)xmalloc(a * sizeof(char *));
  660.     i = 0;
  661.     first = 1;
  662.     while ((c = GETSPOOL()) != EOF && c != '\n') {
  663.     struct str str;
  664.     register struct str *sp = &str;    /* temp string */
  665.  
  666.     /* read one vector */
  667.     STR_INIT(sp);
  668.     do {
  669.         if (first) {
  670.         first = 0;
  671.         if (c == '!')
  672.             continue;
  673.         }
  674.         if (c == '\\') {
  675.         c = GETSPOOL();
  676.         if (c == EOF) {
  677.             STR_FREE(sp);
  678.             write_log(LOG_SYS, "unexpected end of file: %s/%s",
  679.                   spool_dir, input_spool_fn);
  680.             return NULL;    /* no message */
  681.         } else if (c == 'n') {
  682.             c = '\n';
  683.         } else if (c != '\\') {
  684.             write_log(LOG_SYS, "format error in: %s/%s",
  685.                   spool_dir, input_spool_fn);
  686.             return NULL;    /* format error */
  687.         }
  688.         }
  689.         STR_NEXT(sp, c);
  690.     } while ((c = GETSPOOL()) != EOF && c != '\n');
  691.  
  692.     /*
  693.      * finish off the argument string and store it in the vector
  694.      */
  695.     STR_NEXT(sp, '\0');
  696.     STR_DONE(sp);
  697.     DEBUG1(DBG_QUEUE_HI, "read vector <%s> from spool file\n", sp->p);
  698.     argv[i++] = sp->p;
  699.     /* do we need a larger vector? */
  700.     if (i >= a) {
  701.         a += 32;
  702.         argv = (char **)xrealloc((char *)argv, a*sizeof(char *));
  703.     }
  704.     first = 1;
  705.     }
  706.  
  707.     /* finish off the argument vector */
  708.     argv[i] = NULL;
  709.  
  710.     /*
  711.      * login name and arguments processed, scan for From_ lines, if
  712.      * any exist.  As a side effect, this may set the sender if it
  713.      * has not been set already.
  714.      */
  715.     if (read_from_lines() == FAIL) {
  716.     /* read_from_lines logged the error */
  717.     return NULL;
  718.     }
  719.  
  720.     /*
  721.      * if the sender isn't known by now, then use the login user, or
  722.      * postmaster if nothing else is available.  This may be overridden
  723.      * later with a -f or -r flag.
  724.      */
  725.     if (sender == NULL) {
  726.     if (ls[0]) {
  727.         sender = xmalloc(strlen(ls) + 1);
  728.         strcpy(sender, ls);
  729.     } else {
  730.         /* love to gang up on that postmaster */
  731.         DEBUG(DBG_QUEUE_LO, "no login user, set sender to postmaster\n");
  732.         sender = xmalloc(sizeof("Postmaster"));
  733.         strcpy(sender, "Postmaster");
  734.     }
  735.     islocal = TRUE;            /* sender appears to be local */
  736.     }
  737.  
  738.     /*
  739.      * now grab the header lines.
  740.      */
  741.     DEBUG1(DBG_QUEUE_HI, "calling read_header (offset=%d)\n", tell_spool());
  742.     if (read_header() == FAIL) {
  743.     /* read_header logged the error */
  744.     return NULL;
  745.     }
  746.  
  747.     /* save the start position for the message */
  748.     start_msg = tell_spool();
  749.     msg_body_size = msg_size - start_msg;
  750.     DEBUG1(DBG_SPOOL_HI, "body starts at %d\n", start_msg);
  751.  
  752.     /* all done, return the arguments */
  753.     return argv;
  754. }
  755.  
  756. /*
  757.  * read_from_lines - scan From_ lines and build up a sender
  758.  *
  759.  * Scan through to the end of the From_ lines (if any exist).
  760.  * As a side effect, build up a path to the sender, if the
  761.  * sender is not already known.
  762.  *
  763.  * return SUCCESS or FAIL, and log reason for failure.
  764.  */
  765. static int
  766. read_from_lines()
  767. {
  768.     struct str pstr;
  769.     register struct str *path = &pstr;    /* build up sender path here */
  770.     struct str lstr;
  771.     register struct str *line = &lstr;    /* input line */
  772.     int c;                /* input character */
  773.     register char *lp;            /* temp for processing line */
  774.     struct str ustr;
  775.     register struct str *user = &ustr;    /* user name from From_ line */
  776.     char *mark;                /* mark point in line */
  777.     long line_mark;            /* seek position for start of line */
  778.  
  779.     DEBUG1(DBG_SPOOL_HI, "read_from_lines called (offset=%d)\n", tell_spool());
  780.     /* if we already know the sender, then don't bother computing it */
  781.     if (sender == NULL) {
  782.     islocal = TRUE;            /* set FALSE if we find otherwise */
  783.     STR_INIT(user);
  784.     STR_INIT(path);
  785.     }
  786.  
  787.     STR_INIT(line);
  788.  
  789.     /*
  790.      * loop until we have read all of the From_ lines
  791.      */
  792.     for (;;) {
  793.     /* read one complete line */
  794.     line->i = 0;
  795.     line_mark = tell_spool();
  796.     while ((c = GETSPOOL()) != EOF && c != READ_FAIL && c != '\n') {
  797.         STR_NEXT(line, c);
  798.     }
  799.     if (c == READ_FAIL) {
  800.         write_log(LOG_SYS, "read failed: %s/%s: %s",
  801.               spool_dir, input_spool_fn, strerrno());
  802.     }
  803.     if (c != EOF) {
  804.         STR_NEXT(line, c);
  805.     }
  806.     STR_NEXT(line, '\0');
  807.     lp = line->p;
  808.     if (line->i < 20) {
  809.         /* less than 20 chars? couldn't possibly be a From_ line */
  810.         break;
  811.     }
  812.     /* skip beginning > character */
  813.     if (*lp == '>') {
  814.         lp++;
  815.     }
  816.     /* do we have a From line? */
  817.     if (strncmp(lp, "From", 4) != 0 || !isspace(lp[4])) {
  818.         /* no, end of From_ lines */
  819.         break;
  820.     }
  821.     if (sender) {
  822.         continue;            /* we know sender scan next line */
  823.     }
  824.     /* extract the user */
  825.     lp += 5;
  826.     while (isspace(*lp)) {    /* scan for start of user name */
  827.         lp++;
  828.     }
  829.     mark = lp;            /* mark the start of the user name */
  830.     /* skip over the user name token */
  831.     lp = forward_token(lp);
  832.     *lp = '\0';
  833.     user->i = 0;
  834.     STR_CAT(user, mark);
  835.  
  836.     /* search for a remote from host */
  837.     lp = line->p + line->i - 2;
  838.     while (isspace(*lp)) {
  839.         lp--;
  840.     }
  841.     lp[1] = '\0';            /* terminate end of potential host */
  842.     lp = backward_token(line->p, lp); /* back to start of host (?) */
  843.     mark = lp;            /* host may be here */
  844.     lp = backward_token(line->p, lp-1); /* back to start of from (?) */
  845.     if (strncmp(lp, "from", 4) != 0 || !isspace(lp[4])) {
  846.         path->i = 0;        /* no remote from host, toss path */
  847.         DEBUG1(DBG_SPOOL_HI, "no remote from host in <%s>, toss path\n",
  848.            line->p);
  849.         continue;            /* next From_ line */
  850.     }
  851.     lp = backward_token(line->p, lp-1); /* back to start of remote (?) */
  852.     if (strncmp(lp, "remote", 6) != 0 || !isspace(lp[6])) {
  853.         path->i = 0;        /* no remote from host, toss path */
  854.         DEBUG1(DBG_SPOOL_HI, "no remote from host in <%s>, toss path\n",
  855.            line->p);
  856.         continue;            /* next From_ line */
  857.     }
  858.  
  859.     /*
  860.      * found a remote_from host, add it to the sender path
  861.      */
  862.     if (path->i > 0) {
  863.         STR_NEXT(path, '!');    /* ! is separator */
  864.     }
  865.     STR_CAT(path, mark);
  866.     islocal = FALSE;        /* not a local message */
  867.     if (sender_host == NULL && sender_proto == NULL) {
  868.         sender_host = COPY_STRING(mark);
  869.         sender_proto = "uucp";
  870.     }
  871.     }
  872.  
  873.     STR_FREE(line);            /* finished processing input line */
  874.  
  875.     /*
  876.      * if we are building up a sender, then finish doing so
  877.      */
  878.     if (sender == NULL) {
  879.     STR_NEXT(path, '\0');        /* terminate path and user */
  880.     STR_NEXT(user, '\0');
  881.     finish_sender(path->p, user->p);
  882.     STR_FREE(path);        /* free the storage used */
  883.     STR_FREE(user);
  884.     }
  885.  
  886.     /*
  887.      * seek back to the beginning of the last line read, which was not
  888.      * a From_ line
  889.      */
  890.     if (seek_spool(line_mark) == FAIL) {
  891.     write_log(LOG_SYS, "seek failed on: %s/%s: %s",
  892.           spool_dir, input_spool_fn, strerrno());
  893.     return FAIL;
  894.     }
  895.     return SUCCEED;
  896. }
  897.  
  898. /*
  899.  * forward_token - scan past the following token
  900.  *
  901.  * return the position of the character after the first token after p.
  902.  * a token ends in a space, but can include any chars in quotes, or
  903.  * after a \.
  904.  */
  905. static char *
  906. forward_token(p)
  907.     register char *p;            /* start search here */
  908. {
  909.     register int inquote = FALSE;    /* not in a quote */
  910.  
  911.     while (isspace(*p))            /* scan to start of token */
  912.     p++;
  913.  
  914.     /* loop exits by return when done */
  915.     for (;;) {
  916.     switch (*p++) {
  917.     case '\\':
  918.         if (*p)
  919.         p++;
  920.         break;
  921.     case ' ':
  922.     case '\t':
  923.     case '\n':
  924.         if (!inquote) {
  925.         return p-1;        /* past end of token */
  926.         }
  927.         break;
  928.     case '\0':
  929.         return p-1;            /* past end of token */
  930.     case '"':            /* start or end of a quote */
  931.         inquote = !inquote;
  932.         break;
  933.     }
  934.     }
  935. }
  936.  
  937. /*
  938.  * backward_token - scan to beginning of previous token
  939.  *
  940.  * return the position of the first character of a group
  941.  * of non white-space characters before p.
  942.  *
  943.  * NOTE:  we don't take into account text in quotes in scanning
  944.  * backwards.
  945.  */
  946. static char *
  947. backward_token(s,p)
  948.     register char *s;            /* string starts here */
  949.     register char *p;            /* start search here */
  950. {
  951.     /* scan before end of token */
  952.     while (isspace(*p)) {
  953.     if (p == s) {
  954.         return p;
  955.     }
  956.     p--;
  957.     }
  958.  
  959.     /* scan to start of token */
  960.     while (*p && !isspace(*p)) {
  961.     if (p == s) {
  962.         return p;
  963.     }
  964.     p--;
  965.     }
  966.  
  967.     return p+1;
  968. }
  969.  
  970. /*
  971.  * finish_sender - build the sender address
  972.  *
  973.  * Build the sender address out of the path and user computed in
  974.  * read_from_lines.
  975.  */
  976. static void
  977. finish_sender(path, user)
  978.     register char *path;        /* path to sender */
  979.     register char *user;        /* user name of sender */
  980. {
  981.     char *target;            /* parsed target */
  982.     char *remainder;            /* parsed remainder */
  983.     register int form;            /* form from parse_address */
  984.  
  985.     if (user[0] == '\0') {
  986.     return;                /* didn't find anything */
  987.     }
  988.  
  989.     /* parse the user address, which may yield more path information */
  990.     form = parse_address(user, &target, &remainder, (int *)0);
  991.  
  992.     if (form == FAIL) {
  993.     /*
  994.      * failed to parse user, store an error for logging later
  995.      */
  996.     write_log(LOG_MLOG|LOG_SYS, "Error reading sender: %s", remainder);
  997.     sender = COPY_STRING("Postmaster");
  998.     } else if (form == LOCAL && path[0] == '\0' && !islocal) {
  999.     /*
  1000.      * message was flagged as non-local, but we no longer have any
  1001.      * host information, send it to the postmaster
  1002.      */
  1003.     write_log(LOG_MLOG|LOG_SYS, "Path to sender unknown:  sender = %s",
  1004.           user);
  1005.     sender = COPY_STRING("Postmaster");
  1006.     } else if (path[0] == '\0' && form == LOCAL) {
  1007.     /*
  1008.      * simple case: no path, simple user, just make a copy of user
  1009.      */
  1010.     sender = COPY_STRING(remainder);
  1011.     } else if (path[0] == '\0' && form == UUCP_ROUTE) {
  1012.     /*
  1013.      * user is in !-route form, so put the user back together
  1014.      */
  1015.     sender = xprintf("%s!%s", target, remainder);
  1016.     } else if (form == LOCAL) {
  1017.     /*
  1018.      * simple user form, add it to path
  1019.      */
  1020.     sender = xprintf("%s!%s", path, remainder);
  1021.     } else if (form == UUCP_ROUTE) {
  1022.     /*
  1023.      * user is in !-route form, put it back together and add to path
  1024.      */
  1025.     sender = xprintf("%s!%s!%s", path, target, remainder);
  1026.     } else {
  1027.     /*
  1028.      * the difficult case, need to construct a !-route for user
  1029.      */
  1030.     char *error;            /* store error here */
  1031.     char *route = build_uucp_route(remainder, &error, 0);
  1032.  
  1033.     if (route == NULL) {
  1034.         /* found an error building the route */
  1035.         write_log(LOG_MLOG|LOG_SYS, "Error building sender: %s", error);
  1036.     } else if (path[0] == '\0') {
  1037.         /*
  1038.          * no path, just put the user back together
  1039.          */
  1040.         sender = xprintf("%s!%s", target, route);
  1041.     } else {
  1042.         /*
  1043.          * put user back together and add to path
  1044.          */
  1045.         sender = xprintf("%s!%s!%s", path, target, route);
  1046.     }
  1047.     }
  1048. }
  1049.  
  1050.  
  1051. /*
  1052.  * write_body - write the message body to a stdio FILE pointer.
  1053.  *
  1054.  * the body of the message will be sent to the given file.  Pass
  1055.  * the the transport flags to send_spool in writing.
  1056.  *
  1057.  * return SUCCEED, WRITE_FAIL or READ_FAIL.
  1058.  */
  1059. int
  1060. write_body(f, flags)
  1061.     FILE *f;                /* write to this file */
  1062.     long flags;                /* transport flags */
  1063. {
  1064.     if (seek_spool(start_msg) == FAIL) {
  1065.     write_log(LOG_SYS, "seek failed: %s/%s: %s",
  1066.           spool_dir, input_spool_fn, strerrno());
  1067.     return READ_FAIL;
  1068.     }
  1069.     return send_spool(f, flags);
  1070. }
  1071.  
  1072.  
  1073. /*
  1074.  * check_grade - set grade for message to value from Precedence: field
  1075.  *
  1076.  * If a Precedence: field is specified for a message and this grade
  1077.  * is not the message's current grade, then change the grade for the
  1078.  * message, which will involve moving the file to a new name.
  1079.  */
  1080. void
  1081. check_grade()
  1082. {
  1083.     struct list *hq;            /* temp for scanning headers */
  1084.  
  1085.     /* grab the current grade */
  1086.     msg_grade = message_id[strlen(message_id) - 1];
  1087.  
  1088.     /* find the precedence field (or not) */
  1089.     for (hq = header; hq; hq = hq->succ) {
  1090.     if (HDREQ("precedence", hq->text)) {
  1091.         /* found the precedence header */
  1092.         int grade = parse_precedence(index(hq->text, ':') + 1);
  1093.  
  1094.         if (grade && msg_grade != grade) {
  1095.         /*
  1096.          * change the grade to the new grade, if we fail to
  1097.          * change the grade, don't sweat it too badly.
  1098.          */
  1099.         (void) new_grade(grade);
  1100.         msg_grade = grade;
  1101.         }
  1102.         break;
  1103.     }
  1104.     }
  1105. }
  1106.  
  1107. /*
  1108.  * log_incoming - put a message in the log file about the incoming message
  1109.  *
  1110.  * look for a message-id header field and log it, if it exists, otherwise
  1111.  * announce new mail without a previous message-id.
  1112.  */
  1113. void
  1114. log_incoming()
  1115. {
  1116.     struct list *hq;            /* temp for scanning headers */
  1117.     char *old_id = NULL;        /* previous message-id */
  1118.     char *trim_old_id = NULL;        /* trimmed previous message-id */
  1119.     char *resent_mid = NULL;        /* point to Resent-Message-Id field */
  1120.     char *mid = NULL;            /* point to Message-Id field */
  1121.     char *host_string = NULL;        /* combined host name and inet addr */
  1122.  
  1123.     for (hq = header; hq; hq = hq->succ) {
  1124.     if (HDREQ("resent-message-id", hq->text)) {
  1125.         resent_mid = hq->text;
  1126.         break;
  1127.     }
  1128.     if (HDREQ("message-id", hq->text)) {
  1129.         mid = hq->text;
  1130.     }
  1131.     }
  1132.     if (resent_mid) {
  1133.     old_id = index(resent_mid, ':');
  1134.     } else if (mid) {
  1135.     old_id = index(mid, ':');
  1136.     }
  1137.  
  1138.     if (sender_host) {
  1139.     if (sender_host_addr) {
  1140.         host_string = xprintf("%s [%s]", sender_host, sender_host_addr);
  1141.     } else {
  1142.         host_string = xprintf("%s", sender_host);
  1143.     }
  1144.     } else if (sender_host_addr) {
  1145.     host_string = xprintf("[%s]", sender_host_addr);
  1146.     }
  1147.  
  1148.     if (old_id) {
  1149.     register char *p;
  1150.  
  1151.     trim_old_id = xmalloc(strlen(old_id));
  1152.     for (p = trim_old_id, old_id++; *old_id; old_id++) {
  1153.         if (!isspace(*old_id)) {
  1154.         *p++ = *old_id;
  1155.         }
  1156.     }
  1157.     *p = '\0';
  1158.     }
  1159.  
  1160.     write_log(LOG_SYS, "received%s%s%s%s%s%s%s%s%s%s%s%ld%s",
  1161.           "\n|\t     from: ", sender,
  1162.         host_string?
  1163.           "\n|\t     host: ": "", host_string? host_string: "",
  1164.         sender_proto?
  1165.           "\n|\t protocol: ": "", sender_proto? sender_proto: "",
  1166.           "\n|\t  program: ", program,
  1167.         trim_old_id?
  1168.           "\n|\t  orig-id: ": "", trim_old_id? trim_old_id: "",
  1169.           "\n|\t     size: ", msg_size, " bytes");
  1170.  
  1171.     if (host_string) {
  1172.     xfree(host_string);
  1173.     }
  1174.     if (trim_old_id) {
  1175.     xfree(trim_old_id);
  1176.     }
  1177. }
  1178.  
  1179. /*
  1180.  * scan_spool_dirs - scan for work and return spool files in sorted order
  1181.  *
  1182.  * look through all of the spool directories, and sort by precedence and
  1183.  * by creation date.
  1184.  *
  1185.  * the returned filename vector contains data which may be reused on
  1186.  * subsequent calls to scan_spool_dirs().
  1187.  */
  1188. char **
  1189. scan_spool_dirs()
  1190. {
  1191.     static char **mv = NULL;        /* vector of messages */
  1192.     int mc = 0;                /* count of messages */
  1193.     static int mv_size = 32;        /* allocated vector entries */
  1194.     char *dn;                /* current directory to scan */
  1195.     static struct str str;        /* storage for filenames */
  1196.     register int i;            /* temp */
  1197.  
  1198.     /* get the initial vector and string storage allocation */
  1199.     if (mv == NULL) {
  1200.     mv = (char **)xmalloc(mv_size * sizeof(*mv));
  1201.     STR_INIT(&str);
  1202.     }
  1203.  
  1204.     str.i = 0;                /* clear out filename storage */
  1205.  
  1206.     /* loop through all spool directories looking for work */
  1207.     for (dn = strcolon(spool_dirs); dn; dn = strcolon((char *)NULL)) {
  1208.     char *fn;            /* filename from directory */
  1209.     char *in_dn = xprintf("%s/input", dn);
  1210.  
  1211.     for (fn = scan_dir(in_dn); fn; fn = scan_dir((char *)NULL)) {
  1212.         /* see if the file matches the form of a spool file */
  1213.         if (! isdigit(fn[0]) || strlen(fn) != SPOOL_FN_LEN) {
  1214.         continue;        /* nope */
  1215.         }
  1216.  
  1217.         /* add to the list of files */
  1218.         if (mc >= mv_size - 1) {
  1219.         /* note: we allow, in advance, for the last NULL entry */
  1220.         mv_size += 32;        /* get more entries */
  1221.         mv = (char **)xrealloc((char *)mv, mv_size * sizeof(*mv));
  1222.         }
  1223.         /* use offsets for now, to be converted to (char *) later */
  1224.         set_ptr(mv[mc++], str.i);
  1225.         str_printf(&str, "%s/%s%N", in_dn, fn);    /* %N is nul byte */
  1226.     }
  1227.     xfree(in_dn);
  1228.     }
  1229.  
  1230.     /* close off the vectors */
  1231.     mv[mc] = NULL;
  1232.  
  1233.     /* convert the offsets in mv to (char *)'s */
  1234.     for (i = 0; i < mc; i++) {
  1235.     mv[i] = str.p + get_ptr(mv[i]);
  1236.     }
  1237.  
  1238.     /* sort it */
  1239.     (void) qsort((char *)mv, mc, sizeof(*mv), queue_compare);
  1240.  
  1241.     return mv;
  1242. }
  1243.  
  1244. /*
  1245.  * queue_compare - compare two filenames which represent queue files
  1246.  */
  1247. static int
  1248. queue_compare(a, b)
  1249.     char **a;
  1250.     char **b;
  1251. {
  1252.     int a_grade = (*a)[strlen(*a) - 1];
  1253.     int b_grade = (*b)[strlen(*b) - 1];
  1254.  
  1255.     if (a_grade != b_grade) {
  1256.     return a_grade - b_grade;
  1257.     }
  1258.  
  1259.     return strcmpic(rindex(*a, '/'), rindex(*b, '/'));
  1260. }
  1261.