Subject: v23i006: Deliver 2.0 patches, Part06/10 Newsgroups: comp.sources.unix Approved: rsalz@uunet.UU.NET X-Checksum-Snefru: 25ad8b8e 2563342e e42634ff 5fb7fcb8 Submitted-by: Chip Salzenberg Posting-number: Volume 23, Issue 6 Archive-name: deliver2.0pch/part06 NOTE: THIS IS PART OF A COMBINED PATCH. APPLY PATCHES 7-9 TOGETHER. Changes in Deliver 2.0 patch 7-9: 1. A new delivery file, the "error delivery file," is executed when one or more destinations cannot be delivered. This feature should prove useful to those of you who want copies of bounced messages. 2. Deliver interprets a leading "#!" in delivery files in the way you'd expect; e.g. "#!/bin/perl" as the first line of a delivery file makes Deliver execute the given delivery file with Perl instead of the shell. 3. Delivery files can output two new forms of destinations: echo "user|some_program" # Pipe message to the given program echo "user?error message" # Fail with specified error message 4. A missing user name defaults to the current user. This feature is most useful in the context of "|program" or "?error". This patch contains changes to the following files: patchlevel.h Makefile README addr.c config.h context.c debug.c deliver.8 Index: patchlevel.h Prereq: 6 *************** *** 1,1 **** ! #define PATCHLEVEL 6 --- 1,1 ---- ! #define PATCHLEVEL 7 Index: Makefile *************** *** 1,3 **** ! # $Header: Makefile,v 2.5 89/11/10 12:23:47 network Exp $ # # Makefile for deliver --- 1,3 ---- ! # $Header: Makefile,v 2.6 90/03/06 12:15:39 chip Exp $ # # Makefile for deliver *************** *** 74,79 **** HDRS = config.h context.h deliver.h dest.h patchlevel.h misc.h ! DELSRC1 = context.c copymsg.c ! DELSRC2 = debug.c dest.c dfile.c lock.c main.c DELSRC3 = mbox.c procs.c subs.c sysdep.c uucp.c DELYY = unctime.y --- 74,79 ---- HDRS = config.h context.h deliver.h dest.h patchlevel.h misc.h ! DELSRC1 = addr.c context.c copymsg.c ! DELSRC2 = debug.c dest.c dfile.c lock.c log.c main.c DELSRC3 = mbox.c procs.c subs.c sysdep.c uucp.c DELYY = unctime.y *************** *** 86,91 **** SAMPLES = samples samples/* ! DELOBJS = context.o copymsg.o debug.o dest.o dfile.o lock.o \ ! main.o mbox.o procs.o subs.o sysdep.o unctime.o uucp.o UIDOBJS = uid.o HDROBJS = header.o --- 86,91 ---- SAMPLES = samples samples/* ! DELOBJS = addr.o context.o copymsg.o debug.o dest.o dfile.o lock.o \ ! log.o main.o mbox.o procs.o subs.o sysdep.o unctime.o uucp.o UIDOBJS = uid.o HDROBJS = header.o Index: README *************** *** 1,3 **** ! $Header: README,v 2.1 89/06/09 12:25:10 network Exp $ --- 1,3 ---- ! $Header: README,v 2.2 90/03/06 15:11:50 chip Exp $ *************** *** 19,23 **** Deliver was invented to be a direct replacement for the Xenix ! program /usr/lib/mail/mail.local. In fact, on ateng, mail.local is just another link to /usr/bin/deliver. --- 19,23 ---- Deliver was invented to be a direct replacement for the Xenix ! program /usr/lib/mail/mail.local. In fact, on tct, mail.local is just another link to /usr/bin/deliver. *************** *** 76,81 **** If you run into a bug, you are probably not alone. Save your fellow human ! beings from toil and trouble! Send mail to . Bug ! reports accepted; patches greatly appreciated. I will coordinate patches. Thank you, and good night. --- 76,81 ---- If you run into a bug, you are probably not alone. Save your fellow human ! beings from toil and trouble! Bug reports and patches are gladly accepted ! and appreciated. Please mail them to the one of the addresses below. Thank you, and good night. *************** *** 82,88 **** Chip Salzenberg ! A T Engineering ! or ! or ! --- 82,89 ---- Chip Salzenberg ! ComDev/TC Telemanagement ! ! ! ! Index: addr.c *************** *** 0 **** --- 1,220 ---- + /* $Header: addr.c,v 2.2 90/03/06 12:21:18 chip Exp $ + * + * Operations on addresses, (ASCII representations of destinations). + * + * $Log: addr.c,v $ + * Revision 2.2 90/03/06 12:21:18 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. + * + */ + + #include "deliver.h" + + /*---------------------------------------------------------------------- + * Check an address for cleanliness. + * That is, make sure that it is devoid of shell metacharacters. + * And make sure it's got at least one character, too. + */ + + addr_clean(addr) + char *addr; + { + char *p; + static char sanitize[] = SANITIZE; + + if (*addr == 0) + return FALSE; + + for (p = addr; *p; ++p) + { + if (strchr(sanitize, *p)) + return FALSE; + } + + return TRUE; + } + + /*---------------------------------------------------------------------- + * Report the class of a string representation of a destination. + */ + + DCLASS + addr_class(s) + char *s; + { + char *p; + + for (p = s; *p; ++p) + { + switch (*p) + { + case '!': + return CL_UUCP; + case ':': + return CL_MBOX; + case '|': + return CL_PROG; + case '?': + return CL_USER; + } + } + + return CL_USER; + } + + /*---------------------------------------------------------------------- + * Parse a string representation of a destination. + * If dfile_ct is non-NULL, then a missing user name refers to that user; + * otherwise, a missing user name refers to the effective user id. + * If dfile_ct is NULL, then we only permit user names and UUCP addresses; + * otherwise, we permit all kinds of addresses. + */ + + DEST * + addr_dest(s, dfile_ct) + char *s; + CONTEXT *dfile_ct; + { + DEST *d; + CONTEXT *ct; + char *buf, *p, *q, *user, *param; + char sep; + int i, legal; + + /* + * If this is NOT a delivery file, restrict address class + * to user and UUCP. + */ + + if (!dfile_ct) + { + DCLASS class; + + class = addr_class(s); + if ((class != CL_USER && class != CL_UUCP) + || strcmp(s, DFILE_DROP) == 0) + { + d = dest(s, CL_USER, (char *)NULL); + dest_err(d, E_DFONLY); + return d; + } + } + + /* + * Find the additional information after the user name. + * Bang means UUCP, colon means mailbox, pipe means pipe, + * question means error. + */ + + for (i = 0; (sep = s[i]) != 0; ++i) + { + if (sep == '!' || sep == ':' || sep == '|' || sep == '?') + break; + } + + /* + * Deal with UUCP paths before the user-name parsing begins. + */ + + if (sep == '!') + return dest(s, CL_UUCP, (char *)NULL); + + /* + * Copy string to local buffer so we can mangle it. + * Chop it up into user name and parameter. + */ + + buf = copystr(s); + if (buf[i]) + buf[i++] = 0; + user = buf; + param = buf + i; + + /* + * If user name is blank, provide default. + */ + + if (*user == 0) + user = (dfile_ct ? dfile_ct : eff_ct)->ct_name; + + /* + * Determine if we can get there from here. + */ + + if ((ct = name_context(user)) == NULL) + legal = TRUE; /* we can report "no such user" later */ + else if (dfile_ct) + legal = ok_context(dfile_ct->ct_uid, dfile_ct->ct_uid, + dfile_ct->ct_gid, ct); + else + legal = ok_context(eff_uid, real_uid, real_gid, ct); + + /* + * Now take action based on address class. + */ + + /* Could be "user|program" ... */ + + if (sep == '|') + d = legal ? dest(user, CL_PROG, param) : NULL; + + /* Could be "user?error" ... */ + + else if (sep == '?') + { + if (legal) + { + d = dest(user, CL_USER, (char *)NULL); + dest_err(d, E_ERRMSG); + d->d_errmsg = copystr(param); + } + else + d = NULL; + } + + /* Must be "user" or "user:mailbox" */ + + else + { + /* + * Strip whitespace, eliminate duplicate slashes from + * mailbox filename. + */ + + p = q = param; + while (*p) + { + if (isspace(*p)) + ++p; + else if ((*q++ = *p++) == '/') + { + while (*p == '/') + ++p; + } + } + *q = 0; + + /* + * Now that blanks are gone, we decide mailbox or not. + * Delaying this test means that "user" equals "user: ". + */ + + if (*param) + d = legal ? dest(user, CL_MBOX, param) : NULL; + else + d = dest(user, CL_USER, (char *)NULL); + } + + /* + * We allocated the buffer; now we free it. + */ + + free(buf); + + /* Return parsed destination. */ + + return d; + } + Index: config.h *************** *** 1,3 **** ! /* $Header: config.h,v 2.7 90/02/06 11:56:37 chip Exp $ * * Deliver configuration. --- 1,3 ---- ! /* $Header: config.h,v 2.9 90/03/06 12:21:08 chip Exp $ * * Deliver configuration. *************** *** 4,7 **** --- 4,18 ---- * * $Log: config.h,v $ + * Revision 2.9 90/03/06 12:21:08 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.8 90/02/23 14:16:39 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.7 90/02/06 11:56:37 chip * Enforce MBX_MODE regardless of UMASK. *************** *** 51,55 **** */ ! #define TRUSTED_USERS "root", "uucp" /*---------------------------------------------------------------------- --- 62,66 ---- */ ! #define TRUSTED_USERS "root" /*---------------------------------------------------------------------- *************** *** 200,204 **** /*---------------------------------------------------------------------- ! * Name of shell used to execute delivery files. */ --- 211,218 ---- /*---------------------------------------------------------------------- ! * Default shell for executing delivery files and pipes. ! * (Now that Deliver recognizes the "#!" hack, this value is less ! * important than it used to be.) ! * Note that the default shell must support the "-c" option. */ *************** *** 286,289 **** --- 300,304 ---- * SYS_DELIVER system-wide delivery file * POST_DELIVER post-user delivery file + * ERR_DELIVER error delivery file * USER_DELIVER user delivery file (in user's home directory) */ *************** *** 291,294 **** --- 306,310 ---- #define SYS_DELIVER "/usr/local/lib/deliver.sys" #define POST_DELIVER "/usr/local/lib/deliver.post" + #define ERR_DELIVER "/usr/local/lib/deliver.err" #define USER_DELIVER ".deliver" *************** *** 312,317 **** --- 328,335 ---- #define ENV_DLEVEL "DELLEVEL" /* Level of recursion */ #define ENV_DFLAGS "DELFLAGS" /* Flags: [-[Avdt]] */ + #define ENV_SYSDEL "SYSDELFILE" /* System delivery file */ #define ENV_POSTDEL "POSTDELFILE" /* Post-user delivery file */ + #define ENV_ERRDEL "ERRDELFILE" /* Error delivery file */ #define ENV_USERDEL "USERDELFILE" /* User delivery file */ Index: context.c *************** *** 1,3 **** ! /* $Header: context.c,v 2.1 89/06/09 12:25:13 network Exp $ * * User context manager. --- 1,3 ---- ! /* $Header: context.c,v 2.2 90/02/23 16:35:52 chip Exp $ * * User context manager. *************** *** 6,9 **** --- 6,12 ---- * * $Log: context.c,v $ + * Revision 2.2 90/02/23 16:35:52 chip + * \Fix problems determining legality of user references. + * * Revision 2.1 89/06/09 12:25:13 network * Update RCS revisions. *************** *** 105,120 **** /*---------------------------------------------------------------------- ! * Report whether is is possible or not to enter the given context. */ int ! ok_context(ct) CONTEXT *ct; { ! if (! ct) return FALSE; ! if (eff_uid == 0 ! || ((real_uid == ct->ct_uid) && (real_gid == ct->ct_gid))) return TRUE; else --- 108,125 ---- /*---------------------------------------------------------------------- ! * Report whether is is possible or not to go from the given ! * effective uid/real uid/real gid to the given context. */ int ! ok_context(euid, ruid, rgid, ct) ! int euid, ruid, rgid; CONTEXT *ct; { ! if (!ct) return FALSE; ! if (euid == 0 ! || ((ruid == ct->ct_uid) && (rgid == ct->ct_gid))) return TRUE; else Index: debug.c *************** *** 1,3 **** ! /* $Header: debug.c,v 2.1 89/06/09 12:25:17 network Exp $ * * Debugging output. --- 1,3 ---- ! /* $Header: debug.c,v 2.2 90/02/23 14:16:42 chip Exp $ * * Debugging output. *************** *** 4,7 **** --- 4,13 ---- * * $Log: debug.c,v $ + * Revision 2.2 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.1 89/06/09 12:25:17 network * Update RCS revisions. *************** *** 21,29 **** char *when; { ! DEST *d; message("Destinations %s:\n", when); ! for (d = first_dest(); d; d = next_dest(d)) { message("\t%s", d->d_name); --- 27,56 ---- char *when; { ! DEST *d, **dv; ! int i, count; message("Destinations %s:\n", when); ! ! /* ! * Get and sort the array of destinations. ! */ ! ! if ((dv = dest_array(&count)) == NULL) ! { ! message("\tnone\n"); ! return; ! } ! ! /* ! * Now print them in the order we just created. ! */ ! ! for (i = 0; i < count; ++i) { + char *e; + int len; + + d = dv[i]; + message("\t%s", d->d_name); *************** *** 34,38 **** break; case CL_MBOX: ! message(", mailbox='%s'", d->d_mailbox); break; case CL_UUCP: --- 61,65 ---- break; case CL_MBOX: ! message(", mailbox='%s'", d->d_param); break; case CL_UUCP: *************** *** 39,44 **** message(" (UUCP)"); break; } ! message("; "); switch (d->d_state) { --- 66,80 ---- message(" (UUCP)"); break; + case CL_PROG: + message(", program='%s'", d->d_param); + break; } ! ! e = (d->d_state == ST_ERROR) ? derrmsg(d) : ""; ! len = strlen(d->d_name) + strlen(e); ! if (d->d_param) ! len += strlen(d->d_param); ! message(":%s", (len > 60) ? "\n\t\t" : " "); ! switch (d->d_state) { *************** *** 53,57 **** break; case ST_ERROR: ! message("Error (%s)", derrmsg(d->d_error)); break; } --- 89,93 ---- break; case ST_ERROR: ! message("Error (%s)", derrmsg(d)); break; } *************** *** 58,60 **** --- 94,100 ---- message("\n"); } + + /* It's our job to free the array. */ + + free((char *) dv); } Index: deliver.8 *************** *** 1,3 **** ! .\" $Header: deliver.8,v 2.4 89/11/10 14:09:46 network Exp $ .\" .\" Man page for deliver. --- 1,3 ---- ! .\" $Header: deliver.8,v 2.7 90/03/06 15:11:55 chip Exp $ .\" .\" Man page for deliver. *************** *** 4,7 **** --- 4,16 ---- .\" .\" $Log: deliver.8,v $ + .\" Revision 2.7 90/03/06 15:11:55 chip + .\" New location. + .\" + .\" Revision 2.6 90/03/06 13:20:21 chip + .\" Document the error delivery file. + .\" + .\" Revision 2.5 90/02/23 16:35:38 chip + .\" \Fix problems determining legality of user references. + .\" .\" Revision 2.4 89/11/10 14:09:46 network .\" Document cases in which the log files are not written. *************** *** 42,46 **** used to control .I deliver ! are Bourne shell scripts. Thus anything that can be called by a shell script can be used to control mail delivery. .SH OPTIONS --- 51,55 ---- used to control .I deliver ! are shell scripts. Thus anything that can be called by a shell script can be used to control mail delivery. .SH OPTIONS *************** *** 97,100 **** --- 106,114 ---- For security reasons, this option disables setuid privileges. .TP + .BI \-e " error delivery file" + Specify an alternate error delivery file. The default is + .I /usr/local/lib/deliver.err. + For security reasons, this option disables setuid privileges. + .TP .BI \-u " user delivery file" Specify an alternate user delivery file. The default is *************** *** 184,188 **** option was not specified, .I deliver ! executes each user delivery file with the name of the given user as its only argument. .PP --- 198,202 ---- option was not specified, .I deliver ! executes each user's delivery file with the name of the given user as its only argument. .PP *************** *** 196,199 **** --- 210,232 ---- .I deliver executes the post-user delivery file with these addresses as its arguments. + .PP + After executing the post-user delivery file (if any), + .I deliver + attempts delivery to all requested destinations. + .PP + After delivery to all destinations, + .I deliver + looks in its list of destinations for failures of any kind. If any + failed destinations are found, and if the + .B \-n + option was not specified, + .I deliver + executes the error delivery file with the complete list of failed + addresses as its arguments. If the error delivery file generates + any destinations, + .I deliver + attempts delivery to them. However, if such delivery fails, + .I deliver + will not execute the error delivery file more than once. .SH "DELIVERY FILES" Delivery files are shell scripts. They are executed by *************** *** 205,208 **** --- 238,252 ---- option.) .PP + The default shell used to execute delivery files is + configuration-dependant. Typically it is the Bourne shell (/bin/sh). + However, you can make + .I deliver + execute a given delivery file with a different shell by putting a + Berkeley-style "#!" line at the top of the delivery file. For + example, if the first line of a delivery file is "#!/bin/perl", then + .I deliver + will execute /bin/perl instead of /bin/sh, but only for that one + delivery file. + .PP On each system the postmaster may create a .I system delivery file *************** *** 220,224 **** delivery files, whereas the system delivery file cannot. .PP ! Finally, each user may create a .I user delivery file in his home directory. User delivery files are always executed with exactly --- 264,268 ---- delivery files, whereas the system delivery file cannot. .PP ! Each user may create a .I user delivery file in his home directory. User delivery files are always executed with exactly *************** *** 226,229 **** --- 270,284 ---- found. .PP + Finally, the postmaster may create an + .I error delivery file. + After + .I deliver + has attempted delivery to all requested destinations, and if delivery + to one or more of those destinations failed, + .I deliver + executes the error delivery file with the list of failed adresses as + its arguments. Note that failed addresses may contain whitespace, + shell metacharacters or other strangeness--be careful! + .PP Recursive execution of .I deliver *************** *** 280,283 **** --- 335,341 ---- The post-user delivery filename. .TP + .B ERRDELFILE + The error delivery filename. + .TP .B USERDELFILE The user delivery filename, relative to the home directory of each user. *************** *** 305,316 **** .PP .I deliver ! monitors the standard output of delivery files for lines of two forms: ! either "user" or "user:mailbox". Those users whose names appear in the ! output of a delivery file will receive the message. If a mailbox name ! appears after the user name, then that mailbox receives the message. If a ! mailbox name is not specified, the user's default mailbox is used. (The ! default mailbox for a user is configuration-dependent.) If a mailbox is not ! an absolute pathname, it is interpreted relative to the home directory of ! the named user. .SH "UNDELIVERED MAIL" When --- 363,396 ---- .PP .I deliver ! monitors the standard output of delivery files for lines in four formats: ! .TP ! .B "host1!host2!user" ! Send the message with UUCP via the given bang path. ! .TP ! .B "user" ! Append the message to the given user's default mailbox. The location ! of a user's default mailbox is configuration-dependant. ! .TP ! .B "user:mailbox" ! Append the message to the specified mailbox in the given user's ! context. If the mailbox name is not an absolute pathname, it is ! interpreted relative to the given user's home directory. For security ! reasons, only the superuser may request delivery to a specific mailbox ! in another user's context. ! .TP ! .B "user|command" ! Execute the specified command in the given user's context, and feed the ! message to its standard input. For security reasons, only the ! superuser may request command execution in another user's context. ! .TP ! .B "user?error message" ! Do not attempt delivery to the given user. Diagnostic messages, ! including the bounce notice (if any), will include the specified ! message. For security reasons, only delivery files executing as the ! superuser may report an error for another user. ! .PP ! Note that for the "user:mailbox", "user|command" and "user?error ! message" forms, the user name may be omitted (for example: "?Sorry"). ! If the user name is omitted, the current user context is assumed. .SH "UNDELIVERED MAIL" When *************** *** 320,326 **** produces no output, .I deliver ! saves the message in the "undelivered mail" mailbox named "Undel.mail" in ! the home directory of the delivery file's owner. (System and post-user ! delivery files are "owned" by root.) .PP Sometimes a delivery file writer really does want --- 400,406 ---- produces no output, .I deliver ! saves the message in the "undelivered mail" mailbox named "Undel.mail" ! in the home directory of the delivery file's owner. (System, ! post-user and error delivery files are "owned" by root.) .PP Sometimes a delivery file writer really does want *************** *** 336,339 **** --- 416,423 ---- However, if the delivery file outputs any names and/or addresses in addition to "DROP", then "DROP" has no effect. + .PP + Note, however, that the error delivery file is an exception to the + "DROP" rule. The error delivery file never receives valid addresses + as arguments, so producing no output is considered completely normal. .SH "SECURITY" If *************** *** 353,361 **** will ignore any delivery file that might be found there. .PP ! For security reasons, ! .I deliver ! rejects lines of the form "user:mailbox" when generated by a user delivery ! file unless they are output by the given user's delivery file. In other ! words, no user can request writing a mailbox as another user. .SH LOGGING .I Deliver --- 437,442 ---- will ignore any delivery file that might be found there. .PP ! For security reasons, no user can request writing a mailbox as another ! user. Otherwise, any user could modify other users' private files. .SH LOGGING .I Deliver *************** *** 432,435 **** --- 513,518 ---- /usr/local/lib/deliver.post post-user delivery file .br + /usr/local/lib/deliver.err error delivery file + .br ~user/.deliver user delivery file(s) .br *************** *** 441,446 **** .SH SUPPORT Enhancements, enhancement requests, trouble reports, etc., should be mailed ! to ; or for UUCP-only sites, ! . .SH "SEE ALSO" .IR mail (1), --- 524,528 ---- .SH SUPPORT Enhancements, enhancement requests, trouble reports, etc., should be mailed ! to or . .SH "SEE ALSO" .IR mail (1), exit 0 # Just in case...