[comp.sources.unix] v20i024: Deliver, flexible email delivery system, Part02/04

rsalz@uunet.uu.net (Rich Salz) (10/16/89)

Submitted-by: Chip Salzenberg <chip@ateng.com>
Posting-number: Volume 20, Issue 24
Archive-name: deliver2.0/part02

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of shell archive."
# Contents:  config.h context.h deliver.h dest.h patchlevel.h misc.h
#   context.c copymsg.c
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'config.h' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'config.h'\"
else
echo shar: Extracting \"'config.h'\" \(6654 characters\)
sed "s/^X//" >'config.h' <<'END_OF_FILE'
X/* $Header: config.h,v 2.2 89/06/09 13:07:38 network Exp $
X *
X * Deliver configuration.
X *
X * $Log:	config.h,v $
X * Revision 2.2  89/06/09  13:07:38  network
X * Adapt to BSD quirks.
X * 
X * Revision 2.1  89/06/09  12:25:11  network
X * Update RCS revisions.
X * 
X * Revision 1.11  89/06/09  12:23:38  network
X * Baseline for 2.0 release.
X * 
X */
X
X/*----------------------------------------------------------------------
X * SCO Xenix System V compilers define M_SYSV, which implies USG.
X */
X
X#ifdef M_SYSV
X#ifndef USG
X#define USG
X#endif
X#endif
X
X/*----------------------------------------------------------------------
X * Trusted users.
X * Deliver permits "trusted" users to specify delivery filenames
X * without renouncing setuid privileges.  Essentially, these users
X * are given the root password.  Beware!
X */
X
X#define TRUSTED_USERS   "root", "uucp"
X
X/*----------------------------------------------------------------------
X * Signal function type.
X * Signal catching routines have this return value.
X * (For System V Release 3.0 or later, use "void".)
X */
X
X#ifdef USG
X# define SIGTYPE void
X#else
X# define SIGTYPE int
X#endif
X
X/*----------------------------------------------------------------------
X * Signal function declaration.
X * Define this if your <signal.h> doesn't declare signal() correctly.
X */
X
X/* #define DECLARE_SIGNAL */
X
X/*----------------------------------------------------------------------
X * Signal flag type.
X * Variables of this type may be set by signal catching routines.
X */
X
X#ifdef __STDC__
X#define SIGFLAG sig_atomic_t
X#else
X#define SIGFLAG short   /* or "volatile short" for aggressive optimizers */
X#endif
X
X/*----------------------------------------------------------------------
X * Various kinds of mailbox locking.
X * You may define one or both of ML_DOTLOCK and ML_DOTMLK.
X * You may define no more than one of ML_FCNTL, ML_LOCKF and ML_LOCKING.
X *
X * File creation locking:
X *     ML_DOTLOCK   create <mailbox>.lock       (most systems except BSD4.3)
X *     ML_DOTMLK    create /tmp/<basename>.mlk  (Xenix)
X *
X * Kernel record locking:
X *     ML_FCNTL     lock with fcntl(F_SETLKW)   (SVID systems only)
X *     ML_LOCKF     lock with lockf(F_LOCK)     (SVID systems only)
X *     ML_LOCKING   lock with locking(LK_LOCK)  (Xenix systems only)
X */
X
X#ifdef M_XENIX
X#define ML_DOTMLK
X#define ML_LOCKING
X#else
X#define ML_DOTLOCK
X#endif
X
X/*----------------------------------------------------------------------
X * Maximum filename length.
X * Note that this is for _filenames_, not _pathnames_.
X * For AT&T file systems, the usual value is 14.
X * For Berzerkley file systems, use something big like 255.
X */
X
X#ifdef BSD
X#define MAX_NAMESIZE    255
X#else
X#define MAX_NAMESIZE    14
X#endif
X
X/*----------------------------------------------------------------------
X * How to get the host name.
X * Define one.
X *
X * HOSTFILE             file containing name    (Xenix)
X * UNAME                uname()                 (System V)
X * GETHOSTNAME          gethostname()           (BSD)
X * HOSTNAME             host name string        (V7)
X */
X
X#ifdef M_XENIX
X#define HOSTFILE   "/etc/systemid"
X#else
X#ifdef USG
X#define UNAME
X#else
X#ifdef BSD
X#define GETHOSTNAME
X#else
X#define HOSTNAME   "cleese"
X#endif
X#endif
X#endif
X
X/*----------------------------------------------------------------------
X * Is <varargs.h> or <stdarg.h> available?
X */
X
X#ifdef __STDC__
X#define HAS_STDARG
X#else
X#ifdef USG
X#define HAS_VARARGS
X#else
X#ifdef BSD
X#define HAS_VARARGS
X#endif
X#endif
X#endif
X
X/*----------------------------------------------------------------------
X * Are vprintf() and friends available?
X */
X
X#ifdef USG
X#define HAS_VPRINTF
X#endif
X
X/*----------------------------------------------------------------------
X * Is putenv() available?
X */
X
X#ifdef USG
X#define HAS_PUTENV
X#endif
X
X/*----------------------------------------------------------------------
X * Is getopt() available?
X */
X
X#ifdef USG
X#define HAS_GETOPT
X#endif
X
X/*----------------------------------------------------------------------
X * Is setvbuf() backwards?
X * Note: this is true for SCO Xenix Development System 2.2.
X */
X
X/* #define REVERSE_SETVBUF */
X
X/*----------------------------------------------------------------------
X * Name of shell used to execute delivery files.
X */
X
X#define SHELL   "/bin/sh"
X
X/*----------------------------------------------------------------------
X * Characters that may not appear in addresses.
X * (This string should include all metacharacters for your chosen shell.)
X */
X
X#define SANITIZE   "$*?=\\`'\"|^&;{}()<> \t\n"
X
X/*----------------------------------------------------------------------
X * Standard mailbox location.
X *
X * Define either MBX_NAME or MBOX_DIR.
X * If MBX_NAME is defined, then the default mailbox is a file with
X * that name in the user's home directory.
X * If MBX_DIR is defined, then the default mailbox is a file in that
X * directory with the same name as the user.
X *
X * Define MBX_GROUP if all mailboxes must be owned by a specific group.
X * (System V requires this feature.)  If MBX_GROUP is not defined,
X * mailboxes will have their groups set to the recipients' default group.
X *
X * Define MBX_MODE to the file access modes for new mailboxes.
X * (System V requires group write permissions, i.e. 0020.)
X */
X
X#if defined(USG) && !defined(M_XENIX)
X/* #define MBX_NAME   "mbox" */
X#define MBX_DIR     "/usr/mail"
X#define MBX_MODE    0660
X#define MBX_GROUP   "mail"
X#else
X/* #define MBX_NAME   "mbox" */
X#define MBX_DIR     "/usr/spool/mail"
X#define MBX_MODE    0600
X#endif
X
X/*----------------------------------------------------------------------
X * Names of delivery files.
X *
X * SYS_DELIVER          system-wide delivery file
X * POST_DELIVER         post-user delivery file
X * USER_DELIVER         user delivery file (in user's home directory)
X */
X
X#define SYS_DELIVER     "/usr/local/lib/deliver.sys"
X#define POST_DELIVER    "/usr/local/lib/deliver.post"
X#define USER_DELIVER    ".deliver"
X
X/*----------------------------------------------------------------------
X * Environment variables passed to child processes.
X */
X
X#define ENV_DFLAGS      "DELFLAGS"      /* Flags: [-[Avdt]]             */
X#define ENV_SYSDEL      "SYSDELFILE"    /* System delivery file         */
X#define ENV_POSTDEL     "POSTDELFILE"   /* Post-user delivery file      */
X#define ENV_USERDEL     "USERDELFILE"   /* User delivery file           */
X
X#define ENV_HOSTNAME    "HOSTNAME"      /* Name of this host            */
X#define ENV_SENDER      "SENDER"        /* Message sender               */
X#define ENV_HEADER      "HEADER"        /* Message header file          */
X#define ENV_BODY        "BODY"          /* Message body file            */
END_OF_FILE
if test 6654 -ne `wc -c <'config.h'`; then
    echo shar: \"'config.h'\" unpacked with wrong size!
fi
# end of 'config.h'
fi
if test -f 'context.h' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'context.h'\"
else
echo shar: Extracting \"'context.h'\" \(527 characters\)
sed "s/^X//" >'context.h' <<'END_OF_FILE'
X/* $Header: context.h,v 2.1 89/06/09 12:25:15 network Exp $
X *
X * User context, as found in /etc/passwd.
X *
X * $Log:	context.h,v $
X * Revision 2.1  89/06/09  12:25:15  network
X * Update RCS revisions.
X * 
X * Revision 1.3  89/06/09  12:23:40  network
X * Baseline for 2.0 release.
X * 
X */
X
X/*----------------------------------------------------------------------
X * The context structure.
X */
X
X#define CONTEXT struct context
XCONTEXT {
X	CONTEXT *ct_next;
X	int     ct_uid;
X	int     ct_gid;
X	char    *ct_name;
X	char    *ct_home;
X};
END_OF_FILE
if test 527 -ne `wc -c <'context.h'`; then
    echo shar: \"'context.h'\" unpacked with wrong size!
fi
# end of 'context.h'
fi
if test -f 'deliver.h' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'deliver.h'\"
else
echo shar: Extracting \"'deliver.h'\" \(3237 characters\)
sed "s/^X//" >'deliver.h' <<'END_OF_FILE'
X/* $Header: deliver.h,v 2.1 89/06/09 12:25:21 network Exp $
X *
X * General pull-it-together include file.
X *
X * $Log:	deliver.h,v $
X * Revision 2.1  89/06/09  12:25:21  network
X * Update RCS revisions.
X * 
X * Revision 1.10  89/06/09  12:23:44  network
X * Baseline for 2.0 release.
X * 
X */
X
X#include <stdio.h>
X#include <ctype.h>
X#include <sys/types.h>
X
X#include "config.h"
X#include "misc.h"
X#include "context.h"
X#include "dest.h"
X
X/*----------------------------------------------------------------------
X * Global data
X */
X
Xextern  int     verbose;        /* Print debugging messages?            */
Xextern  int     dryrun;         /* Are we making a dry run?             */
Xextern  int     rundfiles;      /* Run delivery files at all?           */
Xextern  int     printaddrs;     /* Address resolution only?             */
Xextern  int     leavetemps;     /* Leave temp files for later perusal   */
Xextern  int     boxdelivery;    /* Args are mailboxes, not addresses    */
Xextern  char    *sender;        /* Who is sending this message?         */
X
Xextern  char    *progname;      /* Name this program was invoked under  */
Xextern  char    *hostname;      /* Name of this host                    */
X
Xextern  char    *sys_deliver;   /* Systemwide delivery file             */
Xextern  char    *post_deliver;  /* Post-user delivery file              */
Xextern  char    *user_deliver;  /* User delivery file                   */
Xextern  char    *shell;         /* Shell used to run delivery files     */
X
Xextern  int     eff_uid;        /* Returned by geteuid()                */
Xextern  int     eff_gid;        /* Returned by getegid()                */
Xextern  int     real_uid;       /* Returned by getuid()                 */
Xextern  int     real_gid;       /* Returned by getgid()                 */
X
Xextern  CONTEXT *eff_ct;        /* Context of effective uid             */
Xextern  CONTEXT *real_ct;       /* Context of real uid                  */
X
Xextern  int     trust_user;     /* Do we trust the user that called us? */
Xextern  int     trust_delfiles; /* Do we trust the delivery files?      */
X
X/* Temp file indices: */
X#define T_HDR      0    /* Message header                       */
X#define T_BODY     1    /* Message body                         */
X#define T_HDRCOPY  2    /* Copy of message header               */
X#define T_BODYCOPY 3    /* Copy of message body                 */
X#define T_MAX      4    /* Number of temp files                 */
X
Xextern  char    *ttype[T_MAX];  /* Temp file types (for messages)       */
Xextern  char    *tfile[T_MAX];  /* Temp file names                      */
Xextern  char    *tenv[T_MAX];   /* Temp file environment names          */
Xextern  int     tfd[T_MAX];     /* Temp file fd's                       */
X
Xextern  SIGFLAG got_sig;        /* We caught a signal and should exit   */
X
X/*----------------------------------------------------------------------
X * Global functions
X */
X
Xchar    *basename();
Xchar    *gethost();
Xchar    *copystr();
Xchar    *derrmsg();
Xchar    *zalloc();
Xchar    *srealloc();
X
XCONTEXT *name_context();
XCONTEXT *uid_context();
X
XFILE    *ct_popenv();
Xint     ct_pclose();
X
XDEST    *dest();
XDEST    *first_dest();
XDEST    *next_dest();
X
Xtime_t  unctime();
END_OF_FILE
if test 3237 -ne `wc -c <'deliver.h'`; then
    echo shar: \"'deliver.h'\" unpacked with wrong size!
fi
# end of 'deliver.h'
fi
if test -f 'dest.h' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'dest.h'\"
else
echo shar: Extracting \"'dest.h'\" \(2503 characters\)
sed "s/^X//" >'dest.h' <<'END_OF_FILE'
X/* $Header: dest.h,v 2.1 89/06/09 12:25:23 network Exp $
X *
X * Description of a mail destination and its state.
X *
X * $Log:	dest.h,v $
X * Revision 2.1  89/06/09  12:25:23  network
X * Update RCS revisions.
X * 
X * Revision 1.3  89/06/09  12:23:48  network
X * Baseline for 2.0 release.
X * 
X */
X
X/*----------------------------------------------------------------------
X * Destination class.
X */
X
Xtypedef enum {
X	CL_USER,                /* User name, no mailbox                */
X	CL_MBOX,                /* User name, with mailbox name         */
X	CL_UUCP                 /* UUCP address (bang path)             */
X} DCLASS;
X
X/*----------------------------------------------------------------------
X * Destination state.
X */
X
Xtypedef enum {
X	ST_WORKING,             /* the "normal" state                   */
X	ST_HOLD,                /* on hold during expansion             */
X	ST_DONE,                /* all processing complete              */
X	ST_ERROR                /* "something is horribly wrong"        */
X} DSTATE;
X
X/*----------------------------------------------------------------------
X * Types of destination errors.
X */
X
Xtypedef enum {
X	E_IVADDR,               /* invalid address string               */
X	E_NSUSER,               /* no such user                         */
X	E_NSHOST,               /* no such host (UUCP addresses)        */
X	E_CTPERM,               /* no permissions for that context      */
X	E_CTLOST,               /* context lost (should never happen)   */
X	E_MBOX,                 /* can't write to mailbox               */
X	E_UUX                   /* can't pipe to uux                    */
X} DERROR;
X
X/*----------------------------------------------------------------------
X * Structure describing a mail destination.
X */
X
X#define DEST    struct dest
XDEST {
X	DEST    *d_next;        /* next destination in the chain        */
X	DEST    *d_prev;        /* previous destination in the chain    */
X	DCLASS  d_class;        /* destination class                    */
X	DSTATE  d_state;        /* destination state                    */
X	DERROR  d_error;        /* error message (if state is ERROR)    */
X	int     d_dfdone;       /* boolean -- delivery file was run     */
X	char    *d_name;        /* context for delivery                 */
X	char    *d_mailbox;     /* mailbox name or NULL for default     */
X};
X
X/*----------------------------------------------------------------------
X * Action macros.
X */
X
X#define dest_err(d,m)   ((d)->d_state = ST_ERROR, (d)->d_error = (m))
END_OF_FILE
if test 2503 -ne `wc -c <'dest.h'`; then
    echo shar: \"'dest.h'\" unpacked with wrong size!
fi
# end of 'dest.h'
fi
if test -f 'patchlevel.h' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'patchlevel.h'\"
else
echo shar: Extracting \"'patchlevel.h'\" \(21 characters\)
sed "s/^X//" >'patchlevel.h' <<'END_OF_FILE'
X#define PATCHLEVEL 0
END_OF_FILE
if test 21 -ne `wc -c <'patchlevel.h'`; then
    echo shar: \"'patchlevel.h'\" unpacked with wrong size!
fi
# end of 'patchlevel.h'
fi
if test -f 'misc.h' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'misc.h'\"
else
echo shar: Extracting \"'misc.h'\" \(2246 characters\)
sed "s/^X//" >'misc.h' <<'END_OF_FILE'
X/* $Header: misc.h,v 2.1 89/06/09 12:25:35 network Exp $
X *
X * Miscellaneous definitions.
X *
X * $Log:	misc.h,v $
X * Revision 2.1  89/06/09  12:25:35  network
X * Update RCS revisions.
X * 
X * Revision 1.7  89/06/09  12:23:56  network
X * Baseline for 2.0 release.
X * 
X */
X
X/*
X * Non-portable include files
X */
X
X#ifdef USG
X#include <fcntl.h>
X#include <string.h>
X#include <memory.h>
X#endif
X
X#ifdef BSD
X#include <strings.h>
X#include <sys/file.h>
X#endif
X
X/*
X * Constants
X */
X
X#ifdef NULL
X#undef NULL
X#endif
X#define NULL    0               /* The One True NULL */
X
X#define FALSE   0
X#define TRUE    1
X
X#ifndef O_RDONLY
X#define O_RDONLY   0
X#define O_WRONLY   1
X#define O_RDWR     2
X#endif
X
X/*
X * Macros.
X */
X
X/* Length parameter for fgets() on given buffer. */
X
X#define GETSIZE(buf)    (int) (sizeof(buf) - 1)
X
X/*
X * Public data.
X */
X
Xextern  char    **environ;
X
X/*
X * Common library functions.
X */
X
Xextern  char    *ctime();
Xextern  char    *getenv();
Xextern  char    *malloc();
Xextern  char    *realloc();
Xextern  char    *mktemp();
Xextern  int     putenv();
Xextern  long    lseek();
Xextern  long    time();
Xextern  void    free();
X
X#ifdef DECLARE_SIGNAL
Xextern  SIGTYPE (*signal())();
X#endif
X
X/*
X * String search functions.
X */
X
X#ifndef USG
X
X#ifndef BSD
Xextern  char    *index();
Xextern  char    *rindex();
X#endif /* not BSD */
X
X#define strchr          index
X#define strrchr         rindex
X
X#endif
X
X/*
X * Memory copy and zero.
X */
X
X#ifdef USG
X#define Copy(d,s,n)     (void) memcpy(d,s,n)
X#define Zero(d,n)       (void) memset(d,0,(int)(n))
X#else /* not USG */
X#ifdef BSD
X#define Copy(d,s,n)     bcopy(s,d,n)
X#define Zero(d,n)       bzero(d,n)
X#else /* not BSD */
X#define MEMFUNCS        /* define Copy() and Zero() in sysdep.c */
X#endif /* not BSD */
X#endif /* not USG */
X
X/*
X * Line-buffering on stdio files.
X */
X
X#ifdef USG
X
Xextern  int     setvbuf();
X
X#ifdef REVERSE_SETVBUF
X#define Linebuf(f)      (void) setvbuf(f, _IOLBF, (char *)NULL, BUFSIZ)
X#else
X#define Linebuf(f)      (void) setvbuf(f, (char *)NULL, _IOLBF, BUFSIZ)
X#endif
X
X#else /* not USG */
X#ifdef BSD
X
Xextern  int     setlinebuf();
X
X#define Linebuf(f)      (void) setlinebuf(f)
X
X#else /* not BSD */
X
X#define Linebuf(f)      /* can't do it */
X
X#endif /* not BSD */
X#endif /* not USG */
END_OF_FILE
if test 2246 -ne `wc -c <'misc.h'`; then
    echo shar: \"'misc.h'\" unpacked with wrong size!
fi
# end of 'misc.h'
fi
if test -f 'context.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'context.c'\"
else
echo shar: Extracting \"'context.c'\" \(2517 characters\)
sed "s/^X//" >'context.c' <<'END_OF_FILE'
X/* $Header: context.c,v 2.1 89/06/09 12:25:13 network Exp $
X *
X * User context manager.
X * This module exists for efficiency reasons; I could just call getpwnam()
X * every time I need context info.
X *
X * $Log:	context.c,v $
X * Revision 2.1  89/06/09  12:25:13  network
X * Update RCS revisions.
X * 
X * Revision 1.5  89/06/09  12:23:39  network
X * Baseline for 2.0 release.
X * 
X */
X
X#include "deliver.h"
X#include <pwd.h>
X#include <grp.h>
X
Xextern  struct passwd   *getpwnam();
Xextern  struct passwd   *getpwuid();
Xextern  struct group    *getgrnam();
Xextern  struct group    *getgrgid();
X
X/*
X * Local functions.
X */
X
Xstatic  CONTEXT *new_context();
X
X/*
X * Local data.
X */
X
Xstatic  CONTEXT *ctlist;	/* Chain of CONTEXT structures.		*/
X
X/*----------------------------------------------------------------------
X * Look up a context by user name.
X */
X
XCONTEXT *
Xname_context(name)
Xchar    *name;
X{
X	struct passwd *pw;
X	CONTEXT *ct;
X
X	for (ct = ctlist; ct; ct = ct->ct_next)
X	{
X		if (strcmp(ct->ct_name, name) == 0)
X			return ct;
X	}
X
X	if ((pw = getpwnam(name)) == NULL)
X		return NULL;
X
X	return new_context(pw);
X}
X
X/*----------------------------------------------------------------------
X * Look up a context by user ID.
X */
X
XCONTEXT *
Xuid_context(uid)
Xint     uid;
X{
X	struct passwd *pw;
X	CONTEXT *ct;
X
X	for (ct = ctlist; ct; ct = ct->ct_next)
X	{
X		if (ct->ct_uid == uid)
X			return ct;
X	}
X
X	if ((pw = getpwuid(uid)) == NULL)
X		return NULL;
X
X	return new_context(pw);
X}
X
X/*----------------------------------------------------------------------
X * Local function -- create a new context structure and return
X * its address.
X */
X
Xstatic CONTEXT *
Xnew_context(pw)
Xstruct passwd *pw;
X{
X	CONTEXT *ct;
X
X	ct = (CONTEXT *) zalloc(sizeof(CONTEXT));
X	ct->ct_uid = pw->pw_uid;
X	ct->ct_gid = pw->pw_gid;
X	ct->ct_name = copystr(pw->pw_name);
X	ct->ct_home = copystr(pw->pw_dir);
X
X	ct->ct_next = ctlist;
X	ctlist = ct;
X
X	return ct;
X}
X
X/*----------------------------------------------------------------------
X * Report whether is is possible or not to enter the given context.
X */
X
Xint
Xok_context(ct)
XCONTEXT *ct;
X{
X	if (! ct)
X		return FALSE;
X
X	if (eff_uid == 0
X	 || ((real_uid == ct->ct_uid) && (real_gid == ct->ct_gid)))
X		return TRUE;
X	else
X		return FALSE;
X}
X
X/*----------------------------------------------------------------------
X * Look up a group ID by name.
X */
X
X#ifdef MBX_GROUP
X
Xint
Xgroup_id(name)
Xchar    *name;
X{
X	struct group *grp;
X
X	if ((grp = getgrnam(name)) == NULL)
X		return -1;
X
X	return grp->gr_gid;
X}
X
X#endif /* MBX_GROUP */
END_OF_FILE
if test 2517 -ne `wc -c <'context.c'`; then
    echo shar: \"'context.c'\" unpacked with wrong size!
fi
# end of 'context.c'
fi
if test -f 'copymsg.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'copymsg.c'\"
else
echo shar: Extracting \"'copymsg.c'\" \(8775 characters\)
sed "s/^X//" >'copymsg.c' <<'END_OF_FILE'
X/* $Header: copymsg.c,v 2.1 89/06/09 12:25:16 network Exp $
X *
X * Take the message from standard input and write it to two temp files,
X * one for the header (including the empty line) and one for the body.
X *
X * $Log:	copymsg.c,v $
X * Revision 2.1  89/06/09  12:25:16  network
X * Update RCS revisions.
X * 
X * Revision 1.9  89/06/09  12:23:40  network
X * Baseline for 2.0 release.
X * 
X */
X
X#include "deliver.h"
X
X/*
X * Macros.
X */
X
X/* Does a string start with "From "? */
X
X#define ISFROM(p) ((p)[0] == 'F' && (p)[1] == 'r' && (p)[2] == 'o' \
X		&& (p)[3] == 'm' && (p)[4] == ' ')
X
X/*
X * Local functions.
X */
X
Xstatic  char    *tempfile();
Xstatic  int     tcreate();
X
X/*----------------------------------------------------------------------
X * Copy the message on the standard input to two temp files:
X * one for the header and one for the body.
X */
X
Xint
Xcopy_message()
X{
X	char    buf[BUFSIZ];
X	FILE    *dfp[T_MAX];
X	char    *p, *from_line, *fsender, *fdate, *fremote;
X	int     t, b, empty_line;
X	int     ret = 0;
X
X	/*
X	 * Create temporary files to hold the header and message body.
X	 */
X
X	for (t = T_HDR; t <= T_BODY; ++t)
X	{
X		int     fd;
X
X		tfile[t] = tempfile();
X		if ((tfd[t] = tcreate(tfile[t])) == -1)
X			return -1;
X
X		if ((fd = dup(tfd[t])) == -1)
X		{
X			syserr("dup %s fd", ttype[t]);
X			return -1;
X		}
X		(void) lseek(fd, 0L, 0);
X		if ((dfp[t] = fdopen(fd, "r+")) == NULL)
X		{
X			error("can't fdopen %s fd", ttype[t]);
X			return -1;
X		}
X	}
X
X	/* Debugging message for later examination of temp files. */
X
X	if (verbose)
X	{
X		message("header=%s, body=%s\n",
X			tfile[T_HDR], tfile[T_BODY]);
X	}
X
X	/*
X	 * If there is a From_ line, find the sender name therein.
X	 */
X
X	from_line = NULL;
X	fsender = fdate = fremote = NULL;
X
X	b = (fgets(buf, GETSIZE(buf), stdin) ? TRUE : FALSE);
X
X	if (b && ISFROM(buf) && (p = strchr(buf, '\n')) != NULL)
X	{
X		b = FALSE;
X
X		/* Make a mungable copy of the From_ line */
X
X		from_line = copystr(buf);
X		if ((p = strchr(from_line, '\n')) != NULL)
X			*p = 0;
X
X		/* Find sender */
X
X		p = from_line + 5;
X		while (*p && isspace(*p))
X			++p;
X		fsender = p;
X		while (*p && !isspace(*p))
X			++p;
X		if (*p)
X			*p++ = 0;
X
X		/* Date received should be around here somewhere */
X
X		fdate = p;
X
X		/* Find 'remote from' phrase (if any) */
X
X		for (; (p = strchr(p, 'r')) != NULL; ++p)
X		{
X			if (strncmp(p, "remote from", 11) == 0)
X			{
X				*p = 0;
X				p += 11;
X				while (*p && isspace(*p))
X					++p;
X				if (*p)
X					fremote = p;
X				break;
X			}
X		}
X
X		/*
X		 * Advance to first non-space in date.
X		 * If there is no date, clear the date pointer.
X		 */
X
X		while (*fdate && isspace(*fdate))
X			++fdate;
X		if (*fdate == 0)
X			fdate = NULL;
X
X		/*
X		 * If sender is missing, or if date is invalid,
X		 * we consider the entire From_ line invalid.
X		 */
X
X		if (*fsender == 0
X		 || (fdate != NULL && unctime(fdate) == -1) )
X		{
X			/* Ignore everything we found. */
X
X			fsender = fdate = fremote = NULL;
X
X			/* Print invalid From_ line in a harmless way. */
X
X			(void) strcpy(from_line, buf);
X			(void) strcpy(buf, "Invalid-UUCP-From: ");
X			(void) strcat(buf, from_line);
X			b = TRUE;
X		}
X	}
X
X	/*
X	 * Write a From_ line to the header file.
X	 */
X
X	/* if caller specified sender, use it */
X	if (sender)
X		; /* fine */
X
X	/* else if we found a From_ line, use it */
X	else if (fsender)
X	{
X		if (fremote)
X		{
X			sender = zalloc(strlen(fremote) + sizeof("!")
X					+ strlen(fsender));
X			(void) sprintf(sender, "%s!%s", fremote, fsender);
X		}
X		else
X			sender = copystr(fsender);
X	}
X
X	/* else use our real ID */
X	else
X		sender = real_ct->ct_name;
X
X	/* debugging message */
X
X	if (verbose)
X		message("copy_msg: sender is \"%s\"\n", sender);
X
X	/*
X	 * Finally!  Write the From_ line.
X	 */
X
X	(void) fputs("From ", dfp[T_HDR]);
X	(void) fputs(sender, dfp[T_HDR]);
X	(void) fputc(' ', dfp[T_HDR]);
X	if (fdate)
X	{
X		(void) fputs(fdate, dfp[T_HDR]);
X		(void) fputc('\n', dfp[T_HDR]);
X	}
X	else
X	{
X		time_t  now;
X
X		(void) time(&now);
X		(void) fputs(ctime(&now), dfp[T_HDR]);
X	}
X
X	/*
X	 * Free the From_ line if we allocated a copy of it.
X	 */
X
X	if (from_line)
X		free(from_line);
X
X	/*
X	 * Copy the rest of the header (if any).
X	 */
X
X	for (; !feof(stdin) && !ferror(stdin); b = FALSE)
X	{
X		if (!b)
X		{
X			if (fgets(buf, GETSIZE(buf), stdin))
X				b = TRUE;
X			else
X				break;
X		}
X
X		/* Empty line means "end of header" */
X
X		if (buf[0] == '\n')
X		{
X			b = FALSE;    /* Don't put this line in the body. */
X			break;
X		}
X
X		/*
X		 * A line too long to fit in buf[] can't be a header line.
X		 * At least, that's my opinion... :-)
X		 */
X
X		if (!strchr(buf, '\n'))
X			break;
X
X		/*
X		 * If line begins with whitespace, it's a continuation.
X		 * Else if line begins with From_ or '>', prepend '>'.
X		 * Else if line doesn't look like a header, this must
X		 * be the beginning of the body.
X		 */
X
X		if (isspace(buf[0]))
X			;               /* continuation */
X		else if (ISFROM(buf) || (buf[0] == '>'))
X			(void) fputc('>', dfp[T_HDR]);
X		else
X		{
X			/* look for the colon on a header label */
X
X			p = buf;
X			while (isalpha(*p) || *p == '-')
X				++p;
X			if ((p == buf) || (*p != ':'))
X				break;  /* Not a header line! */
X		}
X
X		/* Write the line to the header file. */
X
X		(void) fputs(buf, dfp[T_HDR]);
X	}
X
X	/*
X	 * End the header file with a blank line.
X	 * This enables us to simply concatenate it with the body file
X	 * to produce a valid message.
X	 */
X
X	(void) fputc('\n', dfp[T_HDR]);
X
X	/*
X	 * Copy the body (if any).
X	 */
X
X	empty_line = FALSE;
X	for (; !feof(stdin) && !ferror(stdin); b = FALSE)
X	{
X		if (!b)
X		{
X			if (fgets(buf, GETSIZE(buf), stdin))
X				b = TRUE;
X			else
X				break;
X		}
X
X		if (ISFROM(buf))
X			(void) fputc('>', dfp[T_BODY]);
X		(void) fputs(buf, dfp[T_BODY]);
X
X		empty_line = (buf[0] == '\n');
X
X		/*
X		 * Output the rest of a very long line.
X		 * We do this here, instead of going around the loop,
X		 * in order to avoid misinterpreting From_ strings
X		 * that may be found in long lines.
X		 */
X
X		while (!strchr(buf, '\n')
X		    && !feof(stdin)
X		    && !ferror(stdin)
X		    && fgets(buf, GETSIZE(buf), stdin))
X			(void) fputs(buf, dfp[T_BODY]);
X	}
X
X	/* Ensure that the body ends with a blank line. */
X
X	if (! empty_line)
X		(void) fputc('\n', dfp[T_BODY]);
X
X	/*
X	 * If we encountered any trouble writing to the temp files,
X	 * let's not keep it secret.
X	 */
X
X	for (t = T_HDR; t <= T_BODY; ++t)
X	{
X		if (ferror(dfp[t]))
X		{
X			error("error writing to %s file %s\n",
X				ttype[t], tfile[t]);
X			ret = -1;
X		}
X
X		(void) fclose(dfp[t]);
X	}
X
X	/* Return error/success. */
X
X	return ret;
X}
X
X/*----------------------------------------------------------------------
X * Create another copy of each temp file, for security reasons.
X * Also, put their names in the environment.
X */
X
Xint
Xcopy_again()
X{
X	int     r, t;
X
X	for (r = T_HDR, t = T_HDRCOPY; r <= T_BODY; ++r, ++t)
X	{
X		/*
X		 * If the file exists, remove it but keep its name.
X		 * Otherwise, make a new name and put that name in
X		 * the environment.
X		 */
X
X		if (tfile[t])
X			(void) unlink(tfile[t]);
X		else
X		{
X			tfile[t] = tempfile();
X			if (tenv[t])
X				alloc_env(tenv[t], tfile[t]);
X		}
X
X		/*
X		 * Create the file and copy the contents of the
X		 * original file to it.
X		 */
X
X		if (tfd[t] != -1)
X			(void) close(tfd[t]);
X
X		if ((tfd[t] = tcreate(tfile[t])) == -1)
X			return -1;
X
X		(void) lseek(tfd[r], 0L, 0);
X		if (copyfd(tfd[r], tfd[t]) < 0)
X			return -1;
X	}
X
X	if (verbose)
X	{
X		message("copy_again: header to %s, body to %s\n",
X			tfile[T_HDRCOPY], tfile[T_BODYCOPY]);
X	}
X
X	return 0;
X}
X
X/*----------------------------------------------------------------------
X * Copy a file via file descriptors.
X */
X
Xint
Xcopyfd(src_fd, dest_fd)
Xint     src_fd;
Xint     dest_fd;
X{
X	char    buf[BUFSIZ];
X	int     rd, wr;
X
X	while ((rd = read(src_fd, buf, sizeof(buf))) > 0)
X	{
X		if ((wr = write(dest_fd, buf, (unsigned) rd)) != rd)
X		{
X			if (wr == -1)
X				syserr("can't write in copyfd");
X			else
X				error("write error -- disk full?\n");
X			return -1;
X		}
X	}
X
X	return 0;
X}
X
X/*----------------------------------------------------------------------
X * Return a pointer to a temporary filename, or NULL if error.
X */
X
Xstatic char *
Xtempfile()
X{
X	static char template[] = "/tmp/dl.XXXXXX";
X	char    *f;
X
X	f = zalloc(32);
X	(void) strcpy(f, template);
X	if (mktemp(f) == NULL)
X	{
X		error("can't create temporary file");
X		return NULL;
X	}
X	return f;
X}
X
X/*----------------------------------------------------------------------
X * Create a file, or complain if it doesn't work.
X */
X
Xstatic int
Xtcreate(name)
Xchar    *name;
X{
X	int     fd;
X
X#ifdef O_CREAT
X	fd = open(name, O_RDWR|O_CREAT|O_EXCL, 0);
X#else
X	fd = creat(name, 0);
X#endif
X	if (fd == -1)
X	{
X		syserr("can't create %s", name);
X		return -1;
X	}
X
X#ifndef O_CREAT
X	(void) close(fd);
X	if ((fd = open(name, 2)) == -1)
X	{
X		syserr("can't re-open %s", name);
X		return -1;
X	}
X#endif
X
X	return fd;
X}
X
END_OF_FILE
if test 8775 -ne `wc -c <'copymsg.c'`; then
    echo shar: \"'copymsg.c'\" unpacked with wrong size!
fi
# end of 'copymsg.c'
fi
echo shar: End of shell archive.
exit 0


-- 
Please send comp.sources.unix-related mail to rsalz@uunet.uu.net.
Use a domain-based address or give alternate paths, or you may lose out.