chip@tct.uucp (Chip Salzenberg) (03/08/90)
NOTE: THIS IS PART OF A COMBINED PATCH. APPLY PATCHES 7-9 TOGETHER. This patch contains changes to the following files: patchlevel.h deliver.h dest.c dest.h dfile.c log.c Index: patchlevel.h Prereq: 7 *************** *** 1,1 **** ! #define PATCHLEVEL 7 --- 1,1 ---- ! #define PATCHLEVEL 8 Index: deliver.h *************** *** 1,3 **** ! /* $Header: deliver.h,v 2.6 90/02/06 11:54:45 chip Exp $ * * General pull-it-together include file. --- 1,3 ---- ! /* $Header: deliver.h,v 2.8 90/03/06 12:17:42 chip Exp $ * * General pull-it-together include file. *************** *** 4,7 **** --- 4,18 ---- * * $Log: deliver.h,v $ + * Revision 2.8 90/03/06 12:17:42 chip + * Move logging into log.c and address parsing into addr.c. + * New: error delivery file for messages that fail. + * Major rearrangement of delivery file code. + * + * Revision 2.7 90/02/23 14:15:58 chip + * Support "#!" in delivery files. + * Support "user|program" and "user?error" from delivery files. + * Improve debugging and error message formatting. + * Rearrange code for clarity. + * * Revision 2.6 90/02/06 11:54:45 chip * Enforce MBX_MODE regardless of UMASK. *************** *** 59,62 **** --- 70,74 ---- extern char *sys_deliver; /* Systemwide delivery file */ extern char *post_deliver; /* Post-user delivery file */ + extern char *err_deliver; /* Error delivery file */ extern char *user_deliver; /* User delivery file */ extern char *sender; /* Who is sending this message? */ *************** *** 109,112 **** --- 121,126 ---- char *srealloc(); + char **choose_args(); + CONTEXT *name_context(); CONTEXT *uid_context(); *************** *** 113,122 **** FILE *ftcreate(); ! FILE *ct_popenv(); ! int ct_pclose(); DEST *dest(); DEST *first_dest(); DEST *next_dest(); time_t unctime(); --- 127,139 ---- FILE *ftcreate(); ! FILE *ct_fopenv(); + DEST *addr_dest(); DEST *dest(); DEST *first_dest(); DEST *next_dest(); + DEST **dest_array(); + + DCLASS addr_class(); time_t unctime(); Index: dest.c *************** *** 1,3 **** ! /* $Header: dest.c,v 2.2 89/09/29 18:17:57 network Exp $ * * Operations on the list of mail destinations. --- 1,3 ---- ! /* $Header: dest.c,v 2.5 90/03/06 12:21:10 chip Exp $ * * Operations on the list of mail destinations. *************** *** 4,7 **** --- 4,21 ---- * * $Log: dest.c,v $ + * Revision 2.5 90/03/06 12:21:10 chip + * Move logging into log.c and address parsing into addr.c. + * New: error delivery file for messages that fail. + * Major rearrangement of delivery file code. + * + * Revision 2.4 90/02/23 16:35:53 chip + * \Fix problems determining legality of user references. + * + * Revision 2.3 90/02/23 14:16:42 chip + * Support "#!" in delivery files. + * Support "user|program" and "user?error" from delivery files. + * Improve debugging and error message formatting. + * Rearrange code for clarity. + * * Revision 2.2 89/09/29 18:17:57 network * Save message when delivery file produces no output, *************** *** 26,30 **** --- 40,61 ---- #define HEADPTR (&deadhead) + /* + * Local functions. + */ + + static int destcmp(); + static int ptrcmp(); + /*---------------------------------------------------------------------- + * Consider mail to the given user as undeliverable. + */ + + dest_undel(name) + char *name; + { + (void) dest(name, CL_MBOX, MBX_UNDEL); + } + + /*---------------------------------------------------------------------- * Add a new destination to the list (unless it already exists). * Return pointer to DEST. *************** *** 32,49 **** DEST * ! dest(name, mailbox) char *name; ! char *mailbox; { DEST *d; ! DCLASS class; ! if (strchr(name, '!')) ! class = CL_UUCP; ! else if (mailbox) ! class = CL_MBOX; ! else ! class = CL_USER; for (d = HEADPTR->d_next; d != HEADPTR; d = d->d_next) { --- 63,80 ---- DEST * ! dest(name, class, s) char *name; ! DCLASS class; ! char *s; { DEST *d; ! ! /* Make sure that parameter is provided when it's needed. */ ! if ((class == CL_MBOX || class == CL_PROG) != (s != NULL)) ! return NULL; + /* Look for an already-existing copy of the given destination. */ + for (d = HEADPTR->d_next; d != HEADPTR; d = d->d_next) { *************** *** 54,64 **** continue; ! /* ! * If this destination has a named mailbox, then ! * test it for equality as well. ! */ ! ! if (class == CL_MBOX ! && strcmp(d->d_mailbox, mailbox) != 0) continue; --- 85,89 ---- continue; ! if (s && strcmp(d->d_param, s) != 0) continue; *************** *** 78,85 **** d->d_state = ST_WORKING; d->d_name = copystr(name); ! if (class == CL_MBOX) ! d->d_mailbox = copystr(mailbox); ! else ! d->d_mailbox = NULL; /* --- 103,107 ---- d->d_state = ST_WORKING; d->d_name = copystr(name); ! d->d_param = s ? copystr(s) : NULL; /* *************** *** 87,91 **** */ ! if (!valid_address(name)) dest_err(d, E_IVADDR); else if (class != CL_UUCP && name_context(name) == NULL) --- 109,113 ---- */ ! if (!addr_clean(name)) dest_err(d, E_IVADDR); else if (class != CL_UUCP && name_context(name) == NULL) *************** *** 133,146 **** /*---------------------------------------------------------------------- ! * Return an error message given a DERROR. */ char * ! derrmsg(e) ! DERROR e; { static char unknown_buf[40]; ! switch (e) { case E_IVADDR: --- 155,277 ---- /*---------------------------------------------------------------------- ! * Return the number of destinations in the list. ! */ ! ! int ! dest_count() ! { ! DEST *d; ! int count; ! ! count = 0; ! for (d = HEADPTR->d_next; d && d != HEADPTR; d = d->d_next) ! ++count; ! ! return count; ! } ! ! /*---------------------------------------------------------------------- ! * Return an allocated array of DEST pointers, or NULL if none. ! * The given integer is set to the array size. ! * Note that the caller must FREE this array when he's done. ! */ ! ! DEST ** ! dest_array(countp) ! int *countp; ! { ! DEST **dv, *d; ! int i, count; ! ! if ((count = dest_count()) <= 0) ! { ! *countp = 0; ! return NULL; ! } ! ! dv = (DEST **) zalloc(count * sizeof(DEST *)); ! ! i = 0; ! for (d = HEADPTR->d_next; d && d != HEADPTR; d = d->d_next) ! { ! if (i < count) ! dv[i++] = d; ! } ! ! qsort((char *)dv, count, sizeof(dv[0]), destcmp); ! ! *countp = count; ! return dv; ! } ! ! /*---------------------------------------------------------------------- ! * Compare two DEST pointers, for output sorting. ! */ ! ! static int ! destcmp(p1, p2) ! char *p1, *p2; ! { ! DEST *d1, *d2; ! int cmp; ! ! d1 = *(DEST **)p1; ! d2 = *(DEST **)p2; ! ! #if 0 ! /* Errors go last. */ ! if ((d1->d_state == ST_ERROR) != (d2->d_state == ST_ERROR)) ! return (d1->d_state == ST_ERROR) ? 1 : -1; ! #endif ! ! #if 0 ! /* By class. */ ! if ((cmp = (int)d1->d_class - (int)d2->d_class) != 0) ! return (cmp < 0) ? -1 : 1; ! #endif ! ! /* By user name. */ ! if ((cmp = ptrcmp(d1->d_name, d2->d_name)) != 0) ! return cmp; ! ! /* By mailbox/program. */ ! if ((cmp = ptrcmp(d1->d_param, d2->d_param)) != 0) ! return cmp; ! ! return 0; ! } ! ! /*---------------------------------------------------------------------- ! * Compare two pointers, either of which may be NULL. */ + static int + ptrcmp(p, q) + char *p, *q; + { + if (p == q) + return 0; + if (p == NULL) + return -1; + if (q == NULL) + return 1; + return strcmp(p, q); + } + + /*---------------------------------------------------------------------- + * Return a destination's error message. + */ + char * ! derrmsg(d) ! DEST *d; { static char unknown_buf[40]; + static char no_error[] = "no error?!"; + + if (!d || d->d_state != ST_ERROR) + return no_error; ! switch (d->d_error) { case E_IVADDR: *************** *** 149,153 **** return "No such user"; case E_NSHOST: ! return "No such host (UUCP addresses)"; case E_CTPERM: return "No permissions for that context"; --- 280,284 ---- return "No such user"; case E_NSHOST: ! return "No such UUCP host"; case E_CTPERM: return "No permissions for that context"; *************** *** 156,164 **** case E_MBOX: return "Can't write to mailbox"; ! case E_UUX: ! return "Can't pipe to uux"; } ! (void) sprintf(unknown_buf, "Unknown error %d", e); return unknown_buf; } --- 287,301 ---- case E_MBOX: return "Can't write to mailbox"; ! case E_PROG: ! return "Subprocess reported failure when exiting"; ! case E_PIPE: ! return "Subprocess died while reading its standard input"; ! case E_DFONLY: ! return "Address not valid from command line"; ! case E_ERRMSG: ! return d->d_errmsg ? d->d_errmsg : no_error; } ! (void) sprintf(unknown_buf, "Unknown error %d", d->d_error); return unknown_buf; } Index: dest.h *************** *** 1,3 **** ! /* $Header: dest.h,v 2.1 89/06/09 12:25:23 network Exp $ * * Description of a mail destination and its state. --- 1,3 ---- ! /* $Header: dest.h,v 2.2 90/02/23 14:16:41 chip Exp $ * * Description of a mail destination and its state. *************** *** 4,7 **** --- 4,13 ---- * * $Log: dest.h,v $ + * Revision 2.2 90/02/23 14:16:41 chip + * Support "#!" in delivery files. + * Support "user|program" and "user?error" from delivery files. + * Improve debugging and error message formatting. + * Rearrange code for clarity. + * * Revision 2.1 89/06/09 12:25:23 network * Update RCS revisions. *************** *** 19,22 **** --- 25,29 ---- CL_USER, /* User name, no mailbox */ CL_MBOX, /* User name, with mailbox name */ + CL_PROG, /* Program to run with message on stdin */ CL_UUCP /* UUCP address (bang path) */ } DCLASS; *************** *** 39,42 **** --- 46,50 ---- typedef enum { E_IVADDR, /* invalid address string */ + E_DFONLY, /* "user:mbox" etc. for delfiles only */ E_NSUSER, /* no such user */ E_NSHOST, /* no such host (UUCP addresses) */ *************** *** 44,48 **** E_CTLOST, /* context lost (should never happen) */ E_MBOX, /* can't write to mailbox */ ! E_UUX /* can't pipe to uux */ } DERROR; --- 52,58 ---- E_CTLOST, /* context lost (should never happen) */ E_MBOX, /* can't write to mailbox */ ! E_PROG, /* subprocess exited with non-zero */ ! E_PIPE, /* can't pipe to subprocess (incl. uux) */ ! E_ERRMSG /* other user-described error */ } DERROR; *************** *** 57,64 **** DCLASS d_class; /* destination class */ DSTATE d_state; /* destination state */ ! DERROR d_error; /* error message (if state is ERROR) */ int d_dfdone; /* boolean -- delivery file was run */ char *d_name; /* context for delivery */ ! char *d_mailbox; /* mailbox name or NULL for default */ }; --- 67,75 ---- DCLASS d_class; /* destination class */ DSTATE d_state; /* destination state */ ! DERROR d_error; /* error code (if state is ST_ERROR) */ ! char *d_errmsg; /* error message (if error is E_ERRMSG) */ int d_dfdone; /* boolean -- delivery file was run */ char *d_name; /* context for delivery */ ! char *d_param; /* parameter (mailbox or program name) */ }; Index: dfile.c *************** *** 1,3 **** ! /* $Header: dfile.c,v 2.4 89/11/10 12:23:52 network Exp $ * * Filter destination(s) through delivery file(s). --- 1,3 ---- ! /* $Header: dfile.c,v 2.7 90/03/06 12:21:12 chip Exp $ * * Filter destination(s) through delivery file(s). *************** *** 4,7 **** --- 4,21 ---- * * $Log: dfile.c,v $ + * Revision 2.7 90/03/06 12:21:12 chip + * Move logging into log.c and address parsing into addr.c. + * New: error delivery file for messages that fail. + * Major rearrangement of delivery file code. + * + * Revision 2.6 90/02/23 16:35:54 chip + * \Fix problems determining legality of user references. + * + * Revision 2.5 90/02/23 14:16:44 chip + * Support "#!" in delivery files. + * Support "user|program" and "user?error" from delivery files. + * Improve debugging and error message formatting. + * Rearrange code for clarity. + * * Revision 2.4 89/11/10 12:23:52 network * Be more selective about trying to deliver to MBX_UNDEL. *************** *** 27,92 **** /*---------------------------------------------------------------------- ! * Filter all valid destinations through the global delivery file. */ ! sys_dfile(dac, dav) ! int dac; ! char **dav; ! { ! char **fav; ! int fac, a; ! ! /* ! * If there is no global delivery file, forget it. ! */ ! ! if (!exists(relpath(eff_ct->ct_home, sys_deliver))) ! { ! if (verbose) ! message("no system delivery file\n"); ! return -1; ! } ! ! /* ! * If we've been asked not to run delivery files, forget it. ! */ ! if (!rundfiles) { ! if (verbose) ! message("system delivery file disabled\n"); ! return -1; ! } ! /* ! * Collect the arguments for the delivery file. ! */ ! fav = (char **) zalloc((dac + 3) * sizeof(char **)); ! fav[0] = shell; ! fav[1] = sys_deliver; ! fac = 2; ! for (a = 0; a < dac; ++a) ! { ! char *addr; ! addr = dav[a]; ! if (valid_address(addr)) ! { ! /* Let the delivery file handle valid addresses. */ - fav[fac++] = addr; - } else ! { ! /* Note invalid address(es); report them later. */ ! ! (void) dest(addr, (char *) NULL); ! } } - fav[fac] = NULL; - /* * If there were any good names found, let loose the delivery --- 41,83 ---- /*---------------------------------------------------------------------- ! * Filter all valid destinations through the system delivery file. ! * Return 1 (executed), 0 (not executed), -1 (no attempt made). */ ! static int sys_ac; ! static char **sys_av; ! ! char ** ! sys_args(pac) ! int *pac; ! { ! char **fav; ! int fac, a; ! ! fav = (char **) zalloc(sys_ac * sizeof(char **)); ! fac = 0; ! for (a = 0; a < sys_ac; ++a) { ! char *addr; ! addr = sys_av[a]; ! /* Note invalid address(es); report them later. */ ! if (!addr_clean(addr)) ! (void) dest(addr, CL_USER, (char *) NULL); ! ! /* Note non-user address(es); report them later. */ ! ! else if (addr_class(addr) != CL_USER) ! (void) addr_dest(addr, (CONTEXT *)NULL); ! /* Let the system delivery file handle the rest. */ else ! fav[fac++] = addr; } /* * If there were any good names found, let loose the delivery *************** *** 96,118 **** */ ! if (fac > 2) { ! /* ! * If we get nothing back from the system delivery file, ! * put the message in the "undelivered" mailbox. ! */ ! ! if (do_dfile(eff_ct, fav, (DEST *)NULL) == 0) ! { ! if (verbose) ! message("sys_dfile: no output\n"); ! ! (void) dest(eff_ct->ct_name, MBX_UNDEL); ! } } ! free((char *) fav); ! return 0; } --- 87,107 ---- */ ! if (fac <= 0) { ! free((char *) fav); ! fav = NULL; } ! *pac = fac; ! return fav; ! } ! sys_dfile(ac, av) ! int ac; ! char **av; ! { ! sys_ac = ac; ! sys_av = av; ! return glob_dfile("system", sys_deliver, sys_args, FALSE); } *************** *** 120,139 **** * Filter some undelivered destinations through the post-user * delivery file. */ post_dfile() { ! DEST *d; ! char **fav; ! int num_dests, fac; ! /* ! * If there is no post-user delivery file, forget it. ! */ ! if (!exists(relpath(eff_ct->ct_home, post_deliver))) ! { ! if (verbose) ! message("no post-user delivery file\n"); return -1; } --- 109,189 ---- * Filter some undelivered destinations through the post-user * delivery file. + * Return 1 (executed), 0 (not executed), -1 (no attempt made). */ + post_choose(d) + DEST *d; + { + if ((d->d_class == CL_USER || d->d_class == CL_UUCP) + && (d->d_state == ST_WORKING + || (d->d_state == ST_ERROR && d->d_error == E_NSUSER))) + { + d->d_state = ST_HOLD; + return TRUE; + } + + return FALSE; + } + + char ** + post_args(pac) + int *pac; + { + return choose_args(pac, post_choose); + } + post_dfile() { ! return glob_dfile("post-user", post_deliver, post_args, FALSE); ! } ! /*---------------------------------------------------------------------- ! * Filter broken (but well-formed) destinations through the error ! * delivery file. ! * Return 1 (executed), 0 (not executed), -1 (no attempt made). ! */ ! err_choose(d) ! DEST *d; ! { ! return (d->d_state == ST_ERROR); ! } ! ! char ** ! err_args(pac) ! int *pac; ! { ! return choose_args(pac, err_choose); ! } ! ! err_dfile() ! { ! return glob_dfile("error", err_deliver, err_args, TRUE); ! } ! ! /*---------------------------------------------------------------------- ! * Execute a global delivery file given description of delivery file, ! * its path, a function to get arguments, and a boolean to indicate whether ! * a lack of output is a normal condition. ! * Return 1 (executed), 0 (not executed), -1 (no attempt made). ! */ ! ! glob_dfile(desc, dfile, args, silent_ok) ! char *desc; ! char *dfile; ! char **(*args)(); ! int silent_ok; ! { ! char **fav; ! int fac; ! ! /* ! * If the delivery file is missing, forget it. ! */ ! ! if (!exists(relpath(eff_ct->ct_home, dfile))) ! { ! if (verbose) ! message("no %s delivery file\n", desc); return -1; } *************** *** 146,150 **** { if (verbose) ! message("post-user delivery file disabled\n"); return -1; } --- 196,200 ---- { if (verbose) ! message("%s delivery file disabled\n", desc); return -1; } *************** *** 151,198 **** /* ! * Generate the delivery file argument list. */ ! num_dests = 0; ! for (d = first_dest(); d; d = next_dest(d)) ! ++num_dests; ! fav = (char **) zalloc((num_dests + 3) * sizeof(char *)); ! fav[0] = shell; ! fav[1] = post_deliver; ! fac = 2; for (d = first_dest(); d; d = next_dest(d)) { ! if ((d->d_class == CL_USER || d->d_class == CL_UUCP) ! && (d->d_state == ST_WORKING ! || (d->d_state == ST_ERROR && d->d_error == E_NSUSER))) ! { fav[fac++] = d->d_name; - d->d_state = ST_HOLD; - } } ! fav[fac] = NULL; ! ! if (fac > 2) { ! /* ! * If we get nothing back from the post-user delivery file, ! * put the message in the "undelivered" mailbox. ! */ ! ! if (do_dfile(eff_ct, fav, (DEST *)NULL) == 0) ! { ! if (verbose) ! message("post_dfile: no output\n"); ! ! (void) dest(eff_ct->ct_name, MBX_UNDEL); ! } } ! free((char *) fav); ! ! return 0; } --- 201,274 ---- /* ! * Now we can get the argument list. ! * If the list is empty, that's all she wrote. */ ! if ((fav = (*args)(&fac)) == NULL) ! return 0; ! ! /* ! * "Just do it." ! * If we get nothing back from the delivery file, ! * and if silence is not supposed to be permitted, ! * put the message in the "undelivered" mailbox. ! */ ! ! if (run_dfile(eff_ct, dfile, fac, fav, (DEST *)NULL) <= 0 ! && !silent_ok) ! { ! if (verbose) ! message("%s delivery file: no output\n", desc); ! dest_undel(eff_ct->ct_name); ! } ! ! /* ! * The argument function allocated the arguments; we free them. ! */ ! ! free((char *) fav); ! ! /* ! * Return "done". ! */ ! ! return 1; ! } ! ! /*---------------------------------------------------------------------- ! * Generate a delivery file argument list. ! * We do this by getting an array of all destinations, ! * then keeping only the ones we want. ! */ ! ! char ** ! choose_args(pac, choose) ! int *pac; ! int (*choose)(); ! { ! DEST *d; ! char **fav; ! int count, fac; ! ! if ((count = dest_count()) <= 0) ! return NULL; ! fav = (char **) zalloc(count * sizeof(char *)); ! fac = 0; for (d = first_dest(); d; d = next_dest(d)) { ! if ((*choose)(d)) fav[fac++] = d->d_name; } ! if (fac <= 0) { ! free((char *) fav); ! fav = NULL; } ! *pac = fac; ! return fav; } *************** *** 199,202 **** --- 275,279 ---- /*---------------------------------------------------------------------- * Filter all user destinations through their local delivery files. + * Return 1 (some executed), 0 (none executed), -1 (no attempt made). */ *************** *** 204,208 **** { DEST *d; ! int nfound; /* --- 281,285 ---- { DEST *d; ! int nfound, ret; /* *************** *** 214,222 **** if (verbose) message("user delivery files disabled\n"); - return -1; } - /* * Continue to loop through all addresses until no destination --- 291,297 ---- *************** *** 224,227 **** --- 299,303 ---- */ + ret = 0; do { nfound = 0; *************** *** 232,237 **** && !d->d_dfdone) { ! one_dfile(d); d->d_dfdone = TRUE; } } --- 308,314 ---- && !d->d_dfdone) { ! u_dfile(d); d->d_dfdone = TRUE; + ret = 1; } } *************** *** 238,254 **** } while (nfound > 0); ! return 0; } /*---------------------------------------------------------------------- ! * Run the delivery file (if any) for the specified destination. */ ! one_dfile(d) DEST *d; { - CONTEXT *ct; - char *s, *udel_path, *fav[4]; struct stat st; if ((ct = name_context(d->d_name)) == NULL) --- 315,332 ---- } while (nfound > 0); ! return ret; } /*---------------------------------------------------------------------- ! * Run the user delivery file (if any) for the specified destination. */ ! u_dfile(d) DEST *d; { struct stat st; + CONTEXT *ct; + char *s, *udel_path; + int n; if ((ct = name_context(d->d_name)) == NULL) *************** *** 304,325 **** /* * Time to run the file! */ ! fav[0] = shell; ! fav[1] = udel_path; ! fav[2] = d->d_name; ! fav[3] = NULL; ! ! if (do_dfile(ct, fav, d) == 0) ! { ! /* ! * If we get nothing back from the user delivery file, ! * put the message in the user's "undelivered" mailbox. ! */ ! ! if (verbose) ! message("one_dfile: no output\n"); ! ! (void) dest(ct->ct_name, MBX_UNDEL); } --- 382,396 ---- /* * Time to run the file! + * If we get nothing back from the user delivery file, + * put the message in the user's "undelivered" mailbox-- + * or ours, depending on the kind of failure. */ ! n = run_dfile(ct, udel_path, 1, &d->d_name, d); ! if (n <= 0) ! { ! if (verbose) ! message("u_dfile: no output\n"); ! dest_undel((n == 0 ? ct : eff_ct)->ct_name); } *************** *** 328,332 **** /*---------------------------------------------------------------------- ! * Process a delivery file. * Return the count of valid destinations we got back from it. * If delivering to MBX_UNDEL is possible, errors return zero. --- 399,403 ---- /*---------------------------------------------------------------------- ! * Execute a delivery file (global or user). * Return the count of valid destinations we got back from it. * If delivering to MBX_UNDEL is possible, errors return zero. *************** *** 335,346 **** int ! do_dfile(ct, av, d) CONTEXT *ct; ! char **av; DEST *d; { FILE *fp; ! char *name, *mailbox; ! int count; if (!ct) --- 406,420 ---- int ! run_dfile(ct, dfile, fac, fav, d) CONTEXT *ct; ! char *dfile; ! int fac; ! char **fav; DEST *d; { FILE *fp; ! char **av; ! int ac, a, fd, linecount; ! static char buf[BUFSIZ]; if (!ct) *************** *** 347,351 **** return -1; ! if (! ok_context(ct)) { if (d) --- 421,425 ---- return -1; ! if (! ok_context(eff_uid, real_uid, real_gid, ct)) { if (d) *************** *** 382,385 **** --- 456,514 ---- } + /* Produce the arguments in the form we need. */ + + av = (char **) zalloc((fac + 4) * sizeof(char *)); + ac = 0; + + /* Process the "#!" hack. */ + + if ((fd = open(dfile, O_RDONLY)) != -1) + { + char *p; + int rd; + static char hashbang[64]; /* arbitrary */ + + rd = read(fd, hashbang, sizeof(hashbang) - 1); + (void) close(fd); + hashbang[rd > 0 ? rd : 0] = 0; + + if ((p = strchr(hashbang, '\n')) != NULL + && hashbang[0] == '#' + && hashbang[1] == '!') + { + *p = 0; + + /* Interpreter. */ + + p = hashbang + 2; + while (isspace(*p)) + ++p; + av[ac++] = p; + while (*p && !isspace(*p)) + ++p; + if (*p) + *p++ = 0; + + /* Only one argument; sorry. */ + + while (isspace(*p)) + ++p; + if (*p) + av[ac++] = p; + } + } + + /* + * If no "#!" found, use the default shell. + * Then add the delivery file, address(es), and a NULL. + */ + + if (ac == 0) + av[ac++] = shell; + av[ac++] = dfile; + for (a = 0; a < fac; ++a) + av[ac++] = fav[a]; + av[ac] = NULL; + /* Here we go! */ *************** *** 387,391 **** message("Processing delivery file as %s\n", ct->ct_name); ! if ((fp = ct_popenv(ct, shell, av, "r")) == NULL) { error("can't execute delivery file as %s\n", ct->ct_name); --- 516,528 ---- message("Processing delivery file as %s\n", ct->ct_name); ! fp = ct_fopenv(ct, av[0], av, "r"); ! ! /* We don't need the argument vector any more. */ ! ! free((char *) av); ! ! /* If something went wrong, bail out now. */ ! ! if (fp == NULL) { error("can't execute delivery file as %s\n", ct->ct_name); *************** *** 397,503 **** */ ! count = 0; ! while (dfile_gets(fp, &name, &mailbox) >= 0) { ! DEST *nd; ! ! ++count; ! if (strcmp(name, DFILE_DROP) == 0) { ! if (verbose) ! message("delivery file says OK to drop\n"); ! continue; ! } ! nd = dest(name, mailbox); ! if (nd->d_state == ST_HOLD) ! nd->d_state = ST_WORKING; ! ! /* ! * If the delivery file specified a mailbox, verify ! * that the user whose delivery file is running has ! * permissions for the requested context. ! */ ! ! if ((nd->d_state == ST_WORKING) && (mailbox != NULL)) ! { ! CONTEXT *nct; ! if ((nct = name_context(name)) == NULL) ! dest_err(nd, E_CTLOST); ! else if (! ok_context(nct)) ! dest_err(nd, E_CTPERM); } ! if (nd->d_state != ST_ERROR) ! ++count; ! } ! ! (void) ct_pclose(fp); ! return count; ! } ! /*---------------------------------------------------------------------- ! * Get and parse a single delivery file output line. ! */ ! int ! dfile_gets(fp, namep, mailboxp) ! FILE *fp; ! char **namep; ! char **mailboxp; ! { ! char *p, *q; ! static char buf[BUFSIZ]; ! if (fgets(buf, GETSIZE(buf), fp) == NULL) ! return -1; ! if ((p = strchr(buf, '\n')) != NULL) ! *p = 0; ! else ! { ! int c; ! while ((c = fgetc(fp)) != '\n' && c != EOF) ! ; /* keep reading */ ! error("invalid line from delivery file: '%s'\n", buf); ! return -1; ! } ! /* Strip out all whitespace and eliminate duplicated slashes */ ! p = q = buf; ! while (*p) ! { ! if (isspace(*p)) ! ++p; ! else if ((*q++ = *p++) == '/') ! { ! while (*p == '/') ! ++p; ! } } - *q = 0; ! /* Debugging message: display input line */ ! ! if (verbose) ! message("\t'%s'\n", buf); ! ! if ((p = strchr(buf, ':')) != NULL) ! { ! *p++ = 0; ! if ((q = strchr(p, ':')) != NULL) ! *q = 0; ! } ! *namep = buf; ! *mailboxp = p; ! return 0; } --- 534,589 ---- */ ! linecount = 0; ! while (fgets(buf, GETSIZE(buf), fp) != NULL) { ! DEST *nd; ! char *p; ! if ((p = strchr(buf, '\n')) != NULL) ! *p = 0; ! else { ! int c; ! while ((c = fgetc(fp)) != '\n' && c != EOF) ! ; /* keep reading */ ! error("invalid line from delivery file: '%s'\n", buf); ! continue; } ! /* Debugging message: display input line. */ ! if (verbose) ! message("\t'%s'\n", buf); ! /* Okay-to-drop directive is a special case. */ ! if (strcmp(buf, DFILE_DROP) == 0) ! { ! message("\tDelivery file says OK to drop\n"); ! ++linecount; ! continue; ! } ! /* Parse destination; if none, look for next line. */ ! if ((nd = addr_dest(buf, ct)) == NULL) ! continue; ! /* We got some output; remember that. */ ! ++linecount; ! /* If destination was on hold, it's not so anymore. */ ! if (nd->d_state == ST_HOLD) ! nd->d_state = ST_WORKING; } ! (void) ct_fclose(fp); ! return linecount; } Index: log.c *************** *** 0 **** --- 1,403 ---- + /* $Header: log.c,v 2.2 90/03/05 17:54:53 chip Exp $ + * + * Deliver logging. + * + * $Log: log.c,v $ + * Revision 2.2 90/03/05 17:54:53 chip + * Create log.c from various routines throughout the code. + * + */ + + #include "deliver.h" + #include <time.h> + + /*---------------------------------------------------------------------- + * Open temporary log files. + */ + + openlogs() + { + #ifdef ERRLOG + /* If we're delivering and not being verbose, keep an error log. */ + + if (!dryrun && !verbose) + { + errlogfile = tempfile(); + if ((errlog = ftcreate(errlogfile)) == NULL) + syserr("can't create %s", errlogfile); + } + #endif + + #ifdef LOG + /* If we're delivering and the log file exists, keep data for it. */ + + if (!dryrun && exists(LOG)) + { + logfile = tempfile(); + if ((log = ftcreate(logfile)) == NULL) + syserr("can't create %s", logfile); + } + #endif + } + + /*------------------------------------------------------------------------ + * Discard temporary log files. + */ + + tosslogs() + { + if (logfile && unlink(logfile) == -1) + (void) syserr("can't remove %s", logfile); + if (errlogfile && unlink(errlogfile) == -1) + (void) syserr("can't remove %s", logfile); + } + + /*---------------------------------------------------------------------- + * Write a report to the log file. + */ + + logreport(ac, av) + int ac; + char **av; + { + int a; + + if (!log) + return; + + logstart(log); + + if (sender && *sender) + (void) fprintf(log, "sender: %s\n", sender); + if (boxdelivery) + (void) fprintf(log, "mailbox%s:", (ac > 1) ? "es" : ""); + else + (void) fprintf(log, "destination%s:", (ac > 1) ? "s" : ""); + for (a = 0; a < ac; ++a) + (void) fprintf(log, " \"%s\"", av[a]); + (void) fputc('\n', log); + + logstate("delivered", ST_DONE); + logstate("failed", ST_ERROR); + + logdone(log); + } + + /*---------------------------------------------------------------------- + * Log the destinations with the given state. + * If any are found, the list is prefixed with the given description. + */ + + logstate(desc, state) + char *desc; + DSTATE state; + { + DEST *d; + int dcount; + + dcount = 0; + for (d = first_dest(); d; d = next_dest(d)) + { + if (d->d_state != state) + continue; + + if (++dcount == 1) + (void) fprintf(log, "%s:", desc); + (void) fprintf(log, " %s", d->d_name); + if (d->d_class == CL_MBOX) + (void) fprintf(log, ":%s", d->d_param); + else if (d->d_class == CL_PROG) + (void) fprintf(log, "|\"%s\"", d->d_param); + } + if (dcount) + (void) fputc('\n', log); + } + + /*---------------------------------------------------------------------- + * Save contents of temporary logs in the real logfiles. + */ + + savelogs() + { + /* If logs weren't kept, forget it. */ + + if (!log && !errlog) + return; + + /* If temporary logs contain anything, append them to real logs. */ + + if ((log && ftell(log) > 0) + || (errlog && ftell(errlog) > 0)) + { + if (create_lockfile(LOGLOCK) == 0) + { + #ifdef LOG + applog(&log, LOG); + #endif + errdone(); + #ifdef ERRLOG + applog(&errlog, ERRLOG); + #endif + (void) remove_lockfile(LOGLOCK); + } + } + } + + /*---------------------------------------------------------------------- + * Append a temporary log file to a real logfile. + * We pass a FILE **, so that it can be set to NULL when closed; + * this is important, since errlog is used by syserr(). + * Note: The logfile is ass_u_med to be locked already! + */ + + applog(fpp, realfile) + FILE **fpp; + char *realfile; + { + FILE *fp = fpp ? *fpp : NULL; + int fd, realfd; + + /* If log data weren't kept, never mind. */ + + if (fp == NULL) + return; + + /* Flush buffered data. */ + + (void) fflush(fp); + + /* If the file is empty, never mind. */ + + if (ftell(fp) == 0) + { + (void) fclose(fp); + *fpp = NULL; + return; + } + + /* Get an fd and close the stream. */ + + if ((fd = dup(fileno(fp))) == -1) + { + syserr("can't dup log fd"); + (void) fclose(fp); + *fpp = NULL; + return; + } + (void) fclose(fp); + *fpp = NULL; + + /* + * Open the real logfile, creating it if necessary. + * Note that there is no race condition since the logs are locked. + */ + + #ifdef O_CREAT + realfd = open(realfile, O_WRONLY|O_CREAT, 0666); + #else + if ((realfd = open(realfile, O_WRONLY)) == -1) + realfd = creat(realfile, 0666); + #endif + if (realfd == -1) + syserr("can't open %s for writing", logfile); + else + { + /* Append the temporary log to the real log. */ + + (void) lseek(fd, 0L, 0); + (void) lseek(realfd, 0L, 2); + (void) copyfd(fd, realfd); + (void) close(realfd); + } + + /* Close the temporary log. */ + + (void) close(fd); + } + + /*---------------------------------------------------------------------- + * Record any interesting information in the error log file. + */ + + errinfo() + { + if (!errlog) + return; + + /* Log undelivered mail. */ + + errundel(); + + /* If any errors have been logged, record the failed header. */ + + if (ftell(errlog) > 0) + errheader(); + } + + /*---------------------------------------------------------------------- + * Log undelivered mail. + * + * Note that this algorithm assumes that delivery to the MBX_UNDEL mailbox + * is always worth reporting. + */ + + errundel() + { + DEST *d; + + if (!errlog) + return; + + for (d = first_dest(); d; d = next_dest(d)) + { + if (d->d_state == ST_DONE + && d->d_class == CL_MBOX + && strcmp(d->d_param, MBX_UNDEL) == 0) + { + CONTEXT *ct; + char *home; + + if ((ct = name_context(d->d_name)) != NULL) + home = ct->ct_home; + else + home = "~"; /* should never happen */ + + errstart(); + (void) fprintf(errlog, + "Undelivered mail for %s put in %s/%s\n", + d->d_name, home, MBX_UNDEL); + } + } + } + + /*---------------------------------------------------------------------- + * Log the message header. + */ + + errheader() + { + FILE *hfp; + int hfd; + + if (!errlog) + return; + + /* Copy the failed message's header. */ + + hfd = dup(tfd[T_HDR]); + hfp = (hfd < 0) ? NULL : fdopen(hfd, "r"); + if (hfp == NULL) + { + (void) fprintf(errlog, "%s: can't open header file %s\n", + progname, tfile[T_HDR]); + } + else + { + int c, oc; + + (void) fprintf(errlog, "+ Header:\n"); + + (void) fseek(hfp, 0L, 0); + oc = '\n'; + while ((c = getc(hfp)) != EOF) + { + if (oc != '\n' || c != '\n') + { + if (oc == '\n') + (void) fputs("| ", errlog); + (void) putc(c, errlog); + } + oc = c; + } + + (void) fclose(hfp); + } + } + + /*---------------------------------------------------------------------- + * Record a time stamp in the error log file. + */ + + errstart() + { + /* If we've already written a time stamp, don't do it again. */ + + if (!errlog || ftell(errlog) > 0) + return; + + /* Write a time stamp and various useful info. */ + + logstart(errlog); + (void) fprintf(errlog, "process %d", getpid()); + if (rec_parent > 0) + (void) fprintf(errlog, ", parent %d", rec_parent); + (void) fprintf(errlog, ": %s %s\n", progname, version); + } + + /*---------------------------------------------------------------------- + * Record the end of this process's error log entry. + */ + + errdone() + { + /* If we never wrote to the error log file, do nothing. */ + + if (!errlog || ftell(errlog) == 0) + return; + + /* Write a simple closing line for the error log entry. */ + + (void) fprintf(errlog, "process %d", getpid()); + if (rec_parent > 0) + (void) fprintf(errlog, ", parent %d", rec_parent); + (void) fprintf(errlog, ": exit\n"); + + logdone(errlog); + } + + /*---------------------------------------------------------------------- + * Start a log entry. + * Various useful info goes here -- especially a timestamp. + */ + + logstart(fp) + FILE *fp; + { + struct tm *lt; + time_t now; + static char month[12][4] = { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", + }; + + (void) time(&now); + lt = localtime(&now); + + (void) fputc('\n', fp); + if (rec_level) + (void) fprintf(fp, "[%d]", rec_level); + else + (void) fputs("---", fp); + (void) fputs("------------------------ ", fp); + (void) fprintf(fp, "%d %s %d, %02d:%02d:%02d %s\n", + lt->tm_mday, month[lt->tm_mon], lt->tm_year + 1900, + lt->tm_hour, lt->tm_min, lt->tm_sec, + #ifdef USG + tzname[lt->tm_isdst ? 1 : 0] + #else + lt->tm_zone + #endif + ); + } + + /*---------------------------------------------------------------------- + * Write a concluding marker to the given logfile. + * This marker separates instances of Deliver at recursion level zero. + */ + + logdone(fp) + FILE *fp; + { + if (rec_level == 0) + (void) fputs("===========================\n\n", fp); + } -- Chip Salzenberg at ComDev/TCT <chip%tct@ateng.com>, <uunet!ateng!tct!chip> "The Usenet, in a very real sense, does not exist."