[comp.mail.misc] Deliver 2.0 Patch #8

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."