4.3BSD/usr/contrib/mmdf.tar

mmdf/   775      0     12           0  3671117344   4676 mmdf/h/   755      0     12           0  3664425352   5126 mmdf/h/ap.h   444      0     12        6215  3655234577   5775 /*  environment for address parser */

#define AP_SAME   0000          /* do not transorm the address */
#define AP_733    0001          /* follow RFC #733 rules */
#define AP_822    0002          /* follow RFC #822 rules */
#define AP_NODOTS 0004          /* strip down to hostname on next hop */
#define AP_BIG    0010          /* Use Big-endian domains, FLAG */

struct ap_node
{
    char    ap_obtype,            /* parsing type of this object        */
#define APV_NIL  0
#define APV_NAME 1                /* personal name                      */
#define APV_MBOX 2                /* mailbox-part of address            */
#define APV_DOMN 3                /* host-part of address               */
#define APV_DTYP 4                /* "data-type" (e.g., :include:...,)  */
#define APV_CMNT 5                /* comment (...)                      */
#define APV_WORD 6                /* generic word                       */
#define APV_PRSN 7                /* start of personal addr list <...>  */
#define APV_NPER 8                /* name of person                     */
#define APV_EPER 9                /* end of personal address list       */
#define APV_GRUP 10               /* start of group address list x:..;  */
#define APV_NGRP 11               /* name of group                      */
#define APV_EGRP 12               /* end of group list                  */
#define APV_DLIT 13               /* domain literal                     */
	    ap_ptrtype;           /* next node is continuation of this  */
				  /*   address, start of new, or null   */
#define APP_NIL  0                /* there is no next node              */
#define APP_ETC  1                /* next is part of this address       */
#define APP_NXT  2                /* next is start of new address       */

    char   *ap_obvalue;           /* pointer to string value of object  */
    struct ap_node  *ap_chain;    /* pointer to next node               */
};

typedef struct ap_node *AP_ptr;

struct ap_prevstruct
{                                 /* for use when getting indirect input*/
    FILE *ap_curfp;               /* handle on current file input       */
    struct ap_prevstruct   *ap_prvptr;
				  /* next input down the stack, using...*/
    int     (*ap_prvgfunc) ();    /* getchar function for that input    */
    int     ap_opeek,             /* with this as peek-ahead char for it*/
	    ap_ogroup,            /* nesting level of group list        */
	    ap_opersn;            /* nesting level of personal list     */
};


extern  char	ap_llex;
extern  AP_ptr  ap_pstrt;
extern  AP_ptr  ap_pcur;
extern	int	ap_1adr();
extern	AP_ptr	ap_1delete();
extern	AP_ptr	ap_add();
extern	AP_ptr	ap_alloc();
extern	AP_ptr	ap_append();
extern	int	ap_char();
extern	char *	ap_dmflip();
extern	int	ap_dmnormalize();
extern	int	ap_flget();
extern	int	ap_fpush();
extern	int	ap_lex();
extern	AP_ptr	ap_move();
extern	AP_ptr	ap_new();
extern	AP_ptr	ap_normalize();
extern	char *	ap_p2s();
extern	AP_ptr	ap_pinit();
extern	int	ap_ppush();
extern	char *	ap_s2p();
extern	AP_ptr	ap_s2tree();
extern	AP_ptr	ap_sqdelete();
extern	AP_ptr	ap_sqinsert();
extern	AP_ptr	ap_sqmove();
extern	AP_ptr	ap_t2parts();
extern	AP_ptr	ap_t2s();
        /* host-part of address               */
#define APV_DTYP 4                /* "data-type" (e.g., :include:...,)  */
#define APV_CMNT 5                /* comment (...)                      */
#define APV_WORD 6                /* generic word                       */
#define APV_PRSN 7                /* start of personal addr list <...>  */
#define APV_NPER 8    mmdf/h/ap_lex.h   444      0     12        4327  3620510347   6627 /* lexical symbols for address parser */

#define LT_EOL          0	  /* New-Line                             */
#define LT_SPC          0	  /* Space and tab                        */
#define LT_ERR          1	  /* Illegal chars (control chars)        */
#define LT_EOD          2	  /* End of Data (null)                   */
#define LT_COM          3	  /* ,                                    */
#define LT_AT           4	  /* @                                    */
#define LT_COL          5	  /* :                                    */
#define LT_SEM          6	  /* ;                                    */
#define LT_LES          7	  /* <                                    */
#define LT_GTR          8	  /* >                                    */
#define LT_SQT          9	  /* \ (only in quoted strings)           */
#define LT_LTR         10	  /* alphabetics, numbers, and others     */
#define LT_XTR         10	  /* alphabetics, numbers, and others     */
#define LT_NUM         10	  /* alphabetics, numbers, and others     */
#define LT_LPR         11	  /* (                                    */
#define LT_RPR         12	  /* )                                    */
#define LT_QOT         13	  /* "                                    */
#define LT_LSQ         14         /* [                                    */
#define LT_RSQ         15         /* ]                                    */


#define LV_EOD          0	  /* End of Data                          */
#define LV_ERROR        1	  /* These Values go with the above Types */
#define LV_COMMA        2	  /* ,                                    */
#define LV_AT           3	  /* @                                    */
#define LV_COLON        4	  /* :                                    */
#define LV_SEMI         5	  /* ;                                    */
#define LV_COMMENT      6	  /* (text text text)                     */
#define LV_LESS         7	  /* <                                    */
#define LV_GRTR         8	  /* >                                    */
#define LV_WORD         9	  /* atom / string                        */
#define LV_FROM        10	  /* <<                                   */
#define LV_DLIT        11         /* [text text text]                     */
 ();    /* getchar function for that input    */
    int     ap_opeek,             /* with this as peek-ahead char for it*/
	    ap_ogroup,            /* nesting level of group list        */
	    ap_opersn;            /* nesting level of personal list     */
};


extern  char	ap_llex;
extern  APmmdf/h/ap_norm.h   444      0     12         524  3620510347   6765 /* ap_norm.h - table declaration for ap_normalize.c */

/* Structure of translation table */

struct ap_hstab
{
    char   *name;                 /* Host (NODE) Name to look for       */
    char   *at;                   /* use this a hostname                */
    char   *dot;                  /* append this to mbox portion        */
};
          3	  /* ,                                    */
#define LT_AT           4	  /* @                                    */
#define LT_COL          5	  /* :            mmdf/h/d_clogcodes.h   444      0     12         502  3620510347   7573 /*
 *     codes used for logging telephone calls through the auto-dialer
 */

# define  LOG_ABAN       'A'     /*  call was abandoned  */
# define  LOG_COMP       'C'     /*  call was completed normally  */
# define  LOG_START      'S'     /*  call starting  */
# define  LOG_OPEN       'O'     /*  open the call log  */

tion        */
};
          3	  /* ,                                    */
#define LT_AT           4	  /* @                                    */
#define LT_COL          5	  /* :            mmdf/h/d_proto.h   444      0     12       14475  3664425125   7061 
/*
 *     message type codes
 */

# define  DATA           '0'     /*  data packet  */
# define  DATAACK        '1'     /*  acknowledge DATA packet  */
# define  XPATH          '2'     /*  transmit path packet  */
# define  XPATHACK       '3'     /*  acknowledge XPATH packet  */
# define  RPATH          '4'     /*  receive path packet  */
# define  RPATHACK       '5'     /*  acknowledge RPATH packet  */
# define  ESCAPE         '6'     /*  escape packet  */
# define  ESCAPEACK      '7'     /*  acknowledge ESCAPE packet  */
# define  QUIT           '8'     /*  quit packet  */
# define  QUITACK        '9'     /*  acknowledge QUIT packet  */
# define  NBUFF		 'A'	 /*  buffering specification packet  */
# define  NBUFFACK	 'B'	 /*  acknowledge NBUFF pack  */




/*
 *     lengths of the fixed length packets
 */

# define  LHEADER         6     /*  length of the header on all packets  */
				/*  If changed, change TEXTOFF also  */
# define  LTERM		  2     /*  length of the terminator for all pack  */
# define  LACK            6     /*  length of the ACK packet  */
# define  LRESET         40     /*  length of the RESET packet  */
# define  LRESETACK       6     /*  length of the RESETACK packet  */
# define  LESCAPE         8     /*  length of the ESCAPE packet  */
# define  LNBUFF          8     /*  length of the NBUFF packet  */
# define  LQUIT           6     /*  length of the QUIT packet  */
# define  LQUITACK        6     /*  length of the QUITACK packet  */
# define  LPATH          40     /*  length of an XPATH or RPATH packet  */




/*
 *     protocol constants and packet header offsets
 */

# define  CHKOFF           0     /*  start of 4 character checksum  */
# define  TYPEOFF          4     /*  type character  */
# define  FLAGOFF          5     /*  flag offset  */
# define  TEXTOFF          6     /*  text offset  */
# define  MAXPOFF          6     /*  maximum packet length field offset  */
# define  ESCOFF           6     /*  escape character field offset  */
# define  NBOFF            6     /*  buffer limit field offset  */
# define  LEGALOFF        10     /*  legal input character set field  */






/*
 *     bits in the flag byte
 */

# define  MASTERBIT    010     /*  set of the packet is being sent by the  */
                               /*  host which originated the call          */
# define  EOFBIT        04     /*  set if the last packet in a stream  */








/*
 *     various timeouts
 */

# define  CONNWAIT        45     /*  number of seconds to wait for a direct  */
                                 /*  line open to succeed.                   */
# define  DIALWAIT       120     /*  number of seconds to wait for a dial    */
                                 /*  line open to succeed.                   */
# define  ACUWAIT        300     /*  number of seconds to wait for a child   */
				 /*  to succeed opening via the acu     */
# define  XMITWAIT        60     /*  number of seconds to wait for a packet  */
                                 /*  to be transmitted.  this is for the     */
                                 /*  alarm around the port write calls       */
# define  QACKWAIT        10     /*  number of seconds to wait for response  */
                                 /*  to a QUIT packet before retransmission  */
# define  EACKWAIT        10     /*  number of seconds to wait for response  */
                                 /*  to an ESCAPE packet before resending    */
# define  DACKWAIT        10     /*  number of seconds to wait for ACK  */
                                 /*  before retransmission              */
# define  ESCAPEWAIT      60     /*  number of seconds to wait for an  */
                                 /*  ESCAPE packet during protocol     */
                                 /*  startup                           */
# define  NBUFFWAIT       60     /*  number of seconds to wait for NBUFF */
# define  DATAWAIT       180     /*  amount of time to wait for a DATA  */
                                 /*  packet                             */
# define  XPATHWAIT       60     /*  number of seconds to wait for XPATH  */
# define  RPATHWAIT       60     /*  number of seconds to wait for RPATH  */
# define  XPAWAIT         10     /*  number of seconds to wait for XPATHACK  */
# define  RPAWAIT         10     /*  number of seconds to wait for RPATHACK  */
# define  NBACKWAIT       10     /*  number of seconds to wait for NBUFFACK */




/*
 *     manifest configuration constants
 */

# define  MAXPACKET      255     /*  maximum length of packet excluding  */
                                 /*  <cr>                                */
# define  MINPACKET        6     /*  minimum length of packet excluding  */
                                 /*  <cr>                                */
# define  MINPATHSIZ      40     /*  minimum packet size that a system must   */
                                 /*  be capable of receiving or transmitting  */
                                 /*  excluding <cr>                           */
# define  MAXSCRLINE     256     /*  maximum length of line in script file  */
# define  MAXFIELDS       10     /*  maximum fields on a script line */
# define  MATCHLEN       128     /*  maximum length of string to be matched */
				 /*  in the script file                     */
# define  NSENDTRY         5     /*  number of times to attempt message  */
                                 /*  transmission                        */

# define  NBUFFMAX         2     /*  d_nbuff upper limit  */



#ifdef SYS5

/* PORT*I is for c_iflag field in termio */
/* PORT*O is for c_oflag field in termio */
/* PORT*C is for c_cflag field in termio */
/* PORT*L is for c_lflag field in termio */

# define PORTSONI	(IXON|ICRNL|ISTRIP|IGNBRK|IGNPAR)
# define PORTSONO	(0)
# define PORTSONC	(CS7|PARENB|CREAD|HUPCL)
# define PORTSONL	(0)

			/* bits to turn on in script mode */
# define PORTPONI	(IXON|ICRNL|ISTRIP|IGNBRK|IGNPAR)
# define PORTPONO	(0)
# define PORTPONC	(CS7|PARENB|CREAD|HUPCL)
# define PORTPONL	(ICANON)

#else

# define  PORTSON       (EVENP|ODDP|RAW)
				 /*  bits to turn on in script mode    */
# define  PORTSOFF      (ALLDELAY|CRMOD|ECHO|LCASE)
				 /*  bits to turn off in script mode     */
# define  PORTPON       (EVENP|ODDP)
				 /*  bits to turn on in protocol mode    */
# define  PORTPOFF      (ALLDELAY|CRMOD|ECHO|RAW|LCASE)
				 /*  bits to turn off in protocol mode   */
#endif SYS5
 04     /*  set if the last packet in a stream  */








/*
 *     various timeouts
 */

# define  CONNWAIT        45     /*  number of seconds to wait for a direct  */
                        mmdf/h/d_returns.h   444      0     12        5666  3620510350   7365 # define  D_CONTIN	   2	 /*  continue with process  */
# define  D_OK             1     /*  everything is fine  */
# define  D_NO             0     /*  something is wrong  */
# define  D_FATAL         -1     /*  fatal error.  this is returned to  */
				 /*  the calling program.               */
# define  D_NONFATAL      -2     /*  non-fatal error.  this is only used  */
				 /*  internally and will not be returned  */
				 /*  to the calling program.              */
# define  D_INTRPT        -3     /*  interrupt received during system call  */
# define  D_EOF		  -4     /*  unexpected EOF on script file  */
# define  D_UNKNOWN	  -5     /*  unknown command word  */
# define  D_QUOTE         -6     /*  unmatched quotes  */
# define  D_NFIELDS       -7     /*  wrong number of fields for cmd  */
# define  D_RETRY	  -8     /*  retry a failed packet transmission  */




/*  error codes.  these values are loaded into 'd_errno' to indicate the  */
/*  reason for one of the above error returns                             */

# define  D_BUSY           1     /*  the number dialed is busy  */
# define  D_ABAN           2     /*  the call was abandoned or no answer  */
# define  D_BADDIG         3     /*  bad digit passed to dialer (internal)  */
# define  D_NOPWR          4     /*  the dialer has no power  */
# define  D_SYSERR         5     /*  undistinguished system error  */
# define  D_LOCKERR        6     /*  error opening or creating lock file  */
# define  D_FORKERR        7     /*  couldn't fork after several tries  */
# define  D_PORTOPEN       8     /*  error trying to open port  */
# define  D_NOPORT         9     /*  no port or line available  */
# define  D_TSOPEN        10     /*  error opening transcript file  */
# define  D_TSWRITE       11     /*  error writing on transcript file  */
# define  D_PORTRD        12     /*  error on port read  */
# define  D_PORTWRT       13     /*  error on port write  */
# define  D_PORTEOF       14     /*  eof on port  */
# define  D_RCVQUIT       15     /*  a QUIT packet has been received  */
# define  D_PRTSGTTY      16     /*  error doing stty or gtty on port  */
# define  D_SCRERR        17     /*  error in script file  */
# define  D_ACCERR        18     /*  error in access file  */
# define  D_NORESP        19     /*  no response to transmitted packet  */
# define  D_PATHERR       20     /*  error in path packet  */
# define  D_CALLLOG       21     /*  error opening call log file  */
# define  D_PACKERR       22     /*  error in packet format  */
# define  D_INTERR        23     /*  internal package error  */
# define  D_NONUMBS       24     /*  no numbers given to dialer  */
# define  D_NOESC         25     /*  no esacpe character could be found  */
# define  D_BADSPD        26     /*  bad speed designation  */
# define  D_LOGERR        27     /*  log error  */
# define  D_TIMEOUT       28     /*  alarm during port read or write call  */
# define  D_INITERR       29     /*  trouble initializing  */
 calling program.              */
# define  D_INTRPT        -3     /*  intmmdf/h/d_script.h   444      0     12        3754  3641232574   7177 #
/*  script file command codes.  These values have been knocked
 *  up to start at 10, as opposed to the previous 1, in order to
 *  guarentee that they don't conflict with any of the return
 *  values (D_...) which, for some reason, start at 2.
*/

# define  S_DIAL        10      /*  dial the remote system  */
# define  S_XMIT        12      /*  transmit a string  */
# define  S_RECV        13      /*  watch for a string  */
# define  S_GO          14      /*  start the protocol  */
# define  S_END         15      /*  drop the connection  */
# define  S_XILL        16      /*  set illegal transmit characters  */
# define  S_RILL        17      /*  set illegal receive characters  */
# define  S_XPCK        18      /*  set max transmit packet size  */
# define  S_RPCK        19      /*  set max receive packet size  */
# define  S_RETR        20      /*  set number of retran. retries  */
# define  S_TOAK        21      /*  set Time Out limit for AcK  */
# define  S_TODA        22      /*  set Time Out limit for Data  */
# define  S_SELST	23      /*  select begin  */
# define  S_SELEND      24      /*  select end  */
# define  S_ALT         25      /*  alternate  */
# define  S_REM		26	/*  a comment (remember)  */
# define  S_LOG		27      /*  log a message  */
# define  S_ABORT       28      /*  abort the script  */
# define  S_REPLAY      29      /*  Reuse old input  */
# define  S_MARK	30	/*  Mark the place to start a reuse  */
# define  S_PRTTY	31	/*  set tty status bits for protocol mode  */
# define  S_SCTTY	32	/*  set tty status bits for script mode  */
# define  S_DBLBUF	33	/*  status of double buffering  */
# define  S_USEFILE	34	/*  Read script commands from another file  */
# define  S_EOF		35      /*  not actually a command.  This is returned
				 *  by 'getcmd' to indicate an EOF on the
				 *  script src file.
				 */
# define  S_PROMPT	36	/*  check for a prompt from the host to which */
				/*  the master is speaking */
# define  S_BILL	37	/*  start call logging on given number */
/*  a QUIT packet hammdf/h/d_structs.h   444      0     12        5100  3641232574   7365 #
/*
 *     structure of file descriptors returned from 'pipe'
 */

struct  pipedesc
    {
    int  p_readfd;                  /*  read file descriptor  */
    int  p_writefd;                 /*  write file descriptor  */
};




/*
 *     this structure contains the names and relationships of the available
 *     dial out ports, their lock files, and the acu name.
 *
 *     Feb 84  Dan Long  added p_type for line selection
 */

struct  dialports
    {
    char  *p_port;                  /*  path name of port  */
    char  *p_lock;                  /*  path name of lock file  */
    char  *p_acu;                   /*  path name of acu device  */
    char  *p_acupref;               /*  initialization string for acu */
    char  *p_acupost;               /*  dialing termination string, for acu */
    int    p_speed;                 /*  port speed index  */
    char  *p_ltype;                 /*  line type (e.g. WATS) */
    char  *p_access;                /*  path name of access file  */
};




/*
 *     this structure identifies the available direct connect lines, their
 *     speeds, lock files, and access files.
 */

struct directlines
    {
    char  *l_name;                    /*  line name  */
    char  *l_tty;                     /*  local tty path name  */
    char  *l_lock;                    /*  lock file path name  */
    int  l_speed;                     /*  speed index  */
    char  *l_access;                  /*  path name of access list  */
};




/*
 *     structure passed to 'd_dial' which contains the phone number/port
 *     speed pairings for the possible connection paths.
 */

struct  telspeed
    {
    int  t_speed;                   /*  speed index  */
    char  t_number[40];             /*  phone number in canonical form  */
};




/*  structure of speed names/index pairs.  */

struct  speedtab
    {
    char  *s_speed;                 /*  pointer to speed name string  */
    int  s_index;                   /*  speed index  */
};


/*  A structure used in keeping track of packets as they are sent  */

struct pkbuff
    {
    char p_data[MAXPACKET + 2];	/*  the actual data to be sent	*/
    int p_length;		/*  # of chars in the data	*/
    int p_seqnum;		/*  the sequence number		*/
    int p_acktype;		/*  the ack type expected	*/
    int p_ntries;		/*  # of times it has been sent	*/
    int p_timeo;		/*  time out value for ACK	*/
    char p_pktype;		/*  the packet type		*/
};



/*  Used to keep track of the script files that are open  */
struct opfile
  {
  char o_fname[82];
  FILE *o_chan;
  unsigned o_line;
  int o_nfields;
  char *o_fields[MAXFIELDS];
};
oing stty or gtty on port  */
# define  D_SCRERR        17     /*  error in script file  */
# define  D_ACCERR        18     /*  error in access file  */
# define  D_NORESP        19     /*  no response to transmitted packet  */
# define  D_PATHERR       20     /*  error in path packet  */
# define  D_CALLLOG       21     /*  error opening call log file  */
# define  D_PACKERR       22     /*  error in packet format  */
# define  D_INTERR      mmdf/h/d_syscodes.h   444      0     12         361  3620510350   7462 
/*
 *     extensions to normal errno values
 */

# define  EDNPWR          80     /*  acu has no power  */
# define  EDNABAN         81     /*  acu abandon call and retry  */
# define  EDNDIG          82     /*  illegal digit in number  */
         /*  dialing termination string, for acu */
    int    p_speed;                 /*  port speed index  */
    char  *p_ltype;                 /*  line type (e.g. WATS) */
    char  *p_access;                /*  path name of access file  */
};




/*
 *     this stmmdf/h/util.h   444      0     12        3432  3652602610   6330 #ifndef DIDUTIL
#define DIDUTIL
#include <stdio.h>                /* minus the ctype stuff */
#include <ctype.h>
#include <setjmp.h>
#include <sys/types.h>
#include <errno.h>

/* declarations that should have been in the system files */

extern char *strcat ();
extern char *strcpy ();
extern char *sprintf ();

/* */

extern jmp_buf timerest;

/* some common logical values */

#ifndef TRUE
#define TRUE    1
#endif
#ifndef FALSE
#define FALSE   0
#endif
#ifndef YES
#define YES     1
#endif
#ifndef NO
#define NO      0
#endif
#ifndef OK
#define OK      0
#endif
#ifndef DONE
#define DONE    1
#endif
#ifndef NOTOK
#define NOTOK   -1
#endif
#ifndef	MAYBE
#define	MAYBE	-2
#endif

/* some C compilers do not support 'global' statics properly */

#ifndef LOCFUN
#define LOCFUN static             /* function local to module           */
#endif
#ifndef LOCVAR
#define LOCVAR static             /* variable local to module           */
#endif

/* stdio extensions */

#define lowtoup(chr) (islower(chr)?toupper(chr):chr)
#define uptolow(chr) (isupper(chr)?tolower(chr):chr)
#define min(a,b) ((b<a)?b:a)
#define isstr(ptr) ((ptr) != 0 && (ptr)[0] != '\0')
#define isnull(chr) ((chr) == '\0')

#define FOREVER   for (;;)

union pipunion
{
    int     pipcall[2];
    struct pipstruct
    {
	int     prd;
	int     pwrt;
    } pip;
};

typedef union pipunion Pip;

#ifdef v6
long	siz2lon();
#define st_gsize(ino_ptr) ((long)(siz2lon((struct stat *)ino_ptr)))
#else
#define st_gsize(inode) ((long)((struct stat *)inode)->st_size)
#endif

#define gwaitval(val)   ((val) >> 8)
				  /* get exit() value from child        */
				  /* val is the argument to wait()      */
				  /* this macro expects to extract the  */
				  /* high byte from the "returned"      */
				  /* value                              */
#endif DIDUTIL
been sent	*/
    int p_timeo;		/*  time out value for ACK	*/
    char p_pktype;		/*  the packet type		*/
};



/*  Used to keep track of the script files that are open  */
struct opfile
  {
  char o_fname[82];
  FILE *o_chan;
  unmmdf/h/dm.h   444      0     12        5677  3622776543   6006 /*  Domain-Table definitions
 *
 *  A Domain table contains entries that reference specific host names
 *  (i.e., symbolic/routing addresses).  A table may either be used
 *  strictly as a domain table, in which case the format is:
 *
 *      domain-name  host-name [ domain-name, ... ]
 *
 *  so that 'a.b.c' will look up 'a' in 'b.c' and find it on the
 *  left-hand side.  The right-hand part of the entry is then used
 *  as a host name and is looked up in the routing tables.  If the
 *  right-hand part has more than one field, then the first part is
 *  taken as the 'next' host to send to and the remaining parts are
 *  taken as routing references, by domain name.  The order is
 *  sequentially forward, so that the second field references the
 *  host that is to receive the message, after the one we hand it to.
 *  (This is the same as the way that SMTP orders its routing info.)
 *
 *  Alternately, a table may be a standard host table, in which case, its
 *  format is:
 *
 *      host-name address-value
 *
 *  such as 'udel-relay 10.0.0.96'.  That is, the table was originally
 *  built as a name-to-net-address table, but is doubling as a domain
 *  table.  In this case, the 'final' sub-domain name is the same as the
 *  host name, such as 'udel-relay.arpa' mapping to 'udel-relay'.
 *
 *  A table fully specifies its location within the domain hierarchy, so
 *  that a site may have a 'udel.arpa' table, containing the name of
 *  sites at udel, and also have an 'arpa' table, containing the names
 *  of other domains/sites within the arpa domain.  In the list of
 *  domain tables, 'udel.arpa' comes first, so that it intercepts
 *  references to the 'udel' sub-domain.
 *
 *  Note that this domain usage involves 'flat' tables, in that you
 *  only do one domain lookup, at this site, to acquire the symbolic
 *  host address.  This lacks some generality, but should provide some
 *  efficiency.  The design is predicated on the expectation that a
 *  site will not have many domain tables (which are subordinate to
 *  other tables at that site).
 */

struct dm_struct
{
    char    *dm_name;           /* internal name of domain              */
    char    *dm_domain;         /* full name of containing domain       */
    char    *dm_lname;          /* name of this host within the domain  */
    char    *dm_show;           /* pretty-print version of name         */
    Table   *dm_table;          /* table containing entries for domain   */
};

typedef struct dm_struct    Domain;
typedef struct dm_struct    Dmn;
/*HACK*/
#define dm_tell(thetable) (((Table *)thetable) -> dm_tpos)

Domain *dm_nm2struct ();

#define DM_NFIELD  16           /* maximum number of routing fields     */

struct dm_rtstruct
{
    int     dm_argc;            /* number of domain value fields        */
    char   *dm_argv[DM_NFIELD]; /* pointers to each field               */
    char    dm_buf[LINESIZE];   /* where to stash domain value string   */
};

typedef struct dm_rtstruct    Dmn_route;
also have an 'arpa' table, containing the names
 *  of other domammdf/h/cmd.h   444      0     12         403  3620510351   6064 /*      structure for holding command table info        */

struct cmdstruct
{
    char *cmdname;      /* string reference */
    int  cmdtoken;      /* internal token  */
    int  cmdnargs;      /* minimum argument count */
};

typedef struct cmdstruct Cmd;
at this site, to acquire the symbolic
 *  host address.  This lacks some generality, but should provide some
 *  efficiency.  The design is predicated on the expectation that a
 *  site will not have many domain tables (which are subordinate to
 *  othemmdf/h/cnvtdate.h   444      0     12         544  3620510351   7137 #
/*  parameters for cnvdate () */

#undef	TIMJUL  1       /* unique id string */
#undef	TIMID   1
#define	TIMCOM	1	/* compact date (yymmddhhmm) */
#define TIMSECS 2       /* full time, down to the second */
#define TIMREG  3       /* rfc 733 standard format */
#define TIMSHRT 4       /* rfc 733 format, without day of week */


extern char *cnvtdate ();
rovide some
 *  efficiency.  The design is predicated on the expectation that a
 *  site will not have many domain tables (which are subordinate to
 *  othemmdf/h/ch.h   444      0     12       24335  3620510352   5766 #include <stdio.h>

/*
 *      INFORMATION KEPT FOR ALL TABLES
 */
struct tb_struct
{
    char    *tb_name;           /* internal name of table               */
    char    *tb_show;           /* displayable human-oriented string    */
    char    *tb_file;           /* name of file containing table        */
    FILE    *tb_fp;             /* stdio file pointer                   */
    long     tb_pos;            /* position in file                     */
    int	     tb_flags;		/* various bits	(type of table, etc)	*/
};
#define	TB_SRC	000007			/* Source of table data */
#define	TB_FILE		000000		/* Read from file */
#define	TB_DBM		000000		/* Read from DBM database */
#define	TB_NS		000001		/* Read from Nameserver */

#define TB_TYPE 000070			/* what type of question to ask NS */
#define TB_DOMAIN	000010		/* ask the nameserver for a domain */
#define TB_CHANNEL	000020		/* ask the nameserver for a channel */
#define TB_ROOT		000040		/*ask the nameserver for a root domain*/
#define TB_PARTIAL	000100	/* don't ask the nameserver in certain cases */

typedef struct tb_struct    Table;

Table *tb_nm2struct ();

/*****************************************************************/
/*
 *      STRUCTURE USED FOR CACHING  (Version 1, this may change)
 */

struct cache
{
	char    *ca_hostid;     /* host identification */
	int     ca_value;       /* its bad connection value */
	time_t  ca_expire;      /* time at which to expire entry */
	struct  cache *ca_next; /* the next dead host... or NULL */
};

typedef struct cache    Cache;

/*****************************************************************/
/*
 *      STRUCTURE USED FOR ALIAS SOURCES
 */

struct al_source
{
	int	al_flags;
	Table	*al_table;
	struct al_source *al_next;
};
/* For use with al_flags */
#define	AL_TRUSTED	00001	/* this alias file is trustworthy */
#define AL_NOBYPASS	00002	/* this alias file cannot be bypassed */

typedef struct al_source	Alias;

/******************************************************************
 *
 *      STRUCTURES WHICH DESCRIBE CHANNELS
 *
 *  Ch_struct (typedef Chan) contains all of the parametric information
 *  about a channel.  There may be several telephone and/or pobox channels,
 *  but each type is exec'd from a single executable file, so that the
 *  structure provides distinguishing information.  For telephone channels,
 *  Ch_struct has a pointer to an extension structure, called ch_phstruct.
 *  The member is null for other channels.
 */

/*      TELEPHONE-SPECIFIC PARAMETERS
 *
 *  The existing "telephone" channel also is used for low-speed hardwired
 *  machine-machine exchange, through tty ports.
 *
 *  If a phone channel is inactive, by virtue of not having any mail going
 *  from the local machine to the "other side", then we may need to poll
 *  them, periodically, to see if they have any mail to be brought over.
 *  Currently, you are limited to specifying a delay time; ch_poltime
 *  indicates how many 15-minute units of time to wait, since the last
 *  completed exchange, before polling.  NOTE:  you cannot currently
 *  specify time-of-day / day-of-week windows.
 *
 *  In order to keep track of the channel's activity, the files _dstrt,
 *  _ddone, and _pdone are used to note when outbound delivery was last
 *  started, when it was last completed and when the last pickup (inbound
 *  retrieval) was last completed.  The files are zero-length and only
 *  their modification time is of interest.
 *
 *  Ch_access indicates that mail may be retrieved, as well as sent.
 *
 *  ch_script specifies the script file to be followed for creating a
 *  connection to the slave on the other machine.  it usually indicates
 *  what number to call or line to connect to, login sequence, and program
 *  to start running.  it's use is more fully described in the dial
 *  package's documentation.
 */
/**/

/*      DESCRIPTION OF OUTPUT CHANNELS
 *
 *  For active channels, an attempt is made to send queued mail.  For
 *  passive channels, the mail is held until called for.  Some active
 *  channels may be invoked by anyone, others are restricted to the Deliver
 *  daemon (runnable an background).
 *
 *  A channel may have two "names".  One (ch_show) is intended for display
 *  and may contain any characters that will enhance its presentation.
 *  The other (ch_name) is intended for user input and more limited
 *  display.  It should have no special characters (e.g., dash, space) and
 *  should be entirely in lower case.  It must be unique.
 *
 *  A channel services a subset of the entire queue.  It access mail
 *  stored under the sub-queue, specified by ch_queue.  More than one
 *  channel may access a sub-queue, such as having an arpanet channel
 *  and a backup phone channel.
 *
 *  Each channel has an associated table, with the names of hosts on that
 *  channel.  The module, ch_table, handles access to the table and defines
 *  its format.  The local host is usually named in the table.  In order to
 *  capture references to the local machine, we need to know its "official"
 *  name in the table.  ch_lname permits this.
 *
 *  ch_ldomain specifies the RFC822 domain name to use, for this host,
 *  for the channel.
 *
 *  It also may allow some other special handling.  Usually, ch_lname is
 *  the same as the global, locname[], but this is not required.  For
 *  example, if there are several machines providing service to a single
 *  organization, you may want ch_lname to be an internal, local-network
 *  name for the local machine, while locname[] would be the public name
 *  for the organization.
 *
 *  Channels often involve contact with a single, other host, which acts as
 *  a relay for the other machines on a network.  If that is the case, then
 *  the "official" name of that host should be specified (ch_host).  This is
 *  particularly necessary when mail is picked up from (or submitted by)
 *  the relay, so that the Via: field can cite it.
 *
 *  When the channel is passive and a single site acts as a relay for the
 *  entire channel's mail, then there will be a single Unix login
 *  authorized to perform the pickup.  That must be indicated with
 *  ch_login.
 *
 *  If the channel is a telephone-like channel, then the above-described
 *  ch_wait, ch_access, ch_login, must be filled in.
 */
/**/


struct ch_struct
{
    char   *ch_name;              /* specification name of channel      */
    char   *ch_show;              /* pretty-print name for display      */
    Table  *ch_table;             /* pointer to host name table         */
    char   *ch_queue;             /* 'channel' name to enter in queue   */
    int     ch_access;            /* assorted switches                  */
#define DLVRREG    00             /*   Active delivery, by any Deliver  */
#define DLVRBAK    01             /*   Only runnable by daemon          */
#define DLVRPSV    02             /*   Passive wait for pickup          */
#define DLVRDID    04             /* (internal flag) chan was touched   */
				  /*   a flag set by mmdf, sometimes    */
#define DLVRIMM   010             /* may be run immediately (fast/cheap)*/
#define CH_PICK   020             /*   ok to pickup mail                */
#define CH_SEND   040             /*   ok to send mail                  */
#define MQ_INQ   0100             /* queued under this channel          */
    char   *ch_ppath;             /* path to file containing chan pgm   */
    char   *ch_lname;             /* Official host name of local machine */
#define DFLNAME     0             /* use locname as our name on chan    */
#define NOLNAME    -1             /* we are not know on the channel (??)*/
    char   *ch_ldomain;           /* Official domain name of this host  */
    char   *ch_host;              /* name of host doing relay for chan  */
#define NORELAY     0             /* direct receipt, rather than relay  */
    char   *ch_login;             /* valid login name for pickup relay  */
				  /*   ch_access must have PSV bit on   */
				  /*   some PSV chans are direct recip- */
				  /*   ients and not relays             */
#define NOLOGIN     0             /* not a pickup channel               */
    char    ch_poltime;           /* # of 15-minute periods to wait     */
#define ALLPOLL -1                /*  -1 => poll on every wakeup        */
#define NOPOLL   0                /*   0  => no polling allowed         */
    char   *ch_trans;             /* filename of the phone transcript   */
				  /* to use relative to logdfldir       */
#define DEFTRANS 0                /*    0 => use the default trn file   */
    char   *ch_script;            /* pathname to dialing script         */
#define NOSCRIPT 0
    Table  *ch_outsource;         /* list of sources authorized to      */
				  /*   access this channel              */
    Table  *ch_insource;          /* list of sources authorized to      */
				  /*   access this channel              */
#define NOFILTER 0
    Table  *ch_outdest;           /* authorized destinations            */
    Table  *ch_indest;            /* authorized destinations            */
    Table  *ch_known;             /* table of hosts known to hosts on   *
				   * this channel, normally == ch_table */

    char     *ch_confstr;        /* general purpose string              */
    int      ch_apout;            /* default 733 / 822 /same format     */
				  /* for header munging                 */
    char     ch_auth;             /* For authorisation by user control  */
#define CH_FREE        0
#define CH_IN_LOG     01
#define CH_IN_WARN    02
#define CH_IN_BLOCK   04
#define CH_OUT_LOG   010
#define CH_OUT_WARN  020
#define CH_OUT_BLOCK 040
#define CH_HAU      0100        /* authorise on basis of host AND user */
#define CH_DHO      0200

#define CH_IN_AUTH    07
#define CH_OUT_AUTH  070
    Cache   *ch_dead;           /* Current dead hosts */
    time_t  ch_ttl;             /* Number of seconds to keep dead host records */
    char    *ch_logfile;        /* Logfile for this channel */
    int     ch_loglevel;        /* Logging level for this channel */
};


typedef struct ch_struct    Chan; /* make it a "type"                   */

Chan * ch_nm2struct (),           /* chan name -> ptr to its table entry */
     * ch_qu2struct (),           /* queue name -> ptr to its table entry */
     * ch_h2chan ();              /* use host name to find chan name     */

Chan   *ch_h2chan ();          /* which chan name table is host in?  */
ation name of channel      */
    char   *ch_show;              /* pretty-print name for display      */
    Table  *ch_table;             /* pointer to host name table         */
    char   *ch_queue;             /* 'channel' name to enter in queue   */
    int     ch_access;            /*mmdf/h/ll_log.h   444      0     12        3053  3620510352   6616 /*      Definitions for the llog status-recording package                 */

#define LLOGCLS 01		  /* Keep log closed, except when writing */
#define LLOGCYC 02		  /* Cycle to beginning when log full     */
#define LLOGWAT 04		  /* Wait, if log locked and LLOGCLS      */
#define LLOGSOME 07		  /* Occasional logging                   */

#define LLOGFAT  -1		  /* Fatal error                          */
#define LLOGTMP   5		  /* Temporary (minor?) error             */
#define LLOGGEN  10		  /* General information                  */
#define LLOGBST  20		  /* Basic statistics                     */
#define LLOGFST  25		  /* Full statistics                      */
#define LLOGPTR  40               /* Trace of overall program phases      */
#define LLOGBTR  50		  /* Trace of basic program acitivity     */
#define LLOGFTR  55		  /* Full program trace                   */
#define LLOGMAX 126		  /* Maximum possible value               */

struct  ll_struct
{
    char   *ll_file;               /* path name to logging file            */
    char   *ll_hdr;                /* text to put in opening line          */
    int     ll_level;              /* don't print entries higher than this */
    int     ll_msize;              /* max size for log, in 100's of blocks */
    int     ll_stat;               /* assorted switches                    */
    int     ll_fd;                 /* holds file descriptor                */
    FILE   *ll_fp;                 /* Standard IO stream pointer           */
};

typedef struct ll_struct Llog;

typedef struct ll_struct LLog;
20		  /* Basic statistics                     */
#define LLOGFST  25		  /* Full statistics                      */
#define LLOGPTR  40               /* Trace of overall program phases      */
#define LLOGBTR  50		  /* Trace of basic program acitivity     */
#define LLOGFTR  55		  /* Full program trace                   */
#define LLOGMAX 126		  /* Maximum possible value               */

struct  ll_struct
{
    char   *ll_file;               /* path name to loggingmmdf/h/nexec.h   444      0     12        1406  3620510352   6450 
/*  Parameters for newpgml() fork/exec subroutine                         */

/* parameter for fdarray */
#define CLOSEFD -1                /* if equals fdarray[i], close (i);     */


/* parameter for proctyp */
#define PUREXEC 0		  /* simply exec over current process     */
#define FRKEXEC 1		  /* run it in lower process              */
#define SPNEXEC 2		  /* separate new process from old        */

/* parameter for pgmflags */
#define FRKWAIT 1		  /* wait for FRKEXEC to complete         */
#define FRKPIGO 2		  /* Parent's signals off during FRKWAIT  */
#define FRKCIGO 4		  /* Childs signals off before execl      */
#define FRKERRR 8                 /* Give error return, if child exec     */
				  /*  fails; else exit()                  */
#define NUMTRY  30

race of basic program acitivity     */
#define LLOGFTR  55		  /* Full program trace                   */
#define LLOGMAX 126		  /* Maximum possible value               */

struct  ll_struct
{
    char   *ll_file;               /* path name to loggingmmdf/h/adr_queue.h   444      0     12        3167  3620510352   7326 #
/*  Address info stored in queue files                                  */

/*  Every message has an "address list" queued with it.  This file      */
/*  contains the definitions of standard values and formats used in     */
/*  that file.  The primary info is the structure of a single address   */
/*  entry.                                                              */

struct adr_struct                 /* 1 address entry in queued list     */
{
    char    adr_tmp;              /* temp ok on this address            */
#define ADR_AOK   '+'             /*   only address sent, so far        */
#define ADR_CLR   '-'             /*   address is clear to send           */
    char    adr_delv;             /* send to mailbox and/or tty         */
#define ADR_MAIL   'm'            /*   Deliver as mail                  */
#define ADR_TTY    't'            /*   Deliver to tty                   */
#define ADR_BOTH   'b'            /*   Deliver both ways                */
#define ADR_TorM   'e'            /*   either tty or else mail          */
#define ADR_DONE   '*'            /*   No more processing to be done    */
    char   *adr_que;              /* name of output channel queue       */
    char   *adr_host;             /* host string                        */
    char   *adr_local;            /* local-part of address              */
    char   adr_buf[LINESIZE];     /* where to stash the unparsed text   */
    long   adr_pos;		  /* position in address list of this msg */
};

#define ADR_DLOFF   2             /* file offset to adr_delv             */
#define ADR_TMOFF   0             /* file offset to adr_tmp              */
         /*   address is clear to send           */
    char    adr_delv;             /* send to mailbox and/or tty         */
#define ADR_MAIL   'm'            /*   Deliver as mail                  */
#define ADR_TTY    't'            /*   Deliver to tty                   */
#define ADR_BOTH   'b'            /*   Deliver both ways                */
#define ADR_TorM   'e'            /*   eimmdf/h/amp.h   444      0     12         634  3620510353   6106 /* amp.h - global defs (not many) */

/* Structure of translation table */

struct hstab
{
    char   *name;                 /* Host (NODE) Name to look for       */
    char   *at;                   /* use this a hostname                */
    char   *dot;                  /* append this to mbox portion        */
};

/*  addresses of the form "<mbox> @ name" become "<mbox>.dot @ at"      */

long amp_hdr();
 long   adr_pos;		  /* position in address list of this msg */
};

#define ADR_DLOFF   2            mmdf/h/jnt.h   444      0     12         435  3620510353   6123 /*
 *	Return value for OK reception - the NIFTP has excepted
 *	the file + address without problems
 */
#define JNT_OK		0

/*
 *	Temporary error - a retry may be successful
 */
#define JNT_TEMP	1

/*
 *	Return value for JNT Mail permanent error - total reject.
 */
#define JNT_PERM	75
this to mbox portion        */
};

/*  addresses of the form "<mbox> @ name" become "<mbox>.dot @ at"      */

long amp_hdr();
 long   adr_pos;		  /* position in address list of this msg */
};

#define ADR_DLOFF   2            mmdf/h/mmdf.h   444      0     12       17777  3656424562   6353 #
/*  NOTE:   h/util.h must be included BEFORE mmdf.h, in the source
 *          file.  It is not simply included, here, because some
 *          C compilers cannot handle enough levels of #include
 *          nesting. (sigh.)
 */

#include "ll_log.h"               /* file-logging package               */
#include "conf.h"                 /* mmdf site-specific general info    */

short domsg;                      /* verbose reporting to controlling tty */
#define printx if (domsg) printf

/*                      Reply Codes for MMDF

 *  Based on: "Revised FTP Reply Codes", by Jon Postel & Nancy Neigus Arpanet
 *      RFC 640 / NIC 30843, in the "Arpanet Protocol Handbook", E.  Feinler
 *      and J. Postel (eds.), NIC 7104, Network Information Center, SRI
 *      International:  Menlo Park, CA.  (NTIS AD-A0038901)
 *
 *  Actual values are different, but scheme is same.  Codes must fit into
 *  8-bits (to pass on exit() calls); fields are packed 2-3-3 and interpreted
 *  as octal numbers.
 *
 *  Basic format:
 *
 *      0yz: positive completion; entire action done
 *      1yz: positive intermediate; only part done
 *      2yz: Transient negative completion; may work later
 *      3yz: Permanent negative completion; you lose forever
 *
 *      x0z: syntax
 *      x1z: general; doesn't fit any other category
 *      x2z: connections; truly transfer-related
 *      x3z: user/authentication/account
 *      x4x: mail
 *      x5z: file system
 *
 *      3-bit z field is unique to the reply.  In the following,
 *      the RP_xVAL defines are available for masking to obtain a field.
 */
/***************  FIELD DEFINITIONS & BASIC VALUES  ***************** */

/*          Field 1:  Basic degree of success (2-bits)                  */

#define RP_BTYP '\200'            /* good vs. bad; on => bad            */

#define RP_BVAL '\300'            /* basic degree of success            */

#define RP_BOK  '\000'            /* went fine; all done                */
#define RP_BPOK '\100'            /* only the first part got done       */
#define RP_BTNO '\200'            /* temporary failure; try later       */
#define RP_BNO  '\300'            /* not now, nor never; you lose       */

/*          Field 2:  Basic domain of discourse (3-bits)                */

#define RP_CVAL '\070'            /* basic category (domain) of reply   */

#define RP_CSYN '\000'            /* purely a matter of form            */
#define RP_CGEN '\010'            /* couldn't find anywhere else for it */
#define RP_CCON '\020'            /* data-transfer-related issue        */
#define RP_CUSR '\030'            /* pertaining to the user             */
#define RP_CMAI '\040'            /* specific to mail semantics         */
#define RP_CFIL '\050'            /* file system                        */
#define RP_CLIO '\060'            /* local i/o system                   */

/*          Field 3:  Specific value for this reply (3-bits)            */

#define RP_SVAL '\007'            /* specific value of reply            */
/*********************  SPECIFIC SUCCESS VALUES  ******************** */


/*                        Complete Success                              */

#define RP_DONE (RP_BOK | RP_CGEN | '\000')
				  /* done (e.g., w/transaction)         */
#define RP_OK   (RP_BOK | RP_CGEN | '\001')
				  /* general-purpose OK                 */

#define RP_MOK  (RP_BOK | RP_CMAI | '\000')
				  /* message is accepted (w/text)       */
#define RP_DOK  (RP_BOK | RP_CGEN | '\003')
			/* accepted for the delayed submission channel  */


/*                        Partial Success                               */

#define RP_MAST (RP_BPOK| RP_CGEN | '\000')
				  /* you are the requestor              */
#define RP_SLAV (RP_BPOK| RP_CGEN | '\001')
				  /* you are the requestee              */
#define RP_AOK  (RP_BPOK| RP_CMAI | '\000')
				  /* message address is accepted        */
#define RP_HOK  (RP_BPOK| RP_CMAI | '\001')
				  /* host processing completed          */
/*********************  SPECIFIC FALURE VALUES  ********************* */


/*                        Partial Failure                               */

#define RP_AGN  (RP_BTNO | RP_CGEN | '\000')
				  /* not now; maybe later               */
#define RP_TIME (RP_BTNO | RP_CGEN | '\001')
				  /* timeout                            */
#define RP_NOOP (RP_BTNO | RP_CGEN | '\002')
				  /* no-op; nothing done, this time     */
#define RP_EOF  (RP_BTNO | RP_CGEN | '\003')
				  /* encountered an end of file         */

#define RP_NET  (RP_BTNO | RP_CCON | '\000')
				  /* channel went bad                   */
#define RP_BHST (RP_BTNO | RP_CCON | '\001')
				  /* foreign host screwed up            */
#define RP_DHST (RP_BTNO | RP_CCON | '\002')
				  /* host went away                     */
#define RP_NIO  (RP_BTNO | RP_CCON | '\004')
				  /* general net i/o problem            */
#define RP_NS	(RP_BTNO | RP_CCON | '\005')
				  /* temporary nameserver failure	*/

#define RP_FIO  (RP_BTNO | RP_CFIL | '\000')
				  /* error reading/writing file         */
#define RP_FCRT (RP_BTNO | RP_CFIL | '\001')
				  /* unable to create file              */
#define RP_FOPN (RP_BTNO | RP_CFIL | '\002')
				  /* unable to open file                */
#define RP_LIO  (RP_BTNO | RP_CLIO | '\000')
				  /* general local i/o problem          */
#define RP_LOCK (RP_BTNO | RP_CLIO | '\001')
				  /* resource currently locked          */


/*                       Complete Failure                               */

#define RP_MECH (RP_BNO | RP_CGEN | '\000')
				  /* bad mechanism/path; try alternate? */
#define RP_NO   (RP_BNO | RP_CGEN | '\001')
				  /* general-purpose NO                 */

#define RP_PROT (RP_BNO | RP_CCON | '\000')
				  /* general prototocol error           */

#define RP_RPLY (RP_BNO | RP_CCON | '\001')
				  /* bad reply code (PERMANENT ERROR)   */

#define RP_NAUTH (RP_BNO | RP_CUSR  | '\001')
				/* bad authorisation                    */
				/* SEK this will be used for user checks*/

#define RP_NDEL (RP_BNO | RP_CMAI | '\000')
				  /* couldn't deliver                   */

#define RP_HUH  (RP_BNO | RP_CSYN | '\000')
				  /* couldn't parse the request         */
#define RP_NCMD (RP_BNO | RP_CSYN | '\001')
				  /* no such command defined            */
#define RP_PARM (RP_BNO | RP_CSYN | '\002')
				  /* bad parameter                      */
#define RP_UCMD (RP_BNO | RP_CSYN | '\003')
				  /* command not implemented            */
#define RP_USER (RP_BNO | RP_CUSR | '\000')
				  /* unknown user                       */


/*                      STRUCTURE OF A REPLY STRING                     */

struct rp_construct               /* for constant reply conditions      */
{
    char    rp_cval;
    char    rp_cline[50];
};

struct rp_bufstruct               /* for reading reply strings          */
{
    char    rp_val;
    char    rp_line[256];
};

typedef struct rp_bufstruct RP_Buf;

#define rp_conlen(bufnam) (strlen (bufnam.rp_cline) + sizeof (bufnam.rp_cval))

/*              PSEUDO-FUNCTIONS TO ACCESS REPLY INFO                   */

#define rp_gval(val)    ((char) (val))
				  /* get the entire return value        */

/*  The next three give the field's bits, within the whole value        */

#define rp_gbval(val)   (rp_gval (val) & RP_BVAL)
				  /* get the basic part of return value */
#define rp_gcval(val)   (rp_gval (val) & RP_CVAL)
				  /* get the domain part of value       */
#define rp_gsval(val)   (rp_gval (val) & RP_SVAL)
				  /* get the specific part of value     */

/*  The next three give the numeric value withing the field             */

#define rp_gbbit(val)   ((rp_gval (val) >> 6) & 03)
				  /* get the basic part right-shifted   */
#define rp_gcbit(val)   ((rp_gval (val) >> 3 ) & 07)
				  /* get the domain part right-shifted  */
#define rp_gsbit(val)   (rp_gval (val) & 07)
				  /* get the specific part right-shifted */

/*  The following works with SIGNED or UNSIGNED chars!  */
#define rp_isgood(val)  (! rp_isbad(val))
				  /* is return value positive?          */
#define rp_isbad(val)   (rp_gval(val) & 0200)
				  /* is return value negative?          */

extern char *rp_valstr ();
 mmdf/h/bboards.h   444      0     12        3062  3620510353   6763 /* bboards.h - definition of a BBoard structure */

#define	BBOARDS	"bboards"	/* name in /etc/passwd */
#define	DISTADR	"dist-"		/* prefix for distribution addresses */
#define	BBMODE	0644		/* default BBoards mode */

struct bboard {
    char   *bb_name;		/* name of the bboard */
    char  **bb_aka;		/* aliases for the bboards */

    char   *bb_file;		/* file it resides in */
    char   *bb_archive;		/* file where archives reside */
    char   *bb_info;		/* file where maxima resides */
    char   *bb_map;		/* file where binary map resides */

    char   *bb_passwd;		/* password for it */

    char  **bb_leader;		/* list of local leaders */

    char   *bb_addr;		/* network address */
    char   *bb_request;		/* network address for requests */
    char   *bb_relay;		/* host acting as relay in local domain */
    char  **bb_dist;		/* distribution list */

    unsigned int    bb_flags;	/* various flags */
#define	BB_NULL	0x0000
#define	BB_ARCH	0x0007		/* archive policy */
#define   BB_ASAV	0x0001	/*   save in archives/ directory */
#define	  BB_AREM	0x0002	/*   remove without saving */
#define	  BB_INVIS	0x0010	/* invisible to bbc */

    unsigned int    bb_count;	/* unassigned */
    unsigned int    bb_maxima;	/* highest BBoard-Id in it */
    char   *bb_date;		/* date that maxima was written */

    struct bboard *bb_next;	/* unassigned */
    struct bboard *bb_link;	/* unassigned */
};

int     setbbent (), endbbent (), setbbfile (), ldrbb (), ldrchk (),
	getbbdist ();
char   *getbberr ();
struct bboard  *getbbent (), *getbbnam (), *getbbaka (), *getbbcpy();
st         */
#define RP_NCMD (RP_BNO | RP_CSYN | '\001')
				  /* no such command defined            */
#define RP_PARM (RP_BNO | RP_CSYN | '\002')
				  /* bad parameter                      */
#define RP_UCMD (RP_BNO | RP_CSYN | '\003')
				  /* command not implemented            */
#define RP_USER (RP_BNO | RP_CUSR | '\000')
				  /* unknown user                       */


/*                      STRUCTURE OF A REPLY STRING                     */

structmmdf/h/chan.h   444      0     12        1123  3620510353   6254 /*  definitions for channel programs */

/*  the following are used to indicate the state condition of a
 *  channel program.  this may be passed to qu_fakrply() to keep
 *  Deliver happy if the foreign site goes away.
 */

#define SND_ABORT   0             /* terminate on error                 */
#define SND_RINIT   1             /* goto qu_rinit                     */
#define SND_RDADR   2             /* goto qu_radr                      */
#define SND_ARPLY   3             /* goto qu_wrply, for address        */
#define SND_TRPLY   4             /* goto qu_wrply, for msg text       */


    char  **bb_leader;		/* list of local leaders */

    char   *bb_addr;		/* network address */
    char   *bb_request;		/* network address for requests */
    char   *bb_relay;		/* host acting as relay in local domain */
    char  **bb_dist;		/* distribution list */

    unsigned int    bb_flags;	/* various flags */
#define	BB_NULL	0x0000
#define	BB_ARCH	0x0007		/* archive policy */
#define   BB_ASAV	0x0001	/*   save in ammdf/h/chdbm.h   444      0     12         422  3623131444   6404 #define FS '\034'
#define MAXVAL 20       /* allow this many values per entry */
#define ENTRYSIZE 1024

typedef struct DBvalues
{
    char *RECname;      /* name of table */
    char *RECval;       /* value for this record, in this table */
}       DBMValues[MAXVAL + 1];

error                 */
#define SND_RINIT   1             /* goto qu_rinit                     */
#define SND_RDADR   2             /* goto qu_radr                      */
#define SND_ARPLY   3             /* goto qu_wrply, for address  mmdf/h/hdr.h   444      0     12         625  3620510354   6107 /* hdr_read return values */

#define HDR_NAM -1                /* no name                            */
#define HDR_EOH  0                /* end of headers                     */
#define HDR_OK   1                /* nice header                        */
#define HDR_NEW  2                /* new header field                   */
#define HDR_MOR  3                /* continuation of header field       */
  /* goto qu_radr                      */
#define SND_ARPLY   3             /* goto qu_wrply, for address  mmdf/h/msg.h   444      0     12        2547  3620510354   6145 struct msg_struct                /* Structure for sorting files        */
{
    time_t  mg_time;             /* Creation time of message           */
#define LARGESIZE 100000         /* threshold for large message        */
#ifdef LARGESIZE
    char    mg_large;            /* message is over-sized              */
#endif
    char    mg_stat;             /* options                            */
    char    mg_mname[MSGNSIZE];  /* Filename                           */
    char    mg_null;             /* Null for terminating string        */
};
typedef struct msg_struct Msg;

/** The following bits are use in mg_stat **/
#define ADR_CITE   0001           /* When a message is determined to be */
				  /* undeliverable to some addressee(s),*/
				  /* MMDF notifies the sender (if a     */
				  /* return address was given) and an   */
				  /* entire copy of the message is      */
				  /* included in the return, unless this*/
				  /* flag is set, in which case only    */
				  /* a "citation" is returned.          */
#define	ADR_NORET  0002		  /* Do NOT return to sender on error   */
#define	ADR_NOWARN 0004		  /* Do NOT send non-delivery warnings  */
#define	ADR_WARNED 0010		  /* Warning has been sent 		*/

#define msg_cite(val)   (val&ADR_CITE)
#define msg_noret(val)	(val&ADR_NORET)
#define msg_nowarn(val)	(val&ADR_NOWARN)
#define msg_warned(val)	(val&ADR_WARNED)
_link;	/* unassigned */
};

int     setbbent (), endbbent (), setbbfile (), ldrbb (), ldrchk (),
	getbbdist ();
char   *getbberr ();
struct bboard  *getbmmdf/h/phs.h   444      0     12        1366  3620510354   6147 /*
 *      activity phase-recording
 *
 *      use files to timestamp activity phases for channels
 */

#define PHS_CNSTRT  1           /* start connecting to site */
#define PHS_CNGOT   2           /* got connection to site */
#define PHS_CNEND   3           /* end connection to site */
#define PHS_RESTRT  4           /* start reading (sending) mail from site */
#define PHS_REMSG   5           /* finished reading one message */
#define PHS_REEND   6           /* end reading (sending) mail from site */
#define PHS_WRSTRT  7           /* start writing (picking up) mail to site */
#define PHS_WRMSG   8           /* finished writing one message  */
#define PHS_WREND   9           /* end writing (picking up) mail to site */


extern time_t phs_get ();
DF notifies the sender (if a     */
				  /* return address was given) and an   */
				  /* entire copy of the message is      */
				  /* included in the return, unless this*/
				  /* flag is set, in which case only    */
				  /* a "citation" is returned.        mmdf/h/ns.h   444      0     12         563  3644274345   5770 /*
 * timer values used to adjust nameserver performance
 */

/* extreme limits */

#define NS_MINRETRY	1
#define NS_MAXRETRY	7

#define NS_MINRETRANS	5

#define NS_MINTIME (NS_MINRETRY * NS_MINRETRANS)

/* some timer values */

#define NS_UIPTIME	20	/* default for UIP programs */
#define NS_DELTIME	120	/* delay channel */
#define NS_NETTIME	20	/* network programs */

ne PHS_REMSG   5           /* finished reading one message */
#define PHS_REEND   6           /* end reading (sending) mail from site */
#defmmdf/h/gettys.h   444      0     12         274  3620510354   6651 #
/*  the definitions needed to use the package which accesses
 *  the /etc/ttys file.
 */

struct ttys
    {
    int t_valid;
    int t_code;
    char t_name[8];
};

#define		BADDATA		-2
NS_MINRETRANS)

/* some timer values */

#define NS_UIPTIME	20	/* default for UIP programs */
#define NS_DELTIME	120	/* delay channel */
#define NS_NETTIME	20	/* network programs */

ne PHS_REMSG   5           /* finished reading one message */
#define PHS_REEND   6           /* end reading (sending) mail from site */
#defmmdf/h/conf.h   444      0     12        2732  3656424247   6316 #
/*          This contains the most site-dependent definitions             */

#define USERSIZE    8		/* max length of a user system name   */
#define	MAILIDSIZ  64		/* max length of a mailid,
				 * must be at least USERSIZE.		*/
#define MSGNSIZE   14           /* max length of a message file name  */
#define FILNSIZE   64           /* max length of full file name       */
#define ADDRSIZE  256		/* max length of an address           */
#define LINESIZE  256		/* max length of a line/record        */

#define	NUMCHANS   20		/* max number of channels */
#define NUMTABLES  30		/* max number of tables */
#define NUMDOMAINS 20		/* max number of domains */

/************  DIAL IN/OUT (CH_PHONE / SLAVE) TAILORING  *****************/

#define DL_TRIES    2             /* number of re-connects OK for phone */
#define DL_RCVSZ    255           /* longest line receivable            */
#define DL_XMTSZ    255           /* longest line sendable              */

/****************  JNTMAIL Tailoring *******************/

#undef	JNTMAIL
#undef	BOTHEND
#undef	VIATRACE

/**************** Include File Tailoring ****************************/

#include <time.h>
#include <sys/dir.h>

/********************* Privileged UIDs *************************/

/*
 *  This can be a macro as give here or you can provide a function.
 *  It should always authorize root and mmdf.
 *  The global variable effecid is assumed to have the MMDF uid in it.
 */

#define	PRIV_USER(x) ((x)==0 || (x)==1 || (x)==effecid)
  /* a "citation" is returned.        mmdf/h/ph.h   444      0     12         701  3646043304   5740 struct ps_rstruct {             /* save last reply obtained           */
    int     sm_rval;            /* rp.h value for reply               */
    int     sm_rlen;            /* current lengh of reply string      */
    char    sm_rgot;            /* true, if have a reply              */
    char    sm_rstr[LINESIZE];  /* human-oriented reply text          */
};

#define rp_structlen(bufnam) (strlen (bufnam.rp_line) + sizeof (bufnam.rp_val))
/

/****************  JNTMAIL Tailoring *******************/

#mmdf/h/smtp.h   444      0     12        2737  3657737607   6371 
/* timeouts for smtpsrvr */
#define NTIMEOUT        180     /*  3 minute timeout on net I/O  */
#define DTIMEOUT        600     /*  10 minute timeout on submit I/O */

/* smtp timeouts */
#define SM_TIME		20		/* default timeout */
#define SM_HTIME        SM_TIME		/* Time allowed for HELO command */
#define SM_OTIME        40	     	/* Time allowed for a netopen */

/*
 * for waiting for first response after connection
 * give a lot of time to first MX, then back off
 */

#define SM_ATIME	240		/* Time allowed for answer: 1st open */
#define SM_ATMIN	90		/* Time allowed for answer: minimum */
#define SM_ATINC	30		/* backoff increment */

#define SM_STIME        120      	/* Time allowed for MAIL command */
#define SM_TTIME        300      	/* Time allowed for RCPT command */
#define SM_QTIME        SM_TIME		/* Time allowed for QUIT command */

#define SM_RTIME        SM_TIME		/* Time allowed for a RSET command */
#define SM_DTIME        SM_TIME		/* Time allowed for a DATA command */
#define SM_PTIME        120     	/* Time allowed for the "." command */
#define SM_BTIME        120     	/* Time allowed for a block of text */


struct sm_rstruct {      /* save last reply obtained           */
    int     sm_rval;            /* rp.h value for reply               */
    int     sm_rlen;            /* current lengh of reply string      */
    char    sm_rgot;            /* true, if have a reply              */
    char    sm_rstr[LINESIZE];  /* human-oriented reply text          */
};
getbberr ();
struct bboard  *getbmmdf/doc/   755      0     12           0  3652766646   5457 mmdf/doc/review/   755      0     12           0  3635164765   6753 mmdf/doc/review/p0   444      0     12        3721  3620510336   7262 .TL
MMDFII:
A Technical Review
.AU
Douglas P. Kingston III
.AI
Ballistic Research Laboratory
Aberdeen Proving Grounds, Maryland  21005
<dpk@brl>
.AB
.PP
The Multi-channel Memo Distribution Facility (MMDF) is one of
the most sophisticated mail systems available for the UNIX\(dg
.FS \(dg
Unix is a trademark of Bell Laboratories.
.FE
operating
system.
MMDF is a mail transport system that supports a variety of
user interfaces and delivery mechanisms. The design was not encumbered
with the need to be compatible with existing mail systems, and as a
result MMDF has a unified family of mail handling programs.  This review
will discuss MMDF's design and operation, concentrating on those features
that are unique to MMDFII, the latest release of MMDF.
.PP
MMDF's design allows it to grow from a
single-host system to a large mail relay without degradation of mail
system performance, and to degrade gracefully as the load becomes huge.
The demands of a high volume mail relay have led to many of MMDF's
innovative design choices.
.PP
Unlike some other systems, MMDF has separate processes for
mail submission and delivery.
Recent changes to the delivery software to permit intelligent
retry strategies based on the retry history for each dead host will
be explained.
The effect of the new domain server
mechanism on address validation will be discussed.
.PP
The separation of mail into channels is key to MMDF's ability to
handle large amounts of mail. Each channel represents a different class
of delivery and each channel has its own queue.  This isolates
problems and allows one to
provide different ``levels of service'' to different channels.
.PP
Other topics to be discussed will include available user
interfaces, the mailing list processor, aliasing, runtime
configuration, and domain based naming.
.PP
The MMDF system was originally developed at the University of Delaware
and has since seen significant development work at the Ballistic
Research Laboratory and University College London.
.AE
F A REPLY STRING                     */

structmmdf/doc/review/p1   444      0     12       11555  3642650444   7320 .SH
Introduction and History
.PP
The Multi-channel Memo Distribution Facility,
commonly called MMDF,
is a suite of software that has seen a great deal of work since it was
originally released in 1980.
The original code was designed and implemented by Dave Crocker
working under Professor David Farber at the University
of Delaware (UDEL).
The MMDF system was then chosen to form the initial backbone
software for the CSNET project and has been in use for several years
by elements of the U.S. Army.
The software has seen a great deal of change in the process.
The original code is commonly referred to as MMDFI or MMDF Version 1.
A number of minor additions and changes were made while fielding MMDFI
as the result of collaboration between UDEL and BRL and some other
sites.
After the original code was fielded in CSNET, Dave Crocker began the
development of a upgraded version of the MMDF system which was
designed to work in the new Internet
domain naming system
and was to incorporate numerous design changes suggested by
experience with MMDFI.
Dave Crocker
left the CSNET project before completing this work, approximately
two weeks before the TCP/IP switchover of the ARPANET, 1 January 1983.
At this time, BRL was a solid MMDF site.  
We were reluctant to try to retrofit
the existing version of MMDFI
to handle the new mail protocols that also took effect on
1 January, so Doug Kingston of
BRL undertook the task of finishing the work
needed to make MMDFII operational.
A production version of MMDFII was installed at BRL during
the third week of January 1983, and served
as BRL's mail system on three hosts,
but there was no stable version of the MMDFII code until June 1983.
The first few months of MMDFII
were quite rough and it needed a great deal of ``tender loving care''.
.PP
For reasons that will be clear in a moment, this stable version of June 1983
is now referred to as the MMDFII-pre-England version.
Around June, a copy of this stable version was delivered to Steve Kille
of University College London (UCL) and to Brendan Reilly
of UDEL, who
had taken over Dave Crocker's work on MMDF at UDEL.
Steve Kille made a number of major changes to the handling of domains,
address parsing, and handling of the alias files.
Steve also added support for NIFTP, a European file transfer protocol
used for sending mail in a batch environment.
At the same time that
Steve was making his enhancements, Doug Kingston continued
to develop BRL's copy of MMDFII to make it an even more solid
mail system.  BRL's changes were not as major as Steve's
but covered a great deal of code and fixed several major outstanding bugs.
This dual development led to two variants of MMDFII that each
needed the other's improvements.
In late September of 1983
Brendan Reilly and Doug Kingston spent a week in England with Steve
to merge the variants and to discuss future changes and directions
for MMDF.  The result of this meeting was a merged version of
MMDFII which I will call MMDFII-post-England.
Just prior to this trip, the CSNET Information Center (CIC)
received a copy of the pre-England MMDF.
Their later changes were based on this pre-England version
which made merging of their changes into the
post-England version somewhat difficult.
.PP
After the England meeting, Brendan Reilly of UDEL took the role
of coordinator of the subsequent changes to MMDF.  Copies of the
MMDF-post-England were made simultaneously available to BRL, UCL, and UDEL.
Since then many minor changes have been made by all
four sites;
in essentially all cases these changes have been bug fixes or changes
to make MMDF a more stable and robust system.
.PP
Since then, Doug Kingston at BRL has made changes
to the local delivery mechanism, rewriting much of the original
code, and the central delivery program has been upgraded to
take advantage of large-address-space machines, when possible, to
keep retry histories for messages on a host-by-host basis.
Bernie Cosell at the CIC has undertaken to speed up MMDF
execution by providing a facility for compiling in some of the information
normally included in the ASCII text-based version.  Steve Kille
an alternative to the ASCII text based version.  Steve Kille
has continued to refine the address handling and the British
``backwards'' domain code.
.FN
The British do domains backwards.  For example, if in the
US (Internet) we write ``user@VAX1.EE.UDEL.ARPA'' known
as ``little endian'' order, the British
(SERC Net) 
write ``user@ARPA.UDEL.EE.VAX1'' or ``big endian'' order.
Put another way, ``big endians'' put the largest, most general,
or most significant element of the domain first.  ``Little endians''
use the other order, with the most significant part last.
[See 
.I
Gulliver's Travels
.R
by Joanthan Swift.  The "big endian" vs. "little endian" controversy
was a 
.I
causus belli
.R
in Lilliput.]
.FE
Brendan Reilly has made changes to the package to allow it to
run on the Altos system and has fixed numerous bugs in
the PhoneNet code.
 of the MMDF system which was
designed to work in the new Internet
domain naming system
and was to incorporate numerous design changes suggested bymmdf/doc/review/p2   444      0     12       15716  3620510337   7314 .SH
The MMDF Design
.PP
The MMDF system design has not changed fundamentally
from the original design proposed and implemented for MMDFI.
The design of MMDFI is covered in detail in the paper
``An Internetwork Memo Distribution Capability''.
.FN
D. Crocker, E. Szurkowski, and D. Farber,
``An Internetwork Memo Distribution Capability'', Datacom Conference,
September 1979.
.FE
In this chapter, I will summarize the basic design and discuss
those changes that have been made in MMDFII.
.PP
Mail software is normally classed into one of two groups.
A user agent (UA) is a program
that is responsible for providing a ``user friendly'' interface
for reading or writing mail and converting to the canonical
interface of the mail transfer agent as necessary.
A mail transfer agent (MTA) is a program or system
to which you or your user agent entrusts a message for delivery
to someone else's user agent.
There has been a great deal of confusion caused by people who
fail to realize the difference between a mail transfer agent and 
a user agent, or even that a difference exists!
There is a wide variety of user agents which can be used with
MMDF, and it is the responsibility of the use agent to provide a user interface.
This separation of the functions of user agent and mail transfer
agent has many advantages, not the least of which is that MMDF
can support many different user interfaces with ease.
Currently, there are at least five different interfaces available
including the Rand MH system, V6 style mail, Berkeley's Mail (cap-mail),
the Tenex-style Send and Msg programs, and Rmail (for UUCP).
MMDF is a mail transfer agent.
MMDF does not have, nor does it claim to have, a good ``user interface'';
instead it has a good program (MTA-UA) interface.
MMDF accepts messages for delivery either locally or to a remote
site.  It attempts to verify the validity of the addresses at submission
time to the extent possible given only a host table and a list of
local addresses.  If accepted, it will continually try to resend the
message until the retry time is exhausted at which time
the message is returned to the sender.
.PP
The MMDF system can be thought of as two subsystems, responsible
for mail submission and mail delivery, respectively.
Between these two halves is the mail queue.
The mail queue will be discussed later, but basically it stores each
message as two files, an address list with some control information, and
a separate file containing only the message text (header and body).
.PP
The submission half of the system consists mainly of one program,
called ``\fBsubmit\fR'', which is responsible for enqueuing mail to be
delivered.  As much verification as possible is performed on the
message at
submission time.
For mail destined for the local machine, this means making sure the
destination account exists, and that any local mailing list or aliases
expand properly.
For mail that has a non-local host specification, the \fBsubmit\fR
process checks to see if it knows how to reach the specified host.
Mail which for any reason is known to be undeliverable, is not accepted
for delivery.
\fBSubmit\fR is called by two types of processes.  The first group includes
user agents such as the \fBsend\fR program.  The second group 
comprises channel
programs such as \fBrmail\fR
.FN
The \fBrmail\fR program (/bin/rmail) is
invoked by uucp when delivering mail
on your system.
.FE
which are interfacing to remote mail transfer agents.
.PP
The delivery portion of MMDF is represented by two main elements: the
\fBdeliver\fR program which manages the queue, and
the channel programs which handle the details of
delivery to a specific network, host, or mail system.
The \fBdeliver\fR program takes each message which is eligible to
be delivered, and opens the appropriate address list.  For each address
in the list, \fBdeliver\fR ensures that it is running the appropriate channel
program and then passes the envelope information
.FN
The MMDF envelope information consists of the the
return address, the destination addresses, some delivery options and
a reference to the message text file.
.FE
to the channel.  A reference to the file containing the actual
message text is passed to the
channel program.  The channel decides how
to deliver the message and sees to any necessary message reformatting
that may be necessary (e.g. ``header munging'').
.PP
There is currently a variety of channels and the number is growing.
The local channel handles delivery of messages to local addresses.
The list channel is a special, somewhat incestuous, channel which acts
the role of channel, receiving user agent, and sending user agent all
in one.  The list channel resubmits mail back into the mail system
by calling \fBsubmit\fR.
This has several benefits that will be discussed later.
.FN
See the section on the list channel.
.FE
The SMTP channel delivers mail via TCP/IP connections using the SMTP
protocol.
.FN
J. Postel, ``RFC821 - Simple Mail Transfer Protocol'', Network Information
Center, SRI International, August 1982.
.FE
The phone channel uses the PhoneNet link-level protocol software
developed at the University of Delaware for sending mail over
dialup or hard-wired terminal lines.
The NIFTP channel queues mail as files to be transferred using the
NIFTP protocol used in the British research community.
The UUCP channel is used to queue requests for transfer using UUCP.
.PP
Development of MMDFII was started about the same time that the Sendmail 
mail transfer agent was being
written by Eric Allman at Berkeley.
Dave Crocker met with Eric on a number of occasions and was impressed
by his work.
Some elements of the Berkeley software were so useful that
they inspired the development of similar facilities in MMDFII.
Not the least of these was the runtime configuration file.
It is now possible to configure the MMDF software totally from a
single, ASCII-text-based configuration file.
Unlike Sendmail's terse configuration syntax, MMDF uses much more
verbose keyword/value pairing for configuration information.
MMDFII can be configured either from compiled-in values, for fast startup,
or totally from the text configuration file, or from any combination of the
two.
As a result of the fact that many values can be compiled-in, the usual
MMDF tailoring file is one-tenth the size of
a Sendmail tailoring file, and can be even smaller, even on a large relay site.
The runtime configuration file is one of the most useful additions
in MMDFII, especially for sites supporting more than one host,
since one can now run the same binaries on all machines of the same type.
Another Berkeley-inspired facility was the ability to have an alias
file entry that forces a delivery to a file or pipe.
While initially insecure, this facility has been made
reliable by adding code to /fBsubmit/fR that knows when you are
processing an alias file entry and when you are processing some
other type of address.
Simple ownership of the file containing the address was not considered
sufficient protection since there are too many
files left writable to the world that are owned by root or other
privileged users.

Mail which for any reason is known to be undelivemmdf/doc/review/p3   444      0     12       13327  3642650513   7316 .SH
The MMDF File Hierarchy
.PP
The MMDFI queue structure consisted of three directories.
The ``msg'' directory contained the files containing actual
message text.
The ``addr'' directory contained the address list file for each
message, and the ``tmp'' directory contained the address list
while it was being created before moving it to the ``addr''
directory.
The names of the files in ``msg'' and ``addr'' were identical
although the contents differed.
This made it easy to find
the companion file given either filename.
The MMDFII queue structure has changed in one major way from that
of MMDFI.
There is now one extra directory per channel named q.\fIchannel\fR.
If an address list still has any addresses destined to be sent
on any channels,
then a link will exist from the address list
file in ``addr'' to a file with the same name in each queue directory
which is referenced.
All the above directories usually live in /usr/mmdf/lock/home,
although the
root name of this tree can be changed.
The lock directory is kept in 700 or 770 mode, accessible only to
the ``mmdf'' user and possibly a ``systems'' group for
ease of maintenance.
The ``home'' directory and all the underlying queue directories are
kept in 777 mode to allow ease of movement and access for the trusted
but unprivileged programs that operate there.
In particular, the \fBsubmit\fR process is setuid to ``mmdf''
to allow it to
enter the tree, but it then restores
its UID and GID to those of the invoker.
\fBDeliver\fR and the channel programs normally run as the ``mmdf'' user.
Only the local channel, which must access the real user's mailbox files,
and the TCP/IP network daemons, which must access privileged sockets,
need run privileged.  There are several other programs that are
setuid to ``root'', but only so they can change their UID to ``mmdf''
so as to to be a ``trusted submitter'', which allows them to specify
an arbitrary From: line.
.PP
The addition of the separate queuing directories on
a per channel basis was a valuable change.
UNIX performs poorly with large directories,
as any UUCP backbone site can tell you.
This new mechanism allows easy
partitioning of channel activities into separate directories, since
\fBdeliver\fR will never access the copy of the address list in the ``addr''
directory until it goes to finally expunge the message from the queues.
If one channel or site gets backed up, it does not affect the performance of
any of the other channels.
.PP
The format of the address lists has changed somewhat since MMDFI.
The old format was:
.in +1i
.sp .6
S T channel-name host-on-channel address-at-host
.in -1i
.sp .6
where S was either a `\-' indicating unsent or a `+' indicating that
only the address but not the text had been sent.
The T was either the type of delivery
(almost always `m' for mail) or a `*' indicating the message has been
completely delivered.  The channel-name and host-on-channel should be
obvious.  The address-at-host parameter was only the local part of an
address.
The new version differs in two ways.  First, the host-on-channel is now
the full domain address of the host to deliver to, as specified in the
routing information of the domain table.
.FN
See the manual section on the MMDF database (queue.5) for more information.
.FE
Second, the address-at-host is now the full address with
both the local-part and the domain specifier.
.PP
In the future, I will probably change the location of the ``message
completely delivered'' flag to be in the first position (S), since
I feel it was a mistake to not have it there in the past and it is
confusing in its current position.
This has not been done as of February 1985.
.PP
The MMDF file hierarchy also has a logging directory.  Separate
logs are kept here for \fBsubmit\fR and \fBdeliver\fR,
the channel programs,
and the phone dialing package.  This directory is generally
accessible although unreadable and the logs are normally write
only.  This could be made ``mmdf'' access only for some sites, with
the only penalty being that some programs would be unable to add
entries to the log.
A subdirectory of the log directory called ``log/phase'' contains
time stamp files whose modification times are changed by \fBsubmit\fR
and \fBdeliver\fR to indicate such things as last pickup time, last
delivery time, last poll made, and similar information.
.PP
There are two other directories in the MMDF hierarchy which
should be mentioned.  The ``chans'' directory contains
all of the channel programs invoked by the \fBdeliver\fR program,
and other ancillary daemon programs such as the SMTP daemon
and the SMTP server.  The ``table'' directory contains
all files necessary to maintain the MMDF database,
including domain tables, host address files, mailbox
alias files, dialing scripts, and programs to build these
files and incorporate them into the DBM library.
.FN
The DBM package is a set of simple hashed database access routines
that were distributed with V7 Unix and are still widely used.
.FE
.PP
Use of some sort of keyed database system is almost essential for large
MMDF systems, since the table lookup overhead is unbearable
otherwise.  Currently DBM is the only readily available alternative
and it does seem to work well, but it is running out of steam,
particularly due to its limited record length.  A more
flexible replacement for
this package would be welcome.
.PP
The top of the MMDF directory tree contains the directories mentioned
above, the \fBsubmit\fR and \fBdeliver\fR
programs, and a few maintenance programs.
If the site is polled for PhoneNet mail then the ``\fBslave\fR''
program is normally
also located here.  The \fBslave\fR
program is used as the remote site's login
shell and acts much as \fBuucico\fR in managing the
link level communications.
It in turn calls upon \fBsubmit\fR and \fBdeliver\fR to send and receive mail.
 delivery
(almost always `m' for mail) or a `*' indicating the message has been
completely delivered.  The channel-name and host-on-channel should be
obvious.  The address-at-host parameter was only the local part of an
address.
The new version differs in two ways.  First, the host-on-channel is mmdf/doc/review/p4   444      0     12        7321  3620510337   7267 .SH
Submit
.PP
For all the changes to its internals, the external interface of
\fBsubmit\fR has changed remarkably little since MMDFI.
The only notable external changes to \fBsubmit\fR are that it now
processes domain-style addresses (both forward and reverse!)
and both RFC822
and RFC733 format addresses.
.FN
D. Crocker, J. Vittal, K. Pogran, D. Henderson, ``RFC733 - Standard
for the Format of ARPA Network Text Message'',
Network Information Center, SRI International, November 1977.

D. Crocker, ``RFC822 - Standard
for the Format of ARPA Network Text Message'',
Network Information Center, SRI International, August 1982.
.FE
A couple of new options have been added to \fBsubmit\fR to give some
control over the handling of returned mail in the event that a message
cannot be delivered.  This is
useful if the user is a program and does not care if
the message is undeliverable!
It is also now possible to feed a bare message to \fBsubmit\fR and tell
\fBsubmit\fR to find all the addresses and show them and their validity
on a one-by-one basis.  This makes it convenient
to feed mail to \fBsubmit\fR from a smart mail composer that doesn't want to
know how to parse addresses (a major task these days!).
.PP
The \fBsubmit\fR program can operate in one of two modes.
In \fIprotocol\fR mode, \fBsubmit\fR accepts options, a return address,
and optionally a list of addresses for each message, followed by the
message text.  Multiple messages can be submitted
one after another without reinvoking the \fBsubmit\fR process.
Each address is individually acknowledged.  If there is an error,
the submission of that letter is aborted and a new submission
may be made.
In \fIone-shot\fR mode a single message is submitted on the standard
input.
As in \fIprotocol\fR mode, it can be preceded by options and addresses,
or the options can be given on the command line and the addresses taken
from the message text.
.PP
The internal address verification process of \fBsubmit\fR has changed
greatly since MMDFI.  Most of the changes
have been made to properly support domain style addresses.
Additional changes were made to support per-channel
and per-user access
controls.
While \fBsubmit\fR is checking each address, regardless
of origin, it is also compiling the address
list for the message.
Each address list entry contains the destination domain,
.FN
By ``domain'' we mean the full domain specification of a host, e.g.
VAX1.UDEL.ARPA.
.FE
the destination mailbox,
and the channel in whose channel table \fBsubmit\fR first found the destination
host.
The lookup is somewhat complicated.
The destination domain is looked up in the domain tables;
the first entry found is used.
Each domain table entry has associated with it the routing to be used to reach
that domain/host.  Normally this is just the name of the host itself
if the host is directly accessible, but it can be a sequence of hosts if the
destination is not directly accessible.
This ``routing host'' is then looked up in the channel tables
to find a channel which can reach it.  The routing host specifications
and the entries in the channel tables are always full domain specifications,
so as to be unambiguous.
.PP
Authorization is checked after a valid channel for a host is found.
Access to send via a given channel
depends on the originating channel and/or the submitting user.
If access to a given channel is denied, \fBsubmit\fR will continue to look
at subsequent channels to see if some other channel has access to the
same host and is authorized.  This mechanism is commonly used
to restrict access to expensive transport systems or to restrict
message transfer between channels representing private and public
data networks.
It can also be used to restrict relaying of messages between two
"controlled-access" networks.
ftware totally from a
single, ASCII-text-based configuration file.
Unlike Sendmail's terse configuration syntax, MMDF uses much more
verbose keyword/value pairing for configuration information.
MMDFII can be configured either from compiled-in values, for fast startup,
or totally from the text configurammdf/doc/review/p5   444      0     12        4151  3620510340   7260 .SH
Deliver
.PP
The \fBdeliver\fR process changed little until late 1984,
when a dead-host caching facility and advanced retry mechanisms
were added to \fBdeliver\fR.
Until then, the major changes to \fBdeliver\fR were
bug fixes and changes to enhance error recovery.
Most notably, the mechanism to return mail was made much more persistent.
In MMDFI, if a message could not be returned to the sender, it
was ``dropped on the floor''.  This is not acceptable.
The new mechanism first tries to return to the sender; if that fails,
an attempt is made to send the message to the local ``Postmaster''
address.  If this too fails, then \fBdeliver\fR tries to write the message
into a ``dead letter'' file
(usually /usr/mmdf/lock/home/DeadLetters).
The last resort is to scream loudly into the log files.
.PP
The recent changes to \fBdeliver\fR are
designed to reduce \fBdeliver\fR's load
on a system that handles a large amount of mail.
The first change is to move the dead-host caching function out
of the channels (currently only SMTP) and into \fBdeliver\fR.  This has
two advantages.
First, \fBdeliver\fR no longer has to hand every address to
the channel to find out that the host is dead.  This was expensive, since
communications between \fBdeliver\fR and the channel
are interactive using pipes
and thus involved a great deal of context-switching and pipe I/O.
Second, the dead-host caching is now a generally available facility that
can be used by any channel without duplication of code.
.PP
The second change is to add a mechanism for storing the retry history
for each dead host on each channel including retry count and last retry
time.
From this information it is possible to implement intelligent
retry strategies using exponential backoff and maximal retry times.
This greatly reduces the overhead caused by recalcitrant hosts
that are unavailable over a long period of time.
The retry history change is conditionally compiled
into deliver since it can,
by design, use quite a bit of memory if there are a lot of dead
hosts or pending messages (or both!).
Machines with a relatively limited address space may not
be able to use this feature.
 is used.
Each domain table entry has associated with it the routing to be used to reach
that domain/host.  Normally this is just the name of the host itself
if the host is directly accessible, but it can be a sequence of hosts if the
destination is not directly accessible.
This ``routing host'' is then looked up in the channel tables
to find a channel which can reach it.  The routing host specificationsmmdf/doc/review/p6   444      0     12       11246  3642650541   7320 .SH
The Local Channel
.PP
The local channel remained unchanged until late 1983, when
a major reworking of the channel was done by BRL.  The old
local channel could handle delivery in three basic ways.
The first and most common was to deliver directly to the
user's default mailbox, appending the message to the end.
Second, the user could put a program in his
private bin directory
called ``rcvmail'' that would be called by the local channel
to handle delivery of the message.
If the user program (rcvmail) did not complete successfully,
a standard delivery was made instead.
.PP
In early versions of the local channel for MMDFII, Dave Crocker added
support for mailing to files and pipes, but the original version
had a number of security problems,
mostly due to \fBsubmit\fR, so the capability
was not much used.
.PP
The latest version of the local channel has kept
the alias-file-originated delivery to files
and pipes, and the changes Steve Kille
has made to the \fBsubmit\fR program have also made this facility reliable
from a security standpoint.
The rcvmail mechanism has been totally scrapped in favor of a
more general and powerful mechanism which I will call
``mail delivery files''.
.PP
Mail delivery files were designed to give the user as much flexibility
over how his mail was delivered as possible without opening security holes.
The mail system was changed to allow local addresses to have a
suffix appended which consists of an `=' and any simple text;
this suffix is totally ignored except when using mail delivery files.
Each user may create a ``.maildelivery'' file in his home
directory which contains one or more delivery specifications.
A delivery specification has five parts, separated by field separators, 
\fI<FS>\fR, which may be tabs, spaces or commas ``,''.
.sp .5
.ti .6i
\fIfield\ \ <FS>\ \ pattern\ \ <FS>\ \ action\ \ <FS>\ \ A/R/\?\fR\ \ <FS>\ \ ``\fIoptional string\fR''
.sp .5
The \fIfield\fR is the name of a field that is to be searched for a pattern.
Currently supported fields are \fIFrom\fR, \fITo\fR, \fISubject\fR, 
\fISender\R, and \fICc\fR, plus three special fields, \fIaddr\fR, \fI*\fR,
and \fI*\fR.
The \fIaddr\fR field is used to match against the address being delivered to
\fIincluding\fR the suffix (e.g. ``dpk=unixwizards'').
If the user subscribes to different lists with different suffixes
he can use his mail delivery file to segregate his mail by source.
To do this based on the message text alone is impossible to
do right 100% of the time.
The \fIdefault\fR field matches if the message has not been delivered
by any of the preceding lines in the ``.maildelivery'' file.
The \fI*\fR field always matches, regardless of any other action.
.PP
The \fIpattern\fR is some sequence of characters that may be matched
in the \fIfield\R.  Case is not significant, and multiple fields of the same
name are concatenated, separated by spaces.  If the field does not need as
pattern, a dash (-) or similar symbol is usually inserted to show that the
field is present but not used.
.PP
The \fIaction\fR is ``file'' or ``>'', ``pipe'' or ``|'', or ``destroy''.
``File'' or ``>'' appends the message in standard mailbox format to the
file specified in the optional string.
``Pipe'' or ``|''  causes the
program in the optional string to be run with the message available on
the standard input.
``Destroy'' causes the mail to be thrown
away silently.  This is useful if you go away on a long trip and don't
want to unsubscribe to lists, but also don't want to come home to
several thousand messages.
.PP
The \fIA/R/?\fR flag is a single character: `A' for accept, `R' for reject,
or `?' for accept if not delivered yet.  This flag indicates whether the action,
if successful, is sufficient to mark the message as delivered.
If the message is undelivered at the end of the .maildelivery file,
the local channel next consults a system-wide file, such as
\fI/usr/lib/maildevliery\fR.  If the message is still undelivered at the end of
the system-wide file, a standard delivery is made to the default mailbox.  
This protects against
mail being lost due to lack of foresight or errors in the maildelivery files.
.PP
The file is always read completely so that several matches
can be made, and several actions taken.
For example, the user could have a TTY alert
message sent to his terminal and also have the message resent to his new
home machine by the following .maildelivery file:
.sp .5
.in +.6i
addr\ \ \ \ dpk\ \ \ \ pipe\ \ \ R\ \ ``/usr/mmdf/mailutils/ttyalert''
.br
addr\ \ \ \ dpk\ \ \ \ pipe\ \ \ A\ \ ``/usr/brl/bin/resend dpk@brl-vgr.arpa''
.in -.6i
.sp .5
The last line, if completed without error (a return code of 0 from resend),
would mark the message as delivered because of the A (accept) flag in the
fourthcolumn.
endmail tailoring file, and can be even smaller, even on a large relay site.
The runtime configuration file is one of the most useful additions
in MMDFII, especially for sites supporting more than one host,
since one can now run the same binaries on all machines of the same type.
Another Berkeley-inspired facility was the ability to have an alimmdf/doc/review/p7   444      0     12        7126  3620510340   7267 .SH
The List Channel
.PP
The list channel was developed as the result of BRL's experience
in managing \fBlarge\fR Internet mailing lists.
Two major problems were discovered with dealing with mailing lists
in MMDFI.  First, since \fBsubmit\fR always verifies all known addresses
for a message at submission time, if you were on the machine with
a large list and submitted mail to the list you would have to wait
for every address on that list to be verified.
On a busy machine
with a list of hundreds of addresses, this could take
five or ten minutes.
Annoying as this may be for people,
the situation was worse for mail submitted over communications
channels like TCP/IP.  The remote end would continually time out
before the message had been completely verified so the message
could never be sent.
.PP
The second problem with large lists was that there were always
rejected mail notices going back to those
least able to do anything about the mail problem, the original
sender of the message.  What was really desired was a method to
try to have returned mail go to the list maintainer instead
of the message's original sender.
.PP
The solution to both of the problems is embodied in the list channel.
This is a channel with an incestuous relationship
to \fBsubmit\fR, \fBdeliver\fR,
and the alias file.
Use of the list channel is best described in parallel with the
special entries for the list channel in the alias file.
If we were maintaining a large list called ``biglist'', the following
entries would be in the alias file:
.nf
.ta 2.6i
.sp .5
.in +.6i
biglist:	biglist-outbound@list-processor
.br
biglist-outbound:	</usr/mmdf/lists/biglist-file
.br
biglist-request:	maintainer
.in -.6i
.fi
.sp .5
The pseudo-host ``list-processor'' has its own domain table and its
own host table but represents no actual host.  If someone submits
mail to biglist, \fBsubmit\fR will find the alias entry and
upon finding that it's
not a local address, will queue it to the host ``list-processor'',
so verification is complete after only one lookup.
Unknown to
\fBdeliver\fR, the list channel simply calls \fBsubmit\fR and feeds it the
aliased addresses, ``biglist-outbound''.
This time the actual verification is done on the contents of
the address list ``biglist-file''.  Since the list channel is processed
by a background daemon, no one is forced to wait through the
verification process except the background daemon itself, which doesn't care
how long it takes so long as it completes.
.PP
The list channel also performs another function to try to
eliminate the problem of failed mail messages.
For each address given to it, (normally just one), the list channel
sees if there is a matching ``\fIlistname\fR-request'' entry in the alias
table.  It knows enough to try stripping any ``\-outbound''s from
the name first though.  If a ``\-request'' entry is found, then
that address is substituted instead of the original return
address.  The message text is \fInot\fR altered, but the new return address
is recorded for use when resending the message.
The new return address
is supplied in SMTP ``MAIL FROM:<\fIaddress\fR>'' commands and any other
situations where the return address is directly specifiable.
.PP
The changing of the return address is useful only if mail is rejected
when submitted to the foreign host or if that host is smart enough to keep
the return address information around.  Many hosts do not maintain this
information,
and many of the same hosts are also problematic in that they
will completely accept a message containing total garbage and
decide to tell you about it later.  This is precisely what MMDF tries
to avoid by submission-time verification.
nce
in managing \fBlarge\fR Internet mailing lists.
Two major problems were discovered with dealing with mailing lists
in MMDFI.  First, since \fBsubmit\fR always verifies all known addresses
for a message at submission time, if you were on the machine with
a large list and submitted mail to the list you would have to wait
for every address on that list to be verified.
On a busy machine
with a list of hundreds of addressesmmdf/doc/review/p8   444      0     12        7555  3620510340   7276 .SH
The BBOARDS Channel
.PP
Sites that run the MH message system, version mh.5, may install
a \fIbboards\fR channel which delivers messages 
from interest-group mailing lists to a special ``bboards'' directory  .
The bboards software,
which is compatible with the MH message system, keeps track of which
messages have been seen by individual users, and allows designated bboards
managers to control the size and access for different bboards.
.FN
See the \fIman\fR entry mh-gen.8 in the MH distribution.
It is important to note that the choice to install ``bboards'' 
must be made when MMDF is generated.  The ``news'' facility mentioned
in the MH documentation is not supported by CSNET.
.FE
.SH
The UUCP Channel
.PP
The task of integrating UUCP mail into MMDF was a prime
goal for BRL.
Our users would not tolerate having to use
two radically different mail interfaces for two different
kinds of mail connections.
We decided to write a channel to interface to the UUCP
mail world that would take care of the necessary format
conversions to allow mail to traverse the two mail worlds.
The channel has two parts.  The input portion of the channel
is the program \fB/bin/rmail\fR which is executed by the UUCP
program \fBuuxqt\fR when mail is being delivered.
The output portion is a standard channel that invokes the UUCP
system after reformatting the message.
.PP
The \fBrmail\fR
program has been totally rewritten to interface to MMDF.
\fBRmail\fR's primary task is to collect and reformat the
address strings in the message.
To reformat each address, \fBrmail\fR uses the UUCP channel table
to determine what hosts are known to this host and shortens
an host!host!host! string down to the single most distant host we know
about and any subsequent hosts we do not know.  For example
knownA!knownB!knownC!unknown1!unknown2!user would become
knownC!unknown1!unknown2!user.
It then converts this to an RFC822 style address by putting the unknown
hosts and the user in the local part and putting the known host with
a domain in the domain portion, e.g. unknown1!unknown2!user@knownC.UUCP.
If all the hosts are known, then only the user
is left in the local part, and the address winds up being user@known.UUCP.
Since you always know the hosts you talk to, you can build any arbitrary
UUCP path by simply saying arbitrary-host-path@neighbor.UUCP.
.PP
\fBRmail\fR is prepared to accept destination addresses in two forms.
If the addressee is just another UUCP host addressed using host!host!...
notation, then \fBrmail\fR forwards the letter via UUCP without header munging
since the destination host may not support RFC822 style mail.
An addressee of the form user@domain will
cause the message to be fed to \fBsubmit\fR and into MMDF proper
where the message can be delivered to another UUCP site or any other site
accessible via MMDF.
\fBRmail\fR will reformat the message header in the latter case to conform
as much as possible with the RFC822 specifications.
.PP
The outbound portion of the UUCP channel is a MMDF channel program called
``uucp'' which is invoked by \fBdeliver\fR.
The job of this program is much
easier since all it must do is reformat the ``From:'' line to be compatible
with UUCP mail.
The outbound channel must also reformat the destination addresses
which become arguments to \fBuux\fR.
The outbound channel uses the same channel table that
\fBrmail\fR used but performs the reverse action on the address so,
for example, root@mcnc.UUCP becomes unc!duke!mcnc!root and this is
then further divided to form the uux command
``uux unc!rmail duke!mcnc!root'' (assuming the channel table maps
mcnc.UUCP into duke!unc!mcnc).
.PP
The UUCP channel would have to be classed as the only ``flakey''
portion of MMDF since some of these address transformation really need
an advanced AI system to make an intelligent transformation.
In general, though, the channel does a very good job and has little trouble
with ``normal'' UUCP addresses.
 different
kinds of mail connections.
We decided to write a channel to interface to the UUCP
mail world that would take care of the necessary formammdf/doc/review/p9   444      0     12        7337  3620510340   7275 .SH
Using Domain Name Servers
.PP
The use of domain name servers
will have some interesting effects
on the address verification aspects of mail submission.
In the current system, all the information
necessary for verification of addresses is in a local
data base.
When we are using name servers, we can no longer be guaranteed
that all the needed information will be locally cached.
In addition, we are not guaranteed that we will be able to
reach all the necessary name servers at submission time (although
duplicate name servers will make this possibility small).
The \fBsubmit\fR program will call the local domain resolver to
verify each address, and there will be some time limit in which to
complete this task.
The resolver will be expected to first
consult the local cache of domain data
and, if the information is not found,
contact as many servers as
necessary to resolve the address.
.PP
The possible lack of information will force us to provide a
contingent submission queue for those messages that
cannot be verified at submission time.
This does not imply that there will we be no verification.
We will verify that we at least know the top level domain
of each address and verify each sub-domain when possible.
If some sub-domain of the full address is known to be bogus, the
address can be flushed.
Knowing that we have authoritative information that a domain
does not exist is just as important as knowing that it does exist.
.PP
A new channel, much like the list channel, will be used to process
the partially-accepted address for a message.
This channel will continually try to verify the address until
it is known to be good or bad.  It will have the message
returned to the sender, with an explanation, if one or more addresses
is bad.
Most systems will run with a fairly rich cache of host information.
For those systems which cannot afford to keep this information
around, the submission time verification might be a considerable
delay which would be unacceptable for a user interface.
On these systems it will possible to force all message to be accepted for
background verification (via the "verification" channel).
.SH
Conclusion
.PP
MMDFII had some early problems and as a result may have gotten
some initial bad press, but MMDFII has shown that it is
a capable mail system which is both robust and able to handle
very large mail loads.
There now exist a growing number of tools to analyze and manage
large flows of mail in a MMDF system.  These tools include status
programs, sophisticated logging, and log analysis programs.
Because of the separation of mail into separate queues,
multiprocessing of the mail queues is not only possible, but
routinely used to both increase throughput and decrease
delays.
MMDF is also a flexible system.
Runtime reconfiguration is simple, generally easy to understand,
and can be done at any time.
Since the MMDF core software is free of channel specific or
network specific information, one can easily add additional channels
for new networks or protocols without affecting the existing
software.
MMDFII represents a stable, production mail system,
providing a strong base for the development of
new network interconnections and mail handling environments
which are essential in today's distributed computing environment.
.sp 2
.PP
The MMDFII software is available under license,
free of charge
(with the possible exception of a tape copy fee),
for internal use only as follows:
to U.S. Government agencies through the Ballistic Research Labs,
to CSNET sites through the CSNET Coordination and Information Center at BBN,
and to others through Prof. David Farber at the University
of Delaware, Electrical Engineering and
Computer Science Department.
Commercial concerns interested in MMDF for other than internal
use should contact Prof. Farber.
he full address is known to be bogus, the
address can be flushed.
Knowing that we have authoritative information that a domain
does not exist is just as important as knowing that it does exist.
.PP
A new channel, much like the list channel, will be used to process
the partially-accepted ammdf/doc/review/fnmacro   444      0     12          75  3620510341  10323 .nr Fn 0 +1
.de FN
.ps -2
\u\\n+(Fn\d\ 
.ps +2
.FS \\n(Fn
..
ng of the mail queues is not only possible, but
routinely used to both increase throughput and decrease
delays.
MMDF is also a flexible system.
Runtime reconfiguration is simple, generally easy to understand,
and can be done at any time.
Since the MMDF core software is free of channel specific or
network specific information, one can easily add additional channels
for new networks or protocols without affecting the existing
software.
MMDFII represmmdf/doc/administrators/   755      0     12           0  3671074607  10510 mmdf/doc/administrators/building   444      0     12      134447  3656410704  12363 .\".AU
.\"Douglas P. Kingston III
.\".AI
.\"Ballistic Research Laboratory
.\"USA AMCCOM
.\"Aberdeen Proving Ground, Maryland.  21005
.\"(301) 278-6651
.NH 1
Building the \*M System
.NH 2
Organization of Source
.PP
The \*M sources are arranged in a hierarchical directory
structure under a directory such as /usr/src/local/mmdf.
The major directories at the top level are:
.IP \fBconf\fR 10
The \fBconf\fR directory contains subdirectories for each of several sample
sites, for example, csnet-relay or okstate.  Each of these subdirectories
contains most of the files necessary for compile-time
configuration of the \*M system.
.IP \fBdoc\fR 10
The \fBdoc\fR directory contains the NROFF/TROFF source for all \*M documents
except manual pages.
.IP \fBh\fR 10
Include files used by MMDFII.
.IP \fBlib\fR 10
The \fBlib\fR directory contains the source to the \*M library
(libmmdf.a) which is used in compiling all the other \*M programs.
There are five subdirectories in the \fBlib\fR directory: \fBaddr\fR,
\fBdial\fR, \fBmmdf\fR, \fBtable\fR, and \fButil\fR.
.IP \fBlib/addr\fR 15
contains the address parser.
.IP \fBlib/dial\fR 15
contains the PhoneNet dial in/out package.
.IP \fBlib/mmdf\fR 15
contains \*M specific shared routines.
.IP \fBlib/table\fR 15
contains the table lookup functions.
.IP \fBlib/util\fR 15
contains a variety of utility and system interface routines.
.IP \fBlibndir\fR 10
The ``new'' directory access routines for non-Berkeley Unix systems.
This directory may not be included on some distributions (e.g. 4.3BSD
where it is standard).
.IP \fBman\fR 10
The \fBman\fR directory contains the source for all the \*M manual pages.
.IP \fBmh\fR 10
The sources for the MH system. (Not discussed here; see the online
documentation.  May not be included in all distributions.)
.IP \fBsamples\fR 10
The \fRsamples\fR directory contains subdirectories for each of several 
sample sites.  Each site typically contains the runtime-tailoring file
(mmdftailor) and other files such as tables, dialling scripts and 
helpful shell scripts.  Large tables will have been truncated
to a representative set of entries.
.IP \fBsrc\fR 10
The \fBsrc\fR directory contains the source to all \*M programs
except for the mail posting and mail reading user agent programs
(e.g. \fImsg\fR, \fIsend\fR, and \fIv6mail\fR).  Within this directory is 
a subdirectory
for each major program or program set, and one directory called
\fBtools\fR for
miscellaneous programs.
.IP \fBtestmmdf\fR 10
The \fBtestmmdf\fR directory is a complete \*M operational subtree that
can be used to install a test version of \*M in parallel with
another mail system (even another \*M) on the same system.
It also serves as a model of what
the \*M subtree should look like.
.IP \fBuip\fR 10
The \fBuip\fR directory contains user interface routines, primarily \fImsg\fR
and \fIsend\fR.  The subdirectory \fBother\fR contains a number of other user
programs for dealing with mail.
The subdirectory \fBucbmail\fR contains a recent version of Berkeley's
Mail program.  It has been modified to interface well to \*M.
.PP
The source directories are set up to use a hierarchical set of Makefiles.
The directories \fBlib, src\fR, and \fBuip\fR contain master Makefiles.
In each subdirectory is a file called Makefile.real and a program
(shellfile) called
.I gen.
The                                            
.I gen
program calls
.I make
with two parts, Makefile.real and a ``common makefile'' called
Makefile.com that is contained in a parent directory.
Makefile.com contains configuration specific information such
as compiler flags, installation directory names, #ifdef flags,
and the dependency generation code (for ``make depend'').
.PP
There is a strict convention for the format of #include lines
in \*M source files.  If you fail to follow the convention,
you will break ``make depend'' and thereby your makefile
dependencies.
The format is as follows:
.sp
.ne 10
#include <file.h>
.br
.ti 10
For standard include files in /usr/include
.br
#include <subdir/file.h>
.br
.ti 10
For files in subdirectories of /usr/include.
.br
#include ``file.h''
.br
.ti 10
For files in the \*M header file directory (typically ../../h).
.br
#include ``/fullpath/file.h''
.br
.ti 10
For specifying a particular header file by full path name.
.br
#include ``./file.h''
.br
.ti 10
For a file in the current directory.
.PP
There is a problem with the current ``make depend'' methodology
on systems that do not have compiler support for it.
Currently only 4.3BSD has this support through the ``\-M''
option to
.I cc.
As a result, there are two version of the ``depend'' actions
in Makefile.com.
On systems that do not have
.I cc
\-M,
due to the way the dependencies are generated, there is no
preliminary processing of the conditional compile lines.  Files
that conditionally include either one or another file appear
to the ``make depend'' code as though they included both files.
This problem does not show up until you are running the
.I make 
program for the first time.
.I Make
will complain about not being able to make
some header file that does not exist on your system.
The remedy is simply to edit the makefile that broke and 
delete the line containing the file that doesn't exist.
You can then run 
.I make
again.  
.PP
We regret that this process is necessary, but at least
it only has to be done once per site.
Luckily there are only a few files (maybe 5) that are affected.
Look out for the following problem files:  termio.h, ioctl.h,
and fcntl.h.
.NH 2
Getting Ready
.PP
\*M requires a significant amount of space for the
sources, documentation, and binaries
(about 8 to 10 megabytes for a full distribution).
The 4.3BSD distribution will take about 3 megabytes for the basic
sources, and documentation.
The running system will consume much less, about 2 megabytes of
installed binaries and tables, and you can remove your old mail programs.
You will need superuser privileges.
The distribution you receive may inadvertently contain old binaries
or libraries despite the best efforts of those preparing the distribution.
It is recommended that you do a ``make clean'' in the
directories
.B lib,
.B src,
and
.B uip
before starting.
You can accomplish this by running ``make clean'' from the top directory of
the MMDF source tree (the parent of \fBlib\fR, \fBsrc\fR, and \fBuip\fR).
This will give
you a clean slate upon which to base your compile.
.XX
Do a ``make clean'' in the MMDF root directory.
.NH 2
System Compile-Time Configuration and Generation
.PP
The configuration directory, 
.B conf,
contains one directory for
each site being supported that has compiled-in differences.
Many sites may share one compile-time configuration by using
the run-time tailoring facility to specify site-specific information.
There is also a shell program called
.I sitesetup
that is used to install a specified configuration.
The test configuration is set up to run out of the \fBtestmmdf\fR
subdirectory and should only be used for testing.
You will base your running configuration 
on an existing
system with possibly extensive changes.
The following site configurations are available for copying:
.sp
.IP bbn 12
A 4.2BSD Vax Internet host using domain servers for table lookup.
.IP 4.3vax 12
A 4.3BSD Vax running a minimal set of tables and using the
badusers and badhosts channels to pass off unknown addresses
to MCVAX.UUCP (haring.uucp).
.IP csnet-relay 12
A very large PhoneNet relay host with Internet access.
.IP okstate 12
A V7 Perkin-Elmer host on CSNET PhoneNet, UUCP and a local network.
.IP uclvax2 12
An Internet host with links to various UK networks; illustrates
authorization on a per-user basis.
.IP vgr 12
A 4.2BSD Internet host.
.PP
These configurations are supplied as examples and templates for building
your own configuration.
Do not modify the contents of any of these configurations.
You will make your own site's directory in the \fBconf\fR directory.
.XX
Make a directory named after your site in the \fBconf\fR directory.
.XX
Copy the contents of some other system's directory into your directory.
.NH 2
Compile-Time Tailoring conf/\fIsite\fB/conf.h
.XX
Edit the file conf.h
.PP
The constants at the top of this file should not need tailoring.
Possible exceptions might be NUMCHANS, NUMTABLES, and NUMDOMAINS.
These limit the number of domains, channels, and
tables (channel, domain, alias, and others),
The numbers supplied should only
need to be increased on a site acting as a major relay site
with a large number of channels.
The system will complain in the logfiles if the limits are reached.
The ``DL_'' definitions control certain basic
properties of the dial package used by phone and slave programs
for PhoneNet.
They should probably not be modified.
If the dial package is not used, they are ignored.
.PP
The section in conf.h entitled ``JNTMAIL Tailoring'' contains #defines
that are useful
primarily in the United Kingdom and gateways to it, like UCL-CS.
The JNTMAIL definition is used to enable code that is used on the JNT
mail channel.  The BOTHEND definition enables \*M to parse domain
names in either ``little-endian'' or ``big-endian''
format.  ``Little-endian'' is the
standard format in the DARPA Internet and is the format defined in
RFC-822.  ``Big-endian'' is used in the UK community, and perhaps elsewhere.
VIATRACE causes the relaying timestamp to conform to
UK standards by starting with ``Via:'' instead
of the Internet standard ``Received:''.
Despite the best efforts of some British, this remnant remains
from the days of RFC733.
Non-JNT systems will leave JNTMAIL, BOTHEND
and VIATRACE undefined.
.PP
The next section of conf.h contains the #include lines necessary to include
the file time.h and dir.h (as is used with the libndir package).
The former is typically /usr/include/time.h or /usr/include/sys/time.h
(a.k.a. <time.h> or <sys/time.h>).
The latter
may be called ndir.h on some systems.  This is typically
/usr/include/dir.h or /usr/include/ndir.h (a.k.a. <dir.h> or <ndir.h>).
.XX
Check where your time.h and dir.h are and fix the #includes, if necessary.
.PP
The final section of conf.h is used to define a macro that will identify
privileged UIDs that are likely to be submitting mail on behalf of other.
We have defined it as a parameterized
macro in the distribution, but you could undefine the macro
and write a real function called PRIV_USER() if necessary.
When used as a macro, it is assumed that the global variable
.I effecid
will contain the UID of submit.  Currently only submit uses
this macro/function.
The uid for \*M (effecid) is determined at runtime.
.XX
Check that the #define has reasonable UID values (root, daemon?, and effecid).
.NH 2
Compile-Time Tailoring conf/\fIsite\fB/conf.c
.XX
Edit the file conf.c
.NH 3
Public Names
.PP
The first section is PUBLIC NAMES.  The string
.I mmtailor
is the location of your runtime tailoring file.  This is typically
.I /usr/mmdf/mmdftailor.
.PP
The
.I locname
string should be set to your host name without the domain portion.
For example if your host is SPOOK.BRL.MIL, then your locname
should be ``SPOOK'', and your
.I locdomain
should be ``BRL.MIL''.
Case is not important, but it will be preserved in some cases, so
enter case as you would like to see it.  We prefer to use all caps.
.I Sitesignature
should not be altered as it is an indication of which mail system you
are running.
.I Mmdflogin
is the login id that you have chosen to give to the \*M mail system.
This is usually ``mmdf''.
The
.I supportaddr
string gives the address that the mailsystem will send problem
notifications to, and this is also the address that will be placed in
the from field of messages generated by the mail system.  This address
should reach the person or persons maintaining the mail system.
Normally, it is defined as ``Postmaster@\fIlocname.locdomain\fR'', which is, in 
turn, an alias for the appropriate systems people.
.XX
Configure \fIlocname, locdomain, mmdflogin\fR, and \fIsupportaddr\fR.
.NH 3
\*M Directories Locations
.PP
The next section, DEFAULT BASE DIRECTORIES, defines the location of 
certain \*M directories.
The comments are fairly clear about the use of most of them.
The locking directory will only be used if the link based locking
mechanism is used.  If it is, you should make sure this direcory
is cleared at each reboot.
.I Tbldbm
is not actually a directory but is instead the basename of the
DBM style database used by \*M to store tables for fast lookup.
See the manual section \fIdbm(3)\fR for more information.  Systems using
System III or System V will have to obtain this code from another
Unix site, preferably a 4.2BSD site.
The original V7 version of the DBM library only provides
for the files to be opened read/write.
If you have this version you will need
to modify it to try opening the files readonly if read/write
access fails.
The typical values are as follows:
.sp
.in +1i
.KS
.TS
l l .
\fIcmddfldir\fR	/usr/mmdf
\fIlogdfldir\fR	/usr/mmdf/log
\fIphsdfldir\fR	/usr/mmdf/log/phase
\fItbldfldir\fR	/usr/mmdf/table
\fItbldbm\fR	/usr/mmdf/table/mmdfdbm
\fIquedfldir\fR	/usr/mmdf/lock/home
\fIchndfldir\fR	/usr/mmdf/chans
\fIlckdfldir\fR	/tmp/mmdf
.TE
.KE
.sp
.in -1i
.XX
Configure directories, if necessary (unlikely).
.NH 3
Default Logging Parameters
.PP
The next section, DEFAULT LOG LOCATIONS & SETTINGS, controls the main
logging facilities of the \*M system.
There should not be any tailoring necessary here.
The
.I mlogloc
and
.I chlogloc
variables are somewhat magic, in that if they are simple file names,
then they are opened in the default logging directory (\fBlogdfldir\fR
above).  If they are full pathnames, then they will be opened as
specified.
They must exist for any logging to take place.  If they do not exist,
then no logging will take place.
Another possible tailorable item is the default logging level, which
is the third element of the ll_struct structure.  See the explanation
of the ll_struct structure in llog(3)
before altering.  This is normally tailored from
the runtime configuration file.
.XX
Adjust \fImlogloc\fR and \fIchlogloc\fR, if necessary.
.NH 3
Message Queue Parameters
.PP
The section MESSAGE QUEUE SUBSTRUCTURE
has only two tailorable entries,
.I warntime
and
.I failtime.
.I Warntime
is the minimum number of hours before \*M will send a
warning letter indicating that a message has not yet been delivered.
After
.I failtime
hours since submission, a message is eligible to be
returned as undeliverable.  Actual timing of warnings and returns will 
depend on how frequently the 
.I cleanque
program is run.
Neither of these actions will occur unless the program is run.
It is suggested that
.I warntime
be at least 1/2 day and more like 2 or
3 if you are connected to a large network (e.g. the Internet) where
there is a significant chance that a host will be down for a couple
of days on occasion.
.I Failtime
should be at least 3 days, especially on
large networks.  You should be able to take a machine being down over
a long weekend without returning mail.
.XX
Choose the \fIwarntime\fR and \fIfailtime\fR values (in hours).
.NH 3
Command Names and Locations 
.PP
The section COMMAND NAMES & LOCATIONS should only need tailoring if
you do change the names of the programs mentioned
(something you do at your own risk).
.NH 3
Submit Parameters
.PP
The value
.I maxhops
in the section SUBMIT TAILORING is the maximum number of times a message
may be relayed as indicated by the number of Received: lines before
the message is considered dead due to looping.  It is possible that
this value may have to be made significantly larger in the future
to handle mail that has passed to us through numerous RFC-822 UUCP hosts.
For now, we have used the value 20.
.PP
The \fIlnk_listsize\fR value
specifies the maximum number of addresses in a message
before it is considered to have a ``big'' list.
If there are more than the maximum number of addresses, then \*M
will not send a warning message for waiting mail and will only
return a ``citation'' for failed mail.  A ``citation'' consists
of the entire header plus the first few lines of the message
body.
.PP
The \fImgt_addid\fR will cause \fIsubmit\fR to guarantee the existence
of a Message-ID: line on every message entering the \*M system.
A Message-ID will only be added if it is missing.
.XX
Set the value of \fImaxhops\fR, \fIlnk_listsize\fR, and \fImgt_addid\fR.
.NH 3
Deliver Parameters
.PP
The section DELIVER TAILORING controls the deliver program.
.I Maxqueue
is the maximum number of messages that will be tolerated before deliver
stops sorting the mail queue before delivering.  If there are more than
.I maxqueue
messages in the channel's queue,
deliver will process the queue in the order it
is read which will probably not be the order in which messages
were queued.
A suggested value is 300.  This value can be larger if you do not
have any dial-in sites that might time out waiting for you to find a
message in pickup mode.
.I Mailsleep
is the number of seconds that the daemon sleeps between sweeps on the
mail queues looking for messages to deliver.  The value is typically
600 (10 minutes).
.XX
Set the values of \fImaxqueue\fR and \fImailsleep\fR.
.NH 3
Address Transformation
.PP
The section ADDRESS TRANSFORMATION should not be altered.  This section
was used to override the default header munging behavior.
It has been superceded by a more general mechanism, but this
table is kept in case the capability is needed again in some
special situation.  It cannot be runtime tailored.
.NH 3
Local Delivery Options
.PP
The next section, LOCAL DELIVERY TAILORING,
has several variables that can be tuned to local requirements.
The
.I dlvfile
variable is the name of the maildelivery control file (see the manual
section \fImaildelivery(5)\fR).  This should be ``.maildelivery''
to be standard and avoid confusion with the documentation.
.I Sysdlvfile
is the full path of the name of a maildelivery file
to be used if the user does not specify one.
If this is set to `(char *)0', then there will not be a default
maildelivery file.  This file must be owned by root and generally
unwritable.
.I Mldfldir
is used to control the location of default mailboxes.
If the variable is set to 0, the mailbox will be created in the user's
home directory.  If it is non-zero, it will be treated as the name
of a directory in which the mailbox name will be the same as the
username (old V7 style mail boxes).
.I Mldflfil
is the name to call the mailbox if the mailbox is going to be in the
user's home directory.
.I Mldfldir
and
.I mldflfil
have an inverse relationship.  Only one should be defined at a time,
the other should be zero.  We recommended that
.I mldfldir
be zero and that
.I mldflfil
be ``mailbox''.
Another common value for
.I mldflfil
is ``.mail''.
The strings
.I delim1
and
.I delim2
define the message prefix and message suffix respectively for mailbox
files.  These should probably not be altered as some user programs
may still have these values hardcoded to ``\\001\\001\\001\\001''.
If you are introducing MMDF for the first time at your site you will
not have this problem, but it is still unwise to change the delimiter
unless you have a very good reason.
.XX
Set the values of \fImldflfil\fR and \fImldfldir\fR.
.NH 3
UUCP Channel Parameters
.PP
The section UUCP CHANNEL TAILORING controls the behavior of the
.I rmail
and
UUCP channel
programs, the input side of the UUCP channel.
The things to tailor here are the strings
.I Uchan
and
.I Uuxstr.
.I Uchan
defines the default channel that rmail will indicate as
the source channel for incoming UUCP mail.
This should probably be ``uucp''.
The string
.I Uuxstr
controls UUX
invocation.
One or both of the strings in this part may be removed
in the future as the UUCP channel is modified to use more of the
basic channel configuring facilities.
.br
.XX
Adjust the strings \fIUuxstr\fR and \fIUchan\fR.
.NH 3
NIFTP Channel Parameters
.PP
The section headed NIFTP CHANNEL TAILORING is only for UK sites
running the JNTmail code.  Tailoring is self explanatory
(JNTmail spool location).
.NH 3
Multiple Host Feature Parameters
.PP
The variable
.I locmachine
in the section MULTIPLE HOST TAILORING must be changed to contain
your machine name.  This is the simple identifier that distinguishs
your host from others at your site.  This is used by code that
makes a collection of hosts look like one site.  This string
can also be set in the runtime configuration file for sites that
want to run common binaries.  This is normally the name of the host
minus the domain suffix (e.g. VGR for VGR.BRL.MIL, or
BRL-ORANGE for BRL-ORANGE.ARPA).
.XX
Set \fBlocmachine\fR to a string appropriate for your machine.
.NH 3
Authorization Parameters
.PP
The last section, AUTHORIZATION TAILORING, is tailoring for the
authorization code (`authorisation' for you folks in the UK).
The
.I authrequest
string is the address of someone who should get notices of
requests for authorization.
The
.I authfile
string is the file which should contain a politely worded warning
about usage of a restricted path.  The paper on authorization
in the \fIdoc\fR directory,
\fIConfiguring MMDF Authorization\fR by Steve Kille,
describes this facility in greater detail.  Typically
the address used for
.I authrequest
is simply the same as
.I supportaddr
in Section 2.5.1 of this paper.
.XX
Choose appropriate strings for \fIauthrequest\fR and \fIauthfile\fR.
.NH 2
Compile-Time Tailoring conf/\fIsite\fB/chan.c
.PP
The file chan.c is used to tailor certain channel and table
specific structures of \*M.
If you plan on using runtime configuration you can simply skip this
section since this need only be tailored by systems that plan
to hardcode in all channel and table information for speed or
size considerations.
.XX
Edit the file chan.c (if you are not using runtime configuration).
.PP
The variable
.I ch_dflnam
defined the name of the default channel to which mail is submitted.
In any normal system, this should be ``local''.
The definition for the head of the list of alias sources
follows ( Alias *al_list = (Alias *)0 ).
This should not be altered.
The following section defines two more tables (\fItb_mailids\fR and
\fItb_users\fR).  These are only used if the flag variable \fImid_enable\fR
is non-zero indicating that you wish to use the Mail-IDs feature
of \*M.  The Mail-IDs feature disassociates mail addresses from login names.
If Mail-IDs are disabled, you should still leave the table
definitions installed as they are referenced in other modules even
when not used.
.XX
Set \fImid_enable\fR to either 0 or 1 (1 enables Mail-IDs).
.PP
In the next section you should define and initialize channel, table, and
domain structures for all your channels if you are using a hardcoded
configuration.  This is not common and requires a reasonable
knowledge of C and structure initialization.  See the file h/ch.h
for a definition of the channel structures, h/tb* for table
structures,
and h/dm* for domain table
structures. Once these are defined and initialized,
proceed to add all your tables to the arrays
.I chsrch,
.I exsrch,
and
.I tblist.
You will also have to make sure you do not exceed the maximum
array sizes (NUMTABLES, NUMCHANS, and NUMDOMAINS).  (If necessary,
increase these values).  Finally count up the number of pointers
in each array and update the variables
.I tb_numtables,
.I ch_numchans,
and
.I dm_numtables.
.XX
Add pointers to your structures to the appropriate arrays.
.XX
Adjust the xx_numwhatits variables to indicate number in use.
.NH 2
Compile-Time Tailoring conf/\fIsite\fB/conf_dial.c
.XX
Edit the file conf_dial.c
.PP
This file is used to hardcode information for the dial-out package used 
by the PhoneNet channel (phone/slave).
You can ignore this file if you do not intend to use the PhoneNet code.
Although you can hardcode the information for ports (dial-out lines)
and lines (directly connected lines, no separate dialer),
the common practice
is to use runtime configuration.  If you want or need to hardcode,
see the d_*.h files for definitions of the contents of these structures.
As you add entries to the structures, be sure to update
the
.I d_num{prts,lines}
variables and don't exceed the definitions for
NUMPRTS and NUMLINES.  These maximums must be set even if you do
runtime configuration.
If you expect to need more ports or lines
than those #defines allow, even if doing runtime configuration, then
increase the size of these definitions appropriately.
.I D_calllog
is the file in which the dialing software will record call attempts
for billing or statistics purposes.  Choose an appropriate place.
Finally, the
.I d_dialprog
variable is the name of a program that will do dialing for your dial
out ports if you have such a program.  This variable is obsolete and
will likely be removed in the near future.
.XX
Check NUMPRTS and NUMLINES; adjust \fId_calllog\fR if necessary.
.PP
The section PHONE RELAYING TAILORING is for controlling the
behavior protocol layer of the dial package.
The arrays d_lrill and d_lxill have a bit for each of the 128 ASCII characters.
If the bit is set, then that character will never be sent
directly, but will be sent using a two character encoding in printable
ASCII.
These should not need altering unless your system is quite non-standard
and use of a certain character not already disabled causes your terminal
driver to not pass the character directly to the user.
.PP
The section PHONE LOGGING is similar to the logging section in conf.c
You may wish to adjust the path of \fIph_log\fR.  The same
is true of the string
.I def_trn.
.I def_trn
is a transcript file which is not size limited, but is recreated
at the beginning of each call.
.NH 2
Compile-Time Tailoring conf/\fIsite\fB/Makefile.com
.PP
.XX
Set HOST to match your configuration directory name
.PP
The SYSTEM definition is used to select the appropriate
versions of several files that are system type dependent.
Currently, we support SYSTEM types of `4.1', `4.2', `v7', and `5.2',
corresponding to Version 7, 4.1BSD, 4.2BSD and System 5 Release 2
respectively.
For 4.3BSD, use `4.2' (you will need to compile the inetd
version of smtpd manually if you don't want the standalone daemon).
Most (if not all) of the systems in current use can be
fit into one of these four types.
.XX
Set SYSTEM to the type for your system.
.PP
MMPREF is simply the string to be appended to the front of every
command when installed.  This is a seldom-used kludge retained for
system testing.  It should be blank.
The definitions of LIBDIR, CHANDIR, and TBLDIR must coincide with
the variables \fIcmddfldir\fR, \fIchndfldir\fR, and \fItbldfldir\fR
in conf.c (tailored above).
Typically these are /usr/mmdf, /usr/mmdf/chans, and /usr/mmdf/table
respectively.
BINDIR is the directory into which you wish to install the user interface
programs.  Typically, this is /usr/bin or /usr/local/bin.
On BRL Unix systems
this is /usr/brl/bin.
RCVDIR is the directory into which
you wish to install the various ``rcvmail''
programs.  These are automatic mail receiving programs that
are activated by the user's installation of a .maildelivery file to control
the delivery of mail to files and pipes.
The directory should be generally accessible but write protected
for security.  A typical value is /usr/mmdf/rcvmail.
.XX
Set the definitions for MMDF directories.
.PP
The next section relates to access control.
CHOWN should be set to the full pathname of the
.I chown
program on your system.
Normally this is either /bin/chown or /etc/chown.
MMDFLOGIN is the name of the account created for use by
the mail system, and must agree with the variable \fImmdflogin\fR
in the file conf.c (in Section 2.5.1 of this paper).
ROOTLOGIN is the name of the superuser account on your system
(first line in /etc/passwd), typically `root'.
PGMPROT controls the default mode for executables.  It should be
no less restrictive than 511 and no more permissive than 775
if you have a systems group for maintenance activities.  We recommend 755.
.XX
Tailor CHOWN, MMDFLOGIN, ROOTLOGIN, and PGMPROT if necessary.
.PP
The CONFIGDEFS line contains a wealth of tailoring, some of which
may be peculiar to your system and not known to me.
The line is used to define C preprocessor
variables.  To define a C preprocessor variable, prepend "-D" to the variable
and list it on the CONFIGDEFS line.  For example, to define DEBUG level one,
you would add
-DDEBUG=1 to CONFIGDEFS.
CONFIGDEFS is used to build both the CFLAGS and LFLAGS option lines
(these lines should end with $(CONFIGDEFS)).
The available preprocessor variables are:
.IP DOASSIGN 15
Enables the d_assign code in the dial package.  This code calls the program
/bin/assign to gain exclusive access to a file.  It does not appear that 
the d_assign code is ever used so don't bother defining DOASSIGN.
.IP SECURETTY 15
Many sites are running their systems with TTYs in more secure mode
than generally writable.  Usually these systems use the execute bit
to indicate write permission and have a privileged program make the
access.  If you are such a site, you will want to investigate
the effect of SECURETTY and modify it if necessary.  Vanilla sites
should not define SECURETTY.  BRL VAX UNIX sites must define SECURETTY.
.IP DEBUG 15
Most sites should enable DEBUG=1 unless there is a serious
crunch for space.  This will give you fairly detailed debugging of
the system if you need it.
Setting DEBUG=2 will include even more debugging for address parser
and reformatting code.
If you do not enable DEBUG=1, you will seriously
affect your ability to trace problems later.
Once you have your system up an running reliably you can recompile it
without DEBUG of either kind if you want the space and the very minor
performance increase.
.IP D_LOG 15
The same caution applies for D_LOG as for DEBUG.  This variable controls
logging in the dial package.
If you are tight on space or
if you don't use the dial package (no phone or
pobox channels) you may safely omit D_LOG.
.IP D_DBGLOG 15
This controls more debug logging for the dial package.
Again, if you have the
space and if you use the dial package (PhoneNet),
define it to allow extensive tracing if
problems arise later.
.IP RUNALONE 15
This makes the channel programs operate independently from \fIdeliver\fR.  It
hasn't been used for years and requires reading the code to understand its
effect.  Do NOT define RUNALONE! (it is used for debugging)
.IP V4_2BSD 15
Enables code that is specific for 4.2 BSD .  All 4.2 BSD sites must define this
variable.  This should be used by 4.3 BSD sites as well.
4.3 BSD sites will also want the new src/smtp/smtpd.4.3.c for use with INETD.
They will have to compile this manually for now (or copy it to smtpd.4.2.c).
.IP NODIAL 15
Prevents the dial package (PhoneNet protocol) from being compiled.
This saves a fair amount of space and compile time.
Define NODIAL if you do not intend
to use the phone or pobox channels.
(You will also need to take `dial' out of Makefile.lib, see below.)
.IP SYS5 15
Enables code that does Bell System V tricks (probably also useful for System
III installations).  Note: if you are running System V, you must also append
-Drindex=strrchr and -Dindex=strchr to CFLAGS.
.IP ALTOS 15
Enables code that does ALTOS tricks.  Define this variable if you have an ALTOS
system.
.IP NODUP2 15
Define this variable if you don't have a dup2() system call or subroutine.
.IP NOFCNTL 15
If you defined NODUP2, then you should also define NOFCNTL if you don't have
the fcntl(x, F_DUPFD) system call either.
.IP NAMESERVER 15
Enables the nameserver lookup code for accessing domain servers.  The
NAMESERVER support is new.  You will be required to include either
tb_ns.fake.c or tb_ns.<sys>.c.  Currently there is only support for
4.2BSD networking.  See TB_NS below.
.IP UUCPLOCK 15
Uses UUCP/tip/cu-style locking for PhoneNet dial-out lines.  Define UUCPLOCK
if you intend to run UUCP (or tip/cu) and PhoneNet on the same dial-out line.
This causes \*M to use the LCK..tty?? locking of UUCP.
.IP NODOMLIT 15
Prevents Domain Literals (such as [10.0.0.59]) from appearing in addresses.
Since \*M doesn't currently handle Domain Literals properly, use of this option
is strongly advised.
.XX
Tailor the CONFIGDEFS line to your site's requirements and system type.
.PP
The CFLAGS line is the combination of the CONFIGDEFS given above
and any C compiler dependent options.
The basic boilerplate content is ``-O -I../../h''
which asks for the object code optimizer and specifies the path to
the header file directory.  If you do not have an optimizer or
it does not work properly, remove the ``-O''.
.XX
Tailor the CFLAGS line to your system's C compiler.
.PP
LINT is the pathname of your lint program, if you have one.  If not,
define LINT to be something innocuous like `echo'.
LFLAGS are the flags to LINT; tailor as you like.
This line should end with $(CONFIGDEFS) just like CFLAGS.
LDFLAGS are the flags you want to give to the loader (ld).
These may be site dependent; for example, a PDP-11 will require use
of split instruction and data space by using `-i'.
Do not include libraries in the LDFLAGS definition.
Do not tailor MMDFLIBS.
SYSLIBS is a list of libraries required from the system given
either explicitly or using the -l\fIlibname\fR notation.
Typically you will require at least -ldbm for the hashed database
library if you are using it.
Systems without the new Berkeley directory access routines will also need
to include -lndir or ../../libndir/libndir.a.
The ndir library is normally supplied in the sub-directory libndir in
the \*M distribution.
The AR definition is simply the path or name of the library archiver,
typically just `ar'.
.XX
Tailor LDFLAGS and SYSLIBS (and perhaps LINT, LFLAGS, and AR).
.PP
CH_TB selects one of two types of table lookup.  The two choices: use
linear search tables (slow) or the hashed database mechanism (fast).
In an independent survey,
99 out of 100
sites recommended fast over slow (the 100th was a masochist).
If you don't have the dbm library, get it from some other site.
It was distributed with V7 and all BSD systems.  It is FAST.
Set CH_TB to `ch_tbdbm' if you want to use a dbm hashed
database (recommended) or `ch_tbseq' if you want to use linear
searched tables.
.PP
The TB_NS variable indicates which nameserver module to use.
There are currently only
two choices, `fake' and `4.2'.  `fake' is appropriate
if you didn't specify -DNAMESERVER in CFLAGS above
or if your system is a non-BSD4.2 Unix.
.XX
Tailor TB_NS.
.PP
LOCALUTIL is for listing special modules needed based on
different configurations.
.IP \(bu
If you do not want runtime tailoring, include tai_fake.o.
Otherwise you should include tai_file.o.  The latter is highly
recommended (tai_file.o).
.IP \(bu
You must include the name of one of the several locking code
modules.
There are many systems with home grown locking mechanisms that
are much better than those provided with vanilla UNIX.
4.2BSD comes with a very good discretionary locking mechanism.
Examine the various lk_lock.*.c files in lib/util and choose
one that is appropriate for your system and include the name
of its object module here (substitute .o for .c).
If you have a type of locking available for which we do not have
a module, choose one that is close to what you need and modify
it accordingly.
If you have no locking mechanism, you should specify lk_lock.o.
This will include a locking package that uses links to lock files.
.PP
The last item to tailor here is which dependency generation
script to use.  The first one is for systems that do NOT
have the
.I cc
\-M flag.  It uses
.I grep
and
.I sed
to generate
the dependencies.  The second entry for ``depend:'' uses
the \-M option to
.I cc
to generate dependencies more accurately.
This is currently only usable with 4.3BSD systems.
.XX
Make sure the proper version for your
system is uncommented and comment out
the other version.
.NH 2
Compile-Time Tailoring conf/\fIsite\fB/Makefile.lib
.PP
There are only three things to be modified here: all defines at the top
of the file.
First, tailor MAKE.  If you need to use a special path for the make program,
then define MAKE to be that path, otherwise MAKE should just be `make'.
Second, tailor RANLIB.  Some systems (primarily System III and System V
sites) do not have the \fIranlib\fR archive table of contents program
and so must always create a library in the proper order for searching.
This is done with the
.I lorder
and
.I tsort
programs.  If you do not have
.I ranlib,
define RANLIB to be `libfix'.  Libfix is a shellfile
in the directory 
.B lib
that will do the proper things to build
a sorted \*M library.  If you are a site that has
.I ranlib,
then
simply define RANLIB to be `ranlib'.
Finally, if you do not plan to use the dial package (no PhoneNet)
then remove dial from the list of SUBDIRS (in this case
you should also have defined NODIAL in Makefile.com).
Normally SUBDIRS will be ``addr dial mmdf table util''.
.XX
Set MAKE, RANLIB, and SUBDIRS to appropriate strings.
.NH 2 
Compile-Time Tailoring conf/\fIsite\fB/Makefile.src
.PP
There are a couple of experimental or new channels that you may
want to use that are not on the line defining
SUBDIR, or it may be commented out (preceded by a #).
The standard entries are tools, submit, deliver, local,
list, delay, smtp, phone, pobox, uucp, and prog.
If you include the nameserver code you will need `delay', otherwise
it can be left out.
You may also want to remove references to
directories you know you are not going
to use.
This will save compile time, since you will not build things
you do not intend to use.  If you have specified -DNODIAL, you can safely
comment-out the phone and pobox directories.
.XX
Check the contents of SUBDIR and adjust if necessary.
.NH 2
Compile-Time Tailoring conf/\fIsite\fB/msgtailor.c
.PP
There are a quite a few things to tailor for the
.I msg
program.
The first is the number of messages supported in one mailbox.  This
is basically limited only by the host machine's address space.
The
.I msgtailor.c
file already has some typical values for 16 bit and 32 bit systems.
NMSGS is the maximum number of messages to be supported.  For 16 bit
systems, the maximum should be no more than 500, on 32 bit systems,
the upper limit is somewhat arbitrary, but does affect storage space.
We suggest 2000.
.I Savmsgfn
is the name of the place to save mail from the main mailbox (as
tailored in Section 2.5.7).  This is usually ``mbox''.
The string
.I resendprog
is a string that is fed to a subroutine of 
.I msg
to cause the resending
program (supplied as uip/other/resend.c) to be invoked.
It must begin with a `|' (pipe symbol) and
the remainder of the string must be the path to the resending program,
which normally gets installed in BINDIR (see Section 2.8).
It is possible to omit the full path but it is not recommended.
Note that a space character is required at the end of
.I resendprog.
The stand-alone section should be ignored.
.XX
Set up \fIsavemsgfn\fR and \fIresendprog\fR, and NMSGS if necessary.
.PP
The strings
.I dflshell
and
.I dfleditor
define the default shell for subprocesses and default editor
for two window answer mode (respectively).  Both can be
overridden by the normal shell variables (``SHELL'' and ``EDITOR'').
The default editor should be able to edit multiple files simultaneously
if invoked
with more than one file name.
If you have no editors with this feature you can probably concoct a
shell file to do it or make do with just invoking an editor (e.g., 
\fBex\fR or \fBvi\fR)
on a copy of the original message.
.XX
Set up the \fIdflshell\fR and \fIdfleditor\fR.
.PP
The following variables are basically self explanatory and control
the default behavior of such features as command completion, paging,
informative messages, and timezone constants.  If in doubt, leave the
settings alone.  Note that the time stuff need not be tailored
for System V systems.
For unusual time zones (perhaps all time zones), it is recommended
that you use the offset notation from Greenwich Mean Time.
For the US Eastern Time
zone this would be `{ ``-0500'', ``-0600'' };', and Middle European
Time would be `{ ``+0200'', ``+0100'' };'.
The normal BRL settings are given below:
.sp
.TS
l l l .
int	pagesize = 22;		/* default line count */
int	linelength = 80;	/* default line size */

int	paging = OFF;		/* Internal paging of long messages */
int	verbose = ON;		/* Command completion (raw mode) */
int	keystrip = ON;		/* Strip header lines on display */
int	bdots = ON;		/* Binary box dots */
int	mdots = ON;		/* Mailbox dots */
int	bprint = ON;		/* Reading... messages */
int	quicknflag = OFF;	/* Turn off NEW before message prints */
int	filoutflag = ON;	/* Filter text (display controls as ^X) */
int	binsavflag = ON;	/* Save binary box on exec's & pauses */
#ifndef SYSV
long	timezone = 5*60*60L;	/* Seconds West of GMT (Greenwich Mean Time) */
		/* (-1)*60*60L is Middle European Time. */
		/* 5*60*60L is U.S. Eastern Time. */
		/* 8*60*60L is U.S. Pacific Time. */
int	daylight = 1;		/* Do you use daylight savings time? */
		/* 0 = NO, 1 = YES */
char	*tzname[] = {"EST", "EDT",};
		/* Timezone strings, primary and daylight
		savings time if used, in that order */
#endif SYSV
.TE
.NH 2
Compile-Time Tailoring conf/\fIsite\fB/s_tailor.h
.PP
This file defines some of the operating characteristics of the 
\fIsend\fR program.
There are three #defines you can tailor in this file.
Your choice is to either #define or #undef each of the following.
SENTFILE causes a copy of each message sent to be placed in a file
called .sent in the user's home directory.  This option is obsoleted by
the new version of  \fIsend\fR; the user can now tailor this feature
by editing the .sendrc file in the user's home directory.
FNCYPRNT enables paginated printing of the message by the review
command.  If this is not defined, listing will occur without
pagination.  This is normally left undefined.
PWNAME, when defined, enables the code that will try to get the
user's real name from the gecos field of /etc/passwd if no .signature
file exists.
The code stops at the first comma and knows about the '&'
notation for abbreviating the login name.
If your site has gecos fields which contain other
information or the format is not compatible, do not #define PWNAME.
.XX
Check the settings of SENTFILE, FNCYPRNT, and PWNAME.
.NH 2
Compile-Time Tailoring conf/\fIsite\fB/s_tailor.c
.PP
This is the main tailoring file for the \fIsend\fR program.
There are three things to tailor here.
Set the initialization of the strings for the default
line and screen editors, and the default spelling checker.
.I Dfleditor
should be set to the local default line editor.
.I Dflveditor
should be set to the local default visual (screen) editor.
.I Dflchecker
should be set to the local default spelling checker.
If the names are simple names (e.g. "ed") your users will be able
to find versions in their search paths; if the names are full paths,
\fIsend\fR will always run the versions you specify unless overridden
by the user.
Users can always override these settings in their .sendrc files.
.XX
Set the strings \fIdfleditor\fR, \fIdflveditor\fR and \fIdflchecker\fR
for your site.
.NH 2
Other Compile Time Problems
.PP
If you are a 4.3BSD site or have access to the
Berkeley Inetd or another Internet super-server,
you will probably want the version of smtpd designed to
run under these super-servers.
For the time being, you will need to
move smtpd.4.2.c to smtpd.4.2.old
and install smtpd.4.3.c as smtpd.4.2.c in the
directory src/smtp so the makefiles can generate
the module you need.
In the future this difference will be more cleanly handled
in the configuration procedure.
.XX
If you have a super-server, install smtpd.4.3.c.
.NH 2
Compiling Your \*M System
.PP
You are now ready to generate your version of \*M.
You must first go to the conf directory and install your
site specific files by running the `sitesetup' shell program
located there.  Yoursitname is the name of the directory
you chose and should also match the HOST variable in the Makefile.com
you edited earlier.
Please do not try to run this shellscript with C-shell or
some other non-standard shell.
.XX
In conf, run `sh sitesetup yoursitename'.
.PP
If you have not run a ``make clean'' yet, you should do so now.
.PP
Next you should go to the top directory and run ``make depend''.
This will go recursively through \fBlib\fR, \fBsrc\fR and \fBuip\fR
generating the dependencies for your system.  Remember the comments
about this mechanism made at the beginning of this document.
.XX
In the top directory, run `make depend'.
.PP
The master makefile in the top of the \*M source tree
will attempt to recompile everything in the subdirectories
\fBlib\fR, \fBsrc\fR, and \fBuip\fR (in that order).  You can also run
make in each of these directories alone, although
\fImake\fR must be run in \fBlib\fR first.
.PP
If you want to run each of the make's separately,
change directory to the \fBlib\fR directory and run \fImake\fR.
This should result in the file lib/libmmdf.a several minutes
later (15-30 minutes, depending on machine speed).
Assuming libmmdf.a was generated
successfully, change directory to the \fBsrc\fR directory
and run \fImake\fR to build the core \*M programs (burn another 15-30
minutes).
Finally, go to the \fBuip\fR directory and run \fImake\fR there.
In all cases, libmmdf.a must be built before any other directories
are made or the objects will not load successfully (they need
the library!).
.PP
Remember that make depend will insert some bogus dependencies if
you do not have
.I cc
\-M, so make will stop a few times.  When it does
remove the offending bogus dependency and continue.  You will only
have to do this once.  Make depend should not have to be run again
unless significant source changes are made.
.XX
If you didn't run the make's separately
above, run `make' at the top of the \*M source tree
.NH 2
Handling Problems While Compiling
.PP
If you run into problems you can always interrupt the process,
correct the problem, and continue by calling \fImake\fR again
from the directories lib, src, or uip (or just running the master
makefile again from the top directory).
Remember that if you try to build any of the subdirectories of \fBlib\fR, 
\fBsrc\fR, or \fBuip\fR (e.g. \fBsrc/deliver\fR)
while in that directory, you must invoke \fImake\fR via the shellfile
\fIgen\fR in the subdirectory.  \fIGen\fR takes all the same arguments
as \fImake\fR but includes the file ../../Makefile.com along
with ./Makefile.real by using the \-f option of \fImake\fR.
.NH 2
Additional Makefile Features
.PP
The makefiles in \fBsrc\fR and \fBuip\fR also support
the command ``make dist''
which makes a tar-format file with all the executable binaries
and a shellfile called ``install.sh'' which, if run, will install all
the binaries and chown/chmod them appropriately.  A ``make dist'' should 
only be run after the system has been successfully built and (preferably)
tested on the current system.  This facility makes it much easier to
maintain multiple systems from a master source machine and greatly
simplifies distributing and installing new versions.
(This facility is a bit flakey.)
.PP
As mentioned before, all the Makefiles support ``make clean''
and ``make depend'' (or ``gen clean'' and ``gen depend'' as
appropriate to your current directory).
 to the
Berkeley Inetd or another Internet super-server,
you will probably want the version of smtpd designed to
run under these super-servers.
For the time being, you will need to
move smtpd.4.2.c to smtpd.4.2.old
anmmdf/doc/administrators/runtime   444      0     12      123510  3664425123  12236 .\".AU
.\"Julian Onions
.\".AI
.\"Department of Computer Science,
.\"The University,
.\"NOTTINGHAM,
.\"NG7 2RD.
.\"ENGLAND.
.\"(0602) 506101 ext 3595
.bp
.NH
Runtime Tailoring the \*M system
.NH 2
Introduction
.PP
The \*M Mail Transport system
can be run in one of two modes.
It can either have all the site dependent information
tailored at compile time,
or else it can be asked to read the information from an
ASCII human-readable file each time it starts up.
Obviously the latter provides much more scope for tweaking
various constants etc.
but does slow down the start-up time of the programs using
this information.
Most sites will want to run somewhere in the middle, with
most of the static information (bug address, directories,
filenames, etc.) compiled in, and the remainder configured
at runtime.  If you later have a need to change some
compiled in values, you can simply add an item to the runtime
tailoring file to override the compiled-in value and
thus avoid having to recompile the mail system.
There are obvious efficiencies to be had from a smaller configuration
file which makes it attractive to try to keep it small.
Typically one would runtime configure the host name and domain,
the support address, the tables, the channels, the domains, and the
logging levels.
.PP
Many people may be worried about the cost of runtime tailoring.
The method for reading in the tailoring file is very efficient,
involving only a single read system call.  The use of runtime
tailoring adds very little overhead to the \*M mail system.
The advantages of runtime tailoring generally far outweigh the
minor extra cost at startup.
.PP
Occasionally there will be a need to compile in the configuration
at a particular site (e.g. CSNET-RELAY) because the runtime tailoring file
becomes far too large.  There are two tools for helping to do this in
src/tools.
\fBFixtai\fR reads a specified runtime tailoring file and produces structure
initializations for those structures suitable
for being #included into chan.c.
\fBRem_chans\fR comments out specified channel entries in a runtime tailoring
file that you now expect to compile in.  You may wish to do this by
hand if you only have a few.
.PP
In general,
keywords in the tailoring file are case independent,
however such things as filenames and similar values,
obviously are not. The full specification for the tailoring
parsing can be found in manual section tai(3).
Strings are generally quoted to prevent them from being parsed
into separate words accidentally.  Simple words consisting of
only alpha-numeric characters can be safely left unquoted, but
all others strings should be quoted, especially those containing
spaces or punctuation.
.PP
This section describes tailoring through the external file,
but much of this information is applicable when using the
compiled-in tailoring.
The 
subsections below describe the specific entries which can be placed
in the tailoring file.
.PP
Examples are given for a number of sites
including most of those with directories in the \fBconf\fR directory.
See the directory \fBsamples\fR; subdirectories will contain actual
tables and tailoring files for these systems.
.PP
This whole process is perhaps easiest if you copy a tailor file
for some other system
and change the bits you need.
This example will show you common values that have been
used for parameters;
they are not necessarily what you want,
but they should give you a guide.
In some cases, there is more than one way to do something.
For example, setting up the list channel tables was done differently
at BRL and UCL.
This does not mean that one is wrong, just different.
One is perhaps more elegant, but both should work.
.XX
Choose a tailor file to copy.
.XX
Copy the chosen tailor file to the location you specified as
.I mmtailor
in the conf.c file (see section 2.5.1).
.XX
Edit your tailor file as described below.
.NH 2
Local Names and Site-Specific Information
.PP
The MLNAME is the name of your machine
or site as you wish it to
be known throughout the network.
This may be a generic host name used to hide a number of local
hosts.
If a generic host name,
internal hosts will be differentiated by MLOCMACHINE (described below).
.DS
.TS
l l .
MLNAME	``Maths''		# UK site
MLNAME	``Arpahost''		# ARPA site
.TE
.DE
Case should not matter here.
.PP
The MLDOMAIN line gives your full local domain.
This,
combined with the MLNAME (and possibly the MLOCMACHINE) will
give you the full network address.
.DS
.TS
l l .
MLDOMAIN	``Nott.AC.UK''	# UK site
MLDOMAIN	``ARPA''	# ARPA site
.TE
.DE
This will give as your address
.DS
.TS
l l .
Maths.Nott.AC.UK	# UK site
Arpahost.ARPA		# ARPA site
.TE
.DE
.PP
The MLOCMACHINE line is often set to the null string,
unless you are a large transparent site.
The normal use of this is for a site which has several machines
but wishes the machines themselves to appear as one address.
.LP
For example:
.br
Arpahost is, say, really a name that covers 5 machines,
mach1 to mach5.
Mail leaving any ``Arpahost'' machine
is stamped as originating from
Arpahost, and mail is addressed to username@Arpahost by outsiders.
The alias table then maps the incoming address to
.DS
.TS
l l .
username	username@mach1.Arpahost.ARPA
.TE
.DE
Take a look at the ucl-cs setup for an example of how such
things function in real life.
Remember that the mail system will really believe its name
is \fIlocname.locdomain\fR and any mail with that domain will
be verified locally.  If it finds a more specific domain such
as \fIlocmachine.locname.locdomain\fR it will pass it on to
the referenced host.
If defined, the
.I locmachine
string will never be generated in addresses by the system,
although it is recognized and can be supplied
by a user if it is important to
bypass the normal aliasing done by the system.
The
.I locmachine
string will be used in id strings (Received: and Message-ID:
header lines).
.DS
.TS
l l .
MLOCMACHINE	maths70
.TE
.DE
The MSIG line is the signature that MMDF will use when notifying senders
of mail delivery problems.  This should be
something that indicates a message agent was responsible.
.DS
.TS
l l .
MSIG	  ``MMDF Mail System''
.TE
.DE
MLOGIN is the user that owns the MMDF transport system.
.DS
.TS
l l .
MLOGIN	mmdf
.TE
.DE
MSUPPORT is the address to which to send mail that
MMDF can't cope with (i.e. that MMDF can't deliver or return
to sender).  Be sure MSUPPORT is a legal address!
.DS
.TS
l l l .
MSUPPORT	mmdf@Maths.Nott.AC.UK	# UK site
MSUPPORT	mmdf@Arpahost.ARPA	# ARPA site
		or perhaps
MSUPPORT	postmaster@Arpahost.ARPA
.TE
.DE
.PP
AUTHREQUEST is the address to which users should mail if they have questions
about why a message was not authorized for delivery.  It is also used as the
sender of authorization warning messages.  It is not used if authorization
is not enabled on some channel.
.DS
.TS
l l .
AUTHREQUEST	postmaster
.TE
.DE
The MMAILID line is used to enable the use of the the
Mail-IDs facility of \*M which disassociates mail addresses
from login names.  The line is of the form:
.DS
.TS
l l .
MMAILID	       \fInumber\fR
.TE
.DE
where \fInumber\fR is either 0 to disable the feature, or
1 (non-zero) to enable it.
If it is enabled, you should have the tables ``users'' and ``mailids''
with appropriate mapping from userids to mailids and vica-versa.
See section 5.4.4 of this document and tables(5) for more information on
setting up these tables. 
.NH 2
Directory Tailoring
.PP
Next, a whole heap of path names to directories can
be tailored.  The example that follows has the \*M file
under both /usr/spool and /usr/lib/mmdf.  This is the
way UCL arranges their \*M directories.  Also shown are
the values used at BRL where everything is placed under
under /usr/mmdf (see the samples directory for more examples).
.br
MLOGDIR is the default directory in which the log files are kept.
.DS
.TS
l l .
MLOGDIR	 ``/usr/spool/mmdflogs''
MLOGDIR	 ``/usr/mmdf/log''
.TE
.DE
MPHSDIR is the directory in which the timestamps for the
channels are made,
showing what state of activity they are in.
.DS
.TS
l l .
MPHSDIR	   ``/usr/spool/mmdflogs/phase''
MPHSDIR	   ``/usr/mmdf/log/phase''
.TE
.DE
MTBLDIR is the default directory for the table files.
.DS
.TS
l l .
MTBLDIR	   ``/usr/lib/mmdf/mmdftable''
MTBLDIR	   ``/usr/mmdf/table''
.TE
.DE
MQUEDIR is the parent directory for the various queues and
address directories.
.DS
.TS
l l .
MQUEDIR	   ``/usr/spool/mmdflock/que''
MQUEDIR	   ``/usr/mmdf/lock/home''
.TE
.DE
MLCKDIR is the directory where the locking of files takes
place,
this is dependent on what style of locking you are doing.
.DS
.TS
l l .
MLCKDIR	   ``/usr/spool/mmdf/locks''
MLCKDIR	   ``/tmp/mmdf''
.TE
.DE
MTEMPT is the temporary files directory;
if not a full pathname, it is taken relative to MQUEDIR.
.DS
.TS
l l .
MTEMPT	   ``temp/''
.TE
.DE
MADDRQ as above, but holds the address files.
.DS
.TS
l l .
MADDRQ	   ``addr/''
.TE
.DE
MMSGQ as above, used for the message texts.
.DS
.TS
l l .
MMSGQ	   ``msg/''
.TE
.DE
MCMDDIR is the default commands directory
where the various \*M commands live;
any command that does not have a full pathname
is taken relative to this directory.
.DS
.TS
l l .
MCMDDIR	   ``/usr/lib/mmdf''
MCMDDIR	   ``/usr/mmdf''
.TE
.DE
MCHNDIR is where the channel programs are to be found.
.DS
.TS
l l .
MCHNDIR	   ``/usr/lib/mmdf/chans''
MCHNDIR	   ``/usr/mmdf/chans''
.TE
.DE
The MQUEPROT gives the protection mode in octal that the
parent of the MQUEDIR directory will have.
.DS
.TS
l l .
MQUEPROT   0700
.TE
.DE
.NH 2
Database Files
.PP
The MDBM line tells MMDF where to find the database file
containing the associative store.
Dbm style databases get their speed and
flexibility by dynamic hashing and can get quite large.
By default, the file is located in the MTBLDIR dirctory, but
it might need to be relocated due to its size.
.DS
.TS
l l .
MDBM	   ``/usr/spool/mmdfdbm''
.TE
.DE
This is not the actual form of the file name,
as the dbm(3) package forms two files with the suffices
.I \&.pag
and
.I \&.dir
which are closely coupled by the dbm package.
Another file may also appear with a
.I \&.lck
suffix.
This is a file that is used in locking the database.
.NH 2
The Log Files
.PP
Several parameters affecting the logging of information can be tailored.
One parameter is the level of detail that should be logged.  If this parameter
is set for verbose logging,
then enormous amounts of information can be gleaned,
probably most of it indecipherable (but a good start towards
your guru's badge),
and most of the \*M programs become noticeably slower and
I/O bound. To get the fullest logging information available,
the programs have to be compiled with the \-DDEBUG flag.  Setting this
flag may cause
the programs to get too large for some PDP-11
computers.
.PP
The MMSGLOG controls the logging information produced by
\fIdeliver\fR and \fIsubmit\fR; MCHANLOG controls the logging by most
other \*M programs with the following exceptions:
PHLOG controls phone logging information
and AUTHLOG controls authorisation information.
An argument without an equal sign is taken as the new name of
the log.
Logging files and levels can also be specified in the channel
descriptions.  The logging file, if specified there, will override
the MCHANLOG definition.  The logging level for the channel will be
set to the maximum of the MCHANLOG level and the channel description's
level.  The MCHANLOG level can therefore be used to increase logging
on all channels at once.
.DS
.TS
l l .
MMSGLOG	/tmp/mmdf.log, level=FST, size=40, stat=SOME
MCHANLOG	level=FST, size=40, stat=SOME
MPHLOG	level=FST, size=40, stat=SOME
AUTHLOG	level=FST, size=40, stat=SOME
.TE
.DE
These examples set the logging level to FST (full statistics).
Alternatives here are:
.RS
.TS
l l .
FAT	\- Logging of fatal errors only.
TMP	\- Temporary errors and fatal errors.
GEN	\- Include the generally interesting diagnostics.
BST	\- Show some basic statistics.
FST	\- Full statistics.
PTR	\- Give a program trace listing of what is happening.
BTR	\- Give more detailed tracing.
FTR	\- Give every diagnostic you can.
.TE
.RE
.PP
The last two conditions (BTR and FTR) are enabled only if you have compiled
the MMDF system with DEBUG #define'd.  This amount of tracing
can quickly fill up log files.
During normal operation, the logging level should be set somewhere
between GEN and FST.
.PP
Size is set to the number of 25-block lumps you wish your log file
to grow to. When the log files reach that size,
logging either stops or it may start to cycle around overwriting old data.
.PP
Stat sets up various status flags for logging.
Options here are:
.RS
.IP close 10
Close the log after each entry; this allows other processes
to write to it as well.
.IP wait 10
If the log is busy, wait awhile for it to free.
.IP cycle 10
If the log is at maximum length specified above, then
cycle to the beginning (this feature doesn't work currently).
.IP some 10
Sets the values close, cycle, and wait. It is the most
common setting.
.RE
.PP
The logfile tailoring is generally done at the end of the
tailor file to prevent logging the tailoring action itself,
thereby needlessly filling the logfiles when higher tracing
levels are enabled.
If you have bugs in the tailoring, you will obviously want to move
the logfile tailoring closer to the top.
.PP
The DEFTRAN line gives the name of the default transcript
file for use in phone logging.
It is the only log that is not size limited,
as it is created anew each time a connection is made with
the phone channel or slave program.
.DS
.TS
l l .
DEFTRAN	"ph.trn"
.TE
.DE
.NH 2
Runtime Table Definitions
.PP
Table contents are discussed in section 4 of this document.  Table definitions
are discussed here.  Tables fall into three categories: domain, channel, and
others.  Domain and channel tables are discussed below.  The "other" category
is discussed here.  
Regardless of category, table definitions all have the same format.
They all consist of the following parts:
.RS
.IP name 10
A name by which the table is known.
.IP file 10
A file from which the contents of the table are built up.
.IP show 10
A display line,
which is used for pretty printing purposes to explain what
the table is all about.
.IP flags 10
The flags line is optional.  It is used to indicate additional
properties about the table being defined.
This can be specified more than once for a given table to set
more than one flag.
Currently this is used to specify various table options - source type,
nameserver lookup and partial lookups.
There are three source types defined: ``file'', ``dbm'', and ``ns''.
The type ``file'' means that the table comes from a sequentially
read file (the default).
The type ``dbm'' indicates that the table is in the MMDF hashed
database built with \fIdbmbuild\fR.
The type ``ns'' indicates that the table data is obtained by making
queries on a nameserver.  In the case of an ns-type table,
the ``flags'' field will also specify the type of nameserver lookup.
Currently ``domain'',  ``channel'', and ``root'' are supported.
``Domain'' indicates a lookup of the given address in the domain
specified by the table name (e.g. FOO.ARPA in table ARPA).  ``Channel''
indicates a lookup of the given fully-qualified domain name to determine
the address(es) to use in delivering via SMTP.  ``Root'' indicates a
domain lookup of the given fully-qualified domain name without regard to
the table name (e.g. FOO.OXBRIDGE.EDU in table ROOTDOMAIN).
Normally, only fully-qualified names will be checked.  The optional type
``partial'' indicates that, for ns-type domain tables,
partially qualified names can be used (e.g. if ns-type domain tables ARPA
and EDU have the ``partial'' flag set, an address of FOO
will generate queries for FOO.ARPA
and FOO.EDU).  The ``partial'' flag is often used for the local domain table
to allow users to omit the full domain specification when referring to local
machines.
.sp
Note:  In the current release there are only two modes of operation.
If you use tb_io.c, you will get sequential table io ONLY.  If you
use tb_dbm.c you will get DBM and NS support (and the latter only if you
define NAMESERVER).  The source types ``file'' and ``dbm'' are therefore
treated the same.
.RE
.LP
A typical example might be:
.DS
MTBL  name=aliases, file=aliases, show=``User & list aliases''
MTBL  name=smtpchan, flags=channel, flags=ns, show=``SMTP Addrs via NS''
.DE
If the first argument does not have an equal sign in it,
it is taken as the value for the other parameters.
For example:
.DS
MTBL  aliases, show=``Alias table''
.DE
sets the name,
file and show parameters to the string ``aliases'',
and then resets the show parameter to the string ``Alias table''.
.PP
The following tables are provided automatically via chan.c
and the tailoring package when Mail-IDs are enabled (see section 3.2 above):
.RS
.IP users 10
A mapping of usernames to Mail-IDs
.IP mailids 10
A mapping of Mail-IDs to usernames
.RE
.PP
The aliases tables are sometimes compiled in from the configuration
file chan.c, so they can be left out of a runtime configuration.
If they are not in chan.c they must be specified in the runtime configuration.
If this is your first time through, best go for runtime tailoring
until you know what you want.
The tables for mailids and users are also compiled into
chan.c since other parts of MMDF need to know about these tables.
Changes to the mailids or users table definitions are best made in chan.c.
.XX
Define or change the aliases tables (if necessary).
.NH 2
Runtime Alias Definitions
.PP
Alias definitions are used to tell the system about the
various sources of alias information.  \*M can have more than
one alias file.
In addition, each alias source has the option of being trusted
and the option of preventing bypassing (via the\ \~\ on the front
of an address).
.PP
Making an alias file trusted is a serious decision.
A trusted alias file can direct mail to be delivered
to any file or process using the permissions of any user on the
system.  It should only be modifiable by the Superuser,
or guarded as such.  Typically there is only one or two trusted
alias files per system.
.PP
The alias tables are searched in the order they are defined.
When a hit is made, no further searching is done for additional
hits in later tables.
.RS
.IP table 10
Specifies name of the table to be associated with this alias entry.
.IP trusted 10
The entries in the table will be allowed to cause delivery to
files and pipes.
.IP nobypass 10
The `\~address' alias bypass mechanism will not work on this file.
.RE
.DS
ALIAS	table=sysaliases, trusted, nobypass
ALIAS	table=forward\ \ \ \ \ \ # modified by users (== .forward)
ALIAS	table=useraliases, trusted
.DE
.XX
Define your alias tables and their attributes.
.NH 2
Runtime Domain Definitions
.PP
See section 5.2 for a discussion of how to choose a set of domains to include
in the tailor file.
Domain definitions consist of the following:
.RS
.IP name 10
A name by which to refer to
this domain.
.IP show 10
A display line,
which is used for pretty printing purposes to explain what
the domain is all about.
.IP dmn 10
The full domain name (x.y.z...) that specifies this domain.
.IP table 10
The associated table entry of known sites in this domain.  If the specified
table has not been previously defined, it will be defined with the same name,
file, and show as for this domain.
.IP lname 10
An optional local name by which you are known in that
domain.  This defaults to the value of LOCNAME.
.RE
.PP
There are many defaults to make life easier.
As in the table case,
if the first argument does not have an equal sign,
the values of the name,
domain and show parameters take on this value.
.DS
MDMN	name=``nott.ac.uk'', dmn=``nott.ac.uk'', table=nott,
	show=``Nottingham Domain''
MDMN	arpa, show=``ARPANET Domain''
MDMN	``'', show=``Root Domain'', table=rootdomain
.DE
.XX
Define a set of domains in your tailor file.
.NH 2
Runtime Channel Definitions
.PP
The first stage is to determine the set of channels to be used.
Typically, a local and list channel will be required.
Then, there will be one channel for each PhoneNet link, and one channel
(at least) for each protocol network pair (e.g. smtp channel for
SMTP/ARPANET and an NIFTP channel for JNT Mail/X.25).  UUCP
needs only one channel.  In some cases it may be desirable to split what
would normally be one channel
so that some authorisation can be used on the basis of channel.
For example, one may choose to have a separate channel for ARPANET and
Hosts using SMTP/TCP on a local ethernet, even though they could use
the same channel.  Another case might be to have separate JNT Mail
channels for JANET and PSS.   This allows for logical authorisation
functions to be applied on a per-channel basis.
.XX
Decide what channels you will need to use.
.PP
Next you must define these channels in the tailor file.  The channel
definitions are more complex than tables and domains.  There are
a large number
of parameters available because of the diverse behavior of the different
channel programs.  
Many of the parameters are not necessary for most of the
channels.
The specific parameters are as follows:
.RS
.IP name 10
The name of the channel.
.IP show 10
A display line,
which is used for pretty printing purposes to explain what
the channel is used for.
.IP que 10
The queue directory into which messages for this channel should be queued.
.IP tbl 10
The associated table entry of hosts that are accessible on this
channel.  If the specified table has not been previously defined,
it will be defined with the same name, file, and show as for this channel. 
.IP pgm 10
The channel program to be invoked for this channel.
.IP mod 10
The mode in which this channel works.  If several values are selected,
they are cumulative.  Normally, one of ``reg'', ``bak'', ``psv'',
or ``imm'' is selected.  ``Pick'' and ``send'' are usually both selected
on two-way channels such as phone or pobox.
The values are:
.RS
.IP "reg \-" 9
regular mode (the default).  The channel may be explicitly invoked by running
.I deliver.
.IP "bak \-" 9
channel may be invoked only by the background deliver daemon.
.IP "psv \-" 9
the channel is passive; it is a pickup type channel (e.g. pobox).
.IP "imm \-" 9
this channel may be started up immediately; there is no 
need to batch up mail.
.IP "pick \-" 9
This channel may pick up mail from the remote host.
.IP "send \-" 9
This channel may send mail to the remote host.
.RE
.IP ap 10
The address-parser type to be used for reformatting headers
on messages going out on this channel. 
The settings of ap control reformatting.  If none is selected, the
channel will not reformat.  If several values are selected, they are
cumulative:
.RS
.IP "822 \-" 9  
selects RFC 822 style source routes (e.g. @A:B@C)
.IP "733 \-" 9
selects % style\ (JNT) source routes (e.g. B%C@A)
.IP "big \-" 9  
selects NRS hierarchy ordering (for the UK)
.IP "nodots \-" 9
selects output of leftmost part of domain names (e.g. A in A.B.C) for sites
that can't handle domains (usually used in conjunction with `known=' (described
below) to hide domain names behind a smart relay).
.IP "jnt \-" 9
is equivalent to ap=733 + ap=big
.RE
.IP lname 10
This specifies a name overriding the default MLNAME value
when used in this channel.  This is the name by which the local host
is known to hosts accessed by this channel.
This is used when the channel should have non-standard values
for the local domain.  If \fIlocname\fR = `UCL-CS' and \fIlocdomain\fR = `AC.UK',
then for an arpa smtp channel, one would set the parameters
ldomain = `ARPA' (and, implicitly, lname = `UCL-CS').  All local references
in this channel would be mapped to UCL-CS.ARPA if reformatting is used.
.IP ldomain 10
As above, but overriding the value of MLDOMAIN.  See the example above.
.IP host 10
The name of the host that is being contacted by this
channel, usually used in the phone and pobox channels.
(Also the name of the relay host when all mail to hosts in this
channel's table gets relayed to one host.
This is required on the
.I badusers
and
.I badhosts
pseudo-channels.  It is also required to be set to the local host for the
.I list
channel.
.IP user 10
The login name of the account which uses the slave program to pick up mail
(used for the pobox channel only).
.IP scr 10
The name of the dialing script file to be used by the phone channel.
.IP trn 10
The name of the transcript file to be used for this channel;
this currently applies only to PhoneNet,
and defaults to DEFTRAN (usually ph.trn).
.IP poll 10
The frequency of polling the remote machine.  A poll on a two-way channel
(such as the phone channel) is used to see if any mail is waiting to be
picked up from the remote host.
A value of 0 disables polling;
a value of -1 requests polling to be done every time the
channel is started up.
Any other value is the number of 15-minute intervals to wait
between polls.  Note that the value of poll is ignored if any mail is waiting
to be sent; in this case, a connection is always attempted.
.IP insrc 10
Table of hosts controlling message flow.   See the paper
.B "Configuring MMDF Authorisation"
by Steve Kille for details.
.IP outsrc 10
See insrc, above.
.IP indest 10
See insrc, above.
.IP outdest 10
See insrc, above.
.IP known 10
This is a table of hosts that are known on this channel.
If a known table is specified and a given host is not in the table,
then a relay form of the address is used,
specifying that the return address goes through this
host.  Only the LHS of this table is used; the RHS
is ignored.  The LHS should contains hosts, not domains.
Typically, channel tables double as ``known
tables''.  Be sure that the table also contains your own 
fully specified name (localhost.locdomain).  Note that
The ``known table'' must exist.  A tailor entry will not define a ``known
table'' that has not been predefined.
.IP confstr 10
This is a string that is used by some channels for
specifying the invocation of another program.
There is a form of macro expansion in this string,
so that it can include the target address,
the return address, etc.  Refer to the man pages describing specific channels
for more information about using the confstr.  Most channels don't use this
string currently.
.IP auth 10
This specifies the authorisation tests that are made on this
channel.
It takes any combination of the following modes:
.RS
.IP free 10 
default, no checking takes place.
.IP inlog 10
log information regarding incoming messages.
.IP outlog 10
log information regarding outgoing messages.
.IP inwarn 10
warn sender of incoming message if his/her authorisation is
inadequate (the message still goes through).
.IP outwarn 10
as above, but for outgoing messages.
.IP inblock 10
reject incoming messages that have inadequate
authorisation.
.IP outblock 10
as above, but for outgoing messages.
.IP hau 10
Host And User control.  Requires both host and user
authorisations to be acceptable.
.IP dho 10
Direct Host Only. 
When applying host controls, the message must be associated
with a user local to that host (i.e. no source routes).
.RE
.IP ttl 10
The \fIdeliver\fR program keeps track of connection failures to specific hosts.
 When a connection fails, the host is marked ``dead'' for a certain period of
time.  This period is called the ``time-to-live'' of the dead-host entry.
After a connection failure to a given host, retries are blocked for ttl
minutes.  This value may be overridden on the deliver command line.  If the ttl
is not given here or given to deliver, a default of 2 hours is used.
.IP log 10
This specifies the name of the log file to be used instead of
the default channel log file (see CHANLOG above).
Typically this is something like ``log=smtplog'' which will use
a logfile called smptlog if it exists in the default logging directory.
.IP level 10
This specifies the logging level to use for this channel.
The form is the same as that used to specify the default logging
level (e.g. ``level=FST'') as described in section 3.5 of this document.
The actual logging level used is the maximum of this level (if present)
and the default MCHANLOG level.
.RE
.PP
As mentioned above,
the majority of these options are not used for most
channels.
.RS
.TS
center;
l l.
MCHN	name=local, que=local, tbl=local, show=``Local (maths)'',
	pgm=local, poll=0, mod=imm, ap=822, level=BST
MCHN	uucp, name=uucp, que=uucp, tbl=uucp, show=``UUCP'',
	pgm=uucp, poll=0, mod=reg, ap=822, auth=inlog, auth=outlog
MCHN	smtp, show=``SMTP/TCP'', que=smtp, tbl=smtp,
	pgm=smtp, mod=reg, ap=822, log=smtp.log, level=FST,
	confstr=``mysite.mydomain''
MCHN	list, show=``Mailing List Processor'', que=list, tbl=list,
	pgm=list, mod=reg
MCHN	bboards, show=``BBoards Delivery'', que=bboards, tbl=bboards,
	pgm=bboards, mod=reg, ap=822
MCHN	dial-out, show=``Dial-out channel to dial-site'',
	que=dial-site, tbl=dial-site, trn="dial-out.trn", ap=822,
	pgm=phone, mod=reg, mod=send, mod=pick, poll=8,
	host=dial-site, scr="dial-site.script"
MCHN	dial-in,  show=``Dial-in channel from dial-site'',
	que=dial-site, tbl=dial-site, trn="dial-in.trn", ap=822,
	pgm=pobox, mod=psv, mod=send, mod=pick,
	host=dial-site, user=dislave
.TE
.RE
In the ``uucp'' example, the name, que, tbl and pgm could have been omitted;
they would have defaulted to ``uucp'' since it was the first argument.  It is
common, however, to include these explicit settings for clarity.
The order of parameters is unimportant.
.XX
Define the channels needed at your site.
.NH 2
Some Runtime Constants for Messages
.PP
Now there are a collection of constants defined:
.LP
MADDID controls whether
.I submit
will add Message-ID: header lines if they are missing from
messages.  It takes a number as an argument.  If the number
is 0, no action is taken.  If the number is non-zero,
then
.I submit
will add Message-ID: header lines if they are missing from messages.
.DS
MADDID  1
.DE
.LP
MLISTSIZE specifies the maximum number of addresses in a message
before it is considered to have a ``big'' list.
If there are more than the maximum number of addresses, then \*M
will not send a warning message for waiting mail and will only
return a ``citation'' for failed mail.  A ``citation'' consists
of the entire header plus the first few lines of the message
body.
.DS
MLISTSIZE  20
.DE
.LP
MMAXHOPS specifies the maximum number of Received: or Via:
lines in a message before it is considered to be looping, and is rejected.
.DS
MMAXHOPS  20
.DE
.LP
MWARNTIME specifies the time in hours that a message may
remain in a queue before a warning message about delayed
delivery is sent to the sender.
.DS
MWARNTIME  72
.DE
.LP
MFAILTIME is the time a message may remain in a queue before
a failed mail message is sent to the sender and the message is
purged from the queue.
.DS
MFAILTIME  144
.DE
If the number of items in the queue is less than
MMAXSORT,
the messages will be sorted by arrival time and dispatched
in that order;
otherwise a message will be dispatched as it is found in the
directory search.
.DS
MMAXSORT   150
.DE
MSLEEP is the length of time in seconds that the background
delivery daemon sleeps between queue scans.  The following
causes the daemon to sleep 5 minutes between passes.
.DS
MSLEEP	   300
.DE
.NH 2
Runtime UUCP Settings
.PP
The only uucp string worth bothering with is UUXSTR,
which is the command used to invoke the UUCP software.
In general you may want to tailor the grade level if supported
and the disabling of immediate attempts (via \-r).
If you have special per-message  arguments, then you will
need to modify the UUCP channel itself (e.g. \-a\fIsender\fR).
.DS
UUXSTR  ``uux -''
\tor
UUXSTR  ``uux - -r''
\tor perhaps
UUXSTR  ``uux - -r -gA''
.DE
.NH 2
Runtime General Variables
.PP
Now some more general variables.
.DS
.TS
l l .
MSUBMIT	path=submit
MDELIVER	deliver deliver
MPKUP	name=pickup, path=deliver
MV6MAIL	mmdfmail ``/bin/v6mail''
.TE
.DE
MSUBMIT tells MMDF where the \fIsubmit\fR program lives.
MDELIVER is where the \fIdeliver\fR program lives, and MPKUP is the
post box pickup program.
MV6MAIL is the mail program MMDF should use to return
undeliverable messages and to send diagnostics.
It should behave rather like the original Bell \fImail\fR program.
It is not typically necessary to include these tailoring entries.
.NH 2
Runtime Delivery Tailoring
.PP
MDLVRDIR is the directory in which to deliver mail.
If this is null, then the user's home directory is used.
MMBXNAME is the name of the mailbox.
If this is null, then the user's login name is used.
The following would set \*M to deliver mail to /usr/spool/mail
with each mailbox named after the user's login name:
.DS
MDLVRDIR   ``/usr/spool/mail''
MMBXNAME   ``''
.DE
The following causes mailbox files to be created
in the user's home directory:
.DS
MDLVRDIR   ``''
MMBXNAME   ``mailbox''
.DE
MMBXPROT is the protection mode that should be used when
creating the user's mailbox. Its value is an octal number.  See manual 
section \fIchmod(1)\fR.
.DS
MMBXPROT   0600
.DE
MMBXPREF and MMBXSUFF are two strings that are written
before and after the message is written into the mailbox.
These are usually set to a sequence of control-A's.
.DS
MMBXPREF ``\e001\e001\e001\e001\en''
MMBXSUFF ``\e001\e001\e001\e001\en''
.DE
MDLV is the name of the maildelivery file,
used for tailoring the delivery for each user.
See manual section \fImaildelivery\fR(5).
.DS
MDLV ``.maildelivery''
.DE
The preceding parameters are also good contenders for being compiled-in
since they very seldom change once established at a given site.
.NH 2
Runtime PhoneNet Tailoring
.PP
If you do not use the phone or pobox channels, you may skip this section of the
tailoring document.
.PP
DBADOUT and DBADIN are eight octal integers
specifying the ASCII codes that can be used for host-to-host
communications over PhoneNet.
If there is a bit set in the ASCII position,
then that ASCII code is not allowed to be used in the
transmissions and therefore is escaped.
.DS
.TS
l l .
DBADOUT	0177777,0177777,0000010,0000000,
	0000001,0010000,0000000,0100000
DBADIN	0177777,0177777,0000010,0000000,
	0000001,0010000,0000000,0100000
.TE
.DE
In this example, the following
characters are declared ``illegal'' to send or receive: all control characters
(0x000 - 0x037), '#' (0x043), '@' (0x100), '\\' (0x134), and del (0x177).
If the sending and receiving character sets are the same (as in this example),
the DBAD tailoring item may be used to set both character sets at once:
.DS
.TS
l l .
DBAD	0177777,0177777,0000010,0000000,
	0000001,0010000,0000000,0100000
.TE
.DE
.PP
When installing the dial protocol package on a Unix system,
there are a few things that have to be changed to reflect the
environment
such as the names, types, and speeds of the various dial-out ports
and direct connect lines to other hosts.
Each DPORT entry sets the parameters for a given dial-out port.  A
dial-out port is a modem with a separate autodialer associated with it.
Each DPORT entry contains
five positional arguments and then, optionally, any of three named arguments.
The positional arguments are:
.IP 1. 10
The path name of the port itself.
.IP 2. 10
The path name of the lock file which is used to insure that no more than
one process tries to use the same port at a time.  If the UUCPLOCK feature
was specified on the CONFIGDEFS line in Makefile.com, then the path name must
be of the form "/usr/spool/uucp/LCK..tty??" where ?? is the number of the
tty line being used.
.IP 3. 10
The path name of the automatic calling unit (acu) that is associated with the
dial-out port.
.IP 4. 10
The speeds that the modem can support.  This is an octal number whose bits
represent supported speeds.  The relationship is as follows:
.DS
.sp
.TS
box;
c c
l l .
\fIBit\fR	\fIBaud Rate\fR
0	50
1	75
2	110
3	134.5
4	150
5	200
6	300
7	600
8	1200
9	1800
10	2400
11	4800
12	9600
.TE
.DE
For example, 0177 is used for modems that can operate at speeds from 0-300
baud and 0400 is used for modems that only operate at 1200 baud.
.IP 5. 10
The line type of this port.  Line types such as "local", "wats", or "instate"
are referenced in the dialing script (see script(5)) and allow selection
of ports based on the capability of the associated phone line.
.IP pref= 10
The prefix string to send to the acu before sending the
phone number contained in the dialing script.  This is typically a command
string that tells the acu to get ready for a phone number and to select the
appropriate outgoing modem.
.IP suff= 10
The suffix string to send to the acu after sending the
phone number contained in the dialing script.  This is typically a command
string that tells the acu to actually start dialing.
.IP access= 10
This is a filename which contains a list of usernames (one per line) of the
people authorized to use the associated dial-out port.  If this argument is 
omitted, everyone will be given access.  
.PP
For example, the following entry
sets up a dial-out port for a Vadic MACS where the modem is on tty14, 
the dialer is on dn0, the speed is 1200-only.  The prefix string gives 
control information to the dialer such as connection-type and modem slot
address.  The suffix string signals the end of the phone number.
.DS
DPORT ``/dev/tty14'', ``/usr/spool/uucp/LCK..tty14'', ``/dev/dn0'',
	0400, pref=08, suff=``<''
.DE
.PP
The DLINE entry does a similar job for directly connected
lines.  Directly connected lines can be of three types: a hard-wired
connection between two machines, an autodialing modem, and
a pseudo-tty.  These three cases are discussed below and the format of the
DLINE entry for each is presented.
.PP
The first type, a
hard-wired connection between 2 machines, uses a single tty port on each
system.  In this configuration, one machine is designated the master.  The
master machine has logins disabled on its tty port.  The other machine, the
slave, has logins enabled at a known and fixed baud rate.  The master machine's
port is defined with a DLINE entry which tells the name of the line (this gets
used in the ``dial'' command in the dialing script (see script(5)), the path
name of the port itself, the path name of a lock file to use to insure
exclusive access to the port, and the speed to use for the connection.
Thus, for a 9600 baud hard-wired connection, the following DLINE might be used:
.DS
DLINE ``udeleehp'', ``/dev/tty05'', ``/usr/spool/uucp/LCK..tty05'', 9600
.DE
.PP
The second type, an autodialing modem, uses a single tty port to both
tell the modem what number to dial and to exchange data with the remote machine.
The dialing script must contain the necessary commands to tell the modem what
number to call.  The DLINE entry looks just like that for a hard-wired
connection.  More than one DLINE entry may be included with the same name;
the phone channel will try each one in succession until it finds one available.
.DS
DLINE ``hayes'', ``/dev/tty05'', ``/usr/spool/uucp/LCK..tty05'', 1200
DLINE ``hayes'', ``/dev/tty06'', ``/usr/spool/uucp/LCK..tty06'', 1200
.DE
.PP
The third type, a pseudo-tty, allows you to enter commands in the dialing 
script which log back into your own host and which run an auxiliary program
to connect to the remote machine.  This program might be an X.25 pad-out program
or it might be ``cu'' or ``tip''.  You must first allocate a pseudo-tty pair 
(e.g. /dev/ptypf and /dev/ttypf) for this
application by renaming them (e.g. to /dev/ptymmdf and 
/dev/ttymmdf, respectively).  Next, enable logins on the latter device (e.g.
by placing a line such as ``12ttymmdf'' in /etc/ttys).  The DLINE entry that
may then be used to establish a connection back to your own machine is:
.DS
DLINE ``pty'', ``/dev/ptymmdf'', ``/usr/spool/uucp/LCK..ptymmdf'', 9600
.DE
Note that the speed designator must be valid but that it is ignored.
.TP
DACCT specifies a file to use for logging phone calls for accounting purposes.  
Only calls made on a DPORT are logged.  If the specified file does not exist, 
then logging of calls is disabled.
.DS
DACCT ``/usr/mmdf/log/dial_log''
.DE
.NH 2
Some Miscellaneous Bits
.PP
MDFLCHAN is used to set the default channel to something
other than local.
.PP
NIQUEDIR is set to a directory used for NIFTP temporary files
hence is only used on NIFTP channels.
.NH 2
Installation
.PP
Now that you have a runtime tailor file in place, you are ready to
create the required directory structure and install the binaries in it.
Run the program src/tools/xsetup (see \fIsetup\fR(8)) to generate the needed 
directories.  You will have to create the RCVDIR and BINDIR directories by
hand if they don't already exist; \fIsetup\fR
has no notion of these directories (tailored in Makefile.com).
.XX
Run src/tools/xsetup (it will ask for confirmation before changing anything).
.XX
Create the directories RCVDIR and BINDIR if necessary.
.PP
The programs \fIsubmit\fR and \fIdeliver\fR are unable to automatically
create queue directories (q.xxx).  If you later add new channel definitions
to your runtime tailoring file, you will need to create the queue directories
for those channels.
The program \fIsetup\fR can be used to do this.
Note that this program will
sometimes complain unnecessarily about permission problems if
you have played with the file modes to allow group access to the
lock directory.
.PP
Now that the directories have been created, you are ready to install the
binaries.  Running `make install' from the top of the \*M source
tree will install
everything in the subdirectories \fIsrc\fR and \fIuip\fR (in that order).
You can also run `make install' in each of these directories alone.
Note that all \*M programs such as background \fIdeliver\fR's and smtp daemons
must be killed off before running `make install'.
.XX
Run `make install' from the top of the \*M source tree.
.sp 2
.ne 10
.NH 1
Miscellaneous Tasks
.PP
There remain a number of things to be done either now or sometime
later that are not critical but should eventually be attended to.
You will need to setup your tables and run dbmbuild if you
are using the hashed database (DBM library).
Setting up tables is described in the next section of this
document.
You should arrange for the \fIcleanque\fR program
to be called from \fIcron\fR at least twice a day, and perhaps
more often at your choice.
You will need to install a program called `setlogs' in the log directory
to do periodic cleanup on the logging files.
This should also be called from \fIcron\fR at some
reasonable interval.
The \fIcheckup\fR program will complain if the \fIsetup\fR program
is missing, but this is not a critical error.
Eventually you will need to edit /etc/rc to have it start up
at least one background
.I deliver
daemon.
Be sure that there is an ampersand (`&')
on the end of the line invoking
.I deliver
since it does not automatically detach itself.
In some situations you may wish to have more than one.
See the section on tuning for more information.
 each one in succession until it finds one available.
.DS
DLINE ``hayes'', ``/dev/tty05'', ``/usr/spool/uucp/LCK..tty05'', 1200
DLINE ``hayes'', ``/dev/tty06'', ``/usr/spool/uucp/LCK..mmdf/doc/administrators/tuning   444      0     12       23025  3631170262  12032 .\".AU
.\"Douglas P. Kingston III
.\".AI
.\"Ballistic Research Laboratory
.\"USA AMCCOM
.\"Aberdeen Proving Ground, Maryland.  21005
.\"(301) 278-6651
.bp
.NH
Tuning \*M
.NH 2
Introduction
.PP
Mail systems can vary greatly in the amount of traffic they carry
and the communities they serve.  This variety of environments and
loads cannot be properly served without appropriate tuning of the
mailsystem to the environment it must serve.
Tuning can take many forms, but the most common is adjusting
the number and type of
.I deliver
daemons you have servicing the mail queues.
Another common technique is to create additional mail queues
which may serve a special subset of a larger queue to enable
processing of that subset at a higher or lower priority.
.NH 2
Multiple Queues and Channels
.PP
One common method to improve the performance of \*M is to
partition the mail queue into multiple directories.
Generally this is done such that each channel has its own
queue.
The advantage is that when the daemon is working on a given
channel, it need only examine messages which it knows are for that
channel (by virtue of being in the queue directory for that channel).
It is possible to have multiple channels operating from a single
queue, and this might be advantageous if you had a very large number
of channels each with a very small amount of traffic, but this is
unlikely and probably  only worth thinking about when the number
of channels approaches 100 or more.
.PP
Adding channels which cover a subset of hosts accessible by another
channel is a good technique for increasing throughput and
reduce delivery latency.
.PP
A good example of this is the BRLNET channel used at BRL.
All BRL hosts are on the ARPANET (MILNET), and as such could
be queued into the smtp channel for delivery.  If this were
done, the BRLNET messages would be mixed up with the hundreds
of messages pending for tardy ARPANET sites.  In addition, the
delivery latency would rise dramatically since you would have to
wade through all the tardy messages on each pass over the queue.
To provide better service to the directly connected hosts, we
defined a BRLNET channel, with its own queue, and re-used the
smtp program.
This channel is defined before the SMTP channel so when a message
is queued for a BRL host, the \fIsubmit\fR program
finds the BRL host name first in the BRLNET channel table and
therefore queues it into the BRLNET channel.
The \fIdeliver\fR servicing the BRLNET channel services only
the BRLNET channel and a couple of lightly loaded channels
like the local and list channels.  This means the the BRLNET channel
gets a high level of service and because of our high-speed local
area networks, the delivery latency is quite small.
.PP
Another use for separate channels is for tardy hosts.  At times
there have been several hosts tardy over a long period of time (weeks).
Rather than have them fill up the regular mail queues, we created
a special channel with only these ``turkey'' hosts and placed
this channel ahead of the smtp channel in the configuration file.
This caused mail for the turkey hosts to be queued into this channel
rather than the smtp channel.
The turkey channel had its \fIdeliver\fR run less often than normal.
.NH 2
Multiple Daemons
.PP
It is possible to run multiple invocations of
the \fIdeliver\fR daemon.  There are three advantages to multiple
daemons.  The first is increased throughput.  Although the mail
processes do use a considerable amount of cpu time, they spend a
large amount of time waiting while actually delivering mail so
it is possible for two \fIdeliver\fR daemons to deliver close to twice
as much mail in the same period as one daemon.
Second, with more than one deliver, the delivery latency will be reduced
since with more daemons, the chances that one will find your message
in the same amount of time are twice as good.
Third, multiple daemons servicing the same channel provide redundant
service.  If one daemon should die off, the remaining delivers on that
channel will continue to deliver, although providing a reduced level
of service.
.NH 2
Tailoring Delivery Characteristics
.PP
There are numerous options that can be used to control the behavior
of deliver daemons.  The most common is
`\-c\fIchannel,channel,channel\fR' which sets which channels the
deliver will attempt to process.
One might have one daemon processing local, uucp, and localnet
mail and two other daemons just processing smtp
mail if you were a large site relaying a lot of mail to the
Arpanet.
The configuration used on the main
mail host at BRL is as follows:
.sp
.nf
.RS
.nf
deliver \-L/usr/mmdf/log/deliver1 \-b \-l15 \-clocal,brlnet,list,uucp,bb
deliver \-L/usr/mmdf/log/deliver2 \-b \-t24 \-csmtp
deliver \-L/usr/mmdf/log/deliver3 \-b \-t48 \-csmtp
deliver \-L/usr/mmdf/log/deliver4 \-b \-csmtp
deliver \-b \-l15 \-s \-T300 \-cucl
.fi
.RE
.fi
.PP
This is a very good example because it demonstrates the use of almost all the
different delivery tuning mechanisms.
There are seven different queues.
There are five separate \fIdeliver\fR daemons.
The queues can be characterized as follows:
.sp
.nf
.DS
.TS
l l .
local	\- Local mail delivery, light volume, fast
brlnet	\- Mail to other BRL hosts via LANs, medium volume, some backup
list	\- List processor, light volume, fast
uucp	\- UUCP channel, light volume, fast
bb	\- BBoards channel, light volume, fast
smtp	\- SMTP to Arpanet, heavy volume, large queue backups
ucl	\- SMTP to UCL-CS only, heavy volume, occasional large backups
.TE
.DE
.sp
.fi
The first five are grouped together under one deliver daemon,
with the only major user being the BRLNET channel.  Since
the other channels create an insignificant load, the BRLNET
hosts get an essentially dedicated \fIdeliver\fR daemon.
The smtp channel has so much volume, and spends so much time
waiting for read and writes across the country, that we have three
daemons processing this channel.  The ucl channel
was created to handle all the mail headed to one host, UCL-CS,
since we send them a considerable volume of mail, and occasionally
the satellite has a bad day, and the mail backs up.  We have
had over a thousand messages backed up for this host on more
than one occasion.  The first time this happened, it really slowed
down the smtp delivery.  Now when UCL-CS
is tardy, they hurt no one.
.NH 2
Dead Host Cache Time-to-live
.PP
When a host is discovered to be dead or not responding, it is
marked ``dead'' by the \fIdeliver\fR daemon.  All further addresses
for that host in the current message or subsequent messages are
skipped until the time-to-live of the ``dead host'' record has
been reached.  The time-to-live is usually defaulted to 2 hours.
The time-to-live can be changed by using the \-l\fIminutes\fR
flag on the \fIdeliver\fR command line.
For example, you will note that on the first \fIdeliver\fR daemon
in the example, the time-to-live is set to 15 minutes since
we do not expect BRLNET hosts to spend much time down and we
want the \fIdeliver\fR daemon to quickly notice when a BRLNET host comes
back on line.
.NH 2
Recent Message Selection
.PP
One of the properties of sending to hundreds of hosts is that if a host
is down for more than a day or two, it is likely to be down considerably
longer.  It is wasteful to have all the daemons constantly working over
all the old messages when the chance of a machine reappearing are relatively
small compared to the chances that a machine down only 4 hours
will reappear.  The \-t\fIhours\fR switch allows us to force the daemon
to consider only messages that were queued in the past \fIhours\fR hours.
In other words, we force the daemon to concentrate on delivering the
recent messages, which have a higher likelihood of delivery.
One daemon looking after the dregs is enough.
You should only use this if you have at least one daemon that does not
have the \-t flag, processing the same queue.
In the example above you can see the \-t flag is being used on both
the second and third \fIdeliver\fR daemons which both service
the largest channel (smtp).  The third daemon is not restricted
with a \-t.
.NH 2
Daemon Sleep Time
.PP
There are certain channels that you wish to run in the background
but for which you do not wish to attempt delivery
as often as the default settings would try.
The \-T\fIseconds\fR switch changes the amount of time in seconds
the daemon will sleep between attempts to deliver messages.
In our example above, we have used this switch on the ucl channel.
This is generally a low-priority mail queue and we are not concerned
that we wait an extra 10 or 20 minutes to discover that the host
has been down, since when it goes down, it is usually down for hours
or days.
The time when this really pays off is when the host is down and mail
has backed up.
When this happens, each time the daemon wakes up, the daemon examines every
address and then decides to skip it because the host is down.   
Occasionally the daemon
will retry the connection (if the time-to-live has expired) but it spends most
of its time not delivering mail.  We simply elect to not attempt as
often, therefore wasting less time.
.NH 2
Queue Sort Override
.PP
In normal operation, \fIdeliver\fR will attempt to sort the
queue of messages for each channel in the order they were submitted.
Sorting a large mail queue can be quite expensive and time consuming.
On some channels you don't care in what order the messages are
delivered.  In these cases it is advantageous from an efficiency
standpoint to be able to disable the queue sorting.
The \-s option does just that.  If this option is specified,
the daemon will read the queue and process the messages in the
order they were found.  This will often not be the order in which
they were submitted.
her channels create an insignificant load, the BRLNET
hosts get an essentially dedicated \fIdeliver\fR daemon.
The smtp channel has so much volume, and spends so much time
waiting for read and writes across the country, that we have three
daemons processing this channel.  The ucl channel
was created to handle all the mail headed to one host, UCL-CS,
since we send them a considerable volume of mail, and occasionally
the satellite has a bad day, and the mail backs up.  We have
had over a mmdf/doc/administrators/Makefile   444      0     12         260  3620510343  12173 SOURCE	= title overview building runtime tables tuning

troff:	$(SOURCE)
	tbl $(SOURCE) | troff -t -ms > admin.troff

nroff:	$(SOURCE)
	tbl $(SOURCE) | nroff -ms > admin.nroff
 the chance of a machine reappearing are relatively
small compared to the chances that a machine down only 4 hours
will reappear.  The \-t\fIhours\fR switch allows us to force the daemon
to consider only messages that were queued in the past \fIhours\fR hours.
In other words, we force the daemon to concentrate on delivering the
recentmmdf/doc/administrators/overview   444      0     12       12635  3631170252  12400 .\".AU
.\"Douglas P. Kingston III
.\".AI
.\"Ballistic Research Laboratory
.\"USA AMCCOM
.\"Aberdeen Proving Ground, Maryland.  21005
.\"(301) 278-6651
.NH 1
Overview
.NH 2
Introduction
.PP
The \*M mail system has seen a great deal of development over the
past several years and has involved a large number of people.
This guide is an attempt to gather together the information
which until now was only passed along as folklore or learned by reading
the code.  The guide is divided into five parts.  The Overview will attempt
to give a summary of the contents of the \*M distribution and where
to look for more information.
The next section explains, step by step, how to compile your own
tailored \*M.
The following sections cover runtime tailoring from the configuration
file, building host, domain, and alias tables, and finally tuning
\*M for best performance on your system.
The \*M programs are all linked with a library that contains a default
compiled-in configuration (which may be very scant).
The compiled-in values can be overridden or augmented by the
runtime tailoring file and tables.
.NH 2
Available Channels
.NH 3
The Local Channel
.PP
The local channel is responsible for delivering mail to mailboxes and
processes on the local machine.  It normally delivers directly to
a mailbox file in the user's home directory or /usr/spool/mail.
It is also capable of delivering to processes or alternate files under
the control of the alias file and/or the user's own "maildelivery"
file, as described in manual section \fImaildelivery(5)\fR.  There
is also provision for a system default maildelivery file if the user
does not supply one.
.NH 3
The List Channel
.PP
The list channel is a special channel that remails messages.
The channel simply invokes \fBsubmit\fR and feeds the addresses
and text back into the \*M mail system.
This is often useful to avoid long address verification
procedures at posting time or to force the verification
take place in the background.  The list channel also knows how
to replace the return address of a message with the list-request
address when appropriate.
.NH 3
The SMTP Channel
.PP
This channel implements the Simple Mail Transfer Protocol as described
in RFC-821.  There is currently code to support the 4.1aBSD, 4.2BSD,
and 4.3BSD network semantics,
BBN TCP network semantics, and 2.9BSD semantics.
.NH 3
The Delay Channel
.PP
Generally used in conjunction with nameserver support.
If there is a nameserver failure for whatever reason, and this channel
is configured, the mail will be conditionally accepted and queued
to the delay channel.
The delay channel will re-submit the mail at a later date until a
definitive reply is received from the nameserver.
.NH 3
The BadUsers Channel
.PP
There is no specific code associated with this channel.
Submit knows if it finds a username it does not know, it can
queue it on this channel for forwarding to another host that
has a better user database.
The channel name is hardcoded as ``badusers''.
.NH 3
The BadHosts Channel
.PP
Like the BadUsers channel, except this is for mail to hosts
that submit does not know.
The channel name is hardcoded as ``badhosts''.
.NH 3
The Phone Channel
.PP
The PhoneNet dialup network protocol is supported by this channel.
PhoneNet is a dialup package developed at UDEL
and used extensively by the CSNET and the Army
as a link layer for \*M for its hosts not directly connected
to a Local or Wide-Area network.
.NH 3
The Pobox Channel
.PP
This is a do-nothing channel that allows queued mail to be picked
up by another process.  The Pobox channel is typically used in a PhoneNet
slave site, but is by no means limited to this.  In the PhoneNet application,
the Pobox channel passes mail from the queue (via deliver) to the phone (via
the PhoneNet Slave program).
.NH 3
The UUCP Channel
.PP
UUCP mailing is supported by this channel.
Addresses are converted as necessary.
Inbound mail is converted into RFC822 format, preserving
existing 822 format header lines.
Outbound, ``From<space>'' lines are added
and rmail arguments
are generated in UUCP bang route format.
The channel will make use of pathalias output paths to simplify
incoming addresses and to route outgoing addresses.
.NH 3
The NIFTP Channel
.PP
NIFTP is a batched file transfer facility used in the UK.  This channel
uses NIFTP to send and receive mail by creating batched requests that send
mail as files.
.PP
The NIFTP channel produces message files formatted according to the
JNT Mail Protocol.  These are transferred by a file transfer protocol,
typically the UK NIFTP (Network Independent FTP), to the remote site.
.NH 3
The BBoards+POP Channel
.PP
This channel is used to interface into the BBoards system that is part
of MH.{4,5,&6} as developed at UCI.
The channel is also compiled to a slightly different version for
supporting Post Office box Protocol (POP).  (Provided by Marshall Rose)
.NH 3
The EAN Channel
.PP
This channel is used to access the EAN X.400 system produced by the 
University of British Columbia.
.NH 3
The Program Channel
.PP
This channel is used to interface well behaved programs to the \*M mail
system.
It does no more than try to reliably pass mail in or out of \*M.
Systems that require special reformatting of the message (such
as UUCP) cannot use this channel.
For example this channel could be used to interface the ACSNET
network or a Batch SMTP implementation both of which prefer
to deal with RFC822 format mail.
This is a new channel and may change somewhat in the future to make it
more powerful.
n
take place in the background.  The list channel also knows how
to replace the return address of ammdf/doc/administrators/tables   444      0     12       32727  3656410700  12012 .\".AU
.\"Steve Kille
.\".AI
.\"Department of Computer Science
.\"University College London
.\".AB
.\"The \*M mail system provides a flexible table structure to
.\"allow (user) transparent connectivity to a large number of sites,
.\"using multiple mail protocols and a mixture of direct and indirect
.\"connections.   This document explains how set up these tables.
.\".AE
.bp
.NH
Configuring Tables for \*M
.NH 2
Introduction
.PP
This section explains how to construct the tables which are defined in the
runtime tailoring file.  The exact syntax of the tables is documented in
tables(5).  
It is assumed that most sites will be in one of a fairly small number of
standard situations regarding mail connectivity.  It is suggested that
the simplest approach for most sites will be to base the configuration
on that of a site in a similar situation.  For this reason, we supply
a number of sample configurations.  For a given sample site \fIfoo\fR, 
the runtime tailoring file and its associated tables are contained in
the directory \fBsamples/\fIfoo\fR.  
.PP
The current sample configurations are:
.RS
.IP bbn 12
A 4.2BSD Internet host using domain servers.
.IP vgr 12
A 4.2BSD Internet host with heavy mail traffic.
.IP uclvax2 12
An Internet host with links to various UK networks; illustrates
authorization on a per-user basis.
.IP 4.3vax 12
A 4.3BSD workstation on an ethernet.  Offloads remote
traffic to another host.
.IP csnet-relay 12
A very large PhoneNet relay host with Internet access.
.IP okstate 12
A V7 Perkin-Elmer host on CSNET PhoneNet, UUCP and a local network.
.IP laser 12
A System V Release 0 based 68000 workstation.  One UUCP link to a
gateway host.  It pretends its name is that of the gateway to
hide the name laser from the rest of the world.  Unknown hosts
and users are passed to the gateway.
.IP scripts
Various samples of PhoneNet dialout scripts.
.RE
.PP
A basic word about the tables for domains and channels is in order.
The tables may appear similar, but they serve very different functions.
The host lookup procedure is a two-tier process.
A destination is first looked up in the domain tables.
\*M need not find an exact match, a match on a partial domain reference
will also succeed, although the full domain match will be attempted first.
When a match is found in the domain tables, the RHS (right hand side) will
have the full domain specification of a host to which the message should
be passed to reach the destination (in many cases this IS the destination).
That host is then looked up in the channel tables, searching in the order
the channels were defined.
The channel tables contain host names (full domain form) on the LHS and 
typically addressing data on the RHS.
Sometimes addressing data does not make sense (like in the local channel)
and some fixed string is substituted.  (See the addressing document for more
information about the domain matching process).
.PP
In the examples here, the RHS and the LHS are separated by a colon
and some white space.  The colon is in fact optional.  You may find the
colon missing from many of the sample files.
.NH 2
Setting Up Domain Tables
.PP
Domain tables are themselves specified by a domain.  When the
domain of a table
is concatenated with the LHS of any entry in the table, a
domain must result.
On the RHS are HOST values.
This two level hierarchy is intended as a compromise between
efficiency and flexibility.   Domain tables are matched longest first.
Take the `AC.UK' domain for example (United Kingdom, Academic community).
Thus when matching `CS.CAMFORD.AC.UK', if a table "CAMFORD.AC.UK" is
present this will be examined before "AC.UK".  If an entry `CAMFORD'
was found in "AC.UK" (thus corresponding to domain `CAMFORD.AC.UK'),
then it would be identified as a relay point.   If no domain table
is identified, then the domain tables are searched in order.  In this
case, if `CS.CAMFORD' were presented, tables would first be searched for
CS.CAMFORD and then (if no match) for CAMFORD.  If the latter were found
in "AC.UK", then `CAMFORD.AC.UK' would be identified as a relay domain
for `CS.CAMFORD.AC.UK'.
.PP
Note that if #define BOTHEND is set, then this will work for both
RFC 819 and NRS ordering (for those lucky enough to live in the UK).
.PP
The best place to start is with the "TOP" table which matches
rooted domains.  It should contain the domain on the LHS and an
appropriate gateway on the RHS (host name, using full domain notation).
For example, on an ARPANET site it might be:
.ne 8
.nf
.RS
.TS
l l .
mailnet:	mit-multics.arpa
bitnet:	wiscvm.arpa
csnet:	csnet-relay.arpa
uucp:	harvard.arpa
ac.uk:	ucl-cs.arpa
.TE
.RE
.fi
.ne 7
In the UK it might be:
.nf
.RS
.TS
l l .
arpa:	ucl-cs.ac.uk
csnet:	ucl-cs.ac.uk
dec:	ucl-cs.ac.uk
.TE
.RE
.fi
Other domains will be given explicit tables, derived from various sources.
There is a domain extension which can be useful. If
domains are not directly
connected, but require further verification (i.e. beyond the top
level or two) the entry should be of the form:
.sp
	partial-domain:\ \ \ \ host.domain\ \ \.\.\.\ \ \ host.domain
.sp
As with other entries, alternative partial domain names should
follow the first one, with the same RHS value.  The semantics of
the RHS is a source route from RIGHT TO LEFT.  The connect site is
specified by the right-hand host.  The left-hand host value will be
visible only
when ap=host reformatting is used (otherwise, the concatenation
of the preferred partial domain and the domain associated with the
table is used).   For example, if EECS.SALBRIDGE.AC.UK is reached through
CAMFORD.AC.UK, the entry in the ``AC.UK'' table would be:
.sp
	salbridge:\ \ \ \ salbridge.ac.uk\ \ \ camford.ac.uk
.sp
and would be suitable to route all mail destined for machines at
Salbridge via Camford.
.NH 2
Setting up Channel Tables
.PP
In section 3.8, a set of channels was determined.  Now the channel tables
should be built.
The LHS should contain the domain of the host served by that
channel
and the RHS should contain information used by the channel
to connect to the host.
The local channel requires the RHS to be the string specified by MLNAME (or
.I locname
from conf.c if it is not runtime tailored).
The smtp channel, for example, requires the Internet
address on the RHS of its channel table (e.g. "129.11.5.3").
The phone and
pobox channels don't use the RHS (but the RHS is typically set to the host
name).
.NH 2
How to Modify a Standard Configuration
.PP
This section assumes that you have identified a suitable configuration
on which to base your tables.  
.NH 3
Modifying Channel Tables
.PP
Each of the channels will have a set of hosts associated with it.
All channel tables associated with external hosts should be usable
as they stand.  The local channel table (used by both the local and
list channels) should have at least the following two entries:
.ne 12
.nf
.RS
.TS
l l .
\fIlocname\fR.\fIlocdomain\fR:	\fIlocname\fR
\fIlocname\fR:	\fIlocname\fR
.TE
.RE
You may also need the entries with
LHS \fIlocmachine.locname.locdomain\fR and \fIlocmachine\fR
if you are going to hide a number of sites behind a single name.
The RHS should still be \fIlocname\fR.
For example for UFOO.CSNET with locmachine set to "VAX1":
.RS
.TS
l l .
ufoo.csnet:	ufoo
ufoo:	ufoo
vax1.ufoo.csnet:	ufoo
vax1:	ufoo
.TE
.RE
.fi
.NH 3
Modifying Domain Tables
.PP
Domain tables associated with a number of domains will be provided.
All can be taken as given, except the table for the local domain.
To this should be prepended all alternative names of \fIlocname\fR.
The RHS of all these entries should be \fIlocname\fR.
The first entry in this table should be \fIlocname\fR:\fIlocname\fR\ .
Thus if UFOO is also know as UBAR and UPIG, the first entries in the
CSNET domain table should be:
.ne 6
.nf
.RS
.TS
l l .
ufoo:	ufoo
ubar:	ufoo
upig:	ufoo
.TE
.RE
.fi
.NH 3
Alias Tables
.PP
The next step is to set up a small alias file.  Assuming your own
login on UNIX to be ``bill'', the following initial file is suggested
as a example:
.ne 6
.nf
.RS
.TS
l l .
root:	bill
mmdf:	bill
postmaster:	bill
.TE
.RE
.fi
In particular, the \*M \fIcheckup\fR program will check to see
that an alias exists for ``postmaster'' and the \*M login
you specified in the configuration (herein assumed to be ``mmdf'').
It is suggested that one of the sample alias files be used as a model
for the alias file organization.  To handle the expansion of a
mailing-list ``foo'' from a file, the entries for the list ``foo''
should be of the form:
.ne 12
.nf
.RS
.TS
l l .
foo:	foo-outbound@\fIlocname\fR@list
foo-outbound:	:include:filename
foo-request:	list-maintainer
.TE
.RE
For example:
.RS
.TS
l l .
staff:	staff-outbound@ufoo@list
staff-outbound:	:include:/etc/alias/staff
staff-request:	bill
.TE
.RE
.fi
.NH 3
User and Mail-ID Tables
.PP
The final step is to set up the User and Mail-ID tables.  This step is only
necessary if you have enabled use of Mail-IDs.  The ``user'' table maps users to
Mail-IDs.  For example:
.RS
.TS
l l .
wiz:	unix-wizards-request
wiz2:	unix-wizards-request
joe:	joe
bob:	bob
.TE
.RE
The ``mailids'' table maps Mail-IDs to users.  For example:
.RS
.TS
l l .
unix-wizards-request:	wiz
joe:	joe
bob:	bob
.TE
.RE
In this example, mail addressed to ``unix-wizards-request'' would be delivered
to user ``wiz''.  However, both users ``wiz'' and ``wiz2'' could send mail and
the mail would appear to come from ``unix-wizards-request''.
.NH 2
How to Design Your Own Tables
.PP
This section is written for those who have the misfortune not to be
able to base their design on one of the standard configurations, or
who wish to make radical extensions to a standard configuration.
Rather than attempting to
describe many possibilities, it tries to explain what is being aimed at.
.PP
The terms DOMAIN and HOST are used here in a quite specific manner.
The domain name space is assumed to be
a global hierarchy with the most significant component on the RHS
(with apologies to the long suffering UK Mailpeople who will have to
live with back to front names).  Domains are implicitly fully
qualified (i.e. extending to the root).  For example: UK, AC.UK,
CAMFORD.AC.UK, and CS.CAMFORD.AC.UK are all domains.  CAMFORD is not
a domain.  Domains are administrative entities and may be synonymous
with a host.  A host is a computer
to which your computer makes a direct connection.  
The entries on the LHS of channel tables and
RHS of domain tables are hosts.  All host names must be unique within
a given system (to allow host -> channel lookup).
A useful convention
to ensure host uniqueness is to have the host name the same as the fully
qualified domain.  This convention is also likely to facilitate the
introduction of nameservers.
.PP
The key to remember is that the validation of an address is a two-fold
operation.  First, the domain or a parent of it must be found in the
domain tables (CS.CAMFORD.AC.UK, or CAMFORD.AC.UK, or AC.UK, or UK).
Once it is found, the RHS will contain a host or route to a host.
The second step is to try and find/verify the route to the host by
looking up the host or route elements in the various channel tables.
The basic premise is that you should be able to find hosts specified in
the RHS of a domain table by looking in the LHS of channel tables.
.NH 2
Hashing Your Tables
.PP
Once you have created the necessary tables to support the domains and channels
defined in your runtime tailoring file, you must combine these tables into a
hashed database.  If you defined CH_TB to be 'ch_tbseq', then you can skip
this section.  The dbmbuild program uses the definitions in the runtime
tailoring file to build the hashed database.  
It will report if it finds any tables missing.  See
\fIdbmbuild(8)\fR for details.
.XX
Run \fIdbmbuild\fR (no switches are required -- the default -n is correct).
.NH 2
Testing Your Configuration
.PP
Now test your configuration by running the \fIcheckup\fR
program (see \fIcheckup\fR(8)).  This program will report any inconsistencies
in your installation.  Items marked with asterisks are worthy of note.  Items
in brackets are advisory only.
.XX
Run \fIcheckup\fR.
.PP
Now test your tables by seeing if various addresses are accepted by  
\fIcheckaddr\fR(8) or \fIsend\fR(1).
You may want to give the -W option to submit via send or the -w option
to checkaddr.  This will tell you on what channel \*M chose to queue the
message.
.XX
Run \fIcheckaddr\fR or \fIsend\fR to check your tables and configuration.
.PP
If you find
a problem with an address (or more likely, a particular class of addresses), 
you should make the following checks.
Assume as an example that CAMFORD.AC.UK
is in question:
.IP (1)
If there is a domain table for AC.UK, the LHS `CAMFORD' should exist.
If there is no domain table for AC.UK, but there is one for UK, then
LHS `CAMFORD.AC' or `AC' should exist.  Finally if there is no
UK or AC.UK domain table, then on the LHS of the ``top''
table, `UK', `AC.UK', or `CAMFORD.AC.UK' should exist.
It is noted that the required domain should be equal to or a subdomain of
the concatenation of a table key (LHS) and the domain
associated  with the table
(I hope that the
pattern is clear from this).
.IP (2)
Then consider the RHS of the first entry identified above.  This should
be found on the LHS of the appropriate channel and (given that all
domain table RHSs must be unique) not found on the LHS of any inappropriate
channel.  This entry should identify either CAMFORD.AC.UK or the
preferred relay.  (If there are multiple matches, the first one is generally
selected according to the order of channel definitions in the tailoring file).
.IP (3)
These first two criteria should be sufficient to cause the message to
be queued, and the RHS of the channel table entry may be used by
the channel to deliver the message.
e many possibilities, it tries to explainmmdf/doc/administrators/title   444      0     12        2576  3671073136  11645 .ds M MMDFII
.de XX
.br
.ft R
.IP "\(rh\ \ \ [\ ]\ \ " 1i
..
.TL
Installing and Operating
MMDF II\u*\d
.AU
Douglas P. Kingston III
.AI
Ballistic Research Laboratory
Aberdeen Proving Ground, Maryland, USA.  21005
(301) 278-6651
.AU
Steve Kille
.AI
Department of Computer Science
University College London
Gower Street, London, England.  WC1E 6BT
(44) 1-387-7050 ext 733
.AU
Julian Onions
.AI
Department of Computer Science,
The University,
Nottingham, England.  NG7 2RD.
(0602) 506101 ext 3595
.AU
Daniel B. Long
.AI
CSNET Coordination and Information Center
BBN Laboratories
10 Moulton Street
Cambridge, Massachusetts.   02238
(617) 497-2777
.AB
The \*M mail system is a powerful but complex mail system
that requires a certain degree of care in its setup and maintenance.
This paper will try to give a step-by-step installation procedure
and will recommend how to maintain the \*M system on a day-to-day basis.
Topics covered include system generation,
runtime configuration, generation of channel
and domain tables, and tuning of the \*M system.
.AE
.FS
This document describes the second release of MMDF II, generally known
as MMDF IIb.  Credit and thanks are due to several people (besides the authors)
who contributed much time and effort to the MMDF IIb project, in particular
Phil Cockcroft of UCL, Jim Galvin of UDEL, Craig Partridge and
Dennis Rockwell of CSNET, and Howard Walter of BRL.
.FE
.bp
mise is that you should be able to find hosts specified in
the RHS of a domain table by looking in the LHS of channel tables.
.NH mmdf/doc/table.sample.kille   444      0     12       32144  3631170245  11134         Host and Domain Table examples

        Steve Kille

        Febraury 1984

This text is meant to illustrate by example, the mechanism for
setting up host and domain tables.   It is assumed that the
manual pages tailor(5.mmdf) tables(5.mmdf) have been read if
not understood.   This sheet contains three example:
 1) A simple Arpa host with minimal tables
 2) A UK host, illustrating handling of local domain with more
        than two components, and basic use of the list channel
 3) A complex example, illustrating use of locmachine, and various
        other subtle points



1.  Basic ARPA host

The following tailor values are assumed in these examples:

LOCDOMAIN       = ARPA
LOCNAME         = Ivy


For reference in the following tables, the structure is based
on the following table, channel, and domain specifications.

# Only one domain entry needed.  The table maps to a file, and
# then MDMN specifies the entry.  Note that the flags entry is
# probably redundant, but must always be set to 01.

MTBL    "ARPA",file="arpa",show="ARPA Domain"
MDMN    "ARPA",show="ARPA Domain",table="ARPA"

# Channel parameters, with fields not pertinent to address lookup removed

MCHN  local    show="Local Channel"

MCHN  smtp  show="SMTP channel"


First the channel tables will be looked at.  When domains are
reformatted, either host or domain values are output.  The
selection of host names is related to current DCA and JNT
domain policy.  Domains that are 'allowed' are also host names.
In other cases, abbreviated names are used.


# Local channel table

# note - RHS = LOCNAME in all cases
# values for LHS include LOCNAME, and all full domain names
# Northing else should ever be needed
ivy.arpa:ivy
ivy:ivy

# TCP/SMTP hosts channel table


# Only official HOST names in table
alta-coma.arpa:3.1.0.50
yuma.arpa:6.1.0.1
bbncca.arpa:8.0.0.2
bbnccb.arpa:8.1.0.2
bbnccc.arpa:8.2.0.2
bbnccd.arpa:8.3.0.2
bbn-labs-admin.arpa:8.4.0.2
bbncck.arpa:8.5.0.2
.....


#  ARPA Domain table


#
# Here are all of the names from hosts.txt
# Official name first.  HOST  name (as used in channel table) on RHS
alta-coma:alta-coma.arpa
yuma:yuma.arpa
bbncca:bbncca.arpa
bbnccb:bbnccb.arpa
bbnccc:bbnccc.arpa
bbnccd:bbnccd.arpa
bbn-labs-admin:bbn-labs-admin.arpa
bbn-labs:bbn-labs-admin.arpa
bbnl:bbn-labs-admin.arpa
bbncck:bbncck.arpa
......




2.  Baisc UK Host

The following tailor values are assumed in these examples:

LOCDOMAIN       = Camford.AC.UK
LOCNAME         = CS


For reference in the following tables, the structure is based
on the following table, channel, and domain specifications.


#  Camford.AC.UK - always the most local domain first.  The table
#  maps to a file, and the domin spec points to the table
# This domain is for other hosts within the camford domain
# (e.g physics.camford.ac.uk)

MTBL    "Camford",file="camford",show="camford.ac.uk domain table"
MDMN    "Camford.AC.UK",show="Camford.AC.UK domain",table="Camford"

# Then The AC.UK domain for other .AC.UK domains

MTBL    "AC-UK",file="ac-uk",show="AC.UK Domain"
MDMN    "AC.UK",show="AC.UK Domain",table="AC-UK"


# then AC.UK domain, which uses the same table as AC.UK
# The settings of name=AC  and dmn=AC.UK will let
# foo.ac  be mapped to foo.ac.uk

MDMN    "AC",show="AC.UK Domain (AC)",table="AC-UK",name="AC",dmn="AC.UK"


# Channel parameters, with fields not pertinent to address lookup removed

MCHN  local    show="Local Channel"

MCHN  list    show="via List-Channel"

# in this case, the list channel will use the local channel
# table, rather than needing its own table.  If foo is a list,
# The alis file would have entries:
#       foo:foo-outbound@camford@list
#       foo-outbound: :include:/etc/alias/foo
# This would force foo to be deliverd to the list channel.
# The list is then expanded in background

MCHN  niserc   show="via Sercnet with NIFTP"

MCHN uucp  show="with UUCP"



First the channel tables will be looked at.  When domains are
reformatted, either host or domain values are output.  The
selection of host names is related to current JNT
domain policy.  Domains that are 'allowed' are also host names.
In other cases, abbreviated names are used.  (e.g. until the
NRS is introduced, hostnames are foo rather than foo.ac.uk).

NOTE on NRS ordering (Americans ignore this): Internal tables
all use RFC 819 ordering.  Mapping for reformatting output
should be selected.


# Local channel table

# note - RHS = LOCNAME in all cases
# values for LHS include LOCNAME, and all full domain names
cs.camford.ac.uk:cs
cs:cs




# Sercnet Table

# Note that RHS is mapped to a Transport address by the lower
# level (NIFTP)
#
#  As neither DCA nor JNT would like to see names with .AC.UK
# in, UK host names are not full domains.
abxa:abxa/mail
apca:apca/mail
baca:baca/mail
# Some other camford hosts are reached over sercnet
bio.camford:bio.camford/mail

# UUCP table

# This might be used for both remote and camford hosts
eagle:eagle
physics.camford:camphys



The rest of this sample description shows the structuring of
the domain tables.


#  Camford.AC.UK domain table


# This contains all *.camford.ac.uk
# with alternative anmes on LHS, and HOST names on RHS
# Official names first
# Ourselves - note lowercase in tables
cs:cs
comp-sci:cs
# And the rest
bio:bio.camford
biology:bio.camford
physics:physics.camford




# AC.UK Domain table


#
#    List of *.AC.UK
# Note that some are in different channels
abxa:abxa
apca:apca
baca:baca
eagle:eagle



3.  Complex Example

A real extract is given, as this is more straightforeward to
check!

The following tailor values are assumed in these examples:

LOCDOMAIN       = AC.UK
LOCNAME         = Ucl-Cs
LOCMACHINE      = 44a


This mechanism should still work if LOCMACHINE is not set.
For reference in the following tables, the structure is based
on the following table, channel, and domain specifications.


#  Ucl-Cs.AC.UK - always the most local domain first.  The table
#  maps to a file, and the domin spec points to the table

MTBL    "Ucl-Cs",file="ucl-cs",show="Ucl-Cs domain table"
MDMN    "Ucl-Cs.AC.UK",show="Ucl-Cs.AC.UK domain",table="Ucl-Cs"

# Then AC.UK

MTBL    "AC-UK",file="ac-uk",show="AC.UK Domain"
MDMN    "AC.UK",show="AC.UK Domain",table="AC-UK"

# then AC domain, which uses the same table as AC.UK
# The settings of name=AC  and dmn=AC.UK will let
# foo.ac  be mapped to foo.ac.uk

MDMN    "AC",show="AC.UK Domain (AC)",table="AC-UK",name="AC",dmn="AC.UK"

MTBL    "ARPA",file="arpa",show="ARPA Domain"
MDMN    "ARPA",show="ARPA Domain",table="ARPA"

MTBL    "MAILNET",file="mailnet",show="Mailnet Domain"
MDMN    "MAILNET",show="Mailnet Domain",table="MAILNET"

# The top level domain is entered by setting Name and dmn to ""

MTBL    "Top-Level",file="top",show="Top Level Domain"
MDMN    "Top-Level",show="Top level Domain",table="Top-Level",name="",dmn=""


# Channel parameters, with fields not pertinent to address lookup removed

MCHN  local    show="Local (44a)"

MCHN  list    show="via List-Channel"

MCHN  sring show="with SMTP"  (around local ring)

MCHN  satnet  show="via Satnet with SMTP",ldomain="ARPA"

MCHN  niserc   show="via Sercnet with NIFTP"



First the channel tables will be looked at.  When domains are
reformatted, either host or domain values are output.  The
selection of host names is related to current DCA and JNT
domain policy.  Domains that are 'allowed' are also host names.
In other cases, abbreviated names are used.


# Local channel table

# note - RHS = LOCNAME in all cases
# values for LHS include LOCNAME, and all full domain names
ucl-cs:ucl-cs
ucl-cs.ac.uk:ucl-cs
ucl-cs.arpa:ucl-cs
# These two ar only needed if LOCMACHINE is set
# The LHS should be LOCMACHINE and LOC-MACHINE+NAME+DOMAIN
44a:ucl-cs
44a.ucl-cs.ac.uk:ucl-cs


# List channel table

# This has only one entry, and is only needed if LOCMACHINE is
# set to give transparent routing between a number  of machines
# This enables lists on other machines to be referred to iin
# the same manner
# in this case, an alis file entry might be
#       foo:foo-outbound@44c-list
#       foo-outbound: :include: /etc/alias/foo@44c
# The @44c in the second line ensures that mail sent to foo-outbound
# works correctly
44a-list:44a-list


# Ring channel table (abbreviated)


# This is the table associated with the local ring
# The local references need NOT be removed, and so all machines
# can use the same table.  These names are not seen by
# users unless typed explicity.  RHS is transport address.
ucl-cs.arpa:128.16.9.3
44a:smtp44a
44b:smtp44b
44c:smtp44c
...
# References to other list channels are included so that non-local
# lists will be sent around the local net to the next machine
44a-list:smtp44a
44b-list:smtp44b
44c-list:smtp44c
...



# TCP/SMTP hosts channel table


# Note special reference to ourselves at the top of the table
# To protect against table update errors
# NOte that multiples of the RHS are included to do the
# reverse mapping correctly from all registered IP addresses
ucl-cs.arpa:128.16.9.3
ucl-cs.arpa:14.0.0.9
# This is needed for convenient loopback testing
# (e.g to use steve@ucl-cs@satnet  rather than steve@ucl-cs.arpa@satnet
ucl-cs:128.16.9.3
#
#    Arpa hosts are all full domain names.   RHS is familiar numbers
#  Note that no abbreviated names are in this table
alta-coma.arpa:3.1.0.50
yuma.arpa:6.1.0.1
bbncca.arpa:8.0.0.2
bbnccb.arpa:8.1.0.2
bbnccc.arpa:8.2.0.2
bbnccd.arpa:8.3.0.2
bbn-labs-admin.arpa:8.4.0.2
bbncck.arpa:8.5.0.2
.....



# Sercnet Table

# Note that RHS is mapped to a Transport address by the lower
# level (NIFTP)
#
#  As neither DCA nor JNT would like to see names with .AC.UK
# in, UK host names are not full domains.
abxa:abxa/mail
apca:apca/mail
baca:baca/mail
bdga:bdga/mail
bdgb:bdgb/mail
bedford:bedford/mail
bgxa:bgxa/mail
bhga:bhga/mail
bhgb:bhgb/mail
bhia:bhia/mail
bhvs:bhvs/mail
bihh1:bihh1/mail
bihh2:bihh2/mail
bihh3:bihh3/mail
# UK users - note domain ordering!!
# US users - ignore this entry
hcig.nott:nott.hcig/mail
nsga:nsga/mail





The rest of this sample description shows the structuring of
the domain tables.


#  UCL-CS.AC.UK domain table


# Map all known names below the (transparent) UCL-CS.AC.UK
# Thus string 44b maps into DOMAIN 44b.UCL-CS.AC.UK
# which maps into HOST 44b
# which is found in the ring channel  table
# (or in the case of 44a in the local channel  table or 44a-list
# in the list channel table)
44a:44a
44b:44b
44c:44c
.....
44a-list:44a-list
44b-list:44b-list
44c-list:44c-list
....





#  ARPA Domain table


# idis is at top of file as it is used as a relay host
#  A relays entry must come before any hosts realying
# through it to get the official name correct
usc-isid:usc-isid.arpa
#
#  The following hosts  won't talk TCP to UCL
# (or used not to at least).  Therefore we palm them off
# on someone else (boo hiss)
ada-vax:ada-vax.arpa,usc-isid.arpa
isi-vaxb:ada-vax.arpa,usc-isid.arpa
ajpo:ada-vax.arpa,usc-isid.arpa
vaxb:ada-vax.arpa,usc-isid.arpa
almsa-1:almsa-1.arpa,usc-isid.arpa
alta-coma:alta-coma.arpa,usc-isid.arpa
ames-tss:ames-tss.arpa,usc-isid.arpa
ames-67:ames-tss.arpa,usc-isid.arpa
#
# Here are all of the names from hosts.txt
# Official name first.  Host name on RHS
alta-coma:alta-coma.arpa
yuma:yuma.arpa
bbncca:bbncca.arpa
bbnccb:bbnccb.arpa
bbnccc:bbnccc.arpa
bbnccd:bbnccd.arpa
bbn-labs-admin:bbn-labs-admin.arpa
bbn-labs:bbn-labs-admin.arpa
bbnl:bbn-labs-admin.arpa
bbncck:bbncck.arpa


# Mailnet Domain table


# All domains are reached through MIT relay
# the official name of mit-multics si found in
# the Arpa domain
# There is no need for a mailnet channel
carnegie:carnegie.mailnet,mit-multics.arpa
cornella:cornella.mailnet,mit-multics.arpa
dickinson:dickinson.mailnet,mit-multics.arpa
durham:durham.mailnet,mit-multics.arpa
educom:educom.mailnet,mit-multics.arpa



# AC.UK Domain table


#  Lots of amazing names for UCL at the top
ucl-cs:ucl-cs
ucl:ucl-cs
zuxa:ucl-cs
zuxa80:ucl-cs
research:ucl-cs
nss:ucl-cs
unknown-host:ucl-cs
zuxc:ucl-cs
ucl-niftp:ucl-cs
ucl-tg:ucl-cs
src-44a:44c
#
#    Some odinary domain names
abxa:abxa
apca:apca
baca:baca
bdga:bdga
bdgb:bdgb
bedford:bedford
bgxa:bgxa
#  These entries will map HCIG.NOTT, HCIG.NOTTINGHAM
#  NOTT, NOTTINGHAM, HCIG (and all the permutations with .AC or
#  .AC.UK appended) into HCIG.NOTT.AC.UK
hcig.nott:hcig.nott
hcig.nottingham:hcig.nott
nottingham:hcig.nott
nott:hcig.nott
hcig:hcig.nott
#
#  Some examples of Relayed connections.
# Note that the rightmost component should be the host
#  directly connected to.  The leftmost component should be
# The Name required for 'Host' format output
dlia:dlia,rlgb
rlia:rlia,rlgb
rlib:rlib,rlgb
camphx:camphx,caga
caxa:camphx,caga
#  Camring is routed first ot caga, then camjenny, finally to camring
camring:camring,camjenny.ac.uk,caga
camjenny:camjenny,caga
camsteve:camsteve,caga
ecsvax:ecsvax,edxa
rco:rco,edxa
edxd:rco,edxa
eagle:eagle,ukc.ac.uk,rco.ac.uk,edxa
ktda:eagle,ukc.ac.uk,rco.ac.uk,edxa
kent:eagle,ukc.ac.uk,rco.ac.uk,edxa
#
#    Hosts connected to over the PSS Network
aucc:aucc
cardiff:cardiff
cardf:cardiff
essex:essex


#  Top level domain

#  UUCP and Bitnet through berkeley gateway
#  Note that the HOST name of the relay must be given
uucp:ucb-vax.arpa
bitnet:ucb-vax.arpa
# Mailnet relay through MIT.  Domains in the mailnet table can
# be abbreviated.  Others will only work if specified in full
mailnet:mit-multics.arpa

lta-coma.arpa:3.1.0.50
yuma.arpa:6.1.0.1
bbncca.arpa:8.0.0.2
bbnccb.arpa:8.1.0.2
bbnccc.arpa:8.2.0.2
bbnccd.arpa:8.3.0.2
bbn-labs-admin.arpa:8.4.0.2
bbncck.arpa:8.5.0.2
.....



# Sercnet Table

# Note that RHS is mapped to a Transport address by the lower
# level (NIFTP)
#
#  As neither DCA nor JNT would like to see names with .AC.UK
# in, UK host names are not full domains.
abxa:abxa/mail
apca:apca/mail
bacmmdf/doc/dbmdatabase   444      0     12       55302  3620510345   7713 .TL
The MMDF Database
.br
(somewhat obsolete)
.AU
Steve Kille
.AI
Department of Computer Science
University College London
.NH
Overview
.PP
This design specifies modifications to the database access procedures
of the MMDF mail system.
The changes are motivated by the expansion of the names database for the
SRI environment.
The major changes involve the reorganization of the database for more
efficient random access methods that would reduce the processing
inefficiencies of the sequential access of large databases.
The changes take advantage of the database management dbm(3)[*]
.FS
[*] The notation dbm(3) refers to the documentation as found in the Unix
Programmer's Manual, Vol. 1, e.g., the database documentation is in
Section 3 under the title
.B dbm.
.FE
libraries
of V7 Unix.

.NH 1
Scope of Changes
.PP
All changes are restricted to two modules of MMDF.
One module is part of the
.B submit
process
and the other module is shared by the
.B submit,
.B deliver,
and
.B channel
processes.
There are potential efficiencies that could be gained in other modules due
to the use of the new design but these changes are left for a future effort.
The include file
.B ch.h
is compacted by eliminating obsolete fields in the channel
structures.

.NH 2
Affected Modules
.PP
The two modules that are affected are
.B ch_table.c
and
.B adr_submit.c.
.B ch_table
is completely rewritten to use the dbm(3) library.
Two procedures are respecified because their functions have been altered.
The remaining procedures are reimplemented.
The procedure
.B loc_alsrch
in the module
.B adr_submit.c
is modified to remove references to and logic for the obsolete procedure
.B ch_tseek
and to add logic to use the procedure
.B ch_tsetnum.
.NH 2
Interfaces
.PP
The interfaces to the public procedures of ch_table.c are preserved.
The re-implemented procedures have the same names, accept the same
arguments, and
return the same results as the procedures they replace.
The respecified procedures have different names and arguments to
ensure that incompatible calls to the old procedures would be caught and
prevented from improperly linking to the new procedures.
Some of the re-implemented procedures have a boolean argument that indicates
whether the search
should start at the beginning of the file or where the pointer is currently
located.
The argument now indicates whether the partial ordering information stored
in the database should be used to detect possible name circuits.

.NH 2
Affected Procedures
.PP
Only one procedure,
.B loc_alsrch,
in the module
.B adr_submit.c
is modified.
The modification is described above.
All other procedures in this section are in the module
.B ch_table.c.
.SH
ch_tblopen
.IP
This procedure is removed completely and
its replacement,
.B ch_tdbminit,
is made completely internal to the module.
It calls the
.B dbminit
procedure for the first database access
to initialize the database.

.SH
ch_tseek
.IP
This procedure is removed completely.
It is replaced by the procedure
.B ch_tsetseq
which sets the minimum acceptable point for an entry to be in the partial
ordering of the database.
.SH
ch_ttell
.IP
This is really a macro defined in the file
.B ch.h.
It is removed and the procedure
.B ch_tgetseq
is defined in its place to return the partial ordering of the most recently
accessed entry in the database.
.SH
ch_h2adr
.IP
This procedure is rewritten internally to use the dbm(3) library.
The argument
.B first
is redefined to be a flag to indicate whether partial ordering of the entries
is to be enforced.
All other aspects of the procedure remain the same.
.SH
ch_ad2first
.IP
This procedure is rewritten internally to use the dbm(3) library.
The external interface for this procedure remains the same.
.SH
ch_h2first
.IP
This procedure is rewritten internally to use the dbm(3) library.
The argument
.B frst
is redefined to be a flag to indicate whether partial ordering of the entries
is to be enforced.
All other aspects of the procedure remain the same.
.SH
ch_h2chan
.IP
This procedure is rewritten internally to use the dbm(3) library.
The external interface for this procedure remains the same.
.PP
The procedures no longer access each other internally.
They instead set up their own calls to the dbm(3) procedure
.B fetch
and process the returned data themselves.
The linkages between data records is defined in the logic of the access
procedures.

.NH 1
Addressing Database Design
.PP
The support provided by the dbm(3) library consists of procedures to
store and retrieve data items using arbitrary ascii strings as keys.
Since no provision is made for accessing the internal representation of
keys or data pointers, all linkages are maintained as ascii keys.
The interpretation of a data item as a key for further searches of the
database is therefore embedded in the MMDF access procedures.
The subsections below define these relationships between data items and
other keys.

.NH 2
Database Internals
.PP
The sequential database is composed of lists of key and value pairs.
Each list is a separate text file that is appropriate only in certain
contexts.
An address or name is either a mailbox or a host depending on whether it
is in the file used for aliases or the file used for channel host addresses.
The formats however are identical.
.PP
A data entry is composed of three components; the keyword that is used for
access, a partial ordering number, and a list of one or more values
separated by FS ('\\034') characters.
See Figure 2 and Section 5.2.1 for the details and format of the entry.

.PP
A value is composed of a channel code character followed by a printable
ascii string.
There are three basic types of values used in the data entries.
They provide single, multiple, and indirect values for the keys.
All of the types are transparent to the database procedures and are returned
unmodified to the calling procedures for processing.

.IP Single 10
This is the simplest form of database value.
It is a single ascii string that is passed back to the address processor.
The value can be the real name associated with an alias, a host name
associated with a channel, or a host address associated with a host name.

.IP Multiple 10
A multiple value is a comma ',' separated list of single values.
All of the single values are associated simultaneously with the key.
Such a value is used for small distribution lists or for aliasing a name
to mailboxes for a user on different machines.

.IP Indirect 10
The indirect value is an extension of the multiple value.
A value that starts with a left angle bracket '<' character is treated
as a filename (not including the '<') where a list of values may be found.
Distribution lists are implemented with indirect values.
The referred to file is not searched but is read sequentially for
values that are then recursively processed against the database.

.PP
Only one file is used in the dbm(3) library[*].
.FS
[*] There are really two files, one data file and one key hash file,
but they are considered as one in this context since they are intimately
connected in the dbm(3) library.
.FE
Identical keys that are in different lists in the sequential files are
stored under one key in the random access database.
The storing of identical keys is described in the next section.

.NH 3
Partial Ordering Numbers
.PP
The ordering of entries in the sequential database is used by the sequential
database to detect name circuits.
The first two bytes of an entry datum are an unsigned 16 bit ordering number
that indicates the partial ordering between any two entries in the database.
The numbers are assigned by the database management program.
Section 3.2 Name Circuit Prevention describes its use.

.NH 3
Channel Encoding
.PP
The context information implied in the multiple files used for the sequential
database must be preserved in the random access database.
This context is preserved by prepending a code character to the value.
This code is either the official name code or the code character
assigned to a channel.
The code is used both to check whether the value is valid
for a particular channel and to map a name(host) to a channel.
.PP
Identical keys may be stored in more than one file in the sequential database.
This feature is implemented in the random access database by concatenating
the entries from the multiple files and separating them by the FS ('\\034')
character.
The first character of each entry in such a list is the code character for
that entry.
Note that the first character is a code only for these multiple entries and
not for the multiple comma ',' separated values described above.
The code character is stripped by the procedures and not returned.
The sequential order of the entries implies the partial ordering imposed
in the sequential database.

.NH 3
Official Names
.PP
An official name is the primary identifier for a host.
Other names are allowed but the official name is the only name that is
universally known by all other hosts.
Alternate or alias names are available but MMDF converts these names to
the official name before the address is used in the system.
.PP
By convention, the key associated with the first occurrence of a value
(address) in the sequential file is the official name.
It may be found from an address by searching for the first occurrence of the
address in the value field and returning the key as the official name.
The official name is found from an alternate by first finding the alternate
and then by taking its value (address) and searching for the its first
occurrence.

.PP
There are two entries stored in the database for an official name.
One is a normal name-key with address-entry and the other is
an address-key and name-entry.
The name-entry has a code character of '='.
An official name is recognized when the database is being built and the
inverse of the entry is generated by exchanging key and entry and storing
the result.
The official name is also stored as the first entry of all other (alias)
entries that have the same address as the official name.
The official name is retrieved from a value (address) by retrieving
the entry under the address key.
It is retrieved from an alternate name by retrieving the entry with the
alternate name keyword and then selecting the official name entry.

.NH 3
Name List Redirection
.PP
Name list redirection is not part of the sequential search database
procedures.
The procedure
.B alst_proc
parses the value returned from reading the current address.
The address line may either come from an input line or
a database value due to the recursive
logic of the address parsing procedures.
The redirection is done on all addresses that start with the '<' character.
This character is transparent to the database system and is considered part
of the returned value.
The partial ordering numbers are used by the database procedures to detect and
reject name circuits generated when keywords in the indirect files do not
map to out of order entries in the database.
.NH 3
Reserved Code Characters
.PP
The only reserved code character is '='.
This character may not be used for channel codes because it is used to
identify an official name.
It should be considered to be the official name channel code character.
The FS ('\\034') character is also reserved for use as the entry separator.
All other characters are acceptable.

.NH 2
Name Circuit Prevention
.PP
A name circuit exists when a name key is self referencing though an
arbitrary number of name translations.
Such a name would put the address checking procedures in an infinite loop.
The sequential database defeats some cases of self reference by forcing
the search of any one list at any one level to be one pass.
This algorithm forces a partial ordering of all addresses;
but it is only enforced for one file at a time.
.PP
The random access database contains an ordering number for each entry in the
database that identifies the order in which the entries were stored in the
database.
This partial ordering of the
entries prevents name circuits by forcing all translations to be
forward references that would eventually terminate with the end of the list.
The check for partial ordering is under the control of the argument to the
the applicable procedures that controlled whether the sequential search
started from the beginning of the file.
The partial ordering is not enforced if the argument is TRUE to be compatible
with the old procedures.

.NH 1
Database Access Procedures
.PP
This section describes the algorithms used to implement the database with
the dbm(3) package.
These procedures replace those procedures of the same name in the file
.B ch_table.c

.NH 2
Access Initialization
.PP
The procedure
.B ch_tblopen
is removed and it is replaced by the procedure
.B ch_tdbminit
which is callable only by the procedures
within the module.
There is no argument list.
An internal static flag is maintained by the procedure to
record if the database file is initialized.
During the course of this procedure it will call the dbm(3) procedure
.B dbminit
and set the flag so further calls to the procedure will not attempt to
initialize the database again.

.NH 2
Partial Ordering Control
.PP
The old procedure
.B ch_tseek
and the macro
.B ch_ttell
are deleted from the file along with all references to them.
They are replaced by the procedures
.B ch_tsetseq
and
.B ch_tgetseq
which control the partial ordering in the database.
.B ch_tgetseq
returns the sequence number of the most recently accessed entry and
.B ch_tsetseq
changes the internal sequence number value to its argument.
A sequence number is a value of type unsigned short.

.NH 2
Database Record Access
.PP
All records in the database are accessed by the internal procedure
.B ch_fetch.
It correctly forms its string argument into a key for the dbm(3) procedure
.B fetch
and decomposes the returned datum into the partial ordering number and
a vector of char pointers to the individual values.
The number and the vector are stored in the data structure passed to the
procedure.
Success return is indicated by returning the pointer to the data structure
and failure is indicated by a NULL pointer.

.NH 2
Key to Value Access
.PP
This function is done by the procedure
.B ch_h2adr.
The 'name' parameter is passed to the internal procedure
.B ch_fetch.
The returned structure may have multiple entries because the same key may
be used for more than one channel or alias list.
The code character of each entry is checked against the code character
stored in the channel structure and
the entry that matches is returned in the pointer parameter 'buf' with its
code character stripped off.
TRUE is returned if a match was found and FALSE is returned if
.B ch_fetch
can not find the key or if the code character for the channel does not
match an entry in the datum.

.NH 2
Value to Primary Key Access
.PP
This function is performed by the procedure
.B ch_ad2first.
The parameter 'adrstr' is passed to the procedure
.B ch_fetch
as described above and the returned structure is scanned for an entry whose
code character matches the official name code character '='.
The result is returned via the pointer parameter 'buf'.
TRUE is returned if a match was found and FALSE is returned if
.B ch_fetch
can not find the key or if there is no official name.

.NH 2
Key to Primary Key Access
.PP
This function is performed by the procedure
.B ch_h2first.
The parameter 'str' is passed to the procedure
.B ch_fetch
and the returned structure is searched for an official name entry.
The official name is returned via
the pointer parameter 'buf'.
TRUE is returned if a match was found and FALSE is returned if
.B ch_fetch
can not find the key or if there is no official name entry.
The parameter 'str' is returned if no official name is found.

.NH 2
Key to Any Channel Access
.PP
This function is performed by the procedure
.B ch_h2chan.
The parameter 'hostr' is passed to the procedure
.B ch_fetch.
The pointer parameter 'adrstr' is used to return either the official name
if it is found or the parameter 'adrstr' if none is found.
The procedure returns NOTOK if there is no official name.
Each value except the official name in the entry is used in turn to find
the first channel available for the key.
The code character of the value is passed to the procedure
.B ch_c2struct
which will either return a channel pointer or the value NOTOK.
If there is no channel to match any of the code characters then
.B ch_h2chan
also returns NOTOK.
If the channel found by
.B ch_c2struct
is the local channel and local delivery is ok then the procedure returns OK.
Otherwise, it returns the channel pointer.

.NH 1
Database Management Program
.PP
The program
.B dbmbuild
generates the database from ascii files of the same format
as the sequential database.
It does not attempt to do updates to an existing database; rather, it
regenerates the entire database each time and the resultant files are
moved into place with other commands.

.NH 2
Inputs
.PP
The input to the program is in the current sequential file format.
The code character is passed to the program as a command line argument.
A command line argument selects official name processing; the default
action is no processing.

.KS
.DS
line ::= text eol
text ::= comment | entry
comment ::= '#' <any printable ascii string>
entry ::= key sep value
key ::= <lower case printable ascii string>
sep ::= ':' | <space> | <tab>
value ::= <printable ascii string with optional spaces or tabs>
eol ::= '\e012'
.ce
Fig . 1, Input Line format
.DE
.KE
.NH 2
Outputs
.PP
The output file is generated into two files.
The file\fI.dir\fP file contains a bitmap directory of the actual
keys and values
located in the file\fI.pag\fP file.
The file is generated by the
.B store
procedure of the dbm(3) library.
See the Unix Programmer's Manual, Vol 1, dbm(3x), for further details on
the database library.

.NH 3
Record Format
.PP
A record consists of a key and a datum.
The key is an arbitrary ascii text string but for matching purposes
it is all lower case with all extraneous white space compressed out.
The datum is an arbitrary variable length byte string.
The length is controlled by a count field.
The subfields of the datum are defined below.
There is no white space separating any of the fields.
They are either defined by position and count or by the field separator
(FS) character.
.KS
.DS
datum ::= serial-number entry-list
serial-number ::= <two bytes used as unsigned short>
entry-list ::= entry
	     | entry-list FS entry
entry ::= code-char value
code-char ::= <printable ascii char matching channel char>
value ::= <arbitrary compressed printable ascii string>
FS ::= '\e034'
.ce
Fig. 2, Internal Record Format
.DE
.KE

.NH 2
Algorithms
.PP
These algorithms convert a sequential database into the random access
database suitable for the online MMDF.

.NH 3
Partial Ordering Control
.PP
At the start of processing an input file the entry under the key
"Local-Name" is retrieved and the serial number field is retained for
further processing.
If the entry is not found then the serial number is initialized to 1 and
the key "Local-Name" is stored.
The name is stored again at the end of processing with a datum containing
the updated serial number.
Any values stored under the key are preserved so that the key may be
used to store other information in addition to the serial number.
In future, this entry can be used to store the host's local name instead
of the separate file currently used.


.NH 3
Channel Encoding
.PP
Channel code characters are passed to the program with the sequential
data file.
The code characters are retained by the program and prepended to values
as they are stored in the database.
There is currently one code character assigned for each channel in the
system as well as the '=' character which identifies an official name.

.NH 3
Identical Keys
.PP
Identical keys are allowed in the sequential database.
They are implemented in the random database by concatenating the entries
according to the record format in Fig 2.
The key part of an input line is retrieved before it is stored into the
database.
The retrieved datum is used instead of an initialized empty datum
whenever the retrieval returns a valid datum.
The new entry is appended to the end along with its code character and
separated from the rest of the retrieved entry by the FS character.

.NH 3
Official Name Processing
.PP
An official name in the sequential database is the first name associated with
a particular address.
Official names are detected and marked in the random access database in a
similar manner.
Official names are entered into the database under the control of a command
line argument; the default action is to not process official names.
.PP
As the sequential input file is read for entries to store in the database, the
value, i.e., address, part is \fIfetch\fPed to see if it is present.
If the
.B fetch
returns a valid pointer to an official name then it will be used to build
the datum for the current input line.
If the pointer is valid but the value is not an official name then
the official name is appended using the format from Fig 2 and datum is
stored back into the database.
A NULL pointer indicates that the current entry is an official name.
The value from the address part (from above) is used as a key and the key
part of the current input line is used as the value for the new entry.
The entry has a '=' code character and the value
is 'canonicalized', i.e., the first character and the
first character after a '-' are capitalized.
This official name entry is always stored as the first value in the datum for
all names, including itself, that refer to it.
The string is stored in every datum already canonicalized in order to
save processing time for MMDF.
.NH 3
Name Circuit Detection
.PP
A name circuit is exists if there is a valid key to the database that is
equal to the value in the current input line that is not
the inverse key of an official name.
This can occur only if there was a previous entry in the database that
had a key equal to the value of the current entry.
The input line is stored as an additional value in the entry.
(See Section 5.3.3).
There is a change in the way name circuits are rejected by the database
because the partial ordering for the entry remains the same.
Any identical keys which would have been valid in the sequential database
because they were positionally correct are no longer valid because they
are, in effect, moved up to be just below (actually part of)
the first, out of order key.
This restriction prohibits certain obscure or clever alias list
manipulations.
.PP
Forward references, i.e., the value field of a previous entry is equal to
the key of the current entry, are allowed in the sequential database since
the linkages will terminate at the end of file.
These are allowed in the random access database too because the database
procedures will check sequence numbers when requested by the calling
procedure and reject all fetches that are not
forward references thereby preventing the name circuit.
.NH 2
Error Processing
.PP
All system and database inconsistency errors are reported to the
operator on the standard error output.
All improperly formed entries
are rejected.

again at the end of processing with a datum containing
the updated serial number.
Any values stored under the key are preserved so that the key may be
used to store other information in addition to the serial number.
In future, this entry can be used to store the host's local name instead
of the separate file currentmmdf/doc/addressing   444      0     12      106316  3631167646   7647 .nr Fn 0 +1
.de FN
.ps -2
\u\\n+(Fn\d
.ps +2
.FS \\n(Fn
\n(Fn. 
..
.TL
Addressing in MMDF II
.AU
Steve Kille
.sp
<Steve@CS.UCL.AC.UK>
.AI
Department of Computer Science
University College London
.AB
The Multi-channel Memorandum Distribution Facility (MMDF) is a
powerful and flexible Message Handling System for the
.UX
operating system.  It is a message transport system designed to
handle large numbers of messages in a robust and efficient
manner.  MMDF's modular design allows flexible choice of User
Interfaces, and direct connection to several different
message transfer protocols.
.PP
This paper is intended as a sequel to the paper presented by
Doug Kingston at the 1984 Usenix meeting in Salt Lake City \*([.Kingston84\*(.].
A brief technical overview of MMDF is given, and then two aspects are
considered in more detail.
First, the table-driven approach taken by MMDF to
handling a structured address space is described, and then the
extension of this approach to use with distributed nameservers
is considered.
Several distributed message systems using similar, but
distinct, message and address formats have emerged.  The
message reformatting
approach taken by MMDF to allow interworking is described.
Finally, a comparison with other systems is
made.
.AE
.SH
Introduction
.PP
The Multi-channel Memorandum Distribution Facility (MMDF) is a
powerful and flexible Message Handling System for the UNIX
operating system \*([.Crocker79,\|Kingston84\*(.].
It was originally developed for use on
Csnet \*([.Comer83\*(.],
to provide message relaying services.  A version of MMDF
stabilised in 1982 is the production system used by most Csnet
sites.
MMDF\ II, which is described here, has many
changes relative to the earlier system.
MMDF\ II is on beta test at several
sites both in Europe and the US, and is expected to be
fully available before this paper is presented.
Although it is not currently a production system,
it has been used to provide message
relaying services for about two years at the
Ballistic Research Laboratories, Maryland and at University
College London, and more recently on the Csnet hub machine.
Each of these sites has a variety of particularly demanding requirements,
and are regarded as good testbeds.
.PP
The main requirements and features of MMDF\ II are as follows:
.IP (1)
Robust, reliable, and efficient message relaying.  Particular
emphasis is placed on reducing message loss  to an absolute
minimum.
A two phase
warning is used to handle message transfer delays.
.IP (2)
Support for multiple Message Transfer Protocols, and general
interworking between them.  Currently supported are: Phonenet \*([.Crocker79\*(.],
the Arpanet Simple Mail Transfer Protocol (SMTP) \*([.Postel82\*(.],
the UK JNT
Mail Protocol (Grey Book) \*([.Kille84\*(.],
UUCP Mail \*([.Nowitz78\*(.],
and indirectly CCITT X.400 protocols
by use of
the EAN system developed at University of British Columbia \*([.CCITT83,\|Neufeld83\*(.].
.IP (3)
Support for a wide range of User Interfaces.
At present, v6mail, Shoens Mail, msg/send \*([.Vittal81\*(.],
and MH \*([.Borden79\*(.],
can all be used with
MMDF\ II.
.IP (4)
Authentication \- that is submission time verification that a
message conforms to RFC 822 (the standard on which  the generic
message format for all
MMDF\ II messages is based) \*([.Crocker82\*(.],
and that the source is correctly specified.
.IP (5)
Authorisation \- that is the restriction of message flow for
policy reasons, or assigning responsibility for a given message
transfer (possibly for billing purposes) on the basis of
the networks, users, and hosts involved.  This is discussed
more fully in \*([.Brink85\*(.].
.IP (6)
Submission time address checking.  This allows for maximum
address checking within the User Interface, and for more
efficient network use by protocols such as SMTP and Phonenet.
.IP (7)
Flexible handling of local addresses, including aliasing,
delivery to pipes and files, and enhanced support for
distribution lists by use of a list channel.
This is described
in \*([.Kingston84\*(.].
.IP (8)
User configurable delivery time options.  This allows messages
to be sorted according to destination and message header,
and then
processed accordingly.
Processing can include: filing;
redistribution; standard delivery; delivery to a pipe; user
notification; destruction.
This is described in more detail in \*([.Kingston84\*(.].
.IP (9)
Straightforward runtime configuration (by use of a
.B "tailor file" ).
.IP (10)
Flexible handling of remote addresses.
.IP (11)
Message reformatting to support several environments
using protocols similar to, but different from, RFC 822.
.PP
These last two points are the main subject of this paper.
.SH
MMDF System Structure
.PP
To describe the address handling, it is first necessary to
understand the basic system structure.
.DS B

                   -----------      -----------
                   |  User   |      |Protocol |
                   |Interface|      | Server  |
                   -----------      -----------
                        |         /
___________________     |       /
|                  \\\\\\\\    |     /
|                  -----------
|                  | Submit  |
|                  |         |
|                  -----------  . . . . . . . .->
|                       .                         Message
|                       .
|                       .                         Queues
|                  ----------- <- . . . . . . . .
|                  | Deliver |
|                  |         |
|                  -----------
|                /      |     \\\\\\\\
|              /        |       \\\\\\\\
|            /          |         \\\\\\\\
| -----------      -----------      -----------
| |  List   |      |  Local  |      |Protocol |
| | Channel |      | Channel |      | Channel |
| -----------      -----------      -----------
|______|


              MMDF Process Structure

.DE
.PP
The key to the operation of MMDF\ II is the process
.B submit .
Submit accepts messages in one of two modes:
.IP (i)
\'Message' mode, where a message is accepted on the standard
input and addresses extracted from specified message header fields.
.IP (ii)
\'Protocol' mode, where addresses are first checked in an SMTP
like negotiation \*([.Postel82\*(.],
and then the message is accepted.  This mode is usually invoked
by another process interacting with submit by use of a pair of
pipes.
.FN
See
.B "pipe(2)"
in any UNIX reference manual.
.FE
.PP
Submit is invoked both by User Interface processes to send local
messages, and by Message Transfer
Protocol servers (e.g. an SMTP server, rmail,
.FN
rmail is the process invoked by UUCP to transfer mail.
.FE
or a JNT Mail Protocol server).
Submit verifies the addresses, and then checks conformance to
authorisation policies.  It also authenticates the source and
format of the message.  The most important part of this
authentication
is that locally generated messages have a correct originator
specification (i.e. that of the invoker of submit), and that
messages originated remotely are only submitted by a process
with the appropriate privileges.
Finally, the message is queued, with the text of the message
(both body and header) in one file, and the associated
addresses and control information in another file.
Each address is associated with a
.B channel ,
and each channel has an associated queue, managed independently
as a
directory containing files which are links to the appropriate
address file.
The address to channel binding is discussed later.
.PP
Each channel has a process for delivering
messages associated with it.
More than one channel may use the same channel
process (and thus protocol), as channels may have: different
(sets of) hosts associated with them; different routes to the
same hosts; or a different quality of service to the same hosts.
.FN
This flexible mapping becomes particularly important when applying
authorisation on the basis of multiple criteria.
A fuller discussion is given in \*([.Brink85\*(.].
.FE
There are two special channels:
.IP (1)
Local channel.  This delivers messages to local mailboxes,
including any user specified processing.
.IP (2)
List channel.  This allows a list to be expanded in two phases,
by passing the message back to submit with a modified return
address.  This approach gives several advantages, particularly when
handling large lists.
.PP
Both of these channels are discussed in more detail in \*([.Kingston84\*(.].
.B Deliver
is a process which reads the queue associated with one or more
channels, and invokes a channel process with which it interacts through
a pair of pipes.
Deliver will read addresses from the queue, and pass them in turn to the
channel process.  When an address is fully processed  (which
may not be immediately if multiple addresses are being sent to a
given host before any text is transferred) this information is
passed back to deliver.
Deliver then sends any necessary error
messages, and the queue is adjusted accordingly.
Caching and time control parameters allow for a variety of backoff
strategies to handle hosts which do not respond.
Deliver may also be invoked in pickup mode to allow messages
to be pulled as well as pushed, which is particularly important
for dialup links.
This is discussed in \*([.Crocker79\*(.].
.SH
Address handling
.PP
The mechanism for verifying addresses is now considered.
First, some terminology is defined:
.IP Address
Address is used to mean the text that the user supplies to a typical
message sending interface in order to direct a message to a desired
destination.
This loose usage has been selected, because it is familiar to most message
system users.
.IP Domain
.br
The DARPA domain scheme \*([.Mockapetris84\*(.],
and the UK Name Registration Scheme (NRS) \*([.Larmouth83\*(.],
allow an untyped global namespace to be allocated in an
hierarchical fashion.
Examples of domains are: UK, Salford.AC.UK, CompSci.Salford.AC.UK,
USC-ISID.ARPA, ARPA, UUCP.
If a domain is sufficiently specified
.FN
To identify a specific service in the
context of domain UK is unlikely to make sense,
whereas it might for Salford.AC.UK.
.FE
it is possible
to identify a direct connection to a Host, which may be
associated with the domain or be a relay to the domain.
.IP Mailbox
A mailbox (User Agent) is the source or destination of a
message, often associated with a human user.  A mailbox can be
globally specified in RFC 822 as local-part@domain.
.IP Host
A host is a computer connected to directly by the
local computer.  A domain associated with a host not directly
connected to by the local computer
should be regarded only as a domain: the domain to
host binding is immaterial if there is no direct connection.
.IP Route
.br
Routes are considered to be implicitly at the message level.
A Route is an explicit
sequence of domains over which a message should traverse.
Two types of route are considered:
.IP (i) 8
A Formal Route consists of a sequence of globally valid
domains, recognised as a source route by the originating system.
In RFC 822, this is specified by a syntax of the style
<@domain1:local-part@domain2>.
A Formal Route is used to reach domains which cannot be accessed directly,
or where indirect access is preferred.
.IP (ii) 8
An Informal Route is one that is not recognised as a route by
the sending system, but is interpreted as such by a receiving
system.  In the RFC 822 world, a common Informal Route syntax,
making use of a special form of local-part is:
user%domain2@domain1.
Interestingly, this syntax is a Formal Route in the JNT Mail
world.
An Informal Route is used when different domains have a
different interpretation of the global namespace (e.g.
user%domain2@domain1 is used where the message originator's local system does
not interpret domain2 as intended, but domain1 does).
.PP
A number of domain specifications have been adopted informally
by different groups
(e.g. .ARPA .AC.UK .MAILNET .UUCP .CSNET .CDN .EDU),
and it seems likely that many networks will be able to
agree on a global namespace.
All Message Routing in MMDF\ II is currently controlled by tables.
This is likely to continue for hosts where all external
communication is by use of dialup links, and
the NRS (at least) will distribute information as tables..
The approach taken by MMDF\ II to table based domain handling is
now described.
.PP
The MMDF\ II process
.B submit
performs the address checking function.
There are two orthogonal operations.
The first is to parse the address, to extract a Formal Route.
The second phase is to interpret the 'end' domain of the
route.
If the domain being verified is
identified as the local domain, the next
domain component is considered.  If the local-part is
reached, it is then interpreted as an Informal Route.
MMDF\ II supports a number of Informal Route specifications,
including the UUCP "!" syntax.
If the local-part under consideration is
not an Informal  Route, it is looked up as an alias.
If it is identified as an alias, the alias value is then parsed
as a sequence of addresses, and the whole procedure recurses.
Finally, if it is not an alias, it will be checked as a local
account and, if valid, queued for delivery by the local channel.
.PP
Each channel is associated with a set of hosts,
which are
directly connected to the local system.
.FN
The directness is conceptual rather than physical.
This is illustrated later.
.FE
A host may be associated with more than one channel.
Each channel has a channel table
.FN
Tables are considered here as if they were physical files.
In practice, they are implemented by hashed database lookup.
Many of the operations described are optimised further
by taking advantage of the structure of this database.
.FE
which maps its associated hosts
onto the necessary connection information (e.g. transport
addresses).
The host namespace is arbitrary, but given that all hostnames
must be unique (to allow for host to channel binding),
the convention that the hostname is that of the associated
domain is convenient.
A reason for sometimes violating this convention is discussed later.
.PP
Domains are handled in a two level manner.
The top part of the domain is specified in the tailor file, and
indicates an associated table.
This top part is referred to as the
.B "domain table specification"
The rest of the domain is specified as the LHS of the table,
with the RHS of the table specifying the host associated with
the domain.
The split can occur in more than one way.
For example, CS.SALFORD.AC.UK could be split as:
.DS B

domain table specification          table entry

SALFORD.AC.UK                       CS
AC.UK                               SALFORD.CS
UK                                  CS.SALFORD.AC
""                                  CS.SALFORD.AC.UK

.DE
The last case has a null domain table specification, known
informally as the 'top' table.
.PP
The method of domain interpretation is now considered.
When a potential domain is examined by submit, it is first assumed to be
a full domain specification.
.FN
The procedure is substantially optimised for domains in this
form, as this is the most common case.
.FE
Components are repeatedly
stripped from the LHS and matched against the list of domain
table specifications.  Thus, if TREES.BIO.STANFORD.EDU is
considered, domain table specifications are searched for in the
order: BIO.STANFORD.EDU, STANFORD.EDU, EDU, "".  For each
match,  the remainder is looked up in the associated domain
table.  If a domain is identified, it is then mapped into its
official value by looking for the first component in the table
with the same RHS.  This allows for domain aliases.  The RHS (host
name) is then looked up in the channel tables, and the address
is bound to the first channel satisfying management
requirements.
Some extensions to this basic procedure are now described.
.IP "Source Routes" 12
An alternative specification for the RHS of a domain table is
as a series of components; the first being the host directly
connected to, and the rest being a sequence of domains
traversed in order.  These domains will be added to the source
route passed to the channel (message transport protocol).
This source route information can be added in an ad hoc manner,
or can be systematically obtained (e.g. from the NRS).
.IP Subdomains 12
If CS.SALFORD.AC.UK is specified, and has no corresponding domain table
entries, but there is one for SALFORD.AC.UK, CS.SALFORD.AC.UK
is accessed as a source route through SALFORD.AC.UK.
This approach means that not all leaves of the tree need be stored.
In particular, the 'top' table (i.e. the one with null domain
table specification) may have entries such as:
.DS
CSNET:CSNET-RELAY.ARPA
CA.UUCP:UCB-VAX.ARPA
.DE
This would route all subdomains of CSNET (e.g. UPENN.CSNET) through
CSNET-RELAY.ARPA, and all subdomains of CA.UUCP through
UCB-VAX.ARPA.
This is useful in (the currently common) cases
where the logical domain structure
has a physical mapping.
.IP "Partially specified domains" 12
When all these checks have been made on the assumption of fully
specified domains, these checks are repeated in each domain
table on the assumption that the domain table specification was
omitted.  For example, if there is a table ARPA, and BRL is
being tested, BRL will be looked up in table ARPA (and matched)
on the assumption that BRL.ARPA was intended.
.IP "NRS domain ordering" 12
It has been assumed until now that the UK NRS (Name
Registration Scheme) and DARPA Domain scheme specify domains
in the same manner.
An unfortunate difference is, that although the semantics are
similar and syntax the same, the ordering of the hierarchy is
reversed.
Thus, SALFORD.AC.UK in the DARPA form is UK.AC.SALFORD in NRS
form.
This difference has caused much confusion to (UK) users communicating
with systems using both of these schemes, and so
it is not very satisfactory to assume consistent
specification (within the UK).
Therefore, MMDF\ II may be configured to check for domains in
either order. It does this in an interleaved fashion (i.e. for
each check done performing it first in one order, and then the
other).
This minimises the weight given to the order a domain happens
to be specified in, and maximises weight on the degree of specification
(e.g. preferring to interpret "UK.AC.AVON.MULTICS"  as MULTICS.AVON.AC.UK
rather than UK.AC.AVON.MIT-MULTICS.ARPA).
In practice, the few problems which have occurred, have been
caused by palindromic
partial domain specifications.
Except where explicitly noted otherwise, this paper assumes
DARPA ordering, as NRS ordering is mostly confined to the UK
Academic Community.
.IP "Routing by the channel" 12
There are currently two cases of routing by channel.
In the
first case, which can be used for any channel,
the channel connects only to one host acting
as relay for all the hosts in the channel table.
The second is the UUCP channel.
Here, hosts are considered to be any UUCP host reachable
through UUCP, and the RHS of the channel table is the UUCP route
(expressed in the form host!host!host!%s).
Use of this style of channel routing should be minimised, as it is
preferable both to route incrementally and to bind addresses to channels
as late as possible.
.IP "Support for multiple machines" 12
It is common for UNIX sites to have many machines, and to have
an allocation of users between machines with no clear meaning
external to the site.
If the machine name must be specified to send mail to a user,
this leads to names which are hard to memorise/guess, and
makes the movement of mailboxes between machines visible
external to the site.
MMDF\ II allows for multiple machines to share a common namespace,
common binaries and common tables, and for each user to have a default
machine for mail delivery.
Further, many machines can appear externally
to be the same domain.
This is achieved by treating each machine as a (different) subdomain
of the visible domain  (e.g. a message apparently from
Steve@CS.UCL.AC.UK might be originated on machine VAX2.CS.UCL.AC.UK).
This approach is clearly more user-friendly.  In some
circumstances it will be more efficient, and in others, less.
.PP
It is interesting to note how the MMDF\ II addressing approach
fits into the transition of various networks to a
domain based addressing scheme.
There is currently a draft proposal for the UUCP transition \*([.Horton85\*(.].
The author's interpretation of this suggests,
assuming that this proposal is followed, that MMDF\ II
will provide all of the needed functionality for users to get the
full advantage of a domain based scheme.
In the UK, the information supplied by the NRS can be used in a
straightforward fashion.
.PP
Perhaps the most interesting case is the DARPA Domain
Nameserver scheme \*([.Mockapetris84\*(.].
In general, domain information will not be available in table
form, but is obtained dynamically by querying a sequence of
nameservers.
MMDF\ II will be extended to support this within the timescale of
the DARPA transition.
A brief description of a
.I possible
approach is described, the aim being to illustrate that the
DARPA scheme can be integrated, rather than to describe how it
will be integrated.
In general, a final solution will have a mixture of table and
nameserver determined bindings.
It seems likely that the domain namespaces controlled by tables and
nameservers will overlap substantially in many cases.
A channel will be either table driven (as at present), or nameserver driven.
A nameserver channel will be given a domain, which will be
mapped into a connection (and possibly a route) when the
domain in question is processed.
At message submission time, the
domain namespace would first be checked for
fully specified domains contained in domain tables, which
would be dealt with as before.
.FN
Note that a domain determined from a table could be bound to a
nameserver channel.  In general, this would be undesirable.
.FE
A table (most likely the 'top' table) could specify a domain as
being associated with one or more (nameserver) channels
(e.g. ARPA maps to the SMTP channel).
This domain would then be bound to one of those channels on the
basis of management considerations.
This gives submission time checking of only the higher level
domains, with the full nameserver checking occurring in
background when the channel is invoked.
Alternatively, the domain could simply indicate use of a
nameserver.
In this case, the domain being checked would be passed to the
nameserver.
.FN
Detailed interaction with a DARPA nameserver will be provided
by a resolver interface, which is likely to be implemented
as a library of functions on UNIX.
This is certainly the case for two implementations currently
in progress \*([.Karp84,\|Terry84\*(.].
.FE
If matched, any specification of a 'Mail Forwarder' could be
used to specify a source route.
The 'class' of address returned would be looked up (in a table)
to determine a set of channels.
This fuller submission time checking is desirable,
but would only be acceptable if the mean nameserver
response time was not too large.
Then, table checks for partial domains would be made.
Assuming that completion services are provided by a
local nameserver,
the potential partial domain could be checked by the
nameserver, if desired.
This scheme would extend naturally if there was a need to use either
different types of nameserver, or disjoint systems of the same
type of nameserver.
This is however, only one of several potential approaches.
.SH
Message Reformatting
.PP
The need to reformat messages is an unfortunate reality.
It is caused by the requirement of interworking between functionally
similar, but differently encoded, end to end message protocols.
MMDF\ II provides two basic types of message reformatting: the
first, applicable to all message transfer protocols; and the
second protocol specific.
MMDF\ II messages are expected (by submit) to be in a generic
format,  and are queued directly.
This format is flexible, so that User Interface Processes do
not need to have overly stringent requirements in order to generate legal
format messages.
In particular, the following aspects are flexible:
.IP \-
Formal Routes may be specified either in RFC 822 format or as
\'local-part@domain@domain'.
.IP \-
Domains do not have to be
.B normalised ,
that is to say they may be partially specified or fully
specified aliases (e.g. ISID, USC-ISID, ISID.ARPA,
USC-ISID.ARPA are all acceptable).
.IP \-
Local domain specifications may be omitted.
.PP
In a system configured for the UK Academic Community, the following are also
flexible:
.IP \-
Formal routes may also be specified in 'local-part%domain@domain'
form, as used by the JNT Mail Protocol.
.IP \-
Domains may be specified in either NRS or DARPA order.
.PP
The general reformatting provided by MMDF\ II
may be selected optionally on a per channel basis.
This reformatting
should be
regarded conceptually, as being provided by deliver.
It is really performed within the standard routines for
interaction with deliver, called by each channel.
If reformatting is selected, before each message is processed,
a reformatted header will be generated into a temporary file.
This is then used (transparently) by the channel instead of the
real message header.  The reformatting functions
provided are now described.
It is interesting that they all relate to address format,
which in practice is the only interesting message transfer
protocol independent form of reformatting.
If reformatting is selected for a given channel,
an alternative for each of the functions must be specified.
.IP (1)
Domain normalisation (the conversion of all domain
specifications to fully qualified preferred forms) is a basic
function of message reformatting.
An alternative form of normalisation makes use of the host
namespace maintained by MMDF\ II and, in cases where a domain has
an associated host, normalises to the host name.
This is useful when connected to two worlds with
differing views of the global namespace, or in transition
situations.
A common host specification is the leftmost component of the
domain (e.g. UPENN is the host associated with UPENN.CSNET).
.IP (2)
Formal Route specifications may be reformatted to either RFC 822
form (@domain1:local-part@domain2) or to percent form
(user%domain2@domain1).
This is useful for mapping between JNT Mail formal source
routes and RFC 822 formal source routes.
It is also useful where domains are used internally, and are
not known globally.
This mechanism allows formal routes to be mapped into informal
routes, thus making the domain interpretation private.
.IP (3)
Domain/Host specifications may be output in either DARPA or NRS
ordering.
This is protocol independent, as the NRS ordering is associated
with the UK Academic Community and not with any specific
message transfer protocol.
.IP (4)
The local domain may be mapped into another specified form.
This is used when one system has a different name in different
environments.
.IP (5)
A treatment known as 'exorcising' against a table of domains.
It is assumed that only the domains specified are known by
domains accessed through the
channel, and addresses relative
to all other domains will be mapped to an informal
source route behind the local system.
.PP
Message reformatting is also applied on a protocol specific
basis.
This occurs in two places.
.IP "Channel Reformatting" 12
Channels may perform reformatting in addition to that provided
as standard.
The JNT Mail channel performs some simple reformatting, to
ensure that the return path, calculated according to the
protocol, is correct.
MMDF\ II currently supports two UUCP channels.  The first assumes
that the remote site is 'modern' and does no reformatting
on the basis that the remote site expects a standard RFC 822
message.
The second UUCP channel assumes that the remote site is 'old
fashioned', and maps all addresses into UUCP route syntax
(e.g. user@CS.UCL.AC.UK is changed to ucl-cs!user).
Both channels may be used simultaneously by one system.
.IP "Server Reformatting" 12
Where an incoming message does not conform to the generic MMDF\ II
format, the protocol server must perform reformatting.
In practice, this is only done for UUCP and EAN X.400.
The protocol server for UUCP (the rmail process), will map
incoming addresses, which (by their syntax) appear to be from 'old
fashioned' systems into RFC 822 format addresses.
Where possible, UUCP routes are shortened.
This reformatting will not change the message format if the remote UUCP
site is 'modern'.
.SH
Comparison with other Systems
.PP
The only system considered in comparison
is Sendmail \*([.Allman83\*(.].
A comparison with various non-UNIX systems would be
interesting, but not appropriate here.
No other UNIX systems with comparable functionality are known to
the author.
Only addressing and message
reformatting aspects are considered here.
.PP
Sendmail's address parsing and message reformatting is
controlled by a
.B "configuration file" .
Sendmail
.B mailers
are analogous to MMDF\ II channels,
but are less tightly coupled.
Sendmail's address parsing and domain interpretation are
controlled by the configuration file, and are not specifically
decoupled.
Whilst allowing for an amazingly general syntax, it can lead to
cases where domain interpretation is syntax dependent.
.FN
Careful design of Sendmail configuration files should minimise
this dependency, although
generation of configuration files to handle complex cases is
not straightforward.
.FE
The decoupling of address parsing and domain interpretation,
as provided by MMDF\ II,
is seen as both correct and desirable.
.PP
Current domain handling by Sendmail uses explicit recognition
of domains listed in the configuration file to bind to a given
mailer.
After this partial address checking, the message is then queued
for a specific mailer, where further checking is done.
If configuration files are kept to a reasonable size, this
approach must rely on the fact that the higher level domains can
be used to determine the message transfer protocol in most cases.
The MMDF\ II approach of maximising address checking at submission
time is seen as desirable in view of the trend towards logical domain
specifications which do not imply specific message transfer
protocols.
A domain handling package is expected to be added to Sendmail in
the near future, and this may well allow for a more flexible
approach.
.PP
Sendmail reformatting, is highly general and flexible, and has
been used to deal with a variety of unusual formats.
Some difficulty has been encountered when trying to handle NRS
domain ordering, but this is probably not insuperable.
MMDF\ II's more basic facilities cover a wide range of
commonly used formats and, where appropriate, are more
straightforward to configure.
It seems likely that more systems will be moving to standard
formats.
The simpler approach also tends towards greater robustness,
and protects against protocol violations.
.SH
Afterword
.PP
MMDF\ II is expected to be available as a production system by
the time this paper is presented.  It is available in the US
through University of Delaware, and in the UK through
University College London.
There is a (currently nominal) licence fee for commercial
sites, and a tape handling charge.
.sp
.]<
.\"Allman.E.-1983-2
.ds [F Allman83
.]-
.ds [T SENDMAIL - An Internetwork Mail Router
.ds [A E. Allman
.ds [R Paper
.ds [D 1983
.nr [T 0
.nr [A 0
.nr [O 0
.][ 4 tech-report
.\"Borden.B.S.-1979-3
.ds [F Borden79
.]-
.ds [A B.S. Borden
.as [A " and others
.ds [T The MH Message Handling System
.ds [D November 1979
.ds [J Project Airforce document, obtainable from RAND, Santa Monica, Ca 90406
.ds [K MH
.nr [T 0
.nr [A 0
.nr [O 0
.][ 1 journal-article
.\"Brink.D.-1985-4
.ds [F Brink85
.]-
.ds [A D. Brink
.as [A " and S.E. Kille
.ds [T Authorisation and Accounting in Store and Forward Messaging systems
.ds [D June 1985
.ds [J Submitted to Networks 85, Wembley
.nr [T 0
.nr [A 0
.nr [O 0
.][ 1 journal-article
.\"1983-1
.ds [F CCITT83
.]-
.ds [Q CCITT SG 5/VII
.ds [T Recommendations X.400
.ds [D November 1983
.ds [R Message Handling Systems: System Model - Service Elements
.ds [K x400
.nr [T 0
.nr [A 0
.nr [O 0
.][ 4 tech-report
.\"Comer.D.A.-1983-16
.ds [F Comer83
.]-
.ds [A D.A. Comer
.ds [T A Computer Science Research Network: CSNET
.ds [J Communications of the ACM
.ds [V 26
.ds [N 10
.ds [P 747-753
.nr [P 1
.ds [D October 1983
.ds [K csnet overview
.nr [T 0
.nr [A 0
.nr [O 0
.][ 1 journal-article
.\"Crocker.D.-1979-5
.ds [F Crocker79
.]-
.ds [A D. Crocker
.as [A ", E. Szwkowski
.as [A ", and D. Farber
.ds [T An Internetwork Memo Distribution Capability - MMDF
.ds [D November 1979
.ds [J Proc. 6th. Data Comm Symp, IEEE/ACM
.nr [T 0
.nr [A 0
.nr [O 0
.][ 1 journal-article
.\"Crocker.D.H.-1982-6
.ds [F Crocker82
.]-
.ds [A D.H. Crocker
.ds [T Standard of the Format of ARPA Internet Text Messages
.ds [D August 1982
.ds [R RFC 822
.ds [K RFC822
.nr [T 0
.nr [A 0
.nr [O 0
.][ 4 tech-report
.\"Horton.M.R.-1985-7
.ds [F Horton85
.]-
.ds [T UUCP Mail Transmission Format Standard
.ds [A M.R. Horton
.ds [R Draft received as message
.ds [D January 1985
.nr [T 0
.nr [A 0
.nr [O 0
.][ 4 tech-report
.\"Karp.P.D.-1984-18
.ds [F Karp84
.]-
.ds [T DRUID: A Distributed Name Server
.ds [A P.D. Karp
.ds [R Stanford Internal Report
.ds [D June 1984
.nr [T 0
.nr [A 0
.nr [O 0
.][ 4 tech-report
.\"Kille.S.E.-1984-8
.ds [F Kille84
.]-
.ds [A S.E. Kille, (editor)
.ds [T JNT Mail Protocol (revision 1.0)
.ds [D March 1984
.ds [I Joint Network Team
.ds [C Rutherford Appleton Laboratory
.ds [K greybook
.ds [K jntmail
.nr [T 0
.nr [A 0
.nr [O 0
.][ 2 book
.\"Kingston.D.P.-1984-9
.ds [F Kingston84
.]-
.ds [A D.P. Kingston
.ds [T MMDFII: A Technical Review
.ds [J Usenix Conference
.ds [C Salt Lake City
.ds [D August 1984
.ds [K MMDF
.nr [T 0
.nr [A 0
.nr [O 0
.][ 1 journal-article
.\"Larmouth.J.-1983-10
.ds [F Larmouth83
.]-
.ds [A J. Larmouth
.ds [T JNT Name Registration Technical Guide
.ds [D April 1983
.ds [J Salford University Computer Centre
.ds [K NRS
.nr [T 0
.nr [A 0
.nr [O 0
.][ 1 journal-article
.\"Mockapetris.P.V.-1984-17
.ds [F Mockapetris84
.]-
.ds [A P.V. Mockapetris
.ds [T A Domain Nameserver Scheme
.ds [D May 1984
.ds [J IFIP WG 6.5 Conference, Nottingham
.nr [T 0
.nr [A 0
.nr [O 0
.][ 1 journal-article
.ds [F Mockapetris84
.\"Neufeld.G.W.-1983-11
.ds [F Neufeld83
.]-
.ds [A G.W. Neufeld
.ds [T EAN: A distributed message system
.ds [J Proceedings CIPS National Meeting, Ottowa
.ds [P 144-149
.nr [P 1
.ds [D May 1983
.ds [K ean
.nr [T 0
.nr [A 0
.nr [O 0
.][ 1 journal-article
.\"Nowitz.D.A.-1978-12
.ds [F Nowitz78
.]-
.ds [A D.A. Nowitz
.as [A " and M.E. Lesk
.ds [T A Dial-up Network of UNIX systems
.ds [D August 1978
.ds [R UNIX Programmer's Manual, 7th edition, Bell Labs.
.ds [K UUCP
.nr [T 0
.nr [A 0
.nr [O 0
.][ 4 tech-report
.\"Postel.J.B.-1982-15
.ds [F Postel82
.]-
.ds [A J.B. Postel
.ds [T SIMPLE MAIL TRANSFER PROTOCOL
.ds [D August 1982
.ds [R RFC 821
.ds [K RFC821
.ds [K SMTP
.nr [T 0
.nr [A 0
.nr [O 0
.][ 4 tech-report
.\"Terry.D.B.-1984-13
.ds [F Terry84
.]-
.ds [T The Berkeley Internet Name Domain Server
.ds [A D.B. Terry
.as [A " and others
.ds [J Usenix, Salt Lake City
.ds [D August 1984
.ds [K bind
.nr [T 0
.nr [A 0
.nr [O 0
.][ 1 journal-article
.\"Vittal..J.-1981-14
.ds [F Vittal81
.]-
.ds [A J. Vittal
.ds [T MSG: A simple message system
.ds [J Proc. Int. Symp. Computer Message Systems, Ottowa
.ds [I North Holland
.ds [D April 1981
.ds [K msg
.nr [T 0
.nr [A 0
.nr [O 0
.][ 1 journal-article
.]>
.sp
RFC indicates a DARPA Request For Comments, which may be
obtained from: USC Information Sciences Institute, Marina del
Rey, Ca. USA.
.SH
Acknowledgements
.PP
Many people have worked on MMDF\ II, including
Phil Cockroft, Bernie Cosell,
Dave Farber, Dan Long, Lee McLoughlin, Mike Muus,
Julian Onions, Brendan Reilly, Dennis
Rockwell, and Marshall Rose.
Particular credit to Dave
Crocker who designed the bulk of MMDF\ II, and to Doug Kingston who bore
the brunt of making it work as a production system.

.nr [O 0
.][ 1 journal-article
.\"1983-1
.ds [F CCITT83
.]-
.ds [Q CCITT SG 5/VII
.ds [T Recommendations X.400
.ds [D November 1983
.ds [R Message Handling Systems: System Model - Service Elements
.ds [K x400
.nr [T 0
.nr [A 0
.nr [O 0
.][ 4 tech-report
.\"Comer.D.A.-1983-16
.ds [F Comer83
.]-
.ds [A D.Ammdf/doc/auth.guide   444      0     12       44121  3631167650   7527 .\" ***************************************************
.\"  indra.me   - Steve Kille - Jan 84
.\"
.\"  /vax2/staff/steve/doc/indra/in.me
.\"
.\"     based on
.\"
.\"  tmac.pl.in  -  internal/departmental-specific macros
.\"
.\" ***************************************************
.\"
.\"
.\"
.\"       INTERNAL NOTE HEADER
.\"
.\"       called as .IN "Internal Note number" "date" "other id"
.\"
.de IN
.ec
.ls 1P          \" will be reset at top of next page to \n(LS
.sp 4
.in +5
.tl ~Internal Note \\$1~~Internal~
.ie \\w~\\$3~ .tl ~\\$3~~Working \&~
.el .tl ~~~Working \&~
.ie \\w~\\$3~ \\{  .tl ~~~Paper   \&~
.                   tl ~\\$2~~~     \\}
.el .tl ~\\$2~~Paper   \&~
..
.\"
.\" ***************************************************
.\"
.\"       TECHNICAL REPORT HEADER
.\"       called as .TR "Technical Report number" "date" "other id"
.\"
.de TR
.ec
.ls 1P          \" will be reset at top of next page to \n(LS
.sp 4
.tl ~TECHNICAL REPORT \\$1~~UCL-CS TR \\$1    \&~
.ie \\w~\\$3~ .tl ~\\$3~~~
.el .tl ~~~~
.ie \\w~\\$3~ \\{  .tl ~~~~
.                   tl ~\\$2~~~     \\}
.el .tl ~\\$2~~~
..
.\"
.\" ***************************************************
.\"
.\"       TITLE & AUTHOR(S)
.\"
.\"       called as .TA "title" "subtitle" "author(s)" "co-author(s)"
.\"
.de TA
.sp |3i
.rb
.tl ~~\\$1~~
.if \\w~\\$2~ \\{\
.       sp
.       tl ~~\\$2~~   \\}
.sp 3
.tl ~~\\$3~~
.sp
.if \\w~\\$4~ .tl ~~\\$4~~
.r
..
.\"
.\" ***************************************************
.\"
.\"       ABSTRACT START
.\"
.de AB
.sp |5..5i
.ds AS ABSTRACT:
.ll -(\\w~\\*(AS~u-1m)
.in +(\\w~\\*(AS~u+4m)
.ti -(\\w~\\*(AS~u+1m)
\\*(AS \\c
..
.\"
.\" ***************************************************
.\"
.\"       ABSTRACT END
.\"
.de AE
.wh -6 UC
.ls
.ll
.bp 1
..
.\"
.\" ***************************************************
.\"
.\"       UCL LOGO
.\"
.de UC
.tl ~~Department of Computer Science~~
.sp
.tl ~~University College London \&~~
.wh -6
..
.de AP
.nr $A +1
.sh 1 _ \\n($A
.af $A A
.af $1 A
.bp
.ce 2
.uh "Appendix \\n($A"

.rb "\\$1"
..
.IN 1724 "April 1985"
.TA "Configuring MMDF Authorisation" \
"Steve Kille"
.he  '[MMDF Authorisation]''[Indra Note 1724]'
.fo  '[Kille]''[Page %]'
.AB
This note describes how access control is applied in MMDF.
It explains how to configure host based controls and user
based control, and gives examples.
.AE
.ae
.sh 1 "Introduction"
.lp
This is intended as a pragmatic document, explaining how to set
up MMDF to apply these controls.
A more general description of the aims, and a discussion of the
UCL usage in conjunction with a full management system is given
in \*([.Brink85a\*(.].
A more theoretical discussion of the considerations involved is given in \*([.Kille85a\*(.].
.pp
A few concepts are needed to understand this document:
.np
User: A mailbox.  Senders and recipients are two specific types
of user.
.np
Normalised address.  An address / source route in RFC 822
syntax with all domains fully qualified.
Local addresses will simply be a local login name or alias.
.np
Channel: A grouping of hosts, all talking the same protocol.
Channels are also divided on a network basis at UCL.  They
might also be divided on a logical/administrative basis.
For authorisation purposes, many authorisation constraints are
applied to channels, rather than to individual hosts.
.np
Host: Domains directly connected to (or connected to by a
channel with a specific relay host).  Distinguish from a general
domain, which may be accessed indirectly.
.np
Message transfer: A message coming from a sender on a channel,
and being transferred to one recipient on a channel.  Multiple
recipients are not considered from the standpoint of
authorisation.
.np
Configuration file:  the file which dynamically tailors the
channel and authorisation configuration of a local system.
This is constructed manually.
.sh 1 General
.lp
All table formats are considered to be as follows.
The LHS is terminated with tab or space or colon.
Backslash is used as a single character quote.
The RHS is a sequence, as defined in mm_tai(3.mmdf).
.pp
A message is considered to arrive over an inbound channel, and to depart
over an outbound channel.
The outbound channel may be selected from several
possibilities.
Each channel has a parameter "auth" which can be set  to
provide the following controls in each direction for a given
channel.
The following modes are identified for
user access in each direction, with the appropriate values for
"auth" in braces:
.np
FREE: (default) no controls.
.np
LOG.  (inlog/outlog) Just note (and log) authorised / unauthorised access.
.np
WARN.  (inwarn/outwarn) Like
log, but send a warning note (from a standard file - see
tailoring section of the installatin guide)
to the sender of the message.
This is intended to allow for gradual introduction of authorisation.
.np
BLOCK.  (inblock/outblock) Reject the message.
.pp
This mechanism may be used in a straight forward manner to
provide control over routes used  to a given destination.
.sh 1 "Host based Controls"
.lp
Some controls are applied on the basis of just the channels and
hosts involved.  This mechanism may be used to control
bilateral relaying agreements, or to apply specific relaying
policy controls in conjunction with user authorisation.  If a
host based criterion is identified, this is used to validate
both inbound and outbound channel.
There are four tables associated with each channel,
which are now described.
Each table is defined with the appropriate channel parameter
(e.g. to use table "insrc", define and MTBL entry, say MTBL
foo, and then use channel parameter insrc=foo).
The items on the LHS and RHS of the table are then described.
If a match is found, the message is deemed to be authorised.
It is noted that host and not domain values are used in all of
these tables.
.ip outdest
Used when the associated channel is a (potential) outbound channel.
.sp
LHS: outbound host.
.sp
RHS: NULL (LHS valid for all inbound hosts/channels), or list of valid inbound
channels and inbound hosts.
.ip insrc
Used when the associated channel is the inbound channel.
Will be the same as outdest if symmetrical controls are applied.
.sp
LHS: inbound host.
.sp
RHS: NULL (LHS valid for all outbound hosts/channels), or list of valid
outbund channels and outbound hosts.
.ip outsrc
Used when the associated channel is a (potential) outbound channel.
.sp
LHS: inbound host or channel
.sp
RHS: NULL (LHS valid for all outbound hosts/channels),
or list of valid outbound hosts.
.ip indest
Used when the associated channel is the inbound channel.
Will be the same as outsrc if symmetrical controls are applied.
.sp
LHS: outbound host or channel
.sp
RHS: NULL (LHS valid for all inbound hosts/channels),
or list of valid inbound hosts.
.lp
Some examples of this are given in the appendices.
.pp
Instead of using the connect host, it is also possible to use
the full route to/from the originator's host.
This has intrinsically more security holes than the other
approaches.  However, it should be satisfactory in most cases.
.pp
This is done by setting "auth=dho" \**
.(f
\n($f. DHO used to mean Direct Host Only, a relic from when this function
was less general.
.)f
in the channel concerned.
Then, all of the controls above apply, except that the host is
replaced by a route specification.
The route specification \&'knows' about "!" and "%" formats.
Routes are specified by use of mailboxes of the form that are
associted with the route, with the username replaces by the
string "username".
For example: "username@rlgb" "wcwvax!username@ucl"
"xx!username%b@c".
The string, including the sequence "username" is used in the
tables.
An example is given in the appendices.
.sh 1 "User based controls"
.pp
A message may be authorised in terms of its sender and
recipient.
This is done by having a file (the authorisation file), to look
up authorised mailboxes.
The file has RFC 822 route format mailboxes on the LHS, and
control information on the RHS.
These controls are applied both on the incoming and
outgoing channels.
.pp
Controls are applied in terms of a user database.  An example
of this is given in an appendix.  Essentially, a user, with a
given mode of access, is given a number of allowed channels.
The following user modes of access are identified (one per
user):
.np
SEND:  send only.
.np
RECV: receive only (for aliases).
.np
BOTH: send and receive  (normal mode).
.np
LIST: special access for UCL expanded mailing lists.
.np
EXPIRE.  Note that authorisation has lapsed
in any error message sent.
.np
Any other value will be treated as EXPIRE, and the text of the
reason passed back in any failure message.
.pp
In most cases, a messages is authorised by EITHER host controls
OR user controls.
For some channels, it may be desirable to have both host AND
user controls (e.g. to perform policy control on top of user
based controls).
This is done by setting auth=hau (Host And User) in the
channel.
.sh 1 "Logging"
.pp
Logging information relative to these controls is logged in a
special log.  This will be interpreted and loaded into the
database.
Attempted illegal accesses are also noted, with similar format.
.pp
All logging entries contain the message number and the date.
There is a special entry containing the message size, and
length, logged when the message is accepted by the system.
Other logging entries contain: the incoming channel, outgoing
channel, receiver address, authorisation reason, and any
relevant host names.
The following reasons are identified.
It is expected to define specific encoding for the log:
with the following set, only one reason is given, covering both
inbound and outbound authorisation.
The inbound and outbound host/route value is given.
.np
OH - outbound host/route
.np
HC - outbound host/route + inbound channel
.np
HH - inbound host/route + outbound host/route
.np
CC - inbound channel + outbound channel
.np
CH - inbound channel + outbound host/route
.np
IH - inbound host/route
.lp
In the second set, there are two reasons, one for the inbound
channel, and one for the outbound.
If there is no authorisation required for one of the channels,
the reason is left NULL.
.np
IL - inbound channel,  outbound = LIST
.np
OL - outbound channel, dest = LIST
.np
IS - inbound channel by sender
.np
OS - outbound channel by sender
.np
IR - inbound channel by receiver
.np
OR - outbound channel by receiver
.np
*I - inbound channel, logged unauthorised access
.np
*O - outbound channel, logged unauthorised access
.sp
.]<
.\"Brink.D.H.-1985-1
.ds [F Brink85a
.]-
.ds [A D.H. Brink
.as [A " and S.E. Kille
.ds [T Authorisation and Accounting in Store and Forward Messaging systems
.ds [D June 1985
.ds [J Networks 85, Wembley
.nr [T 0
.nr [A 0
.nr [O 0
.][ 1 journal-article
.\"Kille.S.E.-1985-2
.ds [F Kille85a
.]-
.ds [A S.E. Kille
.as [A " and D.H. Brink
.ds [T A Model of Message Flow Control
.ds [D Sepetmber 1985
.ds [J Submitted to IFIP WG 6.5 Congress, Washington
.nr [T 0
.nr [A 0
.nr [O 0
.][ 1 journal-article
.]>
.AP "Tailor file parameter summary"
.pp
The basic configuration file opertaion is described in the
manual page New.tailor (5.mmdf).  This appendix details
specific values associated with authorisation.
The following channel parms and values are identified:
.nf

                /* Various tables for channels  */
insrc
outsrc
indest
outdest


auth            /* channel auth parms  */
        = free          /* default */
        = inlog         /* log inbound */
        = inwarn        /* warn inbound */
        = inblock       /* block inbound */
        = outlog
        = outwarn
        = outblock
        = hau           /* host AND user checks */
                        /* host OR user is default */
        = dho           /* use routes instead of hosts */
.fi
.AP "Sample Log"
.lp
This is a small section of real authorisation log.
It should be self explanatory if the information given so far
has been digested.
Logging entries will usually be in one
line, but have been split here for readability.
.nf

4/29  9:02:15 AU-0000:  msg.a000427: i='local' o='niserc'
    a='cpuc08@interactive-computing-facility.engineering.cambridge.ac.uk'
    r='' r='OS'
4/29  9:02:19 AU-0000:  msg.a000427:
    END size='2573', sender='steve'

4/29  9:16:53 AU-0000:  msg.a000493:
    i='local' o='satnet' a='nsc!idi!kiessig@seismo.arpa' r='' r='OS'
4/29  9:16:54 AU-0000:  msg.a000493: END size='1654', sender='steve'

4/29  9:18:13 AU-0000:  msg.a000507: i='local' o='niserc'
    a='sjl@ukc.ac.uk' r='' r='OS'
4/29  9:18:14 AU-0000:  msg.a000507: END size='640', sender='steve'

4/29  9:44:54 AU-0000:  msg.a000561: i='local' o='nipss'
    a='neil@rsre.AC.UK' r='CH' hi='' ho='username@rsre.ac.uk'
4/29  9:44:54 AU-0000:  msg.a000561: i='local' o='nipss'
    a='laws@rsre.AC.UK' r='CH' hi='' ho='username@rsre.ac.uk'
4/29  9:44:55 AU-0000:  msg.a000561: END size='2102', sender='robert'

4/29  9:50:25 AU-0000:  msg.a000629: i='local' o='satnet'
    a='robinson@dmc-crc.arpa' r='' r='OS'
4/29  9:50:26 AU-0000:  msg.a000629: END size='559', sender='robert'

4/29  9:53:09 AU-0000:  msg.a000653: i='local' o='niserc'
    a='map@edxa.ac.uk' r='' r='OS'
4/29  9:53:10 AU-0000:  msg.a000653: END size='197', sender='phil'

4/29  9:56:12 AU-0000:  msg.a000658: i='local' o='satnet'
    a='zsu@sri-tsc.arpa' r='' r='OS'
4/29  9:56:13 AU-0000:  msg.a000658: i='local' o='satnet'
    a='mathis@sri-tsc.arpa' r='' r='OS'
4/29  9:56:13 AU-0000:  msg.a000658: i='local' o='satnet'
    a='zsu@sri-tsc.arpa' r='' r='OS'
4/29  9:56:14 AU-0000:  msg.a000658: END size='570', sender='robert'

4/29 10:46:11 AU-0000:  msg.a001076: BAD AUTH i='niserc' o='tunnel'
    s='@gec-m.rutherford.ac.uk:nhpq03@alvey.ac.uk'
    r='@csnet-relay.arpa:posty@upenn.csnet'
    msg='(%s)  no  authorization for address
               '@csnet-relay.arpa:posty@upenn.csnet''

4/29  9:58:27 AU-0000:  msg.a000669: i='local' o='niserc'
    a='cpuc08@interactive-computing-facility.engineering.cambridge.ac.uk'
    r='' r='OS'
4/29  9:58:28 AU-0000:  msg.a000669: END size='2180', sender='phil'
.fi
.AP "Sample Authorisation file"
.pp
This is a sample (dummy) auth file.  Note that local users should have
no doamin reference.  Others should be in normalised 822 form.
.nf


#
#       Authorisation file
#
# Mailing  lists
# foo-outbound  should have acceptable source channels
# foo-request should have acceptable dest channels
academic-outbound:list list,niserc,local,sring
academic-request:send niserc,local,sring
mailgroup-outbound:list list,isatnet,niserc,nipss
mailgroup-request:send satnet,niserc,nipss,niipss
#
# Users
brink:both satnet,niserc,nipss,niipss
colin:both satnet,niserc,nipss,niipss
eileen:both satnet,niserc,nipss,niipss
eliza:both satnet,niserc,nipss,niipss
francis:both satnet,niserc,nipss,niipss
#
# routed over tunnel
robert:both tunnel,niserc,nipss,niipss
steve:both tunnel,niserc,nipss,niipss
tom:both tunnel,niserc,nipss,niipss
#
#  Expired
kirstein:"Text justifying reason for lapsed authorisation"
#
# remote users
pfl@gec-d.rutherford.ac.uk:both niserc,tunnel
@gec-m.rutherford.ac.uk:joe@alvey.uk:both niserc,tunnel
.fi
.AP "Configuration for local User Control"
.lp
To have basic control for local users, all remote channels
might have:
.nf
AUTHLOG level=FST

MCHN local auth=inlog,auth=outlog

MCHN uucp auth=inblock,auth=outblock

MCHN smtp auth=inblock,auth=outblock

MTBL auth file="auth"

The file auth might have:
#
# SMTP only users
joe:both smtp
#
# SMTP and UUCP users
fred:both smtp,uucp

.fi
Non-priviliged users (e.g. students) would not be in the auth
file.
Local usage is logged in the auth log.
.AP "Basic channel control"
.lp
The following is designed to allow free local access to all
channels, but to prevent relaying between phone and uucp, or
uucp/uucp  or phone/phone.
.nf
AUTHLOG level=FST

MTBL control file="control"

MCHN local auth=inlog,auth=outlog

MCHN uucp auth=inblock,auth=outblock,indest=control,outsrc=control

MCHN phone auth=inblock,auth=outblock,indest=control,outsrc=control

The file "control" would be simply:
local:

.fi
It can be seen, that if this is the entire set of channels,
this is slightly redundant.
The following explaination of indest for the uucp channel may be
of help: if UUCP is the INbound (INdest) channel, valid
DESTination channels (inDEST) are given by the control file.
In this case, the channel local is valid for all hosts.
The authorization on the phone channel is the same as that on the
UUCP channel.
.AP "Basic route control"
.lp
This example is where a number of routes are authorised through
one channel (xxx).
The basic tailor file would be:
.nf

MTBL yyy file="yyy"
MTBL zzz file="zzz"

MCHN xxx auth=inblock,auth=outblock,auth=dho,outsrc=yyy,indest=yyy
        outdest=zzz,insrc=zzz

File yyy:
# Routes not on channel xxx
#
#  Local is all valid
local:
#  JNT sites - direct
username@cs.ucl.ac.uk:
username@gec-b.rutherford.ac.uk:
@gec-m.rutherford.ac.uk:username@alvey.uk:
# UUCP sites
username@kcl-cs.uucp
mxpoly!username@kcl-cs.uucp
wizzo!yuck!username%yerq@vile.uucp

File zzz:
# Routes on channel xxx
#
# all traffic between unido and channel niserc is valid
username@unido.uucp:niserc
telebox!username@unido.uucp:niserc

.fi
The routes are assumed to be symmetrical.
If not, all four tables would need to be different.
.AP "Complex case"
.lp
An extraction from the UCL tailor file is given.
.nf

MTBL  name=auth, file=auth, display="Authorisation file"

MTBL  name=mod-auth, file=mod.auth, display="Mod host control"

; SMTP channel stuff (two routes to USA)
MCHN  tunnel      show="via IPSS-Tunnel (outbound) with SMTP"
        auth=outblock
MCHN  satnet    show="via Satnet (outbound) with SMTP",
        auth=outblock

; No blockin (US -> UK) ..... YET
MCHN  isatnet     show="via Satnet with SMTP",
        auth=inlog,auth=outblock
MCHN  itunnel    show="via IPSS-Tunnel with SMTP"
        auth=inlog,auth=outblock

MCHN  niserc    show="via Janet with NIFTP"
        auth=inlog,auth=outlog

; Host AND user control on PSS
MCHN  nipss    show="via PSS with NIFTP"
        auth=inlog,auth=outlog,insrc=mod-auth,outdest=mod-auth

MCHN  niipss    show="via IPSS with NIFTP"
        auth=inlog,auth=outblock

MCHN  uucp show="with UUCP"
        auth=inlog,auth=outlog


The file "mod.auth":
rsre:isatnet,satnet,local,sring,list
are-pn:isatnet,satnet,local,sring,list

If nipss had been auth=dho (as it is on one machine at UCL),
the file would be:
username@rsre.ac.uk:isatnet,satnet,local,sring,list
username@are-pn.ac.uk:isatnet,satnet,local,sring,list
.fi
ng
academic-request:send niserc,local,sring
mailgroup-outbound:list list,isatnet,niserc,nipss
mailgroup-request:send satnet,niserc,nipss,niipss
#
# Users
brink:both satnet,niserc,nipss,niipss
colin:both satnet,niserc,nipss,niipss
eileen:both satnet,niserc,nipss,niipss
eliza:both satnet,niserc,nipss,niipss
francis:both satnet,niserc,nipss,niipss
#
# routed over tunnel
robert:both tunnel,niserc,nipss,niipss
steve:both tunnel,nisemmdf/doc/uk.overview   444      0     12        6476  3620510346   7741 .sp 3
.ce 3
\fBUK MMDF OVERVIEW

Steve Kille

February 1985\fP

.sp
This document is a supplement to the MMDF Overview
for sites in the UK Academic Community.  It summarizes those
features and changes peculiar to, or of particular relevance to
such sites.
Aspects peculiar to the JNT Mail Protocol
are described, and a summary of the
JNT Mail modules is given.  The UK Academic community uses a
domain hierarchy ordering reversed from that in all other RFC
822 based networks.  UK ordering, as specified by the JNT Name
registration Scheme is referred to as NRS ordering (e.g.
UK.AC.Salbridge.CS).  Standard
RFC 822 ordering is described as RFC 819 ordering (e.g.
CS.Salbridge.AC.UK).
MMDF uses RFC 819 ordering internally, including in all tables,
as this ordering is implicitly assumed in a large number of
places in the code.  The implications of this are discussed
below.
.sp
When editing conf/sitename/conf.h to set site specific
information, the following parameters are of particular
relevance:
.in +5
.sp
.ti -5
#define JNTMAIL
.br
This should be defined for all sites running the JNT Mail
channel.  Its major function is to ensure that "%" is treated
as lexically equivalent to "@", which is needed for correct
handling of JNT Mail source routes.
.sp
.ti -5
#define BOTHEND
.br
This should be defined by all sites following NRS domain
ordering.  It causes domains to be interpreted in both NRS and
RFC 819 ordering.  As described below, the internal RFC 819
ordering can be made completely transparent to the user.
.sp
.ti -5
#define VIATRACE
.br
If this is defined, trace will be inserted in the "Via:" form
with the domain in NRS ordering.  This conforms to the JNT Mail
protocol.  The RFC 822 header reformatting discussed in
\fBjntmail(8.mmdf)\fP is also turned off.  If this is not
defined, standard RFC 822 "Received:" trace will be used.
VIATRACE should be defined by all UK Academic Community sites
which neither intend to perform relaying from RFC 822
protocols other than JNT Mail to JNT Mail nor intend to make
extensive external use of the list channel.
.in -5
.sp
The function of the JNT Mail channel (sending module) is
described in \fBjntmail(8.mmdf)\fP, and handling of incoming
files in \fBni_niftp(8.mmdf)\fP.   For sites operating the York
NIFTP (Blue Book),  configuration
and changes needed to the York NIFTP
code are described in \fByork-inst(8.mmdf)\fP.
The york code is located in the directory york/ on the UK
distribution.
.sp
As noted above, all internal MMDF tables as described in
\fBtables(5.mmdf)\fP, are in RFC 819 ordering.  To make this
transparent to the user, all channels should be configured to
reformat to NRS ordering.  This is done in the tailor file
described in \fBmmdftailor(5.mmdf)\fP.  The ap channel
parameter should be set to ap=jnt  [which is equivalent to
ap=host (i.e. use host value not domain) + ap=733 (use JNT
source routes) + ap=big (use NRS ordering)].  Some sites may
prefer ap=733 + ap=big.  This will be appropriate when the NRS is
in general use.  Example tailor lines are:
.sp
.nf
MCHN  janet    name=janet,que=janet,tbl=janet,show="Janet Channel",
        pgm=niftp,poll=0,confstr="/usr/lib/x25/mhhcp $(FILE) $(ADR)",
        mod=reg,ap=jnt

or

MCHN  janet    name=janet,que=janet,tbl=janet,show="Janet Channel",
        pgm=niftp,poll=0,mod=reg,confstr="/usr/lib/x25/mhhcp $(FILE) $(ADR)",
        ap=733,ap=big
.fi
both satnet,niserc,nipss,niipss
eliza:both satnet,niserc,nipss,niipss
francis:both satnet,niserc,nipss,niipss
#
# routed over tunnel
robert:both tunnel,niserc,nipss,niipss
steve:both tunnel,nisemmdf/doc/authorization   444      0     12       73520  3631167653  10402 .\" ***************************************************
.\"  indra.me   - Steve Kille - Jan 84
.\"
.\"  /vax2/staff/steve/doc/indra/in.me
.\"
.\"     based on
.\"
.\"  tmac.pl.in  -  internal/departmental-specific macros
.\"
.\" ***************************************************
.\"
.\"
.\"
.\"       INTERNAL NOTE HEADER
.\"
.\"       called as .IN "Internal Note number" "date" "other id"
.\"
.de IN
.ec
.ls 1P          \" will be reset at top of next page to \n(LS
.sp 4
.in +5
.tl ~Internal Note \\$1~~Internal~
.ie \\w~\\$3~ .tl ~\\$3~~Working \&~
.el .tl ~~~Working \&~
.ie \\w~\\$3~ \\{  .tl ~~~Paper   \&~
.                   tl ~\\$2~~~     \\}
.el .tl ~\\$2~~Paper   \&~
..
.\"
.\" ***************************************************
.\"
.\"       TECHNICAL REPORT HEADER
.\"       called as .TR "Technical Report number" "date" "other id"
.\"
.de TR
.ec
.ls 1P          \" will be reset at top of next page to \n(LS
.sp 4
.tl ~TECHNICAL REPORT \\$1~~UCL-CS TR \\$1    \&~
.ie \\w~\\$3~ .tl ~\\$3~~~
.el .tl ~~~~
.ie \\w~\\$3~ \\{  .tl ~~~~
.                   tl ~\\$2~~~     \\}
.el .tl ~\\$2~~~
..
.\"
.\" ***************************************************
.\"
.\"       TITLE & AUTHOR(S)
.\"
.\"       called as .TA "title" "subtitle" "author(s)" "co-author(s)"
.\"
.de TA
.sp |3i
.rb
.tl ~~\\$1~~
.if \\w~\\$2~ \\{\
.       sp
.       tl ~~\\$2~~   \\}
.sp 3
.tl ~~\\$3~~
.sp
.if \\w~\\$4~ .tl ~~\\$4~~
.r
..
.\"
.\" ***************************************************
.\"
.\"       ABSTRACT START
.\"
.de AB
.sp |5..5i
.ds AS ABSTRACT:
.ll -(\\w~\\*(AS~u-1m)
.in +(\\w~\\*(AS~u+4m)
.ti -(\\w~\\*(AS~u+1m)
\\*(AS \\c
..
.\"
.\" ***************************************************
.\"
.\"       ABSTRACT END
.\"
.de AE
.wh -6 UC
.in
.ls
.ll
.bp 1
..
.\"
.\" ***************************************************
.\"
.\"       UCL LOGO
.\"
.de UC
.tl ~~Department of Computer Science~~
.sp
.tl ~~University College London \&~~
.wh -6
..
.IN 1726 "April 1985"
.TA "AUTHORISATION  AND  ACCOUNTING"  \
IN \
"STORE  AND  FORWARD  MESSAGE  HANDLING  SYSTEMS" \
"D.H. Brink and S.E. Kille"
.he  '[Authorisation]''[Indra Note 1726]'
.fo  '[Kille/Brink]''[Page %]'
.AB
Paper to be presented in June at Networks 85, Wembley.
.AE
.rb
.ce 6
AUTHORISATION  AND  ACCOUNTING
IN
STORE  AND  FORWARD  MESSAGE  HANDLING  SYSTEMS


D.H. Brink and S.E. Kille
Department of Computer Science
University College London
.r
.sp 2
Most existing store and forward message handling systems have either ignored
accounting and authorisation problems, or have
treated them in a simple manner.  The widespread adoption of the
CCITT X.400 protocols is likely to introduce relayed services provided
by multiple vendors.
These services will, by their nature,
require sophisticated authorisation and accounting.
This paper develops a model for applying such controls,
and describes experience with an experimental service
implemented on the basis of this model.
Finally, the general utility of this model is considered.
.sp 2
.in +4c
Denis Brink is a research assistant at University College London,
Department of Computer Science.  He has a BA in Philosophy and
MSc in Computer Science from University College London.  He
is currently working on the interconnection service project,
and has worked in industry on microcomputer operating systems.
.sp 3
Steve Kille received a BA in Physics from  Oxford  in  1978,  and
MScs  in  Electrical  Engineering  from UMIST (1980) and Stanford
(1981).  He has been a Research Assistant in the department of Computer
Science,  at University College London since 1981, where he has
worked on various aspects of Message Handling Systems.
His current research work is on Directory Services.
.in -4c
.bp
.sh 1 Introduction
.lp
Most existing store and forward message handling systems have either ignored
accounting and authorisation problems, or have
treated them in a simple manner.  The widespread adoption of the
CCITT X.400 protocols is likely to lead to the introduction of
relayed services provided
by multiple vendors (or management domains in X.400 terms) \*([.CCITT84a\*(.].
These services will, by their nature,
require sophisticated authorisation and accounting.
The first
part of this paper develops a model of the controls which
might be applied to such services.
.pp
The second part of the paper considers an
implementation of such controls.
University College London provides a number of message relaying
services between the UK and the USA for registered groups of
users.  A system has been developed to prevent unauthorised
usage, and to provide accounting and statistics for authorised usage.
The structure of the database
system used to generate control and accounting
information, and the internal message system organisation used
to apply these controls are described.
Experience with the use of this system and pragmatic
difficulties of its application are discussed.
Finally, the application of such services in a wider context
are considered.
.sh 1 "A Model of Authorisation"
.lp
The message services under consideration are assumed to be
provided by systems which can be described in terms of the
CCITT X.400 model of store and forward Message Handling
Systems (MHS).
.sp 7c
.ce
Figure 1: X.400 functional model of MHS
.sp
The X.400 model assumes two layers.  The lower layer is the Message
Transfer Layer, and is provided by
.rb "Message Transfer Agents"
(MTA),
which transfer messages in a store and forward manner.
That is to say, a message may be transferred over a sequence of
MTAs, and be queued at each one.
The upper layer is the Inter Personal Messaging layer,
which is provided by
.rb "User Agents"
(UA).
A UA should be considered as the software with which a user
interacts to compose or to read electronic messages.
Communication between UAs is end to end, but there will be no
OSI connection between a pair of UAs.
Rather, the store and forward service of the MTA layer is
used to provide an asynchronous end to end connection.
A UA is specified by a (globally unique) Originator/Recipient
Name, or
.rb "O/R name" .
There are O/R names at both the UA and MTA level.
The MTA level O/R names (P1 level in X.400) are
analogous to the names on an envelope in paper based mail.
The UA level O/R names (P2 level in X.400) are analogous to the
names in a contained letter.  They are
often, but not necessarily, the same.  The
transfer of a message consists of a number of distinct stages:
.np
A user composes a message and addresses it to a (UA level)
recipient.
.np
The message is transferred to an MTA, together with the MTA level recipient
O/R name.
The MTA will ensure that a valid MTA level originator O/R name is specified.
.np
The message is routed through a sequence of MTAs on the basis of
the MTA level recipient O/R name.
.np
The message is delivered to the recipient's UA.
.np
The recipient reads the message, which is usually interpreted in terms
of UA level O/R names.
.pp
A final concept, which is of relevance here, is that of
.rb "Management Domain" .
A Management Domain is an organisation providing MTA, and
(optionally) UA services.  This is illustrated in figure 1.
A Management Domain does not necessarily supply the
underlying OSI connection services.
.pp
The model proposed here operates on the assumption that MTAs
can in general be trusted, whereas UAs cannot.
Thus all control is at the MTA level.
This has the added advantage that if the
MTA layer is used for traffic other than inter-personal
messages, then the control mechanisms still work correctly.
Controls are now considered, as applied by an individual MTA.
An MTA is assumed to be trusted in two ways:
.np
When accepting a message from a UA, the MTA level originator
O/R  name is specified correctly by the MTA.
.np
When a message is transferred between Management Domains, this
is indicated accurately (i.e. a specification of the Management
Domain being left) in the MTA level  trace information\**.
.(f
\n($f. In X.400, trace information is on the basis of transfer between
Management Domains.  In other systems, MTA level trace is on
the basis of transfer between MTAs, and so what is considered as Management
Domain accounting here, may be MTA accounting in other systems.
.)f
.lp
This allows an MTA to determine responsibility for a given
transfer, and implicitly an authority to bill, on the basis of
two classes of item:
.np
One or more management domains.
Trace information will allow the MTA to determine the sequence
of Management Domains which have been traversed.
A recipient O/R name can determine at least the next Management
Domain, by use of a directory service.
.np
Originator and recipient O/R names.
.lp
It is assumed that the MTA has information allowing certain
forms of access on the basis of these parameters.
The set of parameters determining the authority for the MTA to
process the message is used to determine a
.rb "billing authority" \**.
.(f
\n($f. The term billing authority is used, but this does not
necessarily imply any form of bill.
In practice, the question: "who would pay for this if there was
a charge" is useful to determine responsibility.
.)f
Examples:
.ip -
All traffic from Management Domain X to Management Domain Y is
transferred, and billed to Management Domain X.
.ip -
All traffic from user "Zoe Foobar" may be transferred to
Management Domain "Betta Message Services Inc",
and billed to the organisation "Widget Manufacturing,
Plc".
.pp
As well as determining whether or not a message is authorised
to be
processed by the MTA, the billing authority for a given
message allows Quality of Service Parameters and appropriate
costs to be determined.
Some Quality of Service Parameters of interest are:
.np
Route choice, where message can be sent by more that one route.
This may be an MTA level route, or choice of OSI connection to
the next MTA.
.np
When to send message.  For example, a message transfer may be
timed to
take advantage of the tariff structure of the OSI services.
.np
Selection of OSI connection Quality of Service (e.g. use of
encryption).
.pp
The basis for identification of billing authority is now
considered in more detail.
A common policy will be an agreement between the local Management
Domain and an adjacent Management Domain.
This agreement might be to relay all traffic to a set of other Management
Domains, and to deliver to UAs in (and/or serviced by) the
local Management Domain.
This might or might not be restricted to traffic originating in
the adjacent Management Domain (which can be distinguished from
relayed traffic by the
trace information).
There might also be an agreement with a Management Domain
connected to through an intermediate Management Domain.
It is likely that this intermediate Management Domain will be
explicit, as:
.ip -
The intermediate Management Domain is likely to be party to the
agreement, at least implicitly.
.ip -
Use of a different intermediate Management Domains might cause undesired bills
.ip -
It makes message forgery harder.
.lp
O/R names may be authorised to send and/or receive over a set
of Management Domains.
Again, for the same reasons, O/R names are likely to be
authorised in the context of an explicit route of Management
Domains.
This type of authorisation should be regarded as the
authorisation of individual users to use the services of the
MTA in question.
.lp
The approach described here has the following weaknesses.
.np
It relies on MTA security not being breached.
This seems reasonable, as an MTA will not in general be trusted
unless real guarantees are given (e.g. by the payment of
bills).
.np
It relies on the integrity of the OSI connection used to verify
the identity of a remote MTA.
In principle, this can be made as secure as desired.
.lp
In practice, some low level of forgery is acceptable, provided
accounting and policing techniques allow for higher levels of
abuse to be detected and traced.
Some real experiences with this model are now considered.
.sh 1 "UCL Experience"
.lp
The networking environment of the department of Computer
Science at University College London,
and its control and accounting requirements are now
described.  This is followed by overviews of the
mail system, the database system, and their interaction for
accounting and control.  Problems encountered and weaknesses
of the system are identified.
.br
.ne 7c
.sp 6c
.ce

Figure 2: UCL Connectivity
.pp
The department offers an Interconnection Service\*([.Kirstein85a\*(.]
to allow UK (and some continental European) university and research
institution workers to communicate with their US
counterparts.  The requirement for such a service arises from the
diverse protocol systems used by the interconnected networks \*([.Cole84a\*(.].
.pp
The department has connections to the UK X.25 networks PSS and
JANET, and the DARPA Internet.  PSS (Packet Switched Service) is
British Telecom's national
public X.25 network, with gateways to the international X.25
service.
JANET (Joint Academic NETwork) is the private X.25 network
of the Science and Engineering Research Council and the Computer
Board, linking universities and scientific research
establishments. The higher level protocols used by the academic
community on these X.25
networks in the UK are the "coloured book protocols" serving as an
"intercept" standard pending the introduction of suitable ISO standards \*([.Rosner82a\*(.].
Mail services on Janet are provided by use of the JNT Mail Protocol
(also known as Greybook) \*([.Kille84a\*(.].
This specifies both an MTA level protocol and a UA level
protocol.
.pp
The DARPA (U.S. Defense Advanced Research Projects Agency)
Internet is a concatenation of networks linked by
gateways.
The department's local area network can be treated as
one such network.
These networks
use a common set of internet protocols based on
datagrams \*([.Leiner85a\*(.].
The MTA level protocol is SMTP (Simple Mail Transfer Protocol) \*([.Postel82a\*(.],
and the UA level protocol is a format specification known as RFC 822 \*([.Crocker82a\*(.].
The JNT Mail Protocol UA level is derived from RFC 822, and is
identical in all the major aspects.
Interworking is thus straightforward.
The DARPA protocols are also used to access CSNET (the US
Computer Science Network).
UCL is also connected to a UNIX\**
.(f
\n($f. UNIX is a trademark of AT&T Bell Laboratories.
.)f
dialup telephone network,
Usenet,
which again uses RFC 822 derived protocols \*([.Nowitz78a\*(.].
.pp
The department's local area network consists of three Cambridge rings
interconnected by bridges, and also an Ethernet.
Associated with each wide area network
are one or more network access machines.
Application processes access the network access machines by use
of a special Host to Front-end protocol, which allows for
working over multiple networks in a clean manner.
Current service applications are Terminal Access, File Transfer
and Mail.
.\"Remote users have terminal access
.\"to a host dedicated to providing interconnection services.  Mail
.\"systems run on all the hosts.
.\".pp
.\"Connectivity mail nets -- numbers direct/indirect
.pp
Use of the service is circumscribed by
the policies of the funding bodies, and by the
Value Added Carrier Licence from the UK Department of Trade.  These
restrictions are complex, specifying permitted
national/international relaying and permitted message path, in
terms of billing authority.  Coupled with
the extensive connectivity and high connection
costs, the above restrictions
necessitate effective control and accounting measures.
Users are grouped according to sponsorship, charging
policy and access rights.  In practice this broadly divides users
into a centrally-funded class, with access rights
determined by sponsor, and a directly-funded class with
corresponding access rights.
.pp
The Message Transfer service is provided by MMDF (Multi
Channel Memo Distribution Facility) \*([.Kingston84a,\|Kille85a\*(.].
MMDF is a Message Handling System
for the UNIX
operating system, having support for multiple message transfer
protocols, and a selection of UA systems.  MMDF
provides the user with a homogeneous view of the underlying connectivity.
The MMDF MTA is implemented by a number of processes and queues.
The process
.rb submit
is invoked to accept messages submitted
both by local UA processes and by servers handling incoming
messages.
All authorisation controls are applied by submit at message
submission time.
There are a number of
.rb channels
to associate protocols, networks or logical divisions.
Associated with each channel is a queue into which submit
places incoming messages.
The process
.rb deliver
reads and manages the queues associated with one or more channels
and invokes subordinate channel processes to
perform message transmission.
.pp
Channels are used as a mechanism for logically dividing hosts
into sets for the application of management controls.  There
are three orthogonal mechanisms for doing this:
.np
Splitting a set of hosts into more than one channel.  For example, PSS and
Janet hosts are on separate channels as the former network charges
for services, and
the latter is free, though technically they could be on
the same channel as the same protocols may be used.
.np
Allocating a set of hosts to more than one channel for
route or Quality of Service selection.
For example, there are two paths between UCL and the rest of the DARPA
Internet, for use by different users, and with
different polling frequencies.
.np
Separating inbound and outbound channels, when the policies are
asymmetric (e.g. where a user can receive messages from a given
channel, but not send messages over it).
.pp
It is noted that a message can be considered both to arrive and
to depart
over a channel.
A message arrives over a fixed
.rb "inbound channel" ,
but may leave over one of a set of
.rb "outbound channels" ,
as the requested destination may well be accessible over more
than one channel.
Submit considers each possible outbound channel in turn, and queues the
message for the first (if any) channel satisfying
authorisation criteria.  Access policies for each channel are
implemented in terms of two basic sets of controls.  The
following channel access policies are identified; they may
differ for inbound and outbound directions.
.np
FREE:   No controls.
.np
LOG:    Note unauthorised access.
.ne 4
.np
WARN:   As for LOG, but send warning to sender of message.  This is
particularly useful for transition from a free to a controlled
situation.
.np
BLOCK:  Only authorised access to the channel.
.pp
The first set of controls is applied on the basis of user (O/R name).
There is a (hash encoded) database of O/R names, giving a mode and a list of
authorised channel.
The following O/R name modes are identified.
.np
SEND:   O/R name only valid as originator.
.np
RECV:   O/R name only valid as recipient.
.np
BOTH:   O/R name valid as both sender and recipient.
.np
LIST:   special control for distribution lists expanded as a
value added service.
.lp
When a message is being authorised by submit, the originator and
recipient O/R names are looked up, and a set of valid inbound and
outbound channels identified for each.
This information is used
.np
To determine whether there is authorisation for the inbound channel.
.np
To identify which (if any) of the potential outbound channels
are acceptable.
.lp
The priority on the matches are: 1) the first possible outbound
channel; 2) recipient with list authorisation; 3) sender; 4)
recipient.
.pp
The second set of controls is applied on the basis of just the
MTAs and channels involved.  These controls are
applied by table lookup in four tables associated with each
channel.  Two tables are for inbound and two for outbound control.
As well as MTA and channel, there is the concept of MTA route,
to identify a sequence of MTAs, with the message having
originated at the first MTA in the sequence or (ostensibly) be destined
for a user at the last MTA in the sequence.
An important special case is that of messages originating on,
or destined for an adjacent MTA.
The following may be controlled:
.ip -
Inbound channel to everywhere, or to a set of MTAs and outbound
channels.
.ip -
Outbound channel from everywhere, or from a set of MTAs and inbound
channels.
.ip -
Inbound MTA to everywhere, or to a set of MTAs and outbound
channels.
.ip -
Outbound MTA from everywhere, or from a set of MTAs and inbound
channels.
.ip -
Inbound MTA route to everywhere, or to a set of MTAs and outbound
channels.
.ip -
Outbound MTA route from everywhere, or from a set of MTAs and inbound
channels.
.lp
These MTA/channel controls are applied before the user controls and in
general are considered to override them.  They may also be
applied in addition to the user controls, to
apply policy controls
on top of essentially O/R name based controls.
This mechanism might be used to prevent third  country
relaying, in cases where both originator and recipient are
authorised in other contexts.
.pp
The following checks and actions are performed by MMDF, to
prevent forgery.
.np
Only privileged processes can access the message queues.
.np
Locally originated messages have correct UA level originator O/R name.
.np
Locally originated messages have correct MTA level originator O/R name.
This is the O/R name used for authorisation.
.np
When a message is received, the name of the connecting MTA is
added to the source route associated with the originator O/R
name.
O/R names must have an authorised route, to minimise the risk
of forgery.
.pp
The Interconnection Service Management
System (ISMS) used to provide high level access
to and control of the above mechanisms is now discussed.
ISMS is based on Mistress, a proprietary relational database package \*([.Rhodnius82a\*(.].
The basic model of operation is that high level administrative
information is contained in the ISMS.
This information is used to derive control information for
MMDF, in the form of plain text files to be incorporated into
the hash encoded database used by MMDF.
When a message transfer is authorised or rejected by MMDF, this
is noted in a log, together with: originator O/R name;
recipient O/R name; inbound channel; outbound channel; any MTA
route involved; the size of the message; and
reason for authorisation
for both the inbound and outbound channel.
The MTAs directly involved are implicit in the O/R names.
Example reasons for authorisation:
.ip -
Inbound by originator + no authorisation required outbound.
.ip -
Inbound by recipient + outbound by recipient.
.ip -
Inbound MTA to outbound channel pair.
.lp
These logs are then processed at intervals by the ISMS
(typically once per
day),
and stored in terms of the administrative information in the ISMS.
This information can then be used to generate bills, and
usage statistics.
The following are the main relations in the database.
.ip -
Class of user. This gives the set of channels to which access
is permitted.  Most users fall into one of a very small number
of classes.
.ip -
Project.  A project authorised to use the Interconnection
Service.  This contains funding information, and indicates the
class.
Also various management O/R names.  These can be used for
automatic mailing of accounting, statistical, and other project
information.
.ip -
Mailbox.  Indicates an O/R name in terms of project.
.ip -
Message.  Indicates a transfer, and may be associated with one
or two mailboxes for authorisation.
.ip -
Channel.  Indicates charging policy in terms of class of user.
.pp
To prevent administrative overload,
each project is given an account at UCL allowing project
administrators to remotely update UA registrations by use
of a simple program.  This two level scheme,
means that the UCL administration deals
with projects and not individual users.
.pp
This scheme was brought into operation in February 1985, and
has clearly demonstrated the viability of the proposed model.
There are currently about 150 projects and 2000 UAs
registered.
The system handles about 1000 messages per day.
Perhaps the hardest aspect to deal with was the
transition from an uncontrolled situation, where messages were
relayed without checking (even though they should have been
authorised in principle).
A significant number of project applications were processed
over this period.
The following problems were noted:
.ip -
Accurate naming is essential.  MTA tables (on all MTAs
involved) must be kept
consistent for this scheme to work effectively.
.ip -
Certain MTA protocol requirements are made.  A few implementations
which violated message protocols
for other obscure reasons had problems registering O/R names.
.ip -
Strict matching of the source route for incoming messages means
that a few sites have to register multiple routes for each
O/R name.  This is due to both variable internal routing at these
sites, and non-symmetrical routing for incoming and outgoing
directions.  This is seen as an unfortunate consequence of a
control necessary to minimise forgery.
.ip -
If a message is routed over an insecure OSI connection, or
through an insecure MTA, there is clearly a risk of forgery.
This risk must be borne by the project registering such a
route.
.bp
.ip -
Submission time checking means that messages which are not
correctly delivered are still charged for.
This is undesirable, but removes the (non-trivial) problem of
correlating submission and delivery logs.
It does not appear to be a substantial problem in practice.
.ip -
If a message transfer is optimised by transferring one copy of
the message for multiple recipients, the saving is not passed
on to the user.
This seems reasonable, on the grounds that the user should be
charged for services in a uniform manner.
.sh 1 Conclusions
.lp
The model developed here is not a general one;
A broader description of the theoretical issues may be found in \*([.Kille85b\*(.].
However, we believe that our work has shown the viability of a
scheme where messages are authorised, with no requirements
placed on the remote systems other than accurate and
trustworthy protocol implementation.
Our logs have not, as yet, shown any successful attempts to
break the security.
In fact the problems of correctly registering authorised users,
partly due to the stringency of the checks,
have been far more of a problem.
.pp
The major advantage of the approach described is that it allows for MTA
level accounting,  with minimal assistance from other system
components.
It is seen as has having two basic styles of application.
The first is where a Management Domain (or pair of Management
Domains) offer communication over an expensive link
(or some service such as message format conversion).
This approach would enable billing of users and/or Management
Domains for the use of this service.
The UCL implementation is of this type.
A second area where is might be useful is as a gateway to a
Management Domain providing services for one organisation.
This approach would allow the organisation to control its
employees' access to Message Handling Services, much as
telephone systems are sometimes controlled at present.
This type of approach is seen a particularly important as
controls are introduced into systems without overall control in
this area.
.sp
.]<
.\"1982-1
.ds [F Rhodnius82a
.]-
.ds [T Mistress: Relational Database Management System Version 2.1
.ds [Q Rhodnius Incorporated
.ds [R Manual
.ds [D 1982
.nr [T 0
.nr [A 0
.nr [O 0
.][ 4 tech-report
.\"1984-2
.ds [F CCITT84a
.]-
.ds [Q CCITT SG 5/VII
.ds [T Recommendations X.400
.ds [D November 1984
.ds [R Message Handling Systems: System Model - Service Elements
.ds [K x400
.nr [T 0
.nr [A 0
.nr [O 0
.][ 4 tech-report
.\"Cole.R.H.-1984-3
.ds [F Cole84a
.]-
.ds [A R.H. Cole
.as [A " and others
.ds [T Network Interconnection Facilities at UCL
.ds [D September 1984
.ds [J ICCC, Melbourne
.nr [T 0
.nr [A 0
.nr [O 0
.][ 1 journal-article
.\"Crocker.D.H.-1982-4
.ds [F Crocker82a
.]-
.ds [A D.H. Crocker
.ds [T Standard of the Format of ARPA Internet Text Messages
.ds [D August 1982
.ds [R RFC 822
.ds [K RFC822
.nr [T 0
.nr [A 0
.nr [O 0
.][ 4 tech-report
.\"Kille.S.E.-1984-5
.ds [F Kille84a
.]-
.ds [A S.E. Kille, (editor)
.ds [T JNT Mail Protocol (revision 1.0)
.ds [D March 1984
.ds [I Joint Network Team
.ds [C Rutherford Appleton Laboratory
.ds [K greybook
.ds [K jntmail
.nr [T 0
.nr [A 0
.nr [O 0
.][ 2 book
.\"Kille.S.E.-1985-6
.ds [F Kille85a
.]-
.ds [T Addressing in MMDF II
.ds [A S.E. Kille
.ds [J Proc. EUUG Conference, Paris
.ds [D April 1985
.nr [T 0
.nr [A 0
.nr [O 0
.][ 1 journal-article
.\"Kille.S.E.-1985-7
.ds [F Kille85b
.]-
.ds [A S.E. Kille
.as [A " and D.H. Brink
.ds [T A Model of Message Flow Control
.ds [D Sepetmber 1985
.ds [J Submitted to IFIP WG 6.5 Congress, Washington
.nr [T 0
.nr [A 0
.nr [O 0
.][ 1 journal-article
.\"Kingston.D.P.-1984-8
.ds [F Kingston84a
.]-
.ds [A D.P. Kingston
.ds [T MMDFII: A Technical Review
.ds [J Usenix Conference
.ds [C Salt Lake City
.ds [D August 1984
.ds [K MMDF
.nr [T 0
.nr [A 0
.nr [O 0
.][ 1 journal-article
.\"Kirstein.P.T.-1985-9
.ds [F Kirstein85a
.]-
.ds [T The University College London International Computer Communications Interconnection Service
.ds [A P.T. Kirstein
.ds [J Conference on Communications in Distributed Systems, Karlsruhe
.ds [D March 1985
.nr [T 0
.nr [A 0
.nr [O 0
.][ 1 journal-article
.\"Leiner.B.M.-1985-10
.ds [F Leiner85a
.]-
.ds [T The DARPA Internet Protocol Suite
.ds [A B.M. Leiner
.as [A ", R.H. Cole
.as [A ", J.B. Postel
.as [A ", and D. Mills
.ds [J Proceedings INFOCOM85
.ds [I IEEE
.ds [D March 1985
.ds [l own paper
.nr [T 0
.nr [A 0
.nr [O 0
.][ 1 journal-article
.\"Nowitz.D.A.-1978-11
.ds [F Nowitz78a
.]-
.ds [A D.A. Nowitz
.as [A " and M.E. Lesk
.ds [T A Dial-up Network of UNIX systems
.ds [D August 1978
.ds [R UNIX Programmer's Manual, 7th edition, Bell Labs.
.ds [K UUCP
.nr [T 0
.nr [A 0
.nr [O 0
.][ 4 tech-report
.\"Postel.J.B.-1982-12
.ds [F Postel82a
.]-
.ds [A J.B. Postel
.ds [T SIMPLE MAIL TRANSFER PROTOCOL
.ds [D August 1982
.ds [R RFC 821
.ds [K RFC821
.ds [K SMTP
.nr [T 0
.nr [A 0
.nr [O 0
.][ 4 tech-report
.\"Rosner.R.A.-1982-13
.ds [F Rosner82a
.]-
.ds [T Towards OSI among UK Universities
.ds [A R.A. Rosner
.ds [J Proc. ICCC'82, London
.ds [I North-Holland
.ds [D September 1982
.ds [P 607-611
.nr [P 1
.ds [K OSI Universities
.nr [T 0
.nr [A 0
.nr [O 0
.][ 1 journal-article
.]>
.sp
.ip Note: 7
RFC indicates a DARPA Request For Comments, which may be
obtained from: USC Information Sciences Institute, Marina del
Rey, Ca. USA.
.sh 1 Acknowledgements
.lp
Thanks to Tom Daniel for helpful comments on the paper.

The first is where a Management Domain (or pair of Management
Domains) offer communication over an expensive link
(or some service such as message format conversion).
This appmmdf/doc/README   444      0     12         715  3631176106   6364 
		How to make the Documentation

Unfortunately there is too much variation in document preparation
proceedures for me to make a Makefile for it, but here is the
basic info you need:

addressing:	troff -ms addressing

administrators:	administrators/Makefile

review/*:	troff -ms fnmacro p?

auth.guide:	troff -me auth.guide

authorization:	troff -me authorization

table.sample.kille:	pr table.sample.kille

uk.overview:	troff uk.overview  (Troff in the raw!)

82, London
.ds [I North-Holland
.ds [D September 19mmdf/lib/   755      0     12           0  3656466050   5446 mmdf/lib/addr/   755      0     12           0  3671074611   6353 mmdf/lib/addr/gen   555      0     12          57  3620510355   7071 make -f ../../Makefile.com -f Makefile.real $*
 there is too much variation in document preparation
proceedures for me to make a Makefile for it, but here is the
basic info you need:

addressing:	troff -ms addressing

administrators:	administrators/Makefile

review/*:	troff -ms fnmacro p?

auth.guide:	troff -me auth.guide

authorization:	troff -me authorization

table.sample.kille:	pr table.sample.kille

uk.overview:	troff uk.overview  (Troff in the raw!)

82, London
.ds [I North-Holland
.ds [D September 19mmdf/lib/addr/ap_util.c   444      0     12       30574  3664425127  10274 #include "util.h"
#include "ap.h"

#if DEBUG > 1
extern char debug;
extern char *typtab[];
#endif

/*  Standard routines for handling address list element nodes

/*  < 1978  B. Borden       Wrote initial version of parser code
 *  78-80   D. Crocker      Reworked parser into current form
 *  Apr 81  K. Harrenstein  Hacked for SRI
 *  Jun 81  D. Crocker      Back in the fold.  Finished v7 conversion
 *                          minor cleanups.
 *                          repackaging into more complete set of calls
 *  Jul 81  D. Crocker      ap_free & _alloc check for not 0 or -1
 *                          malloc() error causes jump to ap_init error
 */

struct ap_prevstruct   *ap_fle;   /* parse state top of stack           */
				  /* "fl" => file, but could be other   */

int     (*ap_gfunc) ();           /* Ptr to character get fn            */

extern int      ap_peek;                  /* basic parse state info       */
extern int	ap_perlev;
extern int	ap_grplev;

extern char	*malloc();

/* ********************  LIST NODE PRIMITIVES  *********************** */

AP_ptr
	ap_alloc ()               /* create node, return pointer to it    */
{
    AP_ptr ap;

    /* NOSTRICT */
    ap = (AP_ptr) malloc (sizeof (struct ap_node));
    if (ap == (AP_ptr) 0)
	return ((AP_ptr) 0);

    ap_ninit (ap);
    return (ap);
}

ap_ninit (ap)
    register AP_ptr ap;
{
    ap -> ap_obtype = APV_NIL;
    ap -> ap_obvalue = (char *) 0;
    ap -> ap_ptrtype = APP_NIL;
    ap -> ap_chain = (AP_ptr) 0;
}

ap_free (ap)                      /* free node's storage                */
register AP_ptr ap;
{
    switch ((int)ap)
    {                             /* get rid of node, if have one       */
	case OK:
	case NOTOK:               /* nothing to free                    */
	    break;

	default:                  /* actually have a node               */
	    switch ((int)(ap -> ap_obvalue))
	    {                     /* get rid of its data string         */
		case OK:
		case NOTOK:       /* nothing to free                    */
		    break;

		default:
		    free (ap -> ap_obvalue);
	    }
	    free ((char *) ap);
    }
}

ap_fllnode (ap, obtype, obvalue)     /* add data to node at end of chain     */
register AP_ptr ap;
char   obtype;
register char  *obvalue;
{
    extern char *strdup ();

    ap -> ap_obtype = obtype;
    ap -> ap_obvalue = (obvalue == 0) ? (char *)0 : strdup (obvalue);

#if DEBUG > 1
    if (debug)
	printf ("(%s/'%s')", typtab[obtype], obvalue);
#endif
}

AP_ptr
	ap_new (obtype, obvalue)  /* alloc & fill node                  */
char    obtype;
char   *obvalue;
{
    register AP_ptr nap;

    nap = ap_alloc ();
    ap_fllnode (nap, obtype, obvalue);
    return (nap);
}

/* ***************  LIST MANIPULATION PRIMITIVES  ******************* */

ap_insert (cur, ptrtype, new)     /* create/fill/insert node in list    */
register AP_ptr cur;              /* where to insert after              */
char ptrtype;                     /* inserted is more or new address    */
register AP_ptr new;              /* where to insert after              */
{
 /* Now copy linkages from current node */

    new -> ap_ptrtype = cur -> ap_ptrtype;
    new -> ap_chain = cur -> ap_chain;

 /* Now point current node at inserted node */

    cur -> ap_ptrtype = ptrtype;
    cur -> ap_chain = new;
}

AP_ptr
	ap_sqinsert (cur, type, new)    /* insert sequence */
    register AP_ptr cur,
		    new;
    int type;
{
    AP_ptr oldptr;
    int otype;

    switch ((int)new) {
	case OK:
	case NOTOK:
	    return ((AP_ptr) 0);
    }

    oldptr = cur -> ap_chain;
    otype = cur -> ap_ptrtype;
    cur -> ap_chain = new;
    cur -> ap_ptrtype = type;

    while (new -> ap_ptrtype != APP_NIL &&
		new -> ap_chain != (AP_ptr) 0 &&
		new -> ap_chain -> ap_obtype != APV_NIL)
	new = new -> ap_chain;

    if (new -> ap_chain != (AP_ptr)0 && new -> ap_chain -> ap_obtype == APV_NIL)
	ap_delete (new);

    new -> ap_chain = oldptr;
    new -> ap_ptrtype = otype;
    return (new);
}

ap_delete (ap)                    /* remove next node in sequence       */
register AP_ptr ap;
{
    register AP_ptr next;

    if (ap != (AP_ptr) 0 && ap -> ap_ptrtype != APP_NIL)
    {                             /* only if there is something there   */
	next = ap -> ap_chain;    /* link around one to be removed      */

	ap -> ap_ptrtype = next -> ap_ptrtype;
	ap -> ap_chain = next -> ap_chain;

	ap_free (next);
    }
}

AP_ptr
	ap_append (ap, obtype, obvalue)
				  /* alloc, fill, insert node           */
register AP_ptr ap;               /* node to insert after               */
char    obtype;
char   *obvalue;
{
    register AP_ptr nap;

    nap = ap_alloc ();
    ap_fllnode (nap, obtype, obvalue);
    ap_insert (ap, APP_ETC, nap);
    return (nap);
}
/**/

AP_ptr
       ap_add (ap, obtype, obvalue)
				  /* try to append data to current node   */
register AP_ptr ap;
char    obtype;
register char  *obvalue;
{
    extern char *multcat ();
    register char  *ovalue;

    if (ap -> ap_obtype != obtype)
	return (ap_append (ap, obtype, obvalue));
    else                          /* same type or empty => can append     */
    {
	if (obvalue == 0)         /* No data to add                       */
	    return (OK);

	if ((ovalue = ap -> ap_obvalue) == (char *) 0)
	    ap_fllnode (ap, obtype, obvalue);
	else                      /* add to existing data                 */
	{
	    ovalue = ap -> ap_obvalue;
	    ap -> ap_obvalue = multcat (ovalue, " ", obvalue, (char *)0);
	    free (ovalue);
	}

#if DEBUG > 1
	if (debug)
	    printf ("+%d/'%s')", obtype, obvalue);
#endif
    }
    return (OK);
}

/**/

AP_ptr
	ap_sqdelete (strt_node, end_node) /* remove nodes, through end node     */
register AP_ptr strt_node;
register AP_ptr end_node;
{
    switch ((int)strt_node) {
	case OK:
	case NOTOK:
	    return ((AP_ptr)0);
    }
    while (strt_node -> ap_ptrtype != APP_NIL) {
	if (strt_node -> ap_chain == end_node) {
				/* last one requested                 */
	    ap_delete (strt_node);
	    return (strt_node -> ap_chain);
	}
	ap_delete (strt_node);
    }
    return ((AP_ptr) 0);          /* end of chain                       */
}

AP_ptr
	ap_1delete (ap)                   /* remove all nodes of address to NXT */
register AP_ptr ap;               /* starting node                      */
{
    while (ap -> ap_ptrtype != APP_NIL) {
	if (ap -> ap_ptrtype == APP_NXT)
	    return (ap -> ap_chain);
	ap_delete (ap);
    }
    return ((AP_ptr) 0);                   /* end of chain              */
}

ap_sqtfix (strt, end, obtype)     /* alter obtype of a node subsequence */
register AP_ptr strt;
register AP_ptr end;
register char   obtype;
{
    for ( ; ; strt = strt -> ap_chain) {
	if (strt -> ap_obtype != APV_CMNT)
	    strt -> ap_obtype = obtype;
	if (strt == end || strt -> ap_ptrtype == APP_NIL)
	    break;
    }
}

/**/

AP_ptr
	ap_move (to, from)  /* move node after from to be after to   */
    register AP_ptr to,
		    from;
{
    register AP_ptr nodeptr;

    if (from -> ap_ptrtype == APP_NIL || from -> ap_chain == (AP_ptr) 0)
	return (from);  /* quiet failure */

    nodeptr = from -> ap_chain;

    from -> ap_chain = nodeptr -> ap_chain;
    from -> ap_ptrtype = nodeptr -> ap_ptrtype;

    ap_insert (to, APP_ETC, nodeptr);
    return (from);      /* next in chain, now */
}

AP_ptr
	ap_sqmove (to, from, endtype)    /* move sequence */
    register AP_ptr to,
		    from;
    register char endtype;              /* copy only COMMENT and this */
{
    switch ((int)from) {
	case OK:
	case NOTOK:
	    return ((AP_ptr) 0);
    }

    while (from -> ap_ptrtype != APP_NIL && from -> ap_chain != (AP_ptr) 0) {
	if (endtype != (char) APV_NIL)
	    if (from -> ap_obtype != APV_CMNT && from -> ap_obtype != endtype)
		break;
	to = ap_move (to, from);
    }

    return (to);          /* end of chain                       */
}

/* ************************  PARSE STATE  *************************** */

AP_ptr ap_pstrt,                  /* current last node in parse tree    */
       ap_pcur;                   /* current last node in parse tree    */

ap_iinit (gfunc)                   /* input function initialization     */
int     (*gfunc) ();
{
    ap_gfunc = gfunc;             /* Set character fetch func           */
    ap_peek = -1;                 /* No lex peek char                   */
}

ap_clear ()                     /* Clear out the parser state           */
{
    ap_grplev = 0;                 /* Zero group nesting depth          */
    ap_perlev = 0;                 /* Zero <> nesting depth             */
}

AP_ptr
       ap_pinit (gfunc)           /* init, alloc & set start node       */
int     (*gfunc) ();
{
    ap_iinit (gfunc);
    return (ap_pstrt = ap_pcur = ap_alloc ());
}

/*  parse state saving uses a linked list of state information,
 *  recorded in ap_prevstruct structures.
 *  the list is manipulated as a simple stack.
 */

ap_ppush (gfunc)                  /* save parse context, ap_iinit    */
int     (*gfunc) ();
{
    extern char *malloc ();
    register struct ap_prevstruct  *tfil;

    /*NOSTRICT*/
    if ((tfil = (struct ap_prevstruct *) malloc (sizeof (*tfil))) ==
				(struct ap_prevstruct *) NULL)
	return (NOTOK);

    tfil -> ap_opeek = ap_peek;   /* save regular parse state info      */
    tfil -> ap_ogroup = ap_grplev;
    tfil -> ap_opersn = ap_perlev;
    tfil -> ap_prvgfunc = ap_gfunc;
    tfil -> ap_prvptr = ap_fle;   /* save previous stack entry          */
    ap_fle = tfil;                /* save current stack entry           */
    ap_iinit (gfunc);             /* create new parse state             */
    return (OK);
}

ap_ppop ()                        /* restore previous parse state       */
{
    register struct ap_prevstruct  *tfil;

    tfil = ap_fle;
    ap_peek = tfil -> ap_opeek;
    ap_grplev = tfil -> ap_ogroup;
    ap_perlev = tfil -> ap_opersn;
    ap_gfunc = tfil -> ap_prvgfunc;
    ap_fle = tfil -> ap_prvptr;
    free ((char *) tfil);
}
/**/

/*  the next three routines handle most of the overhead for acquiring
 *  the address list from a file.
 */

ap_flget ()                      /* get character from included file   */
{
    register int c;

    c = getc (ap_fle -> ap_curfp);

    if (c == '\n')
	return (',');           /* a minor convenience */

    return (c);
}

ap_fpush (file)                  /* indirect input from file           */
char   file[];
{
    if (ap_ppush (ap_flget) == NOTOK)   /* save current & set for file input */
	return (NOTOK);

    if ((ap_fle -> ap_curfp = fopen (file, "r")) == (FILE *) NULL)
    {                             /* couldn't get the file, tho         */
	ap_ppop ();
	return (NOTOK);
    }
    return (OK);
}

ap_fpop ()                       /* pop the stack, if any input nested */
{
    if (ap_fle -> ap_curfp != NULL)
	fclose (ap_fle -> ap_curfp);

    ap_ppop ();
}

/* ******************  PARSE LIST MANIPULATION  ********************* */

/*  these echo the basic list manipuation primitives, but use ap_pcur
 *  for the pointer and any insert will cause ap_pcur to be updated
 *  to point to the new node.
 */

ap_palloc ()                      /* alloc, insert after pcur           */
{
    ap_pnsrt (ap_alloc (), APP_ETC);
}

ap_pfill (obtype, obvalue)        /* add data to node at end of chain     */
char   obtype;
register char  *obvalue;
{
    extern char *strdup ();

    ap_pcur -> ap_obtype = obtype;
    ap_pcur -> ap_obvalue =
		(obvalue == (char *) 0) ? (char *) 0 : strdup (obvalue);
#if DEBUG > 1
    if (debug)
	printf ("(%s/'%s')", typtab[obtype], obvalue);
#endif
}

ap_pnsrt (ap, ptrtype)          /* add node to end of parse chain     */
register AP_ptr ap;
char    ptrtype;
{
    register AP_ptr rap_pcur;

    if ((rap_pcur = ap_pcur) -> ap_obtype == APV_NIL)
    {                             /* current one can be used            */
	rap_pcur -> ap_obtype = ap -> ap_obtype;
	rap_pcur -> ap_obvalue = ap -> ap_obvalue;
	ap -> ap_obvalue = 0;
	ap_free (ap);
    } else {                      /* really do the insert               */
	rap_pcur -> ap_ptrtype = ptrtype;
	rap_pcur -> ap_chain = ap;
	ap_pcur = ap;
    }
}

ap_pappend (obtype, obvalue)      /* alloc, fill, append at end         */
char    obtype;
char   *obvalue;
{
    ap_palloc ();                 /* will update pcur                   */
    ap_fllnode (ap_pcur, obtype, obvalue);
}

ap_padd (obtype, obvalue)         /* try to append data to current node */
char    obtype;
char  *obvalue;
{
    register AP_ptr nap;

    nap = ap_add (ap_pcur, obtype, obvalue);

    if (nap != OK)                /* created new node                   */
	ap_pcur = nap;
}
h             */
}

AP_ptr
       ap_pinit (gfunc)           /* init, alloc & set start node       */
int     (*gfunc) ();
{
    ap_mmdf/lib/addr/ap_p2s.c   444      0     12       27653  3671073140  10017 #include "util.h"               /* from ../utildir */
#include "conf.h"               /* from ../mmdf/h */
#include "ch.h"
#include "ap.h"
#include "dm.h"
#include "ll_log.h"

/*  Format one address from pointers to constitutents, in a tree
 *
 *  Returns:    pointer to string if successful or
 *              NOTOK if error
 *
 *  SEK - using ap_p2s to output for ap_t2s has the general problem
 *              of losing comments.   Perhaps ap_t2s should be
 *              separate?
 */

extern LLog *logptr;
extern int ap_outtype;
extern char *multcat();
extern char *strdup();
extern char *ap_dmflip();
extern Domain *dm_v2route();

char *
ap_p2s (group, name, local, domain, route)
    AP_ptr  group,             /* beginning of group name  */
	    name,              /* beginning of person name  */
	    local,             /* beginning of local-part */
	    domain,            /* basic domain reference */
	    route;             /* beginning of 733 forward routing */
{
    Dmn_route   dmnroute;
    AP_ptr      lastptr;
    char        *routp;         /* 822 -> 733 route string */
    int         inperson,
		ingroup;
    register char *strp;        /* The string we are building */
    register char *cp;
    register AP_ptr curptr;
    char *flipptr;
    char *stripptr;
    char tmpbuf [LINESIZE];
    char *tmpdomain [DM_NFIELD];
    int tmpcnt;
    char buf[LINESIZE];         /* buf for dm_v2route                 */
    char *drefptr;              /* pointer to string to be output     */

#ifdef DEBUG
    ll_log (logptr, LLOGFTR, "ap_p2s()");

    if ((ap_outtype & AP_822) == AP_822)   /* AP_733 is implicit default */
	ll_log (logptr, LLOGFTR, "AP_822 on");
    ll_log (logptr, LLOGFTR, (ap_outtype & AP_BIG)
	? "AP_BIG on" : "AP_LITTLE on");
    if ((ap_outtype & AP_NODOTS) == AP_NODOTS)  /* AP_DOTS is implicit def. */
	ll_log (logptr, LLOGFTR, "AP_NODOTS on");
#endif

    inperson = ingroup = FALSE;
    strp = strdup("");
    routp = strdup("");

    if (group != (AP_ptr) 0) {
#ifdef DEBUG
	ll_log (logptr, LLOGFTR, "ap_p2s:  group is '%s'", group -> ap_obvalue);
#endif
	for (curptr = group; curptr != (AP_ptr)0; curptr = curptr -> ap_chain)
	{                             /* print munged addr                  */
	    switch (curptr -> ap_obtype) {
		default:
		case APV_NIL:
		    break;

		case APV_CMNT:        /* Output value as comment */
		    if (name != (AP_ptr) 0) {
				    /* only output comments for pretty forms */
			val2str (tmpbuf, curptr -> ap_obvalue, APV_CMNT);
			cp = multcat(strp, (strp[0]?" ":""), "(",tmpbuf,")", (char *)0);
			free (strp);
			if(cp == (char *)0)
			    return( (char *)NOTOK);
			strp = cp;
		    }
		    continue;

		case APV_NGRP:
		    ingroup = TRUE;
		case APV_GRUP:
		    val2str (tmpbuf, curptr -> ap_obvalue, APV_GRUP);
		    cp = multcat(strp, tmpbuf, (char *)0);
		    free (strp);
		    if(cp == (char *)0)
			return( (char *)NOTOK);
		    strp = cp;
		    continue;
	    }
	    break;
	}
	if (ingroup) {
	    cp = multcat(strp, ": ", (char *)0);
	    free (strp);
	    if(cp == (char *)0)
		return( (char *)NOTOK);
	    strp = cp;
	}
    }

    if (name != (AP_ptr) 0) {
#ifdef DEBUG
	ll_log (logptr, LLOGFTR, "ap_p2s:  name is '%s'", name -> ap_obvalue);
#endif
	for (curptr = name; curptr != (AP_ptr)0; curptr = curptr -> ap_chain)
	{                         /* print munged addr                  */
	    switch (curptr -> ap_obtype) {
		default:
		case APV_NIL:
		    break;

		case APV_CMNT:        /* Output value as comment */
		    val2str (tmpbuf, curptr -> ap_obvalue, APV_CMNT);
		    cp = multcat(strp, (strp[0]?" " : ""), "(", tmpbuf, ")", (char *)0);
		    free (strp);
		    if(cp == (char *)0)
			return( (char *)NOTOK);
		    strp = cp;
		    continue;

		case APV_NPER:
		    inperson = TRUE;
		case APV_PRSN:
		    val2str (tmpbuf, curptr -> ap_obvalue, APV_PRSN);
		    cp = multcat(strp, tmpbuf, (char *)0);
		    free (strp);
		    if(cp == (char *)0)
			return( (char *)NOTOK);
		    strp = cp;
		    continue;
	    }
	    break;
	}
    }

    if (inperson) {
	cp = multcat(strp, " <", (char *)0);
	free (strp);
	if(cp == (char *)0)
	    return( (char *)NOTOK);
	strp = cp;
    }

    if (route != (AP_ptr) 0)      /* we have routing info */
    {
#ifdef DEBUG
	ll_log (logptr, LLOGFTR, "ap_p2s:  route is '%s'", route -> ap_obvalue);
#endif
	for (lastptr = curptr = route; ; curptr = curptr -> ap_chain) {
	    if (curptr == (AP_ptr)0)    /* Grot Grot !!!!!!! */
		goto defcase1;
	    switch (curptr -> ap_obtype) {
		case APV_EPER:
		continue;

		defcase1:;	/* YEUCH !! */
		default:
		case APV_NIL:
		    if ((ap_outtype & AP_822) == AP_822) {
					/* piece of cake */
			cp = multcat(strp, ":", (char *)0);
			free (strp);
			if(cp == (char *)0)
			    return( (char *)NOTOK);
			strp = cp;
		    }
		    break;

		case APV_CMNT:        /* Output value as comment */
		    if (name != (AP_ptr) 0) {
			val2str (tmpbuf, curptr -> ap_obvalue, APV_CMNT);
			cp = multcat(strp, (strp[0]?" ":""), "(",tmpbuf,")", (char *)0);
			free (strp);
			if(cp == (char *)0)
			    return( (char *)NOTOK);
			strp = cp;
		    }
		    continue;

		case APV_DLIT:
		case APV_DOMN:
		    val2str (tmpbuf, curptr -> ap_obvalue, curptr -> ap_obtype);
		    flipptr = stripptr = (char *) 0;
		    drefptr = tmpbuf;
		    if (((ap_outtype & AP_BIG) == AP_BIG) ||
		       (((ap_outtype & AP_NODOTS) == AP_NODOTS) &&
				(curptr == lastptr)) ) {
		        /* check domain ref in either case */
			Domain  *lrval = dm_v2route (tmpbuf, buf, &dmnroute);

			if(lrval == (Domain *)MAYBE)
			    return( (char *)MAYBE);

			if (lrval != (Domain *) NOTOK) {
			    if ((ap_outtype & AP_NODOTS) == AP_NODOTS
				&& curptr == lastptr) {
				/* only strip domain on next-hop in route */
				tmpcnt = cstr2arg (dmnroute.dm_argv[0],
					DM_NFIELD, tmpdomain, '.');
				stripptr = strdup(tmpdomain[0]);
				drefptr = stripptr;
			    } 
			    else 
				if ((ap_outtype & AP_BIG) == AP_BIG) {
				    flipptr = ap_dmflip (buf);
				    drefptr = flipptr;
				}
			}
		    }

		    if ((ap_outtype & AP_822) == AP_822) {
					/* piece of cake */
		        cp = multcat(strp,(curptr!=lastptr?",@":"@"),drefptr,(char *)0);
			free (strp);
			if(cp == (char *)0)
			    return( (char *)NOTOK);
			strp = cp;
		    } else {
			if(routp[0] == '\0')
			    cp = multcat("@", drefptr, (char *)0);
			else
			    cp = multcat("%", drefptr, routp, (char *)0);
			free (routp);
			if(cp == (char *)0)
			    return( (char *)NOTOK);
			routp = cp;
		    }
		    if (flipptr != (char *)0)
			free (flipptr);
		    if (stripptr != (char *)0)
			free (stripptr);
		    continue;
	    }
	    break;
	}
    }

    if (local != (AP_ptr) 0) {
#ifdef DEBUG
	ll_log (logptr, LLOGFTR, "ap_p2s:  local is '%s'", local -> ap_obvalue);
#endif
	for (curptr = local; curptr != (AP_ptr)0; curptr = curptr -> ap_chain) {
	    switch (curptr -> ap_obtype) {		/* print munged addr */
		default:
		case APV_NIL:
		    break;

		case APV_CMNT:        /* SEK - don't skip these */
		    if (name != (AP_ptr) 0) {
			val2str (tmpbuf, curptr -> ap_obvalue, APV_CMNT);
			cp = multcat(strp, (strp[0]?" ":""), "(",tmpbuf,")", (char *)0);
			free (strp);
			if(cp == (char *)0)
			    return( (char *)NOTOK);
			strp = cp;
		    }
		    continue;

		case APV_WORD:
		case APV_MBOX:
					/* SEK - YUK                    */
		    if (strindex (":Include:", curptr -> ap_obvalue) == 0)
			(void) strcpy (tmpbuf, curptr -> ap_obvalue);
		    else
			val2str (tmpbuf, curptr -> ap_obvalue, APV_MBOX);
		    cp = multcat(strp, tmpbuf, (char *)0);
		    free (strp);
		    if(cp == (char *)0)
			return( (char *)NOTOK);
		    strp = cp;
		    continue;
	    }
	    break;
	}
    }

    if (domain != (AP_ptr) 0) {
#ifdef DEBUG
	ll_log (logptr, LLOGFTR, "ap_p2s:  domain is '%s'",
		domain -> ap_obvalue);
#endif
	val2str (tmpbuf, domain -> ap_obvalue, domain -> ap_obtype);
	flipptr = stripptr = (char *) 0;
	drefptr = tmpbuf;
	if (((ap_outtype & AP_BIG) == AP_BIG) ||
	   (((ap_outtype & AP_NODOTS) == AP_NODOTS)) && route == (AP_ptr) 0) {
	    /* check domain ref in either case */
	    Domain  *lrval = dm_v2route (tmpbuf, buf, &dmnroute);

	    if(lrval == (Domain *)MAYBE)
		return( (char *)MAYBE);

	    if (lrval != (Domain *) NOTOK) {
		if (((ap_outtype & AP_NODOTS) == AP_NODOTS) &&
			(route == (AP_ptr) 0)) {
		    /* If there is no route, this domain is next hop: strip */
		    tmpcnt = cstr2arg (dmnroute.dm_argv[0], DM_NFIELD, 
					tmpdomain, '.');
		    stripptr = strdup(tmpdomain[0]);
		    drefptr = stripptr;
		}
		else
		    if ((ap_outtype & AP_BIG) == AP_BIG) {
			flipptr = ap_dmflip (buf);
			drefptr = flipptr;
		    }
	    }
	}

	if ((ap_outtype & AP_822) == AP_822 || routp[0] == '\0')  /* easy */
	    cp = multcat (strp, "@", drefptr, routp, (char *)0);
	else
	    cp = multcat (strp, "%", drefptr, routp, (char *)0);

	if(cp == (char *)0)
		return(cp);
	free (strp);
	strp = cp;
	if (flipptr != (char *) 0)
	    free (flipptr);
	if (stripptr != (char *) 0)
	    free (stripptr);
    }
    free (routp);

    if (inperson) {
	cp = multcat(strp, ">", (char *)0);
	free (strp);
	if(cp == (char *)0)
	    return( (char *)NOTOK);
	strp = cp;
    }
    if (ingroup) {
	cp = multcat(strp, ";", (char *)0);
	free (strp);
	if (cp == (char *)0)
    	    return( (char *)NOTOK);
	strp = cp;
    }
    return (strp);
}


/*
 *  This function is just barely usable.  The hole problem of
 *  quoted strings is hard to get right especially when the
 *  mail system is trying to make up for human forgetfulness.
 *                              -DPK-
 *  SEK - have improved this somewhat by giving knowledge of
 *  the various object types.  Does not handle strings of spaces.
 */
LOCFUN
	val2str (buf, value, obtype)      /* convert to canonical string */
    char *buf,
	 *value,
	 obtype;
{
    int gotspcl;
    int inquote;
    register char *fromptr,
		  *toptr;
#ifdef DEBUG
    ll_log (logptr, LLOGFTR, "val2str ('%s', %d)", value, obtype);
#endif


    if (obtype == APV_CMNT) {
	for (fromptr=value, toptr=buf; *fromptr != '\0'; *toptr++ = *fromptr++)
	    switch (*fromptr) {
		case '\r':
		case '\n':
		case '\\':
		case '(':
		case ')':
		    *toptr++ = '\\';
	    }
	*toptr = '\0';
	return;
    }

    if (obtype == APV_DLIT) {
	for (fromptr=value, toptr=buf; *fromptr != '\0'; *toptr++ = *fromptr++)
	    switch (*fromptr) {
		case '\r':
		case '\n':
		case '\\':
		    *toptr++ = '\\';
		    continue;
		case '[':
		    if (fromptr != value)
		       *toptr++ = '\\';
		    continue;
		case ']':
		    if (*(fromptr + 1) != (char) 0)
			*toptr++ = '\\';
	    }
	*toptr = '\0';
	return;
    }

    inquote = FALSE;
    for (gotspcl = FALSE, fromptr = value; *fromptr != '\0'; fromptr++) {
	switch (*fromptr) {
	    case '"':
		inquote = (inquote == TRUE ? FALSE : TRUE);     /* Flip-Flop */
		break;

	    case '\\':
	    case '\r':
	    case '\n':
		if (inquote == FALSE) {
		    gotspcl = TRUE;
		    goto copyit;
		}
		break;

	    case '<':
	    case '>':
	    case '@':
	    case ',':
	    case ';':
	    case ':':
	    case '\t':
	    case '[':
	    case ']':
	    case '(':
	    case ')':
		if( inquote == FALSE) {
		    gotspcl = TRUE;
		    goto copyit;
		}
		break;

	    case ' ':
		if (inquote == FALSE)
		    if ((obtype == APV_DOMN) || (obtype == APV_MBOX)) {
			gotspcl = TRUE;
			goto copyit;
		    } else {
				/* SEK hack to handle " at "              */
				/* yes - this really is needed          */
			if ((uptolow(*(fromptr + 1)) == 'a') &&
			    (uptolow(*(fromptr + 2)) == 't') &&
			    (*(fromptr + 3) == ' ')) {
			    gotspcl = TRUE;
			    goto copyit;
			}
		    }
		break;

	    case '.':
		if (inquote == FALSE && ((obtype == APV_GRUP) || (obtype == APV_PRSN)))
		{
		    gotspcl = TRUE;
		    goto copyit;
		}
		break;
	}
    }

    gotspcl = inquote;          /* If were in a quote, something's wrong */
copyit:
    toptr = buf;
    if (gotspcl)
	*toptr++ = '"';
    for (fromptr = value; *fromptr != '\0'; *toptr++ = *fromptr++)
	switch (*fromptr) {
	    case '\r':
	    case '\n':
	    case '\\':
	    case '"':
		if (gotspcl)
		    *toptr++ = '\\';
	}
    if (gotspcl)
	*toptr++ = '"';
    *toptr = '\0';
}
se */
	    Domain  *lrval = dm_v2route (tmpbuf, buf, &dmnroute);

	    if(lrval == (Dmmdf/lib/addr/norm.c   444      0     12        4060  3620510360   7543 #include "util.h"
#include "mmdf.h"
#include "ap.h"
#include "ch.h"

extern LLog *logptr;

extern int ap_outtype;

main (argc, argv)
int argc;
char *argv[];
{
    char adr [LINESIZE];
    char *p;

    AP_ptr *ap;
    AP_ptr *group;
    AP_ptr *name;
    AP_ptr *route;
    AP_ptr *domain;
    AP_ptr *local;
    AP_ptr *norm;
    int i;

    Chan *thechan;
    char *dfldomain = (char *) 0;
    char *dflhost = (char *) 0;


    mmdf_init ("NORM");
    logptr -> ll_level = LLOGFTR;
    siginit ();

    if ((thechan = ch_nm2struct ("foo")) == (Chan *) NOTOK)
    {
	printf ("Chan foo not found\n");
	thechan = (Chan *) 0;
    }
    else
    {
	printf ("channel '%s' used\n", thechan -> ch_show);
	dfldomain = thechan -> ch_ldomain;
	dflhost = thechan -> ch_lname;
    }
    fflush (stdout);


    for (i=1; i < argc; i++)
    {
	printf ("received: '%s'\n", argv[i]);
	fflush (stdout);

	ap = ap_s2tree (argv[i]);
	if (ap == (AP_ptr) NOTOK)
	{
		printf ("Parse failed\n\n");
		continue;
	}
	ap_outtype = AP_822;
	ap_t2s (ap, &p);

	printf ("tree in full is: '%s'\n", p);
	fflush (stdout);
	free (p);

	norm = ap_normalize (dflhost, dfldomain, ap, thechan);
	ap_t2s (norm, &p);
	printf ("normalised tree (822, little) is: '%s' \n", p);
	fflush (stdout);
	free (p);

	ap_t2parts (ap, &group, &name, &local, &domain, &route);
	p = ap_p2s ((AP_ptr) 0, (AP_ptr) 0, local, domain, route);
	printf ("real  bits: '%s'\n", p);
	fflush (stdout);
	free (p);

	ap_outtype = AP_733;
	ap_t2s (norm, &p);
	printf ("normalised tree (jnt, little) is: '%s' \n", p);
	fflush (stdout);
	free (p);
	ap_outtype = (AP_822  | AP_BIG);
	ap_t2s (norm, &p);
	printf ("normalised tree (822, big) is: '%s' \n", p);
	free (p);
	fflush (stdout);
	if (thechan == NULL)
	    continue;
	ap_outtype = thechan -> ch_apout;
	ap_t2s (norm, &p);
	printf ("normalised tree (from channel type) is: '%s' \n", p);
	free (p);

	printf ("\n\n");
	fflush (stdout);

    }
}

err_abrt (code, fmt, b, c, d)
char code,
	*fmt, *b, *c, *d;
{
printf ("norm aborting (%s)\n", rp_valstr (code));
printf (fmt, b, c, d);
fflush (stdout);
exit (0);
}

.h"
#include "ch.h"

extern LLog *logptr;

extern int ap_outtype;

main (argc, argv)
int argc;
char *argv[];
{
    char adr [LINESIZE];
    char *p;

    AP_ptr *ap;
    AP_ptr *group;
    AP_ptr *name;
    AP_ptr *route;
    AP_ptr *domain;
    AP_ptr *local;
    AP_ptr *norm;
    int i;

    Chan *thechan;
    char *dfldomain = (char *) 0;
    char *dflhost = (char *) 0;


    mmdf_init ("NORM");
    logptr -> ll_level = LLOGFTR;
    siginit ();

    if ((thmmdf/lib/addr/parse.c   444      0     12        4601  3620510360   7703 /* $Header: parse.c,v 1.3 85/01/17 23:07:11 dpk Exp $ */
/* $Log:	parse.c,v $
 * Revision 1.3  85/01/17  23:07:11  dpk
 * Reworked, it now works
 * 
 * Revision 1.2  83/11/18  17:09:41  reilly
 * Steve Kille's version
 *
 *  < 1978  B. Borden       Wrote initial version of parser code
 *  78-80   D. Crocker      Reworked parser into current form
 */
#if DEBUG < 2
main() {
	fprintf(stderr, "parse is useless when compiled with -DDEBUG=x (x < 2)\n");
}
#else
#include "ap_lex.h"
#include "util.h"
#include "mmdf.h"
#include "ap.h"

extern char ap_llex;
extern char *locname;
AP_ptr ap_fullparse ();

extern LLog *logptr;


extern char *namtab[],
	   *typtab[];
extern char debug;

main (argc, argv)
{
    AP_ptr ap_fp;
    int	getach();

    mmdf_init ("PARSE");
    logptr -> ll_level = LLOGFTR;
    if (argc == 2)
	debug++;

    for (;;)
    {
	printf ("Parse: ");

	switch (ap_fp = ap_fullparse (getach)) {
	case NOTOK:
	    printf ("\nNOTOK: on %s\n", namtab[ap_llex]);
	    break;
	case DONE:
	    printf ("\nNOTOK: on %s\n", namtab[ap_llex]);
	    break;
	case OK:
	    printf ("\nOK?: on %s\n", namtab[ap_llex]);
	    break;
	default:
	    printf ("\n\nAccept\n");
	    pretty (ap_fp);
	    printf ("\n");
	    while (ap_sqdelete (ap_fp, (AP_ptr)0) != 0);
	    ap_free (ap_fp);  /* delete full string         */
	    ap_fp = (AP_ptr) 0;
	    continue;
	}
    	break;
    }
    exit (0);
}

getach ()
{
    int	c;

    c = getchar();
    if (c == '\n')
	return (0);
    if (c == EOF)
	exit(0);
    return (c);
}

pretty (ap)
register AP_ptr ap;
{
    register int    depth = 1;

    do {
	switch (ap -> ap_obtype)
	{
	    case APV_EGRP: 
	    case APV_EPER: 
		depth -= 2;
	}

	printf ("%.*s%-9s", depth, "          ");
    	if (ap -> ap_obtype >= 0 && ap -> ap_obtype <= 13)
		printf("%s %s", typtab[ap -> ap_obtype],
			ap -> ap_obvalue ? ap -> ap_obvalue : "NIL");
    	else
		printf("BOGUS!(%d) %s", ap -> ap_obtype,
			ap -> ap_obvalue ? ap -> ap_obvalue : "NIL");
    	switch (ap -> ap_ptrtype) {
	case APP_NIL:
		printf("\t(NIL)\n");
    		break;
	case APP_NXT:
		printf("\t(NXT)\n\n");
    		break;
	case APP_ETC:
		printf("\t(ETC)\n", ap -> ap_ptrtype);
    		break;
    	default:
		printf("\t(BOGUS!)\n", ap -> ap_ptrtype);
    	}
	switch (ap -> ap_obtype)
	{
	    case APV_NGRP: 
	    case APV_NPER: 
		depth += 2;
	}
    } while ((ap = ap -> ap_chain) != (AP_ptr)0);
    printf ("End on null pointer\n");
}
#endif
= multcat(strp, ">", (char *)0);
	free (strp);
	if(cp == (char *)0)
	    return( (char *)NOTOK);
	strp = cp;
    }
    if (ingrmmdf/lib/addr/ap_s2p.c   444      0     12        1735  3620510361   7763 #include "ap_lex.h"
#include "util.h"
#include "ap.h"

/*
 *		ap_s2p.c
 *
 *	One of the main uses of this routine is to replace adrparse.c
 */

LOCVAR char *s2p_txt;             /* hdr_in() passes to alst()      */

LOCFUN
	s2p_in ()               /* adrs extracted from component text */
{
    if (s2p_txt == (char *) 0)  /* nothing to give it                 */
	return (EOF);

    switch (*s2p_txt)
    {
	case '\0':
	case '\n':            /* end of string, DROP ON THROUGH     */
	    s2p_txt = (char *) 0;
	    return (0);
    }
    return (*(s2p_txt++));
}

char *
ap_s2p (strp, tree, group, name, local, domain, route)
char	*strp;
AP_ptr	*tree, *group, *name, *local, *domain, *route;
{
    s2p_txt = strp;
    *tree = ap_pinit (s2p_in);
    switch (ap_1adr ())
    {
    case DONE: 
    	return ((char *) DONE);
    case OK: 
    	ap_t2parts (*tree, group, name, local, domain, route);
	return (s2p_txt);	/* so they can parse the next chunk */
    }
    return ((char *) NOTOK);
}
ase DONE:
	    printf ("\nNOTOK: onmmdf/lib/addr/ap_lex.c   444      0     12       16305  3664425130  10075 #include "util.h"
#include "ap.h"
#include "ap_lex.h"

/*  Perform lexical analysis on input stream

    The stream is assumed to be "unfolded" and the <crlf>/<lwsp> sequence
    is NOT checked for.  This must be done by the character-acquisition
    routine, if necessary.  In fact, space, tab and newline all return the
    same lexical token.  Due to a number of bagbiting mail systems on the
    net which cannot handle having a space within a mailbox name, period
    (.) has been equated with space.

    Letters, numbers, and other graphics, except specials, also all return
    the same token.

    Note that only printable characters and format effectors are legal.
    All others cause an error return.

    Only COMMENTs and WORDs have data associated with them.

*/

/*  < 1978  B. Borden       Wrote initial version of parser code
 *  78-80   D. Crocker      Reworked parser into current form
 *  Apr 81  K. Harrenstein  Hacked for SRI
 *  Jun 81  D. Crocker      Back in the fold.  Finished v7 conversion
 *                          minor cleanups.
 */

extern char ap_lxtable[];         /* ascii chars -> symbolic terminals    */
extern int ap_intype;
int     ap_peek = -1;             /* one-character look-ahead             */
char    ap_llex;                  /* last lexeme returned by ap_lex ()    */

#if DEBUG > 1
extern char debug;
char   *namtab[] =
{
    "eo-data",                    /* LV_EOD          0                    */
    "error",                      /* LV_ERROR        1                    */
    "comma",                      /* LV_COMMA        2                    */
    "at",                         /* LV_AT           3                    */
    "colon",                      /* LV_COLON        4                    */
    "semi",                       /* LV_SEMI         5                    */
    "comment",                    /* LV_COMMENT      6                    */
    "less",                       /* LV_LESS         7                    */
    "grtr",                       /* LV_GRTR         8                    */
    "word",                       /* LV_WORD         9                    */
    "from",                       /* LV_FROM        10                    */
    "domain-literal"              /* LV_DLIT        11                    */
};
#endif

/**/

ap_lex (lexval)
char    lexval[];
{
    register char   c,
		   *lexptr;
    register int    retval;

    while ((retval = ap_lxtable[c = ap_char ()]) == LT_SPC)
	;                         /* Skip space, tab and newline          */
    lexptr = lexval;
    *lexptr++ = c;

    switch (retval)
    {
	case LT_ERR:              /* Bad Character */
	    retval = LV_ERROR;
	    break;

	case LT_EOD:              /* End Of Data stream */
	    retval = LV_EOD;
	    break;

	case LT_COM:              /* comma ","  -- addr list separator    */
	    retval = LV_COMMA;
	    break;

	case LT_AT:               /* At sign "@"  -- node separator       */
	    retval = LV_AT;
	    break;


/* *******************  DATA TYPES AND GROUP LIST  ******************** */

	case LT_COL:              /* colon  ":" -- data type / group      */
	    retval = LV_COLON;
	    break;

	case LT_SEM:              /* semicolon ";" -- group end           */
	    retval = LV_SEMI;
	    break;



/* ***********************  PERSON ADDRESS LIST  ************************ */

	case LT_LES:              /* less-than-sign "<"  -- person list   */
	    if (ap_lxtable[c = ap_char ()] == LT_LES)
		retval = LV_FROM; /* << implies redirection               */
	    else {
		ap_peek = c;      /* restore xtra char                    */
		retval = LV_LESS;
	    }
	    break;

	case LT_GTR:              /* greater-than-sign ">" -- end person  */
	    retval = LV_GRTR;
	    break;
/* *******************  QUOTED & UNQUOTED WORDS  ********************** */

	case LT_LTR:              /* letters                              */
	case LT_SQT:              /* single-quote "'"  -- just char, here */
	case LT_RPR:              /* right paren ")" -- just char, here   */
	    for (;;) {
		switch (ap_lxtable[*lexptr++ = c = ap_char ()]) {
		    case LT_LTR:
		    case LT_SQT:
		    case LT_RPR:
			continue;

		    case LT_ERR:
			retval = LV_ERROR;
			break;

		    case LT_EOD:  /* permit eod to end string            */
		    default:      /* non-member character                 */
			ap_peek = c;
			lexptr--;
#ifdef notdef /* no more " at " == '@' */
			if (ap_intype == AP_733 &&
			      lexptr == &lexval[2] &&
				uptolow (lexval[0]) == 'a' &&
				uptolow (lexval[1]) == 't'   )
			    retval = LV_AT;
			else
#endif notdef
			    retval = LV_WORD;
		}
		break;
	    }
	    break;

	case LT_QOT:              /* double quote "\""  => string         */
	    retval = LV_WORD;
	    --lexptr;           /* don't put quotes into obvalue  - SEK   */
	    for (;;) {
		switch (ap_lxtable[*lexptr++ = c = ap_char ()]) {
		    case LT_QOT:
			--lexptr;
			break;
		    case LT_SQT:  /* include next char w/out interpeting  */
				  /* and drop on through                  */
			*(lexptr - 1) = ap_char ();
		    case LT_RPR:
		    case LT_LPR:
		    default:
			continue;
		    case LT_ERR:
		    case LT_EOD:
			retval = LV_ERROR;
		}
		break;
	    }
	    break;
/* *************************  COMMENT  ******************************** */

	case LT_LPR:              /* left paren "("  -- comment start     */
	    lexptr--;             /* remove left-most paren */
	    for (retval = 0;;) {  /* retval is count of comment nesting   */
		switch (ap_lxtable[*lexptr++ = c = ap_char ()]) {
		    case LT_LPR:  /* nested comments                      */
			retval++; /* just drop on through                 */
		    default: 
			continue;
		    case LT_SQT:  /* include next char w/out interpeting  */
			*(lexptr - 1) = ap_char ();
			continue;
		    case LT_RPR: 
			if (--retval > 0)
			    break;
			lexptr--;       /* remove right-most paren */
			retval = LV_COMMENT;
			break;
		    case LT_EOD: 
		    case LT_ERR: 
			retval = LV_ERROR;
			break;
		}
		break;
	    }
	    break;


/* *********************  DOMAIN LITERAL  ***************************** */

	case LT_LSQ:              /* left squar bracket "["               */
	    FOREVER
	    {
		switch (ap_lxtable[*lexptr++ = c = ap_char ()])
		{
		    default: 
			continue;
		    case LT_SQT:  /* include next char w/out interpeting  */
			*(lexptr - 1) = ap_char ();
			continue;
		    case LT_RSQ:
			retval = LV_DLIT;
			break;
		    case LT_EOD: 
		    case LT_ERR: 
			retval = LV_ERROR;
			break;
		}
		break;
	    }
	    break;
    }


/* ***********************  CLEANUP AND RETURN  ************************* */

    *lexptr = '\0';

#if DEBUG > 1
    if (debug)
	printf (" %s", namtab[retval]);
#endif

    return (ap_llex = retval);
}
/* *******************  GET NEXT INPUT CHARACTER  ********************* */

ap_char ()
{                                 /* handle lookahead and 8th bit         */
    extern int  (*ap_gfunc) ();   /* Ptr to character get fn              */
    register int    i;

    if (ap_peek == 0)
	return (0);
    if ((i = ap_peek) > 0) {
	ap_peek = -1;
	return (i);
    }

    if ((i = ((*ap_gfunc) ())) == -1)
	return (0);               /*  EOD                                 */

    return ((isascii (i)) ? i : '\177');
				  /* force error, if eighth bit is on     */
}

V_SEMI;
	    break;



/* ***********************  PERSON ADDRESS LIST  ************************ */

	case LT_LES:              /* less-than-sign "<"  -- person list   */
	    if (ap_lxtable[c = ap_char ()] == LT_LES)
		retval = LV_FROM; /* << implies redirection               */
	    else {
		ap_peek = c;      /*mmdf/lib/addr/ap_t2parts.c   444      0     12        5600  3620510361  10651 #include "util.h"
#include "ap.h"
#include "ll_log.h"

/*  Record beginnings of major sections of an address
 *
 *  Returns:    pointer to next node, after address
 *               0 if end of tree
 *              -1 if error
 *
 *  if both rroute and nroute are requested, and the tree has routing
 *  in reverse-path form, then these will point to the same node.
 */


extern LLog *logptr;

LOCVAR	int perlev, grplev;

AP_ptr
	ap_t2parts (tree, group, name, local, domain, route)
    register AP_ptr  tree;      /* the parse tree */
				/* THESE ARE POINTERS TO POINTERS */
				/* where to stuff the relevant pointers */
    AP_ptr  *group,             /* beginning of group name  */
	    *name,              /* beginning of person name  */
	    *local,             /* beginning of local-part */
	    *domain,            /* basic domain reference */
	    *route;             /* beginning of 822 reverse routing */
{
    AP_ptr retptr;
    int gotlocal;

#ifdef DEBUG
    ll_log (logptr, LLOGFTR, "ap_t2parts ()");
#endif

    if (tree -> ap_obtype == APV_NIL)
	return ((AP_ptr) OK);      /* Ignore null stuff */

    grplev = perlev = 0;

    if (group   != (AP_ptr *) 0)
	*group   = (AP_ptr)   0;
    if (name    != (AP_ptr *) 0)
	*name    = (AP_ptr)   0;
    if (local   != (AP_ptr *) 0)
	*local   = (AP_ptr)   0;
    if (domain  != (AP_ptr *) 0)
	*domain  = (AP_ptr)   0;
    if (route   != (AP_ptr *) 0)
	*route   = (AP_ptr)   0;

    for (gotlocal = FALSE; ; tree = tree -> ap_chain) {
					/* print munged addr                  */
	switch (tree -> ap_obtype) {
	    case APV_NIL: 
		retptr = (AP_ptr) 0;
		goto endit;

	    case APV_CMNT:        /* Output value as comment */
		break;

	    case APV_NPER: 
		perlev++;
		if (name != (AP_ptr *) 0 && *name == (AP_ptr) 0)
		    *name = tree;
		break;

	    case APV_PRSN:
		break;

	    case APV_EPER: 
		perlev--;
		break;

	    case APV_NGRP: 
		grplev++;
		if (group != (AP_ptr *) 0 && *group == (AP_ptr) 0)
		    *group = tree;
		break;

	    case APV_GRUP:
		break;

	    case APV_EGRP: 
		grplev--;
		break;

	    case APV_WORD: 
	    case APV_MBOX: 
		if (local != (AP_ptr *) 0 && *local == (AP_ptr) 0)
		    *local = tree;
		gotlocal = TRUE;

		break;

	    case APV_DLIT:
	    case APV_DOMN:
		if (gotlocal) {         /* reference after local        */
		    if (domain != (AP_ptr *) 0 && *domain == (AP_ptr) 0)
			*domain = tree;
		} else {                /* must be 822 route    */
					/* domain precedes local-part   */
		    if (route != (AP_ptr *) 0 && *route == (AP_ptr) 0)
			*route = tree;
		}
		break;

	    default: 
		break;
	}

	switch (tree -> ap_ptrtype) {
	    case APP_NXT:
		if (tree -> ap_chain -> ap_obtype != APV_NIL) {
		    retptr = (AP_ptr) tree -> ap_chain;
		    goto endit;
		}
				 /* else DROP ON THROUGH               */
	    case APP_NIL:
		retptr = (AP_ptr) OK;
		goto endit;		/* Courtesy of Steve Kille */
	}
    }

endit:
    return (retptr);
}
logptr;

LOCVAR	int perlev, grplev;

AP_ptr
	ap_t2parts (tree, group, name, local, domain, route)
    register AP_ptr  tree;    mmdf/lib/addr/ap_s2tree.c   444      0     12        1754  3620510361  10464 #include "util.h"
#include "ap.h"
#include "ll_log.h"

/* parse a string into an address tree */

extern LLog *logptr;

LOCVAR char *ap_strptr;

LOCVAR
	getach ()       /* get next character from string */
{
    if (*ap_strptr == '\0')  /* end of the string, of course */
	return (0);
    return (*ap_strptr++);
}

AP_ptr
	ap_s2tree (thestr)
    char thestr[];
{
    short gotone;
    AP_ptr thetree;

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "ap_s2tree (%s)", thestr);
#endif

    ap_strptr = thestr;

    if ((thetree = ap_pinit (getach)) == (AP_ptr) NOTOK)
	goto badend;
    ap_clear();

    gotone = FALSE;
    for ( ; ; )
	switch (ap_1adr ()) {
	    case NOTOK:
		ap_sqdelete (thetree, (AP_ptr) 0);
		ap_free (thetree);
	badend:
		ap_strptr = (char *) 0;
		return ((AP_ptr) NOTOK);

	    case OK:
	    	gotone = TRUE;
		continue;       /* more to process */

	    case DONE:
		ap_strptr = (char *) 0;
		if (gotone == FALSE)
			return ((AP_ptr) NOTOK);
		return (thetree);
	}
    /* NOTREACHED */
}
parts ()");
#endif

mmdf/lib/addr/ap_t2s.c   444      0     12        2624  3620510362   7766 #include "util.h"               /* from ../utildir */
#include "conf.h"               /* from ../mmdf/h */
#include "ch.h"
#include "ap.h"
#include "dm.h"
#include "ll_log.h"

/*  Format one address from parse tree, into a string
 *
 *  Returns:    pointer to next node, after address
 *               0 if end of tree
 *              NOTOK if error
 */

extern LLog *logptr;
extern char *strdup();
extern char *ap_p2s();
extern AP_ptr ap_t2parts();

AP_ptr
	ap_t2s (thetree, strpp)
    AP_ptr thetree;             /* the parse tree */
    char **strpp;               /* where to stuff the string */
{
    AP_ptr locptr,              /* in case we need to fake personal name */
	   grpptr,
	   namptr,
	   domptr,
	   routptr,
	   rtntree;

#ifdef DEBUG
    ll_log (logptr, LLOGFTR, "ap_t2s ()");
#endif

    rtntree = ap_t2parts (thetree, &grpptr, &namptr,
			  &locptr, &domptr, &routptr);
    if (rtntree == (AP_ptr)NOTOK) {
	ll_log (logptr, LLOGTMP, "ap_t2s: error from ap_t2parts()");
	*strpp = strdup ("(MMDF Error!)");
	return ((AP_ptr)NOTOK);
    }

    *strpp = ap_p2s (grpptr, namptr, locptr, domptr, routptr);

    if(*strpp == (char *)MAYBE){
	*strpp = strdup("");        /* is this needed ?? */
	return ((AP_ptr)MAYBE);
    }
    else if (*strpp == (char *)NOTOK) {
	ll_log (logptr, LLOGTMP, "ap_t2s: error from ap_p2s()");
	*strpp = strdup ("(MMDF Error!)");
	return ((AP_ptr)NOTOK);
    }
    return (rtntree);
}
r)   0;

    for (gotlocal = FALSE; ; tree = tree -> ap_chain) {
					/* print munged addr                  mmdf/lib/addr/fullparse.c   444      0     12        4322  3620510362  10570 #include "ap_lex.h"
#include "util.h"
#include "ap.h"

/*  Build full parse tree from an address list                            */

/*  I expect this not to be used very often, but it was convenient to
    provide and will work just fine when there are small address
    lists.

    It repeatedly invokes ap_1adr to acquire address segments, linking
    them together and NXT's.

*/

/*  < 1978  B. Borden       Wrote initial version of parser code
 *  78-80   D. Crocker      Reworked parser into current form
 */
extern AP_ptr ap_pcur,    /* Current ap node ptr              */
	      ap_pstrt;   /* Beginning of parse tree          */

#if DEBUG > 1
extern char debug;		  /* True if in debug mode                */
extern char *statnam[],
           *ptrtab[],
           *typtab[];
#endif

/**/

AP_ptr
ap_fullparse (gchfunc)
char    (*gchfunc) ();
{
	AP_ptr	ap_fp;        /* Beginning of list                    */
	char	more;
	register AP_ptr ap_sp, ap_prevp = NULL;
	int	naddrs = 0;

	ap_fp = ap_pinit (gchfunc);
	more = 0;
	for (;;)
	{
		ap_sp = ap_pcur;
		switch (ap_1adr ()) {
		case NOTOK: 
			return ((AP_ptr)NOTOK);
		case DONE: 
			if (--more > 0)   /* > 0 means files are stacked          */
			    ap_fpop (); /* just drop on through                 */
			else if (naddrs)
			    return(ap_fp);
			else
			    return ((AP_ptr)DONE);
		case OK: 
			naddrs++;
			switch (ap_sp -> ap_obtype) {
			case APV_DTYP: 
			    if (lexequ ("Include", ap_sp -> ap_obvalue))
			    {	  /* indirect through a file              */
#if DEBUG > 1
				if (debug)
					printf ("(File:%s)",
					    ap_sp -> ap_chain -> ap_obvalue);
#endif
				if (ap_fpush (ap_sp -> ap_chain -> ap_obvalue)
				    != OK)
					return ((AP_ptr)NOTOK);
				more++;
				if ((ap_pcur = ap_sqdelete (ap_sp, ap_pcur)) == 0)
					ap_palloc ();
				else
					ap_pnsrt (ap_alloc (), APP_NXT);
				if (ap_fp == ap_sp)
					ap_fp = ap_pcur;
				if (ap_prevp != ap_sp)
					ap_prevp -> ap_chain = ap_pcur;
				continue;
			    }
			default:
#if DEBUG > 1
			    printf ("[Got an address]\n", ap_pcur);
#endif
			    ap_pcur -> ap_ptrtype = APP_NXT;
			    ap_prevp = ap_pcur;
			    ap_pstrt = ap_pcur = ap_alloc();
			    ap_prevp -> ap_chain = ap_pstrt;
			    continue;
			}
		}
	}
}
	if (gotlocal) {         /* reference after local        */
		    if (domain != (AP_ptr *) 0 && *domain == (AP_ptr) 0)
			*domain = tree;
		} else {                /* must be 822 route    */
					/* domain precedes local-part   */
		    if (route != (AP_ptr *) 0 && *route == (AP_ptr) 0)
			*route = trmmdf/lib/addr/Makefile.real   444      0     12        6530  3635173347  11032 #
#	Makefile for address parser portion of libmmdf.a
#
#  Just the prefix of the name should be given (i.e. "foo" for "foo.c")
MODULES =  ap_s2tree ap_p2s ap_t2s ap_t2parts ap_normali \
	    ap_1adr ap_util ap_lex ap_lxtable ap_dmflip \
	    ap_s2p adrparse td norm parse ghost

OBJECTS =   ap_normali.o ap_s2tree.o ap_p2s.o ap_t2s.o ap_t2parts.o \
	    ap_1adr.o ap_util.o ap_lex.o ap_lxtable.o ap_dmflip.o \
	    ap_s2p.o adrparse.o

LIBSRCS =  ap_s2tree.c ap_p2s.c ap_t2s.c ap_t2parts.c ap_normali.c \
	    ap_1adr.c ap_util.c ap_lex.c ap_lxtable.c ap_dmflip.c \
	    ap_s2p.c adrparse.c

real-default: ../addr-made
../addr-made: $(OBJECTS)
	$(AR) r ../libmmdf.a $(OBJECTS)
	-touch ../addr-made
	-@echo "address parser routines built normally"

parse: parse.o fullparse.o $(MMDFLIBS)
	cc $(LDFLAGS) -o parse parse.o fullparse.o $(MMDFLIBS) $(SYSLIBS)

td: td.o $(MMDFLIBS)
	cc $(LDFLAGS) -o td td.o $(MMDFLIBS) $(SYSLIBS)

norm: norm.o $(MMDFLIBS)
	cc $(LDFLAGS) -o norm norm.o $(MMDFLIBS) $(SYSLIBS)

ghost: ghost.o $(MMDFLIBS)
	cc $(LDFLAGS) -o ghost ghost.o $(MMDFLIBS) $(SYSLIBS)

lint:
	$(LINT) -Caddr $(LFLAGS) $(LIBSRCS)

clean:
	-rm -f parse td norm ghost *.o x.c makedep eddep \
		make.out errs llib-laddr*



# DO NOT DELETE THIS LINE -- make depend uses it

ap_s2tree.o: ap_s2tree.c
ap_s2tree.o: ../../h/util.h
ap_s2tree.o: ../../h/ap.h
ap_s2tree.o: ../../h/ll_log.h
ap_p2s.o: ap_p2s.c
ap_p2s.o: ../../h/util.h
ap_p2s.o: ../../h/conf.h
ap_p2s.o: ../../h/ch.h
ap_p2s.o: ../../h/ap.h
ap_p2s.o: ../../h/dm.h
ap_p2s.o: ../../h/ll_log.h
ap_t2s.o: ap_t2s.c
ap_t2s.o: ../../h/util.h
ap_t2s.o: ../../h/conf.h
ap_t2s.o: ../../h/ch.h
ap_t2s.o: ../../h/ap.h
ap_t2s.o: ../../h/dm.h
ap_t2s.o: ../../h/ll_log.h
ap_t2parts.o: ap_t2parts.c
ap_t2parts.o: ../../h/util.h
ap_t2parts.o: ../../h/ap.h
ap_t2parts.o: ../../h/ll_log.h
ap_normali.o: ap_normali.c
ap_normali.o: ../../h/util.h
ap_normali.o: ../../h/conf.h
ap_normali.o: ../../h/ll_log.h
ap_normali.o: ../../h/ch.h
ap_normali.o: ../../h/ap.h
ap_normali.o: ../../h/ap_norm.h
ap_normali.o: ../../h/dm.h
ap_1adr.o: ap_1adr.c
ap_1adr.o: ../../h/util.h
ap_1adr.o: ../../h/conf.h
ap_1adr.o: ../../h/ap.h
ap_1adr.o: ../../h/ap_lex.h
ap_1adr.o: ../../h/ll_log.h
ap_util.o: ap_util.c
ap_util.o: ../../h/util.h
ap_util.o: ../../h/ap.h
ap_lex.o: ap_lex.c
ap_lex.o: ../../h/util.h
ap_lex.o: ../../h/ap.h
ap_lex.o: ../../h/ap_lex.h
ap_lxtable.o: ap_lxtable.c
ap_lxtable.o: ../../h/ap_lex.h
ap_lxtable.o: ../../h/util.h
ap_lxtable.o: ../../h/conf.h
ap_dmflip.o: ap_dmflip.c
ap_dmflip.o: ../../h/util.h
ap_dmflip.o: ../../h/conf.h
ap_dmflip.o: ../../h/ch.h
ap_dmflip.o: ../../h/dm.h
ap_dmflip.o: ../../h/ll_log.h
ap_s2p.o: ap_s2p.c
ap_s2p.o: ../../h/ap_lex.h
ap_s2p.o: ../../h/util.h
ap_s2p.o: ../../h/ap.h
adrparse.o: adrparse.c
adrparse.o: ../../h/util.h
adrparse.o: ../../h/mmdf.h
adrparse.o: ../../h/ap.h
td.o: td.c
td.o: ../../h/util.h
td.o: ../../h/mmdf.h
td.o: ../../h/ch.h
td.o: ../../h/dm.h
norm.o: norm.c
norm.o: ../../h/util.h
norm.o: ../../h/mmdf.h
norm.o: ../../h/ap.h
norm.o: ../../h/ch.h
parse.o: parse.c
parse.o: ../../h/ap_lex.h
parse.o: ../../h/util.h
parse.o: ../../h/mmdf.h
parse.o: ../../h/ap.h
ghost.o: ghost.c
ghost.o: ../../h/util.h
ghost.o: ../../h/mmdf.h
ghost.o: /usr/include/pwd.h
ghost.o: ../../h/ch.h
ghost.o: ../../h/dm.h
ghost.o: ../../h/ap.h
# DEPENDENCIES MUST END AT END OF FILE
# IF YOU PUT STUFF HERE IT WILL GO AWAY
# see make depend above
 (retval = 0;;) {  /* retval is count of comment nesting   */
		switch (ap_lxtable[*lexptr++ = c = ap_char ()]) {
		    case LT_LPR:  /* nested comments                mmdf/lib/addr/ap_lxtable.c   444      0     12        6126  3620510362  10712 #include "ap_lex.h"
#include "util.h"
#include "conf.h"

/* $Header: ap_lxtable.c,v 1.3 84/09/05 21:31:52 dpk Exp $ */
/* $Log:	ap_lxtable.c,v $
 * Revision 1.3  84/09/05  21:31:52  dpk
 * Added #include of util.h
 * 
 * Revision 1.2  83/11/18  17:05:31  reilly
 * Steve Kille's version
 *  */

/* mappings of lexical symbols to ascii values for address parser */

char    ap_lxtable[] =
{
    LT_EOD, LT_ERR, LT_ERR, LT_ERR,
				  /*    000-003          nul            */
    LT_ERR, LT_ERR, LT_ERR, LT_ERR,
				  /*    004-007                         */
    LT_LTR, LT_SPC, LT_SPC, LT_ERR,
				  /*    010-013          bs tab lf      */
    LT_ERR, LT_ERR, LT_ERR, LT_ERR,
				  /*    014-017                         */
    LT_ERR, LT_ERR, LT_ERR, LT_ERR,
				  /*    020-023                         */
    LT_ERR, LT_ERR, LT_ERR, LT_ERR,
				  /*    024-027                         */
    LT_ERR, LT_ERR, LT_ERR, LT_ERR,
				  /*    030-033                         */
    LT_ERR, LT_ERR, LT_ERR, LT_ERR,
				  /*    034-037                         */
    LT_SPC, LT_XTR, LT_QOT, LT_XTR,
				  /*    040-043          sp !  "  #     */
#ifdef JNTMAIL
				  /*         In JNT domain treat % as @ */
    LT_XTR, LT_AT, LT_XTR, LT_XTR,
#else
    LT_XTR, LT_XTR, LT_XTR, LT_XTR,
				  /*    044-047          $  %  &  '     */
#endif
    LT_LPR, LT_RPR, LT_XTR, LT_XTR,
				  /*    050-053          (  )  *  +     */
    LT_COM, LT_XTR, LT_XTR, LT_XTR,
				  /*    054-057          ,  -  .  /     */
    LT_NUM, LT_NUM, LT_NUM, LT_NUM,
				  /*    060-063          0  1  2  3     */
    LT_NUM, LT_NUM, LT_NUM, LT_NUM,
				  /*    014-067          4  5  6  7     */
    LT_NUM, LT_NUM, LT_COL, LT_SEM,
				  /*    070-073          8  9  :  ;     */
    LT_LES, LT_XTR, LT_GTR, LT_XTR,
				  /*    074-077          <  =  >  ?     */
    LT_AT, LT_LTR, LT_LTR, LT_LTR,
				  /*    100-103          @  A  B  C     */
    LT_LTR, LT_LTR, LT_LTR, LT_LTR,
				  /*    014-107          D  E  F  G     */
    LT_LTR, LT_LTR, LT_LTR, LT_LTR,
				  /*    110-114          H  I  J  K     */
    LT_LTR, LT_LTR, LT_LTR, LT_LTR,
				  /*    115-117          L  M  N  O     */
    LT_LTR, LT_LTR, LT_LTR, LT_LTR,
				  /*    120-123          P  Q  R  S     */
    LT_LTR, LT_LTR, LT_LTR, LT_LTR,
				  /*    124-127          T  U  V  W     */
    LT_LTR, LT_LTR, LT_LTR, LT_LSQ,
				  /*    130-133          X  Y  Z  [     */
    LT_SQT, LT_RSQ, LT_XTR, LT_XTR,
				  /*    134-137          \  ]  ^  _     */
    LT_XTR, LT_LTR, LT_LTR, LT_LTR,
				  /*    140-143          `  a  b  c     */
    LT_LTR, LT_LTR, LT_LTR, LT_LTR,
				  /*    144-147          d  e  f  g     */
    LT_LTR, LT_LTR, LT_LTR, LT_LTR,
				  /*    150-153          h  i  j  k     */
    LT_LTR, LT_LTR, LT_LTR, LT_LTR,
				  /*    154-157          l  m  n  o     */
    LT_LTR, LT_LTR, LT_LTR, LT_LTR,
				  /*    160-163          p  q  r  s     */
    LT_LTR, LT_LTR, LT_LTR, LT_LTR,
				  /*    164-167          t  u  v  w     */
    LT_LTR, LT_LTR, LT_LTR, LT_XTR,
				  /*    170-173          x  y  z  {     */
    LT_XTR, LT_XTR, LT_XTR, LT_ERR,
				  /*    174-177          |  }  ~  del   */
};
t.o: ghost.c
ghost.o: ../../h/util.h
ghost.o: ../../h/mmdf.h
ghost.o: /usr/include/pwd.h
ghost.o: ../../h/ch.h
ghost.o: ../../h/dm.h
ghost.o: ../../h/ap.h
# DEPENDENCIES MUST END AT END OF FILE
# IF YOU PUT STUFF HERE IT WILL GO AWAY
# see make depend above
 (retval = 0;;) {  /* retval is count of comment nesting   */
		switch (ap_lxtable[*lexptr++ = c = ap_char ()]) {
		    case LT_LPR:  /* nested comments                mmdf/lib/addr/ap_1adr.c   444      0     12       35046  3671073141  10136 #include "util.h"
#include "conf.h"
#include "ap.h"
#include "ap_lex.h"

/*  ADDRESS PARSER, as per:

    "Standard for the Format of ARPA Network Text Messages", D.  Crocker,
    J. Vittal, K. Pogran & D. Henderson, in ARPANET PROTOCOL HANDBOOK, E.
    Feinler & J. Postel (eds), NIC-7104-REV-1, Network Information Center,
    SRI International:  Menlo Park, Ca.  (1978) (NTIS AD-A0038901).

    and

    "Standard for the Format of Arpa Internet Text Messages", Revised
    by D. Crocker, RFC #822, in INTERNET PROTOCOL TRANSITION WORKBOOK,
    Feinler & J. Postel (eds), Network Information Center, SRI
    International:  Menlo Park, Ca.  (March 1982).

    A parsed address is normalized to have routing references placed
    into rfc822-type positioning.

    History:

    Fall 1977     Bruce Borden:  Initial Design & Coding
    Summer 1979   Dave Crocker:  Completed it & fixed bugs
				 Major reorganization & re-labeling
				 Minor changes to semantics
				 Documentation
    Sept 81       Andy Knutsen   case STPREND -> STPHRSE, to allow comments
				 afterwards
    Sept 81       Dave Crocker   generalized the use of STPHRSE, so that
				 STOKEND occurs only on comma or eof.
				 changed again, to cycle only accepting
				 comments
    Nov 82        Dave Crocker   Converted to accept 822 syntax, while
				 trying also to juggle 733...
    Aug 83        Steve Kille    Fix to STEPER

    This module is the real parser, tho it is intended to be co-routined
    with a caller who has something specific to do with ap_1adr's output.
    This is organized so as to make some attempt at limiting core
    consumption.  Fullparse(), however, simply causes a full parse tree to
    be built up in core.

    The implementation deviates somewhat from the above specification.
    Deviations and the use of the package are discussed in the companion
    documentation.

    The parser's behavior is fairly straightforward.  A singly-linked flat
    list of labelled (lexical) nodes is built up.  Ap_1adr() is used to get
    the next "address segment", which consists of all of the lexical nodes
    up to the end of the next host-phrase (i.e., usually that means up to
    the next comma, semi-colon, or right-angle bracket).

    The caller is responsible for initializing state variables, via
    ap_init(), and linking together or using ap_1adr's output segments.

    Note that ap_1adr does NOT interpret address text to cause re-directed
    file input.  The caller must do that.  Ap_pshfil() and ap_popfil() can
    be used to save and restore the parse state and acquire the named file.
    The provision for this stacking, given the co-routining, is the reason
    state information is chained through global variables, rather than
    being saved on local (stack) variables.

    The amount of input processed on a single call may seem strange.  The
    idea is to try to guess the most common use of the routine.  This is
    presumed to be for address checking, in which acquisition of the MBOX
    and DOMAIN text are most important, with the rest actually being thrown
    away.  It is, of course, possible for the core-limiting heuristic to
    lose if a ridiculous number of groups and personal lists are specified
    in a particular way.  I am assuming that won't happen.
/**/

#define STITER     0
#define STINIT     1
#define STECMNT    2
#define STEDONE    3    /* Returned when addresses were NOT found */
#define STEOK      4    /* Returned when addresses were found */
#define STEBAD     5
#define STPHRSE    6
#define STSTPER    7
#define STEPER     8
#define STEGRP     9
#define STDTYPE   10
#define STEDTYPE  11
#define STDOMAIN  12
#define STEDOMAIN 13

int  ap_intype = AP_733;          /* default to RFC #733 input           */
int  ap_outtype = AP_822;
				/* default to RFC #822 output          */
				/* with little endian domains          */


int ap_grplev = 0;                /* Group nesting depth                  */
int ap_perlev = 0;                /* <> nesting depth                     */
int ap_routing;                   /* parsing a route                    */

#ifdef DEBUG
#include "ll_log.h"

extern LLog *logptr;
extern AP_ptr ap_sqinsert ();
extern char *strdup();
#endif

#if DEBUG > 1
char    debug;                    /* True if in debug mode                */
char   *statnam[] =
{
    "Iterate", "Init", "CmntEnd", "DoneEnd", "OKEnd", "BadEnd", "Phrase",
    "Persstrt", "PersEnd", "GrpEnd", "DTypNam", "DTypeE", "Domain",
    "DomainE"
};

char   *ptrtab[] =
{
    "Nil", "Etc", "Nxt"
};

char   *typtab[] =
{
    "Nil", "Name", "MBox", "Domain", "DataType", "Comment", "Word",
    "Person", "NPersn", "EPersn", "Group", "NGroup", "EGroup", "DomainLit"
};
#endif
/**/

ap_1adr ()
{
    struct ap_node basenode;
    AP_ptr ap_sp;        /* Saved ap node ptr                    */
    AP_ptr r822ptr,
	   r733prefptr;
    int    got822;
    char    buf[LINESIZE];
    register int    state;

#if DEBUG > 1
    if (debug)
	(void) putchar ('\n');
#endif

    ap_routing = DONE;
    ap_ninit (&basenode);
    ap_sqinsert (&basenode, APP_ETC, ap_pstrt);
    for (state = STINIT, got822 = FALSE, r733prefptr = (AP_ptr) 0; ;){
#if DEBUG > 1
	if (debug)
	    printf ("=>%d (%s)", state,
		(state >= 0 && state <= 13) ? statnam[state] : "BOGUS!");
#endif
	switch (state)
	{
	    case STITER:          /* Iteration to get real address        */
		ap_palloc ();
		state = STINIT;   /* just DROP ON THROUGH                 */
	    case STINIT:          /* START of parse; empty node           */
		ap_sp = ap_pcur;
		switch (ap_lex (buf)){
		    case LV_WORD:
			ap_pfill (APV_WORD, buf);
			state = STPHRSE;
			break;
		    case LV_AT:
			if (!got822){
			    got822 = TRUE;
			    r822ptr = ap_pcur;
			}
			ap_routing = OK;
#if DEBUG > 1
			if (debug)
			    printf ("(routing)");
#endif
			state = STDOMAIN;
			break;
		    case LV_COLON:
				  /* DATA TYPE start                      */
			state = STDTYPE;
			break;
		    case LV_SEMI: /* GROUP LIST end                       */
			state = STEGRP;
			break;
		    case LV_GRTR: /* PERSONAL LIST end                    */
			state = STEPER;
			break;
		    case LV_LESS: /* allow one angle-bracket, here */
		    case LV_FROM: /* FILE source                          */
			ap_pfill (APV_DTYP, "Include");
			state = STITER;
			break;
		    case LV_COMMENT:
			ap_pfill (APV_CMNT, buf);
			state = STITER;
			break;
		    case LV_COMMA:
			break;    /* ignore null addresses                */
		    case LV_EOD:
			if (ap_perlev != 0 || ap_grplev != 0)
				state = STEBAD;
			else
				state = STEDONE;
			break;
		    default:
			state = STEBAD;
			break;
		}
		continue;


/* ***************************  ENDING  ********************************* */

	    case STECMNT:         /* accept comments until end            */
		switch (ap_lex (buf)) {
		    case LV_COMMENT:
			ap_pfill (APV_CMNT, buf);
			break;    /* just cycle, accepting comments     */
		    case LV_COMMA:
			state = STEOK;
			break;
		    case LV_EOD:
			state = STEOK;
			break;
		    default:
			state = STEBAD;
			break;
		}
		continue;

	    case STEDONE:         /* END clean; NO empty nodes?           */
#if DEBUG > 1
		if (debug)
		    (void) putchar ('\n');
#endif
		ap_7to8 (r733prefptr, r822ptr);
		return (DONE);

	    case STEOK:         /* END clean                            */
#if DEBUG > 1
		if (debug)
		    (void) putchar ('\n');
#endif
		ap_7to8 (r733prefptr, r822ptr);
		return (OK);

	    case STEBAD:         /* END error                            */
#if DEBUG > 1
		if (debug)
		    (void) putchar ('\n');
#endif
		ap_clear();		/* Experimental, DPK, 7 Aug 84 */
		return (NOTOK);
/* *********************  GATHER A PHRASE  **************************** */

	    case STPHRSE:         /* PHRASE continuation; empty node      */
		switch (ap_lex (buf)) {
		    case LV_WORD: /* append word to phrase, maybe         */
			ap_padd (APV_WORD, buf);
			break;
		    case LV_AT:   /* MAILBOX (name) end                 */
			if (!got822)
			    r822ptr = ap_pcur;
			ap_sqtfix (ap_sp, ap_pcur, APV_MBOX);
			ap_palloc ();
			state = STDOMAIN;
			break;
		    case LV_LESS: /* PERSON NAME end                      */
			state = STSTPER;
			break;
		    case LV_COLON:
				  /* GROUP NAME end                       */
			if (ap_grplev++ >= 1 && ap_intype == AP_822)
			{        /* may not be nested */
#if DEBUG > 1
			    if (debug)
				printf ("(intype=%d,ap_grplev=%d)",
					   ap_intype, ap_grplev);
#endif
			    state = STEBAD;
			    break;
			}
			ap_sqtfix (ap_sp, ap_pcur, APV_GRUP);
			ap_sp -> ap_obtype = APV_NGRP;
			state = STITER;
			break;
		    case LV_SEMI:
			ap_sqtfix (ap_sp, ap_pcur, APV_MBOX);
			ap_sp -> ap_obtype = APV_MBOX;
			state = STEGRP;
			break;
		    case LV_GRTR:
			state = STEPER;
			break;
		    case LV_COMMA:
			ap_sqtfix (ap_sp, ap_pcur, APV_MBOX);
			state = STEOK;
			break;
		    case LV_EOD:
			ap_sqtfix (ap_sp, ap_pcur, APV_MBOX);
			state = STEOK;
			break;
		    case LV_COMMENT:
			ap_pappend (APV_CMNT, buf);
			break;
		    default:
			state = STEBAD;
			break;
		}
		continue;

/* ***********************  ADDRESS LISTS  **************************** */

	    case STSTPER:         /* PERSONAL address list; NO empty node */
		if (ap_perlev++ > 0 && ap_intype == AP_822)
		{        /* may not be nested */
#if DEBUG > 1
		    if (debug)
			printf ("(intype=%d,ap_perlev=%d)",
				   ap_intype, ap_perlev);
#endif
		    state = STEBAD;
		    break;
		}
		ap_routing = OK;
		ap_sqtfix (ap_sp, ap_pcur, APV_PRSN);
		ap_sp -> ap_obtype = APV_NPER;
		state = STITER;
		continue;

	    case STEPER:
		if (--ap_perlev < 0) {
#if DEBUG > 1
		    if (debug)
			printf ("(ap_perlev=%d)", ap_perlev);
#endif
		    state = STEBAD;
		    break;
		}
		ap_pappend (APV_EPER, (char *) 0);
		ap_palloc ();   /* SEK add storage */
		state = STECMNT; /* allow comments, etc */
		continue;

	    case STEGRP:
		if (--ap_grplev < 0) {
#if DEBUG > 1
		    if (debug)
			printf ("(ap_grplev=%d)", ap_grplev);
#endif
		    state = STEBAD;
		    break;
		}
		ap_pappend (APV_EGRP, (char *) 0);
		state = STECMNT;
		continue;


/* **************************  DATA TYPE  ******************************* */

	    case STDTYPE:          /* DATA TYPE name; empty node           */
		if (ap_intype == AP_822)
		{        /* data types not legal in 822 */
#if DEBUG > 1
		    if (debug)
			printf ("(intype=%d)", ap_intype);
#endif
		    state = STEBAD;
		    break;
		}
		if (ap_lex (buf) != LV_WORD) {
		    state = STEBAD;
		    continue;
		}
		ap_pfill (APV_DTYP, buf);
		state = STEDTYPE;
				  /* Just drop on through                 */
	    case STEDTYPE:         /* DATA TYPE name end; empty node       */
		state = (ap_lex (buf) == LV_COLON)
		    ? STITER : STEBAD;
		continue;
/* *************************  DOMAIN  *********************************** */

	    case STDOMAIN:        /* DOMAIN/Host; NO empty parse node   */
		switch (ap_lex (buf)) {
		    default:
			state = STEBAD;
			continue;
		    case LV_COMMENT:
			ap_pappend (APV_CMNT, buf);
			continue;
		    case LV_DLIT:
			ap_pappend (APV_DLIT, buf);
			state = STEDOMAIN;
			continue;
		    case LV_WORD:
			ap_pfill (APV_DOMN, buf);
			state = STEDOMAIN;
		}                 /* just drop on through                 */

	    case STEDOMAIN:       /* DOMAIN end; NO empty parse node      */
		switch (ap_lex (buf)) {
		    case LV_AT:   /* sequence of HOST's => @ separation   */
				  /* SEK correct here              */
			if (r733prefptr == (AP_ptr) 0)
			    r733prefptr = ap_pcur;
			ap_palloc ();
				 /* node which points to first routing ref */
			state = STDOMAIN;
			break;
		    case LV_SEMI:
			state = STEGRP;
			break;
		    case LV_GRTR:
			state = STEPER;
			break;
		    case LV_COMMA:
			if (ap_routing != DONE)
			    state = STITER;
			else
			    state = STEOK;
			break;
		    case LV_EOD:
			if (ap_routing != DONE || 
			    ap_perlev != 0 || ap_grplev != 0)
			    state = STEBAD;
			else
			    state = STEOK;
			break;
		    case LV_COMMENT:
			ap_pappend (APV_CMNT, buf);
			break;
		    case LV_COLON:
			if (ap_routing != DONE) {
			    ap_routing = DONE;
			    state = STITER;
			    continue;
			}       /* else DROP ON THROUGH */
		    default:
			state = STEBAD;
			break;
		}
		continue;
	}
    }
}



LOCFUN
ap_7to8 (r733prefptr, r822ptr)
    AP_ptr r733prefptr,
	    r822ptr;
{
    AP_ptr routbase;
    AP_ptr ap;
    char  *perneeded;

    if (r733prefptr == (AP_ptr) 0)
	return;               /* don't have to move it to 822 style     */

#ifdef DEBUG
    ll_log (logptr, LLOGFTR, "733 ref");
#endif

   if (ap_pstrt -> ap_obtype == APV_MBOX) {
	perneeded = strdup (r822ptr -> ap_obvalue);
	ap_pappend (APV_EPER, (char *) 0);
   }
   else
	perneeded = (char *) 0;

    routbase = ap_alloc (); /* move the sequence to newroute        */
    while (r733prefptr -> ap_ptrtype != APP_NIL &&
	    r733prefptr -> ap_ptrtype != APP_NXT &&
	    r733prefptr -> ap_chain != (AP_ptr) 0)
   {
	switch (r733prefptr -> ap_chain -> ap_obtype)
	{
	    default:     /* only move domain info */
		goto endit;

	    case APV_CMNT:
		if (routbase -> ap_chain != (AP_ptr) 0)
		{           /* try to append, not prepend, comments */
#ifdef DEBUG
		    ll_log (logptr, LLOGFTR,
			"comment appended'%s'", r733prefptr -> ap_chain -> ap_obvalue);
#endif
		    ap_move (routbase -> ap_chain, r733prefptr);
		    continue;
		}           /* else DROP ON THROUGH */
	    case APV_DLIT:
	    case APV_DOMN:
#ifdef DEBUG
		ll_log (logptr, LLOGFTR,
			"val='%s'", r733prefptr -> ap_chain -> ap_obvalue);
#endif
		ap_move (routbase, r733prefptr);
		continue;
	}
    }

endit:
			/* treatment here depends on whether we have an */
			/*  822 route already.  Note that 822 pointer is*/
			/*  NOT easy,  as an easy pointer was too hard! */
			/* SEK need to copy first part of route */
			/* to r822ptr                       */
   if (r822ptr -> ap_obtype != APV_DOMN && r822ptr -> ap_obtype != APV_DLIT) {
	ap_insert (r822ptr, APP_ETC,
		   ap_new (r822ptr -> ap_obtype,
				 r822ptr -> ap_obvalue));
	free (r822ptr -> ap_obvalue);
	ap_fllnode (r822ptr, routbase -> ap_chain -> ap_obtype,
					routbase -> ap_chain -> ap_obvalue);
	ap_sqinsert (r822ptr, APP_ETC, routbase -> ap_chain -> ap_chain);
		       /* add it before local-part         */
	ap_free (routbase);
	ap_free (routbase -> ap_chain);
   } else {
	ap = r822ptr;
	while (ap -> ap_chain -> ap_obtype == APV_DOMN ||
		    ap -> ap_chain -> ap_obtype == APV_DLIT)
	   ap = ap -> ap_chain;
	ap_sqinsert (ap, APP_ETC, routbase -> ap_chain);
   }


   if (perneeded != 0)          /* SEK need to kludge person name       */
   {
	ap_insert (r822ptr, APP_ETC,
		   ap_new (r822ptr -> ap_obtype,
				 r822ptr -> ap_obvalue));
	free (r822ptr -> ap_obvalue);
	ap_fllnode (r822ptr, APV_NPER, perneeded);
	free (perneeded);
   }
}

TEBAD;
		continue;
/* *************************  DOMAIN  *********************************** */

	    case STDOMAIN:        /* DOMAIN/Host; NO empty parse node   */
		switch (ap_lex (buf)) {
		    default:
			state = STEBAD;
			continue;
		    case LV_COMMENT:
			ap_pappend (APV_CMNT, buf);
			continue;
		    case LV_DLIT:
			ap_pappend (APV_DLIT, buf);
			state = STEDOMAIN;
			continue;
		    case LV_WORD:
			ap_pfill (APV_DOMN, buf);
			state = STEDOMAIN;
		}        mmdf/lib/addr/ap_dmflip.c   444      0     12        1660  3671073142  10536 #include "util.h"
#include "conf.h"
#include "ch.h"
#include "dm.h"
#include "ll_log.h"

extern char *strdup();
extern char *malloc();
extern struct ll_struct *logptr;

char *
ap_dmflip (buf)
char * buf;
{
    char tbuf [LINESIZE];
    char *cp;
    int argc;
    char *argv [DM_NFIELD];
    int  i;

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "Flipping domain '%s'", buf);
#endif
    if ((buf[0] == '"') || (buf[0] == '['))
			/*, don't flip if quoted  or dlit */
	return (strdup (buf));

    (void) strcpy (tbuf, buf);
    argc = cstr2arg (tbuf, DM_NFIELD, argv, '.');

    cp = malloc (strlen (buf) + 1);
    if (cp == (char *) 0) {
	ll_log (logptr, LLOGTMP, "dm_flip - malloc failure");
	return ((char *) 0);
    }
    (void) strcpy (cp, argv [argc - 1]);
    for (i = argc - 2; i >= 0; i--) {
	strcat (cp, ".");
	strcat (cp, argv [i]);
    }

#ifdef DEBUG
   ll_log (logptr, LLOGFTR, "Flipped domain is '%s'", cp);
#endif
   return (cp);
}
route already.  Note that 822 pointer is*/
			/*  NOT easy,  as an easy pointer mmdf/lib/addr/ap_normali.c   444      0     12       36443  3671073144  10755 #include "util.h"
#include "conf.h"
#include "ll_log.h"
#include "ch.h"
#include "ap.h"
#include "ap_norm.h"
#include "dm.h"

/*  Normalize a parse tree, to fill-in host references, etc.
 *
 *  Returns:    0  if ok
 *              -1 if error
 */

extern LLog *logptr;
extern struct ap_hstab *ap_exhstab;     /* translation table */
extern char *locname;
extern char *locdomain;

extern char *strdup ();
extern char *multcat ();
Domain *dm_v2route();

/**/

AP_ptr
	ap_normalize (dflhost, dfldomain, thetree, exorchan)
    char *dflhost,             /* string to append, as host name */
	 *dfldomain;           /* string to append, as domain name */
    AP_ptr thetree;             /* the parse tree */
    Chan  *exorchan;            /* The channel to exorcise with respect to */
{
    struct ap_node  basenode;     /* first node in routing chain          */
    AP_ptr r822prefptr,
	   perptr,
	   mbxprefptr,
	   dmprefptr,
	   lstcmntprefptr,
	   lastptr,
	   grpptr,
	   ap;
    struct ap_hstab  *tabp;
    char    official[128];
    char *save;
    char  *strp;

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "ap_normalize ()");
    ap_t2s (thetree, &strp);
    ll_log (logptr, LLOGFTR, "normalize input:  %s", strp);
    free (strp);
#endif

    ap_ninit (&basenode);
    ap_sqinsert (&basenode, APP_ETC, thetree);
			    /* make sure that 'pref' ptrs are easy  */
    ap_ptinit (&basenode, &perptr, &r822prefptr, &mbxprefptr, &dmprefptr,
				&lstcmntprefptr, &lastptr, &grpptr);
				/* set some pointers */


			/* SEK - lts try replacing this section        */
			/* It may have marginal use                    */
    if (perptr == (AP_ptr) 0 && grpptr == (AP_ptr) 0
	&& lstcmntprefptr != (AP_ptr) 0)
    {                   /* no person name or group, so use last comment */
#ifdef DEBUG
	    ll_log (logptr, LLOGFTR, "comment -> person name");
#endif

	ap_insert (&basenode, APP_ETC,
		    ap_new (APV_NPER,
				lstcmntprefptr -> ap_chain -> ap_obvalue));

	if (mbxprefptr == &basenode)  /* first node was local-part */
	    mbxprefptr = basenode.ap_chain;

	ap_append (lastptr, APV_EPER, (char *) 0);
#ifdef DEBUG
	ap_t2s (basenode.ap_chain, &strp);
	ll_log (logptr, LLOGFTR, "comment result:  %s", strp);
	free (strp);
#endif
    }

    /*
     *  Remember "list: ;"...   -DPK-
     */
    if (mbxprefptr == (AP_ptr) 0) {
#ifdef DEBUG
	ll_log (logptr, LLOGFTR, "No mailbox!?!");
#endif
	return (basenode.ap_chain);
    }

				/* Normalize all refs in source route   */
    if (r822prefptr != (AP_ptr) 0)
    {
	ap = r822prefptr;
	FOREVER
	{
	    switch (ap -> ap_ptrtype) {
		case APP_NIL:
		case APP_NXT:
		    break;
		case  APP_ETC:
		    ap = ap -> ap_chain;
		    if(ap == (AP_ptr)0)
			break;
		    switch (ap -> ap_obtype) {
		    case APV_DOMN:
		   	if(ap_dmnormalize (ap, exorchan) == MAYBE)
				return( (AP_ptr)MAYBE);
		    case APV_DLIT:
		    case APV_CMNT:
			continue;
		    }
	    }
	    break;
	}
    }

#ifndef JNTMAIL
    /*
     *  SEK if JNT mail, % is treated as lexically equivalent
     *  to @, and CSNET style routes ignored.  Might accept
     *  CSNET routes if someone wants this...
     */
    if (dmprefptr == (AP_ptr) 0)
    {                           /* no domain, so add default and leave */
#ifdef DEBUG
	ll_log (logptr, LLOGFTR, "no domain");
#endif
	ap_locnormalize (&basenode, &r822prefptr, &mbxprefptr, &dmprefptr);
#ifdef DEBUG
	ap_t2s (basenode.ap_chain, &strp);
	ll_log (logptr, LLOGFTR, "locnorm result:  %s", strp);
	free (strp);
#endif
    }
#endif not JNTMAIL

    if (dmprefptr != (AP_ptr) 0) {
	switch(ap_dmnormalize (dmprefptr -> ap_chain, exorchan)) {
	case MAYBE:
	    return ( (AP_ptr)MAYBE);
	case OK:;
#ifdef DEBUG
	    ll_log (logptr, LLOGFTR, "Local reference");
#endif
#ifndef JNTMAIL
	    ap_locnormalize (&basenode, &r822prefptr, &mbxprefptr, &dmprefptr);
#ifdef DEBUG
	    ap_t2s (basenode.ap_chain, &strp);
	    ll_log (logptr, LLOGFTR, "locnorm result:  %s", strp);
	    free (strp);
#endif
#endif not JNTMAIL
	}
    }


    if (dmprefptr == (AP_ptr) 0 && r822prefptr == (AP_ptr) 0)
    {                           /* no host references anywhere */
#ifdef DEBUG
	ll_log (logptr, LLOGFTR, "mbx = %o, mbx->typ = %o, mbx->ch = %o",
		mbxprefptr, mbxprefptr->ap_obtype, mbxprefptr->ap_chain);
	if (mbxprefptr != (AP_ptr) 0)
		ll_log (logptr, LLOGFTR, "mbx->ch->ty = %o", mbxprefptr->ap_chain->ap_obtype);
#endif
	if (mbxprefptr == (AP_ptr) 0
	   || mbxprefptr -> ap_chain -> ap_obtype == APV_NGRP
	   || mbxprefptr -> ap_chain -> ap_obtype == APV_GRUP)  /* Yuck. */
	    return (basenode.ap_chain);

	if (!isstr(dflhost))
	{
	    sprintf (official, "%s.%s", locname, locdomain);
	    ap_append (mbxprefptr -> ap_chain, APV_DOMN, official);
#ifdef DEBUG
	    ll_log (logptr, LLOGFTR, "appending %s domain (local)",
		    official);
#endif
	}
	else
	{
	    if (!isstr(dfldomain))
		(void) strcpy (official, dflhost);
	    else
		sprintf (official, "%s.%s", dflhost, dfldomain);
	    ap_append (mbxprefptr -> ap_chain, APV_DOMN, official);
#ifdef DEBUG
	    ll_log (logptr, LLOGFTR, "appending %s domain",
		    official);
#endif
	    if(ap_dmnormalize (mbxprefptr -> ap_chain -> ap_chain,
							exorchan) == MAYBE)
		return( (AP_ptr)MAYBE);
	}
	return (basenode.ap_chain);
    }

#ifdef DEBUG
    ll_log (logptr, LLOGFTR, "exorcise special host refs");
#endif
				/* SEK change the dreaded exorcising */
				/* so as only to apply to the host   */
				/* directly connected to.  It is     */
				/* absurd to do this to the middle of*/
				/* a source route                    */
    if (r822prefptr != (AP_ptr) 0)
	ap = r822prefptr -> ap_chain;
    else
	ap = dmprefptr -> ap_chain;

    for (tabp = ap_exhstab; tabp -> name != (char *) 0; tabp++)
	if (lexequ (tabp -> name, ap -> ap_obvalue))
	{             /* Yes, perform holy rites */
#ifdef DEBUG
	    ll_log (logptr, LLOGFTR, "exorcising: %s -> %%%s@%s",
			ap -> ap_obvalue, tabp -> dot, tabp -> at);
#endif
	    if (isstr (tabp -> dot)) {
		free (ap -> ap_obvalue);
		ap -> ap_obvalue = strdup (tabp -> at);
		save = mbxprefptr -> ap_chain -> ap_obvalue;
		mbxprefptr -> ap_chain -> ap_obvalue = multcat (
		   mbxprefptr -> ap_chain -> ap_obvalue, "%", tabp -> dot, (char *)0);
	    } else {
		save = ap -> ap_obvalue;
		ap -> ap_obvalue = strdup (tabp -> at);
	    }
	    free (save);
	    return (basenode.ap_chain);
	}

    /*
     *  We do not have a specific exorcising rule.  See if the
     *  host is known to this channel, and if not, exocise it anyway.
     *
     *  (I think this will work, closer examination welcome.  -DPK-)
     *
     *   SEK -
     *          only play this game if ch_known is specified.
     *           to allow it to be turned off
     *
     *          assume that the table is a channel table form
     *          and thus we need the first host name
     */
    if (exorchan != NULL && exorchan -> ch_known != NULL) {
	Dmn_route route;
	char dm_buf[LINESIZE];
	Domain *lrval;
	int irval;

	lrval = dm_v2route (ap -> ap_obvalue, dm_buf, &route);
	if(lrval == (Domain *)MAYBE)
	    return( (AP_ptr)MAYBE);
	else if(lrval == (Domain *)NOTOK) {
	    ll_log (logptr, LLOGTMP, "failure to reconvert domain (%s)",
			ap-> ap_obvalue);
	    return (basenode.ap_chain);
	}
				/* SEK this might need beefing up to cope    */
				/* with unknown subdomains              */

	irval = tb_k2val(exorchan->ch_known, TRUE, route.dm_argv[0], official);
	if(irval == MAYBE)
	    return( (AP_ptr)MAYBE);
	else if(irval == NOTOK) {
	    if (r822prefptr == (AP_ptr) 0) {
#ifdef DEBUG
		ll_log (logptr, LLOGFTR, "exorcising: %s -> %%%s@%s",
			ap -> ap_obvalue,
			route.dm_argv[0], exorchan -> ch_lname);
#endif

		save = mbxprefptr -> ap_chain -> ap_obvalue;
		mbxprefptr -> ap_chain -> ap_obvalue = multcat (
			mbxprefptr->ap_chain->ap_obvalue, "%",
			route.dm_argv[0], (char *)0);
		free (save);
		free (dmprefptr -> ap_chain -> ap_obvalue);
		dmprefptr -> ap_chain -> ap_obvalue =
			multcat (exorchan->ch_lname,
				    ".", exorchan->ch_ldomain, (char *)0);
	   } else {
#ifdef DEBUG
		ll_log (logptr, LLOGFTR, "adding exorcise component to route");
#endif
			      /* SEK note ap points to r822prefptr - chain */
		ap_sqinsert (ap, APP_ETC, ap_new (ap -> ap_obtype, ap -> ap_obvalue));
		free (ap -> ap_obvalue);
		ap -> ap_obvalue = multcat (exorchan->ch_lname,
						".", exorchan->ch_ldomain, (char *)0);
	   }
       }
    }
    return (basenode.ap_chain);
}
/**/

LOCFUN
	ap_ptinit (baseprefptr, perptr, r822prefptr, mbxprefptr, dmprefptr,
				lstcmntprefptr, lastptr, grpptr)
    AP_ptr baseprefptr;
    AP_ptr *perptr,
	   *r822prefptr,
	   *mbxprefptr,
	   *dmprefptr,
	   *lstcmntprefptr,
	   *lastptr,
	   *grpptr;
{
    AP_ptr ap;

#ifdef DEBUG
    ll_log (logptr, LLOGFTR, "ap_ptinit ()");
#endif

    *perptr =
	*r822prefptr =
	*mbxprefptr =
	*dmprefptr =
	*lstcmntprefptr =
	*lastptr =
	*grpptr = (AP_ptr) 0;

				/* SEK need switch here to catch */
				/* leadin mbox or domn           */
     switch (baseprefptr -> ap_chain -> ap_obtype)
     {
	case APV_MBOX:
	    *mbxprefptr = baseprefptr;
	    break;
	case APV_DLIT:
	case APV_DOMN:
	    *r822prefptr = baseprefptr;
     }


    for (ap = baseprefptr -> ap_chain; ap -> ap_obtype != APV_NIL;
		ap = ap -> ap_chain)
    {
	*lastptr = ap;
#ifdef DEBUG
	ll_log (logptr, LLOGFTR, "val '%s'", ap -> ap_obvalue);
#endif
	switch (ap -> ap_obtype)
	{
	    case APV_NPER:
#ifdef DEBUG
		ll_log (logptr, LLOGFTR, "perptr");
#endif
		*perptr = ap;
		break;

	    case APV_NGRP:
#ifdef DEBUG
		ll_log (logptr, LLOGFTR, "grpptr");
#endif
		*grpptr = ap;
		break;
	}

	if (ap -> ap_ptrtype == APP_NXT)
	    break;
	if (ap -> ap_ptrtype == APP_NIL)
	    break;
	if (ap -> ap_chain == (AP_ptr) 0)
	    break;

	switch (ap -> ap_chain -> ap_obtype)
	{
	    case APV_CMNT:
#ifdef DEBUG
		ll_log (logptr, LLOGFTR, "gotcmnt");
#endif
		*lstcmntprefptr = ap;
		break;                          /** DPK@BRL **/

	    case APV_WORD:                      /** DPK@BRL **/
#ifdef DEBUG
		ll_log (logptr, LLOGFTR, "gotword");
#endif
		if (*mbxprefptr != (AP_ptr) 0)
		    break;

	    case APV_MBOX:
#ifdef DEBUG
		ll_log (logptr, LLOGFTR, "gotmbxpref");
#endif
		*mbxprefptr = ap;    /* one before the mbox                */
		break;

	    case APV_DLIT:
	    case APV_DOMN:
		if (*r822prefptr == (AP_ptr) 0 && *mbxprefptr == (AP_ptr) 0) {
#ifdef DEBUG
		    ll_log (logptr, LLOGFTR, "got r822prefptr");
#endif
		    *r822prefptr = ap;
		} else
		if ((*dmprefptr == (AP_ptr) 0) && (*mbxprefptr != (AP_ptr) 0)) {
					/* SEK need mailbox befor domain */
#ifdef DEBUG
		    ll_log (logptr, LLOGFTR, "gotdmprefptr");
#endif
		    *dmprefptr = ap;
		}
		break;
	}
    }
#ifdef DEBUG
    if (*lastptr == (AP_ptr) 0)
	ll_log (logptr, LLOGFTR, "no lastptr");
    else
	ll_log (logptr, LLOGFTR, "lastptr '%s'", (*lastptr) -> ap_obvalue);
#endif

}
/**/
#ifndef	JNTMAIL

ap_locnormalize (obaseptr, or822prefptr, ombxprefptr, odmprefptr)
    AP_ptr obaseptr,
	   *or822prefptr,
	   *ombxprefptr,
	   *odmprefptr;
{                           /* tear local-part apart            */
    struct ap_node basenode;
    AP_ptr curptr;
    AP_ptr r822prefptr,
	   perptr,
	   mbxprefptr,
	   dmprefptr,
	   lstcmntprefptr,
	   lastptr,
	   grpptr;
    char *cptr;

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "ap_locnormalize ()");
#endif
				/* SEK need fixex here to normalize any new */
				/* domain references                        */

#ifdef DONE_IN_SUBMIT
    if( ! ((*ombxprefptr) -> ap_obtype == APV_DTYP &&   /* protect files */
      lexequ((*ombxprefptr) -> ap_obvalue, "include")))
	for (cptr = (*ombxprefptr) -> ap_chain -> ap_obvalue;
	     *cptr != '\0'; cptr++)
	    if (*cptr == '.' || *cptr == '%')
		*cptr = '@';
#endif

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "parsing '%s'", (*ombxprefptr) -> ap_chain -> ap_obvalue);
#endif

    ap_ninit (&basenode);
    ap_sqinsert (&basenode, APP_ETC,
		    ap_s2tree ((*ombxprefptr) -> ap_chain -> ap_obvalue));
#ifdef DEBUG
    ap_t2s (obaseptr ->ap_chain, &cptr);
    ll_log (logptr, LLOGFTR, "parse result:  %s", cptr);
    free (cptr);
#endif
    ap_ptinit (&basenode, &perptr, &r822prefptr, &mbxprefptr, &dmprefptr,
				&lstcmntprefptr, &lastptr, &grpptr);
    if (dmprefptr != (AP_ptr) 0)    /* actually have some stuff */
    {
	free ((*ombxprefptr) -> ap_chain -> ap_obvalue);
	(*ombxprefptr) -> ap_chain -> ap_obvalue =
		    strdup (mbxprefptr -> ap_chain -> ap_obvalue);
			    /* replace old reference */
#ifdef DEBUG
	ll_log (logptr, LLOGFTR,
		"newlocal '%s'", (*ombxprefptr) -> ap_chain -> ap_obvalue);
#endif
	if (r822prefptr != (AP_ptr) 0 || *odmprefptr != (AP_ptr) 0)
	    if (*or822prefptr == (AP_ptr) 0) {
#ifdef DEBUG
		ap_t2s (obaseptr ->ap_chain, &cptr);
		ll_log (logptr, LLOGFTR, "odm move result:  %s", cptr);
		free (cptr);
#endif
		*or822prefptr = obaseptr;
	    }                   /* initialize route pointer */

	if (*odmprefptr == (AP_ptr) 0)
	    *odmprefptr = mbxprefptr -> ap_chain;
	else                    /* get rid of old domain reference */
	{
	    ap_move (*or822prefptr, *odmprefptr);
#ifdef DEBUG
	    ap_t2s (obaseptr ->ap_chain, &cptr);
	    ll_log (logptr, LLOGFTR, "odm move result:  %s", cptr);
	    free (cptr);
#endif
	}

	if (r822prefptr != (AP_ptr) 0)
	{                       /* put new chain at end of old          */
#ifdef DEBUG
	    ll_log (logptr, LLOGFTR, "adding new routing to end info");
#endif
	    for (curptr = *or822prefptr;
			curptr -> ap_chain -> ap_obtype == APV_DOMN ||
			    curptr -> ap_chain -> ap_obtype == APV_DLIT ||
			    curptr -> ap_chain -> ap_obtype == APV_CMNT;
			curptr = curptr -> ap_chain);
	    ap_sqmove (curptr, r822prefptr, APV_DOMN);
#ifdef DEBUG
	    ap_t2s (obaseptr ->ap_chain, &cptr);
	    ll_log (logptr, LLOGFTR, "routing move result:  %s", cptr);
	    free (cptr);
#endif
	}

	ap_move (*odmprefptr, dmprefptr);
#ifdef DEBUG
	ap_t2s (obaseptr ->ap_chain, &cptr);
	ll_log (logptr, LLOGFTR, "dm move result:  %s", cptr);
	free (cptr);
#endif
    }
}
#endif	not	JNTMAIL

/**/

ap_dmnormalize (dmptr, thechan)
register AP_ptr dmptr;
Chan *thechan;
{
    Dmn_route dmnroute;
    char official [LINESIZE];
    char buf[LINESIZE];
    Domain *domain;

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "ap_dmnormalize ()");
#endif

    domain = dm_v2route (dmptr -> ap_obvalue, official, &dmnroute);
    switch ((int)domain) {
					/* do we know this reference? */
	case NOTOK:			/* unknown host */
#ifdef DEBUG
	    ll_log (logptr, LLOGBTR, "unknown domain (ap_normalize): '%s'",
				dmptr -> ap_obvalue);
#endif
	    switch ((int)ch_nm2struct (dmptr -> ap_obvalue))
	    {                   /* explicit channel routing? */
		default:        /* it IS a channel name */
		    return (OK);
				/* return an analyse local part        */
				/* leave channel ref in address        */
		case NOTOK:
		   ll_log (logptr, LLOGFST,
				"nonexistent dom/host (ap_normalize): '%s'",
				dmptr -> ap_obvalue);
		    return (NOTOK);
	    }
	case MAYBE:
		return(MAYBE);
	default:
	    sprintf (buf, "%s.%s", domain -> dm_lname, domain -> dm_domain);
				/* SEK might be better to get dm_v2routem*/
				/* to do this                           */
	    if (lexequ (official, buf)) {
		if (thechan == NULL)
		    sprintf (official, "%s.%s", locname, locdomain);
		else
		    sprintf (official, "%s.%s", thechan -> ch_lname,
				thechan -> ch_ldomain);
	    }
    }

#ifdef DEBUG
    ll_log (logptr, LLOGFTR, "Route = '%s', Official name = '%s'",
		dmnroute.dm_buf, official);
#endif
    free (dmptr -> ap_obvalue);
    dmptr -> ap_obvalue = strdup (official);
    return (7);                 /* anything other than OK /  NOTOK */
}
 ap_obvalue, "include")))
	for (cptr = (*ombxprefptr) -> ap_chain -> ap_obvalue;
	     *cptr != '\0'; cptr++)
	    if (*cptr == '.' || *cptr == '%')
		*cptr = '@';
#endif

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "parsinmmdf/lib/addr/td.c   444      0     12        2462  3620510364   7207 #include "util.h"
#include "mmdf.h"
#include "ch.h"
#include "dm.h"

extern LLog *logptr;

main ()
{
    char adr [LINESIZE];
    char result [LINESIZE];
    Dmn_route rbuf;
    Dmn_route *route = &rbuf;
    Domain dbuf;
    Domain *dmn = &dbuf;
    Chan *tchan;

    mmdf_init ("TEST-DM");
    siginit ();
    logptr -> ll_level = LLOGFTR;

    while (TRUE)
    {
	int i;

	printf ("\ngive test string (q to quit):");
	scanf ("%s", adr);
	if (lexequ (adr, "q"))
	{
		printf ("quitting\n");
		exit(0);
	}
	printf ("received: '%s'\n", adr);


	printf ("dm_v2route (%s) gives: ",adr);
	dmn = dm_v2route (adr, result, route);
	if (dmn  == (Domain *) NOTOK)
	{
		printf ("Error \n\n\n");
	}
	else
	{
		printf ("%s \n", dmn->dm_show);
		printf ("domain in full is '%s'\n", result);
		printf ("Route: '%s'\n", route->dm_buf);
		for (i=0; i <= route->dm_argc; i++)
		  printf ("    Component (%d): '%s'\n", i, route->dm_argv[i]);
	}
	fflush (stdout);


	for (i=1;;i++)
	{
	    if ((tchan = ch_h2chan (adr, i)) == (Chan *) NOTOK)
	    {
		printf ("ch_h2chan returns NOTOK\n");
		break;
	    }
	    else
	    {
		if (tchan == (Chan *) OK)
			printf ("local reference (%d)\n", i);
		else
		    printf ("ch_h2chan (%d) gives channel '%s'\n", i, tchan -> ch_show);
	    }
	}


	printf ("\n\n");
   }
}

err_abrt ()
{
printf ("aborting\n");
}
ptr ->ap_chain, &cptr);
	    ll_log (logptr, LLOGFTR, "odm move result:  %s", cptr);
	    free (cptr);
#endif
	}

	if (r822prefptr != (AP_ptr) 0)
	{                       /* put new chain at end of old     mmdf/lib/addr/ghost.c   444      0     12       10425  3671073145   7751 #include "util.h"
#include "mmdf.h"
#include <pwd.h>
#include "ch.h"
#include "dm.h"
#include "ap.h"


extern struct ll_struct *logptr;
extern char *ch_dflnam;
extern char *blt();
extern char *index();
extern char *rindex();
extern char *locname;
extern char *supportaddr;
extern char *sitesignature;
extern char *locmachine;               /* local machine name           */
extern int mgt_inalias;

char *adr_fulldmn;      /* Name of 'full' domain                */

char *adr_orgspec;       /* original mailbox string      */

LOCVAR char adr_gotone;

char *adr_ghost ();



main ()


{

	char instr[1000];
	char thename[1000];
	char * result;
	char tstline[1000];
	char newloc[1000];

	mmdf_init ("GHOST");

	logptr -> ll_level = LLOGFTR ;

	printf ("Locname '%s', Locmachine '%s'\n\n", locname, locmachine);


	while (1)

	{

		printf ("Enter the string to parse: "); 
		fflush(stdout);
		scanf ("%s", instr);

		while (1)
		{
			result = adr_ghost(instr, (short) strlen(instr), thename);

			if (result ==  (char *) NOTOK )
			{
				printf ("Error returned by 'adr_ghost' for '%s'", instr);
				fflush(stdout);
				goto done;
			}
			else
			{
				printf ("Host is '%s'     ", thename); 
				fflush(stdout);
				printf ("Pointer at '%s'\n", result); 
				fflush (stdout);

				if (lexequ(thename, locmachine)) 
				{
					printf ("Let's try again \n") ;
					fflush (stdout);
					continue ;  
				}
	/* keep going until we're out of names or we find one */

				/*  Now, we should have a host left now.  Let's see if it exists */

				if (dm_h2domain (thename, tstline) == (Domain *) NOTOK)
				{
					printf ("Lossage trying to get '%s' in the tables\n", thename); fflush(stdout);
					goto done;
				}
				else
				{
					*result = '\0';		/* terminate the new "local" part */
					printf ("Local is '%s' and Domain is '%s'\n", instr, tstline); fflush(stdout);
				}

			}
		}  /* end WHILE (1)  */
	}

done:  
	printf ("Bye\n"); fflush(stdout);
}

/**/

char *
adr_ghost (buf, len, to)  /* get host field from end of text    */
char   *buf;                      /* the buffer holding the text        */
short     len;                      /* length of the buffer               */
char   *to;                       /* put "hostname" into here           */
{
	extern char *compress ();
	register char  *strptr;
	int    hostlen;
	char   *hostptr;
	char   *endptr;

#ifdef DEBUG
	ll_log (logptr, LLOGFTR, "adr_ghost (%s)", buf);
#endif
	for (strptr = &(buf[len - 1]); isspace (*strptr); strptr--);
	/* skip trailing white space          */
	for (hostptr = endptr = strptr;; hostptr = strptr--)
	{
		if (strptr <= buf)
			return ((char *) NOTOK);       /* reached beginning of string */
		switch (*strptr)
		{
		case '>':             /* a "foo bar <adr @ host>" spec */
			*strptr = '\0';
			for (strptr = buf; ; strptr++)
				switch (*strptr)
				{
				case '\0':
					return ((char *) NOTOK);

				case '<':
					compress (++strptr, buf);
					return (adr_ghost (buf, strlen (buf), to));
				}

		case ')':             /* trailing comment field             */
		{
			short pcount = 1;

			while (--strptr > buf && pcount)
			{
				if (*strptr == '(') pcount--;
				if (*strptr == ')') pcount++;
				if (pcount == 0)
				{
					while (strptr > buf &&
					       isspace (*--strptr));
					*++strptr = '\0';
					break;
				}
			}
		}
			continue;

		default:
			continue;

		case '@':
		case '%':             /* same as @ */
		case '.':             /* same as @ */
			break;

		case ' ':
		case '\t':
			for (strptr--; (strptr > buf) && isspace (*strptr);
			strptr--);
			    switch (*strptr)  /* got one now                        */
			{
			case '@':
			case '%':
			case '.':
				break;

			default:      /* " at "??                           */
				if (equal (&strptr[-1], "AT", 2))
					strptr--;
				else      /* tsk tsk                            */
				return ((char *) NOTOK);
			}
			break;
		}
		break;
	}
	if (to)
	{
		hostlen = endptr - hostptr;
		if (hostlen == 0)
			(void) strcpy (to, locname);
		else
		{
		    *(blt (hostptr, to, hostlen + 1)) = '\0';
		    compress (to, to);
		    if (strlen (to) == 0)       /* null host field */
		    {
			/* default to local host */
			(void) strcpy (to, locname);
		    }
		}
	}
	while (isspace (*--strptr));
	strptr++;                     /* leave pointer just AFTER graphics  */
	return (strptr);
}
/**/

              /* explicit channel routing? */
		default:        /* it IS a channel name */
		    return (OK);
				/* return an analyse local part        */
				/* leave channel ref in address        */
		case NOTOK:
		   ll_log (logptrmmdf/lib/addr/adrparse.c   444      0     12        2350  3671073146  10405 /*
**
**	R E V I S I O N  H I S T O R Y
**
**	03/08/85   LDK	Added compress() in this module to remove
**	           VAK  leading and trailing space.
**
*/

#include "util.h"
#include "mmdf.h"
#include "ap.h"

extern	char *ap_s2p();
extern  char *compress();

parsadr (thestr, name, mbox, host)
register char  *thestr;		  /* string with an address             */
char   *name,			  /* where to put name part             */
       *mbox,			  /* where to put mailbox part          */
       *host;			  /* where to put hostname part         */
{
	register char *cp;
	AP_ptr treep, namep, local, domain;

	if (name != (char *) 0)
		*name = '\0';
	if (mbox != (char *) 0)
		*mbox = '\0';
	if (host != (char *) 0)
		*host = '\0';

	cp = ap_s2p (thestr, &treep, (AP_ptr *)0, &namep,
			&local, &domain, (AP_ptr *)0);
	if (cp == (char *) DONE || cp == (char *) NOTOK)
		return;
	else if (local -> ap_obvalue == NULL)
		return;		/* Bad format */
	if (name && namep != 0 && namep -> ap_obvalue != 0)
		(void) strcpy (name, namep->ap_obvalue);
	if (mbox && local != 0 && local -> ap_obvalue != 0)
		(void) strcpy (mbox, local->ap_obvalue);
	if (host && domain != 0 && domain -> ap_obvalue != 0)
		(void) strcpy (host, domain->ap_obvalue);
	ap_free (treep);
	return;
}
compress ();
	register char  *strptr;
	int    hostlen;
	char   *hostptr;
	char   *endptr;

#ifdef DEBUG
	ll_log (logptr, LLOGFTR, "adr_ghost (%s)", buf);
#endif
	for (strptr = &(buf[len - 1]); isspace (*strptr); strptr--);
	/* skip trailing white space          */
	for (hostptr =mmdf/lib/addr/Notes   444      0     12       21067  3623253340   7472 .TH ADDRESS 3.mmdf
'ta .8i 1.6i 2.4i 3.2i 4.0i 4.8i 5.6i 6.3i
.SH NAME
RFC733 Address Parser (ap_)
.SH SYNOPSIS

.nf
#include "util.h"
#include "ap.h"

struct ap_node
{
  char  ap_obtype,    /* parse-type of object          */
	  APV_NIL
	  APV_NAME    /* personal name                 */
	  APV_MBOX    /* mailbox-part of addr          */
	  APV_NODE    /* host-part of address          */
	  APV_DTYP    /* "data-type"                   */
	  APV_CMNT    /* comment (...)                 */
	  APV_WORD    /* generic word                  */
	  APV_PRSN    /* start of personal list <...>  */
	  APV_NPER    /* name of person                */
	  APV_EPER    /* end of personal list          */
	  APV_GRUP    /* start of group list x:..;     */
	  APV_NGRP    /* name of group                 */
	  APV_EGRP    /* end of group list             */
	ap_ptrtype;   /* "role" of the next node       */
	  APP_NIL     /* no next node                  */
	  APP_ETC     /* part of this address          */
	  APP_NXT     /* start of new address          */
  char *ap_obvalue;   /* string value of object        */
  struct ap_node *ap_chain;
		      /* pointer to next node          */
};
typedef AP_ptr struct ap_node *
.fi

.PD 0
.SS "NODE PRIMITIVES"

.IP "ap_alloc() -> AP_ptr" 25
Allocate a list node element
.IP "ap_free(AP_ptr)" 25
Free a list node elelment
.IP "ap_fllnode(AP_ptr, obtype, obval)" 25
Fill-in node's data fields
.IP "ap_new(obtype, obval) -> AP_ptr" 25
Alloc and fill

.SS "LIST MANIPULATION"

.IP "ap_insert(cur-AP_ptr, new-AP_ptr)" 25
Add new node after current one
.IP "ap_delete(AP_ptr)" 25
Remove node after current one
.IP "ap_append(AP_ptr, obtype, obvalue) -> AP_ptr" 25
Alloc, fill, and insert after current node
.IP "ap_add(AP_ptr, obtype, obvalue) -> AP_ptr" 25
Try to add to data field of current node, else append
.IP "ap_sqdelete(strt-AP_ptr, end-AP_ptr)" 25
Remove sequence of nodes from one after starting node through ending one,
or until end of chain.
.IP "ap_1delete(strt-AP_ptr)" 25
Remove sequence of nodes from one after starting node until end of current
address (ptrtype==APP_NXT) or until end of chain.
.IP "ap_sqtfix(strt-AP_ptr, end-AP_ptr, obtype)" 25
Fix object type of sequence of nodes from starting node to ending one

.SS "PARSE STATE"

.IP "AP_ptr ap_pstrt" 25
Pointer to first parse node
.IP "AP_ptr ap_pcur" 25
Pointer to current(last) node

.IP "ap_init(gfunc)" 25
Context initialization

.IP "ap_ppush(gfunc)" 25
Save parse context, init
.IP "char (*gfunc) ()" 25
.IP "ap_ppop()" 25
Restore parse context
.IP "ap_fpush(filename) -> NOTOK/OK" 25
Ppush, init for new input file
.IP "ap_fpop()" 25
Close file and ppop

.SS "PARSE LIST"
.PP
.RS
These echo the List Manipulation primitives, always relative to
ap_pcur; any insert updates ap_pcur.
.RE

.IP "ap_palloc()" 25
Alloc, insert at end of chain
.IP "ap_pfill()" 25
Fill-in values at end of chain
.IP "ap_pnsrt(new-AP_ptr)" 25
Insert at end of chain
.IP "ap_pappend(obtype, obvalue)" 25
.IP "ap_padd(obtype, obvalue)" 25
.br

.SS "PARSING FUNCTIONS"

.IP "ap_pinit(&getc()) -> AP_ptr" 25
Init, set for input from function getc(), alloc initial node,
and store address in ap_pstrt and ap_pcur
.IP "ap_1adr() -> NOTOK/OK/DONE"
Parse next address, through host reference
.PD
.bp
.SH DESCRIPTION
.PP
This package provides routines for parsing text address strings and
producing an in-core linked list of the parsed elements.  Routines
also are provided for manipulation of the list.
.PP
The syntax of addresses is a superset of the official syntax used
for ArpaNet mail, specified in
"Standard for the Format of ARPA Network Text Messages", D.  Crocker,
J. Vittal, K. Pogran & D. Henderson, in ARPANET PROTOCOL HANDBOOK, E.
Feinler & J. Postel (eds), NIC-7104-REV-1, Network Information Center,
SRI International:  Menlo Park, Ca.  (1978) (NTIS AD-A0038901).
.PP
The parser does not interpret the data values, such as the validity
of a local mailbox.  Nor does it act on specific values.  For example,
the syntax permits referring to addresses which are in another file.
The parser detects the specification, but does not retrieve the
contents of the file.  However,
.I ap_fpush(filename)
and
.I ap_fpop()
are provided for handling most of the overhead for this.
.SS List Manipulation
.PP
The routines for manipulating the address linked-list are loosely
divided into five groups.  The first performs dynamic storage
management for list nodes, having allocate, free, and data value fill-in
calls.
.I ap_new()
simply combines allocate and fill-in.
.PP
The second group performs standard linked-list operations of
insertion and deletion.  The list is singly-linked, with
all operations performed relative to a leading node.
Hence, the deletion call specifies the node which
.I precedes
the one that is to be removed.
For convenience,
.I ap_append()
will create, fill-in, and insert a node.
As an efficiency mechanism, it often
is possible to combine the data values of contiguous parse nodes,
if the nodes are of the same object
type.
.I ap_add()
performs the necessary data concatenation, if
possible; if the node types are not the same, then it will just
do an append of a new node.
.PP
Also within this group are three routines for manipulating a sequence
of list nodes.
.I ap_sqdelete()
performs a series of list deletions,
from the node immediately following the start node, until an
ending node (or until the list is exhausted).
.I ap_1delete()
is identical to
.I ap_sqdelete(),
except that it terminates at the
end of a completed address, much like
.I ap_1adr().
That is, it
terminates when it has deleted the last element before a new
address begins, as indicated by a APP_NXT pointer.  The routine
.I ap_sqtfix()
will scan a list sub-sequence and modify the
ap_obtype field.  This is primarily used by the parser, since the
syntax does not resolve the type of some parse elements
until an indeterminate amount of parsing has been done (e.g., personal
name or group name fields, versus mailbox string of an unnamed address).
.PP
The third group keeps track of the parse state.
The two global variables, ap_pstrt and ap_pcur, point
to the beginning and end (current) of the linked list.
.I ap_init()
initializes these context variables and set the pointer for a
character-input function.  The push and pop calls are used to
start a new parse context (e.g., to expand a mailing list) and
then return to the old one.  Two extra push and pop calls are used
for acquiring file input; the push call requires the name of the
input file, rather than a pointer to a input function.
.PP
The fourth group of functions are used by the parser to build the linked list.
They are essentially the same as the primitives in the first group, but
their actions are relative to ap_pcur and will update ap_pcur, when
an insertion is done.  Also,
.I ap_palloc()
differs by automatically
inserting the new node at the end of the list.
.SS Parsing Routines
.PP
The final group performs the actual parsing.
The routine
.I ap_pinit(gfunc)
is used to initialize the system for
input parsing; this includes allocating and setting ap_pstrt and
ap_pcur to point to a first list node.
Its argument is the address of the function which
acquires address characters, one at a time, and should return -1 on end
of input.
.I ap_1adr()
performs the basic parsing, building a
parse tree, relative to the global pointer, ap_pcur.  As it scans the
input, it adds nodes after ap_pcur and updates it.  The
routine returns a status value to indicate its termination condition.
NOTOK means there was a parsing error;
OK means a full address was acquired; and DONE signals end of input.
.PP
.I ap_1adr()
returns on encountering an end of input or a comma which
separates addresses.  Fully parsing the input, before
returning, would tend to overflow storage.
.I ap_1adr()
therefore tries
to reduce the storage requirement by returning when it has parsed
enough to be a complete unit for most applications.  The caller has
the option of simply repeating the call to the routine, until an
end of input, thereby building a full in-core parse tree.
(The directory containing the package's sources has a routine,
called fullparse.c, which will parse an entire input stream and build
the list for it.)   Elements of one address are linked with
a ap_ptrtype of APP_ETC.  When a new address is started, the link
is APP_NXT.
.SH FILES
.IP "addr/ap.h" 25
Include file
.SH AUTHORS
.PD 0
.IP "< 1978  B. Borden" 25
Wrote initial version of parser code
.IP "78-80   D. Crocker" 25
Reworked parser into current form
.IP "Apr 81  K. Harrenstein" 25
Hacked for SRI
.IP "Jun 81  D. Crocker" 25
Back in the fold.  Finished V7 conversion, minor cleanups, and
repackaging of list primitives.  Documentation.
.PD
linked, with
all operations performed relative to a leading node.
Hence, the deletion call specifies the node which
.I precedes
the one that is to be removed.
For convenience,
.I ap_append()
will create, fill-in, and insert a node.
As an efficiency mechanism, it often
is possible to combine the data values of contiguous parse nodes,
if the nodes are of the same object
type.
.I ap_add()
performs the necessary data concatenation, if
possible; if the node mmdf/lib/mmdf/   755      0     12           0  3671074614   6367 mmdf/lib/mmdf/qu_io.c   444      0     12       27735  3671073147   7770 #include "util.h"
#include "mmdf.h"

/*
 *     MULTI-CHANNEL MEMO DISTRIBUTION FACILITY  (MMDF)
 *
 *
 *     Copyright (C) 1979,1980,1981  University of Delaware
 *
 *     Department of Electrical Engineering
 *     University of Delaware
 *     Newark, Delaware  19711
 *
 *     Phone:  (302) 738-1163
 *
 *
 *     This program module was developed as part of the University
 *     of Delaware's Multi-Channel Memo Distribution Facility (MMDF).
 *
 *     Acquisition, use, and distribution of this module and its listings
 *     are subject restricted to the terms of a license agreement.
 *     Documents describing systems using this module must cite its source.
 *
 *     The above statements must be retained with all copies of this
 *     program and may not be removed without the consent of the
 *     University of Delaware.
 *
 *
 *     version  -1    David H. Crocker    March   1979
 *     version   0    David H. Crocker    April   1980
 *     version  v7    David H. Crocker    May     1981
 *     version   1    David H. Crocker    October 1981
 *
 */
/*  QU_IO:  Channel interaction with Deliver                            */

/*  Sep, 80 Dave Crocker    fix qu_minit, to allow no return address
 *  Nov, 80 Dave Crocker    convert to V7
 *  Mar, 81 John Pickens    add seek arg to qu_rsinit, add qu_fileno
 *  Dec, 82 Doug Kingston   added definition of hd_init() as a long.
 *  Mar, 84 Dennis Rockwell updated for 4.2 (s_io)
 */

#include <sys/stat.h>
#include "adr_queue.h"
#include "ch.h"
#include "phs.h"
#include "ap.h"

/*#define RUNALON */

extern LLog *logptr;
extern long hd_init();
extern char *strdup();
extern char *index();
extern char *mquedir;
extern int ap_outtype;          /* 733 or 822 */
extern FILE *hd_fmtfp;          /* handle on hd_format massaged header */

char	*qu_msgfile;            /* PUBLIC INFO: path to text file     */

long	qu_msglen;              /* byte count of message */

LOCVAR int  qu_nadrs;           /* number of addressees */

/*  some versions of stdio do not handle simultaneous reads and writes, so
 *  we must have two handles on it.  we can play this game only because the
 *  access characteristics are fairly constrained.
 */

LOCVAR FILE *qu_rfp,              /* address list read handle */
	    *qu_wfp;              /* address list write handle */

LOCVAR Chan *qu_chptr;            /* channel we are running as          */

LOCVAR long   qu_seek;            /* beginning of unmassaged body       */

LOCVAR int    qu_txfd;            /* msg text file file descriptor    */

LOCVAR char   qu_hdr;             /* take from massaged header          */

LOCVAR struct rp_construct
	    rp_nxerr =
{
    RP_FIO, 'U', 'n', 'a', 'b', 'l', 'e', ' ', 't', 'o', ' ', 'o', 'p',
    'e', 'n', ' ', 'n', 'e', 'x', 't', ' ', 'm', 'e', 's', 's', 'a', 'g',
    'e', ' ', 'f', 'i', 'l', 'e', '\0'
}          ,
	    rp_rrcerr =
{
    RP_LIO, 'q', 'u', '_', 'r', 'd', 'r', 'e', 'c', ' ', 'p', 'i', 'p',
    'e', ' ', 'e', 'r', 'r', 'o', 'r', '\0'
}          ,
	    rp_rsterr =
{
    RP_FIO, 'q', 'u', '_', 'r', 'd', 's', 't', 'm', ' ', 'f', 'i', 'l',
    'e', ' ', 'e', 'r', 'r', 'o', 'r', '\0'
}          ,
	    rp_tsterr =
{
    RP_NS, 'N', 'a', 'm', 'e', 's', 'e', 'r', 'v', 'e', 'r', ' ', 'T',
    'i', 'm', 'e', 'o', 'u', 't', '\0'
};
/* ****************  (qu_)  DELIVER I/O SUB_MODULE  ***************** */

qu_init (argc, argv)              /* get ready to process Deliver mail  */
int       argc;                     /* NOTE: other modules have no args   */
char   *argv[];
{
#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "qu_init");
#endif

    if ((qu_chptr = ch_nm2struct (argv[0])) == (Chan *) NOTOK)
    {
	ll_log (logptr, LLOGTMP, "chan '%s' unknown", argv[0]);
	qu_chptr = (Chan *) 0;
    }

#ifdef RUNALON
    qu_rfp = fdopen (0, "r");
    qu_wfp = fdopen (1, "w");

    qu_msgfile = strdup ("tmp");
    qu_txfd = open ("tmp", 0);
    qu_sender = strdup ("dcrocker");
    domsg = TRUE;
#else RUNALON
    if (argc < 3)
    {
	ll_log (logptr, LLOGTMP, "Channel invoked with too few arguments (%d)",
				 argc);
	return (RP_NO);
    }

#ifdef DEBUG
    ll_log (logptr, LLOGFTR, "'%s' ('%s','%s')", argv[0], argv[1], argv[2]);
#endif

    /* fdopen input fd to get addresses from
     * and output fd to return responses
     */
    if ( !isdigit (*argv[1]) ||
	 (qu_rfp = fdopen (atoi (argv[1]), "r")) == NULL ||
	 !isdigit (*argv[2]) ||
	 (qu_wfp = fdopen (atoi (argv[2]), "w")) == NULL )
    {
	ll_log (logptr, LLOGTMP, "Invalid arguments given to channel");
	return (RP_NO);
    }

    if( argv[3] && index(argv[3], 'w') )
	domsg = TRUE;
#endif RUNALON
    return (RP_OK);
}

qu_end (type)                     /* done performing Deliver function   */
int     type;                     /* ignore type of termination         */
{
#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "qu_end");
#endif

#ifdef RUNALON
    return (RP_OK);
#endif

    if (qu_wfp != (FILE *) EOF && qu_wfp != NULL)
	fclose (qu_wfp);
    qu_rfp =
	qu_wfp = (FILE *) EOF;

    return (RP_OK);
}

qu_pkinit ()                      /* get ready to receive mail          */
{
#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "qu_pkinit");
#endif

    return (RP_OK);
}

qu_pkend ()                       /* done receiving mail                */
{
#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "qu_pkend");
#endif

    return (RP_OK);
}
/**/

qu_minit (msgfile, dohdr)         /* set up for next message            */
char   msgfile[];                 /* name of message to be processed    */
int     dohdr;                    /* perform address massaging          */
{
    struct stat statbuf;          /* for getting the message size       */
    char    linebuf[LINESIZE];

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "qu_minit (%s, dohdr=%d)", msgfile, dohdr);
#endif

    sprintf (linebuf, "%s%s", mquedir, msgfile);
    qu_msgfile = strdup (linebuf);

    if (qu_txfd > 0)
	(void) close (qu_txfd);          /* reset file descriptor */

    if ((qu_txfd = open (qu_msgfile, 0)) == NOTOK)
    {
	ll_err (logptr, LLOGFAT, "Unable to open message file '%s'.",
			qu_msgfile);
	qu_wrply ((RP_Buf *) &rp_nxerr, rp_conlen (rp_nxerr));
	return (RP_FIO);
    }

    qu_seek = 0L;

    fstat (qu_txfd, &statbuf);
    qu_msglen = st_gsize (&statbuf); /* size of basic message              */
    qu_nadrs = 0;
    qu_hdr = FALSE;
#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "basic msglen = %D", qu_msglen);
#endif

    if (dohdr != AP_SAME)          /* fix up the addresses               */
    {
	ap_outtype = dohdr;
	qu_seek = hd_init (qu_chptr, qu_fileno ());
#ifdef DEBUG
    ll_log (logptr, LLOGFTR, "qu_seek = %ld", qu_seek);
#endif
	if(qu_seek == (long) MAYBE){
	    qu_wrply ((RP_Buf *) &rp_tsterr, rp_conlen (rp_tsterr));
	    return (RP_NS);
	}
	if (qu_seek != 0L) {
	    qu_hdr = TRUE;

	    /*  fix message length count, by subtracting size of original
	     *  header and adding size of new one.
	     */
	    qu_msglen -= qu_seek;
	    fstat (fileno (hd_fmtfp), &statbuf);
	    qu_msglen += st_gsize (&statbuf);
#ifdef DEBUG
	    ll_log (logptr, LLOGBTR, "modified msglen = %D, qu_seek = %ld",
					qu_msglen, qu_seek);
#endif
	}
    }

    return (RP_OK);
}

qu_mend ()                        /* end of current message             */
{
#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "qu_mend");
#endif

    if (qu_seek > 0L)
	qu_hdr = FALSE;

    hd_end ();                /* get rid of massaged data           */
    if (qu_txfd >= 0)
	(void) close (qu_txfd);          /* free unneeded fd's      */
    qu_txfd = NOTOK;
    if (qu_msgfile != NULL)
	free (qu_msgfile);
    qu_msgfile = NULL;
    return (RP_OK);
}
/*  ************  TELL DELIVER OF RESULT ****************** */

qu_wrply (valstr, len)           /* pass a reply to local process      */
RP_Buf   *valstr;    /* structure containing reply         */
int     len;                      /* length of the structure            */
{
#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "qu_wrply()");
    ll_log (logptr, LLOGFTR, "(%s)'%s'",
	    rp_valstr (valstr -> rp_val), valstr -> rp_line);
#endif

    switch (valstr -> rp_val)
    {                             /* do msg stats for typical channels */
	case RP_AOK:
	    qu_nadrs++;
	    break;

	case RP_MOK:
	    if (qu_nadrs == 0)    /* addresses weren't batched  */
		qu_nadrs++;       /*  => one addr/msg           */
	    phs_msg (qu_chptr, qu_nadrs, qu_msglen);
	    qu_nadrs = 0;         /* reset */
	    break;
    }
    return (qu_wrec ((char    *) valstr, len));
}
/*                    BASIC I/O WITH DELIVER                          */

qu_rrec (linebuf, len)           /* read a record from Deliver         */
char   *linebuf;                  /* where to stuff the address         */
int      *len;                      /* where to stuff address length      */
{
#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "qu_rrec ()");
#endif

#ifdef RUNALON
    qu_wrec ("qu_rrec: ", 10);
#endif
    switch (*len = gcread (qu_rfp, linebuf, LINESIZE - 1, "\000\n\377"))
    {
	case NOTOK:
	    ll_err (logptr, LLOGFAT, rp_rrcerr.rp_cline);
	    qu_wrply ((RP_Buf *) &rp_rrcerr,
			rp_conlen (rp_rrcerr));
	    return (RP_LIO);

	case OK:                  /* closed the pipe                    */
#ifdef DEBUG
	    ll_log (logptr, LLOGBTR, "qu_rrec (EOF)");
#endif
	    return (RP_EOF);

	case 1:
	    if (isnull (linebuf[0]) || linebuf[0] == '\n')
	    {
#ifdef DEBUG
		ll_log (logptr, LLOGBTR, "DONE");
#endif
		return (RP_DONE); /* the only valid one-char records    */
	    }
	    ll_err (logptr, LLOGFAT, "1-char record ('%c')", linebuf[0]);
	    return (RP_PARM);     /* bad parameter                        */
    }
    *len -= 1;
    linebuf[*len] = '\0';         /*   so it can be lloged as a string  */
#ifdef DEBUG
    ll_log (logptr, LLOGFTR, "(%d)\"%s\"", *len, linebuf);
#endif
    return (RP_OK);
}
/**/

qu_rsinit (theseek)             /* initialize stream (text) position  */
long theseek;
{
    extern long lseek ();

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "qu_rsinit (%ld)", theseek);
#endif

    if ((theseek == 0L) && (qu_seek > 0L))
    {
	lseek (qu_txfd, qu_seek, 0);
	hd_minit ();
	qu_hdr = TRUE;
    }
    else
	lseek (qu_txfd, theseek, 0);
}

qu_fileno ()                      /* return fd for message text file    */
{
    return (qu_txfd);
}

qu_rstm (buffer, len)            /* read next part of message stream   */
char   *buffer;                   /* where to stuff next part of text   */
int    *len;                      /* where to stuff length of part      */
{
    int want = *len - 1;
#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "qu_rstm ()");
#endif

    if (qu_hdr) {                 /* take from massaged header          */
	switch (*len = hd_read (buffer, want))
	{
	    case NOTOK:
		qu_hdr = FALSE;
		goto fioerr;

	    case OK:
#ifdef DEBUG
		ll_log (logptr, LLOGBTR, "qu_rstm (hdr DONE)");
#endif
		qu_hdr = FALSE;   /* start taking from regular file     */
		break;            /* drop down to file read             */

	    default:
		buffer[*len] = '\0';
#ifdef DEBUG
		ll_log (logptr, LLOGFTR, "\"%s\"", buffer);
#endif
		return (RP_OK);
	}
    }

    switch (*len = read (qu_txfd, buffer, want))
    {                             /* raw read of the file               */
	case NOTOK:
    fioerr:
	    ll_err (logptr, LLOGFAT, rp_rsterr.rp_cline);
	    qu_wrply ((RP_Buf *) &rp_rsterr, rp_conlen (rp_rsterr));
	    return (RP_FIO);

	case 0:                  /* end of message; not treated as EOF */
#ifdef DEBUG
	    ll_log (logptr, LLOGBTR, "qu_rstm (DONE)");
#endif
	    return (RP_DONE);

	case 1:
	    if (isnull (buffer[0]))
		return (RP_DONE);
    	    /* Drop Through */

	default:
	    buffer[*len] = '\0';  /*   so it can be ll_loged as a string  */
#ifdef DEBUG
	    ll_log (logptr, LLOGFTR, "\"%s\"", buffer);
#endif
	    return (RP_OK);
    }
    /* NOTREACHED */
}
/**/

qu_wrec (str, len)               /* write a record to Deliver          */
char    *str;
int     len;
{
#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "qu_wrec() (%d)\"%s\"", len, str ? str : "");
#endif

    if (str != 0)
	fnput (str, len, qu_wfp);
    (void) putc ('\0', qu_wfp);       /* null is record terminator     */
    if (fflush (qu_wfp) == NOTOK)
	return (RP_LIO);

    return (RP_OK);
}
typical channels */
	case RP_AOK:
	mmdf/lib/mmdf/ch_struct.c   444      0     12        4163  3664425134  10617 #include "util.h"
#include "mmdf.h"
#include "ch.h"
/*
 *     MULTI-CHANNEL MEMO DISTRIBUTION FACILITY  (MMDF)
 *     
 *
 *     Copyright (C) 1979,1980,1981  University of Delaware
 *     
 *     Department of Electrical Engineering
 *     University of Delaware
 *     Newark, Delaware  19711
 *
 *     Phone:  (302) 738-1163
 *     
 *     
 *     This program module was developed as part of the University
 *     of Delaware's Multi-Channel Memo Distribution Facility (MMDF).
 *     
 *     Acquisition, use, and distribution of this module and its listings
 *     are subject restricted to the terms of a license agreement.
 *     Documents describing systems using this module must cite its source.
 *
 *     The above statements must be retained with all copies of this
 *     program and may not be removed without the consent of the
 *     University of Delaware.
 *     
 *
 *     version  -1    David H. Crocker    March   1979
 *     version   0    David H. Crocker    April   1980
 *     version  v7    David H. Crocker    May     1981
 *     version   1    David H. Crocker    October 1981
 *
 */

extern Chan **ch_tbsrch;
#ifdef DEBUG
extern LLog *logptr;
#endif

/* *************  (ch_)  ACCESS CHANNEL DESCRIPTIONS  ***************** */

Chan   *ch_nm2struct (name)       /* give pointer to structure for chan  */
register char  *name;		  /* string name of chan                */
{
    register Chan **chanptr;

#ifdef DEBUG
    ll_log(logptr, LLOGBTR, "ch_nm2struct(%s)",name);
#endif

    for (chanptr = ch_tbsrch; *chanptr != 0; chanptr++)
	if (lexequ (name, (*chanptr) -> ch_name))
	    return (*chanptr);    /* return addr of chan struct entry   */

    return ((Chan *)NOTOK);
}

Chan   *ch_qu2struct (name)       /* give pointer to structure for chan  */
register char  *name;             /* internal queue name                */
{
    register Chan **chanptr;

#ifdef DEBUG
    ll_log(logptr, LLOGBTR, "ch_qu2struct(%s)",name);
#endif

    for (chanptr = ch_tbsrch; *chanptr != 0; chanptr++)
	if (lexequ (name, (*chanptr) -> ch_queue))
	    return (*chanptr);    /* return addr of chan struct entry   */

    return ((Chan *) NOTOK);
}

		qu_hdr = FALSE;   /* start taking from regular file     */
		break;            /* drop down to file read             */

	    default:
		buffer[*len] = '\0';
#ifdef DEBUG
		ll_log (logptr, LLOGFTR, "\"%s\"", buffer);
#endif
		return (RP_OK);
	}
    }

    switch (*len = read (qu_txfd, buffer, want))
    {                             /* raw read of the file               */
	case NOTOK:
    fmmdf/lib/mmdf/qu_rdmail.c   444      0     12       11727  3671073150  10615 #include "util.h"
#include "mmdf.h"

/*
 *     MULTI-CHANNEL MEMO DISTRIBUTION FACILITY  (MMDF)
 *     
 *
 *     Copyright (C) 1979,1980,1981  University of Delaware
 *     
 *     Department of Electrical Engineering
 *     University of Delaware
 *     Newark, Delaware  19711
 *
 *     Phone:  (302) 738-1163
 *     
 *     
 *     This program module was developed as part of the University
 *     of Delaware's Multi-Channel Memo Distribution Facility (MMDF).
 *     
 *     Acquisition, use, and distribution of this module and its listings
 *     are subject restricted to the terms of a license agreement.
 *     Documents describing systems using this module must cite its source.
 *
 *     The above statements must be retained with all copies of this
 *     program and may not be removed without the consent of the
 *     University of Delaware.
 *     
 *
 *     version  -1    David H. Crocker    March   1979
 *     version   0    David H. Crocker    April   1980
 *     version  v7    David H. Crocker    May     1981
 *     version   1    David H. Crocker    October 1981
 *
 */
/*  DM_IO:  Channel interaction with Deliver                            */
/*                                                                      */
/*  Nov, 80 Dave Crocker    convert to V7                               */

#include "adr_queue.h"

extern char *strdup();
extern char *strncpy();

extern LLog   *logptr;
LOCVAR char *qu_host,             /* to store 1st adr of msg            */
	   *qu_adr,
	    qu_info[2];

LOCVAR struct rp_construct
	    rp_dmiook =
{
    RP_OK, '\0'
};
/* *************  (qu_)  DELIVER MAIL-READING SUB-MODULE  ************* */


qu_rinit (info, retadr, dohdr)   /* ready to get a message             */
char   *info;			  /* where to stuff init ifno           */
char   *retadr;			  /* where to stuff return address      */
int     dohdr;                    /* massage address headers            */
{
    int     len;
    int     retval;
    char    *arglist[20];
    char    host[LINESIZE],
	    adr[LINESIZE],
	    linebuf[LINESIZE];

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "qu_rinit (dohdr=%d)", dohdr);
#endif

    if (rp_isbad (retval = qu_rrec (linebuf, &len)))
	return (retval);        /* get message initialization info      */

    str2arg (linebuf, 20, arglist, (char *) 0);
    if (lexequ ("end", arglist[0]))
	return (RP_DONE);
    if (!lexequ ("msg", arglist[0]))
    {
	ll_log (logptr, LLOGFAT,
		    "qu_rinit err reading new msg:  '%s'", arglist[0]);
	return (RP_PARM);
    }
    (void) strncpy (retadr, arglist[2], ADDRSIZE-1);
    if (rp_isbad (retval = qu_minit (arglist[1], dohdr)))
	return (retval);          /* open the message text              */

    qu_wrply ((RP_Buf *) &rp_dmiook, rp_conlen (rp_dmiook));

    if (rp_isgood (retval = qu_radr (host, adr)))
    {
	if (!isnull (host[0]))
	    qu_host = strdup (host);
	qu_adr = strdup (adr);
	(void) strcpy (info, qu_info);
    }
    return (retval);
}

qu_radr (host, adr)              /* read next address from Deliver     */
char   *host,			  /* where to put name of next host     */
       *adr;			  /* where to put rest of address       */
{
    int       len;
    short     retval;
    char      linebuf[LINESIZE];
    char      *arglist[20];

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "qu_radr");
#endif

    if (qu_adr != 0)              /* fake read of 1st address           */
    {
	(void) strncpy (adr, qu_adr, ADDRSIZE-1);
	free (qu_adr);
	qu_adr = 0;
	if (qu_host != 0)
	{
	    (void) strncpy (host, qu_host, ADDRSIZE-1);
	    free (qu_host);
	    qu_host = 0;
	}
	else
	    host[0] = '\0';
#ifdef DEBUG
	ll_log (logptr, LLOGFTR, "1st msg/call: '%s'@'%s'", adr, host);
#endif
	return (RP_OK);
    }

    retval = qu_rrec (linebuf, &len);
    if (rp_gval (retval) != RP_OK)
	return (retval);

    str2arg (linebuf, 20, arglist, (char *) 0);
    if (lexequ ("aend", arglist[0]))
	return (RP_DONE);
    if (lexequ ("hend", arglist[0]))
	return (RP_HOK);
    if (!lexequ ("addr", arglist[0]))
    {
	ll_log (logptr, LLOGFAT,
		    "qu_radr err reading addr:  '%s'", arglist[0]);
	return (RP_PARM);
    }
    (void) strncpy (host, arglist[1], ADDRSIZE-1);
    (void) strncpy (adr, arglist[2], ADDRSIZE-1);
				  /* copy host & address parts          */
    qu_info[0] = 'm';

#ifdef DEBUG
    ll_log (logptr, LLOGFTR, "('%s'@'%s')", adr, host);
#endif

    return (RP_OK);
}
/**/

qu_rtinit (theseek)              /* initialize for reading text        */
    long theseek;                /* position in message to start at    */
{
#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "qu_rtinit (%ld)", theseek);
#endif

    return (qu_rsinit (theseek));   /* => stream init                     */
}

qu_rtxt (buffer, len)             /* read next portion of msg text      */
char   *buffer;                   /* where to stuff buffer (not line)   */
int    *len;                     /* where to stuff len (BUF not LINE)  */
{
#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "qu_rtxt");
#endif

    return (qu_rstm (buffer, len));
}

qu_rend ()
{
	return( qu_mend());
}
uf;                  /* where to stuff thmmdf/lib/mmdf/mm_io.c   444      0     12       26600  3671073152   7736 /*
 *     MULTI-CHANNEL MEMO DISTRIBUTION FACILITY  (MMDF)
 *     
 *
 *     Copyright (C) 1979,1980,1981  University of Delaware
 *     
 *     Department of Electrical Engineering
 *     University of Delaware
 *     Newark, Delaware  19711
 *
 *     Phone:  (302) 738-1163
 *     
 *     
 *     This program module was developed as part of the University
 *     of Delaware's Multi-Channel Memo Distribution Facility (MMDF).
 *     
 *     Acquisition, use, and distribution of this module and its listings
 *     are subject restricted to the terms of a license agreement.
 *     Documents describing systems using this module must cite its source.
 *
 *     The above statements must be retained with all copies of this
 *     program and may not be removed without the consent of the
 *     University of Delaware.
 *     
 *
 *     version  -1    David H. Crocker    March   1979
 *     version   0    David H. Crocker    April   1980
 *     version  v7    David H. Crocker    May     1981
 *     version   1    David H. Crocker    October 1981
 *
 */
/*  mm_io:  low-level i/o for local mail submission mail               */

/*  May, 80 Dave Crocker    lm_chend, move kill before closes
 *  Jun, 80 Dave Crocker    lm_chinit, to make submit reply channel
 *                          separate from standard output
 *                          mm_cinit, make regfdaray-setting loop tmp<=NOTOK
 */

#include "nexec.h"
#include "util.h"
#include "mmdf.h"

#define LM_SUBMIT   0
#define LM_PICKUP   1

extern struct ll_struct *logptr;

extern int  *regfdary;           /* nexecl's fd rearrangement array   */
extern char *cmddfldir;
extern char *pathsubmit,         /* program to invoke for submission   */
	    *namsubmit,          /* name of it                         */
	    *pathpkup,           /* program to invoke for pickup       */
	    *nampkup;            /* name of it                         */
extern int numfds;

LOCVAR FILE *mm_rfp,           /* pipe input stream                  */
	    *mm_wfp;           /* pipe output stream                 */

LOCVAR int    mm_cid;          /* process id of child mail process      */

/* ************  (mm_)  LOCAL MAIL I/O SUB-MODULE  ****************** */

mm_init ()                        /* get ready for local mail processing  */
{
#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "mm_init ()");
#endif
    return (RP_OK);
}

mm_end (type)                     /* done with mail process             */
int       type;
{
#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "mm_end ()");
#endif
    mm_cend (type);          /* get rid of child                   */
    return (RP_OK);
}

mm_sbinit ()                      /* initialize local submission        */
{
#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "mm_sbinit ()");
#endif
    return (mm_cinit ((char *) 0));       /* indicate not a pickup              */
}

mm_sbend ()                       /* done with submission               */
{
#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "mm_sbend ()");
#endif
    return (mm_cend (OK));
}

mm_pkinit (chname)                /* initialize local pickup            */
    char chname[];                /* name of channel being picked up    */
{
#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "mm_pkinit ()");
#endif
    return (mm_cinit (chname));
}

mm_pkend ()                       /* done with pickup                   */
{
#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "mm_pkend ()");
#endif
    return (mm_cend (OK));
}
/*                    PROCESS REPLIES                                 */

mm_rrply (valstr, len)           /* get a reply from remote process    */
struct rp_bufstruct *valstr;      /* where to stuff copy of reply text  */
int    *len;                      /* where to indicate text's length    */
{
    short     retval;
    char   *rplystr;

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "mm_rrply()");
#endif

    retval = mm_rrec ((char   *) valstr, len);
    if (rp_gval (retval) != RP_OK)
	return (retval);

#ifdef RUNALON
    switch (rp_gval (valstr -> rp_val))
    {
	case 'd':
	    valstr -> rp_val = RP_DONE;
	    break;
	case 'y':
	    valstr -> rp_val = RP_OK;
	    break;
	case 'm':
	    valstr -> rp_val = RP_MOK;
	    break;
	case 'a':
	    valstr -> rp_val = RP_AOK;
	    break;
	case 'n':
	    valstr -> rp_val = RP_NO;
	    break;
    }
#endif

    rplystr = rp_valstr (valstr -> rp_val);
    if (*rplystr == '*')
    {				  /* replyer did a no-no                */
	ll_log (logptr, LLOGTMP, "ILLEGAL REPLY: (%s)", rplystr);
	valstr -> rp_val = RP_RPLY;
    }
#ifdef DEBUG
    else
	ll_log (logptr, LLOGFTR, "(%s)'%s'", rplystr, valstr -> rp_line);
#endif

    return (RP_OK);
}


mm_wrply (valstr, len)           /* pass a reply to local process      */
struct rp_bufstruct *valstr;      /* string describing reply            */
int     len;                      /* length of the string               */
{
#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "mm_wrply() (%s) '%s'",
	    rp_valstr (valstr -> rp_val), valstr -> rp_line);
#endif

    return (mm_wrec ((char *) valstr, len));
}
/*         READ DATA FROM LOCAL MAIL (SUB)PROCESS                     */

mm_rrec (linebuf, len)           /* read one "record"                    */
char   *linebuf;		  /* where to stuff the text              */
int    *len;                      /* where to stuff the length count      */
{
#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "mm_rrec ()");
#endif

    switch (*len = gcread (mm_rfp, linebuf, LINESIZE - 1, "\000\n\377"))
    {				  /* a record == one line                 */
	case NOTOK:
	    ll_log (logptr, LLOGFAT, "read error");
	    return (RP_LIO);

	case OK:
#ifdef DEBUG
	    ll_log (logptr, LLOGFTR, "eof");
#endif
	    return (RP_EOF);

	case 1:
#ifdef DEBUG
	    ll_log (logptr, LLOGFTR, "DONE");
#endif
	    return (RP_DONE);     /* the only valid one-char record     */
    }
    *len -= 1;			  /* cut off the newline                */
    linebuf[*len] = '\0';	  /* keep things null-terminated        */
#ifdef DEBUG
    ll_log (logptr, LLOGFTR, "(%d)'%s'", *len, linebuf);
#endif
    return (RP_OK);
}
/**/

mm_rstm (buffer, len)            /* read buffered block of text        */
char   *buffer;			  /* where to stuff the text            */
int    *len;                      /* where to stuff count               */
{
    static char goteof;

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "mm_rstm ()");
#endif

    if (goteof)
    {
	goteof = FALSE;
	*len = 0;
	return (RP_DONE);
    }
    switch (*len = gcread (mm_rfp, buffer, LINESIZE - 1, "\000\377"))
    {				  /* read until full                    */
	case NOTOK:
	    ll_log (logptr, LLOGFAT, "read error");
	    return (RP_LIO);

	case OK:
#ifdef DEBUG
	    ll_log (logptr, LLOGFTR, "DONE");
#endif
	    return (RP_DONE);
    }
    buffer[*len] = '\0';
    if (isnull (buffer[*len - 1]))
    {
	goteof = TRUE;
	*len -= 1;
    }
#ifdef DEBUG
    ll_log (logptr, LLOGFTR, "(%d)'%s'", *len, buffer);
#endif
    return (RP_OK);
}
/*            WRITE DATA TO LOCAL MAIL (SUB)PROCESS                   */

mm_wrec (buf, len)		  /* write a record/packet              */
char    *buf;			  /* chars to write                     */
int     len;                      /* number of chars to write           */
{
#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "mm_wrec () (%d)'%s'", len, buf ? buf : "");
#endif

    if (!isstr(buf) || len == 0)
	(void) putc ('\0', mm_wfp);     /* send an end-of-stream        */
    else
    {                             /* send the record text               */
	fnput (buf, len, mm_wfp);
	(void) putc ('\n', mm_wfp);
    }
    fflush (mm_wfp);             /* force it out                       */

    if (ferror (mm_wfp))
    {				  /* direct write                       */
	ll_log (logptr, LLOGFTR, "write error");
	return (RP_LIO);
    }

    return (RP_OK);
}
/**/

mm_wstm (buffer, len)            /* write next part of char stream     */
char    *buffer;		  /* chars to write                     */
int     len;                      /* number of chars to write           */
{
    register char   doflush;      /* flush all the text out?            */

    doflush = (buffer == 0 || len == 0);

#ifdef DEBUG
    if (!doflush)
	buffer[len] = '\0';

    ll_log (logptr, LLOGBTR, "mm_wstm (): (%d)\"%s\"",
	    len, (doflush) ? "[EOF]" : buffer);
#endif

    if (doflush)
	fflush (mm_wfp);
    else
	fnput(buffer, len, mm_wfp);

    if (ferror (mm_wfp))
    {
	ll_log (logptr, LLOGFAT, "write error");
	return (RP_LIO);
    }
    return (RP_OK);
}
/*            LOCAL MAIL PROCESS SYSTEM CREATE/DELETE CALLS           */

LOCFUN
	mm_cinit (chname)        /* get a mail process                 */
char    chname[];                 /* name of channel to invoke          */
{
    Pip    pkpin,
	   pkpout;
    short     tmp;
    char    temppath[LINESIZE];
    char    chbuf[LINESIZE];

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "mm_cinit()");
#endif

#ifdef RUNALON
    mm_rfp = stdin;
    mm_wfp = stdout;
    return (RP_OK);
#endif;

    if (pipe (pkpin.pipcall) < OK || pipe (pkpout.pipcall) < OK)
	err_abrt (RP_LIO, "Unable to pipe() in mm_pkinit()");

    regfdary[0] = pkpout.pip.prd;     /* set up io fd's for the child       */
    if (chname == 0)              /* make reply channel separate from   */
    {                             /*  regular text output for submit    */
	regfdary[1] = 1;
	regfdary[2] = pkpin.pip.pwrt;
    }
    else                          /* deliver keeps them merged          */
    {
	regfdary[1] = pkpin.pip.pwrt;
	regfdary[2] = CLOSEFD;
    }
    for (tmp = numfds-1; tmp > 2; tmp--)
	regfdary[tmp] = CLOSEFD;
    if (chname == 0)              /* exec submit                        */
    {
	getfpath (pathsubmit, cmddfldir, temppath);
#ifdef DEBUG
	ll_log (logptr, LLOGFTR, "Forking %s", temppath);
#endif
	if ((mm_cid = nexecl (FRKEXEC, FRKCIGO, regfdary,
			temppath, namsubmit, (char *)0)) == NOTOK)
	    err_abrt (RP_NO, "Unable to nexecl ('%s')", temppath);
    }
    else                          /* exec pickup process (deliver)      */
    {
	getfpath (pathpkup, cmddfldir, temppath);
	sprintf (chbuf, "-c%s", chname);

#ifdef DEBUG
	ll_log (logptr, LLOGFTR, "Forking %s (%s)", temppath, chbuf);
#endif
	if ((mm_cid = nexecl (FRKEXEC, 0, regfdary,
			temppath, nampkup, "-p", chbuf, (char *)0)) == NOTOK)
	    err_abrt (RP_NO, "Unable to nexecl ('%s')", temppath);
    }

    (void) close (pkpout.pip.prd);
    (void) close (pkpin.pip.pwrt);
    mm_rfp = fdopen (pkpin.pip.prd, "r");
    mm_wfp = fdopen (pkpout.pip.pwrt, "w");
    return (RP_OK);
}

LOCFUN
	mm_cend (type)           /* get rid of local mail process      */
int     type;
{
    register int   status;

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "mm_cend (%d)", type);
#endif

#ifdef RUNALON
    return (OK);
#endif

    if (mm_cid == 0)
	return (OK);

/*  if and ending is dirty, try to kill the child and preemptively
 *  close the file descriptors (if open).  a close, rather than fclose,
 *  is used to avoid the chance of writing to a broken pipe.
 */

    if (type != OK)
    {
	kill (mm_cid, 9);
	if (mm_wfp != (FILE *) EOF && mm_wfp != NULL){
	    (void) close (fileno (mm_wfp));
	    fclose(mm_wfp);
	}
	if (mm_rfp != (FILE *) EOF && mm_rfp != NULL){
	    (void) close (fileno (mm_rfp));
	    fclose(mm_rfp);
	}
    }
    else
    {
	if (mm_wfp != (FILE *) EOF && mm_wfp != NULL)
	    fclose (mm_wfp);
	if (mm_rfp != (FILE *) EOF && mm_rfp != NULL)
	    fclose (mm_rfp);
    }

    mm_wfp = mm_rfp = NULL;
    status = pgmwait (mm_cid);
#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "child = %s", rp_valstr (status));
#endif
    mm_cid = 0;
    return (status);
}
(void) putc ('\n', mm_wfp);
    }
    fflush (mm_wfp);             /* force it out                       */

    if (ferror (mm_mmdf/lib/mmdf/err_abrt.c   444      0     12        4037  3620510366  10413 #include "util.h"
#include "mmdf.h"

/*
 *     MULTI-CHANNEL MEMO DISTRIBUTION FACILITY  (MMDF)
 *     
 *
 *     Copyright (C) 1979,1980,1981  University of Delaware
 *     
 *     Department of Electrical Engineering
 *     University of Delaware
 *     Newark, Delaware  19711
 *
 *     Phone:  (302) 738-1163
 *     
 *     
 *     This program module was developed as part of the University
 *     of Delaware's Multi-Channel Memo Distribution Facility (MMDF).
 *     
 *     Acquisition, use, and distribution of this module and its listings
 *     are subject restricted to the terms of a license agreement.
 *     Documents describing systems using this module must cite its source.
 *
 *     The above statements must be retained with all copies of this
 *     program and may not be removed without the consent of the
 *     University of Delaware.
 *     
 *
 *     version  -1    David H. Crocker    March   1979
 *     version   0    David H. Crocker    April   1980
 *     version  v7    David H. Crocker    May     1981
 *     version   1    David H. Crocker    October 1981
 *
 */
/*
	this is a surrogate err_abrt, which is part of the
	mmdf library and is included in case the user's program
	doesn't specify one.  

	it is called by the lm_ routines, for example.

	the action, here, is to try to  print the error message
	and log the message, and then terminate unceremoniously.

*/

extern struct ll_struct *logptr;

/* VARARGS2 */
err_abrt (code, fmt, b, c, d)     /* terminate ourself                  */
int     code;
char    fmt[],
        b[],
        c[],
        d[];
{
    char    newfmt[LINESIZE];

    if (rp_isbad (code))
    {
	printx ("mmdf: ");
	printx (fmt, b, c, d);
	printx ("\n");
	fflush (stdout);

	sprintf (newfmt, "%s%s\n", "err [ ABEND (%s) ] ", fmt);
	ll_err (logptr, LLOGFAT, newfmt, rp_valstr (code), b, c, d);
	fprintf (stderr, newfmt, rp_valstr (code), b, c, d);
#ifdef DEBUG
	fflush (stdout);
	fflush (stderr);
	abort ();
#endif
    }
    ll_close (logptr);           /* in case of cycling, close neatly   */
    exit (RP_NO);
}
og (logptr, LLOGFTR, "Forking %s", temppath);
#endif
	if ((mm_cid = nexecl (FRKEXEC, FRKCIGO, regfdary,
			temppath, namsubmit, (char *)0)) == NOTOK)
	    err_abrt (RP_NO, "Unable to nexecl ('%s')", temppath);
    }
    else                          /* exec pickup process (deliver)      */
    {
	getfpath (pathpkup, cmddfldir, temppath);
	sprintf (chbuf, "-c%s", chname);

#ifdef DEBUG
	ll_log (logptr, LLOGFTR, "Forking %s (%s)", temppath, chbuf);
#endif
	if ((mm_cid = nexecl (mmdf/lib/mmdf/interrupt.c   444      0     12        4420  3652603432  10645 #include "util.h"
#include "mmdf.h"

/*  INTERRUPT TRAP SETTING AND CATCHING
 *
 *  Aug, 81 Dave Crocker    major addition to timeout, for conditionally
 *                          using timerest only when non-zero.
 */

#include <signal.h>

extern struct ll_struct   *logptr;

/* *******************  SOFTWARE INTERRUPT TRAPS  ********************* */

#ifndef DEBUG
sig10 ()			  /* signal 10 interrupts to here       */
{
    signal (SIGBUS, SIG_DFL);
    ll_err (logptr, LLOGFAT, "Dying on sig10: bus error");
    sigabort ("sig10");
}

sig12 ()			  /* signal 12 interrupts to here       */
{
    signal (SIGSYS, SIG_DFL);
    ll_err (logptr, LLOGFAT, "Dying on sig12: bad sys call arg");
    sigabort ("sig12");
}
#endif DEBUG

sig13 ()			  /* signal 13 interrupts to here       */
{
    signal (SIGPIPE, SIG_DFL);
    ll_err (logptr, LLOGFAT, "Dying on sig13: pipe write w/no reader");
    sigabort ("sig13");
}
/**/

siginit ()			  /* setup interrupt locations          */
{
    extern int timeout();

#ifndef DEBUG
    signal (SIGBUS, sig10);
    signal (SIGSYS, sig12);
#endif DEBUG
    signal (SIGPIPE, sig13);
    signal (SIGALRM, timeout);    /* in case alarm is set & fires       */
}


/* ***********************  TIME-OUT TRAP  **************************** */

jmp_buf timerest;                 /* where to restore to, from timeout  */
				  /* not external, if not used outside  */
				  /* otherwise, set by return routine   */
int     flgtrest;                 /* TRUE, if timerest has been set     */

timeout ()			  /* alarm timeouts/interrupts to here  */
{
    extern int errno;

    signal (SIGALRM, timeout);    /* reenable interrupt for here        */

    if (flgtrest) {
    	errno = EINTR;
    	flgtrest = 0;
	longjmp (timerest, TRUE);
    }

    ll_log (logptr, LLOGFAT, "ALARM signal");
    return;                       /* probably generate an i/o error     */
}

s_alarm(n)
unsigned int n;
{
    flgtrest = ((n == 0) ? 0 : 1);
    alarm(n);
}

/* ARGSUSED */

sigabort (thesig)
    char thesig[];
{
    signal (SIGHUP, SIG_DFL);
    signal (SIGINT, SIG_DFL);
    signal (SIGILL, SIG_DFL);
    signal (SIGBUS, SIG_DFL);
    signal (SIGSYS, SIG_DFL);
    signal (SIGPIPE, SIG_DFL);
    signal (SIGALRM, SIG_DFL);

#ifdef DEBUG
    abort ();
#endif

    exit (NOTOK);
    /* NOTREACHED */
}
ptr, LLOGFAT, newfmt, rp_valstr (code), b, c, d);
	fprintf (stderr, newfmt, rp_valstr (code), b, c, d);
#ifdef DEBUG
	fflush (stdout);
	fflush (stderr);
	abort ();
#endif
    }
    ll_close (logptr);           /* in case of cycling, close nmmdf/lib/mmdf/mm_rdmail.c   444      0     12        6637  3620510366  10564 #include "util.h"
#include "mmdf.h"

/*
 *     MULTI-CHANNEL MEMO DISTRIBUTION FACILITY  (MMDF)
 *     
 *
 *     Copyright (C) 1979,1980,1981  University of Delaware
 *     
 *     Department of Electrical Engineering
 *     University of Delaware
 *     Newark, Delaware  19711
 *
 *     Phone:  (302) 738-1163
 *     
 *     
 *     This program module was developed as part of the University
 *     of Delaware's Multi-Channel Memo Distribution Facility (MMDF).
 *     
 *     Acquisition, use, and distribution of this module and its listings
 *     are subject restricted to the terms of a license agreement.
 *     Documents describing systems using this module must cite its source.
 *
 *     The above statements must be retained with all copies of this
 *     program and may not be removed without the consent of the
 *     University of Delaware.
 *     
 *
 *     version  -1    David H. Crocker    March   1979
 *     version   0    David H. Crocker    April   1980
 *     version  v7    David H. Crocker    May     1981
 *     version   1    David H. Crocker    October 1981
 *
 */
extern struct ll_struct *logptr;

/* ************  (mm_)  LOCAL MAIL-READING SUB-MODULE  **************** */

mm_rinit (info, retadr)          /* get initialization info for msg    */
char   *info,			  /* where to put general init info     */
       *retadr;			  /* where to put return address        */
{
    short   retval;
    int     len;
    char    linebuf[LINESIZE];
    char   *fromptr,
           *toptr;

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "mm_rinit");
#endif

    retval = mm_rrec (linebuf, &len);
    if (rp_gval (retval) != RP_OK)
	return (retval);

    for (fromptr = linebuf, toptr = info;
	    *fromptr != '\n' && *fromptr != ';';
	    *toptr++ = *fromptr++)
	switch (*fromptr)	  /* mailing mode                         */
	{			  /* make sure only has ok values         */
	    case ' ':
	    case 'a':
	    case 'A':
	    case 'm':
	    case 'M':
	    case 's':
	    case 'S':
		break;		  /* ok to copy to info field             */

	    default: 		  /* invalid code                         */
		ll_log (logptr, LLOGFAT,
			"illegal mode value: %c", *fromptr);
		return (RP_NO);
	};
    *toptr = '\0';		  /* copy intialization info            */

    for (toptr = retadr, fromptr++;
	    !isnull (*fromptr) && *fromptr != '\n';
	    *toptr++ = *fromptr++);
    *toptr = '\0';		  /* copy return address                */

    return (RP_OK);
}
/**/

mm_radr (host, adr)              /* get an address spec from remote    */
char   *host,			  /* where to stuff name of next host   */
       *adr;			  /* where to stuff rest of address     */
{
    short     retval;
    int       len;

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "mm_radr()");
#endif

    *host = '\0';		  /* no-op this field                   */

    retval = mm_rrec (adr, &len);
    if (rp_gval (retval) != RP_OK)
	return (retval);

#ifdef DEBUG
    ll_log (logptr, LLOGFTR, "'%s'", adr);
#endif

    return (RP_OK);
}

mm_rtxt (buffer, len)            /* read next part of msg text         */
char   *buffer;			  /* where to stuff copy of text        */
int    *len;                      /* where to indicate text's length    */
{
    short     retval;

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "mm_rtxt()");
#endif

    retval = mm_rstm (buffer, len);
    if (rp_gval (retval) != RP_OK)
	return (retval);

#ifdef DEBUG
    ll_log (logptr, LLOGFTR, "'%s'", buffer);
#endif

    return (RP_OK);
}
(stderr);
	abort ();
#endif
    }
    ll_close (logptr);           /* in case of cycling, close nmmdf/lib/mmdf/mm_wtmail.c   444      0     12        6573  3631170266  10613 #include "util.h"
#include "mmdf.h"

/*
 *     MULTI-CHANNEL MEMO DISTRIBUTION FACILITY  (MMDF)
 *     
 *
 *     Copyright (C) 1979,1980,1981  University of Delaware
 *     
 *     Department of Electrical Engineering
 *     University of Delaware
 *     Newark, Delaware  19711
 *
 *     Phone:  (302) 738-1163
 *     
 *     
 *     This program module was developed as part of the University
 *     of Delaware's Multi-Channel Memo Distribution Facility (MMDF).
 *     
 *     Acquisition, use, and distribution of this module and its listings
 *     are subject restricted to the terms of a license agreement.
 *     Documents describing systems using this module must cite its source.
 *
 *     The above statements must be retained with all copies of this
 *     program and may not be removed without the consent of the
 *     University of Delaware.
 *     
 *
 *     version  -1    David H. Crocker    March   1979
 *     version   0    David H. Crocker    April   1980
 *     version  v7    David H. Crocker    May     1981
 *     version   1    David H. Crocker    October 1981
 *
 */
/*  LM_WTMAIL:  Submit local mail                                       */
/*                                                                      */
/*  Jun, 80 Dave Crocker    make immediate local delivery conditional   */

extern struct ll_struct *logptr;

/* ************  (mm_)  LOCAL MAIL-WRITING SUB-MODULE  **************** */

mm_winit (vianet, info, retadr)  /* initialize for one message         */
char    *vianet;		  /* what channel coming in from        */
char    *info,			  /* general info                       */
        *retadr;		  /* return address for error msgs      */
{
    short     retval;
    char   infoline[LINESIZE];

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "mm_winit (%s, %s, %s)",
	    vianet ? vianet : "", info, retadr ? retadr : "");
#endif

    if (vianet != (char *)0)       /* we are relay; add name of source   */
	sprintf (infoline, "ti%s*",vianet);
    else
	infoline[0] = '\0';

    strcat (infoline, info);

    retval = mm_wrec (infoline, strlen (infoline));

    /* if -r switch used, retadr == 0 */
    /* if no return address available, retadr[0] == '\0' */

    if (isstr(retadr))
	retval = mm_wrec (retadr, strlen (retadr));
    else if (retadr)	/* Don't send a null if handed "" */
	retval = mm_wrec (" ", 1);

    return (retval);
}
/**/

mm_wadr (host, adr)              /* send one address spec to local     */
char    *host,			  /* "next" location part of address    */
        *adr;			  /* rest of address                    */
{
    short     retval;
    char adrbuf[LINESIZE];

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "mm_wadr()");
#endif

    if (isstr (host)) {
	sprintf (adrbuf, "%s@%s", adr, host);
	adr = adrbuf;
    }

    retval = mm_wrec (adr, strlen (adr));
    return (retval);
}

mm_waend ()                      /* end of address list                */
{
    short     retval;

    if (rp_isbad (retval = mm_wrec ("!", 1)))
	return (retval);
    return (RP_DONE);
}

mm_wtxt (buffer, len)            /* send next part of msg text         */
char    *buffer;		  /* the text                           */
int     len;                      /* length of text                     */
{
    return (mm_wstm (buffer, len));
}

mm_wtend ()                      /* end of message text                */
{
    return (mm_wrec ((char *) 0, 0));     /* flush buffer and indicate end      */
}
fer);
#endif

    return (RP_OK);
}
(stderr);
	abort ();
#endif
    }
    ll_close (logptr);           /* in case of cycling, close nmmdf/lib/mmdf/logptr.c   444      0     12         520  3620510367  10074 #include "util.h"
#include "mmdf.h"

/*
	this is a surrogate declaration of logptr.  most of the
	mmdf modules use "logptr" as the generic reference to the
	log structure.  most mmdf main modules bind logptr to
	msg.log or chan.log or somesuch.  

	this file will bind it to chan.log.

*/

extern LLog chanlog;
LLog *logptr = &chanlog;
dif

    if (vianet != (char *)0)       /* we are relay; add name of source   */
	sprintf (infoline, "ti%s*",vianet);
    else
	infoline[0] = '\0';

    strcat (infoline, info)mmdf/lib/mmdf/rp_valstr.c   444      0     12        5364  3620510367  10634 #include "util.h"
#include "mmdf.h"

/*
 *     MULTI-CHANNEL MEMO DISTRIBUTION FACILITY  (MMDF)
 *
 *
 *     Copyright (C) 1979,1980,1981  University of Delaware
 *
 *     Department of Electrical Engineering
 *     University of Delaware
 *     Newark, Delaware  19711
 *
 *     Phone:  (302) 738-1163
 *
 *
 *     This program module was developed as part of the University
 *     of Delaware's Multi-Channel Memo Distribution Facility (MMDF).
 *
 *     Acquisition, use, and distribution of this module and its listings
 *     are subject restricted to the terms of a license agreement.
 *     Documents describing systems using this module must cite its source.
 *
 *     The above statements must be retained with all copies of this
 *     program and may not be removed without the consent of the
 *     University of Delaware.
 *
 *
 *     version  -1    David H. Crocker    March   1979
 *     version   0    David H. Crocker    April   1980
 *     version  v7    David H. Crocker    May     1981
 *     version   1    David H. Crocker    October 1981
 *
 */
/*                  String Labels for Reply Values                      */

char   *
	rp_valstr (val)           /* return text string for reply value */
    int    val;
{
    static char noval[] = "*** Illegal:  0000";
				  /* (noval[0] == '*') => illegal       */

    switch (rp_gval (val))
    {
	case RP_DONE:
	    return ("DONE");

	case RP_OK:
	    return ("OK");

	case RP_MOK:
	    return ("MOK");

	case RP_HOK:
	    return ("HOK");

	case RP_DOK:
	    return ("DOK");

	case RP_MAST:
	    return ("MAST");

	case RP_SLAV:
	    return ("SLAV");

	case RP_AOK:
	    return ("AOK");

	case RP_NET:
	    return ("NET");

	case RP_BHST:
	    return ("BHST");

	case RP_DHST:
	    return ("DHST");

	case RP_LIO:
	    return ("LIO");

	case RP_NIO:
	    return ("NIO");

	case RP_LOCK:
	    return ("LOCK");

	case RP_EOF:
	    return ("EOF");

	case RP_NS:
	    return ("NS");

	case RP_AGN:
	    return ("AGN");

	case RP_TIME:
	    return ("TIME");

	case RP_NOOP:
	    return ("NOOP");

	case RP_FIO:
	    return ("FIO");

	case RP_FCRT:
	    return ("FCRT");

	case RP_PROT:
	    return ("PROT");

	case RP_RPLY:
	    return ("RPLY");

	case RP_MECH:
	    return ("MECH");

	case RP_NO:
	    return ("NO");

	case RP_NDEL:
	    return ("NDEL");

	case RP_HUH:
	    return ("HUH");

	case RP_NCMD:
	    return ("NCMD");

	case RP_PARM:
	    return ("PARM");

	case RP_UCMD:
	    return ("UCMD");

	case RP_USER:
	    return ("USER");

	case RP_FOPN:
	    return ("FOPN");

	case RP_NAUTH:
	    return ("NAUTH");

	default:                  /* print illegal octal value          */
	    noval[15] = rp_gbbit (val) + '0';
	    noval[16] = rp_gcbit (val) + '0';
	    noval[17] = rp_gsbit (val) + '0';
	    return (noval);
    }
}
 }
    else                          /* exec pickup process (deliver)      */
    {
	getfpath (pathpkup, cmddfldir, temppath);
	sprintf (chbuf, "-c%s", chname);

#ifdef DEBUG
	ll_log (logptr, LLOGFTR, "Forking %s (%s)", temppath, chbuf);
#endif
	if ((mm_cid = nexecl (mmdf/lib/mmdf/qu_fakrply.c   444      0     12        7520  3620510367  10771 #include "util.h"
#include "mmdf.h"

/* send RP_DHST replies back to Deliver, if foreign site goes down */

/*  The following state diagram is for the qu2??_send functions.  The concern
 *  is for making sure that Deliver properly receives replies, in the event
 *  that the remote host goes dead.  For simplicity, a modification of the
 *  following is used, to keep the state variable local to this file.
 *
 *      The parenthesized tokens are error-condition actions, to be taken
 *  within qu_fakrply():  ABORT=abort/return, RINIT=qu_rinit, RDADR=qu_radr,
 *                        ARPLY=address ??_wrply, TRPLY=text ??_wrply
 *
 *              (ABORT)
 *                 |
 *            qu_init
 *                 \---->--(RINIT)--->  ??_init
 *            qu_pkinit <--(ABORT)---<---/
 *                 \---->--(RINIT)--->  ??_sbinit
 *                                       |
 *     /->-->   (ABORT) <------------<---/
 *     |           |
 *     |      qu_rinit
 *     |           |
 *     |         [ok] -->--(RDADR)--->  ??_winit ->-\
 *     |         [end]                               |
 *     |           |                                 |
 *     |      qu_pkend                  ----------   |
 *     |           \====>============>  |??_sbend|   |
 *     |                                ----------   |
 *     |  /->   (ABORT) <------------<-------------<-/
 *     |  |        |
 *     |  |     QU_RADR
 *     |  |        |
 *     |  |      [end]-->--(TRPLY)--->  ??_waend ->-\
 *     |  |        |                                 |
 *     |  |      [ok] -->--(ARPLY)--->  ??_wadr      |
 *     |  |                              |           |
 *     |  |                             ??_rrply     |
 *     |  |    QU_WRPLY <--(ABORT)-------/           |
 *     |  \-<------/                                 |
 *     |                                             |
 *     |      qu_rinit <------------<-------------<-/
 *     |           |
 *     |      qu_rtxt  <------------<-------------<-\
 *     |           |                                 |
 *     |         [ok] -->------------>  ??_wtxt  ->-/
 *     |           |
 *     |         [end]-->------------>  ??_wend
 *     |                                 |
 *     |                                ??_rrply
 *     |       QU_WRPLY <------------<---/
 *     \-<---------/
 */

#include "chan.h"

extern struct ll_struct *logptr;

LOCVAR struct rp_construct
			    rp_fdone =
{
    RP_DHST, 'D', 'O', 'N', 'E', ' ', '&', ' ', 's', 't', 'i', 'l',
    'l', ' ', 'g', 'o', 'n', 'e', '\0'
}          ,
			    rp_fstill =
{
    RP_DHST, 's', 't', 'i', 'l', 'l', ' ', 'g', 'o', 'n', 'e', '\0'
};

qu_fakrply (snd_state)            /* send RP_DHST's to Deliver          */
    int snd_state;
{				  /* behavior depends on state          */
    char    host[LINESIZE],
	    adr[LINESIZE],
            info[LINESIZE],
            sender[LINESIZE];

#ifdef DEBUG
    ll_log (logptr, LLOGPTR, "qu_fakrply");
#endif

    switch (snd_state)
    {
	case SND_ABORT:
#ifdef DEBUG
	    ll_log (logptr, LLOGBTR, "SND_ABORT");
#endif
	    return;

	case SND_RINIT:
	    goto rinit;

	case SND_ARPLY:
	    goto arply;

	case SND_TRPLY:
	    goto mrply;
    }

    FOREVER
    {
	switch (rp_gval (qu_radr (host, adr)))
	{
	    case RP_DONE:         /* list done, say ok to text          */
	    mrply:
		qu_wrply ((struct rp_bufstruct *) &rp_fdone,
						    rp_conlen (rp_fdone));
	    rinit:
		if (rp_gval (qu_rinit (info, sender)) != RP_OK)
		{
#ifdef DEBUG
		    ll_log (logptr, LLOGBTR, "fake DONE");
#endif
		    return;
		}
		break;

	    case RP_OK:           /* say ok to an address               */
	    arply:
#ifdef DEBUG
		ll_log (logptr, LLOGFTR, "faking '%s'", adr);
#endif
		qu_wrply ((struct rp_bufstruct *) &rp_fstill,
						    rp_conlen (rp_fstill));
		break;

	    default: 
		ll_log (logptr, LLOGTMP, "error value in loop");
		return;
	}
    }
}
cend (type)           /* get rid of local mail process      */
int     type;
{
    register int   status;

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "mm_cend (%d)", type);
#endmmdf/lib/mmdf/phs_note.c   444      0     12        7443  3642267571  10451 #include "util.h"
#include "mmdf.h"
#include <sys/stat.h>
#include "ch.h"
#include "phs.h"

/*      note mmdf channel activity phases               */

extern Llog *logptr;
extern char *phsdfldir;                /* directory for timestamps */

#define PHS_NUL 0
#define PHS_SND 1
#define PHS_RCV 2

LOCVAR  int phs_mode;                   /* are sending or receiving */
LOCVAR time_t starttime;

LOCVAR char
	    cnstrt[] =  "%s/%s/cstrt",
	    cngot[] =   "%s/%s/cgot",
	    cnend[] =   "%s/%s/cend",
	    restrt[] =  "%s/%s/rstrt",
	    remsg[] =   "%s/%s/rmsg",
	    reend[] =   "%s/%s/rend",
	    wrstrt[] =  "%s/%s/wstrt",
	    wrmsg[] =   "%s/%s/wmsg",
	    wrend[] =   "%s/%s/wend";
/**/
phs_note (thechan, phase)               /* make a timestamp */
    Chan *thechan;
    int phase;
{
    char stamploc[LINESIZE];
    char stampdir[LINESIZE];
    char *fmt;

    switch (phase)
    {
	case PHS_CNSTRT:
		ll_log (logptr, LLOGPTR, "strt");
		fmt = cnstrt;
		/*NOSTRICT*/
		starttime = 0L;
		break;

	case PHS_CNGOT:
		ll_log (logptr, LLOGPTR, "conn");
		fmt = cngot;
		time (&starttime);
		break;

	case PHS_CNEND:
		phs_mode = PHS_NUL;
		/*NOSTRICT*/
		starttime = 0L;
		fmt = cnend;
		break;

	case PHS_RESTRT:
		ll_log (logptr, LLOGPTR, "rcv");
		fmt = restrt;
		phs_mode = PHS_RCV;
		break;

	case PHS_REMSG:
		fmt = remsg;
		break;

	case PHS_REEND:
		ll_log (logptr, LLOGPTR, "rend");
		fmt = reend;
		phs_mode = PHS_NUL;
		break;

	case PHS_WRSTRT:
		ll_log (logptr, LLOGPTR, "writ");
		fmt = wrstrt;
		phs_mode = PHS_SND;
		break;

	case PHS_WRMSG:
		fmt = wrmsg;
		break;

	case PHS_WREND:
		ll_log (logptr, LLOGPTR, "wend");
		fmt = wrend;
		phs_mode = PHS_NUL;
		break;
    }

    sprintf (stamploc, fmt, phsdfldir, thechan -> ch_name);

    /* We rely on umask() == 0 */
    if (close (creat (stamploc, 0666)) < 0) {
      sprintf(stampdir, "%s/%s", phsdfldir, thechan -> ch_name);
      if ( creatdir (stampdir, 0777, 0, 0) != OK ||
           (close (creat (stamploc, 0666)) < 0) )
          return (NOTOK);
    }
    return(0);
}
/**/

phs_msg  (thechan, naddrs, len)     /* note trasmission of 1 message    */
    Chan *thechan;
    int naddrs;
    long len;
{
    if (naddrs <= 0 && len <= 0L)
	return;                     /* nothing to record                */

    switch (phs_mode)
    {
	case PHS_RCV:
	    ll_log (logptr, LLOGFST, "rmsg %4da %10ldc", naddrs, len);
	    phs_note (thechan, PHS_REMSG);      /* make a timestamp */
	    break;

	case PHS_SND:
	    ll_log (logptr, LLOGFST, "wmsg %4da %10ldc", naddrs, len);
	    phs_note (thechan, PHS_WRMSG);      /* make a timestamp */
	    break;
    }
}
/**/

phs_end  (thechan, status)      /* note end of session */
    Chan *thechan;
    int status;                 /* mmdf end value */
{
    time_t endtime;

    if (starttime == 0L)
	ll_log(logptr, LLOGBST, "end (%s)", rp_valstr (status));
    else
    {
	time (&endtime);
	ll_log (logptr, LLOGBST, "end (%s)\t%lds", rp_valstr (status),
				 (long) (endtime - starttime));
	/*NOSTRICT*/
	starttime = 0L;
    }

    phs_note (thechan, PHS_CNEND);      /* make a timestamp */
}

/**/

time_t
	phs_get (thechan, phase)           /* read a timestamp */
    Chan *thechan;
    int phase;
{
    struct stat statbuf;
    char stamploc[LINESIZE];
    char *fmt;

    switch (phase)
    {
	case PHS_CNSTRT:
		fmt = cnstrt;
		break;

	case PHS_CNGOT:
		fmt = cngot;
		break;

	case PHS_CNEND:
		fmt = cnend;
		break;

	case PHS_RESTRT:
		fmt = restrt;
		break;

	case PHS_REMSG:
		fmt = remsg;
		break;

	case PHS_REEND:
		fmt = reend;
		break;

	case PHS_WRSTRT:
		fmt = wrstrt;
		break;

	case PHS_WRMSG:
		fmt = wrmsg;
		break;

	case PHS_WREND:
		fmt = wrend;
		break;
    }

    sprintf (stamploc, fmt, phsdfldir, thechan -> ch_name);

    if (stat (stamploc, &statbuf) < 0)
	return (0L);

    /*NOSTRICT*/
    return (statbuf.st_mtime);
}
 (type != OK)
    {
	kill (mm_cid, 9);
	if (mm_wfp != (FILE *) EOF && mm_wfp != NULL){
	    (void) close (fileno (mm_wfp));
	    fclose(mm_wfp);
	}
	if (mm_rfp != (FILE *) EOF && mm_rfp != NULL){
	    (void) close (filenommdf/lib/mmdf/mq_rdmail.c   444      0     12       30243  3623755265  10612 /*
 *     MULTI-CHANNEL MEMO DISTRIBUTION FACILITY  (MMDF)
 *
 *
 *     Copyright (C) 1979,1980,1981  University of Delaware
 *
 *     Department of Electrical Engineering
 *     University of Delaware
 *     Newark, Delaware  19711
 *
 *     Phone:  (302) 738-1163
 *
 *     This program module was developed as part of the University
 *     of Delaware's Multi-Channel Memo Distribution Facility (MMDF).
 *
 *     This module and its listings may be used freely by United States
 *     federal, state, and local government agencies and by not-for-
 *     profit institutions, after sending written notification to
 *     Professor David J. Farber at the above address.
 *
 *     For-profit institutions may use this module, by arrangement.
 *
 *     Notification must include the name of the acquiring organization,
 *     name and contact information for the person responsible for
 *     maintaining the operating system, name and contact information
 *     for the person responsible for maintaining this program, if other
 *     than the operating system maintainer, and license information if
 *     the program is to be run on a Western Electric Unix(TM) operating
 *     system.
 *     
 *     Documents describing systems using this module must cite its
 *     source.
 *     
 *     Development of this module was supported in part by the
 *     University of Delaware and in part by contracts from the United
 *     States Department of the Army Readiness and Materiel Command and
 *     the General Systems Division of International Business Machines.
 *     
 *     Portions of the MMDF system were built on software originally
 *     developed by The Rand Corporation, under the sponsorship of the
 *     Information Processing Techniques Office of the Defense Advanced
 *     Research Projects Agency.
 *
 *     The above statements must be retained with all copies of this
 *     program and may not be removed without the consent of the
 *     University of Delaware.
 *     
 *
 *     version  -1    David H. Crocker    March  1979
 *     version   0    David H. Crocker    April  1980
 *     version   1    David H. Crocker    May    1981
 *
 */
#include "util.h"
#include "mmdf.h"
#include <signal.h>
#include <sys/stat.h>
#include "ch.h"
#include "msg.h"
/*  msg_cite() is defined in adr_queue.h  */
#include "adr_queue.h"

#ifndef EWOULDBLOCK
#define EWOULDBLOCK     ETXTBSY /* To handle Berkeley 4.2 and Others */
#endif

extern LLog	*logptr;
extern long     lseek();
extern time_t   time();
extern char     *supportaddr;
extern char     *aquedir;
extern char     *mquedir;
extern char     *squepref;      /* string to preface sub-queue name   */
extern char     *lckdfldir;
extern int	lk_open();

long    mq_gtnum();    /* reads long ascii # from addr file  */

LOCVAR  int     mq_fd;         /* read file descriptor, saved */
LOCVAR  FILE    *mq_rfp;        /* read handle, for getting entries   */
LOCVAR  char    mq_dlm;         /* mq_gtnum's delimeter char         */
LOCVAR	long	mq_curpos;	/* Current offset of read pointer (rfp) */
LOCVAR  long    mq_lststrt;     /* Offset to start of addr list       */
LOCVAR  long    mq_optstrt;     /* Offset to options                  */
LOCVAR  Msg     *mq_curmsg;     /* SEK handle on current message      */

mq_rkill (type)                   /* give-up access to msg info         */
    int type;                     /* type of ending (currently ignored) */
{
#ifdef DEBUG
     ll_log (logptr, LLOGBTR, "mq_rkill (type= %d, mq_fd=%d, mq_rfp=%o)",
		 type, mq_fd, mq_rfp);
#endif

    if (mq_fd != NOTOK)
	lk_close (mq_fd, (char *) 0, lckdfldir, mq_curmsg -> mg_mname);
    if (mq_rfp != (FILE *) EOF && mq_rfp != (FILE *) NULL)
	fclose (mq_rfp);

    mq_fd = NOTOK;
    mq_rfp = NULL;
}
/**/

mq_rinit (thechan, themsg, retadr)  /* gain access to info on the msg     */
	Chan *thechan;          /* channel points to sub-queue */
	Msg  *themsg;
	char *retadr;           /* return address               */
{
    char msgname[LINESIZE];
    extern int errno;
    register short     len;
    int       tmpfd;
#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "mq_rinit ()");
#endif

    mq_curmsg = themsg;         /* SEK not message for file locking */

    if (thechan == (Chan *) 0)  /* do the entire queue          */
    {
	sprintf (msgname, "%s%s", aquedir, themsg -> mg_mname);
    }
    else
    {
	sprintf (msgname, "%s%s/%s",
		squepref, thechan -> ch_queue, themsg -> mg_mname);
    }

    if ((mq_fd = lk_open (msgname, 2, lckdfldir, themsg -> mg_mname, 20)) < OK ||
	(tmpfd = dup (mq_fd)) < OK ||
	(mq_rfp = fdopen (tmpfd, "r")) < OK)
    {                             /* msg queued in file w/its name      */
	switch (errno)
	{
	    case ENOENT:          /* another deliver probably did it    */
#ifdef DEBUG
		ll_log (logptr, LLOGFTR, "%s no entry", msgname);
		break;
#endif
	    case EWOULDBLOCK:    /* another deliver probably using it  */
#ifdef DEBUG
		ll_log (logptr, LLOGFTR, "%s busy", msgname);
#endif
		printx ("Message '%s': busy.\n", themsg -> mg_mname);
		break;

	    default:
		printx ("can't open address list '%s'\n", msgname);
		ll_err (logptr, LLOGTMP, "can't open '%s'", msgname);
	}
	mq_rkill (NOTOK);       /* Cleanup open files */
	return (NOTOK);
    }
    mq_lststrt = 0L;
    /*NOSTRICT*/
    themsg -> mg_time = (time_t) mq_gtnum ();
    mq_optstrt = mq_lststrt - 1;      /* Offset to option bits              */
    themsg -> mg_stat = (char) mq_gtnum ();
    themsg -> mg_stat |= (mq_dlm == ADR_DONE ? ADR_WARNED : 0);
				/* whether warning been sent          */
    if (fgets (retadr, ADDRSIZE, mq_rfp) == NULL)
    {
	ll_err (logptr, LLOGTMP, "Can't read %s sender name", themsg -> mg_mname);
	mq_rkill (OK);
	retadr[0] = '\0';         /* eliminate old sender name          */
	return (NOTOK);
    }
    len = strlen (retadr);
    mq_lststrt += len;
    if (retadr[len - 1] != '\n') {
	while (fgetc(mq_rfp) != '\n')
	{
	    mq_lststrt++;
	    if (feof(mq_rfp) || ferror(mq_rfp)) {
		ll_err (logptr, LLOGTMP, "Can't read %s sender name", themsg -> mg_mname);
		mq_rkill (OK);
		retadr[0] = '\0';         /*  eliminate old sender name  */
		return (NOTOK);
	    }
	}
	mq_lststrt++;

	/*  The address was probably too long, substitute orphanage  */
	sprintf (retadr, "%s (Orphanage)", supportaddr);
    }
    else
	retadr[len - 1] = '\0';       /* eliminate the newline              */

    mq_curpos = mq_lststrt;
#ifdef DEBUG
   ll_log (logptr, LLOGFTR, "name='%s' ret='%s' (fd=%d)",
	       themsg -> mg_mname, retadr, fileno (mq_rfp));
#endif
    return (OK);
}
/**/

/*
 *  mq_setpos() repositions the read pointer (rfp) but because mq_rfp is
 *	just a dup of mq_fd, mq_fd is repositioned as well.  Be careful!
 */
mq_setpos (offset)
    long offset;
{
#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "mq_setpos (%ld)", offset);
#endif

    /*
     *  The following is needed due to a bug in V7/SysIII/4.nBSD Stdio
     *  which will not allow you to invalidate the incore buffer of
     *  of a file fopened for reading.   (DPK @ BRL)
     */
    fclose (mq_rfp);
    mq_rfp = NULL;
    if ((mq_rfp = fdopen (dup(mq_fd), "r")) == NULL) {
	mq_rkill (NOTOK);
	ll_log (logptr, LLOGFAT, "mq_setpos(), cannot reopen address list");
    }

    if (offset == 0L) {
	fseek (mq_rfp, mq_lststrt, 0);
    	mq_curpos = mq_lststrt;
    }
    else {
	fseek (mq_rfp, offset, 0);
    	mq_curpos = offset;
    }
}

mq_radr (theadr)          /* obtain next address in msg's queue */
    register struct adr_struct *theadr;
{
    char *arglist[20];
    int argc;
    register int len;

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "mq_read ()");
#endif

    theadr -> adr_que = 0;   /* null it to indicate empty entry    */
    theadr -> adr_pos = mq_curpos;
    for (;;) {
	if (fgets (theadr->adr_buf, sizeof (theadr->adr_buf), mq_rfp) == NULL)
	{
	    if (feof (mq_rfp))
	    {
#ifdef DEBUG
		ll_log (logptr, LLOGFTR, "end of list");
#endif
		return (DONE);     /* simply an end of file              */
	    }
	    ll_err (logptr, LLOGTMP, "Problem reading address");
	    return (NOTOK);
	}
	len = strlen (theadr -> adr_buf);
	if (theadr -> adr_buf[2] != ADR_DONE)
	    break;
	theadr -> adr_pos += len;
    }
    mq_curpos = theadr -> adr_pos + len;
#ifdef DEBUG
    ll_log (logptr, LLOGFTR, "adr: '%s'", theadr -> adr_buf);
#endif

    theadr -> adr_buf[len - 1] = '\0';
				  /* so entry can be handled as string  */
    argc = str2arg (theadr -> adr_buf, 20, arglist, (char *) 0);
    if (argc < 4)
    {
	ll_log (logptr, LLOGTMP, "error with queue address line");
	return (NOTOK);           /* error with address entry           */
    }
    theadr -> adr_tmp   = arglist[0][0];
    theadr -> adr_delv  = arglist[1][0];
    theadr -> adr_que   = arglist[2];
    theadr -> adr_host  = arglist[3];
    theadr -> adr_local = arglist[4];

#ifdef DEBUG
    ll_log (logptr, LLOGFTR, "adr parts: '%c' '%c' '%s' '%s' '%s'",
		theadr -> adr_tmp, theadr -> adr_delv, theadr -> adr_que,
		theadr -> adr_host, theadr -> adr_local);
#endif
    return (OK);
}
/**/

/*
 * If the message was successfully delivered, or if a permanent failure
 * resulted, indicate that this address has been handled by changing the
 * delimiter between the descriptor and address info.
 */
mq_wrply (rply, themsg, theadr)   /* modify addr's entry as per reply   */
RP_Buf *rply;
Msg *themsg;
struct adr_struct *theadr;
{
    char donechr;

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "mq_wrply (%s)", themsg -> mg_mname);
#endif

    fflush (stdout);
    switch (rp_gval (rply -> rp_val))
    {
	case RP_AOK:              /* name ok; text later                  */
#ifdef DEBUG
	    ll_log (logptr, LLOGFTR,
		"AOK @ offset(%ld)", (long) (theadr -> adr_pos + ADR_TMOFF));
#endif
	    donechr = ADR_AOK;    /* temporary mark                     */
	    lseek (mq_fd, (long) (theadr -> adr_pos + ADR_TMOFF), 0);
	    write (mq_fd, &donechr, 1);
	    break;

	case RP_NDEL:             /* won't be able to deliver           */
	case RP_DONE:             /* finished processing this addr      */
#ifdef DEBUG
	    ll_log (logptr, LLOGFTR,
		"DONE @ offset(%ld)", (long) (theadr -> adr_pos + ADR_DLOFF));
#endif
	    donechr = ADR_DONE;
	    lseek (mq_fd, (long) (theadr -> adr_pos + ADR_DLOFF), 0);
	    write (mq_fd, &donechr, 1);
	    break;

	case RP_NOOP:             /* not processed, this time           */
	case RP_NO:               /* no success, this time              */
	default: 
	    if( theadr->adr_tmp == ADR_AOK )    /* We need to reset it */
	    {
#ifdef DEBUG
		ll_log (logptr, LLOGFTR,
		"CLR @ offset(%ld)", (long) (theadr -> adr_pos + ADR_TMOFF));
#endif
		donechr = ADR_CLR;
		lseek (mq_fd, (long) (theadr -> adr_pos + ADR_TMOFF), 0);
		write (mq_fd, &donechr, 1);
	    }
    }

    /*
     *  The following is needed due to a bug in V7/SysIII/4.nBSD Stdio
     *  which will not allow you to invalidate the incore buffer of
     *  of a file fopened for reading.   (DPK @ BRL)
     */
    fclose (mq_rfp);
    mq_rfp = NULL;
    if ((mq_rfp = fdopen (dup(mq_fd), "r")) == NULL) {
	mq_rkill (NOTOK);
	ll_log (logptr, LLOGFAT, "mq_wrply(), cannot reopen address list");
    }
    fseek (mq_rfp, mq_curpos, 0);	/* Back to where we were */
}
/**/

LOCFUN long
	    mq_gtnum ()      /* read a long number from adr queue  */
{
    register long   thenum;
    register char   c;

#ifdef DEBUG
    ll_log (logptr, LLOGFTR, "mq_gtnum");
#endif
    for (thenum = 0; isdigit (c = getc (mq_rfp));
	    thenum = thenum * 10 + (c - '0'), mq_lststrt++);
    mq_lststrt++;

    mq_dlm = c;                  /* note the first non-number read     */
#ifdef DEBUG
    ll_log (logptr, LLOGFTR, "(%ld)'%c'", thenum, c);
#endif
    return (thenum);
}



mq_rwarn ()                     /* note that warning has been sent      */
{
    static char donechr = ADR_DONE;

#ifdef DEBUG
    ll_log (logptr, LLOGFTR, "WARN @ offset(%ld)", (long) (mq_optstrt));
#endif

    lseek (mq_fd, mq_optstrt, 0);
    write (mq_fd, &donechr, 1);
    /*
     *  The following is needed due to a bug in V7/SysIII/4.nBSD Stdio
     *  which will not allow you to invalidate the incore buffer of
     *  of a file fopened for reading.   (DPK @ BRL)
     */
    fclose (mq_rfp);
    mq_rfp = NULL;
    if ((mq_rfp = fdopen (dup(mq_fd), "r")) == NULL) {
	mq_rkill (NOTOK);
	ll_log (logptr, LLOGFAT, "mq_rwarn(), cannot reopen address list");
    }
    fseek (mq_rfp, mq_curpos, 0);	/* Back to where we were */
}

 = '\0';
				  /* so entry can be handled as string  */
    argc = str2arg (theadr -> adr_buf, 20, arglist, (char *) 0);
    if (argc < 4)
    {
	ll_log (logptr, LLOGTMP, "error with queue address line");
	return (NOTOK);           /* error with address entry           */
    }
    theadr -> adr_tmp   = arglist[0][0];
    theadr -> adr_delv  = argmmdf/lib/mmdf/mmdf_init.c   444      0     12        5612  3664425135  10570 #include "util.h"
#include "mmdf.h"
#include "ch.h"

extern LLog *logptr;
extern char *mmtailor;         /* where the tailoring file is          */
extern char *logdfldir;

extern int mid_enable;
extern Table tb_mailids;
extern Table tb_users;
extern Table tb_mc;
extern Table **tb_list;
extern int tb_numtables;
extern int tb_maxtables;

extern char *strdup();

static	char	version[] = "$@(#)MMDFII, Release B";

#define MAXARG 100

mmdf_init (pgmname)            /* initialize an mmdf process           */
char *pgmname;
{
    extern char *dupfpath ();
    char *argv[MAXARG];
    int  argc;
    char *savelog;      /* SEK hold log file name                       */
#ifdef DEBUG
    int dbind;
#endif

    pgmname = (pgmname ? pgmname : "???");	/* Paranoid */
    ll_log (logptr, LLOGPTR, "mmdf_init (%s)", pgmname);

    if (!isstr (mmtailor))      /* tailor info is compiled in           */
	return;

    mmdf_fdinit();

    /* SEK save initial part of log name    */
    savelog = strdup (logptr  -> ll_file);

    ll_hdinit (logptr, pgmname);  /* make header unique                 */

    if (logdfldir != (char *) 0)
	logptr -> ll_file = dupfpath (logptr -> ll_file, logdfldir);

    if (tai_init (mmtailor) != OK)
	err_abrt (RP_FIO, "Can't access tailoring file '%s'", mmtailor);

    while ((argc = tai_get (MAXARG, argv)) > 0)
    {                           /* test for mmdf or dial info   */
#ifdef DEBUG
	ll_log (logptr, LLOGBTR, "argc = %d", argc);
	for (dbind = 0; dbind < argc; dbind++)
	    ll_log (logptr, LLOGFTR, "(%d) '%s'", dbind, argv[dbind]);
#endif
	if (argv[0] == 0 || argv[0][0] == '\0')
		continue;               /* noop  */

	switch (uip_tai (argc, argv))
	{                       /* user program tailoring info? */
	    case YES:
	    case NOTOK:
		continue;
	}

	switch (mm_tai (argc, argv))
	{                       /* mmdf tailoring info? */
	    case YES:
	    case NOTOK:
		continue;
	}

#ifndef NODIAL
	switch (d_tai (argc, argv))
	{                       /* dialing package info? */
	    case YES:
	    case NOTOK:
		continue;
	}
#endif NODIAL

	post_tai (argc, argv);
    }

    if (argc == NOTOK)
	err_abrt (RP_MECH, "Error processing tailoring file '%s'", mmtailor);

    tai_end ();

    if (mid_enable) {
	if ((tb_numtables+1) >= tb_maxtables)
	    err_abrt (RP_MECH, "No space in tb_list for tb_users/mailids");
	tb_list[tb_numtables++] = &tb_users;
	tb_list[tb_numtables++] = &tb_mailids;
	tb_list[tb_numtables] = (Table *) 0;
    }

    if (logdfldir != (char *) 0)
    {                           /* we know of malloc in code, above */
	free (logptr -> ll_file);
	logptr -> ll_file = dupfpath (savelog, logdfldir);
	free (savelog);
    }
    return;
}


tai_error (error, errp, argc, argv)
char *error, *errp;
int argc;
char **argv;
{
	char    errline[LINESIZE];

	argv[argc] = (char *) 0;
	arg2vstr (0, sizeof errline, errline, argv);
	ll_log (logptr, LLOGFAT, "%s (%s) in '%s'", error, errp, errline);
}
t");
    }
    fseek (mq_rfp, mq_curpos, 0);	/* Back to where we were */
}
/**/

LOCFUN long
	    mq_gtnum ()      /*mmdf/lib/mmdf/hd_format.c   444      0     12        6357  3671073153  10571 #include "util.h"
#include "mmdf.h"
#include "ch.h"

/* reformat addresses in the headers of a message       */

extern LLog *logptr;
extern long amp_hdr();
FILE *hd_fmtfp;         /* handle on the formatted header               */

LOCVAR FILE *hd_fpdup;  /*  stream pointers for original and alternate file*/
LOCVAR char hd_fn[] = "/tmp/am.hdrXXXXXX"; /* file for mapped headers */


long
	hd_init (chanptr, fd)   /* ready to process one message         */
    Chan *chanptr;              /* channel to NOT reformat refs to      */
    int fd;                     /* handle on file with message          */
{
    long hd_seek;
    char hd_cur[sizeof hd_fn];
    extern char *mktemp ();

/*  sri transform */
/*  begin special code for header translation, channel initialization phase
 *      (dhc) only call mktemp on the first time through this routine.
 *      the following assumes that x_fn is compile-time specified within
 *      this routine; otherwise, turn the sizeof into a strlen.
 *      a cleaner approach would be to keep the base string somewhere else
 *      and then strcpy it into x_fn and then call mktemp each time.
 */

    hd_seek = 0L;
    hd_fpdup = fdopen (dup (fd), "r");
				  /* open stream to msg text  */

    (void) strcpy (hd_cur, hd_fn);
    mktemp (hd_cur);   /*  create name for translated header file */

    if (close(creat(hd_cur, 0600)) < 0) {
	ll_err (logptr, LLOGTMP, "** '%s' hd_format file open err", hd_cur);
    } else if ((hd_fmtfp = fopen(hd_cur, "a+")) == NULL) {
	ll_err (logptr, LLOGTMP, "Header file re-open failure");
	hd_seek = 0L;
    } else {
	if ( (hd_seek = amp_hdr (chanptr, hd_fpdup, hd_fmtfp)) <= 0L ||
	      hd_seek == (long)MAYBE)
	{
	    ll_log (logptr, LLOGFAT, "Failure in header parse");
	    fclose (hd_fmtfp);      /* give it back                       */
	    hd_fmtfp = (FILE *) NULL;  /* signal no xlated file to read   */
	    if(hd_seek != (long)MAYBE)
		hd_seek = 0L;
	} else                      /* setup for the readings             */
	    fseek (hd_fmtfp, 0L, 0);
    }
    (void) unlink (hd_cur);        /*  Blow it away!!  */

    if (hd_fpdup != (FILE *) NULL)
    {
	fclose (hd_fpdup);         /* toss unneeded stream pointer     */
	hd_fpdup = (FILE *) NULL;
    }
    return (hd_seek);
}

hd_minit ()                         /* ready to process one copy        */
{
#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "hd_minit ()");
#endif

    if (hd_fmtfp != (FILE *) NULL)
	rewind (hd_fmtfp);                /* rewind xlated file */
}

hd_read (buf, max)
    char *buf;                      /* where to put the data            */
    int max;                        /* maximum size of buf              */
{
    int nread;

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "hd_read ()");
#endif

/* sri transform */
/* special code for header translation, each address phase  */

    if (hd_fmtfp != (FILE *) NULL)  {
	/*
	 *  we have a translated file
	 */
	nread = fread (buf, sizeof (char), max, hd_fmtfp);
	if (nread == 0 && ferror(hd_fmtfp))
	    return (NOTOK);
	else
	    return (nread);
    }
    return (0);
}

hd_end ()
{
#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "hd_end ()");
#endif

    if (hd_fmtfp != (FILE *) NULL)
    {
	fclose (hd_fmtfp);      /*  done reading xlated file          */
	hd_fmtfp = (FILE *) NULL;
    }
}
0'), mq_lststrt++);
    mq_lststrt++;

    mq_dlm = c;                  /* note the first non-number read     */
#ifdef DEBUG
    ll_log (logptr, LLOGFTR, "(%ld)'%c'", thenum, c);
#endif
    return (thenum);
}



mq_rwarn ()                     /* note that warning has beemmdf/lib/mmdf/uip_tai.c   444      0     12         620  3620510371  10213 #include "util.h"
#include "mmdf.h"

/* fake user program tailoring, in case the user program does not
 * use it or it is not a user program.  This way, everyone can just
 * call mmdf_init().
 */

uip_tai (argc, argv)            /* tailor some uip parameter */
    int argc;                   /* number of values     */
    char *argv[];               /* list of values       */
{
    return (NO);
}
(chanptr, fd)   /* ready to process one message         */
    Chan *chanptr;              /* channel to NOT refmmdf/lib/mmdf/mm_tai.c   444      0     12       76210  3655234571  10114 #include "util.h"
#include "mmdf.h"
#include "cmd.h"
#include "ch.h"
#include "ap.h"

extern char *malloc ();

extern LLog *logptr, chanlog;

extern char
	    *locmachine,        /* UCL local machine name       */
	    *locname,
	    *locdomain,
	    *sitesignature,
	    *mmdflogin,
	    *supportaddr;

extern char
	    *logdfldir,
	    *phsdfldir,
	    *tbldfldir,
	    *tbldbm,
	    *quedfldir,
	    *cmddfldir,
	    *chndfldir,
	    *lckdfldir,
	    *mldfldir;

extern int
	    queprot,
	    warntime,
	    failtime,
	    maxhops,
	    maxqueue,
	    mgt_addid,
	    lnk_listsize,
	    mid_enable,
	    mailsleep,
	    sentprotect;

extern LLog
	    msglog,
	    chanlog,
	    authlog;

extern char *def_trn;

extern char
	    *tquedir,
	    *aquedir,
	    *mquedir;

extern char
	    *namsubmit,
	    *pathsubmit,
	    *namdeliver,
	    *pathdeliver,
	    *nampkup,
	    *pathpkup,
	    *nammail,
	    *pathmail;

extern char
	    *ch_dflnam,
	    *dlvfile,
	    *mldflfil,
	    *delim1,
	    *delim2;

extern char                     /* NIFTP channel bits                   */
		*pn_quedir;

extern char
		*authrequest,   /* Authorisation request		*/
		*Uuxstr;	/* uux command string			*/
/**/

#define MMLOCHOST        1
#define MMSIGN           2
#define MMLOGIN          3
#define MMSUPPORT        4
#define MMLOGDIR         5
#define MMPHSDIR         6
#define MMTBLDIR         7
#define MMDBM            8
#define MMQUEDIR         9
#define MMCMDDIR        10
#define MMCHANDIR       11
#define MMDLVRDIR       12
#define MMTEMPT         13
#define MMADDRQ         14
#define MMMSGQ          15
#define MMQUEPROT       16
#define MMMSGLOG        17
#define MMCHANLOG       18
#undef	Gonzo           19
#undef	Gonzo           20
#define MMWARNTIME      21
#define MMFAILTIME      22
#define MMMAXSORT       23
#define MMSLEEP         24
#define MMSUBMIT        25
#define MMDELIVER       26
#define MMPICKUP        27
#define MMV6MAIL        28
#define MMMBXPROT       29
#define MMMBXNAME       30
#define MMMBXPREF       31
#define MMMBXSUFF       32
#define MMDLVFILE       33
#define	MMAXHOPS	34
#define	MADDID		35
#define MLISTSIZE	36
#define MMDFLCHAN       37
#define UUxstr          38
#define MMTBL           39
#define MMLOCDOMAIN     40
#define MMDOMAIN        41
#define	ALIAS           42
#define MMLOCMACHINE    43
#define NIQUEDIR        44
#define MMMAILIDS       45
#define MMLCKDIR        46
#define AUTHLOG         47
#define AUTHREQUEST	48
#define MMCHAN          49
#define MMNOOP         100

/**/

Cmd cmdtab[] =
{
    "mlocmachine", MMLOCMACHINE, 1,
    "mlname",      MMLOCHOST,  1,
    "mlochost",    MMLOCHOST,  1,
    "mldomain",    MMLOCDOMAIN, 1,
    "msig",        MMSIGN,     1,
    "mlogin",      MMLOGIN,    1,
    "msupport",    MMSUPPORT,  1,
    "mlogdir",     MMLOGDIR,   1,
    "mphsdir",     MMPHSDIR,   1,
    "mtbldir",     MMTBLDIR,   1,
    "mlckdir",     MMLCKDIR,   1,
    "mdbm",        MMDBM,      1,
    "mdflchan",    MMDFLCHAN,  1,
    "mquedir",     MMQUEDIR,   1,
    "mcmddir",     MMCMDDIR,   1,
    "mchndir",     MMCHANDIR,  1,
    "mdlvrdir",    MMDLVRDIR,  1,
    "mtempt",      MMTEMPT,    1,
    "mtmpt",       MMTEMPT,    1,
    "maddrq",      MMADDRQ,    1,
    "mmsgq",       MMMSGQ,     1,
    "mqueprot",    MMQUEPROT,  1,
    "mmsglog",     MMMSGLOG,   1,
    "mchanlog",    MMCHANLOG,  1,
    "authlog",     AUTHLOG,    1,
    "mwarntime",   MMWARNTIME, 1,
    "mfailtime",   MMFAILTIME, 1,
    "mmaxsort",    MMMAXSORT,  1,
    "msleep",      MMSLEEP,    1,
    "msubmit",     MMSUBMIT,   1,
    "mdeliver",    MMDELIVER,  1,
    "mpkup",       MMPICKUP,   1,
    "mv6mail",     MMV6MAIL,   1,
    "mmbxprot",    MMMBXPROT,  1,
    "mmbxname",    MMMBXNAME,  1,
    "mmbxpref",    MMMBXPREF,  1,
    "mmbxsuff",    MMMBXSUFF,  1,
    "mdlv",        MMDLVFILE,  1,
    "maddid",	   MADDID,     1,
    "mmaxhops",    MMAXHOPS,   1,
    "mlistsize",   MLISTSIZE,  1,
    "uuxstr",      UUxstr,     1,
    "mtbl",        MMTBL,      1,
    "mchn",        MMCHAN,     1,
    "mmailid",     MMMAILIDS,  1,
    "mdmn",        MMDOMAIN,   1,
    "niquedir",    NIQUEDIR,   1,
    "authrequest", AUTHREQUEST,1,
    "alias",	   ALIAS,      1,
    "",            MMNOOP,     0,
    0,             0,          0
};
/**/

mm_tai (argc, argv)     /* process mmdf tailor info     */
    int argc;
    char *argv[];
{
    int retval;

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "m_tai (%s: %d)", argv[0], argc);
#endif

    retval = YES;

    switch (cmdsrch (argv[0], --argc, cmdtab))
    {
	case NO:
	case MMNOOP:
	default:        /* command not known to us      */
	    retval = NO;
	    break;

	case MMLOCMACHINE:
	    locmachine = argv[1];
	    break;

	case MMLOCHOST:
	    locname = argv[1];
	    break;

	case MMLOCDOMAIN:
	    locdomain = argv[1];
	    break;

	case MMSIGN:
	    sitesignature = argv[1];
	    break;

	case MMLOGIN:
	    mmdflogin = argv[1];
	    break;

	case MMSUPPORT:
	    supportaddr = argv[1];
	    break;

	case MMLOGDIR:
	    logdfldir = argv[1];
	    break;

	case MMPHSDIR:
	    phsdfldir = argv[1];
	    break;

	case MMTBLDIR:
	    tbldfldir = argv[1];
	    break;

	case MMDBM:
	    tbldbm = argv[1];
	    break;

	case MMQUEDIR:
	    quedfldir = argv[1];
	    break;

	case MMCMDDIR:
	    cmddfldir = argv[1];
	    break;

	case MMCHANDIR:
	    chndfldir = argv[1];
	    break;

	case MMDLVRDIR:
	    mldfldir = argv[1];
	    break;

	case MMLCKDIR:
	    lckdfldir = argv[1];
	    break;

	case MMTEMPT:
	    tquedir = argv[1];
	    break;

	case MMADDRQ:
	    aquedir = argv[1];
	    break;

	case MMMSGQ:
	    mquedir = argv[1];
	    break;

	case MMQUEPROT:
	    sscanf (argv[1], "%o", &queprot);
	    break;

	case MMMSGLOG:
	    retval = tai_log (argc, &argv[1], &msglog);
	    break;

	case MMCHANLOG:
	    retval = tai_log (argc, &argv[1], &chanlog);
	    break;

	case AUTHLOG:
	    retval = tai_log (argc, &argv[1], &authlog);
	    break;

	case AUTHREQUEST:
	    authrequest = argv[1];
	    break;

	case MMWARNTIME:
	    warntime = atoi (argv[1]);
	    break;

	case MMFAILTIME:
	    failtime = atoi (argv[1]);
	    break;

	case MMMAXSORT:
	    maxqueue = atoi (argv[1]);
	    break;

	case MMSLEEP:
	    mailsleep = atoi (argv[1]);
	    break;

	case MMSUBMIT:
	    retval = tai_pgm (argc, &argv[1], &namsubmit, &pathsubmit);
	    break;

	case MMDELIVER:
	    retval = tai_pgm (argc, &argv[1], &namdeliver, &pathdeliver);
	    break;

	case MMPICKUP:
	    retval = tai_pgm (argc, &argv[1], &nampkup, &pathpkup);
	    break;

	case MMV6MAIL:
	    retval = tai_pgm (argc, &argv[1], &nammail, &pathmail);
	    break;

	case MMMBXPROT:
	    sscanf (argv[1], "%o", &sentprotect);
	    break;

	case MMMBXNAME:
	    mldflfil = argv[1];
	    break;

	case MMMBXPREF:
	    delim1 = argv[1];
	    break;

	case MMMBXSUFF:
	    delim2 = argv[1];
	    break;

	case MMDLVFILE:
	    dlvfile = argv[1];
	    break;

    	case MMAXHOPS:
    	    maxhops = atoi(argv[1]);
    	    break;

    	case MADDID:
    	    mgt_addid = atoi(argv[1]);
    	    break;

    	case MLISTSIZE:
	    lnk_listsize = atoi(argv[1]);
    	    break;

	case UUxstr:
	    Uuxstr = argv[1];
	    break;

	case MMTBL:
	    retval = tb_tai (argc, &argv[1]);
	    break;

	case MMDFLCHAN:
	    ch_dflnam = argv[1];
	    break;

	case MMCHAN:
	    retval = ch_tai (argc, &argv[1]);
	    break;

	case MMMAILIDS:         /* enable/disable use of Mail-Ids */
	    mid_enable = atoi(argv[1]);
	    break;

	case MMDOMAIN:
	    retval = dm_tai (argc, &argv[1]);
	    break;

	case NIQUEDIR:
	    pn_quedir = argv[1];
	    break;

	case ALIAS:
    	    al_tai (argc, &argv[1]);
    	    break;
    }

#ifdef DEBUG
    ll_log (logptr, LLOGFTR, "m_tai (ret=%d)", retval);
#endif

    return (retval);
}
/**/

extern Table    **tb_list;
extern int  tb_maxtables;
extern int  tb_numtables;

#define CMDTBASE    1
#define CMDTNAME    2
#define CMDTFILE    3
#define CMDTSHOW    4
#define CMDTFLAGS   5
#define CMDTNOOP    6

LOCVAR Cmd
	    cmdtbl[] =
{
    "base",     CMDTBASE,   1,
    "name",     CMDTNAME,   1,
    "file",     CMDTFILE,   1,
    "show",     CMDTSHOW,   1,
    "flags",	CMDTFLAGS,  1,
    "",         CMDTNOOP,   0,
    0,          0,          0
};

#define CMDTFFILE	1
#define CMDTFDBM	2
#define CMDTFNS		3
#define CMDTFDOMAIN     4
#define CMDTFCHANNEL    5
#define CMDTFROOT       6
#define CMDTFPARTIAL    7

LOCVAR Cmd
	tbflags [] =
{
    "file",	CMDTFFILE,	0,
    "dbm",	CMDTFDBM,	0,
    "ns",	CMDTFNS,	0,
    "domain",   CMDTFDOMAIN,    0,
    "channel",  CMDTFCHANNEL,   0,
    "root",     CMDTFROOT,      0,
    "partial",  CMDTFPARTIAL,   0,
    0,          0,              0
};

tb_tai (argc, argv)
    int argc;
    char *argv[];
{
    int ind;
    register Table *tbptr;

#ifdef DEBUG
    ll_log (logptr, LLOGFTR, "tb_tai (%s: %d)", argv[0], argc);
#endif

    if (tb_numtables >= tb_maxtables)
    {
	tai_error ("exceed table size", argv[0], argc, argv);
	return (NOTOK);
    }
#ifdef DEBUG
    if (logptr -> ll_level == LLOGFTR)
    {
        ll_log (logptr, LLOGFTR, "old tables (%d)", tb_numtables);
        for (ind = 0; tb_list[ind] != (Table *) 0; ind++)
    	    ll_log (logptr, LLOGFTR, "tb(%d) '%s'", 
		ind, tb_list[ind] -> tb_name);
    }
#endif

    /*NOSTRICT*/
    tb_list[tb_numtables++] = tbptr =
			(Table *) malloc ((unsigned) (sizeof (Table)));
    tb_list[tb_numtables] = (Table *) 0;
    tbptr -> tb_name =  "notablename";
    tbptr -> tb_show =  (char *) 0;
    tbptr -> tb_file =  (char *) 0;
    tbptr -> tb_fp   =  (FILE *) NULL;
    tbptr -> tb_pos  =  0L;
    tbptr -> tb_flags = TB_FILE;

    for (ind = 0; ind < argc; ind++)
    {
	if (!lexequ (argv[ind], "="))
	{
	    if (ind == 0)
	    {                   /* ok to default first field only */
#ifdef DEBUG
		ll_log (logptr, LLOGBTR, "defaulting base '%s'", argv[ind]);
#endif
		goto dobase;
	    }
	    else
	    {
#ifdef DEBUG
		tai_error ("unassignable, bare data field",
				argv[ind], argc, argv);
#endif
		continue;
	    }
	}
	else
	{
	    ind += 2;
#ifdef DEBUG
	    ll_log (logptr, LLOGFTR, "tb '%s'(%d)", argv[ind - 1], argc - ind);
#endif
	    switch (cmdsrch (argv[ind - 1], argc - ind, cmdtbl))
	    {
		case CMDTBASE:
	dobase:
		    tbptr -> tb_name =  argv[ind];
		    tbptr -> tb_show =  argv[ind];
		    tbptr -> tb_file =  argv[ind];
		    break;

		case CMDTNAME:
		    tbptr -> tb_name =  argv[ind];
		    break;

		case CMDTFILE:
		    tbptr -> tb_file =  argv[ind];
		    break;

		case CMDTSHOW:
		    tbptr -> tb_show=  argv[ind];
		    break;

	    	case CMDTFLAGS:
#ifdef DEBUG
		    ll_log (logptr, LLOGFTR, "table flag '%s'", argv[ind]);
#endif
		    switch (cmdsrch (argv[ind], 0, tbflags))
		    {
			case CMDTFFILE:
			    tbptr -> tb_flags |= TB_FILE;
			    break;

			case CMDTFDBM:
			    tbptr -> tb_flags |= TB_DBM;
			    break;

			case CMDTFNS:
			    tbptr -> tb_flags |= TB_NS;
			    break;

			case CMDTFDOMAIN:
			    tbptr -> tb_flags |= TB_DOMAIN;
			    break;

			case CMDTFCHANNEL:
			    tbptr -> tb_flags |= TB_CHANNEL;
			    break;

			case CMDTFROOT:
			    tbptr -> tb_flags |= TB_ROOT;
			    break;

			case CMDTFPARTIAL:
			    tbptr -> tb_flags |= TB_PARTIAL;
			    break;

			default:
#ifdef DEBUG
			    tai_error ("unknown table flag", argv[ind-1],
					argc, argv);
#endif
			    continue;
		    }
	    	    break;

		case CMDTNOOP:
		    break;      /* noop */

		default:
#ifdef DEBUG
		    tai_error ("unknown table parm", argv[ind], argc, argv);
#endif
		    break;
	    }
	}
    }

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "tb_tai (ret=YES)");
    ll_log (logptr, LLOGFTR, "name=%s, ref=%s, file=%s, flags=%0o",
		tbptr -> tb_name, tbptr -> tb_show, tbptr -> tb_file,
		tbptr -> tb_flags);
#endif

    return (YES);
}
/**/

#include "dm.h"

extern Domain   **dm_list;
extern int  dm_maxtables;
extern int  dm_numtables;

#define CMDDBASE    1
#define CMDDNAME    2
#define CMDDDMN     3
#define CMDDSHOW    4
#define CMDDTABLE   5
#define CMDDNOOP    7
#define CMDDLNAME   8

LOCVAR Cmd
	    dmntbl[] =
{
    "base",     CMDDBASE,   1,
    "name",     CMDDNAME,   1,
    "dmn",      CMDDDMN,    1,
    "show",     CMDDSHOW,   1,
    "table",    CMDDTABLE,  1,
    "lname",    CMDDLNAME,  1,
    "",         CMDDNOOP,   0,
    0,          0,          0
};

dm_tai (argc, argv)
    int argc;
    char *argv[];
{
    int ind;
    int tbind,
	showind;
    char *tbname;
    register Domain *dmnptr;

#ifdef DEBUG
    ll_log (logptr, LLOGFTR, "dm_tai (%s: %d)", argv[0], argc);
#endif

    if (dm_numtables >= dm_maxtables)
    {
	tai_error ("exceed domain table size", argv[0], argc, argv);
	return (NOTOK);
    }
#ifdef DEBUG
    if (logptr -> ll_level == LLOGFTR)
    {
        ll_log (logptr, LLOGFTR, "old domains (%d)", dm_numtables);
        for (ind = 0; dm_list[ind] != (Domain *) 0; ind++)
    	    ll_log (logptr, LLOGFTR, "dm(%d) '%s'", 
		ind, dm_list[ind] -> dm_name);
    }
#endif

    /*NOSTRICT*/
    dm_list[dm_numtables++] = dmnptr =
			(Domain *) malloc ((unsigned) (sizeof (Domain)));
    dm_list[dm_numtables] = (Domain *) 0;
    dmnptr -> dm_name     = "nodomainname";
    dmnptr -> dm_domain   =  (char *) 0;
    dmnptr -> dm_lname    =  (char *) 0;
    dmnptr -> dm_show     =  (char *) 0;
    dmnptr -> dm_table    =  (Table *) 0;
    tbind = -1;
    showind = -1;

    for (ind = 0; ind < argc; ind++)
    {
	if (!lexequ (argv[ind], "="))
	{
	    if (ind == 0)
	    {                   /* ok to default first field only */
#ifdef DEBUG
		ll_log (logptr, LLOGBTR, "defaulting base '%s'", argv[ind]);
#endif
		goto dobase;
	    }
	    else
	    {
#ifdef DEBUG
		tai_error ("unassignable, bare data field", argv[ind], argc, argv);
#endif
		continue;
	    }
	}
	else
	{
	    ind += 2;
#ifdef DEBUG
	    ll_log (logptr, LLOGFTR, "dm '%s'(%d)", argv[ind - 1], argc - ind);
#endif
	    switch (cmdsrch (argv[ind - 1], argc - ind, dmntbl))
	    {
		case CMDDBASE:
	dobase:
		    dmnptr -> dm_name   = argv[ind];
		    dmnptr -> dm_domain = argv[ind];
		    showind = ind;
		    break;

		case CMDDNAME:
		    dmnptr -> dm_name =  argv[ind];
		    break;

		case CMDDDMN:
		    dmnptr -> dm_domain =  argv[ind];
		    break;

		case CMDDSHOW:
		    showind = ind;
		    break;

		case CMDDTABLE:
		    tbind = ind;
		    break;

		case CMDDLNAME:
		    dmnptr -> dm_lname = argv[ind];
		    break;

		case CMDDNOOP:
		    break;      /* noop */

		default:
		    argv[ind + 1] = (char *) 0; /* eliminate rest of args */
#ifdef DEBUG
		    tai_error ("unknown domain parm", argv[ind-1], argc, argv);
#endif
		    break;
	    }
	}
    }
    tbname = (tbind >= 0) ? argv[tbind] : dmnptr -> dm_name;
    if ((dmnptr -> dm_table = tb_nm2struct (tbname)) == (Table *) NOTOK)
    {                   /* table does not already exist */
#ifdef DEBUG
	ll_log (logptr, LLOGBTR, "'%s' unknown domain table", tbname);
#endif
	if (tb_numtables >= tb_maxtables)
	{
#ifdef DEBUG
	    tai_error ("exceed tb_list table size", tbname, argc, argv);
#endif
	}
	else
	{
	    /*NOSTRICT*/
	    tb_list[tb_numtables++] = dmnptr -> dm_table =
		(Table *) malloc ((unsigned) (sizeof (Table)));
	    tb_list[tb_numtables] = (Table *) 0;
#ifdef DEBUG
	    ll_log (logptr, LLOGBTR, "allocating dm_table addr (%o)", dmnptr -> dm_table);
#endif
	    dmnptr -> dm_table -> tb_name =  tbname;
	    dmnptr -> dm_table -> tb_file =  tbname;
	    dmnptr -> dm_table -> tb_fp   =  (FILE *) NULL;
	    dmnptr -> dm_table -> tb_pos  =  0L;
	    dmnptr -> dm_table -> tb_show =
		(showind >= 0 ? argv[showind] : tbname);
	}
    }
#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "dm_table addr (%o)", dmnptr -> dm_table);
#endif

    dmnptr -> dm_show = (showind >= 0 ? argv[showind] : dmnptr -> dm_name);
    if (dmnptr -> dm_lname == (char *) 0)
	dmnptr -> dm_lname = locname;

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "dm_tai (ret=YES)");
    ll_log (logptr, LLOGFTR, "domain '%s'('%s'), table='%s'(%s)",
		dmnptr -> dm_show, dmnptr -> dm_name,
		dmnptr -> dm_table -> tb_show, dmnptr -> dm_table -> tb_name);
    ll_log (logptr, LLOGBTR, "dm_table addr (%o)", dmnptr -> dm_table);
#endif

    return (YES);
}
/**/
extern Alias   *al_list;

al_tai (argc, argv)
    int argc;
    char *argv[];
{
    int ind;
    register Alias *alptr, *palptr;

#ifdef DEBUG
    ll_log (logptr, LLOGFTR, "al_tai (%s: %d)", argv[0], argc);
#endif

    /*NOSTRICT*/
    alptr = (Alias *) malloc ((unsigned) (sizeof (Alias)));
    alptr -> al_flags = 0;
    alptr -> al_next = NULL;

    for (ind = 0; ind < argc; ind++)
    {
	if (!lexequ (argv[ind], "="))
	{
	    if (lexequ (argv[ind], "trusted"))
		alptr->al_flags |= AL_TRUSTED;
	    else if (lexequ (argv[ind], "nobypass"))
		alptr->al_flags |= AL_NOBYPASS;
	    else {
#ifdef DEBUG
		tai_error ("unassignable, alias field", argv[ind], argc, argv);
#endif
		continue;
	    }
	} else {
	    ind += 2;
#ifdef DEBUG
	    ll_log (logptr, LLOGFTR, "al '%s'(%d)", argv[ind - 1], argc - ind);
#endif
	    if (lexequ(argv[ind-1], "table")) {
		if ((alptr->al_table = tb_nm2struct(argv[ind]))
		   == (Table *)NOTOK)
		{
#ifdef DEBUG
		    ll_log (logptr, LLOGBTR, "'%s' unknown alias table", argv[ind]);
#endif
		    if (tb_numtables >= tb_maxtables) {
#ifdef DEBUG
			tai_error ("exceed tb_list table size", argv[ind], argc, argv);
#endif
		    } else {
			/*NOSTRICT*/
			tb_list[tb_numtables++] = alptr->al_table =
			    (Table *) malloc ((unsigned) (sizeof (Table)));
			tb_list[tb_numtables] = (Table *) 0;
			alptr -> al_table -> tb_name =  argv[ind];
			alptr -> al_table -> tb_file =  argv[ind];
			alptr -> al_table -> tb_fp   =  (FILE *) NULL;
			alptr -> al_table -> tb_pos  =  0L;
		    	alptr -> al_table -> tb_show =  argv[ind];
		    }
		}
	    } else {
		argv[ind + 1] = (char *) 0; /* eliminate rest of args */
#ifdef DEBUG
		tai_error ("unknown alias parm", argv[ind-1], argc, argv);
#endif
		break;
	    }
	}
    }
    if (al_list == (Alias *)0)
	al_list = alptr;
    else {
	for (palptr = al_list; palptr->al_next != NULL; palptr = palptr->al_next);
	palptr->al_next = alptr;
    }
#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "al_tai (ret=YES)");
#endif
    return (YES);
}
/**/

extern Chan **ch_tbsrch;
extern int  ch_maxchans;
extern int  ch_numchans;

#define CMDCBASE    1
#define CMDCNAME    2
#define CMDCSHOW    3
#undef	Gonzo       4    /* OBSOLETE */
#define CMDCQUE     5
#define CMDCTBL     6
#define CMDCPGM     7
#define CMDCHOST    8
#define CMDCUSER    9
#define CMDCSCRIPT 10
#define CMDCPOLL   11
#define CMDCACCESS 12
#define CMDCNOOP   13
#define CMDCLNAME  14
#define CMDCLDOMN  15
#define CMDCLISRC  16
#define CMDCLOSRC  17
#define CMDCLIDEST 18
#define CMDCLODEST 19
#define CMDCKNOWN  20
#define CMDCCONFSTR 21
#define CMDCAP     22
#define CMDCAUTH   23
#define CMDCTRANS  24
#define CMDCTTL    25
#define CMDCLLOG   26
#define CMDCLLEV   27

LOCVAR Cmd
	    cmdchan[] =
{
    "base",     CMDCBASE,   1,
    "name",     CMDCNAME,   1,
    "show",     CMDCSHOW,   1,
    "que",      CMDCQUE,    1,
    "tbl",      CMDCTBL,    1,
    "pgm",      CMDCPGM,    1,
    "host",     CMDCHOST,   1,
    "user",     CMDCUSER,   1,
    "scr",      CMDCSCRIPT, 1,
    "trn",      CMDCTRANS,  1,
    "poll",     CMDCPOLL,   1,
    "mod",      CMDCACCESS, 1,
    "lname",    CMDCLNAME,  1,
    "ldomain",  CMDCLDOMN,  1,
    "insrc",    CMDCLISRC,  1,
    "outsrc",   CMDCLOSRC,  1,
    "indest",   CMDCLIDEST, 1,
    "outdest",  CMDCLODEST, 1,
    "known",    CMDCKNOWN,  1,
    "confstr",  CMDCCONFSTR,1,
    "ap",       CMDCAP,     1,
    "auth",     CMDCAUTH,   1,
    "ttl",      CMDCTTL,    1,
    "log",      CMDCLLOG,   1,
    "level",    CMDCLLEV,   1,
    "",         CMDCNOOP,   0,
    0,          0,          0
};

#define CMDPREG     1
#define CMDPBAK     2
#define CMDPPSV     3
#define CMDPDID     4
#define CMDPIMM     5
#define CMDPPICK    6
#define CMDPSEND    7
#define CMDPNOOP    8

LOCVAR Cmd
	    parmchan[] =
{
    "reg",      CMDPREG,    0,
    "bak",      CMDPBAK,    0,
    "psv",      CMDPPSV,    0,
    "imm",      CMDPIMM,    0,
    "pick",     CMDPPICK,   0,
    "send",     CMDPSEND,   0,
    "",         CMDPNOOP,   0,
    0,          0,          0
};


#define CMDASAME        1
#define CMDA733         2
#define CMDA822         3
#define CMDABIG         4
#define CMDANODOTS      5
#define CMDAJNT         6



LOCVAR Cmd
	parmap [] =
{
    "same",     CMDASAME,       0,
    "733",      CMDA733,        0,
    "822",      CMDA822,        0,
    "big",      CMDABIG,        0,
    "nodots",   CMDANODOTS,     0,
    "jnt",      CMDAJNT,        0,
    0,          0,              0
};

#define CMDTFREE        0
#define CMDTINLOG       1
#define CMDTINWARN      2
#define CMDTINBLOCK     3
#define CMDTOUTLOG      4
#define CMDTOUTWARN     5
#define CMDTOUTBLOCK    6
#define CMDTHAU         7
#define CMDTDHO         8

LOCVAR  Cmd
	authmap [] =
{
    "free",     CMDTFREE,       0,
    "inlog",    CMDTINLOG,      0,
    "inwarn",   CMDTINWARN,     0,
    "inblock",  CMDTINBLOCK,    0,
    "outlog",   CMDTOUTLOG,     0,
    "outwarn",  CMDTOUTWARN,    0,
    "outblock", CMDTOUTBLOCK,   0,
    "hau",      CMDTHAU,        0,
    "dho",      CMDTDHO,        0,
    0,          0,              0
};

ch_tai (argc, argv)
    int argc;
    char *argv[];
{
    int ind;
    int tbind,
	showind;
    char *tbname;
    register Chan *chptr;

#ifdef DEBUG
    ll_log (logptr, LLOGFTR, "ch_tai (%s: %d)", argv[0], argc);
#endif

    if (ch_numchans >= ch_maxchans)
    {
#ifdef DEBUG
	tai_error ("exceed ch table size", argv[0], argc, argv);
#endif
	return (NOTOK);
    }
#ifdef DEBUG
    if ( logptr -> ll_level == LLOGFTR ) {
	ll_log (logptr, LLOGFTR, "old channels (%d)", ch_numchans);
	for (ind = 0; ch_tbsrch[ind] != (Chan *) 0; ind++)
	    ll_log (logptr, LLOGFTR, "ch(%d) '%s'",
		ind, ch_tbsrch[ind] -> ch_name);
    }
#endif

    /*NOSTRICT*/
    ch_tbsrch[ch_numchans++] = chptr =
			(Chan *) malloc ((unsigned) (sizeof (Chan)));
    ch_tbsrch[ch_numchans] = (Chan *) 0;

    chptr -> ch_name   =  "nochanname";
    chptr -> ch_show   =  (char *) 0;
    chptr -> ch_table  =  (Table *) 0;
    chptr -> ch_queue  =  (char *) 0;
    chptr -> ch_access =  0;                    /* == DLVRREG */
    chptr -> ch_ppath  =  (char *) 0;
    chptr -> ch_lname  =  locname;
    chptr -> ch_ldomain = locdomain;
    chptr -> ch_host   =  (char *) NORELAY;
    chptr -> ch_login  =  (char *) NOLOGIN;
    chptr -> ch_poltime = (char) 8; /* every two hours */
    chptr -> ch_script =  (char *) 0;
    chptr -> ch_trans  =  (char *) DEFTRANS;
    chptr -> ch_insource = (Table *) 0;
    chptr -> ch_outsource = (Table *) 0;
    chptr -> ch_indest  =  (Table *) 0;
    chptr -> ch_outdest =  (Table *) 0;
    chptr -> ch_confstr = (char *) 0;
    chptr -> ch_apout = AP_SAME;
    chptr -> ch_known = (Table  *) 0;
    chptr -> ch_auth = CH_FREE;
    chptr -> ch_dead = (Cache *) 0;
    chptr -> ch_ttl = (time_t)7200;  /* dead cache TimeToLive, two hours */
    chptr -> ch_logfile = chanlog.ll_file;
    chptr -> ch_loglevel = chanlog.ll_level;
    tbind = -1;
    showind = -1;

    for (ind = 0; ind < argc; ind++)
    {
	if (!lexequ (argv[ind], "="))
	{                       /* not an 'assignment' parameter        */
	    if (ind == 0)
	    {
#ifdef DEBUG
		ll_log (logptr, LLOGBTR, "defaulting base '%s'", argv[ind]);
#endif
		goto dobase;    /* default first field only */
	    }
	    else
	    {
#ifdef DEBUG
		tai_error ("unassignable, bare channel field", argv[ind], argc, argv);
#endif
		continue;
	    }
	}
	else
	{
	    ind += 2;
#ifdef DEBUG
	    ll_log (logptr, LLOGFTR, "ch '%s'(%d)", argv[ind - 1], argc - ind);
#endif
	    switch (cmdsrch (argv[ind - 1], argc - ind, cmdchan))
	    {
		case CMDCBASE:
	dobase:
		    chptr -> ch_name = argv[ind];
		    chptr -> ch_queue = argv[ind];
		    chptr -> ch_ppath = argv[ind];
		    break;

		case CMDCNAME:
		    chptr -> ch_name =  argv[ind];
		    break;

		case CMDCSHOW:
		    showind = ind;
		    break;

		case CMDCQUE:
		    chptr -> ch_queue =  argv[ind];
		    break;

		case CMDCTBL:
		    tbind = ind;
		    break;

		case CMDCPGM:
		    chptr -> ch_ppath =  argv[ind];
		    break;

		case CMDCHOST:
		    chptr -> ch_host =  argv[ind];
		    break;

		case CMDCUSER:
		    chptr -> ch_login =  argv[ind];
		    break;

		case CMDCSCRIPT:
		    chptr -> ch_script =  argv[ind];
		    break;

		case CMDCTRANS:
		    chptr -> ch_trans = argv[ind];
		    break;

		case CMDCPOLL:
		    chptr -> ch_poltime =  atoi (argv[ind]);
		    break;

		case CMDCTTL:
		    /*NOSTRICT*/
		    chptr -> ch_ttl = (time_t) atoi (argv[ind]) * 60L;
		    break;

		case CMDCLLOG:
		    chptr -> ch_logfile = argv[ind];
		    break;

		case CMDCLLEV:
		    if ((chptr -> ch_loglevel = tai_llev (argc, &argv[ind])) < 0)
			chptr -> ch_loglevel = chanlog.ll_level;
		    break;

		case CMDCACCESS:
#ifdef DEBUG
		    ll_log (logptr, LLOGFTR, "ch parm '%s'", argv[ind]);
#endif
		    switch (cmdsrch (argv[ind], 0, parmchan))
		    {
			case CMDPREG:
			    chptr -> ch_access |= DLVRREG;
			    break;

			case CMDPBAK:
			    chptr -> ch_access |= DLVRBAK;
			    break;

			case CMDPPSV:
			    chptr -> ch_access |= DLVRPSV;
			    break;

			case CMDPIMM:
			    chptr -> ch_access |= DLVRIMM;
			    break;

			case CMDPPICK:
			    chptr -> ch_access |= CH_PICK;
			    break;

			case CMDPSEND:
			    chptr -> ch_access |= CH_SEND;
			    break;

			case CMDPNOOP:
			    break;      /* noop */

			default:
#ifdef DEBUG
			    tai_error ("unknown access parm", argv[ind-1],
					argc, argv);
#endif
			    continue;
		    }
		    break;

		case CMDCNOOP:
		    break;      /* noop */

		case CMDCLNAME:
		    chptr -> ch_lname =  argv[ind];
		    break;

		case CMDCLDOMN:
		    chptr -> ch_ldomain =  argv[ind];
		    break;

		case CMDCLISRC:
		    if ((chptr -> ch_insource = tb_nm2struct (argv[ind]))
							== (Table *) NOTOK)
		    {
#ifdef DEBUG
			tai_error ("unknown source table", argv[ind], argc, argv);
#endif
			continue;
		    }
		    break;

		case CMDCLOSRC:
		    if ((chptr -> ch_outsource = tb_nm2struct (argv[ind]))
							== (Table *) NOTOK)
		    {
#ifdef DEBUG
			tai_error ("unknown source table", argv[ind], argc, argv);
#endif
			continue;
		    }
		    break;

		case CMDCLIDEST:
		    if ((chptr -> ch_indest = tb_nm2struct (argv[ind]))
							== (Table *) NOTOK)
		    {
#ifdef DEBUG
			tai_error ("unknown dest table", argv[ind], argc, argv);
#endif
			continue;
		    }
		    break;

		case CMDCLODEST:
		    if ((chptr -> ch_outdest = tb_nm2struct (argv[ind]))
							== (Table *) NOTOK)
		    {
#ifdef DEBUG
			tai_error ("unknown dest table", argv[ind], argc, argv);
#endif
			continue;
		    }
		    break;

		case CMDCKNOWN:
		    if ((chptr -> ch_known = tb_nm2struct (argv[ind]))
							== (Table *) NOTOK)
		    {
#ifdef DEBUG
			tai_error ("unknown known table", argv[ind], argc, argv);

#endif
			continue;
		    }
		    break;

		case CMDCCONFSTR:
		    chptr -> ch_confstr = argv [ind];
		    break;

		case CMDCAP:
#ifdef DEBUG
		    ll_log (logptr, LLOGFTR, "Ap style '%s'", argv[ind]);
#endif
		    switch (cmdsrch (argv[ind], 0, parmap))
		    {
			case CMDASAME:
			    chptr -> ch_apout = AP_SAME;
			    break;
			case CMDA733:
			    chptr -> ch_apout = AP_733;
			    break;
			case CMDA822:
			    chptr -> ch_apout = AP_822;
			    break;
			case CMDABIG:
			    chptr -> ch_apout |= AP_BIG;
			    break;
			case CMDANODOTS:
			    chptr -> ch_apout |= AP_NODOTS;
			    break;
			case CMDAJNT:
			     chptr -> ch_apout = (AP_733 | AP_BIG);
			     break;
			default:
#ifdef DEBUG
			    tai_error ("unknown address style", argv[ind], argc, argv);
#endif
			    continue;
		    }
		    break;

		case CMDCAUTH:
#ifdef DEBUG
		    ll_log (logptr, LLOGFTR, "Authorisation '%s'", argv[ind]);
#endif
		    switch (cmdsrch (argv[ind], 0, authmap))
		    {
			case CMDTFREE:
			    chptr -> ch_auth |= CH_FREE;
			    break;
			case CMDTINLOG:
			    chptr -> ch_auth |= CH_IN_LOG;
			    break;
			case CMDTINWARN:
			    chptr -> ch_auth |= CH_IN_WARN;
			    break;
			case CMDTINBLOCK:
			    chptr -> ch_auth |= CH_IN_BLOCK;
			    break;
			case CMDTOUTLOG:
			    chptr -> ch_auth |= CH_OUT_LOG;
			    break;
			case CMDTOUTWARN:
			    chptr -> ch_auth |= CH_OUT_WARN;
			    break;
			case CMDTOUTBLOCK:
			    chptr -> ch_auth |= CH_OUT_BLOCK;
			    break;
			case CMDTHAU:
			    chptr -> ch_auth |= CH_HAU;
			    break;
			case CMDTDHO:
			    chptr -> ch_auth |= CH_DHO;
			    break;

			default:
#ifdef DEBUG
			    tai_error ("unknown auth type", argv[ind], argc, argv);
#endif
			    continue;
		    }
		    break;


		default:
#ifdef DEBUG
		    tai_error ("unknown channel parm", argv[ind], argc, argv);
#endif
		    break;
	    }
	}
    }
    tbname = (tbind >= 0) ? argv[tbind] : chptr -> ch_name;
    if ((chptr -> ch_table = tb_nm2struct (tbname)) == (Table *) NOTOK)
    {                   /* table does not already exist */
#ifdef DEBUG
	ll_log (logptr, LLOGBTR, "'%s' unknown table", tbname);
#endif
	if (tb_numtables >= tb_maxtables)
	{
#ifdef DEBUG
	    tai_error ("exceed tb table size", tbname, argc, argv);
#endif
	    return (NOTOK);
	}
	else
	{
	    /*NOSTRICT*/
	    tb_list[tb_numtables++] = chptr -> ch_table =
		(Table *) malloc ((unsigned) (sizeof (Table)));
	    tb_list[tb_numtables] = (Table *) 0;
	    chptr -> ch_table -> tb_name =  tbname;
	    chptr -> ch_table -> tb_file =  tbname;
	    chptr -> ch_table -> tb_fp   =  (FILE *) NULL;
	    chptr -> ch_table -> tb_pos  =  0L;
	    chptr -> ch_table -> tb_show =
		(showind >= 0 ? argv[showind] : tbname);
	}
    }
    chptr -> ch_show = (showind >= 0 ? argv[showind] : chptr -> ch_name);

    for (ind = 0; ch_tbsrch[ind] != (Chan *) 0; ind++) {
#ifdef DEBUG
	ll_log (logptr, LLOGFTR, "ch(%d) '%s'",
	    ind, ch_tbsrch[ind] -> ch_name);
#endif
	if (ch_tbsrch[ind] == chptr)
	   continue;
	if (lexequ(ch_tbsrch[ind] -> ch_name, chptr -> ch_name)) {
#ifdef DEBUG
		ll_log (logptr, LLOGFTR, "Replacing channel");
#endif
		ch_tbsrch[ind] = chptr;
		ch_tbsrch[--ch_numchans] = (Chan *) 0;
	}
    }
#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "ch_tai (ret=YES)");
    ll_log (logptr, LLOGFTR, "nam=%s, tbl=%s, que=%s, acc=0%o, path=%s",
	    chptr -> ch_name, chptr -> ch_show, chptr -> ch_queue,
	    chptr -> ch_access, chptr -> ch_ppath);
    ll_log (logptr, LLOGFTR, "lnam=%s, ldomain=%s, host=%s",
	    chptr -> ch_lname, chptr -> ch_ldomain, chptr -> ch_host);
    ll_log (logptr, LLOGFTR, "login=%s, pol=%d, script=%s",
	    chptr -> ch_login, (int) (chptr -> ch_poltime), chptr -> ch_script);
    ll_log (logptr, LLOGFTR, "ipcs=%s, ap=%d",
	chptr -> ch_confstr, chptr -> ch_apout);
#endif

    return (YES);
}

/**/

/*      The following is one of those ugly buggers that has to play
 *      hairy games, just to do something simple.  It uses the full
 *      log-tailoring call, just to convert a logging-level specification
 *      into a number.  Yuch.  (Dave Crocker)
 */

tai_llev (argc, argv)            /* return logging level value */
     int argc;
     char *argv[];
{
    LLog faklog;
    char *fakargv[4];

#ifdef DEBUG
    ll_log (logptr, LLOGFTR, "tai_llev (%s : %d)", argv[0], argc);
#endif

    fakargv[0] =  "=";
    fakargv[1] =  "level";
    fakargv[2] =  (char *) 0;
    fakargv[3] =  (char *) 0;

    if (argc <= 0)
	return (NOTOK);

    fakargv[2] = argv[0];       /* assign the user's string as the 'value' */
    faklog.ll_level = LLOGFST;  /* something reasonable? */
#ifdef DEBUG
    ll_log (logptr, LLOGFTR, "tai_llev before (%d)", faklog.ll_level);
#endif
    if (tai_log (3, fakargv, &faklog) < 0)
    {
#ifdef DEBUG
	tai_error ("bad log level", argv[0], argc, argv);
#endif
	return (NOTOK);
    }
#ifdef DEBUG
    ll_log (logptr, LLOGFTR, "tai_llev after (%d)", faklog.ll_level);
#endif
    return (faklog.ll_level);
}
FTR, "Authorisation '%s'", argv[ind]);
#endif
		    switch (cmdsrch (argv[ind], 0, authmap))
		    {
			case CMDTFREE:
			    chptr -> ch_auth |= CH_FREE;
			    break;
			case CMDTINLOG:
			    chptr -> ch_auth |= CH_IN_LOG;
			    break;
			case CMDTINWARN:
			    chptr -> ch_auth |= CH_IN_WARN;
			    break;
			case CMDTINBLOCK:
			    chptr -> ch_auth |= CH_IN_BLOCK;
		mmdf/lib/mmdf/ml_send.c   444      0     12       20131  3671073154  10252 #include "util.h"
#include "mmdf.h"

/*
 *     MULTI-CHANNEL MEMO DISTRIBUTION FACILITY  (MMDF)
 *     
 *
 *     Copyright (C) 1979,1980,1981  University of Delaware
 *     
 *     Department of Electrical Engineering
 *     University of Delaware
 *     Newark, Delaware  19711
 *
 *     Phone:  (302) 738-1163
 *     
 *     
 *     This program module was developed as part of the University
 *     of Delaware's Multi-Channel Memo Distribution Facility (MMDF).
 *     
 *     Acquisition, use, and distribution of this module and its listings
 *     are subject restricted to the terms of a license agreement.
 *     Documents describing systems using this module must cite its source.
 *
 *     The above statements must be retained with all copies of this
 *     program and may not be removed without the consent of the
 *     University of Delaware.
 *     
 *
 *     version  -1    David H. Crocker    March   1979
 *     version   0    David H. Crocker    April   1980
 *     version  v7    David H. Crocker    May     1981
 *     version   1    David H. Crocker    October 1981
 *
 */
/* send a piece of mail, using Unix mail command        */

/*  Basic sequence is:
 *
 *          ml_init (YES, NO, "My Name", "The Subject);
 *          ml_adr ("destination address 1");
 *          ml_adr ("destination address 2");
 *          ...
 *          ml_aend ();
 *          ml_tinit ();
 *          ml_txt ("Some opening text");
 *          ml_txt ("maybe some more text");
 *          ml_file (file-stream-descriptor-of-file-to-include);
 *          if (ml_end (OK)) != OK)
 *          {   error-handling code }
 *
 *  Arguments that are to be defaulted should be zero.
 *
 *  ml_init's arguments specify a) whether return-mail (to the sender
 *  should be allowed, b) whether a Sender field should be used to
 *  specify the correct sender (contingent on next argument), c) text
 *  for the From field, and d) text for the Subject field.  If (b) is
 *  NO, then (c)'s text will be followed by the correct sender
 *  information.
 *
 *  ml_to and ml_cc are used to switch between To and CC addresses.
 *  Normally, only To addresses are used and, for this, no ml_to call is
 *  needed.
 *
 *  An "address" is whatever is valid for your system, as if you were
 *  typing it to the mail command.
 *
 *  You may freely mix ml_txt and ml_file calls.  They just append text
 *  to the message.  The text must contain its own newlines.
 *
 *  Note that a special version of the mail command is used, to handle all
 *  the extra arguments.  If its sources weren't included with the
 *  distribution of this file, you probably have a problem.
 */

#include <signal.h>

extern struct ll_struct   *logptr;
extern char *strdup ();
extern char *pathmail,           /* location of mail command           */
	    *nammail;
extern char *cmddfldir;
extern int numfds;

LOCVAR  FILE * ml_fp;             /* handle on output to mail command   */

LOCVAR int   ml_childid;           /* process id of mail child           */
LOCVAR short ml_curarg;            /* index of next argument             */

LOCVAR char *ml_argv[100];        /* arguments to pass to execv         */
/**/

ml_init (ret, sndr, from, sub)    /* set-up for using mail command      */
int     ret,                      /* allow return mail to sender?       */
	sndr;                     /* include Sender field?              */
char    *sub,                     /* subject line                       */
        *from;			  /* from field                         */
{
    ml_argv[0] = strdup ("mail");
    if (ret)                      /* allow return to sender             */
	ml_curarg = 1;
    else
    {                             /* disable return to sender           */
	ml_argv[1] = strdup ("-r");
	ml_curarg = 2;
    }

    if (from != 0)
    {                             /* user-specified From field          */
	ml_argv[ml_curarg++] = strdup ((sndr) ? "-f" : "-g");
				  /* f => Sender field needed           */
	ml_argv[ml_curarg++] = strdup (from);
    }

    if (sub != 0)
    {                             /* user-specified Subject field       */
	ml_argv[ml_curarg++] = strdup ("-s");
	ml_argv[ml_curarg++] = strdup (sub);
    }

    return (ml_to ());		  /* set-up for To: addresses           */
}
/**/


ml_to ()			  /* ready to specify To: address       */
{
    ml_argv[ml_curarg++] = strdup ("-t");
    return (OK);
}

ml_cc ()			  /* ready to specify CC: address       */
{
    ml_argv[ml_curarg++] = strdup ("-c");
    return (OK);
}

ml_adr (address)		  /* a destination for the mail         */
char    address[];
{
    ml_argv[ml_curarg++] = strdup (address);
    return (OK);
}

ml_aend ()			  /* end of addrs                       */
{
    ml_argv[ml_curarg] = 0;
    return (OK);
}
/**/

ml_tinit ()                     /* ready to send text                 */
{
    Pip    pipdes;              /* output pipe                        */
    char temppath[128];
    register short    c;

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "ml_send arguments:");
    for (c = 0; c < ml_curarg; c++)
	ll_log (logptr, LLOGBTR, "arg(%d) = '%s'", c, ml_argv[c]);
#endif

    if (pipe (pipdes.pipcall))  /* for output to mail                 */
	return (NOTOK);

    ml_childid = fork ();
    switch (ml_childid)
    {
	case NOTOK:               /* bad day all around                 */
	    (void) close (pipdes.pip.prd);
	    (void) close (pipdes.pip.pwrt);
	    return (NOTOK);

	case 0:                   /* this is the child                  */
	    (void) close (0);
	    dup (pipdes.pip.prd);
	    for (c = numfds-1; c > 0; c--)
		(void) close (c);
	    open ("/dev/null", 1);
				  /* give Submit a place to send msgs   */

	    getfpath (pathmail, cmddfldir, temppath);
	    execv (temppath, ml_argv);
	    exit (NOTOK);
    }				  /* BELOW HERE is the parent           */

    (void) close (pipdes.pip.prd);

    ml_fp = fdopen (pipdes.pip.pwrt, "w");
				  /* initialize the stdio for output    */
    while (--ml_curarg >= 0)      /* give the argument list back        */
	free (ml_argv[ml_curarg]);

    return (OK);
}
/**/

ml_file (infp)                    /* send a file to the message         */
register FILE  *infp;             /* input stdio file stream pointer    */
{
    register short len;
    char    buffer[BUFSIZ];

    if ((int) ml_fp == EOF || (int) ml_fp == NULL)
	return (OK);

    while ((len = fread (buffer, sizeof (char), sizeof(buffer), infp )) > 0)
	if (fwrite (buffer, sizeof (char), len, ml_fp) != len)
	{                         /* do raw i/o                         */
	    ml_end (NOTOK);
	    return (NOTOK);
	}

    if (len < OK)
    {
	ml_end (NOTOK);
	return (NOTOK);
    }
    return (OK);
}

ml_txt (text)                     /* some text for the body             */
char text[];                      /* the text                           */
{
    if (ml_fp == (FILE *) EOF || ml_fp == (FILE *) NULL)
	return (OK);

    fputs (text, ml_fp);

    if (ferror (ml_fp))
    {
	ml_end (NOTOK);
	return (NOTOK);
    }
    return (OK);
}
/**/


ml_end (type)			  /* message is finished                */
int     type;                     /* normal ending or not               */
{
    short     retval;               /* wait return value                  */

    switch (ml_childid)
    {
	case OK:
	case NOTOK:
	    return (OK);

	default:
	    switch ((int)ml_fp)
	    {
		case OK:
		case NOTOK:
		    break;

		default:
		    if (ferror (ml_fp) || type == NOTOK)
			kill (ml_childid, SIGKILL);
		    fclose (ml_fp);
		    ml_fp = OK;
	    }

	    retval = pgmwait (ml_childid);
	    ml_childid = OK;
	    return ((retval != 0) ? NOTOK : OK);
    }
}
/**/

ml_1adr (ret, sndr, from, sub, adr)
				  /* all set-up overhead in 1 proc      */
int     ret,                      /* allow return mail to sender?       */
	sndr;                     /* include Sender field?              */
char    *sub,                     /* subject line                       */
	*from,                    /* from field                         */
	*adr;                     /* the one address to receive msg     */
{

    if (ml_init (ret, sndr, from, sub) != OK ||
	    ml_adr (adr) != OK ||
	    ml_aend () != OK ||
	    ml_tinit () != OK)
	return (NOTOK);

    return (OK);
}
] = strdup (sub);
    }

    return (ml_to ());		  /* set-up for To: addresses           */
}
/**/


ml_to ()			  /* ready to specify To: address       */
{
    ml_argv[ml_curarg++] = strdup ("-t");
    return (OK);
}

ml_cc ()			  /* ready to specify CC: address       */
{
    ml_argv[ml_curarg++] = strdup ("-c");
    return (OK);
}

ml_adr (address)		  /* a destination for the mail         */
char    address[];
{
   mmdf/lib/mmdf/gen   555      0     12          57  3620510373   7102 make -f ../../Makefile.com -f Makefile.real $*
eturn (OK);
}

ml_txt (text)                     /* some text for the body             */
char text[];                      /* the text                           */
{
    if (ml_fp == (FILE *) EOF || ml_fp == (FILE *) NULL)
	return (OK);

    fputs (text, ml_fp);

    if (ferror (ml_fp))
    {
	ml_end (NOTOK);
	return (NOTOK);
    }
    return (OK);
}
/**/


ml_end (type)			  /* message is finished                */
int     type;                     /* normal mmdf/lib/mmdf/post_tai.c   444      0     12         566  3620510373  10416 #include "util.h"
#include "mmdf.h"

extern LLog *logptr;

/*      fake garbage collection of unused external tailor info  */

post_tai (argc, argv)
	int argc;
	char *argv[];
{
    char errline[LINESIZE];

    arg2vstr (0, sizeof errline, errline, argv);
    ll_log (logptr, LLOGGEN, "Unprocessed tailor entry '%s'", errline);

    return (YES);   /* we like everthing */
}
;
    }
    return (OK);
}
/**/


ml_end (type)			  /* message is finished                */
int     type;                     /* normal mmdf/lib/mmdf/rtn_proc.c   444      0     12       27250  3671073156  10472 #include "util.h"
#include "mmdf.h"

/*
 *     MULTI-CHANNEL MEMO DISTRIBUTION FACILITY  (MMDF)
 *
 *
 *     Copyright (C) 1979,1980,1981  University of Delaware
 *
 *     Department of Electrical Engineering
 *     University of Delaware
 *     Newark, Delaware  19711
 *
 *     Phone:  (302) 738-1163
 *
 *
 *     This program module was developed as part of the University
 *     of Delaware's Multi-Channel Memo Distribution Facility (MMDF).
 *
 *     Acquisition, use, and distribution of this module and its listings
 *     are subject restricted to the terms of a license agreement.
 *     Documents describing systems using this module must cite its source.
 *
 *     The above statements must be retained with all copies of this
 *     program and may not be removed without the consent of the
 *     University of Delaware.
 *
 *
 *     version  -1    David H. Crocker    March   1979
 *     version   0    David H. Crocker    April   1980
 *     version  v7    David H. Crocker    May     1981
 *     version   1    David H. Crocker    October 1981
 *
 */
/*      send return mail to author                              */

/*  Jun 81  D. Crocker    rtn_list had non-stdio code testing read-return
 *  Dec 81  D. Crocker    rtn_intro did not calculate time-in-queue right
 *  Aug 82  D. Crocker    have signature include hostname in name part
 */
#include "msg.h"
#include "adr_queue.h"

#define DOWARN  1                 /* indicate delay                     */
#define DOFAIL  0                 /* indicate final timeout failure     */

extern FILE    *mq_rfp;
#ifdef line
time_t   curtime;		/* keep lint happy - defined in other module */
#else
extern time_t   curtime;
#endif
extern struct ll_struct *logptr;
extern long     mq_lststrt;
extern int      warntime;        /* initial failure time */
extern int      failtime;        /* final failure time */
extern char     msg_sender[];
extern char     *mmdflogin;
extern char     *mquedir;
extern char     *aquedir;
extern char     *locname;               /* name of host */
extern char     *deadletter;            /* Where dead letters go */
extern char     *delim1;
extern char     *delim2;

LOCVAR char rtn_nosnd[] = "Failed mail",
	    rtn_yetsnd[] = "Waiting mail";

/**/


rtn_error (themsg, retadr, adrptr, reason) /* problem with an address   */
Msg *themsg;
char retadr[];                    /* return address                     */
struct adr_struct  *adrptr;       /* the address                        */
char    reason[];                 /* the problem                        */
{
    char    linebuf[2*LINESIZE],  /* one for each line printed          */
	    theaddr[LINESIZE];    /* canonical address                  */
    static char noreason[] = "(Reason not known)";

#ifdef DEBUG
    ll_log (logptr, LLOGPTR, "rtn_error (%o, '%s@%s', '%s')",
		msg_cite (themsg -> mg_stat), adrptr->adr_local,
		  adrptr->adr_host ? adrptr->adr_host : "(null)", reason);
#endif

    ll_close (logptr);

    sprintf (linebuf, "%s  (%s)", rtn_nosnd, themsg -> mg_mname);
    if (rtn_mlinit (linebuf, retadr) != OK)
	return (NOTOK);           /* set up for returning               */

    rtn_pnam (adrptr, theaddr);


    sprintf (linebuf, "%s\n'%s' %s:  '%s'\n\n",
		"    Your message could not be delivered to",
		theaddr, "for the following\nreason",
		(isstr (reason) ? reason : noreason));

    ml_txt (linebuf);

    if (msg_cite (themsg -> mg_stat))
	rtn_cite (themsg -> mg_mname);
    else
	rtn_all (themsg -> mg_mname);

    return (ml_end (OK));
}
/**/

rtn_warn (themsg, retadr)         /* notify of delay in delivery        */
    Msg *themsg;
    char *retadr;
{
    char   subject[32];
#ifdef DEBUG
    ll_log (logptr, LLOGPTR, "rtn_warn ()");
#endif

    sprintf (subject, "%s  (%s)", rtn_yetsnd, themsg -> mg_mname);
    if (rtn_mlinit (subject, retadr) != OK)
	return (NOTOK);           /* set up for returning               */

    rtn_intro (DOWARN, themsg);   /* do a warning introduction          */

    rtn_cite (themsg -> mg_mname); /* include message citation           */

    return (ml_end (OK));
}
/**/

rtn_time (themsg, retadr)         /* notify of delay in delivery        */
Msg *themsg;                      /* just cite the text                 */
char *retadr;
{
    char   subject[32];
#ifdef DEBUG
    ll_log (logptr, LLOGPTR, "rtn_time (%s)", themsg -> mg_mname);
#endif

    sprintf (subject, "%s  (%s)", rtn_nosnd, themsg -> mg_mname);
    if (rtn_mlinit (subject, retadr) != OK)
	return (NOTOK);           /* set up for returning               */

    rtn_intro (DOFAIL, themsg);   /* do a failure introduction          */

    if (msg_cite (themsg -> mg_stat)) /* return some part of the message    */
	rtn_cite (themsg -> mg_mname);
    else
	rtn_all (themsg -> mg_mname);

    return (ml_end (OK));
}
/**/

LOCFUN
    rtn_intro (dowarn, themsg)    /* print failure intro                */
    int dowarn;                   /* warning or full failure            */
    Msg *themsg;
{
    int days;
    int msghour;
    char    linebuf[LINESIZE];

    msghour = (int) ((curtime - themsg -> mg_time) / 3600);
				  /* number of hours already in queue   */
    days = (msghour + 23) / 24;
				  /* round up to nearest whole day      */
    sprintf (linebuf, "    After %d %s (%d hours), your message %s\nfully delivered.",
	    days, ((days == 1) ? "day" : "days"), msghour,
	    (dowarn ? "has not yet been" : "could not be"));
    ml_txt (linebuf);             /* introductory line                  */

    if (dowarn)
    {
	ml_txt ("  Attempts to deliver the message will continue\n");
    	days = ((failtime - msghour) + 23) / 24;
	sprintf (linebuf, "for %d more days.", days < 0 ? 0 : days);
	ml_txt (linebuf);             /* send apologetic nonsense  */
	ml_txt ("  No further action is required by you.\n\n");
	ml_txt ("    Delivery attempts are still pending for the following address(es):\n\n");
    }
    else
	ml_txt ("\n\n    It failed to be received by the following address(es):\n\n");

    rtn_list (FALSE);      /* list who hasn't got it yet         */

#ifdef NVRCOMPIL
	Due to popular outrage, I am removing the second list, which
	shows successful transmissions, after having shown unsuccessful
	(or not-yet-successfull) ones.  In the case of large address
	lists, this can be obnoxiously long.  The text is being retained
	for reference.  (dhc)

    ml_txt ("\n    A copy HAS been sent to the following addressee(s):\n\n");

    rtn_list (TRUE);      /* list who DID get it                */
#endif

    ml_txt ("\n    Problems usually are due to service interruptions at the receiving\n");
    ml_txt ("machine.  Less often, they are caused by the communication system.\n");
}
/**/

LOCFUN
	rtn_mlinit (subject, retadr)
char    *subject;
char    *retadr;
{
    extern char *sitesignature;
    char fromline[LINESIZE];

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "rtn_mlinit (%s)", subject);
#endif

    if (!isstr (retadr))     /* no return address                  */
    {
	printx ("no return address, ");
	ll_log (logptr, LLOGTMP, "no return address");
	return (NOTOK);
    }
    sprintf (fromline, "%s %s <%s@%s>",
		locname, sitesignature, mmdflogin, locname);
				  /* From field ignores uid             */
    if (ml_1adr (NO, YES, fromline, subject, retadr) != OK)
    {                             /* no return, make Sender field       */
	ll_err (logptr, LLOGTMP, "ml_1adr");
	return (NOTOK);
    }
    return (OK);
}
/**/

LOCFUN
	rtn_list (dodone)         /* list addresses not yet delivered   */
    short dodone;                 /* list the ones delivered            */
{
    struct adr_struct   adrbuf;
    int    numlist;               /* number of addresses listed         */
    char    linebuf[LINESIZE],
	    theaddr[LINESIZE];

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "rtn_list (%d)", dodone);
#endif

    for (numlist = 0, mq_setpos (0L);  mq_radr (&adrbuf) == OK; )
    {                             /* list all failed recipients         */
	rtn_pnam (&adrbuf, theaddr);

#ifdef DEBUG
	ll_log (logptr, LLOGFTR, "(%c) '%s'", adrbuf.adr_delv, theaddr);
#endif

	if ((dodone && adrbuf.adr_delv == ADR_DONE) ||
	    (!dodone && adrbuf.adr_delv != ADR_DONE)  )
	{                         /* format address and print it        */
	    sprintf (linebuf, "\t%s\n", theaddr);
#ifdef DEBUG
	    ll_log (logptr, LLOGFTR, "\tlisted");
#endif
	    ml_txt (linebuf);
	    numlist++;
	}
    }
    if (numlist == 0)
	ml_txt ("\t(none)\n");    /* empty list                         */

#ifdef DEBUG
    ll_log (logptr, LLOGFTR, "end of return list");
#endif
}
/**/

rtn_pnam (adrinfo, buffer)        /* print the name of an addressee     */
register struct adr_struct *adrinfo;
register char *buffer;
{
#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "rtn_pnam");
#endif

    if (adrinfo -> adr_host[0] == '\0')
	(void) strcpy (buffer, adrinfo -> adr_local);
    else                          /* have a host part                   */
	sprintf (buffer, "%s (host: %s) (queue: %s)", adrinfo -> adr_local,
		    adrinfo -> adr_host, adrinfo -> adr_que);
}


LOCFUN
	rtn_all (mname)
    char mname[];
{
    FILE *msg_tfp;
    char file[FILNSIZE];

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "rtn_all ()");
#endif


    ml_txt ("\n    Your message follows:\n\n");

    sprintf (file, "%s%s", mquedir, mname);
    msg_tfp = fopen (file, "r");

    ml_file (msg_tfp);

    fclose (msg_tfp);
}
/**/

rtn_cite (mname)
    char mname[];
{
    short     lines;
    char    linebuf[LINESIZE];
    FILE *msg_tfp;
    char file[FILNSIZE];

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "rtn_cite (%s)", mname);
#endif

    ml_txt ("\n    Your message begins as follows:\n\n");

    sprintf (file, "%s%s", mquedir, mname);
    if ((msg_tfp = fopen (file, "r")) == NULL)
    {
	ll_err (logptr, LLOGTMP, "can't open '%s' msg file '%s'",
		    mname, file);
	ml_txt ("       (Could not open message file.)\n");
	return;
    }

    while (fgets (linebuf, sizeof linebuf, msg_tfp) != NULL)
    {
	ml_txt (linebuf);         /* do headers                           */
	if (linebuf[0] == '\n')
	    break;
    }

    if (ferror (msg_tfp))
    {
	fclose (msg_tfp);
	ll_err (logptr, LLOGTMP, "Error reading text for return-to-msg_sender.");
	return;
    }

    if (!feof (msg_tfp))
    {
	for (lines = 3; --lines > 0 &&
		fgets (linebuf, sizeof linebuf, msg_tfp) != NULL;)
	{
	    if (linebuf[0] == '\n')
		lines++;          /* truly blank lines don't count      */
	    ml_txt (linebuf);
	}
	if (!feof (msg_tfp))       /* if more, give an elipses           */
	    ml_txt ("...\n");
    }
    fclose (msg_tfp);
}

/**/

dead_letter (msgname, reason)
char    *msgname;
char    *reason;
{
	FILE    *dfp;                   /* The deadletter file pointer */
	FILE    *mfp;                   /* Message related file pointers */
	char    file[FILNSIZE];
	char    buf[BUFSIZ];
	int     nread;
#ifdef DEBUG
	ll_log (logptr, LLOGFAT, "dead_letter (%s, %s)", msgname, reason);
#endif
	if ((dfp = fopen(deadletter, "a")) == NULL) {
		printx("can't open '%', mail lost.\n", deadletter);
#ifdef DEBUG
		ll_log (logptr, LLOGFAT, "can't open %s", deadletter);
#endif
		return;
	}

	fputs (delim1, dfp);          /* Message separator */
	fprintf (dfp, "Reason:   %s\n", reason);
	sprintf (file, "%s%s", mquedir, msgname);
	if ((mfp = fopen(file, "r")) == NULL) {
		fprintf (dfp, "Can't open message file %s\n", file);
	} else {
		while ((nread = fread(buf, 1, BUFSIZ, mfp)) > 0)
			fwrite(buf, 1, nread, dfp);
	}
	fputs ("-------End of deadletter, start address list-------\n", dfp);
	sprintf (file, "%s%s", aquedir, msgname);
	if ((mfp = fopen(file, "r")) == NULL) {
		fprintf (dfp, "Can't open address list file %s\n", file);
	} else {
		while ((nread = fread(buf, 1, BUFSIZ, mfp)) > 0)
			fwrite(buf, 1, nread, dfp);
	}
	fputs ("-------End of address list-------\n", dfp);
	fputs (delim2, dfp);          /* Message separator */
	fclose (dfp);
}
ne);
#endif

    for (numlist = 0, mq_setpos (0L);  mq_radr (&adrbuf) == OK; )
    {                             /* list all failed recipients         */
	rtn_pnam (&adrbuf, theaddr);

#ifdef DEBUG
	ll_log (logptr, LLOGFTR, "(%c) '%s'", adrbuf.adr_delv, theaddr);
#endif

	if ((dodone && adrbuf.adr_delv == ADR_DONE) ||
	    (!dodone && adrbuf.mmdf/lib/mmdf/fnput.c   444      0     12         670  3620510373   7724 #include "util.h"
#include "mmdf.h"

/* stdio output  of n characters, skipping nulls */

fnput (str, count, fp)
    register char *str;
    register int   count;
    register FILE *fp;
{
    char *base = str;

    for (; count-- > 0; str++) {
	if (isnull (*str)) {
	    if ((str - base) > 0)
		fwrite(base, sizeof(char), str - base, fp);
	    base = str+1;
	}
    }
    if ((str - base) > 0)
	fwrite(base, sizeof(char), str - base, fp);
}
	if ((dodone && adrbuf.adr_delv == ADR_DONE) ||
	    (!dodone && adrbuf.mmdf/lib/mmdf/getmailid.c   444      0     12        1303  3671073156  10552 /*
 *              G E T M A I L I D . C
 *
 *      This module is used to get the "mailid" for a user given
 *      his username.  Use of the mail-id tables is controlled
 *      by the variable mid_enable.
 */
#include "util.h"
#include "mmdf.h"
#include "ch.h"
#include <pwd.h>

extern LLog *logptr;
extern int mid_enable;

char *
getmailid (username)
char *username;
{
	static char buf[MAILIDSIZ+1];
	extern Table tb_users;

	if (mid_enable) {
		if (tb_k2val (&tb_users, TRUE, username, buf) != OK)
			return (NULL);
		buf[MAILIDSIZ] = '\0';          /* Paranoid */
	} else
		(void) strcpy (buf, username);

#ifdef DEBUG
	ll_log (logptr, LLOGFTR, "getmailid returns '%s'", buf);
#endif
	return (buf);
}
dif
	    ml_txt (linebuf);
	    numlist++;
	}
    }
    if (numlist == 0)
	ml_txt ("\t(none)\n");    /* empty list                         */

#ifdef DEBUG
    ll_log (logptr, LLOGFTR, "end of return list");
#endif
}
/**/

rtn_pnam (adrinfo, buffer)        /* print the name of an addressee     */
register struct admmdf/lib/mmdf/getpwmid.c   444      0     12        1536  3640046121  10427 /*
 *              G E T P W M I D . C
 *
 *      Given a mailid, get the /etc/passwd info for
 *      the user who is associated with that mailid.
 *      If mid_enable is nonzero, then the mailid cannot
 *      be interpreted as a login id, we must lookup
 *      the mailid in a mailid->loginid table.  Once
 *      we have the login id, we just do a getpwnam();
 *      For regular systems, then we assume that the
 *      mailid must be a login id and do a getpwnam();
 */
#include "util.h"
#include "mmdf.h"
#include "ch.h"
#include <pwd.h>

struct passwd *
getpwmid (mailid)
char *mailid;
{
	extern struct passwd *gpwlnam();
	extern Table tb_mailids;
	extern int mid_enable;
	char    buf[ADDRSIZE];

	if (mid_enable) {
		if (tb_k2val (&tb_mailids, TRUE, mailid, buf) != OK)
			return (NULL);
		return (gpwlnam(buf));
	} else
		return (gpwlnam(mailid));
}
o a getpwnam();
 *      For regular systems, then we assume that the
 *      mailid must be a login id and do a getpwnam();
 */
#include "util.h"
#include "mmdf.hmmdf/lib/mmdf/cache.c   444      0     12        5016  3620510374   7653 #include "util.h"
#include "mmdf.h"
#include "ch.h"

/* cache some information */

extern	Llog	*logptr;
extern	char	*strdup();
extern	char	*malloc();

ca_add (cachep, hostid, value, ttl)	/* add entry to cache */
Cache	**cachep;
register char	*hostid;
int	value;
time_t	ttl;			/* time to live */
{
	register Cache *cap, *pcap;
	time_t	timenow;

#ifdef DEBUG
	ll_log (logptr, LLOGGEN, "cache add (%s %o %d)",
			hostid, value, ttl);
#endif
	cap = *cachep;
	pcap = cap;
	time(&timenow);
	for (; cap != NULL; ) {
		if (lexequ (cap->ca_hostid, hostid)) {
			/* Already in the list, reset values */
			cap->ca_value = value;
			cap->ca_expire = timenow + ttl;
#ifdef DEBUG
 			ll_log (logptr, LLOGPTR, "cache reset (%s, %s, %d)",
				hostid, rp_valstr (value), ttl);
#endif
			return (OK);
		} else if (cap->ca_expire <= timenow) {
			register Cache *tcap = cap;

			/* Look for deadwood, and expired record */
#ifdef DEBUG
 			ll_log (logptr, LLOGGEN, "cache clear (%s)",
				cap->ca_hostid);
#endif
			if (cap == pcap)
				*cachep = pcap = cap = cap->ca_next;
			else
				pcap->ca_next = cap = cap->ca_next;
			free (tcap->ca_hostid);
			free (tcap);
		} else {
			pcap = cap;
			cap = cap->ca_next;
		}
	}

	/* Not in the list, so we must add */
	/*NOSTRICT*/
	if ((cap = (Cache *)malloc(sizeof (struct cache))) == NULL)
		return (NOTOK);
	cap->ca_hostid = strdup (hostid);
	cap->ca_expire = timenow + ttl;
	cap->ca_value = value;
	cap->ca_next = *cachep;		/* Insert at head */
	*cachep = cap;
	return (OK);
}
/**/

ca_find (cachep, hostid)	/* is this host in the cache */
Cache	**cachep;
register char	*hostid;	/* key to data in cache */
{
	register Cache *cap, *pcap;
	time_t	timenow;

#ifdef DEBUG
	ll_log (logptr, LLOGBTR, "ca_find (%s)", hostid);
#endif

	cap = *cachep;
	pcap = cap;
	time(&timenow);
	for (; cap != NULL; ) {
		/* Look for deadwood */
		if (cap->ca_expire <= timenow) {
			register Cache *tcap = cap;

			/* Expired record */
#ifdef DEBUG
 			ll_log (logptr, LLOGGEN, "cache clear (%s)",
				cap->ca_hostid);
#endif
			if (cap == pcap)
				*cachep = pcap = cap = cap->ca_next;
			else
				pcap->ca_next = cap = cap->ca_next;
			free (tcap->ca_hostid);
			free (tcap);
		} else if (lexequ (cap->ca_hostid, hostid)) {
			/* Bingo! */
#ifdef DEBUG
 			ll_log (logptr, LLOGPTR, "cache hit (%s)", hostid);
#endif
			if (cap != pcap) {
				/* Move to the head of the class */
				pcap->ca_next = cap->ca_next;
				cap->ca_next = *cachep;
				*cachep = cap;
			}
			return (cap->ca_value);
		} else {
			pcap = cap;
			cap = cap->ca_next;
		}
	}
	return (0);
}
turn;
    }

    if (!feof (msg_tfp))
    {
	for (lines = 3; --lines > 0 &&
		fgets (linebuf, sizeof linebuf, msg_tfp) != NULL;)
	{
	    if (linebuf[0] == '\n')
		lines++;          /* truly blank lines don't count      */
	    ml_txt (linebuf);
	}
	if (!feof (msg_tfp))       /* if more, give an elipses           */
	    ml_txt ("...\n");
    }
    fclose (msg_tfp);
}

/**/

dead_letter (msgname, reason)
char    *msgname;
char    *reason;
{
	FILE    *dfp;                   /* The deadletter fimmdf/lib/mmdf/amp_hdr.c   444      0     12       31631  3671073160  10247 /*  Translate off-net address */

/*  Apr 81  K. Harrenstein  Initially written for SRI
 *  Apr 81  J. Pickens      Defensive initialization, blank line after header
 *  May 81  A. Knutsen      May have done more SRI Hacking
 *  Jun 81  D. Crocker      Changed indenting of data to preserve same
 *                          offset (after the colon) as was in input line
 *                          minor additional cleanup
 *  Sep 81  A. Knutsen      +itmlen >= MAXLEN, rather than >
 *  Apr 82  D. Crocker      reset 'fail' at each call to amp_hdr
 *  Jan 83  D. Kingston     fixed handling of io with ap_1adr().
 *  Jun 83  D. Kingston     Changed handling of bad addresses.
 */


#include "util.h"
#include "mmdf.h"
#include "ap.h"
#include "ap_lex.h"
#include "ch.h"

extern LLog     *logptr;
extern char     *locname;
extern char     *locdomain;
extern char     *locmachine;
extern int      ap_outtype;        /* 733 or 822 */
extern int      ap_perlev;
extern int      getach ();
extern AP_ptr   findap ();

#if DEBUG > 1
extern char *namtab[], *typtab[];	/* In ap_1adr.c */
extern char debug;			/* In ap_1adr.c */

char tdebug;				/* Top-level debug flag */
#endif

#define MAXCOL 78                 /* # of col positions to use */
#define MAXLEN 20                 /* Max length of field name */

LOCVAR int     pcol;              /* Printing col position */
LOCVAR int     nadrs;             /* # of addresses hacked */
LOCVAR int     nonempty;          /* true if we have printed something on
				   * on this line besides the itemname and
				   * and indent whitespace
				   */
LOCVAR int     savech;            /* Char to re-read, else -1 */
LOCVAR char    aread;             /* Set when reading address line in header */
LOCVAR int     lastch;            /* Last char read                     */
LOCVAR int     gotfrom;           /* have we got a source yet?          */

LOCVAR short   itmind;            /* indent for continuation lines      */
LOCVAR short   itmlen;
LOCVAR char    itmbuf[MAXLEN];
LOCVAR char    *itmptr;           /* normally == itmbuf except for Resent */

LOCVAR char   *afldtab[] =
{                                 /* Fields which contain addresses to
				     translate */
    "To",
    "CC",
    "bcc",
    "From",
    "Sender",
    "Reply-to",
    "Resent-From",
    "Resent-Sender",
    "Resent-To",
    "Resent-Cc",
    "Resent-Bcc",
/* Archaic */
    "Resent-By",
    "Remailed-From",
    "Remailed-To",
    "Remailed-By",
    "Redistributed-From",
    "Redistributed-To",
    "Redistributed-By",
    0,
};

LOCVAR FILE * xin, *out;
LOCVAR long curpos;               /* current char position in file      */
				  /* needed because ftell not in v6     */
long
	amp_hdr (chanptr, ain, aout)
Chan *chanptr;                    /* channel that does not need mapping */
FILE *ain, *aout;
{
    char fromsite[FILNSIZE];
    char *ourdomain;
    register AP_ptr aptmp;
    int     amp_fail;
    int     res;

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "amp_hdr ()");
#endif

    xin = ain;
    out = aout;                   /* Set global streams */
    curpos = 0L;                  /* reset the file position hack */
    gotfrom = FALSE;

    if (chanptr == (Chan *) 0) {
	(void) strcpy (fromsite, locname);
	ourdomain = locdomain;
    } else {
	(void) strcpy (fromsite, chanptr -> ch_lname);
	ourdomain = chanptr -> ch_ldomain;
    }

#ifdef DEBUG
    ll_log (logptr, LLOGFTR, "otyp=%d, from='%s.%s'",
				ap_outtype, fromsite, ourdomain);
#endif
    savech = lastch = -1;         /*  defensive initialization */
    aread = FALSE;

    while (getaitm ())            /* Scan header lines */
    {
#if DEBUG > 1
	if (tdebug)
	    fprintf (stderr, "Parse: ");
#endif
	ap_clear();

	amp_fail = FALSE;
	for (aread = TRUE, out_init (itmlen); ; )
	{
	    if (ap_pinit (getach) == (AP_ptr) NOTOK)
	    {                     /* problem during parse               */
		ll_log (logptr, LLOGTMP, "problem parsing message");
		fprintf (out, "PROBLEM PARSING MESSAGE (%s): ;\n", fromsite);
		fflush (out);
		return (NOTOK);
	    }

	    res = ap_1adr ();     /* Parse one addr */
	    if (res == DONE)      /* DONE */
	    {
#if DEBUG > 1
		if (tdebug)
		    fprintf (stderr, ", DONE.");
		ll_log (logptr, LLOGBTR, "amp 1adr done");
#endif
		break;
	    }
	    else if (res == NOTOK)
	    {
		amp_fail = TRUE;      /* pass the garbage and generate warning */
#if DEBUG > 1
		ll_log (logptr, LLOGBTR, "amp reject '%s'", namtab[ap_llex]);
		if (tdebug)
		    fprintf (stderr, "\nReject: %s\n", namtab[ap_llex]);
#endif
	    }
	    else                  /* process the good part */
	    {
#if DEBUG > 1
		if (tdebug) {
		    fprintf (stderr, "\n\nAccept\n");
		    pretty (ap_pstrt);
		    fprintf (stderr, "\n\n");
		}
#endif

/*  Here do the check to find sending site, for use with any address that
 *  lacks a host spec.  This won't work for multi-level addressing (should
 *  copy ALL site specs seen) but we can worry about that later.  The
 *  current algorithm (JRP) is to pick up the first valid hostname field
 *  from either from:  or sender:, whichever comes first.  The algorithm
 *  really needs two passes, which it doesn't currently have..  If, for
 *  example, a to: field comes before the from: field, then patching will
 *  not occur.  Another problem happens when from: comes after sender:, and
 *  from: has no host.  In this case from: gets the host of the sender.
 *  Despite these deficiencies, the mapping should work most of the time.
 *
 *  DPK@BRL, 25 Oct 84
 *  We now properly handle the use of resent lines.  When you get a resent
 *  line, it will be necessary stop using old ourdomain, instead we need to
 *  pickup the sending send from the resent-from line and use that site
 *  to fix resent-{to,cc} lines that lack domains.
 */
		if (prefix("resent-", itmbuf)) {
			itmptr += 7; gotfrom = FALSE;
		} else if (prefix("remailed-", itmbuf)) {
			itmptr += 9; gotfrom = FALSE;
		} else if (prefix("redistributed-", itmbuf)) {
			itmptr += 14; gotfrom = FALSE;
		}  /* Fall into ... */

		if ((!gotfrom) &&
			(lexequ (itmptr, "from") || lexequ (itmptr, "sender")))
		{
#if DEBUG > 1
		    if (tdebug)
			fprintf (stderr, "Checking fromsite: \"");
#endif
		    if (aptmp = findap (ap_pstrt, APV_DOMN))
		    {
			gotfrom = TRUE;
			(void) strcpy (fromsite, aptmp -> ap_obvalue);
			ourdomain = (char *) 0;
				/* SEK ap_normalize will add a suitable */
				/* domain if not already there          */
		    }

#if DEBUG > 1
		    if (tdebug)
			fprintf (stderr, "%s\"\n", fromsite);
#endif
		}
	    }

	    res = out_adr (chanptr, fromsite, ourdomain, ap_pstrt);

	    ap_sqdelete (ap_pstrt, (AP_ptr) 0);
	    ap_free (ap_pstrt);   /* delete full string                 */
	    ap_pstrt = (AP_ptr) 0;

	    if(res == MAYBE)
		return( (long)res);
	}

	(void) putc ('\n', out);
	if (ap_perlev) {        /* If still nested */
#if DEBUG > 1
	    if (tdebug)
		fprintf (stderr, "Still nested at level %d!\n", ap_perlev);
#endif
	    ll_log (logptr, LLOGTMP, "amp_hdr nested, level %d", ap_perlev);
	    amp_fail++;
	}

	if (amp_fail)
#ifndef UCL
	    fprintf (out, "MMDF-Warning:  Parse error in original version of preceding line at %s.%s\n", locname, locdomain);
#else
	    fprintf (out, "MMDF-Warning:  Parse error in original version of preceding line at %s.%s.%s\n", locmachine, locname, locdomain);
#endif
	aread = FALSE;
    }

    (void) putc ('\n', out);             /*  Put a blank line after header  */
    fflush (out);
#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "amp parse done");
#endif
    switch ((int)ap_pstrt)
    {
	case OK:
	case NOTOK:
	    break;

	default:
	    ap_sqdelete (ap_pstrt, (AP_ptr) 0);
	    ap_free (ap_pstrt);      /* delete full string         */
	    ap_pstrt = (AP_ptr) 0;
    }

    return ((ferror(out) || ferror(xin)) ? NOTOK : curpos);
}

int
	getach ()
{
    register int    c;

    if (savech >= 0)
    {
	if(aread && lastch == '\n')
	    return (lastch = 0);        /* Double end of file for addrlist */
	c = savech;
	savech = -1;
	return (lastch = c);
    }
    if (feof (xin))     /* in case we are called after getting eof */
	return (0);
    c = getc (xin);
    if (feof (xin))     /* ended now */
	return (0);

    curpos++;
    if (aread)
	if (lastch == '\n' && !(c == ' ' || c == '\t'))
	{
#if DEBUG > 1
	    ll_log (logptr, LLOGFTR, "getach end of field");
#endif
	    savech = c;           /* Save for copying */
	    return (0);
	}
    return (lastch = c);          /*  xfer one char               */
}

/* Return true when starting to read from an address field.
 *      itmbuf and itmlen will be set.
 * Return false when ready to copy message text.
 */
getaitm ()
{
    register int    c;
    register char  *cp,
		  **tabv;

#ifdef DEBUG
    ll_log (logptr, LLOGFTR, "getaitm ()");
#endif

again: 
    itmptr = itmbuf;            /* RESET possible fiddling above */
    for (itmlen = 0, cp = itmbuf, itmind = 0; ;)
    {                             /* Collect field identifier */
	if ((c = getach ()) == (char ) -1)
	    goto err;

	switch (c)
	{
	     case '\n':
		if (itmlen != 0)
		    break;
	     case '\0':
		return (FALSE);
	}

	(void) putc (c, out);            /* Continuous "echo" */

	switch (c)
	{
	    case ':':             /* Field name collected */
		if (itmlen == 0)
		    goto err;
		*cp = 0;
#ifdef DEBUG
		ll_log (logptr, LLOGFTR, "hdr: '%s'", itmbuf);
#endif
		for (itmind = itmlen + 1;
			isspace (c = getach ()) && c != '\n'; itmind++)
		    (void) putc (c, out);    /* echo the initial white space */

		savech = c;       /* re-read the non-space */
		lastch = 0;

		/*  See if addr-type  */
		for (tabv = afldtab; *tabv; tabv++)
		    if (lexequ (*tabv, itmbuf)) {
			if (savech == '\n') {
			    (void) putc (' ', out);
			    itmind++;
			}
			return (TRUE);
		    }

		goto flsh;

	    case ' ':
		if (itmlen != 0)
		    continue;
		goto err;         /* this should never happen */

	    default:
		if (iscntrl (c))  /* not sure why, but... (dhc) */
		    goto err;

		*cp++ = c;

		if (++itmlen >= MAXLEN)   /* Fieldname too big, so just */
		    goto flsh;            /* flush and restart */
	}
    }

flsh:
    aread = TRUE;
    while ((c = getach ()) != 0)
	(void) putc (c, out);
    aread = FALSE;

    goto again;

err:
#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "amp getfield fail on char '%c'", c);
#endif
#if DEBUG > 1
    if (tdebug)
	fprintf (stderr, "Getfield failure, char '%c'\n", c);
#endif
    return (0);
}
/*
 * Search parse list for specific object type, returns ptr thereto.
 * Works if given zero pointer, so findap(ap->ap_chain, FOO) (ie to
 * find next occurrence of FOO) won't blow up even if
 * already at end of list.
 */

AP_ptr
	findap (aap, nval)
    AP_ptr aap;
{
    register AP_ptr ap;

    for (ap = aap; ap && (ap -> ap_obtype != APV_NIL); ap = ap -> ap_chain)
	if (ap -> ap_obtype == nval)
	    return (ap);
	else
	    if (ap -> ap_ptrtype == APP_NIL)
		break;
    return ((AP_ptr) 0);
}

#if DEBUG > 1
pretty (ap)
register AP_ptr ap;
{
    register int    depth;

    ll_log (logptr, LLOGFTR, "pretty ()");

    for (depth = 1;; ap = ap -> ap_chain)
    {
	switch (ap -> ap_obtype)
	{
	    case APV_NIL: 
		ll_log (logptr, LLOGBTR, "end on null ap_obvalue");
		fprintf (stderr, "End on null ap_obvalue\n");
		return;
	    case APV_EGRP: 
	    case APV_EPER: 
		depth -= 2;
		break;
	}

	ll_log (logptr, LLOGFTR, "%.*s%-9s%s%s",
		depth, "          ", typtab[ap -> ap_obtype],
		ap -> ap_obvalue,
		(ap -> ap_ptrtype == APP_NXT) ? "\t(NXT)" : "");
	fprintf (stderr, "%.*s%-9s%s%s\n",
		depth, "          ", typtab[ap -> ap_obtype],
		ap -> ap_obvalue,
		(ap -> ap_ptrtype == APP_NXT) ? "\t(NXT)" : "");

	switch (ap -> ap_obtype)
	{
	    case APV_NGRP: 
	    case APV_NPER: 
		depth += 2;
	}
	if (ap -> ap_ptrtype == APP_NIL)
	{
	    ll_log (logptr, LLOGBTR, "end on null pointer");
	    fprintf (stderr, "End on null pointer\n");
	    break;
	}
    }
}
#endif



out_init (len)
short len;
{
#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "out_init ()");
#endif

    nadrs = 0;
    nonempty = FALSE;
    pcol = len + 1;
}

out_adr (chanptr, dflsite, dfldomain, ap)
Chan *chanptr;
char *dflsite, *dfldomain;
register AP_ptr ap;
{
    int len;
    char *addrp;
    AP_ptr lrval;

#ifdef DEBUG
    ll_log (logptr, LLOGFTR, "out_adr ()");
#endif

    if (ap -> ap_obtype == APV_NIL)
	return(OK);               /* Ignore null stuff */

    if (nadrs != 0)               /* not the first address on the line  */
    {
	fputs (", ", out);
	pcol += 2;
    }

    ap = ap_normalize (dflsite, dfldomain, ap, chanptr);
    if(ap == (AP_ptr)MAYBE)
	return(MAYBE);
#if DEBUG > 1
    if (tdebug)
	pretty (ap);
#endif

    lrval = ap_t2s (ap, &addrp);       /* format it */
    if(lrval == (AP_ptr)MAYBE)
	return(MAYBE);
    else if(lrval == (AP_ptr)NOTOK)
	return(NOTOK);
#ifdef DEBUG
    ll_log (logptr, LLOGFTR, "out: '%s'", addrp);
#endif

    if ((len = strlen (addrp)) > 0)
    {                               /* print it */
	if ((pcol += len) >= MAXCOL && nonempty)
	{
	    pcol = itmind + len;
	    fprintf (out, "\n%.*s", itmind, "                    ");
	}

	fputs (addrp, out);
	nadrs++;
	nonempty = TRUE;
    }
    free (addrp);
    return(OK);
}
se '\0':
		return (FALSE);
	}

	(void) putc (c, out);            /* Continuous "echo" */

	switch (c)
	mmdf/lib/mmdf/Makefile.real   444      0     12       12140  3652616275  11056 #
#   Makefile for general MMDF utility routines
#
MODULES =   phs_note aliasfetch \
	    ml_send qu_rdmail qu_io qu_fakrply \
	    hd_format amp_hdr rtn_proc \
	    mq_rdmail mm_rdmail mm_wtmail mm_io \
	    ch_struct getmailid getpwmid \
	    interrupt fnput \
	    mmdf_init mmdf_fdinit mm_tai uip_tai post_tai \
	    err_abrt rp_valstr logptr cache ch_llinit alias

OBJECTS =   phs_note.o aliasfetch.o \
	    ml_send.o qu_rdmail.o qu_io.o qu_fakrply.o \
	    hd_format.o amp_hdr.o rtn_proc.o \
	    mq_rdmail.o mm_rdmail.o mm_wtmail.o mm_io.o \
	    ch_struct.o getmailid.o getpwmid.o \
	    interrupt.o fnput.o \
	    mmdf_init.o mmdf_fdinit.o mm_tai.o uip_tai.o post_tai.o \
	    err_abrt.o rp_valstr.o logptr.o cache.o \
	    ch_llinit.o conf.o chan.o alias.o

COBJECTS =  phs_note.c aliasfetch.c \
	    ml_send.c qu_rdmail.c qu_io.c qu_fakrply.c \
	    hd_format.c amp_hdr.c rtn_proc.c \
	    mq_rdmail.c mm_rdmail.c mm_wtmail.c mm_io.c \
	    ch_struct.c getmailid.c getpwmid.c \
	    interrupt.c fnput.c \
	    mmdf_init.c mmdf_fdinit.c mm_tai.c uip_tai.c post_tai.c \
	    err_abrt.c rp_valstr.c logptr.c cache.c \
	    ch_llinit.c ../../conf/$(HOST)/conf.c ../../conf/$(HOST)/chan.c \
	    alias.c

real-default: ../mmdf-made
../mmdf-made:	$(OBJECTS)
	$(AR) r ../libmmdf.a $(OBJECTS)
	-touch ../mmdf-made
	-@echo "mmdf routines built normally"

#
#  Special case dependencies
#
conf.o: ../../conf/$(HOST)/conf.c ../../h/util.h ../../h/mmdf.h \
			   ../../h/ap_norm.h
	$(CC) -c $(CFLAGS) ../../conf/$(HOST)/conf.c

chan.o: ../../conf/$(HOST)/chan.c ../../h/util.h ../../h/mmdf.h \
			   ../../h/ch.h ../../h/dm.h
	$(CC) -c $(CFLAGS) ../../conf/$(HOST)/chan.c


lint:
	$(LINT) -Cmmdf $(LFLAGS) $(COBJECTS)

clean:
	-rm -f *.o x.c makedep eddep make.out errs cachetest llib-lmmdf*


# DO NOT DELETE THIS LINE -- make depend uses it

phs_note.o: phs_note.c
phs_note.o: ../../h/util.h
phs_note.o: ../../h/mmdf.h
phs_note.o: /usr/include/sys/stat.h
phs_note.o: ../../h/ch.h
phs_note.o: ../../h/phs.h
aliasfetch.o: aliasfetch.c
aliasfetch.o: ../../h/util.h
aliasfetch.o: ../../h/mmdf.h
aliasfetch.o: ../../h/ch.h
ml_send.o: ml_send.c
ml_send.o: ../../h/util.h
ml_send.o: ../../h/mmdf.h
ml_send.o: /usr/include/signal.h
qu_rdmail.o: qu_rdmail.c
qu_rdmail.o: ../../h/util.h
qu_rdmail.o: ../../h/mmdf.h
qu_rdmail.o: ../../h/adr_queue.h
qu_io.o: qu_io.c
qu_io.o: ../../h/util.h
qu_io.o: ../../h/mmdf.h
qu_io.o: /usr/include/sys/stat.h
qu_io.o: ../../h/adr_queue.h
qu_io.o: ../../h/ch.h
qu_io.o: ../../h/phs.h
qu_io.o: ../../h/ap.h
qu_fakrply.o: qu_fakrply.c
qu_fakrply.o: ../../h/util.h
qu_fakrply.o: ../../h/mmdf.h
qu_fakrply.o: ../../h/chan.h
hd_format.o: hd_format.c
hd_format.o: ../../h/util.h
hd_format.o: ../../h/mmdf.h
hd_format.o: ../../h/ch.h
amp_hdr.o: amp_hdr.c
amp_hdr.o: ../../h/util.h
amp_hdr.o: ../../h/mmdf.h
amp_hdr.o: ../../h/ap.h
amp_hdr.o: ../../h/ap_lex.h
amp_hdr.o: ../../h/ch.h
rtn_proc.o: rtn_proc.c
rtn_proc.o: ../../h/util.h
rtn_proc.o: ../../h/mmdf.h
rtn_proc.o: ../../h/msg.h
rtn_proc.o: ../../h/adr_queue.h
mq_rdmail.o: mq_rdmail.c
mq_rdmail.o: ../../h/util.h
mq_rdmail.o: ../../h/mmdf.h
mq_rdmail.o: /usr/include/signal.h
mq_rdmail.o: /usr/include/sys/stat.h
mq_rdmail.o: ../../h/ch.h
mq_rdmail.o: ../../h/msg.h
mq_rdmail.o: ../../h/adr_queue.h
mm_rdmail.o: mm_rdmail.c
mm_rdmail.o: ../../h/util.h
mm_rdmail.o: ../../h/mmdf.h
mm_wtmail.o: mm_wtmail.c
mm_wtmail.o: ../../h/util.h
mm_wtmail.o: ../../h/mmdf.h
mm_io.o: mm_io.c
mm_io.o: ../../h/nexec.h
mm_io.o: ../../h/util.h
mm_io.o: ../../h/mmdf.h
ch_struct.o: ch_struct.c
ch_struct.o: ../../h/util.h
ch_struct.o: ../../h/mmdf.h
ch_struct.o: ../../h/ch.h
getmailid.o: getmailid.c
getmailid.o: ../../h/util.h
getmailid.o: ../../h/mmdf.h
getmailid.o: ../../h/ch.h
getmailid.o: /usr/include/pwd.h
getpwmid.o: getpwmid.c
getpwmid.o: ../../h/util.h
getpwmid.o: ../../h/mmdf.h
getpwmid.o: ../../h/ch.h
getpwmid.o: /usr/include/pwd.h
interrupt.o: interrupt.c
interrupt.o: ../../h/util.h
interrupt.o: ../../h/mmdf.h
interrupt.o: /usr/include/signal.h
fnput.o: fnput.c
fnput.o: ../../h/util.h
fnput.o: ../../h/mmdf.h
mmdf_init.o: mmdf_init.c
mmdf_init.o: ../../h/util.h
mmdf_init.o: ../../h/mmdf.h
mmdf_init.o: ../../h/ch.h
mmdf_fdinit.o: mmdf_fdinit.c
mmdf_fdinit.o: ../../h/util.h
mmdf_fdinit.o: ../../h/mmdf.h
mm_tai.o: mm_tai.c
mm_tai.o: ../../h/util.h
mm_tai.o: ../../h/mmdf.h
mm_tai.o: ../../h/cmd.h
mm_tai.o: ../../h/ch.h
mm_tai.o: ../../h/ap.h
mm_tai.o: ../../h/dm.h
uip_tai.o: uip_tai.c
uip_tai.o: ../../h/util.h
uip_tai.o: ../../h/mmdf.h
post_tai.o: post_tai.c
post_tai.o: ../../h/util.h
post_tai.o: ../../h/mmdf.h
err_abrt.o: err_abrt.c
err_abrt.o: ../../h/util.h
err_abrt.o: ../../h/mmdf.h
rp_valstr.o: rp_valstr.c
rp_valstr.o: ../../h/util.h
rp_valstr.o: ../../h/mmdf.h
logptr.o: logptr.c
logptr.o: ../../h/util.h
logptr.o: ../../h/mmdf.h
cache.o: cache.c
cache.o: ../../h/util.h
cache.o: ../../h/mmdf.h
cache.o: ../../h/ch.h
ch_llinit.o: ch_llinit.c
ch_llinit.o: ../../h/util.h
ch_llinit.o: ../../h/mmdf.h
ch_llinit.o: ../../h/ch.h
alias.o: alias.c
alias.o: /usr/include/stdio.h
alias.o: ../../h/util.h
alias.o: ../../h/mmdf.h
# DEPENDENCIES MUST END AT END OF FILE
# IF YOU PUT STUFF HERE IT WILL GO AWAY
# see make depend above
pe == nval)
	    return (ap);
	else
	    if (ap -> ap_ptrtype == APP_NIL)
		break;
    return ((AP_ptr) 0);
}

#if DEBUG > 1
pretty (ap)
register AP_ptr ap;
{
    register int    depth;

    ll_log (logptr, LLOGFTR, "pretty ()");

    for (depth = 1;; ap = ap -> ap_chain)
    {
	switch (ap -> ap_obtype)
	{
	    case APV_NIL: 
		ll_log (logptr, LLOGBTR, "end on null ap_obvalue");
		fprintf (stderr, "End on null apmmdf/lib/mmdf/ch_llinit.c   444      0     12        1213  3664425140  10554 /*
 *	ch_llinit()
 *
 *	Initialize per channel logging.  Called after mmdf_init and
 *	determination of current channel by each channel program
 *	to activate channel specific logging.
 */
#include "util.h"
#include "mmdf.h"
#include "ch.h"

extern	Llog	*logptr;
extern	char	*logdfldir;
extern	char	*dupfpath();

ch_llinit (curchan)
Chan *curchan;
{
	logptr -> ll_file = curchan -> ch_logfile;
	if (logdfldir != (char *) 0)
		logptr -> ll_file = dupfpath (logptr -> ll_file, logdfldir);
	if (logptr -> ll_level < curchan -> ch_loglevel)
		logptr -> ll_level = curchan -> ch_loglevel;
	
	ll_close (logptr);	/* Will cause new values to take effect */
}

hd_format.o: ../../h/mmdf.h
hd_format.o: ../../h/ch.h
amp_hdr.o: amp_hdr.c
amp_hdr.o: ../../h/util.h
amp_hdr.o: ../../h/mmdf.h
amp_hdr.o: ../../h/ap.h
amp_hdr.o: ../../h/ap_lex.h
amp_hdr.o: ../../h/ch.h
rtn_proc.o: rtn_proc.c
rtn_proc.o: ../../h/util.h
rtn_proc.o: ../../h/mmdf.h
rtn_proc.o: ../../h/msg.h
rtn_proc.o: ../../h/adr_queue.h
mq_rdmail.o: mq_rdmail.c
mq_rdmailmmdf/lib/mmdf/mmdf_fdinit.c   444      0     12         710  3620510375  11045 #include "util.h"
#include "mmdf.h"

extern	char *malloc();

int *regfdary;
int numfds = 16;	/* init to 16 as a failsafe value, -DPK- */

mmdf_fdinit()
{
    register int i;

#ifdef V4_2BSD
    numfds = getdtablesize();
#else
#ifdef _NFILE
    numfds = _NFILE;
#endif _NFILE
#endif V4_2BSD
    /*NOSTRICT*/
    regfdary = (int *)malloc(numfds * sizeof(int));
    regfdary[0] = 0; regfdary[1] = 1;
    for (i = numfds-1; i > 1; i--)
	regfdary[i] = NOTOK;
}
logptr -> ll_file, logdfldir);
	if (logptr -> ll_level <mmdf/lib/mmdf/aliasfetch.c   444      0     12        1261  3620510375  10712 /*
 *              A L I A S F E T C H . C
 *
 *      This module hides the multiple file nature of the alias
 *	data.  Only submit should need to bypass this.
 */
#include "util.h"
#include "mmdf.h"
#include "ch.h"

extern Alias *al_list;

aliasfetch (first, key, dest, bypass)
int	first;
char	*key;
char	*dest;
int	bypass;
{
	register Alias *alp;
	register int badretval = NOTOK;
	register int ret;

	for (alp = al_list; alp != (Alias *)0; alp = alp->al_next) {
		if (bypass && !(alp->al_flags & AL_NOBYPASS))
			continue;
		if ((ret = tb_k2val (alp->al_table, first, key, dest)) == OK)
			return (OK);
		if (ret != NOTOK)
			badretval = ret;	/* NS timeout? */
	}
	return (badretval);
}
.o: ../../h/ch.h
amp_hdr.o: amp_hdr.c
amp_hdr.o: ../../h/util.h
amp_hdr.o: ../../h/mmdf.h
amp_hdr.o: ../../h/ap.h
amp_hdr.o: ../../h/ap_lex.h
amp_hdr.o: ../../h/ch.h
rtn_proc.o: rtn_proc.c
rtn_proc.o: ../../h/util.h
rtn_proc.o: ../../h/mmdf.h
rtn_proc.o: ../../h/msg.h
rtn_proc.o: ../../h/adr_queue.h
mq_rdmail.o: mq_rdmail.c
mq_rdmailmmdf/lib/mmdf/alias.c   444      0     12       14422  3671073162   7727 #include <stdio.h>
#include "util.h"
#include "mmdf.h"

#define BSIZE		1024

struct ALIAS {
	char *key;
	int cntr;
	char *names;
	struct ALIAS *next_int;
} *als, alias_ptr, *old_als;

int aflag;
char *adrptr;
char *malloc();

aliasinit( aliasfile )
char *aliasfile;
{
	char alsfilename[128];
	FILE *afd;
	char *akey, *list;
	char tmpbuf[BSIZE];
	aflag = 1;

	(void) strcpy( alsfilename, aliasfile );
		/*
		**  Open the alias file and store the keys and lists
		**  in core
		*/

	afd = fopen( alsfilename, "r" );
	if( afd <= NULL ) {
		aflag = 0;
		perror(" Can't open alias file ");
	}
	als = &alias_ptr;
	old_als = NULL;
	while(1) {
		if(fgets(tmpbuf, sizeof(tmpbuf), afd) != NULL) {
			get_key(tmpbuf, &akey, &list);
			if( old_als > 0 ) {
				if(( als = (struct ALIAS *) malloc(sizeof(alias_ptr))) <= 0) {
					perror( "MALLOC ERROR!");
					break;
				}
				old_als->next_int = als;
			}
			als->key = akey;
			als->cntr = 0;
			als->names = list;
			old_als = als;
		}
		else 
		{
			old_als = NULL;
			break;
		}
	}
}

int linelen, destlen, addrlen;

aliasmap(dest, src, thehost)
char *dest, *src, thehost[];
{
    char addr[BSIZE],
	 name[ADDRSIZE],
	 address[ADDRSIZE],
	 temphost[FILNSIZE],
	 defhost[FILNSIZE],
	 hostname[FILNSIZE];
    char *ptr;
    char *sadrptr;
    int sub_flag;

    if (thehost != 0)                /* save official version of default   */
	(void) strcpy (defhost, thehost);

    for (ptr = dest, linelen = 0; *ptr != '\0'; ptr++, linelen++)
	if (*ptr == '\n')         /* get length of last line of header  */
	    linelen = -1;         /* make it zero for next character    */
    destlen = ptr - dest;

    for (adrptr = src;; )         /* adrptr is state variable, used in  */
    {                             /*   next_address                     */
	switch (m_next_address (addr))
	{
	    case -1:              /* all done                           */

		/* clean markers in aliases structure */
		als = &alias_ptr;
		while(als != NULL ){
			als->cntr = 0;
			als = als->next_int;
		}
		return;

	    case 0:               /* empty address */
		continue;
	}

	name[0] = address[0] = temphost[0] = '\0';

	parsadr (addr, name, address, temphost);
	if (address[0] == '\0')
	{                         /* change the default host */
	    if (temphost[0] != '\0')
		(void) strcpy (defhost, temphost);
	    continue;
	}
	if( aflag && temphost[0]==0){
		als = &alias_ptr;
		sub_flag = 0;
		while( als != NULL ){
			if( lexequ(address, als->key) && als->cntr == 0 ){
				als->cntr++;
				sadrptr = adrptr;
				aliasmap(dest, als->names, thehost);
				adrptr = sadrptr;
				sub_flag = 1;
				break;
			}
			else
				if( lexequ(address, als->key) && als->cntr != 0 ){
					sub_flag = 1;
					break;
				}
			als = als->next_int;
		}
		if(sub_flag == 1)
			continue;
	}
	if (temphost[0] == '\0')
	    (void) strcpy (hostname, defhost);
	else
	    (void) strcpy (hostname, temphost);

	if (name[0] != '\0')      /* put into canonical form            */
	{                         /* name <mailbox@host>                */
	    for (ptr = hostname; !isnull (*ptr); ptr++)
		*ptr = uptolow (*ptr);
				  /* force hostname to lower            */
	    for (ptr = name; ; ptr++)
	    {                     /* is quoting needed for specials?    */
		switch (*ptr)
		{
		    default:
			continue;

		    case '\0':    /* nope                               */
			sprintf (addr, "%s <%s@%s>",
				name, address, hostname);
			goto doaddr;

		    case '<':     /* quoting needed                     */
		    case '>':
		    case '@':
		    case ',':
		    case ';':
		    case ':':
			sprintf (addr, "\"%s\" <%s@%s>",
				    name, address, hostname);
			goto doaddr;
		}
	    }
	}
	else
	{
	    sprintf (addr, "%s@%s", address, hostname);
	}

doaddr:
	addrlen = strlen (addr);

	if ((destlen + addrlen) < (BSIZE - 4))
	{                         /* it will fit into dest buffer       */
	    if (destlen == 0)
		(void) strcpy (dest, addr);
	    else          /* monitor line length                */
	    {
		if ((linelen + addrlen) < 70)
		    sprintf (&dest[destlen], ", %s", addr);
		else
		{
		    sprintf (&dest[destlen], ",\n %s", addr);
		    linelen = 0;
		    destlen += 1;
		}
		destlen += 2;
		linelen += 2;
	    }
	    destlen += addrlen;
	    linelen += addrlen;
	}
    	else
    	{
    		fprintf(stderr,"WARNING: Header line exceeds buffer size!\n");
    		fprintf(stderr,"\tIt was truncated to legal length.\n");
    		break;
    	}
    }
}

get_key( s, okey, olist )
char *s, **okey, **olist;
{
	char tmps[512];
	char *ptmps;
	int len_list;
/*
**  This routine takes the input string s and divides it into two parts,
**  the first of which consists of the first word in the original string.
**  It is returned in okey. The second part is a list of the remaining names.
**  They are returned in the olist. Malloc is used to allocate the necessary
**  storage for the sublists.
*/

	/* throw away initial white space */

	while( *s == ' ' || *s == '\t' )
		*s++;

	/* get the first word. It may be terminated by spaces, tabs, commas, */
	/* newlines or end-of-file 					     */

	ptmps = tmps;
	while( *s != ' ' && *s != '\t' && *s != ',' && *s != '\n' && *s != EOF){
		*ptmps++ = *s++;
	}
	*ptmps = 0;
	len_list = strlen(tmps);
	if(( *okey = malloc( (unsigned) (len_list) + 1 )) <= 0 ) {
		perror("MALLOC ERROR!");
		return(-1);
	}

	(void) strcpy( *okey, tmps );

	/* and now the rest of the list */

	ptmps = s;
	while( *s != '\n' && *s != EOF && *s != '\0' )
		*s++;
	*s = 0;
	len_list = strlen( ptmps );
	if(len_list <= 0 )
		return( -1 ); /* no list - ERROR - ERROR */
	if((*olist = malloc( (unsigned) (len_list) +1 )) <= 0) {
		perror("MALLOC ERROR!");
		return( -1 );
	}

	(void) strcpy( *olist, ptmps );
	return(0);
}
m_next_address (addr)
char    *addr;
{
    int     i = -1;               /* return -1 = end; 0 = empty         */

    for (;;)
	switch (*adrptr)
	{
	    default: 
		addr[++i] = *adrptr++;
		continue;

	    case '"':
		for (addr[++i] = *adrptr++; !isnull (*adrptr); )
		{
		    addr[++i] = *adrptr;
		    if (*adrptr++ == '"')
			break;
		}
		continue;

	    case '(':
		for (addr[++i] = *adrptr++; !isnull (*adrptr); )
		{
		    addr[++i] = *adrptr;
		    if (*adrptr++ == ')')
			break;
		}
		continue;

	    case '\n':
	    case ',': 
		addr[++i] = '\0';
		adrptr++;
		return (i);

	    case '\0': 
		if (i >= 0)
		    addr[++i] = '\0';
		return (i);
	}
}
st, temphost);
	    continue;
	}
	if( aflag && temphost[0]==0){
		als = &alias_ptr;
		sub_flag = 0;
		while( als != NULL ){
			if( lexequ(address, als->key) && als->cntr == 0 ){
				als->cntr++;
				sadrptr = adrptr;
				aliasmap(dest, almmdf/lib/dial/   755      0     12           0  3671352204   6347 mmdf/lib/dial/d_coding.c   444      0     12       17246  3620510376  10376 # include  "util.h"
# include  "d_proto.h"
# include  "d_returns.h"

# define  TAB         '\11'     /*  tab  */
# define  LINEFEED    '\12'     /*  line feed  */
# define  FORMFEED    '\14'     /*  form feed  */
# define  CR          '\15'     /*  carriage return  */

/* speed optimization */

#define	D_TESTBIT(b,v)	((!isascii(b))||((v)[(b)>>4]&(1<<((b)&017))))

/*
 *     D_BLDCVEC
 *
 *     this routine builds a code vector from a hex digit string which
 *     specifies 'illegal' characters for transmission to a host.  each
 *     of the digits in the input vector to be sure that it is a legal
 *     hex digit.
 *
 *     hexvec -- pointer to a 32 character string of hex digits.
 *
 *     codevec -- pointer to an 8 word bit map table for the output.
 */

d_bldcvec(hexvec, codevec)
  register char  *hexvec;
  unsigned  short codevec[];
    {
    extern int  d_errno;
    register int  word, digit;
    unsigned short bits;
    int  value;

/*  each word of the code vector is constructed from 4 hex digits  */

    for (word = 0; word <= 7; word++)
      {
      bits = 0;

      for (digit = 0; digit <= 3; digit++)
        {
        if ((value = d_fromhex(*hexvec++)) < 0)
          {
#ifdef D_LOG
          d_log("d_bldcvec", "bad hex digit in path illegal character field");
#endif D_LOG
          d_errno = D_PACKERR;
          return(D_FATAL);
          }

        bits |= (value & 017) << (4 * digit);
        }

      codevec[word] = bits;
      }

    return(D_OK);
    }

/*
 *     D_BLDHVEC
 *
 *     this routine takes a code vector and builds a string of 32 hex
 *     digits for transmission in a RESET packet.
 *
 *     codevec -- pointer to the code vector to be converted
 *
 *     hexvec -- pointer to place to load the 32 hex digits
 */

d_bldhvec(codevec, hexvec)
  unsigned  short codevec[];
  register char  *hexvec;
    {
    register unsigned  bits, digit;
    int  word;

    for (word = 0; word <= 7; word++)
      {
      bits = codevec[word];

      for (digit = 0; digit <= 3; digit++)
        {
        *hexvec++ = d_tohex(bits & 017);
        bits >>= 4;
        }
      }

    *hexvec = '\0';
    return(D_OK);
    }

/*
 *     D_DECODE
 *
 *     this routine takes encoded strings and forms the plaintext
 *     output.
 *
 *     input -- input string to be decoded
 *
 *     ninput -- the number of characters in the input string
 *
 *     output -- pointer to place for the decoded output
 *
 *     the routine returns the number of output characters.
 */

d_decode(input, ninput, output)
  register char  *input, *output;
  register int  ninput;
    {
    extern char  d_rcvesc;
    extern int  d_errno;
    int  chigh, clow, noutput;

    noutput = 0;

/*  cycle through the input string.  if a character isn't the escape, just  */
/*  copy it to the output.                                                  */

    while (ninput)
      {
      if (*input != d_rcvesc)
        {
        *output++ = *input++;
        ninput--;
        noutput++;
        continue;
        }

/*  must be the escape character.  if the next one is the escape also  */
/*  then load one of them into the output.  otherwise switch for the   */
/*  special cases.                                                     */

      input++;

      if (*input == d_rcvesc)
        {
        *output++ = *input++;
        ninput -= 2;
        noutput++;
        continue;
        }

      switch (*input)
        {
        case  'I':

            *output++ = TAB;
            break;

        case  'J':

            *output++ = LINEFEED;
            break;

        case  'L':

            *output++ = FORMFEED;
            break;

        case  'M':

            *output++ = CR;
            break;

        default:

            chigh = d_fromhex(*input++);
            clow = d_fromhex(*input++);

            if ((chigh < 0) || (clow < 0))
              {
#ifdef D_LOG
              d_log("d_decode", "illegal character encoding");
#endif D_LOG
              d_errno = D_PACKERR;
              return(D_FATAL);
              }

            *output++ = (chigh << 4) | (clow & 017);
            noutput++;
            ninput -= 3;;
            continue;
        }

      input++;
      ninput -= 2;
      noutput++;
      }

    return(noutput);
    }

/*
 *     D_CCODE
 *
 *     this routine computes the encoding for a single character, if any,
 *     based on the bit vector which specifies whether the target host
 *     can receive the character without undesired side effects.
 *
 *     c -- the character to be encoded
 *
 *     string -- place to put the encoding
 */

d_ccode(c, string)
  register unsigned c;
  register char  *string;
    {
    extern unsigned  short d_lcvec[];
    extern char  d_snesc;

/*  if we're encoding the escape character itself, just double it.  */

    if (c == d_snesc)
      {
      *string++ = d_snesc;
      *string = d_snesc;
      return(2);
      }

/*  if the bit corresponding to the character is on in the vector, then we  */
/*  have to encode it.  switch for special cases, otherwise use hex digits  */

    if (D_TESTBIT(c, d_lcvec) == 0)
      {
      *string = c;
      return(1);
      }

    *string++ = d_snesc;

    switch (c)
      {
      case  TAB:

          *string = 'I';
          return(2);

      case  LINEFEED:

          *string = 'J';
          return(2);

      case  FORMFEED:

          *string = 'L';
          return(2);

      case  CR:

          *string = 'M';
          return(2);

      default:

          *string++ = d_tohex(c >> 4);
          *string++ = d_tohex(c & 017);
          return(3);
      }
    }

/*
 *     D_TESTBIT
 *
 *     this routine is called to test if a given bit is on in a bit vector
 *     the routine returns 1 if it is, 0 otherwise.
 *
 *     bit -- the bit to test
 *
 *     bitvector -- the bit vector
 */

d_testbit(bit, bitvector)
  register int  bit;
  register unsigned  short bitvector[];
    {
	return(D_TESTBIT(bit,bitvector));
    }

/*
 *     D_SETBIT
 *
 *     this routine is called to set a bit in a bit vector.
 *
 *     bit -- the bit to set
 *
 *     bitvector -- the bit vector to set it in
 */

d_setbit(bit, bitvector)
  register int  bit;
  register unsigned  short bitvector[];
    {

    bit &= 0177;

    bitvector[bit >> 4] |= (1 << (bit & 017));

    return(D_OK);
    }

/*
 *     D_ORBITVEC
 *
 *     this routine computes a word by word logical or of the input bit
 *     vectors
 *
 *     a, b -- the input bit vector
 *
 *     out -- the bit vector which becomes the bit-wise 'or' of a and b
 */

d_orbitvec(a, b, out)
  register unsigned  short a[], b[], out[];
    {
    int  word;

    for (word = 0; word < 8; word++)
      out[word] = a[word] | b[word];

    return(D_OK);
    }

/*
 *     D_TOHEX
 *
 *     routine which returns the value of its argument as a single hex
 *     character
 *
 *     value -- the value to be converted
 */

d_tohex(value)
  register int  value;
    {

    if ((value >= 0) && (value <= 9))
      return(value + '0');

    if ((value >= 10) && (value <= 15))
      return(value - 10 + 'A');

    return(-1);
    }

/*
 *     D_FROMHEX
 *
 *     routine which returns the value of the hex character given as the
 *     argument.
 *
 *     c -- the character whose value is desired
 */

d_fromhex(c)
  register int  c;
    {

    if ((c >= '0') && (c <= '9'))
      return(c - '0');

    if ((c >= 'A') && (c <= 'F'))
      return(c - 'A' + 10);

    return(-1);
    }

/*
 *     D_SETILL
 *
 *     this routine is used to copy the illegal character bit vectors
 *
 *     in -- pointer to the source bit vector
 *
 *     out -- pointer to the destination bit vector
 */

d_setill(in, out)
  register unsigned  short in[], out[];
    {
    register int  word;

    for (word = 0; word < 8; word++)
      out[word] = in[word];

    return(D_OK);
    }
high = d_fromhex(*input++);
            clow = d_fromhex(*input++);

            if ((chigh < 0) || (clow < 0))
              {
#ifdef D_LOG
              d_log("d_decode", "illegal character encoding");
#endif D_LOG
              d_errno = D_PACKERR;
              return(D_FATAL);
              }

            *output++ = (chigh << 4) | (clow &mmdf/lib/dial/d_assign.c   444      0     12        2374  3671073163  10377 #
/*
 *	D _ A S S I G N . C
 *
 *	Doug Kingston, BRL, 4 August 81
 *
 *	Takes the name of an assignable device as an argument.
 *	The function calles the program /bin/assign to get a device
 *	for exclusive use.
 */
#include "d_returns.h"

#ifdef DOASSIGN
struct {
	char lobyte;
	char hibyte;
};

extern	int	d_errno;

d_assign (devname)
char *devname;
{
	int status;

	if ((status = d_tryfork()) == -1) {
		d_log ("d_assign", "couldn't fork");
		d_errno = D_FORKERR;
		return( D_FATAL );
	}

	if (status == 0) {
		/* CHILD */
		(void) close (1);
		open ("/dev/null", 1);
		execl ("/bin/assign", "mmdf-assign", devname, (char *)0);
		exit (99);
	}
	else {
		/* PARENT */
		wait (&status);
		return (status.hibyte == 0 ? D_OK : D_NO);
	}
}


d_deassign (devname)
char *devname;
{
	int status;

	if ((status = d_tryfork()) == -1) {
		d_log ("d_deassign", "couldn't fork");
		return( D_NO );
	}

	if( status == 0 ) {
		/* CHILD */
		(void) close (1);
		open ("/dev/null", 1);
		execl ("/bin/assign", "mmdf-assign", "-", devname, (char *)0);
		exit (99);
	}
	else {
		/* PARENT */
		wait (&status);
		return (status.hibyte == 0 ? D_OK : D_NO);
	}
}

#else

/* Fake routine for systems without "assign" */

d_assign() {
	return( D_OK );
}

d_deassign() {
	return( D_OK );
}

#endif
t;
  register unsigned  short bitvector[];
    {
	return(D_TESTBIT(bit,bitvector));
    }

/*
 *     D_SETBIT
 *
 *     this routine is called to set a bit in a bit vector.
 *
 *     bit -- the bit to set
 *
 *     bitvector -- the bit vector to set it in
 */
mmdf/lib/dial/d_script2.c   444      0     12        7455  3671073163  10506 #
# include  <stdio.h>
# include  "d_proto.h"
# include  "d_returns.h"
# include  "d_structs.h"

# define	MAXFILES	15

extern char *strcpy ();
extern char *strdup ();
extern unsigned d_scline;
extern char d_scfile[];
extern int d_nfields;
extern char *d_fields[];
extern int errno;

FILE  *d_scfp;            /*  file pointer for script file  */
static struct opfile *d_files[MAXFILES];
static int d_nopen;

/*
 *     D_SCOPEN
 *
 *     this routine opens the script file and sets up a few globals for
 *     use by later routines.
 *
 *     scriptfile -- path name of the script file to be used.
*/

d_scopen (scriptfile, nfields, fields)
  int nfields;
  char scriptfile[], *fields[];
    {
    char *calloc();
    register int index, i;

    /*  Don't let them open so many files that they overrun my array  */
    if (d_nopen > MAXFILES)
    {
#ifdef D_LOG
	d_log ("d_scopen", "Too many files opened");
#endif D_LOG
	return (D_FATAL);
    }

    /*  If this is any other than the first file to be opened,
     *  then I alloc space for it and save status info.
     */
    if (d_nopen != 0)
    {
	index = d_nopen - 1;
	/*NOSTRICT*/
	d_files[index] = (struct opfile *)calloc (1, sizeof (struct opfile));
	if (d_files[index] == 0)
	{
	    d_log ("d_scopen", "couldn't alloc space for struct");
	    return (D_FATAL);
	}
	d_files[index]->o_line = d_scline;
	d_files[index]->o_chan = d_scfp;
	(void) strcpy (d_files[index]->o_fname, d_scfile);
	d_files[index]->o_nfields = d_nfields;
	for (i=0; i < d_nfields; i++)
	{
	    d_files[index]->o_fields[i] = d_fields[i];
	    free(d_fields[i]);
	}
	d_dbglog ("d_scopen", "Saving %d fields; loading %d new ones", d_nfields, nfields);
	for (i=0; i < nfields; i++)
	    d_fields[i] = strdup(fields[i+1]);
	d_nfields = nfields-1;

    }
    d_nopen++;

    if ((d_scfp = fopen(scriptfile, "r")) == NULL)
    {
#ifdef D_LOG
	d_log ("d_scopen", "Can't open script file '%s' (errno %d)",
						scriptfile, errno);
#endif D_LOG
	/*  restore the old file setup incase this was part of a failed
	 *  alternate.
	 */
	d_scclose();
	return (D_FATAL);
    }

    d_scline = 0;
    (void) strcpy (d_scfile, scriptfile);

#ifdef D_LOG
    d_log ("d_scopen", "script file '%s' being used", scriptfile);
#endif D_LOG

    return (D_OK);
}

d_scclose()
    {
    register int index, i;

    if (d_nopen <= 0)
	return (0);


    fclose (d_scfp);
#ifdef D_LOG
    d_log ("d_scclose", "Closing script file '%s'", d_scfile);
#endif D_LOG
    if (--d_nopen > 0)
    {
	index = d_nopen - 1;
	d_scfp = d_files[index]->o_chan;
	(void) strcpy (d_scfile, d_files[index]->o_fname);
	d_scline = d_files[index]->o_line;
	d_nfields = d_files[index]->o_nfields;
	for (i = 0; i < d_nfields; i++)
	    d_fields[i] = d_files[index]->o_fields[i];
	free (d_files[index]);
#ifdef D_LOG
	d_log ("d_scclose", "Resuming use of script file '%s'", d_scfile);
#endif D_LOG
	return (d_nopen);
    }

    return (0);
}



/*
 *     D_SCRGETLINE
 *
 *     this routine is called to read a line in from the script file.  it
 *     handles the possibility of an escaped newline.
 *
 *     linebuf -- place to load the line
 */

d_scgetline(linebuf, channel)
  char  *linebuf;
  FILE *channel;
    {
    register int  c, count;
    register char *cp;

    count = 0;
    cp = linebuf;
    d_scline++;
    while ((c = getc(channel)) != EOF)
    {
	if (c == '\n')
	{                   /*  if newline, see if it's escaped  */
	    if ((cp > linebuf) && (cp[-1] == '\\'))
	    {
		cp--;
		count--;
		d_scline++;
	        continue;
	    }
	    break;
        }

	if (++count > MAXSCRLINE)
	{                   /*  line is too long */
	    d_scerr("line too long");
	    return(D_FATAL);
        }

	*cp++ = c;
    }

    /*  The line parsing routine actually needs two nulls at the
     *  end to guarentee that it doesn't skip it.
     */
    *cp++ = '\0';
    *cp = '\0';
    return(count);
}
== NULL)
    {
#ifdef D_LOG
	d_log ("d_scopen", "Can't open script file '%s' (errno %d)",
						scriptfile, errno);
#endif D_LOG
	/*  restore the old file setup incase this was part of a failed
	 *  alternate.
	mmdf/lib/dial/d_control.c   444      0     12        3621  3620510377  10564 #
# include  "d_proto.h"
# include  "d_returns.h"

/*
 *	D_CONTROL
 *
 *	This routine tries to do something with unexpected
 *	packet types.  If 'rdport' is looking for one packet
 *	type and gets another, then this routine is called.
 *	This allows certain control type packets to be sent
 *	and processed without prior arrangements.  If the
 *	packet is not one of these control packets, then an
 *	error is returned.
 *
 *	packet - a pointer to the start of the packet.
 *
*/

d_control (packet, length)
  char *packet;
  int length;
    {
    int dig1, dig2;
    extern int d_nbuff, d_errno, d_rcvseq;

    /*  switch out to the code to process this packet  */
    switch (packet[TYPEOFF])
    {
	case NBUFF:
	    /*  This is a control packet indicating whether buffering
	     *  is allowed.
	     */
	    dig1 = d_fromhex(packet[TEXTOFF]);
	    dig2 = d_fromhex(packet[TEXTOFF + 1]);

	    /* This should stylistically be done elsewhere, but nobody
	     * else will know we got this packet. */
	    d_rcvseq = d_incseq (d_rcvseq);

	    /*  Make sure the characters are reasonable  */
	    if ((dig1 < 0) ||
		(dig2 < 0)   )
	    {
#ifdef D_LOG
		d_log ("d_control", "NBUFF: Bad hex char in packet ('%c%c')",
				packet[LHEADER], packet[LHEADER+1]);
#endif D_LOG
		return (D_NONFATAL);
	    }

	    /*  transfer to the buffer counter  */
	    d_nbuff = (dig1 << 4) | dig2;
	    return (D_OK);

	case QUIT:
#ifdef D_LOG
	    d_log ("d_control", "received QUIT packet");
#endif D_LOG
	    if (length == LQUIT)
	    {
		d_errno = D_RCVQUIT;
		return (D_FATAL);
	    }
#ifdef D_LOG
	    d_log ("d_control", "Quit packet bad length (%d)", length);
#endif D_LOG
	    return (D_NONFATAL);

	default:
	    /*  If the packet type is not one of the special control
	     *  packets, then just log it as an error.
	     */
#ifdef D_LOG
	    d_log ("d_control", "Bad packet type '%s'", packet);
#endif D_LOG
	    return (D_NONFATAL);
    }
}
error is returned.
 *
 *	packet - a pointer to the start of the packet.
 *
*/

d_control (packet, length)
  chammdf/lib/dial/mktran.c   444      0     12       13567  3641232116  10122 #
/*	Program to help make a transcript file  */

#include	<stdio.h>
#include	"d_returns.h"
#include	"ll_log.h"
#include	"d_proto.h"
#include	"d_script.h"

#define		SCRIPT		"Script"
#define		TRANFILE	"Tranfile"
#define		LOGFILE		"Log"

struct ll_struct log = {
    "Log",
    "mktran log",
    '\0177',
    100,
    0,
    0,
    0,
};

FILE *input, *output;
char *sfile;

main (argc, argv)
  int argc;
  char **argv;
    {
    register int command;
    char linebuf[MAXSCRLINE +2], *fields[MAXFIELDS];
    register int result;
    int nfields;

    
    /*  init various things:  logging, files, etc.  */
    init (argc, argv);

    /*  run through that part of the script which already exists  */
    printf("Starting to process existing script.\n");
    if ((result = doscript ()) < 0)
    {
	printf("Problem encountered in processing existing script.\n");
	printf("Return value of %d\n", result);
	printf("Aborting.\n");
	exit(-1);
    }
    printf("Existing script processed successfully\n");

    /*  Now add more to it  */
    for (;;)
    {
	/*  Read a new command line from the terminal  */
	printf ("Enter command: ");
	fflush (stdout);

	command = d_cmdget (linebuf, &nfields, fields, stdin);
	if (command < 0)
	{
	    /*  may be an error;  may also be the command to turn
	     *  the line around.  Check it.
	     */
	    if (turnar (linebuf))
		doread();
	    else
	    {
		printf ("Unrecognized command.  getcmd returns %d\n", command);
		printf ("Try again\n");
	    }
	    continue;
	}

	/*  write the new line to the script  */
	wrtscr ();


	/*  process the new line  */
	result = d_cmdproc (command, nfields, fields);

	/*  check on the results of the processing  */
	switch (result)
	{
	    case D_CONTIN:
		continue;

	    case D_OK:
		quit ();

	    case D_FATAL:
		printf ("Fatal error on command line.  Abort\n");
		quit ();
	}
    }

}





init (argc, argv)
  int argc;
  char **argv;
    {
    extern int errno;
    extern FILE *d_scfp;
    extern int d_master, d_snseq, d_rcvseq;
    extern int d_debug;
    register int result;
    register int word;

    printf("Usage: %s [<script file> [<debug value> [<transcript file>]]]\n",
			argv[0]);
    /*  First, determine the name of the script file  */
    if (argc > 1)
	sfile = argv[1];
    else
	sfile = SCRIPT;

    /*  open it for appending  */
    output = fopen (sfile, "a");
    if (output == NULL)
    {
	printf ("Can't open '%s': errno %d - ", sfile, errno);
	fflush (stdout);
	perror ("");
	exit (-1);
    }
    fseek (output, 0L, 2);

    /*  check for debugging  */
    if (argc > 2)
	d_debug = atoi (argv[2]);
    else
	d_debug = 0;

    /*  open the log and transcript files  */
    if ((result = d_opnlog (&log)) < 0)
    {
	printf("Couldn't open log file;  d_opnlog returns %d\n", result);
	exit(-1);
    }
    d_init ();


    /*  check to see if a transcript is wanted  */
    if (argc > 3)
	if (atoi (argv[3]) != 0)
	    if ((result = d_tsopen (TRANFILE)) < 0)
		return (result);

    d_master = 1;
    d_snseq = 3;
    d_rcvseq = 3;

    /*  at last, more or less done  */
    return (0);
}






wrtscr ()
    {
    extern char d_origlin[];
    register int length, nwrote;

    length = strlen (d_origlin);

    nwrote = fwrite (d_origlin, sizeof (char), length, output);
    if (nwrote != length)
    {
	printf ("fwrite tried %d, but returned %d\n", length, nwrote);
	printf ("Couldn't write to script file: ");
	fflush (stdout);
	perror ("");
	fflush (stderr);
	exit (-1);
    }

    return;
}





doscript ()
    {
    extern FILE *d_scfp;
    extern int errno;
    int nselect, result;
    char linebuf[MAXSCRLINE + 2],
	 *fields[MAXFIELDS];


    /*  open the script file  */
    if ((result = d_scopen (sfile, 0, (char **) 0)) < 0)
    {
	printf ("Couldn't open '%s': errno %d - ", sfile, errno);
	fflush (stdout);
	perror ("");
	fflush (stderr);
	exit (-1);
    }

    /*  now process it as a normal script with the exception that
     *  an EOF means read from the standard input instead of stop.
     */
    for (;;)
    {
	result = d_scrblk ();
	switch (result)
	{
	    case D_OK:
		return (D_OK);

	    case D_EOF:
	    case D_NFIELDS:
	    case D_QUOTE:
	    case D_UNKNOWN:
	    case D_FATAL:
		/*  a fatal error  */
		return (D_FATAL);

	    case D_EOF:
		/*  a normal termination since we expect the user to
		 *  add more immediately.
		 */
		return (0);

	    case D_NONFATAL:
		/*  An error that may be recoverable.  If there is
		 *  an alternate, use it.
		 */
		if (nselect <= 0)
		    /*  Not within a select block; therefore, not alt  */
		    return (D_FATAL);

		/*  We are within a select block;  go to the alternate  */
		if ((result = d_nxtalt ()) < 0)
		    return (result);
		switch (result)
		{
		    case S_ALT:
			continue;

		    case S_SELEND:
			/*  no more alternates;  return error  */
			return (D_FATAL);

		    default:
			d_scerr ("Bad syntax scriptfile");
			return (D_FATAL);
		}


	    case S_SELST:
		/*  The next line had better be an alternate  */
		if (d_cmdget (linebuf, &nfields, fields, d_scfp) != S_ALT)
		{
		    d_scerr ("Missing 'alternate' after 'begin'");
		    return (D_FATAL);
		}
		nselect++;
		continue;

	    case S_SELEND:
		/*  verify that there was a select block being
		 *  looked at.  If so, then the last alternate
		 *  was the successful one.  Continue normally.
		 */
		if (nselect-- <= 0)
		{
		    d_scerr ("Inappropriate select end");
		    return (D_FATAL);
		}
		continue;

	    case S_ALT:
		/*  First, make sure the context was correct  */
		if (nselect <= 0)
		{
		    d_scerr ("Inappropriate alt\n");
		    return (D_FATAL);
		}
		/*  If we get here, then an alternate within a select
		 *  was completed successfully.  Move on.
		 */
		if ((result = d_selend ()) < 0)
		    return (result);
		nselect--;
		switch (result)
		{
		    case S_SELEND:
			continue;

		    default:
			d_scerr ("error in format of script file");
			return (D_FATAL);
		}
	}

    }
}




quit ()
    {

    printf ("Quitting\n");
    exit (0);
}

open (TRANFILE)) < 0)
		return (result);

    d_master = 1;
    d_snseq = 3;
    d_rcvseq = 3;

    /*  at last, more or less done  */
  mmdf/lib/dial/d_port.c   444      0     12       22315  3664425142  10114 # include  "util.h"
# include  "d_proto.h"
# include  "d_returns.h"
# include <signal.h>
# include  "d_syscodes.h"
# include  <stdio.h>
# include  "d_structs.h"

/*  Jun 81    D. Crocker    Major reworking of alarm usage, trying to
 *                          deal with fact that there may be multiple
 *                          calls to d_rdport trying to get one good packet
 *                          d_rdport fixed to handle 8th bit on
 *                          Converted some d_dbglogs to d_log
 *  Jul 81    D. Crocker    Skip initial non-hex text on line.
 *
 *  Sep 83    M. Laubach    added procedure d_waitprompt to interface
 *                          to HP and IBM software prompting
 *
 *  Mar 84    D. Rockwell   Added select call to d_wrtport, removed alarm
 */

extern int  d_errno, errno;
extern  int     d_prompt;         /* prompt char from script            */
LOCVAR  unsigned d_alrmtim;        /* time to wait for i/o to complete   */


d_alarm (thetime)
    unsigned thetime;
{
    d_alrmtim = thetime;
}


/*
 *
 *     D_RDPORT
 *
 *     this routine is called to read a packet from the port.  the packets
 *     are checked for length errors, bad checksums, and to make sure that
 *     we aren't receiving packets that we have sent ourselves.
 *
 *     packet -- place to load te incoming packet.  should be large enough to
 *               contain a packet one character longer than the maximum.  the
 *               newline is replaced by a null.
 *
 *     returns the length of the incoming packet, minus the newline.
 *
 *     D_FATAL -- fatal error from read call
 *
 *     D_NONFATAL -- eof from port
 */

d_rdport (packet)
char   *packet;
{
    extern  FILE * d_prtfp;
    extern int  d_master,
		d_rcvseq;
    int     flags,
	    seqnum;
    register char  *op;
    register int    count;
    register int   c;

    if (d_alrmtim == 0)
    {
#ifdef D_LOG
	d_log("d_rdport", "*** no more alarm time left");
#endif D_LOG
	d_errno = D_TIMEOUT;
	return(D_INTRPT);
    }

    if (setjmp (timerest)) {
#ifdef D_LOG
	d_log("d_rdport", "read alarm");
#endif D_LOG
	d_errno = D_TIMEOUT;
	return(D_INTRPT);
    }
    for (s_alarm (d_alrmtim); ; )
    {                             /* get valid packet                   */
	for (op = packet, count = 0; count <= MAXPACKET + 1; )
	{                         /* get a line                         */
	    if ((c = d_getc (d_prtfp)) == EOF)
	    {                     /* truly an end of file?              */
		if (ferror (d_prtfp))
		{
		    d_alrmtim = s_alarm (0);
		    if (errno == EINTR)
		    {
#ifdef D_LOG
			d_log("d_rdport", "read alarm");
#endif D_LOG
			d_errno = D_TIMEOUT;
			return(D_INTRPT);
		    }
#ifdef V4_2BSD
		/*
		 * This is a special for 4.2 systems -- a bug in
		 * the tty driver causes EWOULDBLOCK to be returned
		 * instead of an EOF on the tty.  So, we fake it.
		 * The messages are distinguishable.
		 */
		    else if (errno == EWOULDBLOCK)
		    {
#ifdef D_LOG
			d_log("d_rdport", "port read EOF");
#endif D_LOG
			d_errno = D_PORTEOF;
			return(D_NONFATAL);
		    }
#endif V4_2BSD
		    else
		    {
#ifdef D_LOG
			d_log("d_rdport",
				"port read errno %d", errno);
#endif D_LOG
			d_errno = D_PORTRD;
			return(D_FATAL);
		    }
		}
		if (feof (d_prtfp))
		{
		    d_alrmtim = s_alarm (0);
#ifdef D_LOG
		    d_log("d_rdport", "port read eof");
#endif D_LOG
		    d_errno = D_PORTEOF;
		    return(D_NONFATAL);
		}
	    }

	    c = toascii (c);      /* make sure high bit is off          */
	    switch (c)
	    {
		case NULL:        /* nulls are simply skipped           */
		case '\r':        /* carriage returns are skipped       */
		case '\177':      /* DELs  are simply skipped           */
		    continue;

		case '\n':        /* end of line => end of packet       */
		    d_tscribe (packet, count);
		    d_tscribe ("\n", 1);
		    *op = '\0';
		    break;

		default:          /* add to packet buffer               */
		    if (op == packet && !isxdigit (c)) {
#ifdef D_DBGLOG
			d_dbglog ("d_rdport", "noise '%c'", c);
#endif D_DBGLOG
		    } else {       /* packets begin with hex digits      */
			*op++ = c;
			count++;
		    }
		    continue;
	    }

	    if (count <= MAXPACKET + 1)
		break;            /*  so far, so good, now check form   */

#ifdef D_LOG
	    d_log ("d_rdport", "rcv too long (%dc) '%s'",
			count, packet);
#endif D_LOG
	    d_tscribe (packet, count);
	}


/*  make sure that the packet is at least as long as the shortest one.  */
/*  this is done to make sure that when we use fixed offsets for the    */
/*  fields that we're not reading junk.  check the checksum.            */

	if (count < MINPACKET)
	{
#ifdef D_LOG
	    if (count > 0)  /* ignore blank lines */
	        d_log ("d_rdport", "rcv len err (%d)", count);
#endif D_LOG
	    continue;
	}

	if (d_cscheck (packet, count) == D_NO)
	{
#ifdef D_LOG
	    d_log ("d_rdport", "rcv checksum err '%s'", packet);
#endif D_LOG
	    continue;
	}

/*  make sure that we didn't send it.  the other guy may not have echo off  */

	flags = d_fromhex (packet[FLAGOFF]);

	if ((((flags & MASTERBIT) && d_master) ||
		    ((flags & MASTERBIT) == 0) && (d_master == 0)))
	{
#ifdef D_LOG
	    d_log ("d_rdport", "rcv from self");
#endif D_LOG
	    continue;
	}

/*  acknowledge the packet  */

	d_alrmtim = s_alarm (0);  /* ackpack uses the alarm clock */

	seqnum = flags & 03;

#ifdef D_DBGLOG
	d_dbglog ("d_rdport", "Received packet '%s'", packet);
#endif D_DBGLOG

	switch (d_ackpack (packet[TYPEOFF], seqnum))
	{
	    case D_FATAL:
		return (D_FATAL);

	    case D_NONFATAL:
		s_alarm (d_alrmtim);
		continue;

	    default:
		return (count);
	}
    }
}




/*
 *
 *     D_WAITPROMPT
 *
 *     this routine is called to wait for a prompt character from the 
 *     port.  If prompt is not received within a receive timeout period 
 *     then fall through.  Do not return an error. 
 *
 */

d_waitprompt ()
{
    extern  FILE * d_prtfp;
    register int   c;
    int promptalrm = 10;       /* timeout delay in seconds */
    char  ch;

    if (d_prompt == 0) return(0); 

    for (alarm (promptalrm); ; )
    { 
	    if ((c = d_getc (d_prtfp)) == EOF)
	    {                     /* truly an end of file?              */
		if (ferror (d_prtfp))
		{
		    d_alrmtim = alarm (0);
		    if (errno == EINTR)
		    {
                    if (d_prompt == toascii(c)) return(0);
#ifdef D_LOG
                        d_log("d_waitprompt","prompt alarm for %d",d_prompt);
#endif D_LOG
                        d_errno = D_TIMEOUT;
			return(D_INTRPT);
		    }
		    else
		    {
#ifdef D_LOG
			d_log("d_waitprompt","port read errno %d", errno);
#endif D_LOG
			d_errno = D_PORTRD;
			return(D_FATAL);
		    }
		}
		if (feof (d_prtfp))
		{
		    d_alrmtim = alarm (0);
#ifdef D_LOG
		    d_log("d_waitprompt", "port read eof");
#endif D_LOG
		    d_errno = D_PORTEOF;
		    return(D_NONFATAL);
		}
	    }

            ch = toascii(c);
            c  = toascii(c);
            d_tscribe((char *) &ch, 1);   /* put chars into transcript log    */
            if (d_prompt == c) {          /* hoo-rah, break out the champaign */
              promptalrm = alarm(0);      /* cancel alarm                     */
              return(D_OK);
            }
    }
}


/**/

/*
 *     D_WRTPORT
 *
 *     this routine writes a packet on the port.
 *
 *     text -- pointer to packet to be sent.  must have line terminator
 *             already attached.
 *
 *     length -- number of bytes in text
 *
 *     timeout -- number of seconds to wait for write to complete
 *
 *
 *     return values:
 *
 *     OK -- no errors
 *
 *     D_FATAL -- fatal error writing on port
 *
 *     D_NONFATAL -- incomplete packet written on port.  usually means disconnect.
 *
 *     INTERRUPT -- write timer went off
 */

d_wrtport (text, length)
register char  *text;
register int    length;
{
    extern  FILE * d_prtfp;
    register int    result;

    d_tscribe (text, length);

    /*
     *  Added for prompt wait.  Always wait before sending packet
     *  9/28/83   laubach@udel-relay
     */
    if (d_prompt != 0) d_waitprompt();

    /*  Set up an alarm so we won't hang
     *  indefintiely if something goes wrong
     */
    if (setjmp (timerest)) {
	d_errno = D_TIMEOUT;
#ifdef D_LOG
	d_log ("d_wrtport", "write alarm");
#endif D_LOG
	return (D_INTRPT);
    }
    s_alarm (XMITWAIT);

    result = write (fileno (d_prtfp), text, length);
    s_alarm (0);

    if (result == length)
	return (D_OK);

    if (result >= 0)
    {
	d_errno = D_PORTEOF;
	return (D_NONFATAL);
    }

    if (errno == EINTR) {
	d_errno = D_TIMEOUT;
#ifdef D_LOG
	d_log ("d_wrtport", "write alarm");
#endif D_LOG
	return (D_INTRPT);
    }

    d_errno = D_PORTWRT;
    return (D_FATAL);
}

/*
 *      D_BRKPORT
 *
 *      This routine does its best to cause a BREAK on the line.
 *
 */

d_brkport()
{
#ifdef V4_2BSD
#include <sys/ioctl.h>
	ioctl (fileno (d_prtfp), TIOCSBRK, 0);
	sleep ((unsigned) 1);
	ioctl (fileno (d_prtfp), TIOCCBRK, 0);
#else

#ifdef SYS5
#include <termio.h>
	ioctl (fileno(d_prtfp), TCSBRK, 0);
#else
#include <sgtty.h>
	struct sgttyb ttybuf;
	int spdsave;

	ioctl (fileno (d_prtfp), TIOCGETP, &ttybuf);
	spdsave = ttybuf.sg_ospeed;
	ttybuf.sg_ospeed = B150;
	ioctl (fileno (d_prtfp), TIOCSETP, &ttybuf);
	d_wrtport("\0\0\0\0\0", 5);
	ttybuf.sg_ospeed = spdsave;
	ioctl (fileno (d_prtfp), TIOCSETP, &ttybuf);
#endif SYS5
#endif V4_2BSD
}
ackpack uses the alarm clock */

	seqnum = flags & 03;

#ifdef D_DBGLOG
	d_dbglog ("d_rdport", "Received packet '%s'", packet);
#endif D_DBGLOG

	switch (d_ackpack (packet[TYPEOFF], seqnum))
	{
	    case D_FATAL:
		return (D_FATAL);

	    case D_NONFATAL:
		s_alarm (d_alrmtim);
		continue;

	    default:
	mmdf/lib/dial/d_tai.c   444      0     12       12507  3664425143   7710 #include "util.h"
#include "ll_log.h"
#include  "d_proto.h"
#include "d_structs.h"
#include "cmd.h"

/*      tailor the dial package   */
/*
 * Feb 82       Dan Long        Added type field in dports tailor
 * Mar 82	Dan Long	Fixed access setting for d_lines and
 *				added NULL'ing of next item in structs
 *
 */
extern char *def_trn;
extern struct ll_struct ph_log;
#ifdef lint
char *tai_eptr;		/* keep lint happy -- defined in ../mmdf/mm_tai.c */
#else
extern char *tai_eptr;
#endif
extern int errno;
extern struct dialports  *d_prts;
extern int d_numprts;
extern int d_maxprts;
extern struct directlines  *d_lines;
extern int d_numlines;
extern int d_maxlines;
extern char  *d_calllog;
extern unsigned short d_lrill[];
extern unsigned short d_lxill[];
extern struct speedtab d_spdtab[];

#define CMDDPORT    1
#define CMDDLINE    2
#define CMDDACCT    3
#define CMDDBADIN   4
#define CMDDBADOUT  5
#define CMDDBAD     6
#define	CMDPHLOG    7
#define	DEFTRAN     8

LOCVAR Cmd cmddial[] =
{
    "d_port",   CMDDPORT,   4,
    "dport",    CMDDPORT,   4,
    "dprt",     CMDDPORT,   4,
    "port",     CMDDPORT,   4,
    "d_line",   CMDDLINE,   4,
    "dline",    CMDDLINE,   4,
    "dlin",     CMDDLINE,   4,
    "line",     CMDDLINE,   4,
    "daccount", CMDDACCT,   1,
    "dacct",    CMDDACCT,   1,
    "acct",     CMDDACCT,   1,
    "dbadin",	CMDDBADIN,  8,
    "dbadout",	CMDDBADOUT, 8,
    "dbad",	CMDDBAD,    8,
    "phlog",	CMDPHLOG,   1,
    "deftran",  DEFTRAN,    1,
    0,          0,          0
};

#define CMDPACCESS  1
#define CMDPPREF    2
#define CMDPPOST    3

LOCVAR Cmd cmddparm[] =
{
    "pref",     CMDPPREF,   1,
    "suff",     CMDPPOST,   1,
    "access",   CMDPACCESS, 1,
#ifdef NVRCOMPIL
    "post",     CMDPPOST,   1,
#endif
    0,          0,          0
};


d_tai (argc, argv)              /* tailor some dial parameter */
    int argc;                   /* number of values     */
    char *argv[];               /* list of values       */
{
    register int ind;
    int tspeed;

    switch (cmdsrch (argv[0], argc - 1, cmddial))
    {
	case CMDPHLOG:
	    return (tai_log (argc, &argv[1], &ph_log));

	case DEFTRAN:
	    def_trn = argv[1];
	    return(YES);

	case CMDDPORT:
	    d_prts[d_numprts].p_port = argv[1];
	    d_prts[d_numprts].p_lock = argv[2];
	    d_prts[d_numprts].p_acu  = argv[3];
	    sscanf (argv[4], "%o", &(d_prts[d_numprts].p_speed));
	    d_prts[d_numprts].p_ltype = argv[5];

	    for (ind = 6; ind < argc; ind++)
		if (!lexequ (argv[ind], "="))
		{
		    errno = EINVAL;
		    tai_eptr = argv[ind];
		    argv[ind + 1];
		    return (NOTOK);
		}
		else
		{
		    ind += 2;
		    switch (cmdsrch (argv[ind - 1], argc - ind, cmddparm))
		    {
			case CMDPPREF:
			    d_prts[d_numprts].p_acupref = argv[ind];
			    break;

			case CMDPPOST:
			    d_prts[d_numprts].p_acupost = argv[ind];
			    break;

			case CMDPACCESS:
			    d_prts[d_numprts].p_access = argv[ind];
			    break;

			default:
			    errno = EINVAL;
			    tai_eptr = argv[ind - 1];
			    argv[ind + 1];
			    return (NOTOK);
		    }
	    }
	    if (++d_numprts > d_maxprts)
	    {
		d_prts[--d_numprts].p_port = NULL;
		errno = ENFILE;
		tai_eptr = argv[0];
		return (NOTOK);
	    }
            d_prts[d_numprts].p_port = NULL;
	    return (YES);

	case CMDDLINE:
	    d_lines[d_numlines].l_name = argv[1];
	    d_lines[d_numlines].l_tty  = argv[2];
	    d_lines[d_numlines].l_lock = argv[3];

	    sscanf (argv[4], "%d", &tspeed);
	    for (ind=0; d_spdtab[ind].s_speed != 0; ind++) {
		if (tspeed == d_spdtab[ind].s_index ||
		    strcmp (argv[4], d_spdtab[ind].s_speed) == 0)
		    break;
	    }
	    if (d_spdtab[ind].s_speed == 0) {
		    errno = EINVAL;
		    tai_eptr = argv[4];
		    return(NOTOK);
	    }
	    d_lines[d_numlines].l_speed = d_spdtab[ind].s_index;


	    for (ind = 5; ind < argc; ind++)
	    {
		if (lexequ (argv[ind], "="))
		{
		    if ((ind += 2) >= argc)
		    {
			    errno = EINVAL;
			    tai_eptr = argv[ind - 1];
			    argv[ind + 1];
			return (NOTOK);
		    }
		    if (lexequ (argv[ind - 1], "access"))
			d_lines[d_numlines].l_access = argv[ind];
		    else
		    {
			    errno = EINVAL;
			    tai_eptr = argv[ind - 1];
			    argv[ind + 1];
			    return (NOTOK);
		    }
		}
		else
		{
			    errno = EINVAL;
			    tai_eptr = argv[ind];
			    argv[ind + 1];
		    return (NOTOK);
		}
	    }
	    if (++d_numlines > d_maxlines)
	    {
		d_lines[--d_numlines].l_tty = NULL;
		errno = ENFILE;
		tai_eptr = argv[0];
		return (NOTOK);
	    }
	    d_lines[d_numlines].l_tty = NULL;
	    return (YES);

	case CMDDACCT:
	    d_calllog = argv[1];
	    return (YES);

	case CMDDBADOUT:          /* can't send bare */
	    return (d_vec (argc, &argv[1], d_lxill));

	case CMDDBADIN:           /* can't receive bare */
	    return (d_vec (argc, &argv[1], d_lrill));

	case CMDDBAD:             /* send/receive list the same */
	    if (d_vec (argc, &argv[1], d_lxill) == YES &&
	        d_vec (argc, &argv[1], d_lrill) == YES)
		    return (YES);

    }
    return (NO);                /* not a dial parameter         */
}
/**/

d_vec (argc, argv, vec)
    int argc;
    char *argv[];
    unsigned short vec[];
{
    register int ind;
    int tmp;

    if (argc > 8)
	argc = 8;               /* only 128 bits @ 16 bits/word */
    for (ind = 0; ind < argc; ind++) {
	sscanf (argv[ind], "%o", &tmp);
	/* convert to short */
	vec[ind] = (unsigned) tmp;
    }

    return (YES);
}
timeout -- number of seconds to wait for write to complete
 *
 *
 *     return values:
 *
 *     OK -- no errors
 *
 *     D_FATAL -- fatal error writing on port
 *
 *     D_NONFATAL --mmdf/lib/dial/d_utils.c   444      0     12       11266  3671073164  10274 # include  "util.h"
# include  "d_returns.h"
# include  <pwd.h>
# include  <stdio.h>
# include  "d_proto.h"
# include  "d_structs.h"

/*  4 Sept 81   D. Krafft (Cornell) & D. Crocker
 *                              getuser log calls didn't name routine
 *                              also converted to using getpwuid()
 */


/*
 *     routine which reads a line from the password file and pulls out the
 *     user's name
 */

d_getuser(uid, name, path)
  int  uid;
  char  *name, *path;
    {
    extern struct passwd *getpwuid ();
    register struct passwd *pwdptr;

/*  if we did this once, just used the stuff we saved  */

    if ((pwdptr = getpwuid (uid)) == 0)
      {
#ifdef D_LOG
      d_log("d_getuser", "couldn't find user in password file.\n");
#endif D_LOG
      return(D_FATAL);
      }

    if (name)
      (void) strcpy(name, pwdptr -> pw_name);


    if (path)
    {
	if (strlen(pwdptr -> pw_dir) > 64)
	  {
#ifdef D_LOG
	  d_log("d_getuser", "user's default path too long  '%s'\n",
			pwdptr -> pw_dir);
#endif D_LOG
	  return(D_FATAL);
	  }
	(void) strcpy(path, pwdptr -> pw_dir);
    }

    return(D_OK);
    }

/*
 *     D_CANON
 *
 *     routine which translates a C style string specification into
 *     internal format.  it understands 'escape' characters equivalents
 *     for certain control characters: newline ('\n'), carriage return ('\r'),
 *     tab ('\t'), form feed ('\f'), and delete ('\x').  in addition, any
 *     character can be specified with a backslash followed by up to 3 octal
 *     digits.
 *
 *     in -- pointer to canonical format string
 *
 *     out -- pointer to buffer for internal format equivalent.
 *
 *     the number of characters in the (null-terminated) output string is
 *     returned.  a return value of -1 indicates an error.
 */

d_canon(in, out)
  register char  *in, *out;
    {
    int  value, count, place;
    char  c;

    count = 0;

/*  check for obvious bad format  */

    if (*in++ != '"')
      return(-1);

/*  loop through all the characters.  lots of special cases  */

    while (1)
      {
      c = *in++;

      switch (c)
        {
        default:

            *out++ = c;
            break;

        case  '\0':

            return(-1);

        case  '"':

            *out = '\0';
            return(count);

        case  '\\':

/*  handle the escapes  */

            c = *in++;

            switch (c)
              {
              case  '"':

                  *out++ = '"';
                  break;

              case  '\0':

                  return(-1);

              case  '\\':

                  *out++ = '\\';
                  break;

              case  'r':

                  *out++ = '\15';
                  break;

              case  'n':

                  *out++ = '\12';
                  break;

              case  't':

                  *out++ = '\11';
                  break;

              case  'x':

                  *out++ = '\377';
                  break;

	      case  'b':

		  *out++ = '\10';
		  break;

	      case  '#':

		  *out++ = '\376';
		  break;

/*  allow the specification of characters by up to 3 octal digits after a  */
/*  backslash                                                              */

              default:

                  if (!d_isodigit(c))
                    {
                    *out++ = c;
                    break;
                    }

                  value = c & 07;

                  for (place = 0; place <= 1; place++)
                    {
                    c = *in++;

                    if (d_isodigit(c))
                      value = (value << 3) | (c & 07);
                    else
                      {
                      in--;
                      break;
                      }
                    }

                  *out++ = value;

              }
        }

      count++;
      }
    }

/*
 *     D_ISODIGIT
 *
 *     this routine returns a non-zero value if the argument is an octal
 *     digit, ie. between 0 and 7.
 *
 *     c -- the character to be tested
 */

d_isodigit(c)
  char  c;
    {

    if ((c >= '0') && (c <= '7'))
      return(1);
    else
      return(0);
    }

/*
 *     D_TRYFORK
 *
 *     the purpose of this routine is to fork, retrying 10 times if necessary
 *     -1 is returned if failure after all retries
 */

d_tryfork()
    {
    register int  try, pid;

    for (try = 0; try <= 9; try++)
      {
      pid = fork();

      if (pid != -1)
        return(pid);
      }

    return(-1);
    }


/*
 *     D_MINIMUM
 *
 *     this routine returns the minimum of its two arguments
 *
 *     a, b -- the minimum of these values is returned
 */

d_minimum(a, b)
  register int  a, b;
    {

    if (a < b)
      return(a);
    else
      return(b);
    }
   d_log("d_getuser", "couldn't find user in password file.\n");
#endif D_LOG
      return(D_FATAL);
      }

    if (name)
      (void) strcpy(name, pwdptr -> pw_name);


    if (path)
    {
	if (strlen(pwdptr -> pw_dir) > 64)
	  {
#ifdef D_LOG
	  d_log("d_getuser", "user's default path too long  '%s'\n",
			pwdptr -> pw_dir);
mmdf/lib/dial/d_tscript.c   444      0     12        4030  3671073165  10574 # include  <stdio.h>
# include  "d_returns.h"

/*  maintain a transcript of data i/o over the port.  * the transcript is
 *  not buffered, so that it will reflect the * latest activity, for
 *  debugging.  dhc.
 */

static int  d_tsfd;           /*  file descriptor for transcript file  */

/*
 *     D_TSOPEN
 *
 *     this routine creates the transcript file.
 *
 *     filename -- name of the transcript file to be created.  if this arg
 *                 is zero, then the standard output is used.
 */

d_tsopen(filename)
  register char  *filename;
    {
    extern int  d_errno, errno;

    if (filename == 0)
      {
#ifdef D_LOG
      d_log("d_tsopen", "transcript started on standard output");
#endif D_LOG
      d_tsfd = 1;
      return(D_OK);
      }

    if ((d_tsfd = creat(filename, 0662)) < 0)
      {
#ifdef D_LOG
      d_log("d_tsopen", "can't create transcript file '%s'", filename);
#endif D_LOG
      d_errno = D_TSOPEN;
      return(D_FATAL);
      }

    chmod (filename, 0662);
#ifdef D_LOG
    d_log("d_tsopen", "transcript file '%s' created", filename);
#endif D_LOG
    return(D_OK);
    }

/**/
/*
 *     D_TSCLOSE
 *
 *     this routine is called to stop transcribing
 */

d_tsclose()
    {

/*  do nothing if transcribing is not active  */

    if (d_tsfd <= 0)
      return(D_OK);

    if (d_tsfd != 1)
      (void) close(d_tsfd);

#ifdef D_LOG
    d_log("d_tsclose", "transcript stopped");
#endif D_LOG

    return(D_OK);
    }


/*
 *     D_TSCRIBE
 *
 *     routine which writes on the transcript file
 *
 *     text -- pointer to byets to be written
 *
 *     length -- number of bytes to be written
 */

d_tscribe(text, length)
  register char  *text;
  register int  length;
    {
    extern int  d_errno;

/*  if there's a transcript file and something to write, do it  */

    if ((d_tsfd > 0) && (length > 0))
    {
	if (write(d_tsfd, text, length) != length)
	  {
#ifdef D_LOG
	  d_log("d_tscribe", "error writing on transcript file");
#endif D_LOG
	  d_errno = D_TSWRITE;
	  return(D_NONFATAL);
	  }
    }
    return(D_OK);
    }
                 *out++ = '\\';
                  break;

              case  'r':

                  *out++ = '\15';
                  break;

              case  'n':

                  *out++ = '\12';
                  break;

              case  't':

                  *out++ = '\11';
                  break;

              case  'x':

                  *out++ = '\377';
                  break;

	      case  'b':

		  *out++ = '\10';
		  break;

	      case  '#':

		  *out++ = '\mmdf/lib/dial/d_sndstream.c   444      0     12        3323  3620510400  11066 # include  "d_returns.h"

extern char  d_xqueue[], *d_xqpt;
extern int  d_xqcnt, d_maxtext;
  
/*
 *     D_SNDSTREAM
 *
 *	this routine is called to send data
 *	to the other side without necessarily
 *	implying any packet boundaries.
 *
 *     buffer -- pointer to the data to be sent
 *
 *     nbytes -- number of bytes to be sent
 */

d_snstream(buffer, nbytes)
  char  *buffer;
  int  nbytes;
    {
    register int  length, result;
    register char  *bp;

#ifdef D_DBGLOG
    d_dbglog("d_snstream", "sending  '%.*s'", nbytes, buffer);
#endif D_DBGLOG

    bp = buffer;

    while (nbytes--)
      {
      length = d_ccode(*bp & 0177, d_xqpt);

/*  if the encoding of this character will overflow the text, send off what  */
/*  we've already got.                                                       */

      if ((length + d_xqcnt) > d_maxtext)
        {
	/*  Note that this does not actually send the character that
	 *  would overflow the queue.  It never increments the pointer.
	 *  It overwrites the queued char(s) with a null.  It resets
	 *  (adds 1 to) the nbyte counter.  The continue causes it
	 *  to requeue the same char.
	 */
        *d_xqpt = '\0';

	if ((result = d_sndata(0)) < 0)
          return(result);

        nbytes++;
        continue;
        }

/*  otherwise adjust the count and pointers  */

      d_xqcnt += length;
      d_xqpt += length;
      bp++;
      }

    return(D_OK);
    }

/*
 *     D_SNDEOT
 *
 *     this routine is called to flush the transmit buffer and mark the
 *     packet with an EOT
 */

d_sneot()
    {
    register int  result;

    *d_xqpt = '\0';
#ifdef D_DBGLOG
    d_dbglog("d_sneot", "packet '%s'", d_xqueue);
#endif D_DBGLOG

    result = d_sndata(1);
    return(result);
    }
 if there's a transcript file and something to write, do it  */

    if ((d_tsfd > 0) && (length > 0))
    {
	if (write(d_tsfd, text, length) != length)
	  {
#ifdef D_LOG
	  d_log("d_tscribe", "error writing on transcript file");
#endif D_LOG
	  d_errno = D_TSWRITE;
	  return(D_NONFATAL);
	  }
    }
mmdf/lib/dial/d_setbuff.c   444      0     12        2023  3620510400  10520 #
# include  "d_proto.h"
# include  "d_returns.h"
# include  <stdio.h>
# include  "d_structs.h"

/*
 *	D_SETBUFF
 *
 *	Sends the control packet indicating how deeply packets
 *	can be buffered.  Actually, at the moment the only
 *	relevant information is whether buffering is allowed or
 *	not.  The depth of buffering is irrelavent.  However, this
 *	information can be included cheaply, and it may make later
 *	modifications easier, so it is given anyway.
 *
 *	nbuff - the depth of the buffering.
 *		This number is currently 0 to indicate no buffering
 *		(i.e., all packets must be ACKed immediately) or 1
 *		to indicate that one outstanding packet is allowed.
 *
*/

d_setbuff (nbuff)
  int nbuff;
    {
    char tbuff[3], packet[MAXPACKET + 2];
    int length;
    extern int d_snseq;

    tbuff[0] = d_tohex ((nbuff >> 4) & 017);
    tbuff[1] = d_tohex ((nbuff & 017));
    tbuff[2] = '\0';

    d_snseq = d_incseq (d_snseq);
    length = d_bldpack (NBUFF, d_snseq, 1, tbuff, packet);

    return (d_snpkt(NBUFF, packet, length));
}
t resets
	 *  (adds 1 to) the nbyte counter.  The continue causes it
	 *  to requeue the same char.
	 */
        *d_xqpt = '\0';

	if ((result = d_sndata(0)) < 0)
          return(result);

        nbytes++;
        continue;
        }

/*  otherwise adjust the count and pointers  */

      d_xqcnt += length;
      d_xqpt += length;
      bp++;
      }

    return(D_OK);
    }

/*
 *     D_SNDEOT
 *
 *     this routine is called to flush the transmit buffer and mark the
 *     packet withmmdf/lib/dial/d_script.c   444      0     12       64120  3664425147  10441 # include  "util.h"
# include  <signal.h>
# include  "d_syscodes.h"
# include  "d_proto.h"
# include  "d_returns.h"
# include  <stdio.h>
# include  <strings.h>
# include  "d_structs.h"
# include  "d_script.h"

/*  Jun 81  D. Crocker    rgetc() tossed out almost all the control
 *                        characters.  changed to toss only null & DEL
 *  Sep 83  G.B. Reilly   added support for script command for prompt
 *          M. Laubach    recognition.
 */

extern char *dupfpath();
extern char *tbldfldir;
extern int errno;
extern FILE *d_scfp;
extern  unsigned short d_lxill[],
		       d_lrill[];
extern int d_baudrate;
extern int d_errno;
extern int d_lxmax;
extern int d_lrmax;
extern int d_debug;
extern int d_xretry;
extern int d_toack;
extern int d_todata;
extern int d_wpack;
#ifdef SYS5
extern unsigned short d_prbitc, d_prbiti, d_prbito, d_prbitl;
extern unsigned short d_scbitc, d_scbiti, d_scbito, d_scbitl;
#else
extern int d_pron, d_proff, d_scon, d_scoff;
#endif SYS5
extern int d_nbuff;
extern int d_didial;
extern  FILE * d_prtfp;
/*** prompt varibable ******************** reilly@udel-relay     ***/
int d_prompt = 0;         /* global prompt var.  =0 means disabled */
                          /* <>0 is ascii value of prompt character*/

#define MAXNUMS 5
#define NOPARSE -1
#define IGNORE -2
#define ANYARGS -3

/*  structure defining the legal script file commands, their type,
 *  and legal number of fields.
*/
struct scrcmds
{
    char   *s_cmdname;		  /*  command string  */
    int     s_cmdtype;		  /*  command type number  */
    int     s_cmdminfields;	  /*  min number of fields for command  */
    int     s_cmdmaxfields;       /*  max number of fields allowed */
}               d_sccmds[] =
{
                    "dial", S_DIAL, 2, 2,
                    "conn", S_DIAL, 2, 2,
                    "phone", S_DIAL, 2, 2,
                    "xillegal", S_XILL, 2, 2,
                    "rillegal", S_RILL, 2, 2,
                    "xmitill", S_XILL, 2, 2,
                    "recvill", S_RILL, 2, 2,
                    "xmitpack", S_XPCK, 2, 2,
                    "recvpack", S_RPCK, 2, 2,
                    "xmitsize", S_XPCK, 2, 2,
                    "recvsize", S_RPCK, 2, 2,
                    "transmit", S_XMIT, 2, 2,
                    "send", S_XMIT, 2, 2,
                    "xmit", S_XMIT, 2, 2,
                    "receive", S_RECV, 3, 3,
                    "recv", S_RECV, 3, 3,
                    "start", S_GO, 1, 1,
                    "go", S_GO, 1, 1,
                    "end", S_END, 1, 1,
                    "stop", S_END, 1, 1,
                    "bye", S_END, 1, 1,
                    "nrexmit", S_RETR, 2, 2,
                    "waitack", S_TOAK, 2, 2,
                    "waitdata", S_TODA, 2, 2,
		    "{", S_SELST, 1, 1,
		    "}", S_SELEND, 1, 1,
		    "alternate", S_ALT, 1, 1,
		    "case", S_ALT, 1, 1,
		    "cmnt", S_REM, IGNORE, IGNORE,
		    "rem", S_REM, IGNORE, IGNORE, 
		    "rem:", S_REM, IGNORE, IGNORE, 
		    "com", S_REM, IGNORE, IGNORE, 
		    "com:", S_REM, IGNORE, IGNORE,
		    "#", S_REM, IGNORE, IGNORE,
		    "log", S_LOG, 2, 2,
		    "abort", S_ABORT, 1, 1,
		    "bill", S_BILL, 3, 3,
		    "replay", S_REPLAY, 1, 1,
		    "mark", S_MARK, 1, 1,
#ifdef SYS5
		    "stty-pr", S_PRTTY, 3, 5,
		    "stty-sc", S_SCTTY, 3, 5,
#else
		    "stty-pr", S_PRTTY, 3, 3,
		    "stty-sc", S_SCTTY, 3, 3,
#endif SYS5
		    "window", S_DBLBUF, 3, 3,
		    "use", S_USEFILE, 2, ANYARGS,
/*** prompt command has one format  "prompt <val>"      reilly@udel-relay ***/
		    "prompt", S_PROMPT, 2, 2,
                    0, 0, 0, 0,
};

char    d_scfile[82],            /*  script file path name  */
        d_rawq[MATCHLEN],	  /*  pushback queue for string matcher  */
       *d_prawq;                  /*  pointer to next available character */
				  /*  in 'd_rawq'.  */

unsigned d_scline;                /*  current line number in script file  */
int     d_nfields = 0;		  /*  current number of "use" fields */
char    *d_fields[MAXFIELDS];     /*  current "use" fields */

int     d_ntries = 2,             /*  number of times to try to dial numbers
				  */
	d_wait = 15,              /*  number of seconds to wait between dial
				  */
				  /*  attempts.                               
				  */
        d_nrawq;		  /*  number of available characters in
				     'd_rawq'  */
int	d_dodrop;		  /*  non zero if a dial has been made  */

LOCVAR int d_gotxill = 0;	  /* non zero if xill command received */
LOCVAR int d_gotrill = 0;	  /* non zero if rill command received */

/*
 *     D_SCRIPT
 *
 *     this routine reads lines from the script file, parses them up, and
 *     then initiates the requested actions.
 */

d_script ()
    {
    register int nselect, result;
    char linebuf[MAXSCRLINE + 2],
	 *fields[MAXFIELDS];
    int nfields;

    nselect = 0;
    d_dodrop = 0;

    for (;;)
    {
	result = d_scrblk ();

	switch (result)
	{
	    case D_OK:
		return(D_OK);

	    case D_FATAL:
	    case D_EOF:
	    case D_NFIELDS:
	    case D_QUOTE:
	    case D_UNKNOWN:
		return (D_FATAL);

	    case D_NONFATAL:
		/*  An error that may be recoverable.  If there is
		 *  an alternate, use it.
		 */
		if (nselect <= 0)
		    /*  Not within a select block; therefore, not alt  */
		    return (D_FATAL);

		/*  We are within a select block;  go to the alternate  */
		if ((result = d_nxtalt ()) < 0)
		    return (result);
		switch (result)
		{
		    case S_ALT:
			continue;

		    case S_SELEND:
			/*  no more alternates;  return error  */
			return (D_FATAL);

		    default:
			d_scerr ("Bad syntax scriptfile");
			return (D_FATAL);
		}


	    case S_SELST:
		/*  The next line had better be an alternate  */
		if (d_cmdget (linebuf, &nfields, fields, d_scfp) != S_ALT)
		{
		    d_scerr ("Missing 'alternate' after 'begin'");
		    return (D_FATAL);
		}
		nselect++;
		continue;

	    case S_SELEND:
		/*  verify that there was a select block being
		 *  looked at.  If so, then the last alternate
		 *  was the successful one.  Continue normally.
		 */
		if (nselect-- <= 0)
		{
		    d_scerr ("Inappropriate select end");
		    return (D_FATAL);
		}
		continue;

	    case S_ALT:
		/*  First, make sure the context was correct  */
		if (nselect <= 0)
		{
		    d_scerr ("Inappropriate alt\n");
		    return (D_FATAL);
		}
		/*  If we get here, then an alternate within a select
		 *  was completed successfully.  Move on.
		 */
		if ((result = d_selend ()) < 0)
		    return (result);
		nselect--;
		switch (result)
		{
		    case S_SELEND:
			continue;

		    default:
			d_scerr ("error in format of script file");
			return (D_FATAL);
		}

	    default:
		if (result < 0)
		    return (result);
		d_scerr ("d_script", "Internal error: result = %d", result);
		return (D_FATAL);
	}

    }
}

d_scrblk ()
    {
    char linebuf[MAXSCRLINE + 2],
         *fields[MAXFIELDS];
    int command, result, nfields;

    for (;;)
    {
	/*  read the command  */
	command = d_cmdget (linebuf, &nfields, fields, d_scfp);
	if (command < 0)
	    return (command);


	result = d_cmdproc(command, nfields, fields);
	if (result == D_CONTIN)
	    continue;
	return (result);
    }
}

d_cmdproc (command, nfields, fields)
  int command, nfields;
  char *fields[];
    {
    register int result, word, i;
    register char *ptr;
 
    /*  switch out on the command type.  for most of the commands,
     *  there is a routine to handle them.
     */
    switch (command)
    {
	case S_EOF:
	    /*  Not really a script command.  This is returned to
	     *  indicate an unexpected EOF on the script input file.
	     *  Try to revert to a previous script file.  If there is
	     *  none, then this really is an unexpected EOF.  Return
	     *  an error to indicate so.
	     */
	    if (d_scclose() > 0)
		return (D_CONTIN);
	    return (D_EOF);

	case S_DBLBUF:
	    /*  Can use 'd_nbuff' as a counter of the number of
	     *  outstanding packets to allow if we later mod the
	     *  code to allow more than double buffering.
	     *  See the file 'd_packet.c'
	     */
	    result = atoi (fields[1]);
#ifdef D_LOG
	    if ((result != 1) &&
		(result != 2)   )
		d_log("d_cmdproc", "Illegal buffer number, %d", result);
	    else
#endif D_LOG
		d_nbuff = result;

	    /*  This variable will be non-zero if the master startup
	     *  routine should transmit a packet telling the other
	     *  side about the buffering window.  This packet can not
	     *  be transmitted to sites running the old code.
	     */
	    d_wpack = atoi (fields[2]);
	    return (D_CONTIN);

	case S_USEFILE:
	    /*  takes additional input from another file until
	     *  that file is exhausted.
	     */

	    fields[1] = dupfpath(fields[1], tbldfldir);    

	    for (i = 2; i <= nfields; i ++)
	    {
		if (*fields[i] == '"') fields[i]++;
		if (ptr = rindex(fields[i], '"')) *ptr = '\0';
	    }

	    if (d_scopen (fields[1], nfields, &fields[0]) < 0)
		return (D_FATAL);
	    return (D_CONTIN);

	case S_PRTTY:
#ifdef SYS5
	    sscanf(fields[1], "%o", &d_prbitc);
	    sscanf(fields[2], "%o", &d_prbiti);
	    sscanf(fields[3], "%o", &d_prbito);
	    sscanf(fields[4], "%o", &d_prbitl);

#ifdef D_LOG
	    d_log("d_cmdproc", "%s %o %o %o %o", fields[0],
		d_prbitc, d_prbiti, d_prbito, d_prbitl);
#endif D_LOG

#else /* SYS5 */
	    /*  Could use fields[1&2], but let's check consistancy  */
	    sscanf(fields[1], "%o", &d_pron);
	    sscanf(fields[2], "%o", &d_proff);
#ifdef D_LOG
	    d_log("d_cmdproc", "%s %o %o", fields[0], d_pron, d_proff);
#endif D_LOG
#endif SYS5
	    return (D_CONTIN);

	case S_SCTTY:
#ifdef SYS5
	    sscanf(fields[1], "%o", &d_scbitc);
	    sscanf(fields[2], "%o", &d_scbiti);
	    sscanf(fields[3], "%o", &d_scbito);
	    sscanf(fields[4], "%o", &d_scbitl);

#ifdef D_LOG
	    d_log("d_cmdproc", "%s %o %o %o %o", fields[0],
		d_scbitc, d_scbiti, d_scbito, d_scbitl);
#endif D_LOG

#else

	    sscanf(fields[1], "%o", &d_scon);
	    sscanf(fields[2], "%o", &d_scoff);
#ifdef D_LOG
	    d_log("d_cmdproc", "%s %o %o", fields[0], d_scon, d_scoff);
#endif D_LOG
#endif SYS5
	    return (D_CONTIN);

	case S_MARK:
	    d_mark ();
	    return (D_CONTIN);

	case S_REPLAY:
	    d_replay ();
	    return (D_CONTIN);

	case S_LOG:
	    /*  Force this to always log  */
	    d_plog (-1, "%s", fields[1]);
	    return (D_CONTIN);

	case S_BILL:
	    d_didial = 1;  /* pretend we're dialport for dial_log purposes */
	    d_cstart(fields[1], fields[2]);
	    return (D_CONTIN);

	case S_REM:
	    return (D_CONTIN);

	case S_ABORT:
	    return (D_FATAL);

	case S_ALT:
	case S_SELST:
	case S_SELEND:
	    return (command);

	case S_DIAL:        /* get line & make raw mode for matching */
	    /*  Make sure any previous connections are cleaned up  */
	    if (d_dodrop != 0)
	    {
		d_dodrop = 0;
		d_drop ();
	    }

	    /*  Don't let input from last dial get in this one's queue  */
	    d_mark ();

	    if (d_scdial (fields[1]) < 0)
		return (D_NONFATAL);
	    d_dodrop = 1;
	    return (D_CONTIN);

	case S_XMIT: 
	    if ((result = d_scxmit (fields[1])) < 0)
		return (result);
	    return (D_CONTIN);

	case S_RECV: 
	    if ((result = d_screcv (fields[1], fields[2])) < 0)
	        return (result);
	    return (D_CONTIN);

	case S_GO:          /* regular tty mode during session */
/*** expanded to call and log ttscript when prompt active ***/
/*** necessary to get raw mode for control chars          ***/
/*** 9/27/83   laubach@udel-relay                         ***/
	    if (d_prompt == 0) {
                if (d_ttproto (d_baudrate) < 0)
		return (D_NONFATAL);
            } 
            else {
                if (d_ttscript ( d_baudrate) < 0)
                return (D_NONFATAL);
#ifdef D_DBGLOG
                d_dbglog("d_cmdproc","ttscript selected for raw prompt mode");
#endif D_DBGLOG
            }
	    return (D_OK);

	case S_END: 
	    return (D_OK);

	case S_XPCK: 
	    result = atoi (fields[1]);

	    if ((result < MINPATHSIZ) || (result > MAXPACKET))
	    {
		d_scerr ("illegal max transmit packet length: %d", result);
		d_errno = D_SCRERR;
		return (D_NONFATAL);
	    }

#ifdef D_DBGLOG
	    d_dbglog ("d_script", "setting 'd_lxmax' to %d", result);
#endif D_DBGLOG
	    d_lxmax = result;
	    return (D_CONTIN);

	case S_RPCK: 
	    result = atoi (fields[1]);

            if ((result < MINPATHSIZ) || (result > MAXPACKET))
	    {
		d_scerr ("illegal max receive packet length");
		d_errno = D_SCRERR;
		return (D_NONFATAL);
	    }

#ifdef D_DBGLOG
	    d_dbglog ("d_script", "setting 'd_lrmax' to %d", result);
#endif D_DBGLOG
	    d_lrmax = result;
	    return (D_CONTIN);

	case S_XILL: 
	    if (!d_gotxill)  { /* zero the vector on first S_XILL command */
		d_gotxill = 1;
#ifdef D_DBGLOG
		d_dbglog("d_script", "zeroing xill vector");
#endif D_DBGLOG
		for (word = 0; word < 8; word++)
		    d_lxill[word] = 0;
	    }

	    if ((result = d_scill (fields[1], d_lxill)) < 0)
		return (result);

#ifdef D_DBGLOG
	    d_dbglog ("d_script", "d_lxill = %o %o %o %o %o %o %o %o",
			d_lxill[0], d_lxill[1], d_lxill[2], d_lxill[3],
			d_lxill[4], d_lxill[5], d_lxill[6], d_lxill[7]);
#endif D_DBGLOG
	    return (D_CONTIN);

	case S_RILL: 
	    if (!d_gotrill)  { /* zero the vector on first S_RILL command */
		d_gotrill = 1;
#ifdef D_DBGLOG
		d_dbglog("d_script", "zeroing rill vector");
#endif D_DBGLOG
		for (word = 0; word < 8; word++)
		    d_lrill[word] = 0;
	    }

	    if ((result = d_scill (fields[1], d_lrill)) < 0)
		return (result);

#ifdef D_DBGLOG
	    d_dbglog ("d_script", "d_lrill = %o %o %o %o %o %o %o %o",
			d_lrill[0], d_lrill[1], d_lrill[2], d_lrill[3],
			d_lrill[4], d_lrill[5], d_lrill[6], d_lrill[7]);
#endif D_DBGLOG
	    return (D_CONTIN);

	case S_RETR:
	    result = atoi (fields[1]);
	    if (result <= 0)
		d_scerr("d_script", "Bad retry value: %d", result);
	    else
		d_xretry = result;
	    return (D_CONTIN);

	case S_TOAK:
	    result = atoi (fields[1]);
	    if (result <= 0)
		d_scerr ("d_script", "Bad ack time out value: %d", result);
	    else
		d_toack = result;
	    return (D_CONTIN);

	case S_TODA:
	    result = atoi (fields[1]);
	    if (result <= 0)
		d_scerr ("d_script", "Bad data time out value: %d", result);
	    else
		d_todata = result;
	    return (D_CONTIN);

/*** prompt has one field which is the decimal ascii value of the prompt ***/
/*** character                                        reilly@udel-relay  ***/
	case S_PROMPT:
	    d_prompt = atoi (fields[1]);
#ifdef D_DBGLOG
	    d_dbglog ("d_script", "Prompt ASCII %d (decimal)", d_prompt);
#endif D_DBGLOG
	    return (D_CONTIN);

	default: 
	    d_scerr ("internal error -- unknown command %d", command);
	    return (D_FATAL);
    }
}

d_cmdget (linebuf, nfields, fields, channel)
  int *nfields;
  char *linebuf, *fields[];
  FILE *channel;
    {
    register int result;
    char *nstart;
    int command, nwantmin, nwantmax;
    register struct scrcmds *cmdpt;
   
    /*  read a line from the script file  */
again:
    if ((result = d_scgetline (linebuf, channel)) < 0)
	return (result);

    if (result == 0)
	return (S_EOF);

#ifdef D_DBGLOG
    d_dbglog ("d_scrproc", "line %d - '%s'", d_scline, linebuf);
#endif D_DBGLOG

    /*  First, seperate off the first field of the input line  */
    switch (d_getword(linebuf, &fields[0], &nstart))
    {
	case -1:
	    /*  end of line.  I.e, no line.  Try again  */
	    goto again;

	case -2:
	    /*  unterminated quote.  */
	    d_scerr ("quoted string missing end quote");
	    return (D_QUOTE);

	case 0:
	    /*  Found a word OK  */
	    break;

	default:
	    d_scerr ("unknown return from d_getword");
	    return (D_FATAL);
    }

    /*  search the command list for the string in the first field
     *  of the line.
     */
    cmdpt = d_sccmds;
    for(;;)
    {
	if (cmdpt -> s_cmdname == 0)
	{
	    d_scerr ("command not known");
	    return (D_UNKNOWN);
	}

	if (strcmp (fields[0], cmdpt -> s_cmdname) != 0)
	{
	    cmdpt++;
	    continue;
	}

	command = cmdpt -> s_cmdtype;
	nwantmin = cmdpt -> s_cmdminfields;
	nwantmax = cmdpt -> s_cmdmaxfields;
	break;
    }

    /*  if this is meant to be tossed, go get another line */
    if (nwantmin == IGNORE)
	goto again;  

    /*  if this is not meant to be parsed further, don't  */
    if (nwantmin == NOPARSE)
    {
	fields[1] = nstart;
	return (command);
    }

    /*  parse the rest of the line and make sure the right number
     *  of fields are present.
     */
    *nfields = d_linparse (&fields[1], nstart, MAXFIELDS-1);
    if (*nfields < 0)
        switch (*nfields)
	{
	    case -1: 
		d_scerr ("too many fields");
		return (D_NFIELDS);

	    case -2: 
		d_scerr ("quoted string missing end quote");
		return (D_QUOTE);

	    case -3: 
		d_scerr ("backquote appeared at end of line");
		return (D_QUOTE);

	    case -4:
		d_scerr ("invalid $ variable used");
		return (D_FATAL);

	    case -5:
		d_scerr ("line too long");
		return (D_FATAL);
	
	    default: 
		d_scerr ("unknown error from line parser");
	    	return (D_FATAL);
	}

    if ( (nwantmin == ANYARGS || *nfields >= (nwantmin - 1)) &&
         (nwantmax == ANYARGS || *nfields <= (nwantmax - 1)) )
	return (command);

    /*  If it gets here, then this command had wrong # of fields  */
    d_scerr ("command has %d fields", (*nfields+1));
    return (D_NFIELDS);
}




d_selend ()
    {
    register int command;
    char linebuf[MAXSCRLINE + 2],
	 *fields[MAXFIELDS];
    int nfields;

    for (;;)
    {
	command = d_cmdget (linebuf, &nfields, fields, d_scfp);
	if (command < 0)
	    return (command);
	switch (command)
	{
	    default:
		continue;

	    case S_SELEND:
		return (S_SELEND);

	    case S_SELST:
		d_selend ();
		continue;

	}
    }
}

d_nxtalt ()
    {
    register int command;
    char linebuf[MAXSCRLINE + 2],
	 *fields[MAXFIELDS];
    int nfields;

    for (;;)
    {
	command = d_cmdget (linebuf, &nfields, fields, d_scfp);
	if (command < 0)
	    return (command);
	switch (command)
	{
	    default:
		continue;

	    case S_SELEND:
	    case S_ALT:
		return (command);

	    case S_SELST:
		d_selend ();
		continue;
	}
    }
}

/*
 *     D_SCRDIAL
 *
 *     this routine is called to do the dial command in the script file.
 *     it just calls lower level routines to parse the number string and
 *     do the actual dialing.
 *
 *     number -- number specification string from the script file
 */

d_scdial (number)
register char  *number;
{
    register int    nnumbs,
                    result;
    struct telspeed telnumbs[MAXNUMS];

#ifdef D_DBGLOG
    d_dbglog ("d_scdial", "attempting to call '%s'", number);
#endif D_DBGLOG

/*  parse the number spec, do the dialing, and set the port to raw mode  */

    if ((nnumbs = d_numparse (number, telnumbs, MAXNUMS, d_scfile, d_scline)) < 0)
	return (nnumbs);

    if ((result = d_connect (telnumbs, nnumbs, d_ntries, d_wait)) < 0)
	return (result);

    return (D_OK);
}

/*
 *     D_SCRXMIT
 *
 *     this routine is called to transmit a string given in the script file.
 *     the string is converted to standard internal format and then passed
 *     to a lower transmission routine.
 *
 *     string -- string to be transmitted in 'C' style
 */

d_scxmit (string)
register char  *string;
{
    register int    length;
    char    canonstr[MAXSCRLINE];

    if (*string == '"')
	if ((length = d_canon (string, canonstr)) < 0)
	{
	    d_scerr ("transmit string format error");
	    return (D_FATAL);
	}

    if (d_xstring (canonstr, length) < 0)
	return (D_FATAL);

    return (D_OK);
}

/*
 *     D_SCRRECV
 *
 *     this routine is called to wait until a given string is received on
 *     the port.  the routine will return when the sting has been identified.
 *     there is also a timer that is set to cause the routine to return with
 *     an error if the string is not received within the specified interval.
 *
 *     string -- the string to be watched for
 *
 *     timestr -- a timeout value, in seconds, given as an ascii string
 */

d_screcv (string, timestr)
char   *string,
       *timestr;
{
    register unsigned int   timeout;
    register int    length,
                    result;
    char    canonstr[MAXSCRLINE];

#ifdef D_DBGLOG
    d_dbglog ("d_screcv", "looking for '%s' in '%s' seconds", string, timestr);
#endif D_DBGLOG

/*  convert the transmit string, check its length, and convert the timeout  */
/*  value.                                                                  */

    if ((length = d_canon (string, canonstr)) < 0)
    {
	d_scerr ("receive string format error");
	return (D_FATAL);
    }

    if (length > MATCHLEN)
    {
	d_scerr ("receive string too long");
	return (D_FATAL);
    }

    if ((result = atoi (timestr)) < 1)
    {
	d_scerr ("illegal timeout value");
	return (D_FATAL);
    }
    timeout = result;            /* convert to unsigned */

/*  set up the timer  */

    if (setjmp (timerest)) {
	d_scerr ("no match for '%s' after %d seconds", string, timeout);
	d_errno = D_TIMEOUT;
	return (D_NONFATAL);
    }
    s_alarm ((unsigned) timeout);

/*  do the matching  */

    while ((result = d_scmatch (canonstr)) == D_NO)
	d_rgetc ();

    s_alarm (0);

    switch (result)
    {
	case D_OK:
	    return (D_OK);

	case D_NONFATAL:
	    d_scerr ("eof on port while trying match");
	    d_errno = D_PORTEOF;
	    return (D_NONFATAL);

	case D_FATAL:
	    d_scerr ("read error on port while trying match");
	    d_errno = D_PORTRD;
	    return (D_FATAL);

	case D_INTRPT:
	    d_scerr ("no match for '%s' after %d seconds", string, timeout);
	    d_errno = D_TIMEOUT;
	    return (D_NONFATAL);
    }

    d_errno = D_SYSERR;
    return (D_FATAL);
}


/*
 *     D_SCILL
 *
 *     this routine is called to translate a string specifying illegal
 *     characters for either transmission or recption, and set the bits
 *     corresponding to those characters in the given bit vector.
 *
 *     string -- pointer to character specification string in canonical
 *               format
 *
 *     bitvector -- pointer to bit vector
 */

d_scill (string, bitvector)
char   *string;
unsigned short bitvector[];
{
    register int    bit,
                    length;
    register char  *cp;
    char    canonstr[MAXSCRLINE];

/*  translate the character string  */

    if ((length = d_canon (string, canonstr)) < 0)
    {
	d_scerr ("error in illegal character specification");
	return (D_FATAL);
    }

/*  run through the bytes in the converted string and set the bit  */
/*  corresponding to each one                                      */

    cp = canonstr;

    for (bit = 0; bit < length; bit++)
	d_setbit (*cp++, bitvector);

    return (D_OK);
}


/*
 *     D_SCMATCH
 *
 *     this routine checks for a match between the string and the input
 *     stream.
 *
 *     string -- string to be matched in the input stream
 *
 *
 *     return values:
 *
 *          D_OK -- the string has been found
 *
 *          D_NO -- the string cannot be matched with the current starting
 *                character.  delete the first character and try again.
 *
 *          D_NONFATAL -- eof on port while reading characters
 *
 *          D_FATAL -- read error on port
 *
 *          D_INTRPT -- no match after the specified time
 */

d_scmatch (string)
char   *string;
{
    register int    c,
                    result;

    if (*string == '\0')
	return (D_OK);

    if ((c = d_rgetc ()) < D_OK)
	return (c);

    if (c == *string)
    {
	result = d_scmatch (++string);

	if (result < D_OK)
	    d_unrgetc (c);

	return (result);
    }
    else
    {
	d_unrgetc (c);
	return (D_NO);
    }
}

/*
 *     D_RGETC
 *
 *     this routine is used to read characters from the port in raw mode.
 *     to allow matching of strings in the input stream, this function
 *     implements a look behind facility.  the port is read only if the
 *     push back list is empty.
 */

d_rgetc ()
{
    register int    result;
    int     theval;
    char c;

/*  if the queue isn't empty, use what it has  */

    if (d_nrawq)
    {
	d_nrawq--;
	return (*d_prawq--);
    }

/*  have to read the port.  watch for timeout interrupts  */

    while ((theval = d_getc (d_prtfp)) != EOF)
    {
	c = toascii (theval);    /*  filter out some of the junk characters */
	if ((result = d_tscribe ((char *) &c, 1)) < 0)
	    return (result);

	switch (c)
	{                         /* let controls go by, except         */
	    case '\000':          /* skip null & DEL, because they are  */
	    case '\177':          /* almost always just noise           */
		continue;
	}

	return (c);
    }

    if (feof (d_prtfp))
	return (D_NONFATAL);

    if (errno == EINTR)
	return (D_INTRPT);

    return (D_FATAL);
}


/*
 *     D_UNRGETC
 *
 *     this routine pushes back a character onto the look behind queue
 *     used by the 'd_rgetc' routine
 *
 *     c -- the character to be pushed back
 */

d_unrgetc (c)
char    c;
{

/*  set up the queue if it isn't ready  */

    if (d_prawq <= 0)
    {
	d_prawq = d_rawq;
	*d_prawq = c;
	d_nrawq = 1;
	return;
    }

/*  otherwise, stick it on the end  */

    *++d_prawq = c;
    d_nrawq++;
}

/*
 *     D_XSTRING
 *
 *     this routine is called to transmit strings on the port.  If the
 *     string contains octal 377, then the function pauses for 1 second
 *     before continuing.
 *
 *     string -- pointer to string
 *
 *     length -- number of characters in string to be sent.
 */

d_xstring (string, length)
register char   *string;
int     length;
{
    register int    result;

#ifdef D_DBGLOG
    d_dbglog ("d_xstring", "sending '%s', length %d", string, length);
#endif D_DBGLOG

    sleep ((unsigned) 2);       /* permit any needed line-settling      */
    for ( ; length--; string++)
    {
	/* NOSTRICT */
	if ((int)*string == -1)
	    sleep ((unsigned) 1);
	else if ((int)*string == -2)
	    d_brkport();
	else
	    if ((result = d_wrtport (string, 1)) < 0)
		break;
        if (*string == '\r')
	    sleep ((unsigned) 1); /* may nned line-settling   */
    }
    sleep ((unsigned) 2);       /* permit any needed line-settling      */

    return (result);
}

/*
 *     D_SCRERR
 *
 *     routine which is called to log errors in the script file.  the
 *     purpose of this is so the name of the file and the line number
 *     can be tacked on in one place.
 *
 *     format -- 'd_log' type format string
 *
 *     a, b, c, d, ... -- variables to be logged
 */

/* VARARGS1 */
d_scerr (format, a, b, c, d, e, f, g, h)
char   *format;
unsigned a, b, c, d, e, f, g, h;
{
#ifdef D_LOG
    char    tmpfmt[256];

    sprintf (tmpfmt, "%s%s", "Error in %s, line %d: ", format);

    d_log ("d_scerr", tmpfmt, d_scfile, d_scline, a, b, c, d, e, f, g, h);
#endif D_LOG
    d_errno = D_SCRERR;
}
tor);

    return (D_OK);
}


/*
 *     D_SCMATCH
 *
 *     this routine checks for a match between the string and the input
 *     stream.
 *
 *     string -- string to be matched in the input stream
 *
 *
 *     return values:
 *
 *          D_OK -- the string has been found
 *
 *          D_NO -- the string cannot be matched with the current starting
 *                character.  delete the first character and try again.
 *
 mmdf/lib/dial/d_rcvdata.c   444      0     12        3121  3620510401  10507 # include  "d_returns.h"

/*
 *     D_RCVDATA
 *
 *     this routine is called to read data bytes from the other side.
 *     if there are currently any unread characters in the internal
 *     read queue, they are returned before another packet is read.
 *
 *     buffer -- place to load the data
 *
 *     nbytes -- the maximum number of bytes that should be returned
 *               if an oet is found, then not all that the caller
 *               requests may be returned.
 *
 *     eot -- pointer to a variable that is set if the packet, or the
 *            one queued, contained an EOT
 */

d_rcvdata(buffer, nbytes, eot)
  register char  *buffer;
  register int  nbytes;
  int  *eot;
    {
    extern char  *d_rdqpt;
    extern int  d_rdqcnt, d_rdeot;
    register int  count;
    int  result;

    count = 0;

    while (1)
      {
      /*  transfer data until the max is reached or this packet
       *  is emptied.
       */
      while (nbytes && d_rdqcnt)
        {
        *buffer++ = *d_rdqpt++;
        count++;
        nbytes--;
        d_rdqcnt--;
        }

      /*  If there are no more packets available, return EOT  */
      if ((d_rdqcnt == 0) && d_rdeot)
        {
        *eot = 1;
        d_rdeot = 0;
        return(count);
        }

      /*  If there is no more room to transfer to, just return  */
      if (nbytes == 0)
        {
        *eot = 0;
        return(count);
        }

      /*  Else we come here.  No more data in this packet, but there
       *  are more packets to read.  Do it.
       */
      if ((result = d_getdata()) < 0)
        return(result);
      }
    }
 port.  If the
 *     string contains octal 377, then the function pauses for 1 second
 *     before continuing.
 *
 *     string -- pointer to string
 *
 *     length -- number of characters in string to be sent.
 */

d_xstring (string, length)
register char   *string;
int     length;
{
    register int    result;

#ifdef D_DBGLOG
    d_dbglog ("d_xstring", "sending '%s', length %d", string, length);
#endif D_DBGLOG

    sleepmmdf/lib/dial/d_quit.c   444      0     12        1060  3620510401  10045 # include  "d_proto.h"
# include  "d_returns.h"

/*
 *     D_SNDQUIT
 *
 *     this routine is called to send a QUIT packet to the other end
 *     and wait for the reply.
 */

d_snquit()
    {
    extern int  d_snseq;
    register int  length, result;
    char  packet[MAXPACKET + 2];

#ifdef D_LOG
    d_log("d_snquit", "sending QUIT");
#endif D_LOG

/*  build and send the QUIT packet  */

    d_snseq = d_incseq(d_snseq);
    length = d_bldpack(QUIT, d_snseq, 1, (char *) 0, packet);

    result = d_snpkt(QUIT, packet, length);

    return(result);
    }
     }

      /*  If there are no more packets available, return EOT  */
      if ((d_rdqcnt == 0) && d_rdeot)
        {
        *eot = 1;
        d_rdeot = 0;
        return(count);
        }

      /*  If there is no more room to transfer to, just return  */
      if (nbytes == 0)
        {
        *eot = 0;
        return(count);
        }

      /*  Else we come here.  No more data in this packet, but there
       *  are more packets to read.  Do it.
     mmdf/lib/dial/d_pglobals.c   444      0     12        5370  3620510401  10676 # include  "d_proto.h"








unsigned short  d_lxill[8],     /*  local transmit illegal codes  */

	        d_lrill[8],     /*  local receive illegal codes  */

        	d_rxill[8],     /*  remote transmit illegal codes  */

	        d_rrill[8],     /*  remote receive illegal codes  */

	        d_lcvec[8];     /*  local transmit encoding vector  */




int  d_lxmax,             /*  local transmit maximum packet size  */

     d_lrmax,             /*  local receive maximum packet size  */

     d_rxmax,             /*  remote transmit maximum packet size  */

     d_rrmax,             /*  remote receive maximum packet size  */

     d_master,              /*  this is non-zero if this module is the  */
			    /*  protocol master.                        */

     d_rcvseq,             /*  the expected sequence number of the next  */
			    /*  packet to be received.                    */
			    /*  protocol lockup caused change: is now the */
			    /*  sequence number of the last packet received */
			    /*  and is incremented when search for new    */
			    /*  packet is initiated                      */

     d_snseq,             /*  the sequence number of the next packet    */
			    /*  be transmitted.                           */
			    /*  Changed like d_rcvseq                   */

     d_maxtext;             /*  the maximum length of the text portion  */
			    /*  of a packet.                            */




char  d_snesc,            /*  escase character to used for encoding  */
			    /*  transmissions.                         */

      d_rcvesc;            /*  escape character for decoding packets  */
			    /*  received.                              */







/*
 *     the following globals are used to implement the DATA transmit
 *     and receive queues
 */

char  d_xqueue[MAXPACKET + 2],       /*  the transmit queue  */

      *d_xqpt =  &d_xqueue[0];       /*  pointer to the next available  */
				     /*  slot in the transmit queue.    */

int  d_xqcnt;                        /*  number of bytes currently loaded  */
				     /*  in the transmit queue             */

char  d_rdqueue[MAXPACKET + 2],      /*  the receive queue  */

      *d_rdqpt = &d_rdqueue[0];      /*  pointer to the next available  */
				     /*  slot in the receive queue.     */

int  d_rdqcnt,                       /*  number of unread characters in  */
				     /*  the receive queue.              */

     d_rdeot;                        /*  non-zero if the packet currently  */
				     /*  on the queue had EOT set.         */








/*
 *     Things which are subject to tailoring for a given installation
 */

char  d_esclist[] = "`~#%$&!|^@";   /*  list of possible escape characters  */
                                    /*  in order of preference              */
 to be logged
 */

/* VARARGS1 */
d_scerr (format, a, b, c, d, e, f, g, h)
char   *format;
unsigned a, b, c, d, e, f, g, h;
{
#ifdef D_LOG
    char    tmpfmt[256];

    sprintf (tmpfmt, "%s%s", "Error in %s, line %d: ", format);

    d_log ("d_scerr", tmpfmt, d_scmmdf/lib/dial/gen   555      0     12          57  3620510401   7060 make -f ../../Makefile.com -f Makefile.real $*
ize  */

     d_rrmax,             /*  remote receive maximum packet size  */

     d_master,              /*  this is non-zero if this module is the  */
			    /*  protocol master.                        */

     d_rcvseq,             /*  the expected sequence number of the next  */
			    /*  packet to be received.                    */
			    /*  protocol lockup caused change: is now the */
			    /*  sequence number of the last packet received */
			    /* mmdf/lib/dial/d_path.c   444      0     12        7724  3620510401  10034 # include  "d_proto.h"
# include  "d_returns.h"
#include <signal.h>
# include  "d_syscodes.h"

/*  Jul 81  D. Crocker      convert maxlen>255 -> 255
 */

/*
 *     D_SNPATH
 *
 *     this routine is called to send a PATH packet.  it is used for both
 *     XPATH and RPATH packets.
 *
 *     type -- the type of the packet to send (XPATH or RPATH)
 *
 *     ack -- the type of acknowledgement to wait for
 *
 *     ackwait -- the amount of time to wait for the acknowledgement
 *
 *     maxleng -- the maximum packet length for this path
 *
 *     bitvector -- the bit vector of illegal characters (local) for this
 *                  path.
 */

d_snpath(type, maxleng, bitvector)
  char  type;
  int  maxleng;
  unsigned short bitvector[];
    {
    extern int  d_snseq;
    register int  length, result;
    char  packet[MAXPACKET + 2], txtbuf[LPATH];

    /*  assemble the text portion  */
    if (maxleng > 255)            /* must fit into 8-bit field          */
    {
#ifdef D_LOG
	d_log("d_snpath", "max packet len > 255 (%d)", maxleng);
#endif D_LOG
	maxleng = 255;            /* should be safe to trim it down     */
    }
    txtbuf[0] = d_tohex((maxleng >> 4) & 017);
    txtbuf[1] = d_tohex(maxleng & 017);

    d_bldhvec(bitvector, &txtbuf[2]);

    /*  build the complete packet and send it  */
    d_snseq = d_incseq(d_snseq);
    length = d_bldpack(type, d_snseq, 1, txtbuf, packet);

    result = d_snpkt(type, packet, length);
    return(result);
}


/*
 *     D_GETPATH
 *
 *     this routine is called to get a path packet of the specified type
 *     from the port.  it thens calls the routine to decode it.
 *
 *     type -- the type of path packet we're looking for
 *
 *     timeout -- the amount of time that we're willing to wait to receive
 *                it
 *
 *     maxleng -- pointer to where the maximum packet length specified in the
 *                packet should be loaded
 *
 *     bitvector -- pointer to the bit vector where the illegal character
 *                  bit vector should be placed
 */

d_getpath(type, maxleng, bitvector)
  char  type;
  int  *maxleng;
  unsigned short bitvector[];
    {
    register int  length, result;
    char  packet[MAXPACKET + 2];

    length = d_watch(packet, type);

    if (length < 0)
      return(length);

/*  send the packet to the decoding routine  */

    result = d_setpath(packet, length, maxleng, bitvector);
    return(result);
    }

/*
 *     D_SETPATH
 *
 *     this routine decodes the text portion of a path packet and loads
 *     the given variables
 *
 *     packet -- pointer to the path packet
 *
 *     length -- length of the path packet
 *
 *     maxleng -- pointer to where the maximum packet length given in the
 *                packet should be loaded
 *
 *     bitvector -- pointer to the bit vector to be loaded
 */

d_setpath(packet, length, maxleng, bitvector)
  char  packet[];
  int  length, *maxleng;
  unsigned short bitvector[];
    {
    extern int  d_errno;
    register int  dig1, dig2, max;
    int  result;

/*  check the length  */

    if (length != LPATH)
      {
#ifdef D_LOG
      d_log("d_setpath", "bad length (%d) path packet received", length);
#endif D_LOG
      d_errno = D_PATHERR;
      return(D_FATAL);
      }

/*  decode the maximum packet length  */

    dig1 = d_fromhex(packet[TEXTOFF]);
    dig2 = d_fromhex(packet[TEXTOFF + 1]);

    if ((dig1 < 0) || (dig2 < 0))
      {
#ifdef D_LOG
      d_log("d_setpath", "bad hex character in path (length '%c%c', type '%c')",
          dig1, dig2, packet[TYPEOFF]);
#endif D_LOG
      d_errno = D_PATHERR;
      return(D_FATAL);
      }

    max = (dig1 << 4) | dig2;

    if ((max < LPATH) || (max > MAXPACKET))
      {
#ifdef D_LOG
      d_log("d_setpath", "bad max packet size (%d) in path packet, type '%c'",
          max, packet[TYPEOFF]);
#endif D_LOG
      d_errno = D_PATHERR;
      return(D_FATAL);
      }

    *maxleng = max;

/*  decode the illegal character string  */

    result = d_bldcvec(&packet[TEXTOFF + 2], bitvector);
    return(result);
    }
prawq--);
    }

/*  have to read the port. mmdf/lib/dial/d_parse.c   444      0     12        7637  3671073166  10237 #
# include  <ctype.h>
# include  <stdio.h>
# include  "d_proto.h"
# include  "d_returns.h"

extern int d_nfields;
extern char *d_fields[];
extern char *strcpy();
extern char *strncpy();

/*
 *     D_LINPARSE
 *
 *     routine which takes a line and breaks it into fields on blanks.  it
 *     simply accumulates strings embedded in quotes without regard to blanks
 *     until the closing quote.  the input string is altered in place by the
 *     routine by the intsertion of nulls where there was blanks.
 *
 *     fields - pointer to array which will be loaded with pointers to the
 *              fields in the input string.
 *
 *     linebuf - pointer to the input line buffer
 *
 *     maxfields - the maximum number of fields allowed on a line
 *
 *     the routine returns -1 if there are more than the indicated maximum
 *     number of fields; -2 for unclosed quoted strings.  in the normal
 *     case the number of fields isolated is returned.
 */

d_linparse(fields, linebuf, maxfields)
  char  *fields[], linebuf[];
  int  maxfields;
    {
    char *cp, *ncp;
    register int nfields, result;

    nfields = 0;

    if ( (result = d_dollar(linebuf)) < 0)
	return (result);

    cp = linebuf;

    for (;;)
    {
	result = d_getword(cp, &fields[nfields], &ncp);
	switch (result)
	{
	    case -1:
		/*  end of line  */
		return (nfields);

	    case -2:
		/*  unterminated quotes  */
		return (-2);

	    default:
		/*  another call, another word  */
		break;
	}
	if (++nfields > maxfields)
	    return (-1);
	cp = ncp;
    }
}



d_getword (linebuf, start, next)
  char *linebuf;
  char **start, **next;
    {
    register char *cp;

    cp = linebuf;

    /*  skip over any leading white space  */
    while ((*cp == ' ') || (*cp == '\t'))
        *cp++ = '\0';

    /*  return -1 to indicate that there was no word  */
    if ((*cp == '\n') || (*cp == '\0'))
    {
        *cp = '\0';
        return (-1);
    }

    /*  save the pointer to the start of the field  */
    *start = cp;

    /*  special case for quoted strings.  embedded blanks are
     *  ignored.  must find another '"' before the newline or null.
     */
    if (*cp == '"')
    {
        cp++;

        while ((*cp != '"') && (*cp != '\n') && (*cp != '\0'))
            cp++;

        if ((*cp == '\n') || (*cp == '\0'))
            return(-2);

        cp++;
    }
    else
        while ((*cp != ' ') && (*cp != '\t') &&
               (*cp != '\n') && (*cp != '\0'))
            cp++;
    *cp++ = '\0';
    *next = cp;
    return (0);
}



d_dollar(line)
  char *line;
    {
    char templine[MAXSCRLINE], number[10];
    register char *cp;
    register int index;
    int changes = 0;
    int len = 0;

    cp = line;

    for ( ; ;)
    {
      switch (*cp)
      {
	case '\0':
	case '\n':
	     if (changes)
	     {
		templine[len++] = '\0';  
	        (void) strcpy(line, templine);
	        line[len] = '\0'; /* parser requires 2 nulls */
	     }

	     return(0);

	case '$': 
	     changes++;
	     cp++;

	     if (*cp == '$')  /* double $$ expands to $ */
	     {
	        if (addout(templine, &len,  cp++, 1))
		    return(-5);
	        break;
	     }

     	     /* gobble up number and expand */
	     for (index = 0; 
		  isdigit(*cp) && (*cp!='\0') && (*cp!='\n') && (index<10);
		  index++)
     	        number[index] = *cp++;
	     number[++index] = '\0';
     	     index = atoi(number);
#ifdef D_DBGLOG
	     d_dbglog("d_dollar", "variable number: %d, max: %d", 
		      index, d_nfields);
#endif
     	     if ((index <= 0) || (index > d_nfields))
     	        return(-4);
	     if (addout(templine,&len, d_fields[index],strlen(d_fields[index])))
		return(-5);
	     break;

	default:
	     if (addout(templine, &len, cp++, 1))
	        return(-5);
             break;
       }
   }        
}	    

addout(buf, len, str, cnt)
char *buf, *str;
int *len, cnt;
{
    if (*len + cnt >= MAXSCRLINE - 2)
	return(1);
    (void) strncpy(&buf[*len],str,cnt);
    *len += cnt;    
    return(0);
}
is altered in place by the
 *     routine by the intsertion of nulls where there was blanks.
 *
 mmdf/lib/dial/d_master.c   444      0     12       12347  3641232065  10422 # include  "util.h"
# include  "d_proto.h"
# include  "d_returns.h"
# include  <signal.h>
# include  "d_syscodes.h"
# include  "d_clogcodes.h"
# include  "ll_log.h"

/*
 *     D_MASCONN
 *
 *     this routine is called by the master protocl module to initiate a
 *     connection to a remote system.  the scriptfile used contains all
 *     of the information necessary to dial the remote machine, set
 *     parameters.
 *
 *     scriptfile -- name of the script file
 *
 *     logfile -- name of the file to be used for logging.  if this arg
 *                is 0, then logging is done on the standard output
 *
 *     tson -- this argument should be non-zero if the protocol module
 *             should keep a transcript of the connection
 *
 *     tsfile -- name of the file in which the transcript should be kept.
 *
 *     debug -- set to non-zero to enable more extensive logging
 *
 */

d_masconn(scriptfile, logfile, tson, tsfile, debug)
  char  *scriptfile, *tsfile;
  struct ll_struct * logfile;
  int  tson, debug;
    {
    extern int  d_debug, d_master, d_snseq, d_rcvseq;
    register int  result;

    /*  open the log and transcript file  */
    d_debug = debug;

    if ((result = d_opnlog(logfile)) < 0)
      return(result);

    d_init();

    if (tson)
      if ((result = d_tsopen(tsfile)) < 0)
        return(result);

    d_master = 1;
    /* numbers will be incremented before use, thus effectively start at 0 */
    d_snseq = 3;
    d_rcvseq = 3;

    /*  open the script file and start interpreting it  */
    if ((result = d_scopen(scriptfile, 0, (char **) 0)) < 0)
      return(result);

    if ((result = d_script()) < 0)
      return(result);

    /*  when the script file pauses, start the protocol  */
    result = d_masstart();
    return(result);
}

/*
 *     D_MASDROP
 *
 *     this routine is called by the master to drop the connection to the
 *     other side.  the port, lock, transcript, and log files are closed.
 *
 *     sndquit -- non-zero if a QUIT sequence should be initiated with
 *                the other side
 *
 *     doscript -- non-zero if the script should be resumed
 */

d_masdrop(sndquit, doscript)
  register int  sndquit, doscript;
    {
    register int  result;

    d_dbglog("d_masdrop", "dropping connection, sndquit %d, doscript %d",
        sndquit, doscript);

    result = 0;

    /*  go through the QUIT sequence if we're supposed to  */
    if (sndquit)
      d_snquit();

    /*  resume the script, if enabled  */
    if (doscript)
      {
#ifdef D_LOG
      d_log("d_masdrop", "Resuming script file.");
#endif D_LOG
      result = d_script();
      }

#ifdef D_LOG
    d_log("d_masdrop", "master dropped connection");
#endif D_LOG

    /*  drop the connection.  close the transcript and log  */
    d_drop();
    d_tsclose();
    d_clslog();

    return(result);
}

/*
 *     D_MASSTART
 *
 *     this routine is called in the master to start up the protocol once
 *     the matching slave routine has been started.  this function exchanges
 *     RESET's with the slave and sets up the escape characters for the
 *     encoding.
 */

d_masstart()
    {
    extern int  d_lxmax, d_lrmax, d_rxmax, d_rrmax, d_maxtext, d_nbuff;
    extern int d_wpack;
    extern int d_errno;
    extern char  d_snesc, d_rcvesc;
    extern unsigned short  d_lxill[], d_lrill[], d_rxill[], d_rrill[],
	d_lcvec[];
    register int  result;

#ifdef D_DBGLOG
    d_dbglog("d_masstart", "Beginning master host startup sequence");
#endif D_DBGLOG

    /*  get the XPATH and RPATH packets from the slave. then build
     *  the local transmit code vector.
     */
    if ((result = d_getpath(XPATH, &d_rxmax, d_rxill)) < 0)
      return(result);

#ifdef D_DBGLOG
    d_dbglog("d_masstart", "XPATH received ok.");
#endif D_DBGLOG

    if ((result = d_getpath(RPATH, &d_rrmax, d_rrill)) < 0)
      return(result);

#ifdef D_DBGLOG
    d_dbglog("d_masstart", "RPATH received ok.");
#endif D_DBGLOG

    /*  If appropriate, send the packet describing how we are buffering
     *  packets.  This cannot be sent to some sites, as they do not
     *  recognize the packet type.
     */
    if (d_wpack != 0)
	d_setbuff (d_nbuff);

    d_orbitvec(d_lxill, d_rrill, d_lcvec);

    d_maxtext = d_minimum(d_lxmax, d_rrmax);
#ifdef D_DBGLOG
    d_dbglog("d_masstart", "Maximum transmit packet size set to %d", d_maxtext);
#endif D_DBGLOG
    d_maxtext -= (LHEADER + LTERM);

    /*  now send the XPATH and RPATH to the slave  */
    if ((result = d_snpath(XPATH, d_lxmax, d_lxill)) < 0)
      return(result);

#ifdef D_DBGLOG
    d_dbglog("d_masstart", "XPATH sent ok.");
#endif D_DBGLOG

    if ((result = d_snpath(RPATH, d_lrmax, d_lrill)) < 0)
      return(result);

#ifdef D_DBGLOG
    d_dbglog("d_masstart", "RPATH sent ok.");
#endif D_DBGLOG

    /*  get the escape code we're to use for encoding text sent
     *  to the remote host.
     */
    if ((result = d_getescape()) < 0)
      return(result);

#ifdef D_DBGLOG
    d_dbglog("d_masstart", "ESCAPE received ok.  Transmit escape '%c'",
	d_snesc);
#endif D_DBGLOG

    if ((result = d_snfescape()) < 0)
      return(result);

#ifdef D_DBGLOG
    d_dbglog("d_masstart", "ESCAPE sent ok.  Receive escape '%c'", d_rcvesc);
#endif D_DBGLOG

#ifdef D_LOG
    d_log("d_masstart", "Master protocol startup completed ok.");
#endif D_LOG

    return(D_OK);
}
sndquit %d, doscript %d",
        sndquit, doscript);

    result = 0;

    /*  go through the QUIT sequence if we're supposed to  */
    if (sndquit)
      d_snquit();

    /*  resume the script, if enabled  */
    if (doscript)
      {
#ifdef D_LOG
      d_log("d_masdrop", "Resummdf/lib/dial/d_log.c   444      0     12       11113  3620510402   7665 # include  "util.h"
# include  "d_returns.h"
# include  <stdio.h>
# include  "d_proto.h"
# include  "d_structs.h"
# include  "ll_log.h"


int  d_debug;           /*  set to non-zero if debug loggin should be done  */

static struct ll_struct *d_logptr;       /*  file pointer to logging file  */


/*
 *     D_OPNLOG
 *
 *     this routine is called to open the log file for appending.  the
 *     date is written as the first entry.
 *
 *     filename -- path name of the logging file
 */

d_opnlog(logptr)
  struct ll_struct * logptr;
    {

    d_logptr =(struct ll_struct *) (logptr);           /* save the pointer */

    if (d_logptr -> ll_level >= LLOGPTR)
	d_debug = 1;
    return(D_OK);
}


/*
 *     D_CLSLOG
 *
 *     this routine is called to close the log file if one was open.
 */

d_clslog()
    {

#ifdef D_DBGLOG
    d_log("d_clslog", "closing log file");
#endif D_DBGLOG

    ll_close(d_logptr);

    return(D_OK);
}

/*
 *     D_LOG
 *
 *     this routine writes entries on the log file.  its use is similar
 *     to printf, and it accepts the same style format strings.
 *
 *     routine -- the name of the subroutine where the call to this function
 *                is made.
 *
 *     format -- printf style format string
 *
 *     a, b, c, d, ... -- variables to be printed
 */

/* VARARGS2 */

d_log(routine, format, a, b, c, d, e, f, g, h)
  char  *routine, *format;
  unsigned  a, b, c, d, e, f, g, h;
    {

#ifdef D_LOG
#ifdef D_DBGLOG
    if (d_debug)        /*  print routine name if debugging  */
	d_dbglog (routine, format, a, b, c, d, e, f, g, h);
    else
#endif D_DBGLOG
	ll_log(d_logptr, LLOGGEN, format, a, b, c, d, e, f, g, h);
#endif D_LOG

    return(D_OK);
}

/*
 *     D_DBGLOG
 *
 *     this routine is called to write debugging logging stuff on the log
 *     file.  the entry is made only if the 'd_debug' variable is non-zero.
 *
 *     routine -- the name of the subroutine where the call to this function
 *                is made.
 *
 *     format -- printf style format string
 *
 *     a, b, c, d, ... -- variables to be printed
 */

/* VARARGS2 */

d_dbglog(routine, format, a, b, c, d, e, f, g, h)
  char  *routine, *format;
  unsigned  a, b, c, d, e, f, g, h;
    {
#ifdef D_DBGLOG
    char fmtbuf[64];

    if (d_debug)
    {
	sprintf (fmtbuf, "%s%s", "%s: ", format);
	ll_log(d_logptr, LLOGBTR, fmtbuf, routine, a, b, c, d, e, f, g, h);
    }
#endif D_DBGLOG
}



/*
/*
 *     D_FILLOG
 *
 *     routine which is used to log messages concerning things found in
 *     files.  the point of this routine is that there are a lot of messages
 *     in this package that concern errors found on lines in files.  i got
 *     tired of putting stuff in the format to 'd_log' calls to write out
 *     the file name and line number, so i put that in this call.  all this
 *     routine does is build a special format string containg fields for the
 *     name and number, and then call 'd_log'.
 *
 *     filename -- name of the file to be put in entry
 *
 *     linenum -- line number in file to be put in entry
 *
 *     routine -- the name of the subroutine where the call to this function
 *                is made.
 *
 *     format -- printf style format string
 *
 *     a, b, c, d, ... -- variables to be printed
 */

/* VARARGS4 */

d_fillog(filename, linenum, routine, format, a, b, c, d, e, f, g, h)
  char  *filename, *routine, *format;
  unsigned  linenum, a, b, c, d, e, f, g, h;
    {
#ifdef D_LOG
    char  tmpfmt[85];

    /*  if a filename is given, make a new format to include
     *  it.  then call the log routine.
     */

    if (filename)
    {
	sprintf (tmpfmt, "%s%s", "%s, line %d: ", format);
	d_log(routine, tmpfmt, filename, linenum, a, b, c, d, e, f, g, h);
    }
    else
	d_log(routine, format, a, b, c, d, e, f, g, h);
#endif D_LOG
}




/*
 *	D_PLOG
 *
 *	Log a message at a given priority.
 *	It is necessary to have this here so as to access the
 *	struct d_logptr.
 *
 *	priority - the priority level to log at.
 *	format, a, b, ... - standard printf stuff
 *
*/

d_plog (priority, format, a, b, c, d, e, f, g, h)
  int priority;
  char *format;
  unsigned a, b, c, d, e, f, g, h;
    {

    ll_log (d_logptr, priority, format, a, b, c, d, e, f, g, h);
}

/*
 *	D_PELOG
 *
 *	Log a message at a given priority and print ERRNO
 *	It is necessary to have this here so as to access the
 *	struct d_logptr.
 *
 *	priority - the priority level to log at.
 *	format, a, b, ... - standard printf stuff
 *
*/

d_pelog (priority, format, a, b, c, d, e, f, g, h)
  int priority;
  char *format;
  unsigned a, b, c, d, e, f, g, h;
    {

    ll_err (d_logptr, priority, format, a, b, c, d, e, f, g, h);
}
f ((max < LPATH) || (max > MAXPACKET))
      {
#ifdef D_LOG
      d_log("d_setpath", "bad max packet size (%d) in path packet, type '%c'",
          max, packet[TYPEOFF]);
#endif D_LOG
      d_errno = D_PATHERR;
      return(D_FATAL);
      }

    *maxleng = max;

/*  decode the illegal character string  */

    result = d_bldcvec(&packet[TEXTOFF + 2], bitvector);
    return(result);
    }
prawq--);
    }

/*  have to read the port. mmdf/lib/dial/d_init.c   444      0     12        1122  3620510402  10026 # include  "d_returns.h"

/*  4 Sep 81  D. Crocker        getuid() was 11-dependent

/*
 *     D_INIT
 *
 *     routine which is called to do any initialization that's required
 *     before the rest of the routines can be used.  all that's done here
 *     now is to get the user's name and path.
 */

d_init()
    {
    extern char  d_uname[], d_upath[];
    extern int  d_errno;
    int  effecid;
    int  realuid;

    getwho (&realuid, &effecid);

    if (d_getuser(realuid, d_uname, d_upath) < 0)
      {
      d_errno = D_INITERR;
      return(D_FATAL);
      }

    return(D_OK);
    }
logptr, LLOGGEN, format, a, b, c, d, e, f, g, h);
#endif D_LOG

    return(D_OK);
}

/*
 *     D_DBGLOG
 *
 *     this routine is called to write debugging logging stuff on the log
 *     file.  the entry is made only if the 'd_debug' variable is non-zero.
 *
 *     routine -- the name of the subroutine where the call to this function
 *                is made.
 *
 *     format -- printf style format string
 *
 *     a, b, c, mmdf/lib/dial/d_escape.c   444      0     12        5341  3620510403  10333 #include  "d_proto.h"
#include <signal.h>
#include  "d_syscodes.h"
#include  "d_returns.h"

/*
 *     D_FINDESC
 *
 *     this routine attempts to find a suitable escape character to be used
 *     by the remote host for packets transmitted to us.  the character is
 *     chosen from the 'd_esclist' by checking each of the proposed escapes
 *     to see if it can be both transmitted by the remote system and received
 *     by us.  the selected character is returned, if one is found; otherwise
 *     D_FATAL.
 */

char
	d_findesc()
    {
    extern char  d_esclist[];
    extern unsigned  short d_lrill[], d_rxill[];
    extern int  d_errno;
    register char  *cp;

    for (cp = d_esclist; *cp; cp++)
      if (!d_testbit(*cp, d_lrill) && !d_testbit(*cp, d_rxill))
        {
#ifdef D_DBGLOG
        d_dbglog("d_findesc", "Character '%c' selected as escape for receiving",
            *cp);
#endif D_DBGLOG
        return(*cp);
        }

#ifdef D_LOG
    d_log("d_findesc", "Can't find suitable escape character");
#endif D_LOG
    d_errno = D_NOESC;
    return(D_FATAL);
}

/*
 *     D_SNDESCAPE
 *
 *     this routine forms an ESCAPE packet and sends it to the remote host
 */

d_snfescape()
    {
    extern char  d_rcvesc;
    extern int  d_snseq;
    register int  length, result;
    char  packet[MAXPACKET + 2], esctemp[4];

#ifdef D_DBGLOG
    d_dbglog("d_snfescape", "sending ESCAPE to other end");
#endif D_DBGLOG

    /*  find an escape character  */
    if ((d_rcvesc = d_findesc()) < 0)
      return(d_rcvesc);

    /*  form text containing the ascii value of the escape
     *  character expressed as a 2 digit hex number and send it.
     */
    esctemp[0] = d_tohex((d_rcvesc >> 4) & 017);
    esctemp[1] = d_tohex(d_rcvesc & 017);
    esctemp[2] = '\0';

    d_snseq = d_incseq(d_snseq);
    length = d_bldpack(ESCAPE, d_snseq, 1, esctemp, packet);

    result = d_snpkt(ESCAPE, packet, length);
    return(result);
}


/*
 *     D_GETESCAPE
 *
 *     this routine watches for an incoming ESCAPE packet from the remote
 *     host.  it thens set the transmit escape character.
 *
 */

d_getescape()
    {
    extern int  d_errno;
    extern char  d_snesc;
    register int  length;
    char  packet[MAXPACKET + 2];

#ifdef D_DBGLOG
    d_dbglog("d_getescape", "looking for ESCAPE from other end");
#endif D_DBGLOG

/*  set timer so we don't wait forever  */

    length = d_watch(packet, ESCAPE);

    if (length < 0)
	return(length);

    if (length != LESCAPE)
    {
#ifdef D_DBGLOG
      d_dbglog("d_escape", "bad ESCAPE packet length (%d)", length);
#endif D_DBGLOG
      d_errno = D_INITERR;
      return (D_FATAL);
    }

      d_snesc = d_fromhex(packet[ESCOFF]);
      d_snesc = (d_snesc << 4) | d_fromhex(packet[ESCOFF + 1]);
      return(D_OK);
    }
rill) && !d_testbit(*cp, d_rxill))
        {
#ifdef D_DBGLOG
        d_dbglog("d_findesc", "Character '%c' selected as escape for receiving",
            *cp);
#endif D_DBGLOG
        return(*cp);
        }

#ifdef D_LOG
    d_log("d_findesc", "Can't find suitable escape character");
#emmdf/lib/dial/d_dglobals.c   444      0     12        5425  3664425151  10702 # include  "util.h"
#ifdef SYS5
# include <termio.h>
#else
# include  <sgtty.h>
# include  "d_proto.h"
#endif SYS5
# include  <stdio.h>
# include  "d_structs.h"



struct dialports  *d_prtpt;    /*  pointer to currently user port in  */
				/*  'd_prts'                          */

struct directlines  *d_ptline;  /*  pointer to currently used line in  */
                                /*  'd_lines'                          */
FILE    *d_prtfp = (FILE *) EOF; /*  port stdio file pointer  */

int  d_lckfd = -1,             /*  lock file descriptor  */
     d_baudrate,                /*  index of baud rate for this connection  */
     d_didial,                  /*  set to non-zero if this is a dial-up  */
                                /*  connection.                           */
     d_errno;                   /*  error code  */

char  d_uname[10],           /*  login name of user  */
      d_upath[50];           /*  login directory path name of user  */


/*
 *     this structure defines the baud rate strings that will be accpeted
 *     in phone number specifications.
 */

struct speedtab  d_spdtab[] =
  {
  "0",       B0,
  "50",      B50,
  "75",      B75,
  "110",     B110,
  "134.5",   B134,
  "134",     B134,
  "150",     B150,
  "200",     B200,
  "300",     B300,
  "600",     B600,
  "1200",    B1200,
  "1800",    B1800,
  "2400",    B2400,
  "4800",    B4800,
#ifdef SYS5
  "7200",    B7200,
#endif SYS5
  "9600",    B9600,
#ifdef SYS5
  "19200",   B19200,
#else
  "19200",   EXTA,
#endif SYS5
  "EXTA",    EXTA,
  "exta",    EXTA,
  "38400",   EXTB,
  "EXTB",    EXTB,
  "extb",    EXTB,
  0,         0,
  };




/*
 *     array of ascii baud rate names indexed by the speed.  used to print
 *     understandable diagnostics.
 */

int d_xretry = NSENDTRY;     /*  Init # of retries to make  */
int d_toack = DACKWAIT;      /*  Init time out time for ack */
int d_todata = DATAWAIT;     /*  Init time out for data  */

#ifdef SYS5
unsigned short d_prbitc = PORTPONC;	/* terminal protocol bit on */
unsigned short d_prbiti = PORTPONI;
unsigned short d_prbito = PORTPONO;
unsigned short d_prbitl = PORTPONL;

unsigned short d_scbitc = PORTSONC;	/* terminal script bits on */
unsigned short d_scbiti = PORTSONI;
unsigned short d_scbito = PORTSONO;
unsigned short d_scbitl = PORTSONL;

#else

int d_pron = PORTPON;       /*  terminal protocol bit on  */
int d_proff = PORTPOFF;     /*  terminal protocal bit off */
int d_scon = PORTSON;       /*  terminal script bits on   */
int d_scoff = PORTSOFF;     /*  terminal script bits off  */

#endif SYS5

int d_nbuff = 1;	    /*  The number of messages to send out before
			     *  requiring an ACK.  Initially, get an ACK
			     *  immediately.
			     */
int d_wpack;		    /*  Non-zero if the window specification
			     *  packet should be sent.
			     */
  */

struct directlines  *d_ptline;  /*  pointer to currently used line in  */
                                /*  'd_lines'                          */
FILE    *d_prtfp = (FILE *) EOF; /*  port stdio file pointer  */

int  d_lckfd = mmdf/lib/dial/d_data.c   444      0     12        2466  3620510403  10011 #include  "d_proto.h"
#include  "d_returns.h"
#include <signal.h>
#include  "d_syscodes.h"


/*
 *     D_SNDDATA
 *
 *     this function is called to send the stuff in the data transmit queue.
 *
 *     eot -- set to non-zero if the EOT bit should be set on the packet
 */

d_sndata(eot)
  register int  eot;
    {
    extern char  d_xqueue[], *d_xqpt;
    extern int  d_snseq, d_xqcnt;
    register int  length, result;
    char  packet[MAXPACKET + 2];

    d_snseq = d_incseq(d_snseq);
    length = d_bldpack(DATA, d_snseq, eot, d_xqueue, packet);

    d_xqcnt = 0;
    d_xqpt = d_xqueue;

    result = d_snpkt(DATA, packet, length);
    return(result);
    }

/*
 *     D_GETDATA
 *
 *     this function is called to read a DATA packet into the receive
 *     data queue.
 */

d_getdata()
    {
    extern char  d_rdqueue[], *d_rdqpt;
    extern int  d_rdqcnt, d_rdeot;
    register int  nout, length;
    char  packet[MAXPACKET + 2];

#ifdef D_DBGLOG
    d_dbglog("d_getdata", "called for more stuff");
#endif D_DBGLOG

    length = d_watch(packet, DATA);

    if (length < 0)
      return(length);


    if ((nout = d_decode(&packet[TEXTOFF], length - 6, d_rdqueue)) < 0)
      return(nout);

    d_rdqcnt = nout;
    d_rdqpt = d_rdqueue;

    if (d_fromhex(packet[FLAGOFF]) & EOFBIT)
      d_rdeot = 1;

    return(D_OK);
    }
"1800",    B1800,
  "2400",    B2400,
  "4800",    B4800,
#ifdef SYS5
  "7200",    B7200,
#endif SYS5
  "9600",    B9600,
#ifdef SYS5
  "19200",   B19200,
#else
  "19200",   EXTA,
#endif SYS5
  "EXTA", mmdf/lib/dial/d_connect.c   444      0     12       44511  3671073170  10561 # include  "util.h"
# include <signal.h>
# include  "d_syscodes.h"
# include  "d_clogcodes.h"
# include  "d_proto.h"
# include  "d_returns.h"
# include  "d_structs.h"
#ifdef SYS5
# include <termio.h>
#else
# include  <sgtty.h>
#endif SYS5
# include  <sys/stat.h>
#ifdef V4_2BSD
# include  <sys/file.h>
#endif V4_2BSD

/*  Jun 81  Dave Crocker    fixed some text, referring to errno
 *  Aug 81  Dave Crocker    added sleep before kill & alarm around
 *                          parent waiting for acu
 *  Sep 81  Dean Krafft     put signal (ALRM,d_catch) before ACUWAIT
 *  Apr 82  Dave Crocker    modify dial timings & lock-file handling
 *  Jun 82  Dave Crocker    do_dial use of kill() moved around
 *                          d_drop() timeouts around modem line fclose()
 *  Jan 84  Dennis Rockwell converted to do sane things with 4.2 select(2)
 *                          by setting non-blocking open option
 *  Feb 84  Dan Long        moved d_cstart to start of phone conversation
 *  Feb 84  Dan Long        added support for line-type selection
 *  Mar 84  Dennis Rockwell converted from select to s_io library
 */

extern int errno;
extern int d_errno;
extern struct directlines  *d_lines;
extern struct directlines  *d_ptline;
extern struct dialports *d_prtpt;
extern struct dialports *d_prts;
extern char d_uname[];
extern int  d_didial;
extern int  d_baudrate;
extern  FILE * d_prtfp;
extern struct speedtab d_spdtab[];
extern char **d_typelist();
extern char *strdup();
extern char *multcat();
extern int  d_lckfd;


/*
 *     D_CONNECT
 *    
 *     this routine is called to make a connection to the remote system.  it
 *     is capable of using direct lines or dial-up connections, as specified
 *     in the connection path table ('numtab').
 *    
 *     numtab -- array of structure whch contain speed indicies and associated
 *               phone numbers or direct connect line names.
 *    
 *     nnumbs -- number of entries in numtab
 *    
 *     ntries -- number of time to retry a connection.
 *    
 *     interval -- number of seconds between connection retries.
 */

d_connect (numtab, nnumb, ntries, interval)
struct telspeed numtab[];
int     nnumb,
        ntries,
        interval;
{
    register int    num,
                    try,
                    result;

#ifdef D_DBGLOG
    d_dbglog ("d_connect", "nnumb %d, ntries %d, interval %d",
	    nnumb, ntries, interval);
#endif

/*  make sure we were given some numbers to dial  */

    if (nnumb <= 0)
    {
#ifdef D_LOG
	d_log ("d_connect", "no connection paths given!");
#endif D_LOG
	d_errno = D_NONUMBS;
	return (D_FATAL);
    }

/*  for each attempt, try each of the paths given  */

    for (try = 1; try <= ntries; try++)
    {
	for (num = 0; num < nnumb; num++)
	{
#ifdef D_DBGLOG
	    d_dbglog ("d_connect", "%d: (speed %d) num '%s'",
			num, numtab[num].t_speed, numtab[num].t_number);
#endif D_DBGLOG
	    if (numtab[num].t_speed == 0)
		result = d_direct (numtab[num].t_number);

	    else
		result = d_dial (numtab[num].t_number, numtab[num].t_speed);

	    if (result != D_NONFATAL)
		return (result);
	}

/*  if we're going to try again, and we're supposed to wait awhile, sleep  */

	if ((try < ntries) && (interval > 0))
	{
#ifdef D_DBGLOG
	    d_dbglog ("d_connect", "sleeping for %d seconds", interval);
#endif D_DBGLOG
	    sleep ((unsigned) interval);
	}
    }

#ifdef D_LOG
    d_log ("d_connect", "couldn't make connection.");
#endif D_LOG
    return (D_FATAL);
}

/*
 *     D_DIRECT
 *
 *     routine called to try to make a direct connection using the line
 *     given as the argument.
 *
 *     linename -- name of direct connect line
 */

d_direct (linename)
register char  *linename;
{
    int     fd;
    register int    result;
#ifdef D_LOG
    register int    i;
#endif D_LOG
#ifdef SYS5
    struct termio hupbuf;
#endif SYS5

/*  see if a direct line with the given name is available  */

    if ((result = d_avline (linename)) < 0)
	return (result);

    d_baudrate = d_ptline -> l_speed;

#ifdef D_LOG
    for (i=0; d_spdtab[i].s_speed != 0; i++) {
        if (d_spdtab[i].s_index == d_baudrate) {
	   d_log ("d_direct", "Trying direct connection on %s at %s.",
		d_ptline -> l_tty, d_spdtab[i].s_speed);
	   break;
	}
    }
#endif D_LOG

/*  found a line.  set an alarm in case the open hangs  */

    if (setjmp (timerest)) {
#ifdef D_LOG
	    d_log ("d_direct", "direct line open timeout for %s",
			d_ptline -> l_tty);
#endif D_LOG
	d_errno = D_PORTOPEN;
	return (D_FATAL);
    }
    s_alarm (CONNWAIT);
#ifndef V4_2BSD
    if ((fd = open (d_ptline -> l_tty, 2)) >= 0) {
#else V4_2BSD
    if ((fd = open (d_ptline -> l_tty, O_RDWR | O_NDELAY)) >= 0) {
#endif V4_2BSD

#ifdef SYS5
	ioctl (fd, TCGETA, &hupbuf);
	hupbuf.c_cflag |= HUPCL;
	if (ioctl (fd, TCSETA, &hupbuf) < OK) {
#else
	if (ioctl (fd, TIOCHPCL, 0) < OK) {
#endif SYS5
#ifdef D_LOG
	    d_log ("d_direct", "problem setting close-on-hangup; errno = %d", errno);
#endif D_LOG
	}
#ifdef	TIOCEXCL
	if (ioctl (fd, TIOCEXCL, 0) < OK) { /* gain exclusive access [mtr] */
#ifdef D_LOG
	    d_log ("d_direct",
			"problem gaining exclusive access; errno = %d", errno);
#endif D_LOG
	}
#endif	TIOCEXCL
    }
    s_alarm (0);

    if (fd < 0)
    {
#ifndef V4_2BSD
	if (errno == EINTR) {
#ifdef D_LOG
	    d_log ("d_direct", "direct line open timeout for %s",
			d_ptline -> l_tty);
#endif D_LOG
	} else
#endif V4_2BSD
#ifdef D_LOG
	    d_log ("d_direct", "direct line open errno %d for %s",
			errno, d_ptline -> l_tty);
#endif D_LOG

	d_errno = D_PORTOPEN;
	return (D_FATAL);
    }

/* stdio for input and straight write() for output */
    d_prtfp = fdopen (fd, "r");
    if (d_ttsave (d_prtfp, d_ptline -> l_tty) < 0 ||
	    d_ttscript (d_baudrate) < 0)
    {
#ifdef D_LOG
	d_log ("d_direct",
		    "can't set direct-line parameters; errno = %d", errno);
#endif D_LOG
	(void) close (fd);
	fd = NOTOK;     /* make sure it hangs up, later       */
	d_errno = D_PORTOPEN;
	return (D_FATAL);
    }

#ifdef D_LOG
    d_log ("d_direct", "Open");
#endif D_LOG
    return (D_OK);
}

/*
 *     D_DIAL
 *
 *     routine called to make dial-up connection to remote system
 *
 *     number -- number to be called
 *
 *     speed -- speed index
 */

d_dial (number, speed)
char   *number;
int     speed;
{
    register int    result;
    char **tlist;

    /* get ordered list of acceptable linetypes for this phone number */
    tlist = d_typelist(&number);


    /* try to find an available dial-out port for the given speed and types */
    result = d_avport (speed, tlist);

    if (result < 0)
	return (result);

    d_baudrate = speed;

#ifdef D_LOG
    d_log ("d_dial", "port %s at %d", d_prtpt -> p_port, d_baudrate);
#endif D_LOG

    /*  do the dialing  */
    if ((result = d_dodial (number)) < 0)
	d_drop ();

    return (result);
}


/*
 *     D_AVPORT
 *
 *     routine which finds an available dial-out port with the given speed.
 *     an ordered list of acceptable types is given--the first type is best.
 *     exclusive open of lock files is employed to check for other
 *     users.  if the open succeeds, a global pointer 'd_prtpt' is set
 *     to the entry in the available port structure.
 *
 *     speed -- the speed index of the port be sought
 *
 *     typelist -- a list of acceptable line types for the port
 */

d_avport (speed, typelist)
int     speed;
char **typelist;
{
    register struct dialports  *dpt;
    register int    result;
#ifdef D_LOG
    register int    i;
#endif D_LOG
    char **tpt;

#ifdef D_DBGLOG
    d_dbglog ("d_avport", "looking for port with speed %d", speed);
#endif D_DBGLOG

/* for each type in the typelist, look at each of the known dial-out    */
/* ports.  if the type and speed match, check the access list (if there */
/* is one).  they try to lock the port.                                 */



    for (tpt = typelist; *tpt; tpt++)
    {
#ifdef D_DBGLOG
	d_dbglog("d_avport", "checking for type: %s", *tpt);
#endif D_DBGLOG

	for (dpt = d_prts; dpt -> p_port; dpt++)
	{
	    if ( ((dpt -> p_speed & (1 << (speed - 1))) == 0) ||
 	         !lexequ(dpt -> p_ltype, *tpt) )
	        continue;

/*  check the access list for this port, if there is one  */

	    if (dpt -> p_access)
	    {
	        if ((result = d_chkaccess (d_uname, dpt -> p_access)) < 0)
		    return (result);

	        if (result == D_NO)
		    continue;
	    }

/*  try to lock the port.  if we can, we're in business.  */

	    if ((result = d_lock (dpt -> p_lock)) < 0)
	        return (result);


	    if (result == D_OK)
	    {
	        d_prtpt = dpt;
	        d_didial = 1;
	        return (D_OK);
	    }
        }

    }


/*  if we get here, then we've looked at all the ports and found none  */
/*  that are available.                                                */

#ifdef D_LOG
    for (i=0; d_spdtab[i].s_speed != 0; i++) {
        if (d_spdtab[i].s_index == speed) {
	   d_log ("d_avport", "No %s baud dial-out ports available",
		  d_spdtab[i].s_speed);
	   break;
	}
    }
#endif D_LOG
    if (typelist[1] != 0) {
#ifdef D_LOG
        d_log ("d_avport", "of type: %s", typelist[0]);
#endif D_LOG
    } else {
	char *cp, *textlist;
	textlist = strdup(typelist[0]);
	for (tpt = &typelist[1]; *tpt != 0; tpt++)
	{
	    cp = multcat(textlist, ",", *tpt, (char *)0);
	    free(textlist);
	    textlist = cp;
	}
#ifdef D_LOG
        d_log("d_avport", "of types: %s", textlist);
#endif D_LOG
    }

    d_errno = D_NOPORT;
    return (D_NONFATAL);
}


/*
 *     D_AVLINE
 *
 *     routine to search for a direct connect line of the given name, and to
 *     secure its use exclusively.
 *
 *     linename -- direct connect line name
 */

d_avline (linename)
register char  *linename;
{
    register struct directlines *lpt;
    register int    result;
#ifdef D_LOG
    int foundone = 0;      /* any lines exist at all? */
#endif D_LOG

/*  cycle through the available direct lines  */

    for (lpt = d_lines; lpt -> l_name != (char *) 0; lpt++)
    {
	if (strcmp (linename, lpt -> l_name) != 0)
	    continue;

#ifdef D_LOG
	foundone = 1;
#endif D_LOG

/*  check the access (if there is one)  */

	if (lpt -> l_access != (char *) 0)
	{
	    if ((result = d_chkaccess (d_uname, lpt -> l_access)) < 0)
		return (result);

	    if (result == D_NO)
		continue;
	}

/*  try to lock it.  */

	if ((result = d_lock (lpt -> l_lock)) < 0)
		return (result);

	if (result == D_OK)
	{
	    d_ptline = lpt;
	    d_didial = 0;
	    return (D_OK);
	}
    }

/*  if we get here, there is no available direct line  */

#ifdef D_LOG
    if (foundone)
	d_log ("d_avline", "No %s direct lines available", linename);
    else
	d_log ("d_avline", "No direct lines named '%s' are known", linename);
#endif D_LOG
    d_errno = D_NOPORT;
    return (D_NONFATAL);
}


/*
 *     D_DODIAL
 *
 *     this routine trys to make the connection to the remote system.
 *     it forks, the parent opens the port and the child drives the acu
 *
 *     number -- pointer to the phone number string to be passed to the
 *               dialer
 */

d_dodial (number)
char   *number;
{
    register int    parentpid,
                    childpid;
    int     fd;
    int     errcode;
#ifdef SYS5
    struct termio hupbuf;
#endif SYS5

#ifdef D_DBGLOG
    d_dbglog ("d_dodial", "about to attempt to call '%s'", number);
#endif D_DBGLOG
    parentpid = getpid ();
    if ((childpid = d_tryfork ()) == -1)
    {
#ifdef D_LOG
	d_log ("d_dodial", "couldn't fork to dial");
#endif D_LOG
	d_errno = D_FORKERR;
	return (D_FATAL);
    }

    /*  the parent tries to open the port  */
    if (childpid)
    {
#ifdef D_DBGLOG
	d_dbglog ("d_dodial", "dialing parent opening '%s'",
		d_prtpt -> p_port);
#endif D_DBGLOG

    	if (setjmp (timerest)) {
	    d_ttrestore ();
	    return (D_NONFATAL);
    	}
	s_alarm (ACUWAIT);
	fd = open (d_prtpt -> p_port, 2);
	s_alarm (0);
	d_errno = errno;      /* save, so kill does not overwrite   */
#ifdef D_DBGLOG
	d_dbglog ("d_dodial", "parent open return (errno=%d)", d_errno);
#endif D_DBGLOG

	d_cstart (number,d_prtpt -> p_ltype); /* carrier: start call timer */

	kill (childpid, SIGKILL);  /* so it doesn't waste it time */
	errcode = pgmwait (childpid);
				  /* gather up the dialing child        */
#ifdef D_DBGLOG
	d_dbglog ("d_dodial", "acu child exit error code %d", errcode);
#endif D_DBGLOG

	if (fd < 0)
	{
	    if (errno != EINTR)   /* not timeout or child kill          */
	    {                     /* so must terminate                  */
#ifdef D_LOG
		d_log ("d_dodial", "can't open port '%s', errno #%d",
			d_prtpt -> p_port, d_errno);
#endif D_LOG
		d_errno = D_PORTOPEN;
		return (D_FATAL);
	    }
	}
	else
	{
#ifdef SYS5
	    ioctl(fd, TCGETA, &hupbuf);
	    hupbuf.c_cflag |= HUPCL;
	    if (ioctl (fd, TCSETA, &hupbuf) < OK)
#else
	    if (ioctl (fd, TIOCHPCL, 0) < OK)
#endif SYS5
	    {
#ifdef D_LOG
		d_log ("d_dodial",
			    "problem setting hangup on close; errno = %d", errno);
#endif D_LOG
		(void) close (fd);
		fd = NOTOK;     /* make sure it hangs up, later       */
		d_errno = D_PORTOPEN;
		return (D_FATAL);
	    }
	    else
	    {
		d_prtfp = fdopen (fd, "r");
		if (d_ttsave (d_prtfp, d_prtpt -> p_port) < 0 ||
			d_ttscript (d_baudrate) < 0)
		{
#ifdef D_LOG
		    d_log ("d_dodial",
				"can't set dial-port parameters; errno = %d", errno);
#endif D_LOG
		    (void) close (fd);
		    fd = NOTOK;     /* make sure it hangs up, later       */
		    d_errno = D_PORTOPEN;
		    d_ttrestore ();
		    return (D_FATAL);
		}
	    }                     /* stdio for input and straight       */
				  /*   write() for output               */
	}

	switch (d_errno = errcode)
	{
	    case 0:
		if (fd < 0)
		    return (D_NONFATAL);
		return (D_OK);

	    case D_BUSY:
	    case D_ABAN:
		d_ttrestore ();
		return (D_NONFATAL);

	    default:
		d_ttrestore ();
		return (D_FATAL);
	}
    }

    /*  the child drives the acu.  if there's an error, then a
     *  signal is sent to the parent to interrupt his open call.
     *  The error code is returned to the parent through his
     *  exit status.
     */
    d_errno = 0;

    sleep ((unsigned) 5);       /* give parent time to get into open()  */
    d_drvacu (number);

    sleep ((unsigned) CONNWAIT); /* wait for open to complete    */
    kill (parentpid, SIGALRM);   /* kick parent out of open call     */

    exit (d_errno);
#ifdef lint
	return D_FATAL;
#endif lint
}

/*
 *     D_DRVACU
 *
 *     this routine opens the appropriate acu and feeds the phone number to
 *     it.  it's possible that it's in use, so we'll wait up to a minute,
 *     trying every couple of seconds.  D_FATAL and D_NONFATAL errors are
 *     possible here.
 *
 *     number -- phone number string to be passed to the dialer
 */

d_drvacu (number)
char   *number;
{
    int len;
    char linebuf[100];
    register int    acufd,
                    warning;

    warning = 0;
    if (d_prtpt -> p_acupref != (char *) 0)
	(void) strcpy (linebuf, d_prtpt -> p_acupref);
    else
	linebuf[0] = '\0';

    strcat (linebuf, number);
    if (d_prtpt -> p_acupost != (char *) 0)
	strcat (linebuf, d_prtpt -> p_acupost);

    len = strlen (linebuf);
#ifdef D_DBGLOG
    d_dbglog ("d_drvacu", "about to write '%s' on acu", linebuf);
#endif D_DBGLOG

    while (1)
    {                             /* make sure open & write don't hang  */
				/* this doesn't really work under 4.2 */
#ifdef D_DBGLOG
	d_dbglog ("d_drvacu", "opening dialer");
#endif D_DBGLOG
    	if (setjmp (timerest)) {
    	    break;
    	}
	s_alarm (DIALWAIT);
	acufd = open (d_prtpt -> p_acu, 2);
#ifdef D_DBGLOG
	d_dbglog ("d_drvacu", "dialer open (errno=%d)", errno);
#endif D_DBGLOG

/*  if we get it, then try to dial the number.  if that goes alright,  */
/*  return                                                             */

	if (acufd >= 0)
	{
	    sleep ((unsigned) 1);  /* let things settle down           */
#ifdef D_LOG
	    d_log ("d_drvacu", "Dialing...");
#endif D_LOG
	    if (write (acufd, linebuf, len) != len)
		break;

	    (void) close (acufd);      /* free the dialer */
#ifdef D_DBGLOG
	    d_dbglog ("d_drvacu", "acu successful write");
#endif D_DBGLOG
	    s_alarm (0);
	    return (D_OK);
	}

/*  we get here from a failure of the acu open.  if it's worse than just  */
/*  being busy, break out to the common error decoder                     */

	s_alarm(0);
#ifdef D_DBGLOG
	d_dbglog ("d_drvacu", "acu open (errno=%d)", errno);
#endif D_DBGLOG

	if (errno != EBUSY)
	    break;

/*  print the warning message once, and then sleep before trying again  */

	if (!warning)
	{
#ifdef D_LOG
	    d_log ("d_drvacu", "waiting for dialer.");
#endif D_LOG
	    warning = 1;
	}
	sleep ((unsigned) 5);
    }

/*  this stuff prints error messages for failures of both the open and  */
/*  write calls                                                         */

    s_alarm (0);
    switch (errno)
    {
	case EBUSY: 

#ifdef D_LOG
	    d_log ("d_drvacu", "That number is busy.");
#endif D_LOG
	    d_errno = D_BUSY;
	    return (D_NONFATAL);

	case EDNPWR: 

#ifdef D_LOG
	    d_log ("d_drvacu", "dialer power off");
#endif D_LOG
	    d_errno = D_NOPWR;
	    return (D_FATAL);

	case EDNABAN: 

#ifdef D_LOG
	    d_log ("d_drvacu", "call abandoned");
#endif D_LOG
	    d_clog (LOG_ABAN);
	    d_errno = D_ABAN;
	    return (D_NONFATAL);

	case EDNDIG: 

#ifdef D_LOG
	    d_log ("d_drvacu", "internal error: bad digit to dialer");
#endif D_LOG
	    d_errno = D_SYSERR;
	    return (D_FATAL);

	default: 

#ifdef D_LOG
	    d_log ("d_drvacu", "system errno #%d", errno);
#endif D_LOG
	    d_errno = D_INTERR;
	    return (D_FATAL);
    }
}


/*
 *     D_DROP
 *
 *     routine which is called to drop the connection to the remote system.
 *     if closes the port and lock file, and logs the call, of this was a
 *     dial-up.
 */

d_drop ()
{
#ifdef SYS5
    struct termio hupbuf;
#endif SYS5

#ifdef D_DBGLOG
    d_dbglog ("d_drop", "dropping connection.");
#endif D_DBGLOG

    /*  restore the line to its old state  */
    d_ttrestore ();

    /*  close port  */
    if (d_prtfp != (FILE *) EOF && d_prtfp != NULL)
    {
    	if (setjmp (timerest)) {
	    return;
    	}
	s_alarm (CONNWAIT);

#ifdef SYS5
	ioctl (fileno(d_prtfp), TCGETA, &hupbuf);
	hupbuf.c_cflag &= ~CBAUD;	/* set 0 baud -- hangup */
	ioctl (fileno(d_prtfp), TCSETA, &hupbuf);
#endif SYS5

	fclose (d_prtfp);
	s_alarm (0);
/* XXX Why commented out? */	/* d_prtfp = (FILE *) EOF; */
 
	if ((FILE *) EOF == d_prtfp)
	{
#ifdef D_LOG
	    d_log ("d_drop", "problem closing connection line.");
#endif D_LOG
	    return;     /* leave the line locked, in case of major problem */
	}

	if (d_didial)
	    d_clog (LOG_COMP);
    }

    /*  release the port to others by closing lock file  */
    d_unlock (d_ptline -> l_lock);
}
one number string to be passed to the dialer
 */

d_drvacu (number)
char   *number;
{
    int len;
    char linebuf[100];
    register int    acufd,
                    warning;

    mmdf/lib/dial/d_clog.c   444      0     12        5646  3671073171  10043 # include  "util.h"
# include  "d_clogcodes.h"
# include  "d_returns.h"
# include  <stdio.h>
# include  "d_proto.h"
# include  "d_structs.h"

extern	time_t	time();
extern	char	*ctime();
extern	char	*d_calllog;
extern	char	d_uname[];
extern	int	d_errno;

static time_t d_cstime;                /*  time call started  */

static char  d_cnumber[30];           /*  the number called  */
static char  d_linetype[30];          /*  the type of dialout line used */

/*
 *     D_CSTART
 *
 *     routine called to note the beginning of a call.  the current time is
 *     saved, and if the phone number and user name are given, they are
 *     saved also.
 *
 *     number -- character string which is phone number called
 */

d_cstart(number, linetype)
  register char  *number;
  register char  *linetype;
    {

/*  get the time and save the number if given  */

    time(&d_cstime);

    if (number)
      (void) strcpy(d_cnumber, number);

    if (linetype)
      (void) strcpy(d_linetype, linetype);

    return(D_OK);
    }
/**/

/*
 *     D_CLOG
 *
 *     routine which logs calls.  we have to be careful here that
 *     sensitive numbers are protected, but still allow us to see all
 *     long distance calls.  the strategy that's used is to write X's
 *     over the last 4 digits of number which have more than 7 digits.
 *
 *     code -- a character that is written in the file to indicate the
 *             status of call completion
 *     
 */

d_clog(code)
  int  code;
    {
    FILE  *clogfptr;
    long duration;
    long  stoptime;
    int  seconds;
    register int  hours, minutes;

/* check start time--if zero, this is an extraneous call to d_clog */
    if (d_cstime == 0L) return;


/*  open the call log file  */

    if ((clogfptr = fopen(d_calllog, "a")) == NULL)
      {
      d_errno = D_CALLLOG;
      d_log("d_clog", "can't open call log file"); 
      return;
      }
  
/*  get the stop time and calculate the duration of the session  */

    time(&stoptime);
    duration = stoptime - d_cstime;
    /*NOSTRICT*/
    d_cstime = 0L;
    /*NOSTRICT*/
    hours = (int) (duration / 3600);
    /*NOSTRICT*/
    minutes = (int) ((duration - (hours * 3600)) / 60l);
    /*NOSTRICT*/
    seconds = (int) (duration - (hours * 3600) - (minutes * 60));

    fprintf(clogfptr, "%-8s  %c  ", d_uname, code);
    fprintf(clogfptr, "%-16.16s  ", d_cnumber);

/*  now the time data.  considerable craziness to make a pretty duration  */

    fprintf(clogfptr, "%-19.19s  ", ctime(&stoptime));

    if (hours > 0)
      fprintf(clogfptr, "%2d:", hours);
    else
      fprintf(clogfptr, "   ");

    if (minutes > 0)
      fprintf(clogfptr, "%2d:", minutes);
    else
      if (hours > 0)
        fprintf(clogfptr, "00:", minutes);
      else
        fprintf(clogfptr, " 0:", minutes);

    if (seconds > 9)
      fprintf(clogfptr, "%2d   %s\n", seconds, d_linetype);
    else
      fprintf(clogfptr, "0%1d   %s\n", seconds, d_linetype);

    fclose(clogfptr);
    }
);

    if (linetype)
      (void) strcpy(d_linetype, linetype);

    return(D_OK);
    }
mmdf/lib/dial/d_checksum.c   444      0     12        3605  3620510404  10677 # include  "d_returns.h"








/*
 *     D_CSCOMP
 *
 *     routine which computes the checksum for a packet and loads it into
 *     the first 4 bytes as hex digits
 *
 *     text -- pointer to packet to be check summed.  this packet should
 *             be complete except for the checksum field.  the bytes to
 *             be included in the sum should start at text + 4;
 *
 *     length -- total length of the packet.  this count should include the
 *               4 checksum digits but not the line terminator.
 */

d_cscomp(text, length)
  char  *text;
  int  length;
    {
    register char  *cp;
    register int  sum, value;
    int  digit;

/*  add up the bytes  */

    cp = &text[4];
    sum = 0;

    while (length > 4)
      {
      sum += *cp++;
      length--;
      }

/*  build and load the checksum  */

    cp = &text[3];

    for (digit = 0; digit <= 3; digit++)
      {
      value = sum & 017;
      sum >>= 4;
      *cp-- = d_tohex(value);
      }

    return(D_OK);
    }












/*
 *     D_CSCHECK
 *
 *     this routine checks that a packet has the correct checksum.  D_OK is
 *     returned if it's alright, otherwise D_NO.
 *
 *     text -- pointer to the packet, including the checksum bytes
 *
 *     length -- number of bytes in the packet, excluding the line terminator
 */

d_cscheck(text, length)
  char  *text;
  int  length;
    {
    register char  *cp;
    register int  sum, cnum;
    int  digit, chksum, value;

/*  sum up the bytes  */

    cp = &text[4];
    sum = 0;

    for (cnum = 4; cnum < length; cnum++)
      sum += *cp++;

/*  convert the checksum and see if ti matches what we've just computed  */

    cp = text;
    chksum = 0;

    for (digit = 0; digit <= 3; digit++)
      {
      if ((value = d_fromhex(*cp++)) < 0)
        return(D_NO);

      chksum = (chksum << 4) | value;
      }

    if (chksum == sum)
      return(D_OK);

    return(D_NO);
    }
d calculate the duration of the session  */

    time(&stoptime);
    duration = stoptime - d_cstime;
    /*NOSTRICT*/
    mmdf/lib/dial/Makefile.real   444      0     12       16037  3664425155  11053 #
#	Makefile for dial package portion of libmmdf.a
#
#  Just the prefix of the name should be given (i.e. "foo" for "foo.c")
MODULES = d_master d_slave d_path d_quit d_escape \
	  d_sndstream d_rcvdata d_data d_init d_pglobals \
	  d_script d_connect d_pstty d_numbers d_utils \
	  d_clog d_script2 d_dial2 d_parse d_packet \
	  d_port d_checksum d_coding d_dglobals \
	  d_tai d_tscript d_log d_io d_setbuff \
	  d_control d_assign d_lock d_getnbuff mktran test

#
#  The following are put into libmmdf.a
#
OBJECTS = d_master.o d_slave.o d_path.o d_quit.o d_escape.o \
	  d_sndstream.o d_rcvdata.o d_data.o d_init.o d_pglobals.o \
	  d_script.o d_connect.o d_pstty.o d_numbers.o d_utils.o \
	  d_clog.o d_script2.o d_dial2.o d_parse.o d_packet.o \
	  d_port.o d_checksum.o d_coding.o d_dglobals.o \
	  d_tai.o d_tscript.o d_log.o d_io.o d_setbuff.o \
	  d_control.o d_assign.o d_lock.o d_getnbuff.o conf_dial.o

#
#  The following are linted
#
COBJECTS = d_master.c d_slave.c d_path.c d_quit.c d_escape.c \
	  d_sndstream.c d_rcvdata.c d_data.c d_init.c d_pglobals.c \
	  d_script.c d_connect.c d_pstty.c d_numbers.c d_utils.c \
	  d_clog.c d_script2.c d_dial2.c d_parse.c d_packet.c \
	  d_port.c d_checksum.c d_coding.c d_dglobals.c \
	  d_tai.c d_tscript.c d_log.c d_io.c d_setbuff.c \
	  d_control.c d_assign.c d_lock.c d_getnbuff.c

real-default: ../dial-made
../dial-made:    $(OBJECTS)
	$(AR) r ../libmmdf.a $(OBJECTS)
	-touch ../dial-made
	-@echo "dial package routines built normally"

#
#  Special case dependencies
#
conf_dial.o: ../../conf/$(HOST)/conf_dial.c ../../h/util.h \
		../../h/d_proto.h ../../h/d_structs.h
	$(CC) -c $(CFLAGS) ../../conf/$(HOST)/conf_dial.c

mktran: mktran.o $(MMDFLIBS)
	$(CC) -o mktran $(LDFLAGS) mktran.o $(MMDFLIBS) $(SYSLIBS)

test:   test.o $(MMDFLIBS)
	$(CC) -o test $(LDFLAGS) test.o $(MMDFLIBS) $(SYSLIBS)

lint:
	$(LINT) -Cdial $(LFLAGS) $(COBJECTS) ../../conf/$(HOST)/conf_dial.c

clean:
	-rm -f mktran test *.o x.c makedep eddep make.out errs llib-ldial*


# DO NOT DELETE THIS LINE -- make depend uses it

d_master.o: d_master.c
d_master.o: ../../h/util.h
d_master.o: ../../h/d_proto.h
d_master.o: ../../h/d_returns.h
d_master.o: /usr/include/signal.h
d_master.o: ../../h/d_syscodes.h
d_master.o: ../../h/d_clogcodes.h
d_master.o: ../../h/ll_log.h
d_slave.o: d_slave.c
d_slave.o: ../../h/util.h
d_slave.o: ../../h/ll_log.h
d_slave.o: ../../h/d_proto.h
d_slave.o: ../../h/d_returns.h
d_slave.o: ../../h/d_structs.h
d_path.o: d_path.c
d_path.o: ../../h/d_proto.h
d_path.o: ../../h/d_returns.h
d_path.o: /usr/include/signal.h
d_path.o: ../../h/d_syscodes.h
d_quit.o: d_quit.c
d_quit.o: ../../h/d_proto.h
d_quit.o: ../../h/d_returns.h
d_escape.o: d_escape.c
d_escape.o: ../../h/d_proto.h
d_escape.o: /usr/include/signal.h
d_escape.o: ../../h/d_syscodes.h
d_escape.o: ../../h/d_returns.h
d_sndstream.o: d_sndstream.c
d_sndstream.o: ../../h/d_returns.h
d_rcvdata.o: d_rcvdata.c
d_rcvdata.o: ../../h/d_returns.h
d_data.o: d_data.c
d_data.o: ../../h/d_proto.h
d_data.o: ../../h/d_returns.h
d_data.o: /usr/include/signal.h
d_data.o: ../../h/d_syscodes.h
d_init.o: d_init.c
d_init.o: ../../h/d_returns.h
d_pglobals.o: d_pglobals.c
d_pglobals.o: ../../h/d_proto.h
d_script.o: d_script.c
d_script.o: ../../h/util.h
d_script.o: /usr/include/signal.h
d_script.o: ../../h/d_syscodes.h
d_script.o: ../../h/d_proto.h
d_script.o: ../../h/d_returns.h
d_script.o: /usr/include/stdio.h
d_script.o: ../../h/d_structs.h
d_script.o: ../../h/d_script.h
d_connect.o: d_connect.c
d_connect.o: ../../h/util.h
d_connect.o: /usr/include/signal.h
d_connect.o: ../../h/d_syscodes.h
d_connect.o: ../../h/d_clogcodes.h
d_connect.o: ../../h/d_proto.h
d_connect.o: ../../h/d_returns.h
d_connect.o: ../../h/d_structs.h
d_connect.o: /usr/include/sgtty.h
d_connect.o: /usr/include/sys/stat.h
d_connect.o: /usr/include/sys/file.h
d_pstty.o: d_pstty.c
d_pstty.o: ../../h/util.h
d_pstty.o: /usr/include/sgtty.h
d_pstty.o: /usr/include/sys/stat.h
d_pstty.o: ../../h/d_proto.h
d_pstty.o: ../../h/d_returns.h
d_pstty.o: ../../h/d_structs.h
d_numbers.o: d_numbers.c
d_numbers.o: ../../h/util.h
d_numbers.o: ../../h/d_returns.h
d_numbers.o: ../../h/d_proto.h
d_numbers.o: /usr/include/stdio.h
d_numbers.o: ../../h/d_structs.h
d_utils.o: d_utils.c
d_utils.o: ../../h/util.h
d_utils.o: ../../h/d_returns.h
d_utils.o: /usr/include/pwd.h
d_utils.o: /usr/include/stdio.h
d_utils.o: ../../h/d_proto.h
d_utils.o: ../../h/d_structs.h
d_clog.o: d_clog.c
d_clog.o: ../../h/util.h
d_clog.o: ../../h/d_clogcodes.h
d_clog.o: ../../h/d_returns.h
d_clog.o: /usr/include/stdio.h
d_clog.o: ../../h/d_proto.h
d_clog.o: ../../h/d_structs.h
d_script2.o: d_script2.c
d_script2.o: /usr/include/stdio.h
d_script2.o: ../../h/d_proto.h
d_script2.o: ../../h/d_returns.h
d_script2.o: ../../h/d_structs.h
d_dial2.o: d_dial2.c
d_dial2.o: /usr/include/stdio.h
d_dial2.o: ../../h/d_returns.h
d_parse.o: d_parse.c
d_parse.o: ../../h/d_returns.h
d_packet.o: d_packet.c
d_packet.o: ../../h/d_proto.h
d_packet.o: ../../h/d_returns.h
d_packet.o: /usr/include/signal.h
d_packet.o: /usr/include/stdio.h
d_packet.o: ../../h/d_structs.h
d_packet.o: ../../h/d_syscodes.h
d_port.o: d_port.c
d_port.o: ../../h/util.h
d_port.o: ../../h/d_proto.h
d_port.o: ../../h/d_returns.h
d_port.o: /usr/include/signal.h
d_port.o: ../../h/d_syscodes.h
d_port.o: /usr/include/stdio.h
d_port.o: ../../h/d_structs.h
d_port.o: /usr/include/sys/ioctl.h
d_port.o: /usr/include/sgtty.h
d_checksum.o: d_checksum.c
d_checksum.o: ../../h/d_returns.h
d_coding.o: d_coding.c
d_coding.o: ../../h/util.h
d_coding.o: ../../h/d_proto.h
d_coding.o: ../../h/d_returns.h
d_dglobals.o: d_dglobals.c
d_dglobals.o: ../../h/util.h
d_dglobals.o: /usr/include/sgtty.h
d_dglobals.o: ../../h/d_proto.h
d_dglobals.o: /usr/include/stdio.h
d_dglobals.o: ../../h/d_structs.h
d_tai.o: d_tai.c
d_tai.o: ../../h/util.h
d_tai.o: ../../h/ll_log.h
d_tai.o: ../../h/d_proto.h
d_tai.o: ../../h/d_structs.h
d_tai.o: ../../h/cmd.h
d_tscript.o: d_tscript.c
d_tscript.o: /usr/include/stdio.h
d_tscript.o: ../../h/d_returns.h
d_log.o: d_log.c
d_log.o: ../../h/util.h
d_log.o: ../../h/d_returns.h
d_log.o: /usr/include/stdio.h
d_log.o: ../../h/d_proto.h
d_log.o: ../../h/d_structs.h
d_log.o: ../../h/ll_log.h
d_io.o: d_io.c
d_io.o: /usr/include/stdio.h
d_setbuff.o: d_setbuff.c
d_setbuff.o: ../../h/d_proto.h
d_setbuff.o: ../../h/d_returns.h
d_setbuff.o: /usr/include/stdio.h
d_setbuff.o: ../../h/d_structs.h
d_control.o: d_control.c
d_control.o: ../../h/d_proto.h
d_control.o: ../../h/d_returns.h
d_assign.o: d_assign.c
d_assign.o: ../../h/d_returns.h
d_lock.o: d_lock.c
d_lock.o: ../../h/util.h
d_lock.o: /usr/include/sys/stat.h
d_lock.o: /usr/include/sys/file.h
d_lock.o: ../../h/d_returns.h
d_getnbuff.o: d_getnbuff.c
d_getnbuff.o: ../../h/d_proto.h
d_getnbuff.o: /usr/include/signal.h
d_getnbuff.o: ../../h/d_syscodes.h
d_getnbuff.o: ../../h/d_returns.h
mktran.o: mktran.c
mktran.o: /usr/include/stdio.h
mktran.o: ../../h/d_returns.h
mktran.o: ../../h/ll_log.h
mktran.o: ../../h/d_proto.h
mktran.o: ../../h/d_script.h
test.o: test.c
test.o: /usr/include/stdio.h
test.o: ../../h/ll_log.h
# DEPENDENCIES MUST END AT END OF FILE
# IF YOU PUT STUFF HERE IT WILL GO AWAY
# see make depend above
t.o: d_init.c
d_init.o: ../../h/d_returns.h
d_pglobals.o: d_pglobals.c
d_pglobals.o: ../../h/d_proto.h
d_script.o: d_script.c
d_script.o: ../../h/util.h
d_script.o: /usr/include/signal.h
d_script.o: ../../h/d_syscodes.h
d_script.o: ../../h/d_proto.h
d_script.o: ../../h/d_returns.h
d_script.o: /usr/include/stdio.h
d_script.o: ../../h/d_structs.h
d_script.o: ../../h/d_script.h
d_connect.o: d_connect.c
d_connect.o: ../../h/util.h
d_connect.o: /usr/include/signal.h
d_connect.o: ..mmdf/lib/dial/d_dial2.c   444      0     12        7133  3620510404  10070 # include  <stdio.h>
# include  "d_returns.h"


/*
 *     D_CHKACCESS
 *
 *     this routine checks for the user name given as the argument string
 *     in the access file given as the second argument.
 *
 *     username -- the user's name that we're looking for
 *
 *     accessfile -- path name of the access file.  this file has one user name
 *                   per line
 */

d_chkaccess(username, accessfile)
char  *username, *accessfile;
{
    extern int  d_errno;
#ifdef D_LOG
    register int  linenum = 0;
#endif D_LOG
    register int  length;
    char  *fields[1], linebuf[128];
    FILE  *accfp;

#ifdef D_DBGLOG
    d_dbglog("d_chkaccess", "user %s accessfile %s", username, accessfile);
#endif D_DBGLOG

/*  open the access list file.  if we can't, just return quietly  */

    if ((accfp = fopen(accessfile, "r")) == NULL)
      return(0);

    while (fgets(linebuf, sizeof linebuf - 1, accfp) != NULL)
    {
#ifdef D_LOG
	linenum++;
#endif D_LOG
/*  make sure we got the whole line  */
	length = strlen(linebuf);
	if (linebuf[length - 1] != '\n')
	{
#ifdef D_LOG
            d_log("d_chkaccess", "access file '%s', line %d too long",
		accessfile, linenum);
#endif D_LOG
        d_errno = D_ACCERR;
        return(D_FATAL);
        }

/*  parse the line  */

      switch (d_linparse(fields, linebuf, 1))
        {
        case  0:
            continue;

        case  1:
            if (strcmp(username, fields[0]) == 0)
              {
	      fclose(accfp);
              return(D_OK);
              }
            continue;

        case  -1:
#ifdef D_LOG
            d_log("d_chkaccess", "access file '%s', line %d: too many fields",
                accessfile, linenum);
#endif D_LOG
            d_errno = D_ACCERR;
            return(D_FATAL);

        case  -2:
#ifdef D_LOG
            d_log("d_chkaccess",
                "access file '%s', line %d: qupted string too long", accessfile,
                linenum);
#endif D_LOG
            d_errno = D_ACCERR;
            return(D_FATAL);

        default:
#ifdef D_LOG
            d_log("d_chkaccess", "access file '%s', line %d: unknown error",
                accessfile, linenum);
#endif D_LOG
            d_errno = D_ACCERR;
            return(D_FATAL);
        }
      }

#ifdef D_DBGLOG
    d_dbglog("d_chkaccess", "name not found");
#endif D_DBGLOG
    fclose(accfp);
    return(D_NO);

}


/*
 *     D_TYPELIST
 *
 *     this routine returns a list of acceptable dial port types based on the
 *     given phone number.  
 *
 *     Currently, the type is gotten out of the phone number and the
 *     phone number is munged to exclude the type field.
 *
 *     numptr -- pointer to the phone number being called
 *      
*/

char **
d_typelist(numptr)
  char  **numptr;
    {

    extern char *strdup ();
    char tempbuf[25];
    char *tpt, *newnum, *number;
    static char *typelist[] = {
        "LOCAL",
	0
        };

    tpt = tempbuf; 
    number = *numptr;
    newnum = number;

#ifdef D_DBGLOG
    d_dbglog("d_typelist", "number %s", *numptr);
#endif D_DBGLOG

    if (d_instring('<', number) != 0)
       {
       while (*number)
          if (*number != '<')
             *newnum++ = *number++;
          else
             { /* pick off type */
             while (*++number != '>')
                *tpt++ = *number; 
             number++;
             }

       *newnum = '\0';
       }

    *tpt = '\0';

#ifdef D_DBGLOG
    d_dbglog("d_typelist", "new number %s", *numptr);
#endif D_DBGLOG

    if (tempbuf[0] != '\0')
       typelist[0] = strdup(tempbuf);

#ifdef D_DBGLOG
    d_dbglog("d_typelist", "returning type %s", typelist[0]);
#endif D_DBGLOG

    return(typelist);
}
ect.o: ../../h/d_returns.h
d_connect.o: ../../h/d_structs.h
d_connect.o: /usr/include/sgtty.h
d_connect.o: /usr/include/sys/stat.h
d_connect.o: /usr/include/sys/file.h
d_pstty.o: d_pstty.c
d_pstty.o: ../../h/util.h
d_pstty.o: /usr/include/sgtty.h
d_pstty.o: /usr/include/sys/stat.h
d_pstty.o: ../../h/d_proto.h
d_pstty.o: ../../h/d_returns.h
d_pstty.o: ../../h/d_structs.h
d_numbers.o: d_numbers.c
d_numbers.o: ../../h/utmmdf/lib/dial/test.c   444      0     12        1661  3653210456   7563 # include	<stdio.h>
# include	"ll_log.h"

char scfile[128];
struct ll_struct log = {
    "Log",
    "mktran log",
    '\0177',
    100,
    0,
    0,
    0,
};

main(argc, argv)
  int argc;
  char **argv;
    {
    register int result, word;
    extern int d_debug;
    extern int d_master, d_snseq, d_rcvseq;

    mmdf_init();
    siginit();

    if (argc > 1)
	log.ll_level = (char)(atoi (argv[1]));

    if ((result = d_opnlog(&log)) < 0)
    {
	printf("opnlog returns %d\n", result);
	exit(-1);
    }

    d_init();

    if ((result = d_tsopen("Tranfile")) < 0)
    {
	printf("d_tsopen returns %d\n", result);
	exit(-1);
    }

    d_master = 1;
    d_snseq = 3;
    d_rcvseq = 3;

    if ((result = d_scopen("Script", 0, (char **) 0)) < 0)
    {
	printf("d_scopen returns %d\n", result);
	exit(-1);
    }

    result = d_script();
    printf("d_script returns %d\n", result);


    /*  drop the connection  */
    d_drop ();
    exit(0);
}
um++;
#endif D_LOG
/*  make sure we got the whole line  */
	length = strlen(linmmdf/lib/dial/d_numbers.c   444      0     12       14570  3671073172  10607 # include  "util.h"
# include  "d_returns.h"
# include  "d_proto.h"
# include  <stdio.h>
# include  "d_structs.h"

/*
 *     D_NUMPARSE
 *
 *     this routine is called to parse a phone number specification string
 *     of the form:
 *
 *     speed|phone number, speed|phone number, ...
 *
 *     such as:
 *
 *     300|9:738-8003,1200|9:738-2928
 *
 *     and fill a table of structures with the appropriate information.
 *     the elements of each table entry contain the speed index corresponding
 *     to the speed designation (300, 1200, etc.) and the phone number is
 *     converted from the external form into that required by the acu driver.
 *
 *     string - pointer to specification string as above
 *
 *     numtab - pointer to array of structures for the speed index, phone
 *              number pairs
 *
 *     filename - name of the file from which the specification string came.
 *                this name is reported when errors are encountered to aid
 *                in debugging.
 *
 *     lineno - number of the line in the above file which contains the
 *              string
 *
 *
 *      Mar 84 D. Long  added support for "<type>" in phone numbers 
 */

d_numparse(string, numtab, maxnums, filename, lineno)
  char  *string, *filename;
  struct telspeed  *numtab;
  int  maxnums;
  unsigned lineno;
    {
    register char  *in, *ap, *cp;
    int  count;
    char  accum[30], tempbuf[50];
    int  index, num, linetype;

    in = string;
    num = 0;
#ifdef D_DBGLOG
    d_dbglog("d_numparse", "parsing '%s'", string);
#endif D_DBGLOG

/*  treat each speed/phone number pair separately  */

    while (1)
      {

/*  isolate a number and copy it into a temporary buffer  */

      if (*in == ',')
        in++;

      if (*in == '\0')
        return(num);

      if (num >= maxnums)
      {
	  d_fillog(filename, lineno, "d_numparse",
			"more than %d phone numbers", maxnums);
	  return(D_FATAL);
      }

      cp = tempbuf;
      count = 0;

      while (*in && (*in != ','))
	if (++count > 50)
          {
          d_fillog(filename, lineno, "d_numparse", "phone number too long");
          return(D_FATAL);
          }

        else
          *cp++ = *in++;

      *cp = '\0';
      cp = tempbuf;

/*  if the string begins with a letter, then it is taken to be the name of  */
/*  a direct connect line.  the speed index will be set to 0.               */

      if (isalpha(tempbuf[0]))
        {
        if (strlen(tempbuf) > 25)
          {
          d_fillog(filename, lineno, "d_numparse", "direct line name too long");
          return(D_FATAL);
          }

        (void) strcpy(numtab[num].t_number, tempbuf);
        numtab[num].t_speed = 0;
#ifdef D_DBGLOG
	d_dbglog ("d_numparse", "%d: (speed %d) num '%s'",
		    num, numtab[num].t_speed, numtab[num].t_number);
#endif D_DBGLOG
        num++;

        continue;
        }

/*  if the string to be parsed contains a '|', then it is supposed to  */
/*  have a preceeding speed indicator.  get at most 4 digits for this  */

      if (d_instring('|', tempbuf))
        {
        ap = accum;
        count = 0;

        while ((*cp != '|') && (++count <= 4))
          if (isdigit(*cp))
            *ap++ = *cp++;

          else
            {
            d_fillog(filename, lineno, "d_numparse", "bad character in speed");
            return(D_FATAL);
            }

        if (count > 4)
          {
          d_fillog(filename, lineno, "d_numparse", "invalid speed");
          return(D_FATAL);
          }

/*  now look up the string to get the index  */

        *ap = '\0';

        if ((index = d_spdindex(accum)) < 0)
          {
          d_fillog(filename, lineno, "d_numparse", "unknown speed");
          return(D_FATAL);
          }

        numtab[num].t_speed = index;
        cp++;
        }

/*  if there's no speed given, assume a default of 300 baud  */

      else
        numtab[num].t_speed = 7;

/*  accumulate up to 25 characters of phone number  */

      ap = numtab[num].t_number;
      count = 0;
      linetype = 0;

      while (*cp && (*cp != ',') && (count < 25))
        {

        if (isdigit(*cp) || (linetype && (*cp != '>')) )
          {
          *ap++ = *cp++;
          count++;
          continue;
          }

        switch (*cp)
          {
          case '(':
          case ')':
          case '-':

              cp++;
              continue;

          case ':':

	      *ap++ = '=';      /* wait for next dial tone */
              count++;
              cp++;
              continue;

	  case '<':
	      *ap++ = *cp++;
	      count++;
              linetype = 1;
              continue;

	  case '>':
	      *ap++ = *cp++;
	      count++;
	      linetype = 0;
              continue;

	  default:              /* may be wrong, but let it pass  */

              d_fillog(filename, lineno, "d_numparse",
		  "illegal dial character '%c' (%3o'O) in phone number?",
                  *cp, *cp);
              count++;
              cp++;
              continue;

          }
        }

/*  make sure the number isn't too long  */

      if (count >= 25)
        {
        d_fillog(filename, lineno, "d_numparse", "phone number too long");
        return(D_FATAL);
        }

/*  make sure the <> were balanced */

      if (linetype)
        {
        d_fillog(filename, lineno, "d_numparse", "Line type field invalid");
        return(D_FATAL);
        }

      *ap = '\0';

#ifdef D_DBGLOG
      d_dbglog ("d_numparse", "%d: (speed %d) num '%s'",
		  num, numtab[num].t_speed, numtab[num].t_number);
#endif D_DBGLOG
      num++;
      }
    }

/*
 *     D_SPDINDEX
 *
 *     this routine looks up the speed designation string in the table
 *     of legal identifiers and returns the speed index.
 *
 *     speed -- pointer to the speed string
 */

d_spdindex(speed)
  register char  *speed;
    {
    extern struct speedtab  d_spdtab[];
    extern int  d_errno;
    register struct speedtab  *sp;

    sp = d_spdtab;

    while (sp -> s_speed)
      {
      if (strcmp(sp -> s_speed, speed) == 0)
        return(sp -> s_index);

      sp++;
      }

    d_errno = D_BADSPD;
    return(D_FATAL);
    }

/*
 *     D_INSTRING
 *
 *     routine which returns a non-zero value if the specified character
 *     is in the string.
 *
 *     c -- the character to be searched for
 *
 *     string -- the string to search
 */

d_instring(c, string)
  char  c;
  register char  *string;
    {

    while (*string)
      if (c == *string++)
        return(1);

    return(0);
    }
)
        {
        if (strlen(tempbuf) > 25)
          {
          d_fillog(filename, lineno, "d_numparse", "direct line name too long"mmdf/lib/dial/d_lock.c   444      0     12        5616  3671073173  10046 #include  "util.h"
#include  <sys/stat.h>
#ifdef V4_2BSD
#include  <sys/file.h>
#endif V4_2BSD
#include  "d_returns.h"

extern	int d_errno;

/*
 *      D_LOCK
 *
 *      routine which attempts to open a file exclusively, and thereby lock
 *      the associated port or direct line.
 *
 *   Feb 84   D. Long modified to stat the lock rather than d_verifying it
 *
 *   Mar 84   D. Rockwell & D. Long -- use flock instead of UUCP convention
 *            D. Rockwell & D. Long -- fixed bug with d_lckfd in d_lock.
 *
 *	Jul 85   D. Rockwell & D. Long -- add UUCP-compatible locking (yuck)
 *	         This assumes that the phone line is being locked with
 *	          "/usr/spool/uucp/LCK..tty*"
 *
 *      lockfile -- lock file name
 */
d_lock (lockfile)
register char  *lockfile;

{
    extern int  d_lckfd;

#ifdef UUCPLOCK
	char lockername[1024];
	int fd;
	int pid;

	d_lckfd = -1;
	pid = getpid();
	sprintf(lockername, "/usr/spool/uucp/MLTMP.%d", pid);
	(void) unlink(lockername);
	if ((fd = creat(lockername, 0644)) < 0) {
#ifdef D_LOG
		d_log ("d_lock", "can't create locker file '%s'", lockername);
#endif D_LOG                                
		(void) unlink(lockername);	/* never needed? */
		d_errno = D_LOCKERR;
		return(D_FATAL);
	}
	write(fd, &pid, sizeof pid);
	(void) close(fd);
	if (link(lockername, lockfile) < 0) {
#define	NEED_D_VERIFY	1
		if (d_verify(lockfile) >= D_OK) {
			(void) unlink(lockername);
			return(D_NO);
		}
		(void) unlink(lockfile);
		if (link(lockername, lockfile) < 0) {
#ifdef D_LOG
			d_log ("d_lock", "can't link lock file '%s'", lockfile);
#endif D_LOG
			(void) unlink(lockername);
			return(D_NO);
		}
	}
	(void) unlink(lockername);

#else UUCPLOCK
#ifndef V4_2BSD
#define	NEED_D_VERIFY	1
    if (d_verify (lockfile) >= D_OK)
	return (D_NO);      /* it's really busy */

    (void) unlink (lockfile);      /* clean it out, if necessary */
    if ((d_lckfd = creat (lockfile, 0644)) < 0)
    {
#else V4_2BSD
    if (d_lckfd < 0 && (d_lckfd = open (lockfile, O_RDONLY|O_CREAT, 0644)) < 0)
    {
#endif V4_2BSD
#ifdef D_LOG
	d_log ("d_lock", "can't create lock file '%s'", lockfile);
#endif D_LOG
	d_errno = D_LOCKERR;
	return (D_FATAL);
    }

#ifdef V4_2BSD
    if (flock(d_lckfd, LOCK_EX|LOCK_NB) < 0)
    {
	(void) close(d_lckfd);
	d_lckfd = -1;
	return (D_NO); 
    }
#endif V4_2BSD
#endif UUCPLOCK
    return (D_OK);
}

#ifdef	NEED_D_VERIFY
d_verify (port)         /* is the port REALLY active? */
register char  *port;
{
    struct stat statbuf;
    time_t curtime;

    if (stat (port, &statbuf) < 0)
	return (D_NO);  /* really is inaccessable */

    time (&curtime);

    if (curtime > (statbuf.st_atime + (time_t) (15 * 60)))
	return (D_NO);  /* inactive for over 15 minutes */

    return (D_OK);
}
#endif NEED_D_VERIFY

d_unlock(lockfile)
{
    if (d_lckfd >= 0)
    {
#ifdef V4_2BSD
	flock(d_lckfd, LOCK_UN);
#endif V4_2BSD
	(void) close(d_lckfd);
	d_lckfd = -1;
    }
    (void) unlink( lockfile );
}
e;

          }
        }

/*  make sure the number isn't too long  */

      if (count >= 25)
        {
        dmmdf/lib/dial/d_pstty.c   444      0     12       12666  3671073174  10325 # include  "util.h"
#ifdef SYS5
# include <termio.h>
#else
# include  <sgtty.h>
#endif SYS5
# include  <sys/stat.h>
# include  "d_proto.h"
# include  "d_returns.h"
# include  "d_structs.h"

/*
 *  Routines for controlling the tty settings on the control line
 *
 *  Jun 81  D. Crocker      Rewritten to work on v6 & v7
 *  Jul 81  D. Crocker      Make explicit TIOCHPCL call
 *  Dec 82  Doug Kinston    Chmods no longer control return values (DPK@BRL)
 *  Jan 84  D. Rockwell     Make d_ttrestore heed d_savalid
 *                          Save d_savfd instead of FILE *
 */

extern int errno;

LOCVAR int d_savfd;            /*  handle on the opened tty          */
LOCVAR char d_savfname[25];      /*  name of tty                       */
#ifdef SYS5
LOCVAR struct termio d_savtty
#else
LOCVAR struct sgttyb d_savtty;   /*  where to stash original setting   */
#endif SYS5
LOCVAR int d_savmode;            /*  protections on tty                */
LOCVAR int d_savalid;


d_ttsave (fp, fname)              /* save the current setting           */
	FILE *fp;
	char fname[];
{
    struct stat statbuf;
    register int retval;

    d_savfd = fileno(fp);
    if (fname != 0)
	(void) strcpy (d_savfname, fname);
    else
	d_savfname[0] = '\0';

#ifdef D_DBGLOG
    d_dbglog("d_ttsave", "saving terminal modes, fd = %d", d_savfd);
#endif D_DBGLOG

#ifdef SYS5
    if ((retval = ioctl (d_savfd, TCGETA, &d_savtty)) >= 0)
#else
    if ((retval = ioctl (d_savfd, TIOCGETP, &d_savtty)) >= 0)
#endif SYS5
    {
	if ((retval = fstat (d_savfd, &statbuf)) >= 0)
	{
	    d_savalid++;
	    d_savmode = statbuf.st_mode & ~S_IFMT;
	}
    }
    return (retval);
}


d_ttrestore ()                    /*  restore initial setting           */
{
    int retval;

#ifdef D_DBGLOG
    d_dbglog ("d_ttrestore", "restoring terminal characteristics, fd = %d",
	d_savfd);
#endif D_DBGLOG

    if (d_savalid == 0) {
#ifdef D_DBGLOG
	d_dbglog("d_ttrestore", "didn't have to");
#endif D_DBGLOG
    	return(0);
    }
#ifdef SYS5
    retval = ioctl (d_savfd, TCSETAW, &d_savtty);
#else
    retval = ioctl (d_savfd, TIOCSETP, &d_savtty);
#endif
    if (!isnull (d_savfname[0]))
	chmod( d_savfname, d_savmode );

    d_savalid = 0;
    return (retval);
}

d_ttscript (baudrate)             /* tty will be following call script  */
    int baudrate;
{
#ifdef SYS5
    struct termio sttybuf;
    extern unsigned short d_scbitc, d_scbiti, d_scbito, d_scbitl;
#else
    struct sgttyb sttybuf;
    extern int d_scon, d_scoff;
#endif SYS5

    if (!isnull (d_savfname[0]))
	chmod( d_savfname, 0600 );

#ifdef SYS5
    ioctl(d_savfd, TCGETA, &sttybuf);
#endif SYS5

    if (baudrate != 0)
    {                             /*  use special speed                 */
#ifdef SYS5
	sttybuf.c_cflag = (baudrate & CBAUD);
#else
	sttybuf.sg_ispeed = baudrate;
	sttybuf.sg_ospeed = baudrate;
#endif SYS5
    }
    else
    {                             /*  use current speed                 */
#ifdef SYS5
	ssybuf.c_cflag = (d_savtty.c_cflag & CBAUD);
#else
	sttybuf.sg_ispeed = d_savtty.sg_ispeed;
	sttybuf.sg_ospeed = d_savtty.sg_ospeed;
#endif SYS5
    }

#ifdef SYS5
    sttybuf.c_cc[VERASE] = '\010';	/* erase control-h */
    sttybuf.c_cc[VKILL] = '\030';	/* kill control-x */
    sttybuf.c_cc[VTIME] = 0;		/* delay */
    sttybuf.c_cc[VMIN] = 1;		/* hi water mark */

    sttybuf.c_cflag |= d_scbitc;
    sttybuf.c_iflag = d_scbiti;
    sttybuf.c_oflag = d_scbito;
    sttybuf.c_lflag = d_scbitl;

#else
    sttybuf.sg_erase  = '\010';   /* erase = control-H                  */
    sttybuf.sg_kill   = '\030';   /* kill = control-X                   */
    sttybuf.sg_flags  |=  d_scon;
				  /* force on  special bits for script  */
    sttybuf.sg_flags  &=  ~((int) d_scoff);
				  /* force off special bits for script  */
#endif SYS5

#ifdef SYS5
    return (ioctl (d_savdf, TCSETAW, &sttybuf));
#else
    return (ioctl (d_savfd, TIOCSETP, &sttybuf));
#endif SYS5
}


d_ttproto (baudrate)
    int baudrate;
{
#ifdef SYS5
    struct termio sttybuf;
    extern unsigned short d_prbitc, d_prbiti, d_prbito, d_prbitl;
#else
    struct sgttyb sttybuf;
    extern int d_pron, d_proff;
#endif SYS5

    if (!isnull (d_savfname[0]))
	chmod( d_savfname, 0600 );

#ifdef SYS5
    ioctl (d_savfd, TCGETA, &sttybuf);
#endif

    if (baudrate != 0)
    {                             /*  use special speed                 */
#ifdef SYS5
	sttybuf.c_cflag = (baudrate & CBAUD);
#else
	sttybuf.sg_ispeed = baudrate;
	sttybuf.sg_ospeed = baudrate;
#endif SYS5
    }
    else
    {                             /*  use current speed                 */
#ifdef SYS5
	sttybuf.c_cflags = (d_savtty.c_cflags & CBAUD);
#else
	sttybuf.sg_ispeed = d_savtty.sg_ispeed;
	sttybuf.sg_ospeed = d_savtty.sg_ospeed;
#endif SYS5
    }

#ifdef SYS5
    sttybuf.c_cc[VERASE] = '\010';	/* erase control-h */
    sttybuf.c_cc[VKILL] = '\030';	/* kill control-x */
    sttybuf.c_cc[VEOL] = '\n';
    sttybuf.c_cc[VEOF] = '\004';

    sttybuf.c_cflag |= d_prbitc;
    sttybuf.c_iflag = d_prbiti;
    sttybuf.c_oflag = d_prbito;
    sttybuf.c_lflag = d_prbitl;

#else

    sttybuf.sg_erase  = '\010';   /* erase = control-H                  */
    sttybuf.sg_kill   = '\030';   /* kill = control-X                   */
    sttybuf.sg_flags  |= d_pron;  
				  /* force on  special bits for protocol*/
    sttybuf.sg_flags  &=  ~((int) d_proff);
				  /* force off special bits for protocol*/

#endif SYS5

#ifdef SYS5
    return( ioctl (d_savfd, TCSETAW, &sttybuf) );
#else
    return( ioctl (d_savfd, TIOCSETP, &sttybuf) );
#endif SYS5
}
rn(0);
    }
#ifdef SYS5
    retval = ioctl (d_savfd, TCSETAW, &d_savtty);mmdf/lib/dial/d_packet.c   444      0     12       40103  3644552231  10370 #
# include  "d_proto.h"
# include  "d_returns.h"
# include  <signal.h>
# include  <stdio.h>
# include  "d_structs.h"
# include  "d_syscodes.h"

extern int d_master;
extern int d_errno;
extern int d_xretry;
extern int d_nbuff;
extern int d_toack;
extern int d_rcvseq;
extern int d_todata;
static struct pkbuff packst[NBUFFMAX];
static int first;		/*  index of first packet in buff  */
static int nout;		/*  number of packets in buff      */

/*  Jun 81  D. Crocker      Major rework of alarm usage
 *                          Converted some d_dbglogs to d_log
 *                          fix sndpacket() timeout return value
 *  Jul 81  D. Crocker      fix ack timeout to cause retransmit
 *  Dec 81  D. Crocker      add retry hack to separate packets
 *  Feb 84  D. Long         changed separating string to nn instead of nrnr
 */


/*
 *     D_BLDPACK
 *
 *     routine which builds a packet for transmission.  most of the
 *     understanding of the packet format is in this function.
 *
 *     type -- packet type code
 *
 *     seqnum -- packet sequence number
 *
 *     eof -- end of file indicator.  when non-zero, it causes the eof bit
 *            to be set in the outgoing packet
 *
 *     text -- if non-zero, then this is taken to be a pointer to the text to
 *             be included in the packet
 *
 *     buffer -- place to load the complete packet
 */

d_bldpack (type, seqnum, eof, text, buffer)
char    type,
       *buffer;
register char  *text;
int     seqnum,
        eof;
{
    register char  *cp;
    register int    flags;
    int     length;

#ifdef D_DBGLOG
    d_dbglog ("d_bldpack", "building packet - type '%c', seqnum %d, eof %d",
	    type, seqnum, eof);
#endif D_DBGLOG

    buffer[TYPEOFF] = type;

    /*  load the flag byte  */
    flags = seqnum & 03;

    if (eof)
	flags |= EOFBIT;

    if (d_master)
	flags |= MASTERBIT;

    buffer[FLAGOFF] = d_tohex (flags);

    /*  load the text, if any  */
    cp = &buffer[TEXTOFF];
    length = 6;

    if (text)
	while (*text)
	{
	    *cp++ = *text++;
	    length++;
	}

    /*  tack on the checksum and the line terminator  */
    d_cscomp (buffer, length);

    *cp++ = '\r';
    *cp++ = '\n';
    /*  although the '\0' is not used by this package, it makes
     *  printing easier.
     */
    *cp = '\0';
    length += 2;

    return (length);
}

/*
 *
 *     D_SNPKT
 *
 *     this routine sends a packet and waits for a given type response
 *     packet from the other side.  retransmissions are done here.
 *
 *      type -- type of packet being sent; used to determine reply type.
 *
 *     packet -- pointer to fully formed packet
 *
 *     length -- length of the packet
 *
 */

d_snpkt (type, packet, length)
char    type;
char   *packet;
int     length;
{
    register int    result;
    int     next;

#ifdef D_DBGLOG
    d_dbglog ("d_snpkt", "sending packet - code '%c'", packet[TYPEOFF]);
#endif D_DBGLOG

	/*
	 * Should we wait for an outstanding packet before sending this one?
	 */

	if (type != DATAACK && nout == d_nbuff) {
		result = d_confirm (&packst[first]);
		/*  
		 * We always want to pull this msg off the front of
		 * the queue, even if the 'confirm' failed.  Ours is
		 * not to judge whether this is bad or not, but merely
		 * to indicate to the calling routine what happened.
		 * If it doesn't care that the packet wasn't ACKed,
		 * then we don't want to screw things up by leaving
		 * the packet in the queue.
		 */
		first = (first + 1) % NBUFFMAX;
		nout--;
		switch (result)
		{
		case D_OK:
			break;

		case D_FATAL:
			/*
			 * Couldn't get an ACK on the last packet.
			 * Return an error.
			 */
			return (D_FATAL);

		default:
#ifdef D_LOG
			d_log ("d_snpkt", "Unknown return (%d) from d_confirm", result);
#endif D_LOG
			return (D_FATAL);
		}
	}
	/*
	 * We now have a space for this packet, store it.
	 */
	next = (first + nout) % NBUFFMAX;
	d_packset(type, packet, length, &packst[next]);
	/*
	 * Send out the packet for the first time.
	 */
	result = d_transmit(&packst[next]);
	/*
	 * If this was an ACK packet, or if the write to the port failed,
	 * then there is no need to wait around for an ACK.  Note that in
	 * this case the packet was essentially never queued in the buffer
	 * because we never incremented 'nout'.
	 */
	if ((result == D_OK) || (result < 0))
		return (result);

	/* Update the information about outstanding packets */
	nout++;

	/*
	 * In some cases the packet just sent must be ACKed immediately.
	 * These cases are: 1) if buffering is not allowed at all,
	 * 2) If this is an end-of-logical-record packet, or 3) if this
	 * is not a data packet (which implies end of record).
	 * Of course, if we are going to require that the last packet
	 * be ACKed, we had better get ACK's for all the ones preceding
	 * it.
	 *
	 * Performance improvement note:  the above is only strictly true
	 * for QUIT packets.  We can wait for the next transmitted
	 * packet to get the previous ACK in all other cases. -- DR
	 */
	if (((d_fromhex (packst[next].p_data[FLAGOFF]) & EOFBIT) != 0) ||
	    (packst[next].p_data[TYPEOFF] != DATA)) {

		/*
		 * Get an immediate ACK for all outstanding packets
		 */
		while (nout > 0) {
#ifdef D_DBGLOG
			d_dbglog ("d_snpkt", "nout = %d, pack index = %d", nout, first);
#endif D_DBGLOG
			result = d_confirm (&packst[first]);
			/*  See note above for explanation of next two lines  */
			first = (first + 1) % NBUFFMAX;
			nout--;
			switch (result) {
			case D_OK:
				break;

			case D_FATAL:
				return (D_FATAL);

			default:
#ifdef D_LOG
				d_log ("d_snpkt", "Unknown return (%d) from d_transmit",
				        result);
#endif D_LOG
				return (D_FATAL);
			}
		}
		return (D_OK);
	}

    /*  If we get here, then this packet has not yet been ACKed, but
     *  we are going to fudge and return D_OK, planning all the while
     *  to get the ACK later.  If this packet is never ACKed, then its
     *  error will be returned on a later packet.
     */
    return (D_OK);
}

/*
 *
 *	D_TRANSMIT
 *
 *	this routine does the actual work of transmitting the packet.
 *	It returns a value indicating whether an ACK should be expected.
 *
 *	packet - a pointer to the packet structure
 *
*/

d_transmit(pack)
  struct pkbuff *pack;
    {
    register int result;

#ifdef D_DBGLOG
    d_dbglog ("d_transmit", "transmitting '%s'", pack->p_data);
#endif D_DBGLOG

    if ((result = d_wrtport (pack->p_data, pack->p_length)) < 0)
    {
#ifdef D_LOG
	d_log ("d_transmit", "on packet [%c](%d)'%s'",
		pack->p_pktype, pack->p_length, pack->p_data);
#endif D_LOG
	return (result);
    }

    /*  If this is an ACK packet, then it doesn't get acknowledged  */
    switch (pack->p_pktype)
    {
	case DATAACK:	      /*  acknowledge DATA packet           */
	case XPATHACK:        /*  acknowledge XPATH packet          */
	case RPATHACK:        /*  acknowledge RPATH packet          */
	case ESCAPEACK:       /*  acknowledge ESCAPE packet         */
	case QUITACK: 	      /*  acknowledge QUIT packet           */
	case NBUFFACK:	      /*  acknowledge NBUFF packet	    */
	    return (D_OK);

	default:
	    return (D_CONTIN);
    }
}

/*
 *
 *	D_PACKSET
 *
 *	type - the type code for this packet
 *	packet - pointer to the start of the packet itself
 *	length - length of the packet in chars
 *	pack - a structure with data about the packet being sent
 *
 *
*/

d_packset (type, packet, length, pack)
  char type;
  char packet[];
  int length;
  struct pkbuff *pack;
    {
    register char *cp;
    register int index;

    /*  transfer appropriate information to the structure  */
    for (cp = pack->p_data, index = 0;  index < length;  index++)
	*cp++ = packet[index];
    /*  Makes printing easier if needed after an error  */
    *cp = '\0';
    pack->p_length = length;
    pack->p_pktype = type;
    pack->p_seqnum = d_fromhex (packet[FLAGOFF]) & 03;

    switch (type)
    {
	case DATA: 		  /*  data packet  */
	    /*  Remember, if data was sent, an ACK is being waited on  */
	    pack->p_timeo = d_toack;
	    pack->p_acktype = DATAACK;
	    return;

	case XPATH: 	  /*  transmit path packet  */
	    pack->p_timeo = XPAWAIT;
	    pack->p_acktype = XPATHACK;
	    return;

	case RPATH: 	  /*  receive path packet  */
	    pack->p_timeo = RPAWAIT;
	    pack->p_acktype = RPATHACK;
	    return;

	case ESCAPE: 	  /*  escape packet  */
	    pack->p_timeo = EACKWAIT;
	    pack->p_acktype = ESCAPEACK;
	    return;

	case QUIT: 		  /*  quit packet  */
	    pack->p_timeo = QACKWAIT;
	    pack->p_acktype = QUITACK;
	    return;

	case NBUFF:		/* nbuffer packet */
	    pack->p_timeo = NBACKWAIT;
	    pack->p_acktype = NBUFFACK;
	    return;
    }
}

/*
 *
 *	D_CONFIRM
 *
 *	this routine looks for the ACK for the given pack.  If
 *	it fails to recv it in a reasonable time, it retransmits
 *	the pack.
 *
 *	packet - a pointer to the struct containing info about the
 *		 packet we are trying to get ACKed.
 *
*/

d_confirm (packet)
struct pkbuff *packet;
{
	register int result, index;
	register int retries = 0;

	/*  loop, checking for the ACK and trying a retransmit  */

#ifdef D_DBGLOG
	d_dbglog("d_confirm", "Looking for ack of packet [%c](%d)'%s'", 
		packet->p_pktype, packet->p_length, packet->p_data);
#endif D_DBGLOG

	while (retries++ < d_xretry) {
		/*  The packet was sent once before this routine was called.
		 *  Therfore, start off looking for the ACK.
		 */
		result = d_getack (packet);

		if (result > 0)
			return (result);

		if (result == D_RETRY) {
			/*
			 * Because the later packets should've been dropped
			 * due to bad sequence numbers, we must retransmit
			 * this packet and its successors.
			 * Send newlines to clear out any leftover noise.
			 */
#ifdef D_DBGLOG
			d_dbglog("d_confirm", "Retransmitting %d packet[s]", nout);
#endif D_DBGLOG
#ifdef D_LOG
			d_log ("d_confirm", "separating");
#endif D_LOG
			d_wrtport ("\n\n", 2);
			for(index = 0;  index < nout;  index++) {
				if (d_transmit (&packst[(first+index) % NBUFFMAX]) < 0) {
#ifdef D_DBGLOG
					d_dbglog("d_confirm", "Error on retransmission");
#endif D_DBGLOG
					return (D_FATAL);
				}
			}
			continue;
		}

		/*  An unknown error  */
#ifdef D_DBGLOG
		d_dbglog ("d_confirm", "Unknown error from 'getack()'");
#endif D_DBGLOG
		return (result);
	}

#ifdef D_LOG
	d_log ("d_confirm", "retransmissions (%d) exhausted", retries-1);
	d_log ("d_confirm", "on packet [%c](%d)'%s'",
		packet->p_pktype, packet->p_length, packet->p_data);
#endif D_LOG
	d_errno = D_NORESP;
	return (D_FATAL);
}


/*
 *
 *	D_GETACK
 *
 *	Actually tries to get the ACK.  This routine will set up
 *	the time out alarm.  It returns when the alarm sounds, or
 *	when the ACK has been received.	
 *
 *	pack - a pointer to the structure representing the packet
 *
*/

d_getack (pack)
  struct pkbuff *pack;
    {
    char respbuff[MAXPACKET + 2];
    register int recvseq, result;

    /*  read incoming packets and watch for errors.  interrupted
     *  reads return, allowing the calling routine to retransmit.
     *  Other errors cause an immediate return.
     */
    d_alarm (pack->p_timeo);

#ifdef D_DBGLOG
    d_dbglog ("d_getack", "Wanted: ack type '%c', seqnum %d",
				pack->p_acktype, pack->p_seqnum);
#endif D_DBGLOG
    for ( ;; )
    {
	if ((result = d_rdport (respbuff)) < D_OK)
	{
	    if (result == D_INTRPT)
		/*  The ack timed out  */
		return (D_RETRY);
	    return (result);
	}

	/*  check the type and sequence number  */
	if (respbuff[TYPEOFF] == pack->p_acktype)
	{
	    recvseq = d_fromhex (respbuff[FLAGOFF]) & 03;

	    if (recvseq == pack->p_seqnum)
	    {
#ifdef D_DBGLOG
		d_dbglog ("d_getack", "response received");
#endif D_DBGLOG
		return (D_OK);
	    }
#ifdef D_LOG
	    else
		d_log ("d_getack", "bad packet seq num '%s'", respbuff);
#endif D_LOG
	}
#ifdef D_LOG
	else
	    d_log ("d_getack", "bad packet type '%s'", respbuff);
#endif D_LOG
    }

}



/*
 *
 *     D_ACKPACK
 *
 *     this routine is called to acknowledge a received packet.  it builds
 *     the ack and sends it.  an ack won't be acknowledged.
 *
 *     type -- the type of the packet to be acknowledged
 *
 *     seqnum -- the sequence number of the packet
 *
 *
 *     return values:
 *
 *          D_NONFATAL -- returned if the received packet has an unknown type
 */

d_ackpack (type, seqnum)
char    type;
register int    seqnum;
{
    char    ackbuff[MAXPACKET + 2];
    register int    ackleng,
                    result;

#ifdef D_DBGLOG
    d_dbglog ("d_ackpack", "acknowledging packet type '%c', seqnum %d",
	    type, seqnum);
#endif D_DBGLOG

    /*  switch on the type.  some packet don't get ack's sent  */
    switch (type)
    {
	case DATAACK: 
	case XPATHACK: 
	case RPATHACK: 
	case QUITACK: 
	case ESCAPEACK: 
	case NBUFFACK:
	    return (D_OK);
    }

    /*
     * this code produces a protocol change which acts to alleviate
     * a protocol deadlock problem in which the peers got out of sync
     * during the envelope-verification phase.
     */
    /* if this packet is too badly out of sequence, do not ack it */
    if (seqnum == d_incseq(d_rcvseq))
    {
#ifdef D_DBGLOG
	d_dbglog("d_ackpack", "packet way out of seq--no ack--d_rcvseq %d", d_rcvseq);
#endif D_DBGLOG
    	return(D_OK);
    }

    /* now, generate the proper ACK */
    switch(type)
    {
	case DATA: 
	    ackleng = d_bldpack ((type = DATAACK), seqnum,
					1, (char *) 0, ackbuff);
	    break;

	case RPATH: 
	    ackleng = d_bldpack ((type = RPATHACK), seqnum,
					1, (char *) 0, ackbuff);
	    break;

	case XPATH: 
	    ackleng = d_bldpack ((type = XPATHACK), seqnum,
					1, (char *) 0, ackbuff);
	    break;

	case QUIT: 
	    ackleng = d_bldpack ((type = QUITACK), seqnum,
					1, (char *) 0, ackbuff);
	    break;

	case ESCAPE: 
	    ackleng = d_bldpack ((type = ESCAPEACK), seqnum,
					1, (char *) 0, ackbuff);
	    break;

	case NBUFF:
	    ackleng = d_bldpack ((type = NBUFFACK), seqnum,
					1, (char *) 0, ackbuff);
	    break;

	default: 
#ifdef D_LOG
	    d_log ("d_ackpack", "unknown packet type '%c'", type);
#endif D_LOG
	    return (D_NONFATAL);
    }

    /*  send that pack that we've built  */
    result = d_snpkt (type, ackbuff, ackleng);
    return (result);
}

/*
 *
 *     D_WATCH
 *
 *     this routine is called to watch the incoming packets for one of
 *     a specified type.  all other packets, except QUIT's, are ignored.
 *
 *     packet -- place to load the received packet
 *
 *     type -- the type of the packet we're looking for
 */

d_watch (packet, type)
register char  *packet;
char    type;
{
    register int    length;
    int     recvseq;

#ifdef D_DBGLOG
    d_dbglog ("d_watch", "watching for packet with code '%c'", type);
#endif D_DBGLOG

    switch (type)		  /* set timer, for delayed packets     */
    {
	case DATA: 		  /*  data packet                       */
	    d_alarm (d_todata);
	    break;

	case XPATH: 		  /*  transmit path packet              */
	    d_alarm (XPATHWAIT);
	    break;

	case RPATH: 		  /*  receive path packet               */
	    d_alarm (RPATHWAIT);
	    break;

	case ESCAPE: 		  /*  escape packet  */
	    d_alarm (ESCAPEWAIT);
	    break;

	case NBUFF:		/* windowsize packet */
		d_alarm(NBUFFWAIT);
		break;
    }

/*  read packets and search.  QUIT's cause a return after acknowlegement  */

    d_rcvseq = d_incseq (d_rcvseq);

    for ( ;; )
    {
	length = d_rdport (packet);

	if (length < 0)
	{
#ifdef D_LOG
	    d_log ("d_watch", "bad watch read (%d)", length);
#endif D_LOG
	    break;
	}
	recvseq = d_fromhex (packet[FLAGOFF]) & 03;

	if (recvseq != d_rcvseq)
	{
#ifdef D_LOG
	    d_log ("d_watch",
		    "packet out of sequence - type '%c', got %d, expected %d",
		    packet[TYPEOFF], recvseq, d_rcvseq);
	    d_log ("d_watch", "in '%s'", packet);
#endif D_LOG
	    continue;
	}

	if (packet[TYPEOFF] == type)
	    break;            /* success                            */

	/*  We have received a packet, but it is not the type we
	 *  wanted.  Let's try to interrpret it as a special control
	 *  packet.
	 */
	switch (d_control (packet, length))
	{
	    case D_FATAL:
		return (D_FATAL);

	    case D_OK:
	    case D_NONFATAL:
		break;
	}
    }

    return (length);
}

/*
 *
 *     D_INCSEQ
 *
 *     this routine increments the sequence number passed as the argument
 *     and returns the result.
 *
 *     seqnum -- the sequence number to be incremented
 */

d_incseq (seqnum)
register int    seqnum;
{

    seqnum = (seqnum + 1) & 03;
    return (seqnum);
}

    register int    ackleng,
                    result;

#ifdef D_DBGLOG
    d_dbglog ("d_ackpack", "acknowledging packet type '%c', seqnum %d",
	    type, seqnum);
#endif D_DBGLOG

    /*  switch on the type.  some packet don't get ack's sent  */
    switch (type)
    {
	case DATAACK: 
	case XPATHACK: 
	case RPATHACK: 
	case QUITACK: 
	case ESCAPEACK: 
	case NBUFFACK:
	    return (D_OK);
    }

    /*
     * this code produces a protocol mmdf/lib/dial/d_slave.c   444      0     12       11534  3620510406  10231 # include  "util.h"
# include  "ll_log.h"
# include  "d_proto.h"
# include  "d_returns.h"
# include  "d_structs.h"

/*	May 82  D. Crocker  added startup newline, to bypass some noise
 */

extern int errno;
extern int d_errno;

/*
 *     D_SLVCONN
 *
 *     this routine is called by the slave protocol module to acknowledge
 *     a connection by the remote system.  it sets the port characteristics
 *     and performs the protocl startup sequence.
 *
 *     logfile -- name of the log file
 *
 *     tson -- set to non-zero to indicate that a transcript of the connection
 *             should be kept
 *
 *     tsfile -- name of the file in which the transcript should be kept
 *
 *     debug -- set to non-zero to enable debug logging
 *
 *     portfd -- file descriptor of the port
 *
 *     maxrecv -- maximum receive packet length
 *
 *     maxxmit -- maximum transmit packet length
 *
 */

d_slvconn (logfile, tson, tsfile, debug, portfd, maxrecv, maxxmit)
struct ll_struct *   logfile;
char   tsfile[];
int     tson,
        debug,
        portfd,
        maxrecv,
        maxxmit;
{
    extern int  d_master,
                d_debug,
		d_snseq,
		d_rcvseq,
		d_lrmax,
		d_lxmax;
    extern  FILE * d_prtfp;
    register int    result;

    /*  open the log file and transcript  */
    if ((result = d_opnlog (logfile)) < 0)
	return (result);

    if (tson)
	if ((result = d_tsopen (tsfile)) < 0)
	    return (result);

    /*  set up globals  */
    d_debug = debug;
    d_master = 0;
    d_snseq = 3;		/* these are incremented before any ... */
    d_rcvseq = 3;		/* ... packets are expected */
    d_lrmax = maxrecv;
    d_lxmax = maxxmit;

    /* use stdio for input and write() calls for output */
    d_prtfp = fdopen (portfd, "r");

    /*  save current tty parameters and and set tty for protocol mode       */
    if (d_ttsave (d_prtfp, NULL) < 0 || d_ttproto (0) < 0)
    {                             /* don't abort; may be ok             */
#ifdef D_LOG
	d_log ("d_slvconn", "error getting port parameters (errno=%d)",
			errno);
#endif D_LOG
	d_errno = D_PRTSGTTY;
    }


    /*  do the protocol startup sequence  */
    result = d_slvstart ();
    return (result);
}

/*
 *     D_SLVDROP
 *
 *     this routine is called to cause the connection from the master to
 *     be dropped.
 *
 *     sndquit -- called with non-zero value if the slave should attempt a
 *                a QUIT exchange.
 */

d_slvdrop (sndquit)
int     sndquit;
{
    register int    result;

    if (sndquit)
	result = d_snquit ();

#ifdef D_LOG
    d_log ("d_slvdrop", "slave dropped connection");
#endif D_LOG

    if (d_ttrestore () < 0)
    {                             /* restory tty characteristics        */
	d_errno = D_PRTSGTTY;
    }
    d_tsclose ();
    d_clslog ();

    return (result);
}

/*
 *     D_SLVSTART
 *
 *     this routine is called in the slave to start the protocol.
 */

d_slvstart ()
{
    extern int  d_lxmax,
		d_lrmax,
		d_rxmax,
		d_rrmax,
                d_maxtext;
    extern char d_snesc,
		d_rcvesc;
    extern  unsigned short d_lxill[],
	    d_lrill[],
	    d_rxill[],
	    d_rrill[],
	    d_lcvec[];
    register int    result;

#ifdef D_DBGLOG
    d_dbglog ("d_slvstart", "Beginning slave host startup sequence");
#endif D_DBGLOG

    sleep (2);  /* wait for line-settling */

    d_wrtport ("\n", 1);  /* hack to bypass initial noise */

    /*  send XPATH and RPATH to the master  */
    if ((result = d_snpath (XPATH, d_lxmax, d_lxill))
	    < 0)
	return (result);

#ifdef D_DBGLOG
    d_dbglog ("d_slvstart", "XPATH sent ok.");
#endif D_DBGLOG

    if ((result = d_snpath (RPATH, d_lrmax, d_lrill))
	    < 0)
	return (result);

#ifdef D_DBGLOG
    d_dbglog ("d_slvstart", "RPATH sent ok.");
#endif D_DBGLOG

    /*  get XPATH and RPATH from the master.  then form the
     *  local transmission encoding vector.
     */
    if ((result = d_getpath (XPATH, &d_rxmax, d_rxill)) < 0)
	return (result);

#ifdef D_DBGLOG
    d_dbglog ("d_slvstart", "XPATH received ok.");
#endif D_DBGLOG

    if ((result = d_getpath (RPATH, &d_rrmax, d_rrill)) < 0)
	return (result);

#ifdef D_DBGLOG
    d_dbglog ("d_slvstart", "RPATH received ok.");
#endif D_DBGLOG

    d_orbitvec (d_lxill, d_rrill, d_lcvec);

    d_maxtext = d_minimum (d_lxmax, d_rrmax);
#ifdef D_DBGLOG
    d_dbglog ("d_slvstart", "Maximum transmit packet size set to %d", d_maxtext);
#endif D_DBGLOG
    d_maxtext -= LHEADER;

    /*  send our receive escape code to the master and get his  */
    if ((result = d_snfescape ()) < 0)
	return (result);

#ifdef D_DBGLOG
    d_dbglog ("d_slvstart", "ESCAPE sent ok.  Receive escape '%c'", d_rcvesc);
#endif D_DBGLOG

    if ((result = d_getescape ()) < 0)
	return (result);

#ifdef D_DBGLOG
    d_dbglog ("d_slvstart", "ESCAPE received ok.  Transmit escape '%c'",
	    d_snesc);
#endif D_DBGLOG

#ifdef D_LOG
    d_log ("d_slvstart", "Slave protocol startup completed ok.");
#endif D_LOG

    return (D_OK);
}
aximum transmit packet length
 *
 */

d_slvconn (logfile, tson, tsfile, debug, portfd, maxrecv, maxxmit)
struct ll_struct *   logfile;
char   tsfile[];
int     tsonmmdf/lib/dial/d_io.c   444      0     12        5305  3620510406   7505 #
#include	<stdio.h>

/*  This is a set of routines that is called to read characters from
 *  the tty line.  It stores them up so that they may be "replayed"
 *  if necessary.  The characters are stored in a circular buffer.
*/

/*  The following functions are implemented:
 *
 *	getc - returns 1 character from the appropriate place.
 *	mark - designates the next character to be read as the one
 *	       to start with if a replay is requested.
 *	replay - further calls to d_getc() will read characters from
 *		 the buffer until it is exhausted.
 *
 *
 *	Added the select call to adapt to 4.2's signal mechanism.
 *		DRockwell@BBN 31 Jan 84
 *	Backed out above change in favor of the s_io library.
 *		DRockwell@BBN 1 Mar 84
*/

#define		BUFFSIZE		256

static int nleft;	/*  number of chars remaining in this replay	*/
static int current;	/*  index of current char in this replay	*/
static int last;	/*  next position to insert character at	*/
static int nchars;	/*  total number of chars in buffer.  Note that */
			/*  this stays the same throughout a replay.	*/
static int first;	/*  index of the first character in the buffer  */
static int replay;	/*  set if we are doing a replay		*/

#ifdef V4_2BSD
static int rbuff[BUFFSIZE];
#else
static char rbuff[BUFFSIZE];
#endif

d_getc (channel)
register FILE *channel;
{
#ifdef V4_2BSD
    register int c;
#else
    char c;
#endif


    /*  Check to see if we are replaying the input  */
    if (replay != 0)
    {
	if (nleft == 0)
	{
	    replay = 0;
	    return (d_getc (channel));
	}

	c = rbuff[current];
	current = indexinc (current);
	nleft--;

	/*  Be careful not to return an EOF from the queue.  They
	 *  get in here when an alarm call from the timeout code
	 *  interrupts a read.  However, they should clearly not
	 *  be returned from the queue, as they may not represent
	 *  the current state of things.
	 */
	if (c == EOF)
	    return (d_getc (channel));
    }
    else
    {
	if (nchars == BUFFSIZE)
	    /*  Have run out of buffer space.  Rather then leave
	     *  a partial line, which could screw up 'rdport',
	     *  delete the first line in the buffer
	     */
	    skpline();
	else
	    nchars++;
	c = getc (channel);

	rbuff[last] = c;
	last = indexinc (last);
    }

    return (c);
}


d_replay ()
    {

    replay = 1;
    current = first;
    nleft = nchars;

}


d_mark ()
    {

    replay = 0;
    nchars = 0;
    first = 0;
    last = 0;
}


static indexinc (index)
  int index;
    {

    if (index == (BUFFSIZE-1))
	return (0);
    else
	return (index + 1);
}


static skpline ()
    {

    while ((rbuff[first] != '\n') &&
	   (nchars > 0          )   )
    {
	first++;
	nchars--;
    }

    if (nchars <= 0)
	d_mark ();
    else
    {
	first++;
	nchars--;
    }
}
OG

    sleep (2);  /* wait for line-settling */

    d_wrtport ("\n", 1);  /* hack to bypass initial noise */

    /*  send XPATH and RPATH to the master  */
    if ((result = d_snpath (XPATH, d_lxmax, d_lxill))
	    < 0)
	return (result);

#ifdef D_DBGLOG
    d_dbglog ("d_slvstart", "XPATH sent ok.");
#endif D_Dmmdf/lib/dial/d_getnbuff.c   444      0     12        1633  3620510406  10676 #include  "d_proto.h"
#include <signal.h>
#include  "d_syscodes.h"
#include  "d_returns.h"

/*
 *     D_GETNBUFF
 *
 *     this routine watches for an incoming NBUFF packet from the remote
 *     host.  it thens set the transmit buffering limit.
 *
 */

d_getnbuff()
    {
    extern int  d_errno;
    extern int  d_nbuff;
    register int  length;
    char  packet[MAXPACKET + 2];

#ifdef D_DBGLOG
    d_dbglog("d_getnbuff", "looking for NBUFF from other end");
#endif D_DBGLOG

/*  set timer so we don't wait forever  */

    length = d_watch(packet, NBUFF);

    if (length < 0)
	return(length);

    if (length != LNBUFF)
    {
#ifdef D_DBGLOG
      d_dbglog("d_getnbuff", "bad NBUFF packet length (%d)", length);
#endif D_DBGLOG
      d_errno = D_INITERR;
      return (D_FATAL);
    }

      d_nbuff = d_fromhex(packet[NBOFF]);
      d_nbuff = (d_nbuff << 4) | d_fromhex(packet[NBOFF + 1]);
      return(D_OK);
    }
to insert character at	*/
static int nchars;	/*  total number of chars in buffer.  Note that */
			/*mmdf/lib/table/   755      0     12           0  3671074620   6530 mmdf/lib/table/ch_tbseq.c   444      0     12       10240  3671073176  10571 /*
 *     MULTI-CHANNEL MEMO DISTRIBUTION FACILITY  (MMDF)
 *     
 *
 *     Copyright (C) 1979,1980,1981  University of Delaware
 *     
 *     Department of Electrical Engineering
 *     University of Delaware
 *     Newark, Delaware  19711
 *
 *     Phone:  (302) 738-1163
 *     
 *     
 *     This program module was developed as part of the University
 *     of Delaware's Multi-Channel Memo Distribution Facility (MMDF).
 *     
 *     Acquisition, use, and distribution of this module and its listings
 *     are subject restricted to the terms of a license agreement.
 *     Documents describing systems using this module must cite its source.
 *
 *     The above statements must be retained with all copies of this
 *     program and may not be removed without the consent of the
 *     University of Delaware.
 *     
 *
 *     version  -1    David H. Crocker    March   1979
 *     version   0    David H. Crocker    April   1980
 *     version  v7    David H. Crocker    May     1981
 *     version   1    David H. Crocker    October 1981
 *
 */

/*  Sep 81  Dave Crocker      added ch_tclose & ch_tfree, to allow
 *                            handling file descriptor overflow
 *  May 82  Dave Crocker      add ch_tread, to focus input
 *  Jun 82  Dave Crocker      split ch_tio off from ch_table
 *                            if local name, use lname, for proper case
 *  May 84  G. B. Reilly      added in domain support
 */

#include "util.h"
#include "mmdf.h"
#include "ch.h"			  /* has table state strcture def       */
#include "dm.h"			  /* has domain info */

extern Chan **ch_tbsrch;
extern LLog *logptr;
extern char *locname;
extern char *ch_dflnam;

extern Domain **dm_list;


/*******************  FIND HOST NAME IN ANY TABLE  ****************** */

Chan   *
	ch_h2chan (hostr, pos) /* which chan name table is host in?  */
register char  *hostr;		  /* name of host                       */
int	pos;			  /* position */
{
    register Chan  **chanptr;
    char adrstr[LINESIZE];

/*  the list of channel name tables is first searched.  If a hit is found
 *  in the table for the local channel, OK is returned to indicate a local
 *  reference.
 */

    for (chanptr = ch_tbsrch; *chanptr != 0; chanptr++)
    {
	if (tb_k2val ((*chanptr) -> ch_table, TRUE, hostr, adrstr) == OK)
	{			  /* the hostname IS in this table      */
	    if (lexequ (ch_dflnam, (*chanptr) -> ch_name))
	    {
		return ((Chan *) OK); /* local reference                */
	    }
	    return (*chanptr);    /* let caller know which chan         */
	}
    }

    return ((Chan *) NOTOK);      /* host not listed anywhere           */
}
/* ***********  GIVEN Subdomain, FIND Domain  ***************** */



Domain *
	dm_s2dom (subdomain, official, dmbuf)
char	*subdomain;		/* name of subdomain to look up */
char	*official;		/* where to stuff official name */
char	*dmbuf;			/* Domain route buffer */
{
    char *argv[DM_NFIELD];
    char val[LINESIZE];
    register Domain **dmnptr;

    for (dmnptr = dm_list; *dmnptr != 0; dmnptr++)
    {
	if (tb_k2val ((*dmnptr) -> dm_table, TRUE, subdomain, val) == OK) {
	    (void) strcpy(dmbuf, val);
	    str2arg(val, DM_NFIELD, argv, 0);
	    (void) strcpy(official, argv[0]);
    	    return (*dmnptr);
	}
    }

    (void) strcpy (official, subdomain); /* to have something in the buffer */
    (void) strcpy (dmbuf, subdomain);    /* to have something in the buffer */
    return ((Domain *) NOTOK);
}

/* *******  FIND VALUE (address), GIVEN ITS KEY (hostname)  ********* */

tb_k2val (table, first, name, buf) /* use key and return value */
register Table  *table;
int     first;                    /* start at beginning of list?        */
char   *buf;			  /* put value int this buffer          */
register char  *name;		  /* name of ch "member" / key          */
{
    char    host[LINESIZE];

    if (!tb_open (table, first))
	return (NOTOK);		  /* not opened                         */

    while (tb_read (table, host, buf))
    {				  /* cycle through keys                 */
	if (lexequ (name, host))  /* does key match search name?        */
	{
	    compress (buf, buf);  /* get rid of extra white space       */
	    return (OK);
	}
    }

    (void) strcpy (buf, "(ERROR)");
    return (NOTOK);
}
e
    {
	first++;
	nchars--;
    }
}
OG

    sleep (2);  /* wait for line-settling */

    d_wrtport ("\n", 1);  /* hack to bypass initial noise */

    /*  send XPATH and RPATH to the master  */
    if ((result = d_snpath (XPATH, d_lxmax, d_lxill))
	    < 0)
	return (result);

#ifdef D_DBGLOG
    d_dbglog ("d_slvstart", "XPATH sent ok.");
#endif D_Dmmdf/lib/table/gen   555      0     12          57  3620510407   7244 make -f ../../Makefile.com -f Makefile.real $*

extern Chan **ch_tbsrch;
extern LLog *logptr;
extern char *locname;
extern char *ch_dflnam;

extern Domain **dm_list;


/*******************  FIND HOST NAME IN ANY TABLE  ****************** */

Chan   *
	ch_h2chan (hostr, pos) /* which chan name table is host in?  */
register char  *hostr;		  /* name of host                       */
int	pos;			  /* position */
{
    register Chan  **chanptr;
    char adrstr[LINESIZE];

/*  the list of channel name tables is fmmdf/lib/table/dm_table.c   444      0     12       26567  3671073200  10557 /*
 *     MULTI-CHANNEL MEMO DISTRIBUTION FACILITY  (MMDF)
 *
 *
 *     Copyright (C) 1979,1980,1981  University of Delaware
 *
 *     Department of Electrical Engineering
 *     University of Delaware
 *     Newark, Delaware  19711
 *
 *     Phone:  (302) 738-1163
 *
 *
 *     This program module was developed as part of the University
 *     of Delaware's Multi-Channel Memo Distribution Facility (MMDF).
 *
 *     Acquisition, use, and distribution of this module and its listings
 *     are subject restricted to the terms of a license agreement.
 *     Documents describing systems using this module must cite its source.
 *
 *     The above statements must be retained with all copies of this
 *     program and may not be removed without the consent of the
 *     University of Delaware.
 *
 *
 *     version  -1    David H. Crocker    March   1979
 *     version   0    David H. Crocker    April   1980
 *     version  v7    David H. Crocker    May     1981
 *     version   1    David H. Crocker    October 1981
 *
 */

#include "util.h"
#include "mmdf.h"
#include "ch.h"                   /* has table state strcture def       */
#include "dm.h"
#include "chdbm.h"

extern Domain **dm_list;
extern Domain *dm_s2dom();
extern struct ll_struct   *logptr;
extern char *strcpy ();
extern char *index();
extern char *rindex();
extern char *locdomain;
extern char *locname;

/*
 * when mangling addresses, should we try the reverse before or after
 * the RFC822 direction
 * is set by submit otherwise is as below.
 */

typedef struct {char *dptr; int dsize;} datum;


/*
 * Steve Kille   jan 84   Redo this module
 *
 * G. Brendan Reilly May 84     take out dependencies on DBM package
 *
 *
 */


/* *******  FIND VALUE (address), GIVEN ITS KEY (hostname)  ********* */


LOCFUN
dm_k2val (dmntbl, subdmn, domain, dmnroute)
				   /* sub-domain spec -> routing host   */
register Domain  *dmntbl;         /* domain table to look in            */
register char  *subdmn;          /* name of sub-domain to look for     */
char *domain;                   /* where to stuff full domain name      */
Dmn_route *dmnroute;              /* where to put routing information   */
{
    char *p;

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "dm_k2val (%s, %s)", dmntbl -> dm_name, subdmn);
#endif

    switch(tb_k2val (dmntbl -> dm_table, TRUE, subdmn, dmnroute -> dm_buf)){
    case MAYBE:
	return(MAYBE);
    case OK:
#ifdef DEBUG
	ll_log (logptr, LLOGFTR, "hit");
#endif
	dmnroute -> dm_argc = str2arg (dmnroute -> dm_buf,
			DM_NFIELD, dmnroute -> dm_argv, (char *)0);
	(void) strcpy(domain, dmnroute -> dm_argv[0]);
	return (OK);
    }

    p = subdmn;
    while ((p = index (p, '.')) != (char *)0)
    {
	char tdmnbuf[LINESIZE];

	p++;
	switch(tb_k2val (dmntbl -> dm_table, TRUE, p, tdmnbuf)) {
	case MAYBE:
	    return(MAYBE);
	default:
	    continue;
	case OK:
	    break;
	}
					/* get the entry                    */
#ifdef DEBUG
	ll_log (logptr, LLOGFTR, "hit");
#endif
	*(p - 1) = '\0';            /* build the domain             */
	if (dmntbl -> dm_domain[0] == '\0') {
	    if (*p == '\0')
		(void) strcpy (domain, subdmn);
	    else
		sprintf (domain, "%s.%s", subdmn, p);
	}
	else
	    sprintf (domain, "%s.%s.%s", subdmn, p, dmntbl -> dm_domain);

	sprintf (dmnroute -> dm_buf, "%s %s", domain, tdmnbuf);
	dmnroute -> dm_argc = str2arg (dmnroute -> dm_buf,
			DM_NFIELD, dmnroute -> dm_argv, (char *)0);
	return (OK);
    }

    return (NOTOK);
}

/* ***********  GIVEN Subdomain, FIND HOST ROUTE  ************* */
/*
 * If BOTHEND is defined then we are almost certainly running in JNT
 * land and so we should try the reversed address before the RFC822
 * direction, should save some cpu cycles
 */

LOCFUN
Domain *
#ifndef BOTHEND
	dm_sd2route (value, domain, dmnroute)
    char *value;                /* what the use provides                */
    char *domain;               /* full domain value                    */
    Dmn_route *dmnroute;        /* source route                         */
#else
	dm_sd2route (value, reverse, domain, dmnroute)
    char *value;                /* what the use provides                */
    char *reverse;              /* the same bacwards                    */
    char *domain;               /* full domain value                    */
    Dmn_route *dmnroute;        /* source route                         */
#endif
{
    char *sdptr;
    Domain *dmnptr;
    char official[LINESIZE];
#ifdef BOTHEND
    char *revsdptr = reverse;
#endif
#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "dm_sd2route (%s)", value);
#endif
					/* first try the JNT way round if */
					/* got BOTHEND defined */
#ifdef	BOTHEND
    if ((dmnptr = dm_s2dom (reverse, official, dmnroute -> dm_buf)) !=
(Domain *) NOTOK || (dmnptr = dm_s2dom (value, official, dmnroute -> dm_buf))
	!= (Domain *) NOTOK)
#else
    if ((dmnptr = dm_s2dom (value, official, dmnroute -> dm_buf))
	!= (Domain *) NOTOK)
#endif
    {
	if(dmnptr == (Domain *)MAYBE)
	    return(dmnptr);
	(void) strcpy(domain, official);
	dmnroute -> dm_argc = str2arg (dmnroute -> dm_buf, DM_NFIELD,
		dmnroute -> dm_argv, (char *)0);
#ifdef DEBUG
	ll_log (logptr, LLOGFTR, "Domain value '%s' from '%s'",
		domain, dmnptr -> dm_show);
#endif
	return (dmnptr);
    }

    /*
     *  If not, move in from left.  Progressing on both forwards
     *  and backwards options.
     */
    sdptr = value;
    while ((sdptr = index (sdptr, '.')) != (char *) 0)
    {
	char tbuf[LINESIZE];

#ifdef  BOTHEND
	revsdptr = index (revsdptr, '.') + 1;
	if ((dmnptr = dm_s2dom (revsdptr, official, tbuf)) != (Domain *) NOTOK)
	{
	    if(dmnptr == (Domain *)MAYBE)
		return(dmnptr);
	    *(revsdptr - 1) = '\0';
	    sprintf (domain, "%s.%s", reverse, official);
	    sprintf (dmnroute -> dm_buf, "%s %s", domain, tbuf);
	    dmnroute -> dm_argc = str2arg (dmnroute -> dm_buf, DM_NFIELD,
		dmnroute -> dm_argv, (char *)0);
#ifdef DEBUG
	    ll_log(logptr,LLOGFTR, "Domain value (rev) '%s' from '%s',o = '%s'",
		domain, dmnptr -> dm_show, official);
#endif
	    return (dmnptr);
	}
#endif BOTHEND

	if ((dmnptr = dm_s2dom (++sdptr, official, tbuf)) != (Domain *) NOTOK)
	{
	    if(dmnptr == (Domain *)MAYBE)
		return(dmnptr);
	    *(sdptr - 1) = '\0';
	    sprintf (domain, "%s.%s",  value, official);
	    sprintf (dmnroute -> dm_buf, "%s %s", domain, tbuf);
	    dmnroute -> dm_argc = str2arg (dmnroute -> dm_buf, DM_NFIELD,
		dmnroute -> dm_argv, (char *)0);
#ifdef DEBUG
	    ll_log (logptr, LLOGFTR, "Domain value '%s' from '%s', o = '%s'",
		domain, dmnptr -> dm_show, official);
#endif
	    return (dmnptr);
	}
    }

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "'%s' not found", value);
#endif
    return ((Domain *) NOTOK);
}

/* ***********  GIVEN VALUE, FIND HOST ROUTE  ******************** */

/* This will take any value, and map it into a domain route.            */
/* It is optimised for val being a fully qualified domain               */

#ifndef BOTHEND

Domain *
	dm_v2route (value, domain, dmnroute)
    char *value;                /* what the use provides                */
    char *domain;               /* full domain value                    */
    register Dmn_route *dmnroute;        /* source route                */
{
    Dmn_route tmpdmn;           /* hold parsed domain string            */
    register Domain *dmnptr;
    int sublen;                 /* length of right-hand to look up      */
    int ind;

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "dm_v2route (%s)", value);
#endif
    (void) strcpy (tmpdmn.dm_buf, value);
    tmpdmn.dm_argc = cstr2arg (tmpdmn.dm_buf, DM_NFIELD, tmpdmn.dm_argv, '.');
    if (tmpdmn.dm_argc == NOTOK) {
#ifdef  DEBUG
	ll_log (logptr, LLOGTMP, "Cstr2arg failed (%s)", tmpdmn.dm_buf);
#endif
	return ( (Domain *)NOTOK);
    }

#ifdef DEBUG
    ll_log (logptr, LLOGFTR, "%d fields", tmpdmn.dm_argc);
#endif

    dmnroute -> dm_argc = 1;    /* initialize with something safe */
    (void) strcpy (dmnroute -> dm_buf, value);
    dmnroute -> dm_argv[0] = dmnroute -> dm_buf;

    tmpdmn.dm_argc--;

    for (ind = sublen = 0; ind <= tmpdmn.dm_argc; ind++)
    {
	if (ind != 0)
		tmpdmn.dm_buf[sublen - 1] = '.';
				/* undo cstr2arg!                       */
	sublen += strlen (tmpdmn.dm_argv[ind]);
				/* how much to peel off, from right     */
				/* the +1 is for the delimiter          */
	if (ind != tmpdmn.dm_argc)
		sublen++;       /* point to next if not last            */
	if ((dmnptr = dm_nm2struct (&value[sublen])) == (Domain *) NOTOK)
		continue;	/* failed				*/
				/* this is the value			*/
	switch(dm_k2val (dmnptr, tmpdmn.dm_argv[0], domain, dmnroute)){
	case MAYBE:
	    return( (Domain *)MAYBE);
	case OK:
#ifdef DEBUG
	    ll_log (logptr, LLOGFTR,
		"%s' hit in domain table '%s' with '%s'",
		value, dmnptr -> dm_show, dmnroute -> dm_buf);
#endif
	   return (dmnptr);
	}
    }
    return (dm_sd2route (value, domain, dmnroute));
}

#else   BOTHEND

LOCFUN Domain *
dm_rv2route (value, domain, dmnroute)
    char *value;                /* what the use provides                */
    char *domain;               /* full domain value                    */
    register Dmn_route *dmnroute;        /* source route                */
{
    Dmn_route tmpdmn;           /* hold parsed domain string            */
    register Domain *dmnptr;
    int sublen;                 /* length of right-hand to look up      */
    int ind;
    char buf[LINESIZE];

    (void) strcpy (tmpdmn.dm_buf, value);
    tmpdmn.dm_argc = cstr2arg (tmpdmn.dm_buf, DM_NFIELD, tmpdmn.dm_argv, '.');
    if (tmpdmn.dm_argc == NOTOK) {
#ifdef  DEBUG
	ll_log (logptr, LLOGTMP, "Cstr2arg failed (%s)", tmpdmn.dm_buf);
#endif
	return ( (Domain *)NOTOK);
    }

#ifdef DEBUG
    ll_log (logptr, LLOGFTR, "%d fields", tmpdmn.dm_argc);
#endif

    tmpdmn.dm_argc--;
    for (ind = sublen = 0; ind <= tmpdmn.dm_argc; ind++)
    {
	if (ind != 0)
		tmpdmn.dm_buf[sublen - 1] = '.';
				/* undo cstr2arg!                       */
	sublen += strlen (tmpdmn.dm_argv[ind]);
				/* how much to peel off, from right     */
				/* the +1 is for the delimiter          */
	if (ind != tmpdmn.dm_argc)
		sublen++;       /* point to next if not last            */
	if ((dmnptr = dm_nm2struct (&value[sublen])) == (Domain *) NOTOK)
		continue;	/* failed				*/
				/* this is the value			*/
	switch(dm_k2val (dmnptr, tmpdmn.dm_argv[0], domain, dmnroute)){
	case MAYBE:
	    return( (Domain *)MAYBE);
	case OK:
#ifdef DEBUG
	    ll_log (logptr, LLOGFTR,
		"%s' hit in domain table '%s' with '%s'",
		value, dmnptr -> dm_show, dmnroute -> dm_buf);
#endif
	    return (dmnptr);
	}
    }
    return ( (Domain *) 0);     /* not found */
}

Domain *
	dm_v2route (value, domain, dmnroute)
    char *value;                /* what the use provides                */
    char *domain;               /* full domain value                    */
    Dmn_route *dmnroute;        /* source route                         */
{
    Domain *dmnptr;
    char *reverse;              /* to handle bigend mess                */
    extern char *ap_dmflip();

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "dm_v2route (%s)", value);
#endif
    dmnroute -> dm_argc = 1;    /* initialize with something safe */
    (void) strcpy (dmnroute -> dm_buf, value);
    dmnroute -> dm_argv[0] = dmnroute -> dm_buf;

    if ((dmnptr = dm_rv2route (value, domain, dmnroute)) != (Domain *)0)
	return (dmnptr);
    reverse = ap_dmflip (value);
    if( (dmnptr = dm_rv2route (reverse, domain, dmnroute)) != (Domain *)0){
	free (reverse);
	return (dmnptr);
    }
    dmnptr = dm_sd2route (value, reverse, domain, dmnroute);
    free (reverse);
    return (dmnptr);
}
#endif  BOTHEND
gc = cstr2arg (tmpdmn.dm_buf, DM_NFIELD, tmpdmn.dm_argv, '.');
    if (tmpdmn.dm_argc == NOTOK) {
#ifdef  DEBUG
	ll_log (logptr, LLOGTMP,mmdf/lib/table/ch_tbdbm.c   444      0     12       23067  3671073201  10543 /*
 *     MULTI-CHANNEL MEMO DISTRIBUTION FACILITY  (MMDF)
 *
 *
 *     Copyright (C) 1979,1980,1981  University of Delaware
 *
 *     Department of Electrical Engineering
 *     University of Delaware
 *     Newark, Delaware  19711
 *
 *     Phone:  (302) 738-1163
 *
 *
 *     This program module was developed as part of the University
 *     of Delaware's Multi-Channel Memo Distribution Facility (MMDF).
 *
 *     Acquisition, use, and distribution of this module and its listings
 *     are subject restricted to the terms of a license agreement.
 *     Documents describing systems using this module must cite its source.
 *
 *     The above statements must be retained with all copies of this
 *     program and may not be removed without the consent of the
 *     University of Delaware.
 *
 *
 *     version  -1    David H. Crocker    March   1979
 *     version   0    David H. Crocker    April   1980
 *     version  v7    David H. Crocker    May     1981
 *     version   1    David H. Crocker    October 1981
 *
 */

/*  READ "NETWORK" HOST NAME TABLES
 *
 *  These routines use the dbm(3x) procedures of Unix Version 7.
 *  All accesses are via the fetch call of the library. Linkage between
 *  elements of the database are contained within the procedures instead
 *  of the database itself due to the nature of the dbm design.
 *
 *  May 81  Jim Lieb, SRI   rewrite by to use dbm library
 *  Jun 82  D. Crocker      minor cleanup & installation
 *                          force key value to be lower case
 *                          partition sequence #s by channel, not global
 *  Dec 85  P.Cockcroft UCL add nameserver support and add bullet protection
 */
#include "util.h"
#include "mmdf.h"
#include "ch.h"                   /* has table state strcture def       */
#include "dm.h"
#include "chdbm.h"

extern LLog *logptr;
extern char *tbldfldir;
extern char *tbldbm;
extern char *locname;
extern Chan **ch_tbsrch;
extern char *ch_dflnam;

extern char *strcpy();

typedef struct {char *dptr; int dsize;} datum;

extern datum fetch();

/* *******************  FIND HOST NAME IN ANY TABLE  ****************** */

Chan   *
	ch_h2chan (hostr, pos) /* which chan name table is host in?  */
char   *hostr;                   /* name of host                       */
int     pos;                    /* which position to get  (0 = first) */
{
    register	Chan	*chanptr;
    register	char	*cp;
    register	struct DBvalues *dp;
    DBMValues   dbm;
    Chan	**chp;
    char        hostname[ADDRSIZE];
#ifdef	NAMESERVER
    int         ns_done = 0;
#endif	NAMESERVER
    int         dbm_done = 0;

/*  the list of channel name tables is first searched.  If a hit is found
 *  in the table for the local channel, OK is returned to indicate a local
 *  reference.
 */

#ifdef DEBUG
    ll_log( logptr, LLOGFTR, "h2chan ('%s', %d)", hostr, pos);
#endif
    for (chp = ch_tbsrch; (chanptr = *chp) != (Chan *)0; chp++)
    {
#ifdef DEBUG
	ll_log( logptr, LLOGFTR, "h2chan table '%s'",
		chanptr->ch_table->tb_name);
#endif
#ifdef NAMESERVER
	if ((chanptr -> ch_table -> tb_flags & TB_SRC) == TB_NS) {
	    if (!ns_done) {
		/*
		 * assumes the fact we are looking for a 'channel'
		 * if this is not the case you must do the lookup every
		 * time round the loop
		 * This code believes all NS channel tables are equivalent,
		 * and they will return the same answer.  Not unreasonable
		 * at the current time, but watch out.
		 */

		switch(ns_fetch (chanptr->ch_table, hostr, hostname, 1)){
		case OK:
		    ns_done++;
		    break;
		case MAYBE:
		    return( (Chan *)MAYBE);
		}
		ns_done++;
	    }
	    if (ns_done == 1)
		continue;
	    if(--pos > 0)
		continue;
#if DEBUG > 1
	    ll_log( logptr, LLOGFTR, "NSconsider ('%s', '%s')",
		hostname, chanptr->ch_lname);
#endif
	    if (lexequ (chanptr -> ch_name, ch_dflnam))
		return( (Chan *)OK );   /* local ref             */
	    return( chanptr );
	}
	else
#endif NAMESERVER
	{
	    if (!dbm_done) {
		dbm_done++;
		if (tb_fetch (hostr, dbm))
		     dbm_done++;
	    }
	    if (dbm_done == 1)
		continue;
	    for (dp = dbm ; (cp = dp->RECname) != NULL ; dp++) {
#if DEBUG > 1
		ll_log( logptr, LLOGFTR, "consider ('%s')", cp);
#endif
		if (lexequ(cp, chanptr -> ch_table -> tb_name)) {
		    if (--pos > 0)
			break;
		    if (lexequ(chanptr -> ch_name, ch_dflnam))
			return( (Chan *)OK );
		    return(chanptr);
		}
	    }
	}
    }

    return ((Chan *) NOTOK);
}

/* ***********  GIVEN Subdomain, FIND Domain  ***************** */

Domain *
	dm_s2dom (subdomain, official, dmbuf)
char    *subdomain;             /* name of subdomain to look up              */
char    *official;              /* Where to put official name */
char    *dmbuf;                 /* Domain route buffer */
{
    register	Domain	*dmnptr;
    register	struct DBvalues *dp;
    register	char	*cp;
    DBMValues   dbm;
    extern	Domain	**dm_list;
    Domain	**dmp;
    char	*argv[DM_NFIELD];
    int         dbm_done = 0;

#ifdef DEBUG
    ll_log( logptr, LLOGFTR, "dm_s2dom ('%s')", subdomain);
#endif

    for (dmp = dm_list; (dmnptr = *dmp) != (Domain *)0; dmp++)
    {
#ifdef NAMESERVER
	if ((dmnptr -> dm_table -> tb_flags & TB_SRC) == TB_NS) {
	    if ((dmnptr-> dm_table -> tb_flags & TB_PARTIAL) == TB_PARTIAL)
		switch(ns_fetch(dmnptr->dm_table,subdomain,official,1)){
		case OK:
		    /* route is simply official name */
		    (void) strcpy(dmbuf,official);
		    return( dmnptr );
		case MAYBE:
		    return( (Domain *)MAYBE);
		}
	}
	else
#endif NAMESERVER
	{
	    if (!dbm_done) {
		dbm_done++;
		if (tb_fetch (subdomain, dbm))
		     dbm_done++;
	    }
	    if (dbm_done == 1)
		continue;
	    for ( dp = dbm ; (cp = dp->RECname) != NULL ; dp++) {
#ifdef DEBUG
		ll_log( logptr, LLOGFTR, "consider ('%s')", cp);
#endif
		if (!lexequ(cp, dmnptr -> dm_table -> tb_name))
		    continue;
		if(dp->RECval == NULL) {	/* should never happen */
		    *dmbuf = *official = '\0';
		    return(dmnptr);
		}
		(void) strcpy (dmbuf, dp->RECval);
		str2arg(dp->RECval, DM_NFIELD, argv, 0);
		(void) strcpy(official, argv[0]);
		return(dmnptr);
	    }
	}
    }

    (void) strcpy (official, subdomain);
    return ((Domain *) NOTOK);
}

/* *******  Get a record from the database, given a key    ********** */
/*            Note that the strings are in internal statics             */

tb_fetch (name, dbm)                /* return filled entry for name */
char *name;                         /* use this key to fetch entry  */
DBMValues dbm;                      /* put the entry here           */
{
    LOCVAR int dbmopen = FALSE;
    LOCVAR char dbvalue[256];
    datum key, value;
    char *lastchar;
    register char *p, *cp;
    register struct DBvalues *dp;
    register cnt;

    if (!dbmopen)
    {
	extern int dbminit();
	char filename[128];

#ifdef DEBUG
	ll_log (logptr, LLOGBTR, "tb_fetch: dbminit");
#endif
	getfpath (tbldbm, tbldfldir, filename);
	if (dbminit (filename) < 0)
	    err_abrt (LLOGTMP, "Error opening database '%s'", filename);

	dbmopen = TRUE;
    }

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "fetch (%s)", name);
#endif
    key.dptr = name;
    key.dsize = strlen (name) + 1;
    for (p = name; *p; p++)
	*p = uptolow (*p);

    value = fetch (key);
    if (value.dptr == NULL)
    {
#ifdef DEBUG
	ll_log (logptr, LLOGBTR, "fetch of '%s' failed", name);
#endif
	return (FALSE);
    }
    for (cp = value.dptr, p= dbvalue, lastchar = &p[value.dsize];
		p < lastchar; *p++ = *cp++) ;
    *p = '\0';                  /* copy value to separate storage */

    for (p = dbvalue , dp = dbm, cnt = 0; *p && cnt < MAXVAL ; cnt++, dp++)
    {
	for(dp->RECname = p ; *p ; p++)
	    if (*p == ' ') {      /* get table name */
		*p++ = '\0';
		break;
	    }
	for (dp->RECval = (*p ? p : NULL); *p; p++)
	    if (*p == FS) {      /* get value-part */
		*p++ = '\0';
		break;
	    }
#ifdef DEBUG
	ll_log (logptr, LLOGFTR, "fetch val(%d)='%s/%s'",
		cnt, dp->RECname, (dp->RECval==NULL) ? "<NULL>": dp->RECval);
#endif
    }
    for(;cnt <= MAXVAL; cnt++, dp++)    /* zero the rest of the structures */
	dp->RECname = dp->RECval = NULL;

    return (TRUE);
}

/* *******  FIND VALUE (address), GIVEN ITS KEY (hostname)  ********* */


tb_k2val (table, first, name, buf) /* use key and return value */
register Table  *table;
int     first;                    /* start at beginning of list?        */
register char  name[];            /* name of ch "member" / key          */
char   *buf;                      /* put value int this buffer          */
{
LOCVAR DBMValues dbm;
LOCVAR struct DBvalues *dp = (struct DBvalues *) 0;
    register char *cp;
#ifdef  NAMESERVER
    int retval;
#endif
    

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "tb_k2val (%s, first=%d, %s)",
			table -> tb_name, first, name);
#endif

#ifdef NAMESERVER
    if ((table->tb_flags&TB_SRC) == TB_NS) {
	if ((retval = ns_fetch (table, name, buf, first)) != NOTOK)
	    return (retval);
#ifdef DEBUG
	ll_log (logptr, LLOGFTR, "tb_k2val failed");
#endif
	(void) strcpy (buf, "(ERROR)");
	return (NOTOK);
    }
#endif NAMESERVER

    if (!first)
	dp++;
    else
    {
	if (tb_fetch (name, dbm))
	{
#ifdef DEBUG
	    ll_log (logptr, LLOGFTR, "hit");
#endif
	    dp = dbm;
	}
	else
	    dp = (struct DBvalues *) 0;  /* none */
    }

    if (dp)
	for (; (cp = dp->RECname) != NULL; dp++)
	{
	    if (strequ (cp, table -> tb_name))
	    {                       /* this is the right channel        */
#ifdef DEBUG
		ll_log (logptr, LLOGFTR, "table(%s)", cp);
#endif
		if(dp->RECval == NULL)
		    *buf = '\0';
		else
		    (void) strcpy (buf, dp->RECval);
		return (OK);      /* give them the value-part         */
	    }
	}
#ifdef DEBUG
    ll_log (logptr, LLOGFTR, "tb_k2val failed");
#endif
    (void) strcpy (buf, "(ERROR)");
    return (NOTOK);
}
) != NULL ; dp++) {
#ifdef DEBUG
		ll_log( logptr, LLOGFTR, "consider ('%s')", cp);
#endif
		if (!lexequ(cp, dmnptr -> dm_table -> tb_name))
		    continue;
		if(dp->RECval == NULL) {	/* should never happen */
		    *dmbuf = *official = '\0';
		    return(dmnptr);
		}
		(void) strcpy (dmbuf, dp->RECval);
		str2arg(dp->RECval, DM_NFIELD, argv, 0);
		(void) strcpy(official, argv[0]);
		return(dmnptr);
	    }
	}
    }

    (void) strcpy (official, subdomaimmdf/lib/table/dm_struct.c   444      0     12        3231  3620510410  10744 #include "util.h"
#include "mmdf.h"
/*
 *     MULTI-CHANNEL MEMO DISTRIBUTION FACILITY  (MMDF)
 *     
 *
 *     Copyright (C) 1979,1980,1981  University of Delaware
 *     
 *     Department of Electrical Engineering
 *     University of Delaware
 *     Newark, Delaware  19711
 *
 *     Phone:  (302) 738-1163
 *     
 *     
 *     This program module was developed as part of the University
 *     of Delaware's Multi-Channel Memo Distribution Facility (MMDF).
 *     
 *     Acquisition, use, and distribution of this module and its listings
 *     are subject restricted to the terms of a license agreement.
 *     Documents describing systems using this module must cite its source.
 *
 *     The above statements must be retained with all copies of this
 *     program and may not be removed without the consent of the
 *     University of Delaware.
 *     
 *
 *     version  -1    David H. Crocker    March   1979
 *     version   0    David H. Crocker    April   1980
 *     version  v7    David H. Crocker    May     1981
 *     version   1    David H. Crocker    October 1981
 *
 */

#include "ch.h"
#include "dm.h"

extern LLog *logptr;
extern Domain **dm_list;

Domain *dm_nm2struct (name)       /* give pointer to structure for chan  */
char  name[];             /* string name of domain              */
{
    register Domain **dmnptr;

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "dm_nm2struct (%s)", name);
#endif

    for (dmnptr = dm_list; *dmnptr != 0; dmnptr++)
	if (lexequ (name, (*dmnptr) -> dm_name))
	{
#ifdef DEBUG
	    ll_log (logptr, LLOGFTR, "hit");
#endif
	    return (*dmnptr);    /* return addr of chan struct entry   */
	}
    return ((Domain *) NOTOK);
}
le must cite its source.
 *
 *     The above statements must be retained with all copies of this
 *     program and may not be removed without the consent of the
 *     University of Delaware.
 *     
 *
 *     version  -1    David H. Crocker    March   1979
 *     version   0    David H. Crocker    April   1980
 *     version  v7    David H. Crocker    Maymmdf/lib/table/tb_io.c   444      0     12       21741  3620510411  10063 #include "util.h"
#include "mmdf.h"
#include "ch.h"                   /* has table state strcture def       */
/*
 *     MULTI-CHANNEL MEMO DISTRIBUTION FACILITY  (MMDF)
 *
 *
 *     Copyright (C) 1979,1980,1981  University of Delaware
 *
 *     Department of Electrical Engineering
 *     University of Delaware
 *     Newark, Delaware  19711
 *
 *     Phone:  (302) 738-1163
 *
 *
 *     This program module was developed as part of the University
 *     of Delaware's Multi-Channel Memo Distribution Facility (MMDF).
 *
 *     Acquisition, use, and distribution of this module and its listings
 *     are subject restricted to the terms of a license agreement.
 *     Documents describing systems using this module must cite its source.
 *
 *     The above statements must be retained with all copies of this
 *     program and may not be removed without the consent of the
 *     University of Delaware.
 *
 *
 *     version  -1    David H. Crocker    March   1979
 *     version   0    David H. Crocker    April   1980
 *     version  v7    David H. Crocker    May     1981
 *     version   1    David H. Crocker    October 1981
 *
 */

/*  READ "NETWORK" HOST NAME TABLES
 *
 *  BNF for the file is:
 *
 *      entries =   entries entry
 *      entry   =   name separator value eol
 *      name    =   {string of chars not containing a <separator>}
 *      separator=  {see the chars in _hkeyend[]}
 *      value   =   {string of chars not containing an <eol>}
 *      eol     =   {see the chars in _hvalend[]}
 *
 *      where
 *
 *          name    is a key
 *          value   is any relevant text, usually the host "address"
 *
 *  There may be any number of entries with the same value string,
 *  thereby allowing name aliases, such as UDel-EE, UDel, and UDEE.
 *
 *  The key of the first entry with a given value is taken as the
 *  "official" "name" of that "address".  (Sorry about all the
 *  quotation marks.)
 *
 *  Turns out that the local machine's alias file can also be accessed
 *  by these routines.
 *
 *  Any number of tables may be supported simultaneously, since a
 *  single table's state information structure is maintained externally,
 *  and passed with each call, through the parameter netinfo.
 *  The state structure's place for the file descriptor MUST
 *  initially be zero.
 *
 *  "Hard" failures are considered not acceptable and hence cause a
 *  call to the external routine err_abrt(); to change this policy,
 *  just change the calls to err_abrt() to be simple failure returns.
 *  The rest of the code should run "transparently".
 */

/*  Sep 81  Dave Crocker      added ch_tclose & ch_tfree, to allow
 *                            handling file descriptor overflow
 *  May 82  Dave Crocker      add ch_tread, to focus input
 *  Jun 82  Dave Crocker      split ch_tio off from ch_table
 *  Dec 82  Doug Kingston     Fixed bug in tb_read() that caused it to
 *                            ignore EOF's.   (DPK@BRL)
 *  Apr 83  Steve Kille       Change it back again
 */

extern struct ll_struct   *logptr;
extern char *tbldfldir;
extern Table **tb_list;         /* all known tables                     */
				/* SEK change syntax                    */
extern int   tb_numtables;      /* how many of them there are           */
extern char  *index();
extern char  *compress();

LOCVAR char _hkeyend[] = ": \t\n\377",
				  /* chars which delimit end of key     */
	    _hvalend[] = "\n\000\377";
				  /* chars which delimit end of value   */
LOCVAR short tb_nopen;            /* number of opened file descriptors  */

/* ******************  BASIC FILE ACTION  *************************** */

tb_open (table, fromstart)        /* gain access to a table     */
    register Table  *table;       /* table's state information          */
    int     fromstart;            /* ok to reset, if already open?      */
{
    extern int   errno;           /* in case open fails                 */
    char temppath[128];


/*  currently, there is one file (descriptor) per table.  for a large
 *  configuration, you will run out of fd's.  until/unless you run
 *  a single-file dbm-based version, there is a hack, below, which
 *  limits the number of open file-descriptors to 6.  Trying to open
 *  a 7th will cause a currently-opened one to be closed.
 */

    switch ((int)table)
    {
	case 0:
	case NOTOK:
	    return (FALSE);       /* can't open what isn't specified    */
    }
    switch ((int)(table -> tb_fp))
    {
	case 0:                   /* not opened yet                     */
/*HACK*/    table -> tb_pos = 0L;
	    if (table -> tb_file != 0)
	    {                     /* we have a path string              */
		if (tb_nopen < 6 || tb_free ())
		{
		    getfpath (table -> tb_file, tbldfldir, temppath);
				  /* add on directory portion           */
#ifdef DEBUG
		    ll_log (logptr, LLOGBTR, "opening '%s'", temppath);
#endif
		    if ((table -> tb_fp = fopen (temppath, "r")) != NULL)
		    {
			tb_nopen++;
			break;    /* all is well                        */
		    }
		}
		ll_err (logptr, (errno == ENOENT) ? LLOGPTR : LLOGTMP,
		    "Error opening %s name table '%s' (numfiles open = %d)",
				table -> tb_show, temppath, tb_nopen);
				  /* major only if file exists          */
	    }
	    table -> tb_fp = (FILE *) EOF;
				  /* no path str -> quietly fail        */
				  /* DROP ON THROUGH                    */
	case NOTOK:
	    return (FALSE);       /* failed earlier try                 */

	default:
	    if (fromstart)        /* always start from beginning?       */
		tb_seek (table, 0L);
    }
    return (TRUE);
}
/**/

tb_close (table)                  /* close  a table                     */
register Table *table;
{
    switch ((int)table)
    {
	case 0:
	case NOTOK:
	    return (FALSE);       /* can't close what isn't specified   */
    }
    switch ((int)(table -> tb_fp))
    {
	case NOTOK:
	    table -> tb_fp = 0; /* mark as free                       */
	case 0:                   /* not opened yet                     */
	    return (FALSE);       /* no close performed                 */

	default:
	    fclose (table -> tb_fp);
	    table -> tb_fp = 0; /* mark as free                       */
	    tb_nopen--;
	    return (TRUE);
    }
    /* NOTREACHED */
}

tb_free ()                       /* create a free file descriptor      */
{
/*  step from the lowest search priority to the highest, looking for a
 *  channel to close.  return success as soon as one is done.
 */
    register Table **tableptr;
#ifdef DEBUG
    ll_log (logptr, LLOGFTR, "tb_free ()");
#endif
    for (tableptr = tb_list + tb_numtables - 1;
					tableptr >=  tb_list; tableptr--)
    {
	if (tb_close (*tableptr))
	    return (TRUE);        /* got a channel closed               */
    }
    return (FALSE);               /* couldn't get any closed            */
}

tb_seek (table, thepos)           /* reset table position               */
register Table *table;
long thepos;
{
    switch ((int)table)
    {
	case 0:
	case NOTOK:
	    return (NOTOK);       /* can't open what isn't specified    */
    }
    switch ((int)(table -> tb_fp))
    {                             /* not opened yet                     */
	case NULL:
	case EOF:
	    return (NOTOK);
    }
    fseek (table -> tb_fp, thepos, 0);
/*HACK*/ table -> tb_pos = thepos;
    return ((ferror (table -> tb_fp)) ? NOTOK : OK);
}
/* *****************  READ THE NAME TABLE  ************************** */

/*
 *  This routine used not to recognise comments, this meant that dbmbuild
 *  could skip real records in the following case.
 *  #<NL>
 *  nott:nott
 *  would be read as key = '#' value = 'nott:nott' and dbmbuild would
 *  throw it away as a comment. (JPO)
 */
tb_read (table, key, value)       /* read a table's record        */
    register Table *table;
    char *key,
	 *value;
{
    register int len;
    register FILE *fp;
    char terminator;

    fp = table -> tb_fp;

    if (feof (fp))
	return (FALSE);

    while ((len = qread (fp, key, LINESIZE, _hkeyend, '\\')) > 0)
    {
/*HACK*/    table -> tb_pos += len;
	if( key[0] == '\n' )
	    continue;	    /* blank line */
	if( key[0] == '#' )
	    if( key[len - 1] == '\n' )
		continue;   /* have read up to the newline */
	    else
	    {
		if((len = gcread(fp, value, LINESIZE, _hvalend)) > 0)
		{
/*HACK*/	    table -> tb_pos += len;
		    continue;	/* skip to end of line */
		}
		else return (FALSE);	/* EOF I guess */
	    }

    	terminator = key[len - 1];
	key[len - 1] = '\0';  /* get rid of terminator character */
	if (len > 1)
	    compress (key, key);

	if (index(_hvalend, terminator) > 0)
 	    value[0] = '\0';		/* whole line was a key */
        else {
	    if ((len = gcread (fp, value, LINESIZE, _hvalend)) > 0)
	    {
/*HACK*/	table -> tb_pos += len;
		value[len - 1] = '\0';
		if (len > 1)
		    compress (value, value);
	    }
	    else               /* Don't worry about errors here... */
		value[0] = '\0';
        }
	return (TRUE);

    }
    if( len < 0 )        /* (len < 0) indicating IO error */
	err_abrt (RP_FIO, "Error reading %s name table '%s'",
		table -> tb_show, table -> tb_file);

    return (FALSE);             /* EOF on entry boundary,  DPK@BRL */
}
   "Error opening %s name tablemmdf/lib/table/tb_struct.c   444      0     12        3154  3664425162  10776 #include "util.h"
#include "mmdf.h"
#include "ch.h"
/*
 *     MULTI-CHANNEL MEMO DISTRIBUTION FACILITY  (MMDF)
 *     
 *
 *     Copyright (C) 1979,1980,1981  University of Delaware
 *     
 *     Department of Electrical Engineering
 *     University of Delaware
 *     Newark, Delaware  19711
 *
 *     Phone:  (302) 738-1163
 *     
 *     
 *     This program module was developed as part of the University
 *     of Delaware's Multi-Channel Memo Distribution Facility (MMDF).
 *     
 *     Acquisition, use, and distribution of this module and its listings
 *     are subject restricted to the terms of a license agreement.
 *     Documents describing systems using this module must cite its source.
 *
 *     The above statements must be retained with all copies of this
 *     program and may not be removed without the consent of the
 *     University of Delaware.
 *     
 *
 *     version  -1    David H. Crocker    March   1979
 *     version   0    David H. Crocker    April   1980
 *     version  v7    David H. Crocker    May     1981
 *     version   1    David H. Crocker    October 1981
 *
 */

extern Table **tb_list;
#ifdef DEBUG
extern LLog *logptr;
#endif

Table  *tb_nm2struct (name)       /* give pointer to structure for chan  */
register char  *name;		  /* string name of chan                */
{
    register Table **tableptr;

#ifdef DEBUG
    ll_log(logptr, LLOGBTR, "tb_nm2struct(%s)",name);
#endif

    for (tableptr = tb_list; *tableptr != (Table *) 0; tableptr++)
	if (lexequ (name, (*tableptr) -> tb_name))
	    return (*tableptr);    /* return addr of chan struct entry   */

    return ((Table *) NOTOK);
}
greement.
 *     Documents describing systems using this module must cite its source.
 *
 *     The above statements must be retained with all copies of this
 *     program and may not be removed without the consent of the
 *     University of Delaware.
 *     
 *
 *     version  -1    David H. Crocker    March   1979
 *     version   0    David H. Crocker    April   1980
 *     version  v7    David Hmmdf/lib/table/tb_ns.fake.c   444      0     12         256  3620510411  10737 /*
 *	TB_NS.FAKE.C
 *
 *	Module to call on host system provided lookup routines.
 */

/* Place holder only */
int	dummy_thing_for_namelist;	/* to keep ranlib/lorder quiet */
ct (name)       /* give pointer to structure for chan  */
register char  *name;		  /* string name of chan                */
{
    register Table **tableptr;

#ifdef DEBUG
    ll_log(logptr, LLOGBTR, "tb_nm2struct(%s)",name);
#endif

    for (tableptr = tb_list; *tableptr != (Table *) 0; tableptr++)
	if (lexequ (name, (*tableptr) -> tb_nmmdf/lib/table/Makefile.real   444      0     12        3655  3635173604  11210 #       Makefile for name-table lookup-routines
#           Use ch_tbseq if sequential name tables, only, are available
#           Use ch_tbdbm if you have Unix dbm() subroutines available
#

MODULES	= dm_struct dm_table ch_tbdbm ch_tbseq \
	  tb_io tb_ns.$(TB_NS) tb_struct

OBJECTS  =  dm_struct.o dm_table.o $(CH_TB).o \
	    tb_io.o tb_ns.$(TB_NS).o tb_struct.o

COBJECTS =  dm_struct.c dm_table.c $(CH_TB).c \
	    tb_io.c tb_ns.$(TB_NS).c tb_struct.c

real-default: ../table-made
../table-made:	$(OBJECTS)
	-ar r ../libmmdf.a $(OBJECTS)
	-touch ../table-made
	-@echo "table routines built normally"

lint:
	$(LINT) -Ctable $(LFLAGS) $(COBJECTS)

clean:
	-rm -f *.o *.a x.c makedep eddep make.out errs llib-ltable*

# DO NOT DELETE THIS LINE -- make depend uses it

dm_struct.o: dm_struct.c
dm_struct.o: ../../h/util.h
dm_struct.o: ../../h/mmdf.h
dm_struct.o: ../../h/ch.h
dm_struct.o: ../../h/dm.h
dm_table.o: dm_table.c
dm_table.o: ../../h/util.h
dm_table.o: ../../h/mmdf.h
dm_table.o: ../../h/ch.h
dm_table.o: ../../h/dm.h
dm_table.o: ../../h/chdbm.h
ch_tbdbm.o: ch_tbdbm.c
ch_tbdbm.o: ../../h/util.h
ch_tbdbm.o: ../../h/mmdf.h
ch_tbdbm.o: ../../h/ch.h
ch_tbdbm.o: ../../h/dm.h
ch_tbdbm.o: ../../h/chdbm.h
ch_tbseq.o: ch_tbseq.c
ch_tbseq.o: ../../h/util.h
ch_tbseq.o: ../../h/mmdf.h
ch_tbseq.o: ../../h/ch.h
ch_tbseq.o: ../../h/dm.h
tb_io.o: tb_io.c
tb_io.o: ../../h/util.h
tb_io.o: ../../h/mmdf.h
tb_io.o: ../../h/ch.h
tb_ns.4.2.o: tb_ns.4.2.c
tb_ns.4.2.o: ../../h/util.h
tb_ns.4.2.o: ../../h/mmdf.h
tb_ns.4.2.o: ../../h/ch.h
tb_ns.4.2.o: ../../h/ns.h
tb_ns.4.2.o: /usr/include/netdb.h
tb_ns.4.2.o: /usr/include/sys/socket.h
tb_ns.4.2.o: /usr/include/netinet/in.h
tb_ns.4.2.o: /usr/include/arpa/nameser.h
tb_ns.4.2.o: /usr/include/arpa/resolv.h
tb_struct.o: tb_struct.c
tb_struct.o: ../../h/util.h
tb_struct.o: ../../h/mmdf.h
tb_struct.o: ../../h/ch.h
# DEPENDENCIES MUST END AT END OF FILE
# IF YOU PUT STUFF HERE IT WILL GO AWAY
# see make depend above
tb_ns.$(TB_NS).c tb_struct.c

real-default: ../table-made
../table-made:	$(OBJECTS)mmdf/lib/table/tb_ns.4.2.c   444      0     12       41743  3671073203  10413 /*
 *      TB_NS.C
 *
 * Original version by Phil Cockcroft to use MF and MD records.
 * Also had several speed improvements such as caching.  Later
 * reworked by Phil to do MX lookups.
 *
 * Mostly rewritten by Craig Partridge to move routing decisions
 * into SMTP channel.
 *
 */

#include "util.h"
#include "mmdf.h"
#include "ch.h"
#include "ns.h"
#include <netdb.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/nameser.h>
#include <arpa/resolv.h>

extern char *strncpy();

/*
 * if you want caching, define NSCACHE
 */

#define NSCACHE 25

/*
 * next definition should go away as all servers use MX
 * right now if they don't we just try the address.  MF and MD are
 * dead!
 */

#define OLDSERVER

#ifndef MAXADDR
#define MAXADDR		10
#endif

#ifndef MAXADDR_PER
#define MAXADDR_PER	2
#endif

#ifndef MAXDATA
#define MAXDATA (4 * PACKETSZ)		/* tcp tried after udp */
#endif

#ifndef MAXMX
#define MAXMX	(MAXADDR)		/* shouldn't be < MAXADDR */
#endif

extern  struct  ll_struct *logptr;
extern  char    *strdup(), *strcpy();
extern  char *locmachine, *locdomain, *locname;

union ansbuf {			/* potentially huge */
    HEADER ab1;
    char ab2[MAXDATA];
}; 

union querybuf {		/* just for outbound stuff */
    HEADER qb1;			/* didn't want to clobber stack */
    char qb2[2 * MAXDNAME];
}; 

LOCVAR  struct in_addr mx_addrs[MAXADDR];	/* cache of MX addrs */
LOCVAR  int max_mxa= -1, on_mxa= -1;	/* indicies into cache of MX addrs */
LOCVAR  char dn_name[MAXDNAME];
LOCVAR  char local[MAXDNAME];

#ifdef NSCACHE
struct ns_cache {
    int nc_type;
    int nc_count;
    int nc_time;
    char *nc_key;
    char *nc_data;
};

LOCVAR  struct ns_cache *cachedata[NSCACHE+1];
LOCVAR  int cachetime;
#endif

/*
 * table fetch routine for ns
 */

ns_fetch(table, key, value, first)
Table   *table;          /* What "table" are we searching */
char    *key;
char    *value;         /* Where to put result */
int     first;          /* now used */
{
    register char *tmp;
    char lbuf[LINESIZE];
    int	type;
    int rval;

#ifdef  DEBUG
    ll_log(logptr, LLOGFTR, "ns_fetch (%o, %s, %d)",
	table->tb_flags, key, first);
    ll_log(logptr, LLOGFTR, "ns_fetch: timeout (%d), rep (%d), servers (%d)",
	_res.retrans, _res.retry, _res.nscount);
#endif

    type = (table->tb_flags & TB_TYPE);

    if (first)
    {
	switch(type)
	{

	    case TB_DOMAIN:     /* name -> official domain name */
		sprintf(lbuf, "%s.%s", key, table->tb_name);
		key = lbuf;
		break;

	    case TB_ROOT:      /* name -> official domain name (special case) */
	    case TB_CHANNEL:     /* official domain name -> list of addresses */
		break;

	    default:
#ifdef  DEBUG
		ll_log(logptr, LLOGTMP, "unknown table type '%o'",type);
#endif
		return(NOTOK);
	} /* end switch */

	max_mxa = on_mxa = -1;

	if (!cachehit(key,(type==TB_CHANNEL?T_MX:T_CNAME),&rval))
	{
	    if (type == TB_CHANNEL)
	    {
		if ((rval = ns_getmx(key, &max_mxa, mx_addrs, MAXADDR)) == OK)
		{
		    on_mxa = 0;
#ifdef NSCACHE
		    cacheaddr(key,T_MX,max_mxa,mx_addrs);
#endif
		}
	    }	
	    else
	    {
		rval = ns_getcn(key, dn_name, sizeof(dn_name));
#ifdef NSCACHE
		if (rval == OK)
		    cachename(key,T_CNAME,dn_name);
		else if (rval == MAYBE)
		    cachename(key,0,(char *)0);
#endif
	    }
	}

	if (rval != OK)
	{
#ifdef DEBUG
	    if (rval == MAYBE)
		ll_log(logptr, LLOGFTR, "nameserver query timed out");
	    else
		ll_log(logptr, LLOGFTR, "nameserver query failed");
#endif
	    return(rval);
	}
    }

    /* O.K. now give answer */

    switch (type)
    {
	case TB_CHANNEL:
	    /* if NS failure we returned MAYBE above */
	    if ((max_mxa <= 0) || (on_mxa >= max_mxa))
		return(NOTOK);

	    tmp = (char *)&(mx_addrs[on_mxa++]);

	    (void) sprintf(value,"%u.%u.%u.%u",
		    ((unsigned)tmp[0]) & 0xff, ((unsigned)tmp[1]) & 0xff,
		    ((unsigned)tmp[2]) & 0xff, ((unsigned)tmp[3]) & 0xff);
	    break;

	default:
	    /* can't get multiple names */
	    if (!first)
		return(NOTOK);

	    /* give them the name */
	    (void) strcpy(value,dn_name);
	    break;
    }
    
#ifdef  DEBUG
    ll_log(logptr, LLOGFTR, "NS returns '%s'", value);
#endif
    return(OK);
}

/*
 * see if name is not cannonical name
 */

LOCFUN
ns_getcn(key, name, namelen)
char *key, *name;
int namelen;
{
    register int n;
    register char *cp;
    union querybuf qbuf;
    union ansbuf abuf;
    HEADER *hp;
    char *eom;
    extern char *ns_skiphdr();

#ifdef DEBUG
    ll_log(logptr, LLOGFTR, "ns_getcn(%s)",key);
#endif

    n = res_mkquery(QUERY, key, C_IN, T_CNAME, (char *)0, 0, (char *)0,
	(char *)&qbuf, sizeof(qbuf));

    /* what else can we do? */
    if (n < 0)
	return(NOTOK);

    n = res_send((char *)&qbuf,n,(char *)&abuf, sizeof(abuf));

    if (n < 0)
    {
#ifdef DEBUG
	ll_log(logptr, LLOGFTR, "ns_getcn: bad return from res_send");
#endif
	return(MAYBE);
    }

    hp = (HEADER *)&abuf;

    if (hp->rcode != NOERROR)
	return(ns_error(hp));

    if (ntohs(hp->ancount) == 0)
    {
	/* it is the official name */
	(void) strncpy(name,key,namelen);
#ifdef DEBUG
	ll_log(logptr, LLOGFTR, "ns_getcn: %s -> %s",key,name);
#endif
	return(OK);
    }

    /* only get here on NOERRR with ancount != 0 */
#ifdef DEBUG
    ll_log(logptr, LLOGFTR, "ns_getcn: parsing answer to query");
#endif

    /* skip header */
    eom = ((char *)&abuf)+n;

    if ((cp = ns_skiphdr((char *)&abuf, hp, eom)) == 0)
	return(MAYBE);

    /* one CNAME is enough */
    if ((n = dn_expand((char *)&abuf,eom, cp, name, namelen))<0)
	return(MAYBE);

    /* skip to name */
    cp += (n + (3 * sizeof(u_short)) + sizeof(u_long));

    if ((n = dn_expand((char *)&abuf, eom, cp, name, namelen))<0)
	return(MAYBE);
    
#ifdef DEBUG
    ll_log(logptr, LLOGFTR, "ns_getcn: %s -> %s",key,name);
#endif
    return(OK);
}

/*
 * build a list of addresses of MX hosts to try....
 */

LOCFUN
ns_getmx(key, max, mxtab, tsize)
char *key;
int *max;
struct in_addr mxtab[];
int tsize;
{
    register char *cp;
    register int i, j, n;
    HEADER *hp;
    struct hostent *he;
    union querybuf qbuf;
    union ansbuf abuf;
    u_short type, dsize;
    int pref, localpref;
    int count, mxcount;
    int sawmx;			/* are we actually processing mx's? */
    char *eom;
    char buf[MAXDNAME];		/* for expanding in dn_expand */
    char newkey[MAXDNAME]; 	/* in case we get a CNAME RR back... */
    struct {			/* intermediate table */
	char *mxname;
	u_short mxpref;
    } mx_list[MAXMX];
    extern char *ns_skiphdr();

#ifdef DEBUG
    ll_log(logptr, LLOGFTR, "ns_getmx(%s, %x, %x, %d)",key,max,mxtab,tsize);
#endif

restart:

    sawmx = 0;

    n = res_mkquery(QUERY, key, C_IN, T_MX, (char *)0, 0, (char *)0,
	(char *)&qbuf, sizeof(qbuf));

    /* what else can we do? */
    if (n < 0)
	return(NOTOK);

#ifdef DEBUG
    ll_log(logptr, LLOGFTR, "ns_getmx: sending ns query (%d bytes)",n);
#endif

    n = res_send((char *)&qbuf,n,(char *)&abuf, sizeof(abuf));

    if (n < 0)
    {
#ifdef DEBUG
	ll_log(logptr, LLOGFTR, "ns_getmx: bad return from res_send");
#endif
	return(MAYBE);
    }

    hp = (HEADER *)&abuf;

#ifdef OLDSERVER
    if ((hp->rcode != NOERROR) && (hp->rcode != FORMERR))
#else
    if (hp->rcode != NOERROR) 
#endif /* OLDSERVER */
	return(ns_error(hp));

#ifdef OLDSERVER
    if ((ntohs(hp->ancount) == 0) || (hp->rcode == FORMERR))
#else
    if (ntohs(hp->ancount) == 0)
#endif /* OLDSERVER */
    {
	mxcount = 1;
	mx_list[0].mxname = strdup(key);
	mx_list[0].mxpref = 0;
	goto doaddr;
    }

    /* read MX list */
    sawmx = 1;
    count = ntohs(hp->ancount);

    /* need local machine name */
    if (local[0] == '\0')
    {
	if ((locmachine != 0) && (*locmachine != '\0'))
	    (void) sprintf(local,"%s.%s.%s",locmachine,locname,locdomain);
	else
	    (void) sprintf(local,"%s.%s",locname,locdomain);
    }

    /* skip header */
    eom = ((char *)&abuf) + n;
    if ((cp = ns_skiphdr((char *)&abuf, hp, eom))==0)
	return(MAYBE);

    /* get them MX's */
    localpref = -1;
    mxcount = 0;
    *max = 0;

#ifdef DEBUG
    ll_log(logptr, LLOGFTR, "ns_getmx: %d answers to query",count);
#endif

    while ((cp < eom) && (count--))
    {
	n = dn_expand((char *)&abuf,eom, cp, buf, sizeof(buf));
	if (n < 0)
	    goto quit;

	cp += n;
	type = getshort(cp);
	/* get to datasize */
	cp += (2 * sizeof(u_short)) + sizeof(u_long);
	dsize = getshort(cp);
	cp += sizeof(u_short);

	/*
	 * is it an MX ? 
	 * note it could be a CNAME if we didn't use a domain lookup
	 */

	if (type == T_CNAME)
	{
	    ll_log(logptr,LLOGTMP,"ns_getmx: CNAME answer to MX query");
	    n = dn_expand((char *)&abuf,eom, cp, newkey, sizeof(newkey));
	    cp += dsize;
	    if (n < 0)
		continue;	/* pray? */
#ifdef DEBUG
	    ll_log(logptr,LLOGFTR,"ns_getmx: %s -> %s (new query)",key,newkey);
#endif DEBUG
	    key = newkey;
	    goto restart;
	}

	if (type != T_MX)
	{
	    ll_log(logptr,LLOGTMP,"ns_getmx: RR of type %d in response",type);
	    cp += dsize;
	    continue;       /* keep trying */
	}

	pref = getshort(cp);
	cp += sizeof(u_short);

	n = dn_expand((char *)&abuf,eom, cp, buf, sizeof(buf));
	if (n < 0)
	    goto quit;

	cp += n;

	/* is it local? */
	if ((lexequ(local,buf)) && ((localpref < 0) || (pref < localpref)))
	{
	    localpref = pref;
	    for(i=(mxcount-1); i >= 0; i--)
	    {
		if (mx_list[i].mxpref < localpref)
		    break;

		(void) free(mx_list[i].mxname);
		mxcount--;
	    }
	    continue;
	}

	/* now, see if we keep it */
	if ((localpref >= 0) && (pref >= localpref))
	    continue;

	/*  find where it belongs */
	for(i=0; i < mxcount; i++)
	    if (mx_list[i].mxpref > pref)
		break;

	/* not of interest */
	if (i == MAXMX)
	    continue;

	/* shift stuff to make space */
	for(j=mxcount-1; j > i; j--)
	{
	    if (j==(MAXMX-1))
		(void) free(mx_list[j].mxname);

	    mx_list[j].mxname = mx_list[j-1].mxname;
	    mx_list[j].mxpref = mx_list[j-1].mxpref;
	}

	mx_list[i].mxname = strdup(buf);
	mx_list[i].mxpref = pref;

	if (mxcount <= i)
	    mxcount = i + 1;
    }

    /*
     * should read additional RR section for addresses and cache them
     * but let's hold on that.
     */

doaddr:
    /* now build the address list */
#ifdef DEBUG
    ll_log(logptr, LLOGFTR,"ns_getmx: using %d mx hosts",mxcount);
#endif

    for(i=0,j=0; (i < mxcount) && (j < tsize); i++)
    {
	/*
	 * note that gethostbyname() is slow -- we should cache so
	 * we don't ask for an address repeatedly
	 */

	he = gethostbyname(mx_list[i].mxname);

	if (he == 0)
	{
#ifdef DEBUG
	    ll_log(logptr, LLOGFTR, "ns_getmx: no addresses for %s",
		mx_list[i].mxname);
#endif
	    /* nope -- were trying special case and no address */
	    if ((!sawmx) && (h_errno != TRY_AGAIN))
		return(NOTOK);

	    continue;
	}

	for(n=0; (j < tsize) && (n < MAXADDR_PER); n++, j++)
	{
	    if (he->h_addr_list[n] == 0)
		break;

	    bcopy(he->h_addr_list[n],(char *)&mxtab[j],sizeof(struct in_addr));
	}
#ifdef DEBUG
	ll_log(logptr, LLOGFTR, "ns_getmx: %d addresses saved for %s",
		n, mx_list[i].mxname);
#endif
    }
    *max = j;

quit:
    for(i=0; i < mxcount; i++)
	(void) free(mx_list[i].mxname);

#ifdef DEBUG
    if (*max == 0)
	ll_log(logptr, LLOGFTR, "ns_getmx: problems parsing response to query");
#endif

    return (*max == 0 ? MAYBE : OK);
}

/*
 * figure out proper error code to return given an error
 */

LOCFUN
ns_error(hp)
register HEADER *hp;
{

#ifdef DEBUG
    ll_log(logptr, LLOGFTR, "ns_error: server returned code %d",hp->rcode);
#endif

    switch (hp->rcode)
    {
	case NXDOMAIN:
	    return(NOTOK); /* even if not authoritative */

	case SERVFAIL:
	    return(MAYBE);

	default:
	    break;
    }

    return(NOTOK);
}

/*
 * skip header of query and return pointer to first answer RR.
 */

LOCFUN
char *ns_skiphdr(answer, hp, eom)
char *answer;
HEADER *hp;
register char *eom;
{
    register int qdcount;
    register char *cp;
    register int n;
    char tmp[MAXDNAME];

    qdcount = ntohs(hp->qdcount);

    cp = answer + sizeof(HEADER);

    while ((qdcount-- > 0) && (cp < eom))
    {
	n = dn_expand(answer,eom,cp,tmp,sizeof(tmp));
	if (n < 0)
	    return(0);
	cp += (n + QFIXEDSZ);
    }

    return((cp < eom)? cp : 0);
}

/*
 * routine to set the resolver timeouts
 * takes maximum number of seconds you are willing to wait
 */

ns_settimeo(ns_time)
int     ns_time;
{
    register int i;
    register int retry, retrans;
    static int called = 0;
    static struct state oldres;

    if ((_res.options & RES_INIT) == 0)
	    res_init ();

    /* always start afresh */
    if (called)
    {
	bcopy((char *)&oldres,(char *)&_res,sizeof(oldres));
    }
    else
    {
	called = 1;
	bcopy((char *)&_res,(char *)&oldres,sizeof(oldres));
    }
    

    /* too small? */
    if (ns_time <= (NS_MINTIME))
    {
	retry = NS_MINRETRY;
	retrans = NS_MINRETRANS;
	goto done;
    }

    retry = _res.retry;
    retrans = _res.retrans;

    /* bigger than bind? */
    if (ns_time > (retry * retrans))
    {
	/* can't increase servers, and no point to retrans so up retry a bit */
	while ((retry < NS_MAXRETRY) && (ns_time > ((retry+1)*retrans)))
	    retry++;

	goto done;
    }

    /* reduce timer periods */

    for(i=0; ns_time < (retry * retrans); i++)
    {
	switch (i%2)
	{
	    case 0:
		/* cut down rexmits first */
		if (retry > NS_MINRETRY)
		{
		    retry--;
		    continue;
		}
		/* fall thru */

	    case 1:
		/* then interval between rexmits */
		if (retrans > NS_MINRETRANS)
		{
		    retrans /= 2;
		    if (retrans < NS_MINRETRANS)
			retrans = NS_MINRETRANS;
		}
		continue;
	}
    }

done:

    _res.retrans = retrans;
    _res.retry = retry;

#ifdef DEBUG
    ll_log(logptr, LLOGFTR, "ns_timeo: servers(%d), retrans(%d), retry(%d)",
	_res.nscount, _res.retrans, _res.retry);
#endif
}

/*
 * Caching stuff starts here....
 * cache stores following pairs:
 *        key -> mx address list
 *        key -> canonical name
 *        key -> we timed out on
 *
 * must store complete answers to a table fetch  -- storing by RR's
 * leads to incomplete information in the cache -- and thus busted
 * routing.
 */

/*
 * see if key/type pair is in cache.  If so, set rval appropriately
 */

cachehit(key,type,rval)
char *key;
int type;
int *rval;
{
#ifdef NSCACHE
    register int i;
    register struct ns_cache *cp;
    register struct in_addr *ip1, *ip2;

    *rval = OK;

    for(i=0; i < NSCACHE; i++)
    {
	cp = cachedata[i];

	if (cp->nc_type && (cp->nc_type != type))
	    continue;

	if (!lexequ(key,cp->nc_key))
	    continue;

	/* got a hit */
	cp->nc_time = cachetime++;
#ifdef DEBUG
	ll_log(logptr,LLOGFTR,"ns: cache hit, type %d",cp->nc_type);
#endif /* DEBUG */

	switch (cp->nc_type)
	{
	    case 0:	/* key we timed out on */
		*rval = MAYBE;
		return(1);

	    case T_MX:
		/* fill mx cache */
		max_mxa = cp->nc_count;
		on_mxa = 0;

		ip1 = (struct in_addr *)cp->nc_data;
		ip2 = mx_addrs;
		for(i=0; i < max_mxa; i++)
		    *ip2++ = *ip1++;

#ifdef DEBUG
		ll_log(logptr,LLOGFTR,"ns: found %d addresses for %s in cache",
		    max_mxa, key);
#endif
		
		return(1);

	    case T_CNAME:
		/* fill in dn_name */
		if (cp->nc_count)
		    (void) strcpy(dn_name,cp->nc_data);
		else
		    (void) strcpy(dn_name,cp->nc_key);
		return(1);
	    }
    }

#endif /* NSCACHE */
    /* no one should look at rval, but... */
    *rval = NOTOK;
    return(0);
}

#ifdef NSCACHE

cacheput(cp)
struct ns_cache *cp;
{
    register int i;
    register int d;

    cp->nc_time = cachetime++;

#ifdef DEBUG
    ll_log(logptr,LLOGFTR,"ns: cache put (key=%s, type=%d, count=%d)",
	    cp->nc_key, cp->nc_type, cp->nc_count);
#endif

    for(d=0,i=0; i < NSCACHE; i++)
    {
	if (cachedata[i] == 0)
	{
	    cachedata[i] = cp;
	    return;
	}

	if (cachedata[i]->nc_time < cachedata[d]->nc_time)
	    d = i;
    }

#ifdef DEBUG
    ll_log(logptr,LLOGFTR,"ns: full cache, discarding (%s,%d)", cp->nc_key,
	    cp->nc_type);
#endif

    /* must delete someone */
    (void) free(cachedata[d]->nc_key);
    if (cachedata[d]->nc_count)
	(void) free(cachedata[d]->nc_data);
    (void) free((char *)cachedata[d]);

    /* put new one in */
    cachedata[d] = cp;
}

cacheaddr(key,type,count,list)
char *key;
int type, count;
struct in_addr *list;
{
    register struct ns_cache *cp;
    register struct in_addr *ip1, *ip2;
    extern char *malloc(), *calloc();

    /* NOSTRICT */
    if ((cp = (struct ns_cache *)malloc(sizeof(*cp)))==0)
	return;

    /* NOSTRICT */
    if ((cp->nc_data = calloc((unsigned)count,sizeof(*list)))==0)
    {
	(void) free((char *)cp);
	return;
    }
    if ((cp->nc_key = strdup(key))==0)
    {
	(void) free(cp->nc_data);
	(void) free((char *)cp);
	return;
    }

    cp->nc_count = count;
    cp->nc_type = type;

    ip1 = list;
    ip2 = (struct in_addr *)cp->nc_data;

    while (count--)
	*ip2++ = *ip1++;

    cacheput(cp);
}

cachename(key,type,name)
char *key, *name;
int type;
{
    register struct ns_cache *cp;
    extern char *malloc(), *strdup();

    /* NOSTRICT */
    if ((cp = (struct ns_cache *)malloc(sizeof(*cp)))==0)
	return;

    if ((cp->nc_key = strdup(key))==0)
    {
	(void) free((char *)cp);
	return;
    }

    if ((name != 0) && !lexequ(key,name))
    {
	if ((cp->nc_data = strdup(key)) == 0)
	{
	    (void) free(cp->nc_key);
	    (void) free((char *)cp);
	    return;
	}
	cp->nc_count = 1;
    }
    else
    {
	cp->nc_data = 0;
	cp->nc_count = 0;
    }

    cp->nc_type = type;

    cacheput(cp);
}
#endif NSCACHE
;
		}
		/* fall thru */

	   mmdf/lib/util/   755      0     12           0  3671074623   6421 mmdf/lib/util/getllog.bsd.c   444      0     12        2753  3620510412  11044 #include "util.h"
#include <pwd.h>
#include <utmp.h>
#include <lastlog.h>

/* get informtion about who is logged in */

extern char *blt();

LOCVAR char LLPATH[]  = "/usr/adm/lastlog";
LOCVAR FILE *llogfp = NULL;
LOCVAR struct utmp llogentry;
LOCVAR int lloguid;       /* keep track of current uid */

int setllog()
{
	if( llogfp == NULL )
		{
		llogfp = fopen( LLPATH, "r" );
		if ( llogfp == NULL )
			return(NOTOK);
		}
	else
		rewind( llogfp );

	lloguid = 0;
	return (OK);
}

endllog()
{
	if( llogfp != NULL ){
		fclose( llogfp );
		llogfp = NULL;
	}
}

struct utmp *
getllog()
{
	union tmpunion
	{
	    char        io[sizeof (struct lastlog)];
	    struct lastlog entry;
	}       llogbuf;
	struct passwd *pwdptr,
		      *getpwuid ();

	if (llogfp == NULL) {
		if( (llogfp = fopen( LLPATH, "r" )) == NULL )
			return(0);

		lloguid = 0;
	}

	if (fread (llogbuf.io, sizeof (struct lastlog), 1, llogfp) != 1)
		return(0);

	llogentry.ut_time = (long) llogbuf.entry.ll_time;
	blt (llogbuf.entry.ll_line, llogentry.ut_line, 8);

	if ((pwdptr = getpwuid (lloguid)) == 0)
	    strcpy (llogentry.ut_name, "????");
	else
	    blt (pwdptr -> pw_name, llogentry.ut_name, 8);

	lloguid++;
	return(&llogentry);
}

struct utmp *
getllnam(name)
char name[];
{
	struct passwd *pwdptr,
		      *gpwlnam ();

	if ((pwdptr = gpwlnam (name)) == 0)
	    return (0);

	if (setllog () == NOTOK)
		return(NULL);

	lloguid = pwdptr->pw_uid;
	fseek(llogfp, (long)pwdptr->pw_uid * sizeof (struct lastlog), 0);

	return (getllog ());
}
{
	if( llogfp != NULLmmdf/lib/util/gen   555      0     12          57  3620510412   7126 make -f ../../Makefile.com -f Makefile.real $*

	    strcpy (llogentry.ut_name, "????");
	else
	    blt (pwdptr -> pw_name, llogentry.ut_name, 8);

	lloguid++;
	return(&llogentry);
}

struct utmp *
getllnam(name)
char name[];
{
	struct passwd *pwdptr,
		      *gpwlnam ();

	if ((pwdptr = gpwlnam (name)) == 0)
	    return (0);

	if (setllog () == NOTOK)
		return(NULL);

	lloguid = pwdptr->pw_uid;
	fseek(llogfp, (long)pwdptr->pw_uid * sizeof (struct lastlog), 0);

	return (getllog ());
}
{
	if( llogfp != NULLmmdf/lib/util/tai_file.c   444      0     12        3751  3671073204  10424 #include "util.h"
#include <sys/stat.h>

/* Perform process start-up tailoring */

extern int errno;

char *tai_eptr;         /* point to error text, if any  */

LOCVAR char *tai_data;	/* pointer to incore copy of tai file */
LOCVAR char *tai_ptr;   /* pointer to "next" tai record */

tai_init (filename)	/* prepare to get tailoring information */
	char filename[]; /* location of the info */
{
	extern char *malloc ();
	struct stat statbuf;
	unsigned taisize; /* number of bytes in the tai file */
	int fd;

	if (stat (filename, &statbuf) < OK)
		return (NOTOK);	 
	taisize = (unsigned) st_gsize (&statbuf);
	if ((tai_data = malloc (taisize + 1)) == 0)
		return (NOTOK);
	if ((fd = open (filename, 0)) < 0)
		return (NOTOK);
	if (read (fd, tai_data, taisize) != taisize)
	{
		(void) close (fd);
		errno = EIO;    /* i/o error */
		return (NOTOK);
	}
	tai_data[taisize] = '\0';
	(void) close (fd);
	tai_ptr = tai_data;
	return (OK);
}


tai_end ()
{
	tai_data = 0;
	return (OK);
}
/**/

tai_get (max, argv)	/* get next parsed tailoring line */
	int max;	/* maximum number of allowable fields */
	char *argv[];	/* array to hold pointers to field elements */
{
    int retval;
    register char *nxtptr;             /* beginning of next record     */

retry:
    if (tai_ptr == 0)
	    return (0);

    for (nxtptr = tai_ptr; *nxtptr != '\0'; nxtptr++)
	if (*nxtptr == '\n')
	{                       /* skip over to the next record */
	    *nxtptr = ' ';      /* no-op the newline            */
	    nxtptr++;
	    if (*nxtptr != ' ' && *nxtptr != '\t')
	    {                   /* continuation line            */
		nxtptr[-1] = '\0';
		break;
	    }
	}

    retval = str2arg (tai_ptr, max, argv, (char *) 0);
    if (retval < 0)
	tai_eptr = tai_ptr;        /* record where the error happened   */
    if (*nxtptr == '\0')
	tai_ptr = 0;
    else
	tai_ptr = nxtptr;

    if (retval == 0)                /* nothing but comments on line */
	goto retry;                 /* get something useful         */

    return (retval);
}
		/* fall thru */

	   mmdf/lib/util/tai_noop.c   444      0     12        1116  3620510413  10441 #include "util.h"

/* Perform process start-up tailoring */

/*  This version is a No-Op.  It presumes that the tailoring
 *  information has been compiled-in and that an external
 *  file is not to be consulted.
 */

tai_init (filename)	/* prepare to get tailoring information */
	char filename[]; /* location of the info */
{
	return (OK);
}

tai_get (max, argv)	/* get next parsed tailoring line */
	int max;	/* maximum number of allowable fields */
	char *argv[];	/* array to hold pointers to field elements */
{
	return (0);    /* return end-of-list */
}

tai_end ()
{
	return (OK);
}
t_gsize (&statbuf);
	if ((tai_data = malloc (taisize + 1)) == 0)
		return (NOTOK);
	if ((fd = open (filename, 0)) < 0)
		return (NOTOK);
	if (read (fd, tai_data, taisize) != taisize)
	{
		(void) close (fd);
		errno = EIO;    /* i/o error */
		return (NOTOK);
	}
	tai_data[taisize] = '\0';
	(void) close (fd);
	tai_ptr = tai_data;
	return (OK);
}


tai_end ()
{
	tai_data = 0;
	return (OK);
}
/**/

tai_get (max, argv)	/* get next parmmdf/lib/util/blt.c   444      0     12         625  3620510413   7376 #
/*      Copy the given string of the given length to the given          */
/*  destination string.                                                 */
/*                                                                      */

char *
	blt (from, to, length)
register char   *from,
		*to;
register int       length;
{
    if(length != 0)
	do {
	    *to++ = *from++;
	} while(--length);
    return (to);
}
t max;	/* maximum number of allowable fields */
	char *argv[];	/* array to hold pointers to field elements mmdf/lib/util/chrcnv.c   444      0     12        3162  3620510413  10117 #
char				       /* character conversion table   */
	chrcnv[] =                     /*   lower to upper case letters */
{
    '\0', '\1', '\2', '\3', '\4', '\5', '\6', '\7',
    '\10', '\t', '\n', '\13', '\14', '\r', '\16', '\17',
    '\20', '\21', '\22', '\23', '\24', '\25', '\26', '\27',
    '\30', '\31', '\32', '\33', '\34', '\35', '\36', '\37',
    ' ', '!', '"', '#', '$', '%', '&', '\47',
    '(', ')', '*', '+', ',', '-', '.', '/',
    '0', '1', '2', '3', '4', '5', '6', '7',
    '8', '9', ':', ';', '<', '=', '>', '?',
    '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
    'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
    'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
    'X', 'Y', 'Z', '[', '\\', ']', '^', '_',
    '`', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
    'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
    'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
    'X', 'Y', 'Z', '{', '|', '}', '~', '\177',
    '\0', '\1', '\2', '\3', '\4', '\5', '\6', '\7',
    '\10', '\t', '\n', '\13', '\14', '\r', '\16', '\17',
    '\20', '\21', '\22', '\23', '\24', '\25', '\26', '\27',
    '\30', '\31', '\32', '\33', '\34', '\35', '\36', '\37',
    ' ', '!', '"', '#', '$', '%', '&', '\47',
    '(', ')', '*', '+', ',', '-', '.', '/',
    '0', '1', '2', '3', '4', '5', '6', '7',
    '8', '9', ':', ';', '<', '=', '>', '?',
    '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
    'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
    'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
    'X', 'Y', 'Z', '[', '\\', ']', '^', '_',
    '`', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
    'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
    'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
    'X', 'Y', 'Z', '{', '|', '}', '~', '\177'
};
'Q', 'R', 'S', 'T', 'U', 'V', 'W',
    'X', 'Y', 'Z', '[', '\\', ']', '^', '_',
    '`', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
    'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
    'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
    'X', 'Y', 'Z', '{', '|', '}', '~', '\177',
    '\0', '\1', '\2', '\3', '\4', '\5', '\6', '\7',
    '\10', '\t', '\n', '\13', '\14', '\r', '\16', '\17',
    '\20', '\21', '\22', '\23'mmdf/lib/util/cnvtdate.c   444      0     12        6753  3671073205  10466 #include "util.h"
#include "conf.h"
#include "cnvtdate.h"

#ifndef SYS5
#include <sys/timeb.h>
#endif SYS5

#ifdef SYS5
#define timezone envtimezone
#endif SYS5

#ifdef ALTOS
#define timezone mytimezone
#endif ALTOS


char *
cnvtdate (flag, datbuf)   /* produce a date/time  string        */
int     flag;                     /* date format option value           */
char   *datbuf;
{
	static char *day[] = {
		"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
	};
	static char *month[] = {
		"Jan", "Feb", "Mar", "Apr", "May", "Jun",
		"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
	};
#ifndef SYS5
	struct timeb    timeb;
#else SYS5
	time_t tsec;
#endif SYS5
	extern char *timezone();
	extern struct tm  *localtime ();
	register struct tm  *i;

#ifndef SYS5
	ftime (&timeb);
	i = localtime ((time_t *) & timeb.time);
#else SYS5
	time (&tsec);
	i = localtime (&tsec);
#endif SYS5

	switch (flag) {
	case TIMCOM:
		sprintf (datbuf, "%02d%02d%02d%02d%02d",
			i -> tm_year, i -> tm_mon + 1, i -> tm_mday,
			i -> tm_hour, i -> tm_min);
		break;

	case TIMSECS:             /* w/seconds dd mon yy hh:mm:ss       */
		sprintf (datbuf, "%d %s %d %d:%02d:%02d",
			i -> tm_mday, month[i -> tm_mon], i -> tm_year,
			i -> tm_hour, i -> tm_min, i -> tm_sec);
		break;

	case TIMREG:              /* RFC 822 standard time string       */
	default:                  /* "Wed, 21 Jan 76 14:30 PDT"        */
		sprintf (datbuf, "%s, %d %s %d %d:%02d:%02d %s",
			day[i -> tm_wday], i -> tm_mday, month[i -> tm_mon],
			i -> tm_year, i -> tm_hour, i -> tm_min, i -> tm_sec,
#ifndef SYS5
			timezone (timeb.timezone, i -> tm_isdst));
#else
			timezone (0, i -> tm_isdst));
#endif SYS5
		break;

	case TIMSHRT:             /* w/out day of week                  */
		sprintf (datbuf, "%d %s %d %d:%02d %s",
			i -> tm_mday, month[i -> tm_mon], i -> tm_year,
			i -> tm_hour, i -> tm_min,
#ifndef SYS5
			timezone (timeb.timezone, i -> tm_isdst));
#else
			timezone (0, i -> tm_isdst));
#endif SYS5
		break;
	}
	return(datbuf);
}

#ifdef SYS5
char *
envtimezone( ignored, dst )
int ignored;
int dst;
{
	extern char *tzname[2];
	static int doneit = 0;
	static char lcltz[8] = "???";
	extern char *strncpy();
	char *cp;

	if (!doneit) {
		doneit++;
		tzset();
	}
	cp = tzname[dst ? 1 : 0];
	if (cp == (char *)0 || *cp == '\0')
		cp = "???";
	(void) strncpy(lcltz, cp, sizeof(lcltz) - 1);
	return(lcltz);
}
#endif SYS5

#ifdef	ALTOS		/* sigh */
#define	abs(a)	((a) >= 0 ? (a) : -(a))

struct keywd {
	char   *key;
	int     value;
};

#define	NZONES	(sizeof (zones)/sizeof (struct keywd))

static struct keywd zones[] = {	/* order sensitive */
	"UT", 0,
	"EST", -5, "CST", -6,  "MST", -7, "PST", -8, "PDT", -9,
	"A",   -1, "B",   -2,  "C",   -3, "D",   -4,
	"E",   -5, "F",   -6,  "G",   -7, "H",   -8,
	"I",   -9, "K",  -10,  "L",  -11, "M",  -12,
	"N",    1, "O",    2,  "P",    3, "Q",    4,
	"R",    5, "S",    6,  "T",    7, "U",    8,
	"V",    9, "W",   10,  "X",   11, "Y",   12
};

char   *mytimezone (zone, dst)
int     zone, dst;
{
	int     i,
	j,
	hours,
	mins;
	static char buffer[10];

	zone = 0 - zone;

	if (zone < 0) {
		mins = -((-zone) % 60);
		hours = -((-zone) / 60);
	} else {
		mins = zone % 60;
		hours = zone / 60;
	}

	if (dst)
		hours -= 1;

	if (mins == 0)
		for (i = 0, j = NZONES; i < j; i++)
			if (zones[i].value == hours) {
				(void) strcpy (buffer, zones[i].key);
				if (dst && buffer[1] == 'S')
					buffer[1] = 'D';
				return buffer;
			}

	sprintf (buffer, "%s%02d%02d",
		zone < 0 ? "-" : "+", abs (hours), abs (mins));
	return buffer;
}
#endif ALTOS
> tm_hour, i -> tm_mimmdf/lib/util/compress.c   444      0     12        1205  3620510414  10464 #include "util.h"
/* compress-out redundant linear white space & strip trailing lwsp */

char *
	compress (fromptr, toptr)
register char *fromptr,
	      *toptr;
{
    register char   chr;

    chr = ' ';                    /* init to skip leading spaces  */
    while ((*toptr = *fromptr++) != '\0')
    {                             /* convert newlines and tabs to */
	if (isspace (*toptr))
	{                         /* only save first space      */
	    if (chr != ' ')
		*toptr++ = chr  = ' ';
	}
	else
	    chr = *toptr++;
    }

    if (chr == ' ')               /* remove trailing space if any */
	*--toptr = '\0';
    return (toptr);
}
/ 60;
	}

	if (dst)
		hours -= 1;

	if (mins == 0)
		for (i = 0, j = NZONES; i < j; i++)
			if (zones[i].value == hours) {
				(void) strcpy (buffer, zones[i].key);
				if (dst && buffer[1] == 'S')
					buffer[1] = 'D';
				return buffer;
			}

	sprintf (buffer, "%s%02d%02d",
		zone < 0 ? "-" : "+", abs (hours), abs (mins));
	return buffer;
}
#endif ALTOS
> tm_hour, i -> tm_mimmdf/lib/util/endstr.c   444      0     12         750  3620510414  10114 /*    check for EXACT match of suffix string at end of thestring          */

endstr (suffix, thestring, suflen)
    register char   *suffix,
		    *thestring;
    register short    suflen;
{                                 /* does str end with suffix string?     */
    short thelength;

    if (suflen < 0)
	suflen = strlen (suffix);
    thelength = strlen (thestring);

    if (suflen > thelength)
	return (0);

    return (initstr (suffix, &thestring[thelength - suflen], suflen));
}
hr  = ' ';
	}
	else
	   mmdf/lib/util/equal.c   444      0     12         774  3620510414   7732 /*                                                                      */
/*      Determine whether the two given strings have an identical       */
/*  prefix of the given length.                                         */
/*                                                                      */

equal (str1, str2, length)
register char   *str1,
		*str2;
short              length;
{
    extern char chrcnv[];

    while (chrcnv[*str1++] == chrcnv[*str2++] && --length > 0);
    return (length == 0);
}
	   mmdf/lib/util/equwrd.c   444      0     12        1065  3620510414  10144 #include "util.h"
/*                                                                      */
/*      Determine whether the two given strings begin with the same     */
/*  alphabetic prefix of the given length.                              */
/*                                                                      */

equwrd (wrd1, wrd2, length)
register char   *wrd1,
		*wrd2;
int              length;
{
    extern char chrcnv[];

    while (chrcnv[*wrd1++] == chrcnv[*wrd2++] && --length > 0);
    return (length == 0 && !isalpha (*wrd1) && !isalpha (*wrd2));
}
     /* remove trailing space if any */
	*--toptr = '\0';
    return (toptr);
}
/ 60;
	}

	if (dst)
		hours -= 1;

	if (mins == 0)
		for (i = 0, j = NZONES; i < j; i++)
			if (zones[i].value == hours) {
				(void) strcpy (buffer, zones[i].key);
				if (dst && buffer[1] == 'S')
					buffer[1] = 'D';
				return buffer;
			}

	sprintf (buffer, "%s%02d%02d",
		zone < 0 ? "-" : "+", abs (hours), abs (mins));
	return buffer;
}
#endif ALTOS
> tm_hour, i -> tm_mimmdf/lib/util/gcread.c   444      0     12        5320  3620510417  10063 #include "util.h"

/* Modified gcread to use stdio. Jim Lieb SRI International Aug 1980 */
/*   returns -1 for error, 0 for eof, positive with count for success */
/*   Successive calls will return error or eof after first encounter  */
/*   Neither eof nor error will be indicated until buffer is emptied  */
/* Modified by Phil Cockcroft UCL Mar 85 to handle quoted strings properly */

gcread (fp, obuf, len, endchrs)
FILE  *fp;
int       len;                  /* maximum allowed to xfer              */
char   *obuf,                   /* destination buffer                   */
       *endchrs;                /* list of chars to terminate on        */
                                /*   "abcdef\377"                       */
{
	return (qread (fp, obuf, len, endchrs, 0));
}

qread (fp, obuf, len, endchrs, quotechar)
FILE  *fp;
int       len;                  /* maximum allowed to xfer              */
char   *obuf,                   /* destination buffer                   */
       *endchrs;                /* list of chars to terminate on        */
                                /*   "abcdef\377"                       */
char    quotechar;              /* a single character quote             */
{
    char *bptr;
    register int c;
    register short cnt;
    register char *end;

    if (feof (fp))
      return (OK);

    if (ferror (fp))
      return (NOTOK);

    for (bptr = obuf, cnt = 0; cnt < len; cnt++)
    {
        if ((c = getc(fp)) == EOF)      /* maybe end-of-file            */
        {
            if (ferror (fp))
                return ((cnt == 0) ? NOTOK : cnt);
            if (feof (fp))
                return (cnt);
        }
        if(quotechar != '\0' && c == quotechar)
        {
            if ((c = getc(fp)) == EOF)      /* maybe end-of-file        */
            {
                if (bptr != 0)              /* what do we do here ????  */
                    *bptr = quotechar;
                if (ferror (fp))
                    return ((cnt == 0) ? NOTOK : cnt);
                if (feof (fp))
                    return (cnt);
                /* EH ? I hope we will never get here */
            }
            if (bptr != 0)              /*  0 => just skip the text     */
                *bptr++ = c;            /*  xfer one char               */
            continue;
        }
        if (bptr != 0)                  /*  0 => just skip the text     */
            *bptr++ = c;                /*  xfer one char               */

        if (end = endchrs)              /*  check if it's in breakset   */
            while ((int)*end != (-1))	/*  Grrrrrr.... */
                if (c == *end++)
                    return ++cnt;
    }
    return (cnt);                        /* return num xferred           */
}
register int c;
    register short cnt;
    register char *end;

    if (feof (fp))
      return (OK);

    if (ferror (fp))
      return (NOTOK);

    for (bptr = obuf, cnt = 0; cnt < len; cnt++)
    {
        if ((c = getc(fp)) == EOF)      /* maybe end-of-file            */
        {
            if (mmdf/lib/util/initstr.c   444      0     12         523  3620510417  10312 /*  Check for EXACT match prefix string at beginning of thestring       */

initstr (prefix, thestring, preflen)
    register char   *prefix,
		    *thestring;
    register short     preflen;
{

    if (preflen < 0)
	preflen = strlen (prefix);

    while (*prefix++ == *thestring++)
	if (--preflen == 0)
	    return (1);
    return (0);
}
here ????  */
                    *bptr = quotechar;
                if (ferror (fp))
                    return ((cnt == 0) ? NOTOK : cnt);
                if (feof (fp))
 mmdf/lib/util/lexequ.c   444      0     12         751  3620510417  10124 #include "util.h"

/*                                                                      */
/*      Determine if the two given strings are equivalent.              */
/*                                                                      */

lexequ (str1, str2)
register char   *str1,
		*str2;
{
    extern char chrcnv[];

    if(str1 == 0 || str2 == 0)
	return (str1 == str2);

    while (chrcnv[*str1] == chrcnv[*str2++])
	if (*str1++ == 0)
	    return (TRUE);

    return (FALSE);
}
       if (feof (fp))
 mmdf/lib/util/lexrel.c   444      0     12         623  3620510417  10112 
lexrel (str1, str2)
register char   *str1,
		*str2;
{
    extern char chrcnv[];

    while (chrcnv[*str1] == chrcnv[*str2++])
	if (*str1++ == '\0')
	    return (0);           /* exactly equal                      */
    if (chrcnv[*str1] < chrcnv[*--str2])
	return (-1);              /* str1 earlier in lexicon            */
    return (1);                   /* str2 is earlier                    */
}
r1] == chrcnv[*str2++])
	if (*str1++ == 0)
	    return (TRUE);

    return (FALSE);
}
       if (feof (fp))
 mmdf/lib/util/Makefile.real   444      0     12       12007  3635173721  11105 #
#	Makefile for MMDF general utility routines
#
MODULES =	tty_io ll_err ll_log tai_packages cmdsrch \
		cnvtdate creatdir getfpath getwho \
		arg2str str2arg cstr2arg gpwlnam ggrlnam \
		strindex equwrd equal endstr initstr lexrel \
		prefix strequ lexequ chrcnv getutmp getgroup \
		compress multcpy multcat strdup gcread gettys \
		zero blt expand sstr2arg nexec \
		tai_file tai_noop \
		gwdir gwdir.4.2 \
		getllog.v7 getllog.bsd getllog.fak

STANDARD =	tty_io.o ll_err.o ll_log.o tai_packages.o cmdsrch.o \
		cnvtdate.o creatdir.o getfpath.o getwho.o \
		arg2str.o str2arg.o cstr2arg.o gpwlnam.o ggrlnam.o \
		strindex.o equwrd.o equal.o endstr.o initstr.o lexrel.o \
		prefix.o strequ.o lexequ.o chrcnv.o getutmp.o getgroup.o \
		compress.o multcpy.o multcat.o strdup.o gcread.o gettys.o \
		zero.o blt.o expand.o sstr2arg.o nexec.o

STDSRCS =	tty_io.c ll_err.c ll_log.c tai_packages.c cmdsrch.c \
		cnvtdate.c creatdir.c getfpath.c getwho.c \
		arg2str.c str2arg.c cstr2arg.c gpwlnam.c ggrlnam.c \
		strindex.c equwrd.c equal.c endstr.c initstr.c lexrel.c \
		prefix.c strequ.c lexequ.c chrcnv.c getutmp.c getgroup.c \
		compress.c multcpy.c multcat.c strdup.c gcread.c gettys.c \
		zero.c blt.c expand.c sstr2arg.c nexec.c

v7objs =	getllog.v7.o gwdir.o
v7srcs =	getllog.v7.c gwdir.c

5.2objs =	getllog.fak.o gwdir.o
5.2srcs =	getllog.fak.c gwdir.c

4.1objs = 	getllog.bsd.o gwdir.o
4.1srcs = 	getllog.bsd.c gwdir.c

4.2objs =	getllog.bsd.o gwdir.4.2.o
4.2srcs =	getllog.bsd.c gwdir.4.2.c

real-default: ../util-made
../util-made:	${STANDARD} ${LOCALUTIL} ../${SYSTEM}-made
	ar r ../libmmdf.a ${STANDARD} ${LOCALUTIL}
	-touch ../util-made

../v7-made:	${v7objs}
	ar r ../libmmdf.a ${v7objs}
	-touch ../v7-made

../5.2-made:	${5.2objs}
	ar r ../libmmdf.a ${5.2objs}
	-touch ../5.2-made

../4.1-made:	${4.1objs}
	ar r ../libmmdf.a ${4.1objs}
	-touch ../4.1-made

../4.2-made:	${4.2objs}
	ar r ../libmmdf.a ${4.2objs}
	-touch ../4.2-made

lint:	std-lint ${SYSTEM}-lint
	cat llib-lutila* llib-lutilb* > llib-lutil.ln

std-lint:
	$(LINT) -Cutila $(LFLAGS) $(STDSRCS)

v7-lint:
	$(LINT) -Cutilb $(LFLAGS) $(v7srcs)

5.2-lint:
	$(LINT) -Cutilb $(LFLAGS) $(5.2srcs)

4.1-lint:
	$(LINT) -Cutilb $(LFLAGS) $(4.1srcs)

4.2-lint:
	$(LINT) -Cutilb $(LFLAGS) $(4.2srcs)

clean:
	-rm -f *.o x.c makedep eddep make.out errs core llib-lutil*


# DO NOT DELETE THIS LINE -- make depend uses it

tty_io.o: tty_io.c
tty_io.o: ../../h/util.h
tty_io.o: /usr/include/pwd.h
tty_io.o: /usr/include/sys/stat.h
tty_io.o: /usr/include/utmp.h
ll_err.o: ll_err.c
ll_err.o: /usr/include/stdio.h
ll_err.o: ../../h/ll_log.h
ll_log.o: ll_log.c
ll_log.o: ../../h/util.h
ll_log.o: ../../h/conf.h
ll_log.o: /usr/include/sys/stat.h
ll_log.o: ../../h/ll_log.h
tai_packages.o: tai_packages.c
tai_packages.o: ../../h/util.h
tai_packages.o: ../../h/ll_log.h
tai_packages.o: ../../h/cmd.h
cmdsrch.o: cmdsrch.c
cmdsrch.o: ../../h/util.h
cmdsrch.o: ../../h/cmd.h
cnvtdate.o: cnvtdate.c
cnvtdate.o: ../../h/util.h
cnvtdate.o: ../../h/conf.h
cnvtdate.o: ../../h/cnvtdate.h
cnvtdate.o: /usr/include/sys/timeb.h
creatdir.o: creatdir.c
creatdir.o: ../../h/util.h
creatdir.o: /usr/include/sys/stat.h
getfpath.o: getfpath.c
getfpath.o: ../../h/util.h
getwho.o: getwho.c
arg2str.o: arg2str.c
arg2str.o: ../../h/util.h
str2arg.o: str2arg.c
str2arg.o: ../../h/util.h
cstr2arg.o: cstr2arg.c
cstr2arg.o: ../../h/util.h
gpwlnam.o: gpwlnam.c
gpwlnam.o: ../../h/util.h
gpwlnam.o: /usr/include/pwd.h
ggrlnam.o: ggrlnam.c
ggrlnam.o: ../../h/util.h
ggrlnam.o: /usr/include/grp.h
strindex.o: strindex.c
equwrd.o: equwrd.c
equwrd.o: ../../h/util.h
equal.o: equal.c
endstr.o: endstr.c
initstr.o: initstr.c
lexrel.o: lexrel.c
prefix.o: prefix.c
prefix.o: ../../h/util.h
strequ.o: strequ.c
strequ.o: ../../h/util.h
lexequ.o: lexequ.c
lexequ.o: ../../h/util.h
chrcnv.o: chrcnv.c
getutmp.o: getutmp.c
getutmp.o: ../../h/util.h
getutmp.o: /usr/include/utmp.h
getgroup.o: getgroup.c
compress.o: compress.c
compress.o: ../../h/util.h
multcpy.o: multcpy.c
multcat.o: multcat.c
multcat.o: ../../h/util.h
strdup.o: strdup.c
strdup.o: ../../h/util.h
gcread.o: gcread.c
gcread.o: ../../h/util.h
gettys.o: gettys.c
gettys.o: /usr/include/stdio.h
gettys.o: ../../h/gettys.h
zero.o: zero.c
blt.o: blt.c
expand.o: expand.c
expand.o: ../../h/util.h
sstr2arg.o: sstr2arg.c
sstr2arg.o: ../../h/util.h
nexec.o: nexec.c
nexec.o: ../../h/util.h
nexec.o: ../../h/nexec.h
nexec.o: /usr/include/signal.h
nexec.o: /usr/include/fcntl.h
tai_file.o: tai_file.c
tai_file.o: ../../h/util.h
tai_file.o: /usr/include/sys/stat.h
tai_noop.o: tai_noop.c
tai_noop.o: ../../h/util.h
gwdir.o: gwdir.c
gwdir.o: ../../h/conf.h
gwdir.o: ../../h/util.h
gwdir.o: /usr/include/sys/stat.h
gwdir.4.2.o: gwdir.4.2.c
gwdir.4.2.o: ../../h/util.h
getllog.v7.o: getllog.v7.c
getllog.v7.o: ../../h/util.h
getllog.v7.o: /usr/include/utmp.h
getllog.bsd.o: getllog.bsd.c
getllog.bsd.o: ../../h/util.h
getllog.bsd.o: /usr/include/pwd.h
getllog.bsd.o: /usr/include/utmp.h
getllog.bsd.o: /usr/include/lastlog.h
getllog.fak.o: getllog.fak.c
# DEPENDENCIES MUST END AT END OF FILE
# IF YOU PUT STUFF HERE IT WILL GO AWAY
# see make depend above
AGS) $(STDSRCS)

v7-lint:
	$(LINT) -Cutilb $(LFLAGS) $(v7srcs)

5.2-lint:
	$(LINT) -Cutilb $(LFLAGS) $(5.2srcs)

4.1-lint:
	$(LINT) -Cutilb $(LFLAGS) $(4.1srcs)

4.2-lint:
	$(LINT) -Cutilb $(LFLAGS) $(4.2srcs)

clean:
	-rm -f *.o x.c makedep eddep make.out errs core llib-lutil*


# DO NOT DELETE THIS LINE -- make depend uses it

tty_io.o: tty_io.c
tty_io.o: ../../h/util.h
tty_io.o: /usr/include/pwd.h
tty_io.o: /usr/include/sys/stat.h
tty_io.o: /usr/include/utmp.h
ll_err.o: ll_err.c
ll_err.o: /usr/incmmdf/lib/util/multcat.c   444      0     12        1214  3664425166  10323 #include "util.h"
#include <varargs.h>

/*                                                                      */
/*              Concatenate a series of arrays                          */

/*VARARGS0*/
char *multcat (va_alist)
va_dcl
{
	register va_list ap;
	register char  *oldstr, *ptr;
	char    *newstr;
	extern char *malloc();
	unsigned  newlen;

	va_start(ap);
	for (newlen = 1; oldstr = va_arg(ap, char *);)
		newlen += strlen (oldstr);
	va_end(ap);

	if ((ptr = newstr = malloc (newlen+1)) == 0)
	    return((char *)0);

	va_start(ap);
	for (; oldstr = va_arg(ap, char *); ptr--)
		while(*ptr++ = *oldstr++);
	va_end(ap);

	return (newstr);
}
requ.c
strequ.o: ../../h/util.h
lexequ.o: lexequ.c
lexequ.o: ../../h/util.h
chrcnv.o: chrcnv.c
getutmp.o: getutmp.c
getutmp.o: ../../h/util.h
getutmp.o: /usr/include/utmp.h
getgroup.o: getgroup.c
compress.o: compress.c
compress.o: ../../h/util.h
multcpy.o: multcpy.c
multcat.o: multcat.c
multcat.o: ../../h/util.h
strdup.o: strdup.c
strdup.o: ../../h/util.h
gcread.o: gcremmdf/lib/util/multcpy.c   444      0     12        1017  3657737705  10357 #include <varargs.h>

/*                                                                      */
/*      Copy the list of strings to the given location.                 */
/*                                                                      */

char *
multcpy (to, va_alist)
register char   *to;
va_dcl
{
	register va_list ap;
	register char   *from;

	va_start(ap);

	while(from = va_arg(ap, char *)){
		while(*to++ = *from++);
		to--;
	}
	va_end(ap);
	return (to);            /* return pointer to end of str         */
}

;

	va_start(ap);
	for (; oldstr = va_arg(ap, char *); ptr--)
		while(*ptr++ = *oldstr++);
	va_end(ap);

	return (newstr);
}
requ.c
strequ.o: ../../h/util.h
lexequ.o: lexequ.c
lexequ.o: ../../h/util.h
chrcnv.o: chrcnv.c
getutmp.o: getutmp.c
getutmp.o: ../../h/util.h
getutmp.o: /usr/include/utmp.h
getgroup.o: getgroup.c
compress.o: compress.c
compress.o: ../../h/util.h
multcpy.o: multcpy.c
multcat.o: multcat.c
multcat.o: ../../h/util.h
strdup.o: strdup.c
strdup.o: ../../h/util.h
gcread.o: gcremmdf/lib/util/gwdir.c   444      0     12        7414  3671073206   7766 #include "conf.h"
#include "util.h"
#include <sys/stat.h>

LOCVAR char wd_DOT[] = ".";
LOCVAR char wd_DOTDOT[] = "..";
LOCVAR char wd_read[] = "r";

LOCVAR char     wd_name[128];     /* max size of path string            */
LOCVAR jmp_buf  wd_retloc;
LOCVAR union wd                 /* unioned for making i/o cleaner       */
{
	char            io[sizeof (struct direct)];
	struct direct   dir;
} wd_entry;
LOCVAR FILE * wd_fp;

char *
	gwdir ()                    /* what is current working directory? */
{
    struct stat wd_stat;
    short     lastd_ino;            /* last inode num                     */
    char    buf[BUFSIZ];

    if (!setjmp (wd_retloc))
    {				  /* end due to eof or err              */
	if (wd_fp == (FILE *) EOF || wd_fp == (FILE *) NULL ||
	        fclose (wd_fp) == EOF ||
		wd_name[0] == '\0' ||
		chdir (wd_name) < OK)
	    return ((char *) NOTOK);

	return (wd_name);
    }

    wd_name[0] = '\0';
    lastd_ino = -1;		  /* force to be set on first pass      */

    if (stat (wd_DOT, &wd_stat) < 0 ||         /* get inode num for this dir         */
	    (wd_fp = fopen (wd_DOTDOT, wd_read)) == NULL)
	longjmp (wd_retloc, TRUE);
				  /* set to compare DOTDOT inodes       */
    setbuf (wd_fp, buf);

    for (;;)
    {
	for (;;)
	{			  /* cycle thru dir's inodes            */
	    fread (wd_entry.io, sizeof (struct direct), 1, wd_fp);
	    if (ferror (wd_fp) || feof (wd_fp))
		longjmp (wd_retloc, TRUE);

	    if (wd_entry.dir.d_ino == wd_stat.st_ino)
		break;		  /* got a match                        */
	}
	if (wd_fp == (FILE *) EOF || wd_fp == (FILE *) NULL ||
	         fclose (wd_fp) == EOF || ferror (wd_fp) || feof (wd_fp))
	    longjmp (wd_retloc, TRUE);
	if (wd_entry.dir.d_ino == 1)  /* hit the root directory             */
	    wd_ckroot ();	  /* try to find its name               */

	if (wd_entry.dir.d_ino == lastd_ino)
				  /* we are cycling at top              */
	    longjmp (wd_retloc, TRUE);
	else			  /* remember for next level up         */
	    lastd_ino = wd_entry.dir.d_ino;

	wd_cat ();		  /* add current name to end            */

	if (chdir (wd_DOTDOT) < OK || /* set up for next pass this dir      */
	       stat (wd_DOT, &wd_stat) < OK ||
	       freopen (wd_DOTDOT, wd_read, wd_fp) == NULL)
	    longjmp (wd_retloc, TRUE);
    }
}

LOCFUN wd_ckroot ()               /* check root dir for filesys name    */
{
    static char wd_root[] = "/";
    struct stat wd_stat,
		owd_stat;         /* to save device info                */

    if (stat (wd_entry.dir.d_name, &wd_stat) < 0)
	longjmp (wd_retloc, TRUE);

    owd_stat.st_dev = wd_stat.st_dev;

    if (chdir (wd_root) < OK)
	longjmp (wd_retloc, TRUE);

    if (freopen (wd_root, wd_read, wd_fp) == NULL)
	longjmp (wd_retloc, TRUE);

    for (;;)
    {				  /* read thru "/" dir                  */
	fread (wd_entry.io, sizeof (struct direct), 1, wd_fp);
	if (ferror (wd_fp) || feof (wd_fp))
	    longjmp (wd_retloc, TRUE);

	if (wd_entry.dir.d_ino == 0)  /* not allocated                      */
	    continue;

	if (stat (wd_entry.dir.d_name, &wd_stat) < 0)
	    longjmp (wd_retloc, TRUE);

	if (wd_stat.st_dev == owd_stat.st_dev)
	{			  /* device types match                 */
	    wd_stat.st_mode &= S_IFMT;
	    if (wd_stat.st_mode == S_IFDIR)
		break;		  /* and it is a directory              */
	}
    }
    if (strcmp (wd_entry.dir.d_name, wd_DOT) != 0 &&
	    strcmp (wd_entry.dir.d_name, wd_DOTDOT) != 0)
	wd_cat ();		  /* it has a real text name, too       */

    wd_entry.dir.d_name[0] = '\0';    /* hack, to force "/" at front        */
    wd_cat ();
    longjmp (wd_retloc, TRUE);
}

LOCFUN wd_cat ()
{
    extern char *strcpy (),
		*strcat ();

    if (wd_name[0] == 0)
	(void) strcpy (wd_name, wd_entry.dir.d_name);
    else
    {
	strcat (wd_name, "/");
	strcat (wd_name, wd_entry.dir.d_name);
    }
}
y to find its name               */

	if (wd_entry.dir.d_ino == lastd_ino)
				  /* we are cycling at top              */
	    longjmp (wd_retloc, TRUE);
	else			  /* remember for next level up         */
	    lastd_ino = wd_entry.dir.d_ino;

	mmdf/lib/util/rplstr.c   444      0     12         276  3620510420  10143 /*      replace an alloc'd string with a new one                */

rplstr (oldstr, newstr)
    char **oldstr,
	  *newstr;
{
    if (*oldstr != 0)
	free (*oldstr);

    *oldstr = newstr;
}

TDOT, wd_read, wd_fp) == NULL)
	    longjmp (wd_retloc, TRUE);
    }
}

LOCFUN wd_ckroot ()               /* check root dir for filesys name    */
{
    static char wd_root[] = "/";
    struct stat wd_stat,
		owd_stat;         /* to save device info                */

    if (stat (wd_entry.dir.d_name, &wd_stat) < 0)
	lommdf/lib/util/sptotab.c   444      0     12         522  3620510421  10264 #
sptotab (pcurcol, firstab, tabinc)
int     pcurcol,
        firstab,
        tabinc;
{
    register short
                    curcol,
                    diff;

    curcol = pcurcol;
    firstab++;

    if (curcol <= firstab)
	return (firstab - curcol);
    else
	return ((diff = (--curcol % tabinc)) == 0
		? 0
		: (tabinc - diff));
}

    static char wd_root[] = "/";
    struct stat wd_stat,
		owd_stat;         /* to save device info                */

    if (stat (wd_entry.dir.d_name, &wd_stat) < 0)
	lommdf/lib/util/strdup.c   444      0     12        1011  3620510421  10143 #include "util.h"

/*                                                                      */
/*      Create a duplicate copy of the given string.                    */
/*      Modified for V7 Unix by Jim lieb SRI International Aug 80       */

char *
	strdup (str)
register char   *str;
{
    extern char *malloc ();
    register char  *newptr,
		   *newstr;

    if ((newstr = malloc ((unsigned) (strlen (str) + 1))) == 0)
	return ((char *) 0);

    for (newptr = newstr; *newptr++ = *str++; );

    return (newstr);
}
_retloc, TRUE);

    owd_stat.st_dev = wd_stat.st_dev;

    if (chdir (wd_root) < OK)
	longjmp (wd_retloc, TRUE);

    if (freopen (wd_root, wd_read, wd_fp) == NULL)
	longjmp (wd_retloc, TRUE);

    for (;;)
    {				  /* read thru "/" dir                  */
	fread (wd_entry.io, sizeof (struct direct), 1, wd_fp);
	if (ferror (wd_fp) || feof (wd_fp))
	    longjmp (wd_retloc, TRUE);

	if (wd_entry.dir.d_ino == 0)  /* not allocated                      */
	    continue;

	if (stat (wd_entry.dir.d_nammmdf/lib/util/strequ.c   444      0     12         623  3620510421  10135 #include "util.h"

/*                                                                      */
/*      Determine if the two given strings are equivalent.              */
/*                                                                      */

strequ (str1, str2)
register char   *str1,
	*str2;
{
    while (*str1 == *str2++)
    {
	if (*str1++ == '\0')
	    return (TRUE);
    }
    return (FALSE);
}
en (str) + 1))) == 0)
	return ((char *) 0);

    for (newptr = newstr; *newptr++ = *str++; );

    return (nemmdf/lib/util/zero.c   444      0     12         520  3620510421   7565 /*                                                                      */
/*      Zero out the given string of the given length.                  */
/*                                                                      */

zero (str, length)
register char   *str;
register short     length;
{
    while (length--)
	 *str++ = '\0';
}
 (*str1++ == '\0')
	    return (TRUE);
    }
    return (FALSE);
}
en (str) + 1))) == 0)
	return ((char *) 0);

    for (newptr = newstr; *newptr++ = *str++; );

    return (nemmdf/lib/util/nexec.c   444      0     12       10530  3623126410   7756 #include "util.h"
#include "nexec.h"
#include <signal.h>

/*  generic fork/exec procedure, passing re-positioned fd's     */

/*  Jun 81  D. Crocker    make regfdary loop tmp<=HIGHFD
    May 82  D. Crocker    make close of fd tmp<=HIGHFD
    Jul 82  D. Crocker    make vax version, to use vfork
    Mar 84  D. Kingston	  Changed to use _NFILE from stdio.h instead
			  of HIGHFD.  Steve Kille: you don't have
			  to shorten the array if you have less fds.
 */

extern	int	numfds;
static	char	*zap_env[1] = { (char *)0 };

/* VARARGS4 */
#ifdef NO_VARARGS
nexecl (proctyp, pgmflags, fdarray, pgm, pgmparm, a,b,c,d,e,f,g,h,i,j,k,l,m)
#else
nexecl (proctyp, pgmflags, fdarray, pgm, pgmparm)
#endif NO_VARARGS
int   proctyp,                  /* exec / fork / fork-exec            */
	pgmflags,                 /* parent wait? disable interrupts?   */
	*fdarray;                /* what current fd's go where?        */
char	*pgm,                      /* what program to exec?              */
	*pgmparm;                  /* its parameters vector              */
{
    return (nexecv (proctyp, pgmflags, fdarray, pgm, &pgmparm));
}

nexecv (proctyp, pgmflags, fdarray, pgm, pgmparm)
int     proctyp,
	pgmflags,
	*fdarray;
char	*pgm,
	*pgmparm[];
{
    register int fd;
    int     status;
    int     childid;
    int     (*(osig1))();
    int     (*(osig2))();
    int     (*(osig3))();

    if (proctyp != PUREXEC)
    {
				  /* printf ("This is a forking call.\n"); */
	childid = tryfork ();
	if (childid == -1)
	    return (NOTOK);
				  /* printf ("Successful fork\n"); */
	if (childid != 0)
	{			  /* parent process               */
	    if (pgmflags & FRKPIGO)
	    {                     /* disable parent signals       */
				  /* printf ("Parent to be
				     non-interruptible\n"); */
		osig1 = signal (SIGHUP, SIG_IGN);
		osig2 = signal (SIGINT, SIG_IGN);
		osig3 = signal (SIGQUIT, SIG_IGN);
	    }
	    if (((proctyp == FRKEXEC) && (pgmflags & FRKWAIT)) ||
		  proctyp == SPNEXEC)
	    {
				  /* printf ("Parent is to wait\n");      */
		status = pgmwait (childid);
		if (pgmflags & FRKPIGO)
		{
		    (void)signal (SIGHUP, osig1);
		    (void)signal (SIGINT, osig2);
		    (void)signal (SIGQUIT, osig3);
		}
		return (status);
	    }
	    return (childid);
	}
	if (proctyp == SPNEXEC)
	{			  /* want it to be a spawn        */
				  /* printf ("This is a spawn\n");    */
	    switch (childid = tryfork ())
	    {
		case 0:
		    break;        /* gets to do its thing               */

		case NOTOK:       /* oops                               */
		    exit (NOTOK);

		default:          /* grandchild: kill middle proc     */
		    exit (0);
	    }
	}
    }
    if (fdarray)
    {				  /* re-align fd array list           */
	for (fd = 0; fd < numfds; fd++)
	{			  /* first do the re-positions        */
	    if (fdarray[fd] != CLOSEFD && fdarray[fd] != fd)
	    {
#ifndef NODUP2
		(void)dup2( fdarray[fd], fd );
#else NODUP2
		(void)close (fd);
#ifndef NOFCNTL
#include <fcntl.h>	    	
		fcntl (fdarray[fd], F_DUPFD, fd);
#else NOFCNTL
	    	{
		    int dupfd;

		    while ((dupfd = dup (fdarray[fd])) < fd && dupfd != -1);
				  /* did we get right fd?     */
		}
#endif NOFCNTL
#endif NODUP2
	    }
	}
	for (fd = 0; fd < numfds; fd++)
	    if (fdarray[fd] == CLOSEFD)
	    {			  /* printf ("Closing %2d\n", fd);     */
		(void)close (fd);      /* get rid of unwanted ones */
	    }
    }

    if (pgmflags & FRKCIGO)
    {
				  /* printf ("Child's interrupts to be
				     disabled\n");      */
	(void)signal (1, SIG_IGN);
	(void)signal (2, SIG_IGN);
	(void)signal (3, SIG_IGN);
    }

 /* printf ("Execing %s\n", pgm);    */
    execve (pgm, pgmparm, zap_env);
				  /* printf ("Exec not successful\n"); */
    switch (proctyp)
    {
	case PUREXEC:
	    return (NOTOK);

	default:                  /* caller can deal with it            */
	    if (pgmflags & FRKERRR)
		return (NOTOK);
    }
    exit (NOTOK);
    /* NOTREACHED */
}

pgmwait (childid)
    int childid;                  /* process id of child to collect     */
{
    int status;
    register int retval;

    while ((retval = wait (&status)) != childid)
	if (retval == NOTOK)
	    return (NOTOK);

    return (gwaitval (status));
}


LOCFUN
tryfork ()
{
    register int childid;
    register int tried;

    tried = NUMTRY;
    do {
	if(( childid = fork ()) < 0 )
		sleep( 1 );
	else
		return( childid );
    } while( tried-- );
    return( childid );
}
rom stdio.h instead
			  of HIGHFD.  Steve Kille: you don't have
			  to shorten the array if you have less fds.
 */

extern	int	numfds;
static	char	*zap_env[1] = { (chmmdf/lib/util/ll_log.c   444      0     12       16230  3671073210  10131 #include "util.h"
#include "conf.h"
#include <sys/stat.h>
#include "ll_log.h"

/*  Package for logging entries into files                              */

/*      David H. Crocker, Dept. of Electrical Engineering               */
/*      University of Delaware; October 1979                            */

/*  Failure to open a log file, other than due to its being busy,
 *  causes the fd associated with ll_struct to be set to -1.
 *  Later calls then result in a no-op, except for openllog & closellog
 */

#define _LLRETRY  5               /* number of retries if log locked    */
#define _LLSLEEP  5               /* time between tries                 */

#define SHORTIM   0		  /* Short time format                  */
#define LONGTIM   1		  /* Long time format                   */
#define _LLDIDC 0200		  /* Already did cycle, this opening    */

extern char *strdup();
extern char *rindex();

LOCVAR char
	    _cycling[] = "\n\n***  CYCLING  ***\n",
	    _cycled[] = "***  CYCLED  ***\n";

/* *******************  LOG OPENING AND CLOSING  ******************** */

ll_open (loginfo)                /* physically open the file           */
register struct ll_struct *loginfo;
{
    extern int  errno;
    short     retries;

    if (loginfo -> ll_file == 0 || *(loginfo -> ll_file) == '\0')
	return (loginfo -> ll_fd = NOTOK);
				  /* no pathname => no place to log       */

    if (loginfo -> ll_fd <= 0)   /* now closed; ALWAYS try               */
	for (retries = _LLRETRY;
		(loginfo -> ll_fd = open (loginfo -> ll_file, 1)) <= 0;
		sleep (_LLSLEEP))
	{                         /* exclusive & write access             */
	    if (errno != 26 ||    /* not simply busy                    */
		    !(loginfo -> ll_stat & LLOGWAT) ||
		    retries-- <= 0)
		return (loginfo -> ll_fd = NOTOK);
	}
    loginfo -> ll_fp = fdopen(loginfo -> ll_fd, "w");
    return (ll_init (loginfo));
}


ll_close (loginfo)
register struct ll_struct *loginfo;
{                                 /* maybe close file; always clear fd  */
    register int    fd;

    fd = loginfo -> ll_fd;
    loginfo -> ll_fd = 0;        /* clear the fd: erase -1 or handle   */

    if (loginfo -> ll_stat & _LLDIDC)
    {                             /* cycled during this open            */
	if (fd > 0)               /* flag end, from rest of log         */
	    fwrite (&_cycled[5], sizeof(char), (sizeof _cycled) - 6,
			loginfo -> ll_fp);
	loginfo -> ll_stat &= ~_LLDIDC;
    }                             /* clear cycled flag                  */

    return ((fd > 0) ? fclose (loginfo -> ll_fp) : 0);
				  /* -1 or 0 => nothing was open        */
}
/* *********************  LOG INITIALIZATION  *********************** */

ll_hdinit (loginfo, pref)              /* make header field unique           */
register struct ll_struct *loginfo;
register char *pref;
{
    register char *cp;
    char tmpstr[16];

    if (pref == (char *)0 && (pref = loginfo -> ll_hdr) == (char *)0)
	pref = "XXXXXX";
    if (cp = rindex(pref, '/'))
	pref = cp+1;	/* drop leading path */

    sprintf (tmpstr, "%-6.6s%04d", pref, getpid() % 10000);
				/* use last 4 digits of procid          */

    loginfo -> ll_hdr = strdup (tmpstr);
    ll_close (loginfo);          /* start with a clean pointer        */
}

ll_init (loginfo)                /* position log [print header]        */
register struct ll_struct *loginfo;
{
    if (loginfo -> ll_fd < 0)
	return (NOTOK);

    fseek (loginfo -> ll_fp, 0L, 2);

    if (ll_cycle (loginfo) < OK)
	return (NOTOK);		  /* too big and can't cycle            */

    if (loginfo -> ll_stat & LLOGCLS)
	return (OK);              /* don't do header for opening        */

    if (loginfo -> ll_hdr == (char *) 0) /* give it a place-holder      */
	loginfo -> ll_hdr = "";

    ll_time (loginfo, LONGTIM);
    fprintf (loginfo -> ll_fp, "---  OPEN  ---(%d)\n", loginfo -> ll_fd);

    return (OK);
}
/* *********************  DO THE LOGGING  *************************** */

/* VARARGS3 */
ll_log (loginfo, verblev, format, a1, a2, a3, a4, a5, a6, a7, a8, a9, a0)
register struct ll_struct *loginfo;
int     verblev;
char   *format,
       *a1, *a2, *a3, *a4, *a5, *a6, *a7, *a8, *a9, *a0;
{
    if (verblev > loginfo -> ll_level)
	return;              /* too verbose for this setting       */

    switch (loginfo -> ll_fd)
    {
	case NOTOK: 		  /* error in its past                  */
	    return;

	case 0:
	    if (ll_open (loginfo) < OK)
		return;   /* couldn't open it properly          */
	    break;

	default: 		  /* check file size                    */
	    if (ll_cycle (loginfo) < OK)
		return;
    }

    ll_time (loginfo, (loginfo -> ll_stat & LLOGCLS) ? LONGTIM : SHORTIM);
    fprintf(loginfo->ll_fp,
		format, a1, a2, a3, a4, a5, a6, a7, a8, a9, a0);
    (void) putc('\n', loginfo->ll_fp);

    if (loginfo -> ll_stat & LLOGCLS)
	ll_close (loginfo);

    return;
}
/* *********************  INTERNAL ROUTINES  ************************ */

LOCFUN
	ll_cycle (loginfo)        /* file not within limits => cycle     */
register struct ll_struct *loginfo;
{
    int stats;
    register int    numblocks,
		    maxblocks;

    if ((maxblocks = loginfo -> ll_msize) <= 0)
	return (OK);

    maxblocks *= 25;              /* units of 25 blocks (1 kbyte/block)   */
    numblocks = ll_size (loginfo);

    if ((stats = loginfo -> ll_stat) & _LLDIDC)
    {                             /* different test if already cycled     */
	if (numblocks > maxblocks + 1)
	    goto endit;           /* the sucker REwrote length of file    */
    }
    else
	if (numblocks >= maxblocks)
	{                         /* normal uncycled check                */
	    if ((stats & LLOGCLS) || !(stats & LLOGCYC))
	    {                     /* give up if one-liner or no cycling   */
endit:
		ll_close (loginfo);
		return (loginfo -> ll_fd = NOTOK);
	    }
				  /* record status of llog file           */
	    loginfo -> ll_msize = numblocks / 25;
	    loginfo -> ll_stat |= _LLDIDC;
				  /* make notes in it & goto beginning    */

	    fseek (loginfo->ll_fp, -20L, 1);
	    fwrite(_cycling, sizeof(char), sizeof _cycling - 1,
			loginfo->ll_fp);
	    fseek (loginfo->ll_fp, 0L, 0);
	    fwrite(_cycled, sizeof(char), sizeof _cycled - 1,
			loginfo->ll_fp);
	}
    return (OK);
}
/**/

LOCFUN
	ll_size (loginfo)       /* how big is log file?               */
register struct ll_struct *loginfo;
{
    struct stat llstat;

    if(fflush(loginfo->ll_fp) == EOF)  /* must show what is buffered too */
	return (0);
    if (fstat(loginfo -> ll_fd, &llstat) < 0)
	return (0);

    /*NOSTRICT*/
    return ((st_gsize (&llstat) + 1023L)/1024L);
}
/**/

LOCFUN
	ll_time (loginfo, timtype)
register struct ll_struct *loginfo;
int     timtype;
{				  /* record real date/time              */
    extern struct tm   *localtime ();
    time_t clock;
    register struct tm *tpt;

    time (&clock);
    tpt = localtime (&clock);

    switch (timtype)
    {
	case LONGTIM: 		  /* mm/dd hh:mm:ss                       */
	    fprintf (loginfo->ll_fp, "%2d/%2d %2d:%02d:%02d %s:  ",
		    tpt->tm_mon + 1, tpt->tm_mday, tpt->tm_hour,
		    tpt->tm_min, tpt->tm_sec, loginfo -> ll_hdr);
	    break;

	case SHORTIM: 		  /* mm:ss                                */
	    fprintf (loginfo->ll_fp, "%2d:%02d ", tpt->tm_min, tpt->tm_sec);
    }
}
       /* start with a clean pointer        */
}

ll_init (loginfo)                /* position log [print header]        */
register struct ll_struct *loginfo;
{
    if (loginfo -> ll_fd < 0)
	return (NOTOK);

    fseek (loginfo -> ll_fp, 0L, 2);

    if (ll_cycle (loginfo) < OK)
	return (NOTOK);		  /* too big and can't cycle            */

    if (loginfo -mmdf/lib/util/getllog.fak.c   444      0     12         336  3620510422  11011 /* For systems that have no easy way to find last login time */
/* For example: SYSVR2  --  "Consider it stranded." */

setllog()
{
}

endllog()
{
}

getllog()
{
	return (0);
}

getllnam(name)
char *name;
{
	return (0);
}
             /* position log [print header]        */
register struct ll_struct *loginfo;
{
    if (loginfo -> ll_fd < 0)
	return (NOTOK);

    fseek (loginfo -> ll_fp, 0L, 2);

    if (ll_cycle (loginfo) < OK)
	return (NOTOK);		  /* too big and can't cycle            */

    if (loginfo -mmdf/lib/util/ll_err.c   444      0     12        1244  3671073210  10117 #include <stdio.h>
#include "ll_log.h"

/*          Augment ll_io routines to print system call errno value        */

extern int  sys_nerr;
extern char *sys_errlist[];
extern int  errno;
extern char *sprintf ();

/* VARARGS3 */
ll_err (loginfo, level, fmt, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9)
struct ll_struct  *loginfo;
int     level;
char   *fmt,
       *a0, *a1, *a2, *a3, *a4, *a5, *a6, *a7, *a8, *a9;
{
    char newfmt[128];

    sprintf (newfmt,  "%s%s",  "[SYSERR (%d) %s]  ",  fmt);

    ll_log (loginfo,  level,  newfmt, errno,
	    (errno >= 0 && errno <= sys_nerr)
		? sys_errlist[errno]
		: "<- Illegal errno",
	    a0, a1, a2, a3, a4, a5, a6, a7, a8, a9);
}

	loginfo -> ll_hdr = "";

    ll_time (loginfo, LONGTIM);
    fprintf (loginfo -> ll_fp, "---  OPEN  ---(%d)\n", loginfo -> ll_fd);

    return (OK);
}
/* *********************  DO THE LOGGING  *************************** */

/* VARARGS3 */
ll_log (loginfo, verblev, format, a1, a2, a3, a4, a5, a6, a7, a8, a9, a0)
register struct ll_struct *logimmdf/lib/util/getutmp.c   444      0     12        1520  3620510422  10315 #include "util.h"
#include <utmp.h>

/* get informtion about who is logged in */

/* this is modeled after the getpw v7 set of routines */

LOCVAR char UTPATH[]  = "/etc/utmp";
LOCVAR FILE *utmpfp = NULL;
LOCVAR union tmpunion
{
    char        io[sizeof (struct utmp)];
    struct utmp entry;
}       utmp;

setutmp()
{
	if( utmpfp == NULL )
		utmpfp = fopen( UTPATH, "r" );
	else
		rewind( utmpfp );
}

endutmp()
{
	if( utmpfp != NULL ){
		fclose( utmpfp );
		utmpfp = NULL;
	}
}

struct utmp *
getutmp()
{
	if (utmpfp == NULL) {
		if( (utmpfp = fopen( UTPATH, "r" )) == NULL )
			return(0);
	}
	if (fread (utmp.io, sizeof (struct utmp), 1, utmpfp) != 1)
		return(0);
	return(&utmp.entry);
}

struct utmp *
getutnam(name)
char name[];
{
	register struct utmp *p;

	while( (p = getutmp()) && !equal(name,p->ut_name, strlen(name)));

	return(p);
}
NULL )
		utmpfp = fopen( UTPATH, "r" );
	else
		rewind( utmpfp );
}

endutmp()
{
	if( utmpfp != NULL ){
		fclose( utmpfp );
		utmpfp = NULL;
	}
}

struct utmp *
getutmp()
{
	ifmmdf/lib/util/tty_io.c   444      0     12        4166  3620510423  10151 #include "util.h"
#include <pwd.h>
#include <sys/stat.h>
#include <utmp.h>

extern int errno;
extern jmp_buf timerest;
extern char *blt();

FILE *tty_fp;                     /* user sends tty output to this      */

/*  ****************  (tty_) ACCESS USERS' TTYS  ********************** */

tty_init ()                       /* setup for ttyecipient's tty        */
{
    setutmp ();                   /* get the utmp file                  */
    return (OK);
}

tty_end ()                       /* done with tty access               */
{
    endutmp ();                   /* done with utmp package             */
    return (OK);
}

tty_find (usrname)                /* find & open recipient's tty        */
char    usrname[];
{
    extern struct utmp *getutnam ();
    static char tty_num[14] = "/dev/";
				  /* number of tty receiver is on       */
				  /* name is filled in from getutnam    */
    struct utmp *utptr;

    if ((utptr = getutnam (usrname)) == 0)
    {
	errno = ENOENT;           /* not logged in                      */
	return (NOTOK);
    }
    blt (utptr -> ut_line, &tty_num[5], 8);

    return (tty_open (tty_num));
}
/**/

LOCFUN
	tty_open (tty_num)        /* open the named tty                 */
char tty_num[];
{
    struct stat statbuf;
    int tty_fd;

    if (setjmp (timerest))   /* signal, for timerest, must be      */
    {                             /*    set by user program             */
	errno = EBUSY;            /* allow a later try                  */
	return (NOTOK);           /* timeout during tty open            */
    }

#ifndef SECURETTY
    if (stat (tty_num, &statbuf) < OK)
	return (NOTOK);

    if (!(statbuf.st_mode & (S_IWRITE >> 6)))
#else
    if (access (tty_num, 01) != 0)
#endif
    {                             /* check write(I) permissions      */
	errno = EBUSY;
	return (NOTOK);
    }

    s_alarm (15);
    tty_fd = open (tty_num, 1);
    s_alarm (0);

    if (tty_fd < 0)
	return (NOTOK);

    tty_fp = fdopen (tty_fd, "w");
    return (OK);
}


tty_close ()			  /* done writing to tty                */
{
    if (ferror (tty_fp) || fclose (tty_fp) == EOF)
	return (NOTOK);
    return (OK);
}
 units of 25 blocks (1 kbyte/block)   */
    numblocks = ll_size (loginfo);

    if ((stats = loginfo -> ll_stat) & _LLDIDC)
    {                             /* different test if already cycled     */
	if (numblocks > maxblocks + 1)
	    goto endit;           /* the sucker REwrote length of file    */
    }
    else
	if (numblocks >= maxblocks)
	{                         /* normal uncycled mmdf/lib/util/lk_lock.c   444      0     12       17566  3671073211  10315 #include "util.h"
#include <sys/stat.h>

/*
 *	Standardized file-locking package  (using links)
 *
 *  This version presumes no o/s support of locking, with
 *  link(2) being the only available atomic o/s operation.
 */

extern int errno;               /* simulate system error problems */
#ifdef DEBUG
#include "ll_log.h"
extern LLog *logptr;
#endif

extern	char *lckdfldir;

LOCVAR	int breaktime = 120;   /* amount to sleep after breaking lock */
LOCVAR	char *NIL = "NIL";

/**/

LOCFUN
lk_lock (file, lockdir, lockfile, maxtime)
char	*file;			/* file to be locked */
char	*lockdir;		/* directory to put parallel file into */
char	*lockfile;		/* file to lock against */
int	maxtime;		/* maybe break lock after it is this old */
{
    int sleepval;
    int retval;
    int tries;
    char lkname[100];           /* full name of locking file */
    char tmpname[100];          /* name of tmp file to link from */

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "lk_lock (%s,%s,%s,%d)", file,
	    lockdir ? lockdir : NIL, lockfile ? lockfile : NIL, maxtime);
#endif

    if (lk_name (lkname, file, lockdir, lockfile) < OK)
	return (NOTOK);

#ifdef DEBUG
    ll_log (logptr, LLOGFTR, "lkname '%s'", lkname);
#endif

    for (tries = 0; tries < 2; tries++)
	switch (lk_test (file, lkname, maxtime))
	{
	    case OK:                /* lock exists & is ok */
#ifdef DEBUG
		ll_log (logptr, LLOGFTR, "already locked & ok");
#endif
		errno = ETXTBSY;
		return (NOTOK);

	    case NOTOK:             /* lock exists and is old */
#ifdef DEBUG
		ll_log (logptr, LLOGFTR, "old lock");
#endif
		if (lk_unlock (file, lockdir, lockfile) == NOTOK)
		    return (NOTOK); /* hmmmmmm */

/*
 *  the amount of time to sleep is at least breaktime.
 *  the random number generation is used to try to separate
 *  two processes that may be close to syncrony in checking the lock.
 */
		srand (getpid ());
		sleepval = breaktime + (rand ()%100);
#ifdef DEBUG
		ll_log (logptr, LLOGFTR, "sleeping (%d)", sleepval);
#endif
		sleep (sleepval);
		continue;

	    default:
		goto lockit;
	}

/* below here, try to lock the guy */

lockit:
    getfpath (",tmp.XXXXXX", lockdir ? lockdir : lckdfldir, tmpname);
    mktemp (tmpname);           /* have to have something to link from */

    if (close (creat (tmpname, 0666)) < OK)
    {
#ifdef DEBUG
	ll_err (logptr, LLOGFTR, "problem w/lock base file '%s'", tmpname);
#endif
	(void) unlink (tmpname);
	return (NOTOK);
    }

    retval = link (tmpname, lkname);

#ifdef DEBUG
    if (retval < 0)
	ll_err (logptr, LLOGFTR,
		    "problem linking '%s' to '%s'", tmpname, lkname);
    else
	ll_log (logptr, LLOGFTR,
		    "linked '%s' to '%s'", tmpname, lkname);
#endif

    (void) unlink (tmpname);

    return (retval);
}
/**/

LOCFUN
lk_unlock (file, lockdir, lockfile)
char	*file;		/* file to be locked */
char	*lockdir;	/* directory to put parallel file into */
char	*lockfile;	/* file to lock against */
{
    int retval;
    char lkname[100];           /* full name of locking file */

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "lk_unlock (%s,%s,%s)", file,
	    lockdir ? lockdir : NIL, lockfile ? lockfile : NIL);
#endif

    if (lk_name (lkname, file, lockdir, lockfile) < OK)
	return (NOTOK);

    retval = unlink (lkname);

#ifdef DEBUG
    if (retval < 0)
	ll_err (logptr, LLOGFTR, "problem unlinking '%s'", lkname);
#endif

    return (retval);
}

/**/

LOCFUN
	lk_name (lkname, file, lockdir, lockfile)
char	*lkname;		/* put full name of file to use as lock */
char	*file;			/* resource to be locked */
char	*lockdir;		/* dir containing locking file, if any */
char	*lockfile;		/* file to use ask lock */
{
    struct stat statbuf;

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "lk_name (%s,%s,%s)", file,
	    lockdir ? lockdir : NIL, lockfile ? lockfile : NIL);
#endif

    if (lockdir == (char *) 0)
	lockdir = lckdfldir;

    if (lockfile && *lockfile )
	getfpath (lockfile, lockdir, lkname);
    else {                      /* make out own file name */
	if (file == (char *) 0) /* no way to create a fixed name */
	{
	    errno = EINVAL;
	    return (NOTOK);
	}
	else
	{
	    if (stat (file, &statbuf) < OK)
		return (NOTOK);
	    sprintf (lkname, "%s/,LK%05.5o%o",
			lockdir, statbuf.st_dev, statbuf.st_ino);
	}
    }

    return (OK);
}
/**/

LOCFUN
	lk_test (file, lkfile, maxtime)
char	*file;
char	*lkfile;
int	maxtime;
{
    time_t curtime;
    struct stat statbuf;

    time (&curtime);

    if (stat (lkfile, &statbuf) < OK)
    {
#ifdef DEBUG
	ll_err (logptr, LLOGBTR, "lk_test couldn't stat lockfile '%s'", lkfile);
#endif
	return (DONE);          /* assume that it does not exist */
    }
    if (maxtime <= 0)
	return (OK);            /* no time limit */

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "lockfile (%ld:%ld) minutes",
		(((curtime - statbuf.st_mtime)+59L)/60L), (long) (maxtime));
#endif
    if ((((curtime - statbuf.st_mtime)+59L)/60L) > (long) (maxtime))
    {                           /* well, the lock is too old */
	if (file != (char *) 0) /* is the resource inactive, also? */
	    if (stat (file, &statbuf) >= OK)
	    {
#ifdef DEBUG
		ll_log (logptr, LLOGBTR, "resource access (%ld:%ld) minutes",
		    (((curtime - statbuf.st_mtime)+59L)/60L), (long) (maxtime));
#endif
		if ((((curtime - statbuf.st_atime)+59L)/60L) <= (long) (maxtime))
		    return (OK); /* let them go */
	    }
	return (NOTOK);
    }

    return (OK);        /* lock still has time */
}
/**/

lk_open (file, access, lockdir, lockfile, maxtime)
char	*file;			/* file to be locked */
int	access;                 /* read-write permissions */
char	*lockdir;		/* directory to put parallel file into */
char	*lockfile;		/* file to lock against */
int	maxtime;		/* maybe break lock after it is this old */
{
    register int fd;

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "lk_open (%s,%d,%s,%s,%d)", file, access,
	    lockdir ? lockdir : NIL, lockfile ? lockfile : NIL, maxtime);
#endif

    if (lk_lock (file, lockdir, lockfile, maxtime) < OK)
    {
#ifdef DEBUG
	ll_err (logptr, LLOGBTR, "open lock notok");
#endif
	return (NOTOK);
    }

    if ((fd = open (file, access)) < 0)
    {
#ifdef DEBUG
	ll_err (logptr, LLOGBTR, "open notok");
#endif
	lk_unlock (file, lockdir, lockfile);
	return (NOTOK);
    }
    return (fd);
}

lk_close (fd, file, lockdir, lockfile)
int fd;
char	*file;			/* file to be locked */
char	*lockdir;		/* directory to put parallel file into */
char	*lockfile;		/* file to lock against */
{
    register int retval;

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "lk_close (%d,%s,%s,%s)", fd, file,
	    lockdir ? lockdir : NIL, lockfile ? lockfile : NIL);
#endif
    if (fd < 0)
	return (OK);
    retval = close (fd);
    lk_unlock (file, lockdir, lockfile);
    return (retval);
}
/**/
FILE *
	lk_fopen (file, access, lockdir, lockfile, maxtime)
char	*file;			/* file to be locked */
char	*access;		/* read-write permissions */
char	*lockdir;		/* directory to put parallel file into */
char	*lockfile;		/* file to lock against */
int	maxtime;		/* maybe break lock after it is this old */
{
    register FILE *fp;

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "lk_fopen (%s,%s,%s,%s)", file, access,
	    lockdir ? lockdir : NIL, lockfile ? lockfile : NIL);
#endif

    if (lk_lock (file, lockdir, lockfile, maxtime)  < OK)
    {
#ifdef DEBUG
	ll_err (logptr, LLOGBTR, "fopen lock notok");
#endif
	return ((FILE *) NULL);
    }

    if ((fp = fopen (file, access)) == NULL)
    {
#ifdef DEBUG
	ll_err (logptr, LLOGBTR, "fopen notok");
#endif
	lk_unlock (file, lockdir, lockfile);
	return ((FILE *) NULL);
    }
    return (fp);
}


lk_fclose (fp, file, lockdir, lockfile)
FILE	*fp;
char	*file;			/* file to be locked */
char	*lockdir;		/* directory to put parallel file into */
char	*lockfile;		/* file to lock against */
{
#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "lk_fclose (%s,%s,%s)", file,
	    lockdir ? lockdir : NIL, lockfile ? lockfile : NIL);
#endif
    lk_unlock (file, lockdir, lockfile);
    switch ((int)fp) {
	case EOF:
	case NULL:
	    return (OK);
    }
    return (fclose (fp));
}
* make out own file name */
	if (file == (char *) 0) /* no way to create a fixed name */
	{
	    errno = EINVAL;
	    return (NOTOK);
	}
	mmdf/lib/util/getfpath.c   444      0     12        1011  3671073212  10434 #include "util.h"

getfpath (fnam, dflpth, dest)   /* build full pathname              */
    char fnam[],
	 dflpth[];
    char *dest;
{
    if (fnam[0] == '/')       /* fully specified                      */
	(void) strcpy (dest, fnam);
    else
	sprintf (dest, "%s/%s", dflpth, fnam);
}


char *
	dupfpath (fnam, dflpth) /* build full pathname & alloc str  */
    char fnam[],
	 dflpth[];
{
    extern char *strdup ();
    char temppath[128];

    getfpath (fnam, dflpth, temppath);

    return (strdup (temppath));
}
t exist */
    }
    if (maxtime <= 0)
	return (OK);            /* no time limit */

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "lockfile (%ld:%ld) minutes",
		(((curtime - statbuf.st_mtime)+59L)/60L), (long) (maxtime));
#endif
    if ((((curtime - statbuf.st_mtime)+59L)/60L) > (long) (maxtime))
    {                           /* well, the lock is too old */
	if (file != (char *) 0) /* is the resource inactive, also? */
	    if (stat (file, &statbuf) >= OK)
	    {
#ifdef DEBUG
		ll_log (logptr, LLOGmmdf/lib/util/creatdir.c   444      0     12        3714  3671073213  10444 #include "util.h"
#include <sys/stat.h>

/*              create a directory
 *
 *      build intervening directories, if nonexistant
 *      chown new directories to specified uid
 *
 *      some of the system calls are not checked because they can
 *      fail benignly, as when an intermediate directory is executable,
 *      but not readable or writeable.  the only test for ultimate
 *      success is being able to create and stat the final directory.
 */

creatdir (dirptr, mode, owner, group)
    register char *dirptr;     /* pathname to directory                */
    int mode;
    int owner,                  /* non-zero uid of for dir owner        */
	group;
{
    extern int errno;
    struct stat statbuf;
    int realid,
	effecid;
    int uid,
	gid;
    char shcmd[128];
    register char *partpath;
    register char *nptr;       /* last char in partial pathname        */

    if (dirptr == (char *) 0 || isnull (*dirptr))
	 return (NOTOK);      /* programming error                      */

    if (owner != 0)             /* coerced ownship requested            */
    {                           /* noop it, if under requested id's     */
	getwho (&realid, &effecid);
	uid = realid;
	getgroup (&realid, &effecid);
	gid = realid;
	if( uid && (uid != owner || gid != group ))
	    return( NOTOK );
    }

    (void) strcpy (shcmd, "mkdir ");   /* initialize string with command */
    partpath = &shcmd[strlen (shcmd)];

    for (nptr = partpath, *nptr++ = *dirptr++; ; *nptr++ = *dirptr++)
	switch (*dirptr)
	{
	    case '\0':
	    case '/':
		*nptr = '\0';
		if (stat (partpath, &statbuf) < 0)
		{               /* should we try to creat it?           */
#ifndef V4_2BSD
		    system (shcmd);
				/* don't check if it succeeded          */
#else V4_2BSD
		    mkdir (partpath);
#endif V4_2BSD
		    if (owner != 0)
			chown (partpath, owner, group);

		    chmod (partpath, mode);
		}
		if (isnull (*dirptr))
		    return ((stat (partpath, &statbuf) < 0) ? NOTOK : OK);
	}
}
(file, access)) < 0)
    {
#ifdef DEBUG
	ll_err (logmmdf/lib/util/gwdir.4.2.c   444      0     12         307  3620510423  10231 #include "util.h"

char *
	gwdir ()                    /* what is current working directory? */
{
	static char path[1024];

	if (getwd (path) == 0)
		return ((char *)NOTOK);
	else
		return (path);
}
the system calls are not checked because they can
 *      fail benignly, as when an intermediate directory is executable,
 *      but not readable or writeable.  the only test for ultimate
 *      success is being able to create and stat the final directory.
 */

creatdir (dirptr, mode, owner, group)
    registemmdf/lib/util/gpwlnam.c   444      0     12        1177  3620510424  10307 #include "util.h"
#include <pwd.h>

/* modified getpwnam(), to do lexical (case-independent) comparison */

struct passwd *
gpwlnam(name)
char *name;
{
    struct passwd *getpwent();
    char lownam[30];
    register struct passwd *p;
    register int ind;

    for (ind = 0; ind < ((sizeof lownam) - 1) && !isnull (name[ind]); ind++)
	lownam[ind] = uptolow (name[ind]);
    lownam[ind] = '\0';

    for (setpwent(); (p = getpwent()) != NULL; )
    {
	for (ind = 0; !isnull (name[ind]); ind++)
	    p -> pw_name[ind] = uptolow (p -> pw_name[ind]);

	if (strcmp (lownam, p->pw_name) == 0)
	    break;
    }
    endpwent();
    return(p);
}
 for dir owner        */
	group;
{
    extern int errno;
    struct stat statbuf;
    int realid,
	effecid;
    int uid,
	gid;
    char shcmd[128];
    register char *partpath;
    register char *nptr;       /* last char in partial pathname        */

    if (dirptr == (char *) 0 || isnull (*dirptr))
	 return (NOTOK);      /* programming error                      */

    if (owner mmdf/lib/util/strindex.c   444      0     12         515  3620510424  10455 strindex (str, target)           /* return column str starts in target */
register char   *str,
		*target;
{
    char *otarget;
    register short slen;

    for (otarget = target, slen = strlen (str); ; target++)
    {
	if (*target == '\0')
	    return (-1);

	if (equal (str, target, slen))
	    return (target - otarget);
    }
}
)
	lownam[ind] = uptolow (name[ind]);
    lownam[ind] = '\0';

    for (setpwent(); (p = getpwent()) != NULL; )
    {
	for (ind = 0; !isnull (name[ind]); ind++)
	    p -> pw_name[mmdf/lib/util/getgroup.c   444      0     12         242  3620510424  10446 getgroup (realid, effecid)         /* group info for V7 Unix             */
    int   *realid,
	*effecid;
{
    *realid = getgid ();

    *effecid = getegid();
}
(otarget = target, slen = strlen (str); ; target++)
    {
	if (*target == '\0')
	    return (-1);

	if (equal (str, target, slen))
	    return (target - otarget);
    }
}
)
	lownam[ind] = uptolow (name[ind]);
    lownam[ind] = '\0';

    for (setpwent(); (p = getpwent()) != NULL; )
    {
	for (ind = 0; !isnull (name[ind]); ind++)
	    p -> pw_name[mmdf/lib/util/expand.c   444      0     12        2267  3620510424  10122 /*
 *			E X P A N D . C
 *
 *	Expand expands a text string expanding macros provided
 *  in a vector of key and value pairs.
 *
 *  e.g.	expandl(buf, "$A+$B=$(RESULT)", array)
 *  with	char *array[] = {"A", "1",
 *				 "B", "2",
 *				 "RESULT", "3",
 *				 0, 0};
 *  gives	"1+2=3" in buf.
 *
 *  wja@uk.ac.nott.maths		Wed Feb 22 15:00:26 GMT 1984
 *  DPK@BRL, 13 Apr 84	Made more portable and changed array usage.
 */

#include "util.h"

LOCFUN  char *exname(), *exlookup();

char *
expand(buf, fmt, argp)
char *buf, *fmt, **argp;
{
	register char *bp = buf, *cp;
	char name[64];

	while(*fmt) {
		if(*fmt != '$')
			*bp++ = *fmt++;
		else if(*++fmt == '$')
			*bp++ = *fmt++;
		else {
			fmt = exname(name, fmt);
			cp = exlookup(name, argp);
			while(*cp)
				*bp++ = *cp++;
		}
	}
	*bp = '\0';
	return buf;
}

LOCFUN char *
exname(buf, str)
register char *buf, *str;
{
	if(*str == '(') {
		str++;
		while(*str != ')' && *str != '\0')
			*buf++ = *str++;
		if(*str != '\0')
			str++;
	}
	else
		*buf++ = *str++;
	*buf = '\0';
	return str;
}

LOCFUN
char *
exlookup(name, list)
register char *name, **list;
{
	while(*list)
	{
		if(lexequ(name, *(list++)))
			return (*list);
		list++;
	}
	return ("");
}
tgroup (&realid, &effecid);
	gid = realid;
	if( uid && (uid != owner || gid != group ))
	    return( NOTOK );
    }

    (void) strcpy (shcmd, "mkdir ");   /* initialize string with command */
    partpath = &shcmd[strlen (shcmd)];

    for (nptr = partpath, *nptr++ = *dirptr++; ; *nptr++ = *dirptr++)
	switch (*dirptr)
	{
	    mmdf/lib/util/lk_lock.rand.c   444      0     12        4753  3671073214  11215 #include "util.h"

/*
 *	Standardized file-locking package  (RAND exclusive open)
 *
 *  This version assumes the existence of the RAND exclusive open
 *  in the operating system.  (DPK @ BRL, 21 Dec 82)
 */
#define	FEXCLUSV	04	/* OR this in to get an exclusive open */

extern int errno;               /* simulate system error problems */

#ifdef DEBUG
#include "ll_log.h"
extern struct ll_struct *logptr;
#endif

/**/

lk_open (file, access, lockdir, lockfile, maxtime)
char	*file;			/* file to be locked */
int	access;			/* read-write permissions */
char	*lockdir;		/* --Ignored-- */
char	*lockfile;		/* --Ignored-- */
int	maxtime;		/* maybe break lock after it is this old */
{
    register int fd;

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "lk_open (%s,%d,%s,%s,%d)",
		file, access, lockdir, lockfile, maxtime);
#endif

    if ((fd = open (file, FEXCLUSV | access)) < 0)
    {
#ifdef DEBUG
	ll_err (logptr, LLOGBTR, "open notok (err %d)", errno);
#endif
	return (NOTOK);
    }
    return (fd);
}

lk_close (fd, file, lockdir, lockfile)
int	fd;
char	*file;			/* file to be locked */
char	*lockdir;		/* directory to put parallel file into */
char	*lockfile;		/* file to lock against */
{
    register int retval;

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "lk_close (%d,%s,%s,%s)",
		fd, file, lockdir, lockfile);
#endif

    if (fd < 0)
	return (OK);
    retval = close (fd);
    return (retval);
}
/**/

FILE *
	lk_fopen (file, access, lockdir, lockfile, maxtime)
char	*file;			/* file to be locked */
char	*access;		/* read-write permissions */
char	*lockdir;		/* --Ignored-- */
char	*lockfile;		/* --Ignored-- */
int	maxtime;		/* maybe break lock after it is this old */
{
    register int fd;
    register FILE *fp;

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "lk_fopen (%s,%s,%s,%s,%d)",
		file, access, lockdir, lockfile, maxtime);
#endif

    if ((fd = open(file,
	  FEXCLUSV | (access[0]=='r' && access[1]==0 ? 0 : 2))) < 0) {
#ifdef DEBUG
	ll_err (logptr, LLOGBTR, "open notok");
#endif
	return( NULL );
    }

    if ((fp = fdopen (fd, access)) == NULL)
    {
	(void) close( fd );
	return ((FILE *) NULL);
    }
    return (fp);
}

lk_fclose (fp, file, lockdir, lockfile)
FILE	*fp;
char	*file;			/* --Ignored-- */
char	*lockdir;		/* --Ignored-- */
char	*lockfile;		/* --Ignored-- */
{
#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "lk_fclose (%s,%s,%s)", file,
	    (lockdir ? lockdir : ""), (lockfile ? lockfile : ""));
#endif

    switch ((int)fp) {
	case EOF:
	case NULL:
	    return (OK);
    }
    return(fclose (fp));
}
ch (*dirptr)
	{
	    mmdf/lib/util/ggrlnam.c   444      0     12        1175  3620510425  10270 #include "util.h"
#include <grp.h>

/* modified getgrnam(), to do lexical (case-independent) comparison */

struct group  *
ggrlnam(name)
char *name;
{
    struct group *getgrent();
    char lownam[30];
    register struct group *g;
    register int ind;

    for (ind = 0; ind < ((sizeof lownam) - 1) && !isnull (name[ind]); ind++)
	lownam[ind] = uptolow (name[ind]);
    lownam[ind] = '\0';

    for (setpwent(); (g = getgrent()) != NULL; )
    {
	for (ind = 0; !isnull (name[ind]); ind++)
	    g -> gr_name[ind] = uptolow (g -> gr_name[ind]);

	if (strcmp (lownam, g->gr_name) == 0)
	    break;
    }
    endpwent();
    return(g);
}
er it is this old */
{
    register int fd;
    register FILE *fp;

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "lk_fopen (%s,%s,%s,%s,%d)",
		file, access, lockdir, lockfile, maxtime);
#endif

    if ((fd = open(file,
	  FEXCLUSV | (access[0]=='r' && access[1]==0 ? 0 : 2))) < 0) {
#ifdef DEBUG
	ll_err (logptr, LLOGBTR, "open notok");
#endif
	return( NULL );
    }

    if ((fp = fdopen mmdf/lib/util/str2arg.c   444      0     12       11135  3620510425  10242 
#include "util.h"

/*  convert string into argument list
 *
 *  stash a pointer to each field into the passed array.
 *  any common seperators split the words.  extra white-space
 *  between fields is ignored.
 *
 *  if the separator is '=', then the current argument position in
 *  the array points to "=", the next the one is the key and the
 *  value follows it.  This permits detecting variable assignment,
 *  in addition to positional arguments.
 *      i.e.,  key=value ->  = key value
 *
 *  specially-interpreted characters:
 *      space, tab, double-quote, backslash, comma, equal, slash, period,
 *      semi-colon, colon, carriage return, and line-feed (newline).
 *      preceding a special char with a backslash removes its
 *      interpretation.  a backslash not followed by a special is used
 *      to preface an octal specification for one character
 *
 *      a string begun with double-quote has only double-quote and
 *      backslash as special characters.
 *
 *  a field which begins with semi-colon or a hash (#) is interpreted
 *  as marking the rest of the line as a comment and it is skipped, as
 *  are blank lines
 */

extern int errno;

str2arg (srcptr, max, argv, dlm)/* convert srcptr to argument list */
	register char *srcptr;  /* source data */
	int max;                /* maximum number of permitted fields */
	char *argv[];           /* where to put the pointers */
	char dlm[];             /* list of delimiting characters */
{
    char gotquote;      /* currently parsing quoted string */
    char lastdlm;       /* last delimeter character     */
    register int ind;
    register char *destptr;

    if (srcptr == 0)
    {
	errno = EINVAL;     /* emulate system-call failure */
	return (NOTOK);
    }

    for (lastdlm = (char) NOTOK, ind = 0, max -= 2; *srcptr != '\0'; ind++)
    {
	if (ind >= max)
	{
	    errno = E2BIG;      /* emulate system-call failure */
	    return (NOTOK);
	}
	for (argv[ind] = destptr = srcptr; isspace (*srcptr); srcptr++)
		;               /* skip leading white space         */
/**/

	for (gotquote = FALSE; ; )
	{
	    switch (*srcptr)
	    {
		default:        /* just copy it                     */
		    *destptr++ = *srcptr++;
		    break;

		case '\"':      /* beginning or end of string       */
		    gotquote = (gotquote) ? FALSE : TRUE;
		    srcptr++;   /* just toggle */
		    break;

		case '\\':      /* quote next character             */
		    srcptr++;   /* just skip the back-slash         */
		    switch (*srcptr)
		    {           /* convert octal values             */
			case 'r':
			    *destptr++ = '\r';
			    srcptr++;
			    break;

			case 'n':
			    *destptr++ = '\n';
			    srcptr++;
			    break;

			case 'b':
			    *destptr++ = '\b';
			    srcptr++;
			    break;

			case 'f':
			    *destptr++ = '\f';
			    srcptr++;
			    break;

			default:
			    if (*srcptr >= '0' && *srcptr <= '7')
			    {
				*destptr = '\0';
				do
				    *destptr = (*destptr << 3) | (*srcptr++ - '0');
				while (*srcptr >= '0' && *srcptr <= '7');
				destptr++;
				break;
			    }    /* otherwise DROP ON THROUGH */
			case '\\':
			case '\"':
			case ' ':
			case ',':
			case '\t':
			case ';':
			case '#':
			case ':':
			case '=':
			case '/':
			case '|':
			    *destptr++ = *srcptr++;
			    break; /* just copy it */
		    }       /* DROP ON THROUGH */
		    break;

		case '=':   /* make '=' prefix to pair  */
		    if (gotquote)
		    {
			*destptr++ = *srcptr++;
			break;
		    }

		    ind++;      /* put value ahead of '=' */

		    if ( dlm != (char *) 0)
		    {
			dlm[ind - 1] =
			    dlm[ind] = '=';
		    }
		    argv[ind] = argv[ind - 1];
		    lastdlm = '=';
		    argv[ind - 1] = "=";
		    *destptr = '\0';
		    srcptr++;
		    goto nextarg;

		case ' ':
		case '\t':
		case '\n':
		case '\r':
		case ',':
		case ';':
		case '#':
		case ':':
		case '/':
		case '|':
		    if (gotquote)
		    {           /* don't interpret the char */
			*destptr++ = *srcptr++;
			break;
		    }

		    if (isspace (lastdlm))
		    {
			if (isspace (*srcptr))
			{           /* shouldn't be possible */
			    errno = EINVAL;
			    return (NOTOK);
			}
			else
			    if (*srcptr != ';' && *srcptr != '#')
				ind--;  /* "xxx , yyy" is only 2 fields */
		    }
		    lastdlm = *srcptr;
		    if ( dlm != (char *) 0)
			dlm[ind] = *srcptr;
		    srcptr++;
		case '\0':
		    *destptr = '\0';
		    goto nextarg;
	    }

	    lastdlm = (char) NOTOK;     /* disable when field started */
	}

    nextarg:
	if (argv[ind][0] == '\0' && (lastdlm == ';' || lastdlm == '#'))
	    break;      /* rest of line is comment                  */
    }

    argv[ind] = (char *) 0;
    return (ind);
}
iod,
 *      semi-colon, colon, carriage return, and line-feed (newline).
 *      preceding a special char with a backslash removes its
 *      interpretation.  a backslash not followed by a special is used
 *      to preface an octal specification for one character
 *
 *      a string begun with double-quote has only double-quote and
 *      backslash as special characters.
 *
 *  a field which begins with semi-colmmdf/lib/util/gettys.c   444      0     12        5543  3620510425  10163 #
/*  A set of routines in the style of getpw() to read the ttys
 *  file and return the tty info.
 *
 *  Steve Manion
 *
*/

#include	<stdio.h>
#include        "gettys.h"

#define		ETCTTYS		"/etc/ttys"

/*  the channel to read the data from  */
static FILE *input = NULL;

/*  the line number last read from the file  */
static int linenum = 0;

getttyent(userdata)
  struct ttys *userdata;
    {
    char buff[128];

    /*  if the input file is not yet open, do it  */
    if (input == NULL)
	if (openinput(ETCTTYS) < 0)
	    return(BADDATA);

    /*  read the next line from the input  */
    if (getline(buff) == EOF)
	{
	rewind(input);
	linenum = 0;
	return(EOF);
    }

    /*  transfer the data into the user struct  */
    if (process(buff, userdata) < 0)
	return(BADDATA);

    /*  return the line number we are on  */
    return(linenum);
}





openinput(file)
  char *file;
    {

    input = fopen(file, "r");
    if (input == NULL)
	{
	printf("File '%s': ", file);
	fflush(stdout);
	perror("");
	fflush(stderr);
	return(-1);
    }

    return(0);
}





static int getline(buff)
  char *buff;
    {
    register int c;

    for(;;)
	{
	c = getc(input);

	/*  look for the true end of the file  */
	if( (c == EOF) && (feof(input) != 0)   )
	{
	    *buff = '\0';
	    return(EOF);
	}

	/*  transfer character to the buffer and check for the
	 *  end of the line.
	 */
	*buff++ = c;
	if (c == '\n')
	{
	    *buff = '\0';
	    linenum++;
	    return(0);
	}
    }
}





static int process(buff, userdata)
  char *buff;
  struct ttys *userdata;
    {
    register char *cp;

    /*  check the first char of the line.  It should be 0 or 1 as
     *  the line is invalid or valid.
     */
    switch(*buff++)
	{
	case '0':
	    userdata->t_valid = 0;
	    break;

	case '1':
	    userdata->t_valid = 1;
	    break;

	default:
	    fflush(stdout);
	    fprintf(stderr, "Invalid tty file - notify system manager.\n");
	    fflush(stderr);
	    return(-1);
    }

    /*  transfer the control code directly  */
    userdata->t_code = *buff++;

    /*  transfer the tty name to the buffer  */
    for(cp = userdata->t_name;  *buff != '\n';  )
	*cp++ = *buff++;
    *cp = '\0';

    return(0);
}




getttynam(userdata, name)
  struct ttys *userdata;
  char *name;
    {
    register int curline, result;

    /*  the strategy of this routine is to cycle through the
     *  tty file, starting from the current position, until
     *  the tty 'name' is located.  Note that the getttyent()
     *  routine will automaticly rewind the input when appropriate.
     *  Note also that the call to it ultimately updates the
     *  variable 'linenum'.
     */

    curline = linenum;
    do
	{
	result = getttyent(userdata);
	if(result == EOF)
	    continue;
	if(result == BADDATA)
	    break;
	if(strcmp(name, userdata->t_name) == 0)
	    return(result);
    }
    while (curline != linenum);

    return(BADDATA);
}
  if (gotquote)
		    {
			*destptr++ = *srcptr++;
			break;
		    }

		    ind++;      /* put value ahead of '=' */

		    if ( dlm != (char *) 0)
		    {
	mmdf/lib/util/arg2str.c   444      0     12        6163  3620510425  10227 #include "util.h"

/* convert an array of strings to one or more lines of 'arguments'.
 *
 * this is intended to be used along with the str2arg() routine.
 */

/*VARARGS*/
#ifdef NO_VARARGS
arg2lstr (linelen, maxlen, buf, arg1, a,b,c,d,e,f,g,h,i,j,k,l,m)
#else
arg2lstr (linelen, maxlen, buf, arg1) /* convert list to argument array */
#endif NO_VARARGS
    int linelen;        /* when to insert newlines; 0=> don't       */
    int maxlen;
    char *buf;
    char *arg1;		/* merely the first of the list		    */
{
    arg2vstr (linelen, maxlen, buf, &arg1);
}

arg2vstr (linelen, maxlen, buf, argv) /* convert the list to a string            */
    int linelen;
    int maxlen;	/* length of string			*/
    char *buf;	/* where to put the output string	*/
    char **argv;	/* the argument vector			*/
{
    unsigned totlen,    /* total length of current line         */
	     len;       /* length of current argument           */
    unsigned gotdelim;       /* a delimiter char is in arg           */
    unsigned gotpair;        /* key/value pair                       */
    char tmpstr[256];	/* LINESIZE = 256, string under construction  */
    register char *src,
		  *dest;

    for (totlen = gotpair = 0, buf[0] = '\0';
		*argv != (char *) 0; argv++)
    {
	if (gotpair  == 0 && strcmp ("=", *argv) == 0)
	{
	    dest = tmpstr;
	    gotpair = 2;     /* take the next two arguments  */
	    continue;
	}

	for (src = *argv, gotdelim = FALSE; *src != '\0'; src++)
	    switch (*src)
	    {
		case ' ':
		case '\t':
		case '=':
		case ',':
		case ';':
		case ':':
		case '/':
		case '|':
		case '.':
		    gotdelim = TRUE;
		    goto nextone;
	    }

    nextone:
	if (gotpair == 0)
	    dest = tmpstr;
	if (gotdelim)
	    *dest++ = '"';
	for (src = *argv; *src != '\0'; src++)
	{
	    switch (*src)
	    {
		case '\b':
		    *dest++ = '\\';
		    *dest++ = 'b';
		    break;

		case '\t':
		    *dest++ = '\\';
		    *dest++ = 't';
		    break;

		case '\f':
		    *dest++ = '\\';
		    *dest++ = 'f';
		    break;

		case '\r':
		    *dest++ = '\\';
		    *dest++ = 'r';
		    break;

		case '\n':
		    *dest++ = '\\';
		    *dest++ = 'n';
		    break;

		case '\\':		/*  Added by Doug Kingston  */
		case '\"':
		    *dest++ = '\\';
		    *dest++ = *src;
		    break;

		default:
		    if (iscntrl (*src))
		    {
			*dest++ = '\\';
			*dest++ = ((*src >> 6) & 07) + '0';
			*dest++ = ((*src >> 3) & 07) + '0';
			*dest++ =  (*src & 07) + '0';
		    }
		    else
			*dest++ = *src;
	    }
	}

	if (gotdelim)
	    *dest++ = '"';

	if (src == *argv)       /* null-valued argument data            */
	{
	    if (totlen != 0)    /* beyond the beginning, so double it   */
		strcat (buf, ",");
	    else
		strcat (buf, " ");
	    *dest++ = ',';
	}

	switch (gotpair)        /* handle key/value differently         */
	{
	    case 2:
		*dest++ = '=';
		gotpair--;
		continue;

	    case 1:
		gotpair = 0;
	}

	*dest = '\0';
	len = dest - tmpstr;

	if (totlen != 0)
	{
	    if ((linelen > 0) && ((totlen + len) > linelen))
	    {
		strcat (buf, "\n\t");
		totlen = 8;
	    }	
	    else
	    {
		strcat (buf, " ");
		totlen += 1;
	    }
	}
	strcat (buf, tmpstr);
	totlen += len;
    }
}
d - 1] = "=";
		    *destptr = '\0';
		    srcptr++;
		    goto nextarg;

		case ' ':
		case '\t':
		case '\n':
		case '\r':
		case ',':
		case ';':
		case '#':
		case ':':
		case '/':
		case '|':
		    if (gotquote)
		    {           /* don't interpret the char */
			*destptr++ = *srcptr++;
			break;
		    }

		    if (isspace (lastdlm))
		    {
			if (isspace (*srcptr))
			{           /* shoummdf/lib/util/prefix.c   444      0     12         430  3620510425  10107 #include "util.h"

/* lexical check if prefstr is at start of target */

prefix (prefstr, target)
register char  *target,
               *prefstr;
{
    for (; *prefstr; target++, prefstr++)
	if (uptolow (*target) != uptolow (*prefstr))
	    return (FALSE);

    return (TRUE);
}
maxlen, buf, arg1) /* convert list to argument array */
#endif NO_VARARGS
    int linelen;        /* when to insert newlines; 0=> don't       */
    int maxlen;
    char *buf;
    char *arg1;		/* merely the first of the list		    */mmdf/lib/util/tai_packages.c   444      0     12       12736  3620510426  11302 #include "util.h"
#include "ll_log.h"
#include "cmd.h"

extern int errno;               /* put error code into here, like sys call */
extern char *tai_eptr;          /* pointer to error text  */

/*      tailor other packages   */

tai_pgm (argc, argv, nam, path) /* get name&path of a program */
    int argc;                   /* number of values     */
    char *argv[];               /* list of values       */
    char **nam,                 /* where to put the name of the program */
	 **path;                /* where to put path to program */
{
    register int ind;

    for (ind = 0; ind < argc; ind++)
    {
	if (lexequ ("=", argv[ind]))
	{                       /* key/value pair       */
	    if ((ind += 2) >= argc)
	    {
		errno = EFAULT;
		tai_eptr = argv[ind - 1];       /* point to 'key' */
		argv[ind + 1] = 0;
		return (NOTOK);
	    }
	    if (lexequ ("path", argv[ind - 1]))
		*path = argv[ind];
	    else
	    if (lexequ ("name", argv[ind - 1]))
		*nam = argv[ind];
	    else
	    {
		errno = EINVAL;
		tai_eptr = argv[ind - 1];       /* point to 'key' */
		argv[ind + 1] = 0;
		return (NOTOK);
	    }
	}
	else
	{                               /* same string for both purposes */
	    *path = argv[ind];
	    *nam = argv[ind];
	}
    }
    return (YES);
}
/**/

#define CMDLHDR     1
#define CMDLLEVEL   2
#define CMDLSIZE    3
#define CMDLSTAT    4

LOCVAR Cmd
	    cmdlog[] =
{
    "hdr",      CMDLHDR,    1,
    "level",    CMDLLEVEL,  1,
    "size",     CMDLSIZE,   1,
    "stat",     CMDLSTAT,   1,
    0,          0,          0
};

#define CMDLPFAT    1
#define CMDLPTMP    2
#define CMDLPGEN    3
#define CMDLPBST    4
#define CMDLPFST    5
#define CMDLPPTR    6
#define CMDLPBTR    7
#define CMDLPFTR    8

LOCVAR Cmd
	    cmdlevel[] =
{
    "fat",      CMDLPFAT,   0,
    "tmp",      CMDLPTMP,   0,
    "gen",      CMDLPGEN,   0,
    "bst",      CMDLPBST,   0,
    "fst",      CMDLPFST,   0,
    "ptr",      CMDLPPTR,   0,
    "btr",      CMDLPBTR,   0,
    "ftr",      CMDLPFTR,   0,
#ifdef NVRCOMPIL
	these are commented out in order to save a trivial amount of
	space.  if you really want the synonyms, add them.  (dhc)
    "llogfat",  CMDLPFAT,   0,
    "llogtmp",  CMDLPTMP,   0,
    "lloggen",  CMDLPGEN,   0,
    "llogbst",  CMDLPBST,   0,
    "llogfst",  CMDLPFST,   0,
    "llogptr",  CMDLPPTR,   0,
    "llogbtr",  CMDLPBTR,   0,
    "llogftr",  CMDLPFTR,   0,
#endif
    0,          0,          0
};

#define CMDLSCLS    1
#define CMDLSCYC    2
#define CMDLSWAT    3
#define CMDLSSOME   4

LOCVAR Cmd
	    cmdlstat[] =
{
    "close",    CMDLSCLS,   0,
    "wait",     CMDLSWAT,   0,
    "cycle",    CMDLSCYC,   0,
    "some",     CMDLSSOME,  0,
#ifdef NVRCOMPIL
    "llogcls",  CMDLSCLS,   0,
    "cls",      CMDLSCLS,   0,
    "wat",      CMDLSWAT,   0,
    "llogwat",  CMDLSWAT,   0,
    "cyc",      CMDLSCYC,   0,
    "llogcyc",  CMDLSCYC,   0,
    "llogsome", CMDLSSOME,  0,
#endif
    0,          0,          0
};


tai_log (argc, argv, thelog)    /* get ll_log structure values */
    int argc;                   /* number of values     */
    char *argv[];               /* list of values       */
    LLog *thelog;               /* the ll_log struct to modify */
{
    register int ind;

    for (ind = 0; ind < argc; ind++)
    {
	if (lexequ ("=", argv[ind]))
	{                       /* key/value pair       */
	    ind += 2;
	    switch (cmdsrch (argv[ind - 1], argc - ind + 1, cmdlog))
	    {
		case CMDLHDR:
		    thelog -> ll_hdr = argv[ind];
		    break;

		case CMDLLEVEL:
		    if (isdigit (argv[ind][0]))
			sscanf (argv[ind], "%d", &(thelog -> ll_level));
		    else
			switch (cmdsrch (argv[ind], 0, cmdlevel))
			{
			    case CMDLPFAT:
				thelog -> ll_level = LLOGFAT;
				break;

			    case CMDLPTMP:
				thelog -> ll_level = LLOGTMP;
				break;

			    case CMDLPGEN:
				thelog -> ll_level = LLOGGEN;
				break;

			    case CMDLPBST:
				thelog -> ll_level = LLOGBST;
				break;

			    case CMDLPFST:
				thelog -> ll_level = LLOGFST;
				break;

			    case CMDLPPTR:
				thelog -> ll_level = LLOGPTR;
				break;

			    case CMDLPBTR:
				thelog -> ll_level = LLOGBTR;
				break;

			    case CMDLPFTR:
				thelog -> ll_level = LLOGFTR;
				break;

			    default:
				errno = EINVAL;
				tai_eptr = argv[ind - 1];
				argv[ind + 1] = 0;
				return (NOTOK);
			}
		    break;

		case CMDLSIZE:
		    thelog -> ll_msize = atoi (argv[ind]);
		    break;

		case CMDLSTAT:
		    if (isdigit (argv[ind][0]))
			    /* not all stdio's have a test for octal (dhc) */
			sscanf (argv[ind], "%o", &(thelog -> ll_stat));
		    else
			switch (cmdsrch (argv[ind], 0, cmdlstat))
			{
			    case CMDLSCLS:
				thelog -> ll_stat |= LLOGCLS;
				break;

			    case CMDLSWAT:
				thelog -> ll_stat |= LLOGWAT;
				break;

			    case CMDLSCYC:
				thelog -> ll_stat |= LLOGCYC;
				break;

			    case CMDLSSOME:
				thelog -> ll_stat |= LLOGSOME;
				break;

			    default:
				errno = EINVAL;
				tai_eptr = argv[ind - 1];
				argv[ind + 1] = 0;
				return (NOTOK);
			}
		    break;

		default:
		    errno = EINVAL;
		    tai_eptr = argv[ind - 1];   /* point to 'key' */
		    argv[ind + 1] = 0;
		    return (NOTOK);
	    }
	}
	else
	{
	    if (ind == 0)
		thelog -> ll_file = argv[ind];
	    else
	    {
		errno = EINVAL;
		tai_eptr = argv[ind - 1];   /* point to 'key' */
		argv[ind + 1] = 0;
		return (NOTOK);
	    }
	}
    }
/*****  disabled due to bug in mm_tai.c:tai_llev() that calles this
/*****  function with a garbage log pointer.
/*****    ll_close (thelog);          /* start with a clean slate */
    return (YES);
}
t (buf, "\n\t");
		totlen = 8;
	  mmdf/lib/util/tai_test.c   444      0     12         604  3671073214  10437 #include "util.h"

main ()
{
	static char initfile[] = "init.tai";
	char *taiargs[100];
	int nargs;
	register int ind;

	if (tai_init (initfile) < OK)
	{
		perror ("init");
		exit ();
	}
	while ((nargs = tai_get (100, taiargs)) > 0)
	{
		for (ind = 0; ind < nargs; ind++)
			printf ("'%s' ", taiargs[ind]);
		(void) putchar ('\n');
	}
	if (nargs < 0)
		perror ("tai_get");
	tai_end ();
}
         0
};


tai_log (argc, argv, thelog)    /* get ll_log structure values */
    int argc;                   /* number mmdf/lib/util/cmdsrch.c   444      0     12        1123  3620510426  10256 #include "util.h"
#include "cmd.h"

extern int errno;

cmdsrch (str, argc, cmd)        /* find command. return token reference */
    char str[];                 /* test string  */
    int argc;                   /* number of available arguments        */
    register Cmd *cmd;          /* table of known commands */
{
    for ( ; (cmd -> cmdname) != (char *) 0; cmd++)
	if (lexequ (str, cmd -> cmdname))
	{                       /* got a hit            */
	    if (argc < cmd -> cmdnargs)
	    {
		errno = EINVAL;
		return (NOTOK);
	    }
	    return (cmd -> cmdtoken);
	}

    return (NO);
}
g *thelog;               /* the ll_log struct to modify */
{
    register int ind;

    for (ind = 0; ind < argc; ind++)
    {
	if (lexequ ("=", argv[ind]))
	{                       /* key/value pair       */
	    ind += 2;
	    switch (cmdsrch (argv[ind - 1], argc - ind + 1, cmdlog))
	    {
		case CMDLHDR:
		    thelog -> ll_hdr = argv[ind];
		    break;

		case CMDLLEVEL:
		    if (isdigit (argv[ind][0]))
			sscanf (argv[inmmdf/lib/util/cstr2arg.c   444      0     12        5715  3620510426  10375 
#include "util.h"

/*  convert string into argument list
 *
 *  stash a pointer to each field into the passed array.
 *  any common seperators split the words.  extra white-space
 *  between fields is ignored.
 *
 *  if the separator is '=', then the current argument position in
 *  the array points to "=", the next the one is the key and the
 *  value follows it.  This permits detecting variable assignment,
 *  in addition to positional arguments.
 *      i.e.,  key=value ->  = key value
 *
 *  specially-interpreted characters:
 *      space, tab, double-quote, backslash, comma, equal, slash, period,
 *      semi-colon, colon, carriage return, and line-feed (newline).
 *      preceding a special char with a backslash removes its
 *      interpretation.  a backslash not followed by a special is used
 *      to preface an octal specification for one character
 *
 *      a string begun with double-quote has only double-quote and
 *      backslash as special characters.
 *
 *  a field which begins with semi-colon is interpreted as marking the
 *  rest of the line as a comment and it is skipped, as are blank lines
 */


/* Steve Kille
 * Modified version, which splits a string given a specific
 * separator
 */

extern int errno;

cstr2arg (srcptr, max, argv, dlmchar)/* convert srcptr to argument list */
	register char *srcptr;  /* source data */
	int max;                /* maximum number of permitted fields */
	char *argv[];           /* where to put the pointers */
	char dlmchar;           /* Delimiting character */
{
    char gotquote;      /* currently parsing quoted string */
    register int ind;
    register char *destptr;

    if (srcptr == 0)
    {
	errno = EINVAL;     /* emulate system-call failure */
	return (NOTOK);
    }

    for (ind = 0, max -= 2;; ind++)
    {
	if (ind >= max)
	{
	    errno = E2BIG;      /* emulate system-call failure */
	    return (NOTOK);
	}

	argv [ind] = srcptr;
	destptr = srcptr;

/**/

	for (gotquote = FALSE; ; )
	{
	    if (*srcptr == dlmchar)
	    {
		if (gotquote)
		{           /* don't interpret the char */
		    *destptr++ = *srcptr++;
		    continue;
		}

		srcptr++;
		*destptr = '\0';
		goto nextarg;
	    }
	    else
	    {
		switch (*srcptr)
		{
		    default:        /* just copy it                     */
			*destptr++ = *srcptr++;
			break;

		    case '\"':      /* beginning or end of string       */
			gotquote = (gotquote) ? FALSE : TRUE;
			srcptr++;   /* just toggle */
			break;

		    case '\\':      /* quote next character             */
			srcptr++;   /* just skip the back-slash         */
			if (*srcptr >= '0' && *srcptr <= '7')
			{
				*destptr = '\0';
				do
				    *destptr = (*destptr << 3) | (*srcptr++ - '0');
				while (*srcptr >= '0' && *srcptr <= '7');
				destptr++;
				break;
			}    /* otherwise DROP ON THROUGH */
			else
			     *destptr++ = *srcptr++;
			break;


		    case '\0':
			*destptr = '\0';
			ind++;
			argv[ind] = (char *) 0;
			return (ind);
		}
	    }
	}
    nextarg:
	continue;
    }
}

aracters.
 *
 *  a field which begins with semi-colmmdf/lib/util/getllog.v7.c   444      0     12        1546  3620510427  10635 #include "util.h"
#include <utmp.h>

/* get informtion about who is logged in */

/* this is modeled after the getpw v7 set of routines */

LOCVAR char LLPATH[]  = "/usr/adm/lastlog";
LOCVAR FILE *llogfp = NULL;
LOCVAR union tmpunion
{
    char        io[sizeof (struct utmp)];
    struct utmp entry;
}       llogentry;

setllog()
{
	if( llogfp == NULL )
		llogfp = fopen( LLPATH, "r" );
	else
		rewind( llogfp );
}

endllog()
{
	if( llogfp != NULL ){
		fclose( llogfp );
		llogfp = NULL;
	}
}

struct utmp *
getllog()
{
	if (llogfp == NULL) {
		if( (llogfp = fopen( LLPATH, "r" )) == NULL )
			return(0);
	}
	if (fread (llogentry.io, sizeof (struct utmp), 1, llogfp) != 1)
		return(0);
	return(&llogentry.entry);
}

struct utmp *
getllnam(name)
char name[];
{
	register struct utmp *p;

	while( (p = getllog()) && !equal(name,p->ut_name, strlen(name)));

	return(p);
}
;
			ind++;
			argv[ind] = (char *) 0;
			return (ind);
		}
	    }
	}
    nextarg:
	continue;
    }
}

aracters.
 *
 *  a field which begins with semi-colmmdf/lib/util/sstr2arg.c   444      0     12        4657  3620510427  10422 
#include "util.h"

/*  convert string into argument list
 *
 *	This version takes an array of delimiters.	<DPK@BRL>
 *
 *  stash a pointer to each field into the passed array.
 *  any common seperators split the words.  extra white-space
 *  between fields is ignored.
 *
 *  specially-interpreted characters:
 *      double-quote, backslash (preceding a special char with a
 *	backslash removes its interpretation.  A backslash not
 *	followed by a special is used to preface an octal specification
 *	for one character a string begun with double-quote has only
 *      double-quote and backslash as special characters.
 */

extern int errno;

/* convert srcptr to argument list */
sstr2arg (srcptr, max, argv, dlmstr)
register char *srcptr;	/* source data */
int max;		/* maximum number of permitted fields */
char *argv[];		/* where to put the pointers */
char *dlmstr;		/* Delimiting character */
{
    char gotquote;      /* currently parsing quoted string */
    register int ind;
    register char *destptr;

    if (srcptr == 0) {
	errno = EINVAL;     /* emulate system-call failure */
	return (NOTOK);
    }

    for (ind = 0, max -= 2;; ind++) {
	if (ind >= max) {
	    errno = E2BIG;      /* emulate system-call failure */
	    return (NOTOK);
	}

    	/* Skip leading white space */
    	for (; *srcptr == '\t' || *srcptr == ' '; srcptr++);

	argv [ind] = srcptr;
	destptr = srcptr;

	for (gotquote = FALSE; ; ) {
	    register char *cp;

	    for (cp = dlmstr; (*cp != '\0') && (*cp != *srcptr); cp++);

	    if (*cp != '\0')
	    {
		if (gotquote) {           /* don't interpret the char */
		    *destptr++ = *srcptr++;
		    continue;
		}

		srcptr++;
		*destptr = '\0';
		goto nextarg;
	    } else {
		switch (*srcptr) {
		default:        /* just copy it                     */
			*destptr++ = *srcptr++;
			break;

		case '\"':      /* beginning or end of string       */
			gotquote = (gotquote) ? FALSE : TRUE;
			srcptr++;   /* just toggle */
			break;

		case '\\':      /* quote next character             */
			srcptr++;   /* just skip the back-slash         */
			if (*srcptr >= '0' && *srcptr <= '7') {
				*destptr = '\0';
				do
				    *destptr = (*destptr << 3) | (*srcptr++ - '0');
				while (*srcptr >= '0' && *srcptr <= '7');
				destptr++;
				break;
			}
			else
			     *destptr++ = *srcptr++;
			break;

		    case '\0':
			*destptr = '\0';
			ind++;
			argv[ind] = (char *) 0;
			return (ind);
		}
	    }
	}
    nextarg:
	continue;
    }
}
 /* where to put the pointers */
	char dlmchar;           /* Delimiting charactermmdf/lib/util/getwho.c   444      0     12         241  3620510427  10111 getwho (realid, effecid)          /* user info for V7 Unix              */
    int   *realid,
	*effecid;
{
    *realid = getuid ();

    *effecid = geteuid();
}
o = E2BIG;      /* emulate system-call failure */
	    return (NOTOK);
	}

    	/* Skip leading white space */
    	for (; *srcptr == '\t' || *srcptr == ' '; srcptr++);

	argv [ind] = srcptr;
	destptr = srcptr;

	for (gotquote = FALSE; ; ) {
	    register char *cp;

	    for (cp = dlmstr; (*cp != '\0') && (*cp != *srcptr); cp++);

	    if (*cp != '\mmdf/lib/util/lk_lock.4.2.c   444      0     12        4527  3671073215  10574 #include "util.h"
#include <sys/file.h>

/*
 *	Standardized file-locking package  (4.2BSD)
 *
 *  This version assumes the existence of the 4.2BSD flock() system call.
 */
extern int errno;               /* simulate system error problems */

#ifdef DEBUG
#include "ll_log.h"
extern LLog *logptr;
#endif

/**/

lk_open (file, access, lockdir, lockfile, maxtime)
char	*file;			/* file to be locked */
int	access;			/* read-write permissions */
char	*lockdir;		/* --Ignored-- */
char	*lockfile;		/* --Ignored-- */
int	maxtime;		/* maybe break lock after it is this old */
{
    register int fd;

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "lk_open (%s,%d,%d)",
		file, access, maxtime);
#endif

    if ((fd = open (file, access | O_NDELAY)) < 0
	|| flock (fd, LOCK_EX|LOCK_NB) < 0)
    {
#ifdef DEBUG
	ll_err (logptr, LLOGBTR, "open notok (err %d)", errno);
#endif
    	if (fd >= 0)
    	    (void) close (fd);
	return (NOTOK);
    }
    return (fd);
}

lk_close (fd, file, lockdir, lockfile)
int	fd;
char	*file;			/* -- Ignored -- */
char	*lockdir;		/* -- Ignored -- */
char	*lockfile;		/* -- Ignored -- */
{
    register int retval;

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "lk_close (%d)", fd);
#endif
    if (fd < 0)
	return (OK);
    retval = close (fd);
    return (retval);
}
/**/
FILE *
	lk_fopen (file, access, lockdir, lockfile, maxtime)
char	*file;			/* file to be locked */
char	*access;		/* read-write permissions */
char	*lockdir;		/* --Ignored-- */
char	*lockfile;		/* --Ignored-- */
int	maxtime;		/* maybe break lock after it is this old */
{
    register int fd;
    register FILE *fp;

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "lk_fopen (%s,%s,%d)", file, access, maxtime);
#endif

    if ((fd = open(file, ((access[0]=='r' && access[1]==0) ? 0 : 2)
	| O_NDELAY)) < 0 || flock (fd, LOCK_EX|LOCK_NB) < 0) {
#ifdef DEBUG
	ll_err (logptr, LLOGBTR, "open notok");
#endif
	if (fd >= 0)
	    (void) close (fd);
	return (NULL);
    }

    if ((fp = fdopen (fd, access)) == NULL)
    {
	(void) close (fd);
	return ((FILE *) NULL);
    }
    return (fp);
}

lk_fclose (fp, file, lockdir, lockfile)
FILE	*fp;
char	*file;			/* --Ignored-- */
char	*lockdir;		/* --Ignored-- */
char	*lockfile;		/* --Ignored-- */
{
#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "lk_fclose ()");
#endif
    switch ((int)fp) {
    case EOF:
    case NULL:
	    return (OK);
    }
    return (fclose (fp));
}
	argv[ind] = (char *) 0;
			return (ind);
		}
	    }
	}
    nextarg:
	continue;
    }
}
 /* where to put the pointers */
	char dlmchar;           /* Delimiting charactermmdf/lib/util/gwdir.2.9.c   444      0     12       10226  3620510430  10275 #include "util.h"
#include "conf.h"
#include <sys/stat.h>

struct  kb_dir  {
	short   kd_ino;
	char    kd_name[14];
};

LOCVAR char wd_DOT[] = ".";
LOCVAR char wd_DOTDOT[] = "..";
LOCVAR char wd_read[] = "r";

LOCVAR char     wkd_name[128];     /* max size of path string            */
LOCVAR jmp_buf  wd_retloc;
LOCVAR union wd                 /* unioned for making i/o cleaner       */
{
	char            io[sizeof (struct kb_dir)];
	struct kb_dir   dir;
} wd_entry;
LOCVAR  char    dummy;  /* will be null - kludge to stop 14 char filenames */
			/* breaking this routine */

LOCVAR FILE * wd_fp;

char *
	gwdir ()                    /* what is current working directory? */
{
    struct stat wd_stat;
    short     lastkd_ino;            /* last inode num                     */
    char    buf[BUFSIZ];

    if (!setjmp (wd_retloc))
    {                             /* end due to eof or err              */
	if(wd_fp != (FILE *)EOF && wd_fp != NULL)
		setbuf (wd_fp, NULL);
	if (wd_fp == (FILE *) EOF || wd_fp == (FILE *) NULL ||
		fclose (wd_fp) == EOF ||
		wkd_name[0] == '\0' ||
		chdir (wkd_name) < OK)
	    return ((char *) NOTOK);

	return (wkd_name);
    }

    wkd_name[0] = '\0';
    lastkd_ino = -1;              /* force to be set on first pass      */

    if (stat (wd_DOT, &wd_stat) < 0 ||         /* get inode num for this dir 	*/
	    (wd_fp = fopen (wd_DOTDOT, wd_read)) == NULL)
	longjmp (wd_retloc, TRUE);
				  /* set to compare DOTDOT inodes       */
    setbuf (wd_fp, buf);

    for (;;)
    {
	for (;;)
	{                         /* cycle thru dir's inodes            */
	    fread (wd_entry.io, sizeof (struct kb_dir), 1, wd_fp);
	    if (ferror (wd_fp) || feof (wd_fp))
		longjmp (wd_retloc, TRUE);

	    if (wd_entry.dir.kd_ino == wd_stat.st_ino)
		break;            /* got a match                        */
	}
	if (wd_fp == (FILE *) EOF || wd_fp == (FILE *) NULL ||
		 fclose (wd_fp) == EOF || ferror (wd_fp) || feof (wd_fp))
	    longjmp (wd_retloc, TRUE);
	if (wd_entry.dir.kd_ino == 1)  /* hit the root directory             */
	    wd_ckroot ();         /* try to find its name               */

	if (wd_entry.dir.kd_ino == lastkd_ino)
				  /* we are cycling at top              */
	    longjmp (wd_retloc, TRUE);
	else                      /* remember for next level up         */
	    lastkd_ino = wd_entry.dir.kd_ino;

	wd_cat ();                /* add current name to end            */

	if (chdir (wd_DOTDOT) < OK || /* set up for next pass this dir      */
	       stat (wd_DOT, &wd_stat) < OK ||
	       freopen (wd_DOTDOT, wd_read, wd_fp) == NULL)
	    longjmp (wd_retloc, TRUE);
    }
}

LOCFUN wd_ckroot ()               /* check root dir for filesys name    */
{
    static char wd_root[] = "/";
    struct stat wd_stat,
		owd_stat;         /* to save device info                */

    if (stat (wd_entry.dir.kd_name, &wd_stat) < 0)
	longjmp (wd_retloc, TRUE);

    owd_stat.st_dev = wd_stat.st_dev;

    if (chdir (wd_root) < OK)
	longjmp (wd_retloc, TRUE);

    if (freopen (wd_root, wd_read, wd_fp) == NULL)
	longjmp (wd_retloc, TRUE);

    for (;;)
    {                             /* read thru "/" dir                  */
	fread (wd_entry.io, sizeof (struct kb_dir), 1, wd_fp);
	if (ferror (wd_fp) || feof (wd_fp))
	    longjmp (wd_retloc, TRUE);

	if (wd_entry.dir.kd_ino == 0)  /* not allocated                      */
	    continue;

	if (stat (wd_entry.dir.kd_name, &wd_stat) < 0)
	    longjmp (wd_retloc, TRUE);

	if (wd_stat.st_dev == owd_stat.st_dev)
	{                         /* device types match                 */
	    wd_stat.st_mode &= S_IFMT;
	    if (wd_stat.st_mode == S_IFDIR)
		break;            /* and it is a directory              */
	}
    }
    if (strcmp (wd_entry.dir.kd_name, wd_DOT) != 0 &&
	    strcmp (wd_entry.dir.kd_name, wd_DOTDOT) != 0)
	wd_cat ();                /* it has a real text name, too       */

    wd_entry.dir.kd_name[0] = '\0';    /* hack, to force "/" at front        */
    wd_cat ();
    longjmp (wd_retloc, TRUE);
}

LOCFUN wd_cat ()
{
    extern char *strcpy (),
		*strcat ();

    if (wkd_name[0] == 0)
	strcpy (wkd_name, wd_entry.dir.kd_name);
    else
    {
	strcat (wkd_name, "/");
	strcat (wkd_name, wd_entry.dir.kd_name);
    }
}
 |= LLOGWAT;
				break;

			    case CMDLSCYC:
				thelog -> ll_stat |= LLOGCYC;
				break;

			    case CMDLSSOME:
				thelog -> ll_stat |= LLOGSOME;
				break;

			    default:
				errno = EINVAL;
				tai_eptr = argv[ind - 1];
				argv[ind + 1] = 0;
				return (NOTOK);
			}
		    break;

		default:
		    errno = EINVAL;
		    tai_eptr = argv[ind - 1];   /* pmmdf/lib/util/lk_lock.5.2.c   444      0     12        5352  3671073216  10573 #include "util.h"
	/* <lockf.h> may have to be replaced with <unistd.h>    */
	/* on some systems such as the 3B2                      */
#include <lockf.h>
#include <fcntl.h>

/*
 *	Standardized file-locking package  (System 5 Rel 2)
 *
 *  This version assumes the existence of the System 5 lockf() system call
 *  Adapted by Mark Vasoll <vasoll@a.cs.okstate.edu> from the 4.2 BSD version
 */
extern int errno;               /* simulate system error problems */

#ifdef DEBUG
#include "ll_log.h"
extern LLog *logptr;
#endif

/**/

lk_open (file, access, lockdir, lockfile, maxtime)
char	*file;			/* file to be locked */
int	access;			/* read-write permissions */
char	*lockdir;		/* --Ignored-- */
char	*lockfile;		/* --Ignored-- */
int	maxtime;		/* maybe break lock after it is this old */
{
    register int fd;

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "lk_open (%s,%d,%s,%s,%d)",
		file, access, lockdir, lockfile, maxtime);
#endif

    if ((fd = open (file, access | O_CREAT)) < 0
	|| lockf (fd, F_TLOCK, 0) < 0)
    {
#ifdef DEBUG
	ll_err (logptr, LLOGBTR, "open notok (err %d)", errno);
#endif
    	if (fd >= 0)
    	    (void) close (fd);
	return (NOTOK);
    }
    return (fd);
}

lk_close (fd, file, lockdir, lockfile)
int	fd;
char	*file;			/* file to be locked */
char	*lockdir;		/* directory to put parallel file into */
char	*lockfile;		/* file to lock against */
{
    register int retval;

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "lk_close (%d,%s,%s,%s)",
		fd, file, lockdir, lockfile);
#endif
    if (fd < 0)
	return (OK);
    retval = close (fd);
    return (retval);
}
/**/
FILE *
	lk_fopen (file, access, lockdir, lockfile, maxtime)
char	*file;			/* file to be locked */
char	*access;		/* read-write permissions */
char	*lockdir;		/* --Ignored-- */
char	*lockfile;		/* --Ignored-- */
int	maxtime;		/* maybe break lock after it is this old */
{
    register int fd;
    register FILE *fp;

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "lk_fopen (%s,%s,%s,%s,%d)",
		file, access, lockdir, lockfile, maxtime);
#endif

    if ((fd = open(file, (access[0]=='r' && access[1]==0 ? 0 : 2)
	| O_CREAT)) < 0 || lockf (fd, F_TLOCK, 0) < 0) {
#ifdef DEBUG
	ll_err (logptr, LLOGBTR, "open notok");
#endif
	if (fd >= 0)
	    (void) close (fd);
	return (NULL);
    }

    if ((fp = fdopen (fd, access)) == NULL)
    {
	(void) close (fd);
	return ((FILE *) NULL);
    }
    return (fp);
}

lk_fclose (fp, file, lockdir, lockfile)
FILE	*fp;
char	*file;			/* --Ignored-- */
char	*lockdir;		/* --Ignored-- */
char	*lockfile;		/* --Ignored-- */
{
    register int retval;

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "lk_fclose (%s,%s,%s)", file, lockdir, lockfile);
#endif
    switch (fp)
    {
	case EOF:
	case NULL:
	    return (OK);
    }
    retval = fclose (fp);
    return (retval);
}
OT) != 0)
	wd_cat ();                /* it has a real text name, too       */

    wd_entry.dir.kd_name[0] = '\0';    /* hack, to force "/" at front        */
    wd_cat ();
    longjmp (wd_retloc, TRUE);
}

LOCFUN wd_cat ()
{
    extern char *strcpy (),
		*strcat ();

    if (mmdf/lib/libfix   555      0     12         271  3620510430   6673 set -x
set -e

mkdir LIBFIX.$$
cp $1 LIBFIX.$$/oldlib.a
(cd LIBFIX.$$; ar x oldlib.a; rm -f oldlib.a; \
  ar cr newlib.a `lorder *.o | tsort`)
cp LIBFIX.$$/newlib.a $1
rm -rf LIBFIX.$$
le, lockdir, lockfile)
int	fd;
char	*file;			/* file to be locked */
char	*lockdir;		/* directory to put parallel file into */
char	*lockfile;		/* file to lock against */
{
    register int retval;

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "lk_close (%d,%s,%s,%s)",
		fd, file, lockdir, lockfile);
#endif
    if (fd < 0)
	retummdf/lib/Makefile   444      0     12        2236  3652767212   7174 #
#	Makefile for the MMDF library   (libmmdf.a)
#

#  The paths below are relative from the directories below this one.
#
#  The RANLIB define should be "sh libfix" for systems that don't have
#	the ranlib command.  Systems that do have ranlib should set
#	RANLIB to "ranlib".  "libfix" is a Bourne shell file that will
#	use lorder and tsort to reorder the library.
#
MAKE	= make
RANLIB	= ranlib

SUBDIRS = addr dial mmdf table util
ALLSUBDIRS = addr dial mmdf table util

default: remake reorder

remake:
	for dir in $(SUBDIRS); \
	  do (cd $${dir}; echo "Running make in $${dir}"; \
	      gen ${MFLAGS} -${MAKEFLAGS}); \
	  done

install: default
	@echo "Library is up to date, nothing else to do."

reorder: reorder-done

reorder-done: libmmdf.a
	-$(RANLIB) libmmdf.a
	-touch reorder-done

depend:
	for dir in $(SUBDIRS); \
	  do (cd $${dir}; gen ${MFLAGS} -${MAKEFLAGS} depend); done

lint:
	for dir in $(SUBDIRS); \
	  do (cd $${dir}; gen ${MFLAGS} -${MAKEFLAGS} lint); done
	cat */llib-l*.ln > llib-lmmdf.ln

clean:
	-rm -f *-made libmmdf.a make.out core llib-lmmdf.ln reorder-done
	for dir in $(SUBDIRS); \
	  do (cd $${dir}; $(MAKE) -f Makefile.real clean); done

ALWAYS:
*/
char	*access;		/* read-write permissions */
char	*lockdir;		/* --Ignored-- */
char	*lockfile;		/* --Ignored-- */
int	maxtime;		/* maybe break lock after it is this old */
{
    register int fd;
    register FILE *fp;

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "lk_fopen (%s,%s,%s,%s,%d)",
		file, access, lockdir, lockfile, maxtime);
#endif

    if ((mmdf/libndir/   755      0     12           0  3635165674   6330 mmdf/libndir/Makefile   444      0     12        1134  3620510431  10025 # @(#)Makefile	4.2 (Berkeley) 8/2/82

DESTDIR	=
CFLAGS=	-O 

OBJS=	closedir.o opendir.o readdir.o seekdir.o telldir.o

libndir.a: ${OBJS}
	ar rv libndir.a ${OBJS}
	ranlib libndir.a

${DESTDIR}/usr/include/dir.h: dir.h
	cp dir.h ${DESTDIR}/usr/include/dir.h

install: libndir.a
	cp libndir.a ${DESTDIR}/usr/lib/libndir.a
	cp directory.3 /usr/man/man3/directory.3
	cp dir.h ${DESTDIR}/usr/include/dir.h

clean:
	rm -f libndir.a ${OBJS}

.c.o:
	${CC} ${CFLAGS} -c $*.c

closedir.o:	closedir.c dir.h
opendir.o:	opendir.c dir.h
readdir.o:	readdir.c dir.h
seekdir.o:	seekdir.c dir.h
telldir.o:	telldir.c dir.h
	  do (cd $${dir}; $(MAKE) -f Makefile.real clean); done

ALWAYS:
*/
char	*access;		/* read-write permissions */
char	*lockdir;		/* --Ignored-- */
char	*lockfile;		/* --Ignored-- */
int	maxtime;		/* maybe break lock after it is this old */
{
    register int fd;
    register FILE *fp;

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "lk_fopen (%s,%s,%s,%s,%d)",
		file, access, lockdir, lockfile, maxtime);
#endif

    if ((mmdf/libndir/Read_Me   444      0     12        1132  3620510431   7602 The purpose of this library is to simulate the new flexable length
directory names on top of the old directory structure. It allows
programs to be converted to the new directory access interface, so
that they need only be relinked when 4.2bsd becomes available.

The following files should be placed in /usr/src/lib/libndir; then run
``make'' followed by ``make install''. The manual page is available as
``man directory''. The library is accessed by specifying "-lndir" as the
last argument to the compile line. (eg ``cc -o foo foo.c -lndir'')
Report problems to mckusick@berkeley or ucbvax!mckusick.
of the old directory structure. It allows
programs to be converted to the new directory access interface, so
that they need only be relinked when 4.2bsd becomes available.

The following files should be placed in /usr/src/lib/libndir; then run
``make'' followed by ``make install''. The manual page is available as
``man directory''. The library is accessed by specifying "-lndir" as the
last argument to the compile line.mmdf/libndir/closedir.c   444      0     12         456  3620510431  10323 /* Copyright (c) 1982 Regents of the University of California */

static char sccsid[] = "@(#)closedir.c 4.2 3/10/82";

#include <sys/types.h>
#include "dir.h"

/*
 * close a directory.
 */
closedir(dirp)
	register DIR *dirp;
{
	close(dirp->dd_fd);
	dirp->dd_fd = -1;
	dirp->dd_loc = 0;
	free(dirp);
}
 /usr/src/lib/libndir; then run
``make'' followed by ``make install''. The manual page is available as
``man directory''. The library is accessed by specifying "-lndir" as the
last argument to the compile line.mmdf/libndir/dir.h   444      0     12        2245  3620510431   7320 /* Copyright (c) 1982 Regents of the University of California */

/* @(#)ndir.h 4.4 3/30/82 */

/*
 * This sets the "page size" for directories.
 * Requirements are DEV_BSIZE <= DIRBLKSIZ <= MINBSIZE with
 * DIRBLKSIZ a power of two.
 * Dennis Ritchie feels that directory pages should be atomic
 * operations to the disk, so we use DEV_BSIZE.
 */
#define DIRBLKSIZ 512

/*
 * This limits the directory name length. Its main constraint
 * is that it appears twice in the user structure. (u. area)
 */
#define MAXNAMLEN 255

struct	direct {
	long	d_ino;
	short	d_reclen;
	short	d_namlen;
	char	d_name[MAXNAMLEN + 1];
	/* typically shorter */
};

struct _dirdesc {
	int	dd_fd;
	long	dd_loc;
	long	dd_size;
	char	dd_buf[DIRBLKSIZ];
};

/*
 * useful macros.
 */
#undef DIRSIZ
#define DIRSIZ(dp) \
    ((sizeof(struct direct) - MAXNAMLEN + (dp)->d_namlen + sizeof(ino_t) - 1) &\
    ~(sizeof(ino_t) - 1))
typedef	struct _dirdesc DIR;
#ifndef	NULL
#define	NULL	0
#endif

/*
 * functions defined on directories
 */
extern DIR *opendir();
extern struct direct *readdir();
extern long telldir();
extern long lseek();
extern seekdir();
#define rewinddir(dirp)	seekdir((dirp), 0L)
extern closedir();
if
	if (fd >= 0)
	    (void) close (fd);
	return (NULL);
    }

    if ((fp = fdopen (fd, access)) == NULL)
    {
	(void) close (fd);
	return ((FILE *) NULL);
    }
    return (fp);
}

lk_fclose (fp, file, lockdir, lockfile)
FILE	*fp;
char	*file;			/* --Ignored-- */
char	*lockdir;		/* --Ignored-- */
char	*lockfile;		/* --Ignored-- */
{
    regismmdf/libndir/directory.3   444      0     12        4312  3620510432  10457 .TH DIRECTORY 3  3/1/82
.UC 4
.SH NAME
opendir, readdir, telldir, seekdir, rewinddir, closedir \- directory operations
.SH SYNOPSIS
.B #include <ndir.h>
.PP
.SM
.B DIR
.B *opendir(filename)
.br
.B char *filename;
.PP
.SM
.B struct direct
.B *readdir(dirp)
.br
.B DIR *dirp;
.PP
.SM
.B long
.B telldir(dirp)
.br
.B DIR *dirp;
.PP
.SM
.B seekdir(dirp, loc)
.br
.B DIR *dirp;
.br
.B long loc;
.PP
.SM
.B rewinddir(dirp)
.br
.B DIR *dirp;
.PP
.SM
.B closedir(dirp)
.br
.B DIR *dirp;
.SH DESCRIPTION
.I Opendir
opens the directory named by
.I filename
and associates a
.I directory stream
with it.
.I Opendir
returns a pointer to be used to identify the
.I directory stream
in subsequent operations.
The pointer
.SM
.B NULL
is returned if
.I filename
cannot be accessed or is not a directory.
.PP
.I Readdir
returns a pointer to the next directory entry.
It returns
.B NULL
upon reaching the end of the directory or detecting
an invalid
.I seekdir
operation.
.PP
.I Telldir
returns the current location associated with the named
.I directory stream.
.PP
.I Seekdir
sets the position of the next
.I readdir
operation on the
.I directory stream.
The new position reverts to the one associated with the
.I directory stream
when the
.I telldir
operation was performed.
Values returned by
.I telldir
are good only for the lifetime of the DIR pointer from
which they are derived.
If the directory is closed and then reopened,
the
.I telldir
value may be invalidated
due to undetected directory compaction.
It is safe to use a previous
.I telldir
value immediately after a call to
.I opendir
and before any calls to
.I readdir.
.PP
.I Rewinddir
resets the position of the named
.I directory stream
to the beginning of the directory.
.PP
.I Closedir
causes the named
.I directory stream
to be closed,
and the structure associated with the DIR pointer to be freed.
.PP
The preferred way to search the current directory for entry ``name'' is:
.br
	len = strlen(name);
.br
	dirp = opendir(".");
.br
	for (dp = readdir(dirp); dp != NULL; dp = readdir(dir))
.br
		if (dp->d_namlen == len && !strcmp(dp->d_name, name)) {
.br
			closedir(dirp);
.br
			return FOUND;
.br
		}
.br
	closedir(dirp);
.br
	return NOT_FOUND;
.SH "SEE ALSO"
open(2),
close(2),
read(2),
lseek(2)
helog -> ll_stat |= LLOGCYC;
				break;

			    case CMDLSSOME:
				thelog -> ll_stat |= LLOGSOME;
				break;

			    default:
				errno = EINVAL;
				tai_eptr = argv[ind - 1];
				argv[ind + 1] = 0;
				return (NOTOK);
			}
		    break;

		default:
		    errno = EINVAL;
		    tai_eptr = argv[ind - 1];   /* pmmdf/libndir/opendir.c   444      0     12        1067  3620510432  10177 /* Copyright (c) 1982 Regents of the University of California */

static char sccsid[] = "@(#)opendir.c 4.2 3/10/82";

#include <sys/types.h>
#include <sys/stat.h>
#include "dir.h"

extern	char *malloc();

/*
 * open a directory.
 */
DIR *
opendir(name)
	char *name;
{
	register DIR *dirp;
	struct stat sbuf;

	dirp = (DIR *)malloc(sizeof(DIR));
	dirp->dd_fd = open(name, 0);
	if (dirp->dd_fd == -1) {
		free(dirp);
		return NULL;
	}
	fstat(dirp->dd_fd, &sbuf);
	if ((sbuf.st_mode & S_IFDIR) == 0) {
		free(dirp);
		return NULL;
	}
	dirp->dd_loc = 0;
	return dirp;
}
directory stream
with it.
.I Opendir
returns a pointer to be used to identify the
.I directory stream
in subsequent operations.
The pointer
.SM
.B NULL
is returned if
.I filename
cannot be accessed or is not a directory.
.PP
.I Readdir
returns a pointer to the next directory entry.
It returns
.B NULL
upon reaching the end of the directory or detecting
an invalid
.I seekdir
operation.
.PP
.I Telldir
returns the current location associated with the named
mmdf/libndir/readdir.c   444      0     12        2045  3620510432  10146 /* Copyright (c) 1982 Regents of the University of California */

static char sccsid[] = "@(#)readdir.c 4.2 3/12/82";

#include <sys/types.h>
#include "dir.h"

/*
 * read an old stlye directory entry and present it as a new one
 */
#define	ODIRSIZ	14

struct	olddirect {
	ino_t	d_oino;
	char	d_oname[ODIRSIZ];
};

/*
 * get next entry in a directory.
 */
struct direct *
readdir(dirp)
	register DIR *dirp;
{
	register struct olddirect *dp;
	static struct direct dir;

	for (;;) {
		if (dirp->dd_loc == 0) {
			dirp->dd_size = read(dirp->dd_fd, dirp->dd_buf,
			    DIRBLKSIZ);
			if (dirp->dd_size <= 0)
				return NULL;
		}
		if (dirp->dd_loc >= dirp->dd_size) {
			dirp->dd_loc = 0;
			continue;
		}
		dp = (struct olddirect *)(dirp->dd_buf + dirp->dd_loc);
		dirp->dd_loc += sizeof(struct olddirect);
		if (dp->d_oino == 0)
			continue;
		dir.d_ino = dp->d_oino;
		strncpy(dir.d_name, dp->d_oname, ODIRSIZ);
		dir.d_name[ODIRSIZ] = '\0'; /* insure null termination */
		dir.d_namlen = strlen(dir.d_name);
		dir.d_reclen = DIRSIZ(&dir);
		return (&dir);
	}
}
ets the position of the next
.I readdir
operation on the
.I directory stream.
The new position reverts to the one associated with the
.I directory stream
when the
.I telldir
operation was performed.
Values returned by
.I telldir
are good only for the lifetime of the DIR pointer from
which they are derived.
If the directory is closed and then reopened,
the
.I telldir
value may be invalidated
due to undetected directory compaction.
It is safe to use a previous
.I telldir
vmmdf/libndir/seekdir.c   444      0     12        1336  3620510432  10164 /* Copyright (c) 1982 Regents of the University of California */

static char sccsid[] = "@(#)seekdir.c 4.3 2/25/82";

#include <sys/types.h>
#include "dir.h"

/*
 * seek to an entry in a directory.
 * Only values returned by ``telldir'' should be passed to seekdir.
 */
seekdir(dirp, loc)
	register DIR *dirp;
	long loc;
{
	long curloc, base, offset;
	struct direct *dp;

	curloc = telldir(dirp);
	if (loc == curloc)
		return;
	base = loc & ~(DIRBLKSIZ - 1);
	offset = loc & (DIRBLKSIZ - 1);
	if (dirp->dd_loc != 0 && (curloc & ~(DIRBLKSIZ - 1)) == base) {
		dirp->dd_loc = offset;
		return;
	}
	lseek(dirp->dd_fd, base, 0);
	dirp->dd_loc = 0;
	while (dirp->dd_loc < offset) {
		dp = readdir(dirp);
		if (dp == NULL)
			return;
	}
}
->dd_buf + dirp->dd_loc);
		dirp->dd_loc += sizeof(struct olddirect);
		if (dp->d_oino == 0)
			continue;
		dir.d_ino = dp->d_oino;
		strncpy(dir.d_name, dp->d_oname, ODIRSIZ);
		dir.d_name[ODIRSIZ] = '\0'; /* insure null termination */
		dir.d_namlen = strlen(dir.d_name);
		dir.d_reclen =mmdf/libndir/telldir.c   444      0     12         463  3620510433  10156 /* Copyright (c) 1982 Regents of the University of California */

static char sccsid[] = "@(#)telldir.c 4.1 2/21/82";

#include <sys/types.h>
#include "dir.h"

/*
 * return a pointer into a directory
 */
long
telldir(dirp)
	DIR *dirp;
{
	return (lseek(dirp->dd_fd, 0L, 1) - dirp->dd_size + dirp->dd_loc);
}
rp;
	long loc;
{
	long curloc, base, offset;
	struct direct *dp;

	curloc = telldir(dirp);
	if (loc == curloc)
		return;
	base = loc & ~(DIRBLKSIZ - 1);
	offset = loc & (DIRBLKSIZ - 1);
	if (dirp->dd_loc !mmdf/libndir/test.c   444      0     12         636  3620510433   7500 #include <sys/types.h>
#include "dir.h"

main(argc, argv)
char **argv;
{
	int	len;
	DIR	*dirp;
	struct	direct *dp;

	len = strlen( argv[1] );
	dirp = opendir( "." );
	for( dp = readdir(dirp); dp != NULL; dp = readdir(dirp) )
		if( dp->d_namlen == len && !strcmp(dp->d_name, argv[1])){
			closedir(dirp);
			printf("Found %s.\n", dp->d_name);
			exit(0);
		}
	closedir(dirp);
	printf("Not found.\n");
	exit( 1 );
}
oc)
		return;
	base = loc & ~(DIRBLKSIZ - 1);
	offset = loc & (DIRBLKSIZ - 1);
	if (dirp->dd_loc !mmdf/libndir/llib-lndir.ln   444      0     12        2103  3620510433  10747 3]llib-lndir:closedir.c
$closedir`€%]$close€>]$freeÞ]llib-lndir:opendir.ch`opendir£]"`opendir€£]bmalloc€Ò]$open€ò]$free€÷]$fstatL]llib-lndir:readdir.ch@readdir`]2@readdir€]$read€]],$strncpy€b].$strlen7]llib-lndir:seekdir.c$seekdir`€]h@readdir€]%telldir€]%lseek3]llib-lndir:telldir.c%telldir`]telldir€]%lseek6]llib-lndir:test.c$mainR€']h`opendir€ø\h@readdir€]$closedir€]$strlen€I]$strcmp€P]$printf€W]$exitI readdir
operation on the
.I directory stream.
The new position reverts to the one associated with the
.I directory stream
when the
.I telldir
operation was performed.
Values returned by
.I telldir
are good only for the lifetime of the DIR pointer from
which they are derived.
If the directory is closed and then reopened,
the
.I telldir
value may be invalidated
due to undetected directory compaction.
It is safe to use a previous
.I telldir
vmmdf/man/   755      0     12           0  3671352703   5447 mmdf/man/man1/   755      0     12           0  3671074632   6305 mmdf/man/man1/checkmail.1   444      0     12        2106  3642076257  10374 .TH CHECKMAIL 1 "23 DEC 1984"
.UC
.SH NAME
checkmail \- checks for mail which has been submitted but not delivered
.SH SYNOPSIS
.B checkmail
[
.B \-a
] [
.B \-f
] [
.B \-m
]
.SH DESCRIPTION
.I Checkmail
checks the mail queue on the local machine for messages which have been
sent by the invoker.
If invoked without any arguments,
the ``Subject:'' of each message found
is given along with a list of addressees that have not yet received
the message.
Usually, messages are still in the queue because the
addressee's host is down.
.PP
The \fB\-a\fR (all addresses)
option causes all address to be shown (both delivered
and undelivered).  Some delivered addresses may not appear since some
sites prune already delivered addresses from the address list files
for efficiency.
The \fB\-f\fR (fast) option supresses the the printing of
the ``Subject'' line.
The \fB\-m\fR (all messages) option causes checkmail to check all
messages in the mail queue, not just those of the invoker.  This is only
useful for mail system maintainers who wish to find obstinate hosts.
.SH "SEE ALSO"
send(1), deliver(8)
eaddir
operation on the
.I directory stream.
The new position reverts to the one associated with the
.I directory stream
when the
.I telldir
operation was performed.
Values returned by
.I telldir
are good only for the lifetime of the DIR pointer from
which they are derived.
If the directory is closed and then reopened,
the
.I telldir
value may be invalidated
due to undetected directory compaction.
It is safe to use a previous
.I telldir
vmmdf/man/man1/malias.1   444      0     12        1102  3642035006   7700 .TH MALIAS 1
.SH NAME
malias \- determine how an alias is expanded by the mail system
.SH SYNOPSIS
.B malias 
name \. \. \.
.SH DESCRIPTION
.br
.sp
.I Malias
takes a series of names as arguments.  If the names are aliases
they are expanded as far as possible.  The program does not
recurse on the alias file, but this can be done manually.  If
the name is not an alias, it is checked to determine if it
matches a local user name.
.SH "SEE ALSO"
send(1)
.SH DIAGNOSTICS
All diagnostic messages are intended to be self explanatory.
.SH BUGS
Ye jest.
.SH AUTHOR
Steve Kille, UCL.

ll address to be shown (both delivered
and undelivered).  Some delivered addresses may not appear since some
sites prune already delivered addresses from the address list files
for efficiency.
The \fB\-f\fR (fast) option supresses the the printing of
the ``Subject'' line.
The \fB\-m\fR (all messages) option causes checkmail to check all
messages in the mail queue, not just those of the invoker.  This is only
useful for mail system maintainersmmdf/man/man1/mlist.1   444      0     12        3040  3642035007   7566 .TH MLIST 1
.SH NAME
mlist \- mailing list addition and query program
.SH SYNOPSIS
.B mlist
[\-ch] [\-f filename] [arg.....]
.SH DESCRIPTION
.I Mlist
is an interactive program that allows the user to create a new mailing list
or specify changes to an old one, and uses
\fIv6mail\fR(1) to send a request to the system administrator.
For each list, there is a list manager
indicated in the MMDF alias file by an alias of the form
``foo-request'' where ``foo'' is the name of the list.
This person, as well as the
mailsystem owner and the superuser have a mechanism to add and
remove any list entry.
.I Mlist
attempts to ensure that erroneous and duplicate entries do not
get entered.  There is also a mechanism to verify the contents of a
list.
.PP
.I Mlist
may either be used in a self-explanatory interactive mode  (no
arguments), or by use of flags.
.RS
.TP 4
\-c
initiates mail list creation request
.TP 4
\-h
initiates help
.TP 4
\-f
filename     specifies another help file
.TP 4
other args are taken as names of lists to manipulate
.RE
.PP
Mailing list files should never be accessed directly by the
user, and should always be owned by the mail system.  The mode of
the file indicates how it should be handled.  There are four
possibilities.
.RE
.TP 4
666
All users have list manager status
.TP 4
744
Users may add or remove their own names
.TP 4
644
Only list manager may make changes
.TP 4
600
Contents of list private.  This does not affect usage controls.
.RE
.SH FILES
/etc/mmdf/helplist for default helpfile.
.SH "SEE ALSO"
malias(1), tables(5), v6mail(1)


.I opendir
and before any calls to
.I readdir.
.PP
.I Rewinddir
resets the position of the named
.I directory stream
to the beginning of the directory.
.PP
.I Closedir
causes the named
.I directory stream
to be closed,
and the structure associated with the DIR pointer to be freed.
.PP
The preferred way to search the current directory for entry ``name'' is:
.br
	len = strlen(name);
.br
	dirp = opendir(".");
.br
	for (dp = readdir(dirp); dp != NULL; dp = readdir(dir))
.br
		ifmmdf/man/man1/msg.1   444      0     12       42542  3671073273   7267 .TH MSG 1 "30 May 86"
.SH NAME
msg \- read mail
.SH SYNOPSIS
.B msg
[
.B message_file
[
.B mbox_file
]\ ]
.SH DESCRIPTION
.PP
\fIMsg\fR reads mail stored in message files. If it is invoked
with no arguments, it accesses mail contained in a file
associated with the invoker's account.
With one argument, \fImsg\fR reads the named message file.
If it is the users main mailbox, \fImsg\fR will discover this and also
set up to use the default mbox file.
If two arguments are given, the second is the mbox
to be used with the first file.
.PP
The user types only the first letter of a \fImsg\fR command.
\fIMsg\fR recognizes and completes the command.
Commands take the form:
.RS
command [argument(s)]
.RE
.PP
A few of the \fImsg\fR commands take no argument; others require a file name
argument or a confirmation of the command.
\fIMsg\fR will prompt for these. When a file name is needed, ~ may
be used to specify the invokers home directory; ~name will specify the
home directory of the person with the login of name.
.PP
Commands which require confirmation request it by typing ``[Confirm]''
after the command.  
To confirm the command, type a `y' (which will be
expanded to ``yes'' in verbose mode) or just a carriage return.  
.PP
The most commonly required argument is a message number
or number range (called a ``message sequence''),
which indicates on which message(s) the command should be performed.
If a message sequence is required and none is given, the
.I current
message is assumed.
.PP
Commands which are followed by a numerical message
sequence or user-supplied file name or string must be terminated with a
carriage return <CR>.
Commands without arguments, or commands followed by alphabetic message
sequences that do not require a user-supplied string,
are not terminated with <CR> in verbose mode.  In non-verbose mode,
all commands are terminated with <CR>.
.PP
The list of commands below shows which arguments are required for each
command.
.SS "Message Sequences"
.PP
Message sequences may be numeric or alphabetic.
A numeric message sequence takes one of the following forms,
where
.I `n'
is a single valid message number, `.' or `c' which stand for the current
message, `@' which stands for the current message with the ``mark'',
or `$' which stands for the last message, regardless of its number.
.RS
.TP 7
n
represents a single message
.TP 7
n1\-n2
represents a continuous range of messages  (note that `>' or `:' may be
used as message number separators in place of `\-').
.TP 7
m1,m2,...mx
(where each `m' is one of the above
constructs) represents all specified single messages and number ranges.
.RE
.PP
Alphabetic message sequences are recognized and expanded in the 
same manner as commands. Alphabetic and numeric message sequences
may not be combined.  
Alphabetic sequences include the following, and
may be preceded by i(nverse) to cause the specified messages to be
expanded in reverse order.
.RS
.IP "a(ll) messages"
.IP "c(urrent) message"
.IP "d(eleted) messages"
.IP "e(xpression:) [user-supplied string]"
every message whose body includes the user-specified string.
.IP "f(rom:) [user-supplied string]"
.IP "l(eaving) messages"
every message that will be leaving the mailbox upon exit.
.IP "m(ark) message"
.IP "n(ew) messages"
every message whose "from" field contains user-specified string.
.IP "s(ubject:) [user-supplied string]"
every message whose "subject" field includes user-specified string.
.IP "t(o:) [user-supplied string]"
every message whose "to" field contains user-specified string.
.IP "u(ndeleted) messages"
.RE
.PP
.sp 2v
\fIMsg\fR commands include the following:
.IP "a(nswer) [msg seq]"
Calls (\fIsend\fR(1)) program with the ``To:'' and ``Subject:''
fields already filled in.
Their contents are taken from the "From:" and "Subject:" fields of
specified message.
Use the ``headers'' command in send to change their contents, if necessary.
.RS
.PP
The user is prompted as to whom additional (``cc:'') copies should be sent.
The options are to send to all original recipients, to those
listed in the original ``To:'' fields, to those listed in the "cc:'' fields,
or just those who the message was from.  A null input (just a carriage
return) will cause the reply to go just to those listed in the ``From:''
field (the original authors).
.RE
.IP "b(ack up)"
Sets current message to the previous message
and types the message.
.IP "c(urrent)"
Prints the current message number, the number of messages,
and the current mailbox name.
.IP "d(elete) [msg seq]"
Sets a flag saying the message(s) is(are) deleted and removes ``keep'' flag.
.RS
.IP
NOTE:
Nothing in the message file is actually removed unless an o(verwrite)
or e(xit) command is given during the current session.
.RE
.IP "e(xit and update) [Confirm]"
The same as ``o(verwrite)'' followed by ``q(uit)'' (see below)
for any message file that does not have an mbox.
For ``mailbox'' or when an explicit mbox has been specified,
it removes deleted messages, moves undeleted messages to
mbox, then quits.
.IP "f(orward) [msg seq]"
Calls
.I "send,"
with
the body of the draft message initialized to
contain copies of the named message(s).  The ``Subject:'' field of the
message is also initialized.
.IP "g(o to) [msg #]"
Specified message becomes the current message.
.IP "h(eaders) [msg seq]"
Prints a one-line header for each specified message.
For example:
.RS
1 NDKAFP 475: 11 Dec 79 bass\ \ \ Meeting tomorrow
.RE
.RS
The header line contains the following information:
.RS
.br
the message number (in this case, 1)
.br
status flags \- N:new, D:delete, K:keep, A:answered, F:forwarded/resent, P:put
.br
the number of characters in the message (475:)
.br
the date the message was sent (11 Dec 1979)
.br
the author truncated to fit available space (bass)
.br
the subject, truncated to fit available space (Meeting tomorrow)
.RE
.IP "j(ump) into lower fork running:"
Allows user to give any one shell command. Type of shell is determined
from the environment variable SHELL. (\fIsh\fR
(1) is the default).  \fIMsg\fR will prompt for command.
Control is not returned to the \fImsg\fR program until execution of the command
is completed.
.IP "k(eep) [msg seq]"
Keep messages in mailbox and do not move them to mbox on exit and update.
Sets ``keep'' flag and removes ``delete'' flag.
.IP "l(ist) [msg seq]"
Copy messages (without internal separators, and optionally with a page
separator between messages) to file, in preparation for printing.
\fIMsg\fR will prompt for file name. Default is /dev/tty.
Messages may also be piped to other processes by typing ``|\ command''
in place of the file name.
.RS
.TP 7
NOTE:
"Internal separators" are lines of CTRL-A's
(supplied by the message distribution system when the message is delivered)
which separate each message in the mailbox.
.RE
.IP "m(ove) [msg seq]"
Is the same as ``p(ut)'' (see below), followed by ``d(elete)''.
.IP "n(ext)"
Sets the current message to be the next message in the message file
and types the message.
.IP "o(verwrite) [Confirm]"
Actually removes messages marked for deletion; then re-reads the message file.
.IP "p(ut) [msg seq]"
Copies messages (with internal separators) to a file.
\fIMsg\fR prompts for the file name. The default file is mbox.
Messages may also be piped to other processes by typing ``|\ command''
in place of the file name.
.IP "q(uit) [Confirm]"
Exit without changing mailbox.
.RS
.TP 7
NOTE:
If q(uit) command is used, deletions are NOT performed but status
flags, including the ``delete'' flags are retained.
.RE
.IP "r(ead)"
Read a new message file.
\fIMsg\fR will prompt for the file name.
The default is mailbox.
.IP "s(end)[Confirm]"
Calls
.I "send"
(1) program.
.IP "t(ype) [msg seq]"
Types designated messages on the terminal. Embedded control characters
are displayed as ^char with the exception of BS, NL, BEL and TAB.
Control character filtering can be disabled using an ``x(tra command)''
(see below)
or a .msgrc option.
.IP "u(ndelete)[msg seq]"
Removes "delete" marking from messages,
so that they will
.B not
be removed by an o(verwrite) or e(xit) command. Also removes ``keep''
flag.
.IP "x(tra command)"
A second set of commands available by using the xtra command
prefix.  Currently the following xtra commands are available:
.RS
.IP "b(inary file write)"
Writes a fresh copy of the binary file.
.IP "c(trl char filter on/off)"
Turn display of control characters as ^char off or on.
.IP "l(ist body) [msg seq]"
Copy message bodies (without internal separators and headers, and optionally
with a page separator between messages) to file, in preparation for
printing.  \fIMsg\fR will prompt for the output file name.  The default is
/dev/tty.  Messages may
also be piped to other processes by typing "|\ command" in place of the file
name.
.IP "m(ark) message"
The current message number is saved and can be quickly returned to by typing
g(o) followed by a carriage return.
.IP "n(umbered list on/off)"
Turns on or off the "numbered list" mode which
determines whether messages listed with the "list" command are preceded by an
"index" of header lines and whether each message is shown with its number
in the listing.
.IP "o(utput mbox file)"
Prompts for a new mbox file.  There is no default.
The current mailbox and the new mbox will be treated in the
same manner as regular mailbox and mbox are treated.
The new mbox will receive any read but unkept messages on \fIexit\fR.
It will also become the default output file for \fIput\fR and \fImove\fR.
.IP "p(age on/off)"
Turn "page" mode on (or back off). When on, \fImsg\fR pauses after printing
22 lines and displays the
message "Continue? (y)"; a response of "y" or " " will print the
next page, a carriage return will print the next line and "n" or "/" will
stop the output from the current command.
The number of lines between pauses can be changed using the .msgrc
file; the TIOCGWINSZ ioctl will be used if available.
.IP "r(eorder)"
Reorders (sorts) the entire message file. The following keys may be given:
.RS
.TP 4
d \- 
sort by Date: field.
.TP 4
f \- 
sort by From: field.
.TP 4
r \-
sort by date but group together messages with the same subject.
.TP 4
s \-
sort by Subject: field.
.TP 4
t \-
sort by To: field.
.RE
.IP "s(trip on/off)"
When on, the strip option examines each line of the
message header and, if the line begins with a specific keyword, the
line is not printed. When \fImsg\fR is invoked, strip is turned on and
the default list of keywords is via, remailed-from, remailed-to, message,
sender, mail, article, received and origin. The keyword strip list
can be changed by using the .msgrc file described below.
.IP "v(erbose on/off)"
Turn ``verbose'' mode on (or back off).  ``Verbose'' mode causes
.I msg
to complete command names and does not require the user to type a <CR>
to end a command.
.IP "x(tra options status)"
Print the names of the current mailbox and savebox, and the status of 
the
.I \.msgrc
options.
.RE
.IP "y = resend [msg seq]"
Prompts for messages and a list of addresses. Each message will be resent
to the list of addresses. The original From:, To: and Date: fields will
be unchanged; new Resent-from:, Resent-to: and Resent-date: fields will be
added to each message.
.IP "z = two window answer"
Calls the editor specified in the EDITOR environment variable
(or a site dependent choice if EDITOR is not defined)
with two file names.
The first file is the original message being answered, and the second
is the name of a draft file to be edited.
Some editor like Emacs will cause the them to be displayed
in two-window mode with the message displayed in the
top window and space for your reply in the bottom window.
When you exit from
the editor,
\fIsend\fR is called with the reply.
Two window mode will only work if the
editor you specify can handle the multiple file arguments in
a useful fashion.
This can be used by others if they make EDITOR a program
that copies the first file to the second (perhaps with
each line prepended with ``>>\ '' or some such) and then invokes
the EDITOR on the draft file.
.IP "! = sub-shell escape"
!<CR> forks a sub-shell. The type of shell is determined
from the environment variable SHELL. (\fIsh\fR
(1) is the default).
Control is returned to the \fImsg\fR program when the shell is terminated.
!command forks a sub-shell and executes the single command before
returning to the \fImsg\fR program.
.TP 
\@ = undigestify
.br
Breaks the messages contained in a single digest message into
separate messages.
.IP "/ = shorthand for delete current followed by a next command."
.IP "(null character) - mark message"
The current message number is saved and can be quickly returned to by typing
g(o) followed by a carriage return.
.IP ": (current date and time)"
The system "date" command is executed.
.IP "; (comment)"
Ignores the rest of the input line.
.RE
.sp
.SS \fIMsg\fR Tailoring
.PP
When invoked, \fImsg\fR looks for a file called .msgrc in the
home directory which can contain instructions to ``customize'' \fImsg\fR.
Instructions are read from .msgrc one line at a time. The instructions are:
.IP "bdots or nobdots"
turns on or off the printing of a dot for each message when reading or
writing the binary file.
.IP "bprint or nobprint"
turns on or off the printing of messages about reading or
writing the binary file.
.IP "ctrlfil or noctrlfil"
turns on or off the display of control characters as ^char.
.IP "draftdir directoryname"
specifies the directory which will be used for the draft files created
during the two-window answer command. If directoryname begins with a /,
it is interpreted as a full pathname; otherwise, it is interpreted relative
to the invokers home directory (default: <home directory>).
.IP "draftorig filename"
specifies the name of the file which contains the original text from the
two-window answer command and the message text if writemsg is enabled
(default: draft_original).
.IP "writemsg or nowritemsg"
turns on or off the writing of messages when send or answer is invoked. For
example, if you have the following in your .msgrc:
.nf
draftdir .d
draftorig d
writemsg
.nf
then, whenever send or answer is invoked, a copy of the original mail is saved
in <home directory>/.d/d and can be easily referenced using an editor while
in \fIsend\fR.
.IP "linelength \fInumber\fR"
sets the number of characters per screen line. Only used if
paging is on.
.IP "mdots or nomdots"
turns on or off printing of a dot for each message when reading messages
file.
.IP "nostrip"
turns off keyword stripping.
.IP "numberedlist or nonumberedlist"
determines whether messages listed with the "list" command are preceded by an
"index" of header lines and whether each message is shown with its number
in the listing.
.IP "pagesize \fInumber\fR"
sets the number of screen lines printed between pauses (default: 22).
.IP "paging or nopaging"
turns on or off paging.
.IP "quickexit or noquickexit"
determines whether msg exits immediately upon finding an empty mailbox.
.IP "quicknflag or noquicknflag"
turns on or off the ``quick new flag''.
Normally, a message is marked ``new'' until the
entire message is read; interrupting a message (by typing SIGINT while it
is being displayed) leaves it marked ``new'' and ``exit and update''
leaves ``new'' messages in the mailbox.
If this option is on, the ``new'' flag
is removed as soon as the message starts to be typed (default: off).
.IP "savebox filename"
specifies the name of the file to which
undeleted mailbox messages will be moved upon exit
(default: <home directory>/mbox).
.IP "strip keyword"
header lines beginning with keyword will not be printed.
A maximum of 40 keywords may be given
(one per strip).  ``strip'' must be
in lower case but keywords may be upper or lower case
(they are converted to lower case in \fImsg\fR) and will match upper or lower case
characters in header lines.
.IP "verbose or noverbose"
turns on or off verbose mode.  In verbose mode, single-character commands are
automatically "completed" or filled-out.  The verbose mode setting also affects
when you must type <CR> at the end of a command.  In non-verbose mode, you
must type <CR> after each command.  In either mode, you must type <CR> after
commands which are followed by a numerical message sequence or user-supplied 
file name or string.  In non-verbose mode, spaces are not significant and
commands must be abbreviated to their single letters (e.g. ``a'' for answer).
.IP "zbinsave or nozbinsave"
turns on or off the automatic binary file write whenever a SIGTSTP is received.
.PP
For example, if .msgrc contains
.PP
.RS
.nf
nobdots
paging
pagesize 20
strip via
strip cc
.fi
.RE
.PP
then all headers lines beginning with the words ``via'' and ``cc''
will not be printed and dots will not be printed when reading the binary file.
Paging is turned on and \fImsg\fR will pause after every 20 lines in a
message.
.PP
A binary file is created for each mailbox. The binary file
maintains status information and speeds up the subsequent reading of
mailboxes. The binary file for the mailbox mbox is called ._mbox.
.RE
.SH FILES 
The following are typical values which may vary on site by site basis.
Check with your system administration if you want to be sure.
.IP "/etc/passwd" 35
to find login directory
.IP "<login directory>/mailbox" 35
recently received mail
.IP "<login directory>/._mailbox" 35
binary status file for mailbox
.IP "<login directory>/mbox" 35
default output file
.IP "<login directory>/.msgrc" 35
\fImsg\fR tailoring file
.SH "SEE ALSO"
send(1), v6mail(1)
.SH "BUGS"
The user cannot use both \fIv6mail\fR and \fImsg\fR to read his mail
since \fIv6mail\fR does not update information for the binary file.
Unfortunately there is currently no way for \fImsg\fR to know this has happened.
Moral: use one or the other but not both.
directory>).
.IP "draftorig filename"
specifies the name of the file which contains the original text from the
two-window answer command and the message text mmdf/man/man1/rcvalert.1   444      0     12        2621  3664425167  10302 .tr  
.TH RCVALERT 1 "21 May 1986"
.SH NAME
rcvalert \- mail-receipt notification
.SH SYNOPSIS
.B rcvalert
[size]
.SH DESCRIPTION
.I Rcvalert
is run by
the mail delivery system, on the user's behalf, rather than by the user
directly.  A line entered in the user's 
.I .maildelivery
file causes
.I rcvalert
to run when mail is delivered.   The size argument tells the program the
size of the message.
.I Rcvalert
checks whether the user is logged in and the user's terminal may be written to.
If so,
.I rcvalert
prints a scan listing of the
message on the user's terminal.

The scan line rings the terminal's bell and then prints the
number of characters in the message and the contents of
the "From:" and "Subject:" components, if present.  If the "Subject:"
component is not present or is very short, some of the initial
text from the body of the message is included.
.SH EXAMPLE
A typical entry in the user's
.I .maildelivery 
file is:
.sp
*    \-    pipe    R    ``rcvalert $(size)''
.sp
You will need to give the full path to the rcvalert program.
if it is not in a directory normally searched by a vanilla /bin/sh.
.SH SEE ALSO
rcvtrip(1), maildelivery(5).
.SH FILES
.IP "${RCVDIR}/rcvalert" 35
typical location of rcvalert program
.IP /etc/utmp 35
to see who is logged in
.IP "<login directory>/.maildelivery" 35
user's delivery specification file
.SH "SEE ALSO"
rcvtrip(1), maildelivery(5)
.SH AUTHOR
David H. Crocker
r of screen lines printed between pauses (default: 22).
.IP "paging or nopaging"
turns on or off paging.
.IP "qmmdf/man/man1/rcvfile.1   444      0     12        3776  3656410711  10114 .tr ~
.TH RCVFILE 1 "26 December 1985"
.SH NAME
rcvfile \- put message into named file.
.SH SYNOPSIS
.B rcvfile
directory [
.B \-l
logfile ] [
.B \-m
]
.SH DESCRIPTION
This program is intended to be invoked
from the user's
.I \&.maildelivery
file in the manner suggested below.
Its purpose in life is to examine the
.I Subject:
field of the message and determine if this message
is a candidate for filing.
If so, the program skips over the message headers and
files the text into the file given in the subject line.
It can be invoked manually if desired, and a message piped into it.
.PP
A message is considered fair game if the subject line
has the word
.I rcvfile
as the first word of the
.I Subject:
field.
The
.I directory
argument is required.
The destination file name is created by concatenation of the
.I directory,
a "/", and the filename given in the subject field after 
The filename from the subject field is not allowed to contain
any ".." directory components.  If any are found, rcvfile aborts.
.PP
The
.I \-l
option sets the logfile where a record of
.I rcvfile
activity is made.  The file must already exist and be writable
to the recipient.
The
.I \-m
option enables the creation of missing directories in the pathname
of a file to be created.  They will be mode 0755.
.PP
The owner of the created file will be notified by mail of the fact that
a file has been delivered, with information about who sent it
and other interesting bits.
The owner may not be the recipient if the referenced file existed,
was owned by another user, and was writable.
If the file delivery fails for any reason, the message
will be delivered as normal mail.
.SH EXAMPLE
A typical entry in the user's
.I .maildelivery
file is:
.nf
subject    rcvfile      pipe    A    rcvfile
.br
.I or
.br
Addr      user=file    pipe    A    rcvfile
.fi
.sp
You may need to give the full pathname of rcvfile if it is not
in the search path of a vanilla /bin/sh.
.SH FILES
.IP "<login directory>/.maildelivery"
.SH SEE ALSO
maildelivery(5)
.SH AUTHOR
David H. Crocker
-lmmdf/man/man1/rcvprint.1   444      0     12        1373  3656410712  10321 .tr  
.TH RCVPRINT 1 MMDF
.SH NAME
rcvprint \- mail to printer.
.SH SYNOPSIS
This command is intended
to be run from the user's
.I \&.maildelivery
file.
Its purpose is to pipe the body of the message
into a program that will print the message on a line printer.
It tries a variety of different programs until
it finds one that will execute.
It then waits around to see how the program coped
and reports back to the local channel.
.SH EXAMPLE
An typical entry in the user's
.I .maildelivery
file is:
.nf
subject    printer    pipe    A    rcvprint
.fi
.sp
You may need to give the full pathname of rcvprint if it is not
in the search path of a vanilla /bin/sh.
.SH FILES
<login directory>/.maildelivery
.SH SEE ALSO
maildelivery(5)
.SH AUTHOR
.nf
David H. Crocker
dr      user=file    pipe    A    rcvfile
.fi
.sp
You may need to give the full pathname of rcvfile if it is not
in the search path of a vanilla /bin/sh.
.SH FILES
.IP "<login directory>/.maildelivery"
.SH SEE ALSO
maildelivery(5)
.SH AUTHOR
David H. Crocker
-lmmdf/man/man1/rcvtrip.1   444      0     12        6575  3656410714  10156 .TH RCVTRIP 1 "5 December 1984"
.SH NAME
rcvtrip \- notifies mail sender that recipient is away.
.SH SYNOPSIS
.B rcvtrip
[
.B \-d
] [
.B address
]
.SH DESCRIPTION
.I Rcvtrip
is run by MMDF at the user's
direction, rather than by the user directly.
A line entered in the user's .maildelivery file causes
.I rcvtrip
to be run when mail is delivered.
The user's must add an entry to the .maildelivery
file and place a reply message in a file named `tripnote'.
.PP
.I Rcvtrip
makes it possible for you
to notify the sender of the message that you are on
holiday and you won't be answering the message for some time.
.PP
.I Rcvtrip
reads incoming mail, checking for three things:
.RS
.IP "1)"
address(es) in `Reply-To:' or in `From:' and `Sender:',
.IP "2)"
the presence of the user's name in the `To:' or `cc:' fields, and
.IP "3)"
the contents of the `Subject:' field.
.RE
.PP
Any originator of mail who has not previously been notified
receives a reply, as long as the recipient is explicitly mentioned in
an address field (`To:' or `cc:') or is a first-order alias of one
of the `To:'/`cc:' addresses.  The contents of the reply
begins with some standard text; supplied by,
.I rcvtrip
followed by whatever
text the user has placed in the `tripnote' file.
The name(s) are recorded, along with the date and
time the message came in, whether it was answered,
and the first few characters of the subject.
This appears as:
.sp
.nf
+ jpo@nott.ac.uk	Wed Oct 8 16:08 >> about your last message
.fi
.PP
The plus sign indicates that a reply was sent.
.PP
The user has the option of not replying to specified individuals,
by placing their addresses into the file `triplog', one
address per line.  Also, the program will not reply to messages sent to
address lists, unless the recipient is explicitly mentioned
in an address field.  Only one reply is generated per
sender name; further mail from that person is not answered,
although the log entry is made and the mail accepted.
.PP
If the message contains a `Reply-to:' component, then this
address is replied to.  Otherwise a copy of the message
is sent to the addresses given in the `From:' component and the `Sender:'
component, if present.  
.PP
If a `\fB\-d\fR' flag is given,
debugging is enabled.  If an \fBaddress\fR argument is given and no `Sender:'
component is present in the message, the \fBaddress\fR is used in its place.
.SH FILES
.IP "<login directory>/tripnote" 25
The user-written reply message is located in this file.
.IP "<login directory>/triplog" 25
This contains a list of who sent a message, what was its subject,
when it arrived, and if a response was sent.  It can also be initialized by
hand to contain
the addresses, one per line, which are not to receive replies.
.IP "<login directory>/logfile" 25
If this exists, then diagnostic messages are put in it for debugging
purposes. It is not a good idea to leave this lying around if you're
going to be away for some time, as the output can be quite voluminous.
.IP "<login directory>/.maildelivery" 25
The user's mail delivery specification file
.SH EXAMPLE
A typical
entry in the file is:
.sp
.nf
*    \-    pipe    R    rcvtrip $(sender)
.fi
.sp
where the $(sender) argument is optional (but recommended).
You may need to give the full pathname of rcvtrip if it is not
in the search path of a vanilla /bin/sh.
.SH SEE ALSO
rcvalert(1), maildelivery(5)
.SH AUTHOR
.nf
Bruce Whittaker (Initial Version)
.br
Julian Onions (Total Rewrite)
se in \fImsg\fR) and will match upper or lower case
characters in header lines.
.IP "verbose or noverbose"
turns on or off verbose mmdf/man/man1/resend.1   444      0     12        3127  3656410707   7735 .TH RESEND 1 "27 December 1986"
.SH NAME
resend \- redistribute mail using the Resent- notation.
.SH SYNOPSIS
.B resend
[\-rw] [\-\-subargs] addresses [\-t addresses] [\-c addresses]
.SH DESCRIPTION
.I Resend
is responsible for taking as input a standard mail message,
adding the various Resent- components to it, and then handing
it over to \fIsubmit\fR.
.PP
The usual method of operation is to pipe a message into
.I resend
and supply on the command line the addresses to resend the message
to.  Aliases defined through the personal alias mechanism in
.I send,
may also be used with
.I resend.
See \fIsend\fR(1).
The default behavior can be changed by using the following
flags:
.IP \-r
This specifies that error returns for this message are not
wanted, if the message gets into problems with delivery, you
couldn't care less.
.IP \-w
This flag enables you to follow the delivery attempt. Submit
and its children will print out what they are doing.
.IP \-\-
Any argument starting in this manner is passed directly
to submit after losing the \-\-. You had better know what you are
doing!
.PP
After the flags have been processed, the address lists for
the message are built up. Normally all addresses are put onto
one Resent-To: line, but they can be broken up onto
several Resent-To: lines by prefixing a block of addresses
with the \-t flag. Alternatively the \-c flag will start
building up a list of Resent-Cc: addresses.
.I Resend
looks after all the other headers, such as Resent-Date, Resent-From
etc.
.SH FILES
<login directory>/.signature
.br
<login directory>/.sendrc
.SH "SEE ALSO"
send(1), v6mail(1), submit(8)
 \fIsend\fR(1).
The default behavior can be changed by using the following
flags:
.IP \-r
This specifies that error returns for this message are not
wanted, if the message gets into problems with delivery, you
couldn't care less.
.IP \-w
This flag enables you to follow the delivery attempt. Submit
and its children will print out what they are doing.
.IP \-\-
Any argument starting in this manner is passed directly
to submimmdf/man/man1/send.1   444      0     12       32155  3656410716   7431 .TH SEND 1 "4 January 1986"
.SH NAME
send \- send mail
.SH SYNOPSIS
.B send
[address\.\.\.] [\-a\ field:body] [\-b\ address\.\.\.]
[\-c\ address\.\.\.] [\-h\ host] [\-n] [\-s\ subject] [\-t\ address\.\.\.]
.SH DESCRIPTION
.PP
.I Send
sends messages to a user-specified list of recipients.
When invoked without arguments, it prompts for
the following information:
.sp 1
.RS
.nf
To:
cc:
Subject:
.fi
.sp 1
.RE
The expected user responses are:
.RS
.IP "1)" 4
To the "To:" and "cc:" prompts,
a list of valid user names (see below).
.IP "2)" 4
To the "Subject:" prompt,
a brief description of the subject of the message.
.RE
.PP
Copies of the message are delivered to recipients on
the "To:" and "cc:" lists.
A response to the "To:" prompt is required, unless blind carbon
copies (bcc) are specified.
A field may be left empty by typing a carriage return <CR>.
.PP
If the user's input for any of the above fields will require
more than one line, then when typing the input the user
should precede carriage
returns with a backslash (\\).  In any case, the user's input for any
one of these fields must not exceed 512 characters.
.sp
.SS Arguments
.PP
.I "Send"
may be invoked with arguments.
If they are specified, then the
contents of any resulting fields are displayed, before the
user is prompted for additional input.
Unless switches (a character preceded by a dash) are used, the
arguments are taken to be addresses for the "To:" field.
.RS
.IP "\-a" 4
causes the next argument to be added to the header of the 
message.  For example, ``\-a Reply-To:dcrocker''.  Enclose the
argument in quotes if it contains spaces.
.IP "\-b" 4
causes following arguments to be added to the "BCC:" field.
.IP "\-h" 4
causes the next argument to be used as the default host reference,
for addresses not explicitly containing a hostname.  When not
specified, the default is the local host.
.IP "\-c" 4
causes following arguments to be added to the "cc:" field.
.IP "\-s" 4
causes the next argument to be used as
the "Subject:" field's contents.
.IP "\-t" 4
causes following arguments to be added to the "To:" field.
.IP "\-d" 4
causes the state of the directedit
option to be changed (see "directedit" below).
Alone, "\-d" disables the directedit option.  "\-de" causes the directedit
option to use the \fIline editor\fR, and "\-dv" causes the directedit option
to use the \fIvisual (screen) editor\fR.
.IP "\-n" 4
prevents send from prompting for message text either directly or via
direct edit.  The user is immediately given the command prompt after
entering the header data.  Prompting for header data can be prevented
by giving null header arguments to send without contents.  For example:
.sp
.ti +.5i
\fIsend \-t "" \-c "" \-s "" \-n\fR
.RE
.sp
.SS "Valid User Addresses"
.PP
A valid address name takes one of the following forms:
.RS
.IP "1)" 4
A local Unix user's login name, such as "crocker."
.IP "2)" 4
A name which has been designated as a local alias, such as "support."
This usually will not look any different from a login name, except
that it may be longer than the standard 8-character limit.
.IP "3)" 4
The two-part "mailbox @ domain" address for a user on another machine.
.RE
.PP
Validation of names is done at posting time, and is performed
by the message delivery system, rather than by
.I "send."
All addresses
are made to have a domain reference.  If the user does not specify
a domain for an address, then the default host is assumed.
.PP
When the "To:", "cc:" and "Subject:" fields have been completed,
.I "send"
will prompt for the body of the message, with:
.sp 1
.ne 3
.RS
Type message; end with CTRL-D...
.RE
.PP
The user now enters the text of the message.
When the message is completed, or when the user wishes to insert
a file or make changes, s/he types a control-D at the beginning of a new line.
.I Send
then prompts the user for its next action.
.sp
.SS "User Options and Environmental Tailoring"
.PP
The user may define a file in his home directory called ".sendrc".  If such a
file exists,
.I "send"
will open the file and set options as specified,
otherwise the defaults will be used. Below are listed the options available
for user modification along with their default values.
.sp 2
.IP "aliases"
.RS
Allows the user to declare his own set of aliases for use with
.I "send"
and 
.I "resend."
Unless this file name begins with a '/', 
.I send
will look for this file in the user's home directory.
The format for this file is:
.RS
aliasname  user1@domain,user2@domain,user3@domain...
.RE
Note that each entry must be on a single line but each line may be
up to 511 characters long.
.RE
.IP "checker"
.RS
This will be the user's spelling checker while using \fIsend\fR.  The default
for this option is \fIspell\fR.
.RE
.IP "copyfile"
.RS
This is the file in which copies of messages will be stored if
requested by the user. Unless the first character of the supplied
filename is '/', the file will be created in the user's home
directory. The default for the copyfile is a file
called ".sent" in the users home directory.
See also 
"file, nofile, fileonquery" below.
.RE
.IP "directedit"
.RS
If this option is specified, the
.I send
program will immediately enter the editor after the "To:", "cc:" and "Subject:"
fields have been supplied.  The directedit option expects an argument
which specifies which editor to use.  "directedit v" causes the
visual editor to be used (see "veditor" below).  "directedit e" causes the
line editor to be used (see "editor" below).
.RE
.IP "draftdir"
.RS
If supplied, draft files will be created in this directory.
Otherwise, they will be created in the user's home directory.  
Draftdir should be a complete path name.
.RE
.IP "editor"
.RS
This will be the user's line editor while using 
.I "send."
The default for this option is system dependent, but normally set to
.I "ed."
If the environment variable ``EDITOR'' is set, it will be
taken as the default instead of the system default.
.RE 
.IP "file, nofile, fileonquery"
.RS
The presence of one of these options allows the users to control
the keeping of file copies.  ``File''
will result in a copy being kept of every
message sent by the user.  ``Nofile''
will turn off the file copy
option and no copies will ever be kept.  ``Fileonquery''
will cause
.I "send"
to query the user as to whether a copy should be kept or not.
.RE
.IP "signature, nosignature"
.RS
.I "Send"
will automatically fill in the ``From:'' field with your address.  The
form of the contents is either:
.RS
login@host  or  Full Name <login@host>
.RE
where ``login'' is your Unix login name, and ``host''
is the name of the Unix machine you are using.  The second form is the
default on
some systems.  On these systems, ``Full Name'' is derived from the system
password file.  The ``nosignature''
command forces \fIsend\fR to use the form:
.RS
login@host
.RE
.PP
You may personalize your messages by including a signature.  If
your 
.I ".sendrc"
file contains a signature line, its contents
will be used in the ``From:'' field, ahead of the address information.
The signature cannot contain any of the following characters
unless it is quoted with double quotes: `( ) < > @ , ; : \ \ " . [ ]' 
and it cannot be blank.
The program will verify the signature line for correctness at startup
and will complain if your signature is illegal.
The ``From:'' field then will appear as follows:
.RS
signature <login@host>
.RE
.RE
.IP "subargs"
.RS
Allows the user to specify additional flags for the \fIsubmit\fR program
('v' and 'm' are always passed). Probably not for general use.
.RE
.IP "veditor"
.RS
This is the \fIvisual editor\fR (screen editor) for use with
.I "send."
The default for
this editor is site dependent, but is usually some
version of vi or emacs.
If the environment variable ``VISUAL'' is set, it will be
taken as the default instead of the system default.
.RE
.sp
.PP
The format for a typical
.I ".sendrc"
profile is shown below:
.RS
.nf

copyfile sent_mail
draftdir .drafts
signature Joe Foobar
subargs w
veditor emacs
fileonquery
aliases joes_aliases
.fi
.RE
.SS "Commands"
.PP
.I "Send"
commands must be followed by a carriage return.
Only enough of a command to make it unique need be typed.
The following are valid commands:
.IP "?"
.br
displays a list of
.I "send's"
commands.
.IP "bcc"
.br
prompts for addresses for the ``BCC:'' Blind carbon copies field.
If any addresses are specified for Blind carbon copies, 
they receive a slightly different message than those listed
in ``To:'' or ``cc:''.  Their copy has a header component
named ``BCC:''; its
contents are the string ``(Private)''.
If the message had no ``To:'' addresses,
then the ``To:'' field will contain the string ``list:'' and there will be no
BCC field.  In addition, the ``To:'' and ``cc:'' fieldnames
are modified to prevent
recipients from
automatically replying to
anyone in the ``To:'' or ``cc:'' fields.
.sp
.RS
NOTE: If a message has normal and BCC recipients and there is an error
with a BCC address, the normal addresses will have been processed,
and the copy of their message sent, before the BCC address error
is detected.
.RE
.IP "bye"
.br
exits the
.I "send"
program.
If the user has not yet sent the message, the user is asked
to confirm the action.
A ``yes'' (or 'y') is required, followed by a <CR>, or the command will
be aborted and the user returned to the ``Command or ?:'' prompt.
.IP "check spelling"
.br
will invoke the spelling checker with the draft filename as the argument
to the checker program.  (See ``checker'' user option above.)
.IP "delete body"
.br
permits the user to abort
.I "send"
without saving the draft body.
If \fIsend\fR is aborted without giving the ``delete body'' command,
a copy of the draft will be kept as drft.nnnnn in the draft directory
specified by the ``draftdir'' option above.
This command can also be given at any time to erase the current draft
before inputing more text.  It requires confirmation for obvious reasons.
.IP "edit"
.br
will invoke the user's line editor (usually the Unix editor \fIed\fR(1))
and read in the user's message body for editing.
(See ``editor'' user option above.)
.IP "file"
.br
indicates the user wishes to append a file to the end of the message body.
.I "Send"
will prompt for file name.
.IP "header edit"
.br
allows the user to change the contents of the header fields
(``To:'', ``cc:'', and ``Subject:'').  The ``BCC:'' field is included, if not
empty.
For each header field,
.I "send"
shows the current field contents and then repeats the prompt.
The user may type in new input which will then become the contents of
that header field.
To leave the contents of the field unchanged, the user types
only a carriage return.
To delete a specific entry, the user types a minus sign followed
by the address to be deleted (e.g. ``\-dcrocker@udel'').
To simply delete the first entry of a list, the
user can type a minus sign (\-) followed by a carriage return.
To add to the field, the user types a plus sign (+), followed
by the text to be added, followed by a carriage return. For example, to
add a name to a field, one would type the command ``+user@domain''.
More than one address can be added at a time by listing several addresses
separated by commas (e.g. +u1,u2).
.IP "input more body"
.br
this command allows the user to type more text onto the end of the
message body, ending with a control-d on a new line (as before).
.IP "post"
.br
same as ``send''.
.IP "quit"
.br
same as ``bye''.
.IP "review message"
.br
retypes message in its current form.
.IP "send"
.br
posts the message to the message delivery system.
The message is delivered immediately to local users and queued for later
delivery to non-local users.
The sender is informed of the posting disposition of the message.
Each address
is listed and followed by its status, indicating that the
address is ok, or that there was a problem.
.RS
.PP
If all addresses are valid, the sender is then informed that the message
has successfully been posted for delivery.
If any addresses are found to be invalid, the remaining addresses are
still checked, but submission is aborted.  The user
is returned to command input,
so that he may change the addresses and make them legal.
.RE
.IP "set"
.br
allows the user to check the current values of option variables. If followed
by arguments, allows the user to change the current setting of variables.
The changed value lasts only for the current invocation of 
.I "send."
Option values are those listed above under ``User Options and Environmental
Tailoring''.  For example, the command ``set editor ex'' would
change the ``editor'' option to ``ex'' for the duration of the current
.I send
session.
.IP "vedit"
.br
will invoke the screen editor as specified by the ``veditor''
option and read in the user's message body, for editing.
(See ``veditor'' user option above.)
.IP "program run"
.br
allows the user to give a shell command.
.I "Send"
prompts for program.
The user's response is passed to the shell.
Control is returned to
.I "send"
when the command has been executed.
.IP "CTRL-D"
.br
same as ``bye''
.SH FILES
.IP "<login directory>/.sendrc" 38
user options
.IP "<login directory>/aliases" 38
user-supplied alias file
.IP "<login directory>/.sent" 38
sent messages
.IP "<login directory>/drft.XXXXXX" 38
body of current message
.SH "SEE ALSO"
v6mail(1), msg(1), resend(1)
.SH DIAGNOSTICS
All diagnostic messages are intended to be self explanatory.
ser returned to the ``Command or ?:'' prompt.
.IP "check spelling"
.br
will invoke the spelling checker with the draft filename as the argument
to the checker program.  (See ``checker'' user option above.)
.IP "delete body"
.br
permits the user to abort
.I "send"
without saving the draft body.
If \fIsend\fR is aborted without giving the ``delete body'' command,
a copy of the draft will be kept as drfmmdf/man/man1/v6mail.1   444      0     12       15733  3656410720   7674 .TH V6MAIL 1 "7 Nov 84"
.SH NAME
v6mail \- send mail to designated user(s)
.SH SYNOPSIS
.B v6mail 
[-yn ] 
.br
.B v6mail 
to-addressees ... [\-c cc-addressees ...] [\-f from] [\-g signature] [\-i] [\-r] 
.br
	[\-s subject-text] [\-t more-to-addressees ...] [\-\-submit-args]
.sp
(Sometimes installed as "mail" instead of "v6mail")
.SH DESCRIPTION
This document describes a program which is intended to
be upwards compatible with the original Unix Version 6
.I mail
command
from Bell Labs.  This program uses the MMDF system to perform
distribution.  Differences that are visible to the user lie in
the treatment of individual messages during the display
function, the creation of messages which conform with Arpanet
message format (syntax), and the availability of
additional switches during message creation.

The first form of the
.I v6mail
command (no arguments or exactly one which is a switch "\-y" or
"\-n") shows you any new mail which is in your
MMDF mailbox.  The original Bell Labs
.I mail
command lists the contents of the entire mailbox and
then asks if you want it all saved.  If you indicate yes,
then it is added (pre-pended) to the file "mbox" in your
login directory. The
.I v6mail
command behaves similarly,
except that it queries you about the handling of
.I each
message, rather than all of them together, and the messages
are appended to the
.I end
of mbox.  If a switch "\-y" or "\-n" is
specified, then it is taken to be the response to the "Save?"
queries.  To delete a message, you must answer the query
with a wording starting with "n".  Any other response (including
an emply line) will cause the message to be saved in mbox.
Aborting the command will leave your mailbox and mbox untouched.

The second form of the
.I v6mail
command creates and sends a
message to the indicated recipient(s).  Headers for the
message are created automatically and, optionally, as
indicated by certain command arguments as explained below.
These headers are created in the order implied by the
parameter list.  (Note that this can cause multiple 
header-components of the
same name to be created, separated by other components.)  A
"Date:" component is always created, containing the creation
date and time of the message.  The text body of the message
is read from standard input.  The message which is sent
conforms to current Arpanet standards and can be manipulated by
systems such as \fImsg\fR and MH.  
No draft message is created; so the message may
not be edited after initial creation and before it is sent.

When parameters are not preceded by a switch parameter (i.e.,
one with a dash as its first character) they are taken to be
primary addressees for the mail.  These addressees are then
listed in the message's "To:" component(s).  These parameters
must precede any switch parameters.

A recipient address may consist of the login name of a local
user (e.g., "Joe" or "Smith"), an alias which is
contained in the aliases file (e.g., Project-Leaders) or a full
network name (e.g., "Smith@Umn-CS.CSNET" or "Farber@UDel-EE.ARPA").
Obviously, use of network addresses requires the
availability of a network, although reference to the local
host (the one you are using) is always allowed.

.I V6mail
looks for an optional file called ".signature" in the user's home directory
and fills in the signature in the "From:" or "Sender:" component that 
corresponds to the user.  (Make sure that ".signature" contains
one line and that the contents are in double quotes if they contain
non-alphanumeric characters.)  

The "\-t" switch causes following parameters to (again) be
used as addressees in the "To:" header.  Normally, therefore,
the switch is not needed.

The "\-c" switch causes following parameters to be used as
addressees and listed in a "cc:" (carbon copy) header.  This
is in effect until the next switch parameter is encountered.

The "\-s" switch causes the next single parameter to be used
as the text of a "Subject:" component of the message.  Note
that to include more than one word as text, the text must be
surrounded by quotation marks.

The "\-g" switch causes the next single parameter to be used
as the text of the signature in the "From:" component of
the message.  Note that to include more than one word as text, 
the text must be surrounded by quotation marks.
Only the first occurence of either "\-f" or "\-g" is used.  

The "\-f" switch causes the next single parameter to be used
as the text of a "From:" component of the message.  If this
is used, then a "Sender:" component will automatically be
created and contain the address of the user who is actually
running the 
.I v6mail
program.  This is necessary to guarantee
authentication of mail.  This switch is useful when an agent,
such as a daemon process or a secretary, is logged in under
your name and wants to indicate that the agent, rather than you,
issued the note.  Only the first occurence of either "\-f" or "\-g"
is used.

The "\-i" switch instructs 
.I v6mail
to include a "Message-Id:"
component, containing a unique message identification string.
For current usage, this is not normally needed.

The "\-r" switch instructs
.I v6mail
to ignore any problems with delivery.  Normally, the "\-r" switch
is omitted and the sender is notified of any delays or failures
in the delivery process.

The "\-\-" switch can be used to pass arguments on to MMDF's \fIsubmit\fR
program which is responsible for actually processing
(sending) mail.  Only one "\-\-" switch setting may be used and
it may contain several values to be passed to \fIsubmit\fR.  If
more than one "\-\-" switch is specified, the last one will be
used.  It is generally unlikely that you will need to use
this feature; however, two switches might prove useful: "\-\-y"
causes the message to be sent to the user's terminal, if the
user is logged in and the user's screen is writable.  "\-\-w" lets
you watch the delivery process, allowing you to verify that
delivery actually occurs.  This switch, therefore, is useful
when the mail is important and you are not adequately certain
that the mail system can be trusted, such as when it is newly
installed on the system.  To use both switches, simply
specify "\-\-yw".
.SH EXAMPLES
.nf


"v6mail Farber" gives a message with the following headers:

.RS
Date: Sun, 24 Feb 84 12:43:11 EST
From: Dcrocker@UDEE
To: Farber@UDEE
.RE


"v6mail whittake farber \-c dcrocker" gives:

.RS
Date: Sun, 24 Feb 84 12:43:11 EST
From: Dcrocker@UDEE
To: whittake@UDEE, farber@UDEE
cc: dcrocker@UDEE
.RE


"v6mail farber \-c dcrocker \-t sincos@xerox.com \-g 'Dave Crocker' \-s 'test message'" gives:

.RS
Date: Sun, 24 Feb 84 12:43:11 EST
From: Dave Crocker <Dcrocker@UDEE>
Subject: test message
To: Farber@UDEE
To: sincos@xerox.com
cc: dcrocker@UDEE
.RE
.fi
.SH "SEE ALSO"
msg(1), send(1), submit(8)
.SH BUGS
.I V6mail
cannot be run on a mailbox that is being read with
\fImsg\fR(1).  It will print an error message and exit if a binary
file exists for a mailbox to prevent trashing the mailbox later
with \fImsg\fR.
.I V6mail
should be rewritten to maintain the binary file.
.PP
.I V6mail
doesn't like empty .signature files.
UDel-EE.ARPA").
Obviously, use of netmmdf/man/man3/   755      0     12           0  3664425332   6306 mmdf/man/man3/llog.3   444      0     12       15435  3642074472   7443 .TH LLOG 3
'ta .8i 1.6i 2.4i 3.2i 4.0i 4.8i 5.6i 6.3i
.SH NAME
llog \- library logging package
.SH SYNOPSIS
.nf
ll_open  (loginfo)
    struct ll_struct *loginfo;
ll_hdinit (loginfo, pref)
    struct ll_struct *loginfo;
    char *pref;
ll_init  (lloginfo)
    struct ll_struct *loginfo;
ll_log   (loginfo, verbosity, printf args...)
    struct ll_struct *loginfo;
    char verbosity;
ll_err   (loginfo, verbosity, printf args...)
    char verbosity;
    struct ll_struct *loginfo;
ll_close (loginfo)
    struct ll_struct *loginfo;

struct  ll_struct
{
 char *ll_file;  /* path name to logging file        */
 char *ll_hdr;   /* text to put in opening line      */
 char  ll_level; /* max verbosity level              */
	LLOGFAT  /* Fatal error                      */
	LLOGTMP  /* Temporary (minor?) error         */
	LLOGGEN  /* General information              */
	LLOGBST  /* Basic statistics                 */
	LLOGFST  /* Full statistics                  */
	LLOGPTR  /* Trace of program phases          */
	LLOGBTR  /* Trace of more basic activities   */
	LLOGFTR  /* Full program trace               */
	LLOGMAX  /* Maximum possible value           */
 char  ll_msize; /* max log size, in 100's of blocks */
 char  ll_stat;  /* assorted switches                */
	LLOGCLS  /* Close after each write           */
	LLOGCYC  /* Cycle to beginning when log full */
	LLOGWAT  /* Wait, if log locked and LLOGCLS  */
	LLOGSOME /* Occasional logging               */
 char  ll_fd;    /* * CHAR * holds file descriptor   */
 FILE *ll_stream;/* Standard IO stream pointer       */
};
.fi
.SH DESCRIPTION
.PP
This package is intended to perform standardized information
logging for programs.  The time of an entry is recorded in
addition to its text.
.SS Format of Entries
.PP
Depending on the mode of logging (whether the
log is left open between ll_log() calls or is re-opened to make each
entry, as indicated by the LLOGCLS flag) the date, hour and
loginfo.llhdr string are recorded on a header line when the log is
opened (if LLOGCLS is off) or on the logging line (if LLOGCLS is
on).  In addition, the current minute and second are recorded,
followed by the actual log text.  Each log entry is started on a
new line.  The text of the entry is entered with a printf(), called
with parameters passed to the ll_log() function.
.SS Access to Log Files
.PP
Multiple logs can be accessed simultaneously
by the same process, since all of the state information for a
single log is kept in the user-maintained structure, llogstruct,
which is included as a parameter when calling llog functions.
While opened by one process, a single log cannot be shared with
another process, since logging files are opened with exclusive
access (the specific locking mechanism depends on the version of
Unix you are running).
The LLOGCLS flag is intended to help circumvent
this protection.  Also, the LLOGWAT flag may be used to cause
ll_open() to repeatedly retry opening the log, if the failure to
open it was due to its being used by another process.  Ll_open()
will try up to sixty times, waiting two seconds between tries;
after that, it will return.  The failure is treated as temporary
if LLOGCLS is used, implying infrequent logging expected;
otherwise the failure is treated as permanent.
.SS Limiting Size of Log Files
.PP
The package can impose a size limit
to log files.  If lloginfo.llmsize is zero, size-limiting is NOT
imposed, otherwise llog will not allow a log to grow larger (25 *
lloginfo.llmsize) blocks.  If the log is being held open between
entries and cycling is allowed (i.e., LLOGCYC is set and LLOGCLS
is not) then when ll_init() is called (by ll_open() or by the user)
during successive uses, entries will be made starting from the
beginning of the file.  If the LLOGCLS bit is set or if the
LLOGCYC bit is not set, then the log is marked as unusable.

Ll_open() attempts to open the logging file.  It returns the return
value from its open() call.  It does NOT attempt to create the file
if the file does not exist.  The result of the open() call is placed
in the ll_fd member of the llogstruct structure.  The using
program should ensure that ll_fd initially has the value zero.
Llog will simply return, if ll_fd is -1; therefore the caller can
ignore failures to open the log.  Also, the existence of the
logging file can be used as a convenient external switch for
turning logging on and off.

Entries are appended to the end of any old entries in the file,
except when cycling occurs.  If the LLOGCLS bit is set in the
loginfo.llstat field, then the file is opened only long enough to
make an entry; this makes it more reasonable for multiple
processes to share a log into which relatively few entries are
made, such as when only error messages are recorded.

Ll_init() is to be used when the logging file is already opened
(e.g., when the file descriptor has been inherited from a parent
process) instead of calling ll_open().

Ll_log() actually makes the log entries.  If the log is not already
opened and lloginfo.llogfd is 0, ll_log() will call ll_open().  (Note
that this means that the calling program usually does not need to
call ll_open(), unless there is a concern about locking the file.)
The ll_level value indicates how much logging the caller desires.
When called, ll_log() will not make the log entry if the value of the
verbosity argument is GREATER than than the value of ll_level.  This
allows, for example, distinction between mandatory and debugging
information, with many levels of (256) information possible.  As a
guide, the list shown in the synopsis can be used.

Ll_log() uses printf() to make the entries.  There is a limit of ten
(10) arguments that may be passed to it.  The third argument to
ll_log() becomes the first argument to printf().  Flush() is then called
so that the log is always up-to-date; this is deemed more
important than fully minimizing write calls, and in any event, is
required to allow ll_log() to use the printf() mechanism in a way that
does not interfere with its use elsewhere in the process.

Ll_close() closes the logging file and marks ll_fd as clear (i.e.,
resets it to be zero).  Note that this normally need not be
called, since the file will automatically be closed when the
process exits.  However, it is necessary to close the log, during
execution, if a child or some other process is expected to
open the same log independently.  Calling ll_close() also always
clears the file descriptor, making it possible for a a new try at
opening the log, if it failed previously.
.SH DIAGNOSTICS
.PP
Ll_open() returns the value of its open call, if that fails;
otherwise it returns the value of its call to ll_init().  Ll_log() returns
the value of its flush() call, or zero if ll_fd is -1; if flush()
returns less than zero, then ll_log() closes the log.  Ll_close()
returns the value of its close() call.
.SH AUTHOR
.IP "Dave Crocker" 20
Dept. of E.E., Univ. of Delaware
umvent
this protection.  Also, the LLOGWAT flag may be used to cause
ll_open() to repeatedly retry opening the log, if the failure to
open it was due to its being used by another process.  Ll_open()
will try up to sixty times, mmdf/man/man3/ml_send.3   444      0     12        5427  3664425171  10107 .TH ML_SEND 3
'ta .8i 1.6i 2.4i 3.2i 4.0i 4.8i 5.6i 6.3i
.SH NAME
Simple Mail Submission (ml_)
.SH SYNOPSIS

.nf
#include "util.h"
#include <stdio.h>

ml_init (return, sender, from, subject)
    int return,
        sender;
    char *from,
         *subject;
ml_to ();
ml_cc ();
ml_adr (address)
    char *address;
ml_aend ();
ml_tinit ();
ml_txt (text);
    char *text;
ml_file (fil-text);
    FILE *file-text;
ml_end (end-stat)
    int  end-stat;
ml_1adr (return, sender, from, subject, address);
.fi
.RE
.SH DESCRIPTION
.PP
These procedures may be used for simple mail submission, providing
a subroutine interface with the \fIv6mail\fR command.
The basic sequence is:

.RS
.nf
# include "util.h"

ml_init (YES, NO, "My Name", "The Subject");
ml_adr ("destination address 1");
ml_adr ("destination address 2");
...
ml_aend ();
ml_tinit ();
ml_txt ("Some opening text");
ml_txt ("maybe some more text");
ml_file (stdio-file-stream-pointer);
if (ml_end (OK)) != OK)
{   error-handling code }

.fi
Arguments that are to be defaulted should be zero.  All routines
return a status value of OK or NOTOK;
.RE
.PP
ml_init()'s arguments specify:
.IP "a)"
whether return-mail (to the sender) should be allowed,
.IP "b)"
whether a Sender field should be used to specify the
correct sender (contingent on next argument),
.IP "c)"
text for the From field, and
.IP "d)"
text for the Subject field.
.PP
If (b) is NO, then (c)'s text must contain the
correct sender information, or else just contain the "name" portion
of the address.  
.PP
Ml_to() and ml_cc() are used to switch between "To:" and "cc:"
addresses.  Normally, only "To:" addresses are used and, for this,
no ml_to() call is needed.
.PP
Ml_adr() specifies the text of one address.
An "address" is whatever is valid for your system, as if you
were typing it to the \fIv6mail\fR command.  Ml_aend() signals the end
of address specification, and ml_tinit() signals the start of
message text submission.
.PP
Ml_txt() directly enters raw text.  Ml_file() passes a stdio
file pointer, to be used for file text submission; all of the
(remaining) contents of the file are submitted.
Any number of ml_txt() and ml_file() calls may be made and they
may be freely mixed. They just
append text to the message.  The text must contain its own
newlines.
.PP
Ml_1adr() is provided as a convenient way to initiate mail
when there is only one addressee.  Its arguments are the same
as for ml_init(), except there is an extra argument for
the address string.
It may be used, with the earlier example,
to replace the ml_init(), ml_addr(), ml_aend(), and ml_tinit()
calls.
.PP
A special version of the \fIv6mail\fR command, supplied with MMDF, 
is required by the ml_send library.
.SH FILES
.IP "lib/libmmdf.a" 25
contains the ml_send library.
.SH AUTHOR
.IP "Dave Crocker" 20
Dept. of E.E., Univ. of Delaware
se() also always
clears the file descriptor, making it possible for a a new try at
opening the log, if it failed previously.
.SH DIAGNOSTICS
.PP
Ll_open() returns the value of its open call, if that fails;
otherwise it returns the vammdf/man/man3/mmdf.3   444      0     12       12214  3642035646   7421 .TH MMDF 3
'ta .8i 1.6i 2.4i 3.2i 4.0i 4.8i 5.6i 6.3i
.SH NAME
MMDF (mm_) Mail Submission and Pickup
.SH SYNOPSIS
.nf
#include "util.h"
#include "mmdf.h"

.TP 30
mm_init ()
/* get ready */
.TP 30
mm_end (type)
/* done with mail */
.nf
    int          type;    /* OK or NOTOK */
.fi
.TP 30
mm_sbinit ()
/* initialize submission */
.TP 30
mm_sbend ()
/* done with submission */
.TP 30
mm_pkinit (chname)
/* initialize for pickup */
.nf
    char         *chname;    /* Channel being picked up */
.fi
.TP 30
mm_pkend ()
/* done with pickup */
.TP 30
mm_rrply (valstr, len)
/* get a reply */
.nf
    struct rp_bufstruct *valstr;
    int          *len;
.fi
.TP 30
mm_wrply (valstr, len)
/* send a reply */
.nf
    struct rp_bufstruct *valstr;
    int          len;
.fi
.TP 30
mm_rrec (linebuf, len)
/* read one "record" */
.nf
    char *linebuf;
    int  *len;      /* sizeof buf on call, nread on return */
.fi
.TP 30
mm_rstm (buffer, len)
/* buffered text read */
.nf
    char *buffer;
    int  *len;      /* sizeof buf on call, nread on return */
.fi
.TP 30
mm_wrec (linebuf, len)
/* write a record/packet */
.nf
    char *linebuf;
    int  len;
.fi
.TP 30
mm_wstm (buffer, len)
/* buffered text write */
.nf
    char *buffer;
    int  len;
.fi
.TP 30
mm_rinit (info, retadr)
/* message initialization */
.nf
    char *info,
         *retadr;
.fi
.TP 30
mm_radr (host, adr)
/* read an address */
.nf
    char *host,
         *adr;
.fi
.TP 30
mm_rtxt (buffer, len)
/* buffered msg text read */
.nf
    char *buffer;
    int  *len;      /* sizeof buf on call, nread on return */
.fi
.TP 30
mm_winit (vianet, info, retadr)
/* initialize for one message */
.nf
    char *vianet,
         *info,
         *retadr;   /* if == 0, use 'r' or 's' in info */
.fi
.TP 30
mm_wadr (host, adr)
/* write one address */
.nf
    char *host,
         *adr;   /* adr@host, if host is null */
.fi
.TP 30
mm_waend ()
/* end of address list */
.TP 30
mm_wtxt (buffer, len)
/* buffered msg text write */
.nf
    char *buffer;
    int  len;
.fi
.TP 30
mm_wtend ()
/* end of message text */
.SH DESCRIPTION
The
.I mm
package is intended to simplify a user program's task of interacting
with MMDF.  This will primarily be used for message submission (posting).
The basic sequence of calls is to
initialize the package, initialize for the submission of one
message, indicate the addresses for the message and receive
validations for them, send the message text, terminate the message
and receive a final validation for the message, and then optionally
repeat submission from the point of initializing for submission.
.PP
It is important to note the relationship between the info and sender
arguments to mm_winit().  If the info strings contains
either `r' or `s', then the sender must be a (char *)0.
.SS "Sample Submission Code"
.PP
The following depicts a typical use of calls to
.I mm.
(No attempt is made to provide for the
way a user might choose to acquire the address and text
information needed for the variables "sender", "host", "adr", or
"buffer".)

.nf
.RS
.ne 15
send_manage ()
{   /* submit a batch of messages */
    if (rp_isbad (mm_init()) ||
	rp_isbad (mm_sbinit()))
	    abort-submit;
	    /* initialize for submission */

    while (more-messages)
    {   /* process each message */
	if (rp_isbad (do_a_message ()))
	    abort-submit;
    }
    return (RP_OK);
}

.ne 17
do_a_message ()
{   /* process a single message */
    if (rp_isbad (mm_winit("", "mv", sender)) ||
	rp_isbad (mm_rrply(&thereply, &len) ||
	rp_isbad (thereply.rp_val))
	abort-submit;
	/* initialize the message */

    while (more_addresses)
    {   /* step through address list */
	if (rp_isbad (do_an_address ()))
	    abort-submit;
    }
    if (rp_isbad (mm_waend ()))
	abort-submit;
	/* indicate no more addresses */

    return (do_text ());
}

.ne 31
do_an_address ()
{   /* process a single address */
    struct rp_bufstruct thereply;
    int len;

    /* somehow begin with a host and adr */

    if (rp_isbad (mm_wadr(host, adr)) ||
	rp_isbad (mm_rrply (&thereply, &len)))
	    { abort-submit; }
	/* send address & get status */

    switch (rp_gval (thereply.rp_val))
    {       /* was address acceptable? */
	case RP_AOK:    /* yes */
	    { note-the-acceptance }
	    break;

	case RP_NO:     /* not acceptable */
	case RP_USER:
	case RP_NDEL:
	case RP_AGN:
	case RP_NS:
	case RP_NOOP:
	case RP_PARM:
	    { note-failure; }
	    break;

	default:        /* unexpected reply value */
	    { probably-should-treat-as-illegal-value; }
    }
    return (RP_OK);
}

.ne 37
do_text ()
{
    struct rp_bufstruct thereply;
    int len;

    len = sizeof buffer;
    while (more-text)
    {   /* somehow get buffer's text and len */
	if (rp_isbad (mm_wtxt (buffer, len)))
	    abort-submit;
	len = sizeof buffer;
    }       /* send a chunk of text */

    if (rp_isbad (mm_wtend ()))
	abort_submit;

    if (rp_isbad (mm_rrply (&thereply, &len)))
	abort-submit;

    switch (rp_gval (thereply.rp_val))
    {   /* was text acceptable? */
	case RP_OK:
	case RP_MOK:
	    { note-acceptance; }
	    break;

	case RP_NO:
	case RP_NDEL:
	case RP_AGN:
	case RP_NS:
	case RP_NOOP:
	    { note-failure; }
	    break;

	default:
	    { treat-as-illegal-value }
	    break;
    }
    return (RP_OK);
}
ore the caller can
ignore failures to open the log.  Also, the existence of the
logging file can be used as a convenient external switch for
turning logging on and off.

Entries are appended to the end of any old entries in the file,
except when cycling occurs.  If the LLOGCLS bit is set in the
loginfo.llstat field, then the file is opened only long enough to
make an enmmdf/man/man3/phs.3   444      0     12        4451  3656410722   7251 .TH PHS 3
.SH NAME
Note the MMDF transmission phase (phs_)
.SH SYNOPSIS
.nf
#include "util.h"
#include "mmdf.h"
#include "ch.h"
#include "phs.h"

phs_note (thechan, phase)
    Chan *thechan;
    int phase;
phs_msg  (thechan, naddrs, len)
    Chan *thechan;
    int nardr;
    long len;
time_t  phs_get (thechan, phase)
    Chan *thechan;
    int phase;

phases:
    PHS_CNSTRT  /* start connecting to site */
    PHS_CNGOT   /* got connection to site */
    PHS_CNEND   /* end connection to site */
    PHS_RESTRT  /* start reading (sending) mail from site */
    PHS_REEND   /* end reading (sending) mail from site */
    PHS_WRSTRT  /* start writing (picking up) mail to site */
    PHS_WREND   /* end writing (picking up) mail to site */
.fi

.SH DESCRIPTION
.PP
These routines are used to record the progress made during a
connection between MMDF and another mail system.  Sessions
are divided into the phases indicated above.  External
time-stamps are maintained, for each channel, for each phase.
The stamps are primarily used by the
\fIcheckque\fR(8)
program, but notations also are made in the channel log
(see logs(5)).  The time-stamps are maintained by setting the modified
time of files in channel subdirectories of the "phsdfldir" directory specified
in the MMDF configuration.
One file is kept per time-stamp (see FILES below).
.PP
Phs_note()
records the indicated event.
Phs_msg() is a special case; it records the transmission of
one message, noting the number of addresses for it and the
number of characters.  This is recorded only in the channel
log and is not given any external time-stamps.  Phs_get() returns
the time-stamp of the specified channel and phase.  If no time-stamp
exists for the specified channel and phase, phs_get() returns zero.
.SH "FILES"
.IP "<phsdfldir>/<channel>/cstrt" 30
PSH_CNSTRT time-stamp file
.IP "<phsdfldir>/<channel>/cgot" 30
PSH_CNGOT time-stamp file
.IP "<phsdfldir>/<channel>/cend" 30
PSH_CNEND time-stamp file
.IP "<phsdfldir>/<channel>/rstrt" 30
PSH_RESTRT time-stamp file
.IP "<phsdfldir>/<channel>/rmsg" 30
PSH_REMSG time-stamp file
.IP "<phsdfldir>/<channel>/rend" 30
PSH_REEND time-stamp file
.IP "<phsdfldir>/<channel>/wstrt" 30
PSH_WRSTRT time-stamp file
.IP "<phsdfldir>/<channel>/wmsg" 30
PSH_WRMSG time-stamp file
.IP "<phsdfldir>/<channel>/wend" 30
PSH_WREND time-stamp file

t rp_bufstruct thereply;
    int len;

    /* somehow begin with a host and adr */

    if (rp_isbad (mm_wadr(host, adr)) ||
	rp_isbad (mm_rrply (&thereply, &len)))
	    { abort-submit; }
	/* send address & get statmmdf/man/man3/tai.3   444      0     12       12134  3642035017   7244 .TH TAI 3
.SH NAME
tai_init, tai_get, tai_end \- get site tailoring information
.SH SYNOPSIS
.nf
tai_init (file)
    char file[];

tai_get (maxargs, argv)
    int maxargs;
    char *argv[];

tai_end ()
.fi
.SH DESCRIPTION
The
.I tai
package is designed to acquire site-dependent information, from
an external, clear-text file.  It supports a simple file syntax.
.ne 4
.SS "File Format"
.PP
Tailoring information comprises a set of lines.  Lines comprise
a set of fields.  It is usual for the first field on the line to
be treated as the name of the line.
Fields may be separated by space, horizontal
tab, comma, semi-colon, colon, or equal sign.  These separators
are treated similarly.  However, space and tab are skipped, at
the beginning of a field.  Also,
if two fields are separated by an equal sign, they are assumed
to be a key/value pair and are treated somewhat differently by
.I tai_get().
.PP
Fields are formed according to a syntax that is similar to C programs.
A sequence
of printable, non-special characters is a field.  To include
a special character, such as space or comma, the character
must be preceded by a back-slash.  The sequence backslash, octal-string
specifies a single eight-bit character (e.g, \\040 indicates space).
If a field is surrounded by quotation marks, only quotation mark
and backslash are treated as special.
.PP
The following shows how different argument lines are parsed.  A file,
.ne 6
containing:
.RS

.nf
loc UDel-Relay
que /usr/mmdf/que/home
chan a,, arpa\\040net\\ foo=bar
chan b backup blat="gelp,blat=boop"
.fi
.RE

is parsed into the follow four sets of argument
.ne 6
lists:
.RS

.nf
\'loc' 'UDel-Relay'
\'que' '/usr/mmdf/que/home'
\'chan' 'a' '' '=' 'arpa net foo' 'bar'
\'chan' 'b' 'backup' '=' 'blat' 'gelp,blat=boop'
.fi
.RE
.PP
The formal syntax for a tailoring file is given below, in modified
BNF.  Asterisk indicates repetition zero or more times.
Parentheses surround
.ne 10
alternatives.
.RS

.nf
file     =  *line
line     =  *field "\\n"
field    =  *(SPACE / TAB) value *value SPECIAL
value    =   CHAR / echar / qstring
qstring  =   '"' *qvalue '"'
qvalue   =   CHAR / DELIM / echar
echar    =   "\\" (SPECIAL / FORMAT / octal)
SPECIAL  =   DELIM / '"' / "\\"
DELIM    =   SPACE / TAB / "," / ";" / ":"
CHAR     =  {Any ASCII character not a SPECIAL}
FORMAT   =  'n' / 'r' / 'f' / 'b'
octal    =   ochar [ochar [ochar]]
ochar    =  {Digit 0-7}

.fi
.RE
.ne 5
.SS "Procedure Calls"
.PP
.I tai_init()
initializes the tailoring package.  Its argument is the name of
the file that contains tailoring information.  Since the name is
not built into the package, it is possible to have several different
tailoring files.  It returns 0 on normal termination.
.PP
.I tai_get()
acquires and parses the next line of the tailoring file.  The first
argument to the procedure is the maximum number of
tailoring arguments (fields) that can be accepted from the line.
The second procedure argument is an array which will hold character
pointers to the tailoring arguments.  The list of pointers is terminated
by a zero-valued pointer.
.PP
.I tai_get()
returns the actual number of arguments that were parsed and returns
0 when it has reached then end of the file.
The data are in permanent storage, so that they do not have to be copied
before the next call to
.I tai_get().
.PP
The argument list is built in the same sequence as the fields are
listed in the argument line.  Separator characters are not
returned, except for equal-sign.  When an equal-sign is used,
three entries are consumed in the argument pointer array.  The first
points to the string "=", the second is
for the key and the third is for the value.
(I.e., equal-sign is treated as an infix operator, but the parsed
fields are stored in prefix form.)
.PP
When initialization is complete, call
.I tai_end()
for cleanup.  It returns 0 on normal termination.
.ne 4
.SS "Usage"
.PP
The user program should call
.I tai_init()
and then should iteratively call
.I tai_get()
until all of the tailoring information has been processed.
If there are several different software modules that need to
be initialized, then the information returned by
.I tai_get()
can be passed consecutively to the modules' initialization
routines, until one of them returns a value indicating that
it has "consumed" the information.  The following code demonstrates
the use of this package:
.RS

.nf
.ne 30
#include "util.h"

main ()
{
    char *taiargs[20];
    int nargs;
    register int ind;

    if (tai_init ("init.tai") < OK)
    {                       /* initialize               */
	perror ("tai_init");
	exit (-1);
    }

    while ((nargs = tai_get (20, taiargs)) > 0)
	for (ind = 0; ind < nargs; ind++)
	{                   /* who uses it? */
	    if (user_init (taiargs))
		continue;   /* user-level information   */
	    if (util_init (taiargs))
		continue;   /* the utilities package    */
	    /* was not consumed; ignore it              */
	}

    if (nargs < 0)          /* we had some problem      */
	perror ("tai_get");
    tai_end ();
    exit ((nargs < 0) ? -1 : 0);
}
.fi
.RE
.SH  DIAGNOSTICS
All routines return -1 on error.
.SH  AUTHOR
Dave Crocker (DCrocker@UDel)
.br
 7 Jul 82  Initial version
eceded by a back-slash.  The sequence backslash, octal-string
specifies a single eight-bit character (e.g, \\040 indicates space).
If a field is surrounded by quotation marks, only quotation mark
and backslash are treated as special.
.PP
The following shows how different argument lines are parsed.  A file,
.ne 6
containing:
.RS

.nf
loc UDel-Relay
que /usr/mmdf/que/home
chan a,, arpa\\040net\\ foo=bar
chan b backup bmmdf/man/man5/   755      0     12           0  3664425332   6310 mmdf/man/man5/files.5   444      0     12        7327  3642035027   7566 .TH FILES 5 "21 January 86"
'ta .8i 1.6i 2.4i 3.2i 4.0i 4.8i 5.6i 6.3i
.SH NAME
MMDF FILES -- overview
.SH SYNOPSIS
Names, locations, and file modes for MMDF programs and files
.SH DESCRIPTION
.PP
MMDF files separate into documentation, source code,
mail queue, channel host name tables, programs, and logs.
.PP
Pathnames to constructed files (i.e., everything other than source code
and documentation) are derived as a function of a default directory
specification and the specific filename.  The default directory
can be overridden, for a specific file, by having the filename
be a fully-specified path (starting with a slash).  The default
directories are defined in
.I conf.c.
.SS Documentation
.PP
Documentation is located in the MMDF distribution tape directories
.I doc
and
.I man.
The manuals are subdivided by directories for each part of mail software,
according to the V7 manual sections.  
.SS "Source Code"
.PP
The layout and contents of the source directories are described
in the document ``Installing and Operating MMDFII'' (normally
in doc/administrators/* in the MMDF source directories.
.SS Queue
.PP
The queue, for storing mail in transit, is described in
.I queue(5).
.SS Tables
.PP
The tables, for listing extensions to the list of local mailbox names
and names of known hosts, are described in
.I tables(5).
.SS Logs
.PP
MMDF system logs are described in
.I logs(5).
.SS Programs
.PP
It is assumed that a login and uid have been assigned to MMDF.  
The default assumption, throughout the distribution tape, is that
the login name is `mmdf'.  The default location for MMDF programs
is specified in cmddfldir[], for MMDF ``commands'' and in chndfldir[],
for MMDF channel programs which are invoked by 
.I deliver.
Both variables are specified in
.I conf.c.
.RS
.IP submit
Enqueues mail.  Its location is specified by pathsubmit[].
Its protection
modes should be 04711, and it should be owned by MMDF, so that it can
.I chdir(2)
past the quedfldir[] locking directory for the queue.
.IP deliver
Manages message transmission.  Its location is specified by pathmailer[].
Its protection
modes should be 04711, and it should be owned by root.
.IP slave
Runs as the server process, when other sites call over a phone
channel.  Protection should be 0711, since it needs no special
privileges.
.IP "channel programs"
These perform actual data transfers, at the invocation of
.I deliver.

They are named to reflect their actual task
Their locations are specified by the channel
definitions in
.I conf_chan.c
and default to the chndfdir[] directory.
It is very important that the directory containing the channel
programs be accessible only by MMDF processes, or else anyone could
invoke one and send unverified mail; hence make the "chans" directory
have protection 0700 and be owned by MMDF.
The ownership and protection modes of the channel programs
depend upon the resources they
consume.  For example, the program which delivers mail to the local
machine,
.I ch_local,
must be 04711 and owned by root, so that it can manipulate recipient's
protected mailboxes.  At some sites, telephone dial-out may be a protected
resource, so that
.I ch_phone
should be 04711 and owned by the same user as owns the dial-out.
.IP cleanque
Garbage-collects the queue.  It should be run regularly,
such as overnight by
.I cron.
Its protection
modes should be 04711, and it should be owned by root.
.IP setlogs
Resets the logs and may save message and channel statistics.  It is
a shell file and should be run as MMDF.  It should be run regularly,
such as daily, by
.I cron,
unless system performance needs close monitoring.
In this case, the logs should be retained longer.
.RE
.SH "SEE ALSO"
logs(5), queue(5), tables(5), cleanque(8), deliver(8), submit(8)
.br
\fIInstalling and Operating MMDF II\fR
re specified in
.I conf.c.
.RS
.IP submit
Enqueues mail.  Its location is specified by pathsubmit[].
Its protection
modes should be 04711, and it should be owned by MMDF, so that it can
.I chdir(2)
past the quedfldir[] locking directory for the queue.
.IP deliver
Manages message transmission.  Itmmdf/man/man5/logs.5   444      0     12       10524  3642076037   7447 .TH LOGS 5
'ta .8i 1.6i 2.4i 3.2i 4.0i 4.8i 5.6i 6.3i
.SH NAME
MMDF Logs \- Overview
.SH SYNOPSIS
System status, error, and statistics logging for MMDF
.SH DESCRIPTION
.PP
MMDF maintains run-time logs at several levels of activity.
The primary distinction is among message-level, channel-level,
and link-level information.
A standard package is used to perform all logging; it is documented in
llog(3).
The location
for logs is specified by the ll_file member of the ll_log structure definition in
.I conf.c.
All logging settings can be overridden by entries in the runtime tailor file
also.
In MMDF, that member is merged with logdfldir[] to determine the full
pathname to the log.
Logs
should be protected so that any process may write into them, but
only MMDF may read them (i.e., 0622).
.PP
The logging files may be the source of some confusion, since
the
llog
package entails some complexity.  Its three critical
factors are coordinated access, restricted file length, and
restricted verbosity.
.PP
The length of a logging file can be limited to 25-block
units.  This is extremely important since files can grow VERY
long over a period of time, especially if there are many long
messages sent or very verbose logging.
.PP
Restricted verbosity is a way of easily tuning the amount of
text entered into the log.  This is probably the one parameter
you need most to worry about.  Set to full tilt (LLOGFTR), MMDF
get noticeably slower and i/o bound.  It also does a pretty good
job of showing what it is doing and hence helping you figure out
the source of errors.  When you get to trust the code, setting
the logging level down is highly recommended.  The lowest would
be LLOGTMP or LLOGFAT, for temporary or fatal errors.  LLOGGEN
will log errors and general information.  LLOGFST logs errors,
general and statistics information.
.PP
By the same token, there are enough calls to
llog
that the
debugging ones are conditionally compiled.  When you no longer
feel need of them, comment-out the -DDEBUG in 
.I Makefile.com.
.ne 10
.SS "SPECIFIC LOGS"
.PP
Even with the listed divisions, the logs contain a variety of
information.  Only the message-level log's format will be explained
in significant detail.
.IP msg.log 15
records enqueue and dequeue transitions, by
.I submit
and
.I deliver.
Entries by a background
.I deliver
process are noted with a "BG-xxxx" tag, where the x's contain the
4 least-significant decimal digits of the daemon's process id.  This
is to allow distinguishing different daemons.  When
.I deliver
is invoked, by
.I submit,
for an immediate attempt, the tag begins with "DL" rather than "BG".
Entries by
.I submit
begin with "SB".

Every major entry will indicate the name of the message involved.
Entries from
.I submit
will show "lin" if the submission is from a user on the local machine.
In this case, the end of the entry will show the login name of the
sender.  If the entry is labelled "rin", then the mail is being relayed.
The channel name, source host, and sender address are shown.  Within
parentheses, the number of addressees and the byte-length of the message
are listed.

Entries from
.I deliver
show final disposition of a message/addressee.  These are indicated
by "end".  The
.I mmdf.h
final status value is shown in parentheses.  Then, there is the
destination channel and mailbox name.  In brackets, the queue latency
for the address is shown in hours, seconds, and minutes.
.IP chan.log 15
records activity by the channel programs, in
chndfldir[].
The channel programs use the 
.I phs_note()
facility (see phs(3)) to make phase entries in chan.log.
Entries have a tag indicating the type of channel making the entry.
Different channels record different sorts of information.  For example, the 
.I local
channel shows when a
.I rcvmail
private reception program is invoked.  
.IP ph.log 15
is used by the telephone link-level (packet) code.
.IP ph.trn 15
is the one file that is
.I not
size-limited.  It records a transcript of every character sent and
received on a telephone channel.  It is reset to zero length at the
beginning of every phone session.  It is kept verbose, in order to
facilitate checking the status of any telephone channel which is
active.  Hence, just watching for the ph.trn file to get larger
can indicate that there is progress.  Each telephone channel
may have its own transcript file specified in the channel definition
in
.I chan.c
or the runtime tailor file.
ument line.  Separator characters are not
returned, except for equal-sign.  When an equal-sign is used,
three entries are consumed in the argument pointer array.  The firstmmdf/man/man5/maildelivery.5   444      0     12       15625  3664425172  11202 .tr ~
.de NP
.IP "\fI\\$1\fP" 10
..
.de II
.nr P- \\n()P	 \" save the preceeding IP space
.nr )P 0	\" now set it to 0
.NP "\\$1\fP"
.nr )P \\n(P-	 \" restore the preceeding IP space
..
.ds M \fI.maildelivery\fP
.TH MAILDELIVERY 5 "21 January 1986"
.SH NAME
maildelivery
.SH SYNOPSIS
User delivery specification file
.SH DESCRIPTION
The delivery of mail by the local channel can run through various
courses, including using a user tailorable file.
The delivery follows the following strategy, giving up at any point
it considers the message delivered.
.RS
.IP "1)" 4
If the address indicates a pipe or file default
then that is carried out.
.IP "2)" 4
The file \*M
(or something similar) in the home directory is read if it exists
and the actions in it are followed.
.IP "3)" 4
A system-wide file is consulted next, such as
.I /usr/lib/maildelivery
and the actions are similar to 2.
.IP "4)" 4
If the message still hasn't been delivered, then it is put into
the user's normal mailbox 
.RI ( .mail
or
.IR mailbox )
depending on the system.
.RE
.PP
The format of the \*M file is
.RS
.B field
.I <FS>
.B pattern
.I <FS>
.B action
.I <FS>
.B result
.I <FS>
.B string
.RE
where
.br
.NP field
is name of a field that is to be searched for a pattern.
This is any header field that you might find in a message.
The most commonly used headers are usually
From, to, cc, subject and sender.
As well as the standard headers, there are some pseudo-headers
that can also be used. These are:
.RS
.II source
The out of band sender information. This is the address MMDF would
use for reporting delivery problems with the message.
.II addr
the address that was used to mail to you, normally 'yourname' or 
'yourname=string' (see below).
.II default
if the message hasn't been delivered yet, this field is matched.
.II *
this case is always true regardless of any other action.
.RE
.NP pattern
is some sequence of characters that may be matched in the
above
.IR field .
Case is not significant.
.IP \fIaction\fP 10
is one of the mail delivery actions supported by the
local channel.  Currently the supported actions are
.B file
or
.BR > ,
which
appends the message to the given file, with delimiters;
.B pipe
or
.BR | ,
which starts up a process with the message
as the standard input;
and
.B destroy
which throws the message away.
.br
For piped commands, the exit status of the command is significant.
An exit status of 0 implies that the command succeeded and everything
went well. An exit status of octal 0300-0377 indicates that a permanent
failure occurred and the message should be rejected, these error codes
are given in mmdf.h. Any other exit
status indicates a temporary failure and the delivery attempt will
be aborted and restarted at a later time.
.NP result
is one of the letters A, R or ? which stand for
Accept, Reject and "Accept if not delivered yet".
They have the following effects:
.RS
.II A
If the result of this line's action is OK, then the message can be
considered delivered.
.II R
The message is not to be considered delivered by this action.
.II ?
This is equivalent to
.I A
except that the action is not carried
out if the message has already been accepted.
.RE
.PP
The file is always read completely so that several matches
can be made, and several actions taken.
As a security check, the \*M file must be owned by either
the user or root, and must not have group or general
write permission. In addition the system delivery file has the above
restrictions but must also be owned by root.
If the field specified does not need a pattern a dash (\-)
or similar symbol is usually inserted to show that the field is present
but not used.
The field separator character can be either a tab, space or comma (,).
These characters can be included in a string by quoting them with
double quotes (") (double quotes can be included with a backslash '\\').
.PP
MMDF treats local addresses which contain an equals sign ('=')
in a special manner.  Everything in a local address
from an equals sign to the '@' is ignored and passed on to the
local channel.  The local channel will make the entire string available
for matching against the
.I addr
string of the \*M file.
For example, if you were to
subscribe to a digest as "foo=digest@bar.NET",
.B submit
and the local channel will verify
that it is legal to deliver
to "foo", but then the entire string "foo=digest" will be available
for string matching against the \*M file for the
.B addr
field.
.SH ENVIRONMENT
The environment in which piped programs are run
contains a few standard features, specifically:
.ne 5
.sp
.nf
HOME is set to the user's home directory.
USER is set to the user's login name.
SHELL is set to the user's login shell (defaults to /bin/sh).
.sp
.fi
The default umask is set up to 077, this gives a very protective
creation mask.
Initgroups is called if 4.2 version of UNIX is running.
If further requirements are needed, then a shell script
can be run first to set up more complex environments.
.PP
There are certain built-in variables that you can give to
a piped program.  These are
.IR $(sender) ,
.IR $(address) ,
.IR $(size) ,
.I $(reply-to)
and
.IR $(info) .
.I $(sender)
is set to the return address for the message.
.I $(address)
is set to the address that was used to mail to you, normally `yourname'
or `yourname=string'.
.I $(size)
is set to the size in bytes of this message.
.I $(reply-to)
is set to the Reply-To: field (or the From: field if the former is
missing) and so can be used for automatic replies.
.I $(info)
is the info field from the internal mail header and is probably only
of interest to the system maintainers.
.SH EXAMPLE
.PP
Here is a rough idea of what a \*M file looks like:
.ne 12
.nf
.sp
# lines \fIstarting\fP with a '#' are ignored.
# as are blank lines
# file mail with mmdf2 in the "To:" line into file mmdf2.log
To~~~~mmdf2~~~~file~~~~A~~~~mmdf2.log
# Messages from mmdf pipe to the program err-message-archive
From~~~~mmdf~~~~pipe~~~~A~~~~err-message-archive
# Anything with the "Sender:" address "uk-mmdf-workers"
# file in mmdf2.log if not filed already
Sender~~~~uk-mmdf-workers~~~~file~~~~?~~~~mmdf2.log
# "To:" unix \- put in file unix-news
To~~~~Unix~~~~>~~~~A~~~~unix-news
# if the address is jpo=mmdf \- pipe into mmdf-redist
Addr~~~~jpo=mmdf~~~~|~~~~A~~~~mmdf-redist
# if the address is jpo=ack \- send an acknowledgement copy back
Addr~~~~jpo=ack~~~~|~~~~R~~~~resend~~\-r~~$(reply-to)
# anything from steve \- destroy!
from~~~~steve~~~~destroy~~~~A~~~~\-
# anything not matched yet \- put into mailbox
default~~~~\-~~~~>~~~~?~~~~mailbox
# always run rcvalert
*~~~~\-~~~~|~~~~R~~~~rcvalert
.sp
.fi
.SH FILES
$HOME/.maildelivery
\- the files normal location.
.br
/usr/lib/maildelivery \-
the system file. This should be protected against attack.  It
may contain contents such as:
.ne 4
.sp
.nf
default~~~~\-~~~~pipe~~~~A~~~~stdreceive
*~~~~~~~~~\-~~~~pipe~~~~R~~~~ttynotify
.fi
.sp
This allows interfacing to non-standard mail systems,
ones that don't believe in delimiter-separated mailboxes
.SH "SEE ALSO"
rcvtrip(1)
.SH BUGS
And why not?
dered delivered.
.II R
The message is not to be considered delivered by this action.
.II ?
This is equivalemmdf/man/man5/queue.5   444      0     12       16210  3656410723   7624 .TH QUEUE 5
'ta .8i 1.6i 2.4i 3.2i 4.0i 4.8i 5.6i 6.3i
.SH NAME
MMDF QUEUE FILES \- for storing mail in transit
.SH SYNOPSIS
.nf
quedfldir[]  =  e.g.: "/usr/mmdf/lock/home"
.fi
.SH DESCRIPTION
.PP
MMDF stores mail in an isolated part of the file system, so
that only authorized software may access the mail.
Mail is stored under the directory sub-tree, specified in the
.I conf.c
global variable quedfldir[].  
.PP
It must specify a path with
at least two sub-directories.  The next-to-bottom one is used as a "locking"
directory and the bottom one is the \fBhome\fR.  For full
protection, only authorized software can move through the locking
directory.  Hence, it is owned by MMDF and accessible only to it.

.I Submit,
.I deliver,
.I cleanque,
and
.I checkque
must run
\fIsetuid\fR()
to MMDF.  (In order to set both
its effect and real uid/gid to that of mmdf,
.I deliver
is usually run \fIsetuid\fR() to root.
On some systems it may only need to be
be \fIsetuid\fR() to MMDF.  This has not been tested).
The \fBhome\fR
directory is open access.
.SS "QUEUE ENTRIES"
.PP
When mail is queued by
.I submit, 
it is actually stored as two files.  One contains the actual message text and
the other contains some control information and the list of addressees.
.PP
The text file is stored in the \fBmsg\fR directory.  The other file is stored
in the \fBaddr\fR directory and is linked into one or more queue directories.
The queue directories are selected based on the channels on which this message
will be delivered.  Each output channel typically has its own queue directory.
.PP
Another directory below \fBhome\fR is a temporary area called \fBtmp\fR.
It holds temporary address-lists as they are being built.  Queuing
of a message is completed by linking this address file into \fBaddr\fR
and the queue directories.
The \fBmsg\fR directory contains files with message
text.  \fBAddr\fR and \fBmsg\fR files are synchronized by giving
them the same unique name, which MMDF occasionally calls the
message "name".  The message name is derived by use of
.I mktemp(3),
using \fBmsg\fR as the base directory.  The files created in
\fBaddr\fR[] must be open read-write access; the ones in \fBmsg\fR must
be open read access.
.PP
When
.I submit
runs, it changes into \fBhome\fR for its working
directory.  It then does a
\fIsetuid\fR()
to run as the caller.  This
is necessary to permit
.I submit
to access the caller's address-list
files (specified as "< file"), but probably will be changed.
.I Deliver
changes into the queue directory to minimize the time spent
searching for messages to deliver.
.PP
.ne 16
The following depicts the directory organization:
.sp
.nf
	                          \fBlock\fR                        (\fBlock\fR: 0700)
		           |                           (mmdf  only)
		           |
	        ________\fBhome\fR______          (open: 0777)
	       /           |              |          \\
	      /            |              |           \\
                  \fBtmp\fR         \fBaddr\fR          \fBq.*\fR         \fBmsg\fR      (open: 0777)

           addresses  ==> moved      and linked   message text
           built here ==> into here    into here     put here

           entries:            0666          0666          0644

.fi
.SS "QUEUE FILE FORMATS"
.PP
The \fBmsg\fR portion of a queued message simply contains the
message, which must conform to the Arpanet standard syntax,
specified in RFC822.  It is expected that the format of the message
contents file eventually will be more structured, permitting
storage of multi-media mail.
.PP
The following specifies the syntax of the \fBaddr\fR (and queue directory)
address-list portion of the queued message:
.SS "Address File Contents"
.RS
.IP "file ::=" 15
creation late flags '\\n' [rtrn-addr] '\\n' *(adr_queue '\\n')
.IP "creation ::=" 15
{long integer decimal representation of time message was created}
.IP "late ::=" 15
ADR_MAIL / ADR_DONE
{from adr_queue.h}
.IP "flags ::=" 15
{decimal representation of 16-bits of flags}
.IP "rtrn-addr ::=" 15
{rfc822 return address}
.IP "adr_queue ::=" 15
temp_ok mode queue host local {conforms to structure specified in
adr_queue.h}
.IP "temp_ok ::=" 15
{temporary flag indicating whether this address has been verified by the
receiving host: "yes" is "+"; "not yet" is "-"}
.IP "mode ::=" 15
{send to mailbox(m), tty(t), both(b), either(e), or processing completed(*)}
.IP "queue ::=" 15
{name of the queue into which this message is linked for this address}
.IP "host ::=" 15
{official name (and domain) of recipient host}
.IP "local ::=" 15
{local address on receiving host}
.RE
.SS "Address File Description"
.PP
An address queue file contains a creation time-stamp, an
indication if the sender has been notified of delayed delivery,
some flags, an optional return-mail address, and an address list.
Several <flags>
are currently in use (as specified in msg.h).  ADR_NOWARN indicates whether
late warnings
should be sent to the return-mail address if the entire address list has not
been processed within the number of hours specified by "warntime".
ADR_NORET indicates whether mail should be returned to the sender if it hasn't
been completely processed within the number of hours specified by "failtime".
ADR_CITE indicates whether warning and failure messages are to contain only a 
citation of the message.
An ADR_DONE ("*") value, for the "late" flag, indicates that a warning
notice has been sent.  
.PP
The creation date is coded as a long ASCII decimal string, terminated by
the "late" flag.  This has to be stuffed into the file because
Unix doesn't maintain it.  The date is used to sort the queue so
that mail is delivered in the order submitted.
.PP
The return address is a line of text and may be any address
acceptable to
.I submit.
.PP
Each address entry is on a separate line, and conforms to the
adr_struct format, defined in
.I adr_queue.h.
It contains several fields separated by spaces or commas.  Fields containing
spaces or commas must be enclosed in double quotes.
.PP
The temp_ok flag indicates whether the address has been accepted during an
"address verification" dialog with the receiving host.  When the message text
is successfully accepted by the receiving host, then this flag no longer 
applies and the mode is set to ADR_DONE ("*").
Before final acceptance of message text, the mode flag indicates whether the
mail is for a mailbox,
terminal, both, or either.  (Currently only mailbox delivery, ADR_MAIL,
is used.)
.PP
The queue name is the name of the sub-queue in which the message is queued for
this
address.  Each addressee's host may be on a separate queue or some hosts may
share the same queue.  When all addressees in the same queue have been
delivered, the address file is removed from that queue directory.  When all
queues have been processed, the address file (in both \fBaddr\fR and the queue
directory) and the text file (in \fBmsg\fR) are removed.
.PP
The host and local strings are used by the channel program.  The host
determines the type of connection the channel program makes.  The local string
is passed to the host; it should be something meaningful to that host.  The
local string must not contain newline or null and it be a valid local address
per RFC822.
.SH "SEE ALSO"
deliver(8), cleanque(8), submit(8)

tries:            0666          0666          0644

.fi
.SS "QUEUE FILE FORMATS"
.PP
The \fBmsg\fR portion of a queued message simply contains the
message, which must conform to the Arpanet standard syntax,
specified in RFC822.  It is expected that the format of the message
contents file eventually will be more structured, permitting
storage of multi-media mail.
.PP
The folmmdf/man/man5/replies.5   444      0     12        4261  3642035027  10121 .TH REPLIES 5
'ta .8i 1.6i 2.4i 3.2i 4.0i 4.8i 5.6i 6.3i
.SH NAME
MMDF Reply Values
.SH SYNOPSIS
.nf
#include "mmdf.h"

struct rp_bufstruct		  /* for reading reply strings          */
{
    char    rp_val;
    char    rp_line[256];
};

char *rp_valstr (rp_val);

rp_gval(rp_val)     /* get the entire return value        */
rp_gbval(rp_val)    /* get the basic part of return value */
rp_gcval(rp_val)    /* get the domain part of value       */
rp_gsval(rp_val)    /* get the specific part of value     */
rp_gbbit(rp_val)    /* get the basic part right-shifted   */
rp_gcbit(rp_val)    /* get the domain part right-shifted  */
rp_gsbit(rp_val)    /* get the specific part right-shifted */
rp_isgood(rp_val)   /* is return value positive?          */
rp_isbad(rp_val)    /* is return value negative?          */
.fi
.SH DESCRIPTION
.PP
Within MMDF, process, procedure, and protocol reply values
are handled uniformly.  The file
.I mmdf.h
defines all legal reply
values and contains pseudo-functions for interpreting the values.
For protocol replies, a text string is included and may be passed
to the user or recorded in a log.

Values are coded into 8-bits, to allow them to be returned by
Unix PDP-11 processes.  A value comprises three fields.  The first
indicates overall degree of success.  Success or failure can be
complete or partial.

The second field indicates the semantic domain to which the
reply refers, for example "the mail system" or "the file system".

The third field is used to distinguish the exact value.

In order to bypass machine-dependent handling of bit-strings,
.I mmdf.h
contains some macro pseudo-functions.  On PDP-11 Unices, the
main problem encountered is with sign-extension when the value
passes through a register.  The macros eliminate this problem.

The macro, rp_gval(), extracts the whole value.  The macros
rp_isgood() and rp_isbad() will be the most heavily used, since
they test for basic success or failure.

The specific values in
.I mmdf.h
have explanations in comments.

The structure rp_bufstruct allocates a reply buffer, divided
into value and string parts.
.SH FILES
.IP "h/mmdf.h" 20
Actual reply values
.IP "lib/mmdf/rp_valstr.c" 20
maps values to strings, for printout.
ptance of message text, the mode flag indicates whether the
mail is for a mailbox,
terminal, both, or either.  (Currently only mailbox delivery, ADR_MAIL,
is used.)
.PP
The queue name is the name of the sub-queue in which the message is queued for
this
address.  Each addressee's host may be on a separate queue or some hosts may
sharemmdf/man/man5/script.5   444      0     12       40217  3643606660  10013 .TH SCRIPT 5
.SH NAME
script \- PhoneNet connection script
.SH SYNOPSIS
script
.SH DESCRIPTION
The PhoneNet link-level protocol
package establishes connections to a remote system
under control of a file called a ``script file'' or ``dialing script''.  This 
file gives procedures for dialing,
negotiating any login procedure on the remote system
and starting a related PhoneNet protocol module on the remote
system.  The file also contains settings for various connection parameters.
.PP
Script files consist of a series of lines,
each of which contains a command word and zero or more arguments.  
The list of commands below shows which
arguments are required for each command:
.TP
\fB#\fR \fIcomment...\fR
Any blank line or line beginning with ``#'' is treated as a comment and is
ignored. 
.PP
.nf
.B { 
   \fBalternate\fR
       # commands
   .
   .
   .
   \fBalternate\fR
       # commands
.fi
.TP
\fB}\fR
A sequence of commands enclosed by curly-braces is called a ``select block''.
The curly-braces must appear by themselves on separate lines.  A
``select block'' is composed of one or more ``alternate blocks''.  An
``alternate block''
consists of a line containing the command name, \fBalternate\fR,
and then some lines containing script commands.  An ``alternate block'' is
delimited
by the start of another ``alternate block'' at the same level
(``select blocks'' can be nested) or by the end of the current ``select block''.
.sp
The order of command execution within a ``select block'' follows some special
rules.  Control begins with the first command in the first ``alternate block''.
If that ``alternate block'' terminates successfully, then control jumps to the
command following the ``select block''.  If any
.B recv
command in the ``alternate block'' times out, then control passes to the next
``alternate block'' in sequence.  Successive ``alternate blocks'' are executed
until one completes successfully (i.e. no timeouts).
If no more ``alternate blocks'' are available, processing terminates with a
FATAL error.  For example, the following script excerpt uses a ``select block''
to send a ``break'' if no ``login'' prompt appears within 30 seconds:
.sp
.nf
	xmit "\\r\\r"
	{
	alternate
		recv "login" 30
	alternate
		xmit "\\#"
		recv "login" 30
	}
	xmit "accountname\\r"
.fi 
.TP
.B abort
This command causes processing of the script file to cease.  A FATAL
error is returned.
.TP
.B alternate
This command marks the start of an ``alternate block'' (see the \fB{ ... }\fR
command above).
.TP
\fBdial\fR \fIphone-number-spec\fR [, \fIphone-number-spec\fR... ]
This command causes the number(s) given by the \fIphone-number-spec\fR's
to be dialed or a connection via a direct line to be made.
More than one
.I phone-number-spec
may be given, separated by commas.
Each \fIphone-number-spec\fR has the general form:
.sp
.ti +.5i
.IR speed |< type > phone-number
.sp
The 
.I speed
parameter is the baud rate
at which the connection should be made
(from the set: 50, 75, 110, 134.5, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
9600).
The baud rate is separated from the rest of the 
.I phone-number-spec
by a vertical bar, `|'.
.sp
The optional
.RI < type >
specifies the line-type to use.  If 
.RI < type >
is omitted, it defaults to ``LOCAL''.  Note that the angle-brackets are
part of the syntax and must surround the
.I type
to separate it from the rest of the 
.I phone-number-spec.
.sp
A 
.I phone-number
is composed of a series of digits,
in addition to the characters `(', `)', `-', and `:'.
Parentheses and dashes are ignored,
and may be used freely to enhance readability.
The colon indicates that the dialer should await a secondary
dial tone before proceeding with the dialing.
This is used with phone systems that use preliminary
digits to access outgoing lines, tie lines, etc.
.sp
If the
.I phone-number-spec
begins with a letter,
it is taken to be the name of a direct connect line.
The names and speeds of these lines are predefined.  See the "Installing and
Operating MMDFII" document for information about defining these lines.
.sp
Up to five \fIphone-number-spec\fR's can be given on a single
.B dial
command.  If
a connection cannot be made using the first number,
succeeding \fIphone-number-spec\fR's will be tried
in the order given before a failure is indicated.
For example, the following command:

.ti +.5i
dial 1200|738-2928, 300|738-8003

would initiate a call to the first number (738-2928) at 1200 baud.  If
the connection failed, it would try the second number (738-8003) at
300 baud.
.sp
The
.B dial
command also clears the input buffer to prevent rewinding by the
.B replay
command.  See the explanation of the 
.B replay
command below.
.TP
.B end
This command causes the terminal line to close
and interpretation of the script file to cease.
.TP
.B go
This command is a synonym for the
.B start
command (see description below).
.TP
\fBlog\fR \fImessage\fR
This command causes
.I message
to be written into the ph.log file.
.I Message
should be a quoted string if it contains spaces.
.TP
.B mark
As data is read from the remote system, it is collected into a buffer.  This
buffer can be rewound and examined by the 
.B replay
command (see below).  Executing the
.B mark
command clears this input buffer.
The
.B dial
command also clears the input buffer.
.TP 
\fBnrexmit\fR \fIretries\fR
This command sets the maximum number of times to retransmit
a given packet to the remote system in the event of failure to receive an
acknowledgement within the timeout period (set with the \fBwaitack\fR command).
.I Retries
must be greater than zero; five is the default.
.TP
\fBprompt\fR \fIpromptvalue\fR
This command sets the character expected as the prompt to wait for before
transmitting data to the dial-out or direct-connect line.  
.I Promptvalue
is a decimal number giving the ASCII value of the prompt.  If the prompt
hasn't appeared within 10 seconds of a transmission attempt, the connection
will be dropped.  This command is used when running on
half-duplex lines where the prompt indicates that writing is enabled.
.TP
.B replay
This command causes the input buffer to be rewound to the time of the
most recent
.B dial
or
.B mark
command.  The next 
.B recv
command will then look at data which may have already been examined by 
previous 
.B recv
commands.  The ability to replay the input stream is useful for adapting
intelligently to a selection of messages from the remote system.  For example,
the following script segment will connect to either line "A" or line "B"
in a simple terminal switch:
.sp
.nf
	recv "Request"
	mark
	{
	alternate
		xmit "A\\r"
		recv "A\-Connected" 30
	alternate
		replay
		recv "A\-Busy" 1
		log "A was busy\-trying B"
		xmit "B\\r"
		recv "B\-Connected" 30
	alternate
		log "Both A and B were busy"
		abort
	}
.fi
.sp
Note that a small
.I timeout
value was given on the
.B recv
command which followed the
.B replay
command.  A small value is adequate since the ``A\-busy''
string, if received, will already be present in the input buffer.
.TP
\fBrecv\fR \fIquoted-string\fR \fItimeout\fR
This command causes the interpretation of the script file
commands to pause until \fIquoted-string\fR is received from
the remote system.
The
.I timeout
specifies the maximum number of seconds to
wait for
.I quoted-string
before aborting with an error.
This argument must be present and must be greater than zero seconds.
\fIQuoted-string\fR is a string of characters
surrounded by double-quotes and possibly containing any of these
escape sequences:
.sp
.in +.5i
.nf
\\b	\-	 backspace (ASCII 8)
\\f	\-	 form feed (ASCII 12)
\\n	\-	 newline or line feed (ASCII 10)
\\r	\-	 carriage return (ASCII 13)
\\t	\-	 tab (ASCII 9)
\\ddd	\-	 ASCII value ddd (in octal)	 
.fi
.in -.5i
.sp
If the \fBrecv\fR command appears in a ``select block'', instead of causing
the script to abort, a timeout causes control to be passed to the next
``alternate block'' (see the \fB{ ... }\fR command above).
.TP
\fBrecvill\fR \fIillegal-character-codes\fR
This command is used to specify the characters which cannot be
received by the local system.
\fIIllegal-character-codes\fR is a quoted string of the octal
values of characters preceded by a backslash.
For example,
the following command would declare all the codes between 0 and 017
to be illegal:
.sp
.nf
	recvill "\\0\\1\\2\\3\\4\\5\\6\\7\\10\\11\\12\\13\\14\\15\\16\\17"
.fi
.sp
Any number of
.B recvill
commands can be given in a script file;
the resulting illegal-character set will be the union
of the codes given in each command.
All
.B recvill
commands must come before the
.B start
command in the script file,
so that the illegal character set has been completely specified
before the protocol is started.  The default illegal-character
set is zeroed by the first
.B recvill
command to appear in the script.  The default set is: the control characters
(\\0 through \\37) and delete (\\177).
.TP
\fBrecvsize\fR \fIsize\fR
this command specifies the maximum length packet that can be received
by the local system,
not including the carriage return.
The number \fIsize\fR is decimal, with a value:

.ti +.5i
40 <= \fIsize\fR <= 255
.TP
\fBstart\fR
This is placed in the script file at the
point in the login sequence where
the PhoneNet protocol module on the remote
system has been started.  The
.B start
command starts the corresponding PhoneNet 
protocol module on the local system.
When the protocol has completed,
interpretation of the script file resumes with the line
following the
.B start
command.  This allows a clean logout procedure to be executed, if necessary.
.TP 
\fBstty-pr\fR \fIproto-set\fR \fIproto-clear\fR
This command sets the terminal mode of the dial-out line when the dial
package is in protocol mode (as opposed to script mode).  Protocol mode is
initiated by the
.B start
command and is the mode in which PhoneNet messages are exchanged.
.I Proto-set
is an octal number which determines which options should be set when protocol
mode is entered.
.I Proto-clear
is an octal number which determines which options should be cleared when
protocol mode is entered.  The octal numbers are defined in <sgtty.h> and
are documented in tty(4).  The default value for 
.I proto-set
is 0000300 (EVENP|ODDP).  The default value for
.I proto-clear
is 0177474 (ALLDELAY|CRMOD|ECHO|RAW|LCASE).
.TP 
\fBstty-sc\fR \fIscript-set\fR \fIscript-clear\fR
This command sets the terminal mode of the dial-out line when the dial
package is in script mode (as opposed to protocol mode).  Script mode is
the mode during which the script file commands such as 
.B recv
and 
.B xmit
are used.  Script mode is the initial mode and it is also re-entered when
control returns from a 
.B start  
command.
.I Script-set
is an octal number which determines which options should be set when script
mode is entered.
.I Script-clear
is an octal number which determines which options should be cleared when
script mode is entered.  The octal numbers are defined in <sgtty.h> and
are documented in tty(4).  The default value for 
.I script-set
is 0000340 (EVENP|ODDP|RAW).  The default value for
.I script-clear
is 0177434 (ALLDELAY|CRMOD|ECHO|LCASE).
.TP
\fBuse\fR \fIscriptfile\fR [\fIarguments\fR]
This command passes control to the named 
.I scriptfile.
If the 
.I scriptfile
doesn't abort, then control is returned to the line following the
.B use
command in the original script file.  There is no limit to the depth of nesting
available with the
.B use
command.  However, a failure at any level is passed back to the top and all
script processing ceases.  The optional arguments are made available in the
named script as $1 $2 $3....  Arguments containing spaces should be quoted
with double-quotes.  To get a dollar sign within a script, use $$.
.TP
\fBwaitack\fR \fItimeout\fR
This command sets the length of time to wait for data packet acknowledgements
from the
remote system.  If no acknowledgment has been received within 
.I timeout
seconds, the packet will be retransmitted.  The 
.B nrexmit
command sets the maximum number of times to retransmit the packet before
dropping the connection.  
.I Timeout
must be greater than zero; the default value is 10 seconds.
.TP
\fBwaitdata\fR \fItimeout\fR
This command sets the length of time to wait for data packets
from the
remote system.  If no data has been received within 
.I timeout
seconds, the connection is dropped.  
.I Timeout
must be greater than zero; the default value is 180 seconds.  Occasionally, a
heavily loaded system will take longer than 180 seconds to respond (perhaps
while sorting the message queue).
If the remote system cannot be made to respond faster by, 
say, subdividing its queue to reduce the sort time, then the
.I timeout
value will need to be increased.
.TP 
\fBwindow\fR \fIwindowsize\fR \fIsendwindow\fR
This command sets the link-level 
.I windowsize
to use when sending data to
the remote system.  Allowable \fIwindowsize\fR's are `1' (the default) and `2'.
A
.I windowsize
of `1' causes the link-level to pause for acknowledgment from the remote
system after sending one data packet.  A 
.I windowsize
of `2' causes the
link-level to pause for acknowledgment only after sending two data packets.
Normally, the remote system will have acknowledged the first data packet while
the second was being transmitted.  Therefore, setting a window size of two can
greatly increase throughput by eliminating most pausing for acknowledgments.
The 
.I sendwindow
flag indicates whether a special ``windowsize packet'' should be sent to the
remote system during protocol initialization.  If 
.I sendwindow
is 0, the packet is not sent.  If 
.I sendwindow
is 1, the packet is sent.  This packet, if sent, sets the
.I windowsize
that the remote system should use in sending data back to the local
system.  Normally, implementations of the link-level package default to a
.I windowsize
of `1' and require the master side to set
.I sendwindow
to change the default setting.
.TP
\fBxmit\fR \fIquoted-string\fR
This command cause the characters in \fIquoted-string\fR to be sent to
the remote system.  
The format of
.I quoted-string
is identical to that for the
.B recv
command (see above) except that two additional escapes apply:
.sp
.in +.5i
.nf
\\x	\-	 delay one second
\\#	\-	 send a break
.fi
.in -.5i
.sp
.TP
\fBxmitill\fR \fIillegal-character-codes\fR
This command specifies the character codes which the local system
is incapable of transmitting.  The 
.B xmitill
command behaves exactly the same as the
.B recvill
command (described above).
.TP
\fBxmitsize\fR \fIsize\fR
This command specifies the maximum length
(not including the carriage return)
of packets that should be transmitted by the local system
The number \fIsize\fR is decimal, with a value:

.ti +.5i
40 <= \fIsize\fR <= 255
.SH EXAMPLES
.PP
Suppose you wanted to set up a script file to perform 
the following phone call and login dialog using a standalone autodialing modem,
a Vadic 3451PA, configured on direct line "vadic1200":
.sp
.in +.5i
..PP
.if t .ta 1.4i 2.0i
.if n .ta 1.7i 2.5i 
.nf
^E	<-----	user types ^E to enter auto-dial mode
HELLO: I'M READY	<-----	modem responds with message and prompt
*D	<-----	user types dial command
NUMBER?	<-----	modem prompts for phone number
	<-----	user enters number and hits return
12225551000	<-----	user types <cr> to verify echo of number
DIALING: ON LINE	<-----	dials and reports when carrier
		is detected.
Welcome to the 11/70
Name: szurko	<-----	user types login name
Password:	<-----	user types password
channel: elec70	<-----	remote PhoneNet "slave" program
		prompts for MMDF channel name
	<-----	PhoneNet protocol startup
.sp            
.in -.5i
A script to perform this login sequence might be:
.sp
.in +.5i
.nf
window 2 1
xmitpack 130
recvpack 130
dial vadic1200
xmit "\\5\\x\\x\\r"
recv "*" 20
xmit "D\\x\\r"
recv "NUMBER?\\r\\n" 30
xmit "12225551000\\x\\r"
recv "12225551000\\r\\n" 20
xmit "\\r"
recv "ON LINE\\r\\n" 60
xmit "\\x\\x\\r"
recv "Name:" 30
xmit "szurko\\r"
recv "Password:" 30
xmit "secret\\r"
recv "channel:" 45
xmit "elec70\\r"
recv "elec70" 15
start
end
.in -.5i
.sp
.fi
An example script for establishing this connection using a dialport
(separate ACU and modem devices) would be:
.nf
.sp
.in +.5i
window 2 1
xmitpack 130
recvpack 130
dial 1200|<wats>12225551000
xmit "\\x\\x\\r"
recv "Name:" 45
xmit "szurko\\r"
recv "Password:" 15
xmit "secret\\r"
recv "channel:" 45
xmit "elec70\\r"
recv "elec70" 15
start
end
.SH SEE ALSO
``MMDF Dial-up Link Protocol'',  ``Installing and Operating MMDFII''
t value is 180 seconds.  Occasionally, a
heavily loaded system will take longer than 180 seconds to respond (perhaps
while sorting the message queue).
If the remote system cannot be made to respond faster by, 
say, subdividing its queue to reduce the sort time, then the
.I timeout
value will need to be increased.
.TP 
\fBwindow\fR \fIwindowsize\fR \fIsendwindow\fR
Thmmdf/man/man5/tables.5   444      0     12       27052  3642073414   7755 .TH TABLES 5
.SH NAME
MMDF Name Tables
.SH SYNOPSIS
Name tables, for aliases, domains, and hosts
.SH DESCRIPTION
.PP
All of the MMDF name tables are encoded into a database 
which is built on top of the
\fIdbm\fR(3X)
package.
A number of tables are associated with MMDF, the exact set
being specified by the tailor file described in
the "Installing and Operating MMDF II" document.
Name tables all have the same format and are accessed via the
.I tb_dbm.c
module.
Functionally, they permit a simple key/value pairing.
The syntax for tables is specified here:
.RS
.IP "entries ::=" 15
entries entry
.IP "entry ::=" 15
comment / real-entry
.IP "comment ::=" 15
 '#' value eol
.IP "real-entry ::=" 15
name separator value eol
.IP "name ::=" 15
{string of chars not containing a <separator>}
.IP "separator ::=" 15
{see the chars in _hkeyend[], usually ':' and space}
.IP "value ::=" 15
{string of chars not containing an <eol>}
.IP "eol ::=" 15
{see the chars in _hvalend[]}
.IP "where:"
.IP "name is" 15
a key
.IP "value is" 15
any relevant text.
.RE
.PP
Examples of alias, domain, and host usage are
given in a sample sheet in the MMDF documentation.
Special
handling is required for the local machine's user identification
file (on Unix, /etc/passwd).
.SS "Hosts and Domains"
.PP
Two basic types of table are host and domain tables.  This
section gives a brief discussion of these concepts in terms of
the MMDF system.  The domain namespace is treated as a logical global
hierarchy, according to the model of RFC 819, with subdomains
separated by '.'s (e.g ISI.USC.ARPA is a three level hierarchy
with ARPA at the top level).   A host is a computer associated
with a channel as described in
channels(7),
which may be directly connected or reached through a relay
associated with the channel.  The distinction between hosts as
physical entities, and domains as logical entities should be
noted.  All hosts known to an MMDF system must have unique names.
For this reason, the convention of labelling hosts by an
associated domain name is adopted in many cases.  This is a
useful method to guarantee unique names, but is not required.
The domain and host table structures are devised with three
basic aims in mind:
.RS
.IP 1.
To map a string into a fully expanded domain
name.
.IP 2.
To map this domain into a source route starting with a host.
.IP 3.
To obtain the transport address associated with the host.
.RE
.SS "Domain Tables"
.PP
Domains are split in a two-level manner, with the top part of
the tree specified in the tailor file as described in 
the "Installing and Operating MMDF II" document
and the lower parts of the tree in tables.  The two level
structure is intended as a balance between generality and
efficiency.
The order of searching is also specified in the tailor file.
The structure of a domain table is to have name as the part of
the domain not in the tailor file.
Thus for ISI.USC.ARPA there might be a domain ARPA with 
name=isi.usc
or domain USC.ARPA with 
name=isi.  The structure of
value is:
.RS
.IP "value ::=" 15
*(domain dm_separator) host
.RE
.PP
The possible values of dm_separator are given in
tai(3),
although typically ',' or ' ' would be used.
This value is essentially a source route to be traversed from
right to left.  Consider an example table for the domain ARPA:
.nf

# Sample ARPA domain table
isi.usc:a.isi.usc.arpa
b.isi.usc:b.isi.usc.arpa
foobar.isi.usc:b.isi.usc.arpa
graphics.isi.usc:graphics.isi.usc.arpa z.mit.arpa

.fi
Thus, if the "isi.usc.arpa" is analysed, domain table ARPA will
be selected, and host "a.isi.usc.arpa" associated with domain
"isi.usc.arpa".  If only "isi.usc" were given, the domain tables
would be searched in order, and if the ARPA table were the first
one to give a match, then the same result would be reached.  If
"foobar.isi.usc" is given, it would be mapped to host
"b.isi.usc.arpa" and (official) domain "b.isi.usc.arpa".
If "graphics.isi.usc.arpa" is given, a source route to domain
"graphics.isi.usc.arpa" through HOST "z.mit.arpa" will be
identified.  If "xy.isi.usc.arpa" (or "xy.isi.usc") is given, then
it will not be found.  However, a subdomain will be stripped
from the left  and the search repeated. Thus domain
"xy.isi.usc.arpa" will be identified as reached by a source route
through host "a.isi.usc.arpa".
.PP
As specified earlier, the
order of searching is also specified in the tailor file.
For example, a host in domain UCL-CS.AC.UK, might have a search
order UCL-CS.AC.UK, AC.UK, UK, SWEDEN, ARPA, "".   Thus, if
there were a domain isi.usc.ac.uk, it would be the preferred
mapping for isi.usc over isi.usc.arpa.  The last domain
searched is null.  This could be used to contain random fully
qualified domains or to identify gateways to other domains.
An example file is:
.nf

#  Sample Top level domain table
#  Odd host
basservax.australia:basservax.australia scunthorpe.ac.uk
# UUCP Gateway
uucp:seismo.arpa
# Mailnet Gateway  (-> multics -> educom ->mailnet)
mailnet:educom.mailnet mit-multics.arpa

.fi
To specify the top domain in the tailor file, the name and
dmn parameters of the domain should be set to "".
.SS "Host Tables"
.PP
For every host associated with the channel, there will be one
or more entries.  In each case, the key is the name identified
from the domain tables.  A host may have multiple entries if it
has more than one transport address which the channel might
utilise.
.PP
When a channel just sends all its mail to a relaying site, the
address portion (value) of the entry is not needed, directly, 
during the transmission
process.  Hence, it need not be accurate.  However, it still is
used to logically collect together host names, that is, all table
entries with the same value are regarded as being aliases for the same
host.
.SS "P.O. Box Channels"
.PP
POBox channels, for passive, telephone-based exchange, operate
in two modes.  In one case, a single
login is authorized to pickup all mail for the channel.  In this
case, the host-table addresses are only used for the "collecting"
function.  For the second mode, different logins
share the channel and are to receive only some of the mail queued
for the channel.  In this case, the login is treated as an
"address", and the table entries should have the value fields contain
the name of the login authorized to pickup mail for that "host".
.SS "Access control tables"
.PP
Channels also have access control tables associated with them,
to determine whether a message is allowed to use a given route.
Each channel has four (optional) tables that determine the
access controls used: insrc, outsrc, indest, and outdest. 
These tables and the access control mechanism are more fully
described in "Configuring MMDF Authorization" by Steve Kille.
.SS "Reformatting tables"
.PP
There may also be a "known hosts" table associated with each
channel.  This is exactly the same format as a host table.
If a message is being reformatted, and if an address does not
have its host in this list, then it will be modified to appear
as a percent route (RFC733 or JNT Mail route) address, with the local
domain as the root.
.SS "Local Aliases"
.PP
The password file specifies the name of all local recipients; their mailing
names are their login names.  Since this is a rather restricted
name space, and since it is useful to have some other kinds of locally-known
names, there is a second file used to specify "aliases".  The
location of the aliases file
is specified in the tailor file.
.PP
An alias entry may be used for one of five functions:
.RS
.IP 1.
True aliasing, where the key value maps to a local user's login name,
e.g. "dave:dcrocker";
.IP 2.
Forwarding, where the key value maps to a foreign address, such
as "dcrocker:dcrocker@udel"; and
.IP 3.
Address lists, where the key value maps to a set of addresses, such
as "mother:cotton,dcrocker,farber".
.IP 4.
Redirection of a message to a file. For example,
"foobar:dpk/foobar" would cause user and group ids to be set to
dpk and the text of the message to be appended to the file "foobar" in
dpk's default
login directory.   Similarly, "foobar:dpk//tmp/foobar" would do
the same for file /tmp/foobar.
.IP 5.
Redirection of a message to a pipe.  For example,
"news-inject:news|/usr/lib/news/uurec" would
cause a message to be sent to be passed into a unix
pipe (see pipe(2)) 
with userid and groupid set to news.
.RE
.PP
As a convenience, the value-part of an entry may specify a file
name, so that the
.I actual
value is taken from the file.
There are two possible notations for this:
.RS
.IP 1.
By having left-angle bracket
('<') precede the value specification.  For example:
"mother: < /etc/mmdf/mother_list@udel-relay.arpa".
.IP 2.
By using a data type with value "include".  For example:
"mother: :include: /etc/mmdf/mother@udel-relay.arpa"
.RE
In both cases, the @HOST (not a domain) is optional.  If specified, it should
be the local host.
.PP
Recursive specification is permitted.
For example, "crocker" may map to "dcrocker" and
"dcrocker" may map to "dcrocker at udel", so that
both "crocker" and "dcrocker" are locally-known names, but mail
sent to either of them will be forwarded to "dcrocker@udel".
.PP
In practice, it is useful to organize alias files into the following
ordering:
.RS
.IP "List aliases"
which contain a value referring to a later address list.
This constitutes a one-to-one mapping of a key to a value, where
the value points into the "Lists" group.
.IP "Lists"
which contain values referring to multiple addresses;
This constitutes a one-to-many mapping, where some of the addresses
may refer to other entries (address lists) in the Lists group, as
well as other entries (individual addresses) later in the table.
.IP "Mailbox aliases"
which contain values referring to single addresses.
These constitute one-to-one mappings, where the value refers to an
entry in the password file or to an entry in the "Forwarding aliases"
group.
.IP "Forwarding aliases"
which contain values referring to single addresses on other
machines.
These, also, are one-to-one mappings, where the value always refers
to an off-machine address.
.RE
.PP
By organizing the file in this manner, only the "Lists" portion
requires a topological sort.  Since the other three sections will
never point to entries within their section, they may be sorted
more conveniently, such as alphabetically.
Such a structure also tends to make changes easy.  In particular,
the handling of forwarding is easy, since
.I all
references to a user will get intercepted, at the end of the
table.
.SS "Mail-ID tables"
.PP
The Mail-ID tables are used only if the Mail-IDs feature is enabled.
This can be done by initializing the mailids variable in conf/conf.c
or dynamically, in the tailoring file, by defining MMAILID to be 1.
Mail-IDs are used to disassociate mail addresses from login names.
There are two tables that are used to map Mail-IDs to users login names
and login ids to Mail-IDs.  The "users" file is used to map
users (login ids) to Mail-IDs, and the "mailids" file is used to map
Mail-IDs to users.  The names of these files can be overridden, but it
is not recommended.  Each file has lines with two entries per line
(user and Mail-ID, or Mail-ID and user).
.PP
A user can have more than one entry in the Mail-IDs file, but should have
only one entry in the users file.  This does not prevent him from
sending mail with any of his Mail-IDs.  The users file is just a source
of default Mail-IDs.
.SH FILES
.IP conf/<site>/chan.c 20
defines existing channels, including the location of their
host name tables and the local aliases file.
.IP lib/table/tb_dbm.c  20
is the code which accesses the tables.
.IP tbldfldir[] 20
is the default location for host names tables and the local
aliases file.
.SH SEE ALSO
.PP
dbm(3X), tai(3), channels(7), submit(8), "Installing and Operating MMDF II"
3.
Address lists, where the key value maps to a set of addresses, such
as "mother:cotton,dcrocker,farber".
.IP 4.
Redirection of a message to a file. For example,
"foobar:dpk/foobar" would cause user and group ids to be set to
dpk and the text of the message to be appended to the file "foobar" in
dpk's default
login directory.   Similarly, "foobar:dpk//tmp/foobar" would do
the same for file /tmp/foobar.
.IP 5.
Redirection of a message to a pipe.  For example,
"news-mmdf/man/man7/   755      0     12           0  3642035040   6277 mmdf/man/man7/channels.7   444      0     12       21652  3642075664  10313 .TH CHANNELS 7
.ta .8i 1.6i 2.4i 3.2i 4.0i 4.8i 5.6i 6.3i
.SH NAME
MMDF Transmission Channels
.SH SYNOPSIS
From
.I deliver:

.nf
execl (path, name,
		msgfd, addressfd, responsefd,
		msg_txname, msg_sender,
		(domsg) ? "y" : "n", 0)
	char *path,
		*name,
	 	*msg_txname,
		*msg_sender;
	int  msgfd,
		addressfd,
		responsefd;
.fi
.SH DESCRIPTION
.PP
Channel programs view themselves as relatively independent.
Their goal in life is to perform the task of "channel fitting,"
sending mail from one channel (module) to another, possibly also
sending mail in the reverse direction.  Most channel programs
just send mail from
.I deliver
to an outbound channel.  Interaction
with
.I deliver
is co-routined, so that it looks, to the channel program, like
any other mail port.
.I phone
and
.I slave
are the only two programs that currently send in both directions.

Each program's pattern for doing the fitting entails
accessing the same types of modules, using similar types of
interactions.

A channel module has an I/O package, which is called by higher-level
read\-mail and write\-mail packages.  They are called by the the
"fitting" module.  Hence, a process which transfers mail in both
directions between two channels "xx" and "yy" will have the basic
modules relationships:
.ta .8i 1.6i 2.8i 4.0i 4.8i
.nf
.sp
.RS
	xx_rdmail	xx2yy_send	yy_wtmail
.sp
xx_io						yy_io
.sp
	xx_wtmail	yy2xx_send	yy_rdmail
.fi
.RE
.PP
As mentioned above, channel programs commonly send only from
the local machine's
.I deliver
program, through module (qu_), out to
a destination channel.

.ne 7
Such channel programs will have the basic structure:

.RS
.nf
qu_io	qu_rdmail	qu2yy_send	yy_wtmail	yy_io 
.fi
.RE
.SS "CHANNEL INVOCATION"
.PP
When a channel program is invoked by
.I deliver,
the argument list is as shown in the Synopsis.  The msgfd
is a read-only file descriptor on the message text.  The
addressfd is a read-only pipe for getting destination
addresses from
.I deliver.
The responsefd is for sending dispositions back to
.I deliver.
The last two arguments are the "name" of the message (almost
never useful) and the return address, if given, of the message
sender.  This last is often used by channels during submission
of the message to the next site.

The argument list is hidden from channel programs, by mapping
it into the qu_* calls.
.SS "INTERACTION MODES"
.PP
The I/O, read-mail and write-mail modules for channels have
standard, but not identical, calls.  In particular, a given module might
not have certain calls.  Also, some calls have mutually exclusive
return options.  In addition, the permissible sequencing of calls
may be different.

For example, the write-mail call that sends an address can
send either of two positive responses:  Address ok, or Message
ok.  The former means that only the address was sent and that the
message text must be sent separately, when the address list is
completed.  The latter means that a copy of the entire message
was sent.  A channel that can return one of the two values MAY
NOT EVER return the other.  It is the responsibility of the
xx2yy_send module to check for correct return values.

The reason for the differences is that different channels may
have VERY different protocol characteristics.  For example, the 
local channel expects completed delivery of the entire message
for each addressee.
Alternatively, the telephone and SMTP packages expect the address list for
a message to be sent before the (one) copy of the message text.

For the currently implemented channels, the Local
delivery channel expects text-writing calls after each address
call.  Most other channels expect
addresses to be specified before text is sent.  The independent
local submission and pickup modules are list oriented, although
submission can accept text-per-address, somewhat awkwardly.
The
.I deliver
module (qu_) will handle either mode.
.SS "CHANNEL PROCESSING PHASES"
.PP
A channel module might go through several phases:

.nf
.IP 1. 4
Channel initialization
.IP 2. 4
Mode initialization (sending or receiving)
.IP 3. 4
Message initialization
.IP 4. 4
Address transmission
.IP 5. 4
Text transmission
.IP 6. 4
Optional iteration to step 3
.IP 7. 4
Mode termination
.IP 8. 4
Optional iteration to step 2
.IP 9. 4
Channel termination.
.fi
.PP
In many cases, particularly initializations, the module's
calls for a phase are no-ops.  However, they are included for
completeness and to permit easy modification.

To callers, message initialization only requires passing a
mode string (usually containing an "m" to indicate that the mail
is for a mailbox, rather than a terminal) and a return address
string.

Address passing entails iteratively passing a host/local address
combination and retrieving a
.I mmdf.h
reply value for the address.  The
host portion may not be passed to the destination channel, as in
the case of the local channel
since there is no question for which host the address is intended.
(This does not preclude the local address portion from containing
additional host references, if the one receiving the address is a
relay.)

Text transfer is essentially a stream file transfer, effected
by repeated calls to pass successive chunks of the message.
.SS "INSTRUCTION SETS"
.PP
The following are the calls from which a channel draws its
instructions.  No single module uses the entire set.
.PP
.ne 13
Session handling (in xx_io):

.ta .2i 1.0i 2.4i 4.0i 
.RS
.nf
*	xx_init	Get ready to handle mail
*	xx_end	End this session
*	xx_synch	Re-join sub-machines

*	xx_sbinit	Caller will be submitting mail
*	xx_sbend	Done submitting mail

*	xx_pkinit	Caller wishes to receive mail
 	xx_pkkill	Caller wishes to stop receiving
.fi
.RE
.PP
.ne 12
Receiving mail (in xx_rmail):
.RS
.nf
*	xx_rinit	Caller wishes to receive a message
 	xx_rkill	Terminate current message

 	xx_rainit	Caller ready to receive addresses
*	xx_radr	Get an address

*	xx_rtinit	Caller ready to receive text
*	xx_rtxt	Get some message text
 	xx_rtcpy	Give me a handle to text copy
.fi
.RE
.PP
.ne 13
Sending mail (in xx_wtmail):

.RS
.nf
*	xx_winit	Caller ready to send a message
 	xx_wkill	Terminate current message

*	xx_wainit	Get ready for addresses
*	xx_wadr	Here is an address
*	xx_waend	Done sending set of addresses

*	xx_wtinit	Get ready for message text
*	xx_wtxt	Here is some message text
*	xx_wtend	End of message text
.fi
.RE
.PP
.ne 11
Basic I/O (in	xx_io):

.RS
.nf
*	xx_wrply	Here is a reply
*	xx_rrply	Get a reply

*	xx_wrec	Here is a record/packet
*	xx_wstm	Here is part of a stream of text

*	xx_rrec	Get a record
*	xx_rsinit	Prepare to read stream
*	xx_rstm	Get part of a stream of text
.fi
.RE
.PP
Notes:
.RS
.PP
The asterisks indicate which calls currently are used
by at least one channel.

.RE
.PP
.ne 21
A common calling sequence will comprise:

.RS
.ta  1.2i 3.6i
.nf
xx_init	/* init for sending	*/
xx_winit	/* init first message	*/
xx_wadr	/* send first address	 */
xx_rrply	/* address acceptable?	*/
xx_wadr	/* second address...	*/
xx_rrply
  ...
xx_waend	/* no more addresses	*/
xx_wtinit	/* message text next	*/
xx_wtxt	/* send a chunk of it	*/
xx_wtxt	/* another...	*/
  ...
xx_wtend	/* no more message	*/
xx_rrply	/* text accepted ok?*/
xx_winit	/* next message...	*/
  ...
xx_sbend	/* no more sending	*/
xx_end	/* done with channel	*/
.fi
.RE
.PP
Note that the only direct user calls to the xx_io part
of the module are for initialization.  No record or stream calls
are made directly.
.ne 16
.SS "EXISTING MODULES"
.PP
The following shows what channel programs and modules exist
and what their names are.

.nf
.RS
.ne 15
.ta 1.2i 2.4i 3.6i 4.8i 6.0i 
CHANNEL PROGRAMS

Name	Program	Description
-------	--------	------------------
SMTP	smtp	Internet RFC821 SMTP outbound
SMTP	smtpsrvr	Internet RFC821 SMTP inbound
Local	local	Local machine send
Phone	phone	PhoneNet dial-out (send & pickup)
P.O. Box	pobox	Release held mail (currently to PhoneNet slave)
UUCP	uucp	UUCP outbound (calls uux)
UUCP	rmail	UUCP inbound
List	list	Re-submit mailing list mail with header rewriting
Ean (in)	ean2mmdf	EAN X.400 mail system to MMDF
Ean (out)	ean	MMDF to EAN X.400 mail system
Delay	delay	Delay channel (to resubmit due to NS failure)
Prog	sendprog	Generalized program interface (outbound)
Prog	recvprog	Generalized program interface (inbound)

.ne 14
MAIL MODULES

Channel	Basic	Receiving	Sending
-------	----------	---------	-----------
Deliver	qu_io	qu_rdmail
MMDF	mm_io	mm_rdmail	mm_wtmail
Local	lo_io	lo_rdmail	 lo_wtmail
SMTP	sm_io		sm_wtmail
Phone	ph_io	ph_rdmail	ph_wtmail
	ph_iouser
	ph_ioslave
P.O.Box	po_io		po_wtmail
.fi
.RE
.PP
Notes:
.RS
.PP
.I phone
dials out to the site containing
.I ph_slave
and
invokes it.  Ph_io is combined with ph_iouser or
ph_ioslave to make a full I/O set.  

mm_wtmail calls
.I submit.
mm_rdmail calls 
.I deliver
(by default) in pickup mode.  The default pickup
program may be changed by altering the variable
pathpkup[] in
.I conf.c.
The mm module is the user software interface to MMDF, for mail
submission and POBox delivery (pickup).

Lo_wtmail effects actual mailbox-stuffing.  It does not
have an associated lo_io because it was not needed.
 if the one receiving the address is a
relay.)

Text transfer is essentially a stream mmdf/man/man8/   755      0     12           0  3656411051   6305 mmdf/man/man8/checkaddr.8   444      0     12        1730  3642035047  10374 .TH CHECKADDR 8
.SH NAME
checkaddr \- MMDF address verification program
.SH SYNOPSIS
.B checkaddr
[
.B \-w
] [ \fIaddresses...\fR ]
.SH DESCRIPTION
.PP
The \fIcheckaddr\fR program is used to check the validity of an
address with the local mail system (MMDF).
\fICheckaddr\fR can be given addresses either on the command line,
one address per argument, or a list of addresses can be given to
\fIcheckaddr\fR on the standard input, one address per line.
The latter mode is use for checking the addresses in a mailing
list by saying ``\fIcheckaddr\fR\ <\ mailing-list-file''.
\fICheckaddr\fR will announce each address on a separate line
and follow the address with its status (normally ``OK'').
\fICheckaddr\fR uses the MMDF program \fIsubmit\fR to do the
address verification.
.PP
If the \-w option is given,
.I checkaddr
cause submit to generate detailed submission tracing.
This can sometimes be useful to help find problems
in alias files or mailing lists.
.SH "SEE ALSO"
submit(8)


Text transfer is essentially a stream mmdf/man/man8/checkque.8   444      0     12        5470  3642074420  10257 .TH CHECKQUE 8
'ta .8i 1.6i 2.4i 3.2i 4.0i 4.8i 5.6i 6.3i
.SH NAME
checkque \- MMDF queue status report generator
.SH SYNOPSIS
checkque [\-fpsz] [\-t<age>[m]] [\-c \fIchannel channel ...\fR]
.SH DESCRIPTION
.PP
.I Checkque
reports on the amount of mail waiting in the MMDF distribution
queue.  It indicates the total number of messages and the
size of the queue directory.  It then lists the number of
messages waiting for each transmission channel.
.PP
The \-\fBc\fR option allows one or more channel names to be specified.
If present, \fIcheckque\fR restricts it's report to the named channels.
.PP
The \-\fBf\fR option causes \fIcheckque\fR to print the
name of the oldest queued message for each channel.
\-\fBp\fR causes only channels with "problems" to be listed.
Problems are defined as channels which have have mail waiting for over
some "problem threshold".  The default "problem threshold" is 24 hours.
The \-\fBt\fR option is used to change the "problem threshold".  A number
of hours (or minutes, if "m" is appended) should appear without a space
after the \-\fBt\fR.
\-\fBs\fR forces an abbreviated summary listing instead of the
normal multi-line report.
\-\fBz\fR causes channels with no messages queued to be skipped in
the report.
.PP
Since the mail queue usually is protected from access by any uid,
except MMDF's, 
.I checkque
should be run under root or MMDF's uid.  It should not be made
\fIsetuid\fR() to mmdf unless you want to allow non-staff members
to see the queue status.
.PP
Most configurations
will have only two channels.  One is for local delivery and
the second is for off-machine relaying, such as by calling out
or by being called up, or by attaching to ArpaNet hosts.
Local delivery usually happens at the time of submission, so it
is rare that any mail is waiting in it.
Mail in other outbound queues is processed by
.I deliver
according to your site parameters, either by running
.I deliver
as a background daemon or by periodically firing it up via
.I cron.
.PP
A word about the size of the queue directory (aquedir[] under
quedfldir[]):  Under versions of Unix other than BSD4.2, this 
only grows in size; there is no retrieval of space as messages
are processed out of the queue.  Since queues are searched 
linearly, this can lead to extra processing for
.I deliver.
Hence, it can be useful to know the density of the queue (how
many messages are actually waiting, compared with the number of
slots open in the queuing directory) in order to decide whether
to compress the directory.  This is a messy process, but can
be worthwhile in some cases.  Under BSD4.2, however, directories are 
automatically compressed when files are deleted.
.SH FILES
quedfldir[]/addr
.br
quedfldir[]/msg
.br
quedfldir[]/q.*
.br
<phase-directory>/<channel>/*
.SH "SEE ALSO"
phs(3), deliver(8)
.SH AUTHOR
Dave Crocker, Dept. of E.E., Univ. of Delaware
rec	Here is a record/packet
*	xx_wstm	Here is part of a stream of text

*	xx_rrec	Get a record
*	xx_rsinit	Prepare to read stream
*	xx_rstm	Get part of a stream of text
.fi
.RE
.PP
Notes:
.RS
.PP
The mmdf/man/man8/checkup.8   444      0     12        2565  3642073147  10120 .TH CHECKUP 8 "27 December 1985"
.SH NAME
checkup \- Report on things wrong in the MMDF hierarchy
.SH SYNOPSIS
.B checkup
[
.B \-p
.B \-v[\fIdigit\fR]
]
.SH DESCRIPTION
.PP
.I Checkup
is responsible for checking the consistency of
the current MMDF system.
Normally, it reports on all that it finds, including
correct states. Things that it considers wrong are usually
prefixed by two asterisks (**); information that is advisory
is enclosed in square brackets ([]).
.PP
The two optional flags to checkup alter the
verbosity of 
.I checkup's
reporting:
The \-p
option reports only problems that 
.I checkup
comes across.
This is useful for day-to-day checking of the system, such as
mailing the output to postmaster.
.PP
The \-v
flag takes an optional 
.I digit
to set the verbosity to
some arbitrary value. If the
.I digit
is omitted, verbosity is set to the maximum.
Values range from 1 (\-p)
level to 7 (which gives everything).
.PP
Some advice
.I checkup
gives about modes should not be taken too
seriously (e.g. 0755 v. 0711).
In particular it is common for sites to allow group read,
write, or execute on files that
.I checkup
expects to be protected more carefully.
Use of group permissions can greatly ease administration
effort for system managers without compromising security.
Warnings regarding ``others'' permissions should be examined.
.SH "SEE ALSO"
Installing and Operating MMDF II
xed by two asterisks (**); information that is advisory
is enclosed in square brackets ([]).
.PP
The two optional flags to checkup alter thmmdf/man/man8/cleanque.8   444      0     12        1703  3642035050  10253 .TH "CLEANQUE" 8 "7 Nov 84"
.SH NAME
cleanque \- send warnings and return expired mail
.SH SYNOPSIS
cleanque [ \-w ]
.SH DESCRIPTION
.I Cleanque 
removes extraneous files from the \fBtmp\fR and \fBmsg\fR subdirectories
of the MMDF "home queue" directory.  It also sends warnings for mail which
has not been fully delivered after "warntime" hours following submission.  
Finally, it returns mail which has not been fully delivered after "failtime"
hours after submission.  "Warntime" and "failtime" are defined in MMDF's 
.I conf.c
file (or set in the runtime tailor file).
.PP
Generally,
.I cleanque
should be run by
.I cron,
once a day, but may be run at any time to free up space.
.PP
The optional argument, \-w, can be used if you are running
\fIcleanque\fR manually and want to see what the program is doing.
.SH "SEE ALSO"
queue(5), deliver(8)
.SH BUGS
.I Cleanque
should also remove extraneous files from the individual queues (\fBq.*\fR
subdirectories).  
 about modes should not be taken too
seriously (e.g. 0755 v. mmdf/man/man8/dbmbuild.8   444      0     12        3200  3642035050  10232 .TH DBMBUILD 8 "8 January 1986"
.SH NAME
dbmbuild \- database builder for MMDF address database
.SH SYNOPSIS
dbmbuild [ \-nvdk] ] [ database [table ...] ] 
.SH DESCRIPTION
.I Dbmbuild
reads the tables specified in the MMDF tailor file into a hashed
database for use in quickly verifying addresses and efficiently assigning
channels to submitted messages.  If no database file is specified, the
default dbm filename is used from the MMDF tailor file.  If no table files
are specified, all tables listed in the tailor file are used.  In particular,
three tables are read for each channel definition: the list of authorized
sources, the list of authorized destinations, and the table of names/aliases
for that channel.  Also, the remaining tables (MTBL and MDMN) are read.
.PP
The options are:
.IP n
Create a new database.  If this option is omitted, 
.I dbmbuild
updates an existing database.
.IP v
Verbose mode.  Print out information during table processing.
.IP d
Debug mode.  Report everything that happens.
.IP k
Keep going.  If a file is mentioned that doesn't exist, ignore it.
It might be appropriate to make this the default at some sites.
.PP
If no options are specified, `-n' is assumed.  Appropriate locks are placed on
the database so that
.I dbmbuild
can safely be run while MMDF is in operation.
N.B.  Even use of the `-v' option
will turn off the default `-n' option.
.SH FILES
.nf
$(tbldbm).dir    - database directory
$(tbldbm).pag    - database pages
$(tbldbm).lck    - database locking file
$(tbldfldir)/*   - various tables that form the database.
.fi
.SH "SEE ALSO"
dbm(3X), tables(5), dbmedit(8), dbmlist(8), "Installing and Operating MMDFII"
] [ database [table ...] ] 
.SH DESCRIPTION
.I Dbmbuild
reads the tables specified in the MMDF tailor file into a hashed
database for use in quickly verifying addresses and efficiently assigning
channels to submitted messages.  If no database file is specified, the
default dbm filename is used from the MMDF tailor file.  If no table files
are specified, all tables listed in the taimmdf/man/man8/dbmedit.8   444      0     12        7166  3653505706  10113 .TH DBMEDIT 8 "24 April 1986"
.SH NAME
dbmedit \- edit the mmdf database file
.SH SYNOPSIS
.B dbmedit
[
.B \-v
] [
.B \-d
database
] [
.B cmd
.B \.\.\.
]
.SH DESCRIPTION
.I Dbmedit
is a program to manually edit the \fIdbm\fR(3) database used by MMDF.
It is intended to be used by the system maintainer for quick
and simple changes to the database.
It can also be used by careful setuid programs to make controlled
changes on behalf of users.
For example, a \fIforwardmail\fR command might use
dbmedit to change a user's entry in the
dbm database after changing the mail forwarding alias file.
.PP
The \-v option may be used to get a verbose description of the
program's activities.
.PP
The \-d option may be used to specify an alternate database.  The
default is given by the ``tbldbm'' configuration variable or by the
MDBM mmdftailor variable.
.PP
If no arguments are given to \fIdbmedit\fR then the program
goes into an interactive mode, and prompts the user for each
command. Otherwise the arguments are taken as one command.
.PP
Commands in \fIdbmedit\fR refer to keys, tables, and values.
Tables (see tables(5)) are hashed into the database using \fIdbmbuild\fR(8).
(Tables that refer to domain name servers are not part of the database).
The keys appear on the LHS of the tables and the values on the RHS.
In general, only the first occurence of a value for a given
key/table pair is significant.  
For example, the table entries:
.nf
	table1:
		key1: val1      
		key2: val2      
	table2:
		key1: val3      
		key1: val4      
get hashed into the following database entries:
	key1  table1  val1
	key1  table2  val3
	key1  table2  val4
	key2  table1  val2
.fi                        
(In the current implementation, the database is keyed on only
the key, and table/value pairs are encoded in the data portion.
This is likely to change but will not affect this or any other
program.)
.PP
The command lines in interactive mode are parsed using the standard
MMDF string-to-argument routines so the same quoting
and escape conventions are used.
For example, if you want double-quotes or spaces
in the value, they must be
escaped with a backslash or the string must be quoted (for spaces).
.PP
The commands are:
.RS
.TP 4
.I print key [table]
Print the value of the key/table pair.
If the table is omitted, then print the value of any
table entry with this key.
.TP 4
.I add key table value
Add a key/table entry with the given value.
In verbose mode, a warning message is printed if the given
key/table pair already has a value in the database.  
.TP 4
.I delete key [table [value]]
Delete the values for the specified key.  If a table is specified,
delete only the values for the specified key/table pair.  If a value
is also specified, delete only entries for the pair with that value.
It is an error to try to delete something which does not appear in
the database as specified.
.TP 4
.I change key table [oldvalue] newvalue
Change the value of the specified key/table pair to
.I newvalue.
If 
.I oldvalue
is specified, change the entry matching that value.  Otherwise,
change the value of the first occurence or add a new key/table pair
if none already exists.
.TP 4
.I help
Give a brief summary of the commands
.TP 4
.I quit
Exit the program.
.RE
.PP
All commands may be shortened to their first character only.  
If the wrong number of arguments is given to a command, a
``Usage:'' message will be printed.  
All changes are made in real time.
This program may be used while MMDF processes are running.
There is no temporary copy of the database while editing.
.SH FILES
$(tbldbm).{dir,pag} - the mmdf database
.SH "SEE ALSO"
tables(5), dbmbuild(8), dbmlist(8)
.SH AUTHOR
Phil Cockcroft, UCL
 a verbose description of the
program's activities.
.PP
The \-d option may be used to specify an alternate database.  The
default is given by the ``tbldbm'' configuration variable or by the
MDBM mmdftailor variable.
.PP
If no arguments are given to \fIdbmedit\fR then the program
goes into an interactive mode, and prompts the user for each
command. Otherwise the arguments are taken as one commmdf/man/man8/dbmlist.8   444      0     12         756  3642035050  10103 .TH DBMLIST 8 "8 January 1986"
.SH NAME
dbmlist \- List out a database.
.SH SYNOPSIS
.B dbmlist
[
.B file
]
.SH DESCRIPTION
.PP
.I Dbmlist
lists, by default, the current MMDF address database
giving all the records stored in the database.
The optional argument is to specify a different file from the
default database.
.SH FILES
.nf
$(tbldbm).dir  - database directory
$(tbldbm).pag  - database pages
$(tbldbm).lck  - database locking file
.fi
.SH "SEE ALSO"
tables(5), dbmbuild(8), dbmedit(8)
ing.
.SH FILES
$(tmmdf/man/man8/deliver.8   444      0     12       11437  3642074176  10151 .TH DELIVER 8 "20 October 84"
.SH NAME
deliver \- MMDF mail delivery process
.SH SYNOPSIS
deliver [-bdpsw]
[-c\fIchan,chan\fR]
[-l\fImins\fR]
[-t\fIhrs\fR]
[-m\fImaxsort\fR]
[-L\fIlogfile\fR]
[-T\fIsecs\fR]
[-V\fIloglevel\fR]
[\fImessage1 ... messageN\fR]
.SH DESCRIPTION
The deliver program handles the management of all mail delivery
under the MMDF mail system.  \fIDeliver\fR does not deliver mail directly,
but instead calls on MMDF channels to handle actual delivery.
\fIDeliver\fR's actions are guided by the MMDF tailoring file,
\fI/usr/mmdf/mmdftailor\fR, and by the command line options.
The program can run as either a daemon or a user-invoked program.
The program may be called to process the entire mail queue or just
handle some explicitly named messages.
When possible,
.I deliver
will attempt to process messages in the order received.
Deliver also maintains a cache of host information on a per-channel
basis which allows hosts which are unavailable for delivery
to be skipped until available.
.PP
.I Deliver
first builds a list of channels to process, either from the
command line or composed of all the non-passive channels in
the system.  Next, a list of messages to process is collected,
either from the command line or by scanning the mail queue for
for each channel.  If the the number of messages in the queue
for a given channel is more than \fImaxsort\fR (set in tailor
file or on command line), 
the queue directory for that channel
will be processed in the order read, without sorting by submission
time.  If a list of messages is given on the command line, no
sorting will take place and the messages will be delivered in the order
specified.
The sorting keys are (in order): channel,
submission time, and finally host.
This causes many accesses to the messages
but minimizes the invocation of channel programs.
.PP
.I
Deliver
is \fIsetuid\fR to the superuser to allow it to set its
real and effective UID and GID to that of the MMDF user.
.PP
The following options may be used to alter \fIdeliver\fR's behavior:
.TP
.B \-b
Background mode. Causes 
.I deliver
to run as a background daemon
making periodic sweeps over the mail queues looking for undelivered
mail and attempting deliver.  The invoker must be the MMDF user
or the superuser to use this option.
.I Deliver
attempts delivery for all eligible messages, then sleeps,
and then repeats the process.
The default sleep time is 10 minutes but it can be changed
(see the \-T option below).
.TP
.BI \-c channel1,channel2,...
Channel selection.  A comma-separated list of channels to be processed.
.TP
.B \-d
Already in ``quedfldir''.  This option will cause
.I deliver
to assume it is already in the mail queue and therefore it will not
issue an explicit chdir().  This is useful if you wish to have deliver
operate on an alternate mail queue hierarchy, mainly for testing.
.TP
.BI \-l minutes
Sets the ``time-to-live'' for entries in the dead-host cache.
This time defaults to 2 hours.  The dead host cache
is used to prevent attempts to deliver to hosts that are
known to be down.  The ``time-to-live'' is given in minutes.
If the number of minutes is negative, dead host caching is disabled.
.TP
.BI \-m maxsort
Sets the sort threshold.  If there are more than \fImaxsort\fR messages in
a given channel's queue, then they are processed in directory order 
without first sorting by submission time.  
If \-m is not specified, the value of 
\fImaxsort\fR is given in the tailor file by MMAXSORT or in conf.c.
.TP
.B \-p
Pickup only mode. Indicates that the invoker would like to pickup
a passive mail channel.
.TP
.B \-s
Force linear search of the mail queue.  Normally
.I deliver
will deliver messages in the order they were received which
seldom matches the order in the directory.  This option is useful if the
queue gets so large that
.I deliver
can no longer deal with sorting the
queue in a reasonable time.
.TP
.BI \-t hrs
Time limiting. This option prevents
.I deliver
from attempting to deliver messages which have been in the queue for more
than \fIhrs\fR hours.
.TP
.B \-w
Watch the delivery. Causes
.I deliver
to print informative messages on the standard output as
it is attempting delivery.
This option is passed onto the channel programs
which also give informative messages.
.TP
.BI \-L logfile
Sets the logfile for this deliver to the file specified.
The default is to log into the file msg.log in the MMDF
log directory.
This option is only available to the Superuser and MMDF.
.TP
.BI \-T seconds
Sets the sleep time between background sweeps of the mail queue.
This defaults to 10 minutes.
.TP
.BI \-V loglevel
Sets the logging level for this deliver to the level specified.
The \fIloglevel\fR should be a valid mmdf logging level string such as FTR.
This option is only available to the Superuser and MMDF.
.SH "SEE ALSO"
submit(8), channels(7), queue(5), "Installing and Operating MMDF II"
NG MODULES"
.PP
The following shows what channel programs and modules exist
and what their names are.

.nf
.RS
.ne 15
.ta 1.2i 2.4i 3.6i 4.8i 6.0i 
CHANNEL PROGRAMS

Name	Program	Description
-------	--------	-----------------mmdf/man/man8/ean.8   444      0     12        1777  3642073474   7250 .TH EAN 8 MMDF
.SH NAME
EAN X.400 Channel
.SH DESCRIPTION
.PP
The EAN channel is structured and invoked in the manner
described in
.IR channels (7).
The channel passes messages to the EAN X.400 system developed
by the University of British Columbia.
.PP
This code requires licences for MMDF and EAN.  Subject to this,
the EAN/MMDF interface code may be used and modified freely,
provided that any changes are passed back to University College
London.
.PP
The channel Makefile should be modified to point to the EAN
source directories.  Otherwise installation and operation is standard.
A connection named ".local", and private domains appropriate to
any MMDF sender (i.e. the rightmost domain
component) should be added to the EAN directory.  Note that
this is slightly different than the Sendmail mailer, which
assumes .UUCP\ .
.SH FILES
/lib/mmdf/chans/ean
.br
mmdfsource/src/ean/*
.SH "SEE ALSO"
.IR ean2mmdf (8)
.SH BUGS
.PP
Should do more address checking before passing the message to
EAN.
.SH AUTHOR
.PP
Steve Kille
lmmdf/man/man8/ean2mmdf.8   444      0     12        1536  3642035051  10154 .TH EAN2MMDF 8 
.SH NAME
EAN X.400 to MMDF interface
.SH SYNOPSIS
ean2mmdf
[ -f sender ] address ....
.SH DESCRIPTION
.PP
.I Ean2mmdf
is invoked by EAN X.400 to pass messages to MMDF through
.IR submit (8)
.PP
This code requires licences for MMDF and EAN.  Subject to this,
the EAN/MMDF interface code may be used and modified freely,
provided that any changes are passed back to University College
London.
.PP
.I Ean2mmdf
is installed in the MMDF program directory.  The EAN
source nsg/A822/config.c  shold be modified to contain #define
MMDF, and the command line changed to contain the full path of
.I ean2mmdf.  
The EAN directory should contain a connection,
DIFFERENT THAN THE ".local" CONNECTION, with connection info
"nsg,a822,mmdf".  All top level MMDF domains should point to
this, as private domains.
.SH "SEE ALSO"
.IR ean (8)
.SH AUTHOR
Steve Kille

mmdfsource/src/ean/*
.SH "SEE ALSO"
.IR ean2mmdf (8)
.SH BUGS
.PP
Should do more address checking before passing the message to
EAN.
.SH AUTHOR
.PP
Steve Kille
lmmdf/man/man8/jntmail.8   444      0     12        5615  3642073511  10126 .TH JNTMAIL 8 MMDF
.SH NAME
JNT Mail Channel
.SH DESCRIPTION
.PP
The JNT Mail channel is structured and invoked in the manner
described in
.IR channels (7).
The channel generates files of the format specified in the JNT Mail
Protocol.  Copies of the JNT Mail Protocol can be obtained from
the Joint Network Team, c/o Rutherford Appleton Labs., Chilton,
Oxon, UK.
JNT Mail Files are created in the directory specified by the
variable
.br
        char    *pn_quedir
.br
which is tailored by the parameter NIQUEDIR (see the MMDF installation guide).
If #define VIATRACE  is set, the message header in the JNT
Mail File will be modified to ensure that a correct receiving
implementation will determine  a return path corresponding to
that passed to the channel.
.PP
When the file has been created, a file transfer process is
called.  Whilst this could in  principle be any File Transfer
Protocol, it is expected that in general the NIFTP (blue book)
protocol will be used.  The calling (execl) sequence is specified by
the channel tailor parameter confstr.  If the string contains a
sequence $(parm) where parm is one of the strings listed, the
value appropriate to the file will be inserted:
.ne 6
.nf

        file    - The full path of the JNT Mail File
        host    - The host (LHS of channel table)
        adr     - The address (RHS of channel table)
        sender  - Error return address

.fi
.ne 16
Examples:
.nf

For the York NIFTP:

MCHN  janet    name=janet,que=janet,tbl=janet,
        show="via Janet with NIFTP",
        pgm=niftp,poll=0,mod=reg,ap=jnt,
        confstr="/usr/lib/x25/mhhcp mhhcp $(FILE) $(ADR)"

For the UCL/UKC NIFTP:

MCHN  janet    name=janet,que=janet,tbl=janet,
        show="via Janet with NIFTP",
        pgm=niftp,poll=0,mod=reg,ap=jnt,
        confstr="/usr/bin/cpf cpf -t -f\e"$(SENDER)\e" $(FILE) @$(ADR)"
.fi
.ne 15
.PP
The channel interprets the FILE Transfer Protocol exit values
according to the values defined in h/jnt.h:
.nf

#define JNT_OK          0       (Successful)

#define JNT_TEMP        1       (Temporary failure
                                - MMDF will retry)
                                (This is the default)

#define JNT_PERM        75      (Permanent failure
                                - MMDF will send
                                an error message
                                to the sender)

.fi
If JNT_OK is returned, the File Transfer Protocol is expected
to unlink the JNT Mail File, to allow for spooled or
non-spooled operation.   In other cases, the channel will unlink the
file.
.PP
The JNT Mail channel runs setuid to daemon.  If it is used with
a File Transfer Protocol which expects a different uid, both
this uid and the ownership of the JNT Mail spool directory
should be changed accordingly.
.SH FILES
.nf

        <mmdflib>/chans/niftp
        /usr/spool/jntmail.ni.???????
        <mmdfsource>/h/jnt.h
.fi
.SH "SEE ALSO"
.IR ni_niftp (8)
.br
.IR york-inst (8)
MMAXSORT or in conf.c.
.TP
.B \-p
Pickup only mode. Indicates that the invoker would like to pickup
a passive mail mmdf/man/man8/list.8   444      0     12        5201  3642035051   7427 .TH "LIST" 8 "8 Nov 84"
.SH NAME
list \- list processor channel for MMDF
.SH SYNOPSIS
list
.SH DESCRIPTION
.I List
is an MMDF channel program for handling mailing lists.
The channel functions as a feed-through between 
.I deliver
and
.I submit.
The list channel has its own host table and domain table
with one entry for the pseudo host ``list-processor'' or something
similar.
This program is called by the program \fIdeliver\fR and is not
meant to be invoked by users directly.
.PP
The 
.I list
channel performs two basic services.  First, it postpones
the verification of the list addresses and performs 
the (possibly lengthy) verification
in the background when the 
.I list
channel resubmits the message
to the mail system.  This prevents tying up a network connection
or a user's terminal when verifying a long mailing list.
Second, the 
.I list
channel will, under special circumstances, change
the return address for the message to a generic maintainer's address.
The return address is determined by first taking the destination
address (e.g. ``largelist'') and seeing if there is an address
in the alias file called ``largelist\-request''.  If there is,
then ``largelist-request'' is used as the return address.
If that was not found, the list channel checks to see if
the destination address has a trailing ``\-outbound''.
If so, this is
stripped and a ``\-request'' is added and the lookup in the
alias file is made a second time.  If the ``\-request'' address
is found, then that address is used as the return address.
If no ``\-request'' address is found, then the original return address
is used (normally the address of the sender).
.PP
To use the
.I list
channel to process a list, it is generally necessary
to make three entries in the alias file(s).
Let us say that we wish to set up a list called ``largelist''
and we want this list to be processed by the
.I list
channel.
We would need the following entries in the alias file:
.nf
.ta 2.0i
.sp
.in +.6i
largelist:	largelist-outbound@list-processor
.br
largelist-outbound:	</usr/mmdf/lists/largelist-file
.br
largelist-request:	maintainer
.in -.6i
.fi
.sp
The first line causes mail sent to ``largelist'' to be sent through
the list processor, readdressed to ``largelist-outbound''.
The second line is what actually references the mailing list
file for ``largelist''.
The third line is optional, and is used to set up the
(informal) standard maintenance address.  This \-\fIrequest\fR
address, if present, will also be used by the 
.I list
channel
as the return address for mail submitted to the list.
.SH SEE ALSO
deliver(8), submit(8)
.SH FILES
<mmdf-table-directory>/aliases \- to find list\fI\-request\fR addresses
 there is,
then ``largelist-request'' is used as the return address.
If that was not found, the list channel checks to see if
the destination address has a trailing ``\-outbound''.
If so, this is
stripped and a ``\-request'' is added and the lookup in the
alias file is made a second time.  If the ``\-request'' address
is found, then that address is used as the return address.
If nmmdf/man/man8/newssend.8   444      0     12        2260  3642035660  10312 .TH NEWSSEND 8 "28 January 1986"
.SH NAME
newssend \- feed news into mail system
.SH SYNOPSIS
.B newssend
[
.B \-r
.I replyto
]
.I faketo
.I realto
[
.I more-realto's
]
.SH DESCRIPTION
.I Newssend
is responsible for feeding USENET news articles into
the Internet mail system, typically to Internet
mailing lists.
It strips out many of the USENET header lines that are
not necessary or wanted on the Internet.
.PP
The values of the submit options (including the input
channel name) and the header lines to be deleted are
hardcoded in the program but may want to be tailored
on a per-site basis, particularly the channel value.
The following arguments are used:
.IP "\-r replyto"
This specifies the return address (\fIreplyto\fR)
to be used for this message.  Depending on the submit
option used by newssend, this may be ignored.
.IP faketo
Since most USENET articles lack a ``To:'' field, the argument
is used to generate a ``To:'' field for the message.
\fIfaketo\fR should point to an alias that which feeds the message
back into the news system.
.IP "realto \.\.\."
This is the actual address(es) that the article should be
mailed to.  This is typically something like ``info-something-outbound''.
s the return address.
If that was not found, the list channel checks to see if
the destination address has a trailing ``\-outbound''.
If so, this is
stripped and a ``\-request'' is added and the lookup in the
alias file is made a second time.  If the ``\-request'' address
is found, then that address is used as the return address.
If nmmdf/man/man8/ni_niftp.8   444      0     12        3602  3656410724  10276 .TH NI_NIFTP 8 MMDF
.SH NAME
JNT Mail File Submission process
.SH SYNOPSIS
.B ni_niftp
Net File TSaddress
.SH DESCRIPTION
.PP
.I Ni_niftp
is invoked by a File Transfer Protocol server to interpret a
JNT Mail File (see
.IR jntmail (8)
) sent to it.
.I Ni_niftp
queues the message using
.IR submit (8)
and returns error messages relating to bad addresses in the JNT
Mail file.
.I Ni_niftp
must be invoked with a privileged real uid (root, daemon, or
mmdf).
.PP
.I File
is the JNT Mail file to be submitted.
.I Net
is the network which the JNT Mail File has come from.  A table
niftp.nets must exist in the tailor file (see the MMDF installation document)
which has legal values of net on the LHS and the associated
MMDF channel on the RHS.   An example tailor entry is:
.ne 3
.nf

MTBL  name="niftp.nets", file="niftp.nets",
        display="NIFTP net to channel map"

.fi
This indirection allows the NIFTP to have different
channel nomenclature to MMDF,  and gives a measure of security
by preventing JNT Mail on spurious channels.
.I TSaddress
should correspond to the LHS of the associated channel table,
but may be in reverse domain ordering (on the basis that many
NIFTP implementations may only understand NRS ordering).  This
will be used in the message trace information.  If it is not in
the tables, it will be treated as a domain literal.
Any "," or "*" characters in the string will be mapped to "?"
for compatibility with
.IR submit (8).
.PP
The exit values of 
.I ni_niftp 
are standard mmdf exit values
as defined in h/mmdf.h.  RP_NDEL is given special
significance, as a JNT Mail File with no valid destination
address, where no return address could be determined.
.I Ni_niftp
will unlink the JNT Mail file, except in cases of permanent
failure, when the file will be left to assist debugging.
.ne 7
.SH FILES
<mmdflib>/ni_niftp
.br
<mmdfsource>/h/mmdf.h
.SH "SEE ALSO"
jntmail(8), york-inst(8), york-inst(8)
k-inst (8)
MMAXSORT or in conf.c.
.TP
.B \-p
Pickup only mode. Indicates that the invoker would like to pickup
a passive mail mmdf/man/man8/nictable.8   444      0     12        3372  3642074127  10253 .TH NICTABLE 8 "17 June 85"
.SH NAME
nictable - process NIC database into channel/domain tables
.SH SYNOPSIS
nictable
\-[CDT]
[\-d \fIdomain\fR]
[\-s \fIservice\fR]
[\-t \fItransport\fR] 
.SH DESCRIPTION
.PP
.I Nictable
is the tool responsible
for taking the hosts.txt table supplied by the SRI Network
Information Center and creating domain and channel tables.
The format of the NIC table is subject to change, and occasionally
this program must be modified appropriately.
.PP
The \-C option causes the program to generate a channel table on the
standard output.
The \-D option creates a domain table.  This option should be combined
with the \-d option explained below to state which domain table you are
building.
The \-T option creates a ``top'' or ``rootdomain'' table.
No trailing domain spec is removed from the LHS entry.
.PP
There are several options for further restricting the number of hosts chosen.
The \-d \fIdomain\fR option states that only hosts in the domain specified
should be output.  An exception to this is when \-d is combined with \-T.
In this case, all entries will be output EXCEPT for those in the domain
specified.  The intention is that you grap all of one domain with \-D,
and then grab everybody else with \-T.
The \-s \fIservice\fR option states that only hosts that are listed as
supporting the service specified should be output.
The \-t \fItransport\fR option is like \-s except it states that only
hosts supporting the transport protocol specified should be considered.
.PP
Typical usage involves two or three invocations:
.sp
nictable -C < /etc/hosts.txt > smtpchannel
.sp
nictable -D -d ARPA < /etc/hosts.txt > arpadomain
.sp
(and optionally)
.sp
nictable -T -d ARPA < /etc/hosts.txt > rootdomain
.SH "SEE ALSO"
"Installing and Operating MMDF II"
king the hosts.txt table supplied by the SRI Network
Information Center and creating domain and channel tables.
The format of the NIC table is subject to change, and occasionally
this program must be modified appropriately.
.PP
The \-C option causes the program mmdf/man/man8/phone.8   444      0     12       11335  3642075766   7633 .TH PHONE 8
.SH NAME
phone \- MMDF PhoneNet Phone Channel
.SH SYNOPSIS
phone
.SH DESCRIPTION
.PP
The \fIphone\fR channel is the active (or master) component of the
PhoneNet protocol
package.  The passive component is the \fIslave\fR program (see \fIslave\fR(8)).
\fIPhone\fR establishes a connection to a remote machine's \fIslave\fR
by following
the instructions contained in a dialing script.  The dialing script is
specified in the channel's definition in the runtime tailoring file by the scr=
parameter.  Scripts reside, by default, in the table directory.  See script(5)
for information about how to construct a dialing script.
.PP
Once a connection has been established, the ``start'' command initiates
the PhoneNet link-level (dial) protocol.  Several dial package parameters are
set by commands in the dialing script.  The default illegal character sets are
typically compiled into conf_dial.c but those defaults may be overridden by 
using the DBADIN, DBADOUT, or DBAD specifications in the
runtime tailoring file (see section 3.13 of the ``Installing and Operating
MMDF II'' document).  The defaults may be further superseded by specifying
replacement illegal character sets in the dialing script.
The dial package defaults to a packet window size of 1.  It is the master's
responsibility to override that default if desired (see the ``window'' command
documented in script(5)).
.PP
\fIPhone\fR is a two-way channel; mail queued on the local machine is delivered
to the remote machine and mail queued on the remote machine is picked up.
If \fIdeliver\fR is called on a \fIphone\fR channel and mail is queued
for that channel, a connection is attempted immediately.
If no messages are waiting, then deliver decides whether to invoke the channel
to do a pickup of mail from the remote machine.  Such a pickup is called a
"poll".  \fIDeliver\fR looks at the value of the poll= parameter in the 
\fIphone\fR 
channel's runtime tailoring file definition to decide whether to initiate a
poll.  If the value of poll is 0, polls are never performed.  If the value is
-1, polls are always performed.  Otherwise, the value of poll 
represents the number of 15-minute intervals to wait before doing a poll.  For
example, if poll=3 and no messages are queued to be sent out, then any
\fIdeliver\fR's attempted within 45 minutes of the last successful pickup will 
return immediately without attempting connection to the remote machine.
.PP
Once a connection is made with the remote \fIslave\fR, \fIphone\fR sends
one of three commands to the \fIslave\fR: submit, pickup, and end.
If there are messages waiting to be sent to the remote machine
(and if mod=send has been selected
on this channel), then the first command
\fIphone\fR sends is ``submit''.  While submitting messages to the 
remote \fIslave\fR, \fIphone\fR manages the interface between the dial 
package and \fIdeliver\fR (i.e. the mail queue).
.PP
When all waiting messages have been sent, \fIphone\fR sends the ``pickup''
command (if mod=pick has been selected on this channel).  While picking up
messages from the remote \fIslave\fR, \fIphone\fR
passes messages to the local \fIsubmit\fR program
which queues messages on the local machine.  The messages are submitted just as
if a submit were being done with the \fIslave\fR program.  The source channel
and host are set according to the values defined for this \fIphone\fR channel.
.PP
When the remote \fIslave\fR indicates that all messages have been picked up,
\fIphone\fR sends the ``end'' command and resumes processing the dialing script
on the line following the ``start'' command.
.SH "TROUBLESHOOTING"
If a connection to a remote \fIslave\fR cannot be made, check the PHLOG
(usually ph.log) for information about why the connection is failing.  If
possible, try to connect to the remote \fIslave\fR by hand.  If the manual
attempt fails, you know to go check the modem (or direct line) and the remote
machine for problems.
.PP
Often a connection will be made with the remote machine but
there will be a problem getting to the point where the \fIslave\fR begins the
link-level protocol.  A common error message to see in PHLOG is: 
``Timeout while processing script''.  This message indicates that a string
expected by a ``recv'' command was not matched within the specified
number of seconds.  Look in the transcript file (specified by the trn= channel
parameter which defaults to DEFTRAN, usually ph.trn) to see why the ``recv''
string was not matched.  
.PP
Often, noise characters can prevent transmitted
strings from being received correctly by the remote host.  One trick to reduce
the possibility of interference is to precede transmitted lines with the remote
machine's line-erase character (e.g. xmit ``@username\\r'').
.SH "SEE ALSO"
script(5), \fIslave\fR(8), \fIdeliver\fR(8), \fIsubmit\fR(8)

 do a pickup of mail from the remote machine.  Such a pickup is called a
"poll".  \fIDeliver\fR looks at the value of the poll= parameter in the 
\fIphone\fR 
channel's runtime tailoring file definition to decide whether to initiate a
poll.  If the value of poll is 0, polls are never performmdf/man/man8/recvprog.8   444      0     12        3616  3642035663  10324 .TH "RECVPROG" 8 "28 December 85"
.SH NAME
recvprog \- MMDF inbound generalized program channel
.SH SYNOPSIS
.B recvprog
[
.B -D
] [
.B \-c
.I channel
] [
.B \-{hv}
.I host
] [
.B \-s
.I sender
] [
.B \-M?
] [
.I addresses
]
.SH DESCRIPTION
The generalized program inbound channel
is part of the MMDF mail system.
\fIRecvprog\fR is called by the transport layer trying
to be interface to MMDF.  For example, the ACSNET system
might call \fIrecvprog\fR to give mail to the MMDF system.
It in turn calls the MMDF program submit.
Since the program sets the trustme flag when calling submit,
it must be called by a process whose UID is that
of trusted mail submitter (generally only root and mmdf).
.PP
The options available are:
.TP
.B \-D
places the program in debugging mode.
The message is written to stdout
instead of handed to the mail system (mmdf/submit)
and many debugging printfs are turned on.
.TP
.B \-c
can be used to specify the channel into which the mail is being
submitted.
The following argument must be the name of a valid MMDF channel.
.TP
.B \-h
followed by a host name specifies the host from which the message originated.
.B \-v
is like
.B \-h,
but assumes the host name is in reverse domain order
as in the UK JNTMAIL protocol.
.TP
.B \-s
specifies the sender of the message.  The sender string must follow
in the next argument.
.TP
.B \-M?
indicates the mode for the program to allow it to accept
different input formats.
.B \-Mj
indicates the input will be in in JNT mail file format with
the addresses at the head of the file.  If this is not specified,
then the addresses must be given as the last arguments on the command
line.
.B \-Ms
is reserved for specifying Batch-SMTP input format.
.SH "SEE ALSO"
deliver(8), submit(8), sendprog(8)
.SH BUGS
This is a new channel and has not been widely tested, but will continue
to be supported in future versions.
Documentation of this channel is sparse at this time.
 example, the ACSNET system
might call \fIrecvprog\fR to give mail to the MMDF system.
It in turn calls the MMDF pmmdf/man/man8/rmail.8   444      0     12        5520  3642035052   7565 .TH RMAIL 8 "7 January 1986"
.UC 4
.SH NAME
rmail \- submit remote mail received via UUCP
.SH SYNOPSIS
rmail user ...
.SH DESCRIPTION
.I Rmail
interprets incoming mail received via
.IR uucp (1C),
passing the processed mail on to
.IR submit (8)
for processing by the MMDF mail system.
.I Rmail
is explicitly designed for use with
.I UUCP
and the MMDF
.I submit
program.
It is not intended for use by regular users.
.PP
.I Rmail
performs several conversions on the incoming mail before calling 
.IR submit .
The conversions change addresses from the UUCP routing
style (lists of hosts separated by the character `!')
to the domain style of address used within the MMDF mail system.
The incoming message is dealt with in the following manner:
.IP 1)
The initial `From' (or `>From')
line is processed to discover the originating site
and the sender of the message.
Some UUCP mailers do not supply this information as part of the message
body.
If the originating site cannot be found from this information, the
program environment is inspected for the variable `ACCTSYS'; this is
set to the originating system by some implementations of UUCP.
The originating system is used as a table lookup value into the mmdf
table `rmail.chans', the file contains site/channel pairs.
If a match
is found the resulting channel is used for the submit phase.
The default UUCP channel is used if no match is found.
The default channel name is specified in conf.c source and
can be runtime tailored.  Typically it is `uucp'.
The existence of this channel is MANDATORY to prevent dropping
mail from unknown hosts.
.IP 2)
The body of the message is inspected looking for any header lines
containing addresses; the lines are `From:', `To:', `Cc:', `Bcc:'
and `Sender:'.
By scanning the address chains, the
addresses in these lines are converted into `user@known-site.domain' form
using the MMDF tables to evaluate whether the mailer knows the site.
For this to work properly, the unqualified name of all sites should exist
in the appropriate domain tables.
The scanning stops when an unknown site is discovered and a composite
address will be created.
The `From:' line is treated specially to preserve any comment information
which may have been inserted by the originating mailer.
.IP 3)
The `Date:' line is also re-written into ARPA standard form.
.PP
Before 
.I submit
is called,
the message is re-written into RFC822/733 form with all addresses
obeying the appropriate convention.
Any missing header lines are supplied.
The destination address for the message is taken from the argument to
.I rmail ,
and so the header re-writing which is done does not affect the routing
of the message.
.SH "SEE ALSO"
mail(1),
uucp(1C),
submit(8)
.SH BUGS
.I Rmail
should not reside in /bin.
.br
It is virtually impossible to do a completely valid conversion job.
.br
Rmail should be replaced by something that resembles Batch-SMTP.
il queue).
.PP
When all waiting messages have been sent, \fIphone\fR sends the ``pickup''
command (if mod=pick has been selected on this channel).  While picking up
messages frmmdf/man/man8/sendprog.8   444      0     12        3702  3642035053  10303 .TH "SENDPROG" 8 "28 December 85"
.SH NAME
sendprog \- MMDF outbound generalized program channel
.SH SYNOPSIS
sendprog (called by deliver)
.SH DESCRIPTION
The generalized program outbound channel
is part of the MMDF mail system.
\fISendprog\fR is called directly by \fIdeliver\fR.
It follows the standard deliver/channel protocol and handles outbound
traffic via some program specified in the channel table associated
with the channel.
.PP
There are two ways the channel can be configured.
In the first mode, the channel configuration string (`confstr=\.\.\.')
specifies the command to be used to send the mail with
certain strings expanded to per-message information (see below).
In the second mode, the conf string is null, and the address portion
of the channel table (RHS) gives the command to send the mail.
Again, certain strings can be expanded to per-message information.
.PP
The following strings can be expanded in command strings:
.br
.nf
.ta 1.7i
$(from)	expands to the from address
$(local)	expands to the local host name
$(to)	expands to the full addressee string
$(to.user)	expands to the user portion of addressee
$(to.host)	expands to the host portion of addressee
$(to.address)	expands to the address from channel table RHS
	(only when confstr is non-null)
.fi
.PP
The following is a sample program channel description for
the \fImmdftailor\fR file if one were to use the program channel
to interface to ACSNET system (untested to date):
.br
.ta 1.5i
MCHN	acsnet, show=``ACSNET Channel'', que=acsnet, tbl=acsnet,
.br
	pgm=sendprog, mod=reg, ap=822,
.br
	confstr="/usr/acsnet/sendfile -a mailer $(to.address)"
.SH "SEE ALSO"
deliver(8), submit(8), recvprog(8)
.SH BUGS
This is a new channel and has not been widely tested, but will continue
to be supported in future versions.  In particular, the entry in the
channel table may become the name of a ``configuration file'',
describing the program, how to give mail to the program, and how to
interpret the return codes.
ould exist
in the appropriate domain tables.
The scanning stopmmdf/man/man8/setup.8   444      0     12        1540  3642035053   7620 .TH SETUP 8 MMDF
.SH NAME
setup \- Setup the MMDF directory hierachy.
.SH SYNOPSIS
setup [ \-dnt ]
.SH DESCRIPTION
.PP
.I Setup
is the tool responsible for setting up the MMDF directory
hierachy. It runs through the various directories
and, if it discovers they are not present, will prompt the user
to create them. It will not change an already existing directory
but will complain if it thinks that it's set to the wrong mode.
.PP
The
\-dnt
flags are all equivalent, and turn on tracing mode. No actions
are taken but, similar to the \-n flag in
.I make,
the usual actions are reported to the user.
.PP
Usually, 
.I setup
is run as superuser to allow it to create the
relevant directories with the right ownership and modes.
.I Setup
is reasonably verbose and diagnostics should be self explanatory.
.SH "SEE ALSO"
checkup(8), "Installing and Operating MMDF II"
ge information.
.PP
The following strings can be expanded in command strings:
.br
.nf
.ta 1.7i
$(from)	expands to the from address
$(local)	expands to the localmmdf/man/man8/slave.8   444      0     12        4133  3642035053   7573 .TH SLAVE 8
.SH NAME
slave \- MMDF PhoneNet Slave Program
.SH SYNOPSIS
slave
.SH DESCRIPTION
.PP
The \fIslave\fR program is the passive component of the PhoneNet protocol
package.  The active component is the \fIphone\fR channel (see \fIphone\fR(8)).
Typically, \fIslave\fR is the login shell for an account which is reserved
for picking up mail on a particular channel.  When invoked, \fIslave\fR asks
for the channel name of a pobox channel defined in the runtime tailoring file.
The user= field in that channel's definition must match the username of the
account running \fIslave\fR.
.LP
If a valid channel specification has been given, 
\fIslave\fR starts up the PhoneNet link-level protocol (dial package).
The dial package uses the default illegal character sets configured in
conf_dial.c and (possibly) overridden by DBADIN, DBADOUT, or DBAD in the
runtime tailoring file.  See section 3.13 of the ``Installing and Operating
MMDF II'' document for more information regarding illegal character sets.
The dial package defaults to a packet window size of 1.  It is the master's
responsibility to override that default if desired (see the ``window'' command
documented in script(5)).
.LP
\fISlave\fR responds to three commands from the master: pickup, submit, and end.
In pickup mode, \fIslave\fR passes messages which have been queued up locally
on the specified pobox channel out to the remote machine.  
It does this by invoking
\fIdeliver\fR in pickup mode (-p) on the specified channel.  The \fIpobox\fR
program manages the interface between the \fIslave\fR (i.e. the dial package) 
and \fIdeliver\fR (i.e. the mail queue).  
.LP
In submit mode, \fIslave\fR passes messages to the local \fIsubmit\fR program
which queues messages on the local machine.  The messages are submitted just as
if a pickup were being done with the \fIphone\fR channel.  The source channel
and host are set according to the values defined for the specified pobox
channel.
.LP
The end command causes \fIslave\fR to drop the link-level connection with the
remote machine.  Slave then exits.
.SH "SEE ALSO"
\fIphone\fR(8), \fIsubmit\fR(8), \fIdeliver\fR(8)

ine is treated specially to preserve any comment information
which may have been inserted by the originating mailer.
.IP 3)
The `Date:' line is also re-written into ARPA standard form.
.PP
Before 
.I submit
is called,
the message is re-written into RFC822/733 form with all addresses
obeying the appropriate convention.
Any missing header lines are supplied.
The destination address for the message is taken from the argummdf/man/man8/smtp.8   444      0     12        1402  3642035664   7450 .TH "SMTP" 8 "28 December 85"
.SH NAME
smtp \- SMTP outbound channel for MMDF
.SH SYNOPSIS
smtp (called by deliver)
.SH DESCRIPTION
The SMTP outbound channel is part of the MMDF mail system.
\fISmtp\fR is called directly by \fIdeliver\fR.
It follows the standard deliver/channel protocol and handles outbound
SMTP traffic.
\fISmtp\fR uses the channel configuration
string to specify what it should place in the HELO string of the
SMTP transaction.  This will override the use of \fIlocname\fR
for this function.
The following is a typical SMTP channel description from
the \fImmdftailor\fR file:
.sp
.ta 1.5i
MCHN	smtp, show=``SMTP/TCP'', que=smtp, tbl=smtp,
.br
	pgm=smtp, mod=reg, ap=822, confstr=``SERING.UUCP''
.SH "SEE ALSO"
deliver(8), submit(8), smtpd(8), RFC821
dard deliver/channel protocol and handles outbound
SMTP traffic.
\fISmtp\fR uses the channel configuration
string to specify what it should place in the HELO string of the
SMTP transaction.  This will override the use of \fIlocname\fR
for this function.
mmdf/man/man8/smtpd.8   444      0     12        6632  3656410726   7627 .TH "SMTPD" 8 "21 May 85"
.SH NAME
smtpd, smtpsrvr \- SMTP inbound channel for MMDF
.SH SYNOPSIS
smtpd [\-d] [\-f] [\-n maxconnects] smtpsrvr channels  (4.2)
.br
smtpd smtpsrvr channels port maxconnects network  (4.1 or 5.2)
.br
smtpd [\-f] smtpsrvr channels  (4.3)
.br
smtpd sourcehost.sourceport  (sun)
.sp
.br
smtpsrvr remotehost localhost channels
.br
rsmtpsrvr remotehost localhost channels  (finicky)
.SH DESCRIPTION
The SMTP inbound channel is part of the MMDF mail system.
It consists of two programs,
an SMTP server (\fIsmptsrvr\fR) and a connection server (\fIsmtpd\fR).
\fISmptsrvr\fR is designed to be called by a networking daemon
that has determined the identity of the caller and set up the network
connection on the server's standard input and output.
\fISmtpsrvr\fR interfaces between the network and the mail system.
It calls \fIsubmit\fR to pass mail into the system.
.PP
The
.I smtpd
program comes in several flavors depending on your operating system.
In some cases, the burden of limiting and waiting for inbound connections
is borne by a standard "daemon of daemons".  In other cases, 
.I smtpd 
performs these operations.  In any case, 
.I smtpd
is the link between the network connection and the
.I smtpsrvr 
program.
.PP
In the synopsis above, the first two versions of 
.I smtpd
are standalone network daemons that accept incoming
connections and fork off copies of the smptsrvr to process
them.  The last two versions are called by the standard 
``daemon of daemons'' with one connection and fork the smtpsrvr to
process it.
The sun version calls the standard 
.I smtpsrvr
program with channels set to ``smtp''.
.PP
The \-d option enables debugging prints on the stderr output.
The \-f option (the \fBf\fRiniky option) causes 
incoming connections from unknown
hosts to be rejected.
.I Smtpd
notifies the \fIsmtpsrvr\fR program to use the finicky option
by calling it with argv[0] set to `rsmtpsrvr'.
The maxconnects argument sets the maximum number
of simultaneous connections to support.  This value defaults to 4 on 4.2
systems.
.PP
The smtpsrvr argument is the path to the \fIsmtpsrvr\fR
program (relative or absolute).
Generally this is something like /usr/mmdf/chans/smtpsrvr.
.PP
The channels argument is a comma-separated list of channels.  When a connection
is established, 
the source host is looked up in the host tables for these channels.
If the source host is found, the 
corresponding channel name is 
given to \fIsubmit\fR as the channel it should 
consider the mail to have come from for authorization purposes.
In the finicky mode,
if the source host is not found, \fIsmtpsrvr\fR will
reject the incoming mail.  In the normal (non-finicky)
mode, if the source host is not found, \fIsmtpsrvr\fR will assume the
mail came in on the last channel in the channels list.
.PP
If your smtp channel table is a nameserver-type channel table and you
are running in the finicky mode, then
you should include the "local" channel in the list of acceptable
channels.  If you do not include "local" in this case, you will not
be allowed to connect to your own server and submit mail because the
nameserver channel will not let you look up your own address.  (RFC 974
requires this to prevent mail loops).
.PP
Since the \fIsmtpsrvr\fR program sets the trustme flag when calling
\fIsubmit\fR,
it must be called by a process whose UID is that
of a trusted mail submitter (generally only root and mmdf).
.SH SEE ALSO
deliver(8), submit(8), smtp(8), RFC821
 DESCRIPTION
The SMTP inbound channel is part of the MMDF mail system.
It consists of two programs,
anmmdf/man/man8/submit.8   444      0     12       36717  3656410730  10025 .ta .8i 1.6i 2.4i 3.2i 4.0i 4.8i 5.6i 6.3i
.TH SUBMIT 8 "20 October 84"
.SH NAME
submit \- MMDF mail enqueuer
.SH SYNOPSIS
submit [\-L...*V...*Wbcdf...*g...*hi...*jk...*lmnqrstuvwxyz]
.SH DESCRIPTION
.PP
All mail is entered into the MMDF mail transport environment
through the
.I submit
program.
This document is intended to provide the specific information
needed to control
.I submit.
While it can be called directly from a user's terminal,
access to
.I submit
is most
conveniently done through a program such as mail(1) or send(1).
The MMDF library also has subroutine package
for invoking
.I submit.
See the
.I mm_
package, documented in
mmdf(3) for more information.
It has a set of procedures which make this process much easier.
Before proceeding, familiarity with the
channels(7)
document is recommended, since
.I mm
is a member of the family of channel modules.  It also will be useful
to read
replies(5),
which describes reply values.
.SS BASIC MODES
.PP
.I Submit
permits considerable flexibility with respect to
batching multiple submissions, response and error handling, and
address source specification.
.SS Multiple Submissions
.IP "1."
Terminate after one submission, such as is done by the mail command, or
.IP "2."
Permit multiple message submissions, as is done by the
SMTP channel and the MMDF telephone
.I slave.
.PP
The first mode is specified by passing any initialization
information in the submit invocation line (i.e., the
.IR exec (2)
call).  In the second mode, the initialization information is
given as the first input line, for each submission.  The format
of this information is the same for both modes.
.SS Response & Error Handling
.IP "1."
Accept input until error or end of message, but
terminate on any error, or
.IP "2."
Notify result for each
.B segment
and continue.
.PP
Response mode #1 is mandatory with Multiple mode #1.
Response mode #2 is called \fIprotocol mode\fR.  During it, each
address produces a status reply and the message text produces a
reply.  The domain of the term
.B segment
depends on the error.
Simple addressing errors cause rejection only of the erroneous
address.  Other errors may cause rejection of the entire message,
but permit submission of following messages.
.ne 5
.SS Addresses
.IP "1."
Extracted from components of the message text,
.IP "2."
Explicit list given, ahead of message text, or
.IP "3."
Both of the above (extracted and explicit addresses)
.PP
The first mode is common when mode #1 (non-protocol)
is also in force for
the Interaction and the Verification option.
The second mode
is commonly in force when the second modes apply for the other
options (protocol mode).
The third mode is of unclear benefit, but was easy to provide
and originally looked like a good idea.
.ne 5
.SS INITIALIZATION
.PP
A message's initialization information is specified through a
single string, passed either in the process-invocation argument
list or in the first line of
.I submit's
input.  Hence, the string
may be terminated either by a null or newline.  Spaces and tabs
in the line are ignored, unless part of a literal.  Specification
is only required for non-defaults.
.sp
.TS
center;
l l l l.
	Option	Value	Literal

1.	Relay source	a. none	(\fIdefault\fR)
	for the ``Via'' or	b. source channel	i...*
	``Received'' field	c. source host	h...*

2.	From/Sender	a. reject on error	(\fIdefault\fR)
	authentication	b. trust	t
		c. no trust (disclaim)	u

3.	Source-Info Field	a. not included	(\fIdefault\fR)
		b. disclaim author	u
		c. user text	f...*

4.	Address list source	a. explicit list	(\fIdefault\fR)
		b. extract from components	x...*
		c. both (extract and explicit)	g...*

5.	Address verification	a. abort on invalid	(\fIdefault\fR)
		b. report on each address	v

6.	Delivery destination	a. mailbox	m (\fIdefault\fR)
		b. user's tty	y
		c. mailbox and tty	b

7.	Delivery attempt	a. leave for daemon	(\fIdefault\fR)
	(combinable)	b. deliver local now	l
		c. deliver netmail now	n

8.	Observation of	a. none	(\fIdefault\fR)
	immediate attempts	b. user will watch	w

9.	Return address	a. send to submittor	r
		b. send to ``Sender:''	s
		c. do not return	q 
		d. as specified	(\fInext line\fR)

10.	Returned mail	a. entire original	(\fIdefault\fR)
	contents	b. citation only	c

11.	Warnings	a. send warnings	(\fIdefault\fR)
		b. do not send warnings	z

12.	Delay channel	a. enable delay channel	(\fIdefault\fR)
	usage	b. don't use delay	d

13.	Delay channel	a. not delay channel	(\fIdefault\fR)
	indicator	b. delay channel	j

14.	Nameserver	a. short timeouts	 (\fIdefault\fR)
	timeouts	b. as specified	k...*

15.	Submission	a. not shown	(\fIdefault\fR)
	tracing	b. watch submission	W

16.	Logging file	a. as per msglog	(\fIdefault\fR)
		b. as specified	L...*

17.	Logging level	a. as per msglog	(\fIdefault\fR)
		b. as specified	V...*
.TE
.RE
.ne 5
.SS Comments
.IP General
Literals shown as characters, followed by an ellipsis,
followed by an asterisk (e.g. x...*), represent a string.  The first
character specifies the nature of the setting.  The value
for the setting is placed between that character and the
asterisk.  Settings \fBi\fR, \fBh\fR, and \fBf\fR may contain any string
not containing an asterisk, null, or newline.  Settings
\fBx\fR and and \fBg\fR are lists with members comprising strings
as for \fBi\fR, \fBh\fR and \fBf\fR, but further prohibited from containing
commas.  Commas separate members of the list.
.ne 5
.IP Specific
.IP "1. Relaying"
This is used when the calling program is interfacing with
another distribution system, effecting relaying.  The literal
after the \fBi\fR specifies the channel the
message is coming from.
The \fB\h\fR may be used, in conjunction with \fB\i\fR, to
specify the source host.  The literal is the name of the host.
.ne 5
.IP "2. Authentication"
Normally, the message must correctly identify its sender.
Anyone may send "anonymous" (unsigned) mail, but they
must use the \fBu\fR setting which bypasses authentication.
However, it also causes MMDF to include, in the Source-Info:
component, a statement noting the absence of
authentication.  Only root or relays may use the \fBt\fR
setting, which bypasses authentication and does not add a
disclaimer.  Others requesting it get \fBu\fR treatment.
.ne 5
.IP "3. Source-Info"
In addition to the action explained above, Source-Info:
can directly receive text, from the user, through the \fBf\fR
setting.  The value string is replicated on a separate
line in the field.
.ne 5
.IP "4. Address lists"
An explicit list has one address per line.  When \fBx\fR or
\fBg\fR are specified, they list the names of message
components, such as ``To:'' and ``CC:'', which are to be
searched for addresses.
.ne 5
.IP "5. Verification"
Normally, any illegal address will cause the entire
message to be rejected.  In \fBv\fR (verify) mode, the
acceptability of each message is reported and
encountering an illegal address does not abort
submission.
.ne 5
.IP "6. Delivery type"
Mail may be delivered to a recipient's mailbox (file),
online terminal (if the recipient is logged in), or a
combination of the two.  There is no default.  For each
message, its delivery mode must be specified.
(Delivery to online terminals is likely to be removed
in the near future.)
.ne 5
.IP "7. Attempt"
An immediate attempt causes a special
.I deliver
process to be forked and it will attempt to process the indicated
mail immediately.  (The \fBn\fR setting does not allow more
granularity, for historical reasons.) Otherwise, the
system's background daemon will get to it eventually.
The daemon also handles mail that initially could not be
delivered/relayed.  A channel's descriptor structure (in
.I chan.c
or the runtime tailor file)
specifies a channel as being Active, Passive,
or Background.  Only the first is processed by any request
for immediate delivery.  The second indicates a Post
Office Box-style channel.  The third limits the channel
to processing by the background
.I deliver
daemon, which may be necessary for restricting access to special channels,
such as dial-out telephones.
.ne 5
.IP "8. Observation"
If an immediate attempt is requested, the user may elect
to watch its progress.
.I Deliver
and its children will report assorted aspects of their activity.  If a quiet
attempt is requested, 
.I submit
returns as soon as
submission is completed.  That is, a quiet attempt is
performed detached.
.ne 5
.IP "9. Return address"
If the invoker of
.I submit
is not to receive return mail
(e.g., notification of delivery failure) then the next
input line (the first, if settings are specified in the
.IR exec (2)
call), contains an address that should receive the
notification.  It is not validated.  If either the \fBr\fR 
or the \fBs\fR switch is given, 
.I submit
will not read a line for the return address.  If no return mail should be
sent, the return address line should be empty (i.e., consist
of a newline, only.)  If the \fBq\fR switch is given, a return address is read
from the next line of input but the local system will not return mail if
delivery problems are encountered.  The return address given may be used
by other systems (if there are mail relays between the local system and the
recipient).
.ne 5
.IP "10. Return contents"
Normally, a copy of the entire message is sent with a
delivery-failure notice.  Using the \fBc\fR switch causes a
citation, comprising the message header and first three
lines of non-blank lines of the body, to be sent.
If more than 12 addresses are specified, for a message,
citation-only is automatically set.  In addition, no warning
message will be sent for addresses which take a long time to process
(a site dependent value); the final failure notice will always
be sent, if there are addresses that are never fully processed.
.ne 5
.IP "11. Warnings"
Normally MMDF will send a non-delivery warning if a message has
been undelivered after a small period (typically 12 to 72 hours,
depending on the site).  Deliver attempts continue until a
timeout period is reached.  This is typically after 3 to 10 days,
depending on the site.
.ne 5
.IP "12. Disable delay channel"
The delay channel is used to process mail submissions that could not
queued because necessary nameserver information was unavailable
and therefore an authoritative decision on the validity of the address
was not possible.
If the \fBd\fR option is specified, use of the delay channel is
prohibited.  If the nameserver fails, a error is returned, rather
than a conditional OK.
.ne 4
.IP "13. Delay channel indicator"
This option is intended only to be used by the delay channel itself
to indicate to submit that the invoking process IS the delay channel.
This option implies the \fBd\fR option above.
.ne 5
.IP "14. Nameserver timeouts"
By default, MMDF uses a short timeout algorithm.  This is suitable for
user interface programs which don't want to wait a long time for dead
nameservers.  The \fBk\fR option allows a different timeout to be set.
The value given is the number of seconds to wait for the nameserver
lookup to complete.  
.ne 5
.IP "15. Submission tracing"
The \fBW\fR option causes submit to print a detailed
description of its activities
on file descriptor 2.  It will indicate for each
addressee, the channel and addresses queued.
This can generate a great deal of output if a mailing list is
encountered, so it should be used with caution.
.ne 5
.IP "16. Logging file"
The \fBL\fR option allows the specification of an alternate logging file
at runtime.
The string following the \fBL\fR should be the name of the logfile
to be used.
It can be terminated by a * or the end of the arguments.
This option is only available to the Superuser or MMDF.
.ne 5
.IP "17. Logging level"
The \fBV\fR option allows the setting of the logging level at runtime.
The string following the \fBV\fR should be one of the valid MMDF
logging level strings such as FTR or BST.
It can be terminated by a * or the end of the arguments.
This option is only available to the Superuser or MMDF.
.RE
.ne 16
.SS INPUT STREAM
.PP
The following augmented BNF characterizes submit's input
(file descriptor zero) format:
.RS
.IP  stream: 14
*(init-seq '\\n' msg-info null) [null]
.IP  init-seq: 14
*{ switches listed above }
.IP  msg-info: 14
[ret-addr] '\\n'
.br
[addr-seq '!' '\\n']
.br
{ rfc822-format message }
.IP  ret-addr: 14
{ rfc822-format (return) address }
.IP  addr-seq: 14
*{ rfc822-address }
.RE
.SS ADDRESS FORMAT
.PP
Addresses are expected to conform to the ARPANET mail standard
known as RFC-822, available from the Network Information Center
at SRI International.
Submit (and MMDF in general) also continues to support RFC-733 style
mail for compatibility with earlier mail systems.
.PP
Also, addresses may be indirectly referenced, through a file
specification of the form:
.PP
  ``<filename'' or ``:include:filename''
.PP
where the angle-bracket must be the first non-blank character of
the specification (to distinguish it from the ``<...>'' usage, above).

Addresses in the file may be separated by commas or newlines.
.bp
.SS EXAMPLE INTERACTIONS
.PP
Phases involve Invocation (Invoke), data sent into
.I submit
via
its file descriptor zero (To), data returned from
.I submit
via its
file descriptor one (From), iteration back to the specified phase
(Loop), and process exit value (Exit).
.IP "1."
Simple, single-message, as with the \fIv6mail\fR command:
.RS
.IP "a. Invoke:" 15
Parameters, ``-mlrxto,cc*'', indicate that the
message is to be sent to recipients' mailboxes,
local mail should be sent immediately, return
mail goes to the submittor, and addresses are to
be extracted from the ``To:'' and ``cc:``
components.
.IP "b. To:" 15
The entire message
.IP "c. From:" 15
Error messages
.IP "d. Exit:" 15
Process return value, in wait(&val), taken from
.I mmdf.h,
indicating submission status.
.RE
.IP 2.
Standard, multi-message protocol:
.RS
.IP "a. Invoke:" 15
No parameters
.IP "b. To:" 15
Initialization information line.  A typical
user program might have "mlrv", indicating the
message is to be sent to mailboxes, local mail
sent immediately, return mail goes to the
sender, and each address verification is to be
reported.  A relay program might have
"mlntviVGR.BRL.MIL*", with "mlv" as above and the
other settings indicating that mail for non-local
channels is to be sent immediately, the
author information is to be trusted, and the
"Received:  " component should cite the mail as being
relayed via Internet host VGR.BRL.MIL.
.IP "c. To:" 15
One address, terminated by a newline ('\\n').
.IP "d. From:" 15
Status character, from
.I mmdf.h,
plus human-oriented
text plus newline.
.IP "e. Loop:" 15
Back to (c).  Terminate with address line having
only an exclamation mark (\fB!\fR), with newline.
.IP "f. To:" 15
Message text, in Internet RFC #822 format.
Multi-line, terminated by null ('\\0').
.IP "g. From:" 15
Status character, text, newline.
.IP "h. Loop:" 15
Back to (b).  Terminate with initialization line
having only a null, without newline.
.SH CHANNELS
.PP
When MMDF is used in conjunction with the DARPA domain nameserver
system, a ``delay'' channel should be configured to allow
queuing of addresses that fail verification temporarily due
to nameserver failures (unavailability).
Two other special channels that can be configured are
the ``badusers'' and ``badhosts'' channels.
Mail to unknown users or unknown hosts will be queued to
these channels if they are configured.
The bad channels have no special code associated with them.
The channel configuration should reference whatever table and
program is necessary to reach a smarter host which can deliver
or forward the mail.  The channel should have the ``host=''
parameter set to this host name.
The channel names given above are reserved.
.SH FILES
Numerous.  Generally under the MMDF login directory.
.SH "SEE ALSO"
send(1), mmdf(3), deliver(8)
gging level at runtime.
The string following the mmdf/man/man8/york-inst.8   444      0     12       15425  3642035054  10447 .\" Let MMDF stand up and be counted
.ds M \fIMMDF\fP
.ds M2 \*M(II)
.de Ds
.nf
.sp
.in +6
..
.de De
.fi
.sp
.in -6
..
.de XX
.br
.ft R
.IP "\(rh\ \ \ [\ ]\ \ " 11n
..
.TH YORK-INST 8 MMDF
.SH NAME
MMDF - York NIFTP interface guide (release 2.1)
.SH SYNOPSIS
How to go about connecting the York NIFTP code into \*(M2
.SH DESCRIPTION
.PP
This document attempts to step through the process of
adding the York University
NIFTP interface to the current (II) version of the Delaware,
Multichannel Memo Distribution Facility.
This version of the document describes how to connected up
release 2.1 YORKBOX code to \*(M2. There is an accompanying document that
describes the changes needed for release 2.0, which is rather
primitive.
.PP
As may be guessed, this comes in two parts, the transmission
and reception of mail.
You should have already the relevant code in the directory
.Ds
\&.../src/niftp
.De
which will handle the format conversion and the actual \*M
interface.
This should be compiled and will produce two programs.
.Ds
niftp \- the outbound channel
ni_niftp \- the inbound channel
.De
The
.I niftp
program should be installed with the other channel programs.
The
.I ni_niftp
program can be put in various places, as long as hhQ can
find it. We keep ours in the x25 directory /usr/lib/x25 but
it can be put anywhere so long as the relevant pathname is
changed to fit.
.PP
.B IMPORTANT
.br
Take a copy of the following programs in case you have to
put them back in a hurry :-
.Ds
hhP
hhQ
mhhcp
hhmail
.De
.XX
Compile and install
.I niftp
and
.IR ni_niftp .
.SH "OUTBOUND MAIL"
First of all, a channel has to be set up in the tailor file,
it is presumed that you can do this, if
not then there should be some relevant documentation
somewhere. The important part about the tailor entry, is the
.I confstr
entry.
This is what \*M uses to exec the York transport system.
This should be something like
.Ds
.ne 5
MTBL niftp file="niftp.names"
MCHN niftp name="niftp", que=niftp, tbl=niftp,
.in +8
show="via Janet with NIFTP",
pgm=niftp, poll=0, mod=reg, ap=jnt,
confstr="/usr/lib/x25/mhhcp mhhcp $(FILE) $(ADR) $(SENDER)"
.in -8
.De
The
.I Make
like variables will be substituted for by the
outbound NIFTP channel so that the first one contains the
filename of the file containing the message, and the second
one the symbolic address of the host that the message
is being aimed at.
One thing to be sure of, the niftp outbound channel must be
able to exec the mhhcp program, mhhcp is often installed
with restrictive modes. So its important to check what
effective and real user id's niftp runs with to ensure
that things work.
Another point to note is the access permissions on 
/usr/spool/hhcp, the mhhcp program attempts to create a file in this
directory and will obviously fail if the permissions are wrong.
.PP
The first change is to the program mhhcp, this is the interface
between
.I niftp
and the
.I hhP
programs. This has been almost completely rewritten to do things
in a cleaner way and to take a different set of arguments.
It is now called in the following way.
.Ds
mhhcp [-s] filename TSaddress sender
.De
The optional \-s flag causes queueing of the file in the hhcp directory
without starting up the hhP program. This may be a saving if
your outgoing mail usage has sharp peaks or is very high.
The next two arguments are as usual, the sender is the return
address in case of failure. This is stored in the PA file as part
of the structure.
.PP
The exit codes of mhhcp are also changed to reflect the following.
This means that modifications to hhmail are necessary as before
these changes an exit code of 0 meant failure.
.Ds
JNT_OK  	- everything ok
JNT_TEMP	- a temporary error, retry may work
JNT_PERM	- a fatal error, reject this message
.De
.PP
Other changes are made to hhP so that when a mail transfer is
rejected, either for staying in the queue too long or being
rejected by the target host, the rejected item is sent back
to the sender address as supplied by mhhcp rather than to the
controlling UID.
.XX
Change and compile mhhcp, hhP, hhmail and install.
.SH "INBOUND MAIL"
.PP
The changes made here are to link directly the \*M
system with the hhQ program.
Essentially, \*M requires a bit more knowledge about
the incoming message. In the York mail system, this is taken
care of by the program
.I mailer
however as there is only minimal processing to be done, the
role can be taken over by hhQ.c. This does make it
incompatible with the York inbound traffic however, unless
appropriate changes are made to mailer, so as far as incoming
traffic is concerned, its either got to be handled by \*M or
by the york mail interface. They can't coexist.
.PP
The changes to hhQ are fairly small, they consist of
forking an instance of
.I ni_niftp
to pass the message onto \*M, and then wait for the exit
status to see if the message is acceptable to \*M. If not,
hhQ will send back a STOPACK indicating that the message was
fatally rejected by the receiver.
.SS CAVEAT
This is not the best solution. A better solution is to pass
this information back at the ER stage. This would mean far more
changes necessary to the hhQ program and would probably result
in a complete rewrite. This would make fitting to later versions
of hhQ difficult. It does mean that temporary failures communicated
by \*M are not handled gracefully.
.br
End of CAVEAT
.PP
The ni_niftp will decide what channel its coming in from
by using the information in the network field (first
argument) and the mapping table found in niftp.nets.
This table should have the format
.Ds
Network-name-passed-by-hhQ:channel-name
e.g.
serc:niftp
janet:niftp
pss:nipss
\&...
.De
.XX
Compile amd install hhQ.
Generate the table niftp.nets and run dbmbuild.
After recompiling hhQ and installing, all that remains is ....
.SH DEBUGGING
After this, things should work. As a test, the hhmail program
that comes with the York release should be able to send
messages outbound, provided the necessary changes have been
made to it, so try bouncing a few messages sent by
hhmail off a friendly local host that you know can relay JNT
mail (ucl-cs will do).
.Ds
e.g.
hhmail mmdf%ourname@friendly-host
hello world
EOF
.De
This should test the hhQ and ni_niftp interface.
.PP
The outbound side is fairly easy, the main problems comes
with the exec of mhhcp. Other than that, the hhP will leave
around logging files to show whats going on.
Also try turning up the logging on channels to
.I FTR
which should reveal the exec command.
.SH FILES
various files in /usr/spool/hhcp
and in the NIFTP queue directory, although at the moment
they only reside there for a few seconds unless something
goes wrong.
.SH BUGS
see Caveat above.
.PP
Any problems then contact me (Julian Onions) at the
one of the following address
.nf
.sp 2
jpo@uk.ac.nott.cs
.sp
jpo%nott.cs@ucl-cs
.sp
or
.sp
Computer Science Group
University of Nottingham
Nottingham
NG7 2RD
(ENGLAND)
(0602) 506101 x 3595
fail verification temporarily due
to nameserver failures (unavailability).
Two other special channels that can be configured are
the ``badusers'' and ``badhosts'' channels.
Mail to unknown users or unknown hosts will be queued to
thesemmdf/uip/   755      0     12           0  3671305326   5470 mmdf/uip/msg/   755      0     12           0  3671117115   6253 mmdf/uip/msg/gen   555      0     12          57  3620510604   6771 make -f ../../Makefile.com -f Makefile.real $*
The optional \-s flag causes queueing of the file in the hhcp directory
without starting up the hhP program. This may be a saving if
your outgoing mail usage has sharp peaks or is very high.
The next two arguments are as usual, the sender is the return
address in case of failure. This is stored in the PA file as part
of the structure.
.PP
The exit codes of mhhcp are also changed to reflect the following.
This means that modifications to hhmail are necessary as mmdf/uip/msg/msg1.c   444      0     12       43706  3671073275   7413 /*
 *			M S G 1 . C
 *
 * Functions -
 *	main		main command loop
 *
 *
 *  This has wended its way from Illinois through many other sites.
 *  It approximates the Tenex MSG program, written by John Vittal.
 *
 *  Oct 80  Stu Cracraft	Moby edit for conversion from V6 to V7
 *
 *  Nov 80  Dave Crocker	Retrofit for V6 compatibility; add stdio
 *  -May 81			cleanup & improve error detect/correct
 *
 *  Jun 81  Dave Crocker	gitr() fixed '?' case
 *				overwrit() confirm file deletion and
 *				have temp file in same directory as original
 *				xeq() call to date needed 0 at end of args
 *			 	change 'index' to 'equal' for reply tests
 *
 *  Jul 81  Dave Crocker	stop mapping DEL->newline in ttychar()
 *				fix settype(t) to make txtmtch work
 *			 	add 'new' messages added after folder open
 *
 *  Jul 81  Doug Kingston	getnum() check for < 0 -> 0.
 *  Sep 81  Dave Crocker	prmsg() catch form-feeds
 *  Jan 82  Doug Kingston	Modified for use on BRL machines
 *				Threw out some deadwood.
 *  Mar 82  Doug Kingston	Fixed blank line bug in setup().
 *  Jun 82  Doug Kingston	Fixed the "type current" bug.
 *
 *	06/07/82  MJM		First pass on cleanup prior to changover
 *				to dual file version.
 *
 *	06/08/82  MJM	Split the enormous MSG program into pieces.
 *			Added new help messages.
 *
 *	06/10/82  MJM	Optimized file reading, header parsing, eliminated
 *			re-read after overwrite, added additional flags
 *			processing, 'k', '$', ',' commands, plus general
 *			cleanup and optimization.
 *
 *	07/12/81  MJM	Fixed up bugs in Quit signal processing, and in the
 *			',' command.
 *
 *	09/10/82  MJM	Converted output to use STDIO also.
 *
 *	09/13/82  MJM	Added Undigestify code.  Switched msg[] to msgp[].
 *
 *	11/22/82  HW	Added Keyword stripping and Xtra options.
 *
 *	12/04/82  HW	Added P flag, dot options.
 *
 *	03/16/83  MJM	VAXorcised the code.
 *
 *	03/29/83  MJM	Made eXtend a command, not a mode.  Added "d" debug.
 *
 *	08/11/83  DPK	Fixed interrupt handling, added "." in number ranges,
 *			fixed continuation line handling, added close of files
 *			prior to execs, added wait loop.
 *
 *      11/01/85       Craig Partridge: fix bugs in using spool directory
 */

extern char *verdate;

#include "util.h"
#include "mmdf.h"
#include <pwd.h>
#include <signal.h>
#include <sys/stat.h>
#include <sgtty.h>
#ifdef V4_2BSD
#include <sys/ioctl.h>
#endif V4_2BSD
#include "./msg.h"

extern	int Nmsgs;

char    lastc = '\n';
char	ascending = TRUE;

int	(*old3)();
int	(*old13)();
int	(*old18)();

extern	int	pagesize;
extern	int	linelength;

extern	int	paging;
extern	int	keystrip;
extern	int	bdots;
extern	int	mdots;
extern	int	bprint;
extern	int	quicknflag;
extern	int	prettylist;
extern	int	filoutflag;
extern	int	binsavflag;
extern	char	*resendprog;
extern	char    *savmsgfn;
extern	char	*dflshell;
extern	char	*dfleditor;
extern	char	*verdate;

int	outmem = FALSE;
int	readonly = FALSE;
char	*msgrcfn = ".msgrc";
char	*binarypre = "._";	/* Prefix for binary map filename */

FILE * filefp = NULL;		/* Input FILEp */
FILE * outfp = NULL;		/* Output FILEp */
FILE * binfp = NULL;		/* Binary FILEp */

/*
 * Place to put temporary message file, while doing
 * the overwrite in current directory
 */
char	*tempname = "msgtmp.XXXXXX";

/* The HELP command lists are defined in msg3.c */
extern char	*cmdlst[];
extern char	*xtcmdlst[];
extern char	*ctime();

/*
 *			M A I N
 */
main( argc, argv)
int     argc;
char   *argv[];
{
	int     restrt;
	int	j;
	char    tb[1024];
	register struct message **mp;
	extern struct passwd *getpwuid();
	extern int onhangup();
	struct passwd  *pwdptr;
	char *uterm;
	int realid;
	int effecid;
	char *tmpname;
	extern char *getlogin();
	extern char *getenv();
#ifdef TIOCGWINSZ
	struct winsize ws;
#endif

	setbuf( stdout, ttyobuf);
	signal( SIGHUP, onhangup );
	orig = signal( SIGINT, SIG_DFL);
	old3 = signal( SIGQUIT, SIG_IGN);
	old13 = signal( SIGPIPE, onnopipe);
#ifdef SIGTSTP
	if( (old18 = signal(SIGTSTP,SIG_IGN)) == SIG_DFL )
		signal(SIGTSTP,onstop);
#endif SIGTSTP

	printf( "MSG (%s)  Type ? for help.\r\n", verdate);
	fflush( stdout);

	mmdf_init("msg");		/* Should be mmdf_init() ??? */

	homedir = getenv("HOME");
	tmpname = getlogin();

	if ((tmpname == 0) || (homedir == 0))
	{
	    getwho( &realid, &effecid);

	    /* get passwd entry */
	    if ((pwdptr = getpwuid(realid)) == 0)
	    {
		if (tmpname == 0)
		    tmpname = "UnknownUsername";

		if (homedir == 0)
		    homedir = "";
	    }
	    else
	    {
		if (tmpname == 0)
		    tmpname = pwdptr->pw_name;

		if (homedir == 0)
		    homedir = pwdptr->pw_dir;
	    }
	}

	strcpy(username,tmpname);

	sprintf( maininbox, "%s/%s",
		(mldfldir==0 || isnull(mldfldir[0])) ? homedir : mldfldir,
		(mldflfil==0 || isnull(mldflfil[0])) ? username : mldflfil);

	/* Build default mailbox name */
	strcpy( filename, maininbox);

	sprintf( defmbox, "%s/%s", homedir, savmsgfn);
	strcpy( defoutfile, defmbox);

	strcpy( draftorig, "draft_original" );
	/* Build strings for 2-window mode */
	sprintf( draft_work, "%s/draft_work", homedir );
	sprintf( draft_original, "%s/%s", homedir, draftorig );

	/* Get environment variables */
	if( (ushell = getenv("SHELL")) == NULL )
		ushell = dflshell;
	if( (ueditor = getenv("EDITOR")) == NULL )
		ueditor = dfleditor;
#ifdef TIOCGWINSZ
	if (ioctl(fileno(stdout), TIOCGWINSZ, &ws) != -1) {
	    if (ws.ws_row > 0) pagesize = (int)ws.ws_row;
	    if (ws.ws_col > 0) linelength = (int)ws.ws_col;
	} else
#endif TIOCGWINSZ	
	if( (uterm = getenv("TERM")) != NULL && tgetent(tb,uterm) == 1) {
		if ((j = tgetnum("li")) > 0)
			pagesize = j;
		if ((j = tgetnum("co")) > 0)
			linelength = j;
	}

	/* Check for user option file */
	sprintf( msgrcname, "%s/%s", homedir, msgrcfn);
	if( (fpmsgrc = fopen(msgrcname,"r")) != NULL ) {
		xomsgrc( fpmsgrc );
		fclose( fpmsgrc );
	}
	tzset();
	restrt = 0;

	if( argc > 1)
		strcpy( filename, argv[1] );
	if( argc > 2)
		setmbox( argv[2] );
		
	tt_init();		/* get tty modes */
	setjmp( savej );
	signal (SIGINT, onint);

	/*
	 * Initial loading of messages
	 */
	if( !(restrt++))  {
		status.ms_nmsgs = 0;
		status.ms_curmsg = 0;
		status.ms_markno = 0;
		setup( SETREAD );
	}

	if( nottty || argc == 0 || argv[0][0] == 'r' )
		verbose = OFF;
	tt_raw();		/* SET tty modes */

	setjmp( savej);
	autoconfirm = FALSE;
	for(;;)  {
		if( outmem == FALSE )
			newmessage();	/* anything arrived recently? */
		printf( "<- ");
		fflush( stdout);
		unset();		/* turn off M_PROCESS_IT bit */
		ascending = TRUE;
		linecount = 0;

ignore:		nxtchar = ttychar();
		nxtchar = uptolow( nxtchar);

		/* *** KEEP cmdlst[] UP TO DATE WITH LIST OF COMMANDS *** */
		/* It can be found down by the HELP function */
		switch( nxtchar)  {

		case 000:	/* null, see also 'X' 'M' */
			if( verbose )
				printf( "mark set\r\n" );
			status.ms_markno = status.ms_curmsg;
			break;

		case 023:	/* ctl/s */
		case 021:	/* ctl/q */
			goto ignore;

		case 'a': 
			if( verbose)
				printf( "answer message: ");
			gitr();
			if( wmsgflag == ON )
				makedrft();
			ansiter();
			break;

		case 002:		/* ^B */
		case 'b': 
			if( verbose)
				printf( "backup -- previous was:\r\n");
			else
				suckup();
			if( status.ms_curmsg <= 1)
				error( "no prior message\r\n");
			msgno = --status.ms_curmsg;
			mptr = msgp[status.ms_curmsg - 1];
			if( mptr->flags & M_DELETED)
				printf( "# %d deleted\r\n", msgno);
			else
				prmsg();
			break;

		case 'c': 
			if( verbose)
				printf( "current ");
			printf( "message is %d of %d in %s.  Mark at message %d\r\n",
				status.ms_curmsg, status.ms_nmsgs, filename, status.ms_markno);
			break;

		case 'd': 
			if( verbose)
				printf( "delete ");
			if( readonly == TRUE ) {
				printf("in READONLY mode - ignored\r\n");
				break;
			}
			gitr();
			doiter( delmsg);
			if ((j = cntproc()) > 1)
				printf("%d messages deleted\r\n", j);
			break;

		case 'e': 
			if( readonly == TRUE ) {
				if( verbose )
					printf( " quit - READONLY mode" );
			}
			else
				if( verbose)  {
					printf( "exit and update %s", filename);
					if( ismainbox )
						printf( " into %s", defmbox);
					printf( "\r\n"  );
				}
			if( !confirm((char *)0,DOLF))
				break;
			if( readonly == TRUE ) {
				tt_norm();
				exit(0);
			}

			newmessage();		/* Check for new ones */

			/* If the message hasn't fully arrived, inform the
			 * user, and return to command level.  We depend
			 * on a subsequent newmessage() to pick up the
			 * new message.
			 */
			if(
				stat( filename, &statb2) >= 0 &&
				statb2.st_mtime != status.ms_time
			)  {
				printf( "'%s' updated since last read ...\r\n",
					filename);
				break;
			}
			fflush( stdout);

			/*
			 * Special processing for main mailbox
			 */
			if( ismainbox )  {
				autoconfirm = FALSE;
				strcpy( outfile, defmbox);

				/* tag all undeleted messages for action */
				unset();
				settype( 'u');

				/*
				 * un-tag all KEEP messages
				 * Also, don't flush out messages which
				 * have just arrived and haven't been
				 * seen by the user yet.
				 */
				for( mp = &msgp[0]; mp < &msgp[status.ms_nmsgs]; mp++ )  {
					if( (*mp)->flags & (M_KEEP) )
						(*mp)->flags &= ~M_PROCESS_IT;
					if( (*mp)->flags & (M_NEW) ) {

						(*mp)->flags &= ~M_PROCESS_IT;
					}
				}

				/* MOVE all un-deleted, un-kept messages
				 * into the Mbox file.
				 */

				if( (j = cntproc()) > 0) {
					printf("Moving %d undeleted messages to %s\r\n", j, outfile);
					cpyiter( movmsg, DOIT,( int( *)()) 0);
					/* Delete all msgs moved into Mbox */
					doiter( delmsg);
				}
			}

			/*
			 * Overwrite mailbox.  Note that if some of the
			 * messages were marked as "keep", they will be
			 * retained in the mailbox, all by themselves.
			 */
			overwrit();
			binbuild();

			tt_norm();
			exit( 0 );

		case 'f': 
			if( verbose)
				printf( "forward ");
			gitr();
			fwditer();
			break;

		case 'g': 
			if( verbose)
				printf( "go to message number: ");
			gitr();
			doiter( gomsg);
			break;

		case 'h': 
			if( verbose)
				printf( "headers ");
			gitr();
			doiter( prhdr);
			break;

		case 'j': 
			xeq( 'j' );
			break;

		case 'k':
			if( verbose )
				printf("keep ");
			if( readonly == TRUE ) {
				printf("in READONLY mode - ignored\r\n");
				break;
			}
			gitr();
			doiter( keepmsg );
			break;

		case 'l': 
			if( verbose)
				printf( "list ");
			gitr();
			getfn( "to file/pipe: ", outfile, "/dev/tty");
			if( cntproc() > 1)  {
				printf( "Separate messages");
				lstsep = confirm((char *)0,DOLF);
			}  else
				lstsep = FALSE;

			if ( prettylist) {
				lstmore = TRUE;
				cpyiter( hdrfile, DOIT, dolstmsg);
			}
			else {
				lstmore = FALSE;
				cpyiter( lstmsg, DOIT,( int( *)()) 0);
			}
			break;

		case 'm': 
			if( verbose)
				printf( "move ");
			gitr();
			getfn( "to file/pipe: ", outfile, defoutfile);
			cpyiter( movmsg, DOIT,( int( *)()) 0);
			break;

		case '/': 	/* Delete current, Next */
			if( verbose)
				printf( "delete current\r\n");
			if( readonly == TRUE ) {
				printf("in READONLY mode - ignored\r\n");
				break;
			}
			if( status.ms_nmsgs == 0)  {
				printf( "'%s' is empty\r\n", filename);
				error( "" );
			}
			unset();
			if( status.ms_curmsg == 0)  {
				if( status.ms_nmsgs != 0)
					status.ms_curmsg = 1;
				else
					if( status.ms_curmsg > status.ms_nmsgs)
						error( "no current message\r\n");
			}
			setrange( status.ms_curmsg, status.ms_curmsg);
			doiter( delmsg);
			/* Fall Through to case 'n' */

		case 016:	/* ^N */
		case 'n': 
			if( verbose)
				printf( "next message is:\r\n");
			else
				suckup();
			if( status.ms_curmsg >= status.ms_nmsgs)
				error( "no next message\r\n");
			mptr = msgp[status.ms_curmsg];
			msgno = ++status.ms_curmsg;
			if( mptr->flags & M_DELETED)
				printf( "message %d deleted\r\n", msgno);
			else
				prmsg();
			break;

		case 'o': 
			if( verbose)
				printf( "overwrite old file %s", filename);
			if( readonly == TRUE ) {
				printf(" in READONLY mode - ignored\r\n");
				break;
			}
			if( confirm((char *)0,DOLF))  {
				overwrit();
				outmem = FALSE;
			}
			break;

		case 'p': 
			if( verbose)
				printf( "put ");
			gitr();
			getfn( "to file/pipe: ", outfile, defoutfile);
			cpyiter( putmsg, DOIT,( int( *)()) 0);
			break;

		case 004:		/* ^D -- EOF */
		case 'q': 
			if( verbose)
				printf( "quit");
			if( confirm((char *)0,DOLF))  {
				tt_norm();
				binbuild();
				exit( 0);
			}
			break;

		case 'r': 
			/* Update the old binary box */
			binbuild();

			if( verbose)
				printf( "read ");
			else
				suckup();
			getfn( "new file: ", filename, maininbox);
			setup( SETREAD);
			break;

		case 's': 
			if( verbose)
				printf( "send ");
			if( confirm((char *)0,DOLF)) {
				if( wmsgflag == ON && status.ms_curmsg != 0 ) {
					setrange( status.ms_curmsg,status.ms_curmsg );
					makedrft();
				}
				xeq( 'S');
			}
			break;

		case 't': 
			if( verbose)
				printf( "type ");
			gitr();
			doiter( prmsg);
			break;

		case 'u': 
			if( verbose)
				printf( "undelete ");
			if( readonly == TRUE ) {
				printf("in READONLY mode - ignored\r\n");
				break;
			}
			gitr();
			doiter( undelmsg);
			if ((j = cntproc()) > 1)
				printf("%d messages undeleted\r\n", j);
			break;

		case 'x':
			if( verbose)
				printf( "Xtra command: ");
			fflush( stdout);

			while( isspace(nxtchar = ttychar()));
			nxtchar = uptolow( nxtchar);

			switch(nxtchar) {

			case 'b':
				if( verbose)
					printf( "binary file write");
	 			if( readonly == TRUE ) {
					printf(" in READONLY mode - ignored\r\n");
					break;
				}
				if( confirm((char *)0,DOLF))
					binbuild();
				break;

			case 'c':
				if( filoutflag == OFF ) {
					if( verbose )
						printf( "Ctrl char filter on\r\n" );
					filoutflag = ON;
				}
				else {
					if( verbose )
						printf( "Ctrl char filter off\r\n" );
					filoutflag = OFF;
				}
				break;

			case 'd':
				printf("debug\r\n");
				if (status.ms_nmsgs == 0)
					printf ("No message read in...");
				else {
					if( status.ms_curmsg < 1 )
						status.ms_curmsg = 1;
					mptr=msgp[status.ms_curmsg-1];
					printf("\r\nstart=%ld, len=%ld\r\n",mptr->start, mptr->len);
					printf("Date: %s\r",ctime(&mptr->date));
					printf("Datestr: %.*s\r\n", SIZEDATE, mptr->datestr);
					printf("From: %.*s\r\n", SIZEFROM, mptr->from);
					printf("To: %.*s\r\n", SIZETO, mptr->to);
					printf("Subj: %.*s\r\n", SIZESUBJ, mptr->subject);
				}
				break;

			case '\003':
			case '\004':
			case 'e': 
				if( verbose )
					printf( "exit\r\n" );
				else
					suckup();
				goto dont_suck;
	
			case 'l': 
				if( verbose)
					printf( "list body ");
				gitr();
				getfn( "to file/pipe: ", outfile, "/dev/tty");
				if( cntproc() > 1)  {
					printf( "Separate messages");
					lstsep = confirm((char *)0,DOLF);
				}  else
					lstsep = FALSE;
				lstmore = FALSE;
				cpyiter( lstbdy, DOIT, ( int( *)()) 0);
				break;

			case 000:	/* see also null in main switch */
			case 'm':
				if( verbose )
					printf( "mark set\r\n" );
				status.ms_markno = status.ms_curmsg;
				break;

			case 'n':
				if( prettylist == OFF ) {
					if( verbose )
						printf( "Numbered list on\r\n" );
					prettylist = ON;
				}
				else {
					if( verbose )
						printf( "Numbered list off\r\n" );
					prettylist = OFF;
				}
				break;

			case 'o':
				if (!verbose)
					suckup();
				getfn( "output mbox file: ", defmbox, (char *)0);
				setmbox(defmbox);
				break;

			case 'p':
				if( paging == OFF ) {
					if( verbose )
						printf( "Paging on\r\n" );
					paging = ON;
				}
				else {
					if( verbose )
						printf( "Paging off\r\n" );
					paging = OFF;
				}
				break;

			case 'r':
				if( verbose )
					printf("re-order key: ");
				sortbox();
				break;

			case 's':
				if( keystrip == OFF ) {
					if( verbose )
						printf( "Strip on\r\n" );
					keystrip = ON;
				}
				else {
					if( verbose )
						printf( "Strip off\r\n" );
					keystrip = OFF;
				}
				break;

			case 'v':
				if( verbose)  {       
					printf("Verbose off -- Short form typeout\r\n");
					tt_norm();
					verbose = 0;
					goto dont_suck;
				}  else  {       
					suckup();
					if( nottty) error("input not tty\r\n");
					printf("Verbose on -- Long form typeout\r\n");
					verbose++;
					tt_raw();
				}
				break;

			case 'x':
				xostat();
				break;

			case '?': 
				help(xtcmdlst);
				break;

			default: 
				printf( " ...not a command( type ? for help )\r\n");

			}
			break;

		case 'y':
			if( verbose )
				printf("Resend ");
			gitr();
			doiter(prhdr);
			printf("To: ");
			strcpy(outfile,resendprog);
			if( (outfile[strlen(resendprog)]=rdnxtfld()) == '\n' )
				error("Cancelled\r\n");
			else {
				gather( &outfile[strlen(resendprog)], 200 );
				if( verbose )
					printf("Sending ");
				doiter( resendmsg );
				if( verbose )
					printf(" Done\r\n");
			}
			break;

		case 'z':
			if( verbose)
				printf( "z = two window mode.  Answer message: ");
			gitr();
			edansiter();
			break;

		case '\n': 
			if( verbose )
				printf( "\r\n" );
			break;

		case '!': 
			if( verbose )
				printf("!");
			if( (key[0] = rdnxtfld()) == '\n' ) {
				if (verbose) printf("\r\n");
				xeq( 's' );
			}
			else {
				gather( key, 79 );
				xeq( '!' );
			}
			printf("\r\n");
			break;

		case '?': 
			help(cmdlst);
			break;

		case ':': 
			if (verbose) printf( ": ");
			xeq( 'd');
			break;

		case ';': 
			if (verbose) printf( "; ");
			do
			    nxtchar = echochar();
			while( nxtchar != '\n' && nxtchar != '\004' );
			break;

		case ',':
			if (verbose) printf(",\r\n");
			setrange(
				(status.ms_curmsg <= 5) ? 1 : status.ms_curmsg - 5,
				(status.ms_curmsg + 5 > status.ms_nmsgs) ? status.ms_nmsgs : status.ms_curmsg+5
			);
			doiter( prhdr );
			break;

		case '@':
			if (verbose)
				printf( "@ = Undigestify " );
			gitr();
			printf( "Undigestifying " );
			doiter( undigestify );
			printf( "\r\n" );
			break;

		case '$':
			/* Drop down to the last message */
			if (verbose)
				printf("$ = Last message:\r\n");
			status.ms_curmsg = status.ms_nmsgs;
			setrange( status.ms_nmsgs, status.ms_nmsgs);
			doiter( prhdr );
			break;

		case '\177': 	  /* DEL end */
			printf( "use control-D or 'q' to exit\r\n");
			break;

		default: 
			printf( " ...not a command( type ? for help)\r\n");
		}
		if( !(verbose || nxtchar == '\n'))
			suckup();
dont_suck:;
	}
}
 SIZEFROM, mptr->from);
					printf("To: %.*s\r\n", SIZETOmmdf/uip/msg/msg2.c   444      0     12       37764  3671073277   7425 /*
 *			M S G 2 . C
 *
 * This is the second part of the MSG program.
 *
 * Functions -
 *	rdnxtfld	read next field from input
 *	prhdr		print header on stdout
 *	hdrfile		print header on outfp
 *	hdrout		print header on specified FILE p
 *	gomsg		goto a specific message
 *	delmsg		delete specified message
 *	undelmsg	un-delete specified message
 *	keepmsg		mark specified message for keeping
 *	getfn		
 *	cpyiter
 *	cppipe
 *	cpopen
 *	dolstmsg	list the selected messages, maybe separated
 *	lstmsg		list one message, maybe separated
 *	lstbdy		list body of one message
 *	movmsg		"move" a message
 *	putmsg		"put" a message
 *	writmsg		write specified message onto given FD
 *	writbdy		write body of specified message onto given FD
 *	prmsg		print specified message onto terminal
 *	ansiter		top-level iteration for "answer"
 *	ansqry		enquire who to send answer to
 *	ansmsg		create header for answer
 *	fwditer		top-level iteration for "forward"
 *	fwdmsg		copy one forwarded message, with markers
 *	fwdpost		add trailer to forwarded message
 *	srchkey		Keyword search for stripping obnoxious header lines.
 *	filout		Filters control characters before writing on terminal.
 *	makedrft	Make a file containing one or more messages
 *
 *		R E V I S I O N   H I S T O R Y
 *
 *	06/08/82  MJM	Split the enormous MSG program into pieces.
 *
 *	09/10/82  MJM	Modified to use STDIO for output to files, too.
 *
 *	11/14/82  HW	Added keyword filter to ignore Via, Remailed, etc.
 */
#include "util.h"
#include "mmdf.h"
#include <pwd.h>
#include <signal.h>
#include <sys/stat.h>
#include <sgtty.h>
#include "./msg.h"

/*
 *			R D N X T F L D
 *
 * read next field from input
 */
rdnxtfld()
{
	register char   c;

	do  {
	    c = ttychar();
	}  while( (isspace( c) || c == ch_erase) && c != '\n' );

	return(c);
}

/*
 *			P R H D R
 */
prhdr()
{
	hdrout( stdout, TRUE);
}

/*
 *			H D R F I L E
 */
hdrfile()
{
	hdrout( outfp, FALSE);
}




hdrout( fp,crflag)
FILE *fp;
int crflag;
{
	char lbuf[LINESIZE];

	sprintf( lbuf, "%4d%c%c%c%c%c%c%5ld: %-9.9s %-15.15s %.30s%s\n",
		msgno,
		mptr->flags & M_NEW ? 'N' : ' ',
		mptr->flags & M_DELETED ? 'D' : ' ',
		mptr->flags & M_KEEP ? 'K' : ' ',
		mptr->flags & M_ANSWERED ? 'A' : ' ',
		mptr->flags & M_FORWARDED ? 'F' : ' ',
		mptr->flags & M_PUT ? 'P' : ' ',
		mptr->len,
		mptr->datestr,
		mptr->from,
		mptr->subject,
		crflag ? "\r" : ""
	);
	filout( lbuf, fp );
}

/*--------------------------------------------------------------------*/

gomsg()
{
	status.ms_curmsg = msgno;		  /* set the current message number     */
}

delmsg()
{
	mptr->flags |= M_DELETED;
	mptr->flags &= ~M_KEEP;
}

undelmsg()
{
	mptr->flags &= ~(M_DELETED|M_KEEP);
}

keepmsg()
{
	mptr->flags |= M_KEEP;
	mptr->flags &= ~M_DELETED;
}

/*
 *			G E T F N
 *
 * Get a file name for the user.
 */
getfn( s, f, def)
char    *s;		/* Prompt string to provoke user */
char	*f;		/* place to put resulting answer */
char	*def;		/* optional default */
{
	char    tmpbuf[LINESIZE];
	char	name[LINESIZE];
	register char *t, *n;
	register struct passwd *pw;

	tt_norm();
	fputs( s, stdout);
	fflush( stdout);

	strcpy( oldfile, f);

	if( gets( tmpbuf) == NULL || isnull( tmpbuf[0]))  {
		if( def == (char *)0)
			error( "no filename specified\r\n");
		else  {
			strcpy( f, def);
			printf( "%s...\r\n", f);
		}
	}  else  {
		/* Remove leading whitespace */
		for( t = tmpbuf; isspace(*t); t++ )
			;
		if( *t == '~' ) {
			/* Expand */
			for( n = name; *++t && *t != '/'; *n++ = *t )
				;
			*n = '\0';
			if( name[0] == '\0' )
				pw = getpwuid(getuid());
			else
				pw = getpwnam(name);
			if( pw == NULL )
				error("~name not found\r\n");
			(void) strcpy(f, pw->pw_dir);
			(void) strcat(f, t);
		} else
			strcpy(f, t);
	}

	nxtchar = '\n';		  /* note the last character typed      */
	tt_raw();
}

/*
 *			C P Y I T E R
 */
cpyiter( fn, iterfl, post)
int    ( *fn)();		/* per-message function               */
int	iterfl;			/* To iterate or not to iterate	      */
int    ( *post)();		/* post-process function              */
{
	if( outfile[0] == '|')		/* output filter, not file */
		cppipe();		/* get the output pipe */
	else
		cpopen();		/* get the output file */

	tt_norm();
	if( iterfl == DOIT )
		doiter( fn);
	else
		(*fn)();
	tt_norm();

	if(post != 0)
		(*post)();

	if(outfile[0] == '|') {	/* collect the child */
		pclose( outfp );	/* done sending to file/pipe */
		signal( SIGINT, onint );
	} else
		lk_fclose (outfp, outfile, NULL, NULL);

	outfd = -1;
	tt_raw();
}

/*
 *			C P P I P E
 */
cppipe()
{
	char	buf[LINESIZE];

	fflush(stdout);
	if ((outfp = (FILE *) popen(&outfile[1], "w")) == NULL)
		error( "problem starting pipe command\r\n");

	outfd = -1;
	return;
}

/*
 *			C P O P E N
 *
 * open a file to copy into
 */
cpopen()
{

	/* EXCLUSIVE open the file, as we are writing */
	if( (outfd = lk_open( outfile, 1, (char *)0, (char *)0, 5)) < 0)  {
		switch( errno)  {
		case ENOENT:
			if( !autoconfirm)  {
				printf( "Create '%s'", outfile);
				if( !confirm((char *)0,DOLF))
					error( "");
			}
			if( (outfd = creat( outfile, sentprotect)) < 0)  {
				printf( "can't create '%s'", outfile);
				error( "\r\n");
			}

			close( outfd);
			/* EXCLUSIVE open, since writing */
			if( (outfd = lk_open( outfile, 1, (char *)0, (char *)0, 5)) < 0)  {
				printf( "can't open '%s'", outfile);
				error( "\r\n");
			}
			break;

		case ETXTBSY:
			printf( "'%s' is busy; try later", outfile);
			error( "\r\n");

		default:
			perror(outfile);
			error( "\r");
		}
	}

	/*
	 * Set up for STDIO output using the exclusive
	 * write-only FD.  The fclose( outfd ) will flush
	 * the buffers.  The "a" append is necessary in
	 * the event that the file already exists.
	 */
	if( (outfp = fdopen( outfd, "a" )) == NULL ) {
		error( "can't fdopen outfile\n" );
		outfd = -1;
	}
}

/*
 *			D O L S T M S G
 *
 * list the selected messages, maybe separated
 */
dolstmsg()
{
	doiter ( lstmsg);
}

/*
 *			L S T M S G
 *
 * list one message, maybe separated
 */
lstmsg()
{
	if( lstsep && lstmore)
		fputs( "\f\n", outfp );
	lstmore = TRUE;

	if( prettylist) {
		fprintf(outfp, "(Message # %d: %ld bytes", msgno, mptr->len );
		if( mptr->flags & M_DELETED )	fprintf(outfp, ", Deleted");
		if( mptr->flags & M_PUT )	fprintf(outfp, ", Put");
		if( mptr->flags & M_NEW )	fprintf(outfp, ", New");
		if( mptr->flags & M_KEEP )	fprintf(outfp, ", KEEP");
		if( mptr->flags & M_ANSWERED )	fprintf(outfp, ", Answered");
		if( mptr->flags & M_FORWARDED )	fprintf(outfp, ", Forwarded");
		fprintf(outfp, ")\n");
	}
        
	writmsg();
	mptr->flags &= ~M_NEW;		/* Message seen */
}

/*
 *			L S T B D Y
 *
 * list one message body, maybe separated
 */
lstbdy()
{
	if( lstsep && lstmore)
		fputs( "\f\n", outfp );
	lstmore = TRUE;

	writbdy();
	mptr->flags &= ~M_NEW;		/* Message seen */
}

/*
 *			M O V M S G
 */
movmsg()
{
	putmsg();
	delmsg();
}

/*
 *			P U T M S G
 */
putmsg()
{
	register int len;

	len = strlen( delim1);
	fwrite( delim1, sizeof(char), len, outfp );
	writmsg();
	fwrite( delim2, sizeof(char), len, outfp );
	mptr->flags |= M_PUT;
}

/*
 *			W R I T M S G
 */
writmsg()
{
	long size;
	int count;
	char tmpbuf[512];

	fseek( filefp,( long)( mptr->start), 0);
	for( size = mptr->len; size > 0; size -= count)  {
		if( size <( sizeof tmpbuf))
			count = size;
		else
			count =( sizeof tmpbuf);

		if( fread( tmpbuf, sizeof( char), count, filefp) < count)
			error( "error reading\r\n");
		if( fwrite( tmpbuf, sizeof(char), count, outfp ) < count )
			error( "error writing to file\r\n");
	}
}

/*
 *			W R I T B D Y
 */
writbdy()
{
	long size;
	int srcstat;
	char line[LINESIZE];

	fseek( filefp,( long)( mptr->start), 0);
	size = mptr->len;

	srcstat = SP_HNOSP;
	while( size > 0)  {
		if( xfgets( line, sizeof( line), filefp) == NULL )
			break;
		size -= strlen( line );

		if( *line == '\n' ) {
			/* Don't output the separating blank line */
			srcstat = SP_BODY;
			continue;
		}

		if( srcstat == SP_BODY )
			fputs( line, outfp );

	}
}

/*
 *			P R M S G
 */
prmsg()
{
	char line[LINESIZE];
	register long size;
	int	srcstat;

	tt_norm();

	size = mptr->len;
	status.ms_curmsg = msgno;

	printf( "(Message # %d: %ld bytes", msgno, size );
	if( mptr->flags & M_DELETED )	printf(", Deleted");
	if( mptr->flags & M_PUT )	printf(", Put");
	if( mptr->flags & M_NEW )	printf(", New");
	if( mptr->flags & M_KEEP )	printf(", KEEP");
	if( mptr->flags & M_ANSWERED )	printf(", Answered");
	if( mptr->flags & M_FORWARDED )	printf(", Forwarded");
	printf(")\n");
	fflush( stdout );

	if(quicknflag == ON )
		mptr->flags &= ~M_NEW;		/* Message seen */

	fseek( filefp,( long)( mptr->start), 0);

	srcstat = SP_HNOSP;
	while( size > 0)  {
		if( xfgets( line, sizeof( line), filefp) == NULL )
			break;

		if( *line == '\n' )
			srcstat = SP_BODY;
		/* filter obnoxious lines */
		if( keystrip == ON && srcstat != SP_BODY ) {
			if( !(srcstat == SP_HSP &&
			    (*line == '\t' || *line == ' ')))
				if( srchkey( line, keywds) == 0 ) {
					filout( line, stdout );
					srcstat = SP_HNOSP;
				}
				else
					srcstat = SP_HSP;
		}
		else
			filout( line, stdout);

		size -= strlen( line );
	}
	tt_raw();
	mptr->flags &= ~M_NEW;		/* Message seen */
}

/*--------------------------------------------------------------------*/

/*
 *			A N S I T E R
 *
 * Top level for "answer" command.
 */
ansiter()
{
	sndto[0] =
	    sndcc[0] =
	    sndsubj[0] = '\0';
	ansnum = 0;

	doiter( prhdr);
	anstype = ansqry();

	doiter( ansmsg);

	if( ansnum == 0)
		printf( "no messages to answer\r\n");
	else
		if( isnull( sndto[0]))
			printf( "no From field in header\r\n");
		else {
			xeq( 'a');		/* execute an answer command */
			doiter( ansend );	/* Set the A flag */
		}
}

/*
 *			E D A N S I T E R
 *
 * Top level for "answer" command, to drop into EMACS 2-window mode.
 */
edansiter()
{
	register int fd;

	sndto[0] =
	    sndcc[0] =
	    sndsubj[0] = '\0';
	ansnum = 0;

	doiter( prhdr);
	anstype = ansqry();

	doiter( ansmsg);

	if( ansnum == 0)  {
		printf( "no messages to answer\r\n");
		return;
	}
	if( isnull( sndto[0]) )  {
		printf( "no From field in header\r\n");
		return;
	}

	/* Prepare the work files */
	if( (fd = creat(draft_work, sentprotect)) < 0 )  {
		perror( draft_work );
		return;
	}
	close(fd);

	makedrft();

	/* Invoke EMACS in 2-window mode */
	xeq( 'e' );

	/* Feed results into SEND */
	xeq( 'A' );

	/* Clean up */
	unlink( draft_original );
	/* draft_work left behind in case he wants another look at it */
}

/*
 *			A N S Q R Y
 * who to send answer to
 */
ansqry()
{

again:
	printf( "copies to which original addresses: ");
	nxtchar = echochar();
	nxtchar = uptolow( nxtchar);

	switch( nxtchar)  {

	case '\n':
	case '\r':
		if( verbose)
			printf( "from\r\n");
		return( ANSFROM);

	case 'a':
		if( verbose)
			printf( "ll\r\n");
		return( ANSALL);

	case 'c':
		if( verbose)
			printf( "c'd\r\n");
		return( ANSCC);

	case 'f':
		if( verbose)
			printf( "rom\r\n");
		return( ANSFROM);

	case 't':
		if( verbose)
			printf( "o\r\n");
		return( ANSTO);

	case '?':
		if( verbose)
			printf( "\r\n");
		printf( "all\r\n");
		printf( "cc'd\r\n");
		printf( "from [default]\r\n");
		printf( "to\r\n");
		goto again;

	case '\004':
	default:
		error( " ?\r\n");
	}
	/* NOTREACHED */
}


/*
 *			A N S M S G
 *
 * get create To, & Subject fields
 */
ansmsg()
{
	char tmpfrom[M_BSIZE],
	tmprply[M_BSIZE],
	tmpsender[M_BSIZE],
	tmpto[M_BSIZE],
	tmpcc[M_BSIZE],
	tmpsubj[M_BSIZE];
	register unsigned int ind;
	int llenleft;

	ansnum++;
	status.ms_curmsg = msgno;

	tmpfrom[0] =
	    tmprply[0] =
	    tmpsender[0] =
	    tmpto[0] =
	    tmpcc[0] =
	    tmpsubj[0] = '\0';

	gethead( NODATE, tmpfrom, tmpsender, tmprply,
	(anstype & ANSTO) ? tmpto : NOTO,
	(anstype & ANSCC) ? tmpcc : NOCC, tmpsubj);

	if( !isnull( tmprply[0]))
		/* send to Reply-To */
		sprintf( &sndto[strlen( sndto)], ",%s%c", tmprply, '\0');
	else if( !isnull( tmpfrom[0]))
		/* send to From, if no Reply-To */
		sprintf( &sndto[strlen( sndto)], ",%s%c", tmpfrom, '\0');

	if( !isnull( tmpto[0]))
		sprintf( &sndcc[strlen( sndcc)], ",%s%c", tmpto, '\0');

	if( !isnull( tmpcc[0]))
		sprintf( &sndcc[strlen( sndcc)], ",%s%c", tmpcc, '\0');

	if( !isnull( tmpsubj[0]))  {
		/* save the destination */
		if( equal( "re:", tmpsubj, 3))
			for( ind = 3; isspace( tmpsubj[ind]); ind++);
		else
			if( equal( "reply to:", tmpsubj, 9))
				for( ind = 9; isspace( tmpsubj[ind]); ind++);
			else
				ind = 0;

		if( ansnum  == 1)
			/* not the first message */
			sprintf( sndsubj, "Re:  %s%c", &tmpsubj[ind], '\0');
		else {
			/* append more addresses */
			if( (llenleft = sizeof(sndsubj) - strlen(sndsubj))
			    -strlen(&tmpsubj[ind])-9 > 0 )
				sprintf( &sndsubj[strlen( sndsubj)],
					"\n     %s%c", &tmpsubj[ind], '\0');
			else if( llenleft > 7 )
				sprintf( &sndsubj[strlen( sndsubj)],
				"\n...%c", '\0');
		}
	}
}
/*--------------------------------------------------------------------*/
ansend() {		/* Set the A flag */

	mptr->flags |= M_ANSWERED;
}
/*--------------------------------------------------------------------*/
/*
 *			F W D I T E R
 *
 * Top level for forward command.
 */
fwditer()
{
	char *mktemp();

	status.ms_curmsg = msgno;
	fwdnum = 0;
	sndto[0] =
	    sndcc[0] =
	    sndsubj[0] = '\0';

	doiter( prhdr);

	strcpy( outfile, "/tmp/send.XXXXXX");
	mktemp( outfile);

	autoconfirm = TRUE;
	cpyiter( fwdmsg, DOIT, fwdpost);
	autoconfirm = FALSE;

	xeq( 'f');
	unlink( outfile);
}

/*
 *			F W D M S G
 *
 * copy one forwarded message
 */
fwdmsg()
{
	char line[M_BSIZE];
	int	llenleft;
	
	fwdnum++;

	line[0] = '\0';		/* build a subject line */
	gethead( NODATE, NOFROM, NOSNDR, NORPLY, NOTO, NOCC, line);

	if( !isnull( line[0]))  {
		/* If we had a subject line, bracket the subject info */
		if( isnull( sndsubj[0]) )
			/* the first subject line */
			sprintf( sndsubj, "[%s:  %s]%c",
				mptr->from, line, '\0');
		else {
			/* not the first line */
			if( (llenleft = sizeof(sndsubj) - strlen(sndsubj))
			    -strlen(line)-SIZEFROM-10 > 0 )
				sprintf( &sndsubj[strlen( sndsubj)],
				"\n[%s:  %s]%c", mptr->from, line, '\0');
			else if( llenleft > 7 )
				sprintf( &sndsubj[strlen( sndsubj)],
				"\n...%c", '\0');

		}
	}

	fprintf( outfp, "\n----- Forwarded message # %d:\n\n", fwdnum);

	writmsg();

	mptr->flags |= M_FORWARDED;
}

/*
 *			F W D P O S T
 */
fwdpost()
{
	fprintf( outfp, "\n----- End of forwarded messages\n" );
}
/*
 *			S R C H K E Y
 */
srchkey( line, keypt)
	char *line, *keypt[];
{
	register int	n;
	register char	*pkey, *pline;

	for( n = 0; keypt[n] != 0; n++ ) {
		pkey = keypt[n];
		pline = line;

		while( *pkey != '\0' ) {

			if( *pline != *pkey && *pline - 'A' + 'a' != *pkey )
				goto trynext;
			pline++;
			pkey++;
		}
		return(1);
trynext: ;
	}
	return(0);
}
/* ------------------------------------------------------------------ 
 *			F I L O U T
 *
 *	Filter most control chars from text - Prevents letter bombs
 *	Also does paging
 *	****** linecount must be initialized before calling filout ******
 */
filout(line, ofp)
	char *line;
	FILE *ofp;
{
	register int cmd;

	if( paging ) {
		linecount += (strlen( line ) / linelength ) + 1;

		if( doctrlel && strindex( "\014", line) >= 0) {
			/* Got formfeed */
			tt_raw();
			if( !confirm("Formfeed. Continue?",NOLF))
				error("");
			tt_norm();
			linecount = 0;
		}

		if ( linecount >= pagesize - 2) {
			tt_raw();
			if( (cmd = confirm("Continue?",NOLF)) == FALSE )
				error("");
			tt_norm();
			switch( cmd ) {

			case '\n':		/* One more line */
				linecount = pagesize;
				break;
				
			default:
				linecount = 0;
				break;
			}
		}
	}

	if( filoutflag == OFF ) {
		fputs( line, ofp );
		return;
	}
	
	for( ; *line != '\000'; line++ ) {
		if( *line >= ' ' && *line <= '~' )
			putc( *line, ofp );
		else {
			switch ( *line ) {

			case '\007':	/* Bel */
			case '\t':
			case '\n':
			case '\r':
			case '\b':
				putc( *line, ofp );
				break;

			default:
				putc( '^', ofp );
				putc( *line + '@', ofp );
				break;
			}
		}
	}
}
/*
 *  			M A K E D R F T
 *  Make a file containing one or more messages
 */
makedrft() {

	register int fd;

	if( (fd = creat(draft_original, sentprotect)) < 0 )  {
		perror( draft_original );
		return;
	}
	close( fd );

	/* LIST messages into the draft_original file */
	lstsep = TRUE;
	strcpy( outfile, draft_original );
	cpyiter( lstmsg, DOIT, (int(*)()) 0 );

}
 3; isspace(mmdf/uip/msg/msg3.c   444      0     12       41520  3671073301   7373 /*
 *			M S G 3 . C
 *
 * This is the third part of the MSG program.
 *
 * Functions -
 *	xeq		run a sub-process
 *	help		supply help
 *	gitr		get message range
 *	gitrtype	get message range type details
 *	gitrnum		get message range -- numeric
 *	unset		clear all "Process it" flags
 *	cntproc		count number of messages to be processed
 *	setrange	mark group of messages to process
 *	settype		mark all messages according to type command
 *	getnum		input a decimal number
 *	doiter		apply a function to all marked messages
 *	resend		resend message to addresses
 *	sortbox		sort msg box
 *	qcomp		qsort comparison routine
 *
 *		R E V I S I O N   H I S T O R Y
 *
 *	06/09/82  MJM	Split the enormous MSG program into pieces.
 *
 *	03/16/83  MJM	Decoupled "inverse" from "all".
 */
#include "util.h"
#include "mmdf.h"
#include <pwd.h>
#include <signal.h>
#include <sys/stat.h>
#include <sgtty.h>
#ifdef V4_2BSD
#include <sys/wait.h>
#endif V4_2BSD
#include "./msg.h"

/*
 *			X E Q
 *
 *  Execute some program, such as SEND or the Shell.
 */
int (*old2)();
int (*old3)();
int (*old13)();
int (*old18)();

xeq( exflag)
char exflag;
{
	int process;
#ifdef V4_2BSD
	union wait pstatus;
#else V4_2BSD
	int	pstatus;
#endif V4_2BSD
	char *routine;

	tt_norm();
	old2 = signal( SIGINT, SIG_IGN);
	old13 = signal( SIGPIPE, SIG_IGN);
#ifdef SIGTSTP
	old18 =	signal( SIGTSTP, SIG_DFL);
#endif SIGTSTP

	process = fork();
	if( !process)  {
		int	fd;

		signal( SIGINT, orig);
		signal( SIGQUIT, old3);
		signal( SIGPIPE, old13);

#ifndef V4_2BSD
		for (fd = _NFILE-1; fd > 2; fd--)
#else
		for (fd = getdtablesize()-1; fd > 2; fd--)
#endif
			close (fd);

		switch( exflag)  {

		case 'a': 		  /* answer send fork */
			execlp( routine = sndname, sndname, sndto, "-c", sndcc,
				"-s", sndsubj, (char *)0);
			break;

		case 'A':
			/* Invoke SEND after 2-window edit */
			execlp( routine = sndname, sndname, sndto, "-c", sndcc,
				"-s", sndsubj, "-f", draft_work, "-n", (char *)0);
			break;

		case 'd': 
			execlp( routine = "date", "date", (char *)0);
			break;

		case 'e':
			execlp( routine = ueditor, ueditor,
				draft_original, draft_work, (char *)0 );
			break;

		case 'f': 		  /* forward send fork */
			execlp( routine = sndname, sndname, "-f", outfile,
			  "-c", (isnull( sndsubj[0]) ? (char *)0 : "-s"),
			  sndsubj, (char *)0);
			break;

		case 'j': 		  /* One command shell fork */
			if( verbose)  {
				printf( "jump into lower shell running: ");
				fflush(stdout);
			}
			execlp( routine = ushell, ushell, "-t", (char *)0);
			break;

		case 's':		/* shell fork */
			execlp( routine = ushell, ushell, (char *)0);
			break;

		case '!':		/* shell fork */
			execlp( routine = ushell, ushell, "-c", key, (char *)0 );
			break;

		case 'S': 		  /* send fork */
			execlp( routine = sndname, sndname, (char *)0);
			break;
		}
		fprintf (stderr, "Can't execute %s\r\n", routine);
		exit( -1);
	}  else  {
		while (wait( &pstatus ) != -1) ;

		signal( SIGINT, old2);
		signal( SIGQUIT, SIG_IGN);
		signal( SIGPIPE, old13);
#ifdef SIGTSTP
		signal( SIGTSTP, old18);
#endif SIGTSTP
		tt_init();		/* in case he changed the ttymodes */
	}
	tt_raw();
}

/*--------------------------------------------------------------------*/

char *cmdlst[] = {
	"",
	"Command Summary -- Type only the first letter of a command",
	"",
	"MESSAGE HANDLING:			    NAVIGATION:",
	"Answer [message #(s)]			    Backup to prev msg & type it",
	"Forward [message #(s)]			    Next message & type it",
	"Send (compose) a new message		    Headers of [message #(s)]",
	"Type [message #(s)] onto terminal	    Go to message #",
	"Delete [message #(s)]			    Current message # is typed",
	"Undelete [message #(s)]",		
	"Keep [message #(s)]                        /-same as dcn",
	"Y-Resend [message #(s)]",
	"Z-Two window answer",
	"",
	"FILE HANDLING:				    MISCELLANEOUS:",
	"Exit and update -- normal exit		    Jump into sub-Shell",
	"List [message #(s)] into text file	    Xtra user options",
	"Overwrite old file			    : current date and time",
	"Put [message #(s)] into msg file	    ; ignore rest of line",
	"Quit -- fast exit			    @ Undigestify",
	"Read another msg file			    ! sub-Shell",
	"Move [message #(s)] into msg file & mark as deleted",
	"",
	"Examples of message #(s) are:  42  1-4  2,5,7-.  1-5,7-@  c  32-$",
	0
};

char *xtcmdlst[] = {
	"",
	"xtra options command summary -- Type only the first letter of a command",
	"",
	"Binary file write",
	"Control char filter (on/off)",
	"List message body",
	"Mark message",
	"Numbered message lists (on/off)",
	"Output mbox file",
	"Paging (on/off)",
	"Reorder (sort) msg file",
	"Strip keywords (on/off)",
	"Verbose (on/off)",
	"Xtra options status",
	0
};

/*
 *			H E L P
 */
help(helplist)
char *helplist[];
{
	register unsigned int i;

	tt_norm();
	for( i = 0; helplist[i] != 0; i++)
		printf( "%s\r\n", helplist[i]);
	tt_raw();
}

/*--------------------------------------------------------------------*/

/*
 *			G I T R
 *
 *  Get message range.
 */
gitr()
{
	if( status.ms_nmsgs == 0)  {
		printf( "'%s' is empty\r\n", filename);
		error( "" );
	}

	unset();

	if( !gitrtype())
		gitrnum();

	if( cntproc() == 0)
		error( " no such messages\r\n");
}

/*
 *			G I T R T Y P E
 *
 * Get range type.  Alphabetic answers are processed here,
 * numerics are left alone (FALSE return).
 */
gitrtype()
{
	char cmd;

	cmd = nxtchar;
	nxtchar = rdnxtfld();
	nxtchar = uptolow(nxtchar);

	if( cmd == 'g' )  {
		/* Only numerics valid for GOTO command */
		if( isalpha( nxtchar) && nxtchar != 'c' && nxtchar != 'm')
			error( "...not legal for this command\r\n");
	}

	switch( nxtchar)  {
	case 'a': 
		if( verbose)
			printf( "all");
		if( cmd != 'h' ) {
			if( !confirm((char *)0,DOLF) )
				error("");
		}
		else if (verbose)
			printf("\r\n");
		setrange( 1, status.ms_nmsgs);
		break;

	case 'c': 
		if( verbose)
			printf( "current\r\n");
		if( status.ms_curmsg == 0)  {
			if( status.ms_nmsgs != 0)
				status.ms_curmsg = 1;
			else
				if( status.ms_curmsg > status.ms_nmsgs)
					error( "no current message\r\n");
		}
		setrange( status.ms_curmsg, status.ms_curmsg);
		break;

	case 'd': 
		if( verbose)
			printf( "deleted messages\r\n");
		settype( 'd');
		if( cntproc() == 0)
			error( "no deleted messages\r\n");
		break;

	case 'e': 
		if( verbose)
			printf( "expression: ");
		key[0] = rdnxtfld();
		gather( key, 79);
		if (key[0]);
			settype( 'e');
		break;

	case 'f': 
		if( verbose)
			printf( "from: ");
		key[0] = rdnxtfld();
		gather( key, 79);
		if (key[0])
			settype( 'f');
		break;

	case 'i': 
		if( verbose)
			printf( "inverse ");
		ascending = FALSE;	/* flag for doiter() */

		if( !gitrtype() )	/* RECURSE */
			gitrnum();
		return( TRUE );

	case 'k':
		if( verbose )
			printf( "kept messages" );

		if( cmd == 'd' ) {
			if( !confirm((char *)0,DOLF) )
				error("");
		}
		else if (verbose)
			printf("\r\n");

		settype( 'k' );
		break;

	case 'l':
		if( verbose)
			printf( "leaving" );

		if( !ismainbox )
			error( "\r\nNot main mailbox - no messages will leave\r\n");

		if( cmd == 'd' ) {
			if( !confirm((char *)0,DOLF) )
				error("");
		}
		else if (verbose)
			printf("\r\n");

		settype( 'l' );
		break;

	case 'm':
		if(verbose)
			printf( "mark\r\n");
		if( status.ms_markno > 0 && status.ms_markno <= status.ms_nmsgs )
			setrange(status.ms_markno, status.ms_markno);
		else
			error("no mark set\r\n");
		break;

	case 'n':
		if( verbose)
			printf( "new");

		if( cmd == 'd' ) {
			if( !confirm((char *)0,DOLF) )
				error("");
		}
		else if (verbose)
			printf("\r\n");

		settype( 'n' );
		break;

	case 's': 
		if( verbose)
			printf( "subject: ");
		key[0] = rdnxtfld();
		gather( key, 79);
		if (key[0])
			settype( 's');
		break;

	case 't': 
		if( verbose)
			printf( "to: ");
		key[0] = rdnxtfld();
		gather( key, 79);
		if (key[0])
			settype( 't');
		break;

	case 'u': 
		if( verbose)
			printf( "undeleted messages");

		if( cmd == 'd' ) {
			if( !confirm((char *)0,DOLF) )
				error("");
		}
		else if (verbose)
			printf("\r\n");

		settype( 'u');
		if( cntproc() == 0)
			error( "no undeleted messages\r\n");
		break;

	case '?':
		printf( "\r\na message sequence:\r\n");
		printf( " a(ll), c(urrent), d(eleted), e(xpression), f(rom), l(eaving),\r\n");
		printf( " k(ept), m(ark), n(new), i(inverse), s(ubject), t(o), u(ndeleted)\r\n");
		printf( " or a number or list of numbers of the form\r\n");
		printf( " (#  #-#  #,#,#,#  $ .-$)  .=current @=mark $=last\r\n" );
		error( "" );

	case '\003':
		error(" ^C\r\n");
		
	case '\004':
		error(" ^D\r\n");

	case '\n': 
		if( status.ms_curmsg == 0)  {
			if( status.ms_nmsgs != 0)
				status.ms_curmsg = 1;
			else
				if( status.ms_curmsg > status.ms_nmsgs)
					error( "no current message\r\n");
		}
		if( cmd == 'g' && status.ms_markno > 0 && status.ms_markno <= status.ms_nmsgs )
			status.ms_curmsg = status.ms_markno;
		setrange( status.ms_curmsg, status.ms_curmsg);
		switch( cmd)  {

		case 'a': 
		case 'f': 
		case 'h': 
		case 't': 
		case 'y':
		case 'z':
			if (verbose) printf("\r\n");
			break;	  /* message number is printed in header */

		default: 
			/* Print brief header */
			printf( "\r\n # %d  %s\r\n", status.ms_curmsg,
				msgp[status.ms_curmsg-1]->from);
		}			  /* DROP ON THROUGH */
		break;

	default: 
		return( FALSE);
	}
	if (!verbose) suckup();
	return( TRUE);
}

/*
 *			G I T R N U M
 *
 * Get range -- numeric case
 */
gitrnum()
{
	unsigned int getn;
	unsigned int mbegin, mend;

	key[0] = nxtchar;
	gather( key, 78 );
	gc = key;

	while( *gc != '\000' )  {
		switch( *gc )  {

		case ' ':
		case ',':
			gc++;
		}

		if( *gc == '\000')
			break;

		getn = getnum( &mbegin);
		mend = mbegin;

		switch( *gc )  {

		case '\000': 
		case ' ': 
		case ',': 
			if( mbegin == 0 || mbegin > status.ms_nmsgs)
				error( "bad number (type ? for help)\r\n");
			break;

		case '>': 
		case '-': 		  /* ;2 Replace range delimiters */
		case ':': 		  /* ;2 Replace range delimiters */
			if( getn == 0)
				mbegin = 1;
			if( mbegin == 0 || mbegin > status.ms_nmsgs)
				error( " bad first number (type ? for help)\r\n");

			gc++;
			getn = getnum( &mend);

			if( getn == 0)
				mend = status.ms_nmsgs;
			else
				if( mend > status.ms_nmsgs)  {
					printf( "... %d is last message\r\n", status.ms_nmsgs);
					mend = status.ms_nmsgs;
				}
			if( mend == 0)
				error( "bad last number (type ? for help)");
			break;

		default: 
			error( " bad message number (type ? for help)\r\n");
		}
		setrange( mbegin, mend);
	}
}

/*--------------------------------------------------------------------*/

/*
 *			U N S E T
 *
 * Clear all the M_PROCESS_IT flags.  Used prior to setting ranges, etc.
 */
unset()
{
	register struct message **mp;

	/* set all the flags to zero */
	for( mp = &msgp[status.ms_nmsgs]; mp-- != &msgp[0]; )
		(*mp)->flags &= ~M_PROCESS_IT;
}

/*
 *			C N T P R O C
 *
 * Count how many messages to process.
 */
cntproc()
{
	register unsigned int nproc;
	register struct message **mp;

	nproc = 0;
	for( mp = &msgp[status.ms_nmsgs]; mp-- != &msgp[0]; )
		if( (*mp)->flags & M_PROCESS_IT)
			nproc++;
	return( nproc);
}

/*
 *			S E T R A N G E 
 *
 * set group of messages to process
 */
setrange( mbegin, mend )
register unsigned int mbegin;
register unsigned int mend;
{
	if( mbegin == 0 )
		mbegin = 1;
	if( mend > Nmsgs )
		mend = Nmsgs;

	while( mend-- >= mbegin)
		msgp[mend]->flags |= M_PROCESS_IT;
}

/*
 *			S E T T Y P E
 */
settype( type )
char type;
{
	register struct message **mp;
	register unsigned int i;

	switch( type )  {

	case 'd':
		for( mp = &msgp[status.ms_nmsgs]; mp-- != &msgp[0]; )
			if( (*mp)->flags & M_DELETED)
				(*mp)->flags |= M_PROCESS_IT;
		break;

	case 'n':
		for( mp = &msgp[status.ms_nmsgs]; mp-- != &msgp[0]; )
			if( (*mp)->flags & M_NEW)
				(*mp)->flags |= M_PROCESS_IT;
		break;

	case 'k':
		for( mp = &msgp[status.ms_nmsgs]; mp-- != &msgp[0]; )
			if( (*mp)->flags & M_KEEP)
				(*mp)->flags |= M_PROCESS_IT;
		break;

	case 'u':
		for( i = status.ms_nmsgs; i-- != 0;)
			if( !(msgp[i]->flags & M_DELETED))
				msgp[i]->flags |= M_PROCESS_IT;
		break;

	case 's':
		for( i = status.ms_nmsgs; i-- != 0; )
			if( strindex( key, msgp[i]->subject) >= 0)
				msgp[i]->flags |= M_PROCESS_IT;
		break;

	case 'f':
		for( i = status.ms_nmsgs; i-- != 0; )
			if( prefix( msgp[i]->from, "To:"))  {
				if( prefix( key, username))
					msgp[i]->flags |= M_PROCESS_IT;
			}  else  {
				if( strindex( key, msgp[i]->from) >= 0)
					msgp[i]->flags |= M_PROCESS_IT;
			}
		break;

	case 't':
		for( i = status.ms_nmsgs; i-- != 0; )
			if( strindex( key, msgp[i]->to ) >= 0 )
				msgp[i]->flags |= M_PROCESS_IT;
		break;

	case 'e':
		tt_norm();
		for( msgno = 0; msgno < status.ms_nmsgs; msgno++)
			if(txtmtch())
				msgp[msgno]->flags |= M_PROCESS_IT;
		tt_raw();
		break;

	case 'l':
		tt_norm();
		for( mp = &msgp[status.ms_nmsgs]; mp-- != &msgp[0]; )
			if(    !((*mp)->flags & M_KEEP)
			    && !((*mp)->flags & M_DELETED)
			    && !((*mp)->flags & M_NEW))
				(*mp)->flags |= M_PROCESS_IT;
		tt_raw();
		break;

	default:
		error("bad settype() call");
	}
}

/*
 *			G E T N U M
 */
getnum( result)
unsigned int *result;
{
	register unsigned int i;
	register unsigned int n;

	i = n = 0;
	while( *gc == ',' || *gc == '-' || *gc == '>' || *gc == ':' )
		gc++;
	if( *gc == '$')  {
		*result = status.ms_nmsgs;
		gc++;
		return( TRUE);
	} else if ( *gc == '.') {
		*result = status.ms_curmsg;
		gc++;
		return( TRUE );
	} else if ( *gc == '@' ) {
		if( status.ms_markno > 0 && status.ms_markno <= status.ms_nmsgs )
			*result = status.ms_markno;
		else
			error("no mark set\r\n");
		gc++;
		return( TRUE );
	}
	while( isdigit( *gc ) )  {
		n = n * 10 + *gc - '0';
		i++;
		gc++;
	}

	*result = n;
	return( i);
}

/*--------------------------------------------------------------------*/

/*
 *			D O I T E R
 *
 * do iteration on message numbers
 */
doiter( fn)
int ( *fn)();
{
	unsigned int lastdone;

	if( status.ms_nmsgs == 0)
		return;
	tt_norm();
	lastdone = msgno;
	if( ascending == TRUE )  {
		/* process in ascending order */
		for( msgno = 1; msgno <= status.ms_nmsgs; msgno++)
			if( (mptr = msgp[msgno-1])->flags & M_PROCESS_IT)  {
				(*fn)();
				lastdone = msgno;
			}
	}  else  {
		/* Process in inverse (descending) order */
		for( msgno = status.ms_nmsgs; msgno > 0; msgno-- )
			if( (mptr = msgp[msgno-1])->flags & M_PROCESS_IT)  {
				(*fn)();
				lastdone = msgno;
			}
	}
	tt_raw();
	msgno = lastdone;
}

/*-----------------------------------------------------------------------
 *  			R E S E N D M S G
 *  
 *  Resend message to addressees
 */
resendmsg() {

	if( verbose )
		putchar('.');
	lstsep = FALSE;
	lstmore = FALSE;
	cpyiter( lstmsg, NOIT,( int( *)()) 0);
	mptr->flags |= M_FORWARDED;
}
/*			S O R T B O X
 *  
 */
sortbox() {

	register int i,j,k;
	struct message *mpt;
	char *pi, *pj;

	if( status.ms_nmsgs == 0)  {
		printf( "'%s' is empty\r\n", filename);
		error( "" );
	}

	nxtchar = rdnxtfld();
	nxtchar = uptolow(nxtchar);
	switch( nxtchar)  {

	case 'd':
		if( verbose )
			printf("Date\r\n");
		break;

	case 'f': 
		if( verbose)
			printf("From\r\n");
		break;

	case 'r':
		if( verbose )
			printf("date and subject\r\n");
		fflush(stdout);
		/* sort by date first */
		nxtchar = 'd';
		qsort( &msgp[0], status.ms_nmsgs, sizeof(msgp[0]), qcomp );

		/* Match subjects */
		for( i = 0; i < status.ms_nmsgs-1; i++ ) {
			if( msgp[i]->subject[0] == '\0' )
				continue;
			for( pi = msgp[i]->subject; prefix( pi, "Re:" ) == TRUE; )
				pi = strend("Re:",pi);

			for( j = i+1; j < status.ms_nmsgs; j++ ) {
				if( msgp[j]->subject[0] == '\0' )
					continue;
				for( pj = msgp[j]->subject; prefix( pj, "Re:" ) == TRUE; )
					pj = strend("Re:",pj);

				if( lexnequ( pi, pj, SIZESUBJ ) != TRUE )
					continue;

				/* Match - move rest down a slot */
				if( j == i+1 ) {	/* No need to move */
					i++;
					continue;
				}
				mpt = msgp[j];
				for( k = j-1; k > i; k-- )
					msgp[k+1] = msgp[k];
				msgp[i+1] = mpt;
				i++;
			}
		}
		return;

	case 's': 
		if( verbose)
			printf( "Subject\r\n");
		break;

	case 't':
		if( verbose )
			printf("To\r\n");
		break;

	case '?':
		printf( "\r\nd(ate), f(rom), r( date & subject), s(ubject), t(o)\r\n");
		error( "" );

	case '\004':
		error( " ^D\r\n" );

	default:
		error( "" );
	}

	fflush(stdout);
	qsort( &msgp[0], status.ms_nmsgs, sizeof(msgp[0]), qcomp);
}

/*			Q C O M P
 *  
 *  Comparison routine called by qsort
 */
qcomp(pa,pb)
struct message **pa, **pb;
{
	int sval;
	char *cpa, *cpb;
	
	switch( nxtchar ) {

	case 'd':
		return(datecmp((*pa)->date, (*pb)->date));

	case 'f': 
		return(lexrel( (*pa)->from, (*pb)->from ));

	case 's': 
		/* Remove all Re: */
		for( cpa = (*pa)->subject; prefix( cpa, "Re:" ) == TRUE; )
			cpa = strend("Re:",cpa);
		for( cpb = (*pb)->subject; prefix( cpb, "Re:" ) == TRUE; )
			cpb = strend("Re:",cpb);
		if( (sval = lexrel( cpa, cpb )) == 0 )
			return(datecmp((*pa)->date, (*pb)->date));
		else
			return(sval);

	case 't':
		return( lexrel( (*pa)->to, (*pb)->to ) );

	default:
		error( "Bad type in qcomp" );
	}
	/*NOTREACHED*/
}

datecmp(a, b)
long a, b;
{
	if ((a - b) < 0)
		return (-1);
	if ((a - b) > 0)
		return (1);
	return(0);
}
( result)
unsigned int *result;
{
	register unsigned int i;
	register unsigned int n;

	i = n = 0;
	while( *gc == ',' || *gc == '-' || *gc == '>' || *gc == ':' )
		gc++;
	if( *mmdf/uip/msg/msg4.c   444      0     12       42202  3671073303   7374 /*
 *			M S G 4 . C
 *
 * This is the fourth part of the MSG program.
 *
 * Functions -
 *	txtmtch		search for given pattern in text of messages
 *	mainbox		test to see if we are processing home mailbox
 *	gethead		fetch and scan header from message on disk
 *	gothdr		check input line for header field match
 *	prefix		compare string to RFC822 header line prefix
 *	confirm		ask a yes/no question
 *	gather		read in a line
 *	suckup		discard rest of line
 *	echochar
 *	ttychar
 *	tt_init		fetch tty modes
 *	tt_raw		switch to raw mode
 *	tt_norm		switch to cooked mode
 *	onint		interrupt catcher
 *	onnopipe	broken pipe catcher
 *	onhangup	hangup signal catcher
 *	onstop		stop signal catcher
 *	error		error handler
 *	xomsgrc		sets up xtra options when .msgrc file exists
 *	xostat		xtra options status printer
 *	strend		detects keyword at start of str and returns pointer
 *	xfgets		specials fgets that removes nulls
 *
 *		R E V I S I O N   H I S T O R Y
 *
 *	06/09/82  MJM	Split the enormous MSG program into pieces.
 *
 *	11/22/82  HW	Added Xtra options and header keyword stripping.
 *
 *	12/04/82  HW	Added SIGPIPE catcher and fixed SIGINT after pipe
 *			bug.
 *
 *	06/01/84  HW	Added ctrl char filter.
 */
#include "util.h"
#include "mmdf.h"
#include <pwd.h>
#include <signal.h>
#include <sys/stat.h>
#ifdef SYS5
#include <termio.h>
#else SYS5
#include <sgtty.h>
#endif SYS5
#include "./msg.h"

char	*keywds[MAXKEYS] = {
	"via",
	"message",
	"remailed-date",
	"remailed-to",
	"sender",
	"mail",
	"article",
	"origin",
	"received",
	0
};
		
/*
 *			T X T M T C H
 */
txtmtch()
{
	long size;
	int retval;
	char line[LINESIZE];

	mptr = msgp[msgno];

	fseek( filefp, mptr->start, 0);
	retval = FALSE;
	for( size = mptr->len; size > 0; size -= strlen(line))  {
		if( xfgets( line, sizeof( line), filefp) == NULL )
			break;
		if( strindex( key, line) != NOTOK)  {
			retval = TRUE;
			break;
		}
	}

	return( retval);
}
/*--------------------------------------------------------------------*/

/*--------------------------------------------------------------------*/

/*
 *			M A I N B O X
 *
 * last part of filename => main?
 */
mainbox()
{
	register char *cp;

	for( cp = filename; *cp++; );		/* get to end of name */
	while( --cp != filename && *cp != '/');
	/* find last part */
	if( *cp == '/')
		cp++;

	ismainbox = strcmp(cp, (!isstr(mldflfil) ? username : mldflfil)) == 0;
	return( ismainbox );
}

/*
 *			G E T H E A D
 *
 * This routine seeks to the beginning of the message in the disk file,
 * reads in the header, and scans it to find the values of the various
 * fields.  Fields which the caller is not interested are passed as
 * NULLs.
 *
 * NOTE that the fseek() can cause great inefficiency if the message
 * happens to start on an ODD byte in the file...
 *
 * Typical use now is to get info for header on answer/reply.
 */
gethead( datestr, fromstr, sndstr, rplystr, tostr, ccstr, subjstr)
char *datestr;		/* where to save Date     field */
char *fromstr;		/* where to save From     field */
char *sndstr;		/* where to save Sender   field */
char *rplystr;		/* where to save Reply-To field */
char *tostr;		/* where to save To       field */
char *ccstr;		/* where to save cc       field */
char *subjstr;		/* where to save Subj     field */
{
	char line[LINESIZE];
	char curhdr[M_BSIZE];

	if( fromstr != 0)
		fromstr[0] = '\0';
	fseek( filefp, mptr->start, 0);			/* deadly! */
	curhdr[0] = '0';
	while(
		xfgets( line, sizeof( line), filefp) != NULL &&
		line[0] != '\n' &&
		strcmp( delim1, line) != 0 &&
		strcmp( delim2, line) != 0
	 )  {
		if( datestr != 0)
			if( gothdr( curhdr, "date:", line, datestr))
				continue;

		if( fromstr != 0 && isnull( fromstr[0]))
			if( gothdr( curhdr, "from:", line, fromstr))
				continue;

		if( rplystr != 0)
			if( gothdr( curhdr, "reply-to:", line, rplystr))
				continue;

		if( sndstr != 0)
			if( gothdr( curhdr, "sender:", line, sndstr))
				continue;

		if( tostr != 0)
			if( gothdr( curhdr, "To:", line, tostr))
				continue;

		if( ccstr != 0)
			if( gothdr( curhdr, "cc:", line, ccstr))
				continue;

		if( subjstr != 0)
			if( gothdr( curhdr, "subject:", line, subjstr))
				continue;

		/*
		 * If we got here, the line was of a type that we don't
		 * care about.  Zap curhdr, as it will not have been updated
		 * unless the line was processed by gothdr(), and we want
		 * to avoid processing extraneous continuations.
		 */
		 strcpy( curhdr, "boring-header-line:" );
	}
}

/*
 *			G O T H D R
 */
gothdr( curname, name, src, dest)
char *curname;		/* current field name */
char *name;		/* test field name */
char *src;		/* test input line */
char *dest;		/* where to put data part at end */
{
	unsigned int destlen;

	if( isspace( *src ))
	{
		/* This is a continuation line.  Check "remembered" field */
		if( strcmp( curname, name ) != 0 )
			return( FALSE );

		/* Continuatin of correct header line.  Retain "remembered"
		/* field.  Get rid of name & extra space from input line. */
		compress( src, src);

		/*** FALL OUT ***/
	}
	else if (prefix( src, name ))
	{
		/* New line and field name matches, lets go! */

		/* Remember the "current field" */
		strcpy( curname, name);

		if( strcmp( name, "date:" ) != 0 )
			/* get rid of name & extra space */
			compress( &src[strlen( name )], src);
		else
			/* Don't compress dates! */
			strcpy( src, &src[strlen( name )] );

		/*** FALL OUT ***/
	}
	else
	{
		/* New line but field does not match, skip it */
		return(FALSE);
	}

	/* Common code for TRUE replies */
	if( isnull( dest[0]))  {
		/* first entry */
		strcpy( dest, src);
	}  else  {
		/* add to end of field, if room */
		destlen = strlen( dest);
		if( (strlen( src) + destlen) < M_BSIZE - 1)
			sprintf( &dest[destlen], "\n%s%c", src, '\0');
	}
	return( TRUE);
}

/*
 *			P R E F I X
 */
prefix( target, prefstr)
register char  *target,
*prefstr;
{
	for( ; *prefstr; target++, prefstr++)
		if( uptolow( *target) != uptolow( *prefstr))
			return( FALSE);

	return( (!isnull(*target)) ? TRUE : FALSE);
}

/*--------------------------------------------------------------------*/

/*
 *			C O N F I R M
 *
 * Request a yes/no answer
 */
confirm(msg,lfflag)
char	*msg;
int	lfflag;
{
	register char c;

	if(msg == (char *)0)
		printf(" [Confirm] (y) ");
	else
		printf("%s (y) ", msg);
	if( !verbose)
		suckup();			/* BRL */
	c = ttychar();
	if( !verbose)
		suckup();		  /* suck up rest of line */

	switch( c)  {

	case 'Y': 
	case 'y': 
	case '\004':
	case ' ':
	case '\n': 
		if( verbose) {
			if( lfflag == DOLF )
				printf( "yes\r\n");
			else
				printf("\r                               \r");
		}
		return( (int) c );

	case 'N':
	case 'n': 
	case 003:				/* Ctl/C */
	case 007:				/* Ctl/G, for EMACS types */
	case '/':
		if (verbose) printf( "no\r\n");
		return( FALSE);

	default:
	case '?':
		if (verbose)
			printf("\r\nY, SPACE, or RETURN to confirm, N to abort\r\n");
		else
			printf("\r\nY<CR> or just <CR> to confirm, N<CR> to abort\r\n");
		fflush( stdout );
		return( confirm(msg, lfflag) );		/* recurse */
	}
}

/*
 *			G A T H E R
 *
 * gobbles a line of max length from user, stores into string 'sp'.
 * ASSUMES first char is already in sp[0].
 */
gather( sp, max)
char *sp;
{
	register char c;
	register char *p;

	for( p = sp, c = *sp;; ) {
		if( c == '\003' )		/* Error - cancel */
			error(" ^C\r\n");
		else if( c == '\004' )
			error(" ^D\r\n");
		else if( c == ch_erase ) {	/* Delete previous char */
			if( p > sp ) {
				p--;
				printf("\b \b");
			}
		}
		else if( c == '\n' || c == '\r' ) {	/* Done */
			*p = '\0';
			if (verbose) printf("\r\n");
			return;
		}
		else {
			*p++ = c;
			if( (p - sp) >= max )
				error(" Line too long\r\n");
			if (verbose) putc(c, stdout);
		}
		c = ttychar();
	}
}

/*
 *			S U C K U P
 */
suckup()
{
	if( lastc == '\n')
		return;
	while( (nxtchar = echochar()) != '\n' && nxtchar != '\004' );
}

/*
 *			E C H O C H A R
 */
echochar()
{
	register char c;

	c = ttychar();
	if( verbose)  {
		putchar( c);
		if( c == '\n')
			putchar( '\r');
	}
	return( c);
}

/*
 *			T T Y C H A R
 */
ttychar()
{
	register int c;

	fflush( stdout);
	if( (c = getchar()) == EOF) {
		if( ferror( stdin)) {
			tt_norm();
			exit( -1);
		}
		if( feof( stdin) )
			c = '\004';
	}

	c = toascii( c);		/* get rid of high bit */

	if( c == '\r')
		c = '\n';

	return( lastc = c);
}
/*--------------------------------------------------------------------*/

#ifdef SYS5
static struct termio orig_ioctl;
static struct termio raw_ioctl;
#define	TIOCSETN TCSETAW		/* Crafty... (be careful!) */
#else SYS5
static struct sgttyb   orig_ioctl;
static struct sgttyb   raw_ioctl;
#endif SYS5

/*
 *			T T _ I N I T
 */
tt_init()
{
#ifdef SYS5
	if( ioctl( fileno( stdin), TCGETA, &orig_ioctl) == -1) {
#else
	if( ioctl( fileno( stdin), TIOCGETP, &orig_ioctl) == -1) {
#endif
		verbose = 0;
		istty = FALSE;
	}
	else {
		raw_ioctl = orig_ioctl;
#ifdef SYS5
		ch_erase = orig_ioctl.c_cc[VERASE];
		raw_ioctl.c_iflag &= ~(ISTRIP | INLCR | IGNCR | ICRNL);
		raw_ioctl.c_oflag &= ~OPOST;
		raw_ioctl.c_lflag &= ~(ICANON | ECHO);
		raw_ioctl.c_cc[4] = 1;
		raw_ioctl.c_cc[5] = 0;
#else
		ch_erase = orig_ioctl.sg_erase;
		raw_ioctl.sg_flags |= CBREAK;
		raw_ioctl.sg_flags &= ~ECHO;
#endif
		istty = TRUE;
	}
}

/*
 *			T T _ R A W
 */
tt_raw()
{
	if( verbose)  {
		fflush( stdout);
		if( istty == TRUE ) {
			if( ioctl( fileno( stdin), TIOCSETN, &raw_ioctl) == -1) {
				perror( "problem setting raw terminal modes");
				verbose = 0;
			}
		}
	}
}

/*
 *			T T _ N O R M
 */
tt_norm()
{
	fflush( stdout);
	if( verbose)  {
		if( istty == TRUE ) {
			if( ioctl( fileno( stdin), TIOCSETN, &orig_ioctl) == -1)
				perror( "problem resetting normal terminal modes" );
		}
	}
}
/*--------------------------------------------------------------------*/

/*
 *			O N I N T
 */
onint()
{
	signal( SIGINT, onint);
	error( "\r\n" );
}

/*
 *			O N H A N G U P
 *
 * This routine is called when we get a hangup signal.
 */
onhangup()
{
	signal (SIGHUP, SIG_IGN);
	signal (SIGINT, SIG_IGN);
#ifdef SIGTSTP
	signal( SIGTSTP, SIG_IGN);
#endif SIGTSTP

	/* Prevent chatter on a closed line */
	close( 1 );
	close( 2 );
	open( "/dev/null", 0 );		/* Assuming fd will be 1 */
	open( "/dev/null", 0 );		/* Assuming fd will be 2 */
	bprint = bdots = OFF;
	fclose( stdout );

	/* Overwrite the binary box */
	if( binaryvalid )
		binbuild();
	_exit(0);
}

#ifdef SIGTSTP
/*			O N S T O P
 *  
 *  SIGSTOP signal catcher
 */
onstop() {
	int pid;
	int saveverbose = verbose;
	
	pid = getpid();
	if( binsavflag == ON )
		binbuild();
	tt_norm();
	kill(pid,SIGSTOP);
	verbose = 1;
	tt_norm();
	verbose = saveverbose;
	tt_raw();
	signal(SIGTSTP,onstop);
	error("\r\n");
}
#endif SIGTSTP

/*			O N N O P I P E
 *
 *	Catches SIGPIPE
 */
onnopipe() {
	signal( SIGPIPE, onnopipe );
	outfd = -1;
	error( " msg: Broken pipe\r\n" );
}

/*
 *			E R R O R
 *
 *  An error condition has occured.  Report it to the user, and long-jump
 * back to the top of the mainline.
 *
 *  Note:  We should probably close ALL extraneous file descriptors,
 * to prevent getting hung up with an exclusive-use file being left
 * open.
 */
error( s)
char *s;
{
	if( outfp != NULL && fileno(outfp) > 0 )  {
		fclose( outfp );
		outfp = NULL;
	}

	if( exclfd > 0 )  {
		close( exclfd );
		exclfd = -1;
	}

	fputs( s, stdout);
	fflush( stdout);

	tt_raw();

	if( !(verbose || nxtchar == '\n'))
		suckup();

	longjmp( savej, 1);
}

/*		X O M S G R C
 *
 *	Reads user option file .msgrc and sets options
 */
xomsgrc( filept )
	FILE	*filept;
{
	char	tbuf[LINESIZE];
	int	nkeysrd;
	char	*np, *nc;
	static	char sendprog[LINESIZE];
	
	nkeysrd = 0;
	while( xfgets( tbuf, sizeof(tbuf), filept ) != NULL ) {

		if( (np = index( tbuf, '\n' )) == NULL )
			continue;
		*np = '\0';

		if( tbuf[0] == '\0' || tbuf[0] == '#' )
			continue;

		if( (np = strend( "strip", tbuf )) != NULL ) {
			if( nkeysrd+1 >= sizeof(keywds)/sizeof(char *) )
				printf("Too many strip lines - %s IGNORED\r\n", tbuf );
			else if( *np != '\0' ) {
				keywds[nkeysrd] = malloc( strlen( np )+1 );
				for( nc = np; *nc != '\0'; nc++ )
					*nc = uptolow( *nc );
				strcpy( keywds[nkeysrd++], np );
			}
		}

		else if( (np = strend( "numberedlist", tbuf )) != NULL )
			prettylist = ON;

		else if( (np = strend( "nonumberedlist", tbuf )) != NULL )
			prettylist = OFF;

		else if( (np = strend( "nostrip", tbuf )) != NULL )
			keystrip = OFF;

		else if( (np = strend( "quicknflag", tbuf )) != NULL )
			quicknflag = ON;

		else if( (np = strend( "noquicknflag", tbuf )) != NULL )
			quicknflag = OFF;

		else if( (np = strend( "quickexit", tbuf )) != NULL )
			quickexit = ON;

		else if( (np = strend( "noquickexit", tbuf )) != NULL )
			quickexit = OFF;

		else if( (np = strend( "control-l", tbuf )) != NULL )
			doctrlel = ON;

		else if( (np = strend( "nocontrol-l", tbuf )) != NULL )
			doctrlel = OFF;

		else if( (np = strend( "zbinsave", tbuf )) != NULL )
			binsavflag = ON;

		else if( (np = strend( "nozbinsave", tbuf )) != NULL )
			binsavflag = OFF;

		else if( (np = strend( "savebox", tbuf )) != NULL ) {
			if( *np != '\0' ) {
				strcpy( defoutfile, np );
				strcpy( defmbox, np );
			}
		}

		else if( (np = strend( "draftdir", tbuf )) != NULL ) {
			if( *np != '\0' ) {
				if( *np == '/' ) {
					sprintf(draft_work,"%s/draft_work",np);
					sprintf(draft_original,"%s/%s",np,draftorig);
				}
				else {
					sprintf(draft_work,"%s/%s/draft_work",homedir,np);
					sprintf(draft_original,"%s/%s/%s",homedir,np,draftorig);
				}						
			}
		}

		else if( (np = strend( "draftorig", tbuf )) != NULL ) {
			if( *np != '\0' ) {
				strcpy(draftorig,np);
				np = rindex(draft_original,'/');
				*++np = '\0';
				strcat(draft_original,draftorig);
			}
		}
		
		else if( (np = strend( "sendprog", tbuf )) != NULL ) {
			if( *np != '\0' ) {
				if( *np == '/' )
					strcpy(sendprog,np);
				else
					sprintf(sendprog,"%s/%s",homedir,np);

				sndname = sendprog;
			}
		}

		else if( (np = strend( "pagesize", tbuf )) != NULL )
			pagesize = atoi(np);

		else if( (np = strend( "linelength", tbuf )) != NULL )
			linelength = atoi(np);

		else if( (np = strend( "writemsg", tbuf )) != NULL )
			wmsgflag = ON;

		else if( (np = strend( "nowritemsg", tbuf )) != NULL )
			wmsgflag = OFF;

		else if( (np = strend( "ctrlfil", tbuf )) != NULL )
			filoutflag = ON;

		else if( (np = strend( "noctrlfil", tbuf )) != NULL )
			filoutflag = OFF;

		else if( (np = strend( "mdots", tbuf )) != NULL )
			mdots = ON;

		else if( (np = strend( "nomdots", tbuf )) != NULL )
			mdots = OFF;

		else if( (np = strend( "bdots", tbuf )) != NULL )
			bdots = ON;

		else if( (np = strend( "nobdots", tbuf )) != NULL )
			bdots = OFF;

		else if( (np = strend( "bprint", tbuf )) != NULL )
			bprint = ON;

		else if( (np = strend( "nobprint", tbuf )) != NULL )
			bprint = OFF;

		else if( (np = strend( "paging", tbuf )) != NULL )
			paging = ON;

		else if( (np = strend( "nopaging", tbuf )) != NULL )
			paging = OFF;

		else if( (np = strend( "verbose", tbuf )) != NULL )
			verbose = ON;

		else if( (np = strend( "noverbose", tbuf )) != NULL )
			verbose = OFF;

		else
			printf("Unknown .msgrc option: %s\r\n",tbuf);
	}	
	if( nkeysrd > 0 ) 
		keywds[nkeysrd] = 0;
}

/*		S T R E N D
 *
 *	searches for str at beginning of target. If not found,
 * returns NULL. If found, skips white space and returns pointer
 * to first character following white space.
 */
char *
strend (str, target)
	char   *str, *target;
{
	register int slen;

	if( *target == '\0' )
	    return (NULL);

	slen = strlen( str );
	if( lexnequ( str, target, slen) != TRUE )
		return( (char *) NULL );

	for( target += slen; *target == '\t' || *target == ' '; target++ )
		;

	return (target);
}

/*		X O S T A T
 *
 *	Prints Xtra options status
 */
xostat() {
	register int	i;

	printf("\r\nMail file: %s   Binary file: %s\r\n",filename,binarybox);
	printf("Save file: %s", defmbox );
	printf("   Send program: %s\r\n",sndname );
	printf("drafts: %s  %s\r\n", draft_work,draft_original);

	printf("\nbdots: %s\t\t\t\tmdots: %s",
	  bdots?"on.":"off.", mdots?"on.":"off." );
	printf("\r\nbprint: %s\r\n",
	  bprint?"on.":"off." );

	printf("\nNumbered message list: %s\r\n",prettylist?"on.":"off.");
	printf("Writemsg flag: %s\r\n",wmsgflag?"on.":"off.");
	printf("Ctrl-char filter: %s\t\t\t",filoutflag?"on.":"off.");
	printf("Ctrl-L: %s\r\n",doctrlel?"on.":"off.");
	printf("Paging: %s\t\t\t\tPagesize: %d\r\n",
	  paging?"on.":"off.", pagesize);
	printf("Quicknflag: %s\t\t\tLinelength: %d\r\n",
	  quicknflag?"on. ":"off.",linelength);
	printf("Strip: %s\t\t\t\tVerbose: %s\r\n",
	  keystrip?"on.":"off.", verbose?"on.":"off." );

	printf("Strip keywords:\r\n");
	for( i = 0; keywds[i] != 0; i++ )
		printf("\t%s\r\n", keywds[i] );
}
/*			X F G E T S
 *  
 */
char *
xfgets(s, n, iop)
char *s;
register FILE *iop;
{
	register c;
	register char *cs;

	cs = s;
	while (--n>0 && (c = getc(iop))>=0) {
		if( c == 0 )
			*cs++ = '@';
		else
			*cs++ = c;
		if (c=='\n')
			break;
	}
	if (c<0 && cs==s)
		return(NULL);
	*cs = '\0';
	return(s);
}

/*                                                                      */
/*      Determine if the two given strings are equivalent.              */
/*                                                                      */

lexnequ (str1, str2, len)
register char   *str1,
		*str2;
register int	len;
{
	extern char chrcnv[];

	while ( (len-- > 0) && (chrcnv[*str1] == chrcnv[*str2++]) ) {
		if (len == 0 || *str1++ == 0)
			return (TRUE);
	}
	return (FALSE);
}
rintf(draft_original,"%s/%s/%s",homedir,np,draftorig);
				}						
			}
		}

		else if( (np = strend( "draftorig", tbuf )) != NULL ) {
			if( *np != '\0' ) {
				strcpy(draftorig,np);
				np = rindex(draft_original,'/');
				*++np = '\0';
				strcat(draft_original,draftorig);
			}
		}
		
		else if( (np = strend( "sendprog", tbuf )) != NULL ) {
			if( *np != '\0' ) {
				if( *np =mmdf/uip/msg/msg.h   444      0     12       14201  3671073304   7314 /*
 *			M S G . H
 *
 * This file contains the declarations for all the global variables
 * and defines for the MSG program.
 *
 *		R E V I S I O N   H I S T O R Y
 *
 *	06/09/82  MJM	Split the enormous MSG program into pieces.
 *
 *	06/10/82  MJM	Added additional flags bits
 *
 *	08/07/82  MJM	Allowed compile-time setting of NMSGS
 *
 *	09/13/82  MJM	Made msgp[] an array of pointers, to permit
 *			undelete to insert messages.
 *
 *	12/04/82  HW	Added xtra options, .msgrc, fixed two signal bugs.
 */

#define	ON		1
#define	OFF		0

#define SETREAD		1		/* regular file read parsing */
#define SETAPND		2		/* parse appended messages  */
#define SETUNDIGEST	3		/* undigestify one message */

/* Selection of who to answer to */
#define ANSFROM		01		/* just to senders */
#define ANSCC		02		/*   from & cc names */
#define ANSTO		04		/*   from & to names */
#define ANSALL		07		/* copies to all recipients */

/* Flags to gethead() to omit a field */
#define NODATE		(char *)0
#define NOFROM		(char *)0
#define NOSNDR		(char *)0
#define NORPLY		(char *)0
#define NOTO		(char *)0
#define NOCC		(char *)0
#define NOSUBJ		(char *)0

#define SIZEDATE	9
#define SIZEFROM	30	   	/* ALSO change hdrfmt  */
#define SIZESUBJ	40		/* */
#define SIZETO		30

#define	M_BSIZE		256

#define SPSIGS		0400007

struct message {
	long	start;			/* Offset into file */
	long	len;			/* Length of message */
	int	flags;			/* FLAGS word */
	long	date;
	char	datestr[SIZEDATE];
	char	from[SIZEFROM];
	char	to[SIZETO];
	char	subject[SIZESUBJ];
};

/* Flags values in msg[] struct */
#define M_DELETED	0000001		/* Msg flagged for deletion */
#define	M_PUT		0000002		/* Msg put to another file */
#define M_NEW		0000004		/* Msg not seen yet */
#define M_KEEP		0000010		/* Msg to be kept in this file */
#define M_ANSWERED	0000020		/* Msg has been answered */
#define M_FORWARDED	0000040		/* Msg has been forwarded */

#define M_RESTMAIL	0040000		/* Special for OverWrite only */
#define M_PROCESS_IT	0100000		/* Msg flagged for processing */

#ifndef NOEXTERNS
/* MMDF GLOBALS */
extern int 	sentprotect;	/* default mailbox protection */
extern char	*mldflfil;	/* default mailbox file */
extern char	*mldfldir;	/* directory containing mailbox file */
extern char	*delim1;	/* string delimiting messages (start) */
extern char	*delim2;	/* string delimiting messages (end) */
/* End of MMDF GLOBALS */

extern struct message *msgp[];
extern struct message *mptr;

extern int  errno;			/* sys-call error number */

extern int Nmsgs;		/* For Global use */

#define IDENTITY	0x6832		/* Magic Number; current value h2 */
/* Status structure, also exists at the front of a binary index file. */
struct status {
	int		ms_ident;	/* "Magic Number" */
	long		ms_time;	/* Last time file was processed */
	unsigned	ms_nmsgs;	/* Number of messages processed */
	unsigned	ms_curmsg;	/* "Current" message */
	long		ms_eofpos;	/* Msg file eof position */
	unsigned	ms_markno;	/* Marked message no */
	int		ms_xxx1;	/* Place holder - not used */
	int		ms_xxx2;	/* Place holder - not used */
	long		ms_xxx3;	/* Place holder - not used */
} status;

#define OLDIDENTITY	0x6831		/* Magic Number; previous value h1 */
struct oldstatus {
	int		ms_ident;	/* "Magic Number" */
	long		ms_time;	/* Last time file was processed */
	unsigned	ms_nmsgs;	/* Number of messages processed */
	unsigned	ms_curmsg;	/* "Current" message */
	long		ms_eofpos;	/* Msg file eof position */
};

/* Stat buffers, used for comparing times */
struct stat statb1;			/* Reference buffer */
struct stat statb2;			/* Temporary buffer */

char	ch_erase;

int     nottty;
unsigned int    msgno;			/* message currently being processed */

/* user search key for from/subject/text msg sequences */
char    key[80];
char	*gc;

char    filename[64];
char	outfile[256];
char	defmbox[60];
char	oldfile[60];
char	*homedir;		/* Full path to home directory */

char    maininbox[60];
char	binarybox[60];		/* Binary map file filename */
char	msgrcname[60];		/* User options filename */
char    defoutfile[60];
char    username[20];
char    ascending;
char    ismainbox;		/* current file is receiving .mail  */
char    nxtchar;
char    lstsep;			/* separate messages by formfeed   */
char    lstmore;		/* second, or more listed message  */
char	autoconfirm;		/* bypass asking user confirmation */
char	anstype;		/* what addresses to send answers to  */
char	binaryvalid;		/* TRUE if incore binary box is valid */
int	(*orig) ();		/* DEL interrupt value on entry */
int	verbose;

int     outfd;

int exclfd;		/* overwrit() exclusive use fd */
jmp_buf savej;

unsigned int ansnum;
unsigned int fwdnum;

FILE * filefp;			/* Input FILEp */
FILE * outfp;			/* Output FILEp */
FILE * binfp;			/* Binary map FILEp */
FILE * fpmsgrc;			/* User options FILEp */

char    ttyobuf[BUFSIZ];
char    sndto[M_BSIZE];
char    sndcc[M_BSIZE];
char    sndsubj[M_BSIZE + 9];

char	lastc;
int	istty;
int	keystrip;
int	mdots, bdots, bprint;
int	paging;
int	pagesize;
int	quicknflag;
int	quickexit;
int	filoutflag;
int	binsavflag;
int	wmsgflag;
int	doctrlel;
int	prettylist;
int	linelength;
int	linecount;
int	outmem;
char	*ushell, *ueditor;
int	readonly;
char	*tempname;
char	*binarypre;
char	*savmsgfn;
char	*sndname;		/* Name of mail sending program */
char	*nullstr;		/* A null string */

char	draft_work[64];		/* Work file for 2-window answer */
char	draft_original[64];	/* Original message(s) for 2-window ans */
char	draftorig[64];		/* Message saving file */

#define	MAXKEYS		41
char	*keywds[MAXKEYS];
#define	SP_BODY		0	/* Reading msg body */
#define	SP_HNOSP	1	/* Line was NOT stripped */
#define SP_HSP		2	/* Line was stripped */

#define DOIT		0	/* Argument to cpyiter() */
#define NOIT		1	/* Argument to cpyiter() */
#define DOLF		0
#define NOLF		1

extern char *compress ();
extern char *strdup ();
extern char *malloc();
extern char *index(), *strend(), *rindex();

extern prmsg(), delmsg(), undelmsg(), prhdr(), movmsg(), onint(), onnopipe();
extern putmsg(), writmsg(), gomsg(), lstmsg(), keepmsg(), lstbdy(), writbdy();
extern onstop(), hdrfile(), dolstmsg();

extern ansmsg(), fwdmsg(), fwdpost(), resendmsg(), ansend();
extern undigestify();
extern xomsgrc();
extern char *getenv();
extern char *xfgets();
extern long smtpdate();
extern qcomp();

#endif NOEXTERNS
ength: %d\r\n",
	  quicknflag?"on. ":"off.",linelength);
	printf("Strip: %s\t\t\t\tVerbose: %s\r\n",
	  keystrip?"on.":"off.", verbose?"on.":"off." );

	printf("Strip keywords:\r\n");
	for( i = 0; keywds[i] != 0; i++ )
		printf("\t%s\r\n", keywds[i] );
}
/*			X F G E T S
 *  
 */
char *
xfgets(s, n, iop)
char *s;
register FILE *iop;
{
	register c;
	register char *cs;

	cs = s;
	whmmdf/uip/msg/msg6.c   444      0     12       20702  3671073305   7401 /*
 *			M S G 6 . C
 *
 * Functions -
 *	smtpdate	Converts RFC822 date string to UNIX long
 *	sunday
 *	eatwhite	Returns first nonwhitespace char in string
 *	match
 *	tzset
 *	makedate	Convert UNIX time to string
 *
 *
 *		R E V I S I O N   H I S T O R Y
 *
 *	07/08/84  HAW	Created this module for time stamp processing.
 *			Most routines in this module were written
 *			by Ron Natelie.
 *
 */
#include "util.h"
#include "mmdf.h"

extern	long	timezone;
extern	long	atol();
extern	int	daylight;
extern	char	*tzname[];
extern	char	*ctime();

/*
 *  S M T P _ D A T E
 *
 *  Take an RFC822 (more or less) date string cp and convert it to
 *  unix time.  Allows a variety of illegal formats commonly in use.
 *
 */

#define	SPACE	' '
#define	HTAB	'\t'

long	match();

/*
 *  Matchtab structure.
 *  Used by match routine.
 */
struct	match_tab  {
	char	*t_string;		/*  String to match, NULL for last.  */
	long	t_val;			/*  Value to be returned.  */
};

/*
 *  Month tab matches month names to number.
 */
struct	match_tab	month_tab[]  =  {
	"jan", 1,	"feb", 2,	"mar", 3,	"apr", 4,
	"may", 5,	"jun", 6,	"jul", 7,	"aug", 8,
	"sep", 9,	"oct", 10,	"nov", 11,	"dec", 12,
	NULL, 0
};
	
#define	H(x)	( (x)*60L*60L)
struct	match_tab	zone_tab[] = {
	"ut", 0,	"gmt", 0,	"est", H(-5),	"edt", H(-4),
	"cst", H(-6),	"cdt", H(-5),	"mst", H(-7),	"mdt", H(-6),
	"pst", H(-8),	"pdt", H(-7),	"met", H(1),	"z", 0,
	"a", H(-1),	"b", H(-2),	"c", H(-3),	"d", H(-4),
	"e", H(-5),	"f", H(-6),	"g", H(-7),	"h", H(-8),
	"i", H(-9),	"k", H(-10),	"l", H(-11),	"m", H(-12),
	"n", H(1),	"o", H(2),	"p", H(3),	"q", H(4),
	"r", H(5),	"s", H(6),	"t", H(7),	"u", H(8),
	"v", H(9),	"w", H(10),	"x", H(11),	"y", H(12),
	NULL, 1
};

#define	dysize(A) (((A)%4) ? 365: 366)
static int dmsize[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
extern char *index();
extern char *eatwhite();

long
smtpdate(cp)
	register char	*cp;
{
	int	day, month, year, hours, minutes, seconds;
	int	dayno;
	long	tval;
	int	i;
	char	*dp;
	time_t	zone;
/*printf("S entry - date str: %s", cp); */
	cp = eatwhite(cp);
	dp = cp;


	/*
	 *  Look for digit, supposed to be day of the month, skipping
	 *  over day of the week stuff.
	 */
	while(1)  {
		if(*cp == 0)  goto bad_date;
		if(isdigit(*cp)) break;
		cp++;
	}

	day = atoi(cp);
/*printf("S day: %d\n",day); */
	/*  Skip digits and white space to get to month.  */

	while(isdigit(*cp)) cp++;
	while( *cp == SPACE || *cp == HTAB || *cp == '-') cp++;

	month = (int)match(month_tab, cp, 3);
	if(month == 0)  {
		/*  Hack here, look for ctime format.  */
		day = atoi(&dp[8]);
		month = (int)match(month_tab, &dp[4], 3);
		if(month == 0) goto bad_date;
		year = atoi(&dp[20]);
		hours = atoi(&dp[11]);
		minutes = atoi(&dp[14]);
		seconds = atoi(&dp[17]);
		zone = -1;
/*printf("S ctime for: y: %d m: %d d: %d h: %d m: %d s: %d\n",year,month,day,hours,minutes,seconds); */

	}
	else  {
		/*  Skip over to the year  */
		while(1)  {
			if(*cp == 0)  goto bad_date;
			if(isdigit(*cp)) break;
			cp++;
		}
		year = atoi(cp);

		while(isdigit(*cp)) cp++;

		while(*cp && !isdigit(*cp)) cp++;
	

		/*  Hours, minutes, seconds  */
		hours = atoi(cp);
		if(hours > 60)  {
			minutes = hours %100;
			hours = hours/100;
		}  else  {
			cp = index(cp, ':');
			if(cp == NULL) goto bad_date;
			minutes = atoi(++cp);
		}

		dp = index(cp, ':');
		if(dp)  {
			seconds = atoi(++dp);
			cp = dp;
		}
		else	seconds = 0;
/*printf("S hours: %d  mins: %d  secs: %d\n",hours,minutes,seconds); */
		while(isdigit(*cp)) cp++;
		cp = eatwhite(cp);
/*printf("S Zone starts- str: %s",cp); */
		if( (*cp == '+' || *cp == '-')  &&
		   isdigit(cp[1]) &&
		   isdigit(cp[2]) &&
		   isdigit(cp[3]) &&
		   isdigit(cp[4])
		)  {
			zone = (time_t)(atol(cp+3) * 60L);
			cp[3] = 0;
			zone *= (time_t)atol(cp);
		} else {
			if( *cp == '+' || *cp == '-' )
				cp++;
			dp = cp;
			while(isalpha(*cp)) cp++;
			*cp = 0;
			zone = (time_t)(-match(zone_tab, dp, 0));
		}
/*printf("S zone: %d\n",zone); */
	}
	if(year < 1900) year += 1900;
	if(month < 1 || month > 12)  goto bad_date;
	if(day < 1 || day > 31) goto bad_date;
	if(hours == 24)  {
		hours = 0;
		day++;
	}
	if(hours < 0 || hours > 23) goto bad_date;
	if(minutes < 0 || minutes > 59) goto bad_date;
	if(seconds < 0 || seconds > 59) goto bad_date;

	tval = 0;
	dayno = 0;
	for(i = 1970; i < year; i++)
		tval += dysize(i);

	if(dysize(year) == 366  && month >= 3)
		dayno += 1;
	while(--month) dayno += dmsize[month-1];
	tval += dayno;
	tval += day-1;
	tval *= 24;
	tval += hours;
	tval *= 60;
	tval += minutes;
	tval *= 60;
	tval += seconds;
	if(zone == -1)  {
		int beg, end;
		beg = sunday(119, year);
		end = sunday(303, year);
/*printf("S beg: %d  end: %d\n",beg,end); */
		zone = timezone;		
/*printf("S timezone: %d  daylight: %d: dayno: %d\n",timezone,daylight,dayno); */
		if(daylight &&
		    (dayno>beg || (dayno==beg && hours >=2)) &&
		    (dayno<end || (dayno==end && hours <1)))
		{
/*printf("S changing zone for daylight\n"); */
			zone -= 60*60;
		}
	}
/*printf("S before zone: %s",ctime(&tval)); */
	tval += zone;
/*printf("S after zone: %s\n",ctime(&tval)); */
	return tval;

bad_date:
	return -1L;
}

static int
sunday(d, year)
{
	int i;
	int	pdays = 0;

	for(i = 1970; i < year; i++)
		pdays += dysize(i);
	if(d >= 58 && dysize(year) == 366) d++;
	pdays = (pdays - 3) % 7;	/*  Day of week, day 0 was thurs  */
	if(pdays) d += 7-pdays;
	return d;
}
/*
 *  E A T W H I T E
 *
 *  An old favorite, returns the first non whitespace in the string.
 */
char *
eatwhite(cp)
	register char *cp;
{
	while(*cp == SPACE || *cp == HTAB) cp++;
	return cp;
}

long
match(tab, string, size)
	register struct	match_tab	*tab;
	char	*string;
	int	size;
{
	register char *cp, *str;
	char	*bufend;
	char	buffer[512];
	
	bufend = &buffer[ (size == 0) ? 512 : size];
	for(cp = buffer, str = string; cp < bufend; cp++, str++)  {
		if(isupper(*str)) *cp = tolower(*str);
		else	*cp = *str;
		if(*cp == 0) break;
	}

	while(tab->t_string != NULL)  {
		if(size)  {
			if(strncmp(buffer,tab->t_string, size) == 0)
				break;
		}  else  {
			if(strcmp(buffer, tab->t_string) == 0)
				break;
		}
		tab++;
	}
	return tab->t_val;
}

#ifndef SYS5
void
tzset()
{
	register char *p, *q;
	register int n;
	int sign;
	char *getenv();
	if((p = getenv ("TZ")) && *p) {
		n = 3;
		q = tzname[0];
		do {
			*q++ = *p? *p++: ' ';
		} while(--n);
		if(sign = *p == '-')
			p++;
		n = 0;
		while(*p >= '0' && *p <= '9')
			n = (n * 10) + *p++ - '0';
		if(sign)
			n = -n;
		timezone = ((long)(n * 60)) * 60;
		if(daylight = *p != '\0') {
			q = tzname[1];
			n = 3;
			do {
				*q++ = *p? *p++: ' ';
			} while(--n);
		}
	}
	else	{			/* TZ not set in evironment */
#ifdef V4_2BSD
		extern int	gettimeofday();
#else
		extern int	ftime();
#endif
		static struct tzinfo	/* time zone names */
		{
			long	secwest;	/* sec west of GMT */
			char	*stdzone;	/* standard name */
			char	*dstzone;	/* DST name */
		}	tzi[] =
		{
			{	5L * 60L * 60L,	"EST",	"EDT"	},
			{	6L * 60L * 60L,	"CST",	"CDT"	},
			{	7L * 60L * 60L,	"MST",	"MDT"	},
			{	8L * 60L * 60L,	"PST",	"PDT"	},
			{	0L * 60L * 60L,	"GMT",	"?DT"	},
			{    (-1L) * 60L * 60L, "MET",  "?DT"   },
			/* add your favorite time zones here */
		};
		register struct tzinfo	*tzip;
#ifdef V4_2BSD
		struct {
			unsigned long	tv_sec;	/* since Jan. 1, 1970 */
			long		tv_usec;	/* microsec */
		}	tv;		/* real time from kernel */
		struct {
			long	tz_minuteswest;	/* of Greenwich */
			long	tz_dsttime;	/* "DST is in effect" */
		}	tz;		/* time zone data from kernel */
#else
		struct {
			time_t	time;
			unsigned short millitm;
			short	timezone;
			short	dstflag;
		}	tm;
#endif
#ifdef V4_2BSD
		if ( gettimeofday( &tv, &tz ) == 0 )
#else
		if ( ftime(&tm) == 0)
#endif
		{
#ifdef V4_2BSD
			timezone = tz.tz_minuteswest * 60L;
			daylight = tz.tz_dsttime != 0L;
#else
			timezone = tm.timezone * 60L;
			daylight = tm.dstflag != 0L;
#endif
			/* look zone names up in table */
			for ( tzip = tzi;
			      tzip < &tzi[sizeof tzi / sizeof tzi[0]];
			      ++tzip
			    )
				if ( tzip->secwest == timezone )
					{
					tzname[0] = tzip->stdzone;
					tzname[1] = tzip->dstzone;
					return;
					}
			/* zone names not found in table */
			tzname[0] = "?ST";
			tzname[1] = "?DT";
		}
		/* else use hard-coded default values */
	}
}
#endif SYS5

/*
 *			M A K E D A T E
 *
 * Convert UNIX date to string (DD MMM YY)
 */
makedate( date, dest )
long	*date;
char	*dest;
{
	char *cp;

	cp = ctime( date );
 	*dest++ = cp[8];
 	*dest++ = cp[9];
 	*dest++ = ' ';
 	*dest++ = cp[4];
 	*dest++ = cp[5];
 	*dest++ = cp[6];
 	*dest++ = ' ';
 	*dest++ = cp[22];
 	*dest++ = cp[23];

}

	tval *= 60;
	tval += seconds;
	if(zone == -1)  {
		int beg, mmdf/uip/msg/msg5.c   444      0     12       54336  3671073307   7414 /*
 *			M S G 5 . C
 *
 * Functions -
 *	setup		read mailbox file
 *	newmessage	notice & read new mail
 *	overwrit	remove deleted messages from a file by overwriting
 *	undigestify	process an undigestify command
 *	nambinary	build path name for binary parallel file
 *	binbuild	Write out a fresh binary parallel file
 *	binupdate	Update one record of the binary file
 *	binheaderupdate	Update the header of the binary file
 *
 *
 *		R E V I S I O N   H I S T O R Y
 *
 *	09/13/82  MJM	This module split off from msg1.c, to even
 *			the sizes and ease editing.
 *
 *	09/22/82  MJM	Added in most of the support for the parallel
 *			binary map file.
 *
 *	03/16/83  MJM	Added binary update routines, neatened manipulation
 *			of binary box overall.  Faster, too!
 *
 *	11/10/83  DPK	Added Steve Kille (SEK) continuation line fix.
 *
 *	07/15/84  HAW	Made binarybox and memory structures identical.
 *			Added readonly mode.
 */

#include "util.h"
#include "mmdf.h"
#include <pwd.h>
#include <signal.h>
#include <sys/stat.h>
#include "./msg.h"
#ifdef V4_2BSD
#include <sys/file.h>
#endif V4_2BSD

#ifndef V4_2BSD
int	(*oldhup)();
int	(*oldintr)();
int	(*oldquit)();
#endif

/*
 *			S E T U P
 *
 *	This function is called to scan the contents of the mailbox.
 */
setup( setflg)
int     setflg;
{
	char    line[LINESIZE];
	char	name[LINESIZE];
	char	mbox[LINESIZE];
	char    datestr[M_BSIZE];	/* where to save Date field */
	char	fromstr[M_BSIZE];	/* where to save From field */
	char	sndstr[M_BSIZE];	/* where to save Sender field */
	char	tostr[M_BSIZE];		/* where to save To field */
	char	subjstr[M_BSIZE];	/* where to save Subj field */
	char	curhdr[M_BSIZE];	/* Scratch for gothdr() */
	char	digsubject[SIZESUBJ];	/* Scratch for undigest function */
	char	*cpy, *cpz;
	int	needscan;
	unsigned int     inmsg;
	unsigned int    count;
	long    newpos;
	long    curpos;
	int     oldflag = 0;
	int	oldsig;
	static int i;		/* String index */

	needscan = inmsg = 0;

	switch( setflg )  {

	case SETAPND:
		status.ms_time = statb2.st_mtime;
		fseek( filefp, status.ms_eofpos, 0);
		newpos = status.ms_eofpos;
		break;

	case SETREAD:
		if( filefp != NULL)  {
			++oldflag;
			fclose( filefp);
		}
		if( (filefp = fopen( filename, "r")) == NULL)  {
			printf( "can't open '%s'\r\n", filename);
			if( oldflag)  {
				strcpy( filename, oldfile);
				filefp = fopen( filename, "r");
			}
			error( "");
		}
		if( binfp != (FILE *)NULL ) {
			fclose( binfp );
			binfp = (FILE *)NULL;
		}

		fstat( fileno( filefp), &statb1);

		if( statb1.st_size == 0 ) {		/* No mail - exit */
			if( quickexit == ON ) {
				printf("%s is empty!\n",filename);
				tt_norm();
				exit(0);
			}
		}

		status.ms_time = statb1.st_mtime;
		if( (statb1.st_mode & S_IFMT) != S_IFREG)
			error( "File is not a regular file.\r\n");

#ifndef W_OK
#define	W_OK 2
#endif
		if( access( filename, W_OK ) < 0 ) {
			printf(" Unable to write %s - READONLY mode\r\n",filename);
			readonly = TRUE;
		}

		/* Set flags if this is the default mail file */
		mainbox();

		/* Build the name of the binary mail index file */
		nambinary( filename );

		if( (binfp = fopen( binarybox, "r" )) != NULL )  {

#ifndef V4_2BSD
			oldhup = signal(SIGHUP,SIG_IGN);
			oldintr = signal(SIGINT,SIG_IGN);
			oldquit = signal(SIGQUIT,SIG_IGN);
#else
			oldsig = sigsetmask(SPSIGS);
#endif
			binaryvalid = FALSE;

			/* Load binary map first */
			if( bprint == ON ) {
				printf("Loading binary box %s ", binarybox );
				fflush( stdout );
			}
			/* Read header */
			fread( &status, sizeof(struct status), 1, binfp );

			/* Quick hack - change from OLDIDENTITY to
			 * IDENTITY can be done by msg */
			if( status.ms_ident == OLDIDENTITY ) {
				rewind(binfp);
				fread( &status, sizeof(struct oldstatus),
					1, binfp );
				status.ms_markno = 0;
				/* binbuild will be called below */
			} else if( status.ms_ident != IDENTITY )  {
				tt_norm();
				printf("\n\nBinary file version error.");
				printf("  file=%x, program=%x\n", status.ms_ident, IDENTITY );
				printf("This version of msg uses a new binary file which is not compatible\n");
				printf("with the old version. You may either delete the binary\n");
				printf("file %s and restart msg\n", binarybox );
				printf("or use the old version of msg. Deleting the binary file\n");
				printf("will destroy the old status flags but no mail will be lost.\n");
 				printf("The old version may still be used by typing msg.bak but may disappear\n");
				printf("soon so plan to convert to the new version as soon as possible.\n");

				exit(10);
				/* NOTREACHED */
			}

			/* Read data */
			for( i = 0; i < status.ms_nmsgs; i++ )  {
				if( (msgp[i] = (struct message *) malloc(sizeof(struct message)) ) == NULL ) {
					status.ms_nmsgs = i-1;	
					nomem();
					/* NOTREACHED */
				}

				if(fread( msgp[i], sizeof(struct message), 1, binfp ) != 1) {
					tt_norm();
					printf("\nThe binary box is corrupted.\n");
					printf("rm %s and retry 'msg'.\n", binarybox);
					exit(11);
				}

				if( bdots == ON && bprint == ON ) {
					putchar('.');
					fflush(stdout);
				}
			}
			/* Quick check of binary box integrity:
			 * check for delimiters in last message */
			fseek( filefp, status.ms_eofpos-strlen( delim2 ), 0 );
			if( ( xfgets( line, 2+sizeof( delim2 ), filefp ) == NULL )
			    || (strequ( line, delim2 ) == FALSE ))  {
				tt_norm();
				printf("\nThe binary box is corrupted.\n");
				printf("rm %s and retry 'msg'.\n", binarybox);
				exit(11);
			}
			
			if( bprint == ON ) {
				printf(" - Done\r\n");
				fflush( stdout );
			}

			if( status.ms_ident == OLDIDENTITY ) {
				printf("Rebuilding binary box\r\n");
				binbuild();
			}

			/* Proceed with appending the rest of the messages */
			newpos = status.ms_eofpos;

			fclose( binfp );
			binfp = (FILE *)NULL;
			binaryvalid = TRUE;
#ifndef V4_2BSD
			signal(SIGHUP,oldhup);
			signal(SIGINT,oldintr);
			signal(SIGQUIT,oldquit);
#else
			sigsetmask(oldsig);
#endif

			/* Fall through to read any new messages */
			setflg = SETAPND;	/* to get "new" flags */

		}  else  {

			printf( "Reading %s ", filename);
			fflush( stdout);

			status.ms_curmsg = 0;		/* Global */
			status.ms_nmsgs = 0;		/* Global */
			newpos = status.ms_eofpos = 0;

			if( readonly == FALSE )
				if( (i = creat( binarybox, sentprotect )) < 0 )  {
					printf("Unable to create %s - READONLY mode\r\n", binarybox );
					readonly = TRUE;
				}
				else
					close(i);
		}

		if( readonly == FALSE ) {
			/* get exclusive control of file */
			if( (binfp = fopen( binarybox, "r+" )) == NULL ) {
				printf("Can't write %s - READONLY mode\n",binarybox);
				readonly = TRUE;
			}
#ifdef V4_2BSD
			else {
				if( flock(fileno(binfp),LOCK_EX|LOCK_NB) < 0){
					printf( "%s busy - READONLY mode\n", filename);
					readonly = TRUE;
				}
			}
#endif V4_2BSD
		}
		break;

	case SETUNDIGEST:
		fseek( filefp, mptr->start, 0);
		newpos = mptr->start;
		strncpy( digsubject, mptr->subject, SIZESUBJ );
		break;

	default:
		error( "Bad setup()" );
	}

	fseek( filefp, newpos, 0 );
	while( xfgets( line, sizeof( line), filefp) != NULL)  {
		curpos = newpos;
		count = strlen( line);
		newpos += count;

		/*
		 * Check for Message Separators  (Two types)
		 */
		if( strequ( line, delim1) || strequ( line, delim2))  {
			if( setflg == SETUNDIGEST )
				return;
			if( inmsg != 0 )  {
				/* wants message NUMBER */
				binupdate( (int)status.ms_nmsgs );
				status.ms_eofpos = newpos;
			}
			inmsg = 0;
			continue;
		}

		if(
			setflg == SETUNDIGEST &&
			strncmp( line, "--------------------", 20 ) == 0
		)  {
			inmsg = 0;
			continue;
		}

		/*
		 * Note each new message.  If we are not in a message, and
		 * a line of non-separator has been found, start off a new
		 * message.
		 */
		if( inmsg == 0)  {
			if( mdots == ON ) {
				putchar( '.' );
				fflush( stdout);
			}
			/* Clear out header scan area */
			datestr[0] =
			    fromstr[0] =
			    sndstr[0] =
			    tostr[0] =
			    subjstr[0] = '\0';
			curhdr[0] = '\0';	/* SEK continuation fix! */

			/* scan off leading blank lines */
			while( count == 1)  {
				xfgets( line, sizeof( line), filefp);
				curpos = newpos;
				count = strlen( line);
				newpos += count;
			}

			/* Build new table entry */
			if( status.ms_nmsgs >= Nmsgs)  {
				printf("More than %d messages in file.\r\n", Nmsgs);
				status.ms_nmsgs--;
				nomem();
				/* NOTREACHED */
			}

			/*
			 * Allocate a new msgp slot, either at the end of the
			 * messages, or in the middle, depending.
			 */
			if( setflg != SETUNDIGEST )  {
				if( (mptr = msgp[status.ms_nmsgs++] = (struct message *) malloc(sizeof(struct message)) ) == NULL ) {
					status.ms_nmsgs--;
					nomem();
					/* NOTREACHED */
				}
				mptr->flags = 0;
			}  else  {
				/*
				 * scrunch down.
				 *   msgno is current message NUMBER
				 *   status.ms_nmsgs is last message NUMBER.
				 * offsets are one less than numbers.
				 */
				if( (mptr = msgp[status.ms_nmsgs] = (struct message *) malloc(sizeof(struct message)) ) == NULL ) {
					error("Out of memory - operation aborted\n");
					/* NOTREACHED */
				}
				
				for( i = status.ms_nmsgs-1; i >= msgno; i-- )  {
					msgp[i+1] = msgp[i];
				}
				status.ms_nmsgs++;
				msgp[ msgno++ ] = mptr;
				mptr->flags = M_NEW;
			}

			mptr->start = curpos;
			mptr->len = 0L;
			mptr->date = 0L;
			mptr->from[0] = mptr->datestr[0] = mptr->subject[0] = mptr->to[0] = '\0';

			needscan = 1;
			inmsg++;
		}
		mptr->len += count;

		if( count == 1 && needscan)  {
			/*
			 * Blank line found while reading header.
			 * Digest header information and store results
			 * into msgp[] structure entry.
			 */

			/* Process date field( if any) */
			if( !isnull( datestr[0])) {
				if( (mptr->date = smtpdate( datestr )) != -1L)
					makedate( &mptr->date, mptr->datestr);
			}

			/* Process to field ( if any ) */
			if( !isnull( tostr[0] ) ) {
				strncpy( mptr->to, tostr, SIZETO );
				mptr->to[SIZETO-1] = '\0';
			}

			/* Process from field( if any) */
			if( !isnull( fromstr[0]))  {
				parsadr( fromstr, name, mbox, (char *)0);

				if( prefix( username, mbox) && !isnull( tostr[0]))  {
					/* sender was self */
					parsadr(tostr, name, mbox,( char *) 0);
					if( !isnull( name[0]))
						sprintf( fromstr, "To: %s%c", name, '\0');
					else
						sprintf( fromstr, "To: %s%c", mbox, '\0');
				}  else  {
					if( !isnull( name[0]))
						strncpy(fromstr,name,SIZEFROM);
					else
						strncpy(fromstr,mbox,SIZEFROM);
				}
				mptr->from[SIZEFROM-1] = '\0';
				/* Try to eliminate UUCP prefixes */
				{
					register char *cp;
					char * indent;

					indent = cp = fromstr;
					while( *cp )
						if( *cp++ == '!' )
							indent = cp;

					strncpy(mptr->from,indent,SIZEFROM-1);
				}

				mptr->flags |= M_NEW;
			}

			/* Process Subject field, if any */
			if( !isnull( subjstr[0]))  {
				subjstr[M_BSIZE-1] = '\0';
				if( (cpz = index(subjstr,'\n')) != 0 )
					*cpz = '\0';
				if( filoutflag == ON ) {
					for(cpz = subjstr,cpy = mptr->subject;
					    (*cpz != '\0') && (cpy - mptr->subject < SIZESUBJ);
					    cpz++ ) {
						if( *cpz < ' ' || *cpz == '\177' ) {
							*cpy++ = '^';
							*cpy++ = '@' + *cpz;
						}
						else
							*cpy++ = *cpz;
					}
					*cpy = '\0';
				}
				else
					strncpy( mptr->subject, subjstr, SIZESUBJ );
				mptr->subject[SIZESUBJ-1] = '\0';
			}

			/* Header scan complete */
			needscan = 0;
		}

		/* This line is part of the header -- scan it */
		if( needscan && line[0] != '\n' )  {
			gothdr( curhdr, "date:", line, datestr);
			gothdr( curhdr, "from:", line, fromstr);
			gothdr( curhdr, "sender:", line, sndstr);
			gothdr( curhdr, "To:", line, tostr);
			gothdr( curhdr, "subject:", line, subjstr);
		}
	}
	fstat( fileno( filefp), &statb1);
	status.ms_time = statb1.st_mtime;

	/*
	 * Check for having read a message while it was being delivered.
	 * Exclusive opens might be better, but for now...
	 */
	if( inmsg != 0 )  {
		printf("(partial message ignored) ");
		free( (char *) msgp[--status.ms_nmsgs]);
	}

	printf( " %d message%c total.\r\n", status.ms_nmsgs, status.ms_nmsgs == 1 ? ' ' : 's');

	/*
	 * rewrite entire binary file if we expanded the middle of the
	 * table with an undigestify operation.  SETREAD and SETAPND handled
	 * inside loop, with a binupdate() after each new message.
	 */
	if( readonly == FALSE ) {
		if( setflg == SETUNDIGEST )
			binbuild();
		else
			binheaderupdate();
		fflush( binfp );
	}
}

/*
 *			N E W M E S S A G E
 *
 * notice & include new mail
 */
newmessage()
{
	/* Might also be useful to check the sizes */
	if( filefp == NULL ||
	    fstat( fileno( filefp), &statb2) < 0 ||
	    statb2.st_mtime == status.ms_time
	) {
		return;		/* nothing new */
	}
	printf( "Reading new messages ");
	setup( SETAPND );		/* incorporate appended messages */
}

/*
 *			V P U T M S G
 *
 * This routine gives a visual indication of each message being written
 * out, with one dot per message.  Makes the user feel better about the
 * often longish wait.
 *
 * This routine is really intended only for the use of the overwrite function,
 * as it records the NEW position of of the message in the file, to avoid
 * guesswork about message lengths in overwrite().
 */
vputmsg()
{
	long pos, ftell();
	char tbuf[1024];
	register int tt;
		
	if( mdots == ON ) {
		putchar('.');
		fflush( stdout );
	}

	fputs( delim1, outfp );

	pos = ftell( outfp );
	if( mptr->flags & M_RESTMAIL ) {
		/* Write the rest of the mail file */
		fseek(filefp, mptr->start, 0);
		while( (tt = fread(tbuf, sizeof(char), sizeof(tbuf), filefp)) == sizeof(tbuf) )
			fwrite(tbuf, sizeof(char), strlen(tbuf), outfp);
		fwrite(tbuf,sizeof(char), tt, outfp);
	}
	else {
		writmsg();
		fputs( delim2, outfp );
	}
	mptr->start = pos;
}

/*
 *			O V E R W R I T
 *
 * remove deleted messages from file.
 *
 * Note that vputmsg() above updates the message start position info.
 */
overwrit()
{
	static char tempfile[128];	/* build name of temporary file */
	unsigned int ndeleted;
	unsigned int i,ii;
	static char *ptr;
	register struct message **inp, **outp;	/* For mashing table */
	
	if( readonly == TRUE )
		error("READONLY mode - overwrite ignored\n");

	for( ndeleted = 0, i = status.ms_nmsgs; i-- != 0; ) {
		if( msgp[i]->flags & M_RESTMAIL )
			msgp[i]->flags &= ~M_DELETED;

		if( msgp[i]->flags & M_DELETED )
			ndeleted++;		/* how many dead messages */
	}
	
	if( ndeleted == 0)  {
		printf( "file unchanged, so not updated\r\n");
		return;
	}

	if( outmem == FALSE && stat( filename, &statb2) >= 0 && status.ms_time != statb2.st_mtime)  {
			/* Leave the processing to newmessage() */
			/* We can't just return here, because this is called
			 * from the "exit" command...
			 */
			error( "File updated since last read...\r\n");
	}

	/* get exclusive control of file */
	if( (exclfd = lk_open( filename, 1, (char *)0, (char *)0, 5))
	     < 0 && errno == ETXTBSY)  {
		printf( "'%s' busy; try later", filename);
		error( "\r\n");
	}

	if( ndeleted == status.ms_nmsgs)  {
		/*
		 *  All the messages are deleted.
		 *  Truncate (always if mailbox), otherwise delete
		 */
		printf("All messages deleted from %s.\r\n", filename );
		if( ismainbox)  {
			creat( filename, sentprotect);
			creat( binarybox, sentprotect );
		}  else  {
			printf( "delete %s", filename);
			/* for safety... */
			if( confirm((char *)0,DOLF))  {
				unlink( filename );
				unlink( binarybox );
			}
			else {
				creat( filename, sentprotect);
				creat( binarybox, sentprotect );
			}
		}
	}  else  {
		strcpy( tempfile, filename);
		/* get the path to directory of file  */
		if (ptr = rindex(tempfile, '/'))
			*++ptr = '\0';
		else
			ptr = tempfile;
		strcpy(ptr, tempname);
		mktemp(tempfile);

		if( (outfd = creat( tempfile, sentprotect)) < 0)
			error( "can't create temporary file\r\n");

		if( (outfp = fdopen( outfd, "w" )) == NULL )
			error( "can't fdopen temp file\n" );
		outfd = -1;

		/* save the undeleted messages */
		printf("OverWriting %s ", filename);
		unset();
		settype( 'u' );
		doiter( vputmsg );
		status.ms_eofpos = ftell(outfp);
		fclose( outfp );
		if( unlink( filename) < 0)
			error( "can't delete old file\r\n");
		if( link( tempfile, filename) < 0)  {
			printf( "can't rename temporary file '%s' to be '%s'.\r\n",
			tempfile, filename);
			error( "The temporary file still exists.\r\n");
		}
		unlink( tempfile);
	}
	if( exclfd >= 0 ) {
		lk_close( exclfd, filename, NULL, NULL);
		if( stat( filename, &statb2 ) >= 0 )
			status.ms_time = statb2.st_mtime;
	}

	/* Mash table down to reflect new file layout */
	inp = &msgp[0];
	outp = &msgp[0];
	ndeleted = 0;
	i = status.ms_curmsg;
	ii = status.ms_markno;
	for( msgno = 1; msgno <= status.ms_nmsgs; msgno++ )  {
	
		if( (*inp)->flags & M_DELETED )  {
			ndeleted++;
			free(*inp++);
			if( msgno < i )
				status.ms_curmsg--;
			if( msgno < ii )
				status.ms_markno--;
		}  else  {
			/* Note that position changes are done by vputmsg() */
			/*
			 * If the input and output slots are different,
			 * clean up the input slot.  (Else we clobber
			 * ourselves!).
			 */
			if( (*inp) != (*outp) )  {
				*outp = *inp;
			}
			inp++;
			outp++;
		}
	}

	/* Adjust current message so as not to run off end */
	status.ms_nmsgs -= ndeleted;
	if( status.ms_curmsg > status.ms_nmsgs )
		status.ms_curmsg = status.ms_nmsgs;

	if( status.ms_markno > status.ms_nmsgs )
		status.ms_markno = status.ms_curmsg;
		
	if( status.ms_nmsgs == 0 ) {
		status.ms_curmsg = 0;
		return;
	}

	fclose( filefp );
	if( (filefp = fopen( filename, "r" )) == NULL )
		error("Unable to re-open file");

	fstat( fileno( filefp ), &statb1 );
	status.ms_time = statb1.st_mtime;

	printf( " %d message%c total.\r\n", status.ms_nmsgs, status.ms_nmsgs == 1 ? ' ' : 's');

	if( outmem == TRUE ) {
		/* Alter the status struct so the rest of the msgs will
		 *  be recovered by newmessage()
		 */
		for( i = status.ms_nmsgs; i-- != 0; )
			if( msgp[i]->flags & M_RESTMAIL )
				status.ms_eofpos = msgp[i]->start;
		status.ms_nmsgs--;
		status.ms_time = 0;
	}

	/*
	 * At this point, the rest of the program should not even
	 * know that anything has happened.  Things should be in
	 * a good enough state that everything else works normally.
	 */
	/* Update the binary file */
	binbuild();
}

/*
 *			U N D I G E S T I F Y
 */
undigestify()
{
	/* mptr set by doiter for setup() */
	delmsg();
	setup( SETUNDIGEST );
}


/*
 *			N A M B I N A R Y
 *
 * Build the name of the associated binary file, and save the name
 * in the string "binarybox".
 */
nambinary( arg )
register char *arg;
{
	register char *sav, *cp;
	char ending[32];
	char fname[72];

	ending[0] = fname[0] = 0;
	strcpy( fname, arg );

	/* Find the last path component */
	sav = cp = fname;
	while( *cp )
		if( *cp++ == '/' )
			sav = cp;

	if( sav == fname )  {
		/* No slashes found in path name */
		sprintf( binarybox, "%s%s", binarypre, fname );
	}  else  {
		strcpy( ending, sav );
		*(sav-1) = 0;

		sprintf( binarybox, "%s/%s%s", fname, binarypre, ending );
	}
}

/*
 *			B I N B U I L D
 *
 * Rewrite the entire binary file, including the header.
 */
binbuild()
{
	static char tempfile[128];	/* build name of temporary file */
	register int i;
	char *ptr;
	int oldsig;

	if( readonly == TRUE )
		return;

	if( binfp != (FILE *) NULL )
		fclose( binfp );

	/* Don't bother making a new binary file if mailbox is empty */
	if( status.ms_nmsgs <= 0 )  {
		unlink( binarybox );
		return;
	}

#ifndef V4_2BSD
	oldhup = signal(SIGHUP,SIG_IGN);
	oldintr = signal(SIGINT,SIG_IGN);
	oldquit = signal(SIGQUIT,SIG_IGN);
#else
	oldsig = sigsetmask(SPSIGS);	/* Block all signals */
#endif
	strcpy( tempfile, binarybox);
	/* get the path to directory of file  */
	if (ptr = rindex(tempfile, '/'))
		*++ptr = '\0';
	else
		ptr = tempfile;
	strcpy(ptr, tempname);
	mktemp(tempfile);

	if( (i = creat( tempfile, sentprotect )) < 0 )
		error("Unable to create temporary file\r\n");

	if( (binfp = fdopen( i, "w" )) == NULL )
			error( "can't fdopen temp file\n" );

	if( bprint == ON ) {
		printf("Writing binary file:");
		fflush (stdout);
	}

	binheaderupdate();

	for( i=0; i < status.ms_nmsgs; i++ )  {
		fwrite( msgp[i], sizeof(struct message), 1, binfp );
		if( bdots == ON && bprint == ON ) {
			putchar( '.' );
			fflush( stdout );
		}
	}
	fclose( binfp );
	if( unlink( binarybox ) < 0)
			error( "can't delete old binarybox\r\n");
	if( link( tempfile, binarybox ) < 0)  {
		printf( "can't rename temporary file '%s' to be '%s'.\r\n",
			tempfile, binarybox);
		error( "The temporary file still exists.\r\n");
	}
	unlink( tempfile);

	if( (binfp = fopen( binarybox, "r+" )) == NULL )
		error("Can't re-open binary box\r\n");
#ifdef V4_2BSD
	else
		if( flock(fileno(binfp),LOCK_EX|LOCK_NB) < 0)
			error("Can't lock binary box\r\n");
#endif V4_2BSD

	if( bprint == ON )
		printf(" done\r\n");
	fflush( stdout );
#ifndef V4_2BSD
	signal(SIGHUP,oldhup);
	signal(SIGINT,oldintr);
	signal(SIGQUIT,oldquit);
#else
	sigsetmask(oldsig);	/* restore signals */
#endif
}

/*
 *			B I N U P D A T E
 *
 * Update a single message in the binary box.
 * The number given is in the range 1...nmsgs.
 */
binupdate( number )
int number;
{
	long place;

	if( readonly == TRUE )
		return;

	if( number <= 0 || number > Nmsgs )
		printf("binupdate(%d): out of range\n", number);

	if( binfp == (FILE *) NULL )  {
		printf("binupdate: opening binary box\n");
		if( (binfp = fopen( binarybox, "r+" )) == NULL ) {
			printf("binupdate: unable to open binary box");
			fflush(stdout);
			return;
		}
	}	

	place = (number-1) * sizeof(struct message) + sizeof(struct status);
	if( fseek( binfp, place, 0) < 0 )
		printf("binupdate: fseek error\n");

	fwrite( msgp[number-1], sizeof(struct message), 1, binfp );
}

/*
 *			B I N H E A D E R U P D A T E
 */
binheaderupdate()
{
	if( readonly == TRUE )
		return;

	/* Build header record */
	fseek( binfp, (long) 0, 0 );
	status.ms_ident = IDENTITY;
	fwrite( &status, sizeof(struct status), 1, binfp );
}

/*			N O M E M
 *  
 *  Recover gracefully when out of memory
 */
nomem() {

	struct message **sp;
	long ls;

	/* Find the last msg; not always the "last" one */
	ls = msgp[0]->start;
	for( sp = &msgp[1]; sp < &msgp[status.ms_nmsgs]; sp++ ) {
		if( (*sp)->start > ls ) {
			mptr = (*sp);
			ls = (*sp)->start;
		}
	}

	mptr->flags |= M_RESTMAIL;
	outmem = TRUE;
	printf("\n\007***********************************************\n");
	printf("\007Out of memory - Move or delete existing messages then overwrite\n");
	printf("Additional messages will appear after the overwrite!\n");
	error("\007***********************************************\n");
	/* NOTREACHED */
}

setmbox(file)
register char *file;
{
	strcpy(defmbox, file);
	strcpy(defoutfile, file);
	ismainbox++;
}
name[0] = 0;
	strcpy( fname, arg );

	/* Find the last path component */
	sav = cp = fname;
	while( *cp )
		if( *cp++ == '/' )
			sav = cp;

	if( sav == fname )  {
		/* No slashes found in path name */
		sprintf( binarybox, "%s%s", binarypre, fname );
	}  else  {
		strcpy( ending, sav );
	mmdf/uip/msg/Makefile.real   444      0     12        3726  3635355537  10744 #   Instructions to Make, for compilation of MSG

MODULES	= msg1 msg2 msg3 msg4 msg5 msg6 msgtailor version

PARTS	= msg1.o msg2.o msg3.o msg4.o msg5.o msg6.o msgtailor.o

SOURCES	= msg1.c msg2.c msg3.c msg4.c msg5.c msg6.c msgtailor.c

real-default: xmsg

xmsg   :   $(PARTS) version.o $(MMDFLIBS)
	$(CC) $(LDFLAGS) -o xmsg $(PARTS) version.o $(MMDFLIBS) \
		-ltermcap $(SYSLIBS)
	-size xmsg


install:	$(BINDIR)/msg

$(BINDIR)/msg: xmsg
	-mv $(BINDIR)/msg $(BINDIR)/msg.bak
	cp xmsg $(BINDIR)/msg
	-chmod $(PGMPROT) $(BINDIR)/msg

version.o: $(PARTS) $(MMDFLIBS)
	mkversion
	$(CC) $(CFLAGS) -c version.c

lint:
	lint $(LFLAGS) $(SOURCES) $(LLIBS)

clean:
	-rm -f *.o xmsg core make.out tags

# DO NOT DELETE THIS LINE -- make depend uses it

msg1.o: msg1.c
msg1.o: ../../h/util.h
msg1.o: ../../h/mmdf.h
msg1.o: /usr/include/pwd.h
msg1.o: /usr/include/signal.h
msg1.o: /usr/include/sys/stat.h
msg1.o: /usr/include/sgtty.h
msg1.o: /usr/include/sys/ioctl.h
msg1.o: msg.h
msg2.o: msg2.c
msg2.o: ../../h/util.h
msg2.o: ../../h/mmdf.h
msg2.o: /usr/include/pwd.h
msg2.o: /usr/include/signal.h
msg2.o: /usr/include/sys/stat.h
msg2.o: /usr/include/sgtty.h
msg2.o: msg.h
msg3.o: msg3.c
msg3.o: ../../h/util.h
msg3.o: ../../h/mmdf.h
msg3.o: /usr/include/pwd.h
msg3.o: /usr/include/signal.h
msg3.o: /usr/include/sys/stat.h
msg3.o: /usr/include/sgtty.h
msg3.o: /usr/include/sys/wait.h
msg3.o: msg.h
msg4.o: msg4.c
msg4.o: ../../h/util.h
msg4.o: ../../h/mmdf.h
msg4.o: /usr/include/pwd.h
msg4.o: /usr/include/signal.h
msg4.o: /usr/include/sys/stat.h
msg4.o: /usr/include/sgtty.h
msg4.o: msg.h
msg5.o: msg5.c
msg5.o: ../../h/util.h
msg5.o: ../../h/mmdf.h
msg5.o: /usr/include/pwd.h
msg5.o: /usr/include/signal.h
msg5.o: /usr/include/sys/stat.h
msg5.o: msg.h
msg5.o: /usr/include/sys/file.h
msg6.o: msg6.c
msg6.o: ../../h/util.h
msg6.o: ../../h/mmdf.h
msgtailor.o: msgtailor.c
msgtailor.o: msg.h
version.o: version.c
# DEPENDENCIES MUST END AT END OF FILE
# IF YOU PUT STUFF HERE IT WILL GO AWAY
# see make depend above
te: unable to open binary box");
			fflushmmdf/uip/msg/mkversion   555      0     12         266  3620510607  10262 p=`cat version.number`
c=`echo 0 1 $p +p | dc`
d=`date | awk '{ print $3 " " $2 " " $(NF) }'`
echo "char	*verdate = \"$d #$c\";" > version.c
cc -c version.c
echo $c > version.number
msg1.c msg2.c msg3.c msg4.c msg5.c msg6.c msgtailor.c

real-default: xmsg

xmsg   :   $(PARTS) version.o $(MMDFLIBS)
	$(CC) $(LDFLAGS) -o xmsg $(PARTS) version.o $(MMDFLIBS) \
		-ltermcap $(SYSLIBS)
	-size xmsg


install:	$(BINDIR)/msg

$(BINDIR)/msg: xmsg
	-mv $(BINDIR)/msg $(BINDIR)/msg.bak
	cp xmsg $(BINDIR)/msg
	-chmod $(PGMmmdf/uip/msg/version.c   644      0     12          42  3656427347  10143 char	*verdate = "3 May 1986 #69";
 $p +p | dc`
d=`date | awk '{ print $3 " " $2 " " $(NF) }'`
echo "char	*verdate = \"$d #$c\";" > version.c
cc -c version.c
echo $c > version.number
msg1.c msg2.c msg3.c msg4.c msg5.c msg6.c msgtailor.c

real-default: xmsg

xmsg   :   $(PARTS) version.o $(MMDFLIBS)
	$(CC) $(LDFLAGS) -o xmsg $(PARTS) version.o $(MMDFLIBS) \
		-ltermcap $(SYSLIBS)
	-size xmsg


install:	$(BINDIR)/msg

$(BINDIR)/msg: xmsg
	-mv $(BINDIR)/msg $(BINDIR)/msg.bak
	cp xmsg $(BINDIR)/msg
	-chmod $(PGMmmdf/uip/msg/version.number   644      0     12           3  3656427362  11163 69
r	*verdate = "3 May 1986 #69";
 $p +p | dc`
d=`date | awk '{ print $3 " " $2 " " $(NF) }'`
echo "char	*verdate = \"$d #$c\";" > version.c
cc -c version.c
echo $c > version.number
msg1.c msg2.c msg3.c msg4.c msg5.c msg6.c msgtailor.c

real-default: xmsg

xmsg   :   $(PARTS) version.o $(MMDFLIBS)
	$(CC) $(LDFLAGS) -o xmsg $(PARTS) version.o $(MMDFLIBS) \
		-ltermcap $(SYSLIBS)
	-size xmsg


install:	$(BINDIR)/msg

$(BINDIR)/msg: xmsg
	-mv $(BINDIR)/msg $(BINDIR)/msg.bak
	cp xmsg $(BINDIR)/msg
	-chmod $(PGMmmdf/uip/msg/msgtailor.c   444      0     12        3135  3671074272  10513 /*
 *	M S G T A I L O R . C
 */

#define NOEXTERNS
#include "./msg.h"

#ifdef pdp11
#	define NMSGS	500
#else
#	define NMSGS	2000
#endif pdp11

char    *savmsgfn = "savebox";
char	*resendprog = "|/usr/local/mmdf/resend ";
char	*sndname = "/usr/local/mmdf/send"; /* Overridden by .msgrc sendprog */

char	*dflshell = "sh";	/* Overridden by getenv("SHELL"); */
char	*dfleditor = "pen";    /* Overridden by getenv("EDITOR"); */

int	pagesize = 22;		/* default line count */
int	linelength = 80;	/* default line size */

int	paging = OFF;		/* Internal paging of long messages */
int	verbose = OFF;		/* Command completion (raw mode) */
int	wmsgflag = OFF;		/* Save messages when calling send */
int	keystrip = ON;		/* Strip header lines on display */
int	bdots = OFF;		/* Binary box dots */
int	mdots = OFF;		/* Mailbox dots */
int	bprint = OFF;		/* Reading... messages */
int	quicknflag = OFF;	/* Turn off NEW before message prints */
int	quickexit = ON;		/* Don't stay in msg if mailbox is empty */
int	prettylist = ON;	/* Print message numbers in listings */
int	filoutflag = ON;	/* Filter text (display controls as ^X) */
int	binsavflag = ON;	/* Save binary box on exec's & pauses */
int	doctrlel = OFF;		/* Page on control-L */
#ifndef SYS5
long	timezone = 5*60*60;	/* Seconds West of GMT */
int	daylight = 1;		/* Do you use daylight savings time? */
				/* 0 = NO, 1 = YES */
char	*tzname[] = {"EST", "EDT",};
				/* Timezone strings, primary and daylight
				   savings time if used, in that order */
#endif SYS5

/***** Do not tailor below this line *****/
int	Nmsgs = NMSGS;
struct message *msgp[NMSGS];
struct message *mptr;
/../h/mmdf.h
msg5.o: /usr/include/pwd.h
msg5.o: /usr/include/signal.h
msg5.o: /usr/include/sys/stat.h
msg5.o: msg.h
msg5.o: /usr/include/sys/file.h
msg6.o: msg6.c
msg6.o: ../../h/util.h
msg6.o: ../../h/mmdf.h
msgtailor.o: msgtailor.c
msgtailor.o: msg.h
version.o: version.c
# DEPENDENCIES MUST END AT END OF FILE
# IF YOU PUT STUFF HERE IT WILL GO AWAY
# see make depend above
te: unable to open binary box");
			fflushmmdf/uip/other/   755      0     12           0  3671117123   6605 mmdf/uip/other/rcvfile.c   444      0     12       14511  3620510612  10502 #
/***************************************************************************\
* 									    *
*     R C V F I L E . C							    *
* 									    *
*     Written by Dave Crocker.						    *
*     Rewritten by J. Onions, combined the original two programs into	    *
*     one to make documentation easier, not to mention updates etc.	    *
*     most of the original text kept.					    *
*     Reworked again by Doug Kingston to make secure and clean.		    *
* 									    *
*     usage:   rcvfile directory [-l logfile] [-m]			    *
*	"directory" specifies where the files will be created under	    *
*	"-l logfile" specifies the logging file for the program		    *
*       "-m" instructs the program to make directories if necessary	    *
*									    *
*     Message has a Subject: line of the form				    *
*     Subject: rcvfile filename						    *
* 									    *
*     The body of the message will always be filed under the "directory"    *
*     specified (".." is trapped).  This can be made "/" to enable	    *
*     fileing anywhere.							    *
* 									    *
\***************************************************************************/

#include "util.h"
#include "mmdf.h"
#include <pwd.h>
#include <sys/stat.h>

extern int errno;
extern int sentprotect;
extern char *locname;

extern char *compress();
extern char *rindex();

long    filesize;
int	makedirs = 0;
char	*filedir = (char *)0;
char	*logfile = (char *)0;
char	dirname[LINESIZE];
char	filename[LINESIZE];
char	subjbuf[LINESIZE];
char	frombuf[LINESIZE];
char	datebuf[LINESIZE];
char	buffer[BUFSIZ];
char	inbuf[BUFSIZ];

/**/
main (argc, argv)
int	argc;
char	**argv;
{
    register int nread;
    register int fd;
    register int i;
    FILE *output;

    mmdf_init (argv[0]);
    setbuf (stdin, inbuf);

    init_log();

    for (i = 1; i < argc; i++) {
    	if (argv[i][0] != '-') {
	    if (filedir != (char *)0) {
		fputs ("  *** directory already specified\n", stderr);
		finish (RP_PARM);
	    }
	    filedir = argv[i];
	} else {
	    if (strcmp (argv[i], "-l") == 0) {
	    	if (i+1 == argc) {
		    fputs ("  *** missing logfile after -l\n", stderr);
		    finish (RP_PARM);
		}
	    	logfile = argv[++i];
	    } else if (strcmp(argv[i], "-m") == 0) {
		makedirs++;
	    } else {
		fprintf (stderr, "  *** unknown parameter '%s'\n", argv[i]);
		finish (RP_PARM);
	    }
	}
    }

    if (filedir == (char *)0) {
	fputs ("  *** missing filing directory\n", stderr);
	finish (RP_PARM);
    }
    sprintf(filename, "%s/", filedir);

    /*
     *  parse the header, find interesting info and save it.
     */
    while (fgets (buffer, sizeof buffer, stdin) != NULL && buffer[0] != '\n') {
	if (equal ("to", buffer, 2) || equal ("cc", buffer, 2))
	    fputs (buffer, stderr);
				  /* log "to" and "cc" information */
	if (equal ("date", buffer, 4))
	    compress (&buffer[strindex (":", buffer) + 1], datebuf);
				  /* save date    text in buffer        */
	if (equal ("from", buffer, 4))
	    compress (&buffer[strindex (":", buffer) + 1], frombuf);
				  /* save from    text in buffer        */
	if (equal ("subject", buffer, 7))
	    compress (&buffer[strindex (":", buffer) + 1], subjbuf);
				  /* save subject text in buffer        */
    }

    /* We accept "rcvarch" for backwords compat, and its the same size */
    if (!equal ("rcvfile", subjbuf, 7) && !equal ("rcvarch", subjbuf, 7))
    {                             /* doesn't have the command word      */
	fputs ("  *** Subject lacks keyword\n", stderr);
	finish (RP_PARM);
    }

    compress (&subjbuf[7], subjbuf);
    if ((strlen(subjbuf)+strlen(filename)+1) > sizeof filename) {
	fputs ("  *** filename too long\n", stderr);
	finish (RP_PARM);
    }

    /*
     *  Must not contain ".." and must not be null, we also make
     *  some other sanity/security checks.
     */
    if (subjbuf[0] == '\0'
      || strcmp(subjbuf, ".") == 0
      || strcmp(subjbuf, "..") == 0
      || initstr("../", subjbuf, 3)
      || strindex(subjbuf, "/../") != (-1)
      || endstr("/..", subjbuf, 3)) {
	fputs ("  *** Illegal filename\n", stderr);
	finish (RP_PARM);
    }

    strcat (filename, subjbuf);
    dirpart (filename);

    if ((fd = creat (filename, sentprotect)) < OK) {
	if( !makedirs ) {
	    fperror("  *** creat");
	    finish (RP_FCRT);
	} else {
	    if (creatdir (dirname, 0755, 0, 0) < 0) {
		fprintf( stderr, "%s *** unsuccessful mkdir\n",
			 dirname);
		finish (RP_FCRT);
	    }
	}
	if ((fd = creat (filename, sentprotect)) < 0) {
	    fperror("  *** creat after mkdir");
	    finish (RP_FCRT);
	}
    }
    if (chmod (filename, sentprotect) < 0) {
	fperror("  *** chmod");
	finish (RP_FIO);
    }

    if ((output = fdopen (fd, "a")) == NULL) {
	fperror("  *** fdopen");
	finish (RP_FIO);
    }
    filesize = 0;
    while ((nread = fread (buffer, sizeof(char), sizeof buffer, stdin)) != 0) {
	if (fwrite (buffer, sizeof(char), nread, output) != nread) {
	    fperror("  *** File output error");
	    finish (RP_FIO);
	}
    	filesize += nread;
    }
    if (ferror(stdin)) {
	fperror("  *** file input error");
	fclose (output);
	unlink (filename);
	finish (RP_FIO);
    }
    fclose (output);
    fprintf (stderr, "  (%ldc)\n", filesize);
    notify (datebuf, frombuf, filename, filesize);
    finish (RP_MOK);

    /* NOTREACHED */
}
/**/

finish (retval)
    int retval;
{
    fprintf (stderr, "%s\n\n", rp_valstr (retval));
    fflush (stderr);

    exit (retval == RP_MOK ? 0 : RP_MECH);
}

notify (thedate, thefrom, file, size)
char	*thedate, *thefrom, *file;
long	size;
{
    extern struct passwd *getpwuid ();
    struct stat statbuf;
    struct passwd *owner;
    char linebuf[2*LINESIZE];

    if (stat(dirname, &statbuf) < 0 ||
	(owner = getpwuid(statbuf.st_uid)) == NULL) {
	fprintf (stderr, "  *** get owner error (%d)\n", errno);
    }

    sprintf (linebuf, "[%s]%s got %ld characters.\n\nFrom %s, sent %s.\n",
	     locname, file, size, thefrom, thedate);

    if (ml_1adr (NO, NO, "FILE SERVER", (char *) 0, owner -> pw_name) < OK ||
	    ml_txt (linebuf) < OK ||
	    ml_end (OK) < OK )
	fprintf (stderr, "  *** notification error (%d)\n", errno);
}

dirpart (path)
char *path;
{
    register char *ptr;

    strcpy (dirname, path);
    if (ptr = rindex(dirname+1, '/'))
	*ptr = '\0';
    else
	dirname[0] = '\0';
}

init_log()
{
    if( access(logfile, 02) == 0)
    {
	freopen (logfile, "a", stderr);
    }
}

fperror(s)
char *s;
{
	fprintf(stderr, "%s: errno %d\n", s, errno);
}
atebuf);
				  /* save date    text in buffer        */
	if (equal ("from", buffer, 4))
	    compress (&buffer[strindex (":", buffer) + 1], frombuf);
				  /* save from    text in bufmmdf/uip/other/flock.c   444      0     12         527  3620510613  10111 #include <stdio.h>
#include <sys/file.h>

main(argc, argv)
int argc;
char *argv[];
{
	int	fd;

	if (argc < 3) {
		fputs("usage: flock file command\n", stderr);
		exit(1);
	}
	if ((fd = open (argv[1], 0)) < 0
	  || flock (fd, LOCK_EX | LOCK_NB)) {
		perror (argv[1]);
	  	exit (1);
	}

	execvp(argv[2], &argv[2]);
	perror(argv[2]);
	exit(1);
}
== 0
      || initstr("../", subjbuf, 3)
      || strindex(subjbuf, "/../") != (-1)
      || endstr("/..", subjbuf, 3)) {
	fputs ("  *** Illegal filename\n", stderr);
	fmmdf/uip/other/malias.c   444      0     12        5014  3657737771  10327 #include "util.h"
#include "mmdf.h"
#include "ch.h"
#include "dm.h"
#include <pwd.h>

extern char *index();
extern char *rindex();
extern char *multcat();
extern struct passwd *getpwmid();

main (argc, argv)
int argc;
char *argv[];
{
	register int     i;
	register char	*key;

	mmdf_init (argv[0]);
	for (i=1; i < argc; i++) {
		key = multcat (argv[i], "-outbound", (char *)0);
		if (do_arg(key) == 0)
			if (do_arg(argv[i]) == 0)
				printf ("no aliases for '%s'\n", argv[i]);
		free (key);
	}
	exit(0);
}

do_arg(arg)
register char *arg;
{
	char    buf[LINESIZE];
	char	alstr[LINESIZE];
	register char    *p, *q;
	FILE    *fp;
	int	ret;
	int	bypassable = 0;

	if ((ret = aliasfetch(TRUE, arg, buf, 1)) != OK) {
		if (ret == MAYBE) {
			printf("%s: Nameserver timeout\n");
			return(0);
		}
		if ((ret = aliasfetch(TRUE, arg, buf, 0)) != OK) {
			if (ret == MAYBE) {
				printf("%s: Nameserver timeout\n");
				return(0);
			}
			if (getpwmid (arg) != NULL) {
				printf ("%s is a user\n", arg);
				return(1);
			}
			return(0);
		}
		bypassable++;
	}

	strcpy(alstr, arg);
	if (bypassable)
		strcat(alstr, " (bypassable)");
	if( buf[0] == '~' ) {
		printf("%s is a non-recursive alias: %s\n",
			alstr, buf);
		return(1);
	}
	if (index (buf, ',') != 0 && strindex (":include:", buf) < 0
		&& index (buf, '<') == 0 && index (buf, '|') == 0)
	{
		printf ("%s is list of aliases: %s\n", alstr, buf);
		return(1);
	}
	if (((p = index (buf, '@')) == 0) && (index (buf, '/') == 0))
	{
		printf ("%s has alias %s\n", alstr, buf);
		return(1);
	}

	/* Assume if multiple entries, that     */
	/* only the first one is used.		*/
	if ((q= index (buf, ',')) != 0)
		*q = '\0';
	if (p) {
		*p++ = '\0';
		if (ch_h2chan (p, 1)  != (Chan *) OK) {
			printf ("%s is a list expanded on machine %s\n",
					alstr, p);
			return(1);
		}
	} else
		p = buf;
	if ((p = index (buf, '/')) == 0) {
		printf ("bad format for alias '%s': value '%s'\n",
				alstr, buf);
		return(1);
	}
	if ((q = index (buf, '|')) != 0) {
		*q++ = '\0';
		printf ("%s is a pipe alias which runs %s as user %s\n",
				alstr, q, buf[0] ? buf : "root");
		return(1);
	}
	if (index (buf, '<') == 0 && strindex (":include:", buf) < 0)
	{
		*p++ = '\0';
		printf ("mail for %s is filed in %s as user %s\n",
				alstr, p, buf[0] ? buf : "root");
		return(1);
	}
	printf ("%s expands to contents of list file %s:\n", 
		alstr, p);
	if ((fp = fopen (p, "r")) == NULL) {
		printf ("unable to open file %s\n", p);
		return(1);
	}
	while (fgets (buf, LINESIZE, fp) != NULL)
		printf ("%s", buf);
	fclose (fp);
	return(1);
}

char *path;
{
    register char *ptr;

    strcpy (dirname, path);
    if (ptr = rindex(dirname+1, '/'))
	*ptr = '\0';
    else
	dirname[0] = '\0';
}

init_log()
{
    if( access(logfile, 02) == 0)
    {
	freopen (logfile, "a", stderr);
    }
}

fperror(s)
char *s;
{
	fprintf(stderr, "%s: errno %d\n", s, errno);
}
atebuf);
				  /* save date    text in buffer        */
	if (equal ("from", buffer, 4))
	    compress (&buffer[strindex (":", buffer) + 1], frombuf);
				  /* save from    text in bufmmdf/uip/other/mlist.c   444      0     12       37357  3631171106  10220 #include "util.h"
#include "mmdf.h"
#include "ch.h"
#include <pwd.h>
#include <sys/stat.h>
#include <signal.h>
#include <stdio.h>

/*
 *      Program to perform list additions and requests
 *
 *  Feb 84 Steve Kille    - initial coding
 */

FILE *curfp;                             /* for alias file */
char *basehelpfile = "mlist.help";
char *helpfile;

char buf[LINESIZE];

char listname[LINESIZE];
char filename[LINESIZE];
char manager[LINESIZE];
char *username;
int realid;
int curmode;
int gotargs;

#define FREEMODE 0666
#define PUBMODE 0744
#define PRIVMODE 0644
#define SECRMODE 0600

extern char *locmachine;
extern char *supportaddr;
extern char *cmddfldir;

extern char *compress();
extern char *rindex();
extern char *index();
extern char *mktemp();
extern char *strdup();
extern char *getmailid();
extern char *dupfpath();

main (argc, argv)
    int argc;
    char *argv[];
{
    mmdf_init (argv[0]);
    user_init ();
    arg_init (argc, argv);

    if (gotargs)
	exit (OK);

    printf ("Welcome to the mailing list program\n");
    FOREVER
    {
	printf ("\nType 'h' for list of lists, 'c' to create a list,\n");      printf ("'q' to quit, or the name of the list you wish to adjust");
	printf ("\n\n>> ");
	fflush (stdout);
	if (gets (buf) == NULL)
		exit(OK);
	compress (buf, buf);
	if (strlen (buf) == 0)
	    continue;
	if (strlen (buf) == 1)
	switch (buf [0])
	{
	    case '\n':
		continue;
	    case 'q':
		printf ("Mail list program exiting\n");
		exit (OK);
	    case 'c':
		do_create ();
		continue;
	    case 'h':
		do_help ();
		continue;
	}
	do_list (buf);
    }
}
/**/

user_init ()
{
    struct passwd *pwd, *getpwuid();
    char *getmailid();
    int effid;

    umask (07000);                       /* set maks for creation */

    getwho (&realid, &effid);
    if (realid == effid)
	realid = 0;                     /*full priveledges to MMDF      */

    if ((pwd = getpwuid (realid)) == NULL ||
	(username = getmailid (pwd -> pw_name)) == NULL)
    {
	printf ("Problem identifying user\n");
	exit (NOTOK);
    }
    if (realid == 0)
	printf ("You are a mail super-user\n");

}
/**/


arg_init (argc, argv)
    int argc;
    char *argv[];
{
    register int ind;

    helpfile = dupfpath(basehelpfile, cmddfldir);

    gotargs = FALSE;
    for (ind = 1; ind < argc; ind++)
    {
	if (argv[ind][0] != '-')
	{
	    gotargs = TRUE;
	    do_list (argv[ind]);
	}
	else
	    switch (argv[ind][1])
	    {
		case 'f':
		    helpfile = strdup (argv[++ind]);
		    break;

		case 'c':
		    do_create ();
		    printf ("\nMail list program exiting\n");
		    exit (OK);

		case 'h':
		    do_help ();
		    printf ("\nMail list program exiting\n");
		    exit (OK);
		default:
		    printf ("Invalid argument\n");
		    printf ("usage is: mlist [-ch] [-f file] [arg ....]\n");
		    exit (NOTOK);
	    }
   }
}

/**/

do_help ()
{
    FILE *fp;

    signal (SIGINT, SIG_IGN);

    if ((fp = fopen (helpfile, "r")) == NULL)
    {
	printf ("can't open helpfile '%s'\n", helpfile);
	fflush (stdout);
	return;
    }

    while (fgets (buf, LINESIZE, fp) != NULL)
	fputs (buf, stdout);
    fflush (stdout);

    signal (SIGINT, SIG_DFL);
}


do_create ()
{

    ml_1adr (TRUE, FALSE, (char *) 0, "Mailing list creation request",
		supportaddr);
    ml_txt ("Name of list\n");
    printf ("Give name of list: ");
    fflush (stdout);
    if (gets (buf) == NULL)
	exit(OK);
    ml_txt (buf);
    ml_txt ("\n\nFunction:\n");
    printf ("Give a one line description of the list\n: ");
    fflush (stdout);
    if (gets (buf) == NULL)
	exit(OK);
    ml_txt (buf);
    ml_txt ("\n\nDistributed from:\n");
    printf ("Where is the list currently distributed from?\n: ");
    fflush (stdout);
    if (gets (buf) == NULL)
	exit(OK);
    ml_txt (buf);
    ml_txt ("\n\nMaintainers\n");
    printf ("Who will be responible for the list?\n");
    printf ("You can give multiple addresses, but one must be a local ");
    printf ("login\n: ");
    fflush (stdout);
    if (gets (buf) == NULL)
	exit(OK);
    ml_txt (buf);
    printf ("processing message....\n");
    fflush (stdout);
    if (ml_end (OK) != OK)
    {
       printf ("\nproblem processing rquest.  Try again later\n");
       return;
    }
    printf ("Your request has been passed to an administrator\n");
    printf ("You will be notified in a short time when the list has been created\n");
    printf ("You can then use this program to enter names into the list\n\n");
    fflush (stdout);
}

/**/
do_list (list)
char *list;
{
    char tmpbuf [LINESIZE];
    int ismanager;
    struct stat statbuf;
    char *p,
	*q;
    int fd;
    int i;

    strcpy (listname, list);

    ismanager = (realid == 0 ? TRUE : FALSE);
    sprintf (manager, "%s-request", listname);
    if (aliasfetch(TRUE, manager, buf, 0)==OK)
	strcpy (manager, supportaddr);
    else if (!ismanager)
    {
	p = buf;
	while ((q  = index (buf, ',')) != (char *) 0)
	{
	    *q++ = '\0';
	    if (lexequ (p, username))
	    {
		ismanager = TRUE;
		printf ("You are a manager of list '%s'\n", listname);
	    }
	    p = q;
	}
	if (lexequ (p, username))
	{
	    ismanager = TRUE;
	    printf ("You are a manager of list '%s'\n", listname);
	}
    }

    sprintf (tmpbuf, "%s-outbound", listname);
    if (aliasfetch(TRUE, tmpbuf, buf, 0) != OK &&
	aliasfetch(TRUE, listname, buf, 0) != OK)
    {
	printf ("list '%s' does not exist\n", listname);
	return;
    }

    if ((i = strindex (":include:", buf)) < 0)
    {
	printf ("alias '%s' is managed centrally\n", listname);
	printf ("do you wish to mail a request to change the list");
	if (confirm ())
	   u_req ();
	return;
    }

    q = index (&buf[i], '/');
    p = index (q, '@');
    if (p == (char *) 0)
    {
	if ((p = index (q, ',')) != (char *) 0)
	    *p = '\0';
	strcpy (filename, q);
    }
    else
    {
	*p++ = '\0';
	strcpy (filename, q);
	if ((q = index (p, ',')) != (char *) 0)
	    *q = '\0';
	if (ch_h2chan (p, 1) != OK)
	{
	    printf ("Alias '%s' is expanded on machine '%s'\n",
		listname,  p);
	    if (ismanager && realid != 0)
	    {
		printf ("Log in to '%s' and try again\n", p);
		fflush (stdout);
		return;
	    }
	    printf ("do you wish to mail a request to change the list");
	    if (confirm ())
	       u_req ();
	    return;
	}
    }


    if (stat (filename, &statbuf) < 0)
    {                           /* create filewith correct modes */
	if (!ismanager)
	{
	    printf ("File for list '%s' can only be created by manager\n",
			listname);
	    printf ("Do you want to send a message to the list manager");
	    if (confirm ())
	       u_req ();
	    return;
	}
	printf ("Manager creation of list file\n");
	printf ("Do you want any other users to be able to add / remove\n");
	printf ("their own names");

	if (confirm ())
	{
	   printf ("And anyone else's name");
	   if (confirm ())
		curmode = FREEMODE;
	   else
		curmode =  PUBMODE;
	}
	else
	{
	    printf ("Do you want other users to be able to see who is on the list");
	    if (confirm ())
		curmode = PRIVMODE;
	    else
		curmode = SECRMODE;
	}
	printf ("Creating file '%s'\n",  filename);
	if ((fd = creat (filename, curmode)) < 0)
	{
	    printf ("Unable to create filename '%s'\n", filename);
	    return;
	}
	close (fd);
    }
    else
    {
	curmode = (int) statbuf.st_mode;
	curmode = curmode & 0777;

	if (!(ismanager || curmode == PUBMODE || curmode == FREEMODE))
	{
	    printf ("list '%s' can only be updated by its manager\n",
			listname);
	   if (curmode != SECRMODE)
	   {
		printf ("do you want to see who is on the list");
		if (confirm ())
		{
		    if ((curfp = fopen (filename, "r")) == NULL)
		    {
			printf ("Can't open file '%s'\n", filename);
			return;
		    }
		    while (fgets (buf, LINESIZE, curfp) != NULL)
			fputs (buf, stdout);
		    fclose (curfp);
		}
	   }

	   printf ("do you wish to mail a request to be added");
	   if (confirm ())
	      u_req ();
	    return;
	}

	if ((curfp = fopen (filename, "rw")) == NULL)
	{
	   printf ("Can't open file '%s'\n", filename);
	   return;
	}
    }

    printf ("Adjusting list '%s'\n", listname);
    if (ismanager || curmode == FREEMODE)
	master_adj ();
    else
	u_adj ();

}

/**/
u_req ()
{
    ml_1adr (TRUE, FALSE, (char *) 0, "List change request", manager);
    ml_txt ("Automatic request for addition to list: ");
    ml_txt (listname);
    printf ("You may ask for confirmation of any changes requested.\n");
    printf ("Answering no to the next question will save the list ");
    printf ("administrator's time.\n");
    printf ("Do you require confirmation");
    if (confirm ())
	ml_txt ("\n\nConfirmation requested");
    else
	ml_txt ("\n\nConfirmation NOT requested");
    ml_txt ("\n\nNames:\n\n");
    printf ("sending request to list manager (%s)\n", manager);
    printf ("specify names to be added or removed from list '%s'\n",
		listname);
    printf ("end with .<CR> on a newline\n");
    while (fgets (buf, LINESIZE, stdin) != NULL)
	if (strlen (buf) == 2 && buf[0] == '.')
	    break;
	else
	    ml_txt (buf);
   printf ("Processing message....\n");
   fflush (stdout);
   if (ml_end (OK) != OK)
	printf ("problem sending request\n");
   else
   {
	printf ("Your request has been sent\n");
	printf ("It will be processed in the next few days\n");
   }
}

u_adj ()
{
    printf ("would you like a listing of this list");
    if (confirm ())
    {
	if ((curfp = fopen (filename, "r")) == NULL)
	{
	    printf ("Can't open file '%s'\n", filename);
	    return;
	}
	while (fgets (buf, LINESIZE, curfp) != NULL)
		fputs (buf, stdout);
	fclose (curfp);
    }

    printf ("Do you want to add or remove your name");
    if (!confirm ())
    {
	    printf ("Do you want to send a request to the list maintainer\n");
	    if (confirm ())
		u_req ();
	    else
		printf ("sorry, no other options for mortals\n");
	    return;
    }

    if (u_inlist (username))
    {
	printf ("You (%s) are already in this list\n",
		username);
	printf ("do you wish to be removed? ");
	if (confirm ())
	    u_remove (username);
    }
    else
    {
	printf ("You (%s) are not in this list\n",
		username);
	printf ("do you wish to be added? ");
	if (confirm ())
		u_add (username);
    }
}

/**/

master_adj ()
{
char tmpbuf [LINESIZE];

    v_init ();
    printf ("You are in list manager mode\n");
    FOREVER
    {
	printf ("print list (p), verify list (v), add user (a), remove ");
	printf ("user (r), quit (q)?\n");
	printf ("default is assumed to be user name to be added\n\n>>> ");
	fflush (stdout);
	if (gets (tmpbuf) == NULL){
		v_end ();
		exit(OK);
	}
	compress (tmpbuf, tmpbuf);
	tmpbuf [0] = uptolow (tmpbuf[0]);
	switch (tmpbuf[0])
	{
	    case '\0':
	    case '\n':
		continue;
	    case 'q':
		v_end ();
		return;
	    case 'p':
		if ((curfp = fopen (filename, "r")) == NULL)
		{
		    printf ("Can't open file '%s'\n", filename);
		    return;
		}
		while (fgets (tmpbuf, LINESIZE, curfp) != NULL)
			fputs (tmpbuf, stdout);
		fclose (curfp);
		break;
	    case 'v':
		v_list ();
		break;
	    case 'a':
		printf ("give username to be added: ");
		if (gets (tmpbuf) == NULL){
			v_end ();
			exit(OK);
		}
		compress (tmpbuf, tmpbuf);
		if (u_inlist (tmpbuf))
		    printf ("User '%s' already in list\n", tmpbuf);
		else
		    if (verify (tmpbuf))
			u_add (tmpbuf);
		    else
			printf ("Illegal address '%s'\n", tmpbuf);
		break;

	    case 'r':
		printf ("give username to be removed: ");
		if (gets (tmpbuf) == NULL){
			v_end ();
			exit(OK);
		}
		compress (tmpbuf, tmpbuf);
		if (!u_inlist (tmpbuf))
		    printf ("User '%s' not in list\n", tmpbuf);
		else
		    u_remove (tmpbuf);
		break;

	    default:
		if (u_inlist (tmpbuf))
		    printf ("User '%s' already in list\n", tmpbuf);
		else
		    if (verify (tmpbuf))
		    {
			u_add (tmpbuf);
		    }
		    else
			printf ("Illegal address '%s'\n", tmpbuf);
		break;

	}
    }
}

/**/
u_inlist (user)
char *user;
{

    if ((curfp = fopen (filename, "r")) == NULL)
    {
	printf ("Can't open file '%s'\n", filename);
	return (FALSE);
    }
    while (fgets (buf, LINESIZE, curfp) != NULL)
    {
	buf [strlen(buf) - 1] = '\0';
	if (lexequ (user, buf))
	{
	    fclose (curfp);
	    return (TRUE);
	}
    }
    fclose (curfp);
    return (FALSE);
}


u_add (user)
char *user;
{

    if ((curfp = fopen (filename, "a")) == NULL)
    {
       printf ("Can't open file '%s'\n", filename);
       return;
    }
    printf ("adding user '%s'\n", user);
    fputs (user, curfp);
    fputc ('\n', curfp);
    fclose (curfp);
}

u_remove (user)
char *user;
{
    char *template = "al.XXXXXX";
    char fpath [LINESIZE];
    char *cp;
    FILE  *fp;
    int  fd;

			    /* first make temp file in same dir */
    strcpy (fpath, filename);
    cp = rindex (fpath, '/');
    if (cp++ == 0)
	fpath [0] = '\0';
    else
	*cp = '\0';
    strcat (fpath, template);
    mktemp (fpath);
    if ((fd = creat (fpath, curmode)) < 0)
    {
	printf ("Can't create temp file '%s'\n", fpath);
	return;
    }
    if ((fp = fdopen (fd, "w")) == NULL)
    {
	printf ("Can't open temp file '%s'\n", fpath);
	return;
    }


    if ((curfp = fopen (filename, "r")) == NULL)
    {
	printf ("Can't open file '%s'\n", filename);
	return;
    }
    while (fgets (buf, LINESIZE, curfp) != NULL)
    {
	buf [strlen(buf) - 1] = '\0';
	if (lexequ (buf, user) && strlen (buf) == strlen (user))
	    printf ("removing user '%s'\n", buf);
	else
	{
	    fputs (buf, fp);
	    fputc ('\n', fp);
	}
    }
    fclose (curfp);
    fclose (fp);
    unlink (filename);
    link (fpath, filename);
    unlink (fpath);
}


v_list ()                       /* go throughlist and verify    */
{
    int  donesofar;
    int i;
    char tmpbuf [LINESIZE];


    if ((curfp = fopen (filename, "r")) == NULL)
    {
       printf ("Can't open file '%s'\n", filename);
       return;
    }
    donesofar = 0;
    while (fgets (tmpbuf, LINESIZE, curfp) != NULL)
    {
	tmpbuf [strlen(tmpbuf) - 1] = '\0';
	if (!verify (tmpbuf))
	{
	    printf ("Remove user '%s'", tmpbuf);
	    if (confirm ())
	    {
		fclose (curfp);
		u_remove (tmpbuf);
		if ((curfp = fopen (filename, "r")) == NULL)
		{
		    printf ("Can't open file '%s'\n", filename);
		    return;
		}
		for (i = 0; i < donesofar; i++)
		    if (fgets (tmpbuf, LINESIZE, curfp) == NULL)
			continue;
		continue;
	    }
	}
	donesofar++;
    }
}


/**/
				/* address check stuff          */


v_init ()
{
	struct rp_bufstruct thereply;
	int     len;

	if (rp_isbad(mm_init()) || rp_isbad(mm_sbinit()) ||
	    rp_isbad(mm_winit ((char *)0, "vm", "mmdf")) ||
	    rp_isbad(mm_rrply( &thereply, &len ))) {
		printf("Cannot initialize address checking\n");
		mm_end (NOTOK);
		exit( 8 );
	} else if (rp_isbad(thereply.rp_val)) {
		printf ("verify: %s\n", thereply.rp_line);
		mm_end (NOTOK);
		exit(9);
	}

}

v_end ()
{
	mm_end (NOTOK);
}

verify (addr)
char    *addr;
{
	struct rp_bufstruct thereply;
	int     len;

	printf("%s: ", addr);
	fflush(stdout);

	if (rp_isbad (mm_wadr ((char *)0, addr))) {
		if( rp_isbad( mm_rrply( &thereply, &len ))) {
			printf ("Mail system problem\n");
			mm_end (NOTOK);
			exit( 8 );
		} else {
			printf ("%s\n", thereply.rp_line);
			return (FALSE);
		}

	} else {
		if( rp_isbad( mm_rrply( &thereply, &len ))) {
			printf ("Mail system problem\n");
			mm_end (NOTOK);
			exit (7);
		} else if( rp_isbad( thereply.rp_val )) {
			printf ("%s\n", thereply.rp_line);
			return (FALSE);
		} else {
			printf ("OK\n");
			return (TRUE);
		}
	}
	/*NOTREACHED*/
}

/**/
				/*  various utilities                   */

confirm ()
{
    register char   c;


    printf (" [Confirm] ");
    fflush (stdout);

    c = ttychar ();

    switch (c)
    {
	case 'Y':
	case 'y':
	case '\n':
	    printf ("yes\r\n");
	    fflush (stdout);
	    return (TRUE);

	case 'N':
	case 'n':
	    printf ("no\r\n");
	    return (FALSE);
	default:
	    printf ("Type yes or no.  <CR> defaults to yes\n");
	    return (confirm ());
    }
}


ttychar ()
{
    register int    c;

    fflush (stdout);
    fgets (buf, LINESIZE,  stdin);
    c = buf[0];

    c = toascii (c);    /* get rid of high bit */

    if (c == '\r')
	c = '\n';

    return (c);
}
, LINESIZE, curfp) != NULL)
    {
	buf [strlen(buf) - 1] = '\0';
	if (lexequ (user, buf))
	{
	    fclose (curfp);
	    return (TRUE);
	}
    }
    fclose (curfp);
    return (FALSE);
}


u_add (user)
char *user;
{

    if ((curfp = fopen (filename, "a")) == NULL)
    {
   mmdf/uip/other/msgstrip.c   444      0     12        4206  3620510614  10702 /* msgstrip:  remove lines of control-A and all message header text       */

#include "util.h"
#include "mmdf.h"

extern char *delim1;

FILE *tmpout,
     *filin;
int msgnum;
int filnum;

char    tmpfil[] = ",msgstrip";

char tmpstr[64];
char    buf[512];
char doingheader;
char hadtext;


main (argc, argv)
int     argc;
char   *argv[];
{
    char  *thefile;

    mmdf_init (argv[0]);
    for (argc--; filnum++ < argc; )
    {                             /* process a message file/folder        */
	printf ("%s: ", thefile = argv[filnum]);
	if ((tmpout = fopen (tmpfil, "w")) == NULL)
	    endit ("Unable to creat temp");

	if ((filin = fopen (thefile, "r")) == NULL)
	{
	    printf ("Unable to open file\n");
	    continue;
	}

	for (msgnum = 1; !feof (filin); )
	{                         /* process a message                    */
	    for (doingheader = TRUE, hadtext = FALSE;  ; )
		switch (fgets (buf, sizeof buf, filin))
		{                 /* process a line                       */
		    case NULL:
			if (ferror (filin))
			    printf ("Error reading during header skip\n");
			goto nxtmsg;

		    default:
			if (doingheader && buf[0] == '\n')
			{   doingheader = FALSE;
			    continue;
			}         /* drop on through                      */
			if (strcmp (buf, delim1) == 0)
			    goto nxtmsg;
			if (doingheader)
			    continue;
			hadtext = TRUE;
			fputs (buf, tmpout);
		}
nxtmsg:
	    if (hadtext)
		printf ("%d, ", msgnum++);
	}

	fflush (tmpout);
	if (ferror (tmpout))
	    endit ("Error writing");
	sprintf (tmpstr, ",%s", thefile);

	if (link (thefile, tmpstr) < 0 &&
		unlink (tmpstr) &&
		link (thefile, tmpstr) < 0)
	    endit ("Unable to link to backup");
	if (unlink (thefile) < 0)
	    endit ("Unable to unlink file");
	if (link (tmpfil, thefile) < 0)
	    endit ("Unable to link temporary into old file name");
	if (unlink (tmpfil) < 0)
	    endit ("Unable to unlink temporary file");
	sprintf (tmpstr, ".{%s", thefile);
	unlink (tmpstr);          /* get rid of parsefile                 */
	printf ("ok\n");

	fclose (tmpout);
	fclose (filin);
    }
}

endit (str)
char    str[];
{
    printf ("%s\n", str);
    fflush (stdout);
    exit (-1);
}
m_winit ((char *)0, "vm", "mmdf")) ||
	    rp_isbad(mm_rrply( &thereply, &len ))) {
		printf("Cannot initialize address checking\n");
		mm_end (NOTOK);
		exit( 8 );
	} else if (rp_isbad(thereply.rp_val)) {
		printf ("verify: %s\n", thereply.rp_line);
		mm_end (NOTOK);
		exit(9);
	}

}

v_end ()
{
	mm_end (NOTOK);
}

verify (addr)
char    *addr;
{
	struct rp_bufstruct thereplymmdf/uip/other/rcvalert.c   444      0     12       13343  3664425216  10711 #
/*
 *  RCVALERT personal receive-mail program
 *
 *  NOTE:  This is a USER program & is provided as a demonstration
 *         of an MMDF feature.  It is NOT part of the MMDF "package".
 *
 *  Notifies user of new mail, if user logged in, by printing scan line
 *  for the message on the user's terminal.
 *
 *  Does NOT actually deliver message.
 *
 *  Oct/79 David Crocker  Dept. of E.E., Univ. of Delaware
 *  Jun/80 David Crocker  Notify ALL tty's logged in as recipient
 *  Dec/80 David Crocker  Convert to stdio & v7
 *  Apr/83 Doug Kingston  Modified for BRL (mostly fixes)
 *  Apr/84 Doug Kingston  Modified for use as filter (rcvmail expunged)
 */
/**/

#undef	STDALONE /* run standalone, for debugging        */

#include "util.h"
#include "mmdf.h"
#include <pwd.h>

#define TOSHORT 16		  /* subject shorter than this => search  */
				  /*   body for chars to add to subject   */
#define SUBLEN  37		  /* length of subject & from fields      */
#define FROMLEN 15
#define UNSET   0		  /* nothing added to subject from body   */
#define SET     1		  /* something "    "    "     "    "     */

extern LLog *logptr;
extern char *compress();
extern FILE *tty_fp;

char    inbuf[BUFSIZ];

struct
{
    long    msglen;
    char    from[FROMLEN];
    char    subject[SUBLEN];
} scanl;			/* the line to be printed             */

int     sublen;                   /* chars in subject field of scan     */

char	username[10];		  /* string system name of recipient    */

/* *****************************  MAIN  **********************************/

main (argc, argv)
int     argc;
char   *argv[];
{
#ifndef STDALONE
    if (fork () != 0)		  /* let ch_local continue ASAP & do    */
	exit (RP_MECH);		  /*   actual delivery.                 */
				  /* child will do notification         */
#endif

    init(argc,argv);

    scanbld ();

    alert ();
    exit (0);
}

/**/
init(argc,argv)
int argc;
char **argv;
{
    if (argc > 1)
	scanl.msglen = atoi(argv[1]);
    else
	scanl.msglen = -1;

    mmdf_init ("ALERT");
    siginit ();

    setbuf (stdin, inbuf);

    guinfo ();			  /* who 'dis?                            */


}

/**/

guinfo ()			  /* get user name & login directory    */
{
    extern struct passwd *getpwuid ();
    register struct passwd *pwdptr;
    int     realid,
	    effecid;

    getwho (&realid, &effecid);
    pwdptr = getpwuid (effecid);

    strcpy (username, pwdptr -> pw_name);

#ifdef DEBUG
    ll_log (logptr, LLOGFTR, "id=%d, u='%s'", effecid, username);
#endif
}
/**/

alert ()                     /* send message to user's tty           */
{
    extern int errno;

    tty_init();

    for (;;)
    {
	if (tty_find (username) < OK)
	{
	    if (errno == EBUSY)
		continue;
	    else
		break;
	}

	fprintf (tty_fp, "\007\r\nNEW:  (");
	if (scanl.msglen < 0)
	    fprintf (tty_fp, " ???");
	else
	    if (scanl.msglen > 99999)
		fprintf (tty_fp, " BIG");
	    else
		fprintf (tty_fp, "%5ld", scanl.msglen);

	fprintf (tty_fp, ")  %-15s %s\r\n", scanl.from, scanl.subject);

	tty_close ();
    }

    tty_end ();
    return;
}
/**/

scanbld ()
{				  /* build scan line for tty              */
    char    tempbuf[512];
    register int    len;
    register char   addflag;

    sublen = 0;

    for (rewind (stdin); ;)
    {				  /* parse the headers                    */
	if (fgets (tempbuf, sizeof tempbuf, stdin) == NULL)
	    return;
	len = strlen (tempbuf) - 1;
	if (tempbuf[0] == '\n')
	    break;		  /* we done hit d' body                  */
	tempbuf[len] = '\0';

	if (isspace (tempbuf[0]))
	    continue;		  /* continuation line                    */

#ifdef DEBUG
	ll_log (logptr, LLOGFTR, "1: '%s'", tempbuf);
#endif
	if (equal ("from", tempbuf, 4))
	    getfrom (&tempbuf[4]);
	if (equal ("subject", tempbuf, 7))
	    getsub (&tempbuf[7]);
    }

    addflag = UNSET;		  /* not yet added any body to subject    */
    if (sublen < TOSHORT)	  /* see if subject should be added to    */
    {				  /*    from the body                     */
	while (sublen < (SUBLEN - 10) &&
		fgets (tempbuf, sizeof tempbuf, stdin) != NULL &&
		((len = strlen (tempbuf)) - 1) > 0)
	{
	    tempbuf[len - 1] = '\0';
				  /* get rid of newline                 */
#ifdef DEBUG
	    ll_log (logptr, LLOGFTR, "2:'%s'", tempbuf);
#endif
	    if (sublen > 0 && scanl.subject[sublen] != ' ')
				  /* separate from preceding text       */
		scanl.subject[sublen++] = ' ';

	    if (addflag == UNSET)
	    {
		scanl.subject[sublen++] = '(';
		addflag = SET;    /* we now have body text added        */
	    }

	    getsub (tempbuf);
	}
    }

    if (addflag == SET)
	strcpy (&(scanl.subject[sublen]), (len > 0) ? "...)" : ")");
    else
	scanl.subject[sublen] = '\0';
}
/**/

getfrom (src)                      /*  get name of sender                  */
register char  *src;
{
    register int    n;
    register char  *dest;

    while (*src++ != ':');        /* skip over to data                  */
    compress (src, src);

#ifdef DEBUG
    ll_log (logptr, LLOGFTR, "getfrom from '%s'", src);
#endif

    for (dest = scanl.from, n = (sizeof scanl.from); --n > 0;
	    *dest++ = *src++)
    {
	switch (*src)
	{
	    case '\0': 
	    case '\n': 
	    case '(':
	    case '<': 
	    case '@': 
	    case '%':
		break;

	    case ' ':
		if (equal (" at ", src, 4))
		    break;
	    default:
		continue;
	}
	break;
    }
}

getsub (src)			  /* get first part of subject            */
register char  *src;
{
    register char  *dest;

    while (isspace (*src))         /* skip over to data                  */
	src++;
    if (*src == ':')
	src++;
    compress (src, src);          /* eliminate leading white space        */

#ifdef DEBUG
    ll_log (logptr, LLOGFTR, "getsub from '%s'", src);
#endif

    for (dest = &(scanl.subject[sublen]);
	    sublen < (SUBLEN - 5) && !isnull (*src);
	    *dest++ = *src++, sublen++);
}
 will do notification         */
#endif

    init(argc,argv);

    scanbld ();

    alert ();
    exit (0);
}

/**/
init(argc,argv)
int argc;
char **argv;
{
    if (argc > 1)
	scanl.msglen = atoi(argv[1]);
    else
	scanl.msglen = -1;

    mmdf_init ("ALERT");
    siginit ();

    semmdf/uip/other/rcvprint.c   444      0     12        2127  3620510614  10701 /***************************************************************\
* 							        *
* 	Rcvprint - a program to take a mail message as input    *
* 	and print it on a line printer			        *
* 							        *
* 	A really trivial little program.		        *
* 	Rewritten to clean up				        *
* 		Jpo@cs.nott.ac.uk 		19/2/84	        *
* 							        *
\***************************************************************/

#include "util.h"
#include "mmdf.h"

int childid;
int retval;
char linebuf[LINESIZE];

main ()
{
    int pid;

    while( fgets(linebuf, sizeof(linebuf), stdin) != NULL &&
    			linebuf[0] != '\n')
		/* do nothing */;
    if ((childid = fork ()) == 0)
    {
				/* hunt around for a printer program */
	execlp("lpr", "rcvlpr", (char *)0);
	execlp("opr", "rcvopr", (char *)0);
	perror ("rcvopr exec error");
	exit (-1);
    }
    if( childid == -1)
    	exit(RP_MECH);

    while ((pid = wait(&retval)) != childid)
	if (pid == -1)
	    exit(RP_AGN);
	
    exit ((retval&0377) ? RP_AGN : 0);
				  /* say "delivered" only if opr ends   */
				  /* cleanly                            */
}
, src);
#endif

    for (dest = &(scanl.subject[sublen]);
	    sublen < (SUBLEN - 5) && !isnull (*src);
	    *dest++ = *src++, sublen++);
}
 will do notification         */
#endif

    init(argc,argv);

    scanbld ();

    alert ();
    exit (0);
}

/**/
init(argc,argv)
int argc;
char **argv;
{
    if (argc > 1)
	scanl.msglen = atoi(argv[1]);
    else
	scanl.msglen = -1;

    mmdf_init ("ALERT");
    siginit ();

    semmdf/uip/other/checkmail.c   444      0     12       10772  3620510614  10777 /*			CHECKMAIL
 *
 *  Checks mail queues for messages from invoker (or all for mail su's)
 *
 *  Oct 84  H. Walter		initial version
 *  Nov 85  C. Partridge	fix arginit() to not dereference 0!
 *  Dec 85  D. Kingston		swallowed functions of checkmsgs
 */

#include "util.h"
#include "mmdf.h"
#include <sys/stat.h>
#include <pwd.h>
#include "ch.h"
#include "msg.h"
#include "adr_queue.h"

extern char *quedfldir,            /* home directory for mmdf            */
	    *aquedir,              /* subordinate address directory      */
	    *mquedir;		   /* subordinate msg directory		 */
	    
DIR	*ovr_dirp;                  /* handle on the queue directory      */
char    msg_sender[ADDRSIZE + 1]; /* return address of current message  */
int	realuid;		/* Invoker uid */
int	alladdrs = 0;
int	allmsgs = 0;
int	fast = 0;
char    bufout[BUFSIZ];

main (argc, argv)
int       argc;
char   *argv[];
{
    setbuf (stdout, bufout);
    siginit ();                   /* standard interrupt initialization  */
				  /* distinguish different delivers     */
    arginit (argc, argv);

    /* get the real uid */
    realuid = getuid();

    if (allmsgs)
    	setuid(realuid);	  /* Let unix protection do the hard work */

    mmdf_init (argv[0]);
    mn_dirinit ();                /* get right working directory        */

    ovr_queue ();                 /* do the entire mail queue           */

    exit (RP_OK);                 /* "normal" end, even if pgm_bakgrnd  */
}

LOCFUN
arginit (argc, argv)
int	argc;
char	**argv;
{
	register int i;
	register char *cp;

	for (i = 1; i < argc; i++) {
		cp = argv[i];
		if (*(cp++) != '-')
			goto usage;
		while (*cp != '\0')
			switch (*(cp++)) {
			case 'a':
				alladdrs++;
				break;
			case 'f':
				fast++;		/* Don't get subject line */
				break;
			case 'm':
				allmsgs++;
				break;
			default:
				goto usage;
			}
	}
	return;

usage:	fprintf(stderr, "Usage: checkmail [-afm]\n");
	exit(1);
}

LOCFUN
mn_dirinit ()		  /* get to the working directory       */
{
    if (chdir (quedfldir) < OK)    {
	printf ("can't chdir to %s\n", quedfldir);
	exit (-1);
    }
}

ovr_queue ()			  /* Process entire message queue       */
{
    struct direct *dp;

    if ((ovr_dirp = opendir (aquedir)) == NULL)    {
	printf ("can't open address queue\n");
	exit (-1);
    }

    while ((dp = readdir(ovr_dirp)) != NULL)    {
                         /* straight linear processing         */
	if (equal ("msg.", dp->d_name, 4)) {
			msg_proc (dp->d_name);
	}
    }

    closedir (ovr_dirp);
}

extern int    errno;                /* system error number                */

/*  msg_cite() is defined in adr_queue.h                                */

msg_proc (thename)                /* get, process & release a msg       */
    char thename[];
{
    Msg themsg;
    char msgname[LINESIZE];
    char linebuf[LINESIZE];
    struct stat sb;
    FILE *msg_tfp;
    
    strcpy (themsg.mg_mname, thename);
    sprintf (msgname, "%s%s", mquedir, thename);

    if( stat( msgname, &sb) < 0 ) {
	perror(msgname);
	return;
    }
    /* Invoker's file? */
    if( allmsgs || sb.st_uid == realuid )
	if (mq_rinit ((Chan *) 0, &themsg, msg_sender) == OK)    {
		printf ("Message %s: posted %.24s\n",
			 thename, ctime (&themsg.mg_time));
		if (allmsgs)
			printf ("Return address: %s\n", msg_sender);
		if (!fast) {
			if ((msg_tfp = fopen (msgname, "r")) == NULL)
				printf("can't open msg file '%s'\n",msgname);
			else {
				while (fgets (linebuf, sizeof linebuf, msg_tfp) != NULL) {
					if( prefix("subject:",linebuf) == TRUE) {
						printf("%s",linebuf);
						break;
					}
				}
				fclose (msg_tfp);
			}
		}
		adr_each ();
		mq_rkill (OK);
	} else {
		printf ("Message %s:  busy\n", thename);
	}
}

adr_each ()			  /* do each address                    */
{
    struct adr_struct theadr;

    for ( ;; )
	switch (mq_radr (&theadr))	{
	    case NOTOK: 	  /* rest of list must be skipped       */
	    case DONE:	          /* end of list                        */
	    default:
		printf("\n");
		return;

	    case OK:
	        if (theadr.adr_delv != ADR_DONE) 	{
		    if (theadr.adr_host && theadr.adr_host[0] != (char) '\0')
		    	printf("   %s (via %s): not yet sent\n",
				theadr.adr_local, theadr.adr_host);
		    else
		    	printf ("   %s: not yet sent\n", theadr.adr_local);
		    fflush (stdout);
		} else if (alladdrs) {
		    if (theadr.adr_host && theadr.adr_host[0] != (char) '\0')
		    	printf("   %s (via %s): sent\n",
				theadr.adr_local, theadr.adr_host);
		    else
		    	printf ("   %s: sent\n", theadr.adr_local);
		    fflush (stdout);
		}
	}
}
 addremmdf/uip/other/rcvtrip.c   444      0     12       41165  3620510615  10551 /*
 *     MULTI-CHANNEL MEMO DISTRIBUTION FACILITY  (MMDF)
 *
 *
 *     Copyright (C) 1979,1980,1981  University of Delaware
 *
 *     Department of Electrical Engineering
 *     University of Delaware
 *     Newark, Delaware  19711
 *
 *     Phone:  (302) 738-1163
 *
 *     This program module was developed as part of the University
 *     of Delaware's Multi-Channel Memo Distribution Facility (MMDF).
 *
 *     This module and its listings may be used freely by United States
 *     federal, state, and local government agencies and by not-for-
 *     profit institutions, after sending written notification to
 *     Professor David J. Farber at the above address.
 *
 *     For-profit institutions may use this module, by arrangement.
 *
 *     Notification must include the name of the acquiring organization,
 *     name and contact information for the person responsible for
 *     maintaining the operating system, name and contact information
 *     for the person responsible for maintaining this program, if other
 *     than the operating system maintainer, and license information if
 *     the program is to be run on a Western Electric Unix(TM) operating
 *     system.
 *
 *     Documents describing systems using this module must cite its
 *     source.
 *
 *     Development of this module was supported in part by the
 *     University of Delaware and in part by contracts from the United
 *     States Department of the Army Readiness and Materiel Command and
 *     the General Systems Division of International Business Machines.
 *
 *     Portions of the MMDF system were built on software originally
 *     developed by The Rand Corporation, under the sponsorship of the
 *     Information Processing Techniques Office of the Defense Advanced
 *     Research Projects Agency.
 *
 *     The above statements must be retained with all copies of this
 *     program and may not be removed without the consent of the
 *     University of Delaware.
 *
 *
 *     version  -1    David H. Crocker    March  1979
 *     version   0    David H. Crocker    April  1980
 *     version   1    David H. Crocker    May    1981
 *
 */

/*
 *                              RCVTRIP
 *
 *      Rcvtrip responds to incoming mail, recording who sent it, what the
 *              subject was, when it came in, and if it was replied to.
 *
 *      The reply consists of some standard text, followed by any message
 *              which the user wishes to specify.
 *
 *      Oct, 80     Bruce Whittaker         Initial coding and
 *       - May, 81   (Whittake@Udel-EE)       maintenance
 *
 *	Sep  84 Julian Onions  jpo@nott.ac.uk
 *		Total rewrite from the ground upwards, original ideas
 *		kept, but now makes use of the mmdf library routines,
 *		mmdf address parser and alias look up. This should
 *		mean future changes will be easier to deal with. (?!)
 *
 *	Feb  85 Julian Onions  jpo@cs.nott.ac.uk
 *		Rehacked to take into account weirdo addresses
 *		+ normali[zs]ed domain.s
 *
 */

# include	"util.h"
# include	"mmdf.h"
# include	"ap.h"
# include	"ch.h"
# include	"hdr.h"
# include	<pwd.h>

# define	ANIL		((AP_ptr)0)
# define	NILC		((char *)0)

# define	NONFATAL	0	/* Nonfatal reports		*/
# define	FATAL		1	/* Fatal reports (exit)		*/
# define	TRACE		2	/* General info for debug	*/

# define	SENT		0	/* Sent out a reply		*/
# define	UNSENT		1	/* No reply sent - just note	*/

# define	REPLIED		'+'	/* A reply was sent		*/
# define	NOT_REPLIED	'-'	/* A reply wasn't sent		*/

extern Llog    *logptr;
extern int     ap_outtype;

extern char    *index ();
extern char    *strcpy ();
extern char    *strncpy ();
extern char    *fgets ();
extern char    *strcat ();
extern char    *compress ();
extern char    *ap_p2s ();
extern char    *ap_s2p ();
extern char    *getmailid();
extern AP_ptr  ap_t2s ();

int	debug	=	FALSE;		/* output trace messages?	   */
int	found_1 =	FALSE;		/* true if message was sent to	   */
					/* directly to recipient	   */
char	subj_field[ADDRSIZE];		/* contents of subject field	   */
char	username[20];			/* the users username		   */
char	logloc []	= "logfile";	/* where to log error conditions   */
char	msg_note []	= "tripnote";	/* where to get reply message from */
char	wholoc []	= "triplog";	/* where to note incoming messages */
char	sigloc []	= ".signature"; /* where to find user's name	   */
char	tripsig []	= "MESSAGE AGENT";
					/* signature of the returned message*/

AP_ptr  from_adr=	ANIL;		/* address tree for from address   */
AP_ptr  sndr_adr=	ANIL;		/*     - " -	    sender	   */
AP_ptr  repl_adr=	ANIL;		/*     - " -	    reply-to	   */

char	*find_sig();

struct passwd	*getpwuid();

/*
 *	This message is output before the users file message
 */
char	*text_message[] = {
	"\tThis is an automatic reply.  Feel free to send additional\n",
	"mail, as only this one notice will be generated.  The following\n",
	"is a prerecorded message, sent for ", 0
};

/*
 *	This message is output if the users file is not readable or doesn't
 *	exist. (This is where you can have some fun!)
 */
char	*error_txt[] = {
	"\tIt appears that the person in question hasn't left a\n",
	"message file, so the purpose of this message is unknown.\n",
	"However it is assumed that as I, a daemon process,\n",
	"have been called into existance that the person in question\n",
	"is probably away for some time and won't be replying to your\n",
	"message straight away.\n\nCheers,\n\tThe Mail System\n", 0
};

main (argc, argv)
int     argc;
char  **argv;
{
	init (argc, argv);
	parsemsg ();
	doreplies ();
	exit( RP_MECH );
}

/*
 *	Initialisation - get user, setup flags etc.
 */

init (argc, argv)
int     argc;
char  **argv;
{
	register struct  passwd  *pwdptr;
	int	realid, effecid;
	char	*cp;

	ap_outtype = AP_822;		/* standardise on 822 */
	argv ++;
	if (argc > 1 && strcmp(*argv, "-d") == 0)
	{
		argv++;
		argc--;
		debug++;
	}

	if( argc > 1 )  /* we have a command line sender address */
		parse_addr(*argv, &sndr_adr);

	mmdf_init ("TRIP");

	getwho (&realid, &effecid);
	if((pwdptr = getpwuid (realid)) == (struct passwd *)0
	  || (cp = getmailid (pwdptr -> pw_name)) == NULL)
		report( FATAL, "Can't Identify user %d", realid);
	else
		strcpy (username, cp);
}

/*
 *	parse message - just a front end to the real parser
 */

parsemsg()
{
	if( rp_isbad(hdr_fil(stdin)) )
		report( FATAL, "parse of message failed");
}

/*************** Routines to despatch messages ***************/

/*
 *	Send replies to reply-to address if specified else
 *		from and sender address.
 */

doreplies()
{
	if( found_1 != TRUE )
		report( FATAL, "Not sent directly to %s", username);

	if (from_adr == ANIL && sndr_adr == ANIL)
		report( FATAL, "No parsable from or sender lines");

/*
 *	If there were any reply-to lines, use those in preference.
 */
	if( repl_adr != ANIL )
		replyto( repl_adr );
	else	/*	send to both from && sender if they exist */
	{
		if (from_adr != ANIL)
			replyto( from_adr );
		if (sndr_adr != ANIL)
			replyto( sndr_adr );
	}
}

/*
 *	Actually do the work of sending the reply.
 */

replyto( aptr )
AP_ptr aptr;
{
	register char	 *cp;
	register char	 **cpp;
	AP_ptr  user, local, domain;
	AP_ptr	norm;
	char	*addr;
	char	*ref;
	char	*person;
	char	buffer[ADDRSIZE];
	FILE	*fp;

#ifdef	DEBUG
	report( TRACE, "replyto()");
#endif	DEBUG

	norm = ap_normalize( NILC, NILC, aptr, (Chan *)0);
	ap_t2parts(norm, (AP_ptr *)0, &user, &local, &domain, (AP_ptr *)0);
	ref = ap_p2s(ANIL, ANIL, local, domain, ANIL);
#ifdef	DEBUG
	report( TRACE, "normed addr = '%s'", ref);
#endif	DEBUG

	if( user != ANIL)
		person = user-> ap_obvalue;
	else	person = NILC;

	if( checkuser( ref ) )
	{
#ifdef	DEBUG
		report( TRACE, "Seen %s before", ref);
#endif	DEBUG
		noteuser( ref, UNSENT );
		free( ref );
		return;
	}
	if( ap_t2s(norm, &addr) == (AP_ptr)NOTOK)
	{
		report( NONFATAL, "Parse error for %s", addr);
		noteuser( ref, UNSENT);
		free( ref );
		return;
	}
	buffer[0] = '\0';
	if( ! prefix("re:", subj_field) )
		strcpy( buffer, "Re: ");

	strcat( buffer, subj_field);

#ifdef	DEBUG
	report(TRACE, "ml_1adr(NO, NO, '%s' %s, %s)",
			tripsig, buffer, addr);
#endif	DEBUG

	ml_1adr( NO, NO, tripsig, buffer, addr);

	if( person != NILC)
	{
		compress(person, person);
		sprintf( buffer, "\nDear %s,\n", person);
#ifdef	DEBUG
		report( TRACE, "1st line %s", buffer);
#endif	DEBUG
		ml_txt( buffer );
	}

#ifdef	DEBUG
	report( TRACE, "start builtin message");
#endif	DEBUG
	for( cpp = text_message; *cpp != NILC ; cpp++)
		ml_txt( *cpp );
	if(( cp = find_sig()) != NILC)
		ml_txt( cp );
	else	ml_txt( username );
	ml_txt("\n\n\n");

	if( (fp = fopen( msg_note, "r")) != (FILE *)0)
	{
#ifdef	DEBUG
		report( TRACE, "processing file %s", msg_note);
#endif	DEBUG
		ml_file(fp);
		fclose(fp);
	}
	else
	{
#ifdef	DEBUG
		report( TRACE, "Sending built in error message");
#endif	DEBUG
		for( cpp = error_txt; *cpp != NILC; cpp++)
			ml_txt(*cpp);
	}
#ifdef	DEBUG
	report( TRACE, "ml_end(OK)");
#endif	DEBUG
	if( ml_end(OK) == OK)
		noteuser(ref, SENT);
	else	noteuser(ref, UNSENT);
	free( ref );
	free( addr );
}

/*************** Routines to pick apart the message ***************/

/*
 *	The actual picking out of headers
 */

hdr_fil (msg)
FILE * msg;			/* The message file			 */
{
	char    line[LINESIZE];	/* temp buffer			 */
	char    name[LINESIZE];	/* Name of header field		 */
	char    contents[LINESIZE];/* Contents of header field	 */
	int     len;

#ifdef	DEBUG
	report (TRACE, "hdr_fil()");
#endif	DEBUG
	if (msg == (FILE *) NULL)
	{
		report( FATAL, "NULL file pointer");
		return (RP_NO); /* not much point doing anything else */
	}
/* process the file    */

	FOREVER
	{
		if ((len = gcread (msg, line, LINESIZE, "\n\377")) < 1)
		{
			report( NONFATAL, "read error on message");
			return(RP_NO);  /* skip and go home */
		}
		line[len] = '\0';

		switch (hdr_parse (line, name, contents))
		{
		case HDR_NAM: /* No name so contine through header */
			continue;

		case HDR_EOH: /* End of header - lets go home */
			return(RP_OK);

		case HDR_NEW:
		case HDR_MOR: /* We have a valid field	    */
			if (lexequ (name, "to") ||
				lexequ(name, "cc") ||
				lexequ(name, "resent-to") ||
				lexequ(name, "resent-cc") )
			{
				find_user(contents);
				break;
			}
			else	if (lexequ (name, "from"))
			{
				parse_addr(contents, &from_adr);
				break;
			}
			else	if (lexequ (name, "reply-to"))
			{
				parse_addr(contents, &repl_adr);
				break;
			}
			else	if (lexequ (name, "sender"))
			{
				parse_addr(contents, &sndr_adr);
				break;
			}
			else	if (lexequ (name, "subject"))
			{
				strncpy(subj_field, contents, ADDRSIZE-1);
				break;
			}
			else
				continue;
		}
	}
}

/*
 *	The real parser
 */

LOCFUN
hdr_parse (src, name, contents)	/* parse one header line		 */
register char  *src;		/* a line of header text		 */
char   *name,			/* where to put field's name		 */
       *contents;		/* where to put field's contents	 */
{
	extern char    *compress ();
	char		linetype;
	register char  *dest;

#ifdef	DEBUG
	report (TRACE, "hdr_parse('%s')", src);
#endif	DEBUG

	if (isspace (*src))
	{			/* continuation text			 */
#ifdef	DEBUG
		report( TRACE, "hrd_parse -> cmpnt more");
#endif	DEBUG
		if (*src == '\n')
			return (HDR_EOH);
		linetype = HDR_MOR;
	}
	else
	{			/* copy the name			*/
		linetype = HDR_NEW;
		for (dest = name; *dest = *src++; dest++)
		{
			if (*dest == ':')
				break;		/* end of the name	*/
			if (*dest == '\n')
			{	/* oops, end of the line		*/
				*dest = '\0';
				return (HDR_NAM);
			}
		}
		*dest = '\0';
		compress (name, name);/* strip extra & trailing spaces	 */
#ifdef	DEBUG
		report (TRACE, "hdr_parse -> cmpnt name '%s'", name);
#endif	DEBUG
	}

	for (dest = contents; isspace (*src);)
		if (*src++ == '\n')/* skip leading white space		 */
		{		/* unfulfilled promise, no contents	 */
			*dest = '\0';
			return ((linetype == HDR_MOR) ? HDR_EOH : linetype);
		}		/* hack to fix up illegal spaces	 */

	while ((*dest = *src) != '\n' && *src != 0)
		src++, dest++;	/* copy contents and then, backup	 */
	while (isspace (*--dest));/*   to eliminate trailing spaces	 */
	*++dest = '\0';

	return (linetype);
}

/*************** Parsing of addresses got from message ***************/

/*
 *	See if user is in this field. parse address first and
 *		compare mbox's.
 */

find_user(str)
char *str;
{
	AP_ptr tree, local;
	char *p;

#ifdef	DEBUG
	report(TRACE, "find_user('%s')", str);
#endif	DEBUG
	if( found_1 == TRUE)
	{
#ifdef	DEBUG
		report(TRACE, "find_user() name already found\n");
#endif	DEBUG
		return;
	}

	while( (str = ap_s2p(str, &tree, (AP_ptr *)0, (AP_ptr *)0, &local,
		(AP_ptr *)0, (AP_ptr *)0) ) != (char *)DONE
		&& str != (char *) NOTOK )
	{
		p = ap_p2s( ANIL, ANIL, local, ANIL, ANIL);
#ifdef	DEBUG
		report( TRACE, "find_user() -> found mbox %s", p);
#endif	DEBUG
		if( lexequ(p, username) )
			found_1 = TRUE;
		else	found_1 = alias(p, username) ? TRUE: FALSE;
		free(p);
		ap_sqdelete(tree, ANIL);
		ap_free(tree);
		if( found_1 == TRUE )
			return;
	}
}
/*
 *	Attempt to parse a field into an address tree for later use.
 */

parse_addr( str, aptr)
char	*str;
AP_ptr  *aptr;
{

#ifdef	DEBUG
	report( TRACE, "parse_addr('%s')", str);
#endif	DEBUG

	if( *aptr != ANIL)
	{
#ifdef	DEBUG
		report( TRACE, "field '%s' rejected, already seen one");
#endif	DEBUG
		return;
	}

	if( (*aptr = ap_s2tree(str) ) == (AP_ptr)NOTOK)
		report( NONFATAL, "Can't parse address '%s'", str);
}

/*************** User Data Base Routines ***************/

/*
 *	Note the fact we did/didnot reply to a given person
 *	and include the subject line for good measure.
 */

noteuser(addr, mode)
char	*addr;
int	mode;
{
	FILE	*fp;
	time_t  now;
	char	*ctime();

#ifdef	DEBUG
	report( TRACE, "noteuser(%s,%d)", addr, mode);
#endif	DEBUG

	time(&now);

	if( (fp = fopen(wholoc, "a")) != NULL)
	{
		chmod(wholoc, 0600);
		fprintf( fp, "%c %-30s %19.19s >> %.20s\n", mode == SENT? REPLIED: NOT_REPLIED,
			addr, ctime(&now), subj_field);
		fclose(fp);
	}
}
/*
 *	Have we replied to this person before?
 */
checkuser( addr )
char *addr;
{
	FILE	*fp;
	char	buffer[LINESIZE];
	char	compaddr[LINESIZE];

	if((fp = fopen(wholoc, "r")) == NULL)
		return FALSE;
	while( fgets( buffer, sizeof buffer, fp) != NULL)
	{
		if(buffer[0] == NOT_REPLIED )
			continue;
		getaddress(buffer, compaddr);
#ifdef	DEBUG
		report( TRACE, "checkuser, = '%s' & '%s'?", compaddr, addr);
#endif	DEBUG
		if( lexequ(compaddr, addr) )
		{
			fclose(fp);
			return TRUE;
		}
	}
	return FALSE;
}

/*************** Alias Lookup ***************/

/*
 *	Saunter thru the alias table looking for aliases
 *	This only looks for single pass aliases, for two reasons
 *	1)	Its more work than I can be bothered to do now.
 *	2)	If its more than a simple alias substitution, then
 *		its probably part of an address list or something
 *		similarly complicated, in which case its probably
 *		better to keep quite about it.
 */

alias(str, uname)
char	*str;
char	*uname;
{
	char	aliasval[ADDRSIZE];
	register char	*p;

#ifdef	DEBUG
	report( TRACE, "alias(%s, %s)", str, uname);
#endif	DEBUG
	if (aliasfetch(TRUE, str, aliasval, 0) != OK)
		return FALSE;
	p = aliasval;
	if( *p == '~')  p++;		/* strip off leading ~ */

	return lexequ(p, uname);
}

/*************** Some Utility Routines ***************/


/*
 *	Dig out a signature for the user
 */

char *find_sig()
{
	FILE *fp;
	static char buf[ADDRSIZE];
	static int  been_here = FALSE;
	char *p;

#ifdef	DEBUG
	report( TRACE, "find_sig()");
#endif	DEBUG

	if( been_here == TRUE )  /* cuts off at least 1/4 micro-second */
		return buf;

	if((fp = fopen(sigloc, "r")) == NULL)
		return NILC;
	if( fgets(buf, sizeof(buf), fp) == NILC)
	{
		fclose(fp);
		return NILC;
	}
	if( (p = index(buf, '\n')) != NILC)
		*p = '\0';
	fclose(fp);
	return buf;
}

getaddress(source, dest)
char *source, *dest;
{
	register char *p;
	int	depth = 0;

	p = source + 2;		/* skip over reply indicator */
	while(*p)
	{
		switch( *p )
		{
			case '"':	/* in quoted part ? */
				depth = !depth;
				break;
			case ' ':
			case '\t':
				if( depth == 0)
				{
					*dest = '\0';
					return;
				}
				break;
			case '\\':	/* gobble up next char */
				*dest++ = *p++;
				break;
		}
		*dest++ = *p++;
	}
	*dest = '\0';
}

/*VARARGS2*/
report( mode, fmt, a1, a2, a3, a4)
int mode;
char *fmt, *a1, *a2, *a3, *a4;
{
	static FILE *log;

	if( debug == FALSE && mode == TRACE)
		return;
	if( log == NULL && access(logloc, 0) != -1)
	{
		log = fopen(logloc, "a");
		chmod(logloc, 0600);		/* keep things private */
	}
	if( debug == TRUE )
	{
		fprintf( stderr,"%s\t", mode == TRACE?"TRACE":
			(mode == FATAL?"FATAL":"!FATAL"));
		fprintf( stderr, fmt, a1, a2, a3, a4);
		putc('\n', stderr);
		fflush( stderr);
	}
	if( log != NULL)
	{
		fprintf( log, "%s\t", mode == TRACE?"TRACE":
			(mode == FATAL?"FATAL":"!FATAL"));
		fprintf( log, fmt, a1, a2, a3, a4);
		putc('\n', log);
		fflush( log );
	}
	else if(mode != TRACE)
		ll_log( logptr, LLOGTMP, fmt, a1, a2, a3, a4);
	if( mode == FATAL)
		exit(RP_MECH);		/* die a horrible death */
}
 ANIL);
		ap_free(tree);
		if( found_1 == TRUE )
			return;
	}
}
/*
 *	Attempt to parse a field into an address tree for later use.
 */

parse_addr( str, aptr)
char	*str;
AP_ptr  *aptr;
{

#ifdef	DEBUG
	report( TRACE, "parse_addr('%s')", str);
#endif	DEBUG

	if( *aptr != ANIL)
	{
#ifdef	DEBUG
		report( TRACE, "field '%s' rejected, already seen one");
#endif	DEBUG
		return;
	}

	if( (*aptr = ammdf/uip/other/resend.c   444      0     12       20131  3657737773  10360 #include "util.h"                 /* to get mmdf reply codes            */
#include "mmdf.h"                 /* to get mmdf reply codes            */
#include "cnvtdate.h"
#include <pwd.h>
#include <signal.h>
#include <sys/stat.h>

extern char     *locname;
extern char     *locdomain;

char   *logdir;
char   *username;

extern	char *strdup();
extern  char *index();

char    noret;                    /* no return address                  */
char    watchit;                  /* user wants to watch delivery       */
int	hadto;

#define FRMNONE 0                 /* no From field specified            */
#define FRMTXT  1                 /* From field had text only           */
#define FRMSNDR 2                 /* field had sender info, too         */


/* **************************  MAIN  ******************************* */

main (argc, argv)
int     argc;
char   *argv[];
{
	mmdf_init (argv[0]);

	pgminit ();

	get_aliasfile ();

	sendmail (argc, argv);
}

pipsig ()
{
	if (rp_gval (endchild (NOTOK)) == RP_NO)
		err_abrt(RP_LIO, "Abnormal return from submit");
	exit (NOTOK);
}

pgminit ()
{
	extern struct passwd *getpwuid ();
	extern char *getmailid ();
	struct passwd  *pwdptr;
	int     realid,
	effecid;

	signal (SIGPIPE, pipsig);    /* catch write to bad pipe            */

	getwho (&realid, &effecid);   /* who am i?                          */

	if ((pwdptr = getpwuid (realid)) == (struct passwd *) NULL)
		err_abrt (RP_PARM, "Unable to locate user's name");

	/*  save login directory   */
	logdir = strdup(pwdptr -> pw_dir);

	if ((username = getmailid(pwdptr -> pw_name)) == NULL)
		err_abrt (RP_PARM, "Unable to locate user's mailid");
}

#define	NARGS	20

get_aliasfile ()
{
	struct passwd  *pwdptr;
	char	rcfilename[128];
	char	linebuf[128];
	char	*rcname = ".sendrc";
	char 	*av[NARGS+1], *aliasfile[128];
	FILE 	*fp;
	int 	realid, effecid;

	getwho (&realid, &effecid);

	if ((pwdptr = getpwuid (realid)) == (struct passwd *) NULL)
		err_abrt (RP_PARM, "Unable to locate user's name");

	/* Get info from the ".sendrc" file */

	sprintf( rcfilename, "%s/%s", pwdptr->pw_dir, rcname );
	if(( fp = fopen(rcfilename, "r")) != NULL ) {
		char *cp;

		/* Process info a line at a time until we find the alias file */
		while( fgets(linebuf, sizeof(linebuf), fp ) != NULL )  {
			if (cp = index(linebuf, '\n'))
				*cp = 0;
			if (sstr2arg(linebuf, NARGS, av, " \t") < 0)
				continue;
			if ( strncmp(linebuf,"aliases",7) == 0 )  {

				if (av[1][0] != '/')  {
					sprintf( aliasfile, "%s/%s", pwdptr->pw_dir, av[1]);
					aliasinit ( aliasfile );
				} else
					aliasinit ( av[1] );

			}	/* end of if on strncmp */
		}	/* end of while on lines */
	
	}	/* end of if statement on file open */
}	/* end of get_aliasfile routine */


/*^L*/

static char regargs[] = "rmxresent-to,resent-cc*";
/* return to sender, mail, extract addrs from to & cc   */

sendmail (argc, argv)             /* Send a message                     */
int     argc;
char   *argv[];
{
	extern char *sbargs ();
	int     retval;
	char    linebuf[ADDRSIZE];
	char   *sbmtargs;

	if (rp_isbad (mm_init ()) || rp_isbad (mm_sbinit ()))
		err_abrt (RP_MECH, "Unable to submit mail; please report this error");

	strcpy (linebuf, regargs);    /* standard stuff                     */

	if ((sbmtargs = sbargs (argc, argv)) != 0)
		/* any args to be args to submit?       */
		strcat (linebuf, sbmtargs);

	if (noret)
		strcat (linebuf, "q");    /* quiet, do not return on errors  */
	if (watchit)
		strcat (linebuf, "w");    /* user want so watch the process     */

	if (rp_isbad (mm_winit ((char *) 0, linebuf, (char *) 0)))
		err_abrt (RP_MECH, "problem with submit message initialization");

	dumpheader();
	doresent(argc, argv);

	if (!hadto)
		err_abrt (RP_PARM, "No addressees specified");

	dobody ();
	retval = endbody ();

	endchild (OK);

	exit ((rp_isbad (retval)) ? NOTOK : OK);
}
/*  *************  ARGUMENT PARSING FOR SENDMAIL  **************  */

char *
sbargs (argc, argv)               /* any args to be args to submit?   */
int     argc;
char   *argv[];
{
	register int    i;
	char   *argptr;

	for (i = 1, argptr = 0; i < argc; i++)
	{                             /* create message header fields */
		if (argv[i][0] == '-')
			switch (argv[i][1])
			{
			case '-':
				argptr = &(argv[i][2]);
				continue;

			case 'r':
				noret = TRUE;
				break;

			case 'w':
				watchit = TRUE;
				break;
			}
	}
	return (argptr);
}

/**/

dumpheader()
{
	char	line[LINESIZE];

	while (fgets (line, LINESIZE, stdin) != NULL) {
		if (line[0] == '\n')
			break;
		if (prefix ("resent-", line))
			mm_wtxt ("Old-", 4);
		mm_wtxt (line, strlen (line));
	}
}

doresent(argc, argv)
int	argc;
char	**argv;
{
	char    datbuf[64];

	cnvtdate (TIMREG, datbuf);		/* rfc822 format date */
	sndhdr ("Resent-Date:  ", datbuf);
	dosender ();
	doto (argc, argv);
	mm_wtxt ("\n", 1);
}

sndhdr (name, contents)
char    name[],
contents[];
{
	char    linebuf[LINESIZE];

	sprintf (linebuf, "%-10s%s\n", name, contents);
	mm_wtxt (linebuf, strlen (linebuf));
}

dosender ()
{
	int     sigfd;
	int	sigsiz;
	char	linebuf[ADDRSIZE];
	char	sigtxt[FILNSIZE];     /* where is signature text?           */
	char    gotsig;

	gotsig = FALSE;

	sprintf (linebuf, "%s/.signature", logdir);

	if ((sigfd = open (linebuf, 0)) >= 0)
	{                         /* there is a file w/signature?       */
		sigsiz = read (sigfd, sigtxt, sizeof sigtxt);
		if (sigsiz > 0) {
			sigtxt[sigsiz - 1] = '\0';
			gotsig = TRUE;
		}
	}

	if (gotsig)                   /* real name + mailbox                */
		sprintf (linebuf, "%s <%s@%s.%s>",
			 sigtxt, username, locname, locdomain);
	else                          /* just the mailbox info              */
		sprintf (linebuf, "%s@%s.%s", username, locname, locdomain);

	sndhdr ("Resent-From:  ", linebuf);
}
/**/

doto (argc, argv)
int     argc;
char   *argv[];
{
	char    linebuf[ADDRSIZE];
	char    someto;
	register int    i;

	someto = FALSE;
	i = 1;
	while( i < argc )
	{
		linebuf[0] = '\0';
		if( argv[i][0] != '-' )
		{
			someto = TRUE;
			aliasmap( linebuf, argv[i], locname );
			sndhdr( "Resent-To:  ", linebuf );
		}
		else switch( argv[i][1] )
		{
		case 't':
			someto = TRUE;
			i++;
			aliasmap( linebuf, argv[i], locname );
			sndhdr( "Resent-To:  ", linebuf );
			break;
		case 'c':
			someto = TRUE;
			i++;
			aliasmap( linebuf, argv[i], locname );
			sndhdr( "Resent-Cc:  ", linebuf );
			break;
		case 'w':
		case 'r':
		case '-':
			break;
		default:
			fprintf(stderr,"Unknown flag '%c'\n",argv[i][1]);
			break;
		}
		i++;
	}
		
	hadto = (someto ? TRUE : FALSE);
	return;
}

dolist (i, argc, argv, dest)
register int    i;
int     argc;
char   *argv[];
char   *dest;                     /* where to put the list                */
{
    *dest = '\0';                 /* in case nothing found                */
    if (i < argc && *argv[i] != '-')
	for (strcat (dest, argv[i++]); i < argc && *argv[i] != '-';
		strcat (dest, ", "), strcat (dest, argv[i++]));

    return (i - 1);
}
/**/

dobody ()
{
	char    buffer[BUFSIZ];
	register int    i;

	while (!feof (stdin) && !ferror (stdin) &&
	    (i = fread (buffer, sizeof (char), sizeof (buffer), stdin)) > 0)
		if (rp_isbad (i = mm_wtxt (buffer, i)))
			err_abrt (i, "Problem writing body");

	if (ferror (stdin))
		err_abrt (RP_FIO, "Problem reading body");
}

endbody ()
{
	struct rp_bufstruct thereply;
	int     len;

	if (rp_isbad (mm_wtend ()))
		err_abrt (RP_MECH, "problem ending submission");

	if (rp_isbad (mm_rrply (&thereply, &len)))
		err_abrt (RP_MECH, "problem getting submission status");

	if (rp_isbad (thereply.rp_val))
		err_abrt (thereply.rp_val, "%s", thereply.rp_line);

	return (thereply.rp_val);
}

/* *********************  UTILITIES  ***************************  */

endchild (type)
int     type;
{
	int retval;


	if (rp_isgood (retval = mm_sbend ()))
		retval = mm_end (type);

	return ((retval == NOTOK) ? RP_FIO : (retval >> 8));
}

/* VARARGS2 */

err_abrt (code, fmt, b, c, d)     /* terminate the process              */
int     code;                     /* a mmdfrply.h termination code      */
char   *fmt, *b, *c, *d;
{
	if (fmt)
		printf (fmt, b, c, d);
	printf ("\nMessage resending aborted\n");
	fflush (stdout);
	endchild (NOTOK);
	exit (code);
}
 message header fields */
		if (argv[i][0] == '-')
			switch (argv[i][1])
			{
			case '-':
				argptr = &(argv[i][2]);
				continue;

			case 'r':
				noret = TRUE;
				break;

			case 'w':
				watchit = TRUE;
				break;
			}
	}
	return (argptr);
}

/**/

dumpheader()
{
	char	line[LINESIZE];

	while (fgets (line, LINESIZE, stdin) != NULL) {
		if (line[0] == '\n')
			break;
		if (prefix ("resent-", line))
			mm_wtxt ("mmdf/uip/other/v6mail.c   444      0     12       41046  3656410662  10266 #include "util.h"                 /* to get mmdf reply codes            */
#include "mmdf.h"                 /* to get mmdf reply codes            */
#include "cnvtdate.h"
#include <pwd.h>
#include <signal.h>
#include <sys/stat.h>

/*  Upgrade to use MMDF Submit; 10 Nov. 1978; David H. Crocker
 *    1 Dec 78 Dave Crocker:  add mail-reading, approx like old "mail"
 *   15 Dec 78 Dave Crocker:  add sndmsg & memo & "-" interfaces
 *   16 Dec 78 Dave Crocker:  "-" not generate return address
 *   25 Apr 80 Dave Crocker:  fix .mail acquisition, default to save
 *   14 Jun 80 Dave Crocker:  turn tty writing off/on
 *   17 Jul 80 Dave Crocker:  fix -c
 *   24 Sep 80 Dave Crocker:  add -r
 *   12 Oct 80 Dave Crocker:  add -g & signature file
 *   15 Nov 80 Dave Crocker:  conversion to V7 package & use of mm_io
 *    1 Dec 80 Dave Crocker:  check success of ALL system calls
 *   20 Jul 81 Dave Crocker:  fix dobody() to detect end from terminal
 *   16 Jul 82 Dave Crocker:  make addresses have '@' only; no " at "
 *    6 Dec 83 Doug Kingston: remove tty write stuff, this is not the place.
 *   19 May 83 Doug Kingston: fix -r to pass a flag to submit
 */

long int    curnewpos,
	    poshack;              /* keep track of current position     */
				  /*   because ftell isn't on V6        */
FILE *  newfp;                    /* handle on .mail file               */

char   *logdir;
char   *username;
char   *mbxname;                  /* full pathname to mailbox           */
char    *binbox;
char    ttyobuf[BUFSIZ];          /* for buffering ttyoutput            */
char	*index();
char    *strdup();

/* mmdf globals */

extern int sentprotect;           /* default mailbox protection         */
extern char     *mldflfil;        /* default mailbox file               */
extern char     *mldfldir;        /* directory containing mailbox file  */
extern char     *delim1;          /* string delimiting messages         */
extern char     *locname;
extern char     *locdomain;

/* end of mmdf globals */


char    noret;                    /* no return address                  */
char    watchit;                  /* user wants to watch delivery       */
char    qrychar;                  /* user-specified default save status */

char    usertype;
#define U_MAIL  0                 /* the original mail command          */
#define U_SND   1                 /* sndmsg-type prompting              */
#define U_MEMO  2                 /* memo prompting                     */
#define U_LMEM  3                 /* logged in as memo, for memo        */

#define FRMNONE 0                 /* no From field specified            */
#define FRMTXT  1                 /* From field had text only           */
#define FRMSNDR 2                 /* field had sender info, too         */


/* **************************  MAIN  ******************************* */

main (argc, argv)
int     argc;
char   *argv[];
{
    setbuf (stdout, ttyobuf);
    mmdf_init (argv[0]);
    pgminit ();

    if (initstr ("snd", argv[0], 3)
	    || strindex ("/snd", argv[0]) != -1
	    || initstr ("snd", &argv[0][1], 3)
	    || strindex ("sndmsg", argv[0]) != -1)
	usertype = U_SND;
    else
	if (strindex ("memo", argv[0]) != -1)
	    usertype = U_MEMO;
	else
	    if (argv[0][0] == '-' && argv[0][1] == '\0')
	    {                     /* "-" => login shell   */
		usertype = U_LMEM;
		signal (SIGHUP, SIG_IGN);
				  /* renable these for exiting  */
		prompt ("Send a memo\n");
	    }
    if (usertype == U_MAIL && (argc == 1 || (argc == 2 && *argv[1] == '-')))
	readmail (argc, argv);    /* only when invoked at "mail"  */
    else
	sendmail (argc, argv);
}
/**/

leave (val)
int     val;
{
    if (newfp)
	lk_fclose(newfp, mbxname, (char *)0, (char *)0);
    exit (val == OK ? 0 : 99);
}

pipsig ()
{
    if (rp_gval (endchild (NOTOK)) == RP_NO)
	prompt ("Problem accessing mail submission program\n");
				  /* only comment if it looks like      */
				  /*  submit died ugly                  */
    leave (NOTOK);
}

pgminit ()
{
    extern struct passwd *getpwuid ();
    extern char *getmailid ();
    struct passwd  *pwdptr;
    int     realid,
	    effecid;

    signal (SIGPIPE, pipsig);    /* catch write to bad pipe            */

    getwho (&realid, &effecid);   /* who am i?                          */

    if ((pwdptr = getpwuid (realid)) == (struct passwd *) NULL)
	err_abrt (RP_PARM, "Unable to locate user's name");

    /*  save login directory   */
    logdir = strdup(pwdptr -> pw_dir);

    if ((username = getmailid(pwdptr -> pw_name)) == NULL)
	err_abrt (RP_PARM, "Unable to locate user's mailid");
}
/**/

readmail (argc, argv)
int     argc;
char   *argv[];
{
    register int    count;

    if (argc == 2)
	qrychar = argv[1][1];

    readinit ();
    for (count = 0; msgstrt () && showmsg () && savmsg (); count++);
    if (count == 0)
	err_abrt (RP_OK, "No new mail");
    endread ();
}

readinit ()
{
    extern char *multcat ();
    int     newfd;
    extern int  errno;

    mbxname = multcat (
		((mldfldir == 0 || isnull(*mldfldir)) ? logdir : mldfldir),
		 "/",
		((mldflfil == 0 || isnull(*mldflfil)) ? username : mldflfil),
		  0);
    binbox = multcat (
		((mldfldir == 0 || isnull(*mldfldir)) ? logdir : mldfldir),
		 "/._",
		((mldflfil == 0 || isnull(*mldflfil)) ? username : mldflfil),
		  0);
    if (access(binbox, 0) == 0) {
	fprintf(stderr, "Your mailbox has a binary box (%s),\n", binbox);
	fprintf(stderr, "probably created by another mail reading program like MSG.\n");
	fprintf(stderr, "You should remove the binary box before running this program\n");
	fprintf(stderr, "again, or use MSG instead to read your mail.\n");
	exit (99);
    }

    if ((newfd = lk_open (mbxname, 0, (char *)0, (char *)0, 5)) == -1)
	err_abrt (RP_OK,
		    (errno == ETXTBSY) ? "Mailbox is busy" : "No new mail");

    newfp = fdopen (newfd, "r");
    poshack = 0l;
}

endread ()
{
    fflush (stdout);

    if (creat (mbxname, sentprotect) == NOTOK)
				  /* truncate the file            */
	err_abrt (RP_FCRT, "Unable to reset %s mailbox", mbxname);
    leave (OK);
}
/**/

msgstrt ()
{
    char    linebuf[LINESIZE];

    FOREVER
    {
	curnewpos = poshack;
	if (fgets (linebuf, sizeof linebuf, newfp) == NULL)
	    return (FALSE);
	if (!strequ (linebuf, delim1))
	    break;
	poshack += strlen (linebuf);
    }
    fseek (newfp, curnewpos, 0);
    return (TRUE);
}

showmsg ()
{
    char    linebuf[LINESIZE];
    register int    lines;

    for (lines = 0; fgets (linebuf, sizeof linebuf - 1, newfp) != NULL;)
    {
	poshack += strlen (linebuf);
	if (strequ (linebuf, delim1))
	    return (TRUE);

	fputs (linebuf, stdout);
	if (lines++ > 4)
	{
	    fflush (stdout);
	    lines = 0;
	}
    }
    return ((ferror (newfp) || ferror (stdout)) ? FALSE : TRUE);
}
/**/

savmsg ()
{
    static  FILE * mboxfp;
    char    linebuf[LINESIZE];

    if (!querysav ())
	return (TRUE);

    if (mboxfp == NULL)
    {
	if ((mboxfp = fopen ("mbox", "a")) == NULL ||
		chmod ("mbox", sentprotect) == NOTOK)
	    err_abrt (RP_FOPN, "Unable to access/create mbox");
    }

    poshack = curnewpos;
    for ( fseek (newfp, curnewpos, 0);
	    fgets (linebuf, sizeof linebuf - 1, newfp) != NULL;
	    fputs (linebuf, mboxfp))
    {
	poshack += strlen (linebuf);
	if (strequ (linebuf, delim1))
	    break;
    }
    fputs (delim1, mboxfp);
    fflush (mboxfp);
    return (ferror (newfp) ? FALSE : TRUE);
}

querysav ()
{
    register int  respchar;

    if (qrychar == 0)
    {
	prompt ("\nSave? ");
	switch (respchar = getchar ())
	{
	    case '\n':
		break;
	    case '\0':
		exit (-1);
	    default:
		while (getchar () != '\n');
	}
    }
    else
	respchar = qrychar;
    if (respchar == 'n' || respchar == 'N')
	return (0);               /* deletion must be explicit            */
    return (1);                   /* default to saving                    */
}
/* *************************  SENDING ****************************** */

static char regargs[] = "rmxto,cc*";
  /* quick NS timeout, return to sender, mail, extract addrs from to & cc  */

sendmail (argc, argv)             /* Send a message                     */
int     argc;
char   *argv[];
{
    extern char *sbargs ();
    int     retval;
    char    linebuf[ADDRSIZE];
    char   *sbmtargs;
    char    hadfrom,
	    hadto,
	    hadcc;

    if (rp_isbad (mm_init ()) || rp_isbad (mm_sbinit ()))
	err_abrt (RP_MECH, "Unable to submit mail; please report this error");

    strcpy (linebuf, regargs);    /* standard stuff                     */

    if ((sbmtargs = sbargs (argc, argv)) != 0)
				  /* any args to be args to submit?       */
	strcat (linebuf, sbmtargs);

    if (usertype == U_LMEM)
	strcat (linebuf, "u");    /* do not verify the from field       */
    if (noret)
	strcat (linebuf, "q");    /* quiet, do not return on errors  */
    if (watchit)
	strcat (linebuf, "w");    /* user wants to watch the process     */

    if (rp_isbad (mm_winit ((char *) 0, linebuf, (char *) 0)))
	err_abrt (RP_MECH, "problem with submit message initialization");

    dodate ();

    hadfrom = dofrom (argc, argv);

    dosubject (argc, argv);

    if (hadfrom == FRMTXT && usertype != U_LMEM)
	dosender ("Sender:  ", (char *) 0, TRUE);
				/* simple address */
    hadto = doto (argc, argv);
    hadcc = docc (argc, argv);

    if (!hadto && !hadcc && usertype == U_MAIL)
	err_abrt (RP_PARM, "No addressees specified");

    doid (argc, argv);

    if (usertype != U_MAIL)
    {
	doprompt ();              /* get headers          */
	prompt ("Text:\n");
    }
    mm_wtxt ("\n", 1);           /* start the body             */


    dobody ();
    retval = endbody ();

    endchild (OK);

    if (usertype != U_MAIL && rp_isgood (retval))
	prompt ("Message posted\n");

    leave ((rp_isbad (retval)) ? NOTOK : OK);
}
/*  *************  ARGUMENT PARSING FOR SENDMAIL  **************  */

char *
	sbargs (argc, argv)               /* any args to be args to submit?   */
int     argc;
char   *argv[];
{
    register int    i;
    char   *argptr;

    for (i = 1, argptr = 0; i < argc; i++)
    {                             /* create message header fields */
	if (argv[i][0] == '-')
	    switch (argv[i][1])
	    {
		case '-':
		    argptr = &(argv[i][2]);
		    continue;

		case 'r':
		    noret = TRUE;
		    break;

		case 'w':
		    watchit = TRUE;
		    break;
	    }
    }
    return (argptr);
}

sndhdr (name, contents)
char	*name,
	*contents;
{
    char    linebuf[LINESIZE];

    sprintf (linebuf, "%-10s%s\n", name, contents);
    mm_wtxt (linebuf, strlen (linebuf));
}
/**/

dodate ()
{
    char    datbuf[64];

    cnvtdate (TIMREG, datbuf);    /* rfc733 format date                 */
    sndhdr ("Date:  ", datbuf);
}

doid (argc, argv)
int     argc;
char   *argv[];
{
    char    linebuf[LINESIZE];
    char    datbuf[64];
    register int    i;

    for (i = 1; i < argc; i++)
    {                             /* create message header fields */
	if (argv[i][0] == '-' && argv[i][1] == 'i')
	{
	    cnvtdate (TIMCOM, datbuf);
	    sprintf (linebuf, "<%s.%d@%s.%s>", datbuf, getpid(),
			locname, locdomain);
	    sndhdr ("Message-Id:  ", linebuf);
	    return;
	}
    }
}

dosubject (argc, argv)
int     argc;
char   *argv[];
{
    register int    i;

    for (i = 1; i < argc; i++)    /* create message header fields */
	if (argv[i][0] == '-' && argv[i][1] == 's')
	    sndhdr ("Subject:  ", argv[++i]);
}
/**/

dofrom (argc, argv)
int     argc;
char   *argv[];
{
    static char fldnam[] = "From:  ";
    register int    i;

    for (i = 1; i < argc; i++)
    {                             /* create message header fields */
	if (argv[i][0] == '-' && (argv[i][1] == 'f' || argv[i][1] == 'g'))
	{
	    if (argv[i][1] == 'g')
	    {
		dosender (fldnam, argv[++i], TRUE);
		return (FRMSNDR);
	    }
	    else                  /* no mailbox portion                 */
	    {
		sndhdr (fldnam, argv[++i]);
		return (FRMTXT);
	    }
	}
    }
    if (usertype != U_LMEM)       /* not memo                     */
	dosender (fldnam, (char *) 0, TRUE);

    return (FRMNONE);
}
/**/

dosender (cmpnt, name, fancy)
char    cmpnt[],
	name[];
int     fancy;          /* make address fancy? */
{
    int     sigfd,
	    sigsiz;
    char   *ptr,
	    linebuf[ADDRSIZE],
	    sigtxt[FILNSIZE];     /* where is signature text?           */
    char    gotsig;

    gotsig = FALSE;

    if (!fancy || name != 0)
    {
	strcpy (sigtxt, name);
	gotsig = TRUE;
    }
    else
    {                             /* user didn't give us a signature    */
	sprintf (linebuf, "%s/.signature", logdir);

	if ((sigfd = open (linebuf, 0)) >= 0)
	{                         /* there is a file w/signature?       */
	    if ((sigsiz = read (sigfd, sigtxt, sizeof sigtxt)) > 0) {
	    	char *cp;

		sigtxt[sigsiz - 1] = '\0';
	    	if (cp = index(sigtxt, '\n'))
	    	    *cp = '\0';
	    	if (sigtxt[0])
		    gotsig = TRUE;
	    }
	}
    }

    if (gotsig)                   /* real name + mailbox                */
    {
	for (ptr = locname; *ptr != 0; ptr++)
	    *ptr = uptolow (*ptr);      /* Screw locname */
	sprintf (linebuf, "%s <%s@%s>", sigtxt, username, locname);
    }
    else                          /* just the mailbox info              */
	sprintf (linebuf, "%s@%s", username, locname);

    sndhdr (cmpnt, linebuf);
}
/**/

doto (argc, argv)
int     argc;
char   *argv[];
{
    char    linebuf[ADDRSIZE];
    char    someto;
    register int    i;

    someto = FALSE;
    if (usertype == U_MAIL && argv[1][0] != '-')
    {
	someto = TRUE;
	i = dolist (1, argc, argv, linebuf);
	sndhdr ("To:  ", linebuf);
    }
    else
	i = 1;

    for (; i < argc; i++)
    {                             /* create message header fields */
	if (argv[i][0] == '-' && argv[i][1] == 't')
	{
	    someto = TRUE;
	    i = dolist (i + 1, argc, argv, linebuf);
	    sndhdr ("To:  ", linebuf);
	}
    }
    return (someto ? TRUE : FALSE);
}
/**/

docc (argc, argv)
int     argc;
char   *argv[];
{
    char    linebuf[ADDRSIZE];
    char    somecc;
    register int    i;

    for (somecc = FALSE, i = 1; i < argc; i++)
	if (argv[i][0] == '-' && argv[i][1] == 'c')
	{
	    somecc = TRUE;
	    i = dolist (i + 1, argc, argv, linebuf);
	    sndhdr ("cc:  ", linebuf);
	}

    return (somecc ? TRUE : FALSE);
}

dolist (i, argc, argv, dest)
register int    i;
int     argc;
char   *argv[];
char   *dest;                     /* where to put the list                */
{
    *dest = '\0';                 /* in case nothing found                */
    if (i < argc && *argv[i] != '-')
	for (strcat (dest, argv[i++]); i < argc && *argv[i] != '-';
		strcat (dest, ", "), strcat (dest, argv[i++]));

    return (i - 1);
}
/**/

doprompt ()
{
    if (usertype == U_LMEM)       /* "free" memo          */
	cpyprompt ("From:  ");
    cpyprompt ("To:  ");
    if (usertype == U_SND)        /* sndmsg               */
	cpyprompt ("cc:  ");
    cpyprompt ("Subject:  ");
}

prompt (str)
char   *str;
{
    printf ("%s", str);
    fflush (stdout);
}

cpyprompt (prom)
char    prom[];
{
    prompt (prom);
    copylin (prom);
}

copylin (prelim)
char   *prelim;
{
    char    linebuf[LINESIZE];
    register int    morin;
    register int    c;

    for (morin = TRUE; morin; sndhdr (prelim, linebuf), prelim = "")
    {
	morin = FALSE;
	if (fgets (linebuf, sizeof linebuf, stdin) == NULL)
	    err_abrt (RP_LIO, "Input error");

	if (linebuf[0] == '\n')
	    return;

	c = strlen (linebuf);
	linebuf[c - 1] = '\0';
	if (linebuf[c - 2] == '\\')
	{
	    morin = TRUE;
	    linebuf[c-- - 2] = '\n';
	}
    }
}
/**/

dobody ()
{
    char    buffer[BUFSIZ];
    register int    i;

    while (!feof (stdin) && !ferror (stdin) &&
	    (i = fread (buffer, sizeof (char), sizeof (buffer), stdin)) > 0)
	if (rp_isbad (i = mm_wtxt (buffer, i)))
		err_abrt (i, "Problem writing body");

    if (ferror (stdin))
	err_abrt (RP_FIO, "Problem reading body");
}

endbody ()
{
    struct rp_bufstruct thereply;
    int     len;

    if (rp_isbad (mm_wtend ()))
	err_abrt (RP_MECH, "problem ending submission");

    if (rp_isbad (mm_rrply (&thereply, &len)))
	err_abrt (RP_MECH, "problem getting submission status");

    if (rp_isbad (thereply.rp_val))
	err_abrt (thereply.rp_val, "%s", thereply.rp_line);

    return (thereply.rp_val);
}

doreset ()
{
    exit (NOTOK);
}

/* *********************  UTILITIES  ***************************  */

endchild (type)
int     type;
{
    int retval;


    if (rp_isgood (retval = mm_sbend ()))
	retval = mm_end (type);

    return ((retval == NOTOK) ? RP_FIO : (retval >> 8));
}

/*VARARGS2*/
err_abrt (code, fmt, b, c, d)     /* terminate the process              */
int     code;                     /* a mmdfrply.h termination code      */
char   *fmt, *b, *c, *d;
{
    if (fmt) {
	printf (fmt, b, c, d);
    	putchar ('\n');
    }
    if (code != RP_OK)
	printf ("Message posting aborted\n");
    fflush (stdout);
    endchild (NOTOK);
    leave (code);
}
 '\0';
	    	if (sigtxt[0])
		    gotsig = TRUE;
	    }
	}
    }

    if (gotsig)                   /* real name + mailbox                */
    {
	for (ptr = locname; *ptr != 0; ptr++)
	    *ptr = uptolow (*ptr);      /* Screw locname */
	sprintf (linebuf, "%s <%s@%s>", sigtxt, username, locname);
    }
    else                          /* just the mailbox info              */
	sprintf (linebuf, "%s@%s", username, locname);

    sndhdr (cmpnt, linebuf);
}
/**/

doto (mmdf/uip/other/gen   555      0     12          57  3620510616   7327 make -f ../../Makefile.com -f Makefile.real $*
   	if (sigtxt[0])
		    gotsig = TRUE;
	    }
	}
    }

    if (gotsig)                   /* real name + mailbox                */
    {
	for (ptr = locname; *ptr != 0; ptr++)
	    *ptr = uptolow (*ptr);      /* Screw locname */
	sprintf (linebuf, "%s <%s@%s>", sigtxt, username, locname);
    }
    else                          /* just the mailbox info              */
	sprintf (linebuf, "%s@%s", username, locname);

    sndhdr (cmpnt, linebuf);
}
/**/

doto (mmdf/uip/other/Makefile.real   444      0     12       15742  3652516041  11304 #
#		User Interface Processes (UIP)
#
#   Instructions to Make, for compilation of UIP processes
#
# C programs that live in the current directory and do not need
# explicit make lines.
#
PROGS	= xcheckmail xmalias xmlist xnewssend xrcvalert xrcvfile \
	  xrcvprint xrcvtrip xresend xsendmail xv6mail

MODULES = checkmail malias mlist newssend rcvalert rcvfile \
	  rcvprint rcvtrip resend sendmail v6mail

INSTALLS = inst-xcheckmail inst-xmalias inst-xmlist inst-xnewssend \
	   inst-xrcvalert inst-xrcvfile inst-xrcvprint inst-xrcvtrip \
	   inst-xresend inst-xsendmail inst-xv6mail

real-default:	${PROGS}

install: ${INSTALLS}

lint:	l-checkmail l-malias l-mlist l-newssend l-rcvalert l-rcvfile \
	l-rcvprint l-rcvtrip l-resend l-sendmail l-v6mail

clean:
	-rm -f x* *.o core make.out


###########################################################
#
#   checkmail:   what messages are in queue
#
inst-xcheckmail:  $(BINDIR)/checkmail

$(BINDIR)/checkmail:  xcheckmail
	cp xcheckmail $@
	-$(CHOWN) mmdf $@
	-chmod 04$(PGMPROT) $@

xcheckmail :   checkmail.o $(MMDFLIBS)
	$(CC) $(LDFLAGS) -o $@ checkmail.o $(MMDFLIBS) $(SYSLIBS)

l-checkmail:
	lint $(LFLAGS) checkmail.c $(LLIBS)

###########################################################
#
#   malias: alias checkup routine
#
inst-xmalias: $(LIBDIR)/$(PREF)malias

$(LIBDIR)/$(PREF)malias: xmalias
	cp xmalias $@
	-chmod $(PGMPROT) $@

xmalias:	malias.o $(MMDFLIBS)
	$(CC) $(LDFLAGS) -o $@ malias.o $(MMDFLIBS) $(SYSLIBS) $(DBM)

l-malias:
	lint $(LFLAGS) malias.c $(LLIBS)

###########################################################
#
#   mlist: mailing list maintainer
#
inst-xmlist: $(BINDIR)/$(PREF)mlist

$(BINDIR)/$(PREF)mlist: xmlist
	cp xmlist $@
	-$(CHOWN) mmdf $@
	-chmod 04$(PGMPROT) $@

xmlist:	mlist.o $(MMDFLIBS)
	$(CC) $(LDFLAGS) -o $@ mlist.o $(MMDFLIBS) $(SYSLIBS) $(DBM)

l-mlist:
	lint $(LFLAGS) mlist.c $(LLIBS)

###########################################################
#
#   v6mail:   v6mail command replacement
#

inst-xv6mail:  $(LIBDIR)/$(PREF)v6mail

$(LIBDIR)/$(PREF)v6mail:  xv6mail
	cp xv6mail $@
	-$(CHOWN) mmdf $@
	-chmod $(PGMPROT) $@

xv6mail:	v6mail.o $(MMDFLIBS)
	$(CC) $(LDFLAGS) -o $@ v6mail.o $(MMDFLIBS) $(SYSLIBS)

l-v6mail:
	lint $(LFLAGS) v6mail.c $(LLIBS)

###########################################################
#
#   rcvalert:   Alert user of incoming new mail
#

inst-xrcvalert:  $(RCVDIR)/$(PREF)rcvalert

$(RCVDIR)/$(PREF)rcvalert:  xrcvalert
	cp xrcvalert $@
	-$(CHOWN) mmdf $@
	-chmod $(PGMPROT) $@

xrcvalert:	rcvalert.o $(MMDFLIBS)
	$(CC) $(LDFLAGS) -o $@ rcvalert.o $(MMDFLIBS) $(SYSLIBS)

l-rcvalert:
	lint $(LFLAGS) rcvalert.c $(LLIBS)

#############################################################
#
#   rcvfile: archive body of incoming mail into file named in subject
#

inst-xrcvfile:  $(RCVDIR)/$(PREF)rcvfile

$(RCVDIR)/$(PREF)rcvfile: xrcvfile
	cp xrcvfile $@
	-$(CHOWN) mmdf $@
	-chmod $(PGMPROT) $@

xrcvfile:	rcvfile.o $(MMDFLIBS)
	$(CC) $(LDFLAGS) -o $@ rcvfile.o $(MMDFLIBS) $(SYSLIBS)

l-rcvfile:
	lint $(LFLAGS) rcvfile.c $(LLIBS)

############################################################
#
#   resend:  resend a message using RESEND-xxx notation
#

inst-xresend:  $(BINDIR)/resend

$(BINDIR)/$(PREF)resend: xresend
	cp xresend $@
	-$(CHOWN) mmdf $@
	-chmod $(PGMPROT) $@

xresend:	resend.o
	$(CC) $(LDFLAGS) -o $@ resend.o $(MMDFLIBS) $(SYSLIBS)

l-resend:
	lint $(LFLAGS) resend.c $(LLIBS)

############################################################
#
#   newssend:  Program to repost Netnews (USENET) news articles
#	       into an RFC822 mail world.  (based on resend)
#

inst-xnewssend:  $(LIBDIR)/$(PREF)newssend

$(LIBDIR)/$(PREF)newssend: xnewssend
	cp xnewssend $@
	-$(CHOWN) mmdf $@
	-chmod $(PGMPROT) $@

xnewssend:	newssend.o
	$(CC) $(LDFLAGS) -o $@ newssend.o $(MMDFLIBS) $(SYSLIBS)

l-newssend:
	lint $(LFLAGS) newssend.c $(LLIBS)

############################################################
#
#   rcvprint: send a copy of the body of the message to opr
#

inst-xrcvprint:  $(RCVDIR)/$(PREF)rcvprint

$(RCVDIR)/$(PREF)rcvprint: xrcvprint
	cp xrcvprint $@
	-$(CHOWN) mmdf $@
	-chmod $(PGMPROT) $@

xrcvprint:	rcvprint.o $(MMDFLIBS)
	$(CC) $(LDFLAGS) -o $@ rcvprint.o $(MMDFLIBS) $(SYSLIBS)

l-rcvprint:
	lint $(LFLAGS) rcvprint.c $(LLIBS)

##########################################################
#
#   rcvtrip: tell message senders of recipient unavailability
#

inst-xrcvtrip:  $(RCVDIR)/rcvtrip

$(RCVDIR)/$(PREF)rcvtrip: xrcvtrip
	cp xrcvtrip $@
	-$(CHOWN) mmdf $@
	-chmod $(PGMPROT) $@

xrcvtrip:	rcvtrip.o $(MMDFLIBS)
	$(CC) $(LDFLAGS) -o $@ rcvtrip.o $(MMDFLIBS) $(SYSLIBS)

l-rcvtrip:
	lint $(LFLAGS) rcvtrip.c $(LLIBS)

###########################################################
#
#   sendmail:   A fake for the Berkeley /usr/lib/sendmail
#
inst-xsendmail:  /usr/lib/sendmail

/usr/lib/sendmail:  xsendmail
	-if test ! -f $@.orig; then cp $@ $@.orig; fi
	cp xsendmail $@
	-$(CHOWN) mmdf $@
	-chmod 04$(PGMPROT) $@

xsendmail :   sendmail.o $(MMDFLIBS)
	$(CC) $(LDFLAGS) -o $@ sendmail.o $(MMDFLIBS) $(SYSLIBS)

l-sendmail:
	lint $(LFLAGS) sendmail.c $(LLIBS)


# DO NOT DELETE THIS LINE -- make depend uses it

checkmail.o: checkmail.c
checkmail.o: ../../h/util.h
checkmail.o: ../../h/mmdf.h
checkmail.o: /usr/include/sys/stat.h
checkmail.o: /usr/include/pwd.h
checkmail.o: ../../h/ch.h
checkmail.o: ../../h/msg.h
checkmail.o: ../../h/adr_queue.h
malias.o: malias.c
malias.o: ../../h/util.h
malias.o: ../../h/mmdf.h
malias.o: ../../h/ch.h
malias.o: ../../h/dm.h
malias.o: /usr/include/pwd.h
mlist.o: mlist.c
mlist.o: ../../h/util.h
mlist.o: ../../h/mmdf.h
mlist.o: ../../h/ch.h
mlist.o: /usr/include/pwd.h
mlist.o: /usr/include/sys/stat.h
mlist.o: /usr/include/signal.h
mlist.o: /usr/include/stdio.h
newssend.o: newssend.c
newssend.o: ../../h/util.h
newssend.o: ../../h/mmdf.h
newssend.o: ../../h/cnvtdate.h
newssend.o: /usr/include/signal.h
newssend.o: /usr/include/sys/stat.h
rcvalert.o: rcvalert.c
rcvalert.o: ../../h/util.h
rcvalert.o: ../../h/mmdf.h
rcvalert.o: /usr/include/pwd.h
rcvalert.o: /usr/include/sys/stat.h
rcvfile.o: rcvfile.c
rcvfile.o: ../../h/util.h
rcvfile.o: ../../h/mmdf.h
rcvfile.o: /usr/include/pwd.h
rcvfile.o: /usr/include/sys/stat.h
rcvprint.o: rcvprint.c
rcvprint.o: ../../h/util.h
rcvprint.o: ../../h/mmdf.h
rcvtrip.o: rcvtrip.c
rcvtrip.o: ../../h/util.h
rcvtrip.o: ../../h/mmdf.h
rcvtrip.o: ../../h/ap.h
rcvtrip.o: ../../h/ch.h
rcvtrip.o: ../../h/hdr.h
rcvtrip.o: /usr/include/pwd.h
resend.o: resend.c
resend.o: ../../h/util.h
resend.o: ../../h/mmdf.h
resend.o: ../../h/cnvtdate.h
resend.o: /usr/include/pwd.h
resend.o: /usr/include/signal.h
resend.o: /usr/include/sys/stat.h
sendmail.o: sendmail.c
sendmail.o: /usr/include/signal.h
sendmail.o: /usr/include/pwd.h
sendmail.o: ../../h/util.h
sendmail.o: ../../h/mmdf.h
sendmail.o: ../../h/cnvtdate.h
v6mail.o: v6mail.c
v6mail.o: ../../h/util.h
v6mail.o: ../../h/mmdf.h
v6mail.o: ../../h/cnvtdate.h
v6mail.o: /usr/include/pwd.h
v6mail.o: /usr/include/signal.h
v6mail.o: /usr/include/sys/stat.h
# DEPENDENCIES MUST END AT END OF FILE
# IF YOU PUT STUFF HERE IT WILL GO AWAY
# see make depend above
 rcvfile.c $(LLIBS)

#########mmdf/uip/other/newssend.c   444      0     12       11124  3622772765  10720 /*
 *	A program modelled after resend to feed news articles into
 *	the Internet Mail system.
 */

#include "util.h"                 /* to get mmdf reply codes            */
#include "mmdf.h"                 /* to get mmdf reply codes            */
#include "cnvtdate.h"
#include <signal.h>
#include <sys/stat.h>

extern char     *locname;
extern char     *locdomain;

char	*logdir;
char    *strdup();

char    noret;                    /* no return address                  */
char    watchit;                  /* user wants to watch delivery       */
int	hadto;

#define FRMNONE 0                 /* no From field specified            */
#define FRMTXT  1                 /* From field had text only           */
#define FRMSNDR 2                 /* field had sender info, too         */


char *byebyelines[] = {
	"resent",
	"reply-to",
	"arpa-address",
	"status",
	"article-i.d.",
	"article-id",
	"in-reply-to",
	"relay-version",
	"posting-version",
	"date-received",
	"distribution",
	"reference",
	"summary",
	"path",
	"organization",
	"lines",
	0
};
/* **************************  MAIN  ******************************* */

main (argc, argv)
int     argc;
char   *argv[];
{
	mmdf_init (argv[0]);
	pgminit ();

	sendmail (argc, argv);
}

pipsig ()
{
	if (rp_gval (endchild (NOTOK)) == RP_NO)
		err_abrt(RP_LIO, "Abnormal return from submit\n");
	exit (NOTOK);
}

pgminit ()
{
	int     realid,
	effecid;

	signal (SIGPIPE, pipsig);    /* catch write to bad pipe            */

	getwho (&realid, &effecid);   /* who am i?                          */
	if (setuid(effecid) < 0)	/* Need to be MMDF */
		err_abrt (RP_LIO, "Unable to setuid to effecid");
}
/*^L*/

char *newschannel = "smtp";
char regargs[100] = "kqrmthUSENET*";
/* quick NS timeout, do not return on error, return to sender, mail, trustme */

sendmail (argc, argv)             /* Send a message                     */
int     argc;
char   *argv[];
{
	char	*replyto;
	int     retval;
	int	i;

	if (rp_isbad (mm_init ()) || rp_isbad (mm_sbinit ()))
		err_abrt (RP_MECH, "Unable to submit mail; please report this error");

	if (lexequ("-r", argv[1])) {
		replyto = argv[2];
		argc -= 2;
		argv += 2;
	} else
		replyto = (char *)0;

	if (argc < 3)
		err_abrt (RP_MECH, "Usage:  newssend [-r replyto] faketo realto ...");

	if (rp_isbad (mm_winit (newschannel, regargs, replyto)))
		err_abrt (RP_MECH, "problem with submit message initialization");
	for (i = 2; i < argc; i++)
		if (rp_isbad(mm_wadr((char *)0, argv[i])))
			err_abrt (RP_MECH, "problem submitting address %s", argv[i]);
	mm_waend();

	dumpheader();
	sndhdr("To:", argv[1]);
	mm_wtxt("\n", 1);

	dobody ();
	retval = endbody ();

	endchild (OK);

	exit ((rp_isbad (retval)) ? NOTOK : OK);
}
/*  *************  ARGUMENT PARSING FOR SENDMAIL  **************  */

dumpheader()
{
	char	line[LINESIZE];
	int	found;

	found = 0;
	while (fgets (line, LINESIZE, stdin) != NULL) {
		register int j;

		if (line[0] == '\n')
			break;
		if (isspace(line[0]) && found)
			continue;
		found = 0;
		for (j = 0; byebyelines[j] != NULL; j++) {
			if (prefix(byebyelines[j], line)) {
				++found;
				break;
			}
		}
		if (found)
			continue;
		found = 0;
		mm_wtxt (line, strlen (line));
	}
}

sndhdr (name, contents)
char    name[],
contents[];
{
	char    linebuf[LINESIZE];

	sprintf (linebuf, "%-10s%s\n", name, contents);
	mm_wtxt (linebuf, strlen (linebuf));
}

/**/

dobody ()
{
	char    buffer[BUFSIZ];
	register int    i;

	while (!feof (stdin) && !ferror (stdin) &&
	    (i = fread (buffer, sizeof (char), sizeof (buffer), stdin)) > 0)
		if (rp_isbad (i = mm_wtxt (buffer, i)))
			err_abrt (i, "Problem writing body");

	if (ferror (stdin))
		err_abrt (RP_FIO, "Problem reading body");
}

endbody ()
{
	struct rp_bufstruct thereply;
	int     len;

	if (rp_isbad (mm_wtend ()))
		err_abrt (RP_MECH, "problem ending submission");

	if (rp_isbad (mm_rrply (&thereply, &len)))
		err_abrt (RP_MECH, "problem getting submission status");

	if (rp_isbad (thereply.rp_val))
		err_abrt (thereply.rp_val, thereply.rp_line);

	return (thereply.rp_val);
}

/* *********************  UTILITIES  ***************************  */

endchild (type)
int     type;
{
	int retval;


	if (rp_isgood (retval = mm_sbend ()))
		retval = mm_end (type);

	return ((retval == NOTOK) ? RP_FIO : (retval >> 8));
}

/* VARARGS2 */

err_abrt (code, fmt, b, c, d)     /* terminate the process              */
int     code;                     /* a mmdfrply.h termination code      */
char   *fmt,
*b,
*c,
*d;
{
	if (fmt) {
		printf (fmt, b, c, d);
		printf (" [%s]", rp_valstr (code));
		putchar ('\n');
	}
	printf ("Message posting aborted\n");
	fflush (stdout);
	endchild (NOTOK);
	exit (NOTOK);
}
gv[2];
		argc -= 2;
		argv += 2;
	} else
		replyto = (char *)0;

	if (argc < 3)
		err_abrt (RP_MECH, "Usage:  newssend [-r replyto] faketo realto ...");

	if (rp_isbad (mm_winit (newschannel, regargs, replyto)))
		err_abrt (RP_MECH, "problem with submit message initialization");
	for (i = 2; i < argc; i++)
		if (rp_isbad(mm_wadr((char *)0, argv[i])))
			err_abrt (RP_MECH, "problem submitting address %s", argv[i]);
	mm_waend(mmdf/uip/other/sendmail.c   444      0     12       15621  3631171107  10653 /*
**  A Sendmail fake.
*/

#include <signal.h>
#include <pwd.h>
#include "util.h"
#include "mmdf.h"
#include "cnvtdate.h"

extern	char	*locname;
extern	char	*chndfldir;

extern struct passwd *getpwuid ();
extern char *getmailid ();
extern char *getenv();
extern char *dupfpath();
extern char *multcat();
extern int exit();

char	*SMTPSRVR = "smtpsrvr";

char	*FullName;	/* sender's full name */
char	*from;		/* sender's mail address */
char	subflags[128];	/* flags for submit */
int	watch;
int	verify;
int	extract;
int	badaddrs;
int	rewritefrom;

int	die();

/*ARGSUSED*/
main(argc, argv)
int argc;
char **argv;
{
	struct passwd  *pwdptr;
	register char *p;
	register char **av;
	struct rp_bufstruct thereply;
	int	retval;
	int     len;

	mmdf_init(argv[0]);

	if ((pwdptr = getpwuid (getuid())) == (struct passwd *) NULL)
		syserr("Unable to locate user's name");

	if ((from = getmailid(pwdptr -> pw_name)) == NULL)
		syserr("Unable to locate user's mailid");

	from = multcat(from, "@", locname, (char *)0);

	if (signal(SIGINT, SIG_IGN) != SIG_IGN)
		(void) signal(SIGINT, die);
	if (signal(SIGHUP, SIG_IGN) != SIG_IGN)
		(void) signal(SIGHUP, die);
	(void) signal(SIGTERM, die);
	(void) signal(SIGPIPE, die);

	FullName = getenv("NAME");

	av = argv;
	while ((p = *++av) != NULL && p[0] == '-') {
		switch (p[1]) {
		case 'b':	/* operations mode */
			switch (p[2]) {
			case 'a':	/* smtp on stdin */
			case 's':	/* smtp on stdin */
				smtp();
				exit(98);	/* should never happen */
			case 'm':	/* just send mail */
				continue;
			case 'v':	/* verify mode */
				verify++;
				continue;
			default:
				syserr("Invalid operation mode %c", p[2]);
			}
			continue;

		case 'f':	/* from address */
		case 'r':	/* obsolete -f flag */
			p += 2;
			if (*p == '\0' && ((p = *++av) == NULL || *p == '-'))
			{
				p = *++av;
				if (p == NULL || *p == '-') {
					syserr("No \"from\" person");
					av--;
					continue;
				}
			}
			if (rewritefrom) {
				syserr("More than one \"from\" person");
				continue;
			}
			from = p;
			rewritefrom++;
			continue;

		case 'F':	/* set full name */
			p += 2;
			if (*p == '\0' && ((p = *++av) == NULL || *p == '-'))
			{
				syserr("Bad -F flag");
				av--;
				continue;
			}
			FullName = p;
			continue;

		case 'h':	/* hop count */
			p += 2;
			if (*p == '\0' && ((p = *++av) == NULL || !isdigit(*p)))
			{
				syserr("Bad hop count (%s)", p);
				av--;
				continue;
			}
			continue;	/* Ignore */

		case 't':	/* read recipients from message */
			extract++;
			continue;


		case 'v':	/* give blow-by-blow description */
			watch++;
			continue;

		case 'T':	/* set timeout interval */
		case 'C':	/* select configuration file (already done) */
		case 'c':	/* connect to non-local mailers */
		case 'd':	/* debug */
		case 'e':	/* error message disposition */
		case 'i':	/* don't let dot stop me */
		case 'm':	/* send to me too */
		case 'n':	/* don't alias */
		case 'o':	/* set option, handled in line */
		case 's':	/* save From lines in headers */
			continue;
		}
	}

	setuid(getuid());

	if (verify && extract)
		syserr("Verify mode not supported on header components");

	strcpy(subflags, "kml");
	if (rewritefrom)
		strcat(subflags, "t");
	if (watch)
		strcat(subflags, "w");
	if (!extract)
		strcat(subflags, "v");
	else
		strcat(subflags, "xto,cc,bcc*");

	if (rp_isbad (mm_init ()) || rp_isbad (mm_sbinit ()))
		syserr("Unable to submit mail at this time");
	if (rp_isbad (mm_winit ((char *) 0, subflags, from)))
		syserr("Problem with submit message initialization");
	if (!extract) {
		if (rp_isbad (retval = mm_rrply (&thereply, &len)))
			syserr("Initialization reply failure");
		switch (rp_gbval (thereply.rp_val)) {
		case RP_BNO:
		case RP_BTNO:
			syserr("Initialization failure: %s", thereply.rp_line);
		}
	}

	if (*av == NULL && !extract) {
		syserr("Usage: /usr/lib/sendmail [flags] addr...");
	}
	if (!extract) {
		while (*av)
			send_address(*av++);
		if (rp_isbad (mm_waend ()))
			syserr("Problem with address list.");
	}
	if (verify) {
		mm_end(NOTOK);
		exit(badaddrs ? 1 : 0);
	}

	doheader();

	exit(dobody());
}

send_address (addr)
char    *addr;
{
	struct rp_bufstruct thereply;
	int     retval;
	int     len;

	if (watch) {
		printf ("%s:  ", addr);
		fflush (stdout);
	}

	if (rp_isbad (retval = mm_wadr ((char *)0, addr)) ||
	    rp_isbad (retval = mm_rrply (&thereply, &len)))
		syserr("Problem in send_address [%s].", rp_valstr (retval));
	switch (rp_gval (thereply.rp_val)) {
	case RP_AOK: 
		if(watch) printf ("address ok\n");
		break;

	case RP_NO: 
		if(watch) printf ("not deliverable; unknown problem\n");
		badaddrs = TRUE;
		break;

	case RP_USER: 
		if(watch) printf ("not deliverable; unknown address.\n");
		badaddrs = TRUE;
		break;

	case RP_NDEL: 
		if(watch) printf ("not deliverable; permanent error.\n");
		badaddrs = TRUE;
		break;

	case RP_AGN: 
		if(watch) printf ("failed, this attempt; try later\n");
		badaddrs = TRUE;
		break;

	case RP_NOOP: 
		if(watch) printf ("not attempted, this time; perhaps try later.\n");
		badaddrs = TRUE;
		break;

	default: 
		syserr("Unexpected address response:  %s", thereply.rp_line);
	}
	fflush (stdout);
}

doheader()
{
	int	gotfrom, gotsender, gotdate;
	char	line[LINESIZE];

	gotfrom = gotsender = gotdate = 0;
	while (fgets (line, LINESIZE, stdin) != NULL) {
		if (line[0] == '\n')
			break;
		if (prefix ("Date:", line))
			gotdate++;
		if (prefix ("From:", line)) {
			gotfrom++;
			if (rewritefrom) {
				dofrom();
				continue;
			}
		}
		if (prefix ("Sender:", line))
			gotsender++;
		mm_wtxt (line, strlen (line));
	}
	if (!gotdate) {
		strcpy (line, "Date: ");
		cnvtdate (TIMREG, line+6);
		strcat (line, "\n");
		mm_wtxt (line, strlen(line));
	}
	if (!gotfrom)
		dofrom();
	if (!gotsender) {
		sprintf(line, "Sender: %s\n", from);
		mm_wtxt (line, strlen(line));
	}
	mm_wtxt("\n", 1);
}

dofrom()
{
	char	line[128];

	if (isstr(FullName))
		sprintf(line, "From: %s <%s>\n", FullName, from);
	else
		sprintf(line, "From: %s\n", from);
	mm_wtxt(line, strlen(line));
}

dobody()
{
	struct rp_bufstruct thereply;
	char    buffer[BUFSIZ];
	register int    i;
	int     len;

	while (!feof (stdin) && !ferror (stdin) &&
	    (i = fread (buffer, sizeof (char), sizeof (buffer), stdin)) > 0)
		if (rp_isbad (i = mm_wtxt (buffer, i)))
			syserr("Problem writing body");

	if (ferror (stdin))
		syserr("Problem reading body");

	if (rp_isbad (mm_wtend ()) || rp_isbad (mm_rrply (&thereply, &len)))
		syserr("problem ending submission");

	if (rp_isbad (thereply.rp_val))
		syserr("%s", thereply.rp_line);

	return(0);	/* eventually the program exit value */
}

smtp()
{
	char	*smtpd = dupfpath(SMTPSRVR, chndfldir);

	setuid(geteuid());	/* Must become "mmdf" for real */
	execl (smtpd, "sendmail-smtp", from, locname, "local", (char *)0);
	perror(smtpd);
	exit(9);
}

/*VARARGS1*/
syserr(fmt, a, b)
char	*fmt, *a, *b;
{
	fprintf(stderr, fmt, a, b);
	fputc('\n', stderr);
	exit(9);
}

die(sig)
int sig;
{
	mm_end(NOTOK);
	fprintf(stderr, "sendmail: dying from signal %d\n", sig);
	exit(99);
}
line */
		case 's':	/* save From lines in headers */
			continue;
		}
	}

	setuid(getuid());

	if (verify && exmmdf/uip/Makefile   444      0     12        1666  3657737774   7250 #	UIP Subtree Makefile
#
#
# Programs that live in subdirectories, and have makefiles of their own.
#
SUBDIR=	msg send other # ucbmail

all:
	for dir in ${SUBDIR}; do \
		(cd $${dir}; gen ${MFLAGS} -${MAKEFLAGS}); done

install:
	for dir in ${SUBDIR}; do \
		(cd $${dir}; gen ${MFLAGS} -${MAKEFLAGS} install); done

dist:
	-touch `ls */x*|grep -v '\.'`
	echo echo UIP Distribution made `date` > install.sh
	for dir in ${SUBDIR}; do \
		echo cd $${dir} >> install.sh; \
		( cd $${dir}; \
		  gen -n ${MFLAGS} -${MAKEFLAGS} install) >> install.sh; \
		echo cd .. >> install.sh; \
	done
	tar cvf uipdist.tar install.sh `ls */x*|grep -v '\.'` ucbmail/misc/Mail.help*

depend:
	for dir in ${SUBDIR}; do \
		(cd $${dir}; gen ${MFLAGS} -${MAKEFLAGS} depend); done

lint:
	for dir in ${SUBDIR}; do \
		(cd $${dir}; gen ${MFLAGS} -${MAKEFLAGS} lint); done

clean:
	-rm -f make.out
	for dir in ${SUBDIR}; do \
		(cd $${dir}; make -f Makefile.real clean); done
ruct thereply;
	char    buffer[BUFSIZ];
	register int    i;
	int     len;
mmdf/uip/send/   755      0     12           0  3671117116   6417 mmdf/uip/send/gen   555      0     12          57  3620510617   7140 make -f ../../Makefile.com -f Makefile.real $*
 in subdirectories, and have makefiles of their own.
#
SUBDIR=	msg send other # ucbmail

all:
	for dir in ${SUBDIR}; do \
		(cd $${dir}; gen ${MFLAGS} -${MAKEFLAGS}); done

install:
	for dir in ${SUBDIR}; do \
		(cd $${dir}; gen ${MFLAGS} -${MAKEFLAGS} install); done

dist:
	-touch `ls */x*|grep -v '\.'`
	echo echo UIP Distribution made `date` > install.sh
	for dir in ${SUBDIR}; do \
		echo cd $${dir} >> install.sh; \
		( cd $${dir}; \
		  gen -n ${MFLAGS} -${Mmmdf/uip/send/s.h   444      0     12        2600  3631171112   7103 /*
**		S . H
**
**  
**	This file contains definitions required for the SEND program.
**
**	R E V I S I O N  H I S T O R Y
**
**	03/31/83  GWH	Split the SEND program into component parts
**
**	10/19/83  GWH	Added a counter to the ALIAS structure to prevent
**			disaster in local aliasfile loops.
**
*/

#include "util.h"
#include "mmdf.h"
#include "ch.h"
#include <pwd.h>
#include <signal.h>
#include "cnvtdate.h"
#include "./s_tailor.h"

#define DREND           2
#define DRBEGIN         0

#define HDRPRT          YES
#define HDRSIL          NO

#define FLDREMV         0
#define FLDSAME         1
#define FLDOVER         2
#define FLDADD          3
#define	ITMRMV		4

#define BBSIZE		513
#define	S_BSIZE		1024

/* Define somethings for use in "getuinfo" */

#define COPYFILE	0
#define DRAFTDIR	1
#define SIGNATURE	2
#define NOSIGNATURE	3
#define REPLYTO		4
#define ALIASES		5
#define SUBARGS		6
#define EDITOR		7
#define VEDITOR		8
#define CFILE		9
#define QFILE		10
#define NOFILE		11
#define DIREDIT		12
#define CHECKER		13
#define ADDHEADER	14

/* Macro definition for use in s_get.c */

#define MAX(a,b) (a > b ? a : b )
struct RCOPTION {
	char *name;
	int idnum;
};
struct ALIAS {
	char *key;
	int cntr;
	char *names;
	struct ALIAS *next_int;
};
struct header {
	char	*hname;
	char	hdata[S_BSIZE];
	struct	header *hnext;
};

#define	NARGS	20	/* Maximum number of args to set and in .sendrc lines */
p_val))
		syserr("%s", thereply.rp_line);

	return(0);	/* eventually the program exit value */
}

smtp()
{
	char	*smtpd = dupfpammdf/uip/send/s_arginit.c   444      0     12        6123  3652627017  10634 /*
**		S _ A R G I N I T
**
**  This function parses and uses the information found on the command
**  line.
**
**
**	R E V I S I O N  H I S T O R Y
**
**	03/31/83  GWH	Split the SEND program into component parts
**
*/

#include "./s.h"
#include "./s_externs.h"

char *UsageMessage = 
"Usage:  send addrs -a hdr:val -{bct} addrs -d{ev} -f file -h host -s subj -n\n";

arginit (argc, argv)              /* parse and use the argument list    */
int     argc;
char   *argv[];
{
    char *curfld;
    char *cp;
    register short curarg;

    strcpy (host, locname);       /* setup default host reference       */

/*  if an argument is not a switch, assume that it is an address.
 *  several of the switches merely change which header addresses are
 *  to be added to.  after an argument is processed, the pointer to
 *  it is nulled, so that it will no longer be visible by a system-
 *  status command ("ss" on the UDel machine).
 */

    for (curfld = to, curarg = 1; curarg < argc; )
    {                             /* regular scan of arg list           */
				  /* default to adding to To field      */
	if (argv[curarg][0] != '-')
	    addrcat (curfld, argv[curarg], host);
				  /* add it to current field            */
	else {                    /* a switch                           */ 
	    switch (argv[curarg][1])
	    {
	    	case 'a':
	    	    if (++curarg >= argc)
	    		break;
	    	    if (cp = index(argv[curarg], ':'))
	    		*cp++ = '\0';
		    addheader(argv[curarg], cp);
		    break;

		case 'b':         /* add to end of bcc field            */
		    curfld = bcc;
		    break;

		case 'c':         /* add to end of cc field             */
		    ccflag = FALSE;
		    curfld = cc;
		    break;

		case 'd':	  /* alter directedit option		*/
		    switch (argv[curarg][2]) {
			case '\0':
			    dflag = 0;
			    break;
			case 'e':
			    dflag = 2;
			    break;
			case 'v':
			    dflag = 1;
			    break;
			default:
				printf("bad direct edit option\n");
			}
			break;

		case 'f':         /* add file to end of message body    */
	    	    if (++curarg >= argc)
	    		break;
		    strcpy (inclfile, argv[curarg]);
		    break;        /* file is named in next argument     */

		case 'h':         /* default hostname for addresses     */
	    	    if (++curarg >= argc)
	    		break;
		    strcpy (host, argv[curarg]);
		    break;        /* host is named in next argument     */

	    	case 'n':
	    	    noinputflag++;
	    	    break;

		case 's':         /* add to end of Subject field        */
		    subjflag = FALSE;
	    	    if (++curarg >= argc)
	    		break;
		    strncat (subject, argv[curarg],
				(S_BSIZE - strlen(subject) - 2));
		    break;        /* Subject is contained in next arg   */

		case 't':         /* add to end of To field             */
		    toflag = FALSE;
		    curfld = to;
		    break;

		default:
		    snd_abort ("unknown flag in '%s'\n%s", argv[curarg],
				UsageMessage);
	    }
	}
	if (argv[curarg])
		argv[curarg++][0] = '\0';
    }
    if (to[0] != '\0')            /* at least one To was specified      */
	toflag = FALSE;           /* so don't prompt for it             */
}
p_gbval (thereply.rp_val)) {
		case RP_BNO:
		case RP_BTNO:
			syserr("Initialization failure: %s", thereply.rp_line);
		}
	}

	if (*av == NULL && !extract) {
		syserr("Usage: /usr/lib/sendmail [flags] addr...");
	}
	if (!extract) {
		while (*av)
			send_address(*av++);
		if (rp_isbad (mm_waend ()))
			syserr("Problem with address list.");
	}
	if (verify) {
		mm_end(NOTOK);
		exit(badaddrs ? 1 : 0);
	}

	doheader();

	exit(dommdf/uip/send/s_do.c   444      0     12       12127  3620510620   7604 /*
** 		D O _ *
**
**
**	R E V I S I O N  H I S T O R Y
**
**	03/31/83  GWH	Split the SEND program into component parts
**			This module contains do_text, do_hdr,
**			next_address, and snd_abort.
**
**	05/31/83  GWH	Added code for the updating of a copyfile.
*/
#include "./s.h"
#include "./s_externs.h"

int     sentfd;

do_text (isbcc)
    int isbcc;                    /* true, if bcc copy                  */
{
    extern char *cnvtdate ();
    struct header *hp;
    struct rp_bufstruct thereply;
    int     retval;
    int     i;
    int     len;
    char    date[32];
	if( cflag > 0){
	    if ((sentfd = open (copyfile, 1)) >= 0)
		lseek (sentfd, 0l, 2);    /* set up to save a copy           */
	    else
		sentfd = creat (copyfile, sentprotect);
	    if (sentfd >= 0)              /* terminate saved copy            */
		write (sentfd, delim1, strlen (delim1));
	}
    cnvtdate (TIMREG, date);      /* arpanet style date/time            */
    do_hdr (datename, date);
    do_hdr (fromname, signature);

    if (to[0] == '\0')
	do_hdr (toname, "list: ;"); /* no To: addresses, so fake it       */
    else
	do_hdr ((isbcc) ? BCtoname : toname, to);

    if (cc[0] != '\0')
	do_hdr ((isbcc) ? BCccname : ccname, cc);

    if (subject[0] != '\0')
	do_hdr (subjname, subject);

    if (isbcc && to[0] != '\0')   /* put empty Bcc: field in bcc copies */
	do_hdr (bccname, "(Private)");
				  /* when there also are To addresses   */
    for (hp = headers; hp != NULL; hp = hp->hnext)
	if (isstr(hp->hdata))
	    do_hdr (hp->hname, hp->hdata);

    mm_wtxt ("\n", 1);           /* blank line separate headers & body */
	if( cflag > 0 ) {
	    if (sentfd >= 0)
		write (sentfd, "\n", 1);
	}

    dropen (DRBEGIN);

    while ((i = read (drffd, bigbuf, BBSIZE - 1)) > 0)
    {                             /* copy the body                      */
	if (rp_isbad (retval = mm_wtxt (bigbuf, i)))
	    snd_abort ("Problem with writing text buffer [%s].\n", rp_valstr (retval));
	if( cflag > 0 ){
		if (sentfd >= 0)	  /* save the body                  */
		    write (sentfd, bigbuf, i);
	}
    }

	if(cflag > 0 ){
	    if (sentfd >= 0)              /* terminate saved copy           */
	    {
		if (bigbuf[i - 1] != '\n')
		    write (sentfd, "\n", 1);
					 /* make sure it ends with newline  */
		write (sentfd, delim2, strlen (delim2));
		close (sentfd);
	    }
	}

    if (rp_isbad (retval = mm_wtend ()))
	snd_abort ("Problem with ending write of text buffer [%s].\n", rp_valstr (retval));
    if (rp_isbad (retval = mm_rrply (&thereply, &len)))
	snd_abort ("Problem reading reply to text write [%s].\n", rp_valstr (retval));
    switch (rp_gval (thereply.rp_val))
    {                             /* how did MMDF like it?              */
	case RP_OK: 
	case RP_MOK: 
	    break;		  /* Text ok */

	case RP_NO: 
	    snd_abort ("Error in text transmission : General purpose no. RP_NO.\n");
	    break;

	case RP_NDEL: 
	    snd_abort ("Error in text transmission : Could not deliver. RP_NDEL.\n");
	    break;

	case RP_AGN: 
	    snd_abort ("Error in text transmission : Not now, maybe later. RP_AGN.\n");
	    break;

	case RP_NOOP: 
	    snd_abort ("Error in text transmission : Nothing done, this time.\n");
	    break;

	default: 
	    snd_abort (" Unexpected text response:  [%s] %s\n",
			rp_valstr (thereply.rp_val), thereply.rp_line);
    }
    return (RP_OK);
}

do_hdr (name, data)               /* send out a header field            */
    char name[],                  /* name of field                      */
	*data;                    /* value-part of field                */
{
    int retval;
    register int ind;
    register int thesize;
    register char *curptr;

    for (curptr = data, ind = 0; ind >= 0; curptr += ind + 1)
    {
	if ((ind = strindex ("\n", curptr)) >= 0)
	    curptr[ind] = '\0';   /* format lines properly              */

	sprintf (bigbuf, hdrfmt, (curptr == data) ? name : "", curptr);
				  /* begin with blank on added lines    */
	if (rp_isbad (retval = mm_wtxt (bigbuf, (thesize = strlen (bigbuf)))))
	    snd_abort ("Problem with writing header buffer [%s].\n", rp_valstr (retval));

	if(cflag > 0 ){
		if (sentfd >= 0)          /* save the message?                  */
		    write (sentfd, bigbuf, thesize);
	}

	if (ind >= 0)
	    curptr[ind] = '\n';   /* put it back                        */
    }
}


next_address (addr)
char    *addr;
{
    int     i = -1;               /* return -1 = end; 0 = empty         */

    for (;;)
	switch (*adrptr)
	{
	    default: 
		addr[++i] = *adrptr++;
		continue;

	    case '"':
		for (addr[++i] = *adrptr++; !isnull (*adrptr); )
		{
		    addr[++i] = *adrptr;
		    if (*adrptr++ == '"')
			break;
		}
		continue;

	    case '(':
		for (addr[++i] = *adrptr++; !isnull (*adrptr); )
		{
		    addr[++i] = *adrptr;
		    if (*adrptr++ == ')')
			break;
		}
		continue;

	    case '\n':
	    case ',': 
		addr[++i] = '\0';
		adrptr++;
		return (i);

	    case '\0': 
		if (i >= 0)
		    addr[++i] = '\0';
		return (i);
	}
}

/* VARARGS1 */

snd_abort (fmt, b, c, d, e, f)
char   fmt[], b[], c[], d[], e[], f[];
{
    printf ("\n\t");
    printf (fmt, b, c, d, e, f);
    fflush (stdout);
    longjmp (savej, 1);
}
 else
	do_hdr ((isbcc) ? BCtoname : toname, to);

    if (cc[0] != '\0')
	do_hdr ((isbcc) ? BCccname : ccname, cc);

    if (subject[0] != '\0')
	do_hdr (subjname, subject);

    if (isbcc && to[0] != '\0')   /* put empty Bcc: field in bcc copies */
	do_hdr (bccname, "(Private)");
				  /* when there also are To addresses   */
    for (hp = headers; hp != NULL; hp = hp->hnext)
	if (isstr(hp->hdata))
	    do_hdr (hp->hnamemmdf/uip/send/s_drfile.c   444      0     12        2407  3620510621  10430 /*
**		S _ D R F I L E
**
**   This file contains routines for opening and closing the draft file
**   and for truncating the draft file.
**
**
**	R E V I S I O N  H I S T O R Y
**
**	03/31/83  GWH	Split the SEND program into component parts
**			This module contains dropen and drclose.
**
*/

#include "./s.h"
#include "./s_externs.h"

dropen (seektype)
    int seektype;
{
    extern long lseek ();

    if (drffd <= 0)
    {
	if ((drffd = open (drffile, 2)) < 0)
	{
	    printf ("Unable to open draft '%s'.\n", drffile);
	    fflush (stdout);
	    s_exit (-1);
	}
	if (chmod (drffile, 0400) < 0)
	{                         /* protect against crashes            */
	    printf ("Unable to protect draft '%s'.\n", drffile);
	    fflush (stdout);
	    s_exit (-1);
	}
    }
    lseek (drffd, 0L, seektype);
}

drclose ()
{
    if (drffd > 0)
    {
	close (drffd);
	drffd = 0;
	if (chmod (drffile, sentprotect) < 0)
	{                         /* protect against crashes            */
	    printf ("Unable to unlock draft '%s'.\n", drffile);
	    fflush (stdout);
	}
    }
}

drempty ()
{
    drclose ();
    if ((drffd = creat(drffile, sentprotect)) >= 0)
        drclose ();
    else
    {
	printf(" Can't open draft file '%s'.\n", drffile);
	fflush (stdout);
	s_exit( NOTOK );
    }
}
          /* copy the body                      */
	if (rp_isbad (retval = mm_wtxt (bigbuf, i)))
	    snd_abort ("Problem with writing text buffer [%s].\n", rp_valstr (retval));
	if( cflag > 0 ){
		if (sentfd >= 0)	  /* save the body                mmdf/uip/send/s_gather.c   444      0     12        2270  3620510621  10433 /* name:
	gather

function:
	read a single line of console input into the user specified
	buffer.

algorithm:
	Read a character:
	If it is an unescaped new line, null terminate the buffer and return.
	Otherwise, stash the character into the buffer.
	If the length of the buffer has been reached, echo a new line and
	return.

parameters:
	*char   pointer to user buffer of length S_BSIZE

returns:
	nothing

globals:
	S_BSIZE

calls:
	getchar

called by:
	main
	send

*/
/*
**	R E V I S I O N  H I S T O R Y
**
**	03/31/83  GWH	Split the SEND program into component parts
**
*/

#include "./s.h"
#include "./s_externs.h"

gather (sp, max)
char   *sp;
register int max;
{
    register int    c;
    register char   *p;

    fflush (stdout);

    for (p = sp; max-- > 0; *p++ = c)
    {
	if ((c = getchar ()) == EOF)
	    if (ferror (stdin) || feof (stdin))
	    {                   /* something really happened            */
		*p = '\0';
		return (-1);
	    }

	c = toascii (c);        /* make sure it's valid                 */

	if (c == '\n')          /* normal end on newline                */
	    break;
    }

    *p = '\0';
    return (p - sp);              /* return length                      */
}
ect draft '%s'.\n", drffile);
	    fflush (stdout);
	    s_exit (-1);
	}
    }
    lseek (drffd, 0L, seektype);
}

drclose ()
{
    if (drffd > 0)
    {
	close (drffd);
	drffd = 0;
	if (chmod (drffile, sentprotect) < 0)
	{                         /* protect against crashes            */
	    printf ("Unable to unlock draft '%smmdf/uip/send/s_get.c   444      0     12       46560  3671105051   7775 /*
**		S _ G E T
**
**
**	R E V I S I O N  H I S T O R Y
**
**	03/31/83  GWH	Split the SEND program into component parts
**			This module contains getuinfo, gethead, prefix,
**			getaddr, and addrcat.
**
**	04/15/83  DPK	Changed address format from "user at host" to
**			"user@host". Also added call to getmailid() in get
**			getuinfo();
**
**	05/20/83  GWH	Modified getuinfo() to allow a number of user-
**			selectable options. These include the use of a
**			".sendrc" file on the user's home directory. With
**			the use of the "rc" file the user can set such things
**			default editor, local-alias-file, signature block,
**			and a directory for draft files. Also included is a
**			provision for keeping file copies of messages that
**			are successfully copied (incomplete at this time).
**
**	06/22/83  GWH	Added the required code to addrcat to enable local
**			alias file feature.
**
**	10/21/83  GWH	Fixed a bug in the alias file code (in addrcat).
**			Prevents segmentation faults when loops are found
**			in the alias file definitions.
**
**	11/16/83  DPK	Merged in DPK signature checking and Geo loop catcher
**
**	11/21/83  GWH	Added 'directedit' option.
**
**	03/18/85  DPK	Fixed usage of parsadr function so that after parsing
**		  VAK	we now use the components instead of original string.
**
*/


#include <stdio.h>
#include "./s.h"
#include "./s_externs.h"

extern char *getmailid();
extern char *malloc();
extern char *strdup();
extern struct passwd *getpwuid();
extern char *mktemp();
extern char *index();
extern char *getenv();

extern char *dfleditor;
extern char *dflveditor;
extern char *dflchecker;

#define END	(char *)0

struct RCOPTION rcoptions[] = {
	"copyfile", COPYFILE,
	"draftdir", DRAFTDIR,
	"signature", SIGNATURE,
	"nosignature", NOSIGNATURE,
	"replyto", REPLYTO,
	"aliases", ALIASES,
	"subargs", SUBARGS,
	"editor", EDITOR,
	"veditor", VEDITOR,
	"file", CFILE,
	"fileonquery", QFILE,
	"nofile", NOFILE,
	"directedit", DIREDIT,
	"checker", CHECKER,
	"header", ADDHEADER,
	END
};

int picko();

struct passwd *pw;
char *midp;
char *d_subargs = "vm";
char *drft_tmplt = "drft.XXXXXX";
char linebuf[128];
char tmpline[128];

struct ALIAS *als, alias_ptr;
struct ALIAS *old_als;
FILE *afd;

getuinfo() {
	FILE *fp;
	int realid, effecid;
	char *av[NARGS];
	char rcfilename[128];
	register char *p;
	char *rcname = "/.sendrc";

	int i;

	/* build a draft file name */

	mktemp(drft_tmplt);
	/* Who are we this time */

	getwho(&realid, &effecid);

	/* If nothing found return false. */

	if(( pw = getpwuid(effecid)) == NULL  ||
		(midp = getmailid(pw->pw_name)) == NULL )
			return(FALSE);

	/* Build a name for this user */
#ifdef PWNAME
	if (p = index( pw->pw_gecos, ',' )) *p = '\0';
	if (p = index( pw->pw_gecos, '<' )) *p = '\0';
	if (p = index( pw->pw_gecos, '&' )) {
		/* Deal with Berkeley folly */
		*p = 0;
		sprintf( from, "%s%c%s%s <%s@%s>", pw->pw_gecos,
			 lowtoup( pw->pw_name[0] ),
			 pw->pw_name+1, p+1, midp, locname );
	} else
		sprintf( from, "%s <%s@%s>", pw->pw_gecos, midp, locname);
#else
	sprintf( from, "%c%s@%s",	/* default from text */
		lowtoup( midp[0] ), &(midp[1]), locname);
#endif
	/* Set default values for those options that need defaults */

	strcpy(editor, ((p = getenv("EDITOR")) && *p) ? p : dfleditor);
	strcpy(veditor, ((p = getenv("VISUAL")) && *p) ? p : dflveditor);
	strcpy(checker, dflchecker);
	sprintf(linebuf, "%s/.sent", pw->pw_dir);
	strcpy(copyfile, linebuf);
	sprintf(drffile, "%s/%s", pw->pw_dir, drft_tmplt);
	strcpy( subargs, d_subargs );
	wflag = 0;
	cflag = 0;
	aflag = 0;
	rflag = 0;
	qflag = 0;
	dflag = 0;

       	strcpy (signature, from);


	/* Get info from the ".sendrc" file */

	sprintf( rcfilename, "%s/%s", pw->pw_dir, rcname );
	if(( fp = fopen(rcfilename, "r")) != NULL ) {
		char *cp;

		/* Process info a line at a time */
		while( fgets(linebuf, sizeof(linebuf), fp ) != NULL ) {
			if (cp = index(linebuf, '\n'))
				*cp = 0;
			if (sstr2arg(linebuf, NARGS, av, " \t") < 0)
				continue;
			if( (i=picko(av[0])) < 0)
				continue; /* bad option */
			select_o( i, av );
		}	/* end of while on lines */
	}	/* end of if statement on file open */

	return(TRUE);
}	/* end of getuinfo routine */

/* select an option */

picko (pp)
char *pp;
{
	struct RCOPTION *rcopt;

	rcopt = rcoptions;
	while(rcopt->name != END ) {
		if( lexequ( rcopt->name, pp ) )
			return(rcopt->idnum);
		rcopt++;
	}

	return(-1); /* no match */
}

get_key( s, key, list )
char *s, **key, **list;
{
	char tmps[512];
	char *ptmps;
	int len_list;
/*
**  This routine takes the input string s and divides it into two parts,
**  the first of which consists of the first word in the original string.
**  It is returned in key. The second part is a list of the remaining names.
**  They are returned in the list. Malloc is used to allocate the necessary
**  storage for the sublists.
*/

	/* throw away initial white space */

	while( *s == ' ' || *s == '\t' )
		*s++;

	/* get the first word. It may be terminated by spaces, tabs, commas, */
	/* newlines or end-of-file 					     */

	ptmps = tmps;
	while( *s != ' ' && *s != '\t' && *s != ',' && *s != '\n' && *s != EOF){
		*ptmps++ = *s++;
	}
	*ptmps = 0;
	len_list = strlen(tmps);
	if(( *key = malloc( (unsigned) (len_list) + 1 )) <= 0 ) {
		perror(" SEND - GET_KEY -- MALLOC ERROR");
		return(-1);
	}

	strcpy( *key, tmps );

	/* and now the rest of the list */

	ptmps = s;
	while( *s != '\n' && *s != EOF && *s != '\0' )
		*s++;
	*s = 0;
	len_list = strlen( ptmps );
	if(len_list <= 0 )
		return( -1 ); /* no list - ERROR - ERROR */
	if((*list = malloc( (unsigned) (len_list) +1 )) <= 0) {
		perror(" SEND GET_KEY - MALLOC ERROR");
		return( -1 );
	}

	strcpy( *list, ptmps );
	return(0);
}

select_o (key, pp)
int key;
char **pp;
{
	register char *p;
	char *akey, *list;
	char tmpbuf[512];

	switch( key ) {
	case COPYFILE:
		/* Pick a file to store file copies of sent messages */
		strcpy( copyfile, *(++pp));
		if( copyfile[0] != '/' ) {
			strcpy(tmpline, copyfile);
			sprintf(copyfile, "%s/%s",pw->pw_dir,tmpline);
		}
		break;

	case DRAFTDIR:
		/* pickup the draft directory */
		if( *(++pp) != 0 )
			sprintf( drffile, "%s/%s", *pp, drft_tmplt);
		else
			sprintf( drffile, "%s/%s", pw->pw_dir, drft_tmplt);
		break;

	case SIGNATURE:
		/* If he wants a signature block use it */
		if( *(++pp) == 0 ){
#ifdef PWNAME
			sprintf( signature, "%c%s@%s", /* use his login name alone */
			   lowtoup(midp[0]), &(midp[1]), locname );
#else
			strcpy( signature, from );	/* NO-OP ? */
#endif
		} else {
			linebuf[0] = '\0';
			while( *pp != 0){
				strcat(linebuf, *pp);
				strcat(linebuf, " ");
				*pp++;
			}
			if (checksignature (linebuf) == 0) {
				strcpy(tmpline, locname);
				for( p=tmpline ; *p ; p++)
					*p = uptolow(*p);
				sprintf( signature, "%s <%s@%s>",
				   linebuf, midp, tmpline);
			} else {
				printf ("Illegal signature, using default\n");
			}
		}
		break;

	case NOSIGNATURE:
#ifdef PWNAME
		sprintf( signature, "%c%s@%s", /* use his login name alone */
			   lowtoup(midp[0]), &(midp[1]), locname );
#else
		strcpy( signature, from );
#endif
		break;

	case REPLYTO:
		rflag = 1;
		break;

	case ALIASES:
		aflag = 1;
		strcpy( aliasfilename, *(++pp) );
		if( aliasfilename[0] != '/' ) {
			strcpy(tmpline, aliasfilename);
			sprintf(aliasfilename, "%s/%s",pw->pw_dir,tmpline);
		}

		/*
		**  Open the alias file and store the keys and lists
		**  in core
		*/

		afd = fopen( aliasfilename, "r");
		if( afd <= NULL ) {
			aflag = 0;
			perror(" Can't open alias file ");
			break;
		}
		als = &alias_ptr;
		old_als = NULL;
		while(1) {
			if(fgets(tmpbuf, sizeof(tmpbuf), afd) != NULL) {
				get_key(tmpbuf, &akey, &list);
				if( old_als > 0 ) {
					if(( als = (struct ALIAS *) malloc(sizeof(alias_ptr))) <= 0) {
						perror( "SEND GETUINFO - MALLOC ERROR");
						break;
					}
					old_als->next_int = als;
				}
				als->key = akey;
				als->cntr = 0;
				als->names = list;
				old_als = als;
			}
			else {
				old_als = NULL;
				break;
			}
		}
		break;

	case SUBARGS:
		strcpy (subargs, d_subargs);	/* Reset to default (required) */
		while( *(++pp) != NULL )
			strcat(subargs, *pp);
		wflag = 1;
		break;

	case EDITOR:
		/* Default editor for use with the "e" command */
		strcpy( editor, *(++pp) );
		break;

	case VEDITOR:
		/* Default editor for use with the "v" command */
		strcpy( veditor, *(++pp) );
		break;

	case CHECKER:
		/* Default spelling checker for use with the "c" command */
		strcpy( checker, *(++pp) );
		break;

	case DIREDIT:
		dflag = (( **(++pp) == 'e' ) ? 2 : 1);
		break;

	case CFILE:
		cflag = 1;
		break;

	case QFILE:
		qflag = 1;
		/* DROP */
	case NOFILE:
		cflag = 0;
		break;

	case ADDHEADER:
		/* Add a header line to the message */
		++pp;
		if (isstr(*pp))
			addheader(pp[0], pp[1]);
		break;

	default:
		printf( "GETUINFO - NO SUCH OPTION key = %d\n", key);

	} /* end of switch */
}
/* Prompts for a header with print as prompt and gathers it up */

gethead (print, buf, flag)
char    print[];
register char   *buf;
int     flag;                     /* print contents, if any?            */
{
    char    c;
    int retval;                   /* type of FLD modification           */
    register int    n;
    register int    max = S_BSIZE;

    if (flag && *buf)             /* print what already is there        */
	printf (shrtfmt, print, buf);

    flag = TRUE;                  /* always & only look at first char   */
    do
    {
	printf ("%s", print);     /* prompt                             */
	if (*buf == '\\')
	{
	    *buf++ = ',';
	    *buf++ = '\n';
	}
	fflush (stdout);

	if (flag)
	{
	    flag = FALSE;         /* ignore first char of next lines    */
	    switch (c = getchar ())
	    {
		case '-':         /* delete single name                */
			retval = ITMRMV;
			break;
		case '#':         /* delete all contents     */
		    *buf = 0;
		    while (getchar () != '\n');
		    return (FLDREMV);

		case '\n':        /* leave it as it is                  */
		    return (FLDSAME);

		case '+':         /* append to end of field             */
		    retval = FLDADD;
		    if (*buf != '\0')
		    {                 /*   if not already empty             */
			buf = &buf[strlen (buf)];
				      /* point to end of buf                */
			*buf++ = ','; /* assume it's an address field       */
			*buf++ = ' ';
			max -= 2;
		    }
		    break;

		default:          /* copy over it                       */
		    retval = FLDOVER;
		    *buf++ = c;
		    max--;
		    break;
	    }
	}
	n = gather (buf, max);    /* get rest of header line            */
	max -= n;
	buf = &buf[n - 1];
    }
    while (*buf == '\\');         /* user wants to add more?            */
    return (retval);
}

prefix (buf, pre)
char   *buf,
       *pre;
{
    while (*pre)
	if (*buf++ != *pre++)
	    return (FALSE);
    return (TRUE);
}


getaddr (print, buf, flag, defhost)
    char print[];
    char *buf;
    int flag;
    char defhost[];
{
    char addrin[S_BSIZE];     /* temporary store for user input     */
    char    *dn, *gstrindex();

    if (flag && *buf)             /* print what already is there        */
	printf (shrtfmt, print, buf);

    addrin[0] = '\0';             /* mark temp buffer as empty          */
    switch (gethead (print, addrin, NO))
    {
	case FLDREMV:             /* erase any existing contents        */
	    buf[0] = '\0';
	    break;

	case ITMRMV:		 /* delete single name			*/
		dn = gstrindex(addrin, buf);
		if(dn <= 0){
			printf(" no such name in the list; name = '%s'\n",addrin);
			break;
		}
		dlt_name( buf, dn );
	    break;
	case FLDSAME:             /* leave as is                        */
	    break;

	case FLDOVER:             /* overwrite existing contents        */
	    buf[0] = '\0';        /* erase existing, then add           */
	    addrcat (buf, addrin, defhost);
	    break;

	case FLDADD:              /* append to existing                 */
	    addrcat (buf, addrin, defhost);
	    break;
    }
}

	/* these varibles are made global for recursive reasons */

    int linelen,
	destlen,
	addrlen;

addrcat (dest, src, thehost)
    char *dest,
	 *src,
	thehost[];
{
    char addr[S_BSIZE],
	 name[ADDRSIZE],
	 address[ADDRSIZE],
	 temphost[FILNSIZE],
	 defhost[FILNSIZE],
	 hostname[FILNSIZE];
    char *ptr;
    char *sadrptr;
    int sub_flag;

    if (thehost != 0)                /* save official version of default   */
	strcpy (defhost, thehost);

    for (ptr = dest, linelen = 0; *ptr != '\0'; ptr++, linelen++)
	if (*ptr == '\n')         /* get length of last line of header  */
	    linelen = -1;         /* make it zero for next character    */
    destlen = ptr - dest;

    for (adrptr = src;; )         /* adrptr is state variable, used in  */
    {                             /*   next_address                     */
	switch (next_address (addr))
	{
	    case -1:              /* all done                           */

		/* clean markers in aliases structure */
		als = &alias_ptr;
		while(als != NULL ){
			als->cntr = 0;
			als = als->next_int;
		}
		return;

	    case 0:               /* empty address                      */
		continue;
	}

	name[0] =
	    address[0] =
	    temphost[0] = '\0';
	parsadr (addr, name, address, temphost);
	if (address[0] == '\0')
	{                         /* change the default host            */
	    if (temphost[0] != '\0')
		strcpy (defhost, temphost);
	    continue;
	}
	if( aflag && temphost[0]==0){
		als = &alias_ptr;
		sub_flag = 0;
		while( als != NULL ){
			if( lexequ(address, als->key) && als->cntr == 0 ){
				als->cntr++;
				sadrptr = adrptr;
				addrcat(dest, als->names, thehost);
				adrptr = sadrptr;
				sub_flag = 1;
				break;
			}
			else
				if( lexequ(address, als->key) && als->cntr != 0 ){
					sub_flag = 1;
					break;
				}
			als = als->next_int;
		}
		if(sub_flag == 1)
			continue;
	}
	if (temphost[0] == '\0')
	    strcpy (hostname, defhost);
	else
	    strcpy (hostname, temphost);

	if (name[0] != '\0')      /* put into canonical form            */
	{                         /* name <mailbox@host>                */
	    for (ptr = hostname; !isnull (*ptr); ptr++)
		*ptr = uptolow (*ptr);
				  /* force hostname to lower            */
	    for (ptr = name; ; ptr++)
	    {                     /* is quoting needed for specials?    */
		switch (*ptr)
		{
		    default:
			continue;

		    case '\0':    /* nope                               */
			sprintf (addr, "%s <%s@%s>",
				name, address, hostname);
			goto doaddr;

		    case '<':     /* quoting needed                     */
		    case '>':
		    case '@':
		    case ',':
		    case ';':
		    case ':':
			sprintf (addr, "\"%s\" <%s@%s>",
				    name, address, hostname);
			goto doaddr;
		}
	    }
	}
	else
	{
	    sprintf (addr, "%s@%s", address, hostname);
	}

doaddr:
	addrlen = strlen (addr);

	if ((destlen + addrlen) < (S_BSIZE - 15))
	{                         /* it will fit into dest buffer       */
	    if (destlen == 0)
		strcpy (dest, addr);
	    else          /* monitor line length                */
	    {
		if ((linelen + addrlen) < 67)
		    sprintf (&dest[destlen], ", %s", addr);
		else
		{
		    sprintf (&dest[destlen], ",\n%s", addr);
		    linelen = 0;
		}
		destlen += 2;
		linelen += 2;
	    }
	    destlen += addrlen;
	    linelen += addrlen;
	}

    }
}
/*
**	S P E C I A L  S T R I N D E X  F U N C T I O N
**
** Specially modified by George Hartwig to return a pointer to the beginning
** of the requested string instead of the offset.
*/

char *gstrindex (str, target)           /* return column str starts in target */
register char   *str,
		*target;
{
    char *otarget;
    register short slen;

    for (otarget = target, slen = strlen (str); ; target++)
    {
	if (*target == '\0')
	    return ((char *) -1);

	if (equal (str, target, slen))
	    return( otarget + (target - otarget) );
    }
}
dlt_name(buf, pos)
char *buf, *pos;

/* buf is a pointer to a character array and pos is a pointer to a position  */
/* within that array. The purpose of this routine is delete the name field   */
/* pointed to by pos. In order to do this we will search backward to either  */
/* comma ',' or a colon ':' (if a comma, we will then delete that comma also */
/* and then delete forward until another comma or 			     */
/* a end-of-string '\0' is found. 					     */

{
	char tmpbuffer[S_BSIZE];
	char *sd, *ed, *cp;
	int quote_flag;


	cp = pos;
	quote_flag = 0;

	while(cp >= buf ) {
		if( *cp == '"' )
			if( quote_flag == 0 )
				quote_flag = 1;
			else
				quote_flag = 0;
		if( *cp == ',' && quote_flag == 0 )
			break;
		else
			cp--;
	}
	sd = cp;

	cp = pos;

	while( *cp != '\0' ) {
		if( *cp == '"' )
			if( quote_flag == 0 )
				quote_flag = 1;
			else
				quote_flag = 0;
		if( *cp == ',' && quote_flag == 0 )
			break;
		else
			cp++;
	}

	ed = cp;

	/* at this point *sd should point at the comma preceding the name
	** to be deleted or the beginning of the line and ed should be
	** pointing to the trailing comma or the end of the line. So it is
	** necessary at this point to determine just which of these cases
	** have occurred so that the string may be properly reassembled.
	*/

	if( *sd == ',' && *ed == ',' ){
		sd++;
		*sd = '\0';
		ed++;
		sprintf( tmpbuffer, "%s%s", buf, ed);
	}

	else
	if( sd <= buf ){
		ed++;
		strcpy(tmpbuffer, ed);
	}
	else
	if( *ed == '\0' ){
		*sd = '\0';
		strcpy(tmpbuffer, buf);
	}
	strcpy( buf, tmpbuffer );
}

/*
 *	checksignature ()
 *
 *  Verifies that the signature string does not contain any special
 *  characters or is quoted.  Also checks for blank "name" portion.
 */
checksignature (sp)
register char *sp;
{
	char tptr[128], *savptr, *ptr;
	static char quote[] = "\"";

	savptr = sp;
	while (*sp && isspace(*sp))  /* eat leading whitespace */
		sp++;
	if (*sp == '\0') {
		printf ("Signature is blank\n");
		return (-1);     /* must have some signature */
	}
	while (*sp) {
		switch (*sp) {
		case '<':
		case '>':
		case '@':
		case ',':
		case ';':
		case ':':
		case '.':	/*  Arggggg...  */
		case '[':
		case ']':
			/* surround bad signature with quotes */
			tptr[0] = '\0';
			strcat(tptr,quote);
			sp = savptr;
			strcat(tptr,sp);
			if (ptr = rindex(tptr, ' '))
				*ptr = '\0';
			strcat(tptr,quote);
			strcpy(sp,tptr);
			return (0);

		case '\\':
			sp++;
			if (*sp == '\0')
				return (-1);
			break;

		case '"':
			for (sp++; *sp != '"'; sp++) {
				if (*sp == '\\') {
					sp++;
					if (*sp == '\0')
						return (-1);
				} else if (*sp == '\0') {
					printf ("Unmatched '\"' in signature\n");
					return (-1);
				}
			}
			break;

		default:
			break;
		}
		sp++;
	}

	return (0);
}

addheader(name, data)
char	*name;
char	*data;
{
	register struct header *nhp;
	register struct header *ohp;
	register char *cp;
	char	buf[LINESIZE];

	if(!isstr(name))
	    return;
	for (cp = name; *cp; cp++)
		if (!isprint(*cp) && *cp != '\t') {
			printf("Illegal character in header name.\n");
			return;
		}
	data = (isstr(data) ? data : "");
	for (cp = data; *cp; cp++)
		if (!isprint(*cp) && *cp != '\t') {
			printf("Illegal character in header data.\n");
			return;
		}
	if (headers) {
		for (ohp = headers; !lexequ(name, ohp->hname); ohp = ohp->hnext)
			if (ohp->hnext == (struct header *)0)
				break;
		if(lexequ(name, ohp->hname)) {
			strncpy(ohp->hdata, data, sizeof(ohp->hdata)-1);
			return;
		}
	}
	nhp = (struct header *)malloc(sizeof (*nhp));
	sprintf(buf, "%s:  ", name);
	if (nhp == NULL || (nhp->hname = strdup(buf)) == NULL) {
		printf("No memory for header '%s'\n", buf);
		if (nhp)
			free(nhp);
		return;
	}
	strncpy(nhp->hdata, data, sizeof(nhp->hdata)-1);
	nhp->hnext = (struct header *)0;
	nhp->hdata[sizeof(nhp->hdata)-1] = '\0';
	if (headers == (struct header *)0) {
		headers = nhp;
		return;
	}
	ohp->hnext = nhp;
}
len = strlen (str); ; target++)
    {
	if (*target == '\0')
	    return ((char *) -1);

	if (equal (str, target, slen))
	    return( otarget + (mmdf/uip/send/s_input.c   444      0     12       25263  3671105052  10353 /* name:
	input

function:
	To accept the text of the message and allow ed, msg
	to be called.

algorithm:
	Read until end-of-file, then ask for a valid command

parameters:
	none

returns:
	With a null terminated letter in the string pointed to by
	the global variable letter.

globals:
	letter	address of the buffer.

calls:
	write	system
	printf
	getchar
	system  system

called by:
	main


 */
/*
**	R E V I S I O N  H I S T O R Y
**
**	03/31/83  GWH	Split the SEND program into component parts
**
**	04/05/83  GWH	Changed execl calls to execlp's.
**
**	11/21/83  GWH	Added directedit option.
**
**	05/29/84  GWH	Fixed a but that allowed drafts created in an edit
**			mode to be deleted if an interrupt was made by
**			mistake. Also fixed a bug which resulted in the
**			copy flag (cflag) being erroneously set.
**
**	04/02/85 M. Vasoll  Added the spelling checker command.
*/

#include "./s.h"
#include "./s_externs.h"

extern  char *verdate;
extern  int errno;
extern	int onint (), onint2(), onint3 ();

input ()
{
    int     infile;
    int     stat;
    char    tempbuf[80];
    int     (*old1) (),
	    (*old2) (),
	    (*old3) ();
    register int    i;

    printf ("SEND  (%s)\n", verdate);

    if (to[0] != '\0')
	printf (shrtfmt, toname, to);
    if (cc[0] != '\0')
	printf (shrtfmt, ccname, cc);
    if (subject[0] != '\0')
	printf (shrtfmt, subjname, subject);
    if (bcc[0] != '\0')
	printf (shrtfmt, bccname, bcc);

    if (toflag)
	getaddr (toname, to, NO, host);
    if (ccflag)
	getaddr (ccname, cc, NO, host);
    if (subjflag)
	gethead (subjname, subject, NO);

    fflush (stdout);
    if (!noinputflag) {
        if( dflag > 0 )
        {
	    drclose ();
    
	    old1 =  signal (SIGHUP, SIG_IGN); /* ignore signals intended for edit */
	    old2 =  signal (SIGINT, SIG_IGN);
	    old3 =  signal (SIGQUIT, SIG_IGN);
    
	    if (fork () == 0) {
		signal (SIGHUP, old1);
		signal (SIGINT, orig);
		signal (SIGQUIT, old3);
		if( dflag == 2 )
		    execlp (editor, editor, drffile, (char *)0);
		else
		    execlp( veditor, veditor, drffile, (char *)0);
		printf ("can't execute\n");
		s_exit (-1);
	    }
	    wait (&stat);
	    body = TRUE;	/* Don't know but assume he created a draft */
	    lastsend = 0;
	    signal (SIGHUP, old1);     /* restore signals */
	    signal (SIGINT, old2);
	    signal (SIGQUIT, old3);
	    dropen (DREND);
	} else {
	    if (inclfile[0] == '\0')
		printf ("Type message; end with CTRL-D...\n\n");
	    else
		printf ("Enter comment; end with CTRL-D...\n\n");
	
	more: 
	    dropen (DREND);
	    fflush (stdout);
	    signal (SIGINT, SIG_IGN);
	    signal (SIGQUIT, SIG_IGN);
    
	    while ((i = read (0, bigbuf, BBSIZE - 1)) > 0) {
		if (i == 2 && strncmp (bigbuf, ".\n", 2) == 0)
		    break;		/* Accept . on a line by itself */
		body = TRUE;
		if (write (drffd, bigbuf, i) != i)
		    s_exit (NOTOK);
	    }
	    if (i < 0)
		s_exit (NOTOK);
    
	    signal (SIGINT, onint);	
	}
    }
    setjmp (savej);
    nsent = 0;

    if (inclfile[0] != '\0')
    {                             /* tack file to end of message        */
	putc('\n', stdout);
	body = TRUE;
	strcpy (bigbuf, inclfile);
	goto doincl;
    }

    for (;;)
    {
	char *cp;

	signal (SIGINT, onint3);
	printf ("Command or ?: ");
	fflush (stdout);
	aborted = FALSE;
	if (fgets (bigbuf, BBSIZE, stdin) == NULL)
	    goto byebye;
	if (cp = index(bigbuf, '\n'))
	    *cp = '\0';

	if( strncmp( "set", bigbuf, 3) == 0){
		char *av[NARGS];

		i = sstr2arg(bigbuf, 20, av, " \t");
		if( i == 1 ) {	/* tell him what the current options are */
			printf(" Copyfile  - '%s'\n", copyfile);
			printf(" Signature - '%s'\n", signature);
			printf(" Aliases   - '%s'\n", aliasfilename);
			printf(" Editor    - '%s'\n", editor);
			printf(" Veditor   - '%s'\n", veditor);
			printf(" Checker   - '%s'\n", checker);
			printf(" Subargs   - '%s'\n", subargs);
			if( dflag > 0 )
			printf(" Directedit option on with %s\n",
				(dflag==1?veditor:editor));
			if(qflag == 0 && cflag == 1)
				printf(" File copy option is on\n");
			if(qflag == 1 && cflag == 0)
				printf(" File copy on confirmation only\n");
			continue;
		}
		if( (i = picko( av[1] ) ) < 0) {
			printf("Option '%s' is invalid.\n", av[1]);
			continue;
		}
		select_o( i, &av[1] );
		continue;
	}
	compress (bigbuf, bigbuf);
				  /* so prefix works on multi-words     */
	if (bigbuf[0] == '\0')
	    continue;             /* just hit newline                   */

	signal (SIGINT, onint);

	for (i = 0; !isnull (bigbuf[i]); i++)
	    bigbuf[i] = uptolow (bigbuf[i]);

	if (prefix ("bcc", bigbuf))
	{
	    getaddr (bccname, bcc, YES, locname);
	    continue;
	}
	if (prefix ("delete body", bigbuf))
	{
	    printf ("Are you sure?");
	    if (!confirm ())
		continue;
	    body = FALSE;
	    drempty ();
	    continue;
	}

	if (prefix ("edit", bigbuf) || prefix("vedit", bigbuf))
	{
	    drclose ();

	    old1 =  signal (SIGHUP, SIG_IGN); /* ignore signals intended for edit */
	    old2 =  signal (SIGINT, SIG_IGN);
	    old3 =  signal (SIGQUIT, SIG_IGN);

	    if (fork () == 0)
	    {
		signal (SIGHUP, old1);
		signal (SIGINT, orig);
		signal (SIGQUIT, old3);
		if( bigbuf[0] == 'e' )
			execlp (editor, editor, drffile, (char *)0);
		else
			execlp( veditor, veditor, drffile, (char *)0);
		printf ("can't execute\n");
		s_exit (-1);
	    }
	    wait (&stat);
	    body = TRUE;	/* assume he created a draft file */
	    lastsend = 0;
	    signal (SIGHUP, old1);     /* restore signals */
	    signal (SIGINT, old2);
	    signal (SIGQUIT, old3);
	    dropen (DREND);
	    continue;
	}

	if (prefix ("file include", bigbuf))
	{
	    printf ("File: ");
	    gather (bigbuf, BBSIZE - 1);
	    if (bigbuf[0] == '\0')
		continue;

doincl:
	    infile = open (bigbuf, 0);
	    if (inclfile[0] != '\0')
	    {                     /* and include file                   */
		inclfile[0] = '\0';
	    }
	    if (infile < 0)
	    {
		printf ("can't open: '%s'\n", bigbuf);
		continue;
	    }
	    dropen (DREND);
	    body = TRUE;
	    while ((i = read (infile, bigbuf, BBSIZE - 1)) > 0)
		write (drffd, bigbuf, i);
	    close (infile);
	    printf (" ...included\n");
	    continue;
	}

	if (prefix ("header edit", bigbuf))
	{
	    struct header *hp;

	    printf ("[RETURN (skip) - (delete) + (append)]\n");
	    getaddr (toname, to, YES, locname);
	    getaddr (ccname, cc, YES, locname);
	    gethead (subjname, subject, YES);
	    if (bcc[0] != '\0')
		getaddr (bccname, bcc, YES, locname);
	    for (hp = headers; hp != NULL; hp = hp->hnext)
		gethead (hp->hname, hp->hdata, YES);
	    nsent = lastsend = 0;
	    continue;
	}

	if (prefix ("input more body", bigbuf))
	{
	    printf ("Continue message; end with CTRL-D...\n");
	    goto more;
	}


	if (prefix ("program run", bigbuf))
	{
	    printf ("Program: ");
	    fflush (stdout);
	    if (gets (tempbuf) == NULL)
		continue;
	    drclose ();

	    /* ignore signals intended for subprocess */
	    old1 = signal (SIGHUP, SIG_IGN);
	    old2 = signal (SIGINT, SIG_IGN);
	    old3 = signal (SIGQUIT, SIG_IGN);

	    if (fork () == 0)
	    {
		signal (SIGHUP, old1);
		signal (SIGINT, orig);
		signal (SIGQUIT, old3);
		execlp ("sh", "send-shell", "-c", tempbuf, (char *)0);
		printf ("can't execute\n");
		s_exit (-1);
	    }
	    wait (&stat);
	    signal (SIGHUP, old1);     /* restore signals */
	    signal (SIGINT, old2);
	    signal (SIGQUIT, old3);
	    dropen (DREND);
	    continue;
	}

	if (prefix ("quit", bigbuf) ||
		prefix ("bye", bigbuf))
	{
    byebye: 
	    if ( body && !nsent)
	    {
		printf ("Quit without sending draft");
		if (confirm ())
		    return;
		else
		    continue;
	    }
	    drclose ();
	    return;
	}

	if (prefix ("review message", bigbuf))
	{
	    struct header *hp;

	    printf (hdrfmt, fromname, signature);
	    i = 1;
	    if (to[0] != '\0')
	    {
		i++;
	    	do_review(toname, to);
	    }
	    else
		if (bcc[0] != '\0')
		{
		    i++;
		    do_review(toname, "list: ;");
		 }

	    if (cc[0] != '\0')
	    {
		i++;
		do_review(ccname, cc);
	    }
	    if (subject[0] != '\0')
	    {
		i++;
		do_review(subjname, subject);
	    }
	    if (bcc[0] != '\0')
	    {
		i++;
		do_review(bccname, bcc);
	    }
	    for (hp = headers; hp != NULL; hp = hp->hnext)
		if (isstr(hp->hdata))
		    do_review(hp->hname, hp->hdata);
	    putc ('\n', stdout);
	    fflush (stdout);

	    if (body) {
		dropen (DRBEGIN);
		inrdwr = TRUE;
		msgreview (++i);
		inrdwr = FALSE;
	    }
	    continue;
	}

	if (prefix ("check spelling", bigbuf))
	{
	    drclose ();

	    old1 =  signal (SIGHUP, SIG_IGN); /* ignore signals intended for cheker */
	    old2 =  signal (SIGINT, SIG_IGN);
	    old3 =  signal (SIGQUIT, SIG_IGN);

	    if (fork () == 0)
	    {
		signal (SIGHUP, old1);
		signal (SIGINT, orig);
		signal (SIGQUIT, old3);
		execlp (checker, checker, drffile, (char *)0);
		printf ("can't execute\n");
		s_exit (-1);
	    }
	    wait (&stat);
	    signal (SIGHUP, old1);     /* restore signals */
	    signal (SIGINT, old2);
	    signal (SIGQUIT, old3);
	    dropen (DREND);
	    continue;
	}

	if (prefix ("post message", bigbuf) ||
		prefix ("send message", bigbuf))
	{
	    if (lastsend && lastsend == body)
	    {
		printf ("Without changing anything");
		if (!confirm ())
		    break;
	    }
	    signal (SIGINT, onint2);
	    if( qflag == 1 ){
		printf(" Do you want a file copy of this message ?  ");
		fflush(stdout);
		fgets(tempbuf, sizeof(tempbuf), stdin );
		if( tempbuf[0] == 'y' || tempbuf[0] == 'Y' || tempbuf[0] == '\n')
			cflag = 1;
	    	else
	    		cflag = 0;
	    }
	    post ();
	    if ( qflag == 1 )
		cflag = 0;

/* *** auto-exit, upon successfull send, since draft is saved *** */

	    goto byebye;
	}

	if (prefix ("?", bigbuf) || prefix("help", bigbuf))
	{
	    printf ("bcc\n");
	    printf ("bye\n");
	    printf ("check spelling\n");
	    printf ("delete body\n");
	    printf ("edit body (using editor)\n");
	    printf ("vedit body (using veditor)\n");
	    printf ("file include\n");
	    printf ("header edit\n");
	    printf ("input more body\n");
	    printf ("program run\n");
	    printf ("quit\n");
	    printf ("review message\n");
	    printf ("send message\n");
	    printf ("set [option] [option value]\n");
	    continue;
	}

	printf (" unknown command (type ? for help)\n");
    }				  /* end of loop */
}

do_review (name, data)            /* send out a header field            */
    char name[],                  /* name of field                      */
	*data;                    /* value-part of field                */
{
    register int ind;
    register char *curptr;

    for (curptr = data, ind = 0; ind >= 0; curptr += ind + 1)
    {
	if ((ind = strindex ("\n", curptr)) >= 0)
	    curptr[ind] = '\0';   /* format lines properly              */

	printf (hdrfmt, (curptr == data) ? name : "", curptr);
	if (ind >= 0)
	    curptr[ind] = '\n';   /* put it back                        */
    }
}
gets (tempbuf) == NULL)
		continue;
	    drclose ();

	    /* ignore signals intended for subprocess */
	    old1 = signal (SIGHUP, SIG_IGN);
	    old2 = signal (SIGINT, SIG_IGN);
	    old3 = signal (SIGQUIT, SIG_IGN);

	    if (fork () == 0)
	    {
		signal (SIGHUP, old1);
		signal (SIGINT, orig);
		signal (SIGQUIT, old3);
		execlmmdf/uip/send/s_main.c   444      0     12       10647  3622773302  10145 /*
   interactive mail-sender

   ??? ?? ?. ?????          Probably originated in Illinois, but it's
			    like Daniel Boone's knife, with the blade
			    replaced 3 times & the handle 4.
   Oct 80 S. Cracraft       Conversion from V6 to V7 and incorprate changes
			    for all interaction done with MMDF, including
			    *_address routines
   Spr 81 D. Crocker        More cleanup; adding V6 compatability; signature;
			    draft; forward inclusion mod; some help
			    commands; better address parsing
   Jul 81 D. Crocker        add quoting to name-parts of addresses, with
			    special characters; allow comment fields to
			    have special chars.
			    minor diddling with fieldname displaying
    Aug 81 D. Crocker       gather() needed to check characters from getchar
    Sep 81 D. Crocker       have Bcc not printed, when there are no To addrs
    Oct 81 D. Crocker       Make BCC say "(Private)" and disable To & CC,
			    to prevent accidental replies.
			    Make continuation  header lines begin with space,
			    instead of name.
    Nov 81 D. Crocker       save copy in ",sent", instead of ",send"
			    input commands case-insensitive
    Jan 82 D. Kingston	    Modified to make use of the ",sent" file optional
			    based on defining SENTFILE, also changed screen
			    editor to Emacs.  (BRL 6.145)
    Feb 82 D. Kingston	    Users full name is now taken from /etc/passwd,
			    Network name is placed in <> afterwords (BRL 6.152)
    Mar 82 D. Kingston	    Have send close file desc. 3-N before execing
			    subshells or subprograms.
    Apr 82 D. Kingston	    Rearranged the invocation of submit.  Lossage:
			    channel table fd's are still left open on execs
	03/31/83  GWH	    Split SEND into its component parts.

	06/30/83  GWH	Added numerous features to send. See comments in
			"s_get.c", etal. Also added unique draft file naming
			with the draft files' deletion at the end of the
			session.
*/


#include "./s.h"

extern int errno;
extern char *compress ();
extern char *index();

/* mmdf globals */

extern int  sentprotect;
extern char *locname;
extern char *delim1;

/* end of mmdf globals */

char hdrfmt[] = "%-10s%s\n";
char    toname[] =  "To:  ";
char    BCtoname[] = "[To]: ";   /* Bcc copy's To fieldname              */
char    ccname[] =  "cc:  ";
char    BCccname[] = "[cc]: ";   /* Bcc copy's CC fieldname              */
char    subjname[] = "Subject:  ";
char    fromname[] = "From:  ";
char    datename[] = "Date:  ";
char    bccname[] = "BCC:  ";
char    shrtfmt[] = "%s%s\n";

jmp_buf savej;

int     drffd;                  /* handle on the draft file */
int     nsent;
int     badflg;			  /* true if at least one bad address */
int     (*orig) ();               /* to save old signal values */

char   *adrptr;                   /* field currently getting addresses  */
char    bigbuf[BBSIZE],              /* buffer for text of msg */
	signature[S_BSIZE],            /* hold the signature */
	host[64],                 /* default hostname */
	from[64],
	to[S_BSIZE],          /* primary recipients                 */
	bcc[S_BSIZE],         /* blind carbon copy addresses        */
	cc[S_BSIZE],          /* secondary recipients               */
	subject[S_BSIZE],
	stdobuffer[BUFSIZ],
	inclfile[FILNSIZE],
	drffile[S_BSIZE];

char    body,
	lastsend,
	aborted,
	inrdwr,                   /* true if in a read or write */
	noinputflag,
	toflag = TRUE,
	ccflag= TRUE,
	subjflag = TRUE;

/* Set the default user settable options */

char editor[128];
char veditor[128];
char checker[128];
char copyfile[128];
char subargs[128];
char aliasfilename[128];
int wflag, cflag;
int aflag, rflag;
int qflag, dflag;
struct header *headers = (struct header *)0;

/**/

main (argc, argv)
int     argc;
char   *argv[];
{
    extern  onint2 ();
    int     retval;

    mmdf_init( argv[0] );
    setbuf (stdout, stdobuffer);

    if (setjmp(savej) != 0)
	exit(9);

    orig = signal (SIGINT, onint2);
    if (!getuinfo ())
	snd_abort ("Problem acquiring user information.\n");

    if (rp_isbad (retval = mm_init ()) || rp_isbad (retval = mm_sbinit ()))
	snd_abort ("Problem with mail startup [%s].\n", rp_valstr (retval));

    arginit (argc, argv);

    if ((drffd = creat (drffile, sentprotect)) >= 0)
	drclose ();
    else                          /* can't get a clean drf            */
    {
	printf(" Can't open draft file '%s'\n", drffile);
	fflush(stdout);
	s_exit( NOTOK );
    }

    input ();

    mm_end (OK);
    fflush (stdout);
    s_exit (0);
}
rwords (BRL 6.152)
    Mar 82 D. Kingston	    Have send close file desc. 3-N before execimmdf/uip/send/s_onint.c   444      0     12        3543  3671105054  10322 /* name:
	onint, onint2, onint3

function:
	to take appropriate actions before terminating from a del.

calls:
	exit

called by:
	input

*/

/*
**	R E V I S I O N  H I S T O R Y
**
**	03/31/83  GWH	Split the SEND program into component parts
**			This module contains : msgreview, onint, onint2,
**			onint3, confirm.
**
**	10/05/83  GWH	Fixed s_exit so that the draft file will not be
**			removed in abnormal terminations.
**
*/

#include "./s.h"
#include "./s_externs.h"

/*ARGSUSED*/
msgreview (curline)
	register int curline;
{
    FILE *fp;
    register int c;

    fp = fdopen (dup (drffd), "r");
    while ((c = getc (fp)) != EOF && !aborted)
    {
#ifdef FNCYPRNT
	switch (c)
	{
	    case '\n':
		if (++curline <= 20)
		{
		    putchar ('\n');
		    break;
		}
	    case '\f':
		if (c == '\f')
		    printf ("^L");
		putchar ('\n');
		printf (" Continue [Confirm] ");
		fflush (stdout);
		switch (getchar ())
		{
		    case 'Y':
		    case 'y':
			while (getchar () != '\n')
			    continue;
		    case '\n':
			curline = 0;
			break;

		    default:
			while (getchar () != '\n')
			    continue;
			fclose (fp);
			return;
		}
		break;

	    default:
		putchar (c);
	}
#else
	putchar (c);
#endif
    }
    fclose (fp);
}

onint ()
{
    write(1, " XXX\n", 5);
    signal (SIGINT, onint);
    if (inrdwr)
	aborted = TRUE;
    else
	longjmp (savej, 1);
}

onint2 ()
{
    write(1, " XXX\n", 5);
    s_exit (-1);
}

onint3 ()
{
    signal (SIGINT, onint3);

    if (body)
	longjmp (savej, 1);

    s_exit (0);
}

confirm ()
{
    char answer[32];

    clearerr(stdin);	/*  Be friendlier if user types control-D  */
    printf (" [Confirm] ");
    gather (answer, sizeof (answer));
    return (prefix ("yes", answer) ? TRUE : FALSE);
}

/* preliminary exit routine to clean up draft file */
s_exit( xcode )
int xcode;
{
	if( xcode == 0 || !body )
		unlink(drffile);
	exit( xcode );
}
raft files' deletion at the end of the
			session.
*/


#include "./s.h"

extern int errno;
extern char *compress ();
extern char *index();

/* mmdf globals mmdf/uip/send/s_post.c   444      0     12       12160  3631171115  10170 /* name:
	post (and various utility routines)

function:
	To post a message by interacting with submit.

parameters:
	none

globals:
	to, cc, subject, vcc, and bigbuf containing the message to be sent.
	nsent, number of successful posts

calls:
	various lm routines
	various utility routines immediately below

called by:
	input
 */

/*
**	R E V I S I O N  H I S T O R Y
**
**	03/31/83  GWH	Split the SEND program into component parts
**			This module contains post, do_message and
**			do_an_address.
**
*/

#include "./s.h"
#include "./s_externs.h"

post ()
{
    int     retval;

    if( badflg )	/*  died on previous attempt */
	if (rp_isbad (retval = mm_init ()) || rp_isbad (retval = mm_sbinit ()))
	   snd_abort ("Problem with mail restart [%s].\n", rp_valstr (retval));
    badflg = FALSE;
    do_message ();
    fflush (stdout);
}

do_message ()
{
    struct rp_bufstruct thereply;
    int	    len;
    int     retval;
    char    addr[ADDRSIZE];       /* Next address to process */

    if (to[0] != '\0')
    {
	if (bcc[0] != '\0')
	    printf (" --Open recipients--\n");
	if (rp_isbad (retval = mm_winit ((char *) 0, subargs, from)))
	    snd_abort ("Problem with mm_winit [%s].\n", rp_valstr (retval));
	if (rp_isbad (retval = mm_rrply (&thereply, &len)))
	    snd_abort ("Problem with mm_winit reply [%s].\n", rp_valstr (retval));
	switch (rp_gbval (thereply.rp_val)) {
	case RP_BNO:
	case RP_BTNO:
	    snd_abort ("mm_winit error: %s.\n", thereply.rp_line);
	}

	for (adrptr = to;; )
	    switch (next_address (addr))
	    {
		case -1:
		   goto nextto;

		case 0:
		    continue;

		 default:
		    do_an_address (addr);
	    }

    nextto:
	if (cc[0] != '\0')
	    for (adrptr = cc; ; )
		switch (next_address (addr))
		{
		    case -1:
		       goto nextcc;

		    case 0:
			continue;

		     default:
			do_an_address (addr);
		}
    nextcc:

	if (badflg)
	{
	    printf ("Make header legal and try again.\n");
	    if (rp_isbad (retval = mm_end (NOTOK)))
		    printf ("Can't terminate submission.\n");
	    longjmp (savej, 1);
	}

	if (rp_isbad (retval = mm_waend ()))
	    snd_abort ("Problem with mm_waend [%s].\n", rp_valstr (retval));
	if (rp_isbad (retval = do_text (NO)))
	    snd_abort ("Problem writing message text [%s].\n", rp_valstr (retval));
    }
    else
	printf (" no 'To:' addressees.\n");

    if (bcc[0] != '\0')
    {
	if (to[0] != '\0')
	    printf (" --Open copies posted--\n");
	printf (" --Blind recipients--\n");

	if (rp_isbad (retval = mm_winit ((char *) 0, subargs, from)))
	    snd_abort ("Problem with mail's mm_winit [%s].\n", rp_valstr (retval));
	if (rp_isbad (retval = mm_rrply (&thereply, &len)))
	    snd_abort ("Problem with mm_winit reply [%s].\n", rp_valstr (retval));
	switch (rp_gval (thereply.rp_val)) {
	case RP_BNO:
	case RP_BTNO:
	    snd_abort ("mm_winit error: %s.\n", thereply.rp_line);
	}

	for (adrptr = bcc;; )
	    switch (next_address (addr))
	    {
		case -1:
		   goto nextbcc;

		case 0:
		    continue;

		 default:
		    do_an_address (addr);
	    }

    nextbcc:

	if (badflg)
	{
	    printf ("Make header legal and try again.\n");
	    if (rp_isbad (retval = mm_end (NOTOK)))
		    printf ("Can't terminate submission.\n");
	    longjmp (savej, 1);
	}

	if (rp_isbad (retval = mm_waend ()))
	    snd_abort ("Problem with mm_waend [%s].\n", rp_valstr (retval));
	if (rp_isbad (retval = do_text (YES)))
	    snd_abort ("Problem writing message text [%s].\n", rp_valstr (retval));
    }
    if (to[0] != '\0' || bcc[0] != '\0')
	printf (" Message posted.\n\n");
    else
	printf (" *** No addresses to send to!\n\n");
}


do_an_address (addr)
char    addr[];
{
    struct rp_bufstruct thereply;
    int     retval;
    char    name[ADDRSIZE];
    char    address[ADDRSIZE];
    char    hostname[FILNSIZE];
    int     len;

    name[0] =
	address[0]=
	hostname[0] = '\0';
    parsadr (addr, name, address, hostname);

    putchar (' ');
    if (name[0] != '\0')
	printf ("%s <", name);
    printf ("%s", address);
    if (hostname[0] != '\0' && !lexequ(hostname, locname))
	printf ("@%s", hostname);
    if (name[0] != '\0')
	putchar ('>');
    printf (":  ");
    fflush (stdout);

    if (rp_isbad (retval = mm_wadr (hostname, address)) ||
	    rp_isbad (retval = mm_rrply (&thereply, &len)))
	snd_abort ("Problem in do_an_address [%s].\n", rp_valstr (retval));
    switch (rp_gval (thereply.rp_val))
    {
	case RP_AOK: 
	    printf ("address ok\n");
	    nsent++;
	    break;

	case RP_DOK: 
	    printf ("Nameserver timeout; accepted for resubmission\n");
	    nsent++;
	    break;

	case RP_NO: 
	    printf ("not deliverable; unknown problem\n");
	    badflg = TRUE;
	    break;

	case RP_USER: 
	    printf ("not deliverable; unknown address.\n");
	    badflg = TRUE;
	    break;

	case RP_NDEL: 
	    printf ("not deliverable; permanent error.\n");
	    badflg = TRUE;
	    break;

	case RP_AGN: 
	    printf ("failed, this attempt; try later\n");
	    badflg = TRUE;
	    break;

	case RP_NOOP: 
	    printf ("not attempted, this time; perhaps try later.\n");
	    badflg = TRUE;
	    break;

	default: 
	    snd_abort ("Unexpected address response:  [%s] %s\n",
			rp_valstr (thereply.rp_val), thereply.rp_line);
    }
    fflush (stdout);
}
 nextto:
	if (cc[0] != '\0')
	    for (adrptr = cc; ; )
		switch (next_address (addr))
		{
		    case -1:
		       goto nextcc;

		    case 0:
			continue;

		     default:
			do_an_address (addr);
		}
    nextcc:

	if (badflg)
	{
	    printf ("Make header legal and try again.\n");
	    if (rp_isbad (retval = mm_end (NOTOK)))
		    printf ("Can't terminate submission.\n");
	    longjmp (savej, 1);mmdf/uip/send/Makefile.real   444      0     12        2571  3635174531  11075 #
#	Makefile for send
#

OBJS	= s_main.o s_arginit.o s_gather.o s_input.o s_onint.o s_drfile.o \
	  s_post.o s_do.o s_get.o s_tailor.o

SOURCES	= s_main.c s_arginit.c s_gather.c s_input.c s_onint.c s_drfile.c \
	  s_post.c s_do.c s_get.c s_tailor.c

MODULES	= s_main s_arginit s_gather s_input s_onint s_drfile \
	  s_post s_do s_get s_tailor

real-default:	xsend

xsend: $(OBJS) $(MMDFLIBS)
	mkversion
	$(CC) $(LDFLAGS) -o $@ $(OBJS) version.o $(MMDFLIBS) $(SYSLIBS)

install:	$(BINDIR)/send

$(BINDIR)/send:	xsend
	cp xsend $(BINDIR)/send
	-$(CHOWN) $(MMDFLOGIN) $(BINDIR)/send
	-chmod $(PGMPROT) $(BINDIR)/send

lint:
	$(LINT) $(LFLAGS) $(SOURCES) $(LLIBS)

clean:
	-rm -f *.o xsend core tags


# DO NOT DELETE THIS LINE -- make depend uses it

s_main.o: s_main.c
s_main.o: s.h
s_arginit.o: s_arginit.c
s_arginit.o: s.h
s_arginit.o: s_externs.h
s_gather.o: s_gather.c
s_gather.o: s.h
s_gather.o: s_externs.h
s_input.o: s_input.c
s_input.o: s.h
s_input.o: s_externs.h
s_onint.o: s_onint.c
s_onint.o: s.h
s_onint.o: s_externs.h
s_drfile.o: s_drfile.c
s_drfile.o: s.h
s_drfile.o: s_externs.h
s_post.o: s_post.c
s_post.o: s.h
s_post.o: s_externs.h
s_do.o: s_do.c
s_do.o: s.h
s_do.o: s_externs.h
s_get.o: s_get.c
s_get.o: /usr/include/stdio.h
s_get.o: s.h
s_get.o: s_externs.h
s_tailor.o: s_tailor.c
# DEPENDENCIES MUST END AT END OF FILE
# IF YOU PUT STUFF HERE IT WILL GO AWAY
# see make depend above
	    printf ("not attempted, this time; perhaps try later.\n");
	    badflg = TRUE;
	    break;

	default: 
	    snd_abort ("Unexpectedmmdf/uip/send/s_externs.h   444      0     12        2252  3671105046  10665 /*
**		S _ E X T E R N S
**
**  This file contains the external declarations of global variables
**  for the SEND program.
**
**
**	R E V I S I O N  H I S T O R Y
**
**	03/31/83  GWH	Split the SEND program into component parts
**
*/

extern int sentprotect;
extern char hdrfmt[], toname[], BCtoname[], ccname[], BCccname[];
extern char subjname[], fromname[], datename[], bccname[], shrtfmt[];

extern jmp_buf savej;

extern int drffd, nsent, badflg;

extern char *adrptr;

extern char bigbuf[], signature[], host[], from[], to[], bcc[], cc[];
extern char subject[], stdobuffer[], inclfile[], drffile[], sentfile[];

extern char body, lastsend, aborted, inrdwr, toflag, ccflag, subjflag;
extern char noinputflag;
extern char *verdate;
extern int cflag, wflag;
extern int aflag, rflag;
extern int qflag, dflag;
extern char copyfile[], editor[], veditor[], checker[];
extern char subargs[];
extern char replyto[];
extern char aliasfilename[];
extern struct header *headers;

extern (*orig) ();

extern int errno;
extern char *compress ();
extern char *index();
extern char *rindex();

/* mmdf globals */

extern char *locname;
extern char *delim1;
extern char *delim2;

/* end of mmdf globals */
s_get.o: s_get.c
s_get.o: /usr/include/stdio.h
s_get.o: s.h
s_get.o: s_externs.h
s_tailor.o: s_tailor.c
# DEPENDENCIES MUST END AT END OF FILE
# IF YOU PUT STUFF HERE IT WILL GO AWAY
# see make depend above
	    printf ("not attempted, this time; perhaps try later.\n");
	    badflg = TRUE;
	    break;

	default: 
	    snd_abort ("Unexpectedmmdf/uip/send/mkversion   555      0     12         266  3620510623  10423 p=`cat version.number`
c=`echo 0 2 $p +p | dc`
d=`date | awk '{ print $3 " " $2 " " $(NF) }'`
echo "char	*verdate = \"$d #$c\";" > version.c
cc -c version.c
echo $c > version.number
	Split the SEND program into component parts
**
*/

extern int sentprotect;
extern char hdrfmt[], toname[], BCtoname[], ccname[], BCccname[];
extern char subjname[], fromname[], datename[], bccname[], shrtfmt[];

extern jmp_buf savej;

extern int drffd, nsent, badflg;

extern char *adrptr;

extern char bigbuf[], signature[], hosmmdf/uip/send/version.number   644      0     12           4  3656427456  11333 105
at version.number`
c=`echo 0 2 $p +p | dc`
d=`date | awk '{ print $3 " " $2 " " $(NF) }'`
echo "char	*verdate = \"$d #$c\";" > version.c
cc -c version.c
echo $c > version.number
	Split the SEND program into component parts
**
*/

extern int sentprotect;
extern char hdrfmt[], toname[], BCtoname[], ccname[], BCccname[];
extern char subjname[], fromname[], datename[], bccname[], shrtfmt[];

extern jmp_buf savej;

extern int drffd, nsent, badflg;

extern char *adrptr;

extern char bigbuf[], signature[], hosmmdf/uip/send/version.c   644      0     12          43  3656427476  10312 char	*verdate = "3 May 1986 #105";
$p +p | dc`
d=`date | awk '{ print $3 " " $2 " " $(NF) }'`
echo "char	*verdate = \"$d #$c\";" > version.c
cc -c version.c
echo $c > version.number
	Split the SEND program into component parts
**
*/

extern int sentprotect;
extern char hdrfmt[], toname[], BCtoname[], ccname[], BCccname[];
extern char subjname[], fromname[], datename[], bccname[], shrtfmt[];

extern jmp_buf savej;

extern int drffd, nsent, badflg;

extern char *adrptr;

extern char bigbuf[], signature[], hosmmdf/uip/send/s_tailor.h   444      0     12         471  3620510315  10441 /*
 *	Site Tunable Parameters
 */
#undef	SENTFILE yes	/* Define to have copies of mail sent put in ,sent */
#undef	FNCYPRNT yes	/* Define for fancy listing of message (paginated) */
#define	PWNAME yes	/* Define to get real name from /etc/passwd and to */
			/* treat 0 length signatures to get login name only */
Cccname[];
extern char subjname[], fromname[], datename[], bccname[], shrtfmt[];

extern jmp_buf savej;

extern int drffd, nsent, badflg;

extern char *adrptr;

extern char bigbuf[], signature[], hosmmdf/uip/send/s_tailor.c   444      0     12         332  3620510315  10430 /*
 *	S _ T A I L O R . C
 *
 *  Contains strings used to tailor SEND to a particular
 *  site.  Also check s.h for tailorable defines.
 */

char	*dfleditor = "ed";
char	*dflveditor = "vi";
char	*dflchecker = "spell";
real name from /etc/passwd and to */
			/* treat 0 length signatures to get login name only */
Cccname[];
extern char subjname[], fromname[], datename[], bccname[], shrtfmt[];

extern jmp_buf savej;

extern int drffd, nsent, badflg;

extern char *adrptr;

extern char bigbuf[], signature[], hosmmdf/uip/ucbmail/   755      0     12           0  3671347655   7117 mmdf/uip/ucbmail/misc/   755      0     12           0  3670611237  10037 mmdf/uip/ucbmail/misc/Mail.help   444      0     12        1663  3620510633  11655     Mail   Commands
t <message list>		type messages
n				goto and type next message
e <message list>		edit messages
f <message list>		give head lines of messages
d <message list>		delete messages
s <message list> file		append messages to file
u <message list>		undelete messages
R <message list>		reply to message senders
r <message list>		reply to message senders and all recipients
pre <message list>		make messages go back to /usr/spool/mail
m <user list>			mail to specific users
q				quit, saving unresolved messages in mbox
x				quit, do not remove system mailbox
h				print out active message headers
!				shell escape
cd [directory]			chdir to directory or home if none given

A <message list> consists of integers, ranges of same, or user names separated
by spaces.  If omitted, Mail uses the last message typed.

A <user list> consists of user names or aliases separated by spaces.
Aliases are defined in .mailrc in your home directory.
 struct header *headers;

extern (*orig) ();

extern int errno;
extern char *mmdf/uip/ucbmail/gen   555      0     12          57  3620510624   7621 make -f ../../Makefile.com -f Makefile.real $*
ages
n				goto and type next message
e <message list>		edit messages
f <message list>		give head lines of messages
d <message list>		delete messages
s <message list> file		append messages to file
u <message list>		undelete messages
R <message list>		reply to message senders
r <message list>		reply to message senders and all recipients
pre <message list>		make messages go back to /usr/spool/mail
m <user list>			mail to specific users
q				quit, saving unresolvedmmdf/uip/ucbmail/Makefile.real   444      0     12       10454  3671034752  11577 #
# Copyright (c) 1980 Regents of the University of California.
# All rights reserved.  The Berkeley software License Agreement
# specifies the terms and conditions for redistribution.
#
#	@(#)Makefile	5.1 (Berkeley) 6/6/85
#
# Berkeley Mail
#

XSTR=	/usr/ucb/xstr
DESTDIR=
CTAGS=	/usr/ucb/ctags -w
VPRINT=	/usr/ucb/print
LIBES=
AS=	-as
RM=	-rm
OBJS=	version.o aux.o cmd1.o cmd2.o cmd3.o cmdtab.o collect.o \
	config.o dateparse.o edit.o fio.o \
	getname.o head.o v7.local.o lex.o list.o main.o names.o \
	next_address.o optim.o popen.o quit.o send.o strings.o temp.o tty.o \
	vars.o str.o
SRCS=	aux.c cmd1.c cmd2.c cmd3.c cmdtab.c collect.c config.c dateparse.c \
	edit.c fio.c \
	getname.c head.c v7.local.c lex.c list.c main.c \
	names.c optim.c popen.c quit.c send.c strings.c temp.c tty.c vars.c \
	version.c
MODULES= aux cmd1 cmd2 cmd3 cmdtab collect config dateparse \
	edit fio \
	getname head v7.local lex list main \
	names optim popen quit send strings temp tty vars \
	version
HDRS=	rcv.h configdefs.h def.h glob.h v7.local.h local.h
ALL=	xMail xfmt
S = $(SRCS) $(HDRS)

#
# Special massaging of C files for sharing of strings
#
.c.o:
	${CC} -E ${CFLAGS} $*.c | ${XSTR} -c -
	${CC} -c ${CFLAGS} x.c 
	mv x.o $*.o
	@$(RM) -f x.c

real-default: all

all:	${ALL}

xMail:	$S $(OBJS)
	$(RM) -f xMail
	@echo Loading ...
	@$(CC) -o xMail $(OBJS) $(MMDFLIBS) $(SYSLIBS)
	@size xMail

install: ${DESTDIR}/usr/ucb/Mail ${DESTDIR}/usr/ucb/fmt

${DESTDIR}/usr/ucb/Mail: xMail
	install -c -s xMail ${DESTDIR}/usr/ucb/Mail
	cp misc/Mail.help* ${DESTDIR}/usr/lib
	rm -f ${DESTDIR}/usr/ucb/mail
	ln ${DESTDIR}/usr/ucb/Mail ${DESTDIR}/usr/ucb/mail

${DESTDIR}/usr/ucb/fmt: xfmt
	install -c -s xfmt ${DESTDIR}/usr/ucb/fmt

version.o:	version.c
	$(CC) -c -R version.c

$S:
	sccs get $@;

srcs:	$S

tags:	$(SRCS)
	${CTAGS} $(SRCS);

clean:
	$(RM) -f *.o
	$(RM) -f xMail a.out xfmt x.c xs.c tags core
	cp /dev/null strings

lint:
	$(LINT) $(LFLAGS) $(SRCS) $(LLIBS)

xfmt:	fmt.c head.c
	$(CC) fmt.c head.c -o xfmt
	size xfmt

str.o: strings
	$(XSTR)
	$(CC) -R -c xs.c
	mv xs.o str.o
	$(RM) xs.c

cmdtab.o: cmdtab.c
	$(CC) -R -c $(CFLAGS) cmdtab.c

print:	$S fmt.c
	@$(VPRINT) Makefile rcv.h def.h glob.h local.h v7.local.h $(SRCS) fmt.c

wc:
	@wc rcv.h def.h glob.h local.h v7.local.h $(SRCS)

sc:
	@grep -c \; rcv.h def.h glob.h local.h v7.local.h $(SRCS)
  


# DO NOT DELETE THIS LINE -- make depend uses it

aux.o: aux.c
aux.o: rcv.h
aux.o: /usr/include/sys/stat.h
aux.o: /usr/include/ctype.h
aux.o: /usr/include/sys/time.h
cmd1.o: cmd1.c
cmd1.o: rcv.h
cmd1.o: /usr/include/sys/stat.h
cmd2.o: cmd2.c
cmd2.o: rcv.h
cmd2.o: /usr/include/sys/stat.h
cmd2.o: mmdf.h
cmd3.o: cmd3.c
cmd3.o: rcv.h
cmd3.o: /usr/include/sys/stat.h
cmdtab.o: cmdtab.c
cmdtab.o: def.h
collect.o: collect.c
collect.o: rcv.h
collect.o: /usr/include/sys/stat.h
collect.o: sigretro.h
config.o: config.c
config.o: configdefs.h
dateparse.o: dateparse.c
dateparse.o: rcv.h
dateparse.o: /usr/include/ctype.h
dateparse.o: /usr/include/sys/timeb.h
edit.o: edit.c
edit.o: rcv.h
edit.o: /usr/include/stdio.h
edit.o: /usr/include/sys/stat.h
fio.o: fio.c
fio.o: rcv.h
fio.o: mmdf.h
fio.o: /usr/include/sys/stat.h
fio.o: /usr/include/errno.h
fio.o: sigretro.h
getname.o: getname.c
getname.o: /usr/include/pwd.h
getname.o: rcv.h
head.o: head.c
head.o: rcv.h
head.o: /usr/include/ctype.h
v7.local.o: v7.local.c
v7.local.o: rcv.h
v7.local.o: mmdf.h
lex.o: lex.c
lex.o: rcv.h
lex.o: mmdf.h
lex.o: /usr/include/sys/file.h
lex.o: /usr/include/sys/stat.h
lex.o: /usr/include/errno.h
list.o: list.c
list.o: rcv.h
list.o: /usr/include/ctype.h
main.o: main.c
main.o: rcv.h
main.o: /usr/include/sys/stat.h
names.o: names.c
names.o: rcv.h
names.o: mmdf.h
names.o: /usr/include/ctype.h
optim.o: optim.c
optim.o: rcv.h
optim.o: configdefs.h
optim.o: /usr/include/ctype.h
popen.o: popen.c
popen.o: /usr/include/stdio.h
popen.o: /usr/include/signal.h
popen.o: /usr/include/errno.h
popen.o: sigretro.h
quit.o: quit.c
quit.o: rcv.h
quit.o: /usr/include/sys/stat.h
quit.o: /usr/include/sys/file.h
quit.o: mmdf.h
send.o: send.c
send.o: rcv.h
send.o: /usr/include/ctype.h
send.o: /usr/include/sys/stat.h
send.o: mmdf.h
strings.o: strings.c
strings.o: rcv.h
temp.o: temp.c
temp.o: rcv.h
tty.o: tty.c
tty.o: rcv.h
vars.o: vars.c
vars.o: rcv.h
version.o: version.c
# DEPENDENCIES MUST END AT END OF FILE
# IF YOU PUT STUFF HERE IT WILL GO AWAY
# see make depend above
xt (YES)))
	    snd_abort ("Problem writing message text [%s].\n", rp_valstr (retval));
    }
    if (to[0] != '\0' || bcc[0] != '\0')
	printf (" Message posted.\n\n");
    else
	printf (" *** No addresses to senmmdf/uip/ucbmail/CHANGES   444      0     12        5576  3671106536  10201 The code was lint'ed.  No guarantees are made but there shouldn't be any
problems (famous last words).

--- TO DO ---
	Merge in edward's speed improvements
	Merge in guy's update to rfc822
NO	Add "Header" command to allow user defined headers.
		It will disallow "From", "Sender", "To", "Subject", "Cc", "Bcc"

--- DONE ---
cmd2.c	swrite()
	Made "write" command delete entire header instead of just 1st line
list.c	sender()
	Made "type user" do a substring match instead of straight comparison
	(this allows user to match user@machine, which makes it USEFUL)
cmd3.c	unread()
	unread/Unread/new/New command marks messages as unread
list.c	markall()
	Made "-" and "+" skip over deleted messages	(from guy@sun)
cmd1.c	type1()
	if PAGER variable is set, use it instead of MORE (#define)
		to paginate articles
cmd3.c	respond()
	if Replyall variable is set, do a "Reply" (e.g. reverse R and r)
cmd3.c	Respond()
	if Replyall variable is set, do a "reply" (e.g. reverse R and r)
main.c	main()
	Print header info even if -f given.
lex.c	setfile()
	Don't allow editing anything other than regular files.
aux.c	isatty()
	deleted isatty() - it's in the C library
aux.c	strncmp()
	deleted strncmp() - it's in the C library
cmd3.c	shell()
	Lint clean up:	0 -> (char *)0
cmd3.c	dosh()
	Lint clean up:	0 -> (char *)0
optim.c	rename()
	Fix uninitialized variable problem	(from guy@sun)
optim.c	rpair
	Fix uninitialized variable problem	(from guy@sun)
The following changes had to be made to MMDF for Berkeley Mail.

	adjusted ../../Makefile.com to have VMUNIX.
	added conf/SITE/Makefile.uip and changed conf/sitesetup

The following changes indicate how this version of Berkeley Mail
differs from the virgin version.

	change name of cmdtab to CmdTab so as not to conflict with
	the MMDF command table which is called cmdtab.

	use of MMDF routines to determine mail box location, etc.

	must have flock call because it is impossible to pass the
	filename around of the currently open file.  this means the
	lk_open/close interface is not exactly transparent.

	take advantage of knowing flock works by eliminating the
	CANLOCK option.

	add file mmdf.h

	add another parameter to send() to indicate whether or not
	the message being printed should be delimited with MMDF
	message delimiters.

	use the MMDF ml_*() routines to interface to MMDF when sending
	mail.  this is a total rewrite of mail1().

	sorry, but you lose blind carbon copies.

	mail1() no longer returns a meaningful value (of course it
	only did half the time before anyway).

	borrow ../other/adrparse.c and dateparse.c (from ../msg/msg6.c).

One last final note.  You should know that if you begin to use this version
of Mail I don't think that you will be able to switch back to the old
version (almost definitely if you are running MMDF, which I guess is
assumed).  I can not try this but it would seem to be the case.  So be
sure it is what you want to do before you start using it.
quit.o: /usr/include/sys/stat.h
quit.o: /usr/include/sys/file.h
quit.o: mmdf.h
send.o: send.c
send.o: rcv.h
send.o: /usr/include/cmmdf/uip/ucbmail/aux.c   444      0     12       32646  3670657150  10167 /*
 *  A U X . C 
 *
 *  EE/CIS Computer Lab
 *  Department of Computer and Information Sciences
 *  Department of Electrical Engineering
 *  University of Delaware
 *
 *  REVISION HISTORY:
 *
 *  $Revision: 1.4 $
 *
 *  $Log:	aux.c,v $
 * Revision 1.4  86/01/14  14:04:40  galvin
 * 
 * Change nameof so it does not delete the comment associated
 * with an address (it is probably the senders full name).
 * 
 * Change skin() to use MMDF's parsadr to find the route.
 * 
 * Add routeq() to use when comparing routes.  The intent is not to
 * compare the comment part of address when looking for duplicate entries.
 * 
 * Revision 1.3  85/12/18  13:31:12  galvin
 * Change alter to use the MMDF locking routines.
 * 
 * Comment out a call to exit which just didn't look like it belonged.
 * 
 * Revision 1.2  85/12/18  13:28:48  galvin
 * Added comment header for revision history.
 * 
 *
 */

/*
 * Copyright (c) 1980 Regents of the University of California.
 * All rights reserved.  The Berkeley software License Agreement
 * specifies the terms and conditions for redistribution.
 */

#ifndef lint
static char *sccsid = "@(#)aux.c	5.4 (Berkeley) 1/13/86";
#endif not lint

#include "./rcv.h"
#include <sys/stat.h>
#include <ctype.h>

/*
 * Mail -- a mail program
 *
 * Auxiliary functions.
 */

/*
 * Return a pointer to a dynamic copy of the argument.
 */

char *
savestr(str)
	char *str;
{
	register char *cp, *cp2, *top;

	for (cp = str; *cp; cp++)
		;
	top = salloc(cp-str + 1);
	if (top == NOSTR)
		return(NOSTR);
	for (cp = str, cp2 = top; *cp; cp++)
		*cp2++ = *cp;
	*cp2 = 0;
	return(top);
}

#ifdef NOT_USED
/*
 * Copy the name from the passed header line into the passed
 * name buffer.  Null pad the name buffer.
 */

copyname(linebuf, nbuf)
	char *linebuf, *nbuf;
{
	register char *cp, *cp2;

	for (cp = linebuf + 5, cp2 = nbuf; *cp != ' ' && cp2-nbuf < 8; cp++)
		*cp2++ = *cp;
	while (cp2-nbuf < 8)
		*cp2++ = 0;
}
#endif NOT_USED

/*
 * Announce a fatal error and die.
 */

panic(str)
	char *str;
{
	prs("panic: ");
	prs(str);
	prs("\n");
	exit(1);
}

#ifdef NOT_USED
/*
 * Catch stdio errors and report them more nicely.
 */

_error(str)
	char *str;
{
	prs("Stdio Error: ");
	prs(str);
	prs("\n");
	abort();
}
#endif NOT_USED

/*
 * Print a string on diagnostic output.
 */

prs(str)
	char *str;
{
	register char *s;

	for (s = str; *s; s++)
		;
	write(2, str, s-str);
}

/*
 * Touch the named message by setting its MTOUCH flag.
 * Touched messages have the effect of not being sent
 * back to the system mailbox on exit.
 */

touch(mesg)
{
	register struct message *mp;

	if (mesg < 1 || mesg > msgCount)
		return;
	mp = &message[mesg-1];
	mp->m_flag |= MTOUCH;
	if ((mp->m_flag & MREAD) == 0)
		mp->m_flag |= MREAD|MSTATUS;
}

/*
 * Test to see if the passed file name is a directory.
 * Return true if it is.
 */

isdir(name)
	char name[];
{
	struct stat sbuf;

	if (stat(name, &sbuf) < 0)
		return(0);
	return((sbuf.st_mode & S_IFMT) == S_IFDIR);
}

/*
 * Count the number of arguments in the given string raw list.
 */

argcount(argv)
	char **argv;
{
	register char **ap;

	for (ap = argv; *ap != NOSTR; ap++)
		;	
	return(ap-argv);
}

/*
 * Given a file address, determine the
 * block number it represents.
 */

blockof(off)
	off_t off;
{
	off_t a;

	a = off >> 9;
	a &= 077777;
	return((int) a);
}

/*
 * Take a file address, and determine
 * its offset in the current block.
 */

offsetof(off)
	off_t off;
{
	off_t a;

	a = off & 0777;
	return((int) a);
}

/*
 * Return the desired header line from the passed message
 * pointer (or NOSTR if the desired header field is not available).
 */

char *
hfield(field, mp)
	char field[];
	struct message *mp;
{
	register FILE *ibuf;
	char linebuf[LINESIZE];
	register int lc;

	ibuf = setinput(mp);
	if ((lc = mp->m_lines) <= 0)
		return(NOSTR);
	do {
		lc = gethfield(ibuf, linebuf, lc);
#ifdef DEBUG
		if (debug)
			printf("got header \"%s\"\n", linebuf);
#endif DEBUG
		if (lc == -1)
			return(NOSTR);
		if (ishfield(linebuf, field))
			return(savestr(hcontents(linebuf)));
	} while (lc > 0);
	return(NOSTR);
}

/*
 * Return the next header field found in the given message.
 * Return > 0 if something found, <= 0 elsewise.
 * Must deal with \ continuations & other such fraud.
 */

gethfield(f, linebuf, rem)
	register FILE *f;
	char linebuf[];
	register int rem;
{
	char line2[LINESIZE];
	long loc;
	register char *cp, *cp2;
	register int c;


	for (;;) {
		if (rem <= 0)
			return(-1);
		if (readline(f, linebuf) < 0)
			return(-1);
		rem--;
		if (strlen(linebuf) == 0)
			return(-1);
		if (isspace(linebuf[0]))
			continue;
		if (linebuf[0] == '>')
			continue;
		cp = index(linebuf, ':');
		if (cp == NOSTR)
			continue;
		for (cp2 = linebuf; cp2 < cp; cp2++)
			if (isdigit(*cp2))
				continue;
		
		/*
		 * I guess we got a headline.
		 * Handle wraparounding
		 */
		
		for (;;) {
			if (rem <= 0)
				break;
#ifdef CANTELL
			loc = ftell(f);
			if (readline(f, line2) < 0)
				break;
			rem--;
			if (!isspace(line2[0])) {
				fseek(f, loc, 0);
				rem++;
				break;
			}
#else
			c = getc(f);
			ungetc(c, f);
			if (!isspace(c) || c == '\n')
				break;
			if (readline(f, line2) < 0)
				break;
			rem--;
#endif
			cp2 = line2;
			for (cp2 = line2; *cp2 != 0 && isspace(*cp2); cp2++)
				;
			if (strlen(linebuf) + strlen(cp2) >= LINESIZE-2)
				break;
			cp = &linebuf[strlen(linebuf)];
			while (cp > linebuf &&
			    (isspace(cp[-1]) || cp[-1] == '\\'))
				cp--;
			*cp++ = ' ';
			for (cp2 = line2; *cp2 != 0 && isspace(*cp2); cp2++)
				;
			strcpy(cp, cp2);
		}
		if ((c = strlen(linebuf)) > 0) {
			cp = &linebuf[c-1];
			while (cp > linebuf && isspace(*cp))
				cp--;
			*++cp = 0;
		}
		return(rem);
	}
	/* NOTREACHED */
}

/*
 * Check whether the passed line is a header line of
 * the desired breed.
 */

ishfield(linebuf, field)
	char linebuf[], field[];
{
	register char *cp;
	register int c;

	if ((cp = index(linebuf, ':')) == NOSTR)
		return(0);
	if (cp == linebuf)
		return(0);
	cp--;
	while (cp > linebuf && isspace(*cp))
		cp--;
	c = *++cp;
	*cp = 0;
	if (icequal(linebuf ,field)) {
		*cp = c;
		return(1);
	}
	*cp = c;
	return(0);
}

/*
 * Extract the non label information from the given header field
 * and return it.
 */

char *
hcontents(hfld)
	char hfld[];
{
	register char *cp;

	if ((cp = index(hfld, ':')) == NOSTR)
		return(NOSTR);
	cp++;
	while (*cp && isspace(*cp))
		cp++;
	return(cp);
}

/*
 * Compare two strings, ignoring case.
 */

icequal(s1, s2)
	register char *s1, *s2;
{

	while (raise(*s1++) == raise(*s2))
		if (*s2++ == 0)
			return(1);
	return(0);
}

/*
 * Copy a string, lowercasing it as we go.
 */
istrcpy(dest, src)
	char *dest, *src;
{
	register char *cp, *cp2;

	cp2 = dest;
	cp = src;
	do {
		*cp2++ = little(*cp);
	} while (*cp++ != 0);
}

/*
 * The following code deals with input stacking to do source
 * commands.  All but the current file pointer are saved on
 * the stack.
 */

static	int	ssp = -1;		/* Top of file stack */
struct sstack {
	FILE	*s_file;		/* File we were in. */
	int	s_cond;			/* Saved state of conditionals */
	int	s_loading;		/* Loading .mailrc, etc. */
} sstack[NOFILE];

/*
 * Pushdown current input file and switch to a new one.
 * Set the global flag "sourcing" so that others will realize
 * that they are no longer reading from a tty (in all probability).
 */

source(name)
	char name[];
{
	register FILE *fi;
	register char *cp;

	if ((cp = expand(name)) == NOSTR)
		return(1);
	if ((fi = fopen(cp, "r")) == NULL) {
		perror(cp);
		return(1);
	}
	if (ssp >= NOFILE - 2) {
		printf("Too much \"sourcing\" going on.\n");
		fclose(fi);
		return(1);
	}
	sstack[++ssp].s_file = input;
	sstack[ssp].s_cond = cond;
	sstack[ssp].s_loading = loading;
	loading = 0;
	cond = CANY;
	input = fi;
	sourcing++;
	return(0);
}

#ifdef NOT_USED
/*
 * Source a file, but do nothing if the file cannot be opened.
 */

source1(name)
	char name[];
{
	register int f;

	if ((f = open(name, 0)) < 0)
		return(0);
	close(f);
	return(source(name));
}
#endif NOT_USED

/*
 * Pop the current input back to the previous level.
 * Update the "sourcing" flag as appropriate.
 */

unstack()
{
	if (ssp < 0) {
		printf("\"Source\" stack over-pop.\n");
		sourcing = 0;
		return;
	}
	fclose(input);
	if (cond != CANY)
		printf("Unmatched \"if\"\n");
	cond = sstack[ssp].s_cond;
	loading = sstack[ssp].s_loading;
	input = sstack[ssp--].s_file;
	if (ssp < 0)
		sourcing = loading;
}

/*
 * Touch the indicated file.
 * This is nifty for the shell.
 * If we have the utimes() system call, this is better served
 * by using that, since it will work for empty files.
 * The utime() system call can be used if necessary.
 * On non-utime systems, we must sleep a second, then read.
 */
#ifdef UTIMES
#include <sys/time.h>
#endif UTIMES

alter(name)
	char name[];
{
#ifdef UTIMES
	struct stat statb;
	struct timeval tp[2];
	struct timezone tzp;
#else  UTIMES
#ifdef UTIME
	struct stat statb;
	time_t time();
	time_t time_p[2];
#else  UTIME
	register int f;
	char w;
#endif UTIME
#endif UTIMES

#ifdef UTIMES
	if (stat(name, &statb) < 0)
		return;
	if (gettimeofday(&tp[0], &tzp) < 0)
	        return;
	tp[1] = tp[0];
	utimes(name, tp);
#else  UTIMES
#ifdef UTIME
	if (stat(name, &statb) < 0)
		return;
	time_p[0] = time((long *) 0) + 1;
	time_p[1] = statb.st_mtime;
	utime(name, time_p);
#else  UTIME
	sleep(1);
	if ((f = lk_open(name, 0, (char *) 0, (char *) 0, 5)) < 0)
		return;
	read(f, &w, 1);
	lk_close(f, name, (char *) 0, (char *) 0);
	/* exit(0);	/* was this really intended ?? */
#endif UTIME
#endif UTIMES
}

/*
 * Examine the passed line buffer and
 * return true if it is all blanks and tabs.
 */

blankline(linebuf)
	char linebuf[];
{
	register char *cp;

	for (cp = linebuf; *cp; cp++)
		if (*cp != ' ' && *cp != '\t')
			return(0);
	return(1);
}

/*
 * Get sender's name from this message.
 */
char *
nameof(mp, reptype)
	register struct message *mp;
{
	register char *cp, *cp2;

	cp = name1(mp, reptype);
	if (reptype != 0 || charcount(cp, '!') < 2)
		return(cp);
	cp2 = rindex(cp, '!');
	cp2--;
	while (cp2 > cp && *cp2 != '!')
		cp2--;
	if (*cp2 == '!')
		return(cp2 + 1);
	return(cp);
}

#ifdef NOT_USED
/*
 * Skin an arpa net address.
 * 
 * We borrow adrparse from MMDF to do this.
 */
char *
skin(name)
	char *name;
{
	char nbuf[256], namecp[256], mbx[256], host[256];

	strcpy(namecp, name);		/* make it local */
	parsadr(namecp, NOSTR, mbx, host);
	if( *host )
		sprintf(nbuf, "%s@%s", mbx, host);
	else
		strcpy(nbuf, mbx);
	return(savestr(nbuf));
}
#endif NOT_USED
  
/*
 * Fetch the sender's name from the passed message.
 * Reptype can be
 *	0 -- get sender's name for display purposes
 *	1 -- get sender's name for reply
 *	2 -- get sender's name for Reply
 */

char *
name1(mp, reptype)
	register struct message *mp;
{
	char namebuf[LINESIZE];
	char linebuf[LINESIZE];
	register char *cp, *cp2;
	register FILE *ibuf;
	int first = 1;

	if ((cp = hfield("from", mp)) != NOSTR)
		return(cp);
	if (reptype == 0 && (cp = hfield("sender", mp)) != NOSTR)
		return(cp);
	ibuf = setinput(mp);
	copy("", namebuf);
	if (readline(ibuf, linebuf) <= 0)
		return(savestr(namebuf));
newname:
	for (cp = linebuf; *cp != ' '; cp++)
		;
	while (any(*cp, " \t"))
		cp++;
	for (cp2 = &namebuf[strlen(namebuf)]; *cp && !any(*cp, " \t") &&
	    cp2-namebuf < LINESIZE-1; *cp2++ = *cp++)
		;
	*cp2 = '\0';
	if (readline(ibuf, linebuf) <= 0)
		return(savestr(namebuf));
	if ((cp = index(linebuf, 'F')) == NULL)
		return(savestr(namebuf));
	if (strncmp(cp, "From", 4) != 0)
		return(savestr(namebuf));
	while ((cp = index(cp, 'r')) != NULL) {
		if (strncmp(cp, "remote", 6) == 0) {
			if ((cp = index(cp, 'f')) == NULL)
				break;
			if (strncmp(cp, "from", 4) != 0)
				break;
			if ((cp = index(cp, ' ')) == NULL)
				break;
			cp++;
			if (first) {
				copy(cp, namebuf);
				first = 0;
			} else
				strcpy(rindex(namebuf, '!')+1, cp);
			strcat(namebuf, "!");
			goto newname;
		}
		cp++;
	}
	return(savestr(namebuf));
}

/*
 * Count the occurances of c in str
 */
charcount(str, c)
	char *str;
{
	register char *cp;
	register int i;

	for (i = 0, cp = str; *cp; cp++)
		if (*cp == c)
			i++;
	return(i);
}

#ifdef NOT_USED
/*
 * See if the string is a number.
 */

numeric(str)
	char str[];
{
	register char *cp = str;

	while (*cp)
		if (!isdigit(*cp++))
			return(0);
	return(1);
}
#endif NOT_USED

/*
 * Are any of the characters in the two strings the same?
 */

anyof(s1, s2)
	register char *s1, *s2;
{
	register int c;

	while (c = *s1++)
		if (any(c, s2))
			return(1);
	return(0);
}

/*
 * See if the given header field is supposed to be ignored.
 */
isign(field)
	char *field;
{
	char realfld[BUFSIZ];

	/*
	 * Lower-case the string, so that "Status" and "status"
	 * will hash to the same place.
	 */
	istrcpy(realfld, field);

	if (nretained > 0)
		return (!member(realfld, retain));
	else
		return (member(realfld, ignore));
}

member(realfield, table)
	register char *realfield;
	register struct ignore **table;
{
	register struct ignore *igp;

	for (igp = table[hash(realfield)]; igp != 0; igp = igp->i_link)
		if (equal(igp->i_field, realfield))
			return (1);

	return (0);
}

/*
 * When comparing addresses, let's compare only mailbox@host, not
 * a string compare.  Let's use a case insensitive string compare
 * on the route.  Return values are same as icequal().
 */

routeq(adr1, adr2)
char *adr1, *adr2;
{
	char a1[BUFSIZ], a2[BUFSIZ], mbx[BUFSIZ], host[BUFSIZ];

	strcpy(a1, adr1); strcpy(a2, adr2); /* make them local */
	parsadr(a1, NOSTR, mbx, host);
	if (*host)
		sprintf(a1, "%s@%s", mbx, host);
	else
		sprintf(a1, "%s", mbx);
	parsadr(a2, NOSTR, mbx, host);
	if (*host)
		sprintf(a2, "%s@%s", mbx, host);
	else
		sprintf(a2, "%s", mbx);
	return(icequal(a1, a2));
}

blankline(linebuf)
	char linebuf[];
{
	register char *cp;

	for (cp = linebuf; *cp; cp++)mmdf/uip/ucbmail/cmd1.c   444      0     12       24046  3670652025  10205 /*
 *  C M D 1 . C 
 *
 *  EE/CIS Computer Lab
 *  Department of Computer and Information Sciences
 *  Department of Electrical Engineering
 *  University of Delaware
 *
 *  REVISION HISTORY:
 *
 *  $Revision: 1.6 $
 *
 *  $Log:	cmd1.c,v $
 * Revision 1.6  86/01/14  14:09:45  galvin
 * Let's not be lazy and use MMDF's smtpdate and makedate to parse the
 * date of message into something nice (if we can).  Since the new
 * something nice is smaller than the older mess, allow more room for the
 * subject line to print.
 * 
 * Revision 1.5  86/01/07  13:42:35  galvin
 * Change printhead to use the new parse return value.  Be
 * lazy and if parse couldn't figure out the date, then use
 * the contents of the "Date" header field as is.  It probably
 * should be parsed into something nice, but ... .
 * 
 * Revision 1.4  85/12/18  13:19:14  galvin
 * Add another argument to send to indicate whether or not this
 * message should be delimited by MMDF message delimiters.
 * 
 * Revision 1.3  85/11/20  14:31:24  galvin
 * Changed the name of the command table (cmdtab) to CmdTab so as not
 * to conflict with the command table of MMDF.
 * 
 * Revision 1.2  85/11/20  14:24:51  galvin
 * Added comment header for revision history.
 * 
 *
 */

/*
 * Copyright (c) 1980 Regents of the University of California.
 * All rights reserved.  The Berkeley software License Agreement
 * specifies the terms and conditions for redistribution.
 */

#ifndef lint
static char *sccsid = "@(#)cmd1.c	5.2 (Berkeley) 6/21/85";
#endif not lint

#include "./rcv.h"
#include <sys/stat.h>

/*
 * Mail -- a mail program
 *
 * User commands.
 */

/*
 * Print the current active headings.
 * Don't change dot if invoker didn't give an argument.
 */

static int screen;

headers(msgvec)
	int *msgvec;
{
	register int n, mesg, flag;
	register struct message *mp;
	int size;

	size = screensize();
	n = msgvec[0];
	if (n != 0)
		screen = (n-1)/size;
	if (screen < 0)
		screen = 0;
	mp = &message[screen * size];
	if (mp >= &message[msgCount])
		mp = &message[msgCount - size];
	if (mp < &message[0])
		mp = &message[0];
	flag = 0;
	mesg = mp - &message[0];
	if (dot != &message[n-1])
		dot = mp;
	for (; mp < &message[msgCount]; mp++) {
		mesg++;
		if (mp->m_flag & MDELETED)
			continue;
		if (flag++ >= size)
			break;
		printhead(mesg);
		sreset();
	}
	if (flag == 0) {
		printf("No more mail.\n");
		return(1);
	}
	return(0);
}

/*
 * Set the list of alternate names for out host.
 */
local(namelist)
	char **namelist;
{
	register int c;
	register char **ap, **ap2, *cp;

	c = argcount(namelist) + 1;
	if (c == 1) {
		if (localnames == 0)
			return(0);
		for (ap = localnames; *ap; ap++)
			printf("%s ", *ap);
		printf("\n");
		return(0);
	}
	if (localnames != 0)
		cfree((char *) localnames);
	localnames = (char **) calloc((unsigned) c, sizeof (char *));
	for (ap = namelist, ap2 = localnames; *ap; ap++, ap2++) {
		cp = (char *) calloc((unsigned) (strlen(*ap) + 1),
				     sizeof (char));
		strcpy(cp, *ap);
		*ap2 = cp;
	}
	*ap2 = 0;
	return(0);
}

/*
 * Scroll to the next/previous screen
 */

scroll(arg)
	char arg[];
{
	register int s, size;
	int cur[1];

	cur[0] = 0;
	size = screensize();
	s = screen;
	switch (*arg) {
	case 0:
	case '+':
		s++;
		if (s * size > msgCount) {
			printf("On last screenful of messages\n");
			return(0);
		}
		screen = s;
		break;

	case '-':
		if (--s < 0) {
			printf("On first screenful of messages\n");
			return(0);
		}
		screen = s;
		break;

	default:
		printf("Unrecognized scrolling command \"%s\"\n", arg);
		return(1);
	}
	return(headers(cur));
}

/*
 * Compute what the screen size should be.
 * We use the following algorithm:
 *	If user specifies with screen option, use that.
 *	If baud rate < 1200, use  5
 *	If baud rate = 1200, use 10
 *	If baud rate > 1200, use 20
 */
screensize()
{
	register char *cp;
	register int s;
#ifdef	TIOCGWINSZ
	struct winsize ws;
#endif

	if ((cp = value("screen")) != NOSTR) {
		s = atoi(cp);
		if (s > 0)
			return(s);
	}
	if (baud < B1200)
		s = 5;
	else if (baud == B1200)
		s = 10;
#ifdef	TIOCGWINSZ
	else if (ioctl(fileno(stdout), TIOCGWINSZ, &ws) == 0 && ws.ws_row != 0)
		s = ws.ws_row - 4;
#endif
	else
		s = 20;
	return(s);
}

/*
 * Print out the headlines for each message
 * in the passed message list.
 */

from(msgvec)
	int *msgvec;
{
	register int *ip;

	for (ip = msgvec; *ip != NULL; ip++) {
		printhead(*ip);
		sreset();
	}
	if (--ip >= msgvec)
		dot = &message[*ip - 1];
	return(0);
}

/*
 * Print out the header of a specific message.
 * This is a slight improvement to the standard one.
 */

printhead(mesg)
{
	struct message *mp;
	FILE *ibuf;
	char headline[LINESIZE], wcount[10], *subjline, dispc, curind;
	char pbuf[BUFSIZ];
	int s;
	long smtpdate();
	long date;
	struct headline hl;
	register char *cp;

	mp = &message[mesg-1];
	ibuf = setinput(mp);
	readline(ibuf, headline);
	subjline = hfield("subject", mp);
	if (subjline == NOSTR)
		subjline = hfield("subj", mp);

	/*
	 * Bletch!
	 */

	if (subjline != NOSTR && strlen(subjline) > 34)
		subjline[35] = '\0';
	curind = dot == mp ? '>' : ' ';
	dispc = ' ';
	if (mp->m_flag & MSAVED)
		dispc = '*';
	if (mp->m_flag & MPRESERVE)
		dispc = 'P';
	if ((mp->m_flag & (MREAD|MNEW)) == MNEW)
		dispc = 'N';
	if ((mp->m_flag & (MREAD|MNEW)) == 0)
		dispc = 'U';
	if (mp->m_flag & MBOX)
		dispc = 'M';
	if (parse(headline, &hl, pbuf))
		if ((cp = hfield("date", mp)) != NOSTR)
			if ((date = smtpdate(cp)) != -1L) {
				makedate(&date, headline);
				headline[9] = '\0';
				hl.l_date = savestr(headline);
			}
			else
				hl.l_date = cp;
	sprintf(wcount, " %d/%ld", mp->m_lines, mp->m_size);
	s = strlen(wcount);
	cp = wcount + s;
	while (s < 7)
		s++, *cp++ = ' ';
	*cp = '\0';
	if (subjline != NOSTR)
		printf("%c%c%3d %-8.8s %9.9s %s \"%s\"\n", curind, dispc,
		    mesg, nameof(mp, 0), hl.l_date, wcount, subjline);
	else
		printf("%c%c%3d %-8.8s %9.9s %s\n", curind, dispc, mesg,
		    nameof(mp, 0), hl.l_date, wcount);
}

/*
 * Print out the value of dot.
 */

pdot()
{
	printf("%d\n", dot - &message[0] + 1);
	return(0);
}

/*
 * Print out all the possible commands.
 */

pcmdlist()
{
	register struct cmd *cp;
	register int cc;
	extern struct cmd CmdTab[];

	printf("Commands are:\n");
	for (cc = 0, cp = CmdTab; cp->c_name != NULL; cp++) {
		cc += strlen(cp->c_name) + 2;
		if (cc > 72) {
			printf("\n");
			cc = strlen(cp->c_name) + 2;
		}
		if ((cp+1)->c_name != NOSTR)
			printf("%s, ", cp->c_name);
		else
			printf("%s\n", cp->c_name);
	}
	return(0);
}

/*
 * Paginate messages, honor ignored fields.
 */
more(msgvec)
	int *msgvec;
{
	return (type1(msgvec, 1, 1));
}

/*
 * Paginate messages, even printing ignored fields.
 */
More(msgvec)
	int *msgvec;
{

	return (type1(msgvec, 0, 1));
}

/*
 * Type out messages, honor ignored fields.
 */
type(msgvec)
	int *msgvec;
{

	return(type1(msgvec, 1, 0));
}

/*
 * Type out messages, even printing ignored fields.
 */
Type(msgvec)
	int *msgvec;
{

	return(type1(msgvec, 0, 0));
}

/*
 * Type out the messages requested.
 */
jmp_buf	pipestop;

type1(msgvec, doign, page)
	int *msgvec;
{
	register *ip;
	register struct message *mp;
	register int mesg;
	register char *cp;
	int nlines;
	int brokpipe();
	FILE *obuf;

	obuf = stdout;
	if (setjmp(pipestop)) {
		if (obuf != stdout) {
			pipef = NULL;
			pclose(obuf);
		}
		sigset(SIGPIPE, SIG_DFL);
		return(0);
	}
	if (intty && outtty && (page || (cp = value("crt")) != NOSTR)) {
		if (!page) {
			nlines = 0;
			for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++)
				nlines += message[*ip - 1].m_lines;
		}
		if (page || nlines > atoi(cp)) {
			obuf = popen(MORE, "w");
			if (obuf == NULL) {
				perror(MORE);
				obuf = stdout;
			}
			else {
				pipef = obuf;
				sigset(SIGPIPE, brokpipe);
			}
		}
	}
	for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) {
		mesg = *ip;
		touch(mesg);
		mp = &message[mesg-1];
		dot = mp;
		print(mp, obuf, doign);
	}
	if (obuf != stdout) {
		pipef = NULL;
		pclose(obuf);
	}
	sigset(SIGPIPE, SIG_DFL);
	return(0);
}

/*
 * Respond to a broken pipe signal --
 * probably caused by using quitting more.
 */

brokpipe()
{
# ifndef VMUNIX
	signal(SIGPIPE, brokpipe);
# endif
	longjmp(pipestop, 1);
}

/*
 * Print the indicated message on standard output.
 */

print(mp, obuf, doign)
	register struct message *mp;
	FILE *obuf;
{

	if (value("quiet") == NOSTR)
		fprintf(obuf, "Message %2d:\n", mp - &message[0] + 1);
	touch(mp - &message[0] + 1);
	send(mp, obuf, doign, 0);
}

/*
 * Print the top so many lines of each desired message.
 * The number of lines is taken from the variable "toplines"
 * and defaults to 5.
 */

top(msgvec)
	int *msgvec;
{
	register int *ip;
	register struct message *mp;
	register int mesg;
	int c, topl, lines, lineb;
	char *valtop, linebuf[LINESIZE];
	FILE *ibuf;

	topl = 5;
	valtop = value("toplines");
	if (valtop != NOSTR) {
		topl = atoi(valtop);
		if (topl < 0 || topl > 10000)
			topl = 5;
	}
	lineb = 1;
	for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) {
		mesg = *ip;
		touch(mesg);
		mp = &message[mesg-1];
		dot = mp;
		if (value("quiet") == NOSTR)
			printf("Message %2d:\n", mesg);
		ibuf = setinput(mp);
		c = mp->m_lines;
		if (!lineb)
			printf("\n");
		for (lines = 0; lines < c && lines <= topl; lines++) {
			if (readline(ibuf, linebuf) <= 0)
				break;
			puts(linebuf);
			lineb = blankline(linebuf);
		}
	}
	return(0);
}

/*
 * Touch all the given messages so that they will
 * get mboxed.
 */

stouch(msgvec)
	int msgvec[];
{
	register int *ip;

	for (ip = msgvec; *ip != 0; ip++) {
		dot = &message[*ip-1];
		dot->m_flag |= MTOUCH;
		dot->m_flag &= ~MPRESERVE;
	}
	return(0);
}

/*
 * Make sure all passed messages get mboxed.
 */

mboxit(msgvec)
	int msgvec[];
{
	register int *ip;

	for (ip = msgvec; *ip != 0; ip++) {
		dot = &message[*ip-1];
		dot->m_flag |= MTOUCH|MBOX;
		dot->m_flag &= ~MPRESERVE;
	}
	return(0);
}

/*
 * List the folders the user currently has.
 */
folders()
{
	char dirname[BUFSIZ];
	int pid, s, e;

	if (getfold(dirname) < 0) {
		printf("No value set for \"folder\"\n");
		return(-1);
	}
	switch ((pid = fork())) {
	case 0:
		sigchild();
		execlp("ls", "ls", dirname, 0);
		_exit(1);

	case -1:
		perror("fork");
		return(-1);

	default:
		while ((e = wait(&s)) != -1 && e != pid)
			;
	}
	return(0);
}
n struct cmd CmdTab[];

	printf("Commands are:\n");
	for (cc = 0, cp = CmdTab; cp->c_name != NULL; cp++) {
		cc += strlen(cp->c_name) + 2;
		if (cc > 72) {
			printf("\n");
			cc = strlen(cp->c_name) + 2;
		}
		if ((cp+1)->c_name != NOSTR)
			printf("%s, ", cp->c_name);
		else
			printf("%s\n", cp->c_name);
	}
	return(0);
}

/*
 * Paginate messages, honor ignored fields.
 */
more(msgvec)
	int *msgvec;
{
	return (type1(msgvec, 1, 1));
}

/*
 * Paginate messages, even primmdf/uip/ucbmail/TODO   444      0     12        2222  3671110374   7652 Here is a couple of things that could stand to be done.

	If you are running 4.3bsd then undef all the sigmask's.

	I am surprised this runs as fast as it does.  Lots of things could
	be done to speed it up.  It just has too many open files that it
	reads and rereads and rereads.  How about a structure to store
	the headers for instance?

Running Lint:

I did this as of June 2, 1986  (Galvin).  I gave  up on trying  to eliminate
all the possible pointer conversion problems.  For some  reason lint insists
on complaining about all the "calloc" and "salloc" calls.

I  ifdef'ed  whole subprograms  which are   unused  with  "NOT_USED".  Those
subprograms which are actually available from the C library  I ifdef'ed with
NO_LIB.

Remember that all  routines  in  "CmdTab" must  return  zero or non-zero  as
appropriate.  Lint complains about return values that are not used but these
routines are called indirectly.

Untold numbers of  variables/parameters   which were  unused  were  deleted.
Quite a bit of type casting was also done.

What's left?  Mail works.  It probably should only be provided as an interim
facility, though.  Sites should really use msg/send.
;
		touch(mesg);
		mp = &message[mesg-1];
		dot = mp;
		print(mp, obuf, doign);
	}
	if (obuf != stdout) {
		pipef = NULL;
		pclose(obuf);
	}
	sigset(SIGPIPE, SIG_DFL);
	return(0);
}

/*
 * Respond to a broken pipe signal --
 * probably caused by using quitting more.
 */

brokpipe()
{
# ifndef VMUNIX
	signal(SIGPIPE, brokpipe);
# endif
	longjmp(pipestop, 1);
}

/*
mmdf/uip/ucbmail/cmd2.c   444      0     12       26272  3670652024  10210 /*
 *  C M D 2 . C 
 *
 *  EE/CIS Computer Lab
 *  Department of Computer and Information Sciences
 *  Department of Electrical Engineering
 *  University of Delaware
 *
 *  REVISION HISTORY:
 *
 *  $Revision: 1.4 $
 *
 *  $Log:	cmd2.c,v $
 * Revision 1.4  86/01/07  13:41:18  galvin
 * Change swrite to include MMDF delimiters in messages and to
 * output the entire message instead of skipping the first/last line.
 * 
 * Revision 1.3  85/12/18  13:21:13  galvin
 * Add another argument to send to indicate whether or not this
 * message should be delimited by MMDF message delimiters.
 * 
 * Change all but "temp" file opens/closes to use MMDF locking routines.
 * 
 * Revision 1.2  85/12/18  03:10:35  galvin
 * Added comment header for revision history.
 * 
 *
 */

/*
 * Copyright (c) 1980 Regents of the University of California.
 * All rights reserved.  The Berkeley software License Agreement
 * specifies the terms and conditions for redistribution.
 */

#ifndef lint
static char *sccsid = "@(#)cmd2.c	5.2 (Berkeley) 6/21/85";
#endif not lint

#include "./rcv.h"
#include <sys/stat.h>
#include "./mmdf.h"

/*
 * Mail -- a mail program
 *
 * More user commands.
 */

/*
 * If any arguments were given, go to the next applicable argument
 * following dot, otherwise, go to the next applicable message.
 * If given as first command with no arguments, print first message.
 */

next(msgvec)
	int *msgvec;
{
	register struct message *mp;
	register int *ip, *ip2;
	int list[2], mdot;

	if (*msgvec != NULL) {

		/*
		 * If some messages were supplied, find the 
		 * first applicable one following dot using
		 * wrap around.
		 */

		mdot = dot - &message[0] + 1;

		/*
		 * Find the first message in the supplied
		 * message list which follows dot.
		 */

		for (ip = msgvec; *ip != NULL; ip++)
			if (*ip > mdot)
				break;
		if (*ip == NULL)
			ip = msgvec;
		ip2 = ip;
		do {
			mp = &message[*ip2 - 1];
			if ((mp->m_flag & MDELETED) == 0) {
				dot = mp;
				goto hitit;
			}
			if (*ip2 != NULL)
				ip2++;
			if (*ip2 == NULL)
				ip2 = msgvec;
		} while (ip2 != ip);
		printf("No messages applicable\n");
		return(1);
	}

	/*
	 * If this is the first command, select message 1.
	 * Note that this must exist for us to get here at all.
	 */

	if (!sawcom)
		goto hitit;

	/*
	 * Just find the next good message after dot, no
	 * wraparound.
	 */

	for (mp = dot+1; mp < &message[msgCount]; mp++)
		if ((mp->m_flag & (MDELETED|MSAVED)) == 0)
			break;
	if (mp >= &message[msgCount]) {
		printf("At EOF\n");
		return(0);
	}
	dot = mp;
hitit:
	/*
	 * Print dot.
	 */

	list[0] = dot - &message[0] + 1;
	list[1] = NULL;
	return(type(list));
}

/*
 * Save a message in a file.  Mark the message as saved
 * so we can discard when the user quits.
 */
save(str)
	char str[];
{

	return(save1(str, 1));
}

/*
 * Copy a message to a file without affected its saved-ness
 */
copycmd(str)
	char str[];
{

	return(save1(str, 0));
}

/*
 * Save/copy the indicated messages at the end of the passed file name.
 * If mark is true, mark the message "saved."
 */
save1(str, mark)
	char str[];
{
	register int *ip, mesg;
	register struct message *mp;
	char *file, *disp, *cmd;
	int f, *msgvec, lc, t;
	long cc;
	FILE *obuf;
	struct stat statb;

	cmd = mark ? "save" : "copy";
	msgvec = (int *) salloc((msgCount + 2) * sizeof *msgvec);
	if ((file = snarf(str, &f)) == NOSTR)
		return(1);
	if (!f) {
		*msgvec = first(0, MMNORM);
		if (*msgvec == NULL) {
			printf("No messages to %s.\n", cmd);
			return(1);
		}
		msgvec[1] = NULL;
	}
	if (f && getmsglist(str, msgvec, 0) < 0)
		return(1);
	if ((file = expand(file)) == NOSTR)
		return(1);
	printf("\"%s\" ", file);
	fflush(stdout);
	if (stat(file, &statb) >= 0)
		disp = "[Appended]";
	else
		disp = "[New file]";
	if ((obuf = lk_fopen(file, "a", (char *) 0, (char *) 0, 5)) == NULL) {
		perror(NOSTR);
		return(1);
	}
	cc = 0L;
	lc = 0;
	for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) {
		mesg = *ip;
		touch(mesg);
		mp = &message[mesg-1];
		if ((t = send(mp, obuf, 0, 1)) < 0) {
			perror(file);
			lk_fclose(obuf, file, (char *) 0, (char *) 0);
			return(1);
		}
		lc += t;
		cc += mp->m_size;
		if (mark)
			mp->m_flag |= MSAVED;
	}
	fflush(obuf);
	if (ferror(obuf))
		perror(file);
	lk_fclose(obuf, file, (char *) 0, (char *) 0);
	printf("%s %d/%ld\n", disp, lc, cc);
	return(0);
}

/*
 * Write the indicated messages at the end of the passed
 * file name.
 */

swrite(str)
	char str[];
{
	register int *ip, mesg;
	register struct message *mp;
	register char *file, *disp;
	char linebuf[BUFSIZ];
	int f, *msgvec, lc, cc, t;
	FILE *obuf, *mesf;
	struct stat statb;

	msgvec = (int *) salloc((msgCount + 2) * sizeof *msgvec);
	if ((file = snarf(str, &f)) == NOSTR)
		return(1);
	if ((file = expand(file)) == NOSTR)
		return(1);
	if (!f) {
		*msgvec = first(0, MMNORM);
		if (*msgvec == NULL) {
			printf("No messages to write.\n");
			return(1);
		}
		msgvec[1] = NULL;
	}
	if (f && getmsglist(str, msgvec, 0) < 0)
		return(1);
	printf("\"%s\" ", file);
	fflush(stdout);
	if (stat(file, &statb) >= 0)
		disp = "[Appended]";
	else
		disp = "[New file]";
	if ((obuf = fopen(file, "a")) == NULL) {
		perror(NOSTR);
		return(1);
	}
	cc = lc = 0;
	for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) {
		mesg = *ip;
		touch(mesg);
		mp = &message[mesg-1];
		mesf = setinput(mp);
		t = mp->m_lines;
			readline(mesf, linebuf);
		fputs(delim1, obuf);
		while (t-- > 0) {
			fgets(linebuf, BUFSIZ, mesf);
			fputs(linebuf, obuf);
			cc += strlen(linebuf);
		}
		fputs(delim2, obuf);
		lc += mp->m_lines;
		mp->m_flag |= MSAVED;
	}
	fflush(obuf);
	if (ferror(obuf))
		perror(file);
	fclose(obuf);
	printf("%s %d/%d\n", disp, lc, cc);
	return(0);
}

/*
 * Snarf the file from the end of the command line and
 * return a pointer to it.  If there is no file attached,
 * just return NOSTR.  Put a null in front of the file
 * name so that the message list processing won't see it,
 * unless the file name is the only thing on the line, in
 * which case, return 0 in the reference flag variable.
 */

char *
snarf(linebuf, flag)
	char linebuf[];
	int *flag;
{
	register char *cp;

	*flag = 1;
	cp = strlen(linebuf) + linebuf - 1;

	/*
	 * Strip away trailing blanks.
	 */

	while (*cp == ' ' && cp > linebuf)
		cp--;
	*++cp = 0;

	/*
	 * Now search for the beginning of the file name.
	 */

	while (cp > linebuf && !any(*cp, "\t "))
		cp--;
	if (*cp == '\0') {
		printf("No file specified.\n");
		return(NOSTR);
	}
	if (any(*cp, " \t"))
		*cp++ = 0;
	else
		*flag = 0;
	return(cp);
}

/*
 * Delete messages.
 */

delnorm(msgvec)
	int msgvec[];
{
	return(delm(msgvec));
}

/*
 * Delete messages, then type the new dot.
 */

deltype(msgvec)
	int msgvec[];
{
	int list[2];
	int lastdot;

	lastdot = dot - &message[0] + 1;
	if (delm(msgvec) >= 0) {
		list[0] = dot - &message[0];
		list[0]++;
		if (list[0] > lastdot) {
			touch(list[0]);
			list[1] = NULL;
			return(type(list));
		}
		printf("At EOF\n");
		return(0);
	}
	else {
		printf("No more messages\n");
		return(0);
	}
}

/*
 * Delete the indicated messages.
 * Set dot to some nice place afterwards.
 * Internal interface.
 */

delm(msgvec)
	int *msgvec;
{
	register struct message *mp;
	register *ip, mesg;
	int last;

	last = NULL;
	for (ip = msgvec; *ip != NULL; ip++) {
		mesg = *ip;
		touch(mesg);
		mp = &message[mesg-1];
		mp->m_flag |= MDELETED|MTOUCH;
		mp->m_flag &= ~(MPRESERVE|MSAVED|MBOX);
		last = mesg;
	}
	if (last != NULL) {
		dot = &message[last-1];
		last = first(0, MDELETED);
		if (last != NULL) {
			dot = &message[last-1];
			return(0);
		}
		else {
			dot = &message[0];
			return(-1);
		}
	}

	/*
	 * Following can't happen -- it keeps lint happy
	 */

	return(-1);
}

/*
 * Undelete the indicated messages.
 */

undelete(msgvec)
	int *msgvec;
{
	register struct message *mp;
	register *ip, mesg;

	for (ip = msgvec; ip-msgvec < msgCount; ip++) {
		mesg = *ip;
		if (mesg == 0)
			return;
		touch(mesg);
		mp = &message[mesg-1];
		dot = mp;
		mp->m_flag &= ~MDELETED;
	}
}

/*
 * Interactively dump core on "core"
 */

core()
{
	register int pid;
	int status;

	if ((pid = vfork()) == -1) {
		perror("fork");
		return(1);
	}
	if (pid == 0) {
		sigchild();
		abort();
		_exit(1);
	}
	printf("Okie dokie");
	fflush(stdout);
	while (wait(&status) != pid)
		;
	if (status & 0200)
		printf(" -- Core dumped\n");
	else
		printf("\n");
	return(0);
}

/*
 * Clobber as many bytes of stack as the user requests.
 */
clobber(argv)
	char **argv;
{
	register int times;

	if (argv[0] == 0)
		times = 1;
	else
		times = (atoi(argv[0]) + 511) / 512;
	clob1(times);
}

/*
 * Clobber the stack.
 */
clob1(n)
{
	char buf[512];
	register char *cp;

	if (n <= 0)
		return;
	for (cp = buf; cp < &buf[512]; *cp++ = 0xFF)
		;
	clob1(n - 1);
}

/*
 * Add the given header fields to the retained list.
 * If no arguments, print the current list of retained fields.
 */
retfield(list)
	char *list[];
{
	char field[BUFSIZ];
	register int h;
	register struct ignore *igp;
	char **ap;

	if (argcount(list) == 0)
		return(retshow());
	for (ap = list; *ap != 0; ap++) {
		istrcpy(field, *ap);

		if (member(field, retain))
			continue;

		h = hash(field);
		igp = (struct ignore *) calloc(1, sizeof (struct ignore));
		igp->i_field = calloc((unsigned) (strlen(field) + 1),
				      sizeof (char));
		strcpy(igp->i_field, field);
		igp->i_link = retain[h];
		retain[h] = igp;
		nretained++;
	}
	return(0);
}

/*
 * Print out all currently retained fields.
 */
retshow()
{
	register int h, count;
	struct ignore *igp;
	char **ap, **ring;
	int igcomp();

	count = 0;
	for (h = 0; h < HSHSIZE; h++)
		for (igp = retain[h]; igp != 0; igp = igp->i_link)
			count++;
	if (count == 0) {
		printf("No fields currently being retained.\n");
		return(0);
	}
	ring = (char **) salloc((count + 1) * sizeof (char *));
	ap = ring;
	for (h = 0; h < HSHSIZE; h++)
		for (igp = retain[h]; igp != 0; igp = igp->i_link)
			*ap++ = igp->i_field;
	*ap = 0;
	qsort((char *) ring, count, sizeof (char *), igcomp);
	for (ap = ring; *ap != 0; ap++)
		printf("%s\n", *ap);
	return(0);
}

/*
 * Add the given header fields to the ignored list.
 * If no arguments, print the current list of ignored fields.
 */
igfield(list)
	char *list[];
{
	char field[BUFSIZ];
	register int h;
	register struct ignore *igp;
	char **ap;

	if (argcount(list) == 0)
		return(igshow());
	for (ap = list; *ap != 0; ap++) {
		if (isign(*ap))
			continue;
		istrcpy(field, *ap);
		h = hash(field);
		igp = (struct ignore *) calloc(1, sizeof (struct ignore));
		igp->i_field = calloc((unsigned) (strlen(field) + 1),
				      sizeof (char));
		strcpy(igp->i_field, field);
		igp->i_link = ignore[h];
		ignore[h] = igp;
	}
	return(0);
}

/*
 * Print out all currently ignored fields.
 */
igshow()
{
	register int h, count;
	struct ignore *igp;
	char **ap, **ring;
	int igcomp();

	count = 0;
	for (h = 0; h < HSHSIZE; h++)
		for (igp = ignore[h]; igp != 0; igp = igp->i_link)
			count++;
	if (count == 0) {
		printf("No fields currently being ignored.\n");
		return(0);
	}
	ring = (char **) salloc((count + 1) * sizeof (char *));
	ap = ring;
	for (h = 0; h < HSHSIZE; h++)
		for (igp = ignore[h]; igp != 0; igp = igp->i_link)
			*ap++ = igp->i_field;
	*ap = 0;
	qsort((char *) ring, count, sizeof (char *), igcomp);
	for (ap = ring; *ap != 0; ap++)
		printf("%s\n", *ap);
	return(0);
}

/*
 * Compare two names for sorting ignored field list.
 */
igcomp(l, r)
	char **l, **r;
{

	return(strcmp(*l, *r));
}
MTOUCH;
		mp->m_flag &= ~(MPRESERVE|MSAVED|MBOX);
		last = mesg;
	}
	if (last != NULL) {
		dot = &message[last-1];
		last = first(0, MDELETED);
		if (last != NULL) {
			dot = &message[last-1];
			return(0);
		}
		else {
			dot = &message[0];
			return(-1);
		}
	}

	/*
	 * Following can't happen -- it keeps lint happy
	 */

	mmdf/uip/ucbmail/lock.c   444      0     12        4050  3620510627  10256 /*
 * Copyright (c) 1980 Regents of the University of California.
 * All rights reserved.  The Berkeley software License Agreement
 * specifies the terms and conditions for redistribution.
 */

#ifndef lint
static char *sccsid = "@(#)lock.c	5.2 (Berkeley) 6/21/85";
#endif not lint

/*
 * A mailing program.
 *
 * Stuff to do version 7 style locking.
 */

#include "./rcv.h"
#include <sys/stat.h>

char	*maillock	= ".lock";		/* Lock suffix for mailname */
char	*lockname	= "/usr/spool/mail/tmXXXXXX";
char	locktmp[30];				/* Usable lock temporary */
static char		curlock[50];		/* Last used name of lock */
static	int		locked;			/* To note that we locked it */

/*
 * Lock the specified mail file by setting the file mailfile.lock.
 * We must, of course, be careful to remove the lock file by a call
 * to unlock before we stop.  The algorithm used here is to see if
 * the lock exists, and if it does, to check its modify time.  If it
 * is older than 5 minutes, we assume error and set our own file.
 * Otherwise, we wait for 5 seconds and try again.
 */

lock(file)
char *file;
{
	register int f;
	struct stat sbuf;
	long curtime;

	if (file == NOSTR) {
		printf("Locked = %d\n", locked);
		return(0);
	}
	if (locked)
		return(0);
	strcpy(curlock, file);
	strcat(curlock, maillock);
	strcpy(locktmp, lockname);
	mktemp(locktmp);
	remove(locktmp);
	for (;;) {
		f = lock1(locktmp, curlock);
		if (f == 0) {
			locked = 1;
			return(0);
		}
		if (stat(curlock, &sbuf) < 0)
			return(0);
		time(&curtime);
		if (curtime < sbuf.st_ctime + 300) {
			sleep(5);
			continue;
		}
		remove(curlock);
	}
}

/*
 * Remove the mail lock, and note that we no longer
 * have it locked.
 */

unlock()
{

	remove(curlock);
	locked = 0;
}

/*
 * Attempt to set the lock by creating the temporary file,
 * then doing a link/unlink.  If it fails, return -1 else 0
 */

lock1(tempfile, name)
	char tempfile[], name[];
{
	register int fd;

	fd = creat(tempfile, 0);
	if (fd < 0)
		return(-1);
	close(fd);
	if (link(tempfile, name) < 0) {
		remove(tempfile);
		return(-1);
	}
	remove(tempfile);
	return(0);
}
atic char		curlock[50];		/* Last used name of lock */
static	int		locked;			/* To note that we locked it */

/*
 * Lock the specified mail file by setting the file mailfile.lock.
 * We must, of course, be careful to remove the lock file by a call
 * to unlock before we stop.  The algorithm used here is to see if
 * the lock exists, and if it does, to check its modify time.  If it
 * is older than 5 minutes, we assume error and set our own file.
 * Otherwise, we wait fmmdf/uip/ucbmail/cmd3.c   444      0     12       36674  3670652022  10216 /*
 *  C M D 3 . C 
 *
 *  EE/CIS Computer Lab
 *  Department of Computer and Information Sciences
 *  Department of Electrical Engineering
 *  University of Delaware
 *
 *  REVISION HISTORY:
 *
 *  $Revision: 1.3 $
 *
 *  $Log:	cmd3.c,v $
 * Revision 1.3  86/01/14  14:16:30  galvin
 * respond() needed some tuning.  Basically, we use a Reply-To over
 * the From or Sender.  Also, we reply to all address, to and cc, even
 * if we find a Reply-To.  Previously, the existance of a Reply-To
 * caused respond() to behave like Respond().  Change delname() to use
 * routeq() and eliminate a couple skin()'s since MMDF likes full names.
 * Finally make sure we delimit addresses by "," where appropriate.
 * 
 * In Respond(), remove the skin() call and delimit addresses by ",".
 * 
 * Revision 1.2  86/01/07  13:53:36  galvin
 * Added comment header for revision history.
 * 
 *
 */

/*
 * Copyright (c) 1980 Regents of the University of California.
 * All rights reserved.  The Berkeley software License Agreement
 * specifies the terms and conditions for redistribution.
 */

#ifndef lint
static char *sccsid = "@(#)cmd3.c	5.2 (Berkeley) 6/21/85";
#endif not lint

#include "./rcv.h"
#include <sys/stat.h>

/*
 * Mail -- a mail program
 *
 * Still more user commands.
 */

/*
 * Process a shell escape by saving signals, ignoring signals,
 * and forking a sh -c
 */

shell(str)
	char *str;
{
	int (*sig[2])(), stat[1];
	register int t;
	char *Shell;
	char cmd[BUFSIZ];

	strcpy(cmd, str);
	if (bangexp(cmd) < 0)
		return(-1);
	if ((Shell = value("SHELL")) == NOSTR)
		Shell = SHELL;
	for (t = 2; t < 4; t++)
		sig[t-2] = sigset(t, SIG_IGN);
	t = vfork();
	if (t == 0) {
		sigchild();
		for (t = 2; t < 4; t++)
			if (sig[t-2] != SIG_IGN)
				sigsys(t, SIG_DFL);
		execl(Shell, Shell, "-c", cmd, 0);
		perror(Shell);
		_exit(1);
	}
	while (wait(stat) != t)
		;
	if (t == -1)
		perror("fork");
	for (t = 2; t < 4; t++)
		sigset(t, sig[t-2]);
	printf("!\n");
	return(0);
}

/*
 * Fork an interactive shell.
 */

dosh()
{
	int (*sig[2])(), stat[1];
	register int t;
	char *Shell;
	if ((Shell = value("SHELL")) == NOSTR)
		Shell = SHELL;
	for (t = 2; t < 4; t++)
		sig[t-2] = sigset(t, SIG_IGN);
	t = vfork();
	if (t == 0) {
		sigchild();
		for (t = 2; t < 4; t++)
			if (sig[t-2] != SIG_IGN)
				sigsys(t, SIG_DFL);
		execl(Shell, Shell, 0);
		perror(Shell);
		_exit(1);
	}
	while (wait(stat) != t)
		;
	if (t == -1)
		perror("fork");
	for (t = 2; t < 4; t++)
		sigsys(t, sig[t-2]);
	putchar('\n');
	return(0);
}

/*
 * Expand the shell escape by expanding unescaped !'s into the
 * last issued command where possible.
 */

char	lastbang[128];

bangexp(str)
	char *str;
{
	char bangbuf[BUFSIZ];
	register char *cp, *cp2;
	register int n;
	int changed = 0;

	cp = str;
	cp2 = bangbuf;
	n = BUFSIZ;
	while (*cp) {
		if (*cp == '!') {
			if (n < strlen(lastbang)) {
overf:
				printf("Command buffer overflow\n");
				return(-1);
			}
			changed++;
			strcpy(cp2, lastbang);
			cp2 += strlen(lastbang);
			n -= strlen(lastbang);
			cp++;
			continue;
		}
		if (*cp == '\\' && cp[1] == '!') {
			if (--n <= 1)
				goto overf;
			*cp2++ = '!';
			cp += 2;
			changed++;
		}
		if (--n <= 1)
			goto overf;
		*cp2++ = *cp++;
	}
	*cp2 = 0;
	if (changed) {
		printf("!%s\n", bangbuf);
		fflush(stdout);
	}
	strcpy(str, bangbuf);
	strncpy(lastbang, bangbuf, 128);
	lastbang[127] = 0;
	return(0);
}

/*
 * Print out a nice help message from some file or another.
 */

help()
{
	register c;
	register FILE *f;

	if ((f = fopen(HELPFILE, "r")) == NULL) {
		perror(HELPFILE);
		return(1);
	}
	while ((c = getc(f)) != EOF)
		putchar(c);
	fclose(f);
	return(0);
}

/*
 * Change user's working directory.
 */

schdir(str)
	char *str;
{
	register char *cp;

	for (cp = str; *cp == ' '; cp++)
		;
	if (*cp == '\0')
		cp = homedir;
	else
		if ((cp = expand(cp)) == NOSTR)
			return(1);
	if (chdir(cp) < 0) {
		perror(cp);
		return(1);
	}
	return(0);
}

/*
 * Reply to a list of messages.  Extract each name from the
 * message header and send them off to mail1()
 *
 * What we want is to use a Reply-To field over the sender/from,
 * and to include all To and Cc recipients.  Previously the existence
 * of a Reply-To would behave as if "R" was used instead of "r".
 * This is inconsistent with MMDF.
 */

respond(msgvec)
	int *msgvec;
{
	struct message *mp;
	char *cp, *rcv, *replyto;
	char buf[2 * LINESIZE], **ap;
	struct name *np;
	struct header head;

	if (msgvec[1] != 0) {
		printf("Sorry, can't reply to multiple messages at once\n");
		return(1);
	}
	mp = &message[msgvec[0] - 1];
	dot = mp;
	rcv = NOSTR;
	cp = nameof(mp, 1);
	if (cp != NOSTR && *cp)
	    rcv = cp;
	cp = hfield("from", mp);
	if (cp != NOSTR && *cp)
	    rcv = cp;
	replyto = hfield("reply-to", mp);
	if (replyto != NOSTR && *replyto)
		strcpy(buf, replyto);
	else if (rcv != NOSTR && *rcv)
		strcpy(buf, rcv);
	else
		strcpy(buf, "");
	cp = hfield("to", mp);
	if (cp != NOSTR && *cp && *buf) {
		strcat(buf, ", ");
		strcat(buf, cp);
	}
	else if (cp != NOSTR)
		strcpy(buf, cp);
	np = elide(extract(buf, GTO));
	mapf(np, rcv);
	/*
	 * Delete my name from the reply list,
	 * and with it, all my alternate names.
	 */
	np = delname(np, myname, routeq);
	if (altnames)
		for (ap = altnames; *ap; ap++)
			np = delname(np, *ap, routeq);
	head.h_seq = 1;
	cp = detract(np, 0);
	head.h_to = NOSTR;
	if (cp != NOSTR)
		head.h_to = cp;
		else
		printf("Empty To field -- hope that's ok.\n");
	head.h_subject = hfield("subject", mp);
	if (head.h_subject == NOSTR)
		head.h_subject = hfield("subj", mp);
	head.h_subject = reedit(head.h_subject);
	head.h_cc = NOSTR;
		cp = hfield("cc", mp);
		if (cp != NOSTR) {
			np = elide(extract(cp, GCC));
			mapf(np, rcv);
		np = delname(np, myname, routeq);
			if (altnames != 0)
				for (ap = altnames; *ap; ap++)
				np = delname(np, *ap, routeq);
			head.h_cc = detract(np, 0);
		}
	head.h_bcc = NOSTR;
	mail1(&head);
	return(0);
}

/*
 * Modify the subject we are replying to to begin with Re: if
 * it does not already.
 */

char *
reedit(subj)
	char *subj;
{
	char sbuf[10];
	register char *newsubj;

	if (subj == NOSTR)
		return(NOSTR);
	strncpy(sbuf, subj, 3);
	sbuf[3] = 0;
	if (icequal(sbuf, "re:"))
		return(subj);
	newsubj = salloc(strlen(subj) + 6);
	sprintf(newsubj, "Re:  %s", subj);
	return(newsubj);
}

/*
 * Preserve the named messages, so that they will be sent
 * back to the system mailbox.
 */

preserve(msgvec)
	int *msgvec;
{
	register struct message *mp;
	register int *ip, mesg;

	if (edit) {
		printf("Cannot \"preserve\" in edit mode\n");
		return(1);
	}
	for (ip = msgvec; *ip != NULL; ip++) {
		mesg = *ip;
		mp = &message[mesg-1];
		mp->m_flag |= MPRESERVE;
		mp->m_flag &= ~MBOX;
		dot = mp;
	}
	return(0);
}

/*
 * Mark all given messages as unread.
 */
unread(msgvec)
	int	msgvec[];
{
	register int *ip;

	for (ip = msgvec; *ip != NULL; ip++) {
		dot = &message[*ip-1];
		dot->m_flag &= ~(MREAD|MTOUCH);
		dot->m_flag |= MSTATUS;
	}
	return(0);
}

/*
 * Print the size of each message.
 */

messize(msgvec)
	int *msgvec;
{
	register struct message *mp;
	register int *ip, mesg;

	for (ip = msgvec; *ip != NULL; ip++) {
		mesg = *ip;
		mp = &message[mesg-1];
		printf("%d: %ld\n", mesg, mp->m_size);
	}
	return(0);
}

/*
 * Quit quickly.  If we are sourcing, just pop the input level
 * by returning an error.
 */

rexit(e)
{
	if (sourcing)
		return(1);
	if (Tflag != NOSTR)
		close(creat(Tflag, 0600));
	exit(e);
	/*NOTREACHED*/
}

/*
 * Set or display a variable value.  Syntax is similar to that
 * of csh.
 */

set(arglist)
	char **arglist;
{
	register struct var *vp;
	register char *cp, *cp2;
	char varbuf[BUFSIZ], **ap, **p;
	int errs, h, s;

	if (argcount(arglist) == 0) {
		for (h = 0, s = 1; h < HSHSIZE; h++)
			for (vp = variables[h]; vp != NOVAR; vp = vp->v_link)
				s++;
		ap = (char **) salloc(s * sizeof *ap);
		for (h = 0, p = ap; h < HSHSIZE; h++)
			for (vp = variables[h]; vp != NOVAR; vp = vp->v_link)
				*p++ = vp->v_name;
		*p = NOSTR;
		sort(ap);
		for (p = ap; *p != NOSTR; p++)
			printf("%s\t%s\n", *p, value(*p));
		return(0);
	}
	errs = 0;
	for (ap = arglist; *ap != NOSTR; ap++) {
		cp = *ap;
		cp2 = varbuf;
		while (*cp != '=' && *cp != '\0')
			*cp2++ = *cp++;
		*cp2 = '\0';
		if (*cp == '\0')
			cp = "";
		else
			cp++;
		if (equal(varbuf, "")) {
			printf("Non-null variable name required\n");
			errs++;
			continue;
		}
		assign(varbuf, cp);
	}
	return(errs);
}

/*
 * Unset a bunch of variable values.
 */

unset(arglist)
	char **arglist;
{
	register struct var *vp, *vp2;
	int errs, h;
	char **ap;

	errs = 0;
	for (ap = arglist; *ap != NOSTR; ap++) {
		if ((vp2 = lookup(*ap)) == NOVAR) {
			if (!sourcing) {
				printf("\"%s\": undefined variable\n", *ap);
				errs++;
			}
			continue;
		}
		h = hash(*ap);
		if (vp2 == variables[h]) {
			variables[h] = variables[h]->v_link;
			vfree(vp2->v_name);
			vfree(vp2->v_value);
			cfree((char *) vp2);
			continue;
		}
		for (vp = variables[h]; vp->v_link != vp2; vp = vp->v_link)
			;
		vp->v_link = vp2->v_link;
		vfree(vp2->v_name);
		vfree(vp2->v_value);
		cfree((char *) vp2);
	}
	return(errs);
}

/*
 * Put add users to a group.
 */

group(argv)
	char **argv;
{
	register struct grouphead *gh;
	register struct group *gp;
	register int h;
	int s;
	char **ap, *gname, **p;

	if (argcount(argv) == 0) {
		for (h = 0, s = 1; h < HSHSIZE; h++)
			for (gh = groups[h]; gh != NOGRP; gh = gh->g_link)
				s++;
		ap = (char **) salloc(s * sizeof *ap);
		for (h = 0, p = ap; h < HSHSIZE; h++)
			for (gh = groups[h]; gh != NOGRP; gh = gh->g_link)
				*p++ = gh->g_name;
		*p = NOSTR;
		sort(ap);
		for (p = ap; *p != NOSTR; p++)
			printgroup(*p);
		return(0);
	}
	if (argcount(argv) == 1) {
		printgroup(*argv);
		return(0);
	}
	gname = *argv;
	h = hash(gname);
	if ((gh = findgroup(gname)) == NOGRP) {
		gh = (struct grouphead *) calloc(sizeof *gh, 1);
		gh->g_name = vcopy(gname);
		gh->g_list = NOGE;
		gh->g_link = groups[h];
		groups[h] = gh;
	}

	/*
	 * Insert names from the command list into the group.
	 * Who cares if there are duplicates?  They get tossed
	 * later anyway.
	 */

	for (ap = argv+1; *ap != NOSTR; ap++) {
		gp = (struct group *) calloc(sizeof *gp, 1);
		gp->ge_name = vcopy(*ap);
		gp->ge_link = gh->g_list;
		gh->g_list = gp;
	}
	return(0);
}

/*
 * Sort the passed string vecotor into ascending dictionary
 * order.
 */

sort(list)
	char **list;
{
	register char **ap;
	int diction();

	for (ap = list; *ap != NOSTR; ap++)
		;
	if (ap-list < 2)
		return;
	qsort((char *) list, ap-list, sizeof *list, diction);
}

/*
 * Do a dictionary order comparison of the arguments from
 * qsort.
 */

diction(a, b)
	register char **a, **b;
{
	return(strcmp(*a, *b));
}

/*
 * The do nothing command for comments.
 */

null()
{
	return(0);
}

/*
 * Print out the current edit file, if we are editing.
 * Otherwise, print the name of the person who's mail
 * we are reading.
 */

file(argv)
	char **argv;
{
	register char *cp;
	int ed;

	if (argv[0] == NOSTR) {
		newfileinfo();
		return(0);
	}

	/*
	 * Acker's!  Must switch to the new file.
	 * We use a funny interpretation --
	 *	# -- gets the previous file
	 *	% -- gets the invoker's post office box
	 *	%user -- gets someone else's post office box
	 *	& -- gets invoker's mbox file
	 *	string -- reads the given file
	 */

	cp = getfilename(argv[0], &ed);
	if (cp == NOSTR)
		return(-1);
	if (setfile(cp, ed)) {
		perror(cp);
		return(-1);
	}
	newfileinfo();
	return(0);
}

/*
 * Evaluate the string given as a new mailbox name.
 * Ultimately, we want this to support a number of meta characters.
 * Possibly:
 *	% -- for my system mail box
 *	%user -- for user's system mail box
 *	# -- for previous file
 *	& -- get's invoker's mbox file
 *	file name -- for any other file
 */

char	prevfile[PATHSIZE];

char *
getfilename(name, aedit)
	char *name;
	int *aedit;
{
	register char *cp;
	char savename[BUFSIZ];
	char oldmailname[BUFSIZ];

	/*
	 * Assume we will be in "edit file" mode, until
	 * proven wrong.
	 */
	*aedit = 1;
	switch (*name) {
	case '%':
		*aedit = 0;
		strcpy(prevfile, mailname);
		if (name[1] != 0) {
			strcpy(savename, myname);
			strcpy(oldmailname, mailname);
			strncpy(myname, name+1, PATHSIZE-1);
			myname[PATHSIZE-1] = 0;
			findmail();
			cp = savestr(mailname);
			strcpy(myname, savename);
			strcpy(mailname, oldmailname);
			return(cp);
		}
		strcpy(oldmailname, mailname);
		findmail();
		cp = savestr(mailname);
		strcpy(mailname, oldmailname);
		return(cp);

	case '#':
		if (name[1] != 0)
			goto regular;
		if (prevfile[0] == 0) {
			printf("No previous file\n");
			return(NOSTR);
		}
		cp = savestr(prevfile);
		strcpy(prevfile, mailname);
		return(cp);

	case '&':
		strcpy(prevfile, mailname);
		if (name[1] == 0)
			return(mbox);
		/* Fall into . . . */

	default:
regular:
		strcpy(prevfile, mailname);
		cp = expand(name);
		return(cp);
	}
}

/*
 * Expand file names like echo
 */

echo(argv)
	char **argv;
{
	register char **ap;
	register char *cp;

	for (ap = argv; *ap != NOSTR; ap++) {
		cp = *ap;
		if ((cp = expand(cp)) != NOSTR)
			printf("%s ", cp);
	}
	return(0);
}

/*
 * Reply to a series of messages by simply mailing to the senders
 * and not messing around with the To: and Cc: lists as in normal
 * reply.
 */

Respond(msgvec)
	int msgvec[];
{
	struct header head;
	struct message *mp;
	register int s, *ap;
	register char *cp, *cp2, *subject;

	for (s = 0, ap = msgvec; *ap != 0; ap++) {
		mp = &message[*ap - 1];
		dot = mp;
		if ((cp = hfield("from", mp)) != NOSTR)
		    s+= strlen(cp) + 2;
		else
		    s += strlen(nameof(mp, 2)) + 2;
	}
	if (s == 0)
		return(0);
	cp = salloc(s + 2);
	head.h_to = cp;
	for (ap = msgvec; *ap != 0; ap++) {
		mp = &message[*ap - 1];
		if ((cp2 = hfield("from", mp)) == NOSTR)
		    cp2 = nameof(mp, 2);
		*cp++ = ' ';
		cp = copy(cp2, cp);
		*cp++ = ',';
	}
	*--cp = 0;
	mp = &message[msgvec[0] - 1];
	subject = hfield("subject", mp);
	head.h_seq = 0;
	if (subject == NOSTR)
		subject = hfield("subj", mp);
	head.h_subject = reedit(subject);
	if (subject != NOSTR)
		head.h_seq++;
	head.h_cc = NOSTR;
	head.h_bcc = NOSTR;
	mail1(&head);
	return(0);
}

/*
 * Conditional commands.  These allow one to parameterize one's
 * .mailrc and do some things if sending, others if receiving.
 */

ifcmd(argv)
	char **argv;
{
	register char *cp;

	if (cond != CANY) {
		printf("Illegal nested \"if\"\n");
		return(1);
	}
	cond = CANY;
	cp = argv[0];
	switch (*cp) {
	case 'r': case 'R':
		cond = CRCV;
		break;

	case 's': case 'S':
		cond = CSEND;
		break;

	default:
		printf("Unrecognized if-keyword: \"%s\"\n", cp);
		return(1);
	}
	return(0);
}

/*
 * Implement 'else'.  This is pretty simple -- we just
 * flip over the conditional flag.
 */

elsecmd()
{

	switch (cond) {
	case CANY:
		printf("\"Else\" without matching \"if\"\n");
		return(1);

	case CSEND:
		cond = CRCV;
		break;

	case CRCV:
		cond = CSEND;
		break;

	default:
		printf("Mail's idea of conditions is screwed up\n");
		cond = CANY;
		break;
	}
	return(0);
}

/*
 * End of if statement.  Just set cond back to anything.
 */

endifcmd()
{

	if (cond == CANY) {
		printf("\"Endif\" without matching \"if\"\n");
		return(1);
	}
	cond = CANY;
	return(0);
}

/*
 * Set the list of alternate names.
 */
alternates(namelist)
	char **namelist;
{
	register int c;
	register char **ap, **ap2, *cp;

	c = argcount(namelist) + 1;
	if (c == 1) {
		if (altnames == 0)
			return(0);
		for (ap = altnames; *ap; ap++)
			printf("%s ", *ap);
		printf("\n");
		return(0);
	}
	if (altnames != 0)
		cfree((char *) altnames);
	altnames = (char **) calloc((unsigned) c, sizeof (char *));
	for (ap = namelist, ap2 = altnames; *ap; ap++, ap2++) {
		cp = (char *) calloc((unsigned) (strlen(*ap) + 1),
				     sizeof (char));
		strcpy(cp, *ap);
		*ap2 = cp;
	}
	*ap2 = 0;
	return(0);
}
s mbox file
 *	file name -- for any other file
 */

char	prevfile[PAmmdf/uip/ucbmail/mmdf.h   444      0     12        2553  3620510627  10264 /*
 *  M M D F . H 
 *
 *  EE/CIS Computer Lab
 *  Department of Computer and Information Sciences
 *  Department of Electrical Engineering
 *  University of Delaware
 *
 *  REVISION HISTORY:
 *
 *  $Revision: 1.4 $
 *
 *  $Log:	mmdf.h,v $
 * Revision 1.4  86/01/02  16:23:51  galvin
 * Add some defines from "util.h" since we can not include
 * it directly.  What a pity.
 * 
 * Revision 1.3  85/12/18  13:24:03  galvin
 * Add declaration of sentprotect.
 * 
 * Revision 1.2  85/11/20  14:37:19  galvin
 * First set of needed MMDF global variables.  We can read the maildrop now.
 * 
 * Revision 1.1  85/11/18  13:18:42  galvin
 * Added comment header for revision history.
 * 
 *
 */

#ifndef FILE
#include <stdio.h>
#endif

/*
 * Global declarations needed to use some MMDF routines.
 */

extern char	*delim1,	/* message prefix delimiter */
		*delim2,	/* message suffix delimiter */
		*mldflfil,	/* default mailbox file */
		*mldfldir;	/* directory containing mailbox file */

extern FILE	*lk_fopen();
extern char	sentprotect;	/* default protection for mail files */

/*
 * We pick some of "util.h" to put here.  Would be nice to just include
 * it directly but it includes many files which are otherwise include
 * be Berkeley and the hack to undo it is not worth it.
 */

#define	isnull(chr)	((chr) == '\0')

#define YES     1
#define NO      0
#define OK      0
#define NOTOK   -1
y(oldmailname, mailname);
			strncpy(myname, name+1, PATHSIZE-1);
			myname[PATHSIZE-1] = 0;
			findmail();
			cp = savestr(mailname);
			strcpy(mynammdf/uip/ucbmail/edit.c   444      0     12       10513  3670645532  10305 /*
 *  E D I T . C 
 *
 *  EE/CIS Computer Lab
 *  Department of Computer and Information Sciences
 *  Department of Electrical Engineering
 *  University of Delaware
 *
 *  REVISION HISTORY:
 *
 *  $Revision: 1.3 $
 *
 *  $Log:	edit.c,v $
 * Revision 1.3  85/12/18  13:20:26  galvin
 * Add another argument to send to indicate whether or not this
 * message should be delimited by MMDF message delimiters.
 * 
 * Revision 1.2  85/12/18  03:17:35  galvin
 * Added comment header for revision history.
 * 
 *
 */

/*
 * Copyright (c) 1980 Regents of the University of California.
 * All rights reserved.  The Berkeley software License Agreement
 * specifies the terms and conditions for redistribution.
 */

#ifndef lint
static char *sccsid = "@(#)edit.c	5.2 (Berkeley) 6/21/85";
#endif not lint

#include "./rcv.h"
#include <stdio.h>
#include <sys/stat.h>

/*
 * Mail -- a mail program
 *
 * Perform message editing functions.
 */

/*
 * Edit a message list.
 */

editor(msgvec)
	int *msgvec;
{
	char *edname;

	if ((edname = value("EDITOR")) == NOSTR)
		edname = EDITOR;
	return(edit1(msgvec, edname));
}

/*
 * Invoke the visual editor on a message list.
 */

visual(msgvec)
	int *msgvec;
{
	char *edname;

	if ((edname = value("VISUAL")) == NOSTR)
		edname = VISUAL;
	return(edit1(msgvec, edname));
}

/*
 * Edit a message by writing the message into a funnily-named file
 * (which should not exist) and forking an editor on it.
 * We get the editor from the stuff above.
 */

edit1(msgvec, ed)
	int *msgvec;
	char *ed;
{
	register char *cp, *cp2;
	register int c;
	int *ip, pid, mesg, lines;
	long ms;
	int (*sigint)(), (*sigquit)();
	FILE *ibuf, *obuf;
	char edname[15], nbuf[10];
	struct message *mp;
	off_t fsize(), size;
	struct stat statb;
	long modtime;

	/*
	 * Set signals; locate editor.
	 */

	sigint = sigset(SIGINT, SIG_IGN);
	sigquit = sigset(SIGQUIT, SIG_IGN);

	/*
	 * Deal with each message to be edited . . .
	 */

	for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) {
		mesg = *ip;
		mp = &message[mesg-1];
		mp->m_flag |= MODIFY;

		/*
		 * Make up a name for the edit file of the
		 * form "Message%d" and make sure it doesn't
		 * already exist.
		 */

		cp = &nbuf[10];
		*--cp = 0;
		while (mesg) {
			*--cp = mesg % 10 + '0';
			mesg /= 10;
		}
		cp2 = copy("Message", edname);
		while (*cp2++ = *cp++)
			;
		if (!access(edname, 2)) {
			printf("%s: file exists\n", edname);
			goto out;
		}

		/*
		 * Copy the message into the edit file.
		 */

		close(creat(edname, 0600));
		if ((obuf = fopen(edname, "w")) == NULL) {
			perror(edname);
			goto out;
		}
		if (send(mp, obuf, 0, 0) < 0) {
			perror(edname);
			fclose(obuf);
			remove(edname);
			goto out;
		}
		fflush(obuf);
		if (ferror(obuf)) {
			remove(edname);
			fclose(obuf);
			goto out;
		}
		fclose(obuf);

		/*
		 * If we are in read only mode, make the
		 * temporary message file readonly as well.
		 */

		if (readonly)
			chmod(edname, 0400);

		/*
		 * Fork/execl the editor on the edit file.
		 */

		if (stat(edname, &statb) < 0)
			modtime = 0;
		modtime = statb.st_mtime;
		pid = vfork();
		if (pid == -1) {
			perror("fork");
			remove(edname);
			goto out;
		}
		if (pid == 0) {
			sigchild();
			if (sigint != SIG_IGN)
				sigsys(SIGINT, SIG_DFL);
			if (sigquit != SIG_IGN)
				sigsys(SIGQUIT, SIG_DFL);
			execl(ed, ed, edname, 0);
			perror(ed);
			_exit(1);
		}
		while (wait(&mesg) != pid)
			;

		/*
		 * If in read only mode, just remove the editor
		 * temporary and return.
		 */

		if (readonly) {
			remove(edname);
			continue;
		}

		/*
		 * Now copy the message to the end of the
		 * temp file.
		 */

		if (stat(edname, &statb) < 0) {
			perror(edname);
			goto out;
		}
		if (modtime == statb.st_mtime) {
			remove(edname);
			goto out;
		}
		if ((ibuf = fopen(edname, "r")) == NULL) {
			perror(edname);
			remove(edname);
			goto out;
		}
		remove(edname);
		fseek(otf, (long) 0, 2);
		size = fsize(otf);
		mp->m_block = blockof(size);
		mp->m_offset = offsetof(size);
		ms = 0L;
		lines = 0;
		while ((c = getc(ibuf)) != EOF) {
			if (c == '\n')
				lines++;
			putc(c, otf);
			if (ferror(otf))
				break;
			ms++;
		}
		mp->m_size = ms;
		mp->m_lines = lines;
		if (ferror(otf))
			perror("/tmp");
		fclose(ibuf);
	}

	/*
	 * Restore signals and return.
	 */
	sigset(SIGINT, sigint);
	sigset(SIGQUIT, sigquit);
	return(0);

out:
	sigset(SIGINT, sigint);
	sigset(SIGQUIT, sigquit);
	return(-1);
}
- we just
 * flip over the conditional flag.
 */

elsecmd()
{

	switch (cond) {
	case CANY:
		printf("\"Else\" without matching \"if\"\n");
		return(1);

	case CSEND:
		cond = CRCV;mmdf/uip/ucbmail/cmdtab.c   444      0     12       10124  3670644423  10606 /*
 *  C M D T A B . C 
 *
 *  EE/CIS Computer Lab
 *  Department of Computer and Information Sciences
 *  Department of Electrical Engineering
 *  University of Delaware
 *
 *  REVISION HISTORY:
 *
 *  $Revision: 1.3 $
 *
 *  $Log:	cmdtab.c,v $
 * Revision 1.3  85/11/20  14:32:21  galvin
 * Changed the name of the command table (cmdtab) to CmdTab so as not
 * to conflict with the command table of MMDF.
 * 
 * Revision 1.2  85/11/20  14:29:22  galvin
 * Added comment header for revision history.
 * 
 *
 */

/*
 * Copyright (c) 1980 Regents of the University of California.
 * All rights reserved.  The Berkeley software License Agreement
 * specifies the terms and conditions for redistribution.
 */

#ifndef lint
static char *sccsid = "@(#)cmdtab.c	5.3 (Berkeley) 9/15/85";
#endif not lint

#include "./def.h"

/*
 * Mail -- a mail program
 *
 * Define all of the command names and bindings.
 */

extern int type(), preserve(), delnorm(), undelete(), next(), shell(), schdir();
extern int save(), help(), headers(), pdot(), respond(), editor();
extern int edstop(), rexit(), pcmdlist(), sendmail(), from(), copycmd();
extern int messize(), deltype(), unset(), set(), source();
extern int pversion(), group(), top(), core(), null(), stouch(), visual();
extern int swrite(), dosh(), file(), echo(), Respond(), scroll(), ifcmd();
extern int elsecmd(), endifcmd(), mboxit(), clobber(), alternates();
extern int local(), folders(), igfield(), Type(), retfield(), more(), More();
extern int unread();	/* , Header(); */

struct cmd CmdTab[] = {
	"next",		next,		NDMLIST,	0,	MMNDEL,
	"alias",	group,		M|RAWLIST,	0,	1000,
	"print",	type,		MSGLIST,	0,	MMNDEL,
	"type",		type,		MSGLIST,	0,	MMNDEL,
	"Type",		Type,		MSGLIST,	0,	MMNDEL,
	"Print",	Type,		MSGLIST,	0,	MMNDEL,
	"visual",	visual,		I|MSGLIST,	0,	MMNORM,
	"top",		top,		MSGLIST,	0,	MMNDEL,
	"touch",	stouch,		W|MSGLIST,	0,	MMNDEL,
	"preserve",	preserve,	W|MSGLIST,	0,	MMNDEL,
	"delete",	delnorm,	W|P|MSGLIST,	0,	MMNDEL,
	"dp",		deltype,	W|MSGLIST,	0,	MMNDEL,
	"dt",		deltype,	W|MSGLIST,	0,	MMNDEL,
	"undelete",	undelete,	P|MSGLIST,	MDELETED,MMNDEL,
	"unset",	unset,		M|RAWLIST,	1,	1000,
	"mail",		sendmail,	R|M|I|STRLIST,	0,	0,
	"mbox",		mboxit,		W|MSGLIST,	0,	0,
	"more",		more,		MSGLIST,	0,	MMNDEL,
	"page",		more,		MSGLIST,	0,	MMNDEL,
	"More",		More,		MSGLIST,	0,	MMNDEL,
	"Page",		More,		MSGLIST,	0,	MMNDEL,
	"unread",	unread,		MSGLIST,	0,	MMNDEL,
	"Unread",	unread,		MSGLIST,	0,	MMNDEL,
	"new",		unread,		MSGLIST,	0,	MMNDEL,
	"New",		unread,		MSGLIST,	0,	MMNDEL,
	"!",		shell,		I|STRLIST,	0,	0,
	"copy",		copycmd,	M|STRLIST,	0,	0,
	"chdir",	schdir,		M|STRLIST,	0,	0,
	"cd",		schdir,		M|STRLIST,	0,	0,
	"save",		save,		STRLIST,	0,	0,
	"source",	source,		M|STRLIST,	0,	0,
	"set",		set,		M|RAWLIST,	0,	1000,
	"shell",	dosh,		I|NOLIST,	0,	0,
	"version",	pversion,	M|NOLIST,	0,	0,
	"group",	group,		M|RAWLIST,	0,	1000,
	"write",	swrite,		STRLIST,	0,	0,
	"from",		from,		MSGLIST,	0,	MMNORM,
	"file",		file,		T|M|RAWLIST,	0,	1,
	"folder",	file,		T|M|RAWLIST,	0,	1,
	"folders",	folders,	T|M|RAWLIST,	0,	1,
	"?",		help,		M|NOLIST,	0,	0,
	"z",		scroll,		M|STRLIST,	0,	0,
	"headers",	headers,	MSGLIST,	0,	MMNDEL,
	"help",		help,		M|NOLIST,	0,	0,
	"=",		pdot,		NOLIST,		0,	0,
	"Reply",	Respond,	R|I|MSGLIST,	0,	MMNDEL,
	"Respond",	Respond,	R|I|MSGLIST,	0,	MMNDEL,
	"reply",	respond,	R|I|MSGLIST,	0,	MMNDEL,
	"respond",	respond,	R|I|MSGLIST,	0,	MMNDEL,
	"edit",		editor,		I|MSGLIST,	0,	MMNORM,
	"echo",		echo,		M|RAWLIST,	0,	1000,
	"quit",		edstop,		NOLIST, 	0,	0,
	"list",		pcmdlist,	M|NOLIST,	0,	0,
	"local",	local,		M|RAWLIST,	0,	1000,
	"xit",		rexit,		M|NOLIST,	0,	0,
	"exit",		rexit,		M|NOLIST,	0,	0,
	"size",		messize,	MSGLIST,	0,	MMNDEL,
	"hold",		preserve,	W|MSGLIST,	0,	MMNDEL,
	"if",		ifcmd,		F|M|RAWLIST,	1,	1,
	"else",		elsecmd,	F|M|RAWLIST,	0,	0,
	"endif",	endifcmd,	F|M|RAWLIST,	0,	0,
	"alternates",	alternates,	M|RAWLIST,	0,	1000,
	"ignore",	igfield,	M|RAWLIST,	0,	1000,
	"discard",	igfield,	M|RAWLIST,	0,	1000,
	"retain",	retfield,	M|RAWLIST,	0,	1000,
/*	"Header",	Header,		STRLIST,	0,	1000,	*/
	"core",		core,		M|NOLIST,	0,	0,
	"#",		null,		M|NOLIST,	0,	0,
	"clobber",	clobber,	M|RAWLIST,	0,	1,
	0,		0,		0,		0,	0
};
erved.  The Berkeley software License Agreement
 * specifies the terms and conditions for redistribution.
 */

#ifndef lint
static char *sccsid = "@(#)cmdtab.c	5.3 (Berkeley) 9/15/85";
#endif not lint

#include "./def.h"

/*
 * Mail -- a mail program
 *
 * Define all of the command names and bindings.
 */

extern int type(), preserve(), delnorm(), undelete(), next(), shell(), schdir();
extern int save(), help(), headers(), pmmdf/uip/ucbmail/dateparse.c   444      0     12       15073  3642267110  11325 /*
 * We borrow this from MMDF's msg.
 */
/*
 *			M S G 6 . C
 *
 * Functions -
 *	smtpdate	Converts RFC822 date string to UNIX long
 *	sunday
 *	eatwhite	Returns first nonwhitespace char in string
 *	match
 *	makedate	Convert UNIX time to string
 *
 *
 *		R E V I S I O N   H I S T O R Y
 *
 *	07/08/84  HAW	Created this module for time stamp processing.
 *			Most routines in this module were written
 *			by Ron Natelie.
 *
 */
#include "./rcv.h"
#include <ctype.h>

extern	long	timezone;

/*
 *  S M T P _ D A T E
 *
 *  Take an RFC822 (more or less) date string cp and convert it to
 *  unix time.  Allows a variety of illegal formats commonly in use.
 *
 */

#define	SPACE	' '
#define	HTAB	'\t'

/*
 *  Matchtab structure.
 *  Used by match routine.
 */
struct	match_tab  {
	char	*t_string;		/*  String to match, NOSTR for last.  */
	int	t_val;			/*  Value to be returned.  */
};

/*
 *  Month tab matches month names to number.
 */
struct	match_tab	month_tab[]  =  {
	"jan", 1,	"feb", 2,	"mar", 3,	"apr", 4,
	"may", 5,	"jun", 6,	"jul", 7,	"aug", 8,
	"sep", 9,	"oct", 10,	"nov", 11,	"dec", 12,
	NOSTR, 0
};
	
#define	H(x)	( (x)*60*60)
struct	match_tab	zone_tab[] = {
	"ut", 0,	"gmt", 0,	"est", H(-5),	"edt", H(-4),
	"cst", H(-6),	"cdt", H(-5),	"mst", H(-7),	"mdt", H(-6),
	"pst", H(-8),	"pdt", H(-7),	"z", 0,
	"a", H(-1),	"b", H(-2),	"c", H(-3),	"d", H(-4),
	"e", H(-5),	"f", H(-6),	"g", H(-7),	"h", H(-8),
	"i", H(-9),	"k", H(-10),	"l", H(-11),	"m", H(-12),
	"n", H(1),	"o", H(2),	"p", H(3),	"q", H(4),
	"r", H(5),	"s", H(6),	"t", H(7),	"u", H(8),
	"v", H(9),	"w", H(10),	"x", H(11),	"y", H(12),
	NOSTR, 1
};

#define	dysize(A) (((A)%4) ? 365: 366)
static int dmsize[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
extern char *index();
extern char *eatwhite();

long
smtpdate(cp)
	register char	*cp;
{
	int	day, month, year, hours, minutes, seconds;
	int	dayno;
	long	tval;
	int	i;
	char	*dp;
	time_t	zone;
/*printf("S entry - date str: %s", cp); */
	cp = eatwhite(cp);
	dp = cp;


	/*
	 *  Look for digit, supposed to be day of the month, skipping
	 *  over day of the week stuff.
	 */
	while(1)  {
		if(*cp == 0)  goto bad_date;
		if(isdigit(*cp)) break;
		cp++;
	}

	day = atoi(cp);
/*printf("S day: %d\n",day); */
	/*  Skip digits and white space to get to month.  */

	while(isdigit(*cp)) cp++;
	while( *cp == SPACE || *cp == HTAB || *cp == '-') cp++;

	month = match(month_tab, cp, 3);
	if(month == 0)  {
		/*  Hack here, look for ctime format.  */
		day = atoi(&dp[8]);
		month = match(month_tab, &dp[4], 3);
		if(month == 0) goto bad_date;
		year = atoi(&dp[20]);
		hours = atoi(&dp[11]);
		minutes = atoi(&dp[14]);
		seconds = atoi(&dp[17]);
		zone = -1;
/*printf("S ctime for: y: %d m: %d d: %d h: %d m: %d s: %d\n",year,month,day,hours,minutes,seconds); */

	}
	else  {
		/*  Skip over to the year  */
		while(1)  {
			if(*cp == 0)  goto bad_date;
			if(isdigit(*cp)) break;
			cp++;
		}
		year = atoi(cp);

		while(isdigit(*cp)) cp++;

		while(*cp && !isdigit(*cp)) cp++;
	

		/*  Hours, minutes, seconds  */
		hours = atoi(cp);
		if(hours > 60)  {
			minutes = hours %100;
			hours = hours/100;
		}  else  {
			cp = index(cp, ':');
			if(cp == NOSTR) goto bad_date;
			minutes = atoi(++cp);
		}

		dp = index(cp, ':');
		if(dp)  {
			seconds = atoi(++dp);
			cp = dp;
		}
		else	seconds = 0;
/*printf("S hours: %d  mins: %d  secs: %d\n",hours,minutes,seconds); */
		while(isdigit(*cp)) cp++;
		cp = eatwhite(cp);
/*printf("S Zone starts- str: %s",cp); */
		if( (*cp == '+' || *cp == '-')  &&
		   isdigit(cp[1]) &&
		   isdigit(cp[2]) &&
		   isdigit(cp[3]) &&
		   isdigit(cp[4])
		)  {
			zone = atoi(cp+3) * 60L;
			cp[3] = 0;
			zone *= atoi(cp);
		} else {
			if( *cp == '+' || *cp == '-' )
				cp++;
			dp = cp;
			while(isalpha(*cp)) cp++;
			*cp = 0;
			zone = -match(zone_tab, dp, 0);
		}
/*printf("S zone: %d\n",zone); */
	}
	if(year < 1900) year += 1900;
	if(month < 1 || month > 12)  goto bad_date;
	if(day < 1 || day > 31) goto bad_date;
	if(hours == 24)  {
		hours = 0;
		day++;
	}
	if(hours < 0 || hours > 23) goto bad_date;
	if(minutes < 0 || minutes > 59) goto bad_date;
	if(seconds < 0 || seconds > 59) goto bad_date;

	tval = 0;
	dayno = 0;
	for(i = 1970; i < year; i++)
		tval += dysize(i);

	if(dysize(year) == 366  && month >= 3)
		dayno += 1;
	while(--month) dayno += dmsize[month-1];
	tval += dayno;
	tval += day-1;
	tval *= 24;
	tval += hours;
	tval *= 60;
	tval += minutes;
	tval *= 60;
	tval += seconds;
	if(zone == -1)  {
		int beg, end;
		beg = sunday(119, year);
		end = sunday(303, year);
/*printf("S beg: %d  end: %d\n",beg,end); */
		zone = gettimezone();
/*printf("S timezone: %d  daylight: %d: dayno: %d\n",timezone,DAYLIGHT,dayno); */
		if(DAYLIGHT &&
		    (dayno>beg || (dayno==beg && hours >=2)) &&
		    (dayno<end || (dayno==end && hours <1)))
		{
/*printf("S changing zone for DAYLIGHT\n"); */
			zone -= 60*60;
		}
	}
/*printf("S before zone: %s",ctime(&tval)); */
	tval += zone;
/*printf("S after zone: %s\n",ctime(&tval)); */
	return tval;

bad_date:
	return -1L;
}

#ifdef SYS5
time_t
gettimezone()
{
	extern long timezone;
	static int set = 0;

	if (!set) {
		tzset();
		set = 1;
	}
	return((time_t)timezone);
}
#else
#include <sys/timeb.h>

time_t
gettimezone()
{
	static struct timeb tb;
	static int set = 0;

	if (!set) {
		ftime(&tb);
		set = 1;
	}
	return((time_t)tb.timezone);
}
#endif

static int
sunday(d, year)
{
	int i;
	int	pdays = 0;

	for(i = 1970; i < year; i++)
		pdays += dysize(i);
	if(d >= 58 && dysize(year) == 366) d++;
	pdays = (pdays - 3) % 7;	/*  Day of week, day 0 was thurs  */
	if(pdays) d += 7-pdays;
	return d;
}
/*
 *  E A T W H I T E
 *
 *  An old favorite, returns the first non whitespace in the string.
 */
char *
eatwhite(cp)
	register char *cp;
{
	while(*cp == SPACE || *cp == HTAB) cp++;
	return cp;
}

match(tab, string, size)
	register struct	match_tab	*tab;
	char	*string;
	int	size;
{
	register char *cp, *str;
	char	*bufend;
	char	buffer[512];
	
	bufend = &buffer[ (size == 0) ? 512 : size];
	for(cp = buffer, str = string; cp < bufend; cp++, str++)  {
		if(isupper(*str)) *cp = tolower(*str);
		else	*cp = *str;
		if(*cp == 0) break;
	}

	while(tab->t_string != NOSTR)  {
		if(size)  {
			if(strncmp(buffer,tab->t_string, size) == 0)
				break;
		}  else  {
			if(strcmp(buffer, tab->t_string) == 0)
				break;
		}
		tab++;
	}
	return tab->t_val;
}

/*
 *			M A K E D A T E
 *
 * Convert UNIX date to string (DD MMM YY)
 */
makedate( date, dest )
long	*date;
char	*dest;
{
	char *cp;
	char *ctime( );

	cp = ctime( date );
 	*dest++ = cp[8];
 	*dest++ = cp[9];
 	*dest++ = ' ';
 	*dest++ = cp[4];
 	*dest++ = cp[5];
 	*dest++ = cp[6];
 	*dest++ = ' ';
 	*dest++ = cp[22];
 	*dest++ = cp[23];

}
nutes = atoi(&dp[14]);
		seconds = atoi(&dp[17]);
		zone = -1;
/*printf("S ctime for: y: %d m: %d d: %d h: %d m: %d s: %d\n",year,month,day,hours,minutes,seconds); */

	}
	else  {
		/*  Skip over to the year  */
		while(1)  {
			if(*cp == 0)  goto bad_date;
			if(isdigit(*cp)) break;
			cp++;
		}
		year = atoi(cp);

		while(isdigit(*cp)) cp++;

		while(*cp && !isdigit(*cp)) cp++;
	

		/*  Hours, minutes, seconds  */
		hours = atoi(cp);
		if(hours > mmdf/uip/ucbmail/collect.c   444      0     12       42624  3670664432  11015 /*
 *  C O L L E C T . C 
 *  EE/CIS Computer Lab
 *  Department of Computer and Information Sciences
 *  Department of Electrical Engineering
 *  University of Delaware
 *
 *  REVISION HISTORY:
 *
 *  $Revision: 1.5 $
 *
 *  $Log:	collect.c,v $
 * Revision 1.5  86/01/14  14:22:45  galvin
 * Add extract() and detract() calls to parse the lines entered by the
 * user when attempting to add to the To, Cc and Bcc address lists.
 * 
 * Fix addto() to insert commas between addresses.
 * 
 * Revision 1.4  85/12/18  13:20:12  galvin
 * Add another argument to send to indicate whether or not this
 * message should be delimited by MMDF message delimiters.
 * 
 * Revision 1.3  85/11/16  15:18:24  galvin
 * Added define for sigmask for backward compatibility from 4.3bsd to 4.2bsd.
 * 
 * Revision 1.2  85/11/16  14:27:08  galvin
 * Added comment header for revision history.
 * 
 *
 */

/*
 * Copyright (c) 1980 Regents of the University of California.
 * All rights reserved.  The Berkeley software License Agreement
 * specifies the terms and conditions for redistribution.
 */

#ifndef lint
static char *sccsid = "@(#)collect.c	5.2 (Berkeley) 6/21/85";
#endif not lint

/*
 * Mail -- a mail program
 *
 * Collect input from standard input, handling
 * ~ escapes.
 */

#include "./rcv.h"
#include <sys/stat.h>
#include "./sigretro.h"

/*
 * Read a message from standard output and return a read file to it
 * or NULL on error.
 */

/*
 * The following hokiness with global variables is so that on
 * receipt of an interrupt signal, the partial message can be salted
 * away on dead.letter.  The output file must be available to flush,
 * and the input to read.  Several open files could be saved all through
 * Mail if stdio allowed simultaneous read/write access.
 */

static	int	(*savesig)();		/* Previous SIGINT value */
static	int	(*savehup)();		/* Previous SIGHUP value */
# ifdef VMUNIX
static	int	(*savecont)();		/* Previous SIGCONT value */
# endif VMUNIX
static	FILE	*newi;			/* File for saving away */
static	FILE	*newo;			/* Output side of same */
static	int	hf;			/* Ignore interrups */
static	int	hadintr;		/* Have seen one SIGINT so far */

static	jmp_buf	coljmp;			/* To get back to work */

FILE *
collect(hp)
	struct header *hp;
{
	FILE *ibuf, *fbuf, *obuf;
	int lc, cc, escape, collrub(), intack(), collcont(), eof;
	register int c, t;
	char linebuf[LINESIZE], *cp;
	extern char tempMail[];
#ifndef V4_2BSD
#ifndef V4_1BSD
	extern collintsig(), collhupsig();
#endif  V4_1BSD
#endif  V4_2BSD
	char getsub;

	noreset++;
	ibuf = obuf = NULL;
	if (value("ignore") != NOSTR)
		hf = 1;
	else
		hf = 0;
	hadintr = 0;
# ifdef V4_2BSD
	if ((savesig = sigset(SIGINT, SIG_IGN)) != SIG_IGN)
		sigset(SIGINT, hf ? intack : collrub), sigblock(sigmask(SIGINT)
);
	if ((savehup = sigset(SIGHUP, SIG_IGN)) != SIG_IGN)
		sigset(SIGHUP, collrub), sigblock(sigmask(SIGHUP));
	savecont = sigset(SIGCONT, collcont);
# else V4_2BSD
# ifdef V4_1BSD
	if ((savesig = sigset(SIGINT, SIG_IGN)) != SIG_IGN)
		sigset(SIGINT, hf ? intack : collrub), sighold(SIGINT);
	if ((savesig = sigset(SIGHUP, SIG_IGN)) != SIG_IGN)
		sigset(SIGHUP, hf ? intack : collrub), sighold(SIGHUP);
	savecont = sigset(SIGCONT, collcont);
# else
	savesig = signal(SIGINT, SIG_IGN);
	savehup = signal(SIGHUP, SIG_IGN);
# endif V4_1BSD
# endif V4_2BSD
	newi = NULL;
	newo = NULL;
	if ((obuf = fopen(tempMail, "w")) == NULL) {
		perror(tempMail);
		goto err;
	}
	newo = obuf;
	if ((ibuf = fopen(tempMail, "r")) == NULL) {
		perror(tempMail);
		newo = NULL;
		fclose(obuf);
		goto err;
	}
	newi = ibuf;
	remove(tempMail);

	/*
	 * If we are going to prompt for a subject,
	 * refrain from printing a newline after
	 * the headers (since some people mind).
	 */

	t = GTO|GSUBJECT|GCC|GNL;
	getsub = 0;
	if (intty && sflag == NOSTR && hp->h_subject == NOSTR && value("ask"))
		t &= ~GNL, getsub++;
	if (hp->h_seq != 0) {
		puthead(hp, stdout, t);
		fflush(stdout);
	}
	escape = ESCAPE;
	if ((cp = value("escape")) != NOSTR)
		escape = *cp;
	eof = 0;
	for (;;) {
# ifdef V4_2BSD
		int omask = sigblock(0) &~ (sigmask(SIGINT)|sigmask(SIGHUP));
# endif V4_2BSD

		setjmp(coljmp);
# ifdef V4_2BSD
		sigsetmask(omask);
# else  V4_2BSD
# ifdef V4_1BSD
		if (savesig != SIG_IGN)
			sigrelse(SIGINT);
		if (savehup != SIG_IGN)
			sigrelse(SIGHUP);
# else  V4_1BSD
		if (savesig != SIG_IGN)
			signal(SIGINT, hf ? intack : collintsig);
		if (savehup != SIG_IGN)
			signal(SIGHUP, collhupsig);
# endif V4_1BSD
# endif V4_2BSD
		fflush(stdout);
		if (getsub) {
			grabh(hp, GSUBJECT);
			getsub = 0;
			continue;
		}
		if (readline(stdin, linebuf) <= 0) {
			if (intty && value("ignoreeof") != NOSTR) {
				if (++eof > 35)
					break;
				printf("Use \".\" to terminate letter\n",
				    escape);
				continue;
			}
			break;
		}
		eof = 0;
		hadintr = 0;
		if (intty && equal(".", linebuf) &&
		    (value("dot") != NOSTR || value("ignoreeof") != NOSTR))
			break;
		if (linebuf[0] != escape || rflag != NOSTR) {
			if ((t = putline(obuf, linebuf)) < 0)
				goto err;
			continue;
		}
		c = linebuf[1];
		switch (c) {
		default:
			/*
			 * On double escape, just send the single one.
			 * Otherwise, it's an error.
			 */

			if (c == escape) {
				if (putline(obuf, &linebuf[1]) < 0)
					goto err;
				else
					break;
			}
			printf("Unknown tilde escape.\n");
			break;

		case 'C':
			/*
			 * Dump core.
			 */

			core();
			break;

		case '!':
			/*
			 * Shell escape, send the balance of the
			 * line to sh -c.
			 */

			shell(&linebuf[2]);
			break;

		case ':':
		case '_':
			/*
			 * Escape to command mode, but be nice!
			 */

			execute(&linebuf[2], 1);
			printf("(continue)\n");
			break;

		case '.':
			/*
			 * Simulate end of file on input.
			 */
			goto eofl;

		case 'q':
		case 'Q':
			/*
			 * Force a quit of sending mail.
			 * Act like an interrupt happened.
			 */

			hadintr++;
			collrub(SIGINT);
			exit(1);

		case 'h':
			/*
			 * Grab a bunch of headers.
			 */
			if (!intty || !outtty) {
				printf("~h: no can do!?\n");
				break;
			}
			grabh(hp, GTO|GSUBJECT|GCC|GBCC);
			printf("(continue)\n");
			break;

		case 't':
			/*
			 * Add to the To list.
			 */

			hp->h_to = addto(hp->h_to,
				detract(elide(extract(&linebuf[2], GTO)),
					GTO));
			hp->h_seq++;
			break;

		case 's':
			/*
			 * Set the Subject list.
			 */

			cp = &linebuf[2];
			while (any(*cp, " \t"))
				cp++;
			hp->h_subject = savestr(cp);
			hp->h_seq++;
			break;

		case 'c':
			/*
			 * Add to the CC list.
			 */

			hp->h_cc = addto(hp->h_cc,
				detract(elide(extract(&linebuf[2], GCC)),
					GCC));
			hp->h_seq++;
			break;

		case 'b':
			/*
			 * Add stuff to blind carbon copies list.
			 */
			hp->h_bcc = addto(hp->h_bcc,
				detract(elide(extract(&linebuf[2],
						      GBCC)),GBCC));
			hp->h_seq++;
			break;

		case 'd':
			copy(deadletter, &linebuf[2]);
			/* fall into . . . */

		case 'r':
			/*
			 * Invoke a file:
			 * Search for the file name,
			 * then open it and copy the contents to obuf.
			 */

			cp = &linebuf[2];
			while (any(*cp, " \t"))
				cp++;
			if (*cp == '\0') {
				printf("Interpolate what file?\n");
				break;
			}
			cp = expand(cp);
			if (cp == NOSTR)
				break;
			if (isdir(cp)) {
				printf("%s: directory\n");
				break;
			}
			if ((fbuf = fopen(cp, "r")) == NULL) {
				perror(cp);
				break;
			}
			printf("\"%s\" ", cp);
			fflush(stdout);
			lc = 0;
			cc = 0;
			while (readline(fbuf, linebuf) > 0) {
				lc++;
				if ((t = putline(obuf, linebuf)) < 0) {
					fclose(fbuf);
					goto err;
				}
				cc += t;
			}
			fclose(fbuf);
			printf("%d/%d\n", lc, cc);
			break;

		case 'w':
			/*
			 * Write the message on a file.
			 */

			cp = &linebuf[2];
			while (any(*cp, " \t"))
				cp++;
			if (*cp == '\0') {
				fprintf(stderr, "Write what file!?\n");
				break;
			}
			if ((cp = expand(cp)) == NOSTR)
				break;
			fflush(obuf);
			rewind(ibuf);
			exwrite(cp, ibuf, 1);
			break;

		case 'm':
		case 'f':
			/*
			 * Interpolate the named messages, if we
			 * are in receiving mail mode.  Does the
			 * standard list processing garbage.
			 * If ~f is given, we don't shift over.
			 */

			if (!rcvmode) {
				printf("No messages to send from!?!\n");
				break;
			}
			cp = &linebuf[2];
			while (any(*cp, " \t"))
				cp++;
			if (forward(cp, obuf, c) < 0)
				goto err;
			printf("(continue)\n");
			break;

		case '?':
			if ((fbuf = fopen(THELPFILE, "r")) == NULL) {
				perror(THELPFILE);
				break;
			}
			t = getc(fbuf);
			while (t != -1) {
				putchar(t);
				t = getc(fbuf);
			}
			fclose(fbuf);
			break;

		case 'p':
			/*
			 * Print out the current state of the
			 * message without altering anything.
			 */

			fflush(obuf);
			rewind(ibuf);
			printf("-------\nMessage contains:\n");
			puthead(hp, stdout, GTO|GSUBJECT|GCC|GBCC|GNL);
			t = getc(ibuf);
			while (t != EOF) {
				putchar(t);
				t = getc(ibuf);
			}
			printf("(continue)\n");
			break;

		case '^':
		case '|':
			/*
			 * Pipe message through command.
			 * Collect output as new message.
			 */

			obuf = mespipe(ibuf, obuf, &linebuf[2]);
			newo = obuf;
			ibuf = newi;
			newi = ibuf;
			printf("(continue)\n");
			break;

		case 'v':
		case 'e':
			/*
			 * Edit the current message.
			 * 'e' means to use EDITOR
			 * 'v' means to use VISUAL
			 */

			if ((obuf = mesedit(ibuf, obuf, c)) == NULL)
				goto err;
			newo = obuf;
			ibuf = newi;
			printf("(continue)\n");
			break;
		}
	}
eofl:
	fclose(obuf);
	rewind(ibuf);
	sigset(SIGINT, savesig);
	sigset(SIGHUP, savehup);
# ifdef VMUNIX
	sigset(SIGCONT, savecont);
# ifdef V4_2BSD
	sigsetmask(0);
# endif V4_2BSD
# endif VMUNIX
	noreset = 0;
	return(ibuf);

err:
	if (ibuf != NULL)
		fclose(ibuf);
	if (obuf != NULL)
		fclose(obuf);
	sigset(SIGINT, savesig);
	sigset(SIGHUP, savehup);
# ifdef VMUNIX
	sigset(SIGCONT, savecont);
# ifdef V4_2BSD
	sigsetmask(0);
# endif V4_2BSD
# endif VMUNIX
	noreset = 0;
	return(NULL);
}

#ifdef NOT_USED
/*
 * Non destructively interrogate the value of the given signal.
 */

psig(n)
{
	register (*wassig)();

	wassig = sigset(n, SIG_IGN);
	sigset(n, wassig);
	return((int) wassig);
}
#endif NOT_USED

/*
 * Write a file, ex-like if f set.
 */

exwrite(name, ibuf, f)
	char name[];
	FILE *ibuf;
{
	register FILE *of;
	register int c;
	long cc;
	int lc;
	struct stat junk;

	if (f) {
		printf("\"%s\" ", name);
		fflush(stdout);
	}
	if (stat(name, &junk) >= 0 && (junk.st_mode & S_IFMT) == S_IFREG) {
		if (!f)
			fprintf(stderr, "%s: ", name);
		fprintf(stderr, "File exists\n", name);
		return;
	}
	if ((of = fopen(name, "w")) == NULL) {
		perror(NOSTR);
		return;
	}
	lc = 0;
	cc = 0;
	while ((c = getc(ibuf)) != EOF) {
		cc++;
		if (c == '\n')
			lc++;
		putc(c, of);
		if (ferror(of)) {
			perror(name);
			fclose(of);
			return;
		}
	}
	fclose(of);
	printf("%d/%ld\n", lc, cc);
	fflush(stdout);
}

/*
 * Edit the message being collected on ibuf and obuf.
 * Write the message out onto some poorly-named temp file
 * and point an editor at it.
 *
 * On return, make the edit file the new temp file.
 */

FILE *
mesedit(ibuf, obuf, c)
	FILE *ibuf, *obuf;
{
	int pid, s;
	FILE *fbuf;
	register int t;
	int (*sig)(), (*scont)(), signull();
	struct stat sbuf;
	extern char tempMail[], tempEdit[];
	register char *ed;

	sig = sigset(SIGINT, SIG_IGN);
# ifdef VMUNIX
	scont = sigset(SIGCONT, signull);
# endif VMUNIX
	if (stat(tempEdit, &sbuf) >= 0) {
		printf("%s: file exists\n", tempEdit);
		goto out;
	}
	close(creat(tempEdit, 0600));
	if ((fbuf = fopen(tempEdit, "w")) == NULL) {
		perror(tempEdit);
		goto out;
	}
	fflush(obuf);
	rewind(ibuf);
	t = getc(ibuf);
	while (t != EOF) {
		putc(t, fbuf);
		t = getc(ibuf);
	}
	fflush(fbuf);
	if (ferror(fbuf)) {
		perror(tempEdit);
		remove(tempEdit);
		goto fix;
	}
	fclose(fbuf);
	if ((ed = value(c == 'e' ? "EDITOR" : "VISUAL")) == NOSTR)
		ed = c == 'e' ? EDITOR : VISUAL;
	pid = vfork();
	if (pid == 0) {
		sigchild();
		if (sig != SIG_IGN)
			sigsys(SIGINT, SIG_DFL);
		execl(ed, ed, tempEdit, 0);
		perror(ed);
		_exit(1);
	}
	if (pid == -1) {
		perror("fork");
		remove(tempEdit);
		goto out;
	}
	while (wait(&s) != pid)
		;
	if ((s & 0377) != 0) {
		printf("Fatal error in \"%s\"\n", ed);
		remove(tempEdit);
		goto out;
	}

	/*
	 * Now switch to new file.
	 */

	if ((fbuf = fopen(tempEdit, "a")) == NULL) {
		perror(tempEdit);
		remove(tempEdit);
		goto out;
	}
	if ((ibuf = fopen(tempEdit, "r")) == NULL) {
		perror(tempEdit);
		fclose(fbuf);
		remove(tempEdit);
		goto out;
	}
	remove(tempEdit);
	fclose(obuf);
	fclose(newi);
	obuf = fbuf;
	goto out;
fix:
	perror(tempEdit);
out:
# ifdef VMUNIX
	sigset(SIGCONT, scont);
# endif VMUNIX
	sigset(SIGINT, sig);
	newi = ibuf;
	return(obuf);
}

/*
 * Pipe the message through the command.
 * Old message is on stdin of command;
 * New message collected from stdout.
 * Sh -c must return 0 to accept the new message.
 */

FILE *
mespipe(ibuf, obuf, cmd)
	FILE *ibuf, *obuf;
	char cmd[];
{
	register FILE *ni, *no;
	int pid, s;
	int (*savsig)();
	char *Shell;

	newi = ibuf;
	if ((no = fopen(tempEdit, "w")) == NULL) {
		perror(tempEdit);
		return(obuf);
	}
	if ((ni = fopen(tempEdit, "r")) == NULL) {
		perror(tempEdit);
		fclose(no);
		remove(tempEdit);
		return(obuf);
	}
	remove(tempEdit);
	savsig = sigset(SIGINT, SIG_IGN);
	fflush(obuf);
	rewind(ibuf);
	if ((Shell = value("SHELL")) == NULL)
		Shell = "/bin/sh";
	if ((pid = vfork()) == -1) {
		perror("fork");
		goto err;
	}
	if (pid == 0) {
		/*
		 * stdin = current message.
		 * stdout = new message.
		 */

		sigchild();
		close(0);
		dup(fileno(ibuf));
		close(1);
		dup(fileno(no));
		for (s = 4; s < 15; s++)
			close(s);
		execl(Shell, Shell, "-c", cmd, 0);
		perror(Shell);
		_exit(1);
	}
	while (wait(&s) != pid)
		;
	if (s != 0 || pid == -1) {
		fprintf(stderr, "\"%s\" failed!?\n", cmd);
		goto err;
	}
	if (fsize(ni) == 0) {
		fprintf(stderr, "No bytes from \"%s\" !?\n", cmd);
		goto err;
	}

	/*
	 * Take new files.
	 */

	newi = ni;
	fclose(ibuf);
	fclose(obuf);
	sigset(SIGINT, savsig);
	return(no);

err:
	fclose(no);
	fclose(ni);
	sigset(SIGINT, savsig);
	return(obuf);
}

/*
 * Interpolate the named messages into the current
 * message, preceding each line with a tab.
 * Return a count of the number of characters now in
 * the message, or -1 if an error is encountered writing
 * the message temporary.  The flag argument is 'm' if we
 * should shift over and 'f' if not.
 */
forward(ms, obuf, f)
	char ms[];
	FILE *obuf;
{
	register int *msgvec, *ip;
	extern char tempMail[];

	msgvec = (int *) salloc((msgCount+1) * sizeof *msgvec);
	if (msgvec == (int *) NOSTR)
		return(0);
	if (getmsglist(ms, msgvec, 0) < 0)
		return(0);
	if (*msgvec == NULL) {
		*msgvec = first(0, MMNORM);
		if (*msgvec == NULL) {
			printf("No appropriate messages\n");
			return(0);
		}
		msgvec[1] = NULL;
	}
	printf("Interpolating:");
	for (ip = msgvec; *ip != NULL; ip++) {
		touch(*ip);
		printf(" %d", *ip);
		if (f == 'm') {
			if (transmit(&message[*ip-1], obuf) < 0L) {
				perror(tempMail);
				return(-1);
			}
		} else
			if (send(&message[*ip-1], obuf, 0, 0) < 0) {
				perror(tempMail);
				return(-1);
			}
	}
	printf("\n");
	return(0);
}

/*
 * Send message described by the passed pointer to the
 * passed output buffer.  Insert a tab in front of each
 * line.  Return a count of the characters sent, or -1
 * on error.
 */

long
transmit(mailp, obuf)
	struct message *mailp;
	FILE *obuf;
{
	register struct message *mp;
	register int ch;
	long c, n;
	int bol;
	FILE *ibuf;

	mp = mailp;
	ibuf = setinput(mp);
	c = mp->m_size;
	n = c;
	bol = 1;
	while (c-- > 0L) {
		if (bol) {
			bol = 0;
			putc('\t', obuf);
			n++;
			if (ferror(obuf)) {
				perror("/tmp");
				return(-1L);
			}
		}
		ch = getc(ibuf);
		if (ch == '\n')
			bol++;
		putc(ch, obuf);
		if (ferror(obuf)) {
			perror("/tmp");
			return(-1L);
		}
	}
	return(n);
}

/*
 * Print (continue) when continued after ^Z.
 */
collcont()
{

	printf("(continue)\n");
	fflush(stdout);
}

/*
 * On interrupt, go here to save the partial
 * message on ~/dead.letter.
 * Then restore signals and execute the normal
 * signal routine.  We only come here if signals
 * were previously set anyway.
 */

# ifndef VMUNIX
collintsig()
{
	signal(SIGINT, SIG_IGN);
	collrub(SIGINT);
}

collhupsig()
{
	signal(SIGHUP, SIG_IGN);
	collrub(SIGHUP);
}
# endif VMUNIX

collrub(s)
{
	register FILE *dbuf;
	register int c;

	if (s == SIGINT && hadintr == 0) {
		hadintr++;
		fflush(stdout);
		fprintf(stderr, "\n(Interrupt -- one more to kill letter)\n");
		longjmp(coljmp, 1);
	}
	fclose(newo);
	rewind(newi);
	if (s == SIGINT && value("nosave") != NOSTR || fsize(newi) == 0)
		goto done;
	if ((dbuf = fopen(deadletter, "w")) == NULL)
		goto done;
	chmod(deadletter, 0600);
	while ((c = getc(newi)) != EOF)
		putc(c, dbuf);
	fclose(dbuf);

done:
	fclose(newi);
	sigset(SIGINT, savesig);
	sigset(SIGHUP, savehup);
# ifdef VMUNIX
	sigset(SIGCONT, savecont);
# endif VMUNIX
	if (rcvmode) {
		if (s == SIGHUP)
			hangup();
		else
#ifndef VMUNIX
		        stop(s);
#else   VMUNIX
			stop();
#endif  VMUNIX
	}
	else
		exit(1);
}

/*
 * Acknowledge an interrupt signal from the tty by typing an @
 */

intack()
{
	
	puts("@");
	fflush(stdout);
	clearerr(stdin);
}

/*
 * Add a string to the end of a header entry field.
 */

char *
addto(hfld, news)
	char hfld[], news[];
{
	register char *cp, *cp2, *linebuf;
	register int comma;

	if (hfld == NOSTR)
		hfld = "";
	if (*news == '\0')
		return(hfld);
	linebuf = salloc(strlen(hfld) + strlen(news) + 3); /* maybe comma */
	for (cp = hfld; any(*cp, " \t"); cp++)
		;
	comma = *cp ? 1 : 0;
	for (cp2 = linebuf; *cp;)
		*cp2++ = *cp++;
	if (comma)
		*cp2++ = ',';	/* here's the comma */
	*cp2++ = ' ';
	for (cp = news; any(*cp, " \t"); cp++)
		;
	while (*cp != '\0')
		*cp2++ = *cp++;
	*cp2 = '\0';
	return(linebuf);
}
!= pid)
		;
	if (s != 0 || pid == -1) {
		fprintf(stderr, "\"%s\" failed!?\n", cmd);
		goto err;
	}
	if (fsimmdf/uip/ucbmail/config.c   444      0     12        2674  3622773446  10621 /*
 * Copyright (c) 1980 Regents of the University of California.
 * All rights reserved.  The Berkeley software License Agreement
 * specifies the terms and conditions for redistribution.
 */

#ifndef lint
static char *sccsid = "@(#)config.c	5.2 (Berkeley) 6/21/85";
#endif not lint

/*
 * This file contains definitions of network data used by Mail
 * when replying.  See also:  configdefs.h and optim.c
 */

/*
 * The subterfuge with CONFIGFILE is to keep cc from seeing the
 * external defintions in configdefs.h.
 */
#define	CONFIGFILE
#include "./configdefs.h"

/*
 * Set of network separator characters.
 */
char	*metanet = "!^:%@.";

/*
 * Host table of "known" hosts.  See the comment in configdefs.h;
 * not all accessible hosts need be here (fortunately).
 */
struct netmach netmach[] = {
	EMPTY,		EMPTYID,	AN,	/* Filled in dynamically */
	0,		0,		0
};

/*
 * Table of ordered of preferred networks.  You probably won't need
 * to fuss with this unless you add a new network character (foolishly).
 */
struct netorder netorder[] = {
	AN,	'@',
	AN,	'%',
	SN,	':',
	BN,	'!',
	-1,	0
};

/*
 * Table to convert from network separator code in address to network
 * bit map kind.  With this transformation, we can deal with more than
 * one character having the same meaning easily.
 */
struct ntypetab ntypetab[] = {
	'%',	AN,
	'@',	AN,
	':',	SN,
	'!',	BN,
	'^',	BN,
	0,	0
};

struct nkindtab nkindtab[] = {
	AN,	IMPLICIT,
	BN,	EXPLICIT,
	SN,	IMPLICIT,
	0,	0
};
ILE is to keep cc from seeing the
 * external defintions in configdemmdf/uip/ucbmail/configdefs.h   444      0     12        6621  3620510631  11443 /*
 * Copyright (c) 1980 Regents of the University of California.
 * All rights reserved.  The Berkeley software License Agreement
 * specifies the terms and conditions for redistribution.
 *
 *	@(#)configdefs.h	5.1 (Berkeley) 6/6/85
 */

/*
 * This file contains the definitions of data structures used in
 * configuring the network behavior of Mail when replying.
 */

/*
 * The following constants are used when you are running 4.1a bsd or
 * later on a local network.  Under control of the #define flag
 * GETHOST, the host name is determined dynamically using the
 * gethostname() system call.  The name thus found is inserted
 * into the host table slot whose name was originally EMPTY.
 */
#define	EMPTY		"** empty **"
#define	EMPTYID		'E'

/*
 * The following data structure is the host table.  You must have
 * an entry here for your own machine, plus any special stuff you
 * expect the mailer to know about.  If you have #define'd GETHOST
 * in v7.local.h, you needn't add your machine to the host table.
 * Not all hosts need be here, however:
 * Mail can dope out stuff about hosts on the fly by looking
 * at addresses.  The machines needed here are:
 *	1) The local machine
 *	2) Any machines on the path to a network gateway
 *	3) Any machines with nicknames that you want to have considered
 *	   the same.
 * The machine id letters can be anything you like and are not seen
 * externally.  Be sure not to use characters with the 0200 bit set --
 * these have special meanings.
 */
struct netmach {
	char	*nt_machine;
	char	nt_mid;
	short	nt_type;
};

/*
 * Network type codes.  Basically, there is one for each different
 * network, if the network can be discerned by the separator character,
 * such as @ for the arpa net.  The purpose of these codes is to
 * coalesce cases where more than one character means the same thing,
 * such as % and @ for the arpanet.  Also, the host table uses a
 * bit map of these codes to show what it is connected to.
 * BN -- connected to Bell Net.
 * AN -- connected to ARPA net, SN -- connected to Schmidt net.
 */
#define	AN	1			/* Connected to ARPA net */
#define	BN	2			/* Connected to BTL net */
#define	SN	4			/* Connected to Schmidt net */

/*
 * Data structure for table mapping network characters to network types.
 */
struct ntypetab {
	char	nt_char;		/* Actual character separator */
	int	nt_bcode;		/* Type bit code */
};

/*
 * Codes for the "kind" of a network.  IMPLICIT means that if there are
 * physically several machines on the path, one does not list them in the
 * address.  The arpa net is like this.  EXPLICIT means you list them,
 * as in UUCP.
 * By the way, this distinction means we lose if anyone actually uses the
 * arpa net subhost convention: name@subhost@arpahost
 */
#define	IMPLICIT	1
#define	EXPLICIT	2

/*
 * Table for mapping a network code to its type -- IMPLICIT routing or
 * IMPLICIT routing.
 */
struct nkindtab {
	int	nk_type;		/* Its bit code */
	int	nk_kind;		/* Whether explicit or implicit */
};

/*
 * The following table gives the order of preference of the various
 * networks.  Thus, if we have a choice of how to get somewhere, we
 * take the preferred route.
 */
struct netorder {
	short	no_stat;
	char	no_char;
};

/*
 * External declarations for above defined tables.
 */
#ifndef CONFIGFILE
extern struct netmach netmach[1];
extern struct ntypetab ntypetab[1];
extern struct nkindtab nkindtab[1];
extern struct netorder netorder[1];
extern char *metanet;
#endif
are used when you are running 4.1a bsd or
 * later on a local network.  Under control of the #define flag
 * GEmmdf/uip/ucbmail/def.h   444      0     12       20737  3670664436  10140 /*
 * Copyright (c) 1980 Regents of the University of California.
 * All rights reserved.  The Berkeley software License Agreement
 * specifies the terms and conditions for redistribution.
 *
 *	@(#)def.h	5.2 (Berkeley) 11/2/85
 */

#include <sys/param.h>		/* includes <sys/types.h> */
#include <signal.h>
#include <stdio.h>
#include <sgtty.h>
#include "./local.h"

/*
 * Mail -- a mail program
 *
 * Commands are:
 *	t <message list>		print out these messages
 *	r <message list>		reply to messages
 *	m <user list>			mail to users (analogous to send)
 *	e <message list>		edit messages
 *	c [directory]			chdir to dir or home if none
 *	x				exit quickly
 *	w <message list> file		save messages in file
 *	q				quit, save remaining stuff in mbox
 *	d <message list>		delete messages
 *	u <message list>		undelete messages
 *	h				print message headers
 *
 * Author: Kurt Shoens (UCB) March 25, 1978
 */


#define	ESCAPE		'~'		/* Default escape for sending */
#define	NMLSIZE		1024		/* max names in a message list */
#define	PATHSIZE	1024		/* Size of pathnames throughout */
#define	NAMESIZE	32		/* Max size of user name */
#define	HSHSIZE		59		/* Hash size for aliases and vars */
#define	HDRFIELDS	3		/* Number of header fields */
#define	LINESIZE	BUFSIZ		/* max readable line width */
#define	STRINGSIZE	((unsigned) 128)/* Dynamic allocation units */
#define ALIGNMENT	4		/* Alignment for string allocations */
#define	MAXARGC		1024		/* Maximum list of raw strings */
#define	NOSTR		((char *) 0)	/* Null string pointer */
#define	MAXEXP		25		/* Maximum expansion of aliases */
#define	equal(a, b)	(strcmp(a,b)==0)/* A nice function to string compare */

struct message {
	short	m_flag;			/* flags, see below */
	short	m_block;		/* block number of this message */
	short	m_offset;		/* offset in block of message */
	long	m_size;			/* Bytes in the message */
	short	m_lines;		/* Lines in the message */
};

/*
 * flag bits.
 */

#define	MUSED		(1<<0)		/* entry is used, but this bit isn't */
#define	MDELETED	(1<<1)		/* entry has been deleted */
#define	MSAVED		(1<<2)		/* entry has been saved */
#define	MTOUCH		(1<<3)		/* entry has been noticed */
#define	MPRESERVE	(1<<4)		/* keep entry in sys mailbox */
#define	MMARK		(1<<5)		/* message is marked! */
#define	MODIFY		(1<<6)		/* message has been modified */
#define	MNEW		(1<<7)		/* message has never been seen */
#define	MREAD		(1<<8)		/* message has been read sometime. */
#define	MSTATUS		(1<<9)		/* message status has changed */
#define	MBOX		(1<<10)		/* Send this to mbox, regardless */

/*
 * Format of the command description table.
 * The actual table is declared and initialized
 * in lex.c
 */

struct cmd {
	char	*c_name;		/* Name of command */
	int	(*c_func)();		/* Implementor of the command */
	short	c_argtype;		/* Type of arglist (see below) */
	short	c_msgflag;		/* Required flags of messages */
	short	c_msgmask;		/* Relevant flags of messages */
};

/* Yechh, can't initialize unions */

#define	c_minargs c_msgflag		/* Minimum argcount for RAWLIST */
#define	c_maxargs c_msgmask		/* Max argcount for RAWLIST */

/*
 * Argument types.
 */

#define	MSGLIST	 0		/* Message list type */
#define	STRLIST	 1		/* A pure string */
#define	RAWLIST	 2		/* Shell string list */
#define	NOLIST	 3		/* Just plain 0 */
#define	NDMLIST	 4		/* Message list, no defaults */

#define	P	040		/* Autoprint dot after command */
#define	I	0100		/* Interactive command bit */
#define	M	0200		/* Legal from send mode bit */
#define	W	0400		/* Illegal when read only bit */
#define	F	01000		/* Is a conditional command */
#define	T	02000		/* Is a transparent command */
#define	R	04000		/* Cannot be called from collect */

/*
 * Oft-used mask values
 */

#define	MMNORM		(MDELETED|MSAVED)/* Look at both save and delete bits */
#define	MMNDEL		MDELETED	/* Look only at deleted bit */

/*
 * Structure used to return a break down of a head
 * line (hats off to Bill Joy!)
 */

struct headline {
	char	*l_from;	/* The name of the sender */
	char	*l_tty;		/* His tty string (if any) */
	char	*l_date;	/* The entire date string */
};

#define	GTO	1		/* Grab To: line */
#define	GSUBJECT 2		/* Likewise, Subject: line */
#define	GCC	4		/* And the Cc: line */
#define	GBCC	8		/* And also the Bcc: line */
#define	GMASK	(GTO|GSUBJECT|GCC|GBCC)
				/* Mask of places from whence */

#define	GNL	16		/* Print blank line after */
#define	GDEL	32		/* Entity removed from list */
#define	GCOMMA	64		/* detract puts in commas */

/*
 * Structure used to pass about the current
 * state of the user-typed message header.
 */

struct header {
	char	*h_to;			/* Dynamic "To:" string */
	char	*h_subject;		/* Subject string */
	char	*h_cc;			/* Carbon copies string */
	char	*h_bcc;			/* Blind carbon copies */
	int	h_seq;			/* Sequence for optimization */
};

/*
 * Structure of namelist nodes used in processing
 * the recipients of mail and aliases and all that
 * kind of stuff.
 */

struct name {
	struct	name *n_flink;		/* Forward link in list. */
	struct	name *n_blink;		/* Backward list link */
	short	n_type;			/* From which list it came */
	char	*n_name;		/* This fella's name */
};

/*
 * Structure of a variable node.  All variables are
 * kept on a singly-linked list of these, rooted by
 * "variables"
 */

struct var {
	struct	var *v_link;		/* Forward link to next variable */
	char	*v_name;		/* The variable's name */
	char	*v_value;		/* And it's current value */
};

struct group {
	struct	group *ge_link;		/* Next person in this group */
	char	*ge_name;		/* This person's user name */
};

struct grouphead {
	struct	grouphead *g_link;	/* Next grouphead in list */
	char	*g_name;		/* Name of this group */
	struct	group *g_list;		/* Users in group. */
};

#define	NIL	((struct name *) 0)	/* The nil pointer for namelists */
#define	NONE	((struct cmd *) 0)	/* The nil pointer to command tab */
#define	NOVAR	((struct var *) 0)	/* The nil pointer to variables */
#define	NOGRP	((struct grouphead *) 0)/* The nil grouphead pointer */
#define	NOGE	((struct group *) 0)	/* The nil group pointer */

/*
 * Structure of the hash table of ignored header fields
 */
struct ignore {
	struct ignore	*i_link;	/* Next ignored field in bucket */
	char		*i_field;	/* This ignored field */
};

/*
 * Token values returned by the scanner used for argument lists.
 * Also, sizes of scanner-related things.
 */

#define	TEOL	0			/* End of the command line */
#define	TNUMBER	1			/* A message number */
#define	TDASH	2			/* A simple dash */
#define	TSTRING	3			/* A string (possibly containing -) */
#define	TDOT	4			/* A "." */
#define	TUP	5			/* An "^" */
#define	TDOLLAR	6			/* A "$" */
#define	TSTAR	7			/* A "*" */
#define	TOPEN	8			/* An '(' */
#define	TCLOSE	9			/* A ')' */
#define TPLUS	10			/* A '+' */

#define	REGDEP	2			/* Maximum regret depth. */
#define	STRINGLEN	1024		/* Maximum length of string token */

/*
 * Constants for conditional commands.  These describe whether
 * we should be executing stuff or not.
 */

#define	CANY		0		/* Execute in send or receive mode */
#define	CRCV		1		/* Execute in receive mode only */
#define	CSEND		2		/* Execute in send mode only */

/*
 * Kludges to handle the change from setexit / reset to setjmp / longjmp
 */

#define	setexit()	setjmp(srbuf)
#define	reset(x)	longjmp(srbuf, x)

/*
 * VM/UNIX has a vfork system call which is faster than forking.  If we
 * don't have it, fork(2) will do . . .
 */

#ifndef VMUNIX
#define	vfork()	fork()
#endif
#ifndef	SIGRETRO
#define	sigchild()
#endif

/*
 * 4.2bsd signal interface help...
 */
#ifdef V4_2BSD
#define	sigset(s, a)	signal(s, a)
#define	sigsys(s, a)	signal(s, a)
#endif

/*
 * Forward declarations of routine types to keep lint and cc happy.
 */

FILE	*Fdopen();
FILE	*collect();
FILE	*infix();
FILE	*mesedit();
FILE	*mespipe();
FILE	*popen();
FILE	*setinput();
char	*addto();
char	*arpafix();
char	*calloc();
char	*copy();
char	*copyin();
char	*detract();
char	*expand();
char	*gets();
char	*hfield();
char	*index();
char	*name1();
char	*nameof();
char	*nextword();
char	*getenv();
char	*getfilename();
char	*hcontents();
char    *mktemp();
char	*netmap();
char	*netname();
char	*readtty();
char	*reedit();
char	*revarpa();
char	*rindex();
char	*rpair();
char	*salloc();
char	*savestr();
char	*snarf();
char	*strcat();
char	*strcpy();
char    *strncpy();
char	*value();
char	*vcopy();
off_t	fsize();
#ifndef V4_2BSD
int	(*sigset())();
#endif
struct	cmd	*lex();
struct	grouphead	*findgroup();
struct	name	*cat();
struct	name	*delname();
struct	name	*elide();
struct	name	*extract();
struct	name	*gexpand();
struct	name	*outof();
struct	name	*put();
struct	name	*usermap();
struct	var	*lookup();
long	transmit();
int	icequal();
er {
	char	*h_to;			/* Dynamic "Tmmdf/uip/ucbmail/fio.c   444      0     12       31060  3671105567  10135 /*
 *  F I O . C 
 *
 *  EE/CIS Computer Lab
 *  Department of Computer and Information Sciences
 *  Department of Electrical Engineering
 *  University of Delaware
 *
 *  REVISION HISTORY:
 *
 *  $Revision: 1.6 $
 *
 *  $Log:	fio.c,v $
 * Revision 1.6  86/01/13  13:03:48  galvin
 * Change format of debug output and delete one.
 * 
 * Revision 1.5  85/12/18  13:22:33  galvin
 * Add another argument to send to indicate whether or not this
 * message should be delimited by MMDF message delimiters.
 * 
 * Change all but "temp" file opens/closes to use MMDF locking routines.
 * 
 * Revision 1.4  85/11/20  14:34:38  galvin
 * Added some debugging code -- ifdef'ed with DEBUG and dependent on
 * debug -- and code to parse the MMDF message delimiters.  This code
 * should still support the old style Mail format but since I cannot
 * test this I don't know.
 * 
 * Revision 1.3  85/11/16  16:10:36  galvin
 * Added define for sigmask for backward compatibility from 4.3bsd to 4.2bsd.
 * 
 * Revision 1.2  85/11/16  14:33:45  galvin
 * Added comment header for revision history.
 * 
 *
 */

/*
 * Copyright (c) 1980 Regents of the University of California.
 * All rights reserved.  The Berkeley software License Agreement
 * specifies the terms and conditions for redistribution.
 */

#ifndef lint
static char *sccsid = "@(#)fio.c	5.2 (Berkeley) 6/21/85";
#endif not lint

#include "./rcv.h"
#include "./mmdf.h"
#include <sys/stat.h>
#include <ctype.h>
#include <errno.h>
#include "./sigretro.h"

/*
 * Mail -- a mail program
 *
 * File I/O.
 */

/*
 * Set up the input pointers while copying the mail file into
 * /tmp.
 */

setptr(ibuf)
	FILE *ibuf;
{
	register int c;
	register char *cp, *cp2;
	register int count, l;
	long s;
	off_t offset;
	char linebuf[LINESIZE];
	char wbuf[LINESIZE];
	int maybe, mestmp, flag, inhead, newmsg,
		len1 = strlen(delim1) - 1, len2 = strlen(delim2) - 1;
	struct message this;
	extern char tempSet[];

	if ((mestmp = opentemp(tempSet)) < 0)
		exit(1);
	msgCount = 0;
	offset = 0;
	s = 0L;
	l = 0;
	maybe = 1;
	flag = MUSED|MNEW;
	for (;;) {
		cp = linebuf;
		c = getc(ibuf);
		while (c != EOF && c != '\n') {
			if (cp - linebuf >= LINESIZE - 1) {
				ungetc(c, ibuf);
				*cp = 0;
				break;
			}
			*cp++ = c;
			c = getc(ibuf);
		}
		*cp = 0;
		if (cp == linebuf && c == EOF) {
#ifdef DEBUG
			if (debug)
				printf("setptr: no more messages.\n");
#endif DEBUG
			this.m_flag = flag;
			flag = MUSED|MNEW;
			this.m_offset = offsetof(offset);
			this.m_block = blockof(offset);
			this.m_size = s;
			this.m_lines = l;
			if (append(&this, mestmp)) {
				perror(tempSet);
				exit(1);
			}
			fclose(ibuf);
			makemessage(mestmp);
			close(mestmp);
			return;
		}
		count = cp - linebuf + 1;
		if (strncmp(linebuf, delim1, len1) == 0) {
			maybe = newmsg = 1;
			inhead = 0;
			continue;
		}
		if (strncmp(linebuf, delim2, len2) == 0) {
			inhead = maybe = newmsg = 0;
			continue;
		}
		for (cp = linebuf; *cp;)
			putc(*cp++, otf);
		putc('\n', otf);
		if (ferror(otf)) {
			perror("/tmp");
			exit(1);
		}
		if ((newmsg && !inhead) ||
			(maybe && linebuf[0] == 'F' && ishead(linebuf))) {
#ifdef DEBUG
			if (debug)
				printf("setptr: new message.\n");
#endif DEBUG
			msgCount++;
			this.m_flag = flag;
			flag = MUSED|MNEW;
			inhead = 1;
			this.m_block = blockof(offset);
			this.m_offset = offsetof(offset);
			this.m_size = s;
			this.m_lines = l;
			s = 0L;
			l = 0;
			if (append(&this, mestmp)) {
				perror(tempSet);
				exit(1);
			}
			newmsg = 0;
		}
		if (linebuf[0] == 0)
			inhead = 0;
		if (inhead && index(linebuf, ':')) {
			cp = linebuf;
			cp2 = wbuf;
			while (isalpha(*cp))
				*cp2++ = *cp++;
			*cp2 = 0;
			if (icequal(wbuf, "status")) {
				cp = index(linebuf, ':');
				if (index(cp, 'R'))
					flag |= MREAD;
				if (index(cp, 'O'))
					flag &= ~MNEW;
				inhead = 0;
			}
		}
		offset += count;
		s += (long) count;
		l++;
		maybe = 0;
		if (linebuf[0] == 0)
			maybe = 1;
	}
}

/*
 * Drop the passed line onto the passed output buffer.
 * If a write error occurs, return -1, else the count of
 * characters written, including the newline.
 */

putline(obuf, linebuf)
	FILE *obuf;
	char *linebuf;
{
	register int c;

	c = strlen(linebuf);
	fputs(linebuf, obuf);
	putc('\n', obuf);
	if (ferror(obuf))
		return(-1);
	return(c+1);
}

#ifdef NOT_USED
/*
 * Quickly read a line from the specified input into the line
 * buffer; return characters read.
 */

freadline(ibuf, linebuf)
	register FILE *ibuf;
	register char *linebuf;
{
	register int c;
	register char *cp;

	c = getc(ibuf);
	cp = linebuf;
	while (c != '\n' && c != EOF) {
		if (c == 0) {
			c = getc(ibuf);
			continue;
		}
		if (cp - linebuf >= BUFSIZ-1) {
			*cp = 0;
			return(cp - linebuf + 1);
		}
		*cp++ = c;
		c = getc(ibuf);
	}
	if (c == EOF && cp == linebuf)
		return(0);
	*cp = 0;
	return(cp - linebuf + 1);
}
#endif NOT_USED

/*
 * Read up a line from the specified input into the line
 * buffer.  Return the number of characters read.  Do not
 * include the newline at the end.
 */

readline(ibuf, linebuf)
	FILE *ibuf;
	char *linebuf;
{
	register char *cp;
	register int c;

	do {
	clearerr(ibuf);
		c = getc(ibuf);
		for (cp = linebuf; c != '\n' && c != EOF; c = getc(ibuf)) {
			if (c == 0)
				continue;
			if (cp - linebuf < LINESIZE-2)
				*cp++ = c;
		}
	} while (ferror(ibuf) && ibuf == stdin);
	*cp = 0;
	if (c == EOF && cp == linebuf)
		return(0);
	return(cp - linebuf + 1);
}

/*
 * Return a file buffer all ready to read up the
 * passed message pointer.
 */

FILE *
setinput(mp)
	register struct message *mp;
{
	off_t off;

	fflush(otf);
	off = mp->m_block;
	off <<= 9;
	off += mp->m_offset;
	if (fseek(itf, off, 0) < 0) {
		perror("fseek");
		panic("temporary file seek");
	}
	return(itf);
}

/*
 * Take the data out of the passed ghost file and toss it into
 * a dynamically allocated message structure.
 */

makemessage(f)
{
	register struct message *m;
	register char *mp;
	register count;
	long lseek();

	mp = calloc((unsigned) (msgCount + 1), sizeof *m);
	if (mp == NOSTR) {
		printf("Insufficient memory for %d messages\n", msgCount);
		exit(1);
	}
	if (message != (struct message *) 0)
		cfree((char *) message);
	message = (struct message *) mp;
	dot = message;
	lseek(f, 0L, 0);
	while (count = read(f, mp, BUFSIZ))
		mp += count;
	for (m = &message[0]; m < &message[msgCount]; m++) {
		m->m_size = (m+1)->m_size;
		m->m_lines = (m+1)->m_lines;
		m->m_flag = (m+1)->m_flag;
	}
	message[msgCount].m_size = 0L;
	message[msgCount].m_lines = 0;
}

/*
 * Append the passed message descriptor onto the temp file.
 * If the write fails, return 1, else 0
 */

append(mp, f)
	struct message *mp;
{
	if (write(f, (char *) mp, sizeof *mp) != sizeof *mp)
		return(1);
	return(0);
}

/*
 * Delete a file, but only if the file is a plain file.
 */

remove(name)
	char name[];
{
	struct stat statb;
	extern int errno;

	if (stat(name, &statb) < 0)
		return(-1);
	if ((statb.st_mode & S_IFMT) != S_IFREG) {
		errno = EISDIR;
		return(-1);
	}
	return(unlink(name));
}

/*
 * Terminate an editing session by attempting to write out the user's
 * file from the temporary.  Save any new stuff appended to the file.
 */
edstop()
{
	register int gotcha, c;
	register struct message *mp;
	FILE *obuf, *ibuf, *readstat;
	struct stat statb;
	char tempname[30], *id;

	if (readonly)
		return;
	holdsigs();
	if (Tflag != NOSTR) {
		if ((readstat = fopen(Tflag, "w")) == NULL)
			Tflag = NOSTR;
	}
	for (mp = &message[0], gotcha = 0; mp < &message[msgCount]; mp++) {
		if (mp->m_flag & MNEW) {
			mp->m_flag &= ~MNEW;
			mp->m_flag |= MSTATUS;
		}
		if (mp->m_flag & (MODIFY|MDELETED|MSTATUS))
			gotcha++;
		if (Tflag != NOSTR && (mp->m_flag & (MREAD|MDELETED)) != 0) {
			if ((id = hfield("article-id", mp)) != NOSTR)
				fprintf(readstat, "%s\n", id);
		}
	}
	if (Tflag != NOSTR)
		fclose(readstat);
	if (!gotcha || Tflag != NOSTR)
		goto done;
	ibuf = NULL;
	if (stat(editfile, &statb) >= 0 && statb.st_size > mailsize) {
		strcpy(tempname, "/tmp/mboxXXXXXX");
		mktemp(tempname);
		if ((obuf = fopen(tempname, "w")) == NULL) {
			perror(tempname);
			relsesigs();
			reset(0);
		}
		if ((ibuf = lk_fopen(editfile, "r", (char *) 0, (char *) 0, 5)) == NULL) {
			perror(editfile);
			fclose(obuf);
			remove(tempname);
			relsesigs();
			reset(0);
		}
		fseek(ibuf, mailsize, 0);
		while ((c = getc(ibuf)) != EOF)
			putc(c, obuf);
		lk_fclose(ibuf, editfile, (char *) 0, (char *) 0);
		fclose(obuf);
		if ((ibuf = fopen(tempname, "r")) == NULL) {
			perror(tempname);
			remove(tempname);
			relsesigs();
			reset(0);
		}
		remove(tempname);
	}
	printf("\"%s\" ", editfile);
	fflush(stdout);
	if ((obuf = lk_fopen(editfile, "w+", (char *) 0, (char *) 0, 5)) == NULL) {
		perror(editfile);
		relsesigs();
		reset(0);
	}
	c = 0;
	for (mp = &message[0]; mp < &message[msgCount]; mp++) {
		if ((mp->m_flag & MDELETED) != 0)
			continue;
		c++;
		if (send(mp, obuf, 0, 1) < 0) {
			perror(editfile);
			lk_fclose(obuf, editfile, (char *) 0, (char *) 0);
			relsesigs();
			reset(0);
		}
	}
	gotcha = (c == 0 && ibuf == NULL);
	if (ibuf != NULL) {
		while ((c = getc(ibuf)) != EOF)
			putc(c, obuf);
		fclose(ibuf);
	}
	fflush(obuf);
	if (ferror(obuf)) {
		perror(editfile);
		lk_fclose(obuf, editfile, (char *) 0, (char *) 0);
		relsesigs();
		reset(0);
	}
	lk_fclose(obuf, editfile, (char *) 0, (char *) 0);
	if (gotcha) {
		remove(editfile);
		printf("removed\n");
	}
	else
		printf("complete\n");
	fflush(stdout);

done:
	relsesigs();
}

static int sigdepth = 0;		/* depth of holdsigs() */
static int omask = 0;
/*
 * Hold signals SIGHUP - SIGQUIT.
 */
holdsigs()
{

# ifdef V4_2BSD
	if (sigdepth++ == 0)
		omask = sigblock(sigmask(SIGHUP)|sigmask(SIGINT)|sigmask(SIGQUIT));
# endif V4_2BSD
# ifdef V4_1BSD
	if (sigdepth++ == 0) {
		sighold(SIGHUP);
		sighold(SIGINT);
		sighold(SIGQUIT);
	}
# endif V4_1BSD
}

/*
 * Release signals SIGHUP - SIGQUIT
 */
relsesigs()
{

# ifdef V4_2BSD
	if (--sigdepth == 0)
		sigsetmask(omask);
# endif V4_2BSD
# ifdef V4_1BSD
	if (--sigdepth == 0) {
		sigrelse(SIGINT);
		sigrelse(SIGHUP);
		sigrelse(SIGQUIT);
	}
# endif V4_1BSD
}

/*
 * Open a temp file by creating, closing, unlinking, and
 * reopening.  Return the open file descriptor.
 */

opentemp(file)
	char file[];
{
	register int f;

	if ((f = creat(file, 0600)) < 0) {
		perror(file);
		return(-1);
	}
	close(f);
	if ((f = open(file, 2)) < 0) {
		perror(file);
		remove(file);
		return(-1);
	}
	remove(file);
	return(f);
}

/*
 * Determine the size of the file possessed by
 * the passed buffer.
 */

off_t
fsize(iob)
	FILE *iob;
{
	register int f;
	struct stat sbuf;

	f = fileno(iob);
	if (fstat(f, &sbuf) < 0)
		return(0);
	return(sbuf.st_size);
}

/*
 * Take a file name, possibly with shell meta characters
 * in it and expand it by using "sh -c echo filename"
 * Return the file name as a dynamic string.
 */

char *
expand(name)
	char name[];
{
	char xname[BUFSIZ];
	char cmdbuf[BUFSIZ];
	register int pid, l;
	register char *cp, *Shell;
	int s, pivec[2];
	struct stat sbuf;

	if (name[0] == '+' && getfold(cmdbuf) >= 0) {
		sprintf(xname, "%s/%s", cmdbuf, name + 1);
		return(expand(savestr(xname)));
	}
	if (!anyof(name, "~{[*?$`'\"\\"))
		return(name);
	if (pipe(pivec) < 0) {
		perror("pipe");
		return(name);
	}
	sprintf(cmdbuf, "echo %s", name);
	if ((pid = vfork()) == 0) {
		sigchild();
		Shell = value("SHELL");
		if (Shell == NOSTR)
			Shell = SHELL;
		close(pivec[0]);
		close(1);
		dup(pivec[1]);
		close(pivec[1]);
		close(2);
		execl(Shell, Shell, "-c", cmdbuf, 0);
		_exit(1);
	}
	if (pid == -1) {
		perror("fork");
		close(pivec[0]);
		close(pivec[1]);
		return(NOSTR);
	}
	close(pivec[1]);
	l = read(pivec[0], xname, BUFSIZ);
	close(pivec[0]);
	while (wait(&s) != pid);
		;
	s &= 0377;
	if (s != 0 && s != SIGPIPE) {
		fprintf(stderr, "\"Echo\" failed\n");
		goto err;
	}
	if (l < 0) {
		perror("read");
		goto err;
	}
	if (l == 0) {
		fprintf(stderr, "\"%s\": No match\n", name);
		goto err;
	}
	if (l == BUFSIZ) {
		fprintf(stderr, "Buffer overflow expanding \"%s\"\n", name);
		goto err;
	}
	xname[l] = 0;
	for (cp = &xname[l-1]; *cp == '\n' && cp > xname; cp--)
		;
	*++cp = '\0';
	if (any(' ', xname) && stat(xname, &sbuf) < 0) {
		fprintf(stderr, "\"%s\": Ambiguous\n", name);
		goto err;
	}
	return(savestr(xname));

err:
	return(NOSTR);
}

/*
 * Determine the current folder directory name.
 */
getfold(name)
	char *name;
{
	char *folder;

	if ((folder = value("folder")) == NOSTR)
		return(-1);
	if (*folder == '/')
		strcpy(name, folder);
	else
		sprintf(name, "%s/%s", homedir, folder);
	return(0);
}

/*
 * A nicer version of Fdopen, which allows us to fclose
 * without losing the open file.
 */

FILE *
Fdopen(fildes, mode)
	char *mode;
{
	register int f;
	FILE *fdopen();

	f = dup(fildes);
	if (f < 0) {
		perror("dup");
		return(NULL);
	}
	return(fdopen(f, mode));
}
reset(0);
	}
	c = 0;
	for (mp = &message[0]; mp < &message[msgCount]; mp++) {
		if ((mp->m_flag & MDELETED) != 0)
			continue;
		c++;
		if (send(mp, obuf, 0, 1) < 0) {
			perror(editfile);
			lk_fclose(obuf, editfile, (char *) 0, (char *) 0);
			relsesigs();
			reset(0);
		}
	}
	gotcha = (c == 0 && ibuf == NULL);
	if (ibuf != NULL) {
		while ((c = getc(ibuf)) != EOF)
			putc(c, obuf);
		fclose(ibuf);
	}
	fflush(obuf);
	if (ferror(obuf)) {
		perror(editfile);
	mmdf/uip/ucbmail/fmt.c   444      0     12       16750  3620510632  10142 /*
 * Copyright (c) 1980 Regents of the University of California.
 * All rights reserved.  The Berkeley software License Agreement
 * specifies the terms and conditions for redistribution.
 */

#ifndef lint
char *copyright =
"@(#) Copyright (c) 1980 Regents of the University of California.\n\
 All rights reserved.\n";
#endif not lint

#ifndef lint
static char *sccsid = "@(#)fmt.c	5.2 (Berkeley) 6/21/85";
#endif not lint

#include <stdio.h>
#include <ctype.h>

/*
 * fmt -- format the concatenation of input files or standard input
 * onto standard output.  Designed for use with Mail ~|
 *
 * Syntax: fmt [ -width ] [ name ... ]
 * Author: Kurt Shoens (UCB) 12/7/78
 */

#define	NOSTR	((char *) 0)	/* Null string pointer for lint */

int	pfx;			/* Current leading blank count */
int	lineno;			/* Current input line */
int	mark;			/* Last place we saw a head line */
int	width = 72;		/* Width that we will not exceed */

char	*calloc();		/* for lint . . . */
char	*headnames[] = {"To", "Subject", "Cc", 0};

/*
 * Drive the whole formatter by managing input files.  Also,
 * cause initialization of the output stuff and flush it out
 * at the end.
 */

main(argc, argv)
	char **argv;
{
	register FILE *fi;
	register int errs = 0;
	register char *cp;
	int nofile;

	setout();
	lineno = 1;
	mark = -10;
	if (argc < 2) {
single:
		fmt(stdin);
		oflush();
		exit(0);
	}
	nofile = 1;
	while (--argc) {
		cp = *++argv;
		if (*cp == '-') {
			width = atoi(cp+1);
			if (width <= 0 || width >= BUFSIZ-2) {
				fprintf(stderr, "fmt:  bad width: %d\n", width)
;
				exit(1);
			}
			continue;
		}
		nofile = 0;
		if ((fi = fopen(cp, "r")) == NULL) {
			perror(cp);
			errs++;
			continue;
		}
		fmt(fi);
		fclose(fi);
	}
	if (nofile)
		goto single;
	oflush();
	exit(errs);
}

/*
 * Read up characters from the passed input file, forming lines,
 * doing ^H processing, expanding tabs, stripping trailing blanks,
 * and sending each line down for analysis.
 */

fmt(fi)
	FILE *fi;
{
	char linebuf[BUFSIZ], canonb[BUFSIZ];
	register char *cp, *cp2;
	register int c, col;

	c = getc(fi);
	while (c != EOF) {
		
		/*
		 * Collect a line, doing ^H processing.
		 * Leave tabs for now.
		 */

		cp = linebuf;
		while (c != '\n' && c != EOF && cp-linebuf < BUFSIZ-1) {
			if (c == '\b') {
				if (cp > linebuf)
					cp--;
				c = getc(fi);
				continue;
			}
			if ((c < ' ' || c >= 0177) && c != '\t') {
				c = getc(fi);
				continue;
			}
			*cp++ = c;
			c = getc(fi);
		}
		*cp = '\0';

		/*
		 * Toss anything remaining on the input line.
		 */

		while (c != '\n' && c != EOF)
			c = getc(fi);
		
		/*
		 * Expand tabs on the way to canonb.
		 */

		col = 0;
		cp = linebuf;
		cp2 = canonb;
		while (c = *cp++) {
			if (c != '\t') {
				col++;
				if (cp2-canonb < BUFSIZ-1)
					*cp2++ = c;
				continue;
			}
			do {
				if (cp2-canonb < BUFSIZ-1)
					*cp2++ = ' ';
				col++;
			} while ((col & 07) != 0);
		}

		/*
		 * Swipe trailing blanks from the line.
		 */

		for (cp2--; cp2 >= canonb && *cp2 == ' '; cp2--)
			;
		*++cp2 = '\0';
		prefix(canonb);
		if (c != EOF)
			c = getc(fi);
	}
}

/*
 * Take a line devoid of tabs and other garbage and determine its
 * blank prefix.  If the indent changes, call for a linebreak.
 * If the input line is blank, echo the blank line on the output.
 * Finally, if the line minus the prefix is a mail header, try to keep
 * it on a line by itself.
 */

prefix(line)
	char line[];
{
	register char *cp, **hp;
	register int np, h;

	if (strlen(line) == 0) {
		oflush();
		putchar('\n');
		return;
	}
	for (cp = line; *cp == ' '; cp++)
		;
	np = cp - line;

	/*
	 * The following horrible expression attempts to avoid linebreaks
	 * when the indent changes due to a paragraph.
	 */

	if (np != pfx && (np > pfx || abs(pfx-np) > 8))
		oflush();
	if (h = ishead(cp))
		oflush(), mark = lineno;
	if (lineno - mark < 3 && lineno - mark > 0)
		for (hp = &headnames[0]; *hp != (char *) 0; hp++)
			if (ispref(*hp, cp)) {
				h = 1;
				oflush();
				break;
			}
	if (!h && (h = (*cp == '.')))
		oflush();
	pfx = np;
	split(cp);
	if (h)
		oflush();
	lineno++;
}

/*
 * Split up the passed line into output "words" which are
 * maximal strings of non-blanks with the blank separation
 * attached at the end.  Pass these words along to the output
 * line packer.
 */

split(line)
	char line[];
{
	register char *cp, *cp2;
	char word[BUFSIZ];

	cp = line;
	while (*cp) {
		cp2 = word;

		/*
		 * Collect a 'word,' allowing it to contain escaped
		 * white space.
		 */

		while (*cp && *cp != ' ') {
			if (*cp == '\\' && isspace(cp[1]))
				*cp2++ = *cp++;
			*cp2++ = *cp++;
		}

		/*
		 * Guarantee a space at end of line.
		 * Two spaces after end of sentence punctuation.
		 */

		if (*cp == '\0') {
			*cp2++ = ' ';
			if (any(cp[-1], ".:!?"))
				*cp2++ = ' ';
		}
		while (*cp == ' ')
			*cp2++ = *cp++;
		*cp2 = '\0';
		pack(word);
	}
}

/*
 * Output section.
 * Build up line images from the words passed in.  Prefix
 * each line with correct number of blanks.  The buffer "outbuf"
 * contains the current partial line image, including prefixed blanks.
 * "outp" points to the next available space therein.  When outp is NOSTR,
 * there ain't nothing in there yet.  At the bottom of this whole mess,
 * leading tabs are reinserted.
 */

char	outbuf[BUFSIZ];			/* Sandbagged output line image */
char	*outp;				/* Pointer in above */

/*
 * Initialize the output section.
 */

setout()
{
	outp = NOSTR;
}

/*
 * Pack a word onto the output line.  If this is the beginning of
 * the line, push on the appropriately-sized string of blanks first.
 * If the word won't fit on the current line, flush and begin a new
 * line.  If the word is too long to fit all by itself on a line,
 * just give it its own and hope for the best.
 */

pack(word)
	char word[];
{
	register char *cp;
	register int s, t;

	if (outp == NOSTR)
		leadin();
	t = strlen(word);
	s = outp-outbuf;
	if (t+s <= width) {
		
		/*
		 * In like flint!
		 */

		for (cp = word; *cp; *outp++ = *cp++)
			;
		return;
	}
	if (s > pfx) {
		oflush();
		leadin();
	}
	for (cp = word; *cp; *outp++ = *cp++)
		;
}

/*
 * If there is anything on the current output line, send it on
 * its way.  Set outp to NOSTR to indicate the absence of the current
 * line prefix.
 */

oflush()
{
	if (outp == NOSTR)
		return;
	*outp = '\0';
	tabulate(outbuf);
	outp = NOSTR;
}

/*
 * Take the passed line buffer, insert leading tabs where possible, and
 * output on standard output (finally).
 */

tabulate(line)
	char line[];
{
	register char *cp, *cp2;
	register int b, t;

	/*
	 * Toss trailing blanks in the output line.
	 */

	cp = line + strlen(line) - 1;
	while (cp >= line && *cp == ' ')
		cp--;
	*++cp = '\0';
	
	/*
	 * Count the leading blank space and tabulate.
	 */

	for (cp = line; *cp == ' '; cp++)
		;
	b = cp-line;
	t = b >> 3;
	b &= 07;
	if (t > 0)
		do
			putc('\t', stdout);
		while (--t);
	if (b > 0)
		do
			putc(' ', stdout);
		while (--b);
	while (*cp)
		putc(*cp++, stdout);
	putc('\n', stdout);
}

/*
 * Initialize the output line with the appropriate number of
 * leading blanks.
 */

leadin()
{
	register int b;
	register char *cp;

	for (b = 0, cp = outbuf; b < pfx; b++)
		*cp++ = ' ';
	outp = cp;
}

/*
 * Save a string in dynamic space.
 * This little goodie is needed for
 * a headline detector in head.c
 */

char *
savestr(str)
	char str[];
{
	register char *top;

	top = calloc(strlen(str) + 1, 1);
	if (top == NOSTR) {
		fprintf(stderr, "fmt:  Ran out of memory\n");
		exit(1);
	}
	copy(str, top);
	return(top);
}

/*
 * Is s1 a prefix of s2??
 */

ispref(s1, s2)
	register char *s1, *s2;
{

	while (*s1++ == *s2)
		;
	return(*s1 == '\0');
}
 ' '; cp++)
		;
	np = cpmmdf/uip/ucbmail/getname.c   444      0     12        2126  3670632351  10754 /*
 * Copyright (c) 1980 Regents of the University of California.
 * All rights reserved.  The Berkeley software License Agreement
 * specifies the terms and conditions for redistribution.
 */

#ifndef lint
static char *sccsid = "@(#)getname.c	5.2 (Berkeley) 6/21/85";
#endif not lint

#include <pwd.h>

/*
 * Getname / getuserid for those with
 * hashed passwd data base).
 *
 */

#include "./rcv.h"

/*
 * Search the passwd file for a uid.  Return name through ref parameter
 * if found, indicating success with 0 return.  Return -1 on error.
 * If -1 is passed as the user id, close the passwd file.
 */

getname(l_uid, namebuf)
	char namebuf[];
{
	struct passwd *pw;

	if (l_uid == -1) {
		return(0);
	}
	if ((pw = getpwuid(l_uid)) == NULL)
		return(-1);
	strcpy(namebuf, pw->pw_name);
	return 0;
}

/*
 * Convert the passed name to a user id and return it.  Return -1
 * on error.  Iff the name passed is -1 (yech) close the pwfile.
 */

getuserid(name)
	char name[];
{
	struct passwd *pw;

	if (name == (char *) -1) {
		return(0);
	}
	if ((pw = getpwnam(name)) == NULL)
		return 0;
	return pw->pw_uid;
}
he bottom of this whole mess,
 * leading tabs are reinserted.
 */

char	outbuf[BUFSIZ];			/* Sandbagged output line image */
char	*outp;				/* Pointer in above */

/*
 * Initialize the output section.
 */

setout()
{
	outp = NOSTR;
}

/*
 * Pack a word onto the output line.  If this is the beginning of
 * the line, push on the appropriately-sized string of blanks first.
 * If the word won't fit on the current line, flush ammdf/uip/ucbmail/glob.h   444      0     12        7636  3670652011  10272 /*
 *  G L O B . H 
 *
 *  EE/CIS Computer Lab
 *  Department of Computer and Information Sciences
 *  Department of Electrical Engineering
 *  University of Delaware
 *
 *  REVISION HISTORY:
 *
 *  $Revision: 1.3 $
 *
 *  $Log:	glob.h,v $
 * Revision 1.3  86/01/14  15:10:25  galvin
 * Seemed like good place to put the definition of routeq().
 * 
 * Revision 1.2  86/01/14  14:45:28  galvin
 * Added comment header for revision history.
 * 
 *
 */

/*
 * Copyright (c) 1980 Regents of the University of California.
 * All rights reserved.  The Berkeley software License Agreement
 * specifies the terms and conditions for redistribution.
 *
 *	@(#)glob.h	5.1 (Berkeley) 6/6/85
 */

/*
 * A bunch of global variable declarations lie herein.
 * def.h must be included first.
 */

int	msgCount;			/* Count of messages read in */
int	mypid;				/* Current process id */
int	rcvmode;			/* True if receiving mail */
int	sawcom;				/* Set after first command */
int	hflag;				/* Sequence number for network -h */
char	*rflag;				/* -r address for network */
char	*Tflag;				/* -T temp file for netnews */
char	nosrc;				/* Don't source /usr/lib/Mail.rc */
char	noheader;			/* Suprress initial header listing */
#ifdef NOT_USED
int	selfsent;			/* User sent self something */
#endif NOT_USED
int	senderr;			/* An error while checking */
int	edit;				/* Indicates editing a file */
int	readonly;			/* Will be unable to rewrite file */
int	noreset;			/* String resets suspended */
int	sourcing;			/* Currently reading variant file */
int	loading;			/* Loading user definitions */
int	cond;				/* Current state of conditional exc. */
FILE	*itf;				/* Input temp file buffer */
FILE	*otf;				/* Output temp file buffer */
FILE	*pipef;				/* Pipe file we have opened */
int	image;				/* File descriptor for image of msg */
FILE	*input;				/* Current command input file */
char	*editfile;			/* Name of file being edited */
char	*sflag;				/* Subject given from non tty */
int	outtty;				/* True if standard output a tty */
int	intty;				/* True if standard input a tty */
int	baud;				/* Output baud rate */
char	mbox[PATHSIZE];			/* Name of mailbox file */
char	mailname[PATHSIZE];		/* Name of system mailbox */
int	uid;				/* The invoker's user id */
char	mailrc[PATHSIZE];		/* Name of startup file */
char	deadletter[PATHSIZE];		/* Name of #/dead.letter */
char	homedir[PATHSIZE];		/* Path name of home directory */
char	myname[PATHSIZE];		/* My login id */
off_t	mailsize;			/* Size of system mailbox */
int	lexnumber;			/* Number of TNUMBER from scan() */
char	lexstring[STRINGLEN];		/* String from TSTRING, scan() */
int	regretp;			/* Pointer to TOS of regret tokens */
int	regretstack[REGDEP];		/* Stack of regretted tokens */
char	*stringstack[REGDEP];		/* Stack of regretted strings */
int	numberstack[REGDEP];		/* Stack of regretted numbers */
struct	message	*dot;			/* Pointer to current message */
struct	message	*message;		/* The actual message structure */
struct	var	*variables[HSHSIZE];	/* Pointer to active var list */
struct	grouphead	*groups[HSHSIZE];/* Pointer to active groups */
struct	ignore		*ignore[HSHSIZE];/* Pointer to ignored fields */
struct	ignore		*retain[HSHSIZE];/* Pointer to retained fields */
int	nretained;			/* Number of retained fields */
char	**altnames;			/* List of alternate names for user */
char	**localnames;			/* List of aliases for our local host */
int	debug;				/* Debug flag set */
int	rmail;				/* Being called as rmail */
int	routeq();			/* uses icequal to compare routes of
						of addresses */

#include <setjmp.h>

jmp_buf	srbuf;


/*
 * The pointers for the string allocation routines,
 * there are NSPACE independent areas.
 * The first holds STRINGSIZE bytes, the next
 * twice as much, and so on.
 */

#define	NSPACE	25			/* Total number of string spaces */
struct strings {
	char	*s_topFree;		/* Beginning of this area */
	char	*s_nextFree;		/* Next alloctable place here */
	unsigned s_nleft;		/* Number of bytes left here */
} stringdope[NSPACE];
			/* String resets suspended */
int	sourcing;			/* Currently reading variant file */
int	loading;mmdf/uip/ucbmail/head.c   444      0     12       13602  3670664440  10262 /*
 *  H E A D . C 
 *
 *  EE/CIS Computer Lab
 *  Department of Computer and Information Sciences
 *  Department of Electrical Engineering
 *  University of Delaware
 *
 *  REVISION HISTORY:
 *
 *  $Revision: 1.3 $
 *
 *  $Log:	head.c,v $
 * Revision 1.3  86/01/07  13:50:48  galvin
 * Change parse to have a return value.  Make it check to be sure the
 * line it is passed is a valid "From" line.  If it can determine a
 * date then return the date and 0, otherwise return 1.
 * 
 * Revision 1.2  86/01/07  13:46:02  galvin
 * Added comment header for revision history.
 * 
 *
 */

/*
 * Copyright (c) 1980 Regents of the University of California.
 * All rights reserved.  The Berkeley software License Agreement
 * specifies the terms and conditions for redistribution.
 */

#ifndef lint
static char *sccsid = "@(#)head.c	5.2 (Berkeley) 6/21/85";
#endif not lint

#include "./rcv.h"
#include <ctype.h>

/*
 * Mail -- a mail program
 *
 * Routines for processing and detecting headlines.
 */

/*
 * See if the passed line buffer is a mail header.
 * Return true if yes.  Note the extreme pains to
 * accomodate all funny formats.
 */

ishead(linebuf)
	char linebuf[];
{
	register char *cp;
	struct headline hl;
	char parbuf[BUFSIZ];

	cp = linebuf;
	if (strncmp("From ", cp, 5) != 0)
		return(0);
	parse(cp, &hl, parbuf);
	if (hl.l_from == NOSTR || hl.l_date == NOSTR) {
		fail(linebuf, "No from or date field");
		return(0);
	}
	if (!isdate(hl.l_date)) {
		fail(linebuf, "Date field not legal date");
		return(0);
	}
	
	/*
	 * I guess we got it!
	 */

	return(1);
}

fail(linebuf, reason)
	char linebuf[], reason[];
{

	if (value("debug") == NOSTR)
		return;
	fprintf(stderr, "\"%s\"\nnot a header because %s\n", linebuf, reason);
}

/*
 * Split a headline into its useful components.
 * Copy the line into dynamic string space, then set
 * pointers into the copied line in the passed headline
 * structure.  Actually, it scans.  Return 0 if we parsed, 1 otherwise.
 */

parse(line, hl, pbuf)
	char line[], pbuf[];
	struct headline *hl;
{
	register char *cp, *dp;
	char *sp;
	char word[LINESIZE];

	hl->l_from = NOSTR;
	hl->l_tty = NOSTR;
	hl->l_date = NOSTR;
	cp = line;
	sp = pbuf;

	/*
	 * Skip the first "word" of the line, which should be "From"
	 * anyway.
	 */

	if (strncmp(cp, "From", 4) == 0) {
	cp = nextword(cp, word);
	dp = nextword(cp, word);
	if (!equal(word, ""))
		hl->l_from = copyin(word, &sp);
	if (strncmp(dp, "tty", 3) == 0) {
		cp = nextword(dp, word);
		hl->l_tty = copyin(word, &sp);
		if (cp != NOSTR)
			hl->l_date = copyin(cp, &sp);
	}
	else
		if (dp != NOSTR)
			hl->l_date = copyin(dp, &sp);
		return(0);
	}
	else
		return(1);
}

/*
 * Copy the string on the left into the string on the right
 * and bump the right (reference) string pointer by the length.
 * Thus, dynamically allocate space in the right string, copying
 * the left string into it.
 */

char *
copyin(src, space)
	char src[];
	char **space;
{
	register char *cp, *top;
	register int s;

	s = strlen(src);
	cp = *space;
	top = cp;
	strcpy(cp, src);
	cp += s + 1;
	*space = cp;
	return(top);
}

/*
 * Test to see if the passed string is a ctime(3) generated
 * date string as documented in the manual.  The template
 * below is used as the criterion of correctness.
 * Also, we check for a possible trailing time zone using
 * the auxtype template.
 */

#define	L	1		/* A lower case char */
#define	S	2		/* A space */
#define	D	3		/* A digit */
#define	O	4		/* An optional digit or space */
#define	C	5		/* A colon */
#define	N	6		/* A new line */
#define U	7		/* An upper case char */

char ctypes[] = {U,L,L,S,U,L,L,S,O,D,S,D,D,C,D,D,C,D,D,S,D,D,D,D,0};
char tmztypes[] = {U,L,L,S,U,L,L,S,O,D,S,D,D,C,D,D,C,D,D,S,U,U,U,S,D,D,D,D,0};

isdate(date)
	char date[];
{
	register char *cp;

	cp = date;
	if (cmatch(cp, ctypes))
		return(1);
	return(cmatch(cp, tmztypes));
}

/*
 * Match the given string against the given template.
 * Return 1 if they match, 0 if they don't
 */

cmatch(str, temp)
	char str[], temp[];
{
	register char *cp, *tp;
	register int c;

	cp = str;
	tp = temp;
	while (*cp != '\0' && *tp != 0) {
		c = *cp++;
		switch (*tp++) {
		case L:
			if (c < 'a' || c > 'z')
				return(0);
			break;

		case U:
			if (c < 'A' || c > 'Z')
				return(0);
			break;

		case S:
			if (c != ' ')
				return(0);
			break;

		case D:
			if (!isdigit(c))
				return(0);
			break;

		case O:
			if (c != ' ' && !isdigit(c))
				return(0);
			break;

		case C:
			if (c != ':')
				return(0);
			break;

		case N:
			if (c != '\n')
				return(0);
			break;
		}
	}
	if (*cp != '\0' || *tp != 0)
		return(0);
	return(1);
}

/*
 * Collect a liberal (space, tab delimited) word into the word buffer
 * passed.  Also, return a pointer to the next word following that,
 * or NOSTR if none follow.
 */

char *
nextword(wp, wbuf)
	char wp[], wbuf[];
{
	register char *cp, *cp2;

	if ((cp = wp) == NOSTR) {
		copy("", wbuf);
		return(NOSTR);
	}
	cp2 = wbuf;
	while (!any(*cp, " \t") && *cp != '\0')
		if (*cp == '"') {
 			*cp2++ = *cp++;
 			while (*cp != '\0' && *cp != '"')
 				*cp2++ = *cp++;
 			if (*cp == '"')
 				*cp2++ = *cp++;
 		} else
 			*cp2++ = *cp++;
	*cp2 = '\0';
	while (any(*cp, " \t"))
		cp++;
	if (*cp == '\0')
		return(NOSTR);
	return(cp);
}

#ifdef NO_LIB
/*
 * Test to see if the character is an ascii alphabetic.
 */

isalpha(c)
{
	register int ch;

	ch = raise(c);
	return(ch >= 'A' && ch <= 'Z');
}

/*
 * Test to see if the character is an ascii digit.
 */

isdigit(c)
{
	return(c >= '0' && c <= '9');
}
#endif NO_LIB
/*
 * Copy str1 to str2, return pointer to null in str2.
 */

char *
copy(str1, str2)
	char *str1, *str2;
{
	register char *s1, *s2;

	s1 = str1;
	s2 = str2;
	while (*s1)
		*s2++ = *s1++;
	*s2 = 0;
	return(s2);
}

/*
 * Is ch any of the characters in str?
 */

any(ch, str)
register char ch;
register char *str;
{
	return(index(str, ch) ? 1 : 0);
}

/*
 * Convert lower case letters to upper case.
 */

raise(c)
	register int c;
{
        return(islower(c) ? toupper(c) : c );
}
space.
		 */

		while (*cp && *cp != ' ') {
			if (*cp == '\\' && isspace(cp[1]))
				*cp2++ = *cp++;
			*cp2++ = *cp++;
		}

mmdf/uip/ucbmail/lex.c   444      0     12       32521  3670632300  10140 /*
 *  L E X . C 
 *
 *  EE/CIS Computer Lab
 *  Department of Computer and Information Sciences
 *  Department of Electrical Engineering
 *  University of Delaware
 *
 *  REVISION HISTORY:
 *
 *  $Revision: 1.5 $
 *
 *  $Log:	lex.c,v $
 * Revision 1.5  86/01/14  16:15:18  galvin
 * Change the "mmdf.h" include line.
 * 
 * Revision 1.4  85/11/18  16:09:27  galvin
 * Change name of command table to CmdTab so as not
 * to conflict with cmdtab of MMDF.
 * 
 * Revision 1.3  85/11/18  14:34:21  galvin
 * Teach setfile how to MMDF lock files.
 * Have it use the access system call rather than opening and closing files.
 * 
 * Revision 1.2  85/11/18  12:35:29  galvin
 * Added comment header for revision history.
 * 
 *
 */

/*
 * Copyright (c) 1980 Regents of the University of California.
 * All rights reserved.  The Berkeley software License Agreement
 * specifies the terms and conditions for redistribution.
 */

#ifndef lint
static char *sccsid = "@(#)lex.c	5.4 (Berkeley) 11/2/85";
#endif not lint

#include "./rcv.h"
#include "./mmdf.h"
#include <sys/file.h>
#include <sys/stat.h>
#include <errno.h>

/*
 * Mail -- a mail program
 *
 * Lexical processing of commands.
 */

char	*prompt = "& ";

/*
 * Set up editing on the given file name.
 * If isedit is true, we are considered to be editing the file,
 * otherwise we are reading our mail which has signficance for
 * mbox and so forth.
 */

setfile(name, isedit)
	char *name;
{
	FILE *ibuf;
	struct stat stb;
	static int shudclob;
	static char efile[128];
	extern char tempMesg[];

	if ((ibuf = lk_fopen(name, "r", (char *)0, (char *)0, 5)) == NULL)
		return(-1);
	if (!edit) {
	if (fstat(fileno(ibuf), &stb) < 0) {
			perror(name);
			exit(1);
	}
		if (stb.st_size == 0) {
			lk_fclose(ibuf, name, (char *)0, (char *)0);
		return(-1);
	}
	}

	/*
	 * Looks like all will be well.  We must now relinquish our
	 * hold on the current set of stuff.  Must hold signals
	 * while we are reading the new file, else we will ruin
	 * the message[] data structure.
	 */

	holdsigs();
	if (shudclob) {
		lk_fclose(ibuf, name, (char *) 0, (char *) 0);
		if (edit)
			edstop();
		else
			quit();
	}

	/*
	 * Copy the messages into /tmp
	 * and set pointers.
	 */

	readonly = 0;
	if (access(name, W_OK) < 0)
		readonly++;
	if (shudclob) {
		fclose(itf);
		fclose(otf);
	}
	shudclob = 1;
	edit = isedit;
	strncpy(efile, name, 128);
	editfile = efile;
	if (name != mailname)
		strcpy(mailname, name);
	mailsize = fsize(ibuf);
	if ((otf = fopen(tempMesg, "w")) == NULL) {
		perror(tempMesg);
		lk_fclose(ibuf, name, (char *) 0, (char *) 0);
		exit(1);
	}
	if ((itf = fopen(tempMesg, "r")) == NULL) {
		perror(tempMesg);
		lk_fclose(ibuf, name, (char *) 0, (char *) 0);
		exit(1);
	}
	remove(tempMesg);
	setptr(ibuf);
	setmsize(msgCount);
	lk_fclose(ibuf, name, (char *)0, (char *)0);
	relsesigs();
	sawcom = 0;
	return(0);
}

/*
 * Interpret user commands one by one.  If standard input is not a tty,
 * print no prompt.
 */

int	*msgvec;

commands()
{
	int eofloop, shudprompt, stop();
	register int n;
	char linebuf[LINESIZE];
	int hangup(), contin();

# ifdef VMUNIX
	sigset(SIGCONT, SIG_DFL);
# endif VMUNIX
	if (rcvmode && !sourcing) {
		if (sigset(SIGINT, SIG_IGN) != SIG_IGN)
			sigset(SIGINT, stop);
		if (sigset(SIGHUP, SIG_IGN) != SIG_IGN)
			sigset(SIGHUP, hangup);
	}
	shudprompt = intty && !sourcing;
	for (;;) {
		setexit();

		/*
		 * Print the prompt, if needed.  Clear out
		 * string space, and flush the output.
		 */

		if (!rcvmode && !sourcing)
			return;
		eofloop = 0;
top:
		if (shudprompt) {
			printf(prompt);
			fflush(stdout);
# ifdef VMUNIX
			sigset(SIGCONT, contin);
# endif VMUNIX
		} else
			fflush(stdout);
		sreset();

		/*
		 * Read a line of commands from the current input
		 * and handle end of file specially.
		 */

		n = 0;
		for (;;) {
			if (readline(input, &linebuf[n]) <= 0) {
				if (n != 0)
					break;
				if (loading)
					return;
				if (sourcing) {
					unstack();
					goto more;
				}
				if (value("ignoreeof") != NOSTR && shudprompt) {
					if (++eofloop < 25) {
						printf("Use \"quit\" to quit.\n");
						goto top;
					}
				}
				if (edit)
					edstop();
				return;
			}
			if ((n = strlen(linebuf)) == 0)
				break;
			n--;
			if (linebuf[n] != '\\')
				break;
			linebuf[n++] = ' ';
		}
# ifdef VMUNIX
		sigset(SIGCONT, SIG_DFL);
# endif VMUNIX
		if (execute(linebuf, 0))
			return;
more:		;
	}
}

/*
 * Execute a single command.  If the command executed
 * is "quit," then return non-zero so that the caller
 * will know to return back to main, if he cares.
 * Contxt is non-zero if called while composing mail.
 */

execute(linebuf, contxt)
	char linebuf[];
{
	char word[LINESIZE];
	char *arglist[MAXARGC];
	struct cmd *com;
	register char *cp, *cp2;
	register int c;
	int muvec[2];
	int edstop(), e;

	/*
	 * Strip the white space away from the beginning
	 * of the command, then scan out a word, which
	 * consists of anything except digits and white space.
	 *
	 * Handle ! escapes differently to get the correct
	 * lexical conventions.
	 */

	cp = linebuf;
	while (any(*cp, " \t"))
		cp++;
	if (*cp == '!') {
		if (sourcing) {
			printf("Can't \"!\" while sourcing\n");
			unstack();
			return(0);
		}
		shell(cp+1);
		return(0);
	}
	cp2 = word;
	while (*cp && !any(*cp, " \t0123456789$^.:/-+*'\""))
		*cp2++ = *cp++;
	*cp2 = '\0';

	/*
	 * Look up the command; if not found, bitch.
	 * Normally, a blank command would map to the
	 * first command in the table; while sourcing,
	 * however, we ignore blank lines to eliminate
	 * confusion.
	 */

	if (sourcing && equal(word, ""))
		return(0);
	com = lex(word);
	if (com == NONE) {
		printf("Unknown command: \"%s\"\n", word);
		if (loading)
			return(1);
		if (sourcing)
			unstack();
		return(0);
	}

	/*
	 * See if we should execute the command -- if a conditional
	 * we always execute it, otherwise, check the state of cond.
	 */

	if ((com->c_argtype & F) == 0)
		if (cond == CRCV && !rcvmode || cond == CSEND && rcvmode)
			return(0);

	/*
	 * Special case so that quit causes a return to
	 * main, who will call the quit code directly.
	 * If we are in a source file, just unstack.
	 */

	if (com->c_func == edstop && sourcing) {
		if (loading)
			return(1);
		unstack();
		return(0);
	}
	if (!edit && com->c_func == edstop) {
		sigset(SIGINT, SIG_IGN);
		return(1);
	}

	/*
	 * Process the arguments to the command, depending
	 * on the type he expects.  Default to an error.
	 * If we are sourcing an interactive command, it's
	 * an error.
	 */

	if (!rcvmode && (com->c_argtype & M) == 0) {
		printf("May not execute \"%s\" while sending\n",
		    com->c_name);
		if (loading)
			return(1);
		if (sourcing)
			unstack();
		return(0);
	}
	if (sourcing && com->c_argtype & I) {
		printf("May not execute \"%s\" while sourcing\n",
		    com->c_name);
		if (loading)
			return(1);
		unstack();
		return(0);
	}
	if (readonly && com->c_argtype & W) {
		printf("May not execute \"%s\" -- message file is read only\n",
		   com->c_name);
		if (loading)
			return(1);
		if (sourcing)
			unstack();
		return(0);
	}
	if (contxt && com->c_argtype & R) {
		printf("Cannot recursively invoke \"%s\"\n", com->c_name);
		return(0);
	}
	e = 1;
	switch (com->c_argtype & ~(F|P|I|M|T|W|R)) {
	case MSGLIST:
		/*
		 * A message list defaulting to nearest forward
		 * legal message.
		 */
		if (msgvec == 0) {
			printf("Illegal use of \"message list\"\n");
			return(-1);
		}
		if ((c = getmsglist(cp, msgvec, com->c_msgflag)) < 0)
			break;
		if (c  == 0) {
			*msgvec = first(com->c_msgflag,
				com->c_msgmask);
			msgvec[1] = NULL;
		}
		if (*msgvec == NULL) {
			printf("No applicable messages\n");
			break;
		}
		e = (*com->c_func)(msgvec);
		break;

	case NDMLIST:
		/*
		 * A message list with no defaults, but no error
		 * if none exist.
		 */
		if (msgvec == 0) {
			printf("Illegal use of \"message list\"\n");
			return(-1);
		}
		if (getmsglist(cp, msgvec, com->c_msgflag) < 0)
			break;
		e = (*com->c_func)(msgvec);
		break;

	case STRLIST:
		/*
		 * Just the straight string, with
		 * leading blanks removed.
		 */
		while (any(*cp, " \t"))
			cp++;
		e = (*com->c_func)(cp);
		break;

	case RAWLIST:
		/*
		 * A vector of strings, in shell style.
		 */
		if ((c = getrawlist(cp, arglist,
				sizeof arglist / sizeof *arglist)) < 0)
			break;
		if (c < com->c_minargs) {
			printf("%s requires at least %d arg(s)\n",
				com->c_name, com->c_minargs);
			break;
		}
		if (c > com->c_maxargs) {
			printf("%s takes no more than %d arg(s)\n",
				com->c_name, com->c_maxargs);
			break;
		}
		e = (*com->c_func)(arglist);
		break;

	case NOLIST:
		/*
		 * Just the constant zero, for exiting,
		 * eg.
		 */
		e = (*com->c_func)(0);
		break;

	default:
		panic("Unknown argtype");
	}

	/*
	 * Exit the current source file on
	 * error.
	 */

	if (e && loading)
		return(1);
	if (e && sourcing)
		unstack();
	if (com->c_func == edstop)
		return(1);
	if (value("autoprint") != NOSTR && com->c_argtype & P)
		if ((dot->m_flag & MDELETED) == 0) {
			muvec[0] = dot - &message[0] + 1;
			muvec[1] = 0;
			type(muvec);
		}
	if (!sourcing && (com->c_argtype & T) == 0)
		sawcom = 1;
	return(0);
}

/*
 * When we wake up after ^Z, reprint the prompt.
 */
contin()
{

	printf(prompt);
	fflush(stdout);
}

/*
 * Branch here on hangup signal and simulate quit.
 */
hangup()
{

	holdsigs();
	if (edit) {
		if (setexit())
			exit(0);
		edstop();
	}
	else
		quit();
	exit(0);
}

/*
 * Set the size of the message vector used to construct argument
 * lists to message list functions.
 */
 
setmsize(sz)
{

	if (msgvec != (int *) 0)
		cfree((char *) msgvec);
	msgvec = (int *) calloc((unsigned) (sz + 1), sizeof *msgvec);
}

/*
 * Find the correct command in the command table corresponding
 * to the passed command "word"
 */

struct cmd *
lex(word)
	char word[];
{
	register struct cmd *cp;
	extern struct cmd CmdTab[];

	for (cp = &CmdTab[0]; cp->c_name != NOSTR; cp++)
		if (isprefix(word, cp->c_name))
			return(cp);
	return(NONE);
}

/*
 * Determine if as1 is a valid prefix of as2.
 * Return true if yep.
 */

isprefix(as1, as2)
	char *as1, *as2;
{
	register char *s1, *s2;

	s1 = as1;
	s2 = as2;
	while (*s1++ == *s2)
		if (*s2++ == '\0')
			return(1);
	return(*--s1 == '\0');
}

/*
 * The following gets called on receipt of a rubout.  This is
 * to abort printout of a command, mainly.
 * Dispatching here when command() is inactive crashes rcv.
 * Close all open files except 0, 1, 2, and the temporary.
 * The special call to getuserid() is needed so it won't get
 * annoyed about losing its open file.
 * Also, unstack all source files.
 */

int	inithdr;			/* am printing startup headers */

#ifdef _NFILE
static
_fwalk(function)
	register int (*function)();
{
	register FILE *iop;

	for (iop = _iob; iop < _iob + _NFILE; iop++)
		(*function)(iop);
}
#endif

static
xclose(iop)
	register FILE *iop;
{
	if (iop == stdin || iop == stdout ||
	    iop == stderr || iop == itf || iop == otf)
		return;

	if (iop != pipef)
		fclose(iop);
	else {
		pclose(pipef);
		pipef = NULL;
	}
}

#ifndef VMUNIX
stop(s)
#else   VMUNIX
stop()
#endif  VMUNIX
{

# ifndef VMUNIX
	s = SIGINT;
# endif VMUNIX
	noreset = 0;
	if (!inithdr)
		sawcom++;
	inithdr = 0;
	while (sourcing)
		unstack();
	getuserid((char *) -1);

	/*
	 * Walk through all the open FILEs, applying xclose() to them
	 */
	_fwalk(xclose);

	if (image >= 0) {
		close(image);
		image = -1;
	}
	fprintf(stderr, "Interrupt\n");
# ifndef VMUNIX
	signal(s, stop);
# endif
	reset(0);
}

/*
 * Announce the presence of the current Mail version,
 * give the message count, and print a header listing.
 */

char	*greeting	= "Mail version %s.  Type ? for help.\n";

announce(pr)
{
	int vec[2], mdot;
	extern char *version;

	if (pr && value("quiet") == NOSTR)
		printf(greeting, version);
	mdot = newfileinfo();
	vec[0] = mdot;
	vec[1] = 0;
	dot = &message[mdot - 1];
	if (msgCount > 0 && !noheader) {
		inithdr++;
		headers(vec);
		inithdr = 0;
	}
}

/*
 * Announce information about the file we are editing.
 * Return a likely place to set dot.
 */
newfileinfo()
{
	register struct message *mp;
	register int u, n, mdot, d, s;
	char fname[BUFSIZ], zname[BUFSIZ], *ename;

	for (mp = &message[0]; mp < &message[msgCount]; mp++)
		if (mp->m_flag & MNEW)
			break;
	if (mp >= &message[msgCount])
		for (mp = &message[0]; mp < &message[msgCount]; mp++)
			if ((mp->m_flag & MREAD) == 0)
				break;
	if (mp < &message[msgCount])
		mdot = mp - &message[0] + 1;
	else
		mdot = 1;
	s = d = 0;
	for (mp = &message[0], n = 0, u = 0; mp < &message[msgCount]; mp++) {
		if (mp->m_flag & MNEW)
			n++;
		if ((mp->m_flag & MREAD) == 0)
			u++;
		if (mp->m_flag & MDELETED)
			d++;
		if (mp->m_flag & MSAVED)
			s++;
	}
	ename = mailname;
	if (getfold(fname) >= 0) {
		strcat(fname, "/");
		if (strncmp(fname, mailname, strlen(fname)) == 0) {
			sprintf(zname, "+%s", mailname + strlen(fname));
			ename = zname;
		}
	}
	printf("\"%s\": ", ename);
	if (msgCount == 1)
		printf("1 message");
	else
		printf("%d messages", msgCount);
	if (n > 0)
		printf(" %d new", n);
	if (u-n > 0)
		printf(" %d unread", u);
	if (d > 0)
		printf(" %d deleted", d);
	if (s > 0)
		printf(" %d saved", s);
	if (readonly)
		printf(" [Read only]");
	printf("\n");
	return(mdot);
}

/*
 * Print the current version number.
 */

pversion()
{
	printf("Version %s\n", version);
	return(0);
}

/*
 * Load a file of user definitions.
 */
load(name)
	char *name;
{
	register FILE *in, *oldin;

	if ((in = fopen(name, "r")) == NULL)
		return;
	oldin = input;
	input = in;
	loading = 1;
	sourcing = 1;
	commands();
	loading = 0;
	sourcing = 0;
	input = oldin;
	fclose(in);
}
/
 
setmsize(sz)
{

	if (msgvec != (int *) 0)
		cfree((char *) msgvec);
	msgvec = (int *) calloc((unsigned) (sz + 1), sizeof *msgvec);
}

/*
 * Find the correct command in themmdf/uip/ucbmail/list.c   444      0     12       30477  3620510634  10333 /*
 * Copyright (c) 1980 Regents of the University of California.
 * All rights reserved.  The Berkeley software License Agreement
 * specifies the terms and conditions for redistribution.
 */

#ifndef lint
static char *sccsid = "@(#)list.c	5.4 (Berkeley) 11/2/85";
#endif not lint

#include "./rcv.h"
#include <ctype.h>

/*
 * Mail -- a mail program
 *
 * Message list handling.
 */

/*
 * Convert the user string of message numbers and
 * store the numbers into vector.
 *
 * Returns the count of messages picked up or -1 on error.
 */

getmsglist(buf, vector, flags)
	char *buf;
	int *vector;
{
	register int *ip;
	register struct message *mp;

	if (markall(buf, flags) < 0)
		return(-1);
	ip = vector;
	for (mp = &message[0]; mp < &message[msgCount]; mp++)
		if (mp->m_flag & MMARK)
			*ip++ = mp - &message[0] + 1;
	*ip = NULL;
	return(ip - vector);
}

/*
 * Mark all messages that the user wanted from the command
 * line in the message structure.  Return 0 on success, -1
 * on error.
 */

/*
 * Bit values for colon modifiers.
 */

#define	CMNEW		01		/* New messages */
#define	CMOLD		02		/* Old messages */
#define	CMUNREAD	04		/* Unread messages */
#define	CMDELETED	010		/* Deleted messages */
#define	CMREAD		020		/* Read messages */

/*
 * The following table describes the letters which can follow
 * the colon and gives the corresponding modifier bit.
 */

struct coltab {
	char	co_char;		/* What to find past : */
	int	co_bit;			/* Associated modifier bit */
	int	co_mask;		/* m_status bits to mask */
	int	co_equal;		/* ... must equal this */
} coltab[] = {
	'n',		CMNEW,		MNEW,		MNEW,
	'o',		CMOLD,		MNEW,		0,
	'u',		CMUNREAD,	MREAD,		0,
	'd',		CMDELETED,	MDELETED,	MDELETED,
	'r',		CMREAD,		MREAD,		MREAD,
	0,		0,		0,		0
};

static	int	lastcolmod;

markall(buf, f)
	char buf[];
{
	register char **np;
	register int i;
	register struct message *mp;
	char *namelist[NMLSIZE], *bufp;
	int tok, beg, mc, star, other, valdot, colmod, colresult;

	valdot = dot - &message[0] + 1;
	colmod = 0;
	for (i = 1; i <= msgCount; i++)
		unmark(i);
	bufp = buf;
	mc = 0;
	np = &namelist[0];
	scaninit();
	tok = scan(&bufp);
	star = 0;
	other = 0;
	beg = 0;
	while (tok != TEOL) {
		switch (tok) {
		case TNUMBER:
number:
			if (star) {
				printf("No numbers mixed with *\n");
				return(-1);
			}
			mc++;
			other++;
			if (beg != 0) {
				if (check(lexnumber, f))
					return(-1);
				for (i = beg; i <= lexnumber; i++)
					if ((message[i - 1].m_flag & MDELETED) 
== f)
						mark(i);
				beg = 0;
				break;
			}
			beg = lexnumber;
			if (check(beg, f))
				return(-1);
			tok = scan(&bufp);
			regret(tok);
			if (tok != TDASH) {
				mark(beg);
				beg = 0;
			}
			break;

		case TPLUS:
			if (beg != 0) {
				printf("Non-numeric second argument\n");
				return(-1);
			}
			if (valdot < msgCount)
				mark(valdot+1);
			else {
					printf("Referencing beyond EOF\n");
					return(-1);
				}
			break;

		case TDASH:
			if (beg == 0) {
				if (valdot > 1)
					mark(valdot-1);
				else {
						printf("Referencing before 1\n");
						return(-1);
					}
			}
			break;

		case TSTRING:
			if (beg != 0) {
				printf("Non-numeric second argument\n");
				return(-1);
			}
			other++;
			if (lexstring[0] == ':') {
				colresult = evalcol(lexstring[1]);
				if (colresult == 0) {
					printf("Unknown colon modifier \"%s\"\n",
					    lexstring);
					return(-1);
				}
				colmod |= colresult;
			}
			else
				*np++ = savestr(lexstring);
			break;

		case TDOLLAR:
		case TUP:
		case TDOT:
			lexnumber = metamess(lexstring[0], f);
			if (lexnumber == -1)
				return(-1);
			goto number;

		case TSTAR:
			if (other) {
				printf("Can't mix \"*\" with anything\n");
				return(-1);
			}
			star++;
			break;
		}
		tok = scan(&bufp);
	}
	lastcolmod = colmod;
	*np = NOSTR;
	mc = 0;
	if (star) {
		for (i = 0; i < msgCount; i++)
			if ((message[i].m_flag & MDELETED) == f) {
				mark(i+1);
				mc++;
			}
		if (mc == 0) {
			printf("No applicable messages.\n");
			return(-1);
		}
		return(0);
	}

	/*
	 * If no numbers were given, mark all of the messages,
	 * so that we can unmark any whose sender was not selected
	 * if any user names were given.
	 */

	if ((np > namelist || colmod != 0) && mc == 0)
		for (i = 1; i <= msgCount; i++)
			if ((message[i-1].m_flag & MDELETED) == f)
				mark(i);

	/*
	 * If any names were given, go through and eliminate any
	 * messages whose senders were not requested.
	 */

	if (np > namelist) {
		for (i = 1; i <= msgCount; i++) {
			for (mc = 0, np = &namelist[0]; *np != NOSTR; np++)
				if (**np == '/') {
					if (matchsubj(*np, i)) {
						mc++;
						break;
					}
				}
				else {
					if (sender(*np, i)) {
						mc++;
						break;
					}
				}
			if (mc == 0)
				unmark(i);
		}

		/*
		 * Make sure we got some decent messages.
		 */

		mc = 0;
		for (i = 1; i <= msgCount; i++)
			if (message[i-1].m_flag & MMARK) {
				mc++;
				break;
			}
		if (mc == 0) {
			printf("No applicable messages from {%s",
				namelist[0]);
			for (np = &namelist[1]; *np != NOSTR; np++)
				printf(", %s", *np);
			printf("}\n");
			return(-1);
		}
	}

	/*
	 * If any colon modifiers were given, go through and
	 * unmark any messages which do not satisfy the modifiers.
	 */

	if (colmod != 0) {
		for (i = 1; i <= msgCount; i++) {
			register struct coltab *colp;

			mp = &message[i - 1];
			for (colp = &coltab[0]; colp->co_char; colp++)
				if (colp->co_bit & colmod)
					if ((mp->m_flag & colp->co_mask)
					    != colp->co_equal)
						unmark(i);
			
		}
		for (mp = &message[0]; mp < &message[msgCount]; mp++)
			if (mp->m_flag & MMARK)
				break;
		if (mp >= &message[msgCount]) {
			register struct coltab *colp;

			printf("No messages satisfy");
			for (colp = &coltab[0]; colp->co_char; colp++)
				if (colp->co_bit & colmod)
					printf(" :%c", colp->co_char);
			printf("\n");
			return(-1);
		}
	}
	return(0);
}

/*
 * Turn the character after a colon modifier into a bit
 * value.
 */
evalcol(col)
{
	register struct coltab *colp;

	if (col == 0)
		return(lastcolmod);
	for (colp = &coltab[0]; colp->co_char; colp++)
		if (colp->co_char == col)
			return(colp->co_bit);
	return(0);
}

/*
 * Check the passed message number for legality and proper flags.
 */

check(mesg, f)
{
	register struct message *mp;

	if (mesg < 1 || mesg > msgCount) {
		printf("%d: Invalid message number\n", mesg);
		return(-1);
	}
	mp = &message[mesg-1];
	if ((mp->m_flag & MDELETED) != f) {
		printf("%d: Inappropriate message\n", mesg);
		return(-1);
	}
	return(0);
}

/*
 * Scan out the list of string arguments, shell style
 * for a RAWLIST.
 */

getrawlist(line, argv, argc)
	char line[];
	char **argv;
	int  argc;
{
	register char **ap, *cp, *cp2;
	char linebuf[BUFSIZ], quotec;
	register char **last;

	ap = argv;
	cp = line;
	last = argv + argc - 1;
	while (*cp != '\0') {
		while (any(*cp, " \t"))
			cp++;
		cp2 = linebuf;
		quotec = 0;
		if (any(*cp, "'\""))
			quotec = *cp++;
		if (quotec == 0)
			while (*cp != '\0' && !any(*cp, " \t"))
				*cp2++ = *cp++;
		else {
			while (*cp != '\0' && *cp != quotec)
				*cp2++ = *cp++;
			if (*cp != '\0')
				cp++;
		}
		*cp2 = '\0';
		if (cp2 == linebuf)
			break;
		if (ap >= last) {
			printf("Too many elements in the list; excess discarded\n");
			break;
		}
		*ap++ = savestr(linebuf);
	}
	*ap = NOSTR;
	return(ap-argv);
}

/*
 * scan out a single lexical item and return its token number,
 * updating the string pointer passed **p.  Also, store the value
 * of the number or string scanned in lexnumber or lexstring as
 * appropriate.  In any event, store the scanned `thing' in lexstring.
 */

struct lex {
	char	l_char;
	char	l_token;
} singles[] = {
	'$',	TDOLLAR,
	'.',	TDOT,
	'^',	TUP,
	'*',	TSTAR,
	'-',	TDASH,
	'+',	TPLUS,
	'(',	TOPEN,
	')',	TCLOSE,
	0,	0
};

scan(sp)
	char **sp;
{
	register char *cp, *cp2;
	register int c;
	register struct lex *lp;
	int quotec;

	if (regretp >= 0) {
		copy(stringstack[regretp], lexstring);
		lexnumber = numberstack[regretp];
		return(regretstack[regretp--]);
	}
	cp = *sp;
	cp2 = lexstring;
	c = *cp++;

	/*
	 * strip away leading white space.
	 */

	while (any(c, " \t"))
		c = *cp++;

	/*
	 * If no characters remain, we are at end of line,
	 * so report that.
	 */

	if (c == '\0') {
		*sp = --cp;
		return(TEOL);
	}

	/*
	 * If the leading character is a digit, scan
	 * the number and convert it on the fly.
	 * Return TNUMBER when done.
	 */

	if (isdigit(c)) {
		lexnumber = 0;
		while (isdigit(c)) {
			lexnumber = lexnumber*10 + c - '0';
			*cp2++ = c;
			c = *cp++;
		}
		*cp2 = '\0';
		*sp = --cp;
		return(TNUMBER);
	}

	/*
	 * Check for single character tokens; return such
	 * if found.
	 */

	for (lp = &singles[0]; lp->l_char != 0; lp++)
		if (c == lp->l_char) {
			lexstring[0] = c;
			lexstring[1] = '\0';
			*sp = cp;
			return(lp->l_token);
		}

	/*
	 * We've got a string!  Copy all the characters
	 * of the string into lexstring, until we see
	 * a null, space, or tab.
	 * If the lead character is a " or ', save it
	 * and scan until you get another.
	 */

	quotec = 0;
	if (any(c, "'\"")) {
		quotec = c;
		c = *cp++;
	}
	while (c != '\0') {
		if (c == quotec)
			break;
		if (quotec == 0 && any(c, " \t"))
			break;
		if (cp2 - lexstring < STRINGLEN-1)
			*cp2++ = c;
		c = *cp++;
	}
	if (quotec && c == 0)
		fprintf(stderr, "Missing %c\n", quotec);
	*sp = --cp;
	*cp2 = '\0';
	return(TSTRING);
}

/*
 * Unscan the named token by pushing it onto the regret stack.
 */

regret(token)
{
	if (++regretp >= REGDEP)
		panic("Too many regrets");
	regretstack[regretp] = token;
	lexstring[STRINGLEN-1] = '\0';
	stringstack[regretp] = savestr(lexstring);
	numberstack[regretp] = lexnumber;
}

/*
 * Reset all the scanner global variables.
 */

scaninit()
{
	regretp = -1;
}

/*
 * Find the first message whose flags & m == f  and return
 * its message number.
 */

first(f, m)
{
	register int mesg;
	register struct message *mp;

	mesg = dot - &message[0] + 1;
	f &= MDELETED;
	m &= MDELETED;
	for (mp = dot; mp < &message[msgCount]; mp++) {
		if ((mp->m_flag & m) == f)
			return(mesg);
		mesg++;
	}
	mesg = dot - &message[0];
	for (mp = dot-1; mp >= &message[0]; mp--) {
		if ((mp->m_flag & m) == f)
			return(mesg);
		mesg--;
	}
	return(NULL);
}

/*
 * See if the passed name sent the passed message number.  Return true
 * if so.
 */

sender(str, mesg)
	char *str;
{
	register struct message *mp;
	register char *cp;

	mp = &message[mesg-1];
	cp = nameof(mp, 0);
	return(icequal(cp, str));
}

/*
 * See if the given string matches inside the subject field of the
 * given message.  For the purpose of the scan, we ignore case differences.
 * If it does, return true.  The string search argument is assumed to
 * have the form "/search-string."  If it is of the form "/," we use the
 * previous search string.
 */

char lastscan[128];

matchsubj(str, mesg)
	char *str;
{
	register struct message *mp;
	register char *cp, *cp2, *backup;

	str++;
	if (strlen(str) == 0)
		str = lastscan;
	else
		strcpy(lastscan, str);
	mp = &message[mesg-1];
	
	/*
	 * Now look, ignoring case, for the word in the string.
	 */

	cp = str;
	cp2 = hfield("subject", mp);
	if (cp2 == NOSTR)
		return(0);
	backup = cp2;
	while (*cp2) {
		if (*cp == 0)
			return(1);
		if (raise(*cp++) != raise(*cp2++)) {
			cp2 = ++backup;
			cp = str;
		}
	}
	return(*cp == 0);
}

/*
 * Mark the named message by setting its mark bit.
 */

mark(mesg)
{
	register int i;

	i = mesg;
	if (i < 1 || i > msgCount)
		panic("Bad message number to mark");
	message[i-1].m_flag |= MMARK;
}

/*
 * Unmark the named message.
 */

unmark(mesg)
{
	register int i;

	i = mesg;
	if (i < 1 || i > msgCount)
		panic("Bad message number to unmark");
	message[i-1].m_flag &= ~MMARK;
}

/*
 * Return the message number corresponding to the passed meta character.
 */

metamess(meta, f)
{
	register int c, m;
	register struct message *mp;

	c = meta;
	switch (c) {
	case '^':
		/*
		 * First 'good' message left.
		 */
		for (mp = &message[0]; mp < &message[msgCount]; mp++)
			if ((mp->m_flag & MDELETED) == f)
				return(mp - &message[0] + 1);
		printf("No applicable messages\n");
		return(-1);

	case '$':
		/*
		 * Last 'good message left.
		 */
		for (mp = &message[msgCount-1]; mp >= &message[0]; mp--)
			if ((mp->m_flag & MDELETED) == f)
				return(mp - &message[0] + 1);
		printf("No applicable messages\n");
		return(-1);

	case '.':
		/* 
		 * Current message.
		 */
		m = dot - &message[0] + 1;
		if ((dot->m_flag & MDELETED) != f) {
			printf("%d: Inappropriate message\n", m);
			return(-1);
		}
		return(m);

	default:
		printf("Unknown metachar (%c)\n", c);
		return(-1);
	}
}
		c = *cp++;
		}
		*cp2 = '\0';
		*sp = --cp;
		return(TNUMBER);
	}

	/*
	 * Check for single character tokens; return such
	 * if found.
	 */

	for (lp = &singles[0]; lp->l_char != 0; lp++)
		mmdf/uip/ucbmail/local.h   444      0     12        2010  3670652724  10431 /*
 *  L O C A L . H 
 *
 *  EE/CIS Computer Lab
 *  Department of Computer and Information Sciences
 *  Department of Electrical Engineering
 *  University of Delaware
 *
 *  REVISION HISTORY:
 *
 *  $Revision: 1.3 $
 *
 *  $Log:	local.h,v $
 * Revision 1.3  85/11/16  15:54:31  galvin
 * Added include of v7.local.h if 4_2BSD is defined.
 * 
 * Revision 1.2  85/11/16  15:53:32  galvin
 * Added comment header for revision history.
 * 
 *
 */

/*
 * Copyright (c) 1980 Regents of the University of California.
 * All rights reserved.  The Berkeley software License Agreement
 * specifies the terms and conditions for redistribution.
 *
 *	@(#)local.h	5.1 (Berkeley) 6/6/85
 */

#ifdef V4_2BSD
#include "./v7.local.h"
#endif

#ifdef V4_1BSD
#include "./v7.local.h"
#endif

#ifdef V7
#include "./v7.local.h"
#endif

#ifdef CORY
#include "./c.local.h"
#endif

#ifdef INGRES
#include "./ing.local.h"
#endif

#ifdef V6
#include "./v6.local.h"
#endif

#ifdef CC
#include "./cc.local.h"
#endif

#ifdef V40
#include "./40.local.h"
#endif
nt the passed message number.  Return true
 * if so.
 */

sender(str, mesg)
	char *str;
{
	register struct message *mp;
	register char *cp;

	mp = &message[mesg-1];
	cp = nameof(mp, 0);
	return(icequal(cp, str));
}

/*
 * See if the given string matches inside the subject field of the
 * given message.  For the purpose of the scan, we ignore case differences.
 * If it does, return true.  The string search argument is assumed to
 * have the form "/search-string."  If it is of the form "/," we use themmdf/uip/ucbmail/main.c   444      0     12       15004  3670664430  10302 /*
 *  M A I N . C 
 *
 *  EE/CIS Computer Lab
 *  Department of Computer and Information Sciences
 *  Department of Electrical Engineering
 *  University of Delaware
 *
 *  REVISION HISTORY:
 *
 *  $Revision: 1.3 $
 *
 *  $Log:	main.c,v $
 * Revision 1.3  85/11/20  12:23:08  galvin
 * Added call to mmdf_init.
 * 
 * Revision 1.2  85/11/18  15:29:35  galvin
 * Added comment header for revision history.
 * 
 *
 */

/*
 * Copyright (c) 1980 Regents of the University of California.
 * All rights reserved.  The Berkeley software License Agreement
 * specifies the terms and conditions for redistribution.
 */

#ifndef lint
char *copyright =
"@(#) Copyright (c) 1980 Regents of the University of California.\nAll rights reserved.\n";
#endif not lint

#ifndef lint
static char *sccsid = "@(#)main.c	5.2 (Berkeley) 6/21/85";
#endif not lint

#include "./rcv.h"
#include <sys/stat.h>

/*
 * Mail -- a mail program
 *
 * Startup -- interface with user.
 */

jmp_buf	hdrjmp;

/*
 * Find out who the user is, copy his mail file (if exists) into
 * /tmp/Rxxxxx and set up the message pointers.  Then, print out the
 * message headers and read user commands.
 *
 * Command line syntax:
 *	Mail [ -i ] [ -r address ] [ -h number ] [ -f [ name ] ]
 * or:
 *	Mail [ -i ] [ -r address ] [ -h number ] people ...
 */

main(argc, argv)
	char **argv;
{
	register char *ef;
	register int i, argp;
	int mustsend, hdrstop(), (*prevint)(), f;
	struct sgttyb tbuf;

	/*
	 * Set up the MMDF environment.
	 */
	
	mmdf_init(argv[0]);

#ifdef signal
	Siginit();
#endif

	/*
	 * Set up a reasonable environment.  We clobber the last
	 * element of argument list for compatibility with version 6,
	 * figure out whether we are being run interactively, set up
	 * all the temporary files, buffer standard output, and so forth.
	 */

	argv[argc] = (char *) -1;
#ifdef	GETHOST
	inithost();
#endif	GETHOST
	mypid = getpid();
	intty = isatty(0);
	outtty = isatty(1);
	if (outtty) {
		gtty(1, &tbuf);
		baud = tbuf.sg_ospeed;
	}
	else
		baud = B9600;
	image = -1;

	/*
	 * Now, determine how we are being used.
	 * We successively pick off instances of -r, -h, -f, and -i.
	 * If called as "rmail" we note this fact for letter sending.
	 * If there is anything left, it is the base of the list
	 * of users to mail to.  Argp will be set to point to the
	 * first of these users.
	 */

	ef = NOSTR;
	argp = -1;
	mustsend = 0;
	if (argc > 0 && **argv == 'r')
		rmail++;
	for (i = 1; i < argc; i++) {

		/*
		 * If current argument is not a flag, then the
		 * rest of the arguments must be recipients.
		 */

		if (*argv[i] != '-') {
			argp = i;
			break;
		}
		switch (argv[i][1]) {
		case 'r':
			/*
			 * Next argument is address to be sent along
			 * to the mailer.
			 */
			if (i >= argc - 1) {
				fprintf(stderr, "Address required after -r\n");
				exit(1);
			}
			mustsend++;
			rflag = argv[i+1];
			i++;
			break;

		case 'T':
			/*
			 * Next argument is temp file to write which
			 * articles have been read/deleted for netnews.
			 */
			if (i >= argc - 1) {
				fprintf(stderr, "Name required after -T\n");
				exit(1);
			}
			Tflag = argv[i+1];
			if ((f = creat(Tflag, 0600)) < 0) {
				perror(Tflag);
				exit(1);
			}
			close(f);
			i++;
			break;

		case 'u':
			/*
			 * Next argument is person to pretend to be.
			 */
			if (i >= argc - 1) {
				fprintf(stderr, "Missing user name for -u\n");
				exit(1);
			}
			strcpy(myname, argv[i+1]);
			i++;
			break;

		case 'i':
			/*
			 * User wants to ignore interrupts.
			 * Set the variable "ignore"
			 */
			assign("ignore", "");
			break;

		case 'd':
			debug++;
			break;

		case 'h':
			/*
			 * Specified sequence number for network.
			 * This is the number of "hops" made so
			 * far (count of times message has been
			 * forwarded) to help avoid infinite mail loops.
			 */
			if (i >= argc - 1) {
				fprintf(stderr, "Number required for -h\n");
				exit(1);
			}
			mustsend++;
			hflag = atoi(argv[i+1]);
			if (hflag == 0) {
				fprintf(stderr, "-h needs non-zero number\n");
				exit(1);
			}
			i++;
			break;

		case 's':
			/*
			 * Give a subject field for sending from
			 * non terminal
			 */
			if (i >= argc - 1) {
				fprintf(stderr, "Subject req'd for -s\n");
				exit(1);
			}
			mustsend++;
			sflag = argv[i+1];
			i++;
			break;

		case 'f':
			/*
			 * User is specifying file to "edit" with Mail,
			 * as opposed to reading system mailbox.
			 * If no argument is given after -f, we read his
			 * mbox file in his home directory.
			 */
			if (i >= argc - 1)
				ef = mbox;
			else
				ef = argv[i + 1];
			i++;
			break;

		case 'n':
			/*
			 * User doesn't want to source /usr/lib/Mail.rc
			 */
			nosrc++;
			break;

		case 'N':
			/*
			 * Avoid initial header printing.
			 */
			noheader++;
			break;

		case 'v':
			/*
			 * Send mailer verbose flag
			 */
			assign("verbose", "");
			break;

		case 'I':
			/*
			 * We're interactive
			 */
			intty = 1;
			break;

		default:
			fprintf(stderr, "Unknown flag: %s\n", argv[i]);
			exit(1);
		}
	}

	/*
	 * Check for inconsistent arguments.
	 */

	if (ef != NOSTR && argp != -1) {
		fprintf(stderr, "Cannot give -f and people to send to.\n");
		exit(1);
	}
	if (mustsend && argp == -1) {
		fprintf(stderr, "The flags you gave make no sense since you're not sending mail.\n");
		exit(1);
	}
	tinit();
	input = stdin;
	rcvmode = argp == -1;
	if (!nosrc)
		load(MASTER);
	load(mailrc);
	if (argp != -1) {
		mail(&argv[argp]);

		/*
		 * why wait?
		 */

		exit(senderr);
	}

	/*
	 * Ok, we are reading mail.
	 * Decide whether we are editing a mailbox or reading
	 * the system mailbox, and open up the right stuff.
	 */

	if (ef != NOSTR) {
		char *ename;

		edit++;
		ename = expand(ef);
		if (ename != ef) {
			ef = (char *) calloc((unsigned) 1,
					     (unsigned) (strlen(ename) + 1));
			strcpy(ef, ename);
		}
		editfile = ef;
		strcpy(mailname, ef);
	}
	if (setfile(mailname, edit) < 0) {
		if (edit)
			perror(mailname);
		else
			fprintf(stderr, "No mail for %s\n", myname);
		exit(1);
	}
	if (!edit && !noheader && value("noheader") == NOSTR) {
		if (setjmp(hdrjmp) == 0) {
			if ((prevint = sigset(SIGINT, SIG_IGN)) != SIG_IGN)
				sigset(SIGINT, hdrstop);
			announce(1);
			fflush(stdout);
			sigset(SIGINT, prevint);
		}
	}
	if (edit)
		newfileinfo();
	if (!edit && msgCount == 0) {
		printf("No mail\n");
		fflush(stdout);
		exit(0);
	}
	commands();
	if (!edit) {
		sigset(SIGHUP, SIG_IGN);
		sigset(SIGINT, SIG_IGN);
		sigset(SIGQUIT, SIG_IGN);
		quit();
	}
	exit(0);
}

/*
 * Interrupt printing of the headers.
 */
hdrstop()
{

	fflush(stdout);
	fprintf(stderr, "\nInterrupt\n");
	longjmp(hdrjmp, 1);
}
nts.
		 */

		if (*argv[i] != '-') {
			argp = i;
			break;
		}
		switch (argv[i][1]) {
		case 'r':
			/*
			 * Next argument is address to be sent along
			 * to the mailer.
			 */
			if (i >= argc - 1) {
				fprintf(stderr, "Address required after -r\n");
				exit(1);
			}
			mustsend++;
			rflag = argv[i+1];
			i++;
			break;

		case 'T':
			/*
			 * Next argument is temp file to write which
			 * articles have been read/deleted for netnews.
			 */
			if (i >= argc - 1) {
				fprintf(stderr, "Name remmdf/uip/ucbmail/names.c   444      0     12       42752  3670661260  10471 /*
 *  N A M E S . C 
 *
 *  EE/CIS Computer Lab
 *  Department of Computer and Information Sciences
 *  Department of Electrical Engineering
 *  University of Delaware
 *
 *  REVISION HISTORY:
 *
 *  $Revision: 1.3 $
 *
 *  $Log:	names.c,v $
 * Revision 1.3  86/01/14  16:20:09  galvin
 * Throw away extract and rewrite it.  In order to provide some amount of
 * backward compatibility we make a decision about spaces in mboxes.
 * See extract() for details.
 * 
 * Force detract() to always delimit with ",".
 * 
 * Change outof() to insert a Date field and MMDF delimiters.
 * 
 * Revision 1.2  86/01/10  12:35:44  galvin
 * Added comment header for revision history.
 * 
 *
 */

/*
 * Copyright (c) 1980 Regents of the University of California.
 * All rights reserved.  The Berkeley software License Agreement
 * specifies the terms and conditions for redistribution.
 */

#ifndef lint
static char *sccsid = "@(#)names.c	5.3 (Berkeley) 11/10/85";
#endif not lint

/*
 * Mail -- a mail program
 *
 * Handle name lists.
 */

#include "./rcv.h"
#include "./mmdf.h"
#include <ctype.h>

/*
 * Allocate a single element of a name list,
 * initialize its name field to the passed
 * name and return it.
 */

struct name *
nalloc(str)
	char str[];
{
	register struct name *np;

	np = (struct name *) salloc(sizeof *np);
	np->n_flink = NIL;
	np->n_blink = NIL;
	np->n_type = -1;
	np->n_name = savestr(str);
	return(np);
}

/*
 * Find the tail of a list and return it.
 */

struct name *
tailof(name)
	struct name *name;
{
	register struct name *np;

	np = name;
	if (np == NIL)
		return(NIL);
	while (np->n_flink != NIL)
		np = np->n_flink;
	return(np);
}

/*
 * Extract a list of names from a line, and make a list of names from it.
 * Return the list or NIL if none found.
 *
 * We take this from LMCL and use some MMDF routines.  We have to make
 * a probably bad decision about spaces.  In order to provide some
 * amount of backward compatibility, we do not allow spaces in "mbox"
 * as returned by parsadr().  If there are spaces we assume that spaces
 * delimit addresses and parse appropriately.  Ditto if parsadr()
 * does not find an mbox and spaces exist in the address being parsed.
 */

extern char *adrptr;		/* this is for next_addr() */

struct name *
extract(line, ntype)
	char line[];
{
	register char *cp;
	register struct name *top, *np, *t;
	register int ret;
	char nbuf[BUFSIZ], abuf[BUFSIZ], buf[BUFSIZ], thisaddr[BUFSIZ];

	if (line == NOSTR || strlen(line) == 0)
		return(NIL);
	top = NIL;
	np = NIL;
	cp = line;

#ifdef DEBUG
	if (debug)
		printf("extracting from: \"%s\"\n", line);
#endif DEBUG
	for (adrptr = line, ret = next_address(buf);
			ret >= 0; ret = next_address(buf)) {
		if (ret == 0)
			continue; /* no address found */
		strcpy(thisaddr, buf);
		parsadr(thisaddr, NOSTR, nbuf, abuf);
		if (!*nbuf && !*abuf || index(nbuf, ' '))
			goto spaces;
		strcpy(nbuf, buf);

		/* Perform a simple UUCP rewrite into ARPA */
		/* We could probably be smarter here by calling parsadr */
		if (index(nbuf, '@') == NOSTR && index(nbuf, '<') == NOSTR
				&& index(nbuf, ' ') == NOSTR
				&& index(nbuf, '\t') == NOSTR
				&& (cp = index(nbuf, '!')) != NOSTR) {
			*cp = '\0';
			sprintf(buf, "%s@%s.UUCP", cp+1, nbuf);
			strcpy(nbuf, buf);
		}
#ifdef DEBUG
		if (debug)
			printf("extracted: \"%s\"\n", nbuf);
#endif DEBUG
		t = nalloc(nbuf);
		t->n_type = ntype;
		if (top == NIL)
			top = t;
		else
			np->n_flink = t;
		t->n_blink = np;
		np = t;
	}
	return(top);
spaces:
#ifdef DEBUG
	if (debug)
		printf("Assume addresses are delimited by spaces.\n");
#endif DEBUG
	top = NIL;
	np = NIL;
	ret = 0;

	for (cp = line; *cp; ++cp) {
		if (!isspace(*cp)) {
			nbuf[ret++] = *cp;
			continue;
		}
		if (ret == 0)
			continue;
		nbuf[ret] = '\0';
		t = nalloc(nbuf);
		t->n_type = ntype;
		if (top == NIL)
			top = t;
		else
			np->n_flink = t;
		t->n_blink = np;
		np = t;
		ret = 0;
	}
	if (ret) {
		nbuf[ret] = '\0';
		t = nalloc(nbuf);
		t->n_type = ntype;
		if (top == NIL)
			top = t;
		else
			np->n_flink = t;
		t->n_blink = np;
	}
	return(top);
}

/*
 * Turn a list of names into a string of the same names.
 */

char *
detract(np, ntype)
	register struct name *np;
{
	register int s;
	register char *cp, *top;
	register struct name *p;
	register int comma;

	comma = 1;
	if (np == NIL)
		return(NOSTR);
	ntype &= ~GCOMMA;
	s = 0;
	if (debug && comma)
		fprintf(stderr, "detract asked to insert commas\n");
	for (p = np; p != NIL; p = p->n_flink) {
		if (ntype && (p->n_type & GMASK) != ntype)
			continue;
		s += strlen(p->n_name) + 1;
		if (comma)
			s++;
	}
	if (s == 0)
		return(NOSTR);
	s += 2;
	top = salloc(s);
	cp = top;
	for (p = np; p != NIL; p = p->n_flink) {
		if (ntype && (p->n_type & GMASK) != ntype)
			continue;
		cp = copy(p->n_name, cp);
		if (comma && p->n_flink != NIL)
			*cp++ = ',';
		*cp++ = ' ';
	}
	*--cp = 0;
	if (comma && *--cp == ',')
		*cp = 0;
	return(top);
}

#ifdef NOT_USED
/*
 * Grab a single word (liberal word)
 * Throw away things between ()'s.
 */

char *
yankword(ap, wbuf)
	char *ap, wbuf[];
{
	register char *cp, *cp2;

	cp = ap;
	do {
		while (*cp && any(*cp, " \t,"))
			cp++;
		if (*cp == '(') {
			register int nesting = 0;

			while (*cp != '\0') {
				switch (*cp++) {
				case '(':
					nesting++;
					break;
				case ')':
					--nesting;
					break;
				}
				if (nesting <= 0)
					break;
			}
		}
		if (*cp == '\0')
			return(NOSTR);
	} while (any(*cp, " \t,("));
	for (cp2 = wbuf; *cp && !any(*cp, " \t,("); *cp2++ = *cp++)
		;
	*cp2 = '\0';
	return(cp);
}

/*
 * Verify that all the users in the list of names are
 * legitimate.  Bitch about and delink those who aren't.
 */

struct name *
verify(names)
	struct name *names;
{
	register struct name *np, *top, *t, *x;
	register char *cp;

#ifdef SENDMAIL
	return(names);
#else
	top = names;
	np = names;
	while (np != NIL) {
		if (np->n_type & GDEL) {
			np = np->n_flink;
			continue;
		}
		for (cp = "!:@^"; *cp; cp++)
			if (any(*cp, np->n_name))
				break;
		if (*cp != 0) {
			np = np->n_flink;
			continue;
		}
		cp = np->n_name;
		while (*cp == '\\')
			cp++;
		if (equal(cp, "msgs") ||
		    getuserid(cp) != -1) {
			np = np->n_flink;
			continue;
		}
		fprintf(stderr, "Can't send to %s\n", np->n_name);
		senderr++;
		if (np == top) {
			top = np->n_flink;
			if (top != NIL)
				top->n_blink = NIL;
			np = top;
			continue;
		}
		x = np->n_blink;
		t = np->n_flink;
		x->n_flink = t;
		if (t != NIL)
			t->n_blink = x;
		np = t;
	}
	return(top);
#endif
}
#endif NOT_USED

/*
 * For each recipient in the passed name list with a /
 * in the name, append the message to the end of the named file
 * and remove him from the recipient list.
 *
 * Recipients whose name begins with | are piped through the given
 * program and removed.
 */

struct name *
outof(names, fo, hp)
	struct name *names;
	FILE *fo;
	struct header *hp;
{
	register int c;
	register struct name *np, *top;
	time_t time();
	time_t now;
	char *date, *fname, *shell, *ctime();
	FILE *fout, *fin;
	int ispipe, s;
	extern char tempEdit[];

	top = names;
	np = names;
	time(&now);
	date = ctime(&now);
	while (np != NIL) {
		if (!isfileaddr(np->n_name) && np->n_name[0] != '|') {
			np = np->n_flink;
			continue;
		}
		ispipe = np->n_name[0] == '|';
		if (ispipe)
			fname = np->n_name+1;
		else
			fname = expand(np->n_name);

		/*
		 * See if we have copied the complete message out yet.
		 * If not, do so.
		 */

		if (image < 0) {
			if ((fout = fopen(tempEdit, "a")) == NULL) {
				perror(tempEdit);
				senderr++;
				goto cant;
			}
			image = open(tempEdit, 2);
			unlink(tempEdit);
			if (image < 0) {
				perror(tempEdit);
				senderr++;
				goto cant;
			}
			else {
				rewind(fo);
				fprintf(fout, "Date: %s\n", date);
				puthead(hp, fout, GTO|GSUBJECT|GCC|GNL);
				while ((c = getc(fo)) != EOF)
					putc(c, fout);
				rewind(fo);
				putc('\n', fout);
				fflush(fout);
				if (ferror(fout))
					perror(tempEdit);
				fclose(fout);
			}
		}

		/*
		 * Now either copy "image" to the desired file
		 * or give it as the standard input to the desired
		 * program as appropriate.
		 */

		if (ispipe) {
			wait(&s);
			switch (fork()) {
			case 0:
				sigchild();
				sigsys(SIGHUP, SIG_IGN);
				sigsys(SIGINT, SIG_IGN);
				sigsys(SIGQUIT, SIG_IGN);
				close(0);
				dup(image);
				close(image);
				if ((shell = value("SHELL")) == NOSTR)
					shell = SHELL;
				execl(shell, shell, "-c", fname, 0);
				perror(shell);
				exit(1);
				break;

			case -1:
				perror("fork");
				senderr++;
				goto cant;
			}
		}
		else {
			if ((fout = fopen(fname, "a")) == NULL) {
				perror(fname);
				senderr++;
				goto cant;
			}
			fin = Fdopen(image, "r");
			if (fin == NULL) {
				fprintf(stderr, "Can't reopen image\n");
				fclose(fout);
				senderr++;
				goto cant;
			}
			rewind(fin);
			fprintf(fout, "%s", delim1);
			while ((c = getc(fin)) != EOF)
				putc(c, fout);
			fprintf(fout, "%s", delim2);
			if (ferror(fout))
				senderr++, perror(fname);
			fclose(fout);
			fclose(fin);
		}

cant:

		/*
		 * In days of old we removed the entry from the
		 * the list; now for sake of header expansion
		 * we leave it in and mark it as deleted.
		 */

#ifdef CRAZYWOW
		if (np == top) {
			top = np->n_flink;
			if (top != NIL)
				top->n_blink = NIL;
			np = top;
			continue;
		}
		x = np->n_blink;
		t = np->n_flink;
		x->n_flink = t;
		if (t != NIL)
			t->n_blink = x;
		np = t;
#endif

		np->n_type |= GDEL;
		np = np->n_flink;
	}
	if (image >= 0) {
		close(image);
		image = -1;
	}
	return(top);
}

/*
 * Determine if the passed address is a local "send to file" address.
 * If any of the network metacharacters precedes any slashes, it can't
 * be a filename.  We cheat with .'s to allow path names like ./...
 */
isfileaddr(name)
	char *name;
{
	register char *cp;
	extern char *metanet;

	if (any('@', name))
		return(0);
	if (*name == '+')
		return(1);
	for (cp = name; *cp; cp++) {
		if (*cp == '.')
			continue;
		if (any(*cp, metanet))
			return(0);
		if (*cp == '/')
			return(1);
	}
	return(0);
}

/*
 * Map all of the aliased users in the invoker's mailrc
 * file and insert them into the list.
 * Changed after all these months of service to recursively
 * expand names (2/14/80).
 */

struct name *
usermap(names)
	struct name *names;
{
	register struct name *new, *np, *cp;
	struct grouphead *gh;
	register int metoo;

	new = NIL;
	np = names;
	metoo = (value("metoo") != NOSTR);
	while (np != NIL) {
		if (np->n_name[0] == '\\') {
			cp = np->n_flink;
			new = put(new, np);
			np = cp;
			continue;
		}
		gh = findgroup(np->n_name);
		cp = np->n_flink;
		if (gh != NOGRP)
			new = gexpand(new, gh, metoo, np->n_type);
		else
			new = put(new, np);
		np = cp;
	}
	return(new);
}

/*
 * Recursively expand a group name.  We limit the expansion to some
 * fixed level to keep things from going haywire.
 * Direct recursion is not expanded for convenience.
 */

struct name *
gexpand(nlist, gh, metoo, ntype)
	struct name *nlist;
	struct grouphead *gh;
{
	struct group *gp;
	struct grouphead *ngh;
	struct name *np;
	static int depth;
	char *cp;

	if (depth > MAXEXP) {
		printf("Expanding alias to depth larger than %d\n", MAXEXP);
		return(nlist);
	}
	depth++;
	for (gp = gh->g_list; gp != NOGE; gp = gp->ge_link) {
		cp = gp->ge_name;
		if (*cp == '\\')
			goto quote;
		if (strcmp(cp, gh->g_name) == 0)
			goto quote;
		if ((ngh = findgroup(cp)) != NOGRP) {
			nlist = gexpand(nlist, ngh, metoo, ntype);
			continue;
		}
quote:
		np = nalloc(cp);
		np->n_type = ntype;
		/*
		 * At this point should allow to expand
		 * to self if only person in group
		 */
		if (gp == gh->g_list && gp->ge_link == NOGE)
			goto skip;
		if (!metoo && strcmp(cp, myname) == 0)
			np->n_type |= GDEL;
skip:
		nlist = put(nlist, np);
	}
	depth--;
	return(nlist);
}

#ifdef NOT_USED
/*
 * Compute the length of the passed name list and
 * return it.
 */

lengthof(name)
	struct name *name;
{
	register struct name *np;
	register int c;

	for (c = 0, np = name; np != NIL; c++, np = np->n_flink)
		;
	return(c);
}
#endif NOT_USED

/*
 * Concatenate the two passed name lists, return the result.
 */

struct name *
cat(n1, n2)
	struct name *n1, *n2;
{
	register struct name *tail;

	if (n1 == NIL)
		return(n2);
	if (n2 == NIL)
		return(n1);
	tail = tailof(n1);
	tail->n_flink = n2;
	n2->n_blink = tail;
	return(n1);
}

#ifdef NOT_USED
/*
 * Unpack the name list onto a vector of strings.
 * Return an error if the name list won't fit.
 */

char **
unpack(np)
	struct name *np;
{
	register char **ap, **top;
	register struct name *n;
	char *cp;
	char hbuf[10];
	int t, extra;

	n = np;
	if ((t = lengthof(n)) == 0)
		panic("No names to unpack");

	/*
	 * Compute the number of extra arguments we will need.
	 * We need at least two extra -- one for "mail" and one for
	 * the terminating 0 pointer.  Additional spots may be needed
	 * to pass along -r and -f to the host mailer.
	 */

	extra = 2;
	if (rflag != NOSTR)
		extra += 2;
#ifdef SENDMAIL
	extra++;
	metoo = value("metoo") != NOSTR;
	if (metoo)
		extra++;
	verbose = value("verbose") != NOSTR;
	if (verbose)
		extra++;
#endif SENDMAIL
	if (hflag)
		extra += 2;
	top = (char **) salloc((t + extra) * sizeof cp);
	ap = top;
	*ap++ = "send-mail";
	if (rflag != NOSTR) {
		*ap++ = "-r";
		*ap++ = rflag;
	}
#ifdef SENDMAIL
	*ap++ = "-i";
	if (metoo)
		*ap++ = "-m";
	if (verbose)
		*ap++ = "-v";
#endif SENDMAIL
	if (hflag) {
		*ap++ = "-h";
		sprintf(hbuf, "%d", hflag);
		*ap++ = savestr(hbuf);
	}
	while (n != NIL) {
		if (n->n_type & GDEL) {
			n = n->n_flink;
			continue;
		}
		*ap++ = n->n_name;
		n = n->n_flink;
	}
	*ap = NOSTR;
	return(top);
}

/*
 * See if the user named himself as a destination
 * for outgoing mail.  If so, set the global flag
 * selfsent so that we avoid removing his mailbox.
 */

mechk(names)
	struct name *names;
{
	register struct name *np;

	for (np = names; np != NIL; np = np->n_flink)
		if ((np->n_type & GDEL) == 0 && equal(np->n_name, myname)) {
			selfsent++;
			return;
		}
}
#endif NOT_USED

/*
 * Remove all of the duplicates from the passed name list by
 * insertion sorting them, then checking for dups.
 * Return the head of the new list.
 */

struct name *
elide(names)
	struct name *names;
{
	register struct name *np, *t, *new;
	struct name *x;

	if (names == NIL)
		return(NIL);
	new = names;
	np = names;
	np = np->n_flink;
	if (np != NIL)
		np->n_blink = NIL;
	new->n_flink = NIL;
	while (np != NIL) {
		t = new;
		while (nstrcmp(t->n_name, np->n_name) < 0) {
			if (t->n_flink == NIL)
				break;
			t = t->n_flink;
		}

		/*
		 * If we ran out of t's, put the new entry after
		 * the current value of t.
		 */

		if (nstrcmp(t->n_name, np->n_name) < 0) {
			t->n_flink = np;
			np->n_blink = t;
			t = np;
			np = np->n_flink;
			t->n_flink = NIL;
			continue;
		}

		/*
		 * Otherwise, put the new entry in front of the
		 * current t.  If at the front of the list,
		 * the new guy becomes the new head of the list.
		 */

		if (t == new) {
			t = np;
			np = np->n_flink;
			t->n_flink = new;
			new->n_blink = t;
			t->n_blink = NIL;
			new = t;
			continue;
		}

		/*
		 * The normal case -- we are inserting into the
		 * middle of the list.
		 */

		x = np;
		np = np->n_flink;
		x->n_flink = t;
		x->n_blink = t->n_blink;
		t->n_blink->n_flink = x;
		t->n_blink = x;
	}

	/*
	 * Now the list headed up by new is sorted.
	 * Go through it and remove duplicates.
	 */

	np = new;
	while (np != NIL) {
		t = np;
		while (t->n_flink!=NIL &&
		    icequal(np->n_name,t->n_flink->n_name))
			t = t->n_flink;
		if (t == np || t == NIL) {
			np = np->n_flink;
			continue;
		}
		
		/*
		 * Now t points to the last entry with the same name
		 * as np.  Make np point beyond t.
		 */

		np->n_flink = t->n_flink;
		if (t->n_flink != NIL)
			t->n_flink->n_blink = np;
		np = np->n_flink;
	}
	return(new);
}

/*
 * Version of strcmp which ignores case differences.
 */

nstrcmp(s1, s2)
	register char *s1, *s2;
{
	register int c1, c2;

	do {
		c1 = *s1++;
		c2 = *s2++;
	} while (c1 && c1 == c2);
	return(c1 - c2);
}

/*
 * Put another node onto a list of names and return
 * the list.
 */

struct name *
put(list, node)
	struct name *list, *node;
{
	node->n_flink = list;
	node->n_blink = NIL;
	if (list != NIL)
		list->n_blink = node;
	return(node);
}

/*
 * Determine the number of elements in
 * a name list and return it.
 */

count(np)
	register struct name *np;
{
	register int c = 0;

	while (np != NIL) {
		c++;
		np = np->n_flink;
	}
	return(c);
}

#ifdef NOT_USED
cmpdomain(name, dname)
	register char *name, *dname;
{
	char buf[BUFSIZ];

	strcpy(buf, dname);
	buf[strlen(name)] = '\0';
	return(icequal(name, buf));
}
#endif NOT_USED

/*
 * Delete the given name from a namelist, using the passed
 * function to compare the names.
 */
struct name *
delname(np, name, cmpfun)
	register struct name *np;
	char name[];
	int (* cmpfun)();
{
	register struct name *p;

	for (p = np; p != NIL; p = p->n_flink)
		if ((* cmpfun)(p->n_name, name)) {
			if (p->n_blink == NIL) {
				if (p->n_flink != NIL)
					p->n_flink->n_blink = NIL;
				np = p->n_flink;
				continue;
			}
			if (p->n_flink == NIL) {
				if (p->n_blink != NIL)
					p->n_blink->n_flink = NIL;
				continue;
			}
			p->n_blink->n_flink = p->n_flink;
			p->n_flink->n_blink = p->n_blink;
		}
	return(np);
}

/*
 * Call the given routine on each element of the name
 * list, replacing said value if need be.
 */

mapf(np, from)
	register struct name *np;
	char *from;
{
	register struct name *p;

	for (p = np; p != NIL; p = p->n_flink)
		p->n_name = netmap(p->n_name, from);
}

#ifdef NOT_USED
/*
 * Pretty print a name list
 * Uncomment it if you need it.
 */

prettyprint(name)
	struct name *name;
{
	register struct name *np;

	np = name;
	while (np != NIL) {
		fprintf(stderr, "%s(%d) ", np->n_name, np->n_type);
		np = np->n_flink;
	}
	fprintf(stderr, "\n");
}
#endif NOT_USED
/

mechk(names)
	strucmmdf/uip/ucbmail/popen.c   444      0     12        4042  3670632252  10454 /*
 *  P O P E N . C 
 *
 *  EE/CIS Computer Lab
 *  Department of Computer and Information Sciences
 *  Department of Electrical Engineering
 *  University of Delaware
 *
 *  REVISION HISTORY:
 *
 *  $Revision: 1.3 $
 *
 *  $Log:	popen.c,v $
 * Revision 1.3  85/11/16  15:19:15  galvin
 * Added define for sigmask for backward compatibility from 4.3bsd to 4.2bsd.
 * 
 * Revision 1.2  85/11/16  14:43:38  galvin
 * Added comment header for revision history.
 * 
 *
 */

/*
 * Copyright (c) 1980 Regents of the University of California.
 * All rights reserved.  The Berkeley software License Agreement
 * specifies the terms and conditions for redistribution.
 */

#ifndef lint
static char *sccsid = "@(#)popen.c	5.2 (Berkeley) 6/21/85";
#endif not lint

#include <stdio.h>
#include <signal.h>
#include <errno.h>
#include "./sigretro.h"
#define	tst(a,b)	(*mode == 'r'? (b) : (a))
#define	RDR	0
#define	WTR	1
static	int	popen_pid[20];

#ifndef VMUNIX
#define vfork	fork
#endif VMUNIX
#ifndef	SIGRETRO
#define	sigchild()
#endif

FILE *
popen(cmd,mode)
char	*cmd;
char	*mode;
{
	int p[2];
	register myside, hisside, pid;

	if(pipe(p) < 0)
		return NULL;
	myside = tst(p[WTR], p[RDR]);
	hisside = tst(p[RDR], p[WTR]);
	if((pid = vfork()) == 0) {
		/* myside and hisside reverse roles in child */
		sigchild();
		close(myside);
		dup2(hisside, tst(0, 1));
		close(hisside);
		execl("/bin/csh", "sh", "-c", cmd, 0);
		_exit(1);
	}
	if(pid == -1)
		return NULL;
	popen_pid[myside] = pid;
	close(hisside);
	return(fdopen(myside, mode));
}

pclose(ptr)
FILE *ptr;
{
	register f, r;
	int status, omask;
	extern int errno;

	f = fileno(ptr);
	fclose(ptr);
# ifdef V4_2BSD
	omask = sigblock(sigmask(SIGINT)|sigmask(SIGQUIT)|sigmask(SIGHUP));
# endif V4_2BSD
# ifdef V4_1BSD
	sighold(SIGINT);
	sighold(SIGQUIT);
	sighold(SIGHUP);
# endif V4_2BSD
	while((r = wait(&status)) != popen_pid[f] && r != -1 && errno != EINTR)
		;
	if(r == -1)
		status = -1;
# ifdef V4_2BSD
	sigsetmask(omask);
# endif V4_2BSD
# ifdef V4_1BSD
	sigrelse(SIGINT);
	sigrelse(SIGQUIT);
	sigrelse(SIGHUP);
# endif V4_2BSD
}
egister int c = 0;

	while (np != NIL) {
		c++;
		np = np->n_flink;
	}
	return(c);
}

#ifdef NOT_USED
cmpdomain(name, dname)
	register char *name, *dname;
{
	char buf[BUFSIZ];

	strcpy(buf, dname);
	buf[strlen(name)] = '\0';
	return(icequal(name, buf));
}
#endif NOT_USED

/*
 * Delete the given name from a namelist, using the passed
 * function to compare the names.
 */
struct name *
delname(np, name, cmpfun)
	register struct name *np;
	char name[];
	int (* cmpfun)();
{
	remmdf/uip/ucbmail/optim.c   444      0     12       40104  3670664434  10511 /*
 * Copyright (c) 1980 Regents of the University of California.
 * All rights reserved.  The Berkeley software License Agreement
 * specifies the terms and conditions for redistribution.
 */

#ifndef lint
static char *sccsid = "@(#)optim.c	5.5 (Berkeley) 11/2/85";
#endif not lint

/*
 * Mail -- a program for sending and receiving mail.
 *
 * Network name modification routines.
 */

#include "./rcv.h"
#include "./configdefs.h"
#include <ctype.h>

/*
 * Map a name into the correct network "view" of the
 * name.  This is done by prepending the name with the
 * network address of the sender, then optimizing away
 * nonsense.
 */

char *
netmap(name, from)
	char name[], from[];
{
	char nbuf[BUFSIZ], ret[BUFSIZ];
	register char *cp;

	if (strlen(from) == 0)
		return(name);
	if (any('@', name) || any('%', name))
		return(savestr(arpafix(name, from)));
	cp = revarpa(from);
	if (cp == NOSTR)
		return(name);
	strcpy(nbuf, cp);
	cp = &nbuf[strlen(nbuf) - 1];
	while (!any(*cp, metanet) && cp > nbuf)
		cp--;
	if (cp == nbuf)
		return(name);
	*++cp = 0;
	strcat(nbuf, revarpa(name));
	optim(nbuf, ret);
	cp = revarpa(ret);
	if (!icequal(name, cp))
		return(savestr(cp));
	return(name);
}

#ifdef NOT_USED
/*
 * Rename the given network path to use
 * the kinds of names that we would right here.
 */

char *
rename(str)
	char str[];
{
	register char *cp, *cp2;
	char buf[BUFSIZ], path[BUFSIZ];
	register int c, host;

	cp = str;
	strcpy(path, "");
	for (;;) {
		if ((c = *cp++) == 0)
			break;
		cp2 = buf;
		while (!any(c, metanet) && c != 0) {
			*cp2++ = c;
			c = *cp++;
		}
		*cp2 = 0;
		if (c == 0) {
			strcat(path, buf);
			break;
		}
		host = netlook(buf, ntype(c));
		strcat(path, netname(host));
		stradd(path, c);
	}
	if (strcmp(str, path) != 0)
		return(savestr(path));
	return(str);
}
#endif NOT_USED

/*
 * Turn a network machine name into a unique character
 */
netlook(machine, attnet)
	char machine[];
{
	register struct netmach *np;
	register char *cp, *cp2;
	char nbuf[BUFSIZ];

	/*
	 * Make into lower case.
	 */

	for (cp = machine, cp2 = nbuf; *cp; *cp2++ = little(*cp++))
		if (cp2 >= &nbuf[sizeof(nbuf)-1])
			break;
	*cp2 = 0;

	/*
	 * If a single letter machine, look through those first.
	 */

	if (strlen(nbuf) == 1)
		for (np = netmach; np->nt_mid != 0; np++)
			if (np->nt_mid == nbuf[0])
				return(nbuf[0]);

	/*
	 * Look for usual name
	 */

	for (np = netmach; np->nt_mid != 0; np++)
		if (strcmp(np->nt_machine, nbuf) == 0)
			return(np->nt_mid);

	/*
	 * Look in side hash table.
	 */

	return(mstash(nbuf, attnet));
}

/*
 * Make a little character.
 */

little(c)
register char c;
{
        return(isupper(c) ? tolower(c) : c);
}

/*
 * Turn a network unique character identifier into a network name.
 */

char *
netname(mid)
{
	register struct netmach *np;
	char *mlook();

	if (mid & 0200)
		return(mlook(mid));
	for (np = netmach; np->nt_mid != 0; np++)
		if (np->nt_mid == mid)
			return(np->nt_machine);
	return(NOSTR);
}

/*
 * Deal with arpa net addresses.  The way this is done is strange.
 * In particular, if the destination arpa net host is not Berkeley,
 * then the address is correct as stands.  Otherwise, we strip off
 * the trailing @Berkeley, then cook up a phony person for it to
 * be from and optimize the result.
 */
char *
arpafix(name, from)
	char name[];
	char from[];
{
	register char *cp;
	register int arpamach;
	char newname[BUFSIZ];
	char fake[5];
	char fakepath[20];

	if (debug) {
		fprintf(stderr, "arpafix(%s, %s)\n", name, from);
	}
	cp = rindex(name, '@');
	if (cp == NOSTR)
		cp = rindex(name, '%');
	if (cp == NOSTR) {
		fprintf(stderr, "Somethings amiss -- no @ or % in arpafix\n");
		return(name);
	}
	cp++;
	arpamach = netlook(cp, '@');
	if (arpamach == 0) {
		if (debug)
			fprintf(stderr, "machine %s unknown, uses: %s\n", cp, name);
		return(name);
	}
	if (((nettype(arpamach) & nettype(LOCAL)) & ~AN) == 0) {
		if (debug)
			fprintf(stderr, "machine %s known but remote, uses: %s\n",
			    cp, name);
		return(name);
	}
	strcpy(newname, name);
	cp = rindex(newname, '@');
	if (cp == NOSTR)
		cp = rindex(newname, '%');
	*cp = 0;
	fake[0] = arpamach;
	fake[1] = ':';
	fake[2] = LOCAL;
	fake[3] = ':';
	fake[4] = 0;
	prefer(fake);
	strcpy(fakepath, netname(fake[0]));
	stradd(fakepath, fake[1]);
	strcat(fakepath, "daemon");
	if (debug)
		fprintf(stderr, "machine local, call netmap(%s, %s)\n",
		    newname, fakepath);
	return(netmap(newname, fakepath));
}

/*
 * Take a network machine descriptor and find the types of connected
 * nets and return it.
 */

nettype(mid)
{
	register struct netmach *np;

	if (mid & 0200)
		return(mtype(mid));
	for (np = netmach; np->nt_mid != 0; np++)
		if (np->nt_mid == mid)
			return(np->nt_type);
	return(0);
}

/*
 * Hashing routines to salt away machines seen scanning
 * networks paths that we don't know about.
 */

#define	XHSIZE		19		/* Size of extra hash table */
#define	NXMID		(XHSIZE*3/4)	/* Max extra machines */

struct xtrahash {
	char	*xh_name;		/* Name of machine */
	short	xh_mid;			/* Machine ID */
	short	xh_attnet;		/* Attached networks */
} xtrahash[XHSIZE];

struct xtrahash	*xtab[XHSIZE];		/* F: mid-->machine name */

short	midfree;			/* Next free machine id */

/*
 * Initialize the extra host hash table.
 * Called by sreset.
 */

minit()
{
	register struct xtrahash *xp, **tp;

	midfree = 0;
	tp = &xtab[0];
	for (xp = &xtrahash[0]; xp < &xtrahash[XHSIZE]; xp++) {
		xp->xh_name = NOSTR;
		xp->xh_mid = 0;
		xp->xh_attnet = 0;
		*tp++ = (struct xtrahash *) 0;
	}
}

/*
 * Stash a net name in the extra host hash table.
 * If a new entry is put in the hash table, deduce what
 * net the machine is attached to from the net character.
 *
 * If the machine is already known, add the given attached
 * net to those already known.
 */

mstash(name, attnet)
	char name[];
{
	register struct xtrahash *xp;
	struct xtrahash *xlocate();
	int x;

	xp = xlocate(name);
	if (xp == (struct xtrahash *) 0) {
		printf("Ran out of machine id spots\n");
		return(0);
	}
	if (xp->xh_name == NOSTR) {
		if (midfree >= XHSIZE) {
			printf("Out of machine ids\n");
			return(0);
		}
		xtab[midfree] = xp;
		xp->xh_name = savestr(name);
		xp->xh_mid = 0200 + midfree++;
	}
	x = ntype(attnet);
	if (x == 0)
		xp->xh_attnet |= SN;
	else
		xp->xh_attnet |= x;
	return(xp->xh_mid);
}

/*
 * Search for the given name in the hash table
 * and return the pointer to it if found, or to the first
 * empty slot if not found.
 *
 * If no free slots can be found, return 0.
 */

struct xtrahash *
xlocate(name)
	char name[];
{
	register int h, q, i;
	register char *cp;
	register struct xtrahash *xp;

	for (h = 0, cp = name; *cp; h = (h << 2) + *cp++)
		;
	if (h < 0 && (h = -h) < 0)
		h = 0;
	h = h % XHSIZE;
	cp = name;
	for (i = 0, q = 0; q < XHSIZE; i++, q = i * i) {
		xp = &xtrahash[(h + q) % XHSIZE];
		if (xp->xh_name == NOSTR)
			return(xp);
		if (strcmp(cp, xp->xh_name) == 0)
			return(xp);
		if (h - q < 0)
			h += XHSIZE;
		xp = &xtrahash[(h - q) % XHSIZE];
		if (xp->xh_name == NOSTR)
			return(xp);
		if (strcmp(cp, xp->xh_name) == 0)
			return(xp);
	}
	return((struct xtrahash *) 0);
}

/*
 * Return the name from the extra host hash table corresponding
 * to the passed machine id.
 */

char *
mlook(mid)
{
	register int m;

	if ((mid & 0200) == 0)
		return(NOSTR);
	m = mid & 0177;
	if (m >= midfree) {
		printf("Use made of undefined machine id\n");
		return(NOSTR);
	}
	return(xtab[m]->xh_name);
}

/*
 * Return the bit mask of net's that the given extra host machine
 * id has so far.
 */

mtype(mid)
{
	register int m;

	if ((mid & 0200) == 0)
		return(0);
	m = mid & 0177;
	if (m >= midfree) {
		printf("Use made of undefined machine id\n");
		return(0);
	}
	return(xtab[m]->xh_attnet);
}

/*
 * Take a network name and optimize it.  This gloriously messy
 * operation takes place as follows:  the name with machine names
 * in it is tokenized by mapping each machine name into a single
 * character machine id (netlook).  The separator characters (network
 * metacharacters) are left intact.  The last component of the network
 * name is stripped off and assumed to be the destination user name --
 * it does not participate in the optimization.  As an example, the
 * name "research!vax135!research!ucbvax!bill" becomes, tokenized,
 * "r!x!r!v!" and "bill"  A low level routine, optim1, fixes up the
 * network part (eg, "r!x!r!v!"), then we convert back to network
 * machine names and tack the user name on the end.
 *
 * The result of this is copied into the parameter "name"
 */

optim(net, name)
	char net[], name[];
{
	char netcomp[BUFSIZ], netstr[40], xfstr[40];
	register char *cp, *cp2;
	register int c;

	strcpy(netstr, "");
	cp = net;
	for (;;) {
		/*
		 * Rip off next path component into netcomp
		 */
		cp2 = netcomp;
		while (*cp && !any(*cp, metanet))
			*cp2++ = *cp++;
		*cp2 = 0;
		/*
		 * If we hit null byte, then we just scanned
		 * the destination user name.  Go off and optimize
		 * if its so.
		 */
		if (*cp == 0)
			break;
		if ((c = netlook(netcomp, *cp)) == 0) {
			printf("No host named \"%s\"\n", netcomp);
err:
			strcpy(name, net);
			return;
		}
		stradd(netstr, c);
		stradd(netstr, *cp++);
		/*
		 * If multiple network separators given,
		 * throw away the extras.
		 */
		while (any(*cp, metanet))
			cp++;
	}
	if (strlen(netcomp) == 0) {
		printf("net name syntax\n");
		goto err;
	}
	optim1(netstr, xfstr);

	/*
	 * Convert back to machine names.
	 */

	cp = xfstr;
	strcpy(name, "");
	while (*cp) {
		if ((cp2 = netname(*cp++)) == NOSTR) {
			printf("Made up bad net name\n");
			printf("Machine code %c (0%o)\n", cp[-1], cp[-1]);
			printf("Sorry -- dumping now.  Alert K. Shoens\n");
			core();
			goto err;
		}
		strcat(name, cp2);
		stradd(name, *cp++);
	}
	strcat(name, netcomp);
}

/*
 * Take a string of network machine id's and separators and
 * optimize them.  We process these by pulling off maximal
 * leading strings of the same type, passing these to the appropriate
 * optimizer and concatenating the results.
 */

optim1(netstr, name)
	char netstr[], name[];
{
	char path[40], rpath[40];
	register char *cp, *cp2;
	register int tp, nc;

	cp = netstr;
	prefer(cp);
	strcpy(name, "");
	/*
	 * If the address ultimately points back to us,
	 * just return a null network path.
	 */
	if (strlen(cp) > 1 && cp[strlen(cp) - 2] == LOCAL)
		return;
	while (*cp != 0) {
		strcpy(path, "");
		tp = ntype(cp[1]);
		nc = cp[1];
		while (*cp && tp == ntype(cp[1])) {
			stradd(path, *cp++);
			cp++;
		}
		switch (netkind(tp)) {
		default:
			strcpy(rpath, path);
			break;

		case IMPLICIT:
			optimimp(path, rpath);
			break;

		case EXPLICIT:
			optimex(path, rpath);
			break;
		}
		for (cp2 = rpath; *cp2 != 0; cp2++) {
			stradd(name, *cp2);
			stradd(name, nc);
		}
	}
	optiboth(name);
	prefer(name);
}

/*
 * Return the network of the separator --
 *	AN for arpa net
 *	BN for Bell labs net
 *	SN for Schmidt (berkeley net)
 *	0 if we don't know.
 */

ntype(nc)
	register int nc;
{
	register struct ntypetab *np;

	for (np = ntypetab; np->nt_char != 0; np++)
		if (np->nt_char == nc)
			return(np->nt_bcode);
	return(0);
}

/*
 * Return the kind of routing used for the particular net
 * EXPLICIT means explicitly routed
 * IMPLICIT means implicitly routed
 * 0 means don't know
 */

netkind(nt)
	register int nt;
{
	register struct nkindtab *np;

	for (np = nkindtab; np->nk_type != 0; np++)
		if (np->nk_type == nt)
			return(np->nk_kind);
	return(0);
}

/*
 * Do name optimization for an explicitly routed network (eg BTL network).
 */

optimex(net, name)
	char net[], name[];
{
	register char *cp, *rp;
	register int m;
	char *rindex();

	strcpy(name, net);
	cp = name;
	if (strlen(cp) == 0)
		return; /* error */
	if (cp[strlen(cp)-1] == LOCAL) {
		name[0] = 0;
		return;
	}
	for (cp = name; *cp; cp++) {
		m = *cp;
		rp = rindex(cp+1, m);
		if (rp != NOSTR)
			strcpy(cp, rp);
	}
	return;
}

/*
 * Do name optimization for implicitly routed network (eg, arpanet,
 * Berkeley network)
 */

optimimp(net, name)
	char net[], name[];
{
	register char *cp;
	register int m;

	cp = net;
	if (strlen(cp) == 0)
		return; /* error */
	m = cp[strlen(cp) - 1];
	if (m == LOCAL) {
		strcpy(name, "");
		return;
	}
	name[0] = m;
	name[1] = 0;
}

/*
 * Perform global optimization on the given network path.
 * The trick here is to look ahead to see if there are any loops
 * in the path and remove them.  The interpretation of loops is
 * more strict here than in optimex since both the machine and net
 * type must match.
 */

optiboth(net)
	char net[];
{
	register char *cp, *cp2;
	char *rpair();

	cp = net;
	if (strlen(cp) == 0)
		return;
	if ((strlen(cp) % 2) != 0) {
		printf("Strange arg to optiboth\n");
		return;
	}
	while (*cp) {
		cp2 = rpair(cp+2, *cp);
		if (cp2 != NOSTR)
			strcpy(cp, cp2);
		cp += 2;
	}
}

/*
 * Find the rightmost instance of the given (machine, type) pair.
 */

char *
rpair(str, mach)
	char str[];
{
	register char *cp, *last;

	cp = str;
	last = NOSTR;
	while (*cp) {
		if (*cp == mach)
			last = cp;
		cp += 2;
	}
	return(last);
}

/*
 * Change the network separators in the given network path
 * to the preferred network transmission means.
 */

prefer(name)
	char name[];
{
	register char *cp;
	register int state, n;

	state = LOCAL;
	for (cp = name; *cp; cp += 2) {
		n = best(state, *cp);
		if (n)
			cp[1] = n;
		state = *cp;
	}
}

/*
 * Return the best network separator for the given machine pair.
 */

best(src, dest)
{
	register int dtype, stype;
	register struct netorder *np;

	stype = nettype(src);
	dtype = nettype(dest);
	fflush(stdout);
	if (stype == 0 || dtype == 0) {
		printf("ERROR:  unknown internal machine id\n");
		return(0);
	}
	if ((stype & dtype) == 0)
		return(0);
	np = &netorder[0];
	while ((np->no_stat & stype & dtype) == 0)
		np++;
	return(np->no_char);
}

#ifdef	GETHOST
/*
 * Initialize the network name of the current host.
 */
inithost()
{
	register struct netmach *np;
	static char host[64];

	gethostname(host, sizeof host);
	for (np = netmach; np->nt_machine != 0; np++)
		if (strcmp(np->nt_machine, EMPTY) == 0)
			break;
	if (np->nt_machine == 0) {
		printf("Cannot find empty slot for dynamic host entry\n");
		exit(1);
	}
	np->nt_machine = host;
}
#endif	GETHOST

/*
 * Code to twist around arpa net names.
 */

#define WORD 257			/* Token for a string */

static	char netbuf[256];
static	char *yylval;

/*
 * Reverse all of the arpa net addresses in the given name to
 * be of the form "host @ user" instead of "user @ host"
 * This function is its own inverse.
 */

char *
revarpa(str)
	char str[];
{

	if (yyinit(str) < 0)
		return(NOSTR);
	if (name())
		return(NOSTR);
	if (strcmp(str, netbuf) == 0)
		return(str);
	return(savestr(netbuf));
}

/*
 * Parse (by recursive descent) network names, using the following grammar:
 *	name:
 *		term {':' term}
 *		term {'^' term}
 *		term {'!' term}
 *		term '@' name
 *		term '%' name
 *
 *	term:
 *		string of characters.
 */

name()
{
	register int t;
	register char *cp;

	for (;;) {
		t = yylex();
		if (t != WORD)
			return(-1);
		cp = yylval;
		t = yylex();
		switch (t) {
		case 0:
			strcat(netbuf, cp);
			return(0);

		case '@':
		case '%':
			if (name())
				return(-1);
			stradd(netbuf, '@');
			strcat(netbuf, cp);
			return(0);

		case WORD:
			return(-1);

		default:
			strcat(netbuf, cp);
			stradd(netbuf, t);
		}
	}
}

/*
 * Scanner for network names.
 */

static	char *charp;			/* Current input pointer */
static	int nexttok;			/* Salted away next token */

/*
 * Initialize the network name scanner.
 */

yyinit(str)
	char str[];
{
	static char lexbuf[BUFSIZ];

	netbuf[0] = 0;
	if (strlen(str) >= sizeof lexbuf - 1)
		return(-1);
	nexttok = 0;
	strcpy(lexbuf, str);
	charp = lexbuf;
	return(0);
}

/*
 * Scan and return a single token.
 * yylval is set to point to a scanned string.
 */

yylex()
{
	register char *cp, *l_dot;
	register int s;

	if (nexttok) {
		s = nexttok;
		nexttok = 0;
		return(s);
	}
	cp = charp;
	while (*cp && isspace(*cp))
		cp++;
	if (*cp == 0)
		return(0);
	if (any(*cp, metanet)) {
		charp = cp+1;
		return(*cp);
	}
	l_dot = cp;
	while (*cp && !any(*cp, metanet) && !any(*cp, " \t"))
		cp++;
	if (any(*cp, metanet))
		nexttok = *cp;
	if (*cp == 0)
		charp = cp;
	else
		charp = cp+1;
	*cp = 0;
	yylval = l_dot;
	return(WORD);
}

/*
 * Add a single character onto a string.
 */

stradd(str, c)
	register char *str;
	register int c;
{

	str += strlen(str);
	*str++ = c;
	*str = 0;
}
 loops
 * in the path and remove them.  The interpretation of loops is
 * more strict here than in optimex since both the machine and net
 * type must match.
 */

optiboth(net)
	char net[];
{
	register char *cp, *cp2;
	char *rpair();

	cp = net;
	if (strlen(cp) == 0)
		return;
	if ((strlen(cp) % 2) != 0) {
		printf("Strange arg to optiboth\n");
		return;
	}
	while (*cp) {
		cp2 = rpair(cp+2, *cp);
		if (cp2 != NOSTR)
			strcpy(cp, cp2);
		cmmdf/uip/ucbmail/quit.c   444      0     12       20041  3670632250  10330 /*
 *  Q U I T . C 
 *
 *  EE/CIS Computer Lab
 *  Department of Computer and Information Sciences
 *  Department of Electrical Engineering
 *  University of Delaware
 *
 *  REVISION HISTORY:
 *
 *  $Revision: 1.3 $
 *
 *  $Log:	quit.c,v $
 * Revision 1.3  85/12/18  13:25:49  galvin
 * Add another argument to send to indicate whether or not this
 * message should be delimited by MMDF message delimiters.
 * 
 * Change all but "temp" file opens/closes to use MMDF locking routines.
 * 
 * Revision 1.2  85/12/18  03:18:48  galvin
 * Added comment header for revision history.
 * 
 *
 */

/*
 * Copyright (c) 1980 Regents of the University of California.
 * All rights reserved.  The Berkeley software License Agreement
 * specifies the terms and conditions for redistribution.
 */

#ifndef lint
static char *sccsid = "@(#)quit.c	5.2 (Berkeley) 6/21/85";
#endif not lint

#include "./rcv.h"
#include <sys/stat.h>
#include <sys/file.h>
#include "./mmdf.h"

/*
 * Rcv -- receive mail rationally.
 *
 * Termination processing.
 */

/*
 * Save all of the undetermined messages at the top of "mbox"
 * Save all untouched messages back in the system mailbox.
 * Remove the system mailbox, if none saved there.
 */

quit()
{
	int mcount, p, modify, autohold, anystat, holdbit, nohold;
	FILE *ibuf, *obuf, *fbuf, *rbuf, *readstat, *abuf;
	register struct message *mp;
	register int c;
	extern char tempQuit[], tempResid[];
	struct stat minfo;
	char *id;

	/*
	 * If we are read only, we can't do anything,
	 * so just return quickly.
	 */

	if (readonly)
		return;
	/*
	 * See if there any messages to save in mbox.  If no, we
	 * can save copying mbox to /tmp and back.
	 *
	 * Check also to see if any files need to be preserved.
	 * Delete all untouched messages to keep them out of mbox.
	 * If all the messages are to be preserved, just exit with
	 * a message.
	 *
	 * If the luser has sent mail to himself, refuse to do
	 * anything with the mailbox, unless mail locking works.
	 */

	if ((fbuf = lk_fopen(mailname, "r", (char *) 0, (char *) 0, 5)) == NULL)
		goto newmail;
	rbuf = NULL;
	if (fstat(fileno(fbuf), &minfo) >= 0 && minfo.st_size > mailsize) {
		printf("New mail has arrived.\n");
		rbuf = fopen(tempResid, "w");
		if (rbuf == NULL || fbuf == NULL)
			goto newmail;
#ifdef APPEND
		fseek(fbuf, mailsize, 0);
		while ((c = getc(fbuf)) != EOF)
			putc(c, rbuf);
#else
		p = minfo.st_size - mailsize;
		while (p-- > 0) {
			c = getc(fbuf);
			if (c == EOF)
				goto newmail;
			putc(c, rbuf);
		}
#endif
		fclose(rbuf);
		if ((rbuf = fopen(tempResid, "r")) == NULL)
			goto newmail;
		remove(tempResid);
	}

	/*
	 * Adjust the message flags in each message.
	 */

	anystat = 0;
	autohold = value("hold") != NOSTR;
	holdbit = autohold ? MPRESERVE : MBOX;
	nohold = MBOX|MSAVED|MDELETED|MPRESERVE;
	if (value("keepsave") != NOSTR)
		nohold &= ~MSAVED;
	for (mp = &message[0]; mp < &message[msgCount]; mp++) {
		if (mp->m_flag & MNEW) {
			mp->m_flag &= ~MNEW;
			mp->m_flag |= MSTATUS;
		}
		if (mp->m_flag & MSTATUS)
			anystat++;
		if ((mp->m_flag & MTOUCH) == 0)
			mp->m_flag |= MPRESERVE;
		if ((mp->m_flag & nohold) == 0)
			mp->m_flag |= holdbit;
	}
	modify = 0;
	if (Tflag != NOSTR) {
		if ((readstat = fopen(Tflag, "w")) == NULL)
			Tflag = NOSTR;
	}
	for (c = 0, p = 0, mp = &message[0]; mp < &message[msgCount]; mp++) {
		if (mp->m_flag & MBOX)
			c++;
		if (mp->m_flag & MPRESERVE)
			p++;
		if (mp->m_flag & MODIFY)
			modify++;
		if (Tflag != NOSTR && (mp->m_flag & (MREAD|MDELETED)) != 0) {
			id = hfield("article-id", mp);
			if (id != NOSTR)
				fprintf(readstat, "%s\n", id);
		}
	}
	if (Tflag != NOSTR)
		fclose(readstat);
	if (p == msgCount && !modify && !anystat) {
		if (p == 1)
			printf("Held 1 message in %s\n", mailname);
		else
			printf("Held %2d messages in %s\n", p, mailname);
		lk_fclose(fbuf, mailname, (char *) 0, (char *) 0);
		if (rbuf != NULL)
			fclose(rbuf);
		return;
	}
	if (c == 0) {
		if (p != 0) {
			lk_fclose(fbuf, mailname, (char *) 0, (char *) 0);
			writeback(rbuf);
			return;
		}
		goto cream;
	}

	/*
	 * Create another temporary file and copy user's mbox file
	 * darin.  If there is no mbox, copy nothing.
	 * If he has specified "append" don't copy his mailbox,
	 * just copy saveable entries at the end.
	 */

	mcount = c;
	if (value("append") == NOSTR) {
		if ((obuf = fopen(tempQuit, "w")) == NULL) {
			perror(tempQuit);
			lk_fclose(fbuf, mailname, (char *) 0, (char *) 0);
			return;
		}
		if ((ibuf = fopen(tempQuit, "r")) == NULL) {
			perror(tempQuit);
			remove(tempQuit);
			fclose(obuf);
			lk_fclose(fbuf, mailname, (char *) 0, (char *) 0);
			return;
		}
		remove(tempQuit);
		if ((abuf = fopen(mbox, "r")) != NULL) {
			while ((c = getc(abuf)) != EOF)
				putc(c, obuf);
			fclose(abuf);
		}
		if (ferror(obuf)) {
			perror(tempQuit);
			fclose(ibuf);
			fclose(obuf);
			lk_fclose(fbuf, mailname, (char *) 0, (char *) 0);
			return;
		}
		fclose(obuf);
		close(creat(mbox, sentprotect));
		if ((obuf = fopen(mbox, "r+")) == NULL) {
			perror(mbox);
			fclose(ibuf);
			lk_fclose(fbuf, mailname, (char *) 0, (char *) 0);
			return;
		}
	}
	if (value("append") != NOSTR)
		if ((obuf = fopen(mbox, "a")) == NULL) {
			perror(mbox);
			lk_fclose(fbuf, mailname, (char *) 0, (char *) 0);
			return;
		}
	for (mp = &message[0]; mp < &message[msgCount]; mp++)
		if (mp->m_flag & MBOX)
			if (send(mp, obuf, 0, 1) < 0) {
				perror(mbox);
				if (ibuf != NULL)
				fclose(ibuf);
				fclose(obuf);
				lk_fclose(fbuf, mailname, (char *)0, (char *)0);
				return;
			}

	/*
	 * Copy the user's old mbox contents back
	 * to the end of the stuff we just saved.
	 * If we are appending, this is unnecessary.
	 */

	if (value("append") == NOSTR) {
		rewind(ibuf);
		c = getc(ibuf);
		while (c != EOF) {
			putc(c, obuf);
			if (ferror(obuf))
				break;
			c = getc(ibuf);
		}
		fclose(ibuf);
		fflush(obuf);
	}
	if (ferror(obuf)) {
		perror(mbox);
		fclose(obuf);
		lk_fclose(fbuf, mailname, (char *) 0, (char *) 0);
		return;
	}
	fclose(obuf);
	if (mcount == 1)
		printf("Saved 1 message in mbox\n");
	else
		printf("Saved %d messages in mbox\n", mcount);

	/*
	 * Now we are ready to copy back preserved files to
	 * the system mailbox, if any were requested.
	 */

	if (p != 0) {
		lk_fclose(fbuf, mailname, (char *) 0, (char *) 0);
		writeback(rbuf);
		return;
	}

	/*
	 * Finally, remove his /usr/mail file.
	 * If new mail has arrived, copy it back.
	 */

cream:
	if (rbuf != NULL) {
		lk_fclose(fbuf, mailname, (char *) 0, (char *) 0);
		if ((abuf = lk_fopen(mailname, "w+", (char *) 0, (char *) 0, 5)) == NULL)
			goto newmail;
		while ((c = getc(rbuf)) != EOF)
			putc(c, abuf);
		fclose(rbuf);
		lk_fclose(abuf, mailname, (char *) 0, (char *) 0);
		alter(mailname);
		return;
	}
	demail();
	lk_fclose(fbuf, mailname, (char *) 0, (char *) 0);
	return;

newmail:
	printf("Thou hast new mail.\n");
	if (fbuf != NULL)
		lk_fclose(fbuf, mailname, (char *) 0, (char *) 0);
}

/*
 * Preserve all the appropriate messages back in the system
 * mailbox, and print a nice message indicated how many were
 * saved.  On any error, just return -1.  Else return 0.
 * Incorporate the any new mail that we found.
 */
writeback(res)
	register FILE *res;
{
	register struct message *mp;
	register int p, c;
	FILE *obuf;

	p = 0;
	if ((obuf = lk_fopen(mailname, "w+", (char *) 0, (char *) 0, 5)) == NULL) {
		perror(mailname);
		return;  /* error */
	}
#ifndef APPEND
	if (res != NULL)
		while ((c = getc(res)) != EOF)
			putc(c, obuf);
#endif
	for (mp = &message[0]; mp < &message[msgCount]; mp++)
		if ((mp->m_flag&MPRESERVE)||(mp->m_flag&MTOUCH)==0) {
			p++;
			if (send(mp, obuf, 0, 1) < 0) {
				perror(mailname);
				lk_fclose(obuf, mailname, (char *) 0, (char *) 0);
				return;   /* error */
			}
		}
#ifdef APPEND
	if (res != NULL)
		while ((c = getc(res)) != EOF)
			putc(c, obuf);
#endif
	fflush(obuf);
	if (ferror(obuf)) {
		perror(mailname);
		lk_fclose(obuf, mailname, (char *) 0, (char *) 0);
		return;   /* error */
	}
	if (res != NULL)
		fclose(res);
	lk_fclose(obuf, mailname, (char *) 0, (char *) 0);
	alter(mailname);
	if (p == 1)
		printf("Held 1 message in %s\n", mailname);
	else
		printf("Held %d messages in %s\n", p, mailname);
	return;
}
in.  If there is no mbox, copy nothing.
	 * If he has specified "append" don't copy his mailbox,
	 * just copy saveable entries at the end.
	 */

	mcount = c;
	if (value("append") == NOSTR) {
		if ((obuf = fopen(tempQuit, "w")) == NULL) {
			perror(tempQuit);
			lk_fclose(fbuf, mailname, (char *) 0, (char *) 0);
			return;
		}
		if ((ibuf = fopen(tempQuit, "r")) == NULL) {
			perror(tempQuit);
			remove(tempQuit);
			fclose(obuf);
			lk_fclose(fbuf, mailname, (char *) 0, (chmmdf/uip/ucbmail/send.c   444      0     12       27040  3670661256  10315 /*
 *  S E N D . C 
 *
 *  EE/CIS Computer Lab
 *  Department of Computer and Information Sciences
 *  Department of Electrical Engineering
 *  University of Delaware
 *
 *  REVISION HISTORY:
 *
 *  $Revision: 1.4 $
 *
 *  $Log:	send.c,v $
 * Revision 1.4  86/01/14  16:29:24  galvin
 * Change send() to check for From line instead of believing it is there.
 * 
 * Change sendmail() to extract()/detract() the To field.
 * 
 * mail1() needed some tuning and cleaning.  Basically change it to
 * interface to the MMDF ml_* routines.  One other major change was to
 * take out the call to verify() and depend on MMDF to do it.  We have
 * to do this anyway since we are allowing address forms that choke Mail.
 * 
 * Change savemail() to include MMDF delimiters.
 * 
 * Revision 1.3  86/01/02  14:29:57  galvin
 * Add another parameter to send to indicate whether or not this message
 * should be delimited by MMDF message delimiters.
 * 
 * Revision 1.2  85/12/18  03:00:41  galvin
 * Added comment header for revision history.
 * 
 *
 */

/*
 * Copyright (c) 1980 Regents of the University of California.
 * All rights reserved.  The Berkeley software License Agreement
 * specifies the terms and conditions for redistribution.
 */

#ifndef lint
static char *sccsid = "@(#)send.c	5.2 (Berkeley) 6/21/85";
#endif not lint

#include "./rcv.h"
#include <ctype.h>
#include <sys/stat.h>

#include "./mmdf.h"

/*
 * Mail -- a mail program
 *
 * Mail to others.
 */

/*
 * Send message described by the passed pointer to the
 * passed output buffer.  Return -1 on error, but normally
 * the number of lines written.  Adjust the status: field
 * if need be.  If doign is set, suppress ignored header fields.
 * If delim is set, output MMDF delimiters around message.
 */
send(mailp, obuf, doign, delim)
	struct message *mailp;
	FILE *obuf;
	int delim;
{
	register struct message *mp;
	long c;
	FILE *ibuf;
	char line[LINESIZE], field[BUFSIZ];
	int lc, ishead, infld, fline, dostat;
	char *cp, *cp2;

	mp = mailp;
	ibuf = setinput(mp);
	c = mp->m_size;
	ishead = 1;
	dostat = 1;
	infld = 0;
	fline = 1;
	lc = 0;
	if (delim)
		fputs(delim1, obuf);
	while (c > 0L) {
		fgets(line, LINESIZE, ibuf);
		c -= (long) strlen(line);
		lc++;
		if (ishead) {
			/* 
			 * If first line is the "From line", then just print.
			 * Since we are dealing with MMDF, it probably isn't.
			 */
			if (fline) {
				fline = 0;
				if (!strncmp(line, "From ", 5))
				goto writeit;
			}
			/*
			 * If line is blank, we've reached end of
			 * headers, so force out status: field
			 * and note that we are no longer in header
			 * fields
			 */
			if (line[0] == '\n') {
				if (dostat) {
					statusput(mailp, obuf, doign);
					dostat = 0;
				}
				ishead = 0;
				goto writeit;
			}
			/*
			 * If this line is a continuation (via space or tab)
			 * of a previous header field, just echo it
			 * (unless the field should be ignored).
			 */
			if (infld && (isspace(line[0]) || line[0] == '\t')) {
				if (doign && isign(field)) continue;
				goto writeit;
			}
			infld = 0;
			/*
			 * If we are no longer looking at real
			 * header lines, force out status:
			 * This happens in uucp style mail where
			 * there are no headers at all.
			 */
			if (!headerp(line)) {
				if (dostat) {
					statusput(mailp, obuf, doign);
					dostat = 0;
				}
				putc('\n', obuf);
				ishead = 0;
				goto writeit;
			}
			infld++;
			/*
			 * Pick up the header field.
			 * If it is an ignored field and
			 * we care about such things, skip it.
			 */
			cp = line;
			cp2 = field;
			while (*cp && *cp != ':' && !isspace(*cp))
				*cp2++ = *cp++;
			*cp2 = 0;
			if (doign && isign(field))
				continue;
			/*
			 * If the field is "status," go compute and print the
			 * real Status: field
			 */
			if (icequal(field, "status")) {
				if (dostat) {
					statusput(mailp, obuf, doign);
					dostat = 0;
				}
				continue;
			}
		}
writeit:
		fputs(line, obuf);
		if (ferror(obuf))
			return(-1);
	}
	if (delim)
		fputs(delim2, obuf);
	if (ferror(obuf))
		return(-1);
	if (ishead && (mailp->m_flag & MSTATUS))
		printf("failed to fix up status field\n");
	return(lc);
}

/*
 * Test if the passed line is a header line, RFC 733 style.
 */
headerp(line)
	register char *line;
{
	register char *cp = line;

	while (*cp && !isspace(*cp) && *cp != ':')
		cp++;
	while (*cp && isspace(*cp))
		cp++;
	return(*cp == ':');
}

/*
 * Output a reasonable looking status field.
 * But if "status" is ignored and doign, forget it.
 */
statusput(mp, obuf, doign)
	register struct message *mp;
	register FILE *obuf;
{
	char statout[3];

	if (doign && isign("status"))
		return;
	if ((mp->m_flag & (MNEW|MREAD)) == MNEW)
		return;
	if (mp->m_flag & MREAD)
		strcpy(statout, "R");
	else
		strcpy(statout, "");
	if ((mp->m_flag & MNEW) == 0)
		strcat(statout, "O");
	fprintf(obuf, "Status: %s\n", statout);
}


/*
 * Interface between the argument list and the mail1 routine
 * which does all the dirty work.
 */

mail(people)
	char **people;
{
	register char *cp2;
	register int s;
	char *buf, **ap;
	struct header head;

	for (s = 0, ap = people; *ap != (char *) -1; ap++)
		s += strlen(*ap) + 1;
	buf = salloc(s+1);
	cp2 = buf;
	for (ap = people; *ap != (char *) -1; ap++) {
		cp2 = copy(*ap, cp2);
		*cp2++ = ' ';
	}
	if (cp2 != buf)
		cp2--;
	*cp2 = '\0';
	head.h_to = buf;
	head.h_subject = NOSTR;
	head.h_cc = NOSTR;
	head.h_bcc = NOSTR;
	head.h_seq = 0;
	mail1(&head);
}


/*
 * Send mail to a bunch of user names.  The interface is through
 * the mail routine below.
 */

sendmail(str)
	char *str;
{
	struct header head;

	if (blankline(str))
		head.h_to = NOSTR;
	else
		head.h_to = detract(elide(extract(str, GTO)), GTO);
	head.h_subject = NOSTR;
	head.h_cc = NOSTR;
	head.h_bcc = NOSTR;
	head.h_seq = 0;
	mail1(&head);
	return(0);
}

/*
 * Mail a message on standard input to the people indicated
 * in the passed header.  (Internal interface).
 */

mail1(hp)
	struct header *hp;
{
	register char *cp;
	int gotcha;
	struct name *to, *np;
	struct stat sbuf;
	FILE *mtf, *postage;
	int remote = rflag != NOSTR || rmail;

	/*
	 * Collect user's mail from standard input.
	 * Get the result as mtf.
	 */

	if ((mtf = collect(hp)) == NULL)
		return;
	hp->h_seq = 1;
	if (hp->h_subject == NOSTR)
		hp->h_subject = sflag;
	if (intty && value("askcc") != NOSTR)
		grabh(hp, GCC);
	else if (intty) {
		printf("EOT\n");
		fflush(stdout);
	}

	/*
	 * Now, get the user names from the combined to and cc lists
	 */

	senderr = 0;
	to = usermap(cat(extract(hp->h_bcc, GBCC),
	    cat(extract(hp->h_to, GTO), extract(hp->h_cc, GCC))));
	if (to == NIL) {
		printf("No recipients specified\n");
		goto topdog;
	}

	/*
	 * Look through the recipient list for names with /'s
	 * in them which we write to as files directly.
	 */

	to = outof(to, mtf, hp);
	if (senderr && !remote) {
topdog:
		rewind(mtf);
		if ((mtf = infix(hp, mtf)) == NULL) {
			fprintf(stderr, ". . . message lost, sorry.\n");
			return;
		}
		if (fsize(mtf) != 0) {
			remove(deadletter);
			exwrite(deadletter, mtf, 1);
		}
		goto out;
	}
	for (gotcha = 0, np = to; np != NIL; np = np->n_flink)
		if ((np->n_type & GDEL) == 0) {
			gotcha++;
			break;
		}
	if (!gotcha)
		goto out;
	to = elide(to);
	if (count(to) > 1)
		hp->h_seq++;
	hp->h_to = detract(to, GTO|GCOMMA);
	hp->h_cc = detract(to, GCC|GCOMMA);
	hp->h_bcc = detract(to, GBCC|GCOMMA);
	if (hp->h_seq > 0 && !remote) {
		if (fsize(mtf) == 0)
		    if (hp->h_subject == NOSTR)
			printf("No message, no subject; hope that's ok\n");
		    else
			printf("No message; hope that's ok\n");
		}
	if (debug) {
		printf("Recipients of message:\n");
		if (hp->h_to != NOSTR)
			printf(" \"%s\"", hp->h_to);
		if (hp->h_cc != NOSTR)
			printf(" \"%s\"", hp->h_cc);
		if (hp->h_bcc != NOSTR)
			printf(" \"%s\"", hp->h_bcc);
		printf("\n");
		fflush(stdout);
		return;
	}
	rewind(mtf);
	if (ml_init(YES, NO, NOSTR, hp->h_subject) != OK)
		goto topdog;
	if (!isnull(hp->h_to) && ml_adr(hp->h_to) != OK)
		goto topdog;
	if (!isnull(hp->h_cc) && ml_cc() != OK || ml_adr(hp->h_cc) != OK)
		goto topdog;
	if (!isnull(hp->h_bcc) && ml_adr(hp->h_bcc) != OK)
		goto topdog;
	if (ml_aend() != OK)
		goto topdog;
	if (ml_tinit() != OK)
		goto topdog;
	if (ml_file(mtf) != OK)
		goto topdog;
	if (ml_end(OK) != OK)
		goto topdog;
	rewind(mtf);
	if ((mtf = infix(hp, mtf)) == NULL) {
		fprintf(stderr, ". . . message lost, sorry.\n");
		return;
	}

	rewind(mtf);
	if ((cp = value("record")) != NOSTR)
		savemail(expand(cp), mtf);

		if (!stat(POSTAGE, &sbuf))
			if ((postage = fopen(POSTAGE, "a")) != NULL) {
				fprintf(postage, "%s %d %d\n", myname,
				    count(to), fsize(mtf));
				fclose(postage);
			}
out:
	fclose(mtf);
	return;
}

#ifdef NOT_USED
/*
 * Fix the header by glopping all of the expanded names from
 * the distribution list into the appropriate fields.
 * If there are any ARPA net recipients in the message,
 * we must insert commas, alas.
 */

fixhead(hp, tolist)
	struct header *hp;
	struct name *tolist;
{
	register int f;
	register struct name *np;

	for (f = 0, np = tolist; np != NIL; np = np->n_flink)
		if (any('@', np->n_name)) {
			f |= GCOMMA;
			break;
		}

	if (debug && f & GCOMMA)
		fprintf(stderr, "Should be inserting commas in recip lists\n");
	hp->h_to = detract(tolist, GTO|f);
	hp->h_cc = detract(tolist, GCC|f);
}
#endif NOT_USED

/*
 * Prepend a header in front of the collected stuff
 * and return the new file.
 */

FILE *
infix(hp, fi)
	struct header *hp;
	FILE *fi;
{
	extern char tempMail[];
	register FILE *nfo, *nfi;
	register int c;

	rewind(fi);
	if ((nfo = fopen(tempMail, "w")) == NULL) {
		perror(tempMail);
		return(fi);
	}
	if ((nfi = fopen(tempMail, "r")) == NULL) {
		perror(tempMail);
		fclose(nfo);
		return(fi);
	}
	remove(tempMail);
	puthead(hp, nfo, GTO|GSUBJECT|GCC|GNL);
	c = getc(fi);
	while (c != EOF) {
		putc(c, nfo);
		c = getc(fi);
	}
	if (ferror(fi)) {
		perror("read");
		return(fi);
	}
	fflush(nfo);
	if (ferror(nfo)) {
		perror(tempMail);
		fclose(nfo);
		fclose(nfi);
		return(fi);
	}
	fclose(nfo);
	fclose(fi);
	rewind(nfi);
	return(nfi);
}

/*
 * Dump the to, subject, cc header on the
 * passed file buffer.
 */

puthead(hp, fo, w)
	struct header *hp;
	FILE *fo;
{
	register int gotcha;

	gotcha = 0;
	if (hp->h_to != NOSTR && w & GTO)
		fmt("To: ", hp->h_to, fo), gotcha++;
	if (hp->h_subject != NOSTR && w & GSUBJECT)
		fprintf(fo, "Subject: %s\n", hp->h_subject), gotcha++;
	if (hp->h_cc != NOSTR && w & GCC)
		fmt("Cc: ", hp->h_cc, fo), gotcha++;
	if (hp->h_bcc != NOSTR && w & GBCC)
		fmt("Bcc: ", hp->h_bcc, fo), gotcha++;
	if (gotcha && w & GNL)
		putc('\n', fo);
}

/*
 * Format the given text to not exceed 72 characters.
 */

fmt(str, txt, fo)
	register char *str, *txt;
	register FILE *fo;
{
	register int col;
	register char *bg, *bl, *pt, ch;

	col = strlen(str);
	if (col)
		fprintf(fo, "%s", str);
	pt = bg = txt;
	bl = 0;
	while (*bg) {
		pt++;
		if (++col >72) {
			if (!bl) {
				bl = bg;
				while (*bl && !isspace(*bl))
					bl++;
			}
			if (!*bl)
				goto finish;
			ch = *bl;
			*bl = '\0';
			fprintf(fo, "%s\n    ", bg);
			col = 4;
			*bl = ch;
			pt = bg = ++bl;
			bl = 0;
		}
		if (!*pt) {
finish:
			fprintf(fo, "%s\n", bg);
			return;
		}
		if (isspace(*pt))
			bl = pt;
	}
}

/*
 * Save the outgoing mail on the passed file.
 */

savemail(name, fi)
	char name[];
	FILE *fi;
{
	register FILE *fo;
	register int c;
	time_t time();
	time_t now;
	char *ctime();
	char *n;

	if ((fo = fopen(name, "a")) == NULL) {
		perror(name);
		return;
	}
	time(&now);
	n = rflag;
	if (n == NOSTR)
		n = myname;
	fputs(delim1, fo);
	fprintf(fo, "From %s %s", n, ctime(&now));
	rewind(fi);
	for (c = getc(fi); c != EOF; c = getc(fi))
		putc(c, fo);
	fprintf(fo, "\n");
	fputs(delim2, fo);
	fflush(fo);
	if (ferror(fo))
		perror(name);
	fclose(fo);
}
intf(" \"%s\"", hp->h_cc);
		if (hp->h_bcc != NOSTR)
			printf(" \"%s\"", hp->h_bcc);
		printf("\n");
		fflush(stdout);
		return;
	}
	rewind(mtf);
	if (ml_init(YES, NO, NOSTR, hp->h_subject) != OK)
		goto topdog;
	if (!isnull(hp->h_to) && ml_adr(hp->h_to) != OK)
		goto topdog;
	if (!isnull(hp->h_cc) && ml_cc() != OK || ml_adr(hp->h_cc) != OK)
		goto topdog;
	if (!isnull(hp->h_bcc) && ml_adr(hp->h_bcc) != OK)
		goto topdog;
	if (ml_aend() != OK)
		goto topdog;
	if (ml_tinit() mmdf/uip/ucbmail/rcv.h   444      0     12         662  3670652723  10123 /*
 * Copyright (c) 1980 Regents of the University of California.
 * All rights reserved.  The Berkeley software License Agreement
 * specifies the terms and conditions for redistribution.
 *
 *	@(#)rcv.h	5.1 (Berkeley) 6/6/85
 */

/*
 * Mail -- a mail program
 *
 * This file is included by normal files which want both
 * globals and declarations.
 */

#ifdef	pdp11
#include <whoami.h>
#endif
#include "./def.h"
#include "./glob.h"
t(to), fsize(mtf));
				fclose(postage);
			}
out:
	fclose(mtf);
	return;
}

#mmdf/uip/ucbmail/sigretro.c   444      0     12       16561  3620510637  11217 /*
 * Copyright (c) 1980 Regents of the University of California.
 * All rights reserved.  The Berkeley software License Agreement
 * specifies the terms and conditions for redistribution.
 */

#ifndef lint
static char *sccsid = "@(#)sigretro.c	5.2 (Berkeley) 6/21/85";
#endif not lint

#include <signal.h>
#include <errno.h>
#include <setjmp.h>
#include "./sigretro.h"

/*
 * Retrofit new signal interface to old signal primitives.
 * Supported routines:
 *	sigsys(sig, func)
 *	sigset(sig, func)
 *	sighold(sig)
 *	sigrelse(sig)
 *	sigignore(sig)
 *	sigpause(sig)
 * Also,
 *	sigchild()
 *		to set all held signals to ignored signals in the
 *		child process after fork(2)
 */

typedef	int	(*sigtype)();

sigtype	sigdisp(), sighold(), sigignore();

/*
 * The following helps us keep the extended signal semantics together.
 * We remember for each signal the address of the function we're
 * supposed to call.  s_func is SIG_DFL / SIG_IGN if appropriate.
 */
struct sigtable {
	sigtype	s_func;			/* What to call */
	int	s_flag;			/* Signal flags; see below */
} sigtable[NSIG + 1];

/*
 * Signal flag values.
 */
#define	SHELD		1		/* Signal is being held */
#define	SDEFER		2		/* Signal occured while held */
#define	SSET		4		/* s_func is believable */
#define	SPAUSE		8		/* are pausing, waiting for sig */

jmp_buf	_pause;				/* For doing sigpause() */

/*
 * Approximate sigsys() system call
 * This is almost useless since one only calls sigsys()
 * in the child of a vfork().  If you have vfork(), you have new signals
 * anyway.  The real sigsys() does all the stuff needed to support
 * the real sigset() library.  We don't bother here, assuming that
 * you are either ignoring or defaulting a signal in the child.
 */
sigtype
sigsys(sig, func)
	sigtype func;
{
	sigtype old;

	old = sigdisp(sig);
	signal(sig, func);
	return(old);
}

/*
 * Set the (permanent) disposition of a signal.
 * If the signal is subsequently (or even now) held,
 * the action you set here can be enabled using sigrelse().
 */
sigtype
sigset(sig, func)
	sigtype func;
{
	sigtype old;
	int _sigtramp();
	extern int errno;

	if (sig < 1 || sig > NSIG) {
		errno = EINVAL;
		return(BADSIG);
	}
	old = sigdisp(sig);
	/*
	 * Does anyone actually call sigset with SIG_HOLD!?
	 */
	if (func == SIG_HOLD) {
		sighold(sig);
		return(old);
	}
	sigtable[sig].s_flag |= SSET;
	sigtable[sig].s_func = func;
	if (func == SIG_DFL) {
		/*
		 * If signal has been held, must retain
		 * the catch so that we can note occurrance
		 * of signal.
		 */
		if ((sigtable[sig].s_flag & SHELD) == 0)
			signal(sig, SIG_DFL);
		else
			signal(sig, _sigtramp);
		return(old);
	}
	if (func == SIG_IGN) {
		/*
		 * Clear pending signal
		 */
		signal(sig, SIG_IGN);
		sigtable[sig].s_flag &= ~SDEFER;
		return(old);
	}
	signal(sig, _sigtramp);
	return(old);
}

/*
 * Hold a signal.
 * This CAN be tricky if the signal's disposition is SIG_DFL.
 * In that case, we still catch the signal so we can note it
 * happened and do something crazy later.
 */
sigtype
sighold(sig)
{
	sigtype old;
	extern int errno;

	if (sig < 1 || sig > NSIG) {
		errno = EINVAL;
		return(BADSIG);
	}
	old = sigdisp(sig);
	if (sigtable[sig].s_flag & SHELD)
		return(old);
	/*
	 * When the default action is required, we have to
	 * set up to catch the signal to note signal's occurrance.
	 */
	if (old == SIG_DFL) {
		sigtable[sig].s_flag |= SSET;
		signal(sig, _sigtramp);
	}
	sigtable[sig].s_flag |= SHELD;
	return(old);
}

/*
 * Release a signal
 * If the signal occurred while we had it held, cause the signal.
 */
sigtype
sigrelse(sig)
{
	sigtype old;
	extern int errno;
	int _sigtramp();

	if (sig < 1 || sig > NSIG) {
		errno = EINVAL;
		return(BADSIG);
	}
	old = sigdisp(sig);
	if ((sigtable[sig].s_flag & SHELD) == 0)
		return(old);
	sigtable[sig].s_flag &= ~SHELD;
	if (sigtable[sig].s_flag & SDEFER)
		_sigtramp(sig);
	/*
	 * If disposition was the default, then we can unset the
	 * catch to _sigtramp() and let the system do the work.
	 */
	if (sigtable[sig].s_func == SIG_DFL)
		signal(sig, SIG_DFL);
	return(old);
}

/*
 * Ignore a signal.
 */
sigtype
sigignore(sig)
{

	return(sigset(sig, SIG_IGN));
}

/*
 * Pause, waiting for sig to occur.
 * We assume LUSER called us with the signal held.
 * When we got the signal, mark the signal as having
 * occurred.  It will actually cause something when
 * the signal is released.
 *
 * This is probably useless without job control anyway.
 */
sigpause(sig)
{
	extern int errno;

	if (sig < 1 || sig > NSIG) {
		errno = EINVAL;
		return;
	}
	sigtable[sig].s_flag |= SHELD|SPAUSE;
	if (setjmp(_pause) == 0)
		pause();
	sigtable[sig].s_flag &= ~SPAUSE;
	sigtable[sig].s_flag |= SDEFER;
}

/*
 * In the child process after fork(2), set the disposition of all held
 * signals to SIG_IGN.  This is a new procedure not in the real sigset()
 * package, provided for retrofitting purposes.
 */
sigchild()
{
	register int i;

	for (i = 1; i <= NSIG; i++)
		if (sigtable[i].s_flag & SHELD)
			signal(i, SIG_IGN);
}

/*
 * Return the current disposition of a signal
 * If we have not set this signal before, we have to
 * ask the system
 */
sigtype
sigdisp(sig)
{
	extern int errno;
	sigtype old;

	if (sig < 1 || sig > NSIG) {
		errno = EINVAL;
		return(BADSIG);
	}
	/*
	 * If we have no knowledge of this signal,
	 * ask the system, then save the result for later.
	 */
	if ((sigtable[sig].s_flag & SSET) == 0) {
		old = signal(sig, SIG_IGN);
		sigtable[sig].s_func = old;
		sigtable[sig].s_flag |= SSET;
		signal(sig, old);
		return(old);
	}
	/*
	 * If we have set this signal before, then sigset()
	 * will have been careful to leave something meaningful
	 * in s_func.
	 */
	return(sigtable[sig].s_func);
}

/*
 * The following routine gets called for any signal
 * that is to be trapped to a user function.
 */
_sigtramp(sig)
{
	extern int errno;
	sigtype old;

	if (sig < 1 || sig > NSIG) {
		errno = EINVAL;
		return;
	}

top:
	old = signal(sig, SIG_IGN);
	/*
	 * If signal being paused on, wakeup sigpause()
	 */
	if (sigtable[sig].s_flag & SPAUSE)
		longjmp(_pause, 1);
	/*
	 * If signal being held, mark its table entry
	 * so we can trigger it when signal released.
	 * Then just return.
	 */
	if (sigtable[sig].s_flag & SHELD) {
		sigtable[sig].s_flag |= SDEFER;
		signal(sig, _sigtramp);
		return;
	}
	/*
	 * If the signal is being ignored, just return.
	 * This would make SIGCONT more normal, but of course
	 * any system with SIGCONT also has the new signal pkg, so...
	 */
	if (sigtable[sig].s_func == SIG_IGN)
		return;
	/*
	 * If the signal is SIG_DFL, then we probably got here
	 * by holding the signal, having it happen, then releasing
	 * the signal.  I wonder if a process is allowed to send
	 * a signal to itself?
	 */
	if (sigtable[sig].s_func == SIG_DFL) {
		signal(sig, SIG_DFL);
		kill(getpid(), sig);
		/* Will we get back here? */
		return;
	}
	/*
	 * Looks like we should just cause the signal...
	 * We hold the signal for the duration of the user's
	 * code with the signal re-enabled.  If the signal
	 * happens again while in user code, we will recursively
	 * trap here and mark that we had another occurance
	 * and return to the user's trap code.  When we return
	 * from there, we can cause the signal again.
	 */
	sigtable[sig].s_flag &= ~SDEFER;
	sigtable[sig].s_flag |= SHELD;
	signal(sig, _sigtramp);
	(*sigtable[sig].s_func)(sig);
	/*
	 * If the signal re-occurred while in the user's routine,
	 * just go try it again...
	 */
	sigtable[sig].s_flag &= ~SHELD;
	if (sigtable[sig].s_flag & SDEFER)
		goto top;
}
return(old);
}

/*
 * Release a signal
 * If the signal occurred while we had it held, cause the signal.
 */
sigtype
sigrelse(sig)
{
	sigtype ommdf/uip/ucbmail/sigretro.h   444      0     12        1064  3620510637  11174 /*
 * Copyright (c) 1980 Regents of the University of California.
 * All rights reserved.  The Berkeley software License Agreement
 * specifies the terms and conditions for redistribution.
 *
 *	@(#)sigretro.h	5.1 (Berkeley) 6/6/85
 */

/*
 * Define extra stuff not found in signal.h
 */

#ifndef BADSIG
#define	BADSIG		(int (*)()) -1		/* Return value on error */
#endif BADSIG

#ifndef SIG_HOLD
#define	SIG_HOLD	(int (*)()) 3		/* Phony action to hold sig */
#endif SIG_HOLD

#ifndef sigmask
#define sigmask(s)	(1<<(s-1))		/* this exists in 4.3 */
#endif sigmask

nal again.
	 */
	sigtable[sig].s_flag &= ~SDEFER;
	sigtable[sig].s_flag |= SHELD;
	signal(sig, _sigtramp);
	(*sigtable[sig].s_func)(sig);
	/*
	 * If the signal re-occurred while in the user's routine,
	 * just go try it again...
	 */
	sigtable[sig].s_flag &= ~SHELD;
	if (sigtable[sig].s_flag & SDEFER)
		goto top;
}
return(old);
}

/*
 * Release a signal
 * If the signal occurred while we had it held, cause the signal.
 */
sigtype
sigrelse(sig)
{
	sigtype ommdf/uip/ucbmail/strings.c   444      0     12        4146  3670632342  11031 /*
 * Copyright (c) 1980 Regents of the University of California.
 * All rights reserved.  The Berkeley software License Agreement
 * specifies the terms and conditions for redistribution.
 */

#ifndef lint
static char *sccsid = "@(#)strings.c	5.2 (Berkeley) 6/21/85";
#endif not lint

/*
 * Mail -- a mail program
 *
 * String allocation routines.
 * Strings handed out here are reclaimed at the top of the command
 * loop each time, so they need not be freed.
 */

#include "./rcv.h"

/*
 * Allocate size more bytes of space and return the address of the
 * first byte to the caller.  An even number of bytes are always
 * allocated so that the space will always be on a word boundary.
 * The string spaces are of exponentially increasing size, to satisfy
 * the occasional user with enormous string size requests.
 */

char *
salloc(size)
{
	register char *t;
	register int s;
	register struct strings *sp;
	int ind;

	/*
	 * calculate actual size required by incrementing s until it
	 * reaches a size that is a multiple of the required alignment.
	 */
	s = size;
	while (s & (ALIGNMENT - 1))
		s++;

	ind = 0;
	for (sp = &stringdope[0]; sp < &stringdope[NSPACE]; sp++) {
		if (sp->s_topFree == NOSTR && (STRINGSIZE << ind) >= s)
			break;
		if (sp->s_nleft >= s)
			break;
		ind++;
	}
	if (sp >= &stringdope[NSPACE])
		panic("String too large");
	if (sp->s_topFree == NOSTR) {
		ind = sp - &stringdope[0];
		sp->s_topFree = (char *) calloc(STRINGSIZE << ind,
		    (unsigned) 1);
		if (sp->s_topFree == NOSTR) {
			fprintf(stderr, "No room for space %d\n", index);
			panic("Internal error");
		}
		sp->s_nextFree = sp->s_topFree;
		sp->s_nleft = STRINGSIZE << ind;
	}
	sp->s_nleft -= s;
	t = sp->s_nextFree;
	sp->s_nextFree += s;
	return(t);
}

/*
 * Reset the string area to be empty.
 * Called to free all strings allocated
 * since last reset.
 */

sreset()
{
	register struct strings *sp;
	register int ind;

	if (noreset)
		return;
	minit();
	ind = 0;
	for (sp = &stringdope[0]; sp < &stringdope[NSPACE]; sp++) {
		if (sp->s_topFree == NOSTR)
			continue;
		sp->s_nextFree = sp->s_topFree;
		sp->s_nleft = STRINGSIZE << ind;
		ind++;
	}
}
SIG);
	}
	old = sigdisp(sig);
	if ((sigtable[sig].s_flag & SHELD) == 0)
		return(old);
	sigtable[sig].s_flag &= ~SHELD;
	if (sigtable[sig].s_flag & SDEFER)
		_sigtramp(sig);
	/*
	 * If disposition was the default, then we can unset the
	 * catch to _sigtramp() and let the system do the work.
	 */
	if (sigtable[sig].s_func == SIG_DFL)
		signal(sig, SIG_DFL);
	return(old);
}

/*
 * Ignore a signal.
 */
sigtypmmdf/uip/ucbmail/strings   444      0     12           0  3671107244  10510 mmdf/uip/ucbmail/next_address.c   444      0     12        1341  3620510637  12012 /*
 * This is take from "send/s_do.c".
 */

#include "./mmdf.h"

char *adrptr;

next_address (addr)
char    *addr;
{
    int     i = -1;               /* return -1 = end; 0 = empty         */

    for (;;)
	switch (*adrptr)
	{
	    default: 
		addr[++i] = *adrptr++;
		continue;

	    case '"':
		for (addr[++i] = *adrptr++; !isnull (*adrptr); )
		{
		    addr[++i] = *adrptr;
		    if (*adrptr++ == '"')
			break;
		}
		continue;

	    case '(':
		for (addr[++i] = *adrptr++; !isnull (*adrptr); )
		{
		    addr[++i] = *adrptr;
		    if (*adrptr++ == ')')
			break;
		}
		continue;

	    case '\n':
	    case ',': 
		addr[++i] = '\0';
		adrptr++;
		return (i);

	    case '\0': 
		if (i >= 0)
		    addr[++i] = '\0';
		return (i);
	}
}
t the string area to be empty.
 * Called to free all strings allocated
 * since last reset.
 */

sreset()
{
	register struct strings *sp;
	register int ind;

	if (noreset)
		return;
	minit();
	ind = 0;
	for (sp = &stringdope[0]; sp < &stringdope[NSPACE]; sp++) {
		if (sp->s_topFree == Nmmdf/uip/ucbmail/temp.c   444      0     12        3144  3670632340  10300 /*
 * Copyright (c) 1980 Regents of the University of California.
 * All rights reserved.  The Berkeley software License Agreement
 * specifies the terms and conditions for redistribution.
 */

#ifndef lint
static char *sccsid = "@(#)temp.c	5.2 (Berkeley) 6/21/85";
#endif not lint

#include "./rcv.h"

/*
 * Mail -- a mail program
 *
 * Give names to all the temporary files that we will need.
 */

char	tempMail[14];
char	tempQuit[14];
char	tempEdit[14];
char	tempSet[14];
char	tempResid[14];
char	tempMesg[14];

tinit()
{
	register char *cp;
	char uname[PATHSIZE];
	register int pid;

	pid = getpid();
	sprintf(tempMail, "/tmp/Rs%05d", pid);
	sprintf(tempResid, "/tmp/Rq%05d", pid);
	sprintf(tempQuit, "/tmp/Rm%05d", pid);
	sprintf(tempEdit, "/tmp/Re%05d", pid);
	sprintf(tempSet, "/tmp/Rx%05d", pid);
	sprintf(tempMesg, "/tmp/Rx%05d", pid);

	if (strlen(myname) != 0) {
		uid = getuserid(myname);
		if (uid == -1) {
			printf("\"%s\" is not a user of this system\n",
			    myname);
			exit(1);
		}
	}
	else {
		uid = getuid() & UIDMASK;
		if (username(uid, uname) < 0) {
			copy("ubluit", myname);
			if (rcvmode) {
				printf("Who are you!?\n");
				exit(1);
			}
		}
		else
			copy(uname, myname);
	}
	cp = value("HOME");
	if (cp == NOSTR)
		cp = ".";
	copy(cp, homedir);
	findmail();
	cp = copy(homedir, mbox);
	copy("/mbox", cp);
	cp = copy(homedir, mailrc);
	copy("/.mailrc", cp);
	cp = copy(homedir, deadletter);
	copy("/dead.letter", cp);
	if (debug) {
		printf("uid = %d, user = %s, mailname = %s\n",
		    uid, myname, mailname);
		printf("deadletter = %s, mailrc = %s, mbox = %s\n",
		    deadletter, mailrc, mbox);
	}
}
ssume LUSER called us with the signal held.
 * When we got the signal, mark the signal as having
 * occurred.  It will actually cause something when
 * the signal is released.
 *
 * This is probably useless without job control anyway.
 */
sigpause(sig)
{
	extern int errno;

	if (sig < 1 || sig > NSIG) {
		errno = EINVAL;
		return;
	}
	sigtable[sig].s_flag |= SHELD|SPAUSE;
	if (setjmp(_pause) == 0)
		pause();
mmdf/uip/ucbmail/tty.c   444      0     12       10542  3670644061  10176 /*
 * Copyright (c) 1980 Regents of the University of California.
 * All rights reserved.  The Berkeley software License Agreement
 * specifies the terms and conditions for redistribution.
 */

#ifndef lint
static char *sccsid = "@(#)tty.c	5.2 (Berkeley) 6/21/85";
#endif not lint

/*
 * Mail -- a mail program
 *
 * Generally useful tty stuff.
 */

#include "./rcv.h"

static	int	c_erase;		/* Current erase char */
static	int	c_kill;			/* Current kill char */
static	int	hadcont;		/* Saw continue signal */
static	jmp_buf	rewrite;		/* Place to go when continued */
#ifndef TIOCSTI
static	int	ttyset;			/* We must now do erase/kill */
#endif

/*
 * Read all relevant header fields.
 */

grabh(hp, gflags)
	struct header *hp;
{
	struct sgttyb ttybuf;
	int ttycont(), signull();
#ifndef TIOCSTI
	int (*savesigs[2])();
#endif
	int (*savecont)();
#ifndef TIOCSTI
	register int s;
#endif  TIOCSTI

# ifdef VMUNIX
	savecont = sigset(SIGCONT, signull);
# endif VMUNIX
#ifndef TIOCSTI
	ttyset = 0;
#endif
	if (gtty(fileno(stdin), &ttybuf) < 0) {
		perror("gtty");
		return;
	}
	c_erase = ttybuf.sg_erase;
	c_kill = ttybuf.sg_kill;
#ifndef TIOCSTI
	ttybuf.sg_erase = 0;
	ttybuf.sg_kill = 0;
	for (s = SIGINT; s <= SIGQUIT; s++)
		if ((savesigs[s-SIGINT] = sigset(s, SIG_IGN)) == SIG_DFL)
			sigset(s, SIG_DFL);
#endif
	if (gflags & GTO) {
#ifndef TIOCSTI
		if (!ttyset && hp->h_to != NOSTR)
			ttyset++, stty(fileno(stdin), &ttybuf);
#endif
		hp->h_to = readtty("To: ", hp->h_to);
		if (hp->h_to != NOSTR)
			hp->h_seq++;
	}
	if (gflags & GSUBJECT) {
#ifndef TIOCSTI
		if (!ttyset && hp->h_subject != NOSTR)
			ttyset++, stty(fileno(stdin), &ttybuf);
#endif
		hp->h_subject = readtty("Subject: ", hp->h_subject);
		if (hp->h_subject != NOSTR)
			hp->h_seq++;
	}
	if (gflags & GCC) {
#ifndef TIOCSTI
		if (!ttyset && hp->h_cc != NOSTR)
			ttyset++, stty(fileno(stdin), &ttybuf);
#endif
		hp->h_cc = readtty("Cc: ", hp->h_cc);
		if (hp->h_cc != NOSTR)
			hp->h_seq++;
	}
	if (gflags & GBCC) {
#ifndef TIOCSTI
		if (!ttyset && hp->h_bcc != NOSTR)
			ttyset++, stty(fileno(stdin), &ttybuf);
#endif
		hp->h_bcc = readtty("Bcc: ", hp->h_bcc);
		if (hp->h_bcc != NOSTR)
			hp->h_seq++;
	}
# ifdef VMUNIX
	sigset(SIGCONT, savecont);
# endif VMUNIX
#ifndef TIOCSTI
	ttybuf.sg_erase = c_erase;
	ttybuf.sg_kill = c_kill;
	if (ttyset)
		stty(fileno(stdin), &ttybuf);
	for (s = SIGINT; s <= SIGQUIT; s++)
		sigset(s, savesigs[s-SIGINT]);
#endif
}

/*
 * Read up a header from standard input.
 * The source string has the preliminary contents to
 * be read.
 *
 */

char *
readtty(pr, src)
	char pr[], src[];
{
	char ch, canonb[BUFSIZ];
	int c, signull();
	register char *cp, *cp2;

	fputs(pr, stdout);
	fflush(stdout);
	if (src != NOSTR && strlen(src) > BUFSIZ - 2) {
		printf("too long to edit\n");
		return(src);
	}
#ifndef TIOCSTI
	if (src != NOSTR)
		cp = copy(src, canonb);
	else
		cp = copy("", canonb);
	fputs(canonb, stdout);
	fflush(stdout);
#else
	cp = src == NOSTR ? "" : src;
	while (c = *cp++) {
		if (c == c_erase || c == c_kill) {
			ch = '\\';
			ioctl(0, TIOCSTI, &ch);
		}
		ch = c;
		ioctl(0, TIOCSTI, &ch);
	}
	cp = canonb;
	*cp = 0;
#endif
	cp2 = cp;
	while (cp2 < canonb + BUFSIZ)
		*cp2++ = 0;
	cp2 = cp;
	if (setjmp(rewrite))
		goto redo;
# ifdef VMUNIX
	sigset(SIGCONT, ttycont);
# endif VMUNIX
	clearerr(stdin);
	while (cp2 < canonb + BUFSIZ) {
		c = getc(stdin);
		if (c == EOF || c == '\n')
			break;
		*cp2++ = c;
	}
	*cp2 = 0;
# ifdef VMUNIX
	sigset(SIGCONT, signull);
# endif VMUNIX
	if (c == EOF && ferror(stdin) && hadcont) {
redo:
		hadcont = 0;
		cp = strlen(canonb) > 0 ? canonb : NOSTR;
		clearerr(stdin);
		return(readtty(pr, cp));
	}
#ifndef TIOCSTI
	if (cp == NOSTR || *cp == '\0')
		return(src);
	cp2 = cp;
	if (!ttyset)
		return(strlen(canonb) > 0 ? savestr(canonb) : NOSTR);
	while (*cp != '\0') {
		c = *cp++;
		if (c == c_erase) {
			if (cp2 == canonb)
				continue;
			if (cp2[-1] == '\\') {
				cp2[-1] = c;
				continue;
			}
			cp2--;
			continue;
		}
		if (c == c_kill) {
			if (cp2 == canonb)
				continue;
			if (cp2[-1] == '\\') {
				cp2[-1] = c;
				continue;
			}
			cp2 = canonb;
			continue;
		}
		*cp2++ = c;
	}
	*cp2 = '\0';
#endif
	if (equal("", canonb))
		return(NOSTR);
	return(savestr(canonb));
}

# ifdef VMUNIX
/*
 * Receipt continuation.
 */
ttycont()
{

	hadcont++;
	longjmp(rewrite, 1);
}
# endif VMUNIX

/*
 * Null routine to satisfy
 * silly system bug that denies us holding SIGCONT
 */
signull()
{}
lag |= SSET;
		signal(sig, old);
		return(old);
	}
	/*
	 * If we have set this signal before, then sigset()
	 * will have been careful to leave something meanmmdf/uip/ucbmail/v7.local.c   444      0     12        3546  3670632345  10773 /*
 *  V 7 . L O C A L . C 
 *
 *  EE/CIS Computer Lab
 *  Department of Computer and Information Sciences
 *  Department of Electrical Engineering
 *  University of Delaware
 *
 *  REVISION HISTORY:
 *
 *  $Revision: 1.4 $
 *
 *  $Log:	v7.local.c,v $
 * Revision 1.4  85/12/18  01:54:04  galvin
 * Create maildrop using MMDF default protection modes.
 * 
 * Revision 1.4  85/12/18  01:44:59  galvin
 * Create the mailbox using the MMDF default protection mode
 * as defined by sentprotect.
 * 
 * Revision 1.3  85/11/20  14:38:17  galvin
 * Change findmail to locate the maildrop via MMDF defaults.
 * 
 * Revision 1.2  85/11/17  21:33:07  galvin
 * Added comment header for revision history.
 * 
 *
 */

/*
 * Copyright (c) 1980 Regents of the University of California.
 * All rights reserved.  The Berkeley software License Agreement
 * specifies the terms and conditions for redistribution.
 */

#ifndef lint
static char *sccsid = "@(#)v7.local.c	5.2 (Berkeley) 6/21/85";
#endif not lint

/*
 * Mail -- a mail program
 *
 * Version 7
 *
 * Local routines that are installation dependent.
 */

#include "./rcv.h"
#include "./mmdf.h"

/*
 * Locate the user's mailbox file (ie, the place where new, unread
 * mail is queued).  In Version 7, it is in /usr/spool/mail/name.
 */

findmail()
{
	sprintf( mailname, "%s/%s",
		(mldfldir==0 || isnull(mldfldir[0])) ? homedir : mldfldir,
		(mldflfil==0 || isnull(mldflfil[0])) ? myname : mldflfil);
}

/*
 * Get rid of the queued mail.
 */

demail()
{

	if (value("keep") != NOSTR)
		close(creat(mailname, sentprotect));
	else {
		if (remove(mailname) < 0)
			close(creat(mailname, sentprotect));
	}
}

/*
 * Discover user login name.
 */

username(l_uid, namebuf)
	char namebuf[];
{
	register char *np;

	if (l_uid == getuid() && (np = getenv("USER")) != NOSTR) {
		strncpy(namebuf, np, PATHSIZE);
		return(0);
	}
	return(getname(l_uid, namebuf));
}
h(stdout);
#else
	cp = src == NOSTR ? "" : src;
	while (c = *cp++) {
		if (c == c_erase || c == c_kill) {
			ch = '\\';
			ioctl(0, TIOCSTI, &ch);
		}
		cmmdf/uip/ucbmail/v7.local.h   444      0     12        4771  3670652721  11001 /*
 *  V 7 . L O C A L . H 
 *
 *  EE/CIS Computer Lab
 *  Department of Computer and Information Sciences
 *  Department of Electrical Engineering
 *  University of Delaware
 *
 *  REVISION HISTORY:
 *
 *  $Revision: 1.6 $
 *
 *  $Log:	v7.local.h,v $
 * Revision 1.6  86/01/14  16:33:59  galvin
 * Add DAYLIGHT define for use by smtpdate().
 * 
 * Revision 1.5  86/01/05  18:01:36  galvin
 * Add discovered option CANTELL.
 * 
 * Revision 1.4  85/12/18  03:28:31  galvin
 * Undef CANLOCK because we use MMDF locking.
 * 
 * Revision 1.3  85/11/16  14:49:49  galvin
 * Undef SENDMAIL and move a comment to where it belongs.
 * 
 * Revision 1.2  85/11/16  14:46:33  galvin
 * Added comment header for revision history.
 * 
 *
 */

/*
 * Copyright (c) 1980 Regents of the University of California.
 * All rights reserved.  The Berkeley software License Agreement
 * specifies the terms and conditions for redistribution.
 *
 *	@(#)v7.local.h	5.1 (Berkeley) 6/6/85
 */

/*
 * Declarations and constants specific to an installation.
 *
 * Vax/Unix version 7.
 */

#define	GETHOST				/* System has gethostname syscall */
#ifdef	GETHOST
#define	LOCAL		EMPTYID		/* Dynamically determined local host */
#else   GETHOST
#define	LOCAL		'*'		/* Local host id */
#endif	GETHOST

/* fine	MAIL		"/bin/mail"	/* Name of mail sender */
/* fine SENDMAIL	"/usr/lib/sendmail"
					/* Name of classy mail deliverer */
#define	EDITOR		"/usr/ucb/ex"	/* Name of text editor */
#define	VISUAL		"/usr/ucb/vi"	/* Name of display editor */
#define	SHELL		"/bin/csh"	/* Standard shell */
#define	MORE		"/usr/ucb/more"	/* Standard output pager */
#define	HELPFILE	"/usr/lib/Mail.help"
					/* Name of casual help file */
#define	THELPFILE	"/usr/lib/Mail.help.~"
					/* Name of casual tilde help */
#define	POSTAGE		"/usr/adm/maillog"
					/* Where to audit mail sending */
#define	UIDMASK		0177777		/* Significant uid bits */
#define	MASTER		"/usr/lib/Mail.rc"
#define	APPEND				/* New mail goes to end of mailbox */
/* fine CANLOCK				/* Locking protocol actually works */
/* fine	UTIME				/* System implements utime(3C) */
/* UTIMES is preferred if you have it.  */
#define UTIMES                          /* System implements utimes(2) */
#define CANTELL				/* System implements ftell(3S) */
#define DAYLIGHT	1		/* 1 if use daylight, else 0 */

#ifdef V4_2BSD
#ifndef VMUNIX
#define VMUNIX
#endif  VMUNIX
#endif V4_2BSD

#ifdef V4_1BSD
#ifndef VMUNIX
#define	VMUNIX
#endif  VMUNIX
#endif V4_1BSD

#ifndef VMUNIX
#include "./sigretro.h"			/* Retrofit signal defs */
#endif VMUNIX
MDF locmmdf/uip/ucbmail/vars.c   444      0     12        5431  3670652015  10310 /*
 * Copyright (c) 1980 Regents of the University of California.
 * All rights reserved.  The Berkeley software License Agreement
 * specifies the terms and conditions for redistribution.
 */

#ifndef lint
static char *sccsid = "@(#)vars.c	5.2 (Berkeley) 6/21/85";
#endif not lint

#include "./rcv.h"

/*
 * Mail -- a mail program
 *
 * Variable handling stuff.
 */

/*
 * Assign a value to a variable.
 */

assign(name, val)
	char name[], val[];
{
	register struct var *vp;
	register int h;

	h = hash(name);
	vp = lookup(name);
	if (vp == NOVAR) {
		vp = (struct var *) calloc(sizeof *vp, 1);
		vp->v_name = vcopy(name);
		vp->v_link = variables[h];
		variables[h] = vp;
	}
	else
		vfree(vp->v_value);
	vp->v_value = vcopy(val);
}

/*
 * Free up a variable string.  We do not bother to allocate
 * strings whose value is "" since they are expected to be frequent.
 * Thus, we cannot free same!
 */

vfree(cp)
	register char *cp;
{
	if (!equal(cp, ""))
		cfree(cp);
}

/*
 * Copy a variable value into permanent (ie, not collected after each
 * command) space.  Do not bother to alloc space for ""
 */

char *
vcopy(str)
	char str[];
{
	register char *top, *cp, *cp2;

	if (equal(str, ""))
		return("");
	if ((top = calloc((unsigned) (strlen(str)+1), 1)) == NULL)
		panic ("Out of memory");
	cp = top;
	cp2 = str;
	while (*cp++ = *cp2++)
		;
	return(top);
}

/*
 * Get the value of a variable and return it.
 * Look in the environment if its not available locally.
 */

char *
value(name)
	char name[];
{
	register struct var *vp;

	if ((vp = lookup(name)) == NOVAR)
		return(getenv(name));
	return(vp->v_value);
}

/*
 * Locate a variable and return its variable
 * node.
 */

struct var *
lookup(name)
	char name[];
{
	register struct var *vp;
	register int h;

	h = hash(name);
	for (vp = variables[h]; vp != NOVAR; vp = vp->v_link)
		if (equal(vp->v_name, name))
			return(vp);
	return(NOVAR);
}

/*
 * Locate a group name and return it.
 */

struct grouphead *
findgroup(name)
	char name[];
{
	register struct grouphead *gh;
	register int h;

	h = hash(name);
	for (gh = groups[h]; gh != NOGRP; gh = gh->g_link)
		if (equal(gh->g_name, name))
			return(gh);
	return(NOGRP);
}

/*
 * Print a group out on stdout
 */

printgroup(name)
	char name[];
{
	register struct grouphead *gh;
	register struct group *gp;

	if ((gh = findgroup(name)) == NOGRP) {
		printf("\"%s\": not a group\n", name);
		return;
	}
	printf("%s\t", gh->g_name);
	for (gp = gh->g_list; gp != NOGE; gp = gp->ge_link)
		printf(" %s", gp->ge_name);
	printf("\n");
}

/*
 * Hash the passed string and return an index into
 * the variable or group hash table.
 */

hash(name)
	char name[];
{
	register unsigned h;
	register char *cp;

	for (cp = name, h = 0; *cp; h = (h << 2) + *cp++)
		;
	if ((int) h < 0)
		h = -h;
	if ((int) h < 0)
		h = 0;
	return(h % HSHSIZE);
}
isfy
 * silly system bug that denies us holding SIGCONT
 */
signull()
{}
lag |= SSET;
		signal(sig, old);
		return(old);
	}
	/*
	 * If we have set this signal before, then sigset()
	 * will have been careful to leave something meanmmdf/uip/ucbmail/version.c   444      0     12        1007  3670632326  11020 /*
 * Copyright (c) 1980 Regents of the University of California.
 * All rights reserved.  The Berkeley software License Agreement
 * specifies the terms and conditions for redistribution.
 */

#ifndef lint
static char *sccsid = "@(#)version.c	5.2 (Berkeley) 6/21/85";
#endif not lint

/*
 * Just keep track of the date/sid of this version of Mail.
 * Load this file first to get a "total" Mail version.
 */
#ifndef lint
static	char	*SccsID = "@(#)UCB Mail Version 5.2 (6/21/85)";
#endif
char	*version = "5.2 6/21/85";
okup(name);
	if (vp == NOVAR) {
		vp = (struct var *) calloc(sizeof *vp, 1);
		vp->v_name = vcopy(name);
		vp->v_link = variables[h];
		variables[h] = vp;
	}
	else
		vfree(vp->v_value);
	vp->v_value = vcopy(val);
}

/*
 * Free up a variable string.  We do not bother to allocate
 * strings whose value is "" since they are expected to be frequent.
 * Thus, we cannot free same!
 */

vfree(cp)
	register char *cp;
{
	if (!equal(cp, ""))
		cfree(cp);
}

/*
 * Copy a variable value into permanent (ie, not cmmdf/uip/unsupported/   755      0     12           0  3635165373  10066 mmdf/uip/unsupported/README   444      0     12         772  3623253710  11005 These are some useful programs that are not integrated into
the standard MMDF environment.  In particular, they do not use
the runtime tailoring or standarm MMDF library (values are
hardcoded).  However, they would be simple to "bring into the
fold", given a little time, and they may be useful as they
stand to many site.  Read them first and understand what you
are getting.

				-Doug-

				Doug Kingston
				Centrum voor Wiskunde en Informatica
				Kruislaan 413
				1098 SJ Amsterdam, The Netherlands
.2 6/2mmdf/uip/unsupported/autores.8   444      0     12        2072  3631175250  11714 .TH AUTORES 8 "13/2/86"
.SH NAME
autores \- Automatic response system for mmdf
.SH SYNOPSIS
In the mmdf alias files add
.br
information:mmdf|/usr/lib/mmdf/autores "$(sender)"
.br
or
namedalias:mmdf|/usr/lib/mmdf/autores "$(sender)" filename
.SH DESCRIPTION
.I Autores
provides a simple automatic answering service for the MMDF mail system.
Users request information by
mailing to the alias which is used for the service (`information' at 
UKC)
supplying a `Subject:' line which maps onto
a stored file name.
The file is returned to the user in the mail.
A single named alias can be used to request a single file by supplying
a second parameter in the alias file.
In this case the body of the mail
message is ignored.
.PP
If the file starts with a tilde (`~'), then the first line of the file
is takes to be the subject line of the returned message.
.PP
All actions are stored on a logging file 
.BR /usr/adm/log/information .
.SH FILES
.ta \w'/usr/adm/log/information   'u
/usr/lib/mmdf/auto/...	Source files
.br
/usr/adm/log/information	Logging file
.SH AUTHOR
Peter Collinson UKC
*) calloc(sizeof *vp, 1);
		vp->v_name = vcopy(name);
		vp->v_link = variables[h];
		variables[h] = vp;
	}
	else
		vfree(vp->v_value);
	vp->v_value = vcopy(val);
}

/*
 * Free up a variable string.  We do not bother to allocate
 * strings whose value is "" since they are expected to be frequent.
 * Thus, we cannot free same!
 */

vfree(cp)
	register char *cp;
{
	if (!equal(cp, ""))
		cfree(cp);
}

/*
 * Copy a variable value into permanent (ie, not cmmdf/uip/unsupported/autores.c   444      0     12       26246  3631175060  12017 #ifndef lint
static char sccsid[] = "@(#)autores.c	1.6 (UKC) 14/2/86";
#endif  lint
/***

* program name:
	autores.c
* function:
	Send an automatic reply to mail depending on the subject field
	The subject field is used to select a file in 
	/usr/lib/mmdf/auto which is interpreted as a mail item to send out
	Subject lines starting with 'Re:' are ignored
	If the first line of the file starts with '~', it is taken
	to be the Subject line for the outgoing mail.
	A line should be installed in the mmdf aliases file of the form
		information:mmdf|/usr/lib/mmdf/autores "$(sender)"
	If there are two parameters, the subject line is ignored and the
	second parameter is used to select a file
* switches:
	Called with the return address
	and optionally a named file
* libraries used:
	standard
* compile time parameters:
	cc -o autores autores.c
* history:
	Written Feb 1986
	Peter Collinson UKC

***/
#include <stdio.h>
#include <ctype.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sys/time.h>
#include <sys/timeb.h>
#include <sys/wait.h>

/*
 *	Source directory
 */
char *srcdir = "/usr/lib/mmdf/auto";

/*
 *	who to reply to
 */
char *sender; 

/*
 *	system log file
 */
char *logfile = "/usr/adm/log/information";
int	logfd = -1;
int	logpid;

/*
 *	file to look for
 */
char *command;
int	fromarg;
char	submit[]	= "/usr/lib/mmdf/submit";
char	subargs[]	= "-vmltnqxto,*";
char	signature[]	= "information@Ukc";

int	pipein[2];	/* pipes for communication */
int	pipeout[2];

FILE	*pout;		/* re-opened FILE pointer for pipeout */
FILE	*pin;		/* re-opened FILE pointer for pipein */

/*
 *	error response line
 */
char	errline[BUFSIZ];
int	senderror;

char	stdinfo[] =	/* Put out in each message */
"\n\
An index of the available information can be obtained by mailing to\n\
information@ukc.ac.uk with a Subject: line containing the word index\n\
i.e.\n\
	Subject: index\n\
\n";

/*
 *	Data structure used to scan and recognise an inbound mail message
 *	All this is overkill but I had the code hanging around and I am
 *	never one to re-write stuff which works
 */
typedef struct
{	char	*header;
	char	*dataline;
	int	hadthis;
} Header_line;

Header_line header_line[] =
{	{ "subject", },
	{ 0, }
};
#define HSUBJECT	0


main(argc, argv)
char **argv;
{	openlog();	
	if (argc == 3)
	{	command = argv[2];
		fromarg++;
	}
	else
	if (argc != 2)
		fatal("Usage: autores sender\n");
	sender = argv[1];
	if (chdir(srcdir) < 0)
		fatal("No source directory\n");	
	/*
	 *	Mail header decoding
	 *	all we want is the subject line
	 */
	if (fromarg == 0)
	{	if (readheader(stdin)) /* if not OK */
			command = NULL;
	}
	clear_input(stdin);
	if (command)
		send_info(command);
	if (senderror)
		send_error();
	closelog();
	exit(0);
}

/*
 *	Read an incoming mail item looking for the subject line
 *	This all borrowed from another program
 */
readheader(fin)
FILE *fin;
{	register char *cp;
	char *index();

	if (get_header_lines(fin))
		return(1);
	if (header_line[HSUBJECT].hadthis == 0)
	{	usererror("No Subject: line found\n");
		return(1);
	}
	command = header_line[HSUBJECT].dataline;		
	/*
	 * look for Re: bits in lines
	 */
	while ((*command == 'R' || *command == 'r') &&
	       (command[1] == 'e' || command[1] == 'E' ) &&
	       (command[2] == ':'))
	{	log("Re: found request from %s ignored\n", sender);
		return(1);
	}
	return(0);
}

/*
 *	Scan the inbound mail header
 */
get_header_lines(fin)
FILE *fin;
{	register char *p;
	register char *startfield;
	register Header_line *hp;
	char	line[BUFSIZ];
	int	peekc;
	Header_line *look_header();
	char	*storestr();
	char	*storepair();
	char 	*index();
	
	for (;;)
	{	if (fgets(line, sizeof line - 1, fin) == NULL)
		{	usererror("Unexpected end of mail input file\n");
			return(1);
		}
		peekc = getc(fin);
		(void) ungetc(peekc, fin);
		if (line[0] == '\n')
			break;
		hp = (Header_line *)0;
		if (p = index(line, ':'))
		{	*p++ = '\0';
			if (hp = look_header(line))
			{	/* we want to know about this line */
				if (hp->hadthis++)
				{	/* we have had this line before */
					usererror("More than one %s header line found\n", hp->header);
					return(1);
				}
				/* clean up this line */
				if (*p == ' ') p++;
				startfield = p;
				p = index(startfield, '\n');
				if (p == NULL)
					line[BUFSIZ-1] = '\0';
				else
					*p = '\0';
				hp->dataline = storestr(startfield);
			}
		}
		/*
		 *	cope with continuation lines
		 */
		while (peekc == ' ' || peekc == '\t')
		{	if (fgets(line, sizeof line -1, fin) == NULL)
			{	usererror("Unexpected end of mail input file\n");
				return(1);
			}
			peekc = getc(fin);
			(void) ungetc(peekc, fin);
			/*
			 * if hp is set then we need to concatenate the
			 * new bit onto the old stored piece
			 */
			if (hp)
			{	if (p = index(line, '\n'))
					*p = '\0';
				else	line[BUFSIZ-1] = '\0';
				hp->dataline = storepair(hp->dataline, line, 1);
			}
		}
	}
	return(0);
}

/*
 *	scan the Header_line structure looking for a matching
 *	string which is a header - use equstr to match
 */
Header_line *
look_header(str)
char *str;
{	register Header_line *hp;
	for (hp = header_line; hp->header; hp++)
		if (equstr(str, hp->header) == 0)
			return(hp);
	return((Header_line *)0);
}

/*
 *	read very hard on the file descriptor throwing the data away
 */
clear_input(fin)
FILE *fin;
{	register c;
	while ((c = getc(fin)) != EOF);
}

/*
 *	Send information
 */
send_info(cmd)
register char *cmd;
{	register char *p;
	register bytes;
	char	line[BUFSIZ];
	FILE *fin;
	
	/*
	 *	first clean up the command
	 *	All must start with an alpha character
	 */
	while (!isalpha(*cmd))
		cmd++;
	for (p = cmd; *p; p++)
	{	if (isupper(*p))
			*p = tolower(*p);
		if (isspace(*p) || ! isprint(*p))
		{	*p = '\0';
			break;
		}
	}
	if ((fin = fopen(cmd, "r")) == NULL)
	{	usererror("No information on: %s\n", cmd);
		return;
	}
	log("%s requests %s\n", sender, cmd);
	/* Subject is the 1st line of source - assuming is starts with '~' */
	fgets(line, sizeof line, fin);
	init_mail();
	mail_header(cmd, line[0] == '~' ? &line[1] : NULL);
	fprintf(pout, "Thank you for your information request\n");
	fprintf(pout, stdinfo);
	fprintf(pout, "-----%s-----\n", cmd);
	/*
	 * replace initial line it it is not a comment
	 */
	if (line[0] != '~')
		fputs(line, pout);
	/*
	 *	now read the remainder and send to pipe
	 */
	while (bytes = fread(line, 1, sizeof line, fin))
		fwrite(line, 1, bytes, pout);
	(void) fclose(pout);
	mail_termination();
}

/*
 *	Send an error reply
 */
send_error()
{	init_mail();
	mail_header("Error response", NULL);
	if (command)
		fprintf(pout, "Your request for information with a subject of `%s'\n", command);
	else
		fprintf(pout, "Your request for information\n");
	fprintf(pout, "has failed. The reason was:\n\n%s\n", errline);
	fprintf(pout, stdinfo);
	(void) fclose(pout);
	mail_termination();
}

/*
 *	Initialise a pipe to submit
 */
init_mail()
{	register pid;
	register i;
	char	*subcmd;
	char	*rindex();
	char	*maildate();
		
	subcmd = rindex(submit, '/');
	if (subcmd)
		subcmd++;
	else
		subcmd = submit;
		
	(void) pipe(pipein);	/* parent will read from pipein[0] */
	(void) pipe(pipeout);	/* parent will write to pipeout[1] */
	
	if ((pid = fork()) < 0)
	{	fatal("Cannot create a process for mail submission\n");
		return;
	}
	if (pid == 0)
	{	/* child */
		/* stdin is pipeout[0] */
		/* stdout/stderr is pipein[1] */
		(void) close(pipeout[1]);
		(void) close(pipein[0]);
		(void) close(0);
		(void) dup(pipeout[0]);
		(void) close(pipeout[0]);
		(void) close(1);
		(void) dup(pipein[1]);
		(void) close(pipein[1]);
		(void) close(2);
		(void) dup(1);
		for (i = 3; i < 20; i++)
			(void) close(i);
		execl(submit, subcmd, subargs, 0);
		(void) close(0);
		(void) close(1);
		(void) close(2);
		exit(1);
	}
	/*
	 *	Parent
	 */
	(void) close(pipein[1]);
	(void) close(pipeout[0]);
	pout = fdopen(pipeout[1], "w");
	pin = fdopen(pipein[0], "r");
}

/*
 *	Deal with the termination of the mail process
 */
mail_termination()
{	char	logbuf[BUFSIZ];
	union	wait retstat;
	
	while(fgets(logbuf, sizeof logbuf, pin))
		log("%s", logbuf);
	(void) fclose(pin);
	
	if (wait(&retstat) > 0)
	{	if (retstat.w_retcode == 9 &&
			retstat.w_termsig == 0)
		{	log("successful submit\n");
			return;
		}
		log("Submit failed, returning %x %d\n", retstat, retstat);
	}
	log("Submit failed\n");
}

/*
 *	Do a mail header
 */
mail_header(cmd, subject)
char *cmd;
char *subject;
{
	fprintf(pout, "%s\n", signature);	/* special validation argument */
	fprintf(pout, "To: %s\n", sender);
 	fprintf(pout, "From: Information service <%s>\n", signature);
	if (subject)
		fprintf(pout, "Subject: Re: %s - %s", cmd, subject);
	else	fprintf(pout, "Subject: Re: %s\n", cmd);
	fprintf(pout, "Date: %s\n", maildate());
	fprintf(pout, "\n");
}

/*
 *	return a static string with the data in RFC822 format
 */
char *
maildate()
{
	static	char datbuf[64];
	static char *day[] = {
		"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
	};
	static char *month[] = {
		"Jan", "Feb", "Mar", "Apr", "May", "Jun",
		"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
	};
	struct timeb    timeb;
	extern char *timezone();
	extern struct tm  *localtime ();
 	register struct tm  *i;
		
	ftime(&timeb);
	i = localtime ((time_t *) & timeb.time);
	(void) sprintf(datbuf, "%s, %d %s %d %d:%02d:%02d %s",
		day[i -> tm_wday], i -> tm_mday, month[i -> tm_mon],
		i -> tm_year, i -> tm_hour, i -> tm_min, i -> tm_sec,
			timezone (timeb.timezone, i -> tm_isdst));
	return(datbuf);
}
	 
/*
 *	See if two strings are the same irrespective of case
 *	return 0 if equal, 1 otherwise
 */
equstr(a, b)
register char *a, *b;
{	register ca, cb;
	while (ca = *a)
	{	cb = *b;
		if (cb == 0)
			return(1);
		if (ca != cb)
		{	if (isupper(ca))
				ca = tolower(ca);
			if (isupper(cb))
				cb = tolower(cb);
			if (ca != cb)
				return(1);
		}
		a++;
		b++;
	}
	return(*b == '\0' ? 0 : 1);
}

/*
 *	string management routines
 */
char *
storestr(str)
char *str;
{	register nchs;
	char *area;
	char *malloc();
	
	nchs = strlen(str) + 1;
	if ((area = malloc((unsigned) nchs)) == NULL)
		fatal("No memory for string storage\n");
	bcopy(str, area, nchs);
	return (area);
}

/*
 *	Concatenate and store a pair of strings
 *	the last parameter is used as a mask to indicate that one or other
 *	of the paramaters can be free()'d
 */
char *
storepair(a, b, msk)
char *a;
char *b;
{	register nchs;
	register alen;
	register blen;
	char *area;

	alen = strlen(a);
	blen = strlen(b);
	nchs = alen + blen + 1;
	if ((area = malloc((unsigned) nchs)) == NULL)
		fatal("No memory for string storage\n");
	bcopy(a, area, alen);
	bcopy(b, &area[alen], blen+1);
	if (msk & 01)
		free(a);
	if (msk & 02)
		free(b);
	return(area);
}

/*
 *	set up an error reply line
 */
/*VARARGS1*/
usererror(fmt, a, b, c, d)
char *fmt;
{	(void) sprintf(errline, fmt, a, b, c, d);
	senderror++;
	log(fmt, a, b, c, d);

}

/*
 *	open the log file
 */
openlog()
{
	logpid = getpid();
	
	logfd = open(logfile, O_WRONLY|O_APPEND);

}

/*VARARGS1*/
log(fmt, a, b, c, d)
char *fmt;
{	char line[BUFSIZ];
	time_t ti;
	char *ctime();
	register hdrlen;
	
	if (logfd < 0)
		return;
	
	(void) time(&ti);
	(void) sprintf(line, "%6d %15.15s: ", logpid, ctime(&ti)+4);
	hdrlen = strlen(line);
	(void) sprintf(&line[hdrlen], fmt, a, b, c, d);
	write(logfd, line, strlen(line));
}

closelog()
{	if (logfd >= 0)
		close(logfd);
}

/*VARARGS1*/
fatal(fmt, a, b, c, d)
char *fmt;
{	log(fmt, a, b, c, d);
	closelog();
	exit(0);
}
is pipein[1] */
		(void) close(pipeout[1]);
		(void) close(pipein[0]);
		(void) close(0);
		(void) dup(pipeout[0]);
		(void) close(pipeout[0]);
		(void) close(1);
		(void) dup(pipein[1]);
		(void) close(pipein[1]);
		(void) close(2);
		(void) dup(1);
		for (i = 3; i < 20; i++)
			(void) close(i);
		execl(submit, subcmd, subargs, 0);
		(void) clmmdf/uip/unsupported/cvmbox.1   444      0     12         542  3623253717  11507 .TH CVMBOX 1
.SH NAME
cvmbox \- convert mailbox
.SH SYNOPSIS
.B cvmbox
file ...
.SH DESCRIPTION
.I Cvmbox
reads the argument files (standard input default), which should be
mailboxes in the old mailbox format, and converts them to the new mailbox
format.
The result is printed on standard output.
.SH DIAGNOSTICS
Should be more or less self-explanatory.
(&ti);
	(void) sprintf(line, "%6d %15.15s: ", logpid, ctime(&ti)+4);
	hdrlen = strlen(line);
	(void) sprintf(&line[hdrlen], fmt, a, b, c, d);
	write(logfd, limmdf/uip/unsupported/cvmbox.c   444      0     12       13423  3623253722  11627 /*
 *	SYNOPSIS
 *		cvmbox file ...
 *	DESCRIPTION
 *		This program expects its input (either the argument files
 *		or standard input) to be in the old mailbox format.  It
 *		writes to standard output a mailbox file in the new format.
 *		This program was based on a program called "mailsplit"
 *		written by Dick Grune, Vrije Universiteit, Amsterdam.
 *	AUTHOR	Sjoerd Mullender, Vrije Universiteit, Amsterdam
 *	VERSION	Fri Jan 31 13:05:35 MET 1986
 */

#include	<stdio.h>
extern char *sprintf();
extern char *strcat(), *strcpy();

char mail_delim[] = "\1\1\1\1\n";

char *weekdays[] = {
	"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", 0
};

char *months[] = {
	"Jan", "Feb", "Mar", "Apr", "May", "Jun",
	"Jul", "Aug", "Sep", "Oct", "Nov", "Dec", 0
};

char *word(), *search_text();

char *iname;			/* name of input file */
FILE *ifile;			/* its stream */
int lncnt;			/* its line count */
char date[40];			/* our version of the Date: line */
char from[1024];		/* our version of the From: line */
int date_present;		/* there was a Date: line in the header */
int from_present;		/* there was a From: line in the header */

main(argc, argv)
char *argv[];
{
	if (argc == 1) {
		iname = "stdin";
		ifile = stdin;
		lncnt = 0;
		process();
	}
	else
	while (argc > 1) {
		iname = argv[1];
		ifile = fopen(iname, "r");
		if (ifile == NULL)
			error("cannot open %s", iname);
		lncnt = 0;
		process();
		fclose(ifile);
		argc--, argv++;
	}
	
	return 0;
}

process()
{
	if (!get_new_line())		/* one-line read-ahead */
		return;
	do {
		date_present = 0;
		from_present = 0;
		fputs(mail_delim, stdout);
		copy_header();
		copy_letter();
		fputs(mail_delim, stdout);
	} while (is_header_line());
}

char line[1024];

int
get_new_line()
{
	if (fgets(line, sizeof line, ifile) == NULL)
		return 0;
	lncnt++;
	return 1;
}

int
is_header_line()
{
	return strncmp(line, "From ", 5) == 0;
}

copy_header()
{
	condense_From_line();
	make_date();
	while (get_new_line() && line[0] != '\n')
		copy_header_line();
	if (from_present)
		printf("Apparently-%s", from);
	else
		fputs(from, stdout);
	if (!date_present)
		fputs(date, stdout);
}

copy_header_line()
{
	int ch;
	
	if (strncmp(line, "Date: ", 6) == 0)
		date_present = 1;
	if (strncmp(line, "From: ", 6) == 0)
		from_present = 1;
	fputs(line, stdout);
}

copy_letter()
{
	do	fputs(line, stdout);
	while (get_new_line() && !is_header_line());
}

condense_From_line()
{
	/* will try to read successive >From lines and compose
	 * a summary line.
	 */
	char name[1024];
	char prefix[1024];
	int ch;
	
	dissect_line(name, prefix, date);
	while ((ch = getc(ifile)) == '>') {
		if (!get_new_line())
			error("Abrupt EOF", (char *)0);
		dissect_line(name, &prefix[strlen(prefix)], date);
	}
	ungetc(ch, ifile);		/* regrettable */
	sprintf(from, "From: %s%s\n", prefix, name);
}

dissect_line(nm, pr, dt)
	char *nm, *pr, *dt;
{
	/* dissects the line into a name, in nm, a prefix (from
	 * the 'remote from' part, in pr, and the date in dt,
	 * if dt is not NULL.
	 */
	char *wd = word(line, NULL);		/* skip From */
	char *rm = search_text(line, "remote from ");
	
	line[strlen(line) - 1] = '\0';		/* remove NL */
	wd = word(wd, nm);			/* the name */
	if (rm) {
		char *rn = word(word(rm, NULL), NULL);
		if (!rn)
			/*bad_format()*/;
		rn = word(rn, pr);		/* the prefix */
		if (rn)
			/*bad_format()*/;
		strcat(pr, "!");
		while (rm[-1] == ' ')
			*--rm = '\0';
	}
	else
		*pr ='\0';			/* no prefix */
	if (dt)
		strcpy(dt, wd);			/* the date */
}

make_date()
{
	/* Parses the date part of a From line and makes a Date: line out of
	 * it.  This date has the following format:
	 * DDD MMM d[d] hh:mm[:ss] yyyy [ZZZ]
	 * where DDD is the day of the week, MMM is the month, d[d] is the
	 * day of the month, hh:mm:ss is the time, yyyy is the year and ZZZ
	 * is the timezone.
	 */
	char timezone[10];
	int weekday;
	int year = 0, month = 1, day = 0;
	int hour = 0, min = 0, sec = 0;
	char *wd = date;
	int n;

	weekday = wordpos(wd, weekdays);
	if (weekday == 0)
		formaterr(iname, lncnt);
	wd = word(wd, NULL);
	
	month = wordpos(wd, months);
	if (month == 0)
		formaterr(iname, lncnt);
	wd = word(wd, NULL);
	
	if (sscanf(wd, "%d", &day) != 1)
		formaterr(iname, lncnt);
	wd = word(wd, NULL);
	
	if ((n = sscanf(wd, "%d:%d:%d", &hour, &min, &sec)) != 3 && n != 2)
		formaterr(iname, lncnt);
	if (n == 2)
		sec = 0;
	wd = word(wd, NULL);
	
	if ('A' <= *wd && *wd <= 'Z')		/* time zone indicator */
		wd = word(wd, timezone);
	else
		timezone[0] = 0;
	if (sscanf(wd, "%d", &year) != 1)
		formaterr(iname, lncnt);
	wd = word(wd, NULL);
	
	sprintf(date, "Date: %s, %d %s %d %02d:%02d:%02d %s\n",
		weekdays[weekday-1], day, months[month-1],
		year-1900, hour, min, sec, timezone);
}

char *
word(s, bf)
	char *s, *bf;
{
	/* returns the address of the first new word after s,
	 * or NULL otherwise.
	 * If bf is not 0, the word is copied to the buffer bf.
	 */
	if (!s) {
		if (bf)
			*bf = '\0';
		return NULL;
	}
	while (*s && *s != ' ' && *s != '\t') {
		if (bf)
			*bf++ = *s;
		s++;
	}
	if (bf)
		*bf = '\0';
	while (*s && (*s == ' ' || *s == '\t'))
		s++;
	if (!*s)
		return NULL;
	return s;
}

char *
search_text(ln, txt)
	char *ln, *txt;
{
	/* returns a pointer to the text txt in the array ln,
	 * or NULL otherwise.
	 */
	int length = strlen(txt);
	
	while (*ln) {
		if (strncmp(ln, txt, length) == 0)
			return ln;
		ln++;
	}
	return NULL;
}

int
wordpos(wd, lst)
	char *wd, *lst[];
{
	/* the index (starting at 1) of the word in the list or
	 * 0 otherwise
	 */
	int pos = 0;
	
	if (!wd)
		return 0;
	while (lst[pos] && strncmp(wd, lst[pos], strlen(lst[pos])) != 0)
		pos++;
	if (!lst[pos])
		return 0;
	return pos+1;
}

formaterr(inm, lc)
	char *inm;
{
	char buff[300];
	
	sprintf(buff, "\"%s\", line %d: improper date format", inm, lc);
	error("%s", buff);
}

error(fmt, str)
	char *fmt, *str;
{
	fprintf(stderr, fmt, str);
	fprintf(stderr, "\n");
	exit(1);
}
}

int
is_header_line()
{
	return strncmp(line, "From ", 5) == 0;
}

copy_header()
{
	condense_From_line();
	make_date();
	while (get_new_line() && line[0] != '\n')
		copy_header_line();
	if (from_present)
		printf("Apparently-%s", from)mmdf/uip/unsupported/autores.mk   444      0     12         253  3631175304  12133 CFLAGS=-O

autores:	autores.o
	cc -o autores $(CFLAGS) autores.o

clean:
	rm -f autores errs *.o

install:	autores
	install -s -m 700 -o mmdf -g cur autores /usr/lib/mmdf

}

error(fmt, str)
	char *fmt, *str;
{
	fprintf(stderr, fmt, str);
	fprintf(stderr, "\n");
	exit(1);
}
}

int
is_header_line()
{
	return strncmp(line, "From ", 5) == 0;
}

copy_header()
{
	condense_From_line();
	make_date();
	while (get_new_line() && line[0] != '\n')
		copy_header_line();
	if (from_present)
		printf("Apparently-%s", from)mmdf/testmmdf/   755      0     12           0  3635165676   6532 mmdf/testmmdf/log/   755      0     12           0  3635165306   7301 mmdf/testmmdf/log/setlogs   755      0     12         317  3620510575  10751 mv msg.log  omsg.log
mv chan.log ochan.log
mv ph.log   oph.log
mv ph.trn   oph.trn
cp /dev/null msg.log
cp /dev/null chan.log
cp /dev/null ph.log
cp /dev/null ph.trn
chmod 622 msg.log chan.log ph.log ph.trn
;
{
	fprintf(stderr, fmt, str);
	fprintf(stderr, "\n");
	exit(1);
}
}

int
is_header_line()
{
	return strncmp(line, "From ", 5) == 0;
}

copy_header()
{
	condense_From_line();
	make_date();
	while (get_new_line() && line[0] != '\n')
		copy_header_line();
	if (from_present)
		printf("Apparently-%s", from)mmdf/testmmdf/log/oph.log   644      0     12           0  3620510575  10561 mmdf/testmmdf/log/oph.trn   644      0     12           0  3620510575  10603 mmdf/testmmdf/log/msg.log   644      0     12           0  3620510575  10561 mmdf/testmmdf/log/chan.log   644      0     12           0  3620510575  10704 mmdf/testmmdf/log/omsg.log   644      0     12           0  3620510575  10740 mmdf/testmmdf/log/ochan.log   644      0     12           0  3620510575  11063 mmdf/testmmdf/log/ph.log   644      0     12           0  3620510575  10402 mmdf/testmmdf/log/ph.trn   644      0     12           0  3620510576  10425 mmdf/testmmdf/chans/   755      0     12           0  3635165306   7614 mmdf/testmmdf/lock/   755      0     12           0  3635165310   7443 mmdf/testmmdf/lock/home/   755      0     12           0  3635165315  10400 mmdf/testmmdf/lock/home/q.uucp/   755      0     12           0  3635165310  11606 mmdf/testmmdf/lock/home/q.smtp/   755      0     12           0  3635165311  11616 mmdf/testmmdf/lock/home/q.local/   755      0     12           0  3635165311  11725 mmdf/testmmdf/lock/home/q.list/   755      0     12           0  3635165312  11607 mmdf/testmmdf/lock/home/addr/   755      0     12           0  3635165313  11310 mmdf/testmmdf/lock/home/msg/   755      0     12           0  3635165314  11165 mmdf/testmmdf/lock/home/tmp/   755      0     12           0  3635165314  11177 mmdf/testmmdf/lock/home/q.brlnet/   755      0     12           0  3635165315  12125 mmdf/testmmdf/table/   755      0     12           0  3635165320   7603 mmdf/testmmdf/table/makearpa   555      0     12         443  3620510600  11343 #! /bin/sh
: This shell file makes a new smtp host table, based upon
: the NIC host table.
nictable -C < /etc/nic-table > NEWsmtp
mv -f smtp smtp.bak; mv -f NEWsmtp smtp

: Here, we make a new Arpa Domain table.
nictable -D < /etc/nic-table > NEWarpa
mv -f arpa arpa.bak; mv -f NEWarpa arpa
_line()
{
	return strncmp(line, "From ", 5) == 0;
}

copy_header()
{
	condense_From_line();
	make_date();
	while (get_new_line() && line[0] != '\n')
		copy_header_line();
	if (from_present)
		printf("Apparently-%s", from)mmdf/testmmdf/table/aliases   444      0     12        6272  3620510601  11227 a1-hacks	</d/mike/.lists/a1-hacks
acst	mike,ron,dpk,gwyn,kermit,steve
alpha_1	</d/mike/.lists/alpha_1
alpha_1-group	alpha_1-group@list-processor
alpha_1-group-out	</d/mike/.lists/alpha_1-group
ged	ged-outbound@list-processor
ged-outbound	</d/mike/.lists/ged
ged-request	mike
gurus	</d/mike/.lists/gurus
hep-unix	hep-unix-outbound@list-processor
hep-unix-outbound	</d/mike/.lists/hep-unix
hep-unix-request	mike
info-micro	info-micro-outbound@list-processor
info-micro-outbound	</d/ron/.rbn/info-micro
info-micro-request	rbn
info-music	info-music-outbound@list-processor
info-music-outbound	</d/ron/.rbn/info-music
info-music-request	rbn
XXinfo-unix	</d/mike/.wiz/.info-master
info-unix-from-usenet	info-unix-arpa
info-unix-arpa	info-unix-outbound@list-processor
XXinfo-unix-outbound	</d/mike/.wiz/.info
XXinfo-unix-request	unix-wizards-request
lpr	osg@brl-bmd
mmdf	</usr/src/newbrl/mmdf/testmmdf/table/mmdflist
tcp-outbound	</d/mike/.tcp/.list
testlist	testlist-outbound@list-processor
testlist-outbound	</d/dpk/testlist
testlist-request	dpk
XXunix-wizards	</d/mike/.wiz/.wiz-master
XXunix-wizards-from-usenet	unix-wizards-arpa
XXunix-wizards-arpa	unix-wizards-outbound@list-processor
XXunix-wizards-outbound	</d/mike/.wiz/.wiz
usenet	acst
abcooper	abc
armendt	brad
babel	babel@hel-ace
backup	gurus
bdbroom	bdbroome
billy	billy@hel-ace
bin	gurus
brl-bmd!dpk	dpk
broom	broome
chuck	kermit
ckennedy	kermit
cohen	cohen@utah-20
cstacy	cstacy@mc
ctab-inv	schantz
dae	gurus
dan	dan@hel-ace
danish	mbd
datacomm	datacom
deitz	phd
demo	gurus
det	det@hel-ace
dietz	phd
dink	dink@hel-ace
dkingston	dpk
dll	dll@hel-ace
doc	gurus
exchange	gurus
fish	fish@utah-cs
forrest	mcmains@of8
frp	frp@hel-ace
fwalker	fwalker@udel
goldie	goldie@hel-ace
greig	greig@nswc-wo
grk	klair
guru	gurus
hippy	hippy@hel-ace
horley	horley@hel-ace
human-nets	human-nets@rutgers
info-physics	info-physics@sri-unix
ingres	gurus
init	gurus
ip-digest	tcp-ip
ip-request	tcp-ip
irucker	rucker
jburk	Burk@of8
jdunning	jdunning@darcom-hq
jesse	rnj
kangaroos	gurus
kc	kc@hel-ace
kennedy	kermit
kerm	kermit
kingston	dpk
knarf	knarf@hel-ace
kuhl	kuhl@hel-ace
kysor	kysor@hel-ace
lad	lad@hel-ace
lama	acst
lamas	acst
learn	gurus
llama	acst
llamas	acst
lllama	acst
lllamas	acst
machle	machle@hel-ace
mdqs	gurus
meb	meb@hel-ace
merc	merc@hel-ace
michael	mike
miles	rsm
mills	mills@isid
mjm	sue
mmuuss	mike
moose	mike
mount	gurus
msgroup	msggroup
msgroup-request	msggroup-request
mus	mike
muss	mike
muus	mike
muuss	mike
nic	nic@sri-nic
nicolo	nicolo@hel-ace
operator	gurus
opr	gurus
ozzy	ozzy@hel-ace
peaches	schantz
postel	postel@isif
postmaster	acst
rbaldwin	baldwin@xx
rich	riesenfeld@utah-20
rmiles	rsm
root	acst
rsparks	rsparks@apg-1
salkind	salkind@nyu
sf-lovers	sf-lovers@rutgers
sharon	sharon@hel-ace
stark	mcdonald
swolff	steve
sys	gurus
taus	taus@hel-ace
tbd-chiefs	tbdbrchfs
tbd-secretaries	tbdbrsecs
tcp-digest	tcp-ip
tcp-ip-digest	tcp-ip
tcp-ip-digest-request	tcp-ip
tcp-ip-digest-requests	tcp-ip
tcp-ip-request	tcp-ip
tcp-ip-requests	tcp-ip
tcp-request	tcp-ip
tcp-requests	tcp-ip
tek	tek@hel-ace
telecom	telecom@usc-eclb
tip-bug	bug-tip
unix-sources	unix-sources-request
unixsources	unix-sources-request
unix-sources-request	dpk
who	gurus
wiz	unix-wizards-request
wolff	steve
works	works@Rutgers
 is the year and ZZZ
	 * is the timezone.
	 */
	char timezone[10];
	int weekday;
	int year = 0, month = 1, day = 0;
	int hour = 0, min = 0, sec = 0;
	char *wd = date;
	int n;

	weekday = wordpos(wd, weekdays);
	if (weekday == 0)
		formaterr(iname, lncnt);
	wd = word(wd, NULL);
	
	month = wordpos(wd, months);
	if (month == 0)mmdf/testmmdf/table/smtp   444      0     12        1417  3620510601  10565 BRL-AOS.ARPA:192.5.22.82
BRL-CYBER.ARPA:192.5.22.19
BRL-MIS.ARPA:192.5.22.17
BRL-ZAP.ARPA:192.5.23.16
BRL-VGR.ARPA:192.5.21.6
BRL-VGR.ARPA:128.20.1.1
BRL-TGR.ARPA:192.5.21.4
BRL-SVG.ARPA:192.5.21.3
BRL-VLD.ARPA:192.5.21.2
BRL-BMD.ARPA:192.5.21.1
BRL-TAC4.ARPA:128.20.2.4
BRL-LFD.ARPA:128.20.0.4
BRL-TAC3.ARPA:128.20.2.3
BRL-TBD.ARPA:128.20.0.3
BRL-VOC.ARPA:128.20.3.2
BRL-VOC.ARPA:192.5.22.18
BRL-TAC2.ARPA:128.20.2.2
BRL-HEP.ARPA:128.20.1.2
BRL-VAT.ARPA:128.20.0.2
BRL-TAC1.ARPA:128.20.2.1
BRL-TAC.ARPA:26.2.0.29
BRL.ARPA:26.0.0.29
YUMA.ARPA:6.1.0.1
BBNCCA.ARPA:8.0.0.2
BBNCCB.ARPA:8.1.0.2
BBNCCC.ARPA:8.2.0.2
BBNCCD.ARPA:8.3.0.2
BBN-LABS-ADMIN.ARPA:8.4.0.2
BBNCCK.ARPA:8.5.0.2
BBNCCG.ARPA:8.0.0.3
BBNCC2-TAC.ARPA:8.2.0.3
BBNCCF.ARPA:8.0.0.4
BBNCCP.ARPA:8.2.0.4
BBNCCI.ARPA:8.3.0.4
ace
human-nets	human-nets@rutgers
info-physics	info-physics@sri-unix
ingres	gurus
init	gurus
ip-digest	tcp-ip
ip-request	tcp-ip
irucker	rucker
jburk	Burk@of8
jdunning	jdunning@darcom-hq
jesse	rnj
kangaroos	gurus
kc	kc@hel-ace
kennedy	kermit
mmdf/testmmdf/table/arpa   444      0     12        1665  3620510601  10532 BRL-AOS:BRL-AOS.ARPA
AOS:BRL-AOS.ARPA
RUCKER-VAX:BRL-AOS.ARPA
BRL-CYBER:BRL-CYBER.ARPA
MFA:BRL-CYBER.ARPA
BRL-MIS:BRL-MIS.ARPA
BRL-ZAP:BRL-ZAP.ARPA
BRL-VGR:BRL-VGR.ARPA
VGR:BRL-VGR.ARPA
BRL-TGR:BRL-TGR.ARPA
TGR:BRL-TGR.ARPA
BRL-SVG:BRL-SVG.ARPA
BRL-VLD:BRL-VLD.ARPA
VLD70:BRL-VLD.ARPA
BRL-BMD:BRL-BMD.ARPA
BMD70:BRL-BMD.ARPA
BRL-TAC4:BRL-TAC4.ARPA
BRL-LFD:BRL-LFD.ARPA
LFD:BRL-LFD.ARPA
BRL-TAC3:BRL-TAC3.ARPA
BRL-TBD:BRL-TBD.ARPA
TBD:BRL-TBD.ARPA
BRL-VOC:BRL-VOC.ARPA
VOC:BRL-VOC.ARPA
BRL-TAC2:BRL-TAC2.ARPA
BRL-HEP:BRL-HEP.ARPA
BRL-VAT:BRL-VAT.ARPA
VAT:BRL-VAT.ARPA
BRL-TAC1:BRL-TAC1.ARPA
BRL-TAC:BRL-TAC.ARPA
BRL:BRL.ARPA
YUMA:YUMA.ARPA
BBNCCA:BBNCCA.ARPA
BBNCCB:BBNCCB.ARPA
BBNCCC:BBNCCC.ARPA
BBNCCD:BBNCCD.ARPA
BBN-LABS-ADMIN:BBN-LABS-ADMIN.ARPA
BBN-LABS:BBN-LABS-ADMIN.ARPA
BBNL:BBN-LABS-ADMIN.ARPA
BBNCCK:BBNCCK.ARPA
BBNCCG:BBNCCG.ARPA
BBNCC2-TAC:BBNCC2-TAC.ARPA
BBNCC2:BBNCC2-TAC.ARPA
BBNCCF:BBNCCF.ARPA
BBNCCP:BBNCCP.ARPA
BBNCCI:BBNCCI.ARPA
	jdunning@darcom-hq
jesse	rnj
kangaroos	gurus
kc	kc@hel-ace
kennedy	kermit
mmdf/testmmdf/table/alias.local   444      0     12        2361  3620510601  11763 a1-hacks		</d/mike/.lists/a1-hacks
acst			mike,ron,dpk,gwyn,kermit,steve
alpha_1			</d/mike/.lists/alpha_1
alpha_1-group		alpha_1-group@list-processor
alpha_1-group-out	</d/mike/.lists/alpha_1-group
ged			ged-outbound@list-processor
ged-outbound		</d/mike/.lists/ged
ged-request		mike
gurus			</d/mike/.lists/gurus
hep-unix		hep-unix-outbound@list-processor
hep-unix-outbound	</d/mike/.lists/hep-unix
hep-unix-request	mike
info-micro		info-micro-outbound@list-processor
info-micro-outbound	</d/ron/.rbn/info-micro
info-micro-request	rbn
info-music		info-music-outbound@list-processor
info-music-outbound	</d/ron/.rbn/info-music
info-music-request	rbn
XXinfo-unix		</d/mike/.wiz/.info-master
info-unix-from-usenet	info-unix-arpa
info-unix-arpa		info-unix-outbound@list-processor
XXinfo-unix-outbound	</d/mike/.wiz/.info
XXinfo-unix-request	unix-wizards-request
lpr			osg@brl-bmd
mmdf			</usr/src/newbrl/mmdf/testmmdf/table/mmdflist
tcp-outbound		</d/mike/.tcp/.list
testlist		testlist-outbound@list-processor
testlist-outbound	</d/dpk/testlist
testlist-request	dpk
XXunix-wizards		</d/mike/.wiz/.wiz-master
XXunix-wizards-from-usenet	unix-wizards-arpa
XXunix-wizards-arpa	unix-wizards-outbound@list-processor
XXunix-wizards-outbound	</d/mike/.wiz/.wiz
usenet			acst
l-ace
michael	mike
miles	rsm
mills	mills@isid
mjm	sue
mmuuss	mike
moose	mike
mount	gurus
msgroup	msggroup
msgroup-request	msggroup-request
mus	mike
muss	mike
muus	mike
muuss	mike
nic	nic@sri-nic
nicolo	nicolo@hel-ace
operator	gurus
opr	gurus
ozzy	ozzy@hel-ace
peaches	schmmdf/testmmdf/table/alias.global   444      0     12        4260  3620510601  12131 abcooper		abc
armendt			brad
babel			babel@hel-ace
backup			gurus
bdbroom			bdbroome
billy			billy@hel-ace
bin			gurus
brl-bmd!dpk		dpk
broom			broome
chuck			kermit
ckennedy		kermit
cohen			cohen@utah-20
cstacy			cstacy@mc
ctab-inv		schantz
dae			gurus
dan			dan@hel-ace
danish			mbd
datacomm		datacom
deitz			phd
demo			gurus
det			det@hel-ace
dietz			phd
dink			dink@hel-ace
dkingston		dpk
dll			dll@hel-ace
doc			gurus
exchange		gurus
fish			fish@utah-cs
forrest			mcmains@of8
frp			frp@hel-ace
fwalker			fwalker@udel
goldie			goldie@hel-ace
greig			greig@nswc-wo
grk			klair
guru			gurus
hippy			hippy@hel-ace
horley			horley@hel-ace
human-nets		human-nets@rutgers
info-physics		info-physics@sri-unix
ingres			gurus
init			gurus
ip-digest		tcp-ip
ip-request		tcp-ip
irucker			rucker
jburk			Burk@of8
jdunning		jdunning@darcom-hq
jesse			rnj
kangaroos		gurus
kc			kc@hel-ace
kennedy			kermit
kerm			kermit
kingston		dpk
knarf			knarf@hel-ace
kuhl			kuhl@hel-ace
kysor			kysor@hel-ace
lad			lad@hel-ace
lama			acst
lamas			acst
learn			gurus
llama			acst
llamas			acst
lllama			acst
lllamas			acst
machle			machle@hel-ace
mdqs			gurus
meb			meb@hel-ace
merc			merc@hel-ace
michael			mike
miles			rsm
mills			mills@isid
mjm			sue
mmuuss			mike
moose			mike
mount			gurus
msgroup			msggroup
msgroup-request		msggroup-request
mus			mike
muss			mike
muus			mike
muuss			mike
nic			nic@sri-nic
nicolo			nicolo@hel-ace
operator		gurus
opr			gurus
ozzy			ozzy@hel-ace
peaches			schantz
postel			postel@isif
postmaster		acst
rbaldwin		baldwin@xx
rich			riesenfeld@utah-20
rmiles			rsm
root			acst
rsparks			rsparks@apg-1
salkind			salkind@nyu
sf-lovers		sf-lovers@rutgers
sharon			sharon@hel-ace
stark			mcdonald
swolff			steve
sys			gurus
taus			taus@hel-ace
tbd-chiefs		tbdbrchfs
tbd-secretaries		tbdbrsecs
tcp-digest		tcp-ip
tcp-ip-digest		tcp-ip
tcp-ip-digest-request	tcp-ip
tcp-ip-digest-requests	tcp-ip
tcp-ip-request		tcp-ip
tcp-ip-requests		tcp-ip
tcp-request		tcp-ip
tcp-requests		tcp-ip
tek			tek@hel-ace
telecom			telecom@usc-eclb
tip-bug			bug-tip
unix-sources		unix-sources-request
unixsources		unix-sources-request
unix-sources-request	dpk
who			gurus
wiz			unix-wizards-request
wolff			steve
works			works@Rutgers
s@Rutgers
 is the year and ZZZ
	 * is the timezone.
	 */
	char timezone[10];
	int weekday;
	int year = 0, month = 1, day = 0;
	int hour = 0, min = 0, sec = 0;
	char *wd = date;
	int n;

	weekday = wordpos(wd, weekdays);
	if (weekday == 0)
		formaterr(iname, lncnt);
	wd = word(wd, NULL);
	
	month = wordpos(wd, months);
	if (month == 0)mmdf/testmmdf/table/makealias   555      0     12        2036  3620510602  11533 #!/bin/sh
if test ! -r alias.local
then
	echo "Cannot read 'alias.local'"
	exit 1
elif test ! -r alias.global
then
	echo "Cannot read 'alias.global'"
	exit 1
fi

trap 'rm -f /tmp/$$.* newalias 2> /dev/null; echo --Aborted--; exit' 1 2 3 15
set -u
umask 077
LOCALHOST=`hostname`
echo "Building alias file for host '${LOCALHOST}'"

#
#  Put editor commands to convert YOUR hostname into the canonical form
#
cat > /tmp/$$.sed <<DONE
s/[ 	]*#.*//
/^$/d
s/@BRL-VGR/@brl-vgr/
s/@Brl-Vgr/@brl-vgr/
s/@VGR/@brl-vgr/
s/@Vgr/@brl-vgr/
s/@vgr/@brl-vgr/
s/@/ @/
DONE

cat > /tmp/$$.awk <<DONE
{
	if( \$3 == "" || \$3 == "@$LOCALHOST" ) {
		if( \$1 != \$2 )
			printf( "%s\t%s%s\n", \$1, \$2, \$3 )
	} else
		printf( "%s\t%s%s\n", \$1, \$2, \$3 )
}
DONE

echo -n "Processing alias.local"
sed -f /tmp/$$.sed alias.local | awk -f /tmp/$$.awk > newalias

echo -n " and alias.global"
sed -f /tmp/$$.sed alias.global | awk -f /tmp/$$.awk >> newalias

echo "."
rm /tmp/$$.*
chmod 644 newalias
mv -f aliases aliases.bak
mv -f newalias aliases
echo "New aliases file built"
ocal
then
	echo "Cannot read 'alias.local'"
	exit 1
elif test ! -r alias.global
then
	echo "Cannot read 'alias.global'"
	exit 1
fi

trap 'rm -f /tmp/$$.* newalias 2> /dev/null; echo --Aborted--; exit' 1 2 3 15
set -u
umask 077
LOCALHOST=`hostname`
echo "Building alias file for host '${LOCALHOST}'"

#
#  Put editor commands to convert YOUR hostname into the canonical form
#
cat > /tmp/$$.sed <<DONE
s/[ 	]*#.*//
/^$/d
s/@BRL-VGR/@brl-vgr/
s/@Brl-Vgr/@brl-vgr/
s/@VGR/@brl-vgr/
s/@mmdf/testmmdf/table/doit   555      0     12         352  3620510602  10522 #! /bin/sh
PATH=.:/bin:/usr/bin
export PATH
umask 002		# vital! -Mike
# to do everything, run this file.
echo "Processing new MMDF tables for `hostname`"
/bin/sh makearpa
/bin/sh makemailids
/bin/sh makealias `hostname`
dbmbuild -Onv
 3 15
set -u
umask 077
LOCALHOST=`hostname`
echo "Building alias file for host '${LOCALHOST}'"

#
#  Put editor commands to convert YOUR hostname into the canonical form
#
cat > /tmp/$$.sed <<DONE
s/[ 	]*#.*//
/^$/d
s/@BRL-VGR/@brl-vgr/
s/@Brl-Vgr/@brl-vgr/
s/@VGR/@brl-vgr/
s/@mmdf/testmmdf/table/makemailids   555      0     12         700  3620510602  12040 #! /bin/sh
#
#	makemailids.sh
#
#	Generates the mailids and users files from /etc/passwd.
#	/etc/passwd is expected to contain an "<mailid>" entry in
#	the GCOS field.  This field may eventually contain more than
#	one mailid.
#
PATH=/bin:/usr/bin:.

ed - /etc/passwd <<DONE
v/</d
g/:.*</s//	/
g/>.*/s///
w users.new
g/\(.*\)	\(.*\)/s//\2	\1/
w mailids.new
q
DONE
mv users users.bak
mv users.new users
mv mailids mailids.bak
mv mailids.new mailids
d
s/@BRL-VGR/@brl-vgr/
s/@Brl-Vgr/@brl-vgr/
s/@VGR/@brl-vgr/
s/@mmdf/testmmdf/table/mailids   444      0     12        2666  3620510602  11234 root	root
root	croot
dae	dae
bin	bin
who	who
mount	mount
init	init
uucp	uucp
ftp	ftp
backup	backup
mmdf	mmdf
harry	harry
learn	learn
notes	notes
news	news
steve	steve
alpha1	alpha1
charlie	charlie
geo	geo
psl	psl
rnj	rnj
mdqs	mdqs
vitali	vitali
mail-in	mail-in
dickmc	dickmc
sys	sys
doc	doc
osg	osg
tcp-ip	tcp-ip
dpk	dpk
mort	mort
exchange	exchange
earl	earl
mike	mike
sue	mjm
jcp	jcp
randy	randy
fish	fish
ron	ron
ingres	ingres
abc	abc
dll	dll
phd	phd
lee	lee
durf	durf
oz	oz
andr	andr
john	john
kinch	kinch
robert	robert
myra	myra
karen	karen
keith	keith
gary	gary
howard	howard
barb	barb
rsm	rsm
joanw	joanw
dougb	dougb
schlegel	schlegel
schantz	schantz
kermit	kermit
ginny	ginny
moss	moss
ace	ace
jill	jill
wildman	wildman
ryan	ryan
crimmins	crimmins
salkind	salkind
dickk	dickk
duke	duke
kirky	kirky
cae	cae
kevin	kevin
cmoore	cmoore
jra	jra
hans	hans
fsbrn	fsbrn
kenny	kenny
lrk	lrk
dumer	dumer
ge	ge
broome	broome
rich	rich
jwood	jwood
gwyn	gwyn
matt	matt
hugh	hugh
donnelly	donnelly
todd	todd
schmidt	schmidt
cider	cider
karr	karr
walinch	walinch
lockhart	lockhart
bulmash	bulmash
rbn	rbn
merritt	merritt
andy	andy
glimm	glimm
wmartin	wmartin
stay	stay
unix-wizards-request	wiz
jeffh	jeffh
jnolan	jnolan
michele	michele
dino	dino
kille	kille
cain	cain
hanratty	hanratty
aic	aic
davisson	davisson
cohen	cohen
whh	whh
hwalt	hwalt
towson	towson
erim	erim
dchubb	dchubb
lgarn	lgarn
mills	mills
wouk	wouk
tcs	tcs
det	det
reilly	reilly
launer	launer
jjwu	jjwu
ll	dll
phd	phd
lee	lee
durf	durf
oz	oz
andr	andr
john	john
kinch	kinch
robmmdf/testmmdf/table/brlnet   444      0     12         563  3620510602  11052 brl.arpa	"26.0.0.29"
brl-bmd.arpa	"192.5.21.1"
brl-vld.arpa	"192.5.21.2"
brl-tgr.arpa	"192.5.21.4"
brl-vgr.arpa	"128.20.1.1"
brl-voc.arpa	"128.20.3.2"
brl-vat.arpa	"128.20.0.2"
brl-tbd.arpa	"128.20.0.3"
brl-aos.arpa	"192.5.22.82"
brl-mis.arpa	"192.5.22.17"
brl-lfd.arpa	"128.20.0.4"
amsaa.arpa	"128.20.3.1"
hel-ace.arpa	"128.20.0.5"
mikes-test-dont-delete	"128.20.99.99"
ls
wouk	wouk
tcs	tcs
det	det
reilly	reilly
launer	launer
jjwu	jjwu
ll	dll
phd	phd
lee	lee
durf	durf
oz	oz
andr	andr
john	john
kinch	kinch
robmmdf/testmmdf/table/users   444      0     12        2666  3620510603  10754 root	root
croot	root
dae	dae
bin	bin
who	who
mount	mount
init	init
uucp	uucp
ftp	ftp
backup	backup
mmdf	mmdf
harry	harry
learn	learn
notes	notes
news	news
steve	steve
alpha1	alpha1
charlie	charlie
geo	geo
psl	psl
rnj	rnj
mdqs	mdqs
vitali	vitali
mail-in	mail-in
dickmc	dickmc
sys	sys
doc	doc
osg	osg
tcp-ip	tcp-ip
dpk	dpk
mort	mort
exchange	exchange
earl	earl
mike	mike
mjm	sue
jcp	jcp
randy	randy
fish	fish
ron	ron
ingres	ingres
abc	abc
dll	dll
phd	phd
lee	lee
durf	durf
oz	oz
andr	andr
john	john
kinch	kinch
robert	robert
myra	myra
karen	karen
keith	keith
gary	gary
howard	howard
barb	barb
rsm	rsm
joanw	joanw
dougb	dougb
schlegel	schlegel
schantz	schantz
kermit	kermit
ginny	ginny
moss	moss
ace	ace
jill	jill
wildman	wildman
ryan	ryan
crimmins	crimmins
salkind	salkind
dickk	dickk
duke	duke
kirky	kirky
cae	cae
kevin	kevin
cmoore	cmoore
jra	jra
hans	hans
fsbrn	fsbrn
kenny	kenny
lrk	lrk
dumer	dumer
ge	ge
broome	broome
rich	rich
jwood	jwood
gwyn	gwyn
matt	matt
hugh	hugh
donnelly	donnelly
todd	todd
schmidt	schmidt
cider	cider
karr	karr
walinch	walinch
lockhart	lockhart
bulmash	bulmash
rbn	rbn
merritt	merritt
andy	andy
glimm	glimm
wmartin	wmartin
stay	stay
wiz	unix-wizards-request
jeffh	jeffh
jnolan	jnolan
michele	michele
dino	dino
kille	kille
cain	cain
hanratty	hanratty
aic	aic
davisson	davisson
cohen	cohen
whh	whh
hwalt	hwalt
towson	towson
erim	erim
dchubb	dchubb
lgarn	lgarn
mills	mills
wouk	wouk
tcs	tcs
det	det
reilly	reilly
launer	launer
jjwu	jjwu
e
peaches			schantz
postel			postel@isif
postmaster		acst
rbaldwin		baldwimmdf/testmmdf/table/local   444      0     12          46  3620510603  10633 brl-vgr.arpa	brl-vgr
vgr.arpa	brl-vgr
ho	who
mount	mount
init	init
uucp	uucp
ftp	ftp
backup	backup
mmdf	mmdf
harry	harry
learn	learn
notes	notes
news	news
steve	steve
alpha1	alpha1
charlie	charlie
geo	geo
psl	psl
rnj	rnj
mdqs	mdqs
vitali	vitali
mail-in	mail-in
dickmc	dickmc
sys	sys
doc	doc
osg	osg
tcp-ip	tcp-ip
dpk	dpk
mort	mort
exchange	exchange
earl	earl
mike	mike
mjm	sue
jcp	jcp
randy	randy
fish	fish
ron	ron
ingres	ingres
abc	abc
dll	dll
phd	phd
lee	lee
durf	durf
oz	oz
andr	andr
john	john
kinch	kinch
robmmdf/testmmdf/table/list   444      0     12          67  3620510603  10517 list-processor	list-processor
list-proc	list-processor
t
init	init
uucp	uucp
ftp	ftp
backup	backup
mmdf	mmdf
harry	harry
learn	learn
notes	notes
news	news
steve	steve
alpha1	alpha1
charlie	charlie
geo	geo
psl	psl
rnj	rnj
mdqs	mdqs
vitali	vitali
mail-in	mail-in
dickmc	dickmc
sys	sys
doc	doc
osg	osg
tcp-ip	tcp-ip
dpk	dpk
mort	mort
exchange	exchange
earl	earl
mike	mike
mjm	sue
jcp	jcp
randy	randy
fish	fish
ron	ron
ingres	ingres
abc	abc
dll	dll
phd	phd
lee	lee
durf	durf
oz	oz
andr	andr
john	john
kinch	kinch
robmmdf/testmmdf/table/mmdflist   444      0     12           5  3620510603  11333 dpk,
processor	list-processor
list-proc	list-processor
t
init	init
uucp	uucp
ftp	ftp
backup	backup
mmdf	mmdf
harry	harry
learn	learn
notes	notes
news	news
steve	steve
alpha1	alpha1
charlie	charlie
geo	geo
psl	psl
rnj	rnj
mdqs	mdqs
vitali	vitali
mail-in	mail-in
dickmc	dickmc
sys	sys
doc	doc
osg	osg
tcp-ip	tcp-ip
dpk	dpk
mort	mort
exchange	exchange
earl	earl
mike	mike
mjm	sue
jcp	jcp
randy	randy
fish	fish
ron	ron
ingres	ingres
abc	abc
dll	dll
phd	phd
lee	lee
durf	durf
oz	oz
andr	andr
john	john
kinch	kinch
robmmdf/testmmdf/mmdftailor   444      0     12        2430  3620510603  10647 MLDOMAIN ARPA
MLNAME	BRL-VGR
MLOCMACHINE BRL-VGR

MDBM "/usr/src/newbrl/mmdf/testmmdf/table/mmdfdbm"

; Table entries
MTBL	local,		file="local",	show="Local Host Aliases"
MTBL	brlnet,		file="brlnet",	show="BRLNET Hosts"
MTBL	smtp,		file="smtp",	show="TCP/ARPA Hosts"
MTBL	arpa,		file="arpa",	show="ARPA Domains"
MTBL	list,		file="list",	show="List Pseudo Hosts"

; Channel entries
MCHN	local, show="Local Delivery", que=local, tbl=local,
	pgm=local, mod=imm
MCHN	brlnet, show="BRLNET", que=brlnet, tbl=brlnet
	pgm=smtp, mod=reg, known=smtp, ap=822
MCHN	smtp, show="SMTP/TCP", que=smtp, tbl=smtp
	pgm=smtp, mod=reg, ap=822, confstr="BRL-VGR.ARPA"
MCHN	list, show="Mailing List Processor", que=list, tbl=list
	pgm=list, mod=imm

; Domain tables
MDMN    ARPA, show="ARPA Domain", table=arpa, flags=01

MLOGDIR "/usr/src/newbrl/mmdf/testmmdf/log"
MPHSDIR "/usr/src/newbrl/mmdf/testmmdf/log/phase"
MTBLDIR "/usr/src/newbrl/mmdf/testmmdf/table"
MQUEDIR "/usr/src/newbrl/mmdf/testmmdf/lock/home"
MCMDDIR "/usr/src/newbrl/mmdf/testmmdf"
MCHNDIR "/usr/src/newbrl/mmdf/testmmdf/chans"
MLOGIN  mmdf
MSUPPORT "mmdf@brl-vgr.arpa"
PHLOG "/usr/src/newbrl/mmdf/testmmdf/log/ph.log"
PHTRAN "/usr/src/newbrl/mmdf/testmmdf/log/ph.trn"

;logging levels
MMSGLOG     level=FST
MCHANLOG    level=FST

MWARNTIME	24
MFAILTIME	48
ile="arpa",	show="ARPA Domains"
MTBL	list,		file="list",	show="List Pseudo Hosts"

; Channel entries
MCHN	local, show="Local Delivery", que=local, tbl=local,
	pgm=local, mod=imm
MCHN	brlnet, show="BRLNET", que=brlnet, tbl=brlnet
	pgmmdf/src/   755      0     12           0  3671117034   5457 mmdf/src/local/   755      0     12           0  3671074624   6560 mmdf/src/local/Makefile.real   444      0     12        3323  3635174102  11216 #
#   local:   local (direct) delivery channel transmission
#
MODULES =	ch_local qu2lo_send lo_wtmail

OBJECTS =	ch_local.o qu2lo_send.o lo_wtmail.o

SOURCES =	ch_local.c qu2lo_send.c lo_wtmail.c

real-default:	local

install:	$(CHANDIR)/$(MMPREF)local

$(CHANDIR)/$(MMPREF)local:	xlocal
	cp xlocal $(CHANDIR)/$(MMPREF)local
	-$(CHOWN) root $(CHANDIR)/$(MMPREF)local
	-chmod 04$(PGMPROT) $(CHANDIR)/$(MMPREF)local
	-@ls -ls $(CHANDIR)/$(MMPREF)local
	-@echo "local installed normally"; echo ""

local:	xlocal
xlocal:	$(OBJECTS) $(MMDFLIBS)
	    $(CC) $(LDFLAGS) -o $@ $(OBJECTS) $(MMDFLIBS) $(SYSLIBS)

lint:	;  $(LINT) $(LFLAGS) $(SOURCES) $(LLIBS)

clean:
	-rm -f xlocal *.o x.c makedep eddep make.out errs


# DO NOT DELETE THIS LINE -- make depend uses it
deliver.o: deliver.c
ch_local.o: ch_local.c
ch_local.o: ../../h/util.h
ch_local.o: ../../h/mmdf.h
ch_local.o: /usr/include/signal.h
ch_local.o: ../../h/ch.h
ch_local.o: ../../h/phs.h
qu2lo_send.o: qu2lo_send.c
qu2lo_send.o: ../../h/util.h
qu2lo_send.o: ../../h/mmdf.h
qu2lo_send.o: ../../h/phs.h
qu2lo_send.o: ../../h/ap.h
qu2lo_send.o: ../../h/ch.h
qu2lo_send.o: /usr/include/pwd.h
qu2lo_send.o: /usr/include/sys/stat.h
qu2lo_send.o: /usr/include/signal.h
qu2lo_send.o: /usr/include/sgtty.h
qu2lo_send.o: ../../h/adr_queue.h
lo_wtmail.o: lo_wtmail.c
lo_wtmail.o: ../../h/util.h
lo_wtmail.o: ../../h/mmdf.h
lo_wtmail.o: ../../h/phs.h
lo_wtmail.o: ../../h/ap.h
lo_wtmail.o: ../../h/ch.h
lo_wtmail.o: /usr/include/pwd.h
lo_wtmail.o: /usr/include/sys/stat.h
lo_wtmail.o: /usr/include/signal.h
lo_wtmail.o: /usr/include/sgtty.h
lo_wtmail.o: ../../h/adr_queue.h
lo_wtmail.o: ../../h/hdr.h
# DEPENDENCIES MUST END AT END OF FILE
# IF YOU PUT STUFF HERE IT WILL GO AWAY
# see make depend above
hel-ace
tbd-chiefs		tbdbrchfs
tbd-secretaries		tbdbrsecs
tcp-digest		tcp-ip
tcp-ip-digest		tcp-ip
tcp-ip-digest-request	tcp-ip
tcp-ip-digest-requests	tcp-ip
tcp-ip-request		tcp-ip
tcp-ip-requests		tcp-ip
tcp-request		tcp-ip
tcp-requests		tcp-ip
tek			tek@hel-ace
telecom			telecom@usc-eclb
tip-bug			bmmdf/src/local/ch_local.c   444      0     12       10040  3620510515  10552 /*
 *     MULTI-CHANNEL MEMO DISTRIBUTION FACILITY  (MMDF)
 *     
 *
 *     Copyright (C) 1979,1980,1981  University of Delaware
 *     
 *     Department of Electrical Engineering
 *     University of Delaware
 *     Newark, Delaware  19711
 *
 *     Phone:  (302) 738-1163
 *     
 *     
 *     This program module was developed as part of the University
 *     of Delaware's Multi-Channel Memo Distribution Facility (MMDF).
 *     
 *     Acquisition, use, and distribution of this module and its listings
 *     are subject restricted to the terms of a license agreement.
 *     Documents describing systems using this module must cite its source.
 *
 *     The above statements must be retained with all copies of this
 *     program and may not be removed without the consent of the
 *     University of Delaware.
 *     
 *
 *     version  -1    David H. Crocker    March   1979
 *     version   0    David H. Crocker    April   1980
 *     version  v7    David H. Crocker    May     1981
 *     version   1    David H. Crocker    October 1981
 *
 */

#include "util.h"
#include "mmdf.h"
#include <signal.h>
#include "ch.h"
#include "phs.h"

extern Chan	*ch_nm2struct();
extern LLog *logptr;

Chan	*chanptr;

/*
 *  CH_LOCAL --  Actual delivery of mail, out of transport environment
 *
 *  lo_wtmail does most of the interesting work.  qu2lo_send handles the
 *  deliver interface and the verification. Lo_wtmail handles all the piping
 *  filing, and the maildelivery stuff and looks after return values etc.
 *
 *  in any event, this process runs setuid to root.  for each recipient, it
 *  determines the uid, etc, and then forks a child to do the actual work
 *  (do the parsing of files etc).The child does a setuid
 *  to the recipient.  the parent version of the process just waits for the
 *  child to complete.  its job is to a) acquire the next address, b) get
 *  the recipient id info, and c) fork and wait on the child.
 */

/*      MAIN MAIN MAIN MAIN MAIN MAIN MAIN MAIN MAIN MAIN MAIN MAIN     */

main (argc, argv)
int     argc;
char   *argv[];
{
    extern char *dupfpath ();
    short retval;
    int   realid,
	  effecid;

    mmdf_init (argv[0]);
    siginit ();
    signal (SIGINT, SIG_IGN);   /* always ignore interrupts             */

    if ((chanptr = ch_nm2struct (argv[0])) == (Chan *) NOTOK)
    {
	ll_log (logptr, LLOGTMP, "qu2lo_send (%s) unknown channel", argv[0]);
	exit (NOTOK);
    }

    getwho (&realid, &effecid);
    if (effecid != 0)              /* MUST run as superuser              */
	err_abrt (RP_BHST, "not running as superuser");

    retval = ch_local (argc, argv);
    ll_close (logptr);              /* clean log end, if cycled  */
    exit (retval);
}
/***************  (ch_) LOCAL MAIL DELIVERY  ************************ */

ch_local (argc, argv)		  /* send to local machine               */
int     argc;
char   *argv[];
{
    ch_llinit (chanptr);
#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "ch_local ()");
#endif

    if (rp_isbad (qu_init (argc, argv)))
	return (RP_NO);		  /* problem setting-up for deliver     */
    phs_note (chanptr, PHS_CNSTRT);         /* make a timestamp */
    phs_note (chanptr, PHS_CNGOT);         /* make a timestamp */

    if (rp_isbad (qu2lo_send ()))
	return (RP_NO);		  /* send the batch of outgoing mail    */

    qu_end (OK);                  /* done with Deliver function         */
    phs_end  (chanptr, RP_OK);     /* note end of session */

    return (RP_OK);		  /* NORMAL RETURN                      */
}


/* VARARGS2 */
err_abrt (code, fmt, b, c, d)     /* terminate ourself                  */
short     code;
char    *fmt, *b, *c, *d;
{
    char linebuf[LINESIZE];

    qu_end (NOTOK);
    if (rp_isbad (code))
    {
	if (rp_gbval (code) == RP_BNO || logptr -> ll_level >= LLOGBTR)
	{                         /* don't worry about minor stuff      */
	    sprintf (linebuf, "%s%s", "err [ ABEND (%s) ]\t", fmt);
	    ll_log (logptr, LLOGFAT, linebuf, rp_valstr (code), b, c, d);
#ifdef DEBUG
	    abort ();
#endif
	}
    }
    ll_close (logptr);           /* in case of cycling, close neatly   */
    exit (code);
}
the terms of a license agreement.
 *     Documents describing systems using this module must cite its source.
 *
 *     The above statements must be retained with all copies of this
 *     program and may not be removed without the consent of the
 *     University of Delaware.
 *     
 *
 *     version  -1    David H. Crocker    March   1979
 *     version   0    David H. Crocker    April   1980
 *     version  v7    David H. Crocker    May     1981
 *     version   1    Davimmdf/src/local/lo_wtmail.c   444      0     12       63325  3671073221  11017 /*
 *	Deliver to the users mailbox
 *	Does most of the interesting, non-mechanical work. It tries hard
 *	not to parse the message unless needed.
 *
 *	Severe hacking done this file reconstructed from bits of old
 *	qu2lo_send.c and a lot of new stuff.
 *
 *	Julian Onions <jpo@cs.nott.ac.uk>	August 1985
 */

#include "util.h"
#include "mmdf.h"
#include "phs.h"
#include "ap.h"
#include "ch.h"
#include <pwd.h>
#include <sys/stat.h>
#include <signal.h>
#include <sgtty.h>
#include "adr_queue.h"
#include "hdr.h"

extern Chan	*chanptr;
extern LLog	*logptr;

extern int	errno;

extern int	sentprotect;
extern int	flgtrest;
extern int	numfds;
extern long	qu_msglen;
extern jmp_buf  timerest;

extern char     *strcpy();

extern char *mldflfil;		/* name of local mailbox file		*/
extern char *mldfldir;		/* name of local mailbox directory	*/
extern char *delim1;		/* prefix and suffix lines for		*/
extern char *delim2;		/*	delivery mailbox/msg		*/
extern char *dlvfile;		/* name of user deliver control file	*/
extern char *sysdlvfile;	/* name of system deliver control file  */
extern char *lckdfldir;		/* directory to lock in			*/

LOCVAR int	mbx_fd = -1;	/* handle on recipient mailbox		*/

extern char lo_info[];		/* type of delivery		*/
extern char lo_sender[];	/* return address for message	*/
extern char lo_adr[];		/* destination address from deliver */
extern char lo_replyto[];	/* the reply address		*/
extern char lo_size[];
extern char *lo_parm;		/* parameter portion of address */
extern struct passwd *lo_pw;	/* passwd struct for recipient  */

extern  long	lseek();
extern	char	*strdup();
extern	char	*expand();

int	sigpipe;		/* has pipe gone bad? */
int	onpipe();		/* catch that pipe write failure */
LOCVAR char mbx_wasclear;	/* this message the first in mbox?	*/

/*	Structure used in lookup routines */

typedef struct {
	char	*l_key;
	short	l_val;
} Lookup;

# define	MAXLINES	200	/* max no. of lines in .maildelivery */

/* the action types */
# define	M_FILE		1
# define	M_PIPE		2
# define	M_DESTROY	4

/* the result types */
# define	M_ACCEPT	01
# define	M_REJECT	02
# define	M_CONDACC	03

/* the special headers */
# define	M_DEFAULT	1
# define	M_TRUE		2
# define	M_ADDRESS	3
# define	M_SENDER	4

typedef struct mdlvry {
	char	*m_header;		/* the header string	*/
	char	*m_pattern;		/* the pattern to match */
	char	*m_options;		/* the options string	*/
	unsigned short m_parse	  : 1;  /* header type		*/
	unsigned short m_special  : 1;  /* special actions?	*/
	unsigned short m_hit	  : 1;  /* have we hit		*/
	unsigned short m_ar	  : 2;  /* accept reject bits	*/
	unsigned short m_dollar   : 3;  /* the dollar field	*/
	unsigned short m_action   : 4;  /* the action to do	*/
	unsigned short m_hdrtype  : 4;  /* the special hdr type */
} Mdlvry;

Lookup  spec_hdrs[] = { /* the table of special headers */
	"default",	M_DEFAULT,
	"true",		M_TRUE,
	"addr",		M_ADDRESS,
	"source",	M_SENDER,
	"*",		M_TRUE,
	0,		0,
};

Lookup  actions[] = {	/* the table of actions */
	"pipe",		M_PIPE,
	"qpipe",	M_PIPE,
	"file",		M_FILE,
	"destroy",	M_DESTROY,
	"|",		M_PIPE,
	"^",		M_PIPE,
	">",		M_FILE,
	0,	0,
};

Lookup  dolfields[] = {
	"$(reply-to)",  1,
	0,		0,
};

/*
 *	Give the user something nice in the environment.
 */
LOCVAR char *envp[] = {
	(char *) 0,		/* HOME=xxx  */
	(char *) 0,		/* SHELL=xxx */
	(char *) 0,		/* USER=xxx  */
	0
};

/*
 *	Some things that can be macro expanded.
 */
LOCVAR char *vararray[] = {
	"sender",	lo_sender,	/* 0/1 */
	"address",	lo_adr,		/* 2/3 */
	"info",		lo_info,	/* 4/5 */
	"reply-to",	lo_replyto,	/* 6/7 */
	"size",		lo_size,	/* 8/9 */
	0,		0
};

/*
 * characters that need to be padded to pass to a shell
 */

LOCVAR char *padchar = " \\\"'%$@#?!*|<>()&^~=+-;";


/*  */

lo_slave ()
{
	char	buf[LINESIZE];
	register int	  result;
	Mdlvry	mdlv;	/* A fake construct */
	Mdlvry	*mp;

#ifdef DEBUG
	ll_log (logptr, LLOGBTR, "lo_slave()");
#endif
	mp = &mdlv;
	printx ("\n");
	/* The production system of delivery attempts */

	/* FIRST Attempt: alias file specified file or pipe delivery */

	switch (*lo_parm) {
	case '|':	/* send to special process	    */
		printx ("(sending message to piped process)\r\n\t");
		sprintf (buf, "default - | A \"%s\"", lo_parm+1);
		if( parse_line(mp, buf) != OK)
			ll_log (logptr, LLOGTMP, "problem parsing '%s'", buf);
		return ( lo_dorules(mp, mp+1) );
	case '^':
		printx ("(sending message to process unformatted)\r\n\t");
		sprintf (buf, "default - ^ A \"%s\"", lo_parm + 1);
		if( parse_line (mp, buf) != OK)
			ll_log (logptr, LLOGTMP, "problem parsing '%s'", buf);
		return ( lo_dorules(mp, mp+1) );
	case '/':
		printx ("(placing into mail file '%s')\r\n\t", lo_parm+1);
		sprintf (buf, "default - > A \"%s\"", lo_parm +1 );
		if( parse_line (mp, buf) != OK)
			ll_log (logptr, LLOGTMP, "problem parsing '%s'", buf);
		return (lo_dorules (mp, mp + 1));
	}

	/*
	 * SECOND Attempt: user's .maildelivery file
	 */

	if (rp_isgood (result = lo_dlvfile(dlvfile))
			|| result != RP_MECH)
		return (result);

	/* THIRD Attempt: system delivery file */

	if (rp_isgood (result = lo_dlvfile(sysdlvfile))
			|| result != RP_MECH)
		return (result);

	/* FOURTH Attempt: regular deliver to the mailbox */

	printx ("trying normal delivery\r\n");
	sprintf (buf, "%s/%s",
		(mldfldir == 0 || isnull(mldfldir[0])) ? "." : mldfldir,
		(mldflfil == 0 || isnull(mldflfil[0])) ? lo_pw->pw_name : mldflfil);
	return (lo_dofile (buf));
}
/**************** FILE DELIVERY ROUTINES *************** */

LOCFUN
lo_dofile(mboxname)
char	*mboxname;
{
	register int	retval;

#ifdef	DEBUG
	ll_log (logptr, LLOGBTR, "lo_dofile(%s)", mboxname);
#endif	DEBUG
	printx ("\tdelivering to file '%s'", mboxname);

	if (rp_gval(retval = mbx_open (mboxname)) == RP_LOCK) {
		printx (", locked out\r\n");
		return (RP_LOCK);
	} else if (rp_isbad(retval)) {
		printx (", failed\r\n");
		return (retval);
	}
	retval = qu2lo_txtcpy (mbx_fd, TRUE);
	mbx_close ();
	printx (rp_isgood (retval) ? ", succeeded\r\n" : ", failed\r\n");
	return (retval);
}

LOCFUN
qu2lo_txtcpy (ofd, tstdelim)	/* copy the text of the message	      */
register int ofd;		/* where to send the text */
register int tstdelim;		/* should we check for message delimiters? */
{
	register int offset;
	int	len;
	int	result;
	char	buffer[BUFSIZ];

#ifdef DEBUG
	ll_log (logptr, LLOGBTR, "qu2lo_txtcpy()");
#endif

	qu_rtinit (0L);
	len = sizeof(buffer);
	while (rp_gval (result = qu_rtxt (buffer, &len)) == RP_OK) {
		if (tstdelim) {	   /* text may not contain a delimiter	 */
			/*
			 *  Check for occurences of the message delimiter.
			 *  If we find any, change the string by changing
			 *  the first char to a space.
			 */
			if( isstr(delim1) )
				for (offset = 0;
				     (offset = strindex (delim1, buffer)) >= 0;
					buffer[offset] = ' ');

			if( isstr(delim2) )
				for (offset = 0;
				     (offset = strindex (delim2, buffer)) >= 0;
					buffer[offset] = ' ');
		}
		if (write (ofd, buffer, len) != len) {
			ll_err (logptr, LLOGTMP, "error writing out text");
			return (RP_LIO);
		}
		len = sizeof(buffer);
	}

	if (rp_gval (result) != RP_DONE)
		return (RP_LIO);	 /* didn't get it all across? */

	return (RP_MOK);	      /* got the text out		    */
}
/**************** PIPE DELIVERY ROUTINES *************** */

LOCFUN
lo_dopipe(prog)
char	*prog;
{
	int	childid;		/* child does the real work */
	int	ofd;			/* what to write on */
	char	tmpbuf[LINESIZE];
	char	buffer[BUFSIZ];
	int	(*savepipe)();
	Pip	pipdes;
	int	result;
	int	len;

#ifdef	DEBUG
	ll_log (logptr, LLOGBTR, "lo_dopipe(%s)", prog);
#endif	DEBUG

	if( pipe(pipdes.pipcall) == NOTOK)
		return (RP_LIO);

	ll_close(logptr);
	switch (childid = fork ()) {
	case NOTOK:
		(void) close(pipdes.pip.prd);
		(void) close(pipdes.pip.pwrt);
		return (RP_LIO);	/* problem with system */

	case OK:
		/* Child */
		lo_padadr();
		/* first - expand out any '$' keywords */
		expand (tmpbuf, prog, vararray);
#ifdef DEBUG
		ll_log (logptr, LLOGBTR, "lo_dopipe() child (%s)", tmpbuf);
#endif
		printx ("\tdelivering to pipe '%s'", tmpbuf);
		fflush (stdout);
		(void) close(0);
		dup (pipdes.pip.prd);

		setupenv();

		execle ("/bin/sh", "sh", "-c", tmpbuf, (char *)0, envp);
		ll_log( logptr, LLOGFAT, "Can't execl /bin/sh (%d)",errno);
		exit (RP_MECH);
	}

	/* Parent */
	/* our job is to pass on the message to the process */
	(void) close (pipdes.pip.prd);
	ofd = pipdes.pip.pwrt;  /* nicer name */
	qu_rtinit(0L);		/* init the message */
	savepipe = signal(SIGPIPE, onpipe);
	sigpipe = FALSE;
	len = sizeof(buffer);
	while (rp_gval(result = qu_rtxt(buffer, &len)) == RP_OK)
	{
		if( sigpipe )	/* childs had enough */
			break;
		if( write(ofd, buffer, len) != len)
		{
			if( errno != EPIPE)
				ll_err (logptr, LLOGTMP, "error on pipe %d",
					errno);
			break;
		}
		len = sizeof(buffer);
	}
	(void) close(ofd);	/* finished with pipe */
	signal(SIGPIPE, savepipe);	/* restore the dying signal */
	if(rp_gval(result) != RP_OK && rp_gval(result) != RP_DONE)
		return result;
	return (lo_pwait (childid, prog)); /* .... and wait */
}

LOCFUN
lo_pwait (procid, prog)		/* wait for child to complete	      */
int	procid;			/* id of child process		      */
char	*prog;
{
	int status;

#ifdef DEBUG
	ll_log (logptr, LLOGBTR, "lo_pwait ()");
#endif

	if (setjmp (timerest) == 0)
	{			/* alarm is in case user's pgm hangs	*/
		flgtrest = TRUE;
		/*NOSTRICT*/
		s_alarm ((unsigned) (qu_msglen / 20) + 300);
		errno = 0;
		status = pgmwait (procid);
		s_alarm (0);
		if (status == NOTOK) {
			/* system killed the process */
			ll_err (logptr, LLOGTMP, "bad return: (%s) %s [wait=NOTOK]",
				lo_pw->pw_name, prog);
			return (RP_LIO);
		}
		ll_log (logptr, LLOGBTR, "(%s) %s [%s]",
			lo_pw->pw_name, prog, rp_valstr (status));
		if( status == 0)	/* A-OK */
		{
			printx (", succeeded\r\n");
			return (RP_MOK);
		}
		else if( rp_gbval(status) == RP_BNO)	/* permanent error */
		{
			printx (", failed (perm)\r\n");
			return (RP_NO);
		}
		else	/* temp error */
		{
			printx (", failed (temp)\r\n");
			return RP_MECH;
		}
		/* NOTREACHED */
	}
	else
	{
		flgtrest = FALSE;
		printx (", user program taking too long");
		ll_log (logptr, LLOGGEN, "user program taking too long - killing");
#ifndef V4_2BSD
		kill (procid, SIGKILL); /* we're superuser, so always works */
#else	V4_2BSD
		killpg (procid, SIGKILL); /* we're superuser, so always works*/
#endif  V4_2BSD
		return (RP_TIME);
	}
}

/* ensure that all addresses can be passed to shell */
lo_padadr()
{
    static done_pad = 0;
    register int i;
    register char *c1, *c2, *p;
    char tmp[2 * LINESIZE];

    if (done_pad)
	return;

    for(i=0; vararray[i] != 0; i += 2)
    {
	(void) strcpy(tmp,vararray[i+1]);

	for(c1=tmp,c2=vararray[i+1]; *c1 != '\0'; c1++)
	{
	    for(p=padchar; *p != '\0';  p++)
	    {
		if (*p == *c1)
		{
		    *c2++ = '\\';
		    break;
		}
	    }

	    *c2++ = *c1;
	}
	*c2 = '\0';
    }
}

/********  (mbx_)  STANDARD DELIVERY TO MAILBOX STYLE FILE  *********** */

mbx_open (file)			/* stuff into mailbox		      */
char	*file;
{
	struct stat	mbxstat;
	register int	count;
	short	 retval;
	int	mbxmade;		/* Infinite loop preventer */

#ifdef DEBUG
	ll_log (logptr, LLOGBTR, "mbx_open (%s)", file);
#endif
	mbxmade = mbx_wasclear = FALSE;
reopen:
	if ((mbx_fd = lk_open (file, 2, lckdfldir, lo_pw->pw_name, 10)) < 0)
	{
		switch (errno) {
#ifdef EWOULDBLOCK
		case EWOULDBLOCK:
#endif EWOULDBLOCK
		case ETXTBSY:
			printx (", mail file is locked");
			return (RP_LOCK);

		case ENOENT:	  /* doesn't exist */
			if (!mbxmade && rp_isgood (mbx_create (file))) {
				printx (", mail file created");
				mbxmade = mbx_wasclear = TRUE;
				goto reopen;
			}
			/* DROP THROUGH */
		default:
			ll_err (logptr, LLOGTMP, "can't open mailbox '%s'",
								file);
			return (RP_LIO);
		}
	}
	else
	{
		if (fstat (mbx_fd, &mbxstat) < 0)
		{
			ll_err (logptr, LLOGTMP, "can't fstat %s", file);
			return (RP_LIO);
		}
		mbx_wasclear = (st_gsize (&mbxstat) == 0L);
	}

	if (!mbx_wasclear && rp_isbad (retval = mbx_chkdelim ()))
	    return (retval);	      /* mbox has bad terminator	    */

	count = strlen (delim1);      /* write prefatory separator	    */
	if (write (mbx_fd, delim1, count) != count)
	{
		ll_err (logptr, LLOGTMP, "error writing delim1");
		return (RP_LIO);
	}

	return (RP_OK);
}
/**/

LOCFUN
mbx_create (mboxname)	/* create receiver's mailbox file     */
char	*mboxname;
{
#ifdef DEBUG
	ll_log (logptr, LLOGBTR, "mbx_create (%s)", mboxname);
#endif

	if ((mbx_fd = creat (mboxname, sentprotect)) < 0)
	{
		ll_err (logptr, LLOGFAT, "can't create mailbox '%s'",
						mboxname);
		return (RP_LIO);
	}
	(void) close (mbx_fd);	/* unix create() forces wrong modes	*/

	return (RP_OK);
}

LOCFUN
mbx_close ()	/* done with mailbox	*/
{
	short	  count;

#ifdef DEBUG
	ll_log (logptr, LLOGBTR, "mbx_close ()");
#endif
	if (mbx_fd < 0)
	{
#ifdef DEBUG
		ll_log (logptr, LLOGFTR, "not open");
#endif
		return;
	}

	count = strlen (delim2);
	if (write (mbx_fd, delim2, count) != count)
	{	/* couldn't put ending delimiter on   */
		ll_err (logptr, LLOGTMP, "error writing delim2");
	}
	lk_close (mbx_fd, (char *) 0, lckdfldir, lo_pw->pw_name);
	mbx_fd = -1;
}
/**/

LOCFUN
mbx_chkdelim () /* last msg delimited properly?	      */
{		/* check ending delimiter of last msg */
	int	count;
	char	ldelim[LINESIZE];
	struct stat statb;

#ifdef DEBUG
	ll_log (logptr, LLOGBTR, "mbx_chkdelim ()");
#endif

	count = strlen (delim2);

	if (lseek (mbx_fd, (long)(-count), 2) == (long) NOTOK ||
	    read (mbx_fd, ldelim, count) != count) {
	    printx(", mailbox is missing delimiter");
	    if (fstat(mbx_fd, &statb) == 0 && statb.st_size < count) {
	    	lseek(mbx_fd, 0L, 2);
	    	goto patch;
	    }
	    printx(", mailbox lseek/read errors");
	    return (RP_LIO);
        }

	if (count == 0)
		return (RP_OK);
	if (!initstr (delim2, ldelim, count))
	{	/* previous didn't end cleanly. patch */
		ll_err (logptr, LLOGTMP, "bad delim; patching");
patch:		printx("\n\tpatching in missing delimiter");

		if ((write (mbx_fd, "\n", 1) != 1) ||
		    (write (mbx_fd, delim2, count) != count))
		{			/* write separator	*/
			ll_err (logptr, LLOGTMP, "unable to patch");
			return (RP_LIO);
		}
	}
	return (RP_OK);
}
/******************* MAILDELIVERY HANDLING ROUTINES ***************/

/*
 *	Given a maildelivery format file, this routine parses the
 *	file into an array - then calls dorules to follow the actions.
 *	The file is either personal
 *		e.g. .maildelvery
 *	or else system wide
 *		i.e. /usr/lib/maildlvry
 */

lo_dlvfile (dlv_file)
char	*dlv_file;		/* the file to scan */
{
	register FILE *fp;
	struct stat sb;
	Mdlvry  thefile[MAXLINES];	/* the representation of the md file */
	register Mdlvry *mptr, *maxptr;	/* some handy pointers  */
	char	dlvline[LINESIZE];
	int	len;

#ifdef	DEBUG
	ll_log (logptr, LLOGBTR, "lo_dlvfile(%s)", dlv_file);
#endif	DEBUG

	if ((!isstr(dlv_file)) || (fp = fopen(dlv_file, "r")) == NULL)
		return (RP_MECH);

	/*		Security Check!
	 *
	 *  The file must owned by the person we are delivering to
	 *  or the superuser.  In addition, the file must not have
	 *  write permission to anyone accept the owner.   (DPK)
	 *  In the case of system wide file, this is constrained to
	 *  be root generally. (JPO)
	 */

	if (fstat (fileno(fp), &sb) < 0
		|| (sb.st_uid != lo_pw->pw_uid && sb.st_uid != 0)
		|| sb.st_mode & 022) {
		printx("ownership problems on '%s'\n", dlv_file);
		ll_log (logptr, LLOGBTR, "ownership problems on '%s'",
				dlv_file);
		fclose(fp);
		return (RP_MECH);
	}

	for (mptr = thefile;
		(len = gcread(fp, dlvline, LINESIZE, "\n\377")) > 0
			&& mptr < &thefile[MAXLINES];)
	{
		if( dlvline[0] == '\n' || dlvline[0] == '#' )
			continue;	/* skip this line */

		dlvline[len - 1] = '\0'; /* zap the lf */

		if( parse_line(mptr, dlvline) == OK)
			mptr ++;
	}
	if ( mptr >= &thefile[MAXLINES] )
		ll_log (logptr, LLOGFST, "more than %d lines in %s (%s)",
			MAXLINES, dlv_file, lo_pw->pw_name);

	maxptr = mptr;
	fclose (fp);	/* OK, file finished, take a deep breath and ... */
	return lo_dorules(thefile, maxptr);
}

/*
 * apply the given rules. mptr is the begining of an array of
 * rules, mpmax is the last rule in the array + 1.
 */

lo_dorules(mptr, mpmax)	/* apply the maildelivwery rules */
Mdlvry	*mptr, *mpmax;
{
	int	doneparse = FALSE;	/* a couple of bools */
	int	delivered = FALSE;
	int	retval;
/*
 *	Now comes the bit where we try and be fearfully clever.
 *	We proceed down the array of md lines, whistling nochalantly,
 *	until we hit a situation where parsing must take place.
 *	At this point (with a sheepish grin) we then go and dig into
 *	the message and see what we can find, and then continue on as
 *	if nothing had happened.
 */

	for(; mptr < mpmax; mptr ++)
	{
		if( delivered && mptr->m_ar == M_CONDACC)
			continue;	/* this line can be ignored */
		if( mptr->m_parse && !doneparse)	/* arggh caught out! */
		{
			if(rp_isgood( dotheparse(mptr, mpmax)))
				doneparse = TRUE;
			else	return RP_NO;
		}
		if(! mptr->m_special )  /* this is a real header */
		{
			if( !mptr->m_hit)
				continue;	/* but no hit */
#ifdef	DEBUG
			ll_log (logptr, LLOGFTR, "hit with %s (%s)",
				mptr->m_pattern, mptr->m_header);
#endif	DEBUG
			/* else we drop through to beyond the switch */
		}	/* beware the dangling else !! */
		else	/* do the switch */
		{
/* Run through the special case headers, break if hit, continue if miss */
			switch( mptr->m_hdrtype)
			{
			case	M_DEFAULT:
				if( ! delivered ) /* this applys */
					break;
				continue;
			case	M_ADDRESS:
				if( strindex(mptr->m_pattern, lo_adr) >= 0)
					break;
				continue;
			case	M_TRUE: /* always true */
				break;
			case	M_SENDER:
				if( strindex(mptr->m_pattern, lo_sender) >= 0)
					break;
				continue;
			default: /* what else can we do ? */
				continue;
			}
		}
				/* OK, do the action */
		retval = lo_doaction(mptr);

		switch( mptr->m_ar)	/* now how did it go ? */
		{
			case	M_CONDACC:
			case	M_ACCEPT:
				if( rp_isgood(retval))  /* delivered */
					delivered = TRUE;
				else if(rp_gval(retval) != RP_NO)
					return RP_AGN;  /* went bad (temp) */
				break;
			case	M_REJECT:
				continue;
		}
	}
	return(delivered ? RP_MOK : RP_MECH);
}

parse_line(mptr, line)  /* dump the line into the Mdlvry struct */
Mdlvry  *mptr;
char	*line;
{
	char	*argv[15];	/* the argv array */
	char	opts[LINESIZE]; /* temp space for gathering opts */
	int	argc;
	int	type;
	int	i;
	Lookup  *lp;

#ifdef	DEBUG
	ll_log (logptr, LLOGBTR, "parse_line(%s)", line);
#endif	DEBUG

	argc = sstr2arg(line, 15, argv, " \t,");	/* split it */
	if( argc < 4 )  /* not good enough! */
		return NOTOK;
	if( (type = lookup(argv[0], spec_hdrs)) == -1 ) /* not a special ?*/
	{
		mptr->m_parse = TRUE;
		mptr->m_special = FALSE;
		mptr->m_header = strdup(argv[0]);
		mptr->m_pattern = strdup(argv[1]);
#ifdef	DEBUG
		ll_log (logptr, LLOGFTR, "header='%s' pat='%s'",
				mptr->m_header, mptr->m_pattern);
#endif	DEBUG
	}
	else	/* Oh, it is a special */
	{
		mptr->m_special = TRUE;
		mptr->m_parse = FALSE;
		mptr->m_hdrtype = type;
		mptr->m_header = NULL;
		mptr->m_pattern = strdup(argv[1]);
#ifdef	DEBUG
		ll_log (logptr, LLOGFTR, "special pat='%s'", mptr->m_pattern);
#endif	DEBUG
	}
	switch( argv[3][0] )	/* only a,A & ? - anything else == R */
	{
		case	'A':
		case	'a':
			mptr->m_ar = M_ACCEPT;
			break;
		case	'?':
			mptr->m_ar = M_CONDACC;
			break;
		default:
		case	'r':
		case	'R':
			mptr->m_ar = M_REJECT;
			break;
	}
#ifdef	DEBUG
	ll_log (logptr, LLOGFTR, "action=%o", mptr->m_ar);
#endif	DEBUG
	if( (type = lookup(argv[2], actions)) == -1)
		ll_log(logptr, LLOGFAT, "Unknown action to perform '%s'\n",
				argv[2]); /* a `never happens case' */
	else	mptr->m_action = type;
#ifdef	DEBUG
	ll_log (logptr, LLOGFTR, "action=%o", mptr->m_action);
#endif	DEBUG
	switch (mptr->m_action )
	{
		case	M_FILE:
			mptr->m_options = strdup(argv[4]);
			break;
		case	M_DESTROY:
			break;
		case	M_PIPE: /* gather together the argv's */
			opts[0] = '\0';
			for( i = 4; i < argc; i++)
			{
				strcat(opts, argv[i]);
				if( i != argc -1 )
					strcat(opts, " ");
			}
			mptr->m_dollar = 0;
			mptr->m_options = strdup(opts);
			for(lp = dolfields; lp->l_key != NULL; lp++)
				if( strindex(lp->l_key, opts) >= 0)
				{
					mptr->m_parse = TRUE;
					mptr->m_dollar |= lp->l_val;
				}
			break;
		default:
			break;
	}
	mptr->m_hit = 0;
#ifdef	DEBUG
	ll_log (logptr, LLOGFTR, "dol=%o parse=%o opts='%s'",
			mptr->m_dollar, mptr->m_parse, mptr->m_options);
#endif	DEBUG
	return OK;
}
lo_doaction(mp)
Mdlvry	*mp;
{
# ifdef	DEBUG
	ll_log (logptr, LLOGBTR, "lo_doaction");
#endif DEBUG

	switch( mp->m_action )
	{
		case	M_DESTROY:	/* /dev/null it */
			return (RP_MOK);

		/* pipe has gone away */
		case	M_PIPE:
			return lo_dopipe(mp->m_options);
		case	M_FILE:
			return lo_dofile(mp->m_options);
		default:
			ll_log( logptr, LLOGFAT, "unidentified action type %o",
					mp->m_action);
			return RP_NO;
	}
	/* NOTREACHED */
}
/**************** PARSING DELIVERY ROUTINES *************** */

/*
 * the routine that actually parses the message and sets up hit/miss
 * bits in the md struct.
 */

dotheparse(mpbase, mpmax)
Mdlvry  *mpbase, *mpmax;
{
	char	line[LINESIZE];
	char	name[LINESIZE], contents[LINESIZE];
	Mdlvry  *mp;
	int	len;
	int	gotrepl = 0;

# ifdef DEBUG
	ll_log (logptr, LLOGBTR, "dotheparse()");
# endif DEBUG
	/* start reading header */
	qu_rtinit(0L);

	while((len = hdr_line(line, LINESIZE)) > 0)
	{
		line[len] = '\0';
		switch(hdr_parse(line, name, contents))
		{
		case	HDR_NAM:
			continue;	/* to avoid break */

		case	HDR_EOH:
			break;	/* break out of loop below */

		case	HDR_NEW:
		case	HDR_MOR:
			for(mp = mpbase; mp < mpmax; mp++)
			{
				if( !mp->m_hit && lexequ(mp->m_header, name) &&
					strindex(mp->m_pattern, contents) >= 0)
				{
					mp->m_hit = TRUE;
#ifdef	DEBUG
			ll_log (logptr, LLOGFTR, "hit in parse '%s' & '%s'",
					contents, mp->m_pattern);
#endif	DEBUG					
				}
				if(( mp->m_dollar & 1))
					if(! gotrepl && lexequ(name, "from"))
						(void) strcpy(lo_replyto, contents);
					else if(lexequ(name, "reply-to"))
					{
						(void) strcpy(lo_replyto, contents);
						gotrepl = TRUE;
					}
			}
			continue;
		} /* end switch */
	    break;
	}

	if( len == -1)
		return RP_NO;
	return  RP_OK;
}

/* basic processing of incoming header lines */
LOCFUN
hdr_line(buf, len)
char *buf;
int len;
{
    register int count;
    static char hdr_buf[LINESIZE];	/* buffer */
    static int buf_cnt = 0;	/* chars in buf */
    static char *buf_ptr;
    register char *hp;
    register char *bp;

    count = len;
    hp = buf_ptr;
    bp = buf;
    count--;
    while (count > 0)
    {
	if (buf_cnt == 0)
	{
	    buf_cnt = sizeof(hdr_buf);
	    if (qu_rtxt(hdr_buf,&buf_cnt) != RP_OK)
		buf_cnt = 0;	/* break below */
	    hp = hdr_buf;
	}
	
	/* any more chars? */
	if (buf_cnt <= 0)
	    break;

	count--;

	if ((*bp++ = *hp++) == '\n')
	    break;
    }

    *bp++ = '\0';
    buf_ptr = hp;

#ifdef DEBUG
    ll_log(logptr, LLOGFTR, "hdr_line (%s)",buf);
#endif

    return(len - count);
}

LOCFUN
hdr_parse (src, name, contents)	  /* parse one header line		*/
    register char *src;		  /* a line of header text		*/
    char *name,			  /* where to put field's name		*/
	 *contents;		  /* where to put field's contents	*/
{
    extern char *compress ();
    char linetype;
    register char *dest;

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "hdr_parse(%s)", src);
#endif

    if (isspace (*src))
    {				  /* continuation text			*/
#ifdef DEBUG
	ll_log (logptr, LLOGFTR, "cmpnt more");
#endif
	if (*src == '\n')
	    return (HDR_EOH);
	linetype = HDR_MOR;
    }
    else
    {				  /* copy the name			*/
	linetype = HDR_NEW;
	for (dest = name; *dest = *src++; dest++)
	{
	    if (*dest == ':')
		break;		  /* end of the name			*/
	    if (*dest == '\n')
	    {			  /* oops, end of the line		*/
		*dest = '\0';
		return (HDR_NAM);
	    }
	}
	*dest = '\0';
	compress (name, name);	  /* strip extra & trailing spaces	*/
#ifdef DEBUG
	ll_log (logptr, LLOGFTR, "cmpnt name '%s'", name);
#endif
    }

    for (dest = contents; isspace (*src); )
	if (*src++ == '\n')	  /* skip leading white space		*/
	{			  /* unfulfilled promise, no contents	*/
	    *dest = '\0';
	    return ((linetype == HDR_MOR) ? HDR_EOH : linetype);
	}			   /* hack to fix up illegal spaces	 */

    while ((*dest = *src) != '\n' && *src != 0)
	     src++, dest++;	  /* copy contents and then, backup	*/
    while (isspace (*--dest));	  /*   to eliminate trailing spaces	*/
    *++dest = '\0';

    return (linetype);
}
/***************  (misc) MISCELLANEOUS ROUTINES  ******************** */

/*
 *	Dig into table for a match - return associated value else -1
 *	table ends when key == 0;
 *		(JPO)
 */

LOCFUN
lookup( str, table )
char	*str;
Lookup  table[];
{
	register Lookup *p;

	for(p = table; p -> l_key ; p++)
	{
/*
 * match with prefix, this allows "addr" to match "address"
 */
		if( prefix(p -> l_key, str) )
			return (p -> l_val);
	}
	return (-1);
}

/*
 * setup the environment for the process to run in. This includes :-
 *	Environment variables
 *	file descriptors - close all but fd0
 *	process groups
 *	controlling ttys.
 *	umask
 */

LOCFUN setupenv()
{
	int	fd;
	static char	homestr[64];		/* environment $HOME param */
	static char	shellstr[64];		/* environment $SHELL param */
	static char	userstr[64];		/* environment $USER param */

#ifdef	DEBUG
	ll_log (logptr, LLOGBTR, "setupenv()");
#endif	DEBUG
	for (fd = 1; fd < numfds; fd++)
		(void) close (fd);
	fd = open ("/dev/null", 1);	/* stdout */
	dup (fd);			/* stderr */

	umask(077);	/* Restrictive umask */

#ifdef TIOCNOTTY
	if (setjmp (timerest) == 0)
	{
		flgtrest = TRUE;
		s_alarm(15);	/* should be enough */
		if( (fd = open("/dev/tty", 2)) != NOTOK) {
			(void) ioctl(fd, TIOCNOTTY, (char *)0);
			(void) close(fd);
		}
		s_alarm(0);
	}
	else
	{
		flgtrest = FALSE;
		ll_log (logptr, LLOGGEN, "TIOCNOTTY not available");
	}
#endif TIOCNOTTY
#ifdef V4_2BSD
	setpgrp (0, getpid());
#endif
	sprintf (homestr, "HOME=%s", lo_pw->pw_dir);
	sprintf (shellstr, "SHELL=%s",
			isstr(lo_pw->pw_shell) ? lo_pw->pw_shell : "/bin/sh");
	sprintf (userstr, "USER=%s", lo_pw->pw_name);
	envp[0] = homestr;
	envp[1] = shellstr;
	envp[2] = userstr;
}

onpipe()
{
	signal(SIGPIPE, onpipe);
	sigpipe = TRUE;
}
;
	}

	if( len == -1)
		return RP_NO;
	return  RP_OK;
}

/* basic processing of incoming header lines */
LOCFUN
hdr_line(buf, len)
char *buf;
int len;
{
    register int count;
    static char hdr_buf[LINESIZE];	/* buffer */
    static int buf_cnt = 0;	/* chars in buf */
    static char *buf_ptr;
 mmdf/src/local/qu2lo_send.c   444      0     12       14556  3671073222  11106 /*
 *     MULTI-CHANNEL MEMO DISTRIBUTION FACILITY  (MMDF)
 *
 *
 *     Copyright (C) 1979,1980,1981  University of Delaware
 *
 *     Department of Electrical Engineering
 *     University of Delaware
 *     Newark, Delaware  19711
 *
 *     Phone:  (302) 738-1163
 *
 *
 *     This program module was developed as part of the University
 *     of Delaware's Multi-Channel Memo Distribution Facility (MMDF).
 *
 *     Acquisition, use, and distribution of this module and its listings
 *     are subject restricted to the terms of a license agreement.
 *     Documents describing systems using this module must cite its source.
 *
 *     The above statements must be retained with all copies of this
 *     program and may not be removed without the consent of the
 *     University of Delaware.
 *
 *
 *     version  -1    David H. Crocker    March   1979
 *     version   0    David H. Crocker    April   1980
 *     version  v7    David H. Crocker    May     1981
 *     version   1    David H. Crocker    October 1981
 *
 */
/*                  SEND FROM DELIVER TO LOCAL MACHINE
 *
 *  Jul 81 Dave Crocker     change to have a child handle actual delivery,
 *          + BRL crew      setuid'ing to recipient.
 *  Aug 81 Steve Bellovin   mb_unlock() added to x2y_each
 *  Apr 84 Doug Kingston    Major rewrite.
 *  Aug 84 Julian Onions    Extended the delivery file syntax quite a bit
 *  Feb 85 Julian Onions    Reworked to parse only if really really necessary
 */

#include "util.h"
#include "mmdf.h"
#include "phs.h"
#include "ap.h"
#include "ch.h"
#include <pwd.h>
#include <sys/stat.h>
#include <signal.h>
#include <sgtty.h>
#include "adr_queue.h"

char    lo_info[2 * LINESIZE],
	lo_sender[2 * LINESIZE],
	lo_adr[2 * LINESIZE],
	lo_replyto[2 * LINESIZE],
	lo_size[16],
	*lo_parm;

struct passwd *lo_pw;

extern Chan     *chanptr;
extern LLog     *logptr;

extern int      errno;

extern char     *qu_msgfile;          /* name of file containing msg text   */
extern long     qu_msglen;

extern char *index();
extern struct passwd *getpwmid();

LOCVAR struct rp_construct
rp_aend  =
{
	RP_OK, 'l', 'o', 'c', ' ', 'e', 'n', 'd', ' ', 'o', 'f', ' ', 'a',
	'd', 'd', 'r', ' ', 'l', 'i', 's', 't', '\0'
},
rp_hend  =
{
	RP_NOOP, 'e', 'n', 'd', ' ', 'o', 'f', ' ', 'h', 'o', 's', 't', ' ',
	'i', 'g', 'n', 'o', 'r', 'e', 'd', '\0'
};

/**/

qu2lo_send ()                       /* overall mngmt for batch of msgs    */
{
	short     result;

#ifdef DEBUG
	ll_log (logptr, LLOGBTR, "qu2lo_send ()");
#endif

	if (rp_isbad (result = qu_pkinit ()))
		return (result);

	/* While there are messages to process... */
	for(;;){
		result = qu_rinit (lo_info, lo_sender, chanptr -> ch_apout);
		if(rp_gval(result) == RP_NS){
			qu_rend();
			continue;
		}
		else if(rp_gval(result) != RP_OK)
			break;
		phs_note (chanptr, PHS_WRSTRT);

		sprintf (lo_size, "%ld", qu_msglen);
		result = qu2lo_each ();
		qu_rend();
		if (rp_isbad (result))
			return (result);
	}

	if (rp_gval (result) != RP_DONE)
	{
		ll_log (logptr, LLOGTMP, "not DONE (%s)", rp_valstr (result));
		return (RP_RPLY);         /* catch protocol errors      */
	}

	qu_pkend ();                  /* done getting messages          */
	phs_note (chanptr, PHS_WREND);

	return (result);
}
/**/

LOCFUN
qu2lo_each ()               /* send copy of text per address      */
{
	RP_Buf  replyval;
	short   result;
	char    host[LINESIZE];

#ifdef DEBUG
	ll_log (logptr, LLOGBTR, "qu2lo_each()");
#endif
	/* while there are addessses to process... */
	FOREVER
	{
		result = qu_radr (host, lo_adr);
		if (rp_isbad (result))
			return (result);      /* get address from Deliver */

		if (rp_gval (result) == RP_HOK)
		{                       /* no-op the sub-list indication */
			qu_wrply ((RP_Buf *) &rp_hend, rp_conlen (rp_hend));
			continue;
		}

		if (rp_gval (result) == RP_DONE)
		{
			qu_wrply ((RP_Buf *) &rp_aend, rp_conlen (rp_aend));
			return (RP_OK);       /* end of address list */
		}

		replyval.rp_val = lo_verify ();
		if (rp_isbad (replyval.rp_val)) {
			qu_wrply (&replyval, (sizeof replyval.rp_val));
			continue;                       /* go for more! */
		}

		replyval.rp_val = lo_master ();
		switch (rp_gval (replyval.rp_val)) {
		case RP_LIO:
		case RP_TIME:
			/* RP_AGN prevents deliver from calling us dead */
			replyval.rp_val = RP_AGN;
		case RP_LOCK:
		case RP_USER:         /* typicial valid responses */
		case RP_BHST:
		case RP_NOOP:
		case RP_NO:
		case RP_MOK:
		case RP_AOK:
		case RP_OK:
			break;
		default:              /* not expected so may abort          */
			if (rp_isgood (replyval.rp_val))
			{
				ll_log (logptr, LLOGFAT,
				"lo_wadr = %s", rp_valstr (replyval.rp_val));
				replyval.rp_val = RP_RPLY;  /* Conservative */
			}
		}
		qu_wrply (&replyval, (sizeof replyval.rp_val));
	}
}

LOCFUN
lo_verify ()                    /* send one address spec to local     */
{
	char    mailid[MAILIDSIZ];      /* mailid of recipient */
	register char   *p;
	extern char     *strncpy();
#ifdef DEBUG
	ll_log (logptr, LLOGBTR, "lo_verify()");
#endif
	/* Strip any trailing host strings from address */
	if (p = index (lo_adr, '@'))
		*p = '\0';

	for (p = lo_adr; ; p++) {
		switch (*p) {
		case '|':
		case '/':
		case '=':
		case '\0':
			lo_parm = p;
			strncpy (mailid, lo_adr, p - lo_adr);
			mailid[p - lo_adr] = '\0';
			goto checkit;
		}
	}

checkit:
	if ((lo_pw = getpwmid (mailid)) == (struct passwd *) NULL) {
		ll_err (logptr, LLOGTMP, "user '%s' unknown", mailid);
		return (RP_USER);     /* can't deliver to non-persons       */
	}
	return (RP_OK);          /* Invalid addresss or real problems */
}

LOCFUN
lo_master()
{
	int   childid;                /* child does the real work           */

	switch (childid = fork ()) {
	case NOTOK:
		return (RP_LIO);        /* problem with system */

	case OK:
		ll_close (logptr);      /* since the process is splitting */

#ifdef V4_2BSD
		if (initgroups (lo_pw->pw_name, lo_pw->pw_gid) == NOTOK
		  || setgid (lo_pw->pw_gid) == NOTOK
#else
		if (setgid (lo_pw->pw_gid) == NOTOK
#endif V4_2BSD
		  || setuid (lo_pw->pw_uid) == NOTOK) {
			ll_err (logptr, LLOGTMP, "can't set id's (%d,%d)",
				lo_pw->pw_uid, lo_pw->pw_gid);
			exit (RP_BHST);
		}

		if (chdir (lo_pw->pw_dir) == NOTOK) {
			/* move out of MMDF queue space       */
			ll_err (logptr, LLOGTMP, "can't chdir to '%s'",
				lo_pw->pw_dir);
			exit (RP_LIO);
		}

		exit (lo_slave());

	default:                  /* parent just waits  */
		return (pgmwait (childid));
	}
	/* NOTREACHED */
}
f msgs    */
{
	short     result;

#ifdef DEBUG
	ll_log (logptr, LLOGBTR, "qu2lo_send ()");
#endif

	if (rp_isbad (result = qu_pkinit ()))
		returmmdf/src/local/gen   555      0     12          57  3620510517   7272 make -f ../../Makefile.com -f Makefile.real $*
lyval.rp_val = lo_master ();
		switch (rp_gval (replyval.rp_val)) {
		case RP_LIO:
		case RP_TIME:
			/* RP_AGN prevents deliver from calling us dead */
			replyval.rp_val = RP_AGN;
		case RP_LOCK:
		case RP_USER:         /* typicial valid responses */
		case RP_BHST:
		case RP_NOOP:
		case RP_NO:
		case RP_MOK:
		case RP_AOK:
		case RP_OK:
			break;
		default:              /* not expected so may abort          */
			if (rp_isgood (replyval.rp_val))
			{
				llmmdf/src/phone/   755      0     12           0  3664425311   6572 mmdf/src/phone/gen   555      0     12          57  3620510517   7311 make -f ../../Makefile.com -f Makefile.real $*
lyval.rp_val = lo_master ();
		switch (rp_gval (replyval.rp_val)) {
		case RP_LIO:
		case RP_TIME:
			/* RP_AGN prevents deliver from calling us dead */
			replyval.rp_val = RP_AGN;
		case RP_LOCK:
		case RP_USER:         /* typicial valid responses */
		case RP_BHST:
		case RP_NOOP:
		case RP_NO:
		case RP_MOK:
		case RP_AOK:
		case RP_OK:
			break;
		default:              /* not expected so may abort          */
			if (rp_isgood (replyval.rp_val))
			{
				llmmdf/src/phone/qu2ph_send.c   444      0     12       24044  3664425202  11114 #include "util.h"
#include "mmdf.h"

/*
 *     MULTI-CHANNEL MEMO DISTRIBUTION FACILITY  (MMDF)
 *
 *
 *     Copyright (C) 1979,1980,1981  University of Delaware
 *
 *     Department of Electrical Engineering
 *     University of Delaware
 *     Newark, Delaware  19711
 *
 *     Phone:  (302) 738-1163
 *
 *
 *     This program module was developed as part of the University
 *     of Delaware's Multi-Channel Memo Distribution Facility (MMDF).
 *
 *     Acquisition, use, and distribution of this module and its listings
 *     are subject restricted to the terms of a license agreement.
 *     Documents describing systems using this module must cite its source.
 *
 *     The above statements must be retained with all copies of this
 *     program and may not be removed without the consent of the
 *     University of Delaware.
 *     
 *
 *     version  -1    David H. Crocker    March   1979
 *     version   0    David H. Crocker    April   1980
 *     version  v7    David H. Crocker    May     1981
 *     version   1    David H. Crocker    October 1981
 *
 */
/*  CH_PHONE:  deliver-to-phone transmission                            */
/*                                                                      */

/*  Jun 81  D. Crocker      Add d2p_nadrs mechanism to send no msg text
 *                          when there are not valid addresses
 *  Jul 81  D. Crocker      Change state variable values, for more robust
 *                          state diagrams, to avoid hang with Deliver
 */


#include "ch.h"
#include "chan.h"
#include "phs.h"
#include "ap.h"

extern struct ll_struct  *logptr;
extern Chan *curchan;
extern char *strdup();
extern char *ap_p2s();
extern int  ap_outtype;
extern char *supportaddr;
extern char *mmdflogin;

/*#define RUNALON   */

LOCVAR int numadrs;                    /* number of valid addresses          */
LOCVAR char *sender = (char *)NULL;    /* return address for message         */
LOCVAR char *adr    = (char *)NULL;    /* recipient address                  */
LOCVAR struct rp_construct
			    rp_hend  =
{
    RP_NOOP, 'e', 'n', 'd', ' ', 'o', 'f', ' ', 'h', 'o', 's', 't', ' ',
    'i', 'g', 'n', 'o', 'r', 'e', 'd', '\0'
};

char ph_state = SND_RINIT;  /* state of processing current msg    */


qu2ph_send (chname)                 /* overall mngmt for batch of msgs    */
    char chname[];                /* name of channel we are             */
{
    short     result;
    char    info[LINESIZE],
	    sendbuf[ADDRSIZE];

    if (rp_isbad (result = qu_pkinit ()))
	return (result);

    if (rp_isbad (result = ph_sbinit ()))
    {
	printx ("remote site refuses to receive mail... ");
	return (result);
    }

    ph_state = SND_ABORT;
    for(;;){
	AP_ptr  loctree, domtree, routree, sendtree;
	AP_ptr  ap_s2tree(), ap_normalize();

	result = qu_rinit (info, sendbuf, curchan->ch_apout);
	if(rp_gval(result) == RP_NS){
	    qu_rend();
	    continue;
	}
	if(rp_gval(result) == RP_DONE)
	    break;

	if (rp_gval(result) == RP_FIO)          /* Can't open message file */
	    continue;
	else if (rp_gval(result) != RP_OK)      /* Some other error */
	    break;

	if (sender != (char *)NULL){
	    free (sender);
	    sender = NULL;
	}

	if ( sendbuf[0] == '\0' ||
	    (sendtree = ap_s2tree( sendbuf )) == (AP_ptr) NOTOK) {
		printx("return address unparseable, using Orphanage\n");
		fflush(stdout);
		if ((sendtree = ap_s2tree( supportaddr )) == (AP_ptr) NOTOK) {
		    printx("Orphanage unparseable, using MMDF\n");
		    fflush(stdout);
		} else
		if ((sendtree = ap_s2tree( mmdflogin )) == (AP_ptr) NOTOK) {
		    result = RP_PARM;
		    qu_rend();
		    goto bugout;
		}
	}
	ap_outtype = curchan -> ch_apout;
	sendtree = ap_normalize (curchan -> ch_lname,
			curchan  -> ch_ldomain, sendtree, curchan);
	if(sendtree == (AP_ptr)MAYBE){
	    result = RP_NS;
	    qu_rend();
	    goto bugout;
	}
	ap_t2parts (sendtree, (AP_ptr *)0, (AP_ptr *)0, &loctree,
			&domtree, &routree);
	sender = ap_p2s ((AP_ptr)0, (AP_ptr)0, loctree, domtree, routree);
	if(sender == (char *)MAYBE){
	    result = RP_NS;
	    qu_rend();
	    goto bugout;
	}
	ap_sqdelete( sendtree, (AP_ptr) 0 );
	ap_free( sendtree );

	ph_state = SND_RDADR;
	if (rp_isbad (result = ph_winit (chname, info, sender)))
	    return (result);      /* ready to process a message         */

	if (rp_isbad (result = q2p_admng ()))
	    return (result);      /* send the address list              */

	if (rp_gval (result) != RP_DONE)
	    break;                /* catch protocol errors              */

	if (rp_isbad (result = q2p_txmng ()))
	    return (result);      /* send the message text              */

	qu_rend();
    }
    qu_rend();

    if (rp_gval (result) != RP_DONE)
    {
	ll_log (logptr, LLOGTMP, "not DONE (%s)", rp_valstr (result));
	return (RP_RPLY);         /* catch protocol errors              */
    }

bugout:
    ph_state = SND_ABORT;
    qu_pkend ();                  /* done getting messages              */
    if (rp_isbad (result = ph_sbend ()))
				  /* done sending messages              */
	printx ("bad ending for outgoing submission... ");

    return (result);
}
/**/

LOCFUN
	q2p_admng ()              /* send address list                  */
{
    struct rp_bufstruct thereply;
    short     result;
    int       len;
    char    host[ADDRSIZE];
    char    adrbuf[ADDRSIZE];

    AP_ptr  loctree, domtree, routree, adrtree;
    AP_ptr  ap_s2tree(), ap_normalize();

    for (numadrs = 0; ; )         /* reset for each new messages        */
    {                             /* we already have and adr            */
	ph_state = SND_ABORT;
	result = qu_radr (host, adrbuf);
	if (rp_isbad (result))
	    return (result);      /* get address from Deliver           */

	if (rp_gval (result) == RP_HOK)
	{                         /* no-op the sub-list indication      */
	    qu_wrply ((struct rp_bufstruct *) &rp_hend, rp_conlen (rp_hend));
	    continue;
	}

	if (rp_gval (result) == RP_DONE)
	    break;                /* end of address list                */

	if (adr != (char *)NULL)
		free (adr);

	if ( (adrtree = ap_s2tree(adrbuf)) == (AP_ptr) NOTOK)  {
	    adr = strdup( adrbuf );
	} else {
	    ap_outtype = curchan -> ch_apout;
	    adrtree = ap_normalize (curchan -> ch_lname,
				curchan  -> ch_ldomain, adrtree, curchan);
	    if(adrtree == (AP_ptr)MAYBE){
		result = RP_NS;
		goto bugout;
	    }
	    ap_t2parts (adrtree, (AP_ptr *)0, (AP_ptr *)0, &loctree,
			&domtree, &routree);
	    adr = ap_p2s ((AP_ptr)0, (AP_ptr)0, loctree, domtree, routree);
	    if(adr == (char *)MAYBE){
		result = RP_NS;
		goto bugout;
	    }
	    ap_sqdelete( adrtree, (AP_ptr) 0 );
	    ap_free( adrtree );
	}

	ph_state = SND_ARPLY;
	if (rp_isbad (result = ph_wadr (host, adr)))
	    return (result);      /* give to remote site                */

	if (rp_isbad (result = ph_rrply (&thereply, &len)))
	    return (result);      /* how did remote like it?            */

bugout:
	switch (rp_gval (thereply.rp_val))
	{                         /* was address acceptable?            */
	    case RP_AOK:          /* address ok, text not yet sent      */
		numadrs++;
		ph_state = SND_ABORT;
		qu_wrply (&thereply, len);
		break;

	    case RP_NO:           /* remaining acceptible responses     */
		thereply.rp_val = RP_NDEL;
	    case RP_NS:
	    case RP_USER:
	    case RP_NDEL:
	    case RP_AGN:
	    case RP_NOOP:
		ph_state = SND_ABORT;
		qu_wrply (&thereply, len);
		break;

	    default:              /* responses which force abort        */
		if (rp_isbad (thereply.rp_val))
		    return (thereply.rp_val);
		return (RP_RPLY);
	}
    }
    ph_state = SND_TRPLY;
    return (ph_waend ());        /* tell remote of address list end    */
}
/**/

LOCFUN
	q2p_txmng ()              /* send message text                  */
{
    struct rp_bufstruct thereply;
    int     len;
    short   result;
    char    buffer[BUFSIZ];
    static char faktxt[] = "   ";       /* to keep dial happy   */

/*  the main portion handles normal transmission, when there have been
 *  some addresses accepted.  when no addresses have been accepted, then
 *  the text will be ignored by the receiving side.  due to a problem
 *  with the dial package, you must send some text.  this is handled
 *  in the 'else' condition.
 */
    printx ("\tsending text...:");
    fflush (stdout);
    if (numadrs > 0)            /* really need to send the text       */
    {
	len = sizeof(buffer);
	for (qu_rtinit (0L);
		(rp_gval (result = qu_rtxt (buffer, &len))) == RP_OK; )
	{
	    printx (".");         /* to show watcher we're working      */
	    fflush (stdout);
	    if (rp_isbad (result = ph_wtxt (buffer, len)))
		return (result);                         
	    if (rp_gval (result) != RP_OK)
		return (RP_RPLY);
	    len = sizeof(buffer);
	}
	printx (" ");             /* keep it pretty                     */
	fflush (stdout);

	if (rp_isbad (result))
	    return (result);
	if (rp_gval (result) != RP_DONE)
	    return (RP_RPLY);     /* didn't get it all across?          */
    }
    else                          /* send some text to keep dial happy  */
    {
	if (rp_isbad (result = ph_wtxt (faktxt, (sizeof faktxt) - 1)))
	    return (result);
	if (rp_gval (result) != RP_OK)
	    return (RP_RPLY);
    }


    if (rp_isbad (result = ph_wtend ()))
	return (result);          /* flag end of message                */

    if (rp_isbad (result = ph_rrply (&thereply, &len)))
	return (result);          /* problem getting reply?             */

    switch (rp_gval (thereply.rp_val))
    {                             /* was text acceptable?               */
	case RP_OK: 
	    thereply.rp_val = RP_MOK;
	case RP_MOK:              /* text was accepted                  */
	    qu_wrply (&thereply, len);
	    break;

	case RP_NO:               /* remaining acceptible responses     */
	    thereply.rp_val = RP_NDEL;
	case RP_NDEL: 
	case RP_AGN: 
	case RP_NOOP: 
	    qu_wrply (&thereply, len);
	    thereply.rp_val = RP_OK;
	    break;                /* don't abort process                */

	default:                  /* responses which force abort        */
	    if (rp_isbad (thereply.rp_val))
		return (thereply.rp_val);
	    return (RP_RPLY);
    }
    return (thereply.rp_val);     /* just quote remote                  */
}
urchan -> ch_apout;
	    adrtree = ap_normalize (curchan -> ch_lname,
				curchan  -> ch_ldomain, adrtree, curchan);
	    if(adrtree == (AP_ptr)MAYBE){
		result = RP_NS;
		goto bugout;
	    }
	    ap_t2parts (adrtree, (AP_ptr *)0, (AP_ptr *)0, &loctree,
			&domtree, &routree);
	    adr = ap_p2s ((AP_ptr)0, (AP_ptr)0, loctree, domtree, routree);
	    if(adr == (char *)MAYBE){
		result = RP_NS;
		goto bugout;
	    }
	    ap_sqdelete( adrtree, (AP_ptr) 0 );
	    ap_free( adrmmdf/src/phone/ph_rdmail.c   444      0     12        7311  3620510517  10755 #include "util.h"
#include "mmdf.h"
/*
 *     MULTI-CHANNEL MEMO DISTRIBUTION FACILITY  (MMDF)
 *     
 *
 *     Copyright (C) 1979,1980,1981  University of Delaware
 *     
 *     Department of Electrical Engineering
 *     University of Delaware
 *     Newark, Delaware  19711
 *
 *     Phone:  (302) 738-1163
 *     
 *     
 *     This program module was developed as part of the University
 *     of Delaware's Multi-Channel Memo Distribution Facility (MMDF).
 *     
 *     Acquisition, use, and distribution of this module and its listings
 *     are subject restricted to the terms of a license agreement.
 *     Documents describing systems using this module must cite its source.
 *
 *     The above statements must be retained with all copies of this
 *     program and may not be removed without the consent of the
 *     University of Delaware.
 *     
 *
 *     version  -1    David H. Crocker    March   1979
 *     version   0    David H. Crocker    April   1980
 *     version  v7    David H. Crocker    May     1981
 *     version   1    David H. Crocker    October 1981
 *
 */

extern struct ll_struct   *logptr;

/* **************  (ph_)  PHONE MAIL-READING SUB-MODULE  ************** */

LOCVAR short  ph_nadrs;             /* number of addresses this message   */
LOCVAR  long ph_nchrs;            /* number of chars in msg text        */

ph_rinit (info, retadr)          /* get initialization info for msg    */
char   *info,			  /* where to put general init info     */
       *retadr;			  /* where to put return address        */
{
    short     retval;
    int       len;
    char    linebuf[LINESIZE];
    char   *fromptr,
           *toptr;

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "ph_rinit");
#endif

    ph_nadrs = 0;
    ph_nchrs = 0L;

    retval = ph_rrec (linebuf, &len);
    if (rp_gval (retval) != RP_OK)
	return (retval);

    for (fromptr = linebuf, toptr = info;
	    *fromptr != '\n' && *fromptr != ';';
	    *toptr++ = *fromptr++)
	switch (*fromptr)	  /* mailing mode                         */
	{			  /* make sure only has ok values         */
	    case ' ': 
	    case 'A':
	    case 'a': 
	    case 'M':
	    case 'm': 
	    case 'S':
	    case 's': 
		break;		  /* ok to copy to info field             */

	    default: 		  /* invalid code                         */
		ll_log (logptr, LLOGFAT,
			"illegal mode value: %c", *fromptr);
		return (RP_NO);
	};
    *toptr = '\0';

    for (toptr = retadr, fromptr++;
	    !isnull (*fromptr) && *fromptr != '\n';
	    *toptr++ = *fromptr++);
    *toptr = '\0';		  /* copy return address                */

    return (RP_OK);
}
/**/

ph_radr (host, adr)              /* get an address spec from remote    */
char   *host,			  /* where to stuff name of next host   */
       *adr;			  /* where to stuff rest of address     */
{
    short     retval;
    int       len;

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "ph_radr()");
#endif

    *host = '\0';		  /* no-op this field                   */

    retval = ph_rrec (adr, &len);
    if (rp_gval (retval) != RP_OK)
	return (retval);

    ph_nadrs++;

#ifdef DEBUG
    ll_log (logptr, LLOGFTR, "'%s'", adr);
#endif

    return (RP_OK);
}

ph_rtxt (buffer, len)            /* read next part of msg text         */
char   *buffer;			  /* where to stuff copy of text        */
int     *len;                      /* where to indicate text's length    */
{
    short     retval;

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "ph_rtxt()");
#endif

    retval = ph_rstm (buffer, len);
    if (rp_gval (retval) != RP_OK)
	return (retval);

    ph_nchrs += (long) *len;

#ifdef DEBUG
    ll_log (logptr, LLOGFTR, "'%s'", buffer);
#endif

    return (RP_OK);
}

ph_rtend ()
{                               /* end of message text */
    return (RP_OK);
}
ical Engineering
 *     University of Delaware
 *     Newark, Delaware  19711
 *
 *     Phone:  (302) 738-1163
 *     
 *     
 *     This program module was developed as part of the University
 *     of Delaware's Multi-Channel Memo Distribution Facility (MMDF).
 *     
 *     Acquisition, use, and distributimmdf/src/phone/ph_wtmail.c   444      0     12        6437  3620510520  11004 #include "util.h"
#include "mmdf.h"
/*
 *     MULTI-CHANNEL MEMO DISTRIBUTION FACILITY  (MMDF)
 *     
 *
 *     Copyright (C) 1979,1980,1981  University of Delaware
 *     
 *     Department of Electrical Engineering
 *     University of Delaware
 *     Newark, Delaware  19711
 *
 *     Phone:  (302) 738-1163
 *     
 *     
 *     This program module was developed as part of the University
 *     of Delaware's Multi-Channel Memo Distribution Facility (MMDF).
 *     
 *     Acquisition, use, and distribution of this module and its listings
 *     are subject restricted to the terms of a license agreement.
 *     Documents describing systems using this module must cite its source.
 *
 *     The above statements must be retained with all copies of this
 *     program and may not be removed without the consent of the
 *     University of Delaware.
 *     
 *
 *     version  -1    David H. Crocker    March   1979
 *     version   0    David H. Crocker    April   1980
 *     version  v7    David H. Crocker    May     1981
 *     version   1    David H. Crocker    October 1981
 *
 */

extern struct ll_struct   *logptr;

/* **************  (ph_)  PHONE MAIL-WRITING SUB-MODULE  ************** */

LOCVAR short  ph_nadrs;             /* number of addresses this message   */
LOCVAR  long ph_nchrs;            /* number of chars in msg text        */

/* ARGSUSED */

ph_winit (vianet, info, retadr)  /* pass msg initialization info       */
char    vianet[];		  /* what channel coming in from        */
char    info[],			  /* general info                       */
        retadr[];		  /* return address for error msgs      */
{
    short     retval;
    char  initbuf[LINESIZE];

/* DBG:  make sure info has right form */

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "ph_winit");
#endif

    ph_nadrs = 0;
    ph_nchrs = 0L;

    sprintf (initbuf, "%s;%s", info, retadr);
				  /* slave:  <info> \n <retadr>    */
    retval = ph_wrec (initbuf, strlen (initbuf));

    return (retval);
}
/**/
/*ARGSUSED*/
ph_wadr (host, adr)               /* send one address spec to local     */
char	*host;			  /* not used on phone channel */
char	*adr;			  /* "next" location part of address    */
{
    extern char *strdup ();
    short     retval;
    register char  *adrstr;

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "ph_wadr()");
#endif

    ph_nadrs++;

    adrstr = strdup (adr);
				  /* tell the user, if watching         */
    retval = ph_wrec (adrstr, strlen (adrstr));

    free (adrstr);
    return (retval);
}

ph_waend ()                      /* end of address lsit                */
{
    short     retval;

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "ph_waend");
#endif

    if (rp_isbad (retval = ph_wrec ((char *) 0, 0)))
	return (retval);
    return (RP_DONE);
}
/**/

ph_wtxt (buffer, len)            /* send next part of msg text         */
char    buffer[];		  /* the text                           */
short     len;                      /* length of text                     */
{
#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "ph_wtxt()");
#endif

    ph_nchrs += (long) len;

    return (ph_wstm (buffer, len));
}

ph_wtend ()                      /* end of message text                */
{
#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "ph_wtend");
#endif

    return (ph_wstm ((char *) 0, 0));
				/* flush text buffer / send eos       */
}
  Phone:  (302) 738-1163
 *     
 *     
 *     This program module was developed as part of the University
 *     of Delaware's Multi-Channel Memo Distribution Facility (MMDF).
 *     
 *     Acquisition, use, and distributimmdf/src/phone/ph_slave.c   444      0     12       16545  3620510520  10642 #include "util.h"
#include "mmdf.h"

/*
 *     MULTI-CHANNEL MEMO DISTRIBUTION FACILITY  (MMDF)
 *     
 *
 *     Copyright (C) 1979,1980,1981  University of Delaware
 *     
 *     Department of Electrical Engineering
 *     University of Delaware
 *     Newark, Delaware  19711
 *
 *     Phone:  (302) 738-1163
 *     
 *     
 *     This program module was developed as part of the University
 *     of Delaware's Multi-Channel Memo Distribution Facility (MMDF).
 *     
 *     Acquisition, use, and distribution of this module and its listings
 *     are subject restricted to the terms of a license agreement.
 *     Documents describing systems using this module must cite its source.
 *
 *     The above statements must be retained with all copies of this
 *     program and may not be removed without the consent of the
 *     University of Delaware.
 *     
 *
 *     version  -1    David H. Crocker    March   1979
 *     version   0    David H. Crocker    April   1980
 *     version  v7    David H. Crocker    May     1981
 *     version   1    David H. Crocker    October 1981
 *
 */
/*
 *  PHONENET SLAVE PROCESS
 *
 *  Handles telephone link & mail protocols, interfacing to MMDF for
 *  submission and pickup, via the mm_ package.
 *
 *  One argument is required:  The name of the channel the caller is
 *  claiming to come in from.  (It may be possible for one username to have
 *  access to mail for more than one channel, so the caller must indicate
 *  the channel of interest, for pickup.  However, Submit and/or Ch_PoBox
 *  will verify the claim.)
 *
 *  Sep 81  D. Crocker      allow reading channel name as first input line
 *  Nov 81  D. Crocker      commands case-insensitive
 *  Apr 82  D. Crocker      check chan name; have llog hdr show chan char
 *  Feb 84  D. Long	    made full channel name show in llog hdr
 *  Apr 84  D. Long         fixed phs_note call after pickup 
 */

#include <signal.h>
#include "ch.h"
#include "phs.h"

#define CTIMEOUT	180	/* 3 minute timeout on response to channel prompt */

extern char *dupfpath ();
extern LLog *logptr;

extern jmp_buf timerest;           /* return location saved for timeout  */
extern int     flgtrest;

char    ttyobuf[BUFSIZ];

LOCVAR struct rp_construct        /* replies                            */
	    rp_unknown =
{
    RP_PROT, 'U', 'n', 'k', 'n', 'o', 'w', 'n', ' ', 'p', 'r', 'o',
    't', 'o', 'c', 'o', 'l', ' ', 'c', 'o', 'm', 'm', 'a', 'n', 'd', '\0'
}          ,
	    rp_sbok =
{
    RP_OK, 's', 'e', 'n', 'd', ' ', 'o', 'k', '\0'
}          ,
	    rp_pkok =
{
    RP_OK, 'p', 'i', 'c', 'k', 'u', 'p', ' ', 'o', 'k', '\0'
};


/*   MAIN MAIN MAIN MAIN MAIN MAIN MAIN MAIN MAIN MAIN MAIN MAIN      */

main (argc, argv)
int       argc;
char   *argv[];
{
    Chan *curchan;
    char chaname[64];

    umask(0);

    setbuf (stdout, ttyobuf);
    mmdf_init (argv[0]);

    siginit ();
    if (argv[0][0] == '-')
    {                             /* if login shell, ignore quit & int  */
	signal (SIGINT, SIG_IGN);
	signal (SIGQUIT, SIG_IGN);
    }

    if (setjmp (timerest) != 0)   /* other side took too long; give up    */
    {
	flgtrest = FALSE;
	err_abrt (RP_TIME, "Timeout");
    }
    else
	flgtrest = TRUE;

    if (argc == 1)                /* channel name not specified as arg  */
    {
	s_alarm (CTIMEOUT);
	printf ("channel: ");
	fflush (stdout);
	gets (chaname);           /* read it                            */
	s_alarm (0);
    }
    else                          /* use the argument                   */
	strcpy (chaname, argv[1]);

    ll_hdinit (logptr, chaname); /* which channel are we?    */

    if ((curchan = ch_nm2struct (chaname)) == (Chan *) NOTOK)
	err_abrt (RP_PARM, "Channel '%s' is unknown", chaname);

    ph_slave (curchan);
}

/* *****************  SERVER/SLAVE MODE  ****************************** */

ph_slave (curchan)                /* service requests from caller       */
    Chan *curchan;
{
    int       len;
    short     retval;
    char    linebuf[LINESIZE];
    register char *ptr;

    ch_llinit (curchan);
    if (rp_isbad (retval = ph_init (curchan)))
	err_abrt (retval, "Error with initialization");
				  /* set-up to service requests         */

    FOREVER
    {				  /* what does caller want to do?       */
	if (rp_isbad (retval = ph_rrec (linebuf, &len)))
	    err_abrt (retval, "Error return getting command");

#ifdef DEBUG
	ll_log (logptr, LLOGFTR, "Command: \"%s\"", linebuf);
#endif

	for (ptr = linebuf; !isnull (*ptr); ptr++)
	    *ptr = uptolow (*ptr);

	if (len == 0 ||		  /* all done?                          */
		equal (linebuf, "end", 3) ||
		rp_gval (retval) == RP_DONE)
	    break;		  /* exit                               */

	if (equal (linebuf, "submit", 6))
	{			  /* gonna RECEIVE mail                 */
	    sl_submit (curchan);
	    continue;
	}
	if (equal (linebuf, "pickup", 6))
	{			  /* gonna SEND mail                    */
	    sl_pickup (curchan);
	    continue;
	}

	ph_wrply ((struct rp_bufstruct *) &rp_unknown,
					    rp_conlen (rp_unknown));
	err_abrt (rp_unknown.rp_cval, "%s:  '%s'",
			rp_unknown.rp_cline, linebuf);
    }
    ph_end (OK);
    mm_end (OK);
    exit (RP_DONE);
}
/**/

sl_submit (curchan)               /* dm_slave submission management     */
    Chan *curchan;
{
    short     retval;

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "[ Submit ]");
#endif

    if (rp_isbad (retval = ph_wrply ((struct rp_bufstruct *) &rp_sbok,
			    rp_conlen (rp_sbok))))
		    err_abrt (retval, "Unable to ack submission request");

    phs_note (curchan, PHS_RESTRT);

    if (rp_isbad (retval = ph2mm_send (curchan)))
	err_abrt (retval, "ph2mm_send error");

    phs_note (curchan, PHS_REEND);
}

sl_pickup (curchan)               /* dm_slave pickup management         */
    Chan *curchan;
{
    short     retval;

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "[ Pickup ]");
#endif

    if (rp_isbad (retval = ph_wrply ((struct rp_bufstruct *) &rp_pkok,
			    rp_conlen (rp_pkok))))
		    err_abrt (retval, "Unable to ack pickup request");

    if (rp_isbad (retval = mm2ph_send (curchan)))
	err_abrt (retval, "mm2ph_send error");
}

/* *************************  UTILITIES  ****************************** */

/* VARARGS2 */

err_abrt (code, fmt, b, c, d)
int       code;
char   *fmt,
       *b,
       *c,
       *d;
{
    if (fmt != 0)
	err_gen (code, fmt, b, c, d);

    ph_end (NOTOK);

    ll_log (logptr, LLOGFAT, "Ending [%s]", rp_valstr (code));

    ll_close (logptr);            /* put out "cycled" msg, if needed    */

#ifdef DEBUG
    if (rp_gbval (code) == RP_BTNO && rp_gcval (code) == RP_CCON)
	exit (code);		  /* connection-related temp error      */

    abort ();
#else
    exit (code);
#endif
}
/**/

/* VARARGS2 */

err_gen (code, fmt, b, c, d)      /* standard error processing          */
int       code;                     /* see err_abrt, for explanation      */
char   *fmt,
       *b,
       *c,
       *d;
{
    extern int    errno;
    char newfmt[LINESIZE];

    if (rp_isgood (code))	  /* it wasn't an error                 */
	return;

    printf (fmt, b, c, d);        /* the user what the problem is       */
    putchar ('\n');
    fflush (stdout);

    mm_end (NOTOK);
    switch (code)		  /* log the error?                     */
    {
	case RP_HUH: 		  /* not if it was a user error         */
	case RP_PARM: 
	case RP_USER: 
	    break;

	default: 
	    sprintf (newfmt, "%s%s", "err [ ABEND (%s) ] ", fmt);
	    ll_err (logptr, LLOGFAT, newfmt, rp_valstr (code), b, c, d);
    }
}
_alarm (0);
    }
    else                          /* use the argument                   */
	strcpy (chaname, argv[1]);

    ll_hdinit (logptr, chaname); mmdf/src/phone/mm2ph_send.c   444      0     12       13361  3620510520  11066 #include "util.h"
#include "mmdf.h"

/*
 *     MULTI-CHANNEL MEMO DISTRIBUTION FACILITY  (MMDF)
 *     
 *
 *     Copyright (C) 1979,1980,1981  University of Delaware
 *     
 *     Department of Electrical Engineering
 *     University of Delaware
 *     Newark, Delaware  19711
 *
 *     Phone:  (302) 738-1163
 *     
 *     
 *     This program module was developed as part of the University
 *     of Delaware's Multi-Channel Memo Distribution Facility (MMDF).
 *     
 *     Acquisition, use, and distribution of this module and its listings
 *     are subject restricted to the terms of a license agreement.
 *     Documents describing systems using this module must cite its source.
 *
 *     The above statements must be retained with all copies of this
 *     program and may not be removed without the consent of the
 *     University of Delaware.
 *     
 *
 *     version  -1    David H. Crocker    March   1979
 *     version   0    David H. Crocker    April   1980
 *     version  v7    David H. Crocker    May     1981
 *     version   1    David H. Crocker    October 1981
 *
 */
/*                  SEND FROM PHONE TO LOCAL (SUBMIT)                   */

#include "ch.h"

extern struct ll_struct   *logptr;

mm2ph_send (curchan)                /* overall mngmt for batch of msgs    */
    Chan *curchan;                /* channel being picked up            */
{
    short     result;
    char    info[LINESIZE],
            sender[LINESIZE];

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "mm2ph_send (%s)", curchan -> ch_name);
#endif

    if (rp_isbad (result = mm_pkinit (curchan -> ch_name)))
	return (result);
    if (rp_isbad (result = ph_sbinit ()))
	return (result);

    while (rp_gval (result = mm_rinit (info, sender)) == RP_OK)
    {
#ifdef DEBUG
	ll_log (logptr, LLOGPTR, "new message");
#endif
	if (rp_isbad (result = ph_winit (curchan -> ch_name, info, sender)))
	    return (result);      /* ready to process a message         */

	if (rp_isbad (result = m2p_admng ()))
	    return (result);      /* send the address list              */

	if (rp_isbad (result = m2p_txmng ()))
	    return (result);      /* send the message text              */
    }
#ifdef DEBUG
    ll_log (logptr, LLOGPTR, "done w/msgs");
#endif

    mm_pkend ();                  /* done getting messages              */
    ph_sbend ();                  /* done sending messages              */

    switch (rp_gval (result))
    {
	case RP_DONE: 
	case RP_EOF: 
	    return (RP_OK);
    }
    return (result);
}
/**/

LOCFUN
	m2p_admng ()              /* send address list                  */
{
    struct rp_bufstruct thereply;
    int       len;
    short     result;
    char    host[LINESIZE];
    char    adr[LINESIZE];

#ifdef DEBUG
    ll_log (logptr, LLOGPTR, "doing adrs");
#endif

    FOREVER			  /* iterate thru list                    */
    {				  /* we already have and adr              */
#ifdef DEBUG
	ll_log (logptr, LLOGBTR, "new addr");
#endif
	result = mm_radr (host, adr);
	if (rp_isbad (result))
	    return (result);      /* get address from Deliver           */
	if (rp_gval (result) == RP_DONE)
	    break;		  /* end of address list                */

	if (rp_isbad (result = ph_wadr (host, adr)))
	    return (result);      /* give to remote site                */

	if (rp_isbad (result = ph_rrply (&thereply, &len)))
	    return (result);      /* how did remote like it?            */

	switch (rp_gval (thereply.rp_val))
				  /* was address acceptable?            */
	{
	    case RP_AOK: 	  /* address ok, text not yet sent      */
		mm_wrply (&thereply, len);
		break;

	    case RP_NO: 	  /* remaining acceptible responses     */
		thereply.rp_val = RP_NDEL;
	    case RP_USER: 
	    case RP_NDEL: 
	    case RP_AGN: 
	    case RP_NOOP: 
		mm_wrply (&thereply, len);
		break;

	    default: 		  /* responses which force abort        */
		ll_log (logptr, LLOGFAT,
			"adr eof reply err (%s)'%s'",
			rp_valstr (thereply.rp_val), thereply.rp_line);
		if (rp_isbad (thereply.rp_val))
		    return (thereply.rp_val);
		return (RP_RPLY);
	}
    }
    return (ph_waend ());        /* tell remote of address list end    */
}
/**/

LOCFUN
	m2p_txmng ()              /* send message text                  */
{
    struct rp_bufstruct thereply;
    char    buffer[BUFSIZ];
    int     len;
    short     result;

#ifdef DEBUG
    ll_log (logptr, LLOGPTR, "doing text");
#endif
    len = sizeof(buffer);
    while ((rp_gval (result = mm_rtxt (buffer, &len))) == RP_OK) {
	if (rp_isbad (result = ph_wtxt (buffer, len)))
	    return (result);
	len = sizeof(buffer);
    }
#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "done sending");
#endif

    if (rp_isbad (result))
	return (result);	  /* problem with transmission          */
    if (rp_gval (result) != RP_DONE)
	return (RP_RPLY);	  /* protocol error in reply code       */
    if (rp_isbad (result = ph_wtend ()))
	return (result);	  /* flag end of message                */
    if (rp_isbad (result = ph_rrply (&thereply, &len)))
	return (result);	  /* problem getting reply?             */

    switch (rp_gval (thereply.rp_val))
				  /* was text acceptable?               */
    {
	case RP_OK: 
	    thereply.rp_val = RP_MOK;
	case RP_MOK: 		  /* text was accepted                  */
	    mm_wrply (&thereply, len);
	    break;

	case RP_NO: 		  /* remaining acceptible responses     */
	    thereply.rp_val = RP_NDEL;
	case RP_NDEL: 
	case RP_AGN: 
	case RP_NOOP: 
	    mm_wrply (&thereply, len);
	    thereply.rp_val = RP_OK;
	    break;                /* don't abort the process            */

	default: 		  /* responses which force abort        */
	    ll_log (logptr, LLOGFAT,
		    "text eof reply err (%s)'%s'",
		    rp_valstr (thereply.rp_val), thereply.rp_line);
	    if (rp_isbad (thereply.rp_val))
		return (thereply.rp_val);
	    return (RP_RPLY);
    }
    return (RP_OK);
}
FOREVER			  /* iterate thru list                    */
    {				  /* we already have and adr              */
#ifdef DEBUG
	ll_log (logptr, LLOGBTR, "new addr");
#endif
	result = mm_radr (host, adr);
	if (rp_isbad (result))
	    return (result);      /* get address from Dmmdf/src/phone/ph_io.c   444      0     12       24117  3620510520  10131 #include "util.h"
#include "mmdf.h"
#include "d_returns.h"
/*
 *     MULTI-CHANNEL MEMO DISTRIBUTION FACILITY  (MMDF)
 *     
 *
 *     Copyright (C) 1979,1980,1981  University of Delaware
 *     
 *     Department of Electrical Engineering
 *     University of Delaware
 *     Newark, Delaware  19711
 *
 *     Phone:  (302) 738-1163
 *     
 *     
 *     This program module was developed as part of the University
 *     of Delaware's Multi-Channel Memo Distribution Facility (MMDF).
 *     
 *     Acquisition, use, and distribution of this module and its listings
 *     are subject restricted to the terms of a license agreement.
 *     Documents describing systems using this module must cite its source.
 *
 *     The above statements must be retained with all copies of this
 *     program and may not be removed without the consent of the
 *     University of Delaware.
 *     
 *
 *     version  -1    David H. Crocker    March   1979
 *     version   0    David H. Crocker    April   1980
 *     version  v7    David H. Crocker    May     1981
 *     version   1    David H. Crocker    October 1981
 *
 */
/*#define RUNALON   */

/*                  Handle telephone-based i/o                          */

extern struct ll_struct   *logptr;
extern int    d_errno;              /* dial package error                 */
extern char *blt ();

/*  NOTE:  load initialization routines from ph_iouser or ph_ioslave    */

/*   ************  (ph_)  PHONE MAIL I/O SUB-MODULE  ****************** */

ph_wrply (valstr, len)           /* pass a reply to local process      */
struct rp_bufstruct *valstr;      /* string describing reply            */
int       len;                      /* length of the string               */
{
    char    rp_string[LINESIZE];

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "ph_wrply()");
    ll_log (logptr, LLOGFTR, "(%s)'%s'",
	    rp_valstr (valstr -> rp_val), valstr -> rp_line);
#endif
				  /* turn rp code into two hex bytes    */
				  /*   to avoid using high bit on phone */
    rp_string[0] = d_tohex ((valstr -> rp_val >> 4) & 017);
				  /* high four bits                     */
    rp_string[1] = d_tohex (valstr -> rp_val & 017);
				  /* low four bits                      */
    blt (valstr -> rp_line, &rp_string[2], len);
    return (ph_wrec (rp_string, len + 1));
}
/**/

ph_rrply (valstr, len)           /* get a reply from remote process    */
struct rp_bufstruct *valstr;      /* where to stuff copy of reply text  */
int      *len;                      /* where to indicate text's length    */
{
    short     retval;
    char   *rplystr;
    char    rp_string[LINESIZE];

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "ph_rrply()");
#endif

    retval = ph_rrec (
#ifdef RUNALON
	    (char  *) valstr,
#else
	        rp_string,
#endif
	        len);
    if (rp_gval (retval) != RP_OK)
	return (retval);

#ifdef RUNALON
    switch (rp_gval (valstr -> rp_val))
    {
	case 'd': 
	    valstr -> rp_val = RP_DONE;
	    break;
	case 'y': 
	    valstr -> rp_val = RP_OK;
	    break;
	case 'm': 
	    valstr -> rp_val = RP_MOK;
	    break;
	case 'a': 
	    valstr -> rp_val = RP_AOK;
	    break;
	case 'n': 
	    valstr -> rp_val = RP_NO;
	    break;
    }
#else
    valstr -> rp_val = ((d_fromhex (rp_string[0]) & 017) << 4) |
	(d_fromhex (rp_string[1]) & 017);
    *len -= 1;			  /* reply is shorter                   */
    blt (&rp_string[2], valstr -> rp_line, *len);
				  /* copy string + its null ending      */
#endif

    rplystr = rp_valstr (valstr -> rp_val);
    if (*rplystr == '*')
    {				  /* replyer did a no-no                */
	ll_log (logptr, LLOGTMP, "ILLEGAL REPLY: (%s)", rplystr);
	valstr -> rp_val = RP_RPLY;
    }
#ifdef DEBUG
    else
	ll_log (logptr, LLOGFTR, "(%s)'%s'", rplystr, valstr -> rp_line);
#endif

    return (RP_OK);
}
/*                READ FROM REMOTE MAIL PROCESS                    */

ph_rrec (linebuf, len)           /* read one "record"                    */
char   *linebuf;		  /* where to stuff the text              */
int      *len;                      /* where to stuff the length count      */
{
    short     retval;
    int       goteot;               /* completed packet                   */

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "ph_rrec ()");
#endif

#ifdef RUNALON
    *len = read (0, linebuf, LINESIZE - 1);
    *len -= 1;
#else
    if ((*len = d_rcvdata (linebuf, LINESIZE - 1, &goteot)) < 0)
    {
	retval = ph_d2rpval (*len);
	ll_log (logptr, LLOGTMP,
		"ph_rrec:  d_rcvdata err (%s)", rp_valstr (retval));
	return (retval);
    }
#endif

    if (*len == 0)
    {
	ll_log (logptr, LLOGFTR, "zero len read");
	return (RP_DONE);
    }
    linebuf[*len] = '\0';	  /* keep things null-terminated        */

    if (!goteot)		  /* packet is too long                 */
	ll_log (logptr, LLOGFTR, "*** oversized packet");
				  /* let is slip, for now               */

#ifdef DEBUG
    ll_log (logptr, LLOGFTR, "(%d)'%s'", *len, linebuf);
#endif
    return (RP_OK);
}
/**/

ph_rstm (buffer, len)            /* read buffered block of text        */
char   *buffer;			  /* where to stuff the text            */
int      *len;                      /* where to stuff count               */
{
    static int    goteos;           /* save end-of-stream across calls    */

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "ph_rstm ()");
#endif

    if (goteos)			  /* was set automatically by dial      */
    {
#ifdef DEBUG
	ll_log (logptr, LLOGFTR, "returning DONE");
#endif
	goteos = FALSE;
	*buffer = '\0';
	*len = 0;
	return (RP_DONE);
    }

#ifdef RUNALON
    printx ("dm_rstm: ");

    *len = read (0, buffer, *len - 1);
    if (*len == 0)
	goteos = TRUE;
#else
    *len = d_rcvdata (buffer, *len - 1, &goteos);
#endif

    if (*len < 0)
    {
	ll_log (logptr, LLOGGEN, "rdstm: error rcvdata");
	return (ph_d2rpval (*len));
    }
    buffer[*len] = '\0';

#ifdef DEBUG
    ll_log (logptr, LLOGFTR, "(%d)'%s'", *len, buffer);
#endif
    return (RP_OK);
}
/*                WRITE TO REMOTE MAIL PROCESS                        */

ph_wrec (linebuf, len)           /* write a record/packet              */
char	*linebuf;		  /* chars to write                     */
int	len;                      /* number of chars to write           */
{
    short     retval;

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "ph_wrec () (%d)'%s'", len, linebuf ? linebuf : "");
#endif

#ifdef RUNALON
    if (linebuf == 0 && len == 0)
    {
	printf ("p_wrec (eof)\n");
	fflush (stdout);
	return (RP_OK);
    }
    if (write (1, linebuf, len) != len)
	return (RP_DHST);
    write (1, "\n", 1);		  /* make it pretty */
    return (RP_OK);
#endif

    if ((retval = d_snstream (linebuf, len)) < D_OK ||
	    (retval = d_sneot ()) < D_OK)
	return (ph_d2rpval (retval));

    return (RP_OK);
}
/**/

ph_wstm (buffer, len)            /* write next part of char stream     */
char    buffer[];		  /* chars to write                     */
int       len;                      /* number of chars to write           */
{
    short     retval;

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "ph_wstm () (%d)'%s'", len, buffer ? buffer : "");
#endif

#ifdef RUNALON
    retval = write (1, buffer, len);
    if (retval == len)
	return (RP_OK);
    else
	return (RP_NO);
#endif

    if (buffer == 0 && len == 0)
	retval = d_sneot ();     /* flush the buffer                   */
    else
	retval = d_snstream (buffer, len);

    if (retval < D_OK)
	return (ph_d2rpval (retval));
    return (RP_OK);
}
/**/

ph_d2rpval (retval)               /* map d_errno into rp.h value        */
int       retval;
{
#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "(%d) => dial err (%d)", retval, d_errno);
#endif

    switch (d_errno)
    {
	case D_LOGERR: 		  /* probably locked                    */
	    ll_err (logptr, LLOGTMP, "phone log");
	    return (RP_AGN);      /* maybe  transient problem           */

	case D_NOPORT:            /* no port or line available          */
	case D_BUSY: 
	case D_ABAN: 
	    return (RP_AGN);      /* highly transient problem           */

	case D_TIMEOUT:           /* alarm during port read/write call  */
	    return (RP_TIME);     /* same type of code                  */

	case D_RCVQUIT:           /* a QUIT packet has been received    */
	    return (RP_DONE);

	case D_PORTEOF:           /* eof on port                        */
	    return (RP_EOF);

	case D_NOPWR:             /* the dialer has no power            */
	case D_PORTOPEN:          /* error trying to open port          */
	    return (RP_NET);

	case D_PORTRD:            /* error on port read                 */
	case D_PORTWRT:           /* error on port write                */
	    return (RP_NIO);

	case D_NORESP:            /* no response to transmitted packet  */
	    return (RP_DHST);     /* assume the host is messed up       */

	case D_PATHERR:           /* error in path packet               */
	case D_PACKERR:           /* error in packet format             */
	    return (RP_BHST);

	case D_TSOPEN:            /* error opening transcript file      */
	case D_LOCKERR:           /* error opening/creating lock file   */
	    return (RP_LOCK);

	case D_CALLLOG:           /* error opening call log file        */
	    return (RP_FOPN);

	case D_TSWRITE:           /* error writing on transcript file   */
	    return (RP_FIO);

	case D_SYSERR:            /* undistinguished system error       */
	case D_FORKERR:           /* couldn't fork after several tries  */
	case D_PRTSGTTY:          /* error doing stty or gtty on port   */
	    return (RP_LIO);

	case D_SCRERR:            /* error in script file               */
	case D_ACCERR:            /* error in access file               */
	case D_NONUMBS:           /* no numbers given to dialer         */
	case D_NOESC:             /* no esacpe character could be found */
	case D_BADSPD:            /* bad speed designation              */
	    return (RP_PARM);

	case D_INTERR:            /* internal package error             */
	case D_INITERR:           /* trouble initializing               */
	case D_BADDIG:            /* bad digit passed to dialer         */
	    return (RP_NO);

	default: 		  /* punt                               */
	    ll_err (logptr, LLOGTMP,
		    "unclassified category (%d) phone error (%d)",
				retval, d_errno);
	    return (RP_NO);
    }
}
s to write           */
{
    short     retval;

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "ph_wrec () (%d)'%s'", len, linebuf ? linebuf : "");
#endif

#ifdef RUNALON
    if (linebuf == 0 && len == 0)
    {
	printf ("p_wrec (eof)\n");
	fflush (stdout);
	return (RP_OK);
    }
    if (write (1, linebuf, len) != len)
	return (RP_DHST);
    write (1, "\n", 1);		  /* make it pretty */
    return (RP_OK);
#endif

    if ((retval = d_snmmdf/src/phone/ch_phone.c   444      0     12       15465  3620510521  10625 #include "util.h"
#include "mmdf.h"

/*
 *     MULTI-CHANNEL MEMO DISTRIBUTION FACILITY  (MMDF)
 *     
 *
 *     Copyright (C) 1979,1980,1981  University of Delaware
 *     
 *     Department of Electrical Engineering
 *     University of Delaware
 *     Newark, Delaware  19711
 *
 *     Phone:  (302) 738-1163
 *     
 *     
 *     This program module was developed as part of the University
 *     of Delaware's Multi-Channel Memo Distribution Facility (MMDF).
 *     
 *     Acquisition, use, and distribution of this module and its listings
 *     are subject restricted to the terms of a license agreement.
 *     Documents describing systems using this module must cite its source.
 *
 *     The above statements must be retained with all copies of this
 *     program and may not be removed without the consent of the
 *     University of Delaware.
 *     
 *
 *     version  -1    David H. Crocker    March   1979
 *     version   0    David H. Crocker    April   1980
 *     version  v7    David H. Crocker    May     1981
 *     version   1    David H. Crocker    October 1981
 *
 */
/*  CH_PHONE:  Telephone channel transmission                           */
/*                                                                      */
/*  May, 80 Dave Crocker    fix timerest declaration                    */
/*  Jul, 80 Dave Crocker    remove ph_end from ch_phone for(;;)         */
/*  Aug, 80 Dave Crocker    ch_phone, move retry ok to AFTER sending    */

/*  Jun 81  D. Crocker      Add d2p_nadrs mechanism to send no msg text
 *                          when there are not valid addresses
 *  Jul 81  D. Crocker      Change state variable values, for more robust
 *                          state diagrams, to avoid hang with Deliver
 *  Jun 82  D. Crocker      Add chmod, after close(creat())
 */

#include <signal.h>
#include "ch.h"
#include "phs.h"

extern char *dupfpath();
extern LLog *logptr;

extern char ph_state;              /* state of processing current msg    */
extern jmp_buf  timerest;
extern int      flgtrest;

Chan *curchan;         /* what channel am I?                 */

/*    MAIN MAIN MAIN MAIN MAIN MAIN MAIN MAIN MAIN MAIN MAIN MAIN     */

main (argc, argv)
int     argc;
char   *argv[];
{
    short retval;

    mmdf_init (argv[0]);
    siginit ();
    signal (SIGINT, SIG_IGN);          /* always ignore interrupts           */

    if ((curchan = ch_nm2struct (argv[0])) == (Chan *) NOTOK)
	err_abrt (RP_PARM, "unknown channel name '%s'", argv[0]);
				  /* find out who I am                  */

    if (argc < 2)
	err_abrt (RP_PARM, "ch_phone (%s) arg count", curchan -> ch_name);

    ch_llinit (curchan);
    ll_hdinit (logptr, argv[0]);   /* which channel are we?              */
    if (argc <= 2)
    {
	logptr -> ll_hdr[0] = 'P'; /* note that this is poll mode       */

	if (argv[1][0] != '-')
	    err_abrt (RP_PARM, "ill-formed argument '%s'", argv[1]);
	if (argv[1][1] == 'w')
	    domsg = TRUE;
    }

    ll_log (logptr, LLOGPTR, "%s", argv[0]);

    retval = ch_phone (argc, argv);

    ll_log (logptr, LLOGGEN, "norm end");
    ll_close (logptr);           /* in case of cycling, close neatly   */
    exit (retval);
}
/*************  (ch_) PHONE-BASED MAIL USER/SENDER  ***************** */

ch_phone (argc, argv)		  /* send on phone-type chan            */
int     argc;
char   *argv[];
{
    short     result;
    short     triesleft;            /* number of re-dials we may do       */
    char    maysend;		  /* have messages to send out          */

/*  NOTE:   Besides deliverying mail out through the telephone, this
 *          procedure manages retrieval of mail from the remote host.  To
 *          keep track of its transaction state, it time-stamps several
 *          flag-files.  The time-stamping is used (by Deliver) to decide
 *          whether to force a pickup, when there hasn't been any outgoing
 *          mail for a long time.
 */

    if (argc > 2)		  /* we running under Deliver?          */
    {
	maysend = TRUE;
	if (rp_isbad (qu_init (argc, argv)))
	    return (RP_NO);       /* problem setting-up for deliver     */
    }
    else
	maysend = FALSE;

    for (triesleft = DL_TRIES; triesleft > 0; triesleft--)
    {
	if (setjmp (timerest) != 0)
	{
	    flgtrest = FALSE;
	    break;                /* no retry on dialup/send error      */
	}
	else
	    flgtrest = TRUE;

	if (rp_isbad (result = ph_init ()))
	{                         /* problem dialing foreign site       */
	    if (result != RP_AGN)
		break;            /* isn't worth retrying               */
	    continue;             /* retrying ok                        */
	}

	if (maysend && (curchan -> ch_access & CH_SEND))
	{                         /* TRANSMISSION: SEND MESSAGES        */
	    phs_note (curchan, PHS_WRSTRT);

	    if (rp_isbad (qu2ph_send (argv[0])))
		break;            /* send outgoing.  no retry           */
	    maysend = FALSE;      /* done with sending                  */

	    phs_note (curchan, PHS_WREND);
	    qu_end (OK);          /* done with Deliver function         */
	}

	if (!curchan -> ch_access & CH_PICK)
	    break;                /* pickup not allowed for channel     */

	if (setjmp (timerest) != 0)
	{
	    flgtrest = FALSE;
	    continue;             /* TIMEOUT during pickup; retry ok    */
	}
	else
	    flgtrest = TRUE;

	ll_log (logptr, LLOGPTR, "pickup");
	phs_note (curchan, PHS_RESTRT);

	result = ph2mm_send (curchan);
				  /* RECEPTION: PICKUP MESSAGES         */
	if (rp_gbval (result) == RP_BNO)
	    break;		  /* retrying won't help                */
	if (rp_gbval (result) == RP_BTNO)
	    continue;		  /* maybe retrying will help           */

	triesleft = 0;		  /* nothing to re-try for              */
	phs_note (curchan, PHS_REEND);
	ll_log (logptr, LLOGPTR, "normal hangup");
	ph_end (OK);              /* say goodbye to the nice slave      */
	return (RP_OK);           /* NORMAL RETURN                      */
    }				  /* BELOW THIS is error handling       */

    ll_log (logptr, LLOGPTR, "error hangup");
    ph_end (NOTOK);
    if (maysend)		  /* gave up during attempt to send?    */
	qu_fakrply (ph_state);   /* just pass DEADs back to Deliver      */

    err_abrt (RP_DHST, "Remote site not available");
    /* NOTREACHED */
}

/**/

/* VARARGS2 */
err_abrt (code, fmt, b, c, d)     /* terminate ourself                  */
short     code;
char    fmt[],
        b[],
        c[],
        d[];
{
    char linebuf[LINESIZE];

    qu_end (NOTOK);
    ph_end (NOTOK);
    if (rp_isbad (code))
	if (rp_gbval (code) == RP_BNO || logptr -> ll_level >= LLOGBTR)
	{                         /* don't worry about minor stuff      */
	    sprintf (linebuf, "%s%s", "err [ ABEND (%s) ]\t", fmt);
	    ll_log (logptr, LLOGFAT, linebuf, rp_valstr (code), b, c, d);
				/* disable error reporting */
#ifdef DEBUG
	    sigabort (fmt);
#endif
	}
    ll_close (logptr);           /* in case of cycling, close neatly   */
    exit (code);
}
(RP_PARM, "ill-formed argument '%s'", argv[1]);
	if (argv[1][1] == 'w')
	    domsg = TRUE;
    }

    ll_log (logptr, LLOGPTR, "%s", argv[0]);

    retval = ch_phone (argc, argv);

    ll_log (logptr, LLmmdf/src/phone/Makefile.real   444      0     12        4715  3635174224  11250 #
#   phone:   telephone-based channel transmission
#
MODULES =	ch_phone qu2ph_send ph2mm_send ph_rdmail \
		ph_wtmail ph_io ph_iouser

POBJECTS =	ch_phone.o qu2ph_send.o ph2mm_send.o ph_rdmail.o \
		ph_wtmail.o ph_io.o ph_iouser.o

SOBJECTS =	ph_slave.o mm2ph_send.o ph2mm_send.o ph_rdmail.o \
		ph_wtmail.o ph_io.o ph_ioslave.o

PSOURCES =	ch_phone.c ph_iouser.c qu2ph_send.c

SSOURCES = 	ph_slave.c ph_ioslave.c mm2ph_send.c

CSOURCES =	ph2mm_send.c ph_rdmail.c \
		ph_wtmail.c ph_io.c

real-default:	phone slave

install:	$(CHANDIR)/phone $(LIBDIR)/slave

$(CHANDIR)/phone  :   xphone
	cp xphone $(CHANDIR)/phone
	-$(CHOWN) $(MMDFLOGIN) $(CHANDIR)/phone
	-chmod 04$(PGMPROT) $(CHANDIR)/phone
	-@ls -ls $(CHANDIR)/phone
	-@echo "PhoneNet outbound channel installed normally"; echo ""

phone:	xphone
xphone:	$(POBJECTS) $(MMDFLIBS)
	    $(CC) $(LDFLAGS) -o $@ $(POBJECTS) $(MMDFLIBS) $(SYSLIBS)

$(LIBDIR)/slave  :   xslave
	cp xslave $(LIBDIR)/slave
	-$(CHOWN) $(MMDFLOGIN) $(LIBDIR)/slave
	-chmod 0$(PGMPROT) $(LIBDIR)/slave
	-@ls -ls $(LIBDIR)/slave
	-@echo "PhoneNet slave installed normally"; echo ""

slave:	xslave
xslave:	$(SOBJECTS) $(MMDFLIBS)
	    $(CC) $(LDFLAGS) -o $@ $(SOBJECTS) $(MMDFLIBS) $(SYSLIBS)

lint:   l-phone l-slave

l-phone:
	$(LINT) $(LFLAGS) $(PSOURCES) $(CSOURCES) $(LLIBS)

l-slave:
	$(LINT) $(LFLAGS) $(SSOURCES) $(CSOURCES) $(LLIBS)

clean:
	-rm -f xphone xslave *.o x.c makedep eddep make.out errs


# DO NOT DELETE THIS LINE -- make depend uses it

ch_phone.o: ch_phone.c
ch_phone.o: ../../h/util.h
ch_phone.o: ../../h/mmdf.h
ch_phone.o: /usr/include/signal.h
ch_phone.o: ../../h/ch.h
ch_phone.o: ../../h/phs.h
qu2ph_send.o: qu2ph_send.c
qu2ph_send.o: ../../h/util.h
qu2ph_send.o: ../../h/mmdf.h
qu2ph_send.o: ../../h/ch.h
qu2ph_send.o: ../../h/chan.h
qu2ph_send.o: ../../h/phs.h
qu2ph_send.o: ../../h/ap.h
ph2mm_send.o: ph2mm_send.c
ph2mm_send.o: ../../h/util.h
ph2mm_send.o: ../../h/mmdf.h
ph2mm_send.o: ../../h/ch.h
ph2mm_send.o: ../../h/phs.h
ph_rdmail.o: ph_rdmail.c
ph_rdmail.o: ../../h/util.h
ph_rdmail.o: ../../h/mmdf.h
ph_wtmail.o: ph_wtmail.c
ph_wtmail.o: ../../h/util.h
ph_wtmail.o: ../../h/mmdf.h
ph_io.o: ph_io.c
ph_io.o: ../../h/util.h
ph_io.o: ../../h/mmdf.h
ph_io.o: ../../h/d_returns.h
ph_iouser.o: ph_iouser.c
ph_iouser.o: ../../h/util.h
ph_iouser.o: ../../h/mmdf.h
ph_iouser.o: ../../h/d_returns.h
ph_iouser.o: ../../h/ch.h
ph_iouser.o: ../../h/phs.h
# DEPENDENCIES MUST END AT END OF FILE
# IF YOU PUT STUFF HERE IT WILL GO AWAY
# see make depend above
l.c \
		ph_wtmail.c ph_io.c

real-default:	phone slmmdf/src/phone/ph_iouser.c   444      0     12       14017  3620510521  11027 #include "util.h"
#include "mmdf.h"
/*
 *     MULTI-CHANNEL MEMO DISTRIBUTION FACILITY  (MMDF)
 *     
 *
 *     Copyright (C) 1979,1980,1981  University of Delaware
 *     
 *     Department of Electrical Engineering
 *     University of Delaware
 *     Newark, Delaware  19711
 *
 *     Phone:  (302) 738-1163
 *     
 *     
 *     This program module was developed as part of the University
 *     of Delaware's Multi-Channel Memo Distribution Facility (MMDF).
 *     
 *     Acquisition, use, and distribution of this module and its listings
 *     are subject restricted to the terms of a license agreement.
 *     Documents describing systems using this module must cite its source.
 *
 *     The above statements must be retained with all copies of this
 *     program and may not be removed without the consent of the
 *     University of Delaware.
 *     
 *
 *     version  -1    David H. Crocker    March   1979
 *     version   0    David H. Crocker    April   1980
 *     version  v7    David H. Crocker    May     1981
 *     version   1    David H. Crocker    October 1981
 *
 */
/*#define RUNALON   */

/*                  Handle telephone-based i/o                          */

/*  Jul, 80 Dave Crocker    remove ph_d_started setting from ph_init    */
/*  Feb, 82 D. Long	    improved ll_log hdr field for wider chan name */
/*  Mar, 84 D. Long         added use of per-channel phone transcript */

#include "d_returns.h"
#include "ch.h"
#include "phs.h"

extern time_t time ();
extern Chan *curchan;
extern struct ll_struct   *logptr,
			   ph_log;
extern char *logdfldir;
extern char *tbldfldir;
extern char *def_trn;             /* straight transcript of char i/o    */

LOCVAR char ph_d_started;         /* called dial package yet?           */
LOCVAR char *ph_trn;		  /* filled-out version of phone trn file */
LOCVAR char *ph_script;           /* filled-out version of dialing script */

/* ********  (ph_)  PHONE MAIL MASTER INIT/END SUB-MODULE  ********** */

ph_init ()              /* call remote, to do mail            */
{
    extern char *dupfpath ();
    short     retval;

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "ph_init ()");
#endif
    if (ph_d_started)             /* tried before?                      */
    {
	ph_end (NOTOK);           /* make sure old channel gone         */
	sleep (10);               /* just for the hell of it            */
	ll_log (logptr, LLOGTMP, "[ REDIAL ]");
	printx ("re-");
    }
    else
    {
/*	ll_hdinit (&ph_log, " D");   /* header unique for send             */
	ll_hdinit (&ph_log, logptr -> ll_hdr);   /* init header for send    */
/*	ph_log.ll_hdr[0] = logptr -> ll_hdr[0];  */
	ph_log.ll_hdr[0] = 'D';  /* unique letter for send */
	ph_log.ll_file = dupfpath (ph_log.ll_file, logdfldir);
				   /* fill-out path to log files        */

	ph_script = dupfpath (curchan -> ch_script, tbldfldir);

	if (curchan -> ch_trans == (char *) DEFTRANS)
	    ph_trn = dupfpath (def_trn, logdfldir);
	else
	    ph_trn = dupfpath (curchan -> ch_trans, logdfldir);

	ll_log (logptr, LLOGGEN, "dialing");
    }
    printx ("dialing... ");
    fflush (stdout);
#ifndef RUNALON
    ph_d_started = TRUE;
    phs_note (curchan, PHS_CNSTRT);
				  /* note WHICH phone channel           */
    if ((retval = d_masconn (ph_script, &ph_log, TRUE, ph_trn,
			    (ph_log.ll_level >= LLOGBTR) ? TRUE : FALSE))
		< D_OK)
    {
	printx ("couldn't connect...\r\n\t");
	retval = ph_d2rpval (retval);
				  /* map return to rp.h value           */
	ll_log (logptr, LLOGTMP,
		    "unable to connect (%s)", rp_valstr (retval));
	return (retval);
    }
#endif

    printx ("\007connected...\r\n\t");
    fflush (stdout);
    phs_note (curchan, PHS_CNGOT);
    return (RP_OK);
}
/**/

ph_end (type)                     /* done with mail process             */
short     type;                     /* clean / dirty ending               */
{

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "ph_end ()");
#endif

    if (ph_d_started)             /* is phone channel even active?      */
    {
	ph_d_started = FALSE;
	if (type == OK)           /* try clean good end                 */
	{
	    if (rp_isgood (ph_wrec ("end", 3)))
	    {                     /* should also look for the reply     */
		d_masdrop (0, TRUE);
				  /* finish the script                  */
		phs_end(curchan, RP_OK);
		return;
	    }
	}
#ifndef RUNALON
	else                      /* clean bad end                      */
	    d_masdrop (0, 0);     /* don't finish the script            */
#endif

	phs_end(curchan, RP_NO);
	return;
    }
}
/**/

ph_sbinit ()                      /* ready to submit to remote site     */
{
    struct rp_bufstruct thereply;
    int       len;

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "ph_sbinit ()");
#endif
    if (rp_isbad (ph_wrec ("submit", 6)) ||
	    rp_isbad (ph_rrply ((struct rp_bufstruct  *) & thereply,
		                                           &len)) ||
	                                            rp_isbad (thereply.rp_val))
    {
						    ll_log (logptr, LLOGFAT, "can't submit");
	return (RP_NO);
    }
#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "starting to submit");
#endif
    return (RP_OK);
}

ph_sbend ()                       /* done with submission               */
{
#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "ph_sbend ()");
#endif

    if (rp_isbad (ph_wrec ((char *) 0, 0)))
    {
	ll_log (logptr, LLOGTMP, "bad sbend()");
	return (RP_NO);
    }
    return (RP_OK);
}
/**/

ph_pkinit ()                      /* initialize remote pickup            */
{
    struct rp_bufstruct thereply;
    int       len;

#ifdef DEBUG
    ll_log (logptr, LLOGPTR, "ph_pkinit ()");
#endif
    if (rp_isbad (ph_wrec ("pickup", 6)) ||
	    rp_isbad (ph_rrply ((struct rp_bufstruct  *) & thereply,
		                                           &len)) ||
	                                            rp_isbad (thereply.rp_val))
    {
						    ll_log (logptr, LLOGFAT, "can't pickup");
	return (RP_NO);
    }
    return (RP_OK);
}

ph_pkend ()                       /* done with pickup                   */
{
#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "ph_pkend ()");
#endif
    return (RP_OK);
}
        */
	}

	if (maysend && (curchan -> ch_access & CH_SEND))
	{                         /* TRANSMISSION: SEND MESSAGES        */
	    phs_note (curchan, PHS_WRSTRT);

	    if (rp_isbad (qu2ph_send (argv[0])))
		break;            /* send outgoing.  no retry           */
	    maysend = FALSE;      /* done with sending                  */

	    phs_note (curchan, PHS_WREND);
	    qu_end (OK);          /* done with Deliver function         */
	}

	if (!curchan -> ch_access & CH_PICK)
	    bremmdf/src/phone/ph_ioslave.c   444      0     12        7242  3620510521  11145 #include "util.h"
#include "mmdf.h"
/*
 *     MULTI-CHANNEL MEMO DISTRIBUTION FACILITY  (MMDF)
 *     
 *
 *     Copyright (C) 1979,1980,1981  University of Delaware
 *     
 *     Department of Electrical Engineering
 *     University of Delaware
 *     Newark, Delaware  19711
 *
 *     Phone:  (302) 738-1163
 *     
 *     
 *     This program module was developed as part of the University
 *     of Delaware's Multi-Channel Memo Distribution Facility (MMDF).
 *     
 *     Acquisition, use, and distribution of this module and its listings
 *     are subject restricted to the terms of a license agreement.
 *     Documents describing systems using this module must cite its source.
 *
 *     The above statements must be retained with all copies of this
 *     program and may not be removed without the consent of the
 *     University of Delaware.
 *     
 *
 *     version  -1    David H. Crocker    March   1979
 *     version   0    David H. Crocker    April   1980
 *     version  v7    David H. Crocker    May     1981
 *     version   1    David H. Crocker    October 1981
 *
 */
/*#define RUNALON   */

/*                  Handle telephone-based i/o                          */

/*  May 82  D. Crocker  add chan.log rmsg/wmsg stats, like ch_phone has
 *  Mar 84  D. Long     add use of per-channel phone transcripts
 */

#include "ch.h"
#include "phs.h"
#include "d_returns.h"

extern struct ll_struct    *logptr,
			    ph_log;
extern char *logdfldir;
extern char *def_trn;

LOCVAR Chan *ph_curchan;
LOCVAR char *ph_trn;

/* *********  (ph_)  PHONE MAIL SLAVE INIT/END SUB-MODULE  ************ */

ph_init (curchan)                 /* we are slave: initialize channel   */
    Chan *curchan;
{
    extern char *dupfpath ();
    short retval;

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "ph_init (%s)", curchan -> ch_name);
#endif

    ph_curchan = curchan;
    ph_log.ll_hdr = logptr -> ll_hdr;
    ph_log.ll_file = dupfpath (ph_log.ll_file, logdfldir);
				  /* fill-out path to log file         */
    if (curchan -> ch_trans == (char *) DEFTRANS)
       ph_trn = dupfpath (def_trn, logdfldir);
    else
       ph_trn = dupfpath (curchan -> ch_trans, logdfldir);
    phs_note (curchan, PHS_CNSTRT);
#ifndef RUNALON
    if ((retval = d_slvconn (&ph_log, TRUE, ph_trn,
			(ph_log.ll_level >= LLOGBTR) ? TRUE : FALSE,
			0, DL_RCVSZ, DL_XMTSZ) < D_OK))
    {				  /* standard i/o port, max buf sizes   */
	ll_log (logptr, LLOGTMP, "dial package problem with startup");
	return (ph_d2rpval (retval));
    }
#endif

    phs_note (curchan, PHS_CNGOT);
    return (RP_OK);
}

/* ARGSUSED */

ph_end (type)                     /* say goodbye to the caller          */
short     type;               
{

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "ph_end ()");
#endif
#ifndef RUNALON
    d_slvdrop (0);		  /* always just drop the line          */
#endif

    phs_end (ph_curchan, type ? RP_NO : RP_OK);
    ph_curchan = OK;
    return (RP_OK);
}

/**/

ph_sbinit ()                      /* ready to submit to remote site     */
{
#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "ph_sbinit ()");
#endif
    return (RP_OK);
}

ph_sbend ()                       /* done with submission               */
{
#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "ph_sbend ()");
#endif

    if (rp_isbad (ph_wrec ((char *)0, 0)))
    {
	ll_log (logptr, LLOGTMP, "bad submit ending");
	return (RP_NO);
    }
    return (RP_OK);
}

ph_pkinit ()                      /* initialize remote pickup            */
{
#ifdef DEBUG
    ll_log (logptr, LLOGPTR, "ph_pkinit ()");
#endif
    return (RP_OK);
}

ph_pkend ()                       /* done with pickup                   */
{
#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "ph_pkend ()");
#endif
    return (RP_OK);
}
ickup; retry ok    */
	}
	else
	    flgtrest = TRUE;

	ll_log (logptr, LLOGPTR, "pickup");
	phs_note (curchan, PHS_RESTRT);

	result = ph2mm_send (curchan);
				  /* RECEPTION: PICKUP MESSAGES         */
	if (rp_gbval (result) == RP_BNO)
	    break;		  /* retrying won't help                */
	if (rp_gbval (result) == RP_BTNO)
	    continue;		  /* mmdf/src/phone/ph2mm_send.c   444      0     12       16446  3631170302  11077 #include "util.h"
#include "mmdf.h"

/*
 *     MULTI-CHANNEL MEMO DISTRIBUTION FACILITY  (MMDF)
 *     
 *
 *     Copyright (C) 1979,1980,1981  University of Delaware
 *     
 *     Department of Electrical Engineering
 *     University of Delaware
 *     Newark, Delaware  19711
 *
 *     Phone:  (302) 738-1163
 *     
 *     
 *     This program module was developed as part of the University
 *     of Delaware's Multi-Channel Memo Distribution Facility (MMDF).
 *     
 *     Acquisition, use, and distribution of this module and its listings
 *     are subject restricted to the terms of a license agreement.
 *     Documents describing systems using this module must cite its source.
 *
 *     The above statements must be retained with all copies of this
 *     program and may not be removed without the consent of the
 *     University of Delaware.
 *     
 *
 *     version  -1    David H. Crocker    March   1979
 *     version   0    David H. Crocker    April   1980
 *     version  v7    David H. Crocker    May     1981
 *     version   1    David H. Crocker    October 1981
 *
 *     Mar 84   D. Long         added RP_PARM in p2m_txmsg() for "no
 *                              addressees" return
 *     Apr 84   D. Long         added RP_PARM & flush in p2m_admng() for
 *                              "illegal return address" return
 *                              moved RP_PARM in p2m_txmsg to return RP_NDEL
 */
/*                  SEND FROM PHONE TO LOCAL (SUBMIT)                   */

#include "ch.h"
#include "phs.h"

extern Chan *curchan;
extern LLog *logptr;

LOCVAR long msglen;
LOCVAR int numadrs;

ph2mm_send (chan)             /* overall mngmt for batch of msgs    */
    Chan *chan;               /* who to say is doing relaying       */
{
    struct rp_bufstruct thereply;
    int       len;
    short     result;
    char    info[LINESIZE],
            sender[LINESIZE];

#ifdef DEBUG
    ll_log (logptr, LLOGPTR, "ph2mm_send");
#endif

    if (rp_isbad (result = ph_pkinit ()))
    {
	printx ("remote site refuses to send mail... ");
	fflush (stdout);
	return (result);
    }
    if (rp_isbad (result = mm_sbinit ()))
	return (result);

    printx ("starting to pickup mail... ");
    fflush (stdout);

    while (rp_gval (result = ph_rinit (info, sender)) == RP_OK)
    {
	strcat (info, "v");
	if (rp_isbad (result = mm_winit (chan -> ch_name, info, sender)))
	    return (result);      /* ready to process a message         */

	if (rp_isbad (result = mm_rrply (&thereply, &len)))
	    return (result);      /* how did remote like it?            */

	switch (rp_gbval (thereply.rp_val))
	{			  /* was source acceptable?            */
	    case RP_BNO:
    	    case RP_BTNO:
		return (thereply.rp_val);
	}

	if (rp_isbad (result = p2m_admng ()))
	    return (result);      /* send the address list              */

	if (rp_isbad (result = p2m_txmng ()))
	    return (result);      /* send the message text              */

	phs_msg (chan, numadrs, msglen);
    }

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "done with submission");
#endif
    if (rp_isbad (ph_pkend ()))   /* done getting messages              */
	printx ("bad ending for incoming submission");
    printx ("done\n");
    fflush (stdout);

    mm_sbend ();                  /* done sending messages              */

    if (rp_gval (result) == RP_DONE)
	return (RP_OK);
    return (result);
}
/**/

LOCFUN
	p2m_admng ()              /* get & process address list         */
{
    struct rp_bufstruct thereply;
    int       len;
    short     result;
    int       flush = FALSE;
    char    host[LINESIZE];
    char    adr[LINESIZE];

    for (numadrs = 0; ; )         /* iterate through list from remote   */
    {				  
	result = ph_radr (host, adr);
	if (rp_isbad (result))
	    return (result);      /* get address from remote            */
	if (rp_gval (result) == RP_DONE)
	    break;		  /* end of address list                */

#ifdef DEBUG
	ll_log (logptr, LLOGFTR, "'%s'@'%s'", adr, host);
#endif

	if (!flush)
	{
    	    if (rp_isbad (result = mm_wadr (host, adr)))
                return (result);      /* give to local submit           */

	    if (rp_isbad (result = mm_rrply (&thereply, &len)))
	        return (result);      /* how did submit like it?        */
        }
       switch (rp_gval (thereply.rp_val))
        			  /* was address acceptable?            */
       {
	    case RP_AOK: 	  /* address ok, text not yet sent      */
		ph_wrply (&thereply, len);
		numadrs++;
		break;

	    case RP_PARM:         /* bad return address--reject all     */
	        flush = TRUE;     /* "to" addresses                     */
	    case RP_NO: 	  /* remaining acceptible responses     */
		thereply.rp_val = RP_NDEL;
	    case RP_USER: 	  /* unknown user                       */
	    case RP_NDEL: 
	    case RP_AGN: 
	    case RP_NOOP: 
		ph_wrply (&thereply, len);
		break;

	    default: 		  /* responses which force abort        */
		ll_log (logptr, LLOGFAT,
			"reply err (%s)'%s'",
			rp_valstr (thereply.rp_val), thereply.rp_line);
		if (rp_isbad (thereply.rp_val))
		    return (thereply.rp_val);
		return (RP_RPLY);
	}
    }
    return (mm_waend ());        /* tell submit of address list end     */
}
/**/

LOCFUN
	p2m_txmng ()              /* send message text                  */
{
    static short  msgcount;
    struct rp_bufstruct thereply;
    int     len;
    short   result;
    char    buffer[BUFSIZ];

#ifdef DEBUG
    ll_log (logptr, LLOGPTR, "doing text");
#endif
    printx ("%d", ++msgcount);    /* keep it pretty                     */
    fflush (stdout);
    msglen = 0;

    len = sizeof(buffer);
    while ((rp_gval (result = ph_rtxt (buffer, &len))) == RP_OK)
    {
	printx (".");		  /* to show watcher we're working      */
	fflush (stdout);
	msglen += len;
	if (rp_isbad (result = mm_wtxt (buffer, len)))
	    return (result);
	len = sizeof(buffer);
    }
    printx (" ");		  /* keep it pretty                     */
    fflush (stdout);
#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "done sending");
#endif

    if (rp_isbad (result))
	return (result);	  /* problem with transmission          */
    if (rp_gval (result) != RP_DONE)
	return (RP_RPLY);	  /* protocol error in reply code       */

    if (rp_isbad (result = ph_rtend ()))
	return (result);	  /* flag end of message                */

    if (rp_isbad (result = mm_wtend ()))
	return (result);	  /* flag end of message                */

    if (rp_isbad (result = mm_rrply (&thereply, &len)))
	return (result);	  /* problem getting reply?             */
#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "got text reply");
#endif

    switch (rp_gval (thereply.rp_val))
    {                             /* was text acceptable?               */
	case RP_OK: 
	    thereply.rp_val = RP_MOK;
	case RP_MOK: 		  /* text was accepted                  */
	    ph_wrply (&thereply, len);
	    break;

	case RP_NO: 		  /* remaining acceptible responses     */
	case RP_PARM:
	    thereply.rp_val = RP_NDEL;
	case RP_NDEL: 
	case RP_AGN: 
	case RP_NOOP: 
	    ph_wrply (&thereply, len);
	    thereply.rp_val = RP_OK;
	    break;                /* don't abort the process            */

	default: 		  /* responses which force abort        */
		ll_log (logptr, LLOGFAT,
			"reply err (%s)'%s'",
			rp_valstr (thereply.rp_val), thereply.rp_line);
	    if (rp_isbad (thereply.rp_val))
		return (thereply.rp_val);
	    return (RP_RPLY);
    }

    return (RP_OK);
}
 = ph2mm_send (curchan);
				  /* RECEPTION: PICKUP MESSAGES         */
	if (rp_gbval (result) == RP_BNO)
	    break;		  /* retrying won't help                */
	if (rp_gbval (result) == RP_BTNO)
	    continue;		  /* mmdf/src/deliver/   755      0     12           0  3671074625   7121 mmdf/src/deliver/ch_io.c   444      0     12       30000  3671073224  10434 #include "util.h"
#include "mmdf.h"

/*
 *     MULTI-CHANNEL MEMO DISTRIBUTION FACILITY  (MMDF)
 *
 *
 *     Copyright (C) 1979,1980,1981  University of Delaware
 *
 *     Department of Electrical Engineering
 *     University of Delaware
 *     Newark, Delaware  19711
 *
 *     Phone:  (302) 738-1163
 *
 *
 *     This program module was developed as part of the University
 *     of Delaware's Multi-Channel Memo Distribution Facility (MMDF).
 *
 *     Acquisition, use, and distribution of this module and its listings
 *     are subject restricted to the terms of a license agreement.
 *     Documents describing systems using this module must cite its source.
 *
 *     The above statements must be retained with all copies of this
 *     program and may not be removed without the consent of the
 *     University of Delaware.
 *     
 *
 *     version  -1    David H. Crocker    March   1979
 *     version   0    David H. Crocker    April   1980
 *     version   1    David H. Crocker    October 1981
 *
 */
/*  CH_IO:  Channel invocation & communication                          */

/*  Jun 80 Dave Crocker     fix ch_close fildes setting to -1
 *  Nov 80 Dave Crocker     convert to ch_struct pointer from ch_code
 *  Jul 81 Dave Crocker     ch_rdrec a little cleaner on error handling
 */

#define TIMEOUT 3600             /* 60 minute sanity timer on channel IO */

#include "ch.h"
#include <signal.h>
#include "nexec.h"

extern char pgm_bakgrnd;
extern LLog *logptr;
extern char *chndfldir;
extern int  *regfdary;           /* for nexec                         */

extern char *blt();

Chan    *ch_curchan;               /* currently active channel program   */
RP_Buf ch_rp;         /* last channel reply text            */

/* ***************  (ch_) COMMUNICATE WITH CHANNEL  ***************** */

LOCVAR FILE *ch_outfp,            /* output file handle for chan */
	   *ch_infp;              /* input file handle for channel */

LOCVAR int    ch_child;           /* Process id of channel program      */
LOCVAR int  (* ch_opipe) ();      /* old pipe action value              */

LOCFUN
	ch_catch ()               /* catch interrupt                    */
{
}

ch_end (type)                     /* get ready to send mail to channels */
    int type;
{                                 /* NOTOK => close; 0 => addr list end */
#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "ch_end (%d)", type);
#endif

    return (ch_sbend (type));
}
/**/

ch_sbinit (newchan)               /* "get" a specific channel           */
register Chan  *newchan;          /* pointer to chan structure          */
{                                 /* NOTOK => close; 0 => addr list end */
    int temp;

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "ch_open(%s)", newchan -> ch_name);
#endif

    if (newchan == ch_curchan)
	return (RP_OK);           /* already have the channel opened    */

    if (!pgm_bakgrnd && (newchan -> ch_access & DLVRBAK))
	return (RP_NOOP);         /* not allowed unless background      */

    if (ch_curchan != OK)         /* already have chan -> get rid of it */
	ch_end (OK);

    newchan -> ch_access |= DLVRDID; /* note that we did this channel      */

    if ((temp = ch_exec (newchan)) == RP_OK)
	ch_curchan = newchan;

    ch_opipe = signal (SIGPIPE, ch_catch);
    signal (SIGALRM, ch_catch);
    return (temp);
}
/**/

ch_sbend (type)               /* free the channel                   */
int type;                     /* clean ending / abort               */
{
    register int status;

#ifdef DEBUG
    ll_log(logptr, LLOGBTR, "ch_end (%d)", type);
#endif

    if (ch_child == 0)            /* don't have one                     */
	return (OK);

    switch (type)
    {
	case NOTOK:               /* ABORTING;  try to kill child       */
	    ll_log (logptr, LLOGTMP, "Trying to kill child  '%s' pid = (%d)",
			ch_curchan -> ch_name, ch_child);
	    kill (ch_child, SIGKILL);
	    goto closeit;

	case OK:                  /* normal end; just eof the pipes     */
	    ch_wrec ("end");      /* say goodbye */

    closeit:
	    fclose (ch_outfp);
	    fclose (ch_infp);

	    ch_outfp =            /* flag i/o ports as closed           */
		ch_infp = (FILE *) EOF;
	    signal (SIGPIPE, ch_opipe);
				  /* probably set it back to abort      */
	    break;

	default:
	    err_abrt (RP_NCMD, "Illegal closedest call (%d)", type);
    }

    status = pgmwait (ch_child);  /* pickup child's stats               */
    if (status == NOTOK)          /* abnormal proces termination        */
	ll_err (logptr, LLOGTMP, "(%s) child NOTOK", ch_curchan -> ch_name);

    if (rp_gbval (status) == RP_BNO)
	ll_log (logptr, LLOGTMP, "(%s) child (%s)",
		    rp_valstr (status), ch_curchan -> ch_name);
				  /* log major error returns only       */

    ch_curchan = OK;              /* mark current channel as "none"     */
    ch_child = 0;                 /* don't have one any more            */
    return (RP_OK);
}
/**/

ch_rrply (valstr, len)     /* read reply from channel            */
    RP_Buf *valstr;
    int *len;
{
    int     result;

#ifdef notdef
    ll_log(logptr, LLOGBTR, "ch_rrply()");
#endif

    ch_rp.rp_val = RP_NOOP;
    (void) strcpy (ch_rp.rp_line, "(No reason given)");

    result = ch_rrec ((char *) valstr, len);

#ifdef RUNALON
    switch (valstr -> rp_val)
    {
	case 'y':
	    result = RP_OK;
	    break;
	case 'n':
	    result = RP_NO;
	    break;
	case 'a':
	    result = RP_AOK;
	    break;
	case 'm':
	    result = RP_MOK;
	    break;
	case 't':
	    result = RP_TIME;
	    break;
	case 'c':
	    result = RP_NET;
    }
#endif

    *len -= 1;
    if (*len > 0)
    {
	blt (valstr -> rp_line, ch_rp.rp_line, *len);
	ch_rp.rp_line[*len] = '\0';
    }
    else
	*len = 0;


#ifdef DEBUG
    ll_log(logptr, LLOGFTR, "reply: (%s) '%s'",
	    rp_valstr (valstr -> rp_val), valstr -> rp_line);
#endif

    ch_rp.rp_val = valstr -> rp_val;

    if (rp_isbad (result))
	return (result);

    return (RP_OK);
}
/**/

ch_wrec (str)                     /* send info to channel               */
char    str[];                    /* string to send                     */
{
#ifdef notdef
    ll_log(logptr, LLOGBTR, "ch_wrec ()");
#endif
				  /* default error message              */
    if (ch_curchan == OK)         /* channel isn't really there         */
	return (RP_DHST);

#ifdef DEBUG
    ll_log(logptr, LLOGFTR, "sending:  '%s'", str);
#endif
#ifdef RUNALON
    printx ("Channel output:  '%s'\n", str);
#else
    if (setjmp (timerest)) {
	ll_err (logptr, LLOGTMP, "(%s) output err to channel",
		ch_curchan -> ch_name);
	ch_sbend (NOTOK);         /* close it                           */
	return (RP_NET);          /* class it as a "network" error      */
    }
    s_alarm (TIMEOUT);
    if (str)                  /* send it to channel                 */
	fputs (str, ch_outfp);

    putc ('\0', ch_outfp);
    fflush (ch_outfp);	/* This causes real IO */
    s_alarm (0);

    if (ferror (ch_outfp))
    {
	ll_err (logptr, LLOGTMP, "(%s) output err to channel",
		ch_curchan -> ch_name);
	ch_sbend (NOTOK);         /* close it                           */
	return (RP_NET);          /* class it as a "network" error      */
    }
    return (RP_OK);
#endif
}
/**/

LOCFUN
	ch_rrec (rplyline, len)    /* read reply from channel            */
    char *rplyline;
    int *len;
{
    int     result;
    char *valstr;

#ifdef notdef
    ll_log(logptr, LLOGBTR, "ch_rrec()");
#endif
#ifdef RUNALON
    printx ("Chrcv:  ");
#endif

    if (setjmp (timerest)) {
	ll_err (logptr, LLOGTMP, "(%s) read err from channel",
		    ch_curchan -> ch_name);
	ch_end (NOTOK);             /* probably not there, anyhow         */
	return (RP_NET);
    }
    s_alarm (TIMEOUT);
    result = RP_OK;
    rplyline[0] = getc (ch_infp); /* one-char rp.h reply value          */
    s_alarm (0);

    *len = 1;
    if (feof (ch_infp))
    {
	ll_log (logptr, LLOGTMP, "(%s) premature channel eof",
		    ch_curchan -> ch_name);
	ch_end (NOTOK);             /* probably not there, anyhow         */
	return (RP_NET);
    }
    if (ferror (ch_infp))
    {
	ll_err (logptr, LLOGTMP, "(%s) read err from channel",
		    ch_curchan -> ch_name);
	ch_end (NOTOK);             /* probably not there, anyhow         */
	return (RP_NET);
    }

#ifdef RUNALON
    switch (rplyline[0])
    {
	case 'y':
	    replyline[0] = RP_OK;
	    break;
	case 'n':
	    replyline[0] = RP_NO;
	    break;
	case 'a':
	    replyline[0] = RP_AOK;
	    break;
	case 'm':
	    replyline[0] = RP_MOK;
	    break;
	case 't':
	    replyline[0] = RP_TIME;
	    break;
	case 'c':
	    replyline[0] = RP_NET;
    }
#endif
    if (setjmp (timerest)) {
	ll_err (logptr, LLOGTMP, "(%s) Channel died", ch_curchan -> ch_name);
	*len += 1;
	return (rplyline[0] = result = RP_NET);
    }
    s_alarm (TIMEOUT);
    if ((*len = gcread (ch_infp, &rplyline[1], LINESIZE - 2, "\n\000\377"))
			== NULL)
    {
	ll_err (logptr, LLOGTMP, "(%s) Channel died", ch_curchan -> ch_name);
	rplyline[0] =
	    result = RP_NET;
    }
    s_alarm (0);

    valstr = rp_valstr (rplyline[0]);
    if (*valstr == '*')
    {
	ll_log (logptr, LLOGFAT, "(%s) (%s) '%s'",
		ch_curchan -> ch_name, valstr, &rplyline[1]);
	return (RP_RPLY);
    }

    *len += 1;
#ifdef DEBUG
    ll_log(logptr, LLOGFTR, "record: (%s) '%s'", valstr, &rplyline[1]);
#endif
    return (result);
}
/**/

LOCFUN
	ch_exec (thechan)         /* invoke process which "is" channel  */
Chan   *thechan;      /* channel descriptor structure       */
{
    int     nampipe[2];           /* to xmit process for addr name        */
    int     respipe[2];           /* from xmit process, for responses     */
    int     oldpid;               /* parent pid */
    char    temppath[128];
    char    arg1[10];             /* fd for input (names/addrs)         */
    char    arg2[10];             /* fd for output (responses)          */
    char    arg3[20];             /* options                            */

#ifdef notdef
    ll_log(logptr, LLOGBTR, "ch_exec()");
#endif
#ifdef RUNALON
    printx ("Forking %s\n", thechan -> ch_name);
    ch_infp = fdopen (0, "r");
    ch_outfp = fdopen (1, "w");

    return (RP_OK);               /* debug mode                         */
#endif;

    if (pipe (nampipe) < OK)
    {
	ll_err(logptr, LLOGTMP, "problem with 1st ch_exec pipe");
	return (RP_NO);
    }
    if (pipe (respipe) < OK)
    {
	ll_err(logptr, LLOGTMP, "problem with 2d ch_exec pipe");
	(void) close (nampipe[0]);
	(void) close (nampipe[1]);
	return (RP_NO);
    }
    sprintf (arg1, "%d", nampipe[0]);
    sprintf (arg2, "%d", respipe[1]);
    arg3[0] = '\0';
    if( domsg == TRUE )
	strcat( arg3, "w" );

    regfdary[0] = 0;              /* must keep open, in case of POBox   */
    regfdary[1] = 1;              /*   or other interaction with caller */
    regfdary[2] = NOTOK;
    regfdary[nampipe[0]] = nampipe[0];
    regfdary[respipe[1]] = respipe[1];

    getfpath (thechan -> ch_ppath, chndfldir, temppath);

#ifdef DEBUG
    ll_log(logptr, LLOGBTR, "[%s]'%s' ('%s','%s', '%s')",
		temppath, thechan -> ch_name, arg1, arg2, arg3);

#endif
    ll_close (logptr);            /* since we are about to fork()       */
    fflush (stdout);
    fflush (stderr);
    oldpid = getpid ();

    ch_child = nexecl (FRKEXEC, FRKERRR, regfdary, temppath,
			thechan -> ch_name, arg1, arg2, arg3, (char *)0);

    (void) close (nampipe[0]);     /* Close other end of pipes */
    (void) close (respipe[1]);
    if (ch_child == NOTOK)        /* didn't get the child               */
    {
	ll_err (logptr, LLOGFAT, "Unable to run '%s'", thechan -> ch_ppath);
	ch_child = 0;
	(void) close (nampipe[1]);       /* Close other end of pipes     */
	(void) close (respipe[0]);
	if (getpid () == oldpid)
	    return (RP_NO);       /* still the parent => fork() error   */
	else
	    exit (RP_MECH);      /* must be child                       */
    }
				  /* clean up regfd arrary for future   */
    regfdary[nampipe[0]] = -1;
    regfdary[respipe[1]] = -1;

    ch_infp = fdopen (respipe[0], "r");
    ch_outfp = fdopen (nampipe[1], "w");

    printx ("[ Accessing %s (%s)]\n", 
		thechan -> ch_name, thechan -> ch_show);
    ll_log (logptr, LLOGPTR, "[ %s ] (r%d,w%d)",
		thechan -> ch_name, respipe[0], nampipe[1]);

    return (RP_OK);
}

mmdf/src/deliver/deliver.c   444      0     12      146273  3671073230  11046 /*
 *     MULTI-CHANNEL MEMO DISTRIBUTION FACILITY  (MMDF)
 *
 *
 *     Copyright (C) 1979,1980,1981  University of Delaware
 *
 *     Department of Electrical Engineering
 *     University of Delaware
 *     Newark, Delaware  19711
 *
 *     Phone:  (302) 738-1163
 *
 *     This program module was developed as part of the University
 *     of Delaware's Multi-Channel Memo Distribution Facility (MMDF).
 *
 *     This module and its listings may be used freely by United States
 *     federal, state, and local government agencies and by not-for-
 *     profit institutions, after sending written notification to
 *     Professor David J. Farber at the above address.
 *
 *     For-profit institutions may use this module, by arrangement.
 *
 *     Notification must include the name of the acquiring organization,
 *     name and contact information for the person responsible for
 *     maintaining the operating system, name and contact information
 *     for the person responsible for maintaining this program, if other
 *     than the operating system maintainer, and license information if
 *     the program is to be run on a Western Electric Unix(TM) operating
 *     system.
 *     
 *     Documents describing systems using this module must cite its
 *     source.
 *     
 *     Development of this module was supported in part by the
 *     University of Delaware and in part by contracts from the United
 *     States Department of the Army Readiness and Materiel Command and
 *     the General Systems Division of International Business Machines.
 *     
 *     Portions of the MMDF system were built on software originally
 *     developed by The Rand Corporation, under the sponsorship of the
 *     Information Processing Techniques Office of the Defense Advanced
 *     Research Projects Agency.
 *
 *     The above statements must be retained with all copies of this
 *     program and may not be removed without the consent of the
 *     University of Delaware.
 *     
 *
 *     version  -1    David H. Crocker    March  1979
 *     version   0    David H. Crocker    April  1980
 *     version   1    David H. Crocker    May    1981
 *
 */
#include "util.h"
#include "mmdf.h"
#include <signal.h>
#include <sys/stat.h>
#include <pwd.h>

/* ***        RUN SETUID TO ROOT, so it can setuid to MMDF          *** */
/* ***                                                              *** */
/* ***  This is so that BOTH real and effective id's show as MMDF   *** */

/*
 *  OVERVIEW
 *
 *      The program may be called to process the entire mail queue or just
 *  handle some explicitly-named messages.  It will try all available
 *  channels that the mail needs to go out on, or will limit itself to
 *  explicitly, named channels.  It tries to minimize channel invocations,
 *  and process messages in order.
 *
 *      A list of channels to process is made from an argument, or simply
 *  is copied from the list of all active channels.
 *
 *      A list of messages to process is made from the arguments, or is
 *  built from a scan of the entire queue.  This list is in-core, so it is
 *  subject to serious size limits.  If more than 1000 messages are in the
 *  queue, the list is not built and the message names are read directly
 *  from the directory, as the messages are to be processed.
 *
 *      With an incore list, the messages can be sorted, according to
 *  creation date, and therefore processed in order.  In the overflow
 *  condition, this is not possible.  The primary sorting key is channels,
 *  with messages next.  This causes many passes over the messages (i.e.,
 *  access to their address lists) but minimizes channel invocations.  A
 *  final sorting key should be hosts on the channels, to minimize re-
 *  connections, but this currently is not done.
 */
/**/

/*  DELIVER:  Mail transmission manager
 *
 *  May 80 Dave Crocker     fix fd, sig, setuid handling & extral dial
 *  Jun 80 Dave Crocker     mn_mmdf reset userid, when setuid to mmdf
 *                          fix fd bugs in rtn_report
 *                          added ovr_free, fixing abnormal ending
 *  Aug 80 Dave Crocker     moved ovr_free, so free always done
 *                          removed use of ovr_seq
 *                          created msg_note, to control printout
 *  Sep 80 Dave Crocker     added -r switch to rtn-report mail call
 *  Oct 80 Dave Crocker     converted to stdio & V7 ability
 *                          split off rtn_io and ml_io
 *  Nov 80 Dave Crocker     re-organized ovr_ module
 *  Jun 81 Dave Crocker     remove use of ffio package
 *                          change adr_t2ok -> adr_t2fin, to allow
 *                          permanent failure during text phase
 *                          move adr_tok=FALSE from adr_t2fin to adr_done
 *  Jul 81 Dave Crocker     ovr_mfree check for valid ovr_mlist before free
 *         Mike Minnich     add -s, to force no-sort of queue
 *         Dave Crocker     ovr_cstep had bad fread return test
 *                          upped max # messages to 1000
 *                          adr_more=TRUE in adr_done() on temp error
 *                          disable SIGQUIT, for background mode
 *  Aug 81 Dave Crocker     change mn_*init ordering for safe callerid refs
 *                          allow partitioning queue by message size
 *  Sep 81 Dave Crocker     adr_send() added RP_RPLY and RP_PROT to BHST
 *                          case for 'temporary' error
 *  Nov 81 Dave Crocker     ovr_ismsg require message name begin "msg."
 *  Dec 81 Dave Crocker     ovr_curch -> arg rather than global
 *                          ch_init called with chan ptr, not char code
 *  Jan 82 Dave Crocker     add setgid, along with setuid
 *  Mar 83 Doug Kingston    changed to use format independent directory
 *                          access routines a la 4.2BSD  (libndir.a)
 */

/**/

extern LLog msglog;
extern char *logdfldir;
LLog *logptr = &msglog;

extern char *blt ();
extern time_t time ();
extern char *strdup();
extern struct passwd *getpwuid();

time_t   curtime;                 /* Current time secconds              */

int     effecid,                  /* system number of pgm/file's owner  */
	callerid;                 /* who invoked me?                    */
char    *callername = NULL;       /* login name of invoker (for pickup) */
int   named;                      /* number of named files to do        */

char    pgm_bakgrnd;              /* True if running as system daemon   */

extern char *mquedir;            /* directory under quedfldir for texts */
extern char *supportaddr;        /* orphaned mail goes here             */

/*  mn_  */

short   inhome;                   /* need to do full-path chdir?        */

/*  ch_  */

#include "ch.h"

extern Chan **ch_tbsrch,          /* full list of channels              */
	    **ch_exsrch,          /* default order to execute channels  */
				  /*   not same list as ch_tbsrch       */
	    *ch_ptrlist[],        /* where to put list of chans to proc */
	    *ch_curchan;          /* currently invoked channel program  */

extern int  ch_numchans;          /* number of channels which are known */

extern RP_Buf ch_rp;              /* last channel reply text            */

/*  ovr_  */

extern int  maxqueue;             /* when to do linear processng        */
DIR     *ovr_dirp;                /* handle on the queue directory      */

short   ovr_pickup;               /* pickup passive channels            */
char    ovr_dolin;                /* force linear search                */
/*NOSTRICT*/
time_t  ovr_time = 0L;            /* time based selection (0 = off)     */

/**/

/*  msg_  */

#include "msg.h"		/*  msg_cite() is defined in adr_queue.h  */

char    msg_sender[ADDRSIZE + 1]; /* return address of current message  */
/*NOSTRICT*/
time_t  msg_ttl = 0L;           /* Time to live for cache entries (secs) */
				/* This value will override the channel's */
				/* value if it is non-zero */

/*  adr_  */

#include "adr_queue.h"

long    adr_start;              /* Start of sublist being sent */
long    adr_end;                /* Where we left off to go back */
short   adr_skipped,              /* some addresses were skipped        */
	adr_more,                 /* some addreseses not completed      */
	adr_tok;                  /* adr ok; text not yet sent          */


/*****  (mn_)  MAIN MAIN MAIN MAIN MAIN MAIN MAIN MAIN MAIN MAIN  ***** */

char    obuf[BUFSIZ];   /* buffering for STDOUT */

extern char *quedfldir,          /* home directory for mmdf            */
	    *aquedir,            /* subordinate address directory      */
	    *squepref;
extern unsigned mailsleep;   /* how long daemon should sleep       */

main (argc, argv)
int       argc;
char   *argv[];
{
    extern char *dupfpath ();

    mmdf_init (argv[0]);
    setbuf( stdout, obuf );
    siginit ();                   /* standard interrupt initialization  */
				  /* distinguish different delivers     */

    getwho (&callerid, &effecid); /* who am I and who is running me?    */

    time (&curtime);              /* Get current time (in hours)        */

    mn_mmdf();                    /* set up id's properly               */
    mn_arginit (argc, argv);      /* listen to the parameters           */

    if (!inhome && chdir (quedfldir) < OK)	/* already in quedfldir */
	    err_abrt (RP_LIO, "can't chdir to %s", quedfldir);

    if (named != 0)
				  /* active send of named messages      */
	ovr_named (argc, argv);
				  /* argv has the list                  */
    else
    {                             /* go through the whole queue         */
	if (pgm_bakgrnd)          /* background daemon                  */
	    FOREVER
	    {
		ovr_cstep (ovr_dolin, 0);
				  /* do the entire mail queue           */
		mn_epilogue (TRUE);
				  /* random special actions             */
		sleep (mailsleep);/* DAEMON PERIODICITY                 */
	    }
	else                      /* one-shot pass over the queue       */
	    ovr_cstep (ovr_dolin, 0);
    }


    /*
     *  Poll only if full-queue daemon and not invoked for pickup only.
     */
    mn_epilogue ((named == 0) && (!ovr_pickup));

#ifdef DEBUG
    printx ("%s:  normal end\n", argv[0]);
#endif
    exit (RP_OK);                 /* "normal" end, even if pgm_bakgrnd      */
}
/**/

LOCFUN
	mn_mmdf ()                /* setuid to mmdf: bypass being root  */
{                                 /* get sys id for mmdf; setuid to it  */
    extern char *pathsubmit;     /* submit command file name           */
    extern char *cmddfldir;      /* directory w/mmdf commands          */
    char    temppath[LINESIZE];
    struct stat    statbuf;

#ifdef DEBUG
    ll_log (logptr, LLOGPTR, "mn_mmdf(); effec==%d, pickup==%d",
		effecid, ovr_pickup);
#endif

/*  the following is a little strange, doing a stat on the object
 *  file, because setuid-on-execute does not work when the caller
 *  is root, as will happen when this is started by the rc file.
 *  hence, the effective id, from a getuid, will show root & not mmdf.
 *
 *  the goal is to have this process be name-independent of the caller,
 *  so that returned mail comes from mmdf and not the invoker.
 *
 *  in pickup mode, the id of the caller has to be retained, since
 *  pobox channels use that to determine access rights to mail.
 *
 *  Also sets gid to mmdf's gid  --  <DPK@BRL>
 */

    if (effecid == 0 && !ovr_pickup)
    {
	getfpath (pathsubmit, cmddfldir, temppath);

	if (stat (temppath, &statbuf) < OK)
	    err_abrt (RP_LIO, "Unable to stat %s", temppath);
				  /* use "submit" to get mmdf id        */

	if (setgid (statbuf.st_gid) == NOTOK)
	    err_abrt (RP_LIO, "Can't setgid to mmdf (%d)", statbuf.st_gid);
	if (setuid (statbuf.st_uid) == NOTOK)
	    err_abrt (RP_LIO, "Can't setuid to mmdf (%d)", statbuf.st_uid);

	effecid = statbuf.st_uid; /* mostly needed for return mail      */
    }
}

LOCFUN
	mn_arginit (argc, argv)   /* basic process initialization       */
int       argc;
char   *argv[];
{
    register int    tmp;

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "mn_arginit");
#endif

    for (tmp = 1; tmp < argc; tmp++)
    {                             /* tell us your arguments             */
	if (*argv[tmp] == '-')    /* a real switch                      */
	    prm_set (&argv[tmp][1]);
	else                      /* was a msgname, if not a switch     */
	    named++;              /* count number of named messages     */
    }

    prm_end ();                   /* parm cleanup & value verification  */
}

LOCFUN
	mn_epilogue (maypoll)     /* cleanup after a pass               */
    int maypoll;
{
    register Chan **chanptr;

#ifdef DEBUG
	    ll_log (logptr, LLOGBTR, "mn_epil");
#endif
    ovr_end (OK);                 /* no children kept around            */

#ifndef RUNALON
    if (maypoll)                  /* do any channel-polling             */
	ch_poll (ch_ptrlist);
#endif

    for (chanptr = ch_tbsrch; *chanptr != 0; chanptr++)
	(*chanptr) -> ch_access &= ~DLVRDID;
				/* clear the "did it" bit on full list  */
    ll_close (logptr);         /* in case it's in overflow condition    */
}
/*******************  (prm_)  PARMETER SETTING  ********************* */

prm_set (str)                     /* assign parm vals, as user specs    */
register char  *str;
{
    extern char *prm_cload();

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "prm_set '%s'", str);
#endif
    for (; !isnull (*str); str++)
	switch (*str)
	{
	    case 'L':           /* Specify alternate logfile, SU only */
		if ((callerid == 0 || callerid == effecid) && *(++str)) {
		    ll_log (logptr, LLOGFST, "Logfile is '%s'", str);
		    logptr->ll_file = dupfpath(str, logdfldir);
		}
		return;

	    case 'T':             /* set daemon sleep time       */
		str++;
		if (*str != '\0')
		{
		    mailsleep = atoi (str);
#ifdef DEBUG
		    ll_log (logptr, LLOGFTR, "Mailsleep %dsecs", mailsleep);
#endif
		}
		return;

	    case 'V':
		if ((callerid == 0 || callerid == effecid) && *(++str)) {
		    char *cp = str;
		    int level;

		    ll_log (logptr, LLOGFST, "Loglevel '%s'", str);
		    if ((level = tai_llev (1, &cp)) < 0)
			logptr->ll_level = level;
		}
		return;

	    case 'b':             /* run as background daemon           */
		if (callerid == 0 || callerid == effecid)
		    pgm_bakgrnd = TRUE;
		else
		    err_abrt (RP_PARM,
				"User id (%d) not authorized for background",
				callerid);
		break;

	    case 'c':             /* process specific channels          */
		str = prm_cload (++str);
		if (isnull (*str))
		    return;
		break;

	    case 'd':             /* already in quedfldir, only for SU */
		if (callerid == 0 || callerid == effecid)
		    inhome = TRUE;
		break;

	    case 'l':           /* Specify the time to live in minutes */
		/*NOSTRICT*/
		msg_ttl = ((time_t)atoi(++str)) * 60L;
		return;

	    case 'm':             /* set maxsort threshold		*/
		str++;
		if (*str != '\0')
		{
		    maxqueue = atoi(str);
#ifdef DEBUG
		    ll_log (logptr, LLOGFTR, "Maxsort set to '%d' msgs",
						maxqueue);
#endif
		}
		return;

	    case 'p':             /* pickup passive channel mail        */
		ovr_pickup = TRUE;
		break;

	    case 's':             /* force linear search                  */
		ovr_dolin = TRUE;
		break;

	    case 't':             /* time selection of messages */
		ovr_time = (time_t) atoi(++str) * 3600; /* get value in secs */
		return;

	    case 'w':
		domsg = TRUE;
		break;            /* caller wants to watch                */

	    default:
		err_abrt (RP_PARM, "switch '%c' is not defined", *str);
	}
}
/**/

prm_end ()                        /* cleanup vals after user settings   */
{
    register int ch_cand,         /* candidate for checking             */
		 ch_auth;         /* authorized for checking            */

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "prm_end");
#endif

    (void) signal (SIGINT, SIG_IGN);    /* disable keyboard interrupt   */
    if (pgm_bakgrnd)              /* running as daemon                  */
    {                             /* get rid of unnecessary fd's        */
	(void) signal (SIGQUIT, SIG_IGN);/* disable keyboard quit       */
	sprintf (logptr -> ll_hdr, "BG-%04d", getpid () % 1000);
				  /* distringuish different daemons     */
	freopen ("/dev/null", "r", stdin);
				  /* open stdin to null                 */
	if (!domsg)               /*  stdout null if no domsg           */
	    freopen ("/dev/null", "w", stdout);
	freopen ("/dev/null", "w", stderr);
				  /*  stderr null always                */
    }
    if (!ovr_pickup && !domsg)
	(void) signal (SIGHUP, SIG_IGN); /* Ignore hangups               */

    if (ovr_pickup) {
	register struct passwd *pw;

	if ((pw = getpwuid(callerid)) == (struct passwd *)NULL)
	    err_abrt (RP_LIO, "UID (%d) not in /etc/passwd, pickup denied");
	callername = strdup(pw->pw_name);
#ifdef DEBUG
	ll_log (logptr, LLOGBTR, "pickup caller's name is '%s'", callername);
#endif
	printx ("pickup caller's name is '%s'\n", callername);
    }

    if (ch_ptrlist[0] == (Chan *) 0) /* none requested, make all eligible  */
    {
#ifdef DEBUG
	ll_log (logptr, LLOGBTR, "do all chans");
#endif
	for (ch_cand = 0; ch_cand <= ch_numchans; ch_cand++)
	    ch_ptrlist[ch_cand] = ch_exsrch[ch_cand];
				  /* copy the default execution list    */
    }
#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "check channel authorization");
    ll_log (logptr, LLOGBTR, "callerid=%d,effecid=%d,bakgrnd=%d,pickup=%d",
		callerid, effecid, pgm_bakgrnd, ovr_pickup);
#endif
    for (ch_cand = ch_auth = 0; ch_ptrlist[ch_cand] != (Chan *) 0; ch_cand++)
    {                             /* what channels really to be checked */
	/* CHANNEL INVOCATION POLICY */
#ifdef DEBUG
	ll_log (logptr, LLOGFTR, "(%d) %s:  passive = %o", ch_cand,
		    ch_ptrlist[ch_cand] -> ch_name,
		    (ch_ptrlist[ch_cand] -> ch_access) & DLVRPSV);
#endif
	if ((!pgm_bakgrnd && (callerid != 0) && (callerid != effecid) &&
	    (ch_ptrlist[ch_cand] -> ch_access & DLVRBAK)) ||
				  /* background only and we are not     */
				  /* root or mmdf                       */
	    (!ovr_pickup && (ch_ptrlist[ch_cand] -> ch_access & DLVRPSV)) ||
				  /* passive only & we aren't           */
	    (ovr_pickup && !(ch_ptrlist[ch_cand] -> ch_access & DLVRPSV)) ||
				  /* active chan & we aren't            */
	    (ovr_pickup && (ch_ptrlist[ch_cand] -> ch_login != 0) &&
	      !lexequ(ch_ptrlist[ch_cand] -> ch_login, callername)))
	{
	    printx ("Channel %s (%s) not authorized\n",
			ch_ptrlist[ch_cand] -> ch_show,
			ch_ptrlist[ch_cand] -> ch_name);
	    ll_log (logptr, LLOGTMP, "Channel %s (%s) not authorized",
			ch_ptrlist[ch_cand] -> ch_show,
			ch_ptrlist[ch_cand] -> ch_name);
	}
	else
	    ch_ptrlist[ch_auth++] = ch_ptrlist[ch_cand];
    }
    ch_ptrlist[ch_auth] = 0;      /* end list of authorized channels    */
}
/**/

LOCFUN  char *                    /* make list of chans to process      */
		prm_cload (strtptr)
    register char *strtptr;
{                                 /* format: "channame1,channame2;"     */
    short namelen;
    char channame[LINESIZE];
    short more;
    register int ch_ind;
    register char *endptr;

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "prm_cload (%s)", strtptr);
#endif

    for (more = TRUE, ch_ind = 0, endptr = strtptr; more; strtptr = ++endptr)
    {                             /* list is put into ch_ptrlist        */
	FOREVER
	{
	    switch (*endptr)
	    {
		case '\0':
		case ';':
		    more = FALSE;
		case ',':
		    namelen = endptr - strtptr;
		    blt (strtptr, channame, namelen);
		    channame[namelen] = '\0';
		    break;

		default:
		    endptr++;
		    continue;
	    }
	    break;
	}

#ifdef DEBUG
	ll_log (logptr, LLOGFTR, "channame = '%s'", channame);
#endif
	if ((ch_ptrlist[ch_ind] = ch_nm2struct (channame)) == (Chan *) NOTOK)
	{
	    printx ("channel '%s' is not defined\n", channame);
	    ll_log (logptr, LLOGFST, "*** channel '%s' is not defined", channame);
	    continue;
	}
	ch_ind++;
    }
    ch_ptrlist[ch_ind] = (Chan *) 0; /* end of list of chans to process    */
#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "chans to proc = %d", ch_ind);
    ll_log (logptr, LLOGFTR, "chans:");
    for (ch_ind = 0; ch_ptrlist[ch_ind] != (Chan *) 0; ch_ind++)
	ll_log (logptr, LLOGFTR, "(%d) %s", ch_ind,
		     ch_ptrlist[ch_ind] -> ch_name);
#endif
    return (--endptr);              /* indicate where end of parameter is */
}
/************  (ovr_)  OVERALL SEQUENCING MANAGEMENT  *************** */

extern int  warntime,    /* hours to wait before notification  */
	    failtime;    /* hours to wait before returning msg */


LOCVAR Msg *ovr_mlist;              /* list of files in queue             */

ovr_end (status)                    /* cleanup and terminate session      */
register int    status;
{
#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "ovr_end (%d)", status);
#endif

    if (status == NOTOK)            /* abnormal termination               */
	mq_rkill (OK);           /* terminate message processing       */

    ch_end (status);                /* force child to die                 */
}


ovr_named (argc, argv)            /* process a set of named messages    */
    int   argc;
    char *argv[];
{

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "ovr_named ()");
#endif

    if (ovr_malloc (named) == NOTOK)
	err_abrt (RP_MECH, "unable to ovr_malloc for (%d) messages", named);

    named = ovr_nload (argc, argv); /* load names into ovr_mlist          */
    ovr_cstep (ovr_dolin, named);   /* step through channels for list     */

    ovr_mfree ();                  /* free up ovr_malloc'd space          */
    ovr_end (OK);                 /* signal done with channel           */
    return;
}
/**/

ovr_queue (curch, dolin)         /* Process entire message queue       */
	Chan *curch;
	int dolin;

{
    int ovr_qnum ();
    int maxcount;
    int actualcount;
    char subque[LINESIZE];

#ifdef DEBUG
    ll_log (logptr, LLOGBTR,
		"ovr_queue (%s, %o)", curch -> ch_name, dolin);
#endif

    sprintf (subque, "%s%s", squepref, curch -> ch_queue);
    if ((ovr_dirp = opendir (subque)) == NULL)
	 /*   err_abrt (RP_FOPN, "can't open address queue"); */
			      /* SEK - I don't see any reason to abort */
    {
	ll_log (logptr, LLOGGEN, "Failure to open queue dir '%s'", subque);
	ovr_dirp = (DIR *) EOF;
	return;
    }

    if (dolin)                /* sequential processing demanded     */
    {
	ovr_mstep (curch, TRUE, NOTOK);
	goto endit;
    }

    printx ("sorting the queue.\n");
    maxcount = ovr_qnum ();   /* how many messages in the queue?    */

    if (maxcount == 0)
	goto endit;

/*  The following is a hack to deal with the fact that the queue searching
 *  is linear and therefore VERY slow when it gets large.  This can lead
 *  to deliver's taking too long, before coming up with the first message
 *  to send.  A caller, using a pickup channel, may timeout before getting
 *  the next data packet.
 */
    if (maxcount > maxqueue)    /* XXX was "ovr_pickup && ..." */
    {                             /* sequential, if too large           */
	ovr_mstep (curch, TRUE, NOTOK);
	goto endit;
    }

    if (ovr_malloc (maxcount += 5) == NOTOK)
				  /* allow a few extra arrivals         */
    {                             /* problem with allocation            */
	ovr_mstep (curch, TRUE, maxcount);
	goto endit;               /* cop out with linear processing     */
    }

    actualcount = ovr_qload (curch, maxcount);
    printx ("%d messages, %d to be processed\n", maxcount - 5, actualcount);
    (void) fflush (stdout);
    if (actualcount != 0)
    {                             /* actual number to be processed      */
	ovr_sort (actualcount);      /* get into processing order          */
	ovr_mstep (curch, FALSE, actualcount);
				  /* step through channels              */
    }
    ovr_mfree ();                 /* free up ovr_mlist storage          */

endit:
    closedir (ovr_dirp);
    ovr_dirp = (DIR *) EOF;
}
/**/

LOCFUN
	ovr_cstep (dolin, numproc) /* step by chan, thru list of msgs    */
	    int dolin;
	    int numproc;
{
    register Chan *curch;
    register int ind;
#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "ovr_cstep (linear=%o, num=%d)", dolin, numproc);
#endif

#ifdef DEBUG
    ll_log (logptr, LLOGFTR, "chans:");
    for (ind = 0; ch_ptrlist[ind] != (Chan *) 0; ind++)
	ll_log (logptr, LLOGFTR, "(%d) %s", ind, ch_ptrlist[ind] -> ch_name);
#endif

    for (ind = 0; (curch = ch_ptrlist[ind]) != (Chan *) 0; ind++)
    {                             /* choose channel list & process it   */
#ifdef DEBUG
	ll_log (logptr, LLOGBTR, "checking %s (%s)",
		    curch -> ch_show, curch -> ch_name);
#endif
	printx ("\n[ Checking for %s (%s) mail ]\n",
		    curch -> ch_show, curch -> ch_name);
	(void) fflush (stdout);

/* process message queue, for this channel; abort if problem with channel */
	if (numproc != 0)       /* we have a named list */
	    ovr_mstep (curch, dolin, numproc);
	else                    /* build the list from the queue */
	    ovr_queue (curch, dolin);
    }
}
/**/

LOCFUN
	ovr_mstep (curch, dolin, numproc)
				  /* step through msgs for a channel    */
    Chan *curch;
    int dolin;
    int numproc;
{
    register struct direct *dp;
    register int  ind;

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "ovr_mstep(chan=%s, lin=%d, %d)",
		curch -> ch_name, dolin, numproc);
#endif

    if (dolin)                /* have to read the queue directory   */
    {                             /* for each message in directory      */
	ll_log (logptr, LLOGGEN, "unsorted queue search");
	printx ("Linear search of unsorted full queue.\n\t");
	if (ovr_malloc (1) == NOTOK)
	    err_abrt (RP_MECH, "unable to ovr_malloc for 1 message");
				  /* create a placeholder               */
	for (rewinddir (ovr_dirp), numproc = 0;
		(dp = readdir (ovr_dirp)) != NULL; )
	{                         /* straight linear processing         */
	    if (ovr_ismsg (dp))
	    {
		numproc++;
		(void) strcpy (ovr_mlist[0].mg_mname, dp->d_name);
		if (msg_proc (curch, &ovr_mlist[0]) == RP_NET)
		{
			printx("killing channel '%s'\n", curch->ch_name);
			(void) fflush( stdout );
			ch_sbend( NOTOK );
		}
	    }
	}
	ovr_mfree ();
    }
    else                          /* we have an in-core list            */
    {
	for (ind = 0; ind < numproc; ind++)
	    switch (msg_proc (curch, &ovr_mlist[ind]))
	    {
		case RP_DONE:
		    break;

	        case RP_NET:        /* problem with channel */
		    printx("killing channel '%s'\n", curch->ch_name);
		    (void) fflush( stdout );
		    ch_sbend (NOTOK);
	    }
    }
    return;
}
/**/

LOCFUN
	ovr_nload (argc, argv)  /* load named message list            */
    register int   argc;
    char *argv[];
{
    register int argind,
		 msgind;

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "ovr_nload()");
#endif

    for (msgind = 0, argind = 1; argind < argc; argind++)
	if (argv[argind][0] != '-') {
#ifdef DEBUG
	    ll_log (logptr, LLOGFTR, "name='%s'", argv[argind]);
#endif
	    (void) strcpy (ovr_mlist[msgind++].mg_mname, argv[argind]);
	}

    return (msgind);
}

LOCFUN  int
    ovr_qnum ()  /* how many messages are in the queue */
{
    register struct direct *dp;
    register int num;

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "ovr_qnum()");
#endif

    for (rewinddir (ovr_dirp), num = 0; (dp = readdir (ovr_dirp)) != 0; )
	if (ovr_ismsg (dp)) {
	    num++;
	    if (num > maxqueue)
		break;
	}


#ifdef DEBUG
    ll_log (logptr, LLOGFTR, "%d msgs", num);
#endif
    return (num);
}
/**/

LOCFUN
	ovr_qload (curch, numproc)  /* get list of messages from queue    */
	    Chan *curch;
	    int numproc;
{
    struct stat statbuf;
    char msg_txname[LINESIZE];
    register struct direct *dp;
    register int num;
    time_t cutoff_time;

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "ovr_qload(%d)", numproc);
#endif
    rewinddir (ovr_dirp);
    if (ovr_time != 0L) {
	time(&curtime);			/* get current time in seconds */
	cutoff_time = curtime - ovr_time;
    }
    for (num = 0; num < numproc && (dp = readdir (ovr_dirp)) != NULL; )
	if (ovr_ismsg (dp))
	{
	    (void) strcpy (ovr_mlist[num].mg_mname, dp->d_name);
				  /* get queue entry name (msg name)   */
	    if (mq_rinit (curch, &ovr_mlist[num], msg_sender) != OK)
		continue;

	    if (ovr_time != 0L && ovr_mlist[num].mg_time < cutoff_time) {
		mq_rkill (OK);
		printx ("%s: old msg\n", dp->d_name);
		(void) fflush (stdout);
		continue;               /* Ignore old messages */
	    }

#ifdef LARGESIZE
	    sprintf (msg_txname, "%s%s", mquedir, ovr_mlist[num].mg_mname);
	    if (stat (msg_txname, &statbuf) != NOTOK &&
		    st_gsize (&statbuf) > LARGESIZE)
	    {
		ovr_mlist[num].mg_large = TRUE;
#ifdef DEBUG
		ll_log (logptr, LLOGFTR, "large message (%s)",
			    ovr_mlist[num].mg_mname);
#endif
	    }
	    else
		ovr_mlist[num].mg_large = FALSE;
#endif
	    mq_rkill (OK);
	    num++;
	}

#ifdef DEBUG
    ll_log (logptr, LLOGFTR, "actual = %d", num);
#endif
    return (num);
}
/**/

LOCFUN
	ovr_qcompare (a, b)       /* compare msg creation times         */
      Msg *a,             /*    msg a                           */
	  *b;                /*    msg b                           */
{                                 /* called by qsort                    */
    register long   a2b_diff;

#ifdef LARGESIZE
    if (b -> mg_large)           /* B > 100K bytes                     */
    {
	if (!a -> mg_large)      /* but A is not                       */
	    return (-1);
    }
    else
	if (a -> mg_large)       /* A isn't but B is                   */
	    return (1);
#endif
    if ((a2b_diff = a -> mg_time - b -> mg_time) == 0)
	return (0);               /* straight time-of-creation compare  */
    return ((a2b_diff  < 0) ? -1 : 1);
}

LOCFUN
	ovr_sort (numproc)        /* Sort the entries by creation date.   */
	   int numproc;
{
#ifdef DEBUG
    ll_log (logptr, LLOGFTR, "ovr_sort (%d)", numproc);
    ovr_log ("before", numproc);
#endif
    qsort ((char *)ovr_mlist, numproc, sizeof (Msg), ovr_qcompare);
#ifdef DEBUG
    ovr_log ("after", numproc);
#endif
}

#ifdef DEBUG
ovr_log (tag, numproc)
    char tag[];
    int numproc;
{
    register int ind;

    ll_log (logptr, LLOGFTR, "que %s:", tag);
    for (ind = 0; ind < numproc; ind++)
	ll_log (logptr, LLOGFTR, "(%d) %s %ld %s", ind,
		    ovr_mlist[ind].mg_mname, ovr_mlist[ind].mg_time,
		    (ovr_mlist[ind].mg_large == TRUE) ? "LARGE" : "");
}
#endif
/**/
LOCFUN
	ovr_malloc (count)          /* allocate array for msg names       */
unsigned  count;                    /* number of entries in queue list    */
{
    extern char *calloc();

#ifdef DEBUG
    ll_log (logptr, LLOGFTR, "ovr_malloc (%d)", count);
#endif
    if (ovr_mlist != (Msg *)0) {
	ll_log (logptr, LLOGTMP, "*** ovr_malloc of un-freed ovr_mlist");
	ovr_mfree ();
    }

    /*NOSTRICT*/
    if ((ovr_mlist = (Msg *) calloc (count, sizeof (Msg))) == (Msg *)0) {
	ll_log (logptr, LLOGTMP, "*** ovr_malloc error (%d msgs -> %d bytes)",
		count, (count * sizeof (Msg)));
    	return(NOTOK);
    }
    return (count);
}

LOCFUN
	ovr_ismsg (entry)         /* a processable message?             */
    register struct direct *entry;
{
#ifdef DEBUG
    ll_log (logptr, LLOGFTR, "ovr_ismsg (name='%s')",
			entry -> d_name);
#endif
    return ((entry -> d_namlen <= MSGNSIZE
	     && equal (entry -> d_name, "msg.", 4)) ? TRUE : FALSE);
}

LOCFUN
	ovr_mfree ()              /* free queue list storage            */
{
#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "ovr_mlist (%o)", ovr_mlist);
#endif
    if(ovr_mlist != (Msg *)0)
	    free ((char *) ovr_mlist);
    ovr_mlist = (Msg *)0;
}
/****************  (msg_)  HANDLE A SINGLE MESSAGE  ***************** */

extern int    errno;                /* system error number                */

msg_proc (curch, themsg)        /* get, process & release a msg       */
    Chan *curch;
    Msg *themsg;
{
    register char   retval;

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "msg_proc (%s)", themsg -> mg_mname);
#endif
    if (mq_rinit (curch, themsg, msg_sender) == OK)
    {
	msg_note(themsg);     /* inform watchers of intent to send  */
	retval = msg_mng (curch, themsg);
	mq_rkill (OK);
	printx ("End of processing for %s\n", themsg -> mg_mname);
	(void) fflush(stdout);
	return (retval);
    }
    return (RP_DONE);
}

msg_note (themsg)                 /* notify watchers about this msg     */
    Msg *themsg;
{
    int elapsed;                /* Time in queue in hours */

    /*NOSTRICT*/
    elapsed = (int)((curtime - themsg -> mg_time)/3600L);
    printx ("\nMessage '%s', from '%s'\n", themsg -> mg_mname, msg_sender);
    printx ("Queued: %d Days, %d Hours\n", elapsed/24, elapsed%24);
    ll_log (logptr, LLOGPTR, "[ %s, %ld, %s ]",
	    themsg -> mg_mname, themsg -> mg_time, msg_sender);
}
/**/

LOCFUN
	msg_dequeue (theque, themsg) /* remove message from queue          */
	    char *theque;
	    Msg *themsg;
{
    char thename[LINESIZE];
#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "msg_deque (%s,%s)", 
		(theque == (char *) 0) ? "(Base entry)" : theque,
		themsg -> mg_mname);
#endif

    printx ("dequeuing %s from %s\n", 
		themsg -> mg_mname, theque != 0 ? theque : "main queue");
#ifdef RUNALON
    return;
#endif

    if (theque == (char *) 0)
	sprintf (thename, "%s%s", aquedir, themsg -> mg_mname);
    else
	sprintf (thename, "%s%s/%s",
			squepref, theque, themsg -> mg_mname);

    if (unlink (thename) < OK) /* this is real queue handle  */
	ll_err (logptr, LLOGTMP, "Problem unlinking '%s' address: %s",
		    themsg -> mg_mname, thename);

    if (theque == (char *) 0)
    {                             /* get rid of ALL the message */
	sprintf (thename, "%s%s", mquedir, themsg -> mg_mname);
	if (unlink (thename) < OK) /* the text is just "baggage"         */
	    ll_err (logptr, LLOGTMP, "Problem unlinking %s text: '%s'",
		themsg -> mg_mname, thename);
    }
}

/**/

LOCFUN
	msg_mng (curch, themsg)    /* overall management of transmission */
		Chan *curch;
		Msg *themsg;
{
    int retval;

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "msg_mng");
#endif

    if ((retval = adr_each (curch, themsg)) == RP_DONE)
    {                             /* do a FOREACH loop thru addr list   */
#ifdef DEBUG
	ll_log (logptr, LLOGFTR, "skipped (%o), more(%o)",
			adr_skipped, adr_more);
#endif
	if (adr_skipped && adr_more)
	    return (RP_OK);       /* not really done with message   */

	if (!adr_more)
	{                         /* done with this sub-queue           */
	    msg_dequeue (curch -> ch_queue, themsg); /* ain't no mo' to process         */
	    if (!adr_skipped)     /* done with entire message           */
	    {
		msg_dequeue ((char *) 0, themsg);
		printx ("Message completely processed.\n");
	    }
	    else
		printx ("Message fully processed in sub-queue '%s'\n",
			    curch -> ch_queue);
	    (void) fflush (stdout);
	}
	return (RP_DONE);
    }
    return (retval);
}
/************  (adr_)  INDIVIDUAL ADDR DELIVERY ATTEMPT  ************ */

LOCVAR Chan *adr_lastdone;        /* flag to tell if e-o-list needed    */


adr_each (curch, themsg)          /* do each address                    */
	Chan *curch;              /* Channel of current interest        */
	Msg *themsg;
{
    RP_Buf rply;                  /* success of address                 */
    struct adr_struct theadr;     /* address parts                      */
    char    ch_chost[FILNSIZE];   /* current host being processed       */
    char    first;                /* first address send to a channel?   */
    char    addr_sent;            /* At least one address went to chan  */

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "adr_each");
#endif
/*  Normal termination returns RP_DONE; a problem specific to the channel
 *  returns OK (ok to proceed to next channel).
 *  Problems with the address list are treated as general,
 *  so that the return is a general NOTOK.
 */

    adr_more =
	adr_tok =
	adr_skipped =
	addr_sent = FALSE;

    adr_start = 0L;
    for (first = TRUE, mq_setpos (0L); ; )
    {                             /* go through the list                */
	switch (mq_radr (&theadr))
	{                         /* was theadr setup correctly?        */
	    case DONE:            /* end of list                        */
		if (!first)       /* Something was exchanged with chan  */
			adr_done (themsg);
				  /* mark temps and return              */
		return (RP_DONE);

	    case NOTOK:           /* rest of list must be skipped       */
		return (RP_NO);   /* treat as permanent problem         */
	}

	/* filter what addresses get processed */
	if (theadr.adr_delv == ADR_DONE)
	    continue;             /* this one's already been finished   */

	if (!lexequ (curch -> ch_queue, theadr.adr_que))
	{
	    adr_skipped = TRUE;   /* pending, but...                    */
	    continue;             /* not the chan we are looking for    */
	}

	if (ovr_pickup && (curch -> ch_login == 0)
	  && !lexequ(callername, theadr.adr_host)) {
	    adr_skipped = TRUE;   /* caller can only pickup for his host */
	    continue;
	}

	if (curch != ch_curchan || !strequ (ch_chost, theadr.adr_host))
	{
	    /*
	     *  This stuff gets called the first time through but
	     *  that's OK since the routines all handle the case when
	     *  nothing has actually happened yet.
	     */
	    if (addr_sent)                      /* update any temp-ok's */
		if (adr_hdone (themsg, ch_chost, ch_curchan) != RP_NOOP) {
		    addr_sent = FALSE;
		    adr_start = theadr.adr_pos;	/* start new sublist */
		}
	    (void) strcpy (ch_chost, theadr.adr_host);
	}

	if (ca_find (&curch -> ch_dead, theadr.adr_host) != 0) {
		adr_more = TRUE;
#ifdef DEBUG
		ll_log (logptr, LLOGBTR, "address skipped (dead host)");
#endif
		printx ("%s", theadr.adr_local);
		if (theadr.adr_host[0] != '\0')
			printx (" via %s", theadr.adr_host);
		printx (":  dead host\n");
		(void) fflush (stdout);
		continue;
	}

	/*
	 *  The following condition will also go off if the channel
	 *  bombs out in the middle of adr_hdone().
	 */
	if (curch != ch_curchan) {              /* need a whole new channel */
	    if (rp_isbad (ch_sbend (OK)))
		return (RP_OK);
	    if (rp_isbad (ch_sbinit (curch)))
		return (RP_OK);   /* treat as permanent problem */
	    first = TRUE;
	}

	if (first)                /* msg's 1st addr */
	{                         /* tell chan of msg_sender & name */
#ifdef DEBUG
	    ll_log (logptr, LLOGBTR, "chan new msg");
#endif
	    printx ("Channel: %s\n", curch -> ch_name);
	    (void) fflush (stdout);
	    first = FALSE;        /* treat as if done with channel      */
	    if (rp_isbad (ch_winit (msg_sender, themsg -> mg_mname)))
		return (RP_OK);
#ifdef DEBUG
	    ll_log (logptr, LLOGFTR, "first host '%s'", ch_chost);
#endif
	}

	theadr.adr_tmp = ADR_CLR; /* reset temp bit, if set earlier     */
	rply.rp_line[0] = '\0';

	adr_end = theadr.adr_pos;

	if (rp_isbad (adr_wadr (&theadr)))
	{
	    adr_more = TRUE;
	    rply.rp_val = RP_NO;  /* give to channel & get status       */
	}
	else
	    adr_rrply (&rply, curch, themsg, &theadr);

	addr_sent = TRUE;

	switch (rply.rp_val)  /* oops    */
	{
	    case RP_NDEL:
		printx ("cannot deliver");
		(void) fflush (stdout);
		if (msg_noret (themsg->mg_stat)) {
		    printx (", error returns not wanted");
		    ll_log (logptr, LLOGGEN, "%s err noret", themsg->mg_mname);
		}
		else if (rtn_error (themsg, msg_sender, &theadr, rply.rp_line)
		     == OK) {
		    printx (", returned");
		    ll_log (logptr, LLOGGEN, "%s err ret", themsg -> mg_mname);
		} else {
		    char orphanage[ADDRSIZE];

		    sprintf (orphanage, "Orphanage <%s>", supportaddr);
		    printx (", couldn't return\n");
		    printx ("to '%s', trying orphanage", msg_sender);
		    (void) fflush (stdout);
		    ll_log (logptr, LLOGGEN, "%s orph ret", themsg->mg_mname);
		    if (rtn_error (themsg, orphanage, &theadr, rply.rp_line)
			 == OK) {
			printx (", returned");
		    } else {
			printx (", couldn't return.");
			dead_letter (themsg->mg_mname, rply.rp_line);
		    }
		}
		break;

	    case RP_NO:
		adr_more = TRUE;                /* Paranoid */
		printx("queuing for retry");
		break;

	    case RP_NET:        /* problem with channel, so terminate it */
	    case RP_NIO:
		printx("\n%s channel I/O error\n", curch->ch_name);
		adr_t2fin (curch, themsg, RP_NO);
		return (RP_NET);        /* skip the rest of this message */
	}

	mq_wrply (&rply, themsg, &theadr);
				  /* modify addr's queue entry)         */
	printx ("\n");
	(void) fflush (stdout);
    }
    /* NOTREACHED */
}

adr_log (theval, thechan, themsg, theadr) /* record final status of address */
    char theval[];
    Chan *thechan;
    Msg *themsg;
    struct adr_struct   *theadr;
{
    time_t thetime,
	   elapsed;
    int   msg_elhour,           /* # hours since message queued       */
	    msg_elsec;            /* additional seconds                 */

    time (&thetime);

    elapsed = thetime - themsg -> mg_time;
    msg_elhour = (int) (elapsed / (time_t) 3600);
    msg_elsec = (int) (elapsed % (time_t) 3600);

    if (lexequ (theadr -> adr_que, thechan -> ch_name))
	ll_log (logptr, LLOGFST, "end %s (%s) %s %s %s [%d:%2d:%2d]",
		themsg -> mg_mname, theval, theadr -> adr_que,
		theadr -> adr_host, theadr -> adr_local,
		msg_elhour, msg_elsec / 60, msg_elsec % 60);
    else                        /* chan name is not redundant w/queue */
	ll_log (logptr, LLOGFST, "end %s (%s) %s(%s) %s %s [%d:%2d:%2d]",
		themsg -> mg_mname, theval, theadr -> adr_que,
		thechan -> ch_name, theadr -> adr_host, theadr -> adr_local,
		msg_elhour, msg_elsec / 60, msg_elsec % 60);
}
/**/

LOCFUN
	adr_t2fin (chan, msg, final) /* convert temp to final status       */
    register Chan *chan;             /* channel to convert stats for       */
    register Msg *msg;
    int  final;                  /* final status                       */
{
    RP_Buf rply;
    struct adr_struct theadr;

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "adr_t2fin (%s[%s], %s, %s) tok=%d",
		chan -> ch_show, chan -> ch_name,
		msg -> mg_mname, rp_valstr (final), adr_tok);
#endif

    if (!adr_tok)             /* there weren't any temp ok's    */
	return;

    adr_tok = FALSE;          /* reset */

    switch (rp_gbval (final))
    {                     /* if result is permanent             */
	case RP_BOK:      /* good ending                        */
	    final = RP_AOK;
	case RP_BNO:      /* irreversibly bad ending            */
	    rply.rp_val = RP_DONE;
	    rply.rp_line[0] = '\0';
	    mq_setpos (adr_start);
	    /*
	     *  A side effect of the ordering of the condition below
	     *  is to cause us to read to the same point that brought
	     *  us here (the first address not in this sublist.  In this
	     *  we detect it because adr_pos is nolonger <= adr_end.
	     *                                  DPK@BRL
	     */
	    while(mq_radr (&theadr) == OK && theadr.adr_pos <= adr_end)
	    {             /* get address values & mark status    */
		if (lexequ (theadr.adr_que, chan -> ch_queue) &&
		    theadr.adr_tmp == ADR_AOK && theadr.adr_delv != ADR_DONE)
		{         /* same chan && marked with temp_ok   */
		    mq_wrply (&rply, msg, &theadr);
			  /* modify addr's entry as per reply */
		    adr_log (rp_valstr (final), chan, msg, &theadr);

		    /*
		     *  Here we handle the case of a permanent
		     *  error after the successful receipt of
		     *  the addresses.   (From Steve Kille, UCL)
		     */
		    if (rp_gbval (final) == RP_BNO) {
			printx ("  Permanent failure for '%s via %s'",
			    theadr.adr_local, theadr.adr_host);
			(void) fflush (stdout);
			if (msg_noret (msg->mg_stat)) {
			    printx (", NOT returned on request");
			    ll_log (logptr, LLOGGEN, "%s err noret",
				    msg->mg_mname);
			} else if (rtn_error (msg, supportaddr, &theadr,
					ch_rp.rp_line) == OK) {
			    printx (", returned to supportaddr");
			} else {
			    printx (", couldn't return\n");
			    printx ("attempting to append to DeadLetters");
			    dead_letter (msg -> mg_mname, ch_rp.rp_line);
			}
			printx ("\n");
			(void) fflush (stdout);
		    }
		}
	    }
	    printx ((rp_gbval (final) == RP_BNO) ? "flushed\n" : "sent\n");
	    (void) fflush (stdout);
	    if (rp_gbval (final) == RP_BNO)
		goto logit;
	    break;

	default:          /* note that tok's weren't completed  */
	    /*
	     *  See comment above!
	     *  (typically you get in when there is an error sending text)
	     */
	    mq_setpos (adr_start);
	    while(mq_radr (&theadr) == OK && theadr.adr_pos <= adr_end)
		if (lexequ (theadr.adr_que, chan -> ch_queue) &&
		    theadr.adr_tmp == ADR_AOK && theadr.adr_delv != ADR_DONE) {
		    mq_wrply (&rply, msg, &theadr);     /* Clear AOK */
		    deadhost (&chan -> ch_dead, theadr.adr_host,
				RP_BHST, chan -> ch_ttl);
		}
	    printx ("not sent, queued for retry\n");
	    (void) fflush (stdout);
	    adr_more = TRUE;

    logit:
	    ll_log (logptr, LLOGTMP, "end %s (%s) ** %s (%s) error **",
			msg -> mg_mname, rp_valstr (final),
			chan -> ch_show, chan -> ch_name);
    }
}
/**/

LOCFUN
adr_wadr (theadr) /* tell channel of address   */
    struct adr_struct *theadr;
{
#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "adr_wadr (%s, %s, %s)",
		theadr -> adr_que, theadr -> adr_host, theadr -> adr_local);
#endif

    if( ch_curchan != 0 && lexequ(theadr -> adr_que, ch_curchan->ch_queue) )
	adr_lastdone = ch_curchan;
    else
	adr_lastdone = ch_qu2struct (theadr -> adr_que);
				/* standardized reference to the queue */
    printx ("%s", theadr -> adr_local);
    if (theadr -> adr_host[0] != '\0')
	printx (" via %s", theadr -> adr_host);
    printx (":  ");
    (void) fflush (stdout);

    return (ch_wadr (theadr -> adr_host, theadr -> adr_local));
}
/**/


LOCFUN
adr_rrply (therply, chan, themsg, theadr) /* tell channel of address   */
    Chan *chan;
    RP_Buf *therply;
    Msg *themsg;
    struct adr_struct *theadr;
{                                 /* channel must prshortx addr to user   */
    int     rplen;
    register int    retval;

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "adr_rrply ()");
#endif

    if (rp_isbad (retval = ch_rrply (therply, &rplen)))
    {
	adr_more = TRUE;
	therply -> rp_val = retval;
	return;
    }

    retval = therply -> rp_val;
    if (rp_gval (retval) == RP_MOK || rp_gbval (retval) == RP_BNO)
				   /* note final status of address       */
	adr_log (rp_valstr (retval), chan, themsg, theadr);
#ifdef DEBUG
    else                          /* note temp errors except DHST */
	if (rp_gval (retval) != RP_DHST)
	    ll_log (logptr, LLOGBTR, "%s (%s) %s %s %s",
		    themsg -> mg_mname, rp_valstr (retval),
		    theadr -> adr_que, theadr -> adr_host,
		    theadr -> adr_local);
#endif

    switch (rp_gval (retval))
    {                             /* map reply into final meaning       */
	case RP_RPLY:             /* host sent bad reply                */
	case RP_PROT:             /* host send other bad reply          */
	case RP_BHST:             /* Can't handle host behavior         */
	    adr_more = TRUE;
	    printx ("error at destination, ");
	    deadhost (&chan -> ch_dead, theadr -> adr_host, RP_BHST,
			chan -> ch_ttl);
	    retval = RP_NO;       /* sometimes recoverable...           */
	    break;

	case RP_USER:             /* bad address                        */
	    printx ("unknown address, ");
	    retval = RP_NDEL;
	    break;

	case RP_NDEL:             /* Permanent failure                  */
	    retval = RP_NDEL;     /* have to give up on the message     */
	    break;

	case RP_AOK:              /* only address accepted, so far      */
	    adr_tok = TRUE;
	    printx ("address ok");
	    retval = RP_AOK;
	    break;

	case RP_MOK:              /* all of message sent                  */
	    printx ("sent");
	    retval = RP_DONE;
	    break;

	case RP_LOCK:             /* mailbox locked                       */
	    adr_more = TRUE;
	    printx ("mailbox busy, ");
	    retval = RP_NO;
	    break;

	case RP_AGN:              /* Temporary failure                    */
	    adr_more = TRUE;
	    printx ("not deliverable now, ");
	    retval = RP_NO;
	    break;

	case RP_NS:               /* Temporary NS failure                 */
	    adr_more = TRUE;
	    printx ("NameServer timed out, ");
	    deadhost (&chan -> ch_dead, theadr -> adr_host, RP_NS,
			chan -> ch_ttl);
	    retval = RP_NO;
	    break;

	case RP_TIME:             /* Host timed out                       */
	    adr_more = TRUE;
	    printx ("destination took too long, ");
	    deadhost (&chan -> ch_dead, theadr -> adr_host, RP_TIME,
			chan -> ch_ttl);
	    retval = RP_NO;
	    break;

	case RP_NET:              /* Sockets closed or something          */
	    adr_more = TRUE;
	    printx ("transmission error, ");
	    deadhost (&chan -> ch_dead, theadr -> adr_host, RP_NET,
			chan -> ch_ttl);
	    retval = RP_NO;
	    break;

	case RP_DHST:             /* Couldn't open                        */
	    adr_more = TRUE;
	    printx ("destination not available, ");
	    deadhost (&chan -> ch_dead, theadr -> adr_host, RP_DHST,
			chan -> ch_ttl);
	    retval = RP_NO;
	    break;

	case RP_NOOP:             /* Skip over this one                   */
	    adr_more = TRUE;
	    printx ("skipping address, ");
	    retval = RP_NOOP;
	    break;

	default: 
	    printx ("unknown problem, ");
	    if (*rp_valstr (retval) == '*')
	    {
		ll_log (logptr, LLOGTMP, "%s", rp_valstr (retval));
		adr_more = TRUE;
		retval = RP_NO;   /* illegal return value               */
	    }
	    else
	    if (rp_gbval (retval) == RP_BNO)
		retval = RP_NDEL; /* some sort of permanent error       */
	    else
	    {
		deadhost (&chan -> ch_dead, theadr->adr_host, RP_RPLY,
				chan -> ch_ttl);
		adr_more = TRUE;
		retval = RP_NO;
	    }
    }

    therply -> rp_val = retval;
}
/**/

LOCFUN
	adr_done (themsg)   /* end of address list for this chan  */
    Msg *themsg;
{
    int retval;

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "adr_done (%s)", themsg -> mg_mname);
#endif

    if (ch_curchan != 0 && (adr_lastdone == ch_curchan))
    {                             /* if have a chan, say we are done    */
	retval = ch_waend ();
	adr_t2fin (adr_lastdone, themsg, retval);
    }

    adr_lastdone = (Chan *) 0;
}

LOCFUN
	adr_hdone (themsg, oldhost, chan) /* end of address list for host  */
    Msg *themsg;
    char *oldhost;
    Chan *chan;
{
    int retval = RP_OK;

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "adr_hdone (%s)", themsg -> mg_mname);
#endif

    if (ch_curchan != 0 && (adr_lastdone == ch_curchan))
    {                             /* if have a chan, say we are done    */
	retval = ch_whend ();
	switch (retval)
	{
	    case RP_NOOP:         /* channel doesn't distinguish        */
		break;

	    case RP_MOK:
		adr_t2fin (adr_lastdone, themsg, retval);
		break;

	    default:               /* believe bad, but not good         */
		if (rp_isgood (retval)) {
		    printx("illegal HEND reply (%s)\n", rp_valstr(retval));
		    retval = RP_NO;     /* change response to temp. error */
		    ll_log (logptr, LLOGTMP, "illegal HEND reply (%s)", rp_valstr (retval));
		}
		deadhost (&chan -> ch_dead, oldhost, RP_BHST, chan -> ch_ttl);
		adr_t2fin (adr_lastdone, themsg, retval);
	}
    }
    return (retval);
}

deadhost (cachep, host, why, ttl)
Cache   **cachep;
char    *host;
int     why;
time_t  ttl;
{
	char    *index();       /* don't cache delay hosts */

	if(index(host, '&') != NULL)
		return;
	if (!isstr(host))
		return;                 /* Don't timeout local host */
	ca_add (cachep, host, why, msg_ttl ? msg_ttl : ttl);
}

/***************  (err_)  PROCESS ERROR CONDITIONS  ***************** */

/* VARARGS2 */
err_abrt (code, fmt, b, c, d)     /* terminate ourself                  */
int       code;
char    *fmt, *b, *c, *d;
{
    char    newfmt[LINESIZE];

    ovr_end (NOTOK);

    if (rp_isbad (code))
    {
/*      printf() is used, rather than printx(), to force
 *      the caller to see the case for the abort.
 */
	printf ("deliver: ");
	printf (fmt, b, c, d);
	printf ("\n");
	(void) fflush (stdout);

	sprintf (newfmt, "%s%s", "err [ ABEND (%s) ] ", fmt);
	ll_err (logptr, LLOGFAT, newfmt, rp_valstr (code), b, c, d);
#ifdef DEBUG
	sigabort (fmt);
#endif
    }
    ll_close (logptr);           /* in case of cycling, close neatly   */
    exit (RP_NO);
}
rver timed out, ");
	    deadhost (&chan -> ch_dead, theadr -> adr_host, RP_NS,
			chan -> ch_ttl);
	    retval = RP_NO;
	    break;

	case RP_TIME:             /* Host timed out                       */
	    adr_more = TRUE;
	    printx ("destination took too long, ");
	    deadhost (&chan -> ch_dead, theadr -> adr_host, Rmmdf/src/deliver/ch_poll.c   444      0     12       11170  3620510524  10773 /*
 *     MULTI-CHANNEL MEMO DISTRIBUTION FACILITY  (MMDF)
 *     
 *
 *     Copyright (C) 1979,1980,1981  University of Delaware
 *     
 *     Department of Electrical Engineering
 *     University of Delaware
 *     Newark, Delaware  19711
 *
 *     Phone:  (302) 738-1163
 *     
 *     
 *     This program module was developed as part of the University
 *     of Delaware's Multi-Channel Memo Distribution Facility (MMDF).
 *     
 *     Acquisition, use, and distribution of this module and its listings
 *     are subject restricted to the terms of a license agreement.
 *     Documents describing systems using this module must cite its source.
 *
 *     The above statements must be retained with all copies of this
 *     program and may not be removed without the consent of the
 *     University of Delaware.
 *     
 *
 *     version  -1    David H. Crocker    March   1979
 *     version   0    David H. Crocker    April   1980
 *     version  v7    David H. Crocker    May     1981
 *     version   1    David H. Crocker    October 1981
 *
 */
/* do any polling for mail to pickup                                    */

/*    Apr 82  Steve Bellovin	tbldfldir -> logdfldir, for timings files 
 */

#include "util.h"
#include "mmdf.h"
#include "ch.h"
#include <sys/stat.h>
#include "nexec.h"
#include "phs.h"

extern struct ll_struct *logptr;
extern char *logdfldir;
extern char *chndfldir;
extern int *regfdary;
extern int numfds;

ch_poll (checklist)               /* poll hosts for pickup mail         */
    Chan *checklist[];            /* chans which may be processed       */
{
    extern time_t time ();
    time_t  thetime;              /* current time                       */
    register Chan **chanptr;

#ifdef DEBUG
    ll_log (logptr, LLOGPTR, "ch_poll");
#endif

    time (&thetime);
    thetime = thetime / (time_t) 900;
				  /* turn it into quarter-hours         */
    for (chanptr = checklist; *chanptr != 0; chanptr++)
	if ((*chanptr) -> ch_poltime != NOPOLL)
	    if (pol_chk (thetime, *chanptr))
		pol_doit (*chanptr);
}
/**/

LOCFUN
	pol_chk (ptime, pchan)    /* channel due for polling?           */
    time_t    ptime;
    register Chan *pchan;
{
    long    dstrt,                /* when a delivery was last started   */
	    ddone,                /*  "   "     "     "    "  finished  */
	    pstrt,                /*  "   " pickup    "    "  started   */
	    pdone;                /*  "   "     "     "    "  finished  */

    if (pchan -> ch_access & DLVRDID)
	return (FALSE);           /* channel been done, this wakeup     */
    if (! (pchan -> ch_access & CH_PICK))
	return (FALSE);           /* pickup not allowed                 */
    if (pchan -> ch_poltime == NOPOLL)
	return (FALSE);           /* polling not allowed                */

    if (pchan -> ch_poltime == ALLPOLL)
	return (TRUE);            /* poll every wakeup                  */

    dstrt = phs_get (pchan, PHS_WRSTRT);
    if (dstrt > 0L)
	dstrt /= 900L;

    ddone = phs_get (pchan, PHS_WREND);
    if (ddone > 0L)
	ddone /= 900L;

    pstrt = phs_get (pchan, PHS_RESTRT);
    if (pstrt > 0L)
	pstrt /= 900L;

    pdone = phs_get (pchan, PHS_REEND);
    if (pdone > 0L)
	pdone /= 900L;


#ifdef DEBUG
    ll_log (logptr, LLOGFTR,
	    "%s(%d) did=%o, dstrt=%ld, ddone=%ld, pstrt=%ld, pdone=%ld, cur=%ld",
	    pchan -> ch_name, pchan -> ch_poltime,
	    pchan -> ch_access&DLVRDID, dstrt, ddone, pstrt, pdone, ptime);
#endif

    ptime -= pchan -> ch_poltime;

    if (dstrt > ddone)		/* outbound mail still pending		*/
	return (FALSE);
    if (pstrt > pdone)          /* inbound mail probably still pending  */
	return (TRUE);
    if (ddone > ptime || pdone > ptime)
	return (FALSE);           /* still within delay window          */

#ifdef DEBUG
    ll_log (logptr, LLOGFTR, "performing pickup");
#endif

    return (TRUE);
}

LOCFUN
	pol_doit (thechan)
    register Chan *thechan;
{
    char temppath[128];
    short tmp;

    ll_log (logptr, LLOGGEN, "pickup %s", thechan -> ch_name);
    printx ("forcing pickup of %s... ", thechan -> ch_name);
    fflush (stdout);

    regfdary[0] = 0;
    regfdary[1] = 1;
    for (tmp = 2; tmp < numfds; regfdary[tmp++] = NOTOK);

/*  run the channel program, in polling mode.  collect its return value.
 *  if there is a really serious error, force a logging message, otherwise,
 *  treat it more casually.
 */
    getfpath (thechan -> ch_ppath, chndfldir, temppath);

    if ((tmp = nexecl (FRKEXEC, FRKWAIT, regfdary, temppath,
		thechan -> ch_name, (domsg) ? "-w" : "-p", (char *)0)) < OK)
	ll_err (logptr, (rp_gbval (tmp) == RP_BNO) ? LLOGTMP : LLOGBTR,
		    "(%s) during %s pickup", rp_valstr (tmp),
		    thechan -> ch_name);

    printx ("\n");
}

egister Chan *pchan;
{
    long    dstrt,                /* when a delivery was last started   */
	    ddone,                /*  "   "     "     "    "  finished  */
	    pstrt,                /*  "   " pickup    "    "  started   */
	    pdone;                /*  "   "     "     "    "  finished  */

    if (pchan -> ch_access & DLVRDID)
	return (FALSE);           /* channel been done, thmmdf/src/deliver/ch_wtmail.c   444      0     12        6725  3620510524  11314 #include "util.h"
#include "mmdf.h"

/*
 *     MULTI-CHANNEL MEMO DISTRIBUTION FACILITY  (MMDF)
 *
 *
 *     Copyright (C) 1979,1980,1981  University of Delaware
 *
 *     Department of Electrical Engineering
 *     University of Delaware
 *     Newark, Delaware  19711
 *
 *     Phone:  (302) 738-1163
 *
 *
 *     This program module was developed as part of the University
 *     of Delaware's Multi-Channel Memo Distribution Facility (MMDF).
 *
 *     Acquisition, use, and distribution of this module and its listings
 *     are subject restricted to the terms of a license agreement.
 *     Documents describing systems using this module must cite its source.
 *
 *     The above statements must be retained with all copies of this
 *     program and may not be removed without the consent of the
 *     University of Delaware.
 *     
 *
 *     version  -1    David H. Crocker    March   1979
 *     version   0    David H. Crocker    April   1980
 *     version  v7    David H. Crocker    May     1981
 *     version   1    David H. Crocker    October 1981
 *
 */
/*  CH_IO:  Channel invocation & communication                          */

/*  Jun 80 Dave Crocker     fix ch_close fildes setting to -1
 *  Nov 80 Dave Crocker     convert to ch_struct pointer from ch_code
 *  Jul 81 Dave Crocker     ch_rdrec a little cleaner on error handling
 */

/*#define RUNALON                 /* run without invoking actual channels */

#include "ch.h"
#include <signal.h>

extern char pgm_bakgrnd;
extern struct ll_struct   *logptr;
extern char *chndfldir;

Chan    *ch_curchan;               /* currently active channel program   */


/* **************  (ch_) WRITE MAIL TO AND ADDRESS  ***************** */


ch_winit (ret, msg)    /* initial parameters for a message   */
char *ret, *msg;           /* name of message                    */
{
    RP_Buf rply;
    int len;
    int retval;
    char linebuf[LINESIZE];

#ifdef DEBUG
    ll_log (logptr, LLOGFTR, "ch_winit (%s, %s)", ret, msg);
#endif

    arg2lstr (0, LINESIZE, linebuf, "msg", msg, ret, (char *)0);

    if (rp_isgood (retval = ch_wrec (linebuf)))
	if (rp_isgood (retval = ch_rrply (&rply, &len)))
	{
	    retval = rply.rp_val;
	    if (rp_isbad (retval))
		printx ("Problem with queue: %s\n", rply.rp_line);
	}
    return (retval);
}
/**/

ch_wadr (host, adr)             /* send channel an address              */
    char host[],
	 adr[];
{
    char linebuf[LINESIZE];

#ifdef DEBUG
    ll_log (logptr, LLOGFTR, "ch_wadr (%s, %s)", host, adr);
#endif

    arg2lstr (0, LINESIZE, linebuf, "addr", host, adr, (char *)0);

    return (ch_wrec (linebuf));
}

ch_whend ()                     /* end of sub-list for this host        */
{
    RP_Buf rply;
    int len,
	retval;

    if (rp_isgood (retval = ch_wrec ("hend")))
	if (rp_isgood (retval = ch_rrply (&rply, &len)))
	{
	    retval = rply.rp_val;
	    if (rp_isbad (retval) && rp_gval (retval) != RP_NOOP)
		printx ("Problem host ending: %s\n", rply.rp_line);
	}
#ifdef DEBUG
    ll_log (logptr, LLOGFTR, "ch_hend (%s)", rp_valstr (retval));
#endif
    return (retval);
}

ch_waend ()                     /* end of list for channel              */
{
    RP_Buf rply;
    int len,
	retval;

    if (rp_isgood (retval = ch_wrec ("aend")))
	if (rp_isgood (retval = ch_rrply (&rply, &len)))
	{
	    retval = rply.rp_val;
	    if (rp_isbad (retval))
		printx ("Problem address ending: %s\n", rply.rp_line);
	}
#ifdef DEBUG
    ll_log (logptr, LLOGFTR, "ch_waend (%s)", rp_valstr (retval));
#endif
    return (retval);
}

(FALSE);           /* channel been done, thmmdf/src/deliver/gen   555      0     12          57  3620510524   7630 make -f ../../Makefile.com -f Makefile.real $*
/
/*  CH_IO:  Channel invocation & communication                          */

/*  Jun 80 Dave Crocker     fix ch_close fildes setting to -1
 *  Nov 80 Dave Crocker     convert to ch_struct pointer from ch_code
 *  Jul 81 Dave Crocker     ch_rdrec a little cleaner on error handling
 */

/*#define RUNALON                 /* run without invoking actual channels */

#include "ch.h"
#include <signal.h>

extern char pgm_bakgrnd;
extern struct ll_struct   *logptr;
extmmdf/src/deliver/Makefile.real   444      0     12        3143  3635174054  11564 #
#   deliver:    Manage message queues & invoke channel programs
#
MODULES =	deliver ch_wtmail ch_io ch_poll

OBJECTS =	deliver.o ch_wtmail.o ch_io.o ch_poll.o

SOURCES =	deliver.c ch_wtmail.c ch_io.c ch_poll.c

real-default:	deliver
deliver:	xdeliver
xdeliver:	$(OBJECTS) $(MMDFLIBS)
	$(CC) $(LDFLAGS) -o $@ $(OBJECTS) $(MMDFLIBS) $(SYSLIBS)

install:	$(LIBDIR)/$(MMPREF)deliver

$(LIBDIR)/$(MMPREF)deliver:	xdeliver
	cp xdeliver $(LIBDIR)/$(MMPREF)deliver
	-$(CHOWN) root $(LIBDIR)/$(MMPREF)deliver
	-chmod 04$(PGMPROT) $(LIBDIR)/$(MMPREF)deliver
	-@ls -ls $(LIBDIR)/$(MMPREF)deliver
	-@echo "deliver installed normally"; echo ""


lint:
	$(LINT) $(LFLAGS) $(SOURCES) $(LLIBS)

clean:
	-rm -f xdeliver *.o x.c makedep eddep make.out errs

# DO NOT DELETE THIS LINE -- make depend uses it
deliver.o: deliver.c
deliver.o: deliver.c
deliver.o: ../../h/util.h
deliver.o: ../../h/mmdf.h
deliver.o: /usr/include/signal.h
deliver.o: /usr/include/sys/stat.h
deliver.o: /usr/include/pwd.h
deliver.o: ../../h/ch.h
deliver.o: ../../h/msg.h
deliver.o: ../../h/adr_queue.h
ch_wtmail.o: ch_wtmail.c
ch_wtmail.o: ../../h/util.h
ch_wtmail.o: ../../h/mmdf.h
ch_wtmail.o: ../../h/ch.h
ch_wtmail.o: /usr/include/signal.h
ch_io.o: ch_io.c
ch_io.o: ../../h/util.h
ch_io.o: ../../h/mmdf.h
ch_io.o: ../../h/ch.h
ch_io.o: /usr/include/signal.h
ch_io.o: ../../h/nexec.h
ch_poll.o: ch_poll.c
ch_poll.o: ../../h/util.h
ch_poll.o: ../../h/mmdf.h
ch_poll.o: ../../h/ch.h
ch_poll.o: /usr/include/sys/stat.h
ch_poll.o: ../../h/nexec.h
ch_poll.o: ../../h/phs.h
# DEPENDENCIES MUST END AT END OF FILE
# IF YOU PUT STUFF HERE IT WILL GO AWAY
# see make depend above
 /* end of sub-list for this host        */
{
    RP_Buf rply;
    int len,
	retval;

    if (rp_isgood (retval = ch_wrec ("hend")))
	if (rp_isgood (retval = ch_rrply (&rply, &len)))
	{
	    retval = rply.rp_val;
	    if (rp_isbad (retval) && rp_gval (retval) != RP_NOOP)
		printx ("Problem host ending: %s\n", rply.rp_line);
	}
#ifdef DEBUG
    ll_log (logptr, LLOGFTR, "ch_hend (%s)", rp_valstr (retval));
#endimmdf/src/pobox/   755      0     12           0  3656466146   6624 mmdf/src/pobox/ch_pobox.c   444      0     12        7054  3620510525  10640 #include "util.h"
#include "mmdf.h"

/*
 *     MULTI-CHANNEL MEMO DISTRIBUTION FACILITY  (MMDF)
 *     
 *
 *     Copyright (C) 1979,1980,1981  University of Delaware
 *     
 *     Department of Electrical Engineering
 *     University of Delaware
 *     Newark, Delaware  19711
 *
 *     Phone:  (302) 738-1163
 *     
 *     
 *     This program module was developed as part of the University
 *     of Delaware's Multi-Channel Memo Distribution Facility (MMDF).
 *     
 *     Acquisition, use, and distribution of this module and its listings
 *     are subject restricted to the terms of a license agreement.
 *     Documents describing systems using this module must cite its source.
 *
 *     The above statements must be retained with all copies of this
 *     program and may not be removed without the consent of the
 *     University of Delaware.
 *     
 *
 *     version  -1    David H. Crocker    March   1979
 *     version   0    David H. Crocker    April   1980
 *     version  v7    David H. Crocker    May     1981
 *     version   1    David H. Crocker    October 1981
 *
 */
/*                      SEND ON PICKUP REQUEST                          */

#include <signal.h>
#include "ch.h"
#include "phs.h"

extern LLog *logptr;
extern char po_state;	/* state of processing current msg    */

LOCVAR char ibuf[BUFSIZ],         /* input buffer for fd 0              */
	    obuf[BUFSIZ];         /* output buffer for fd 1             */

/*      MAIN MAIN MAIN MAIN MAIN MAIN MAIN MAIN MAIN MAIN MAIN MAIN     */

main (argc, argv)
int     argc;
char   *argv[];
{
    extern char *dupfpath ();
    int retval;

    setbuf (stdin, ibuf);
    setbuf (stdout, obuf);
    mmdf_init (argv[0]);
    siginit ();
    signal (SIGINT, SIG_IGN);      /* always ignore interrupts             */

    retval = ch_pobox (argc, argv);

    ll_close (logptr);

    exit (retval);
}
/*****************  (ch_) LOCAL MAIL DELIVERY  ********************** */

ch_pobox (argc, argv)		  /* send to local machine               */
int     argc;
char   *argv[];
{
    Chan *curchan;
#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "ch_pobox ()");
#endif

    if ((curchan = ch_nm2struct (argv[0])) == (Chan *) NOTOK)
	err_abrt (RP_PARM, "unknown channel name '%s'", argv[0]);
				  /* find out who I am                  */

    ch_llinit (curchan);
    if (rp_isbad (qu_init (argc, argv)))
	return (RP_NO);		  /* problem setting-up for deliver     */
    if (rp_isbad (po_init ()))
	return (RP_NO);

    phs_note (curchan, PHS_WRSTRT);

    if (rp_isbad (qu2po_send (argv[0])))
    {                             /* send the batch of outgoing mail    */
	qu_fakrply (po_state);   /* problem, but hide from Deliver     */
	po_end (NOTOK);
    }
    else
    {
	po_end (OK);
	phs_note (curchan, PHS_WREND);
    }

    qu_end (OK);                  /* done with Deliver function         */
    return (RP_OK);		  /* NORMAL RETURN                      */
}

/**/

/* VARARGS2 */

err_abrt (code, fmt, b, c, d)     /* terminate ourself                  */
int     code;
char    fmt[],
        b[],
        c[],
        d[];
{
#ifdef DEBUG
    char linebuf[LINESIZE];
#endif

    qu_end (NOTOK);
    po_end (NOTOK);
    if (rp_isbad (code))
    {
#ifdef DEBUG
	if (rp_gbval (code) == RP_BNO || logptr -> ll_level >= LLOGBTR)
	{                         /* don't worry about minor stuff      */
	    sprintf (linebuf, "%s%s", "err [ ABEND (%s) ]\t", fmt);
	    ll_log (logptr, LLOGFAT, linebuf, rp_valstr (code), b, c, d);
	    abort ();
	}
#endif
    }
    ll_close (logptr);           /* in case of cycling, close neatly   */
    exit (code);
}
val = ch_waend ();
	adr_t2fin (adr_lastdone, themsg, retval);
    }

    adr_lastdone = (Chan *) 0;
}

LOCFUN
	adr_hdone (themsg, oldhost, chan) /* end of address list for host  */
    Msg *themsg;
    char *oldhost;
    Chan *chan;
{
    int retval = RP_OK;

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "adr_hdone (%s)", themsg -> mg_mname);
#endif

    if (ch_curchan != 0 && (adr_lastdone == ch_curchan))
    {                             /* if have a chan, say we armmdf/src/pobox/qu2po_send.c   444      0     12       32106  3620510526  11133 #include "util.h"
#include "mmdf.h"

/*
 *     MULTI-CHANNEL MEMO DISTRIBUTION FACILITY  (MMDF)
 *
 *
 *     Copyright (C) 1979,1980,1981  University of Delaware
 *
 *     Department of Electrical Engineering
 *     University of Delaware
 *     Newark, Delaware  19711
 *
 *     Phone:  (302) 738-1163
 *
 *
 *     This program module was developed as part of the University
 *     of Delaware's Multi-Channel Memo Distribution Facility (MMDF).
 *
 *     Acquisition, use, and distribution of this module and its listings
 *     are subject restricted to the terms of a license agreement.
 *     Documents describing systems using this module must cite its source.
 *
 *     The above statements must be retained with all copies of this
 *     program and may not be removed without the consent of the
 *     University of Delaware.
 *     
 *
 *     version  -1    David H. Crocker    March   1979
 *     version   0    David H. Crocker    April   1980
 *     version  v7    David H. Crocker    May     1981
 *     version   1    David H. Crocker    October 1981
 *
 */

/*              SEND FROM DELIVER OVER TO CALLER (PASSIVE)              */

#include "ch.h"
#include "chan.h"
#include <pwd.h>
#include "ap.h"

extern struct ll_struct    *logptr;

extern char *mmdflogin;
extern char *supportaddr;

extern char *strdup();
extern char *ap_p2s();

extern int ap_outtype;

LOCVAR char *sender = (char *)NULL;    /* return address for message         */
LOCVAR char *adr    = (char *)NULL;    /* recipient address                  */
LOCVAR struct rp_construct
			    rp_hend  =
{
    RP_NOOP, 'e', 'n', 'd', ' ', 'o', 'f', ' ', 'h', 'o', 's', 't', ' ',
    'i', 'g', 'n', 'o', 'r', 'e', 'd', '\0'
};
LOCVAR struct rp_construct
			    rp_skip =
{
    RP_NOOP, 's', 'k', 'i', 'p', ' ', 't', 'h', 'i', 's', ' ',
    'a', 'd', 'd', 'r', 'e', 's', 's', '\0'
};

LOCVAR Chan *q2p_chan;            /* what channel am I?                 */

LOCVAR int    q2p_uid;            /* user id of caller                  */

LOCVAR unsigned short q2p_nadrs;  /* number of valid addresses          */

LOCVAR char *q2p_info;            /* initialization info for message    */

LOCVAR char q2p_username[USERSIZE + 1];
				  /* name of caller                     */
LOCVAR char q2p_out;              /* at least one adr sent              */
char po_state = SND_RINIT;/* state of processing current msg    */
/**/

qu2po_send (chname)               /* overall mngmt for batch of msgs    */
    char chname[];                /* name of channel we are             */
{
    extern char *strdup ();
    short     result;
    char    info[LINESIZE],
	    sendbuf[ADDRSIZE];

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "qu2po_send");
#endif

    q2p_gcinfo ();                /* get info on the caller             */

    q2p_chan = ch_nm2struct (chname);
    if (q2p_chan == (Chan *) NOTOK)
	err_abrt (RP_PARM, "unknown channel name '%s'", chname);
				  /* find out who I am                  */

    if (q2p_chan -> ch_login != 0)  /* only one login may pickup chan     */
    {
#ifdef DEBUG
	ll_log (logptr, LLOGFTR, "%s =?= %s",
		    q2p_username, q2p_chan -> ch_login);
#endif
	if (!lexequ (q2p_username, q2p_chan -> ch_login) &&
	    !lexequ (q2p_username, mmdflogin))
	{
	    ll_log (logptr, LLOGFAT,
		    "'%s' not authorized to pickup for %s (%s)",
		    q2p_username, q2p_chan -> ch_table -> tb_name, 
		    q2p_chan -> ch_name);
	    return (RP_USER);     /* caller not authorized to pickup    */
	}
    }

    if (rp_isbad (result = qu_pkinit ()))
	return (result);

    if (rp_isbad (result = po_sbinit ()))
	return (result);

    po_state = SND_ABORT;
    for(;;){
	AP_ptr  loctree, domtree, routree, sendtree;
	AP_ptr  ap_s2tree(), ap_normalize();

	result = qu_rinit (info, sendbuf, q2p_chan->ch_apout);
	if(rp_gval(result) == RP_NS){
	    qu_rend();
	    continue;
	}
	else if(rp_gval(result) == RP_DONE)
	    break;

	if (rp_gval(result) == RP_FIO)          /* Can't open message file */
		continue;
	else if (rp_gval(result) != RP_OK)      /* Some other error */
		break;

	q2p_info = strdup (info); /* not sent until an address goes out */

	if (sender != (char *)NULL){
		free (sender);
		sender = NULL;
	}

	if ( sendbuf[0] == '\0' ||
	    (sendtree = ap_s2tree( sendbuf )) == (AP_ptr) NOTOK) {
		printx("return address unparseable, using Orphanage\n");
		fflush(stdout);
		sender = strdup (supportaddr);
	} else {
		ap_outtype = q2p_chan -> ch_apout;
		sendtree = ap_normalize (q2p_chan -> ch_lname,
			       q2p_chan  -> ch_ldomain, sendtree, q2p_chan);
		if(sendtree == (AP_ptr)MAYBE)
		    return(RP_NS);
		ap_t2parts (sendtree, (AP_ptr *)0, (AP_ptr *)0, &loctree, &domtree, &routree);
		sender = ap_p2s ((AP_ptr)0, (AP_ptr)0, loctree, domtree, routree);
		if(sender == (char *)MAYBE)
			return(RP_NS);
		ap_sqdelete( sendtree, (AP_ptr) 0 );
		ap_free( sendtree );
	}

	po_state = SND_RDADR;
	if (rp_isbad (result = q2p_admng ()))
	    return (result);      /* send the address list              */

	if (rp_gval (result) != RP_DONE)
	    break;                /* catch protocol errors              */

	result = q2p_txmng ();
	free (q2p_info);          /* don't have to zero                 */
	if (rp_isbad (result))
	    return (result);      /* send the message text              */
    }

    if (rp_gval (result) != RP_DONE)
    {
	ll_log (logptr, LLOGTMP, "not DONE (%s)", rp_valstr (result));
	return (RP_RPLY);         /* catch protocol errors              */
    }

    po_state = SND_ABORT;
    qu_pkend ();                  /* done getting messages              */
    return (po_sbend ());         /* done sending messages              */
}
/**/

LOCFUN
	q2p_admng ()              /* send address list                  */
{
    struct rp_bufstruct thereply;
    short     result;
    int       len;
    char    host[ADDRSIZE];
    char    adrbuf[ADDRSIZE];

    AP_ptr  loctree, domtree, routree, adrtree;
    AP_ptr  ap_s2tree(), ap_normalize();


#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "q2p_admng");
#endif

    q2p_nadrs = 0;                /* reset for each new messages        */
    q2p_out = FALSE;
    FOREVER                       /* iterate thru list                  */
    {                             /* we already have and adr            */
	po_state = SND_ABORT;
	result = qu_radr (host, adrbuf);
	if (rp_isbad (result))
	    return (result);      /* get address from Deliver           */

	if (rp_gval (result) == RP_HOK)
	{                         /* no-op the sub-list indication      */
	    qu_wrply ((struct rp_bufstruct *) &rp_hend, rp_conlen (rp_hend));
	    continue;
	}

	if (rp_gval (result) == RP_DONE)
	    break;                /* end of address list                */

	po_state = SND_ARPLY;
	if (!q2p_mayadr (host))
	{                         /* not authorized to go out           */
	    qu_wrply ((struct rp_bufstruct *) &rp_skip, rp_conlen (rp_skip));
	    continue;
	}

	if (!q2p_out)             /* first one to go out                */
	{
	    q2p_out = TRUE;
	    if (rp_isbad (result =
			    po_winit ((char *) 0, q2p_info, sender)))
		return (result);
	}

	if (adr != (char *)NULL)
		free (adr);

	if ( (adrtree = ap_s2tree(adrbuf)) == (AP_ptr) NOTOK)  {
		adr = strdup( adrbuf );
	} else {
		ap_outtype = q2p_chan -> ch_apout;
		adrtree = ap_normalize (q2p_chan -> ch_lname,
				q2p_chan  -> ch_ldomain, adrtree, q2p_chan);
		if(adrtree == (AP_ptr)MAYBE)
		    return(RP_NS);
		ap_t2parts (adrtree, (AP_ptr *)0, (AP_ptr *)0, &loctree, &domtree, &routree);
		adr = ap_p2s ((AP_ptr)0, (AP_ptr)0, loctree, domtree, routree);
		if(adr == (char *)MAYBE)
		    return(RP_NS);
		ap_sqdelete( adrtree, (AP_ptr) 0 );
		ap_free( adrtree );
	  }

	if (rp_isbad (result = po_wadr (host, adr)))
	    return (result);      /* give to remote site                */

	if (rp_isbad (result = po_rrply (&thereply, &len)))
	    return (result);      /* how did remote like it?            */

	switch (rp_gval (thereply.rp_val))
	{                         /* was address acceptable?            */
	    case RP_AOK:          /* address ok, text not yet sent      */
		q2p_nadrs++;
		po_state = SND_ABORT;
		qu_wrply (&thereply, len);
		break;

	    case RP_NO:           /* remaining acceptible responses     */
		thereply.rp_val = RP_NDEL;
	    case RP_USER: 
	    case RP_NDEL: 
	    case RP_AGN: 
	    case RP_NOOP: 
		po_state = SND_ABORT;
		qu_wrply (&thereply, len);
		break;

	    default:              /* responses which force abort        */
		if (rp_isbad (thereply.rp_val))
		    return (thereply.rp_val);
		return (RP_RPLY);
	}
    }
    po_state = SND_TRPLY;
    if (q2p_out)                  /* at least one went out              */
	return (po_waend ());    /* tell remote of address list end    */
    else
	return (RP_DONE);
}
/**/

LOCFUN
	q2p_txmng ()              /* send message text                  */
{
    struct rp_bufstruct thereply;
    int     len;
    short   result;
    char    buffer[BUFSIZ];
    static char faktxt[] = "   ";       /* to keep dial happy   */

/*  the main portion handles normal transmission, when there have been
 *  some addresses accepted.  when no addresses have been accepted, then
 *  the text will be ignored by the receiving side.
 */
#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "q2p_txmng");
#endif

    if (!q2p_out)
    {                             /* no addresses tried                 */
	ll_log (logptr, LLOGGEN, "no addrs sent");
	qu_wrply ((struct rp_bufstruct *) &rp_skip, rp_conlen (rp_skip));
	return (RP_OK);
    }

    if (q2p_nadrs == 0)           /* no valid addresses                 */
    {                             /* send some text to keep things happy*/
	if (rp_isbad (result = po_wtxt (faktxt, (sizeof faktxt) - 1)))
	    return (result);
	if (rp_gval (result) != RP_OK)
	    return (RP_RPLY);
    }
    else
    {
	qu_rtinit (0L);           /* get ready to read text             */
    	len = sizeof(buffer);
	while ((rp_gval (result = qu_rtxt (buffer, &len))) == RP_OK)
	{
	    if (rp_isbad (result = po_wtxt (buffer, len)))
		return (result);
	    if (rp_gval (result) != RP_OK)
		return (RP_RPLY);
	    len = sizeof(buffer);
	}

	if (rp_isbad (result))
	    return (result);
	if (rp_gval (result) != RP_DONE)
	    return (RP_RPLY);     /* didn't get it all across?          */
    }


    if (rp_isbad (result = po_wtend ()))
	return (result);          /* flag end of message                */

    if (rp_isbad (result = po_rrply (&thereply, &len)))
	return (result);          /* problem getting reply?             */

    switch (rp_gval (thereply.rp_val))
    {                             /* was text acceptable?               */
	case RP_OK: 
	    thereply.rp_val = RP_MOK;
	case RP_MOK:              /* text was accepted                  */
	    qu_wrply (&thereply, len);
	    break;

	case RP_NO:               /* remaining acceptible responses     */
	    thereply.rp_val = RP_NDEL;
	case RP_NDEL: 
	case RP_AGN: 
	case RP_NOOP: 
	    qu_wrply (&thereply, len);
	    break;

	default:                  /* responses which force abort        */
	    if (rp_isbad (thereply.rp_val))
		return (thereply.rp_val);
	    return (RP_RPLY);
    }
    return (thereply.rp_val);     /* just quote remote                  */
}
/**/

LOCFUN
	q2p_gcinfo ()             /* who is the caller?                 */
{
    struct passwd *getpwuid ();
    int   effecid;
    register struct passwd  *pwdptr;

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "q2p_gcinfo ()");
#endif

    getwho (&q2p_uid, &effecid);
    pwdptr = getpwuid (q2p_uid);

    strcpy (q2p_username, pwdptr -> pw_name);
}
/**/

LOCFUN
	q2p_mayadr (host)         /* may caller get this message?       */
char    host[];                   /* destination host                   */
{
    char    adrline[LINESIZE];

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "q2p_mayadr");
#endif

/* PICKUP POLICY:   Who may pickup messages?
 *
 *  Two situations will be most common:  pickup net & relay
 *
 *  A "pickup net" is a network of callers who each will have
 *  mail to pickup.  Each caller is a single recipient (machine).
 *  Hence, a "host" equates to a single receiver (machine).  The
 *  hostname, therefore, must equal the name (unix login name) of
 *  the caller.
 *
 *  A "relay" is a single caller, retrieving mail for a set of
 *  hosts.  Here, the hostname has nothing to do with the
 *  caller's id and is part of the address that is passed on.
 *  (For local, arpanet, and pickup-net mail, the hostname part
 *  of the address is not passed on to the recipient.)  In this
 *  case, the id of the authorized caller is hard-wired into a
 *  variable.
 *
 *  The authorization test, for relaying, is done in qu2ph_send's
 *  startup.  The test for pickup-net is done in this routine.
 */

    if (q2p_chan -> ch_login == 0)
    {                             /* login => host, not chan            */
	if (tb_k2val (q2p_chan -> ch_table, TRUE, host, adrline)!=OK)
	{
	    ll_log (logptr, LLOGTMP, "pobox host '%s' not in table!", host);
	    return (FALSE);       /* map name -> address                  */
	}

#ifdef DEBUG
	ll_log (logptr, LLOGFTR, "%s =?= %s", adrline, q2p_username);
#endif

	if (!lexequ (adrline, q2p_username))
	    return (FALSE);       /* "address" == this user's id?         */
    }

    return (TRUE);                /* OK to send                           */
}
2p_txmng");
#endif

    if (!q2p_out)
    {                             /* no addresses tried                 */
	ll_log (logptr, LLOGGEN, "no addrs sent");
	qu_wrply ((struct rp_bufstruct *) &rp_skip, rp_conlen (rp_skip));
	return (RP_OK);
    }

    if (q2p_nadrs == 0)           /* no valid addresses                 */
    {                             /* send some text to keep things happy*/
	if (rp_isbad (result = po_wtxt (faktxt, (simmdf/src/pobox/po_wtmail.c   444      0     12        6130  3620510526  11025 #include "util.h"
#include "mmdf.h"

/*
 *     MULTI-CHANNEL MEMO DISTRIBUTION FACILITY  (MMDF)
 *     
 *
 *     Copyright (C) 1979,1980,1981  University of Delaware
 *     
 *     Department of Electrical Engineering
 *     University of Delaware
 *     Newark, Delaware  19711
 *
 *     Phone:  (302) 738-1163
 *     
 *     
 *     This program module was developed as part of the University
 *     of Delaware's Multi-Channel Memo Distribution Facility (MMDF).
 *     
 *     Acquisition, use, and distribution of this module and its listings
 *     are subject restricted to the terms of a license agreement.
 *     Documents describing systems using this module must cite its source.
 *
 *     The above statements must be retained with all copies of this
 *     program and may not be removed without the consent of the
 *     University of Delaware.
 *     
 *
 *     version  -1    David H. Crocker    March   1979
 *     version   0    David H. Crocker    April   1980
 *     version  v7    David H. Crocker    May     1981
 *     version   1    David H. Crocker    October 1981
 *
 */
extern struct ll_struct   *logptr;

/* *******  (po_)  POBOX (PICKUP) MAIL-WRITING I/O SUB-MODULE  ******** */

/* ARGSUSED */

po_winit (vianet, info, retadr)  /* pass msg initialization info       */
char    vianet[];		  /* what channel coming in from        */
char    info[],			  /* general info                       */
        retadr[];		  /* return address for error msgs      */
{
    short     retval;
    char linebuf[LINESIZE];

/* DBG:  make sure info has right form */

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "po_winit");
#endif

    sprintf (linebuf, "%s;%s", info, retadr);
				  /* slave:  <info> ';'<retadr>    */
    retval = po_wrec (linebuf, strlen (linebuf));

    return (retval);
}
/**/

/*ARGSUSED*/
po_wadr (host, adr)              /* send one address spec to local     */
char    *host,			  /* "next" location part of address    */
        *adr;			  /* rest of address                    */
{
    short     retval;
    char adrbuf[LINESIZE];

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "po_wadr()");
#endif

    strcpy (adrbuf, adr);
				  /* tell the user, if watching         */
    retval = po_wrec (adrbuf, strlen (adrbuf));

    return (retval);
}

po_waend ()                      /* end of address lsit                */
{
    short     retval;
#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "po_waend");
#endif

    if (rp_isbad (retval = (po_wrec ((char *) 0, 0))))
	return (retval);
    return (RP_DONE);
}
/**/

po_wtxt (buffer, len)            /* send next part of msg text         */
char    buffer[];		  /* the text                           */
short     len;                      /* length of text                     */
{
#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "po_wtxt()");
#endif

    return (po_wstm (buffer, len));
}

po_wtend ()                      /* end of message text                */
{
#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "po_wtend");
#endif

    po_wstm ((char *) 0, 0);      /* flush the output buffer            */
    return (po_wrec ((char *) 0, 0));  /* signal end of stream               */
}
nse agreement.
 *     Documents describing systems using this module must cite its source.
 *
 *     The above statements must be retained with all copies of this
 *     program and may not be removed without the consent of the
 *     University of Delaware.
 *     
 *
 *     version  -1    David H. Crocker    March   1979
 *     version   0    David H. Crocker    April   1980
 *     version  v7    David H. Crocker    Mammdf/src/pobox/po_io.c   444      0     12       13050  3620510526  10156 #include "util.h"
#include "mmdf.h"

/*
 *     MULTI-CHANNEL MEMO DISTRIBUTION FACILITY  (MMDF)
 *     
 *
 *     Copyright (C) 1979,1980,1981  University of Delaware
 *     
 *     Department of Electrical Engineering
 *     University of Delaware
 *     Newark, Delaware  19711
 *
 *     Phone:  (302) 738-1163
 *     
 *     
 *     This program module was developed as part of the University
 *     of Delaware's Multi-Channel Memo Distribution Facility (MMDF).
 *     
 *     Acquisition, use, and distribution of this module and its listings
 *     are subject restricted to the terms of a license agreement.
 *     Documents describing systems using this module must cite its source.
 *
 *     The above statements must be retained with all copies of this
 *     program and may not be removed without the consent of the
 *     University of Delaware.
 *     
 *
 *     version  -1    David H. Crocker    March   1979
 *     version   0    David H. Crocker    April   1980
 *     version  v7    David H. Crocker    May     1981
 *     version   1    David H. Crocker    October 1981
 *
 */

#include "ch.h"

extern LLog   *logptr;

/* ***********  (po_)  POBOX PICKUP-MAIL I/O SUB-MODULE  ************** */

po_init ()                /* get ready for mail pickup          */
{
#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "po_init ()");
#endif
    return (RP_OK);
}

/* ARGSUSED */

po_end (type)                     /* done with mail process             */
short     type;
{
#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "po_end ()");
#endif
#ifdef RUNALON
    printx ("po_end, ");
#endif
    return (RP_OK);
}

po_sbinit ()                      /* initialize local submission        */
{
#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "po_sbinit ()");
#endif
    return (RP_OK);
}

po_sbend ()                       /* done with submission               */
{
#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "po_sbend ()");
#endif
#ifdef RUNALON
    printx ("po_sbend, ");
#endif
    return (RP_DONE);
}
/*                    PROCESS REPLIES                                 */

po_rrply (valstr, len)           /* get a reply from remote process    */
struct rp_bufstruct *valstr;      /* where to stuff copy of reply text  */
int    *len;                      /* where to indicate text's length    */
{
    short     retval;
    char   *rplystr;

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "po_rrply()");
#endif

    retval = po_rrec ((char *) valstr, len);
    if (rp_gval (retval) != RP_OK)
	return (retval);

#ifdef RUNALON
    switch (rp_gval (valstr -> rp_val))
    {
	case 'd': 
	    valstr -> rp_val = RP_DONE;
	    break;
	case 'y': 
	    valstr -> rp_val = RP_OK;
	    break;
	case 'm': 
	    valstr -> rp_val = RP_MOK;
	    break;
	case 'a': 
	    valstr -> rp_val = RP_AOK;
	    break;
	case 'n': 
	    valstr -> rp_val = RP_NO;
	    break;
    }
#endif

    rplystr = rp_valstr (valstr -> rp_val);
    if (*rplystr == '*')
    {				  /* replyer did a no-no                */
	ll_log (logptr, LLOGTMP, "ILLEGAL REPLY: (%s)", rplystr);
	valstr -> rp_val = RP_RPLY;
    }
#ifdef DEBUG
    else
	ll_log (logptr, LLOGFTR, "(%s)'%s'", rplystr, valstr -> rp_line);
#endif

    return (RP_OK);
}
/*         READ DATA FROM LOCAL MAIL (SUB)PROCESS                     */

po_rrec (linebuf, len)           /* read one "record"                    */
char   *linebuf;		  /* where to stuff the text              */
int      *len;                    /* where to stuff the length count      */
{
#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "po_rrec ()");
#endif

    switch (*len = gcread (stdin, linebuf, LINESIZE - 1, "\000\n\377"))
    {				  /* a record == one line                 */
	case NOTOK: 
	    ll_log (logptr, LLOGFAT, "read error");
	    return (RP_LIO);

	case OK: 
#ifdef DEBUG
	    ll_log (logptr, LLOGFTR, "eof");
#endif
	    return (RP_EOF);

	case 1: 
#ifdef DEBUG
	    ll_log (logptr, LLOGFTR, "DONE");
#endif
	    return (RP_DONE);     /* the only valid one-char record     */
    }
    linebuf[*len] = '\0';	  /* keep things null-terminated        */
    *len -= 1;
#ifdef DEBUG
    ll_log (logptr, LLOGFTR, "(%d)'%s'", *len, linebuf);
#endif
    return (RP_OK);
}
/*            WRITE DATA TO LOCAL MAIL (SUB)PROCESS                   */

po_wrec (buf, len)           /* write a record/packet              */
char    *buf;			  /* chars to write                     */
int	len;                      /* number of chars to write           */
{
#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "po_wrec () (%d)'%s'", len, buf ? buf : "");
#endif

    if (!isstr(buf) && len == 0)
    {				  /* send an end-of-stream              */
#ifdef RUNALON
	printx ("po_wrec (eof), ");
#endif
	putchar ('\0');
    }
    else
    {
	fwrite (buf, 1, len, stdout);
	putchar ('\n');
    }
    fflush (stdout);
    if (ferror (stdout))
    {
	ll_log (logptr, LLOGFTR, "write error");
	return (RP_LIO);
    }
    return (RP_OK);
}
/**/

po_wstm (buffer, len)            /* write next part of char stream     */
char    buffer[];		  /* chars to write                     */
int     len;                      /* number of chars to write           */
{
    register char   doflush;      /* flush all the text out?            */

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "po_wstm ()");
#endif

    doflush = (buffer == 0 && len == 0);

    if (!doflush)
	buffer[len] = '\0';
#ifdef DEBUG
    ll_log (logptr, LLOGFTR, "po_wstm (): (%d)\"%s\"",
	    len, (doflush) ? "[EOF]" : buffer);
#endif
    if (doflush)
	fflush (stdout);
    else
	fwrite (buffer, 1, len, stdout);

    if (ferror (stdout))
    {
	ll_log (logptr, LLOGFAT, "write error");
	return (RP_LIO);
    }
    return (RP_OK);
}
G
    ll_log (logptr, LLOGBTR, "q2p_mayadr");
#endif

/* PICKUP POLICY:   Who may pickup messages?
 *
 *  Two situations will be most common:  pickup net & relay
 *
 *  A "pickup net" is a network of callers who each will have
 *  mail to pickup.  Each caller is a single recipient (machine).
 *  Hence, a "host" equates to a single receiver (machine).  The
 *  hostname, therefore, must equal the name (unix login name) of
 *  the caller.
 *
 *  A "relay" is a single calmmdf/src/pobox/Makefile.real   444      0     12        2555  3635174251  11266 #
# ch_pobox:   passive delivery (wait for pickup) channel transmission
#

MODULES =	ch_pobox qu2po_send po_wtmail po_io

OBJECTS =	ch_pobox.o qu2po_send.o po_wtmail.o po_io.o

SOURCES =	ch_pobox.c qu2po_send.c po_wtmail.c po_io.c

real-default:	pobox

install:	$(CHANDIR)/pobox

$(CHANDIR)/pobox  :   xpobox
	cp xpobox $(CHANDIR)/pobox
	-$(CHOWN) $(MMDFLOGIN) $(CHANDIR)/pobox
	-chmod 0$(PGMPROT) $(CHANDIR)/pobox
	-@ls -ls $(CHANDIR)/pobox
	-@echo "pobox channel installed normally"; echo ""

pobox:	xpobox
xpobox:	$(OBJECTS) $(MMDFLIBS)
	$(CC) $(LDFLAGS) -o $@ $(OBJECTS) $(MMDFLIBS) $(SYSLIBS)

lint:
	$(LINT) $(LFLAGS) $(SOURCES) $(LLIBS)

clean:
	-rm -f xpobox *.o x.c makedep eddep make.out errs


# DO NOT DELETE THIS LINE -- make depend uses it

ch_pobox.o: ch_pobox.c
ch_pobox.o: ../../h/util.h
ch_pobox.o: ../../h/mmdf.h
ch_pobox.o: /usr/include/signal.h
ch_pobox.o: ../../h/ch.h
ch_pobox.o: ../../h/phs.h
qu2po_send.o: qu2po_send.c
qu2po_send.o: ../../h/util.h
qu2po_send.o: ../../h/mmdf.h
qu2po_send.o: ../../h/ch.h
qu2po_send.o: ../../h/chan.h
qu2po_send.o: /usr/include/pwd.h
qu2po_send.o: ../../h/ap.h
po_wtmail.o: po_wtmail.c
po_wtmail.o: ../../h/util.h
po_wtmail.o: ../../h/mmdf.h
po_io.o: po_io.c
po_io.o: ../../h/util.h
po_io.o: ../../h/mmdf.h
po_io.o: ../../h/ch.h
# DEPENDENCIES MUST END AT END OF FILE
# IF YOU PUT STUFF HERE IT WILL GO AWAY
# see make depend above
the only valid one-char record     */
    }
    linebuf[*len] = '\0';	  /* keep things null-terminated        */
    *len -= 1;
#ifdef DEBUG
    llmmdf/src/pobox/gen   555      0     12          57  3620510526   7327 make -f ../../Makefile.com -f Makefile.real $*
up) channel transmission
#

MODULES =	ch_pobox qu2po_send po_wtmail po_io

OBJECTS =	ch_pobox.o qu2po_send.o po_wtmail.o po_io.o

SOURCES =	ch_pobox.c qu2po_send.c po_wtmail.c po_io.c

real-default:	pobox

install:	$(CHANDIR)/pobox

$(CHANDIR)/pobox  :   xpobox
	cp xpobox $(CHANDIR)/pobox
	-$(CHOWN) $(MMDFLOGIN) $(CHANDIR)/pobox
	-chmod 0$(PGMPROT) $(CHANDIR)/pobox
	-@ls -ls $(CHANDIR)/pobox
	-@echo "pobox channel installed normally"; echo ""

pobox:	xpobox
xpommdf/src/ean/   755      0     12           0  3652727672   6240 mmdf/src/ean/Makefile.real   444      0     12        4665  3635174406  10710 #
#       ean  --  EAN X.400 Mail system to MMDF channel
#
MODULES =       ch_ean qu2en_send eanlog

OBJECTS =       ch_ean.o qu2en_send.o en_wtmail.o eanlog.o

SOURCES =       ch_ean.c qu2en_send.c eanlog.c

EANDIR = /usr/src/ean

UTILOBJ         = ${EANDIR}/src/util/interface.a
CCITTOBJ        = ${EANDIR}/src/ccitt/interface.a
MTAOBJ          = ${EANDIR}/src/mta/interface.a
UAOBJ           = ${EANDIR}/src/ua/interface.a
MTAMAINTOBJ     = ${EANDIR}/src/mta/maint/interface.a
HOSTLIB         = ${EANDIR}/src/host/host/interface.a
FOOLIB          = ${EANDIR}/src/nsg/interface.a

EANLIBS = ${FOOLIB} ${UAOBJ} ${MTAOBJ} \
	${CCITTOBJ} ${UTILOBJ} ${HOSTLIB} /usr/lib/libtermcap.a


real-default:   ean2mmdf ean

install:        $(LIBDIR)/$(MMPREF)ean2mmdf $(CHANDIR)/$(MMPREF)ean


$(LIBDIR)/$(MMPREF)ean2mmdf:      xean2mmdf
	cp xean2mmdf $(LIBDIR)/$(MMPREF)ean2mmdf
	-$(CHOWN) root $(LIBDIR)/$(MMPREF)ean2mmdf
	-chmod 04$(PGMPROT) $(LIBDIR)/$(MMPREF)ean2mmdf
	-@ls -ls $(LIBDIR)/$(MMPREF)ean2mmdf
	-@echo "ean2mmdf installed normally"; echo ""

ean2mmdf:  xean2mmdf
xean2mmdf: ean2mmdf.o $(MMDFLIBS)
	    $(CC) $(LDFLAGS) -o $@ ean2mmdf.o $(MMDFLIBS) \
			$(HOSTLIBS) $(SYSLIBS)



$(CHANDIR)/$(MMPREF)ean:      xean
	cp xean $(CHANDIR)/$(MMPREF)ean
	-$(CHOWN) root $(CHANDIR)/$(MMPREF)ean
	-chmod 04$(PGMPROT) $(CHANDIR)/$(MMPREF)ean
	-@ls -ls $(CHANDIR)/$(MMPREF)ean
	-@echo "ean installed normally"; echo ""

ean:  xean
xean: $(OBJECTS) $(MMDFLIBS) $(EANLIBS)
	    $(CC) $(LDFLAGS) -o $@ $(OBJECTS) $(EANLIBS) \
		$(MMDFLIBS) $(HOSTLIBS) $(SYSLIBS)

en_wtmail.o:  en_wtmail.c
	cc -c -O -I$(EANDIR)/src en_wtmail.c

en_wtmail.o: en_wtmail.c
en_wtmail.o: ../../h/util.h
en_wtmail.o: ../../h/mmdf.h


lint:
	$(LINT) $(LFLAGS) $(SOURCES) $(LLIBS)

clean:
	-rm -f core xean xean2mmdf *.o x.c makedep eddep make.out errs


# DO NOT DELETE THIS LINE -- make depend uses it
deliver.o: deliver.c
ch_ean.o: ch_ean.c
ch_ean.o: ../../h/util.h
ch_ean.o: ../../h/mmdf.h
ch_ean.o: /usr/include/signal.h
ch_ean.o: ../../h/ch.h
ch_ean.o: ../../h/phs.h
qu2en_send.o: qu2en_send.c
qu2en_send.o: ../../h/util.h
qu2en_send.o: ../../h/mmdf.h
qu2en_send.o: ../../h/phs.h
qu2en_send.o: ../../h/ap.h
qu2en_send.o: ../../h/ch.h
qu2en_send.o: /usr/include/pwd.h
qu2en_send.o: /usr/include/sys/stat.h
qu2en_send.o: ../../h/adr_queue.h
eanlog.o: eanlog.c
eanlog.o: ../../h/util.h
eanlog.o: ../../h/mmdf.h
# DEPENDENCIES MUST END AT END OF FILE
# IF YOU PUT STUFF HERE IT WILL GO AWAY
# see make depend above
: "");
#endif

    if (!isstr(buf) && len == 0)
    {				  /* send an end-ommdf/src/ean/ch_ean.c   444      0     12        4643  3622771453   7704 #include "util.h"
#include "mmdf.h"

/*
 *   Copyright University College London - 1984
 *
 *   MMDF channel mapping to the EAN X.400 system
 *
 *   Steve Kille        November 1984
 */

#include <signal.h>
#include "ch.h";
#include "phs.h"

extern Chan     *ch_nm2struct();
extern LLog	*logptr;
Chan    *chanptr;


/*      MAIN MAIN MAIN MAIN MAIN MAIN MAIN MAIN MAIN MAIN MAIN MAIN     */

main (argc, argv)
int     argc;
char   *argv[];
{
    extern char *dupfpath ();
    short retval;
    int   realid,
	  effecid;

    mmdf_init (argv[0]);
    siginit ();
    signal (SIGINT, SIG_IGN);   /* always ignore interrupts             */

    if ((chanptr = ch_nm2struct (argv[0])) == (Chan *) NOTOK)
    {
	ll_log (logptr, LLOGTMP, "qu2en_send (%s) unknown channel", argv[0]);
	exit (NOTOK);
    }

    getwho (&realid, &effecid);
    if (effecid != 0)              /* MUST run as superuser              */
	err_abrt (RP_BHST, "not running as superuser");

    ch_llinit(chanptr);
    retval = ch_ean (argc, argv);
    ll_close (logptr);              /* clean log end, if cycled  */
    exit (retval);
}
/***************  (ch_) EAN MAIL DELIVERY  ************************ */

ch_ean (argc, argv)
int     argc;
char   *argv[];
{
#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "ch_ean ()");
#endif

    if (rp_isbad (qu_init (argc, argv)))
	return (RP_NO);           /* problem setting-up for deliver     */
    phs_note (chanptr, PHS_CNSTRT);         /* make a timestamp */
    phs_note (chanptr, PHS_CNGOT);         /* make a timestamp */

    if (rp_isbad (qu2en_send ()))
	return (RP_NO);           /* send the batch of outgoing mail    */

    qu_end (OK);                  /* done with Deliver function         */
    phs_end  (chanptr, RP_OK);     /* note end of session */

    return (RP_OK);               /* NORMAL RETURN                      */
}


/* VARARGS2 */
err_abrt (code, fmt, b, c, d)     /* terminate ourself                  */
short     code;
char    *fmt, *b, *c, *d;
{
    char linebuf[LINESIZE];

    qu_end (NOTOK);
    if (rp_isbad (code))
    {
#ifdef DEBUG
	if (rp_gbval (code) == RP_BNO || logptr -> ll_level >= LLOGBTR)
	{                         /* don't worry about minor stuff      */
	    sprintf (linebuf, "%s%s", "err [ ABEND (%s) ]\t", fmt);
	    ll_log (logptr, LLOGFAT, linebuf, rp_valstr (code), b, c, d);
	    abort ();
	}
#endif
    }
    ll_close (logptr);           /* in case of cycling, close neatly   */
    exit (code);
}
 running as superuser");

    ch_llinit(chanptr);
    retval = ch_ean (argc, argv);
    ll_clmmdf/src/ean/ean2mmdf.c   444      0     12       13045  3631170273  10165 #include "util.h"
#include "mmdf.h"

/* Copyright University College London - 1984 */

/*
 *  Steve Kille - November 1984
 *
 *  Routine to pass a message from EAN to MMDF
 *
 *  Usage  ean2mmdf -f sender dest dest .....
 */

extern char *supportaddr;
extern char *mmdflogin;
extern LLog *logptr;

char *submitargs = "vmtiean*";
char *signature = "EAN to MMDF Agent";

char *template = "/tmp/ean.XXXXXX";
FILE  *newfp;

int infile;
int  gotone;

main (argc, argv)
int argc;
char *argv[];
{
	char buf[BUFSIZ];
	struct rp_bufstruct thereply;
	int i;
	int len;
	int firstone;
	char *sender;


	mmdf_init (argv[0]);
	siginit ();

	if (lexequ (argv[1], "-f"))
	{
		if (argc < 4)
		{
			ll_log (logptr, LLOGTMP, "Insufficient arguments");
			exit (NOTOK);
		}
		sender = argv[2];
		firstone = 3;
	}
	else
	{
		if (argc < 2)
		{
			ll_log (logptr, LLOGTMP, "Insufficient arguments");
			exit (NOTOK);
		}
		sender = supportaddr;
		firstone = 1;
	}


	ll_log (logptr, LLOGFST, "EAN message from '%s'", sender);

	infile = FALSE;


	if (rp_isbad (mm_init ()) || rp_isbad (mm_sbinit()))
	{
		ll_log (logptr, LLOGTMP, "Failed to start submit");
		byebye (NOTOK);
	}

	if (rp_isbad (mm_winit ((char *)0, submitargs, sender))
	  || rp_isbad (mm_rrply (&thereply, &len)))
	{
		ll_log (logptr, LLOGTMP, "Failed on mm_winit");
		byebye (NOTOK);
	}
	switch (rp_gbval(thereply.rp_val)) {
	case RP_BNO:
	case RP_BTNO:
		ll_log (logptr, LLOGTMP, "mm_winit reply RP_NO");
		byebye (NOTOK);
	}

	for (i = firstone; i < argc; i++)
		do_adr (sender, argv[i]);


	if (!gotone)
	{
		ll_log (logptr, LLOGBST, "No valid addresses");
		byebye (OK);
	}

	if (rp_isbad (mm_waend ()))
	{
		ll_log (logptr, LLOGTMP, "Failed on mm_waend");
		byebye (NOTOK);
	}


			/* If Strat of messsage is in file, get it */
	if (infile)
	{
		while ((i = fread(buf, sizeof(char), sizeof buf, newfp))
			!= NULL)
		{
			if (rp_isbad (mm_wtxt (buf, i)))
			{
				ll_log (logptr, LLOGTMP, "Failed on mm_wtxt");
				byebye (NOTOK);
			}
		}
	}


	while ((i = fread(buf, sizeof(char), sizeof buf, stdin)) != NULL)
	{
		if (rp_isbad (mm_wtxt (buf, i)))
		{
			ll_log (logptr, LLOGTMP, "Failed on mm_wtxt");
			byebye (NOTOK);
		}
	}

	if (rp_isbad (mm_wtend ()))
	{
		ll_log (logptr, LLOGTMP, "Failed on mm_wtend");
		byebye (NOTOK);
	}

	if (rp_isbad (mm_rrply (&thereply, &len)))
	{
		ll_log (logptr, LLOGTMP, "Failed on mm_rrply");
		byebye (NOTOK);
	}


	if (rp_isbad (thereply.rp_val))
	{
		ll_log (logptr, LLOGTMP, "Bad reply value (%s) '%s'",
			rp_valstr (thereply.rp_val), thereply.rp_line);
		byebye (NOTOK);
	}


	if (rp_isbad (mm_sbend ()))
	{
		ll_log (logptr, LLOGTMP, "Failed on mm_sbend");
		byebye (NOTOK);
	}

	ll_log (logptr, LLOGFST, "Mail success  from EAN to MMDF");

	byebye (OK);
}
/* */

				/* process one address                  */
do_adr (sender, adr)
char *sender;
char *adr;
{
	int retval;
	struct rp_bufstruct thereply;
	int len;


	ll_log (logptr, LLOGFST, "EAN doing address  (%s)", adr);


	if (rp_isbad (retval = mm_wadr ("", adr)) ||
		rp_isbad (retval = mm_rrply (&thereply, &len)))
	{
		ll_log (logptr, LLOGTMP, "Failed to write adr '%s' for reason '%s'",
			adr, rp_valstr (retval));
		byebye (NOTOK);
	}
	switch (rp_gval (thereply.rp_val))
	{
		case RP_AOK:
			ll_log (logptr, LLOGFST, "Good address %s",
					adr);
			gotone = TRUE;
			break;
		default:
			ll_log (logptr, LLOGBST, "Bad address '%s' [%s] %s",
				adr, rp_valstr (thereply.rp_val),
				thereply.rp_line);

			if (!infile)
			{
					/* put citatin into file */
				getcite ();
				infile = TRUE;
			}

			ml_1adr (TRUE, FALSE, signature,
				"Failed Mail Transfer", sender);
			ml_txt ("Message not delivered to address: ");
			ml_txt (adr);
			ml_txt ("\n\nThe reason for the failure was:\n    [");
			ml_txt (rp_valstr (thereply.rp_val));
			ml_txt ("] ");
			ml_txt (thereply.rp_line);
			ml_txt ("\n\nYour Message begins as follows: \n\n");
			ml_file (newfp);
			fseek (newfp, 0L, 0);
			ml_txt ("\n.......\n");
			if (ml_end (OK) == OK)
			{
			     ll_log (logptr, LLOGFST, "Sent error to '%s'",
					sender);
			     break;
			}

			     ll_log (logptr, LLOGTMP, "Failed to send warning to '%s'", sender);

					/* Now try support */
			ml_1adr (TRUE, FALSE, signature,
				"Failed Mail Transfer", supportaddr);
			ml_txt ("Message not delivered to address: ");
			ml_txt (sender);
			ml_txt ("\n\n Failure message for transfer to adr: ");
			ml_txt (adr);
			ml_txt ("\n\nThe reason for the failure was:\n  [");
			ml_txt (rp_valstr (thereply.rp_val));
			ml_txt ("] ");
			ml_txt (thereply.rp_line);
			ml_txt ("\n\nMessage begins a follows: \n\n");
			ml_file (newfp);
			fseek (newfp, 0L, 0);
			ml_txt ("\n.......\n");
			if (ml_end (OK) != OK)

			     ll_log (logptr, LLOGTMP, "Failed to send warning to '%s'", supportaddr);
			else
			      ll_log (logptr, LLOGFST, "Sent error report to '%s'",
				supportaddr);
			break;
	}
}


/* */

getcite()
{
	char buf[LINESIZE];
	int i;

	file_init();
	i =0;
	while (fgets (buf, LINESIZE, stdin) != NULL)
	{
		fputs (buf, newfp);
		if (strlen (buf) > 2)
			i++;
		if (i > 300)
			break;
	}
	fclose (newfp);
	if ((newfp =  fopen (template, "r")) == NULL)
	{
		ll_log (logptr, LLOGTMP, "failed to reopen template '%s'",
			template);
		byebye (NOTOK);
	}
}
				/* misc routines  */
file_init ()
{
	int fd;

	mktemp (template);
	if ((fd = creat (template, 0666)) < 0)
	{
		ll_log (logptr, LLOGTMP, "Failed to create temp file %s",
			template);
		exit (NOTOK);
	}
	newfp = fdopen (fd, "w");
}

byebye (retval)
int retval;
{
	if (infile)
	{
		fclose (newfp);
		unlink (template);
	}
	if (retval  == OK)
		mm_end (OK);
	else
		mm_end (NOTOK);
	exit (retval);
}

}
G
    ll_log (logptr, LLOGBTR, "q2p_mayadr");
#endif

/* PICKUP POLICY:   Who may pickup messages?
 *
 *  Two situations will be most common:  pickup net & relay
 *
 *  A "pickup net" is a network of callers who each will have
 *  mail to pickup.  Each caller is a single recipient (machine).
 *  Hence, a "host" equates to a single receiver (machine).  The
 *  hostname, therefore, must equal the name (unix login name) of
 *  the caller.
 *
 *  A "relay" is a single calmmdf/src/ean/eanlog.c   444      0     12         456  3620510534   7677 #include "util.h"
#include "mmdf.h"
/*
 *  EAN call of MMDF logging
 */

extern LLog *logptr;

eanlog (format, a0, a1, a2, a3, a4, a5,  a6, a7, a8, a9)
char *format, *a0, *a1, *a2, *a3, *a4, *a5, *a6, *a7, *a8, *a9;
{
     ll_log (logptr, LLOGFST,  format,
		a0, a1, a2, a3, a4, a5, a6, a7, a8, a9);
}
etval;
	struct rp_bufstruct thereply;
	int len;


	ll_log (logptr, LLOGFST, "EAN doing address  (%s)", adr);


	if (rp_isbad (retval = mm_wadr ("", adr)) ||
		rp_isbad (retval = mm_rrply (&thereply, &len)))
	{
mmdf/src/ean/en_wtmail.c   444      0     12       10165  3620510534  10447 /*
 *  Copyright University College London - 1984
 *
 *  Routines used by the EAN channel to access EAN
 *  Developed from Rick Sample's Sendmail mailer
 *
 *  Steve Kille  - November 1984
 */

#include "util/interface.h"
#include "ccitt/interface.h"
#include "mta/rec.h"
#include "mta/p1.h"
#include "mta/P1interf.h"
#include "ua/message.h"
#include "ua/P2.h"
#include "ua/key.h"
#include "ua/or.h"
#include "ua/input.h"
#include "nsg/A822/A822_in.h"
#include <sysexits.h>
#include <stdio.h>

#define NOTOK -1
#define CONNECTION ".local"


EDESC                   P1_desc =
  {
    P1_get,
    P1_getbuff,
    P1_put,
    P1_putbuff,
    0,
    0
  };


static    char *mydomain;
static    char *ean_sender;
static    int reccount;
static    ENODE*    reclist;




en_init (sender, domain)
char *sender;
char *domain;
{
    eanlog ("ean_init ('%s', '%s')", sender, domain);

    if (Local_info () != OK)
      {
	eanlog ("Local info unavailable");
	return (NOTOK);
      }

    ean_sender = cpystr (sender);
    mydomain = cpystr (domain);

    reclist = NULL;

    reccount = 0;

    return (OK);
}


en_end ()
{
	eanlog ("en_end()");

	free (ean_sender);
	free (mydomain);
	return (OK);
}

en_wadr (adr,  rplystr)
char *adr;                              /* address passed down */
char *rplystr;                          /* where to place problem string */
{
    int rc;
    ENODE*    recip;
    ENODE*    orname;
    ENODE*    e;

    eanlog ("en_wadr (%s)", adr);

    if ((orname = Bld_ORname (adr, &rc)) == NULL)
    {
	eanlog ("bad OR Name '%s' [%d]", adr, rc);
	sprintf (rplystr, "Bad OR Name '%s'", adr);
	return (NOTOK);
    }

    recip = NULL;
    Set_add (&recip, orname);
    e = Bit_set ((ENODE*) NULL, P1_RESPONSIBLE);
    Bit_set (e, P1_URQBASIC);
    e->id = CONTEXT + 1L;
    Set_add (&recip, e);
    Set_add (&recip, Bld_int (CONTEXT+0L, ++reccount));
    Seq_add (&reclist, recip);

    return (OK);
}


en_txt (fp, mydomain)
FILE *fp;
char *mydomain;
{
    ENODE*    dsi;
    ENODE*    trace;
    ENODE*    tr_seq;
    ENODE*    e;
    ENODE*    env;
    ENODE*    domain;
    ENODE*    mpdu;
    ENODE*    content;
    FILE*     f;
    P1ID*     p1id;
    long      cont_len;
    TIME      t;
    int       rc;

    eanlog ("en_txt ()");

    reclist->id = CONTEXT + 2L;

    if (P1_open (CONNECTION, NULL, &p1id) != OK)
    {
	eanlog ("P1_open failed");
	return (NOTOK);
    }

    P1_desc.id = (char*) p1id;


    domain = NULL;
    Seq_add (&domain, Bld_cons (P1_COUNTRY, Bld_prim (E_PRINTSTR, "")));
    Seq_add (&domain, Bld_cons (P1_ADMINDOM, Bld_prim (E_PRINTSTR, "")));
    Seq_add (&domain, Bld_prim (E_PRINTSTR, mydomain));;

    domain->id = P1_GLOBALID;

    if ((content = A822_in (&ean_sender, fp, FALSE, mydomain, &cont_len))
		 == NULL)
    {
	eanlog ("Failed to write contents");
	return (NOTOK);
    }

    env = NULL;

    /* create P1.MPDUIdentifier for message envelope */
    e = NULL;
    Seq_add (&e, Ecpy (domain));
    Seq_add (&e, Bld_prim (E_IA5_STRING, TS_get ()));
    e->id = P1_MPDUID;
    Set_add (&env, e);

    if ((e = Bld_recip (ean_sender, &rc)) == NULL
	 || (e = Set_find (e, CONTEXT + 0L)) == NULL
	 || (e = Set_find (e, P1_ORNAME)) == NULL)
      {
	eanlog ("Didn't like the sender");
	return (NOTOK);
      }

    Set_add (&env, e);

    Set_add (&env, Bld_int (P1_CONTTYPE, 2L));

    if ((e = Find_heading (content, P2_MESSAGEID)) != NULL)
	Set_add (&env, Bld_prim (P1_UACONTID, Unb_MID (e)));

    Set_add (&env, reclist);

    trace = NULL;
    Seq_add (&trace, Ecpy (domain));

    dsi = NULL;

    e = Bld_time (TR_get (&t));
    e->id = CONTEXT + 0L;
    Set_add (&dsi, e);

    Set_add (&dsi, Bld_int (CONTEXT+2L, 0L));

    Seq_add (&trace, dsi);

    tr_seq = NULL;
    Seq_add (&tr_seq, trace);
    tr_seq->id = P1_TRACEINFO;

    Set_add (&env, tr_seq);

    mpdu = NULL;
    Seq_add (&mpdu, env);

    mpdu->id = P1_USERMPDU;

    if (P1_import (p1id, mpdu) != OK)
    {
	eanlog ("Message transfer failure (P1_import)");
	return (NOTOK);
    }

    Ewrite (&P1_desc, content);

    if (P1_end (p1id, TRUE) != OK)
    {
	eanlog ("P1_end failure");
	return (NOTOK);
    }

    Efree (&content);
    P1_close (&p1id);

    return (OK);
}
ee (ean_sender);
	free (mydomain);
	return (OK);
}

en_wadr (adr,  rplystr)
char *adr;                              /* address passed down */
char *rplystr;                          /* where to place problem string */
{
    int rc;
    ENODE*    recip;
    ENODE*    orname;
    ENODE*    e;

    eanlog ("en_wadr (%s)", adr);

    if ((orname = Bld_ORname (adr, &rc)) == NULL)
    {
	eanlog ("bmmdf/src/ean/mailer.c   444      0     12       13330  3620510534   7736 /*----------------------------------------------------------*/
/*     EAN Mail System User Agent -- UUCP/EAN gateway mailer*/
/*                                                          */
/*                   September, 1983                        */
/*                                                          */
/*          Author: Rick Sample                             */
/*                  University of British Columbia          */
/*                                                          */
/*      This is a utility program to interface to UUCP mail */
/*  delivery, and re-route it into the EAN mail system. It  */
/*  reads the message (in ARPA RFC 822 format) from the     */
/*  standard input.                                         */
/*                                                          */
/*----------------------------------------------------------*/

#include "../util/interface.h"
#include "../util/log.h"
#include "../ccitt/interface.h"
#include "../mta/rec.h"
#include "../mta/p1.h"
#include "../mta/P1interf.h"
#include "../ua/message.h"
#include "../ua/P2.h"
#include "../ua/key.h"
#include "../ua/or.h"
#include "../ua/input.h"
#include "../nsg/A822/A822_in.h"
#include <sysexits.h>
#include <stdio.h>

#define	        DOM_STR	        "uucp"
#define	        CONNECTION      ".local"

EDESC			P1_desc =
  {
    P1_get,
    P1_getbuff,
    P1_put,
    P1_putbuff,
    0,
    0
  };


main (argc, argv)
int    argc;
char  *argv[];
  {
    LOG_DESC	log;
    char	str[100];
    char*     recip_name;
    char      userid[20];
    char      hostid[20];
    char*     sender;
    char*     dflt;
    int       i,
	      rc,
	      len,
	      count,
	      ignore;
    ENODE*    dsi;
    ENODE*    trace;
    ENODE*    tr_seq;
    ENODE*    e;
    ENODE*    env;
    ENODE*    domain;
    ENODE*    recip;
    ENODE*    reclist;
    ENODE*    orname;
    ENODE*    mpdu;
    ENODE*    content;
    FILE*     f;
    P1ID*     p1id;
    long      cont_len;
    TIME      t;
    
    if (Local_info () != OK) 
      {
	fputs ("Local info unavailable.\n", stderr);
	exit (EX_SOFTWARE);
      }

    reclist = NULL;

    count = 0;
    
    recip_name = cpystr ("");
    len = 1;

    for (i = 0; i < argc; ++i)
	if (argv[i][0] == '-')
	    switch (argv[i][1])
	      {
		case 'd': 
		    if (i++ == argc)
	                exit (EX_DATAERR);
		    while (i < argc && argv[i][0] != '-') 
		      {
			if ((orname = Bld_ORname (argv[i], &rc)) == NULL) 
			  {
			    fprintf (stderr, "Bad address argument: %s.\n",
				    argv[i]);
			    exit (EX_DATAERR);
			  }

		        recip = NULL;
			Set_add (&recip, orname);
			e = Bit_set ((ENODE*) NULL, P1_RESPONSIBLE);
			Bit_set (e, P1_URQBASIC);
			e->id = CONTEXT + 1L;
			Set_add (&recip, e);
			Set_add (&recip, Bld_int (CONTEXT+0L, ++count));
			Seq_add (&reclist, recip);
    
			catstr (&recip_name, &len, argv[i++]);
			catstr (&recip_name, &len, " ");
		      }
		    break;
	      }

    if (reclist == NULL) 
      {
	fputs ("no recipient specified.\n", stderr);
	exit (1);
      }

    reclist->id = CONTEXT + 2L;

    if (P1_open (CONNECTION, NULL, &p1id) != OK) 
      {
	fputs ("Message Transfer Error.\n", stderr);
	exit (EX_UNAVAILABLE);
      }
      
    P1_desc.id = (char*) p1id;

    Userid (userid);
    gethostname (hostid, 20);
    
    sender = cpystr (userid);
    len = strlen (sender) + 1;
    catstr (&sender, &len, "@");
    catstr (&sender, &len, hostid);
    catstr (&sender, &len, ".");
    catstr (&sender, &len, DOM_STR);

    domain = NULL;
    Seq_add (&domain, Bld_cons (P1_COUNTRY, Bld_prim (E_PRINTSTR, "")));
    Seq_add (&domain, Bld_cons (P1_ADMINDOM, Bld_prim (E_PRINTSTR, "")));
    Seq_add (&domain, Bld_prim (E_PRINTSTR, DOM_STR));;
    
    domain->id = P1_GLOBALID;

    ignore = (	   strcmp (userid, "ean")     != 0
		&& strcmp (userid, "root")    != 0
		&& strcmp (userid, "mmdf")    != 0
		&& strcmp (userid, "uucp")    != 0
		&& strcmp (userid, "daemon")  != 0
		&& strcmp (userid, "network") != 0);
    
    if ((content = A822_in (&sender, stdin, ignore, DOM_STR, &cont_len))
		 == NULL)
      {
	fprintf (stderr, "Invalid or empty message.\n");
	exit (EX_DATAERR);
      }
    
    env = NULL;

    /* create P1.MPDUIdentifier for message envelope */
    e = NULL;
    Seq_add (&e, Ecpy (domain));
    Seq_add (&e, Bld_prim (E_IA5_STRING, TS_get ()));
    e->id = P1_MPDUID;
    Set_add (&env, e);

    if ((e = Bld_recip (sender, &rc)) == NULL
         || (e = Set_find (e, CONTEXT + 0L)) == NULL
	 || (e = Set_find (e, P1_ORNAME)) == NULL)
      {
	fputs ("invalid originator.\n", stderr);
	exit (EX_DATAERR);
      }

    
    Set_add (&env, e);
    
    Set_add (&env, Bld_int (P1_CONTTYPE, 2L));
    
    if ((e = Find_heading (content, P2_MESSAGEID)) != NULL) 
	Set_add (&env, Bld_prim (P1_UACONTID, Unb_MID (e)));
    
    Set_add (&env, reclist);
    
    trace = NULL;
    Seq_add (&trace, Ecpy (domain));
    
    dsi = NULL;

    e = Bld_time (TR_get (&t));
    e->id = CONTEXT + 0L;
    Set_add (&dsi, e);
    
    Set_add (&dsi, Bld_int (CONTEXT+2L, 0L));
    
    Seq_add (&trace, dsi);
  
    tr_seq = NULL;
    Seq_add (&tr_seq, trace);
    tr_seq->id = P1_TRACEINFO;

    Set_add (&env, tr_seq);

    mpdu = NULL;
    Seq_add (&mpdu, env);

    mpdu->id = P1_USERMPDU;

    if (P1_import (p1id, mpdu) != OK) 
      {
	fputs ("Message Transfer Failure.\n", stderr);
	exit (EX_TEMPFAIL);
      }
    
    Ewrite (&P1_desc, content);

    if (P1_end (p1id, TRUE) != OK) 
      {
	fputs ("Message Transfer Failed.\n", stderr);
	exit (EX_OSERR);
      }
      
    Efree (&content);
    P1_close (&p1id);

    Log_init( LOG_SENDMAIL, &log );
    sprintf( Log_str, "in : %d %0.25s %0.25s", cont_len, sender, recip_name );
    Log_enter( &log, LOG_NORM, NULL );
    Log_term( &log );

    exit (EX_OK);
  }
t
 *  of the address is not passed on to the recipient.)  In this
 *  case, the id of the authorized caller is hard-wired into a
 *  variable.
 *
 *  The authorization test, for relaying, is done in qu2ph_send's
 *  startup.  The test for pickup-net is done in this routine.
 */

    if (q2p_chanmmdf/src/ean/qu2en_send.c   444      0     12       14425  3620510535  10537 /*
 *  Copyright University College London - 1984
 *
 *  Channel to submit EAN Mail
 *
 *  Steve Kille - November 1984
 */
#include "util.h"
#include "mmdf.h"
#include "phs.h"
#include "ap.h"
#include "ch.h"
#include <pwd.h>
#include <sys/stat.h>
#include "adr_queue.h"

extern Chan     *chanptr;
extern LLog     *logptr;

extern int      errno;
extern int      ap_outtype;

extern char *strcpy ();
extern char *index();
extern char *rindex();
extern char *ap_p2s();

LOCVAR char en_info[LINESIZE];
LOCVAR char en_sender[LINESIZE];
LOCVAR char en_adr[LINESIZE];

LOCVAR int gotone;

LOCVAR struct rp_construct
	rp_adr  =
{
    RP_AOK, 'a', 'd', 'd', 'r', 'e', 's', 's', ' ', 'o', 'k', '\0'
},
	rp_gdtxt =
{
    RP_MOK, 't', 'e', 'x', 't', ' ', 's', 'e', 'n', 't', ' ', 'o', 'k', '\0'
},
	rp_lio =
{
    RP_LIO, 'l', 'o', 'c', 'a', 'l', ' ', 'g', 'l', 'i', 't', 'c', 'h', '\0'
}          ,
	rp_bdtxt =
{
   RP_NDEL, 't', 'e', 'x', 't', ' ', 'c', 'o', 'p', 'y', ' ', 'f', 'a', 'i',
	'l', 'u', 'r', 'e', ' ', 't', 'o', ' ', 'E', 'A', 'N', '\0'
},
rp_hend  =
{
	RP_NOOP, 'e', 'n', 'd', ' ', 'o', 'f', ' ', 'h', 'o', 's', 't', ' ',
	'i', 'g', 'n', 'o', 'r', 'e', 'd', '\0'
};

/**/

qu2en_send ()                       /* overall mngmt for batch of msgs    */
{
	short     result;
	AP_ptr ap;
	AP_ptr local,
		domain,
		route;
	char *sender;
	char *mydomain;

#ifdef DEBUG
	ll_log (logptr, LLOGBTR, "qu2en_send ()");
#endif

	if (rp_isbad (result = qu_pkinit ()))
		return (result);

	/* While there are messages to process... */
	for(;;){
		result = qu_rinit (en_info, en_sender, chanptr -> ch_apout);
		if(rp_gval(result) == RP_NS){
		    /* qu_rend(); */
		    continue;
		}
		if(rp_gval(result) != RP_OK)
		    break;
		ap_outtype = AP_733;
		ap = ap_s2tree (en_sender);
		if (ap != (AP_ptr) NOTOK)
		{
			ap_t2parts (ap, (AP_ptr *) 0, (AP_ptr *) 0,
				&local, &domain, &route);
			sender = ap_p2s ((AP_ptr) 0, (AP_ptr) 0,
				local, domain, route);
			if(sender == (char *)MAYBE)
			    return (RP_NS);
			ap_sqdelete (ap);
			ap_free (ap);
		}
		else
		{
			ll_log (logptr,  "Duff sender '%s'", en_sender);
			return (RP_NO);
		}

		ll_log (logptr, LLOGFST, "Message from '%s'", sender);

		if ((mydomain = rindex (sender, '.')) == (char *) 0)
		{
			ll_log (logptr, LLOGTMP, "Sender with no dots '%s'");
			return (RP_NO);
		}
		mydomain++;

		if (en_init (sender, mydomain) != OK)
		{
		    ll_log (logptr, LLOGTMP, "Failed to initialise EAN");
		    return (RP_LIO);
		}
		free (sender);

		phs_note (chanptr, PHS_WRSTRT);

		if (rp_isbad (result = qu2en_each ()))
			return (result);
	}

	if (en_end() != OK)
	{               /* Make sure all is still happy */
		ll_log (logptr, LLOGTMP, "Bad  EAN termination");
		return (RP_RPLY);
	}

	if (rp_gval (result) != RP_DONE)
	{
		ll_log (logptr, LLOGTMP, "not DONE (%s)", rp_valstr (result));
		return (RP_RPLY);         /* catch protocol errors            */
	}

	qu_pkend ();                  /* done getting messages              */
	phs_note (chanptr, PHS_WREND);

	return (result);
}
/**/

LOCFUN
qu2en_each ()               /* send addresses and then text */
{
	RP_Buf  replyval;
	short   result;
	char    host[LINESIZE];
	AP_ptr ap;
	AP_ptr local,
		domain,
		route;
	char    *adr;

#ifdef DEBUG
	ll_log (logptr, LLOGBTR, "qu2en_each()");
#endif
	ap_outtype = AP_733;
	gotone = FALSE;

	/* while there are addessses to process... */
	FOREVER
	{
		result = qu_radr (host, en_adr);
		if (rp_isbad (result))
			return (result);      /* get address from Deliver */

		if (rp_gval (result) == RP_HOK)
		{                       /* no-op the sub-list indication */
			qu_wrply ((RP_Buf *) &rp_hend, rp_conlen (rp_hend));
			continue;
		}

		if (rp_gval (result) == RP_DONE)
		{

		    if (!gotone)
		    {
			ll_log (logptr, LLOGFST, "NO valid addrs so no text");
			qu_wrply ((RP_Buf *) &rp_gdtxt, rp_conlen (rp_gdtxt));
			return (RP_OK);
		    }

		    if (rp_isgood (result = qu2en_txtcpy()))
			qu_wrply ((RP_Buf *) &rp_gdtxt, rp_conlen (rp_gdtxt));
		    else
		    {
			if (result == RP_LIO)
			    qu_wrply ((RP_Buf *) &rp_lio,
					rp_conlen (rp_lio));
			else
			    qu_wrply ((RP_Buf *) &rp_bdtxt,
					rp_conlen (rp_bdtxt));
		    }
		    return (RP_OK);       /* end of message  */
		}

		ap = ap_s2tree (en_adr);
		if (ap != (AP_ptr) NOTOK)
		{
			ap_t2parts (ap, (AP_ptr *) 0, (AP_ptr *) 0,
				&local, &domain, &route);
			if (route != (AP_ptr) 0)
			    route = route -> ap_chain;
			adr = ap_p2s ((AP_ptr) 0, (AP_ptr) 0,
				local, domain, route);
			if(adr == (char *)MAYBE)
			    return (RP_NS);
#ifdef DEBUG
			ll_log (logptr, LLOGFST, "Fixed adr to '%s'",
				adr);
#endif
			ap_sqdelete (ap);
			ap_free (ap);
		}
		else
		{
			ll_log (logptr, LLOGTMP, "Bad format adr '%s'",
					en_adr);
			return (RP_NDEL);
		}

		if (en_wadr (adr,  replyval.rp_line) == OK)
		{
			gotone = TRUE;
			qu_wrply ((RP_Buf *) &rp_adr, rp_conlen (rp_adr));
		}
		else
		{
			replyval.rp_val = RP_USER;
			ll_log (logptr, LLOGFST, "EAN failed on addr '%s'",
				en_adr);
			qu_wrply (&replyval, (sizeof replyval.rp_val));
		}
		free (adr);
	}
}

/**/
char *template = "/tmp/enn.XXXXXX";


LOCFUN
qu2en_txtcpy ()                 /* copy the text of the message       */
{
	register int offset;
	int     len;
	int     result;
	char    buffer[BUFSIZ];
	int     fd;
	FILE    *fp;

#ifdef DEBUG
	ll_log (logptr, LLOGBTR, "qu2en_txtcpy()");
#endif



	mktemp (template);
	if ((fd = creat (template, 0666)) < 0)
	{
		ll_log (logptr, LLOGTMP, "Failed to create temp file %s",
			template);
		return (RP_LIO);
	}

	qu_rtinit (0L);
	len = sizeof(buffer);
	while (rp_gval (result = qu_rtxt (buffer, &len)) == RP_OK)
	{
		if (write (fd, buffer, len) != len)
		{
			ll_err (logptr, LLOGTMP, "error writing out text");
			return (RP_LIO);
		}
		len = sizeof(buffer);
	}

	if (rp_gval (result) != RP_DONE)
		return (RP_LIO);         /* didn't get it all across? */

	if (close (fd) != OK)
	{
		ll_log (logptr, LLOGTMP, "Failed to close file '%s'",
			template);
		return (RP_LIO);
	}

	if ((fp = fopen (template, "r")) == NULL)
	{
		ll_log (logptr, LLOGTMP, "Failed to reopne '%s'",
			template);
		return (RP_LIO);
	}

	if ((result = en_txt (fp)) != OK)
	{
		ll_log (logptr, LLOGTMP, "EAN copy of file '%s' failed",
			template);
		fclose (fp);
		return (RP_NO);
	}

	fclose (fp);
	unlink (template);

	return (RP_MOK);              /* got the text out                   */
}
esult));
		return (RP_RPLY);         /* catch protocol errors            */
	}

	qu_pkend ();                  /* done getting messages              */
	phs_note (chanptr, PHS_WREND);

	return (result);
}
/**/

LOCFUN
qu2en_each ()   mmdf/src/ean/gen   555      0     12          57  3620510535   6743 make -f ../../Makefile.com -f Makefile.real $*
((RP_Buf *) &rp_hend, rp_conlen (rp_hend));
			continue;
		}

		if (rp_gval (result) == RP_DONE)
		{

		    if (!gotone)
		    {
			ll_log (logptr, LLOGFST, "NO valid addrs so no text");
			qu_wrply ((RP_Buf *) &rp_gdtxt, rp_conlen (rp_gdtxt));
			return (RP_OK);
		    }

		    if (rp_isgood (result = qu2en_txtcpy()))
			qu_wrply ((RP_Buf *) &rp_gdtxt, rp_conlen (rp_gdtxt));
		    else
		    {
			if (result == RP_LIO)
			    qu_wrply ((RP_Buf *) &rp_lio,
					rmmdf/src/list/   755      0     12           0  3656466127   6447 mmdf/src/list/qu2ls_send.c   444      0     12       20641  3655234552  10772 #
/*
 *     MULTI-CHANNEL MEMO DISTRIBUTION FACILITY  (MMDF)
 *
 *     Copyright (C) 1979,1980,1981  University of Delaware
 *
 *     Department of Electrical Engineering
 *     University of Delaware
 *     Newark, Delaware  19711
 *
 *     Phone:  (302) 738-1163
 *
 *     This program module was developed as part of the University
 *     of Delaware's Multi-Channel Memo Distribution Facility (MMDF).
 *
 *     Acquisition, use, and distribution of this module and its listings
 *     are subject restricted to the terms of a license agreement.
 *     Documents describing systems using this module must cite its source.
 *
 *     The above statements must be retained with all copies of this
 *     program and may not be removed without the consent of the
 *     University of Delaware.
 *
 *
 *     version  -1    David H. Crocker    March   1979
 *     version   0    David H. Crocker    April   1980
 *     version  v7    David H. Crocker    May     1981
 *     version   1    David H. Crocker    October 1981
 *
 */
/*                SEND FROM DELIVER TO SUBMIT
 *
 *  Feb 83  Doug Kingston       Initial version of list processor
 *  Apr 86  Craig Partridge     Minor efficiency tweaking
 */

#include "util.h"
#include "mmdf.h"
#include "phs.h"
#include "ch.h"
#include "ap.h"
#include <pwd.h>

extern struct ll_struct   *logptr;
extern Chan *chanptr;
extern long qu_msglen;
extern char *supportaddr;

extern char *rindex();
extern char *strdup();
extern char *multcat();
extern char *blt();

char *findreturn();

LOCVAR int nadrs;
LOCVAR char sender[ADDRSIZE];

LOCVAR struct rp_construct
	rp_bdrem =
{
    RP_BHST, 'B', 'a', 'd', ' ', 'r', 'e', 's', 'p', 'o', 'n', 's', 'e',
    ' ', 'f', 'r', 'o', 'm', ' ', 's', 'u', 'b', 'm', 'i', 't', '\0'
},
	rp_adr =
{
    RP_AOK, 'a', 'd', 'd', 'r', 'e', 's', 's', ' ', 'o', 'k', '\0'
},
	rp_gdtxt =
{
    RP_MOK, 't', 'e', 'x', 't', ' ', 's', 'e', 'n', 't', ' ', 'o', 'k', '\0'
},
	rp_noop =
{
    RP_NOOP, 's', 'u', 'b', '-', 'l', 'i', 's', 't', ' ', 'n', 'o', 't', ' ',
    's', 'p', 'e', 'c', 'i', 'a', 'l', '\0'
};

/**/

qu2ls_send ()             /* overall mngmt for batch of msgs    */
{
    int 	started = FALSE;
    short       result;
    char        info[LINESIZE];

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "qu2ls_send ()");
#endif

    if (rp_isbad (result = qu_pkinit ()))
	return (result);

    /*
     *  While there are messages to process ...
     */
    for(;;){                /* get initial info for new message   */
	result = qu_rinit (info, sender, chanptr -> ch_apout);

	switch(rp_gval(result)) {

	    case RP_NS:
	    case RP_FIO:
		qu_rend();
		continue;

	    case RP_OK:
		break;

	    default:
		goto done;
	}

	/* make sure submit is running */

	if ((!started) && (rp_isbad(mm_init()) || rp_isbad(mm_sbinit()))) {
	    /* quit... */
	    ll_log(logptr, LLOGTMP, "couldn't start submit");
	    goto done;
	}
	started = TRUE;

	switch (rp_gval (result = qu2ls_each())) {

	    case RP_OK:
	    case RP_MECH:
		break;

	    case RP_BTNO:	/*  submit was difficult */
		started = FALSE;
		break;

	    case RP_BNO:	/* deliver was difficult */
	    default:
		goto done;
	}

	qu_rend();
    }

done:
    qu_rend();

    if (rp_gval (result) != RP_DONE) {
	ll_log (logptr, LLOGFAT, "not DONE (%s)", rp_valstr (result));
	return (RP_RPLY);         /* catch protocol errors              */
    }

    if (started) {
	mm_sbend();
	mm_end(OK);
    }

    qu_pkend ();                  /* done getting messages              */
    return (result);
}
/**/

LOCFUN
	qu2ls_each ()             /* send copy of text per address      */
{
    struct rp_bufstruct thereply;
    short   result;
    int     len;
    char    host[ADDRSIZE];
    char    adr[ADDRSIZE];
    char    *retadr;
    char    *p;
    int     ndone=0;

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "qu2ls_each()");
#endif

    nadrs = 0;

    /*
     *  For every address in the message ...
     */
    while( 1 ) {
	result = qu_radr (host, adr);
	if (rp_isbad (result))
	    return (RP_BNO);      /* get address from Deliver           */

	switch (rp_gval (result))
	{
	    case RP_HOK:          /* end of sub-list */
		qu_wrply ((RP_Buf *)&rp_noop, sizeof rp_noop);
		break;

	    case RP_DONE:         /* end of full address list           */
		if (ndone != 0)
		    qu2ls_txtcpy(&thereply);
		else
		    blt((char *)&rp_bdrem,(char *)&thereply,sizeof rp_bdrem);

		qu_wrply(&thereply, sizeof (thereply.rp_val) +
		    strlen (thereply.rp_line));

		if (ndone == 0 || rp_isbad(thereply.rp_val)) {
		    mm_end(NOTOK);
		    return(RP_BTNO);
		}
		return (RP_OK);         /* END for this message */

	    default:            /* actually have an address */
		/* Strip host reference if there */
		if (p = rindex (adr, '@'))
		    *p = '\0';

		if (nadrs++ == 0)
		{
		    if ((retadr = findreturn(adr)) == (char *)NULL)
			retadr = sender;

		    /* should allow W to be passed here */
		    if (rp_isbad (mm_winit (chanptr->ch_name, "tlzvm", retadr))
			  || rp_isbad (mm_rrply(&thereply, &len))) {
			mm_end (NOTOK);
			printx ("Error in submit startup\n");
			fflush (stdout);
			qu_wrply((RP_Buf *)&rp_bdrem, sizeof rp_bdrem);
			return(RP_BTNO);
		    }

		    switch (rp_gbval (thereply.rp_val)) {
		    case RP_BNO:
		    case RP_BTNO:
			mm_end (NOTOK);
			printx ("Error in submit startup (%s)\n", thereply.rp_line);
			fflush (stdout);
			qu_wrply(&thereply, len);
			return(RP_BTNO);
		    }
		    if (rp_isbad (mm_wadr ("", adr))
			 || rp_isbad (mm_rrply(&thereply, &len))) {
			mm_end (NOTOK);
			printx ("Error in submit startup\n");
			fflush (stdout);
			qu_wrply((RP_Buf *)&rp_bdrem, sizeof rp_bdrem);
			return(RP_BTNO);
		    }
		}
		else
		{
		    if (rp_isbad (mm_wadr ("", adr))
			   || rp_isbad (mm_rrply(&thereply, &len)))
		    {
			mm_end (NOTOK);
			printx ("Error in passing address\n");
			fflush (stdout);
			qu_wrply((RP_Buf *)&rp_bdrem, sizeof rp_bdrem);
			return(RP_BTNO);
		    }
		}

		switch (rp_gval (thereply.rp_val))
		{                 /* was address acceptable?            */
		    case RP_AOK:
		    case RP_DOK:
			qu_wrply((RP_Buf *)&rp_adr, sizeof  rp_adr);
			ndone++;
			continue;

		    case RP_PARM:
		    case RP_USER:
		    case RP_NO:
			break;    /* report failure and continue        */

		    case RP_RPLY:
			ll_log (logptr, LLOGTMP, "unusual return: (%s)%s",
				rp_valstr (thereply.rp_val), thereply.rp_line);
			break;    /* notify deliver */
		}
		/* tell deliver about all this */
		qu_wrply (&thereply, len);
	} /* end switch */
    }
    /* NOTREACHED */
}

/**/

LOCFUN
	qu2ls_txtcpy (rp)     /* copy the text of the message       */
RP_Buf *rp;
{
    int       len;
    short     result;
    char      buffer[BUFSIZ];

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "qu2ls_txtcpy()");
#endif

    qu_rtinit (0L);

    if (rp_isbad (result = mm_waend())) {
	blt ((char *)&rp_bdrem, (char *) rp, sizeof rp_bdrem);
	return;
    }

    printx ("sending...:");
    fflush (stdout);

    len = sizeof(buffer);
    while ((rp_gval (result = qu_rtxt (buffer, &len))) == RP_OK)
    {
	result = mm_wtxt (buffer, len);
	if (rp_isbad (result))
	    break;
	printx (".");
	fflush (stdout);
	if (rp_gval (result) != RP_OK) {
	    blt ((char *)&rp_bdrem, (char *) rp, sizeof rp_bdrem);
	    return;
	}
	len = sizeof(buffer);
    }

    if (rp_isbad (result) || rp_gval (result) != RP_DONE) {
	blt ((char *)&rp_bdrem, (char *) rp, sizeof rp_bdrem);
	return;
    }

    if (rp_isbad(mm_wtend()) || rp_isbad(mm_rrply(rp,&len))) {
	rp->rp_val = RP_RPLY;
	strcpy(rp->rp_line,"Unknown problem");
	return;
    }

    blt ((char *)&rp_gdtxt, (char *)rp, sizeof rp_gdtxt);
}

/********************/

char *
findreturn (adr)
char *adr;
{
    extern struct passwd *getpwmid();
    char newretadr[ADDRSIZE];
    char *msg = "Changing return address to '%s'\n";
    char *key;
    char *cp;

    printx ("\n\t");
    key = multcat (adr, "-request", (char *)0);

    if (aliasfetch(TRUE, key, newretadr, 0) == OK)
	goto done;
    if (getpwmid(key) != NULL)
	goto done;

    free (key);
    key = strdup (adr);
    if ((cp = rindex (key, '-')) && lexequ (cp, "-outbound")) {
	*cp = 0;
	cp = key;
	key = multcat (key, "-request", (char *)0);
	free (cp);
	if (aliasfetch(TRUE, key, newretadr, 0) == OK)
	    goto done;
	if (getpwmid(key) != NULL)
	    goto done;
    }
    free (key);

    /* If address goes thru list channel then take charge */
    key = multcat ("List Manager <", supportaddr, ">", (char *)0);
    msg = "Using default return address '%s'\n";
done:
    printx (msg, key);
    fflush (stdout);
    return (key);
}
 rp_isbad(thereply.rp_val)) {
		    mm_end(NOTOK);
		    return(RP_BTNO);
		}
		return (RP_OK);mmdf/src/list/gen   555      0     12          57  3620510544   7153 make -f ../../Makefile.com -f Makefile.real $*
) {
	    blt ((char *)&rp_bdrem, (char *) rp, sizeof rp_bdrem);
	    return;
	}
	len = sizeof(buffer);
    }

    if (rp_isbad (result) || rp_gval (result) != RP_DONE) {
	blt ((char *)&rp_bdrem, (char *) rp, sizeof rp_bdrem);
	return;
    }

    if (rp_isbad(mm_wtend()) || rp_isbad(mm_rrply(rp,&len))) {
	rp->rp_val = RP_RPLY;
	strcpy(rp->rp_line,"Unknown problem");
	return;
    }

    blt ((char *)&rp_gdtxt, (char *)rp, sizeof rp_gdtxt);
}

/*******************mmdf/src/list/Makefile.real   444      0     12        2153  3635174122  11101 #
#  List Channel
#

MODULES =	ch_list qu2ls_send

OBJECTS =	ch_list.o qu2ls_send.o

SOURCES =	ch_list.c qu2ls_send.c

real-default:	list

install:	$(CHANDIR)/list

$(CHANDIR)/list  :   xlist
	     cp xlist $(CHANDIR)/list
	    -$(CHOWN) $(MMDFLOGIN) $(CHANDIR)/list
	    -chmod 0$(PGMPROT) $(CHANDIR)/list
	    -@ls -ls $(CHANDIR)/list
	    -@echo "list channel installed normally"; echo ""

list:	xlist
xlist:	$(OBJECTS) $(MMDFLIBS)
	$(CC) $(LDFLAGS) -o $@ $(OBJECTS) $(MMDFLIBS) $(SYSLIBS)

lint:   ;  $(LINT) $(LFLAGS) $(SOURCES) $(LLIBS)

clean:
	-rm -f xlist *.o x.c makedep eddep make.out errs


# DO NOT DELETE THIS LINE -- make depend uses it
ch_list.o: ch_list.c
ch_list.o: ch_list.c
ch_list.o: ../../h/util.h
ch_list.o: ../../h/mmdf.h
ch_list.o: /usr/include/signal.h
ch_list.o: ../../h/phs.h
ch_list.o: ../../h/ch.h
qu2ls_send.o: qu2ls_send.c
qu2ls_send.o: ../../h/util.h
qu2ls_send.o: ../../h/mmdf.h
qu2ls_send.o: ../../h/phs.h
qu2ls_send.o: ../../h/ch.h
qu2ls_send.o: ../../h/ap.h
qu2ls_send.o: /usr/include/pwd.h
# DEPENDENCIES MUST END AT END OF FILE
# IF YOU PUT STUFF HERE IT WILL GO AWAY
# see make depend above
2ls_send.c

real-default:	list

install:	$(CHANDIR)/list

$(CHANDIR)/list  :   xlist
	     cp xlist $(CHANDIR)/list
	    -$(CHOWN) $(MMDFLOGIN) $(CHANDIR)/list
	    -chmod 0$(PGMPROT) $(CHANDIR)/list
	    -@ls -ls $(CHANDIR)/list
	    -@echo "list channel installed normally"; echo ""

list:	xlist
xlist:	$(OBJECTS) $(MMDFLIBS)
	$(CC) $(LDFLAGS) -o $@ $(OBJECTS) $(MMDFLIBS) $(SYSLIBS)

lint:   ;  $(LINT)mmdf/src/list/ch_list.c   444      0     12        6306  3620510545  10311 #include "util.h"
#include "mmdf.h"
/*
 *     MULTI-CHANNEL MEMO DISTRIBUTION FACILITY  (MMDF)
 *     
 *
 *     Copyright (C) 1979,1980,1981  University of Delaware
 *     
 *     Department of Electrical Engineering
 *     University of Delaware
 *     Newark, Delaware  19711
 *
 *     Phone:  (302) 738-1163
 *     
 *     
 *     This program module was developed as part of the University
 *     of Delaware's Multi-Channel Memo Distribution Facility (MMDF).
 *     
 *     Acquisition, use, and distribution of this module and its listings
 *     are subject restricted to the terms of a license agreement.
 *     Documents describing systems using this module must cite its source.
 *
 *     The above statements must be retained with all copies of this
 *     program and may not be removed without the consent of the
 *     University of Delaware.
 *     
 *
 *     version  -1    David H. Crocker    March   1979
 *     version   0    David H. Crocker    April   1980
 *     version  v7    David H. Crocker    May     1981
 *     version   1    David H. Crocker    October 1981
 *
 */

/*                  LIST PROCESSING CHANNEL                             */

#include <signal.h>
#include "phs.h"
#include "ch.h"

extern LLog *logptr;

Chan *chanptr;
char obuf[BUFSIZ];		/* Buffer for status printfs */

/*      MAIN MAIN MAIN MAIN MAIN MAIN MAIN MAIN MAIN MAIN MAIN MAIN     */

main (argc, argv)
int       argc;
char   *argv[];
{
    char *dupfpath ();
    short retval;

    mmdf_init (argv[0]);
    setbuf( stdout, obuf );

#ifdef RUNALON
    logptr -> ll_fd = 1;
    ll_init (logptr);
#endif

    siginit ();
    signal (SIGINT, SIG_IGN);     /* always ignore interrupts             */

    if ((chanptr = ch_nm2struct (argv[0])) == (Chan *) NOTOK)
    {
	ll_log (logptr, LLOGTMP, "listprocessor (%s) unknown channel", argv[0]);
	exit (RP_PARM);
    }
    retval = ch_list (argc, argv);
    ll_close (logptr);
    exit (retval);
}
/***************  (ch_) MAILING LIST DELIVERY  ******************** */

ch_list (argc, argv)              /* send to internet                   */
int       argc;
char   *argv[];
{
    ch_llinit (chanptr);
#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "ch_list()");
#endif

    if (rp_isbad (qu_init (argc, argv)))
	return (RP_NO);		  /* problem setting-up for deliver     */
    
    phs_note (chanptr, PHS_WRSTRT);

    if (rp_isbad (qu2ls_send ()))
	return (RP_NO);		  /* send the batch of outgoing mail    */

    phs_note (chanptr, PHS_WREND);

    qu_end (OK);                  /* done with Deliver function         */

    return (RP_OK);		  /* NORMAL RETURN                      */
}

/**/

/* VARARGS2 */
err_abrt (code, fmt, b, c, d)     /* terminate ourself                  */
short     code;
char    fmt[],
        b[],
        c[],
        d[];
{
#ifdef DEBUG
    char linebuf[LINESIZE];
#endif

    qu_end (NOTOK);

#ifdef DEBUG
    if (rp_gbval (code) == RP_BNO || logptr -> ll_level >= LLOGBTR)
    {                         /* don't worry about minor stuff      */
	sprintf (linebuf, "%s%s", "err [ ABEND (%s) ]\t", fmt);
	ll_log (logptr, LLOGFAT, linebuf, rp_valstr (code), b, c, d);
	abort ();
    }
#endif
    ll_close (logptr);            /* in case of cycling, close neatly   */

    exit (code);
}
ctrical Engineering
 *     University of Delaware
 *     Newark, Delaware  19711
 *
 *     Phone:  (302) 738-1163
 *     
 *     
 *     This program module was developed as part of the University
 *     of Delaware's Multi-Channel Memo Distribution Facility (MMDF).
 *     
 *     Acquisition, use, and distributimmdf/src/bboards/   755      0     12           0  3657740066   7106 mmdf/src/bboards/Makefile.real   444      0     12        3446  3653247035  11554 #
#   bboards:	BBoards delivery channel transmission
#
MODULES =	ch_bboards qu2bb_send bb_wtmail

OBJECTS =	ch_bboards.o qu2bb_send.o bb_wtmail.o

SOURCES =	ch_bboards.c qu2bb_send.c bb_wtmail.c \
		getbbent.c dropsbr.c lock.c r1bindex.c strindex.c uprf.c \
		mmdfonly.c

ZOTLIBS	=	getbbent.o dropsbr.o lock.o r1bindex.o strindex.o uprf.o \
		mmdfonly.o

.c.o:;		$(CC) $(CFLAGS) -DMMDFONLY -c $*.c

real-default:	bboards

install:	$(CHANDIR)/$(MMPREF)bboards

$(CHANDIR)/$(MMPREF)bboards:	xbboards
		-cp $(CHANDIR)/$(MMPREF)bboards zxbboards
		-chmod 0$(PGMPROT) zxbboards
		cp xbboards $(CHANDIR)/$(MMPREF)bboards
		-$(CHOWN) root $(CHANDIR)/$(MMPREF)bboards
		-chmod 04$(PGMPROT) $(CHANDIR)/$(MMPREF)bboards
		-@ls -ls $(CHANDIR)/$(MMPREF)bboards
		-@echo "BBoards installed normally"; echo ""

bboards:	xbboards
xbboards:	$(OBJECTS) $(MMDFLIBS) $(ZOTLIBS)
		$(CC) $(LDFLAGS) -o $@ $(OBJECTS) $(MMDFLIBS) $(SYSLIBS) \
			$(ZOTLIBS)

lint:;		$(LINT) $(LFLAGS) -DMMDFONLY $(LLIBS) $(SOURCES)

clean:;		rm -f zxbboards xbboards *.o x.c makedep eddep make.out errs


# DO NOT DELETE THIS LINE -- make depend uses it
# DEPENDENCIES MUST END AT END OF FILE
ch_bboards.o: ch_bboards.c
ch_bboards.o: ../../h/util.h
ch_bboards.o: ../../h/mmdf.h
ch_bboards.o: /usr/include/signal.h
ch_bboards.o: ../../h/ch.h
ch_bboards.o: ../../h/phs.h
qu2bb_send.o: qu2bb_send.c
qu2bb_send.o: ../../h/util.h
qu2bb_send.o: ../../h/mmdf.h
qu2bb_send.o: ../../h/phs.h
qu2bb_send.o: ../../h/ch.h
bb_wtmail.o: bb_wtmail.c
bb_wtmail.o: ../../h/util.h
bb_wtmail.o: ../../h/mmdf.h
bb_wtmail.o: bboards.h
bb_wtmail.o: ../../h/cnvtdate.h
bb_wtmail.o: ../../h/ch.h
bb_wtmail.o: ../../h/phs.h
bb_wtmail.o: /usr/include/pwd.h
bb_wtmail.o: /usr/include/sys/stat.h
# DEPENDENCIES MUST END AT END OF FILE
# IF YOU PUT STUFF HERE IT WILL GO AWAY
# see make depend above
annel", argv[0]);
	exit (RP_PARM);
    }
    retval = ch_list (argc, argv);
    ll_close (logptr);
    exit (retval);
}
/***************  (ch_) MAILING LIST DELIVERY  ******************** */

ch_list (argc, argv)     mmdf/src/bboards/READ-ME   444      0     12        2040  3653247035  10114 0.  Replace the directory ${MMDF-II-SOURCE-ROOT}/src/bboards/ with
    the directory ${MH-SOURCE-ROOT}/support/bboards/mmdfII/bboards/.
    The sources for the BBoards channel that come with MH are always
    much more current than the ones that come with MMDF-II.


1.  In ${MMDF-II-SOURCE-ROOT}/src/Makefile, add bboards to SUBDIR, so it
    looks something like:

    SUBDIR= tools uip submit deliver local phone pobox bboards smtp uucp


2.  In mmdftailor, add these lines

MTBL	name=cbboards, file="c-bboards"
MTBL	name=dbboards, file="d-bboards", show="BBOARDS pseudo-Domain"
MCHN	bboards, show="BBoards Delivery",
	que=bboards, tbl=cbboards, pgm=bboards, mod=reg, ap=822
MDMN	bboards, tbl=dbboards, name="", dmn=""


3.   Create the table for the BBoards domain, one line:

	bboards:		bboards


4.   Create the table for the BBoards channel, one line:
	bboards:		bboards


5.    Run dbmbuild


6.    In your mmdf.start file, either add the bboards channel to an
      existing background deliver, or give it one of its own:

	deliver -b -cbboards &
t errs


# DO NOT DELETE THIS LINE -- make depend uses it
# DEPENDENCIES MUST END AT END OF FILE
ch_bboards.o: ch_bboards.c
ch_bboards.o: ../../h/util.h
ch_bboards.o: ../../h/mmdf.h
ch_bboards.o: /usr/include/signal.h
ch_bboards.o: ../../h/ch.h
ch_bboards.o: ../../h/phs.h
qu2bb_send.o: qu2bb_send.c
qu2bb_send.o: ../../h/util.h
qu2bb_send.o: ../../h/mmdf.h
qu2bb_send.o: ../../h/phs.h
qu2bb_send.o: ../../h/ch.h
bb_wtmail.o: bb_wtmail.c
bb_wtmail.o: ../../h/util.h
bb_wtmail.o: .mmdf/src/bboards/bb_wtmail.c   444      0     12       40066  3657737720  11326 #ifndef	POP
/* bb_wtmail.c - write mail to a BBoard */
#else	POP
/* po_wtmail.c - write mail for a POP subscriber */
#endif	POP


#include "util.h"
#include "mmdf.h"
#include "bboards.h"
#include "cnvtdate.h"
#include "ch.h"
#include "phs.h"
#include <pwd.h>
#include <sys/stat.h>

/*  */

#ifndef	RP_DOK
#define	submitopts	"vmth%s*"
#else	RP_DOK
#define	submitopts	"vkmth%s*"
#endif	RP_DOK

#ifndef	POP
#define	RP_NOPE	RP_AOK

#define	MBXMODE	BBMODE
#else	POP
#define	RP_NOPE	RP_USER

#define	MBXMODE	sentprotect

extern int   sentprotect;
#endif	POP


int	err_fd = NOTOK;

int     ds_address ();

extern int  errno;

int    bbrduid, bbrdgid;

char   *chnlname,
        chnlinfo[LINESIZE];
#ifndef	POP
char	bbrdaddr[LINESIZE],
        bbrdfrom[LINESIZE],
        bbrdheader[LINESIZE],
	bbrdhome[LINESIZE],
        bbrdtime[LINESIZE];
#endif	not POP

extern char *qu_msgfile,
            *delim1,
            *delim2,
	    *lckdfldir,
            *locname,
	    *sitesignature,
	    *supportaddr;

struct bboard  *curbb;

extern LLog *logptr;

FILE *lk_fopen();

long    lseek ();
char   *index (), *rindex (), *sprintf ();
struct passwd  *getpwnam ();

/*  */

bb_init (chanptr)
Chan * chanptr;
{
    int     uid,
            eid;
    struct passwd *pw;

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "bb_init(chanptr=%s)", chanptr -> ch_name);
#endif

    chnlname = chanptr -> ch_name;
#ifndef	notdef
    sprintf (chnlinfo, submitopts, chnlname);
#else	notdef			/* the following is probably a BAD idea */
    if (chanptr -> ch_host == NULL)
	chnlinfo[0] = NULL;	/* local delivery ONLY */
    else
	sprintf (chnlinfo, submitopts, chanptr -> ch_host);
#endif	notdef

#ifndef	POP
    if ((pw = getpwnam (BBOARDS)) == NULL)
	err_abrt (RP_BHST, "no passwd entry for '%s'", BBOARDS);
#else	POP
    if ((pw = getpwnam (POPUID)) == NULL)
	err_abrt (RP_BHST, "no passwd entry for '%s'", POPUID);
#endif	POP

    bbrduid = pw -> pw_uid;
    bbrdgid = pw -> pw_gid;
#ifndef	POP
    sprintf (bbrdfrom, "%s@%s", pw -> pw_name, locname);
#ifdef DEBUG
    ll_log (logptr, LLOGGEN, "distributing as '%s'", bbrdfrom);
#endif
    sprintf (bbrdhome, pw -> pw_dir);
#endif	not POP

#ifndef	POP
    if (!setbbent ())
	err_abrt (RP_BHST, "setbbent() failed");
#else	POP
    if (!setpwinfo (pw, POPDB, 1))
	err_abrt (RP_BHST, "setbbinfo(%s, %s, 1) failed",
		pw -> pw_name, POPDB);
#endif	POP

    getwho (&uid, &eid);
    if (eid != 0)
	err_abrt (RP_BHST, "not running as root");

    return RP_OK;
}


bb_end (result)
short   result;
{
#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "bb_end(result=0%o)", result);
#endif

    return RP_OK;
}

/*  */

bb_sbinit () {
#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "bb_sbinit()");
#endif

    return RP_OK;
}


bb_sbend () {
#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "bb_sbend()");
#endif

    return RP_OK;
}

/*  */

bb_winit (info, sender)
char   *info,
       *sender;
{
#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "bb_winit(info='%s',sender='%s')",
	    info, sender);
#endif

    return RP_OK;
}

/*  */

bb_wtadr (host, adr)
char   *host,
       *adr;
{
    short   count,
            result;
    int     len,
	    md,
            offset,
	    size;
    long    start,
            stop,
            pos;
    char   *cp,
            buffer[BUFSIZ];

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "bb_wtadr(host=%s,adr=%s)", host, adr);
#endif

    if ((cp = index (adr, '@')) != NULL)
	*cp = NULL;
    make_lower (adr, adr);
    if ((curbb = getbbnam (adr)) == NULL)
	return RP_USER;
#ifndef	POP
    sprintf (bbrdaddr, "local-%s-request@%s", curbb -> bb_name, locname);
#endif	not POP
#ifdef DEBUG
    ll_log (logptr, LLOGGEN, "=> BBoard %s: file='%s' info='%s'",
	    curbb -> bb_name, curbb -> bb_file, curbb -> bb_info);
#endif

    if (curbb -> bb_file == NULL || *curbb -> bb_file == NULL)
	return RP_NOPE;
#ifdef DEBUG
    ll_log (logptr, LLOGGEN, "begin local delivery...");
#endif
    printx ("\r\nperforming local delivery to file %s...\n",
	    curbb -> bb_file);

    qu_rtinit (0L);

    if ((md = mbx_open (curbb -> bb_file, bbrduid, bbrdgid, MBXMODE)) == NOTOK)
	return RP_FIO;

#ifndef	POP
    if (rp_isbad (result = mbx_init ())) {
	mbx_close (curbb -> bb_file, md);
	return result;
    }
#endif	not POP

    pos = lseek (md, 0L, 1);
    count = strlen (delim1);
    if (write (md, delim1, count) != count) {
	ll_log (logptr, LLOGTMP, "error writing delim1");
	result = NOTOK;
	goto clean_up;
    }
    start = lseek (md, 0L, 1);
    size = 0;

#ifndef	POP
    count = strlen (bbrdheader);
    if (write (md, bbrdheader, count) != count) {
	ll_log (logptr, LLOGTMP, "error writing BBoard information");
	result = NOTOK;
	goto clean_up;
    }
    for (cp = bbrdheader; *cp; cp++, size++)
	if (*cp == '\n')
	    size++;
#endif	not POP

    for (len = BUFSIZ;
	    rp_gval (result = qu_rtxt (buffer, &len)) == RP_OK;
	    len = BUFSIZ) {
	for (offset = 0;
		(offset = strindex (delim1, buffer)) >= 0;
		buffer[offset]++)
	    continue;
	for (offset = 0;
		(offset = strindex (delim2, buffer)) >= 0;
		buffer[offset]++)
	    continue;
	if (write (md, buffer, len) != len) {
	    ll_log (logptr, LLOGTMP, "error writing to file '%s'",
		    curbb -> bb_file);
	    result = NOTOK;
	    goto clean_up;
	}
	for (offset = 0, cp = buffer; offset < len; offset++, size++)
	    if (*cp++ == '\n')
		size++;
    }

    if (result < 0)
	ll_log (logptr, LLOGTMP, "error reading from message file '%s'",
		qu_msgfile);
clean_up: ;

    stop = lseek (md, 0L, 1);
    count = strlen (delim2);
    if (write (md, delim2, count) != count)
	ll_log (logptr, LLOGTMP, "error writing delim2");
    map_write (curbb -> bb_file, md, curbb -> bb_maxima, start, stop, pos,
	size, 0);
#ifdef DEBUG
    ll_log (logptr, LLOGGEN, "end local delivery...");
#endif

    if (result < 0)
	mbx_close (curbb -> bb_file, md);
    else
	result = mbx_close (curbb -> bb_file, md);

    return (result != NOTOK ? RP_OK : RP_FIO);
}

/*  */

bb_txtcpy () {
#ifndef	POP
    short   result;

#ifdef	DEBUG
    ll_log (logptr, LLOGBTR, "bb_txtcpy()");
#endif

    if (curbb -> bb_dist == NULL
	    || *curbb -> bb_dist == NULL
	    || chnlinfo[0] == NULL)
	return RP_MOK;
#ifdef DEBUG
    ll_log (logptr, LLOGGEN, "begin distribution...");
#endif
    if (curbb -> bb_file == NULL || *curbb -> bb_file == NULL)
	printx ("\r\n");
    printx("\rperforming remote distribution\n");

    if (rp_isbad (result = dist_init ())
	    || rp_isbad (result = dist_adrs ())
	    || rp_isbad (result = dist_text ())
	    || rp_isbad (result = dist_end ()))
	return dist_lose (result);
#ifdef DEBUG
    ll_log (logptr, LLOGGEN, "end distribution...");
#endif

    if (err_fd != NOTOK)
	dist_lose (RP_MOK);
    else
	printx ("\rmessage distributed\n");
#endif	not POP

    return RP_MOK;
}

/*  */

#ifndef	POP
/* **************** (dist_)  BBOARD DISTRIBUTION **************** */

dist_init () {
    short   result;
#ifdef	RP_NS
    int	    len;
    struct rp_bufstruct reply;
#endif	RP_NS

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "dist_init()");
#endif

    if (rp_isbad (result = mm_init ()))
	return ds_log (result, LLOGFAT, "mm_init() failed [%s]",
		rp_valstr (result));
    if (rp_isbad (result = mm_sbinit ()))
	return ds_log (result, LLOGFAT, "mm_sbinit() failed [%s]",
		rp_valstr (result));
    if (rp_isbad (result = mm_winit (chnlname, chnlinfo, bbrdaddr)))
	return ds_log (result, LLOGFAT,
		"mm_winit('%s','%s','%s') failed [%s]",
		chnlname, chnlinfo, bbrdaddr, rp_valstr (result));
#ifdef	RP_NS
	if (rp_isbad (result = mm_rrply (&reply, &len)))
	    return ds_log (result, LLOGFAT, "problem with sender address [%s]",
		    rp_valstr (result));
#endif	RP_NS

    return result;
}

/*  */

dist_adrs ()
{
    short   result;

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "dist_adrs()");
#endif

    if (getbbdist (curbb, ds_address))
	return ds_log (RP_NO, LLOGTMP, "getbbdist failed: %s", getbberr ());

    if (rp_isbad (result = mm_waend ()))
	return ds_log (result, LLOGFAT, "mm_waend() failed [%s]",
		rp_valstr (result));

    return result;
}

/*  */

ds_address (addr, host)
char *addr,			/* local part */
     *host;			/* rest */
{
    short   result;
    int     len;
    struct rp_bufstruct reply;

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "ds_address(addr='%s',host='%s')", addr, host);
#endif

    printx ("\rperforming distribution to %s@%s...\n", addr, host);
    if (rp_isbad (result = mm_wadr (host, addr))) {
	ds_log (result, LLOGFAT, "mm_wadr('%s','%s') failed [%s]",
		host, addr, rp_valstr (result));
	return NOTOK;
    }
    if (rp_isbad (result = mm_rrply (&reply, &len))) {
	ds_log (result, LLOGFAT,
		"mm_rrply() failed [%s] getting status of '%s@%s'",
		rp_valstr (result), addr, host);
	return NOTOK;
    }

    switch (rp_gval (reply.rp_val)) {
	case RP_AOK:
#ifdef	RP_DOK
	case RP_DOK:
#endif	RP_DOK
#ifdef DEBUG
	    ll_log (logptr, LLOGGEN, "address '%s@%s' [%s] -- %s",
		    addr, host, rp_valstr (reply.rp_val), reply.rp_line);
#endif
	    return OK;

	case RP_NO:
#ifdef	RP_NS
	case RP_NS:
#endif	RP_NS
	case RP_USER:
	case RP_NDEL:
	case RP_AGN:
	case RP_NOOP:
	    ds_log (reply.rp_val, LLOGTMP, "address '%s@%s' [%s] -- %s",
		    addr, host, rp_valstr (reply.rp_val), reply.rp_line);
	    return OK;		/* fail-soft */

	default:
	    ds_log (reply.rp_val, LLOGFAT, "unexpected reply [%s] -- %s",
		    rp_valstr (reply.rp_val), reply.rp_line);
	    return NOTOK;
    }
}

/*  */

dist_text ()
{
    short   result;
    int     len;
    char    buffer[BUFSIZ];

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "dist_text()");
#endif

    qu_rtinit (0L);
    for (len = BUFSIZ;
	    rp_gval (result = qu_rtxt (buffer, &len)) == RP_OK;
	    len = BUFSIZ)
	if (rp_isbad (result = mm_wtxt (buffer, len)))
	    return ds_log (result, LLOGFAT, "mm_wtxt() failed [%s]",
		    rp_valstr (result));

    if (result < 0)
	return ds_log (RP_FIO, LLOGTMP,
		"error reading from message file '%s'", qu_msgfile);

    if (rp_isbad (result = mm_wtend ()))
	return ds_log (result, LLOGFAT, "mm_wtend() failed [%s]",
		rp_valstr (result));

    return result;
}

/*  */

dist_end ()
{
    short   result;
    int     len;
    struct rp_bufstruct reply;

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "dist_end()");
#endif

    if (rp_isbad (result = mm_rrply (&reply, &len)))
	return ds_log (result, LLOGFAT,
		"mm_rrply() failed [%s] getting final status",
		rp_valstr (result));

    switch (rp_gval (reply.rp_val)) {
	case RP_OK:
	case RP_MOK:
#ifdef DEBUG
	    ll_log (logptr, LLOGGEN, "message [%s] -- %s",
		    rp_valstr (reply.rp_val), reply.rp_line);
#endif
	    mm_sbend ();
	    mm_end (OK);
	    return result;

	case RP_NO:
	case RP_NDEL:
	case RP_AGN:
	case RP_NOOP:
	    return ds_log (RP_NO, LLOGTMP, "not delivered [%s] -- %s",
		    rp_valstr (reply.rp_val), reply.rp_line);

	default:
	    return ds_log (RP_RPLY, LLOGFAT,
		    "unexpected final reply [%s] -- %s",
		    rp_valstr (reply.rp_val), reply.rp_line);
    }
}

/*  */

dist_lose (result)
short   result;
{
    int     i;
    char   *cp,
            intro[BUFSIZ],
            buffer[BUFSIZ];

#ifdef	DEBUG
    ll_log (logptr, LLOGBTR, "dist_lose(result=0%o)", result);
#endif	DEBUG

    mm_end (NOTOK);

    printx ("\rerrors during distribution: ");
    if (domsg)
	(void) fflush (stdout);
    (void) sprintf (intro, "bboards%d distribution for %s failed [%s]\n",
	    getpid (), curbb -> bb_name, rp_valstr (result));
    if (loseaux (bbrdaddr, bbrdfrom, intro) != OK
	    && loseaux (bbrdfrom, (char *) 0, intro) != OK) {
	printx ("unable to post advisory.\n");
	ll_log (logptr, LLOGFAT, "unable to post failure notice");
	if (err_fd != NOTOK) {
	    (void) lseek (err_fd, 0L, 0);
	    if ((i = read (err_fd, buffer, sizeof buffer)) > 0) {
		buffer[i] = NULL;
		if (cp = index (buffer, '\n'))
		    *cp = NULL;
		ll_log (logptr, LLOGFAT, "info: %s", buffer);
	    }
	}
	if (loseaux (supportaddr, (char *) 0, intro) != NOTOK)
	    ll_log (logptr, LLOGFAT, "unable to advise %s of failure!",
		    supportaddr);
    }
    else
	printx ("advisory posted.\n");
    if (domsg)
	(void) fflush (stdout);

    if (err_fd != NOTOK) {
	close (err_fd);
	err_fd = NOTOK;
    }
    return RP_MOK;
}

/*  */

int	loseaux (to, cc, intro)
char   *to,
       *cc,
       *intro;
{
    int     i;
    char    buffer[BUFSIZ];

    if (ml_init (NO, NO, sitesignature, "Re-distribution Failure") != OK
	    || ml_adr (to) != OK)
	return NOTOK;
    if (cc && (ml_cc () != OK || ml_adr (cc) != OK))
	return NOTOK;
    if (ml_aend () != OK || ml_tinit () != OK)
	return NOTOK;

    ml_txt (intro);
    if (err_fd != NOTOK) {
	lseek (err_fd, 0L, 0);
	while ((i = read (err_fd, buffer, sizeof buffer)) > 0) {
	    buffer[i] = NULL;
	    ml_txt (buffer);
	}
    }
    encap ();

    return ml_end (OK);
}

/*  */

/* very similar to sbr/cpydgst.c */

#define	S1	0
#define	S2	1

#define	output(c)	if (bp >= dp) {flush (); *bp++ = c;} else *bp++ = c
#define	flush()		if (bp - outbuf) \
			    *bp = NULL, ml_txt (outbuf), bp = outbuf

static  encap () {
    register int    state;
    short   result;
    int     len,
	    init;
    register char  *cp,
                   *ep;
    char    buffer[BUFSIZ];
    register char  *bp,
                   *dp;
    char    outbuf[BUFSIZ];

    qu_rtinit (0L);

    dp = (bp = outbuf) + sizeof outbuf;
    init = 0;
    for (state = S1, len = BUFSIZ;
	    rp_gval (result = qu_rtxt (buffer, &len)) == RP_OK;
	    len = BUFSIZ)
	for (ep = (cp = buffer) + len; cp < ep; cp++) {
	    if (*cp == NULL)
		continue;
	    switch (state) {
		case S1: 
		    if (*cp == '-') {
			if (init == 0) {
			    ml_txt ("\n------- Forwarded Message\n\n");
			    init++;
			}
			output ('-');
			output (' ');
		    }
		    state = S2;	/* fall */

		case S2: 
		    if (init == 0) {
			ml_txt ("\n------- Forwarded Message\n\n");
			init++;
		    }
		    output (*cp);
		    if (*cp == '\n')
			state = S1;
		    break;
	    }
	}

    flush ();

    if (result < 0) {
	ll_log (logptr, LLOGTMP, "error reading message when noting failure");
	if (init)
	    ml_txt ("\n------- End of Forwarded Message\n\n");
	ml_txt ("[ error reading message ]\n");
    }
    else
	if (init)
	    ml_txt ("\n------- End of Forwarded Message\n\n");
	else {
	    ll_log (logptr, LLOGTMP, "message empty when noting failure");
	    ml_txt ("[ message empty ]\n");
	}
}

/*  */

/* VARARGS3 */

ds_log (result, level, fmt, a, b, c, d, e)
short   result;
int     level;
char   *fmt,
       *a,
       *b,
       *c,
       *d,
       *e;
{
    int     i;
    char    buffer[BUFSIZ],
	    tmpfil[BUFSIZ];

    ll_log (logptr, level, fmt, a, b, c, d, e);

    sprintf (buffer, fmt, a, b, c, d, e);
    strcat (buffer, "\n");

    printx ("\rerror: %s", buffer);

    if (err_fd == NOTOK) {
	unlink (mktemp (strcpy (tmpfil, "/tmp/bboardsXXXXXX")));
	if ((err_fd = creat (tmpfil, 0600)) == NOTOK)
	    return result;
	close (err_fd);
	if ((err_fd = open (tmpfil, 2)) == NOTOK)
	    return result;
	unlink (tmpfil);
	lseek (err_fd, 0L, 0);
    }
    i = strlen (buffer);
    write (err_fd, buffer, i);

    return result;
}
#endif	not POP

/*  */

/* mbx_	    local mailbox routines */

#ifndef	POP
mbx_init () {
    int     fd,
            clear;
    char    name[BUFSIZ];
    FILE * fp;

    if ((fd = mbx_Xopen (curbb -> bb_info, bbrduid, bbrdgid, MBXMODE, &clear))
	    == NOTOK) {
	if (errno == ETXTBSY) {
	    printx ("\runable to lock %s\n", curbb -> bb_info);
	    ll_err (logptr, LLOGTMP, "unable to lock %s",
		    curbb -> bb_info);
	    return RP_LOCK;
	}
	printx ("\runable to open '%s'", curbb -> bb_info);
	ll_log (logptr, LLOGTMP, "unable to open '%s'", curbb -> bb_info);
	return RP_FOPN;
    }
    if ((fp = fdopen (fd, "w")) == (FILE *) NULL) {
	printx ("\runable to fdopen '%s'", curbb -> bb_info);
	ll_err (logptr, LLOGTMP, "unable to fdopen '%s'", curbb -> bb_info);
	mbx_close (curbb -> bb_info, fd);
	return RP_LIO;
    }

    strcpy (name, curbb -> bb_name);
    if ((curbb = getbbnam (name)) == (struct bboard *) NULL) {
	printx ("\runable to get information on BBoard %s\n", name);
	ll_err (logptr, LLOGFAT, "unable to get info on %s", name);
	lkfclose (fp, curbb -> bb_info);
	return RP_LIO;
    }
    sprintf (bbrdheader, "BBoard-ID: %d\nBB-Posted: %s\n",
	    ++curbb -> bb_maxima, cnvtdate (TIMREG, bbrdtime));
    fprintf (fp, "%d\n%s\n", curbb -> bb_maxima, bbrdtime);

    lkfclose (fp, curbb -> bb_info);

    return RP_OK;
}
#endif	not POP
e-distribution Failure") != OK
	    || ml_adr (to) != OK)
	return NOTOK;
    if (cc && (ml_cc () != OK || ml_adr (cc) != OK))
	return NOTOK;
    if (ml_aend () != OK || ml_tinit () != OK)
	return NOTOK;

    ml_txt (intro);
    if (err_fd != NOTOK) {
	lseek (err_fd, 0L, 0);
	while ((i = read (err_fd, buffer, sizeof buffer)) > 0) {
	    buffer[i] = NULL;
	    ml_txt (buffer);
	}
    }
    encap ();

    return ml_end (OK);
}

/*  */

/* very similar to smmdf/src/bboards/bboards.h   444      0     12        4427  3653247036  10760 /* bboards.h - definition of a BBoard structure */

#define	BBOARDS	"bboards"	/* name in /etc/passwd */
#define	BBDB	"BBoards"	/* file in BBOARDS' home directory */
#define	BBMODE	0644		/* default BBoards mode */
#define	DISTADR	"dist-"		/* prefix for distribution addresses */

#ifdef	POP
#define	POPUID	"pop"		/* name in /etc/passwd */
#define	POPDB	"POP"		/* file in POPUID's home directory */
#define	POMODE	0600		/* default POP subscriber maildrop mode */
#endif	POP

struct bboard {
    char   *bb_name;		/* name of the bboard */
    char  **bb_aka;		/* aliases for the bboards */

    char   *bb_file;		/* file it resides in */
    char   *bb_archive;		/* file where archives reside */
    char   *bb_info;		/* file where maxima resides */
    char   *bb_map;		/* file where binary map resides */

    char   *bb_passwd;		/* password for it */

    char  **bb_leader;		/* list of local leaders */

    char   *bb_addr;		/* network address */
    char   *bb_request;		/* network address for requests */
    char   *bb_relay;		/* host acting as relay in local domain */
    char  **bb_dist;		/* distribution list */

    unsigned int    bb_flags;	/* various flags */
#define	BB_NULL	0x0000
#define	BB_ARCH	0x0007		/* archive policy */
#define   BB_ASAV	0x0001	/*   save in archives/ directory */
#define	  BB_AREM	0x0002	/*   remove without saving */
#define BB_INVIS	0x0010	/* invisible to bbc */
#define	BB_REMOTE	0x0020	/* remote to bbc */
#define	BBITS	"\020\01ARCHIVE\02REMOVE\05INVIS\06REMOTE"

    union {			/* unassigned */
	unsigned int    un_count;
	long		un_mtime;
    } bb_un;
#define	bb_count	bb_un.un_count
#define	bb_mtime	bb_un.un_mtime
    
    unsigned int    bb_maxima;	/* highest BBoard-Id in it */
    char   *bb_date;		/* date that maxima was written */

    struct bboard *bb_next;	/* unassigned */
    struct bboard *bb_link;	/* unassigned */
    struct bboard *bb_chain;	/* unassigned */
};

				/* flags for setbbent () */
#define	SB_NULL	0x0000
#define	SB_STAY	0x0001		/*   stay open between calls */
#define	SB_FAST	0x0002		/*   fast parse of file */

void	make_lower ();
int     setbbent (), endbbent (), setbbfile (), setbbinfo (), setpwinfo (),
	ldrbb (), ldrchk (), getbbdist ();
long	getbbtime ();
char   *getbberr ();
struct bboard  *getbbent (), *getbbnam (), *getbbaka (), *getbbcpy();
> bb_info);
	return RP_FOPN;
    }
    if ((fp = fdopen (fd, "w")) == (FILE *) NULL) {
	printx ("\runable to fdopen '%s'", curbb -> bb_info);
	ll_err (logptr, LLOGTMP, "unable to fdopen '%s'", curbb -> bb_info);
	mbx_close (curbb -> mmdf/src/bboards/ch_bboards.c   444      0     12       10145  3653247036  11437 #include "util.h"
#include "mmdf.h"
#ifndef	POP

/* 
 *			   C H _ B B O A R D S . C
 *
 *			   the new BBoards channel
 */

#else	POP

/* 
 *			       C H _ P O P . C
 *
 *			       the POP channel
 */

#endif	POP

/*  */

#ifndef	POP

/*
 *	This is the channel that is used to handle Internet BBoard
 *	distribution in an intelligent fashion.  In order to run it, you
 *	need the UCI BBoards facility installed.  This requires the
 *	establishment of a special login called ``bboards'', and the
 *	getbbent() package.
 *
 *	The idea is simple.  Distribution lists get aliased to go through
 *	this channel.  Suppose that the relay (or site) using ch_bboards
 *	subscribes to UNIX-WIZARDS.  The maintainer of the list is given
 *	the address ``dist-unix-wizards'' to send to for this relay and all
 *	sites that it serves.  The site manager then defines the following
 *	alias in the aliases file:
 *
 *		dist-unix-wizards:	unix-wizards@bboards
 *
 *	This channel (and this channel alone) is then defined to serve the
 *	``bboards'' host.  When it gets invoked, the channel does two
 *	things:  First, if the relay itself subscribes to the BBoard (the
 *	bb_file entry in the BBoards file is non-empty), then it delivers
 *	the message to the file.  Second, if other sites subscribe to the
 *	BBoard, then ch_bboards will enter the message back into the queue
 *	system using the ``bboards'' login as the sender.
 *
 *	This achieves two goals:  first, the incoming bandwidth of relays
 *	is not degraded by many sites subscribing to the same BBoard;
 *	second, if an address goes bad down the line, the relay's
 *	``bboards'' login gets the message back (not the originator).  Since
 *	the relay's PostMaster is assumed to monitor this mailbox, problems
 *	can be found and corrected.
 *
 *	Finally, ch_bboards can be run by a site that does not relay for
 *	other sites.  In this case, the bb_dist field is empty.
 */

/* 	Unlike previous versions of ch_bboards, this version does not change
 *	the contents of the headers of the message being re-distributed.
 *	The following changes are made:
 *
 *	    Envelope:	The failure address is changed to bboards@locname
 *	    Headers:	Another Received: is added
 *
 *	The local copy going to the BBoard has two entries prepended to the
 *	headers:
 *
 *	    BBoard-ID: n
 *	    BB-Posted: RFC822 date/time
 */

#else	POP

/* 
 *	The POP channel is a subset of the BBoards channel, and just
 *	handles local mail delivery for remote users.  As such, it
 *	only needs to know how to store a maildrop locally, and doesn't
 *	have to mess around with .cnt files and remote delivery.
 */

#endif	POP

/*  */

#include <signal.h>
#include "ch.h"
#include "phs.h"

extern LLog chanlog;
LLog   *logptr = &chanlog;
extern char *logdfldir;

/*  */

main (argc, argv)
int     argc;
char  **argv;
{
    Chan    *chanptr;
    char    *dupfpath ();
    short   retval;

    mmdf_init (argv[0]);

#ifdef RUNALON
    logptr -> ll_fd = 1;
    ll_init (logptr);
#endif

    siginit ();
    signal (SIGINT, SIG_IGN);

    if ((chanptr = ch_nm2struct (argv[0])) == (Chan *) NOTOK)
	err_abrt (RP_PARM, "unknown channel name '%s'", argv[0]);

    ch_llinit(chanptr);

    retval = ch_bboards (argc, argv, chanptr);
    ll_close (logptr);

    exit (retval);
}

/*  */
/* ****************  (ch_) BBOARD DELIVERY **************** */

ch_bboards (argc, argv, chanptr)
int     argc;
char  **argv;
Chan  *chanptr;
{
#ifdef	DEBUG
    ll_log (logptr, LLOGBTR, "ch_bboards(argc=%d,*argv='%s')", argc, *argv);
#endif

    if (rp_isbad (qu_init (argc, argv)))
	return RP_NO;
    if (rp_isbad (bb_init (chanptr)))
	return RP_NO;

    phs_note (chanptr, PHS_WRSTRT);
    
    if (rp_isbad (qu2bb_send (chanptr)))
	return RP_NO;

    phs_note (chanptr, PHS_WREND);

    qu_end (OK);
    bb_end (OK);

    return RP_OK;
}

/*  */

/* VARARGS2 */
err_abrt (code, fmt, b, c, d)
short   code;
char   *fmt,
       *b,
       *c,
       *d;
{
    char    linebuf[LINESIZE];

    qu_end (NOTOK);
    bb_end (NOTOK);

    sprintf (linebuf, "%s%s", "err [ ABEND (%s) ]\t", fmt);
    ll_log (logptr, LLOGFAT, linebuf, rp_valstr (code), b, c, d);
    ll_close (logptr);

    exit (code);
}
to) != OK)
	return NOTOK;
    if (cc && (ml_cc () != OK || ml_adr (cc) != OK))
	return NOTOK;
    if (ml_aend () != OK || ml_tinit () != OK)
	return NOTOK;

    ml_txt (intro);
    if (err_fd != NOTOK) {
	lseek (err_fd, 0L, 0);
	while ((i = read (err_fd, buffer, sizeof buffer)) > 0) {
	    buffer[i] = NULL;
	    ml_txt (buffer);
	}
    }
    encap ();

    return ml_end (OK);
}

/*  */

/* very similar to smmdf/src/bboards/dropsbr.c   444      0     12       32363  3653247037  11033 /* dropsbr.c - write to a mailbox */

#include <stdio.h>
#ifndef	MMDFONLY
#include "../h/mh.h"
#include "../h/dropsbr.h"
#include "../zotnet/mts.h"
#else	MMDFONLY
#include "dropsbr.h"
#include "strings.h"
#include "mmdfonly.h"
#endif	MMDFONLY
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>


#define	MMDF	1
#define	UUCP	2

/*  */

static	int	mbx_style = MMDF;


extern int  errno;


long   lseek ();

/*  */

int     mbx_mmdf () {
    int     style = mbx_style;

    mbx_style = MMDF;
    return style;
}


int     mbx_uucp () {
    int     style = mbx_style;

    mbx_style = UUCP;
    return style;
}

/*  */

int     mbx_open (file, uid, gid, mode)
char   *file;
int     uid,
        gid,
	mode;
{
    int     clear,
            fd;

    if ((fd = mbx_Xopen (file, uid, gid, mode, &clear)) == NOTOK)
	return fd;

    if (!clear)
	switch (mbx_style) {
	    case MMDF: 
	    default: 
		if (mbx_chk (fd) == NOTOK) {
		    (void) close (fd);
		    return NOTOK;
		}
		break;

	    case UUCP: 
		if (lseek (fd, 0L, 2) == (long) NOTOK) {
		    (void) close (fd);
		    return NOTOK;
		}
		break;
	}

    return fd;
}

/*  */

int	mbx_Xopen (file, uid, gid, mode, clear)
char   *file;
int	uid,
	gid,
	mode,
       *clear;
{
    register int j;
    int	    count,
            fd;
    struct stat st;

    for (*clear = 0, count = 4, j = 0; count > 0; count--)
	if ((fd = lkopen (file, 6)) == NOTOK)
	    switch (errno) {
		case ENOENT: 
		    if (mbx_create (file, uid, gid, mode) == NOTOK)
			return NOTOK;
		    (*clear)++;
		    break;

#ifdef	BSD42
		case EWOULDBLOCK:
#endif	BSD42
		case ETXTBSY: 
		    j = errno;
		    sleep (5);
		    break;

		default: 
		    return NOTOK;
	    }
	else {
	    *clear = fstat (fd, &st) != NOTOK && st.st_size == 0L;
	    break;
	}

    errno = j;
    return fd;
}

/*  */

static int  mbx_create (file, uid, gid, mode)
char   *file;
int     uid,
        gid,
	mode;
{
    int     fd;

    if ((fd = creat (file, 0600)) == NOTOK)
	return NOTOK;

    (void) close (fd);
    (void) chown (file, uid, gid);
    (void) chmod (file, mode);

    return OK;
}


static int  mbx_chk (fd)
int     fd;
{
    int     count;
    char    ldelim[BUFSIZ];

    count = strlen (mmdlm2);

    if (lseek (fd, (long) (-count), 2) == (long) NOTOK
	    || read (fd, ldelim, count) != count)
	return NOTOK;
    ldelim[count] = NULL;

    if (strcmp (ldelim, mmdlm2)
	    && write (fd, "\n", 1) != 1
	    && write (fd, mmdlm2, count) != count)
	return NOTOK;

    return OK;
}

/*  */

int	mbx_read (fp, pos, drops, noisy)
register FILE  *fp;
register long	pos;
struct drop **drops;
int	noisy;
{
    register int    len,
                    size;
    long    ld1,
            ld2;
    register char  *bp;
    char    buffer[BUFSIZ];
    register struct drop   *cp,
                           *dp,
                           *ep,
                           *pp;

    pp = (struct drop  *) calloc ((unsigned) (len = MAXFOLDER), sizeof *dp);
    if (pp == NULL) {
	if (noisy)
	    admonish (NULLCP, "unable to allocate drop storage");
	return NOTOK;
    }

    ld1 = (long) strlen (mmdlm1);
    ld2 = (long) strlen (mmdlm2);

    (void) fseek (fp, pos, 0);
    for (ep = (dp = pp) + len - 1; fgets (buffer, sizeof buffer, fp);) {
	size = 0;
	if (strcmp (buffer, mmdlm1) == 0)
	    pos += ld1, dp -> d_start = pos;
	else {
	    dp -> d_start = pos, pos += (long) strlen (buffer);
	    for (bp = buffer; *bp; bp++, size++)
		if (*bp == '\n')
		    size++;
	}

	while (fgets (buffer, sizeof buffer, fp) != NULL)
	    if (strcmp (buffer, mmdlm2) == 0)
		break;
	    else {
		pos += (long) strlen (buffer);
		for (bp = buffer; *bp; bp++, size++)
		    if (*bp == '\n')
			size++;
	    }

	if (dp -> d_start != pos) {
	    dp -> d_id = 0;
	    dp -> d_size = size;
	    dp -> d_stop = pos;
	    dp++;
	}
	pos += ld2;

	if (dp >= ep) {
	    register int    curlen = dp - pp;

	    cp = (struct drop  *) realloc ((char *) pp,
		                    (unsigned) (len += MAXFOLDER) * sizeof *pp);
	    if (cp == NULL) {
		if (noisy)
		    admonish (NULLCP, "unable to allocate drop storage");
		free ((char *) pp);
		return 0;
	    }
	    dp = cp + curlen, ep = (pp = cp) + len - 1;
	}
    }

    if (dp == pp)
	free ((char *) pp);
    else
	*drops = pp;
    return (dp - pp);
}

/*  */

int	mbx_write (mailbox, md, fp, id, pos, stop, mapping, noisy)
char   *mailbox;
register FILE *fp;
int     md,
	id,
	mapping,
	noisy;
register long	pos,
		stop;
{
    register int    i,
                    j,
                    size;
    register long   start,
                    off;
    register char  *cp;
    char    buffer[BUFSIZ];

    off = lseek (md, 0L, 1);
    j = strlen (mmdlm1);
    if (write (md, mmdlm1, j) != j)
	return NOTOK;
    start = lseek (md, 0L, 1);
    size = 0;

    (void) fseek (fp, pos, 0);
    while (fgets (buffer, sizeof buffer, fp) != NULL && pos < stop) {
	i = strlen (buffer);
	for (j = 0; (j = stringdex (mmdlm1, buffer)) >= 0; buffer[j]++)
	    continue;
	for (j = 0; (j = stringdex (mmdlm2, buffer)) >= 0; buffer[j]++)
	    continue;
	if (write (md, buffer, i) != i)
	    return NOTOK;
	pos += (long) i;
	if (mapping)
	    for (cp = buffer; i-- > 0; size++)
		if (*cp++ == '\n')
		    size++;
    }

    stop = lseek (md, 0L, 1);
    j = strlen (mmdlm2);
    if (write (md, mmdlm2, j) != j)
	return NOTOK;
    if (mapping)
	(void) map_write (mailbox, md, id, start, stop, off, size, noisy);

    return OK;
}

/*  */

int     mbx_copy (mailbox, md, fd, mapping, text, noisy)
char   *mailbox;
int     md,
        fd,
	mapping,
	noisy;
char   *text;
{
    register int    i,
                    j,
                    size;
    register long   start,
                    stop,
                    pos;
    register char  *cp;
    char    buffer[BUFSIZ];
    register FILE  *fp;

    pos = lseek (md, 0L, 1);
    size = 0;

    switch (mbx_style) {
	case MMDF: 
	default: 
	    j = strlen (mmdlm1);
	    if (write (md, mmdlm1, j) != j)
		return NOTOK;
	    start = lseek (md, 0L, 1);

	    if (text) {
		i = strlen (text);
		if (write (md, text, i) != i)
		    return NOTOK;
		for (cp = text; *cp++; size++)
		    if (*cp == '\n')
			size++;
	    }
		    
	    while ((i = read (fd, buffer, sizeof buffer)) > 0) {
		for (j = 0;
			(j = stringdex (mmdlm1, buffer)) >= 0;
			buffer[j]++)
		    continue;
		for (j = 0;
			(j = stringdex (mmdlm2, buffer)) >= 0;
			buffer[j]++)
		    continue;
		if (write (md, buffer, i) != i)
		    return NOTOK;
		if (mapping)
		    for (cp = buffer; i-- > 0; size++)
			if (*cp++ == '\n')
			    size++;
	    }

	    stop = lseek (md, 0L, 1);
	    j = strlen (mmdlm2);
	    if (write (md, mmdlm2, j) != j)
		return NOTOK;
	    if (mapping)
		(void) map_write (mailbox, md, 0, start, stop, pos, size, noisy);

	    return (i != NOTOK ? OK : NOTOK);

	case UUCP: 		/* I hate this... */
	    if ((j = dup (fd)) == NOTOK)
		return NOTOK;
	    if ((fp = fdopen (j, "r")) == NULL) {
		(void) close (j);
		return NOTOK;
	    }
	    start = lseek (md, 0L, 1);

	    if (text) {
		i = strlen (text);
		if (write (md, text, i) != i)
		    return NOTOK;
		for (cp = buffer; *cp++; size++)
		    if (*cp == '\n')
			size++;
	    }
		    
	    for (j = 0; fgets (buffer, sizeof buffer, fp) != NULL; j++) {
		if (j != 0 && strncmp (buffer, "From ", 5) == 0) {
		    (void) write (fd, ">", 1);
		    size++;
		}
		i = strlen (buffer);
		if (write (md, buffer, i) != i) {
		    (void) fclose (fp);
		    return NOTOK;
		}
		if (mapping)
		    for (cp = buffer; i-- > 0; size++)
			if (*cp++ == '\n')
			    size++;
	    }

	    (void) fclose (fp);
	    (void) lseek (fd, 0L, 2);
	    stop = lseek (md, 0L, 1);
	    if (mapping)
		(void) map_write (mailbox, md, 0, start, stop, pos, size,
			    noisy);

	    return OK;
    }
}

/*  */

int	mbx_size (md, start, stop)
int	md;
long	start,
	stop;
{
    register int    i,
                    fd;
    register long   pos;
    register FILE  *fp;

    if ((fd = dup (md)) == NOTOK || (fp = fdopen (fd, "r")) == NULL) {
	if (fd != NOTOK)
	    (void) close (fd);
	return NOTOK;
    }

    (void) fseek (fp, start, 0);
    for (i = 0, pos = stop - start; pos-- > 0; i++)
	if (fgetc (fp) == '\n')
	    i++;

    (void) fclose (fp);

    return i;
}

/*  */

int     mbx_close (mailbox, md)
char   *mailbox;
int     md;
{
    (void) lkclose (md, mailbox);

    return OK;
}

/*  */

/* This function is performed implicitly by getbbent.c:

		bb -> bb_map = map_name (bb -> bb_file);
*/

char    *map_name (file)
register char	*file;
{
    register char  *cp,
                   *dp;
    static char buffer[BUFSIZ];

    if ((dp = index (cp = r1bindex (file, '/'), '.')) == NULL)
	dp = cp + strlen (cp);
    if (cp == file)
	(void) sprintf (buffer, ".%.*s%s", dp - cp, cp, ".map");
    else
	(void) sprintf (buffer, "%.*s.%.*s%s", cp - file, file, dp - cp,
		cp, ".map");

    return buffer;
}

/*  */

int	map_read (file, pos, drops, noisy)
char   *file;
long	pos;
struct drop **drops;
int	noisy;
{
    register int    i,
                    md,
                    msgp;
    register char  *cp;
    struct drop d;
    register struct drop   *mp,
                           *dp;

    if ((md = open (cp = map_name (file), 0)) == NOTOK
	    || map_chk (cp, md, mp = &d, pos, noisy)) {
	if (md != NOTOK)
	    (void) close (md);
	return 0;
    }

    msgp = mp -> d_id;
    dp = (struct drop  *) calloc ((unsigned) msgp, sizeof *dp);
    if (dp == NULL) {
	(void) close (md);
	return 0;
    }

    (void) lseek (md, (long) sizeof *mp, 0);
    if ((i = read (md, (char *) dp, msgp * sizeof *dp)) < sizeof *dp) {
	i = 0;
	free ((char *) dp);
    }
    else
	*drops = dp;

    (void) close (md);

    return (i / sizeof *dp);
}

/*  */

int     map_write (mailbox, md, id, start, stop, pos, size, noisy)
register char   *mailbox;
int     md,
        id,
	size,
	noisy;
long    start,
        stop,
        pos;
{
    register int    i;
    int     clear,
            fd,
            td;
    char   *file;
    register struct drop   *dp;
    struct drop    d1,
		   d2,
                  *rp;
    register FILE *fp;

    if ((fd = map_open (file = map_name (mailbox), &clear, md)) == NOTOK)
	return NOTOK;

    if (!clear && map_chk (file, fd, &d1, pos, noisy)) {
	(void) unlink (file);
	(void) mbx_close (file, fd);
	if ((fd = map_open (file, &clear, md)) == NOTOK)
	    return NOTOK;
	clear++;
    }

    if (clear) {
	if ((td = dup (md)) == NOTOK || (fp = fdopen (td, "r")) == NULL) {
	    if (noisy)
		admonish (file, "unable to %s", td != NOTOK ? "fdopen" : "dup");
	    if (td != NOTOK)
		(void) close (td);
	    (void) mbx_close (file, fd);
	    return NOTOK;
	}

	switch (i = mbx_read (fp, 0L, &rp, noisy)) {
	    case NOTOK:
		(void) fclose (fp);
		(void) mbx_close (file, fd);
		return NOTOK;

	    case OK:
		break;

	    default:
		d1.d_id = 0;
		for (dp = rp; i-- >0; dp++) {
		    if (dp -> d_start == start)
			dp -> d_id = id;
		    (void) lseek (fd, (long) (++d1.d_id * sizeof *dp), 0);
		    if (write (fd, (char *) dp, sizeof *dp) != sizeof *dp) {
			if (noisy)
			    admonish (file, "write error");
			(void) mbx_close (file, fd);
			(void) fclose (fp);
			return NOTOK;
		    }
		}
		free ((char *) rp);
		break;
	}
    }
    else {
	dp = &d2;
	dp -> d_id = id;
	dp -> d_size = size ? size : mbx_size (fd, start, stop);
	dp -> d_start = start;
	dp -> d_stop = stop;
	(void) lseek (fd, (long) (++d1.d_id * sizeof *dp), 0);
	if (write (fd, (char *) dp, sizeof *dp) != sizeof *dp) {
	    if (noisy)
		admonish (file, "write error");
	    (void) mbx_close (file, fd);
	    return NOTOK;
	}
    }

    dp = &d1;
    dp -> d_size = DRVRSN;
    dp -> d_start = DRMAGIC;
    dp -> d_stop = lseek (md, 0L, 1);
    (void) lseek (fd, 0L, 0);
    if (write (fd, (char *) dp, sizeof *dp) != sizeof *dp) {
	if (noisy)
	    admonish (file, "write error");
	(void) mbx_close (file, fd);
	return NOTOK;
    }

    (void) mbx_close (file, fd);

    return OK;
}

/*  */

static int  map_open (file, clear, md)
char   *file;
int    *clear,
	md;
{
    int	    mode;
    struct  stat st;

    mode = fstat (md, &st) != NOTOK ? (int) (st.st_mode & 0777) : m_gmprot ();
    return mbx_Xopen (file, st.st_uid, st.st_gid, mode, clear);
}

/*  */

int  map_chk (file, fd, dp, pos, noisy)
char   *file;
int     fd,
	noisy;
register struct drop *dp;
long	pos;
{
    long    count;
    struct drop d;
    register struct drop    *dl;

    if (read (fd, (char *) dp, sizeof *dp) != sizeof *dp) {
#ifdef	notdef
	admonish (NULLCP, "%s: missing or partial index", file);
#endif	notdef
	return NOTOK;
    }
    if (dp -> d_size != DRVRSN) {
	if (noisy)
	    admonish (NULLCP, "%s: version mismatch", file);
	return NOTOK;
    }
    if (dp -> d_start != DRMAGIC) {
	if (noisy)
	    admonish (NULLCP, "%s: bad magic number", file);
	return NOTOK;
    }
    if (dp -> d_stop != pos) {
	if (noisy && pos != 0L)
	    admonish (NULLCP,
		    "%s: pointer mismatch or incomplete index (%ld!=%ld)", 
		    file, dp -> d_stop, pos);
	return NOTOK;
    }

    if ((long) ((dp -> d_id + 1) * sizeof *dp) != lseek (fd, 0L, 2)) {
	if (noisy)
	    admonish (NULLCP, "%s: corrupt index(1)", file);
	return NOTOK;
    }

    dl = &d;
    count = (long) strlen (mmdlm2);
    (void) lseek (fd, (long) (dp -> d_id * sizeof *dp), 0);
    if (read (fd, (char *) dl, sizeof *dl) != sizeof *dl
	    || (dl -> d_stop != dp -> d_stop
		&& dl -> d_stop + count != dp -> d_stop)) {
	if (noisy)
	    admonish (NULLCP, "%s: corrupt index(2)", file);
	return NOTOK;
    }

    return OK;
}
L) {
	(void) close (md);
	return 0;
    }

    (void) lseek (md, (long) sizeof *mp, 0);
    if ((i = read (md, (char *) dp, msgp * sizeof *dp)) < sizeof *dp) {
	i = 0;
	free ((char *) dp);
    }
    else
	*drops = dp;

    (void) close (md);

    return (i / sizeof *dpmmdf/src/bboards/dropsbr.h   444      0     12        2167  3653247040  11011 /* dropsbr.h - definitions for maildrop-style files */


/* A file which is formatted like a maildrop may have a corresponding map
   file which is an index to the bounds of each message.  The first record
   of such an map is special, it contains:

	d_id    = number of messages in file
	d_size	= version number of map
	d_start = magic cookie
	d_stop  = size of file

    Each record after that contains:
	d_id	= BBoard-ID: of message, or similar info
	d_size	= size of message in ARPA Internet octets (\n == 2 octets)
	d_start	= starting position of message in file
	d_stop	= stopping position of message in file

   Note that d_st{art,op} do NOT include the message delimiters, so
   programs using the map can simply fseek to d_start and keep reading
   until the position is at d_stop.
 */

#define	DRVRSN	2
#define	DRMAGIC	(0xff00 | DRVRSN)

struct drop {
    int     d_id;
    int	    d_size;
    long    d_start;
    long    d_stop;
};


int	map_chk (), map_read (), map_write ();
char   *map_name ();

int	mbx_mmdf (), mbx_uucp ();
int	mbx_open (), mbx_Xopen (), mbx_copy (), mbx_size (), mbx_close ();
int	mbx_read (), mbx_write ();
)) == NOTOK)
	    return NOTOK;
	clear++;
    }

    if (clear) {
	if ((td = dup (md)) == NOTOK || (fp = fdopen (td, "r")) == NULL) {
	    if (noisy)
		admonish (file, "unable to %s", td != NOTOK ? "fdopen" : "dup");
	    if (td != NOTOK)
		(void) close (td);
	    (void) mbx_close (file, fd);
	    return NOTOK;
	}

	switch (i = mbx_read (fp, 0L, &rp, noisy)) {
	    case NOTOK:
		(void) fclommdf/src/bboards/gen   555      0     12          57  3653247040   7620 make -f ../../Makefile.com -f Makefile.real $*
iles */


/* A file which is formatted like a maildrop may have a corresponding map
   file which is an index to the bounds of each message.  The first record
   of such an map is special, it contains:

	d_id    = number of messages in file
	d_size	= version number of map
	d_start = magic cookie
	d_stop  = size of file

    Each record after that contains:
	d_id	= BBoard-ID: of message, or similar info
	d_size	= size of message in ARPA Internet octets (\n == 2 mmdf/src/bboards/getbbent.c   444      0     12       33715  3655273070  11152 /* getbbent.c - subroutines for accessing the BBoards file */

/* LINTLIBRARY */

#include "bboards.h"
#ifndef	MMDFONLY
#include "../h/strings.h"
#include <sys/types.h>
#else	MMDFONLY
#include "util.h"
#include "mmdf.h"
#include "strings.h"
#endif	MMDFONLY
#include <ctype.h>
#include <pwd.h>
#include <grp.h>
#include <stdio.h>
#include <sys/stat.h>


#ifndef	MMDFONLY
#define	NOTOK	(-1)
#define	OK	0
#endif	not MMDFONLY


#define	MaxBBAka	100
#define	MaxBBLdr	100
#define	MaxBBDist	100


#define	NCOLON	9		/* currently 10 fields per entry */

#define	COLON	':'
#define	COMMA	','
#define	NEWLINE	'\n'


#define	ARCHIVE	"archive"
#define	CNTFILE	".cnt"
#define	DSTFILE	".dist"
#define	MAPFILE ".map"

/*  */

static int  BBuid = -1;

static unsigned int  BBflags = SB_NULL;

static char BBName[BUFSIZ] = BBOARDS;
static char BBDir[BUFSIZ] = "";
static char BBData[BUFSIZ] = "";

static  FILE *BBfile = NULL;


static struct bboard    BB;
static struct bboard   *bb = &BB;

static int  BBload = 1;

static char BBFile[BUFSIZ];
static char BBArchive[BUFSIZ];
static char BBInfo[BUFSIZ];
static char BBMap[BUFSIZ];
static char *BBAkas[MaxBBAka];
static char *BBLeaders[MaxBBLdr];
static char *BBDists[MaxBBDist];
static char BBAddr[BUFSIZ];
static char BBRequest[BUFSIZ];
static char BBDate[BUFSIZ];
static char BBErrors[BUFSIZ];

#ifdef	MMDFONLY
extern LLog *logptr;
#endif	MMDFONLY

char   *bbskip (), *getcpy ();

char   *crypt (), *getpass ();
struct group  *getgrnam ();
struct passwd *getpwnam (), *getpwuid ();

/*  */

int     setbbfile (file, f)
register char  *file;
register int    f;
{
    if (BBuid == -1)
	return setbbinfo (BBOARDS, file, f);

    (void) strcpy (BBData, file);

    BBflags = SB_NULL;
    (void) endbbent ();

    return setbbent (f);
}

/*  */

int	setbbinfo (user, file, f)
register char  *user,
               *file;
register int	f;
{
    register struct passwd *pw;

    if ((pw = getpwnam (user)) == NULL) {
	(void) sprintf (BBErrors, "unknown user: %s", user);
	return 0;
    }

    return setpwinfo (pw, file, f);
}


int	setpwinfo (pw, file, f)
register struct passwd *pw;
register char  *file;
register int	f;
{
    if (!setpwaux (pw, file))
	return 0;

    BBflags = SB_NULL;
    (void) endbbent ();

    return setbbent (f);
}

/*  */

static int  setbbaux (name, file)
register char  *name,
	       *file;
{
    register struct passwd *pw;

    if ((pw = getpwnam (name)) == NULL) {
	(void) sprintf (BBErrors, "unknown user: %s", name);
	return 0;
    }

    return setpwaux (pw, file);
}


static int  setpwaux (pw, file)
register struct passwd *pw;
register char  *file;
{
    (void) strcpy (BBName, pw -> pw_name);
    BBuid = pw -> pw_uid;
    (void) strcpy (BBDir, pw -> pw_dir);
    (void) sprintf (BBData, "%s/%s",
	    *file != '/' ? BBDir : "",
	    *file != '/' ? file : file + 1);

    BBflags = SB_NULL;

    return 1;
}

/*  */

int     setbbent (f)
register int     f;
{
    if (BBfile == NULL) {
	if (BBuid == -1 && !setbbaux (BBOARDS, BBDB))
	    return 0;

	if ((BBfile = fopen (BBData, "r")) == NULL) {
	    (void) sprintf (BBErrors, "unable to open: %s", BBData);
	    return 0;
	}
    }
    else
	rewind (BBfile);

    BBflags |= f;
    return (BBfile != NULL);
}


int     endbbent () {
    if (BBfile != NULL && !(BBflags & SB_STAY)) {
	(void) fclose (BBfile);
	BBfile = NULL;
    }

    return 1;
}


long    getbbtime () {
    struct stat st;

    if (BBfile == NULL) {
	if (BBuid == -1 && !setbbaux (BBOARDS, BBDB))
	    return 0;

	if (stat (BBData, &st) == NOTOK) {
	    (void) sprintf (BBErrors, "unable to stat: %s", BBData);
	    return 0;
	}
    }
    else
	if (fstat (fileno (BBfile), &st) == NOTOK) {
	    (void) sprintf (BBErrors, "unable to fstat: %s", BBData);
	    return 0;
	}

    return ((long) st.st_mtime);
}

/*  */

struct bboard  *getbbent () {
    register int    count;
    register char  *p,
                   *q,
                   *r,
                   *d,
                   *f,
                  **s;
    static char line[BUFSIZ];

    if (BBfile == NULL && !setbbent (SB_NULL))
	return NULL;

retry: ;
    if ((p = fgets (line, sizeof line, BBfile)) == NULL)
	return NULL;

    for (q = p, count = 0; *q != NULL && *q != NEWLINE; q++)
	if (*q == COLON)
	    count++;

    if (count != NCOLON) {
#ifdef	MMDFONLY
	if (q = index (p, NEWLINE))
	    *q = NULL;
	ll_log (logptr, LLOGTMP, "bad entry in %s: %s", BBData, p);
#endif	MMDFONLY
	goto retry;
    }
    
    bb -> bb_name = p;
    p = q = bbskip (p, COLON);
    p = bb -> bb_file = bbskip (p, COLON);
    bb -> bb_archive = bb -> bb_info = bb -> bb_map = "";
    p = bb -> bb_passwd = bbskip (p, COLON);
    p = r = bbskip (p, COLON);
    p = bb -> bb_addr = bbskip (p, COLON);
    p = bb -> bb_request = bbskip (p, COLON);
    p = bb -> bb_relay = bbskip (p, COLON);
    p = d = bbskip (p, COLON);
    p = f = bbskip (p, COLON);
    (void) bbskip (p, NEWLINE);

    s = bb -> bb_aka = BBAkas;
    while (*q) {
	*s++ = q;
	q = bbskip (q, COMMA);
    }
    *s = NULL;

    s = bb -> bb_leader = BBLeaders;
    if (*r == NULL) {
	if (!(BBflags & SB_FAST))
	    *s++ = BBName;
    }
    else
	while (*r) {
	    *s++ = r;
	    r = bbskip (r, COMMA);
	}
    *s = NULL;

    s = bb -> bb_dist = BBDists;
    while (*d) {
	*s++ = d;
	d = bbskip (d, COMMA);
    }
    *s = NULL;

    if (*f)
	(void) sscanf (f, "%o", &bb -> bb_flags);
    else
	bb -> bb_flags = BB_NULL;
    bb -> bb_count = bb -> bb_maxima = 0;
    bb -> bb_date = NULL;
    bb -> bb_next = bb -> bb_link = bb -> bb_chain = NULL;

    if (BBload)
	BBread ();

    return bb;
}

/*  */

struct bboard  *getbbnam (name)
register char   *name;
{
    register struct bboard *b = NULL;

    if (!setbbent (SB_NULL))
	return NULL;
    BBload = 0;
    while ((b = getbbent ()) && strcmp (name, b -> bb_name))
	continue;
    BBload = 1;
    (void) endbbent ();

    if (b != NULL)
	BBread ();

    return b;
}


struct bboard  *getbbaka (aka)
register char   *aka;
{
    register char **ap;
    register struct bboard *b = NULL;

    if (!setbbent (SB_NULL))
	return NULL;
    BBload = 0;
    while ((b = getbbent ()) != NULL)
	for (ap = b -> bb_aka; *ap; ap++)
	    if (strcmp (aka, *ap) == 0)
		goto hit;
hit: ;
    BBload = 1;
    (void) endbbent ();

    if (b != NULL)
	BBread ();

    return b;
}

/*  */

static int  BBread () {
    register int    i;
    register char  *cp,
                   *dp,
		   *p,
		   *r;
    char    prf[BUFSIZ];
    static char line[BUFSIZ];
    register    FILE * info;

    if (BBflags & SB_FAST)
	return;

    p = index (bb -> bb_request, '@');
    r = index (bb -> bb_addr, '@');
    BBRequest[0] = NULL;

    if (*bb -> bb_request == '-')
	if (p == NULL && r && *r == '@')
	    (void) sprintf (BBRequest, "%s%s%s",
		    bb -> bb_name, bb -> bb_request, r);
	else
	    (void) sprintf (BBRequest, "%s%s",
		    bb -> bb_name, bb -> bb_request);
    else
	if (p == NULL && r && *r == '@' && *bb -> bb_request)
	    (void) sprintf (BBRequest, "%s%s", bb -> bb_request, r);

    if (BBRequest[0])
	bb -> bb_request = BBRequest;
    else
	if (*bb -> bb_request == NULL)
	    bb -> bb_request = *bb -> bb_addr ? bb -> bb_addr
		: bb -> bb_leader[0];

    if (*bb -> bb_addr == '@') {
	(void) sprintf (BBAddr, "%s%s", bb -> bb_name, bb -> bb_addr);
	bb -> bb_addr = BBAddr;
    }
    else
	if (*bb -> bb_addr == NULL)
	    bb -> bb_addr = bb -> bb_name;

    if (*bb -> bb_file == NULL)
	return;
    if (*bb -> bb_file != '/') {
	(void) sprintf (BBFile, "%s/%s", BBDir, bb -> bb_file);
	bb -> bb_file = BBFile;
    }

    if ((cp = rindex (bb -> bb_file, '/')) == NULL || *++cp == NULL)
	(void) strcpy (prf, ""), cp = bb -> bb_file;
    else
	(void) sprintf (prf, "%.*s", cp - bb -> bb_file, bb -> bb_file);
    if ((dp = index (cp, '.')) == NULL)
	dp = cp + strlen (cp);

    (void) sprintf (BBArchive, "%s%s/%s", prf, ARCHIVE, cp);
    bb -> bb_archive = BBArchive;
    (void) sprintf (BBInfo, "%s.%.*s%s", prf, dp - cp, cp, CNTFILE);
    bb -> bb_info = BBInfo;
    (void) sprintf (BBMap, "%s.%.*s%s", prf, dp - cp, cp, MAPFILE);
    bb -> bb_map = BBMap;

    if ((info = fopen (bb -> bb_info, "r")) == NULL)
	return;

    if (fgets (line, sizeof line, info) && (i = atoi (line)) > 0)
	bb -> bb_maxima = (unsigned) i;
    if (!feof (info) && fgets (line, sizeof line, info)) {
	(void) strcpy (BBDate, line);
	if (cp = index (BBDate, NEWLINE))
	    *cp = NULL;
	bb -> bb_date = BBDate;
    }

    (void) fclose (info);
}

/*  */

int     ldrbb (b)
register struct bboard  *b;
{
    register char  *p,
                  **q,
                  **r;
    static int  uid = 0,
                gid = 0;
    static char username[10] = "";
    register struct passwd *pw;
    register struct group  *gr;

    if (b == NULL)
	return 0;
    if (BBuid == -1 && !setbbaux (BBOARDS, BBDB))
	return 0;

    if (username[0] == NULL) {
	if ((pw = getpwuid (uid = getuid ())) == NULL)
	    return 0;
	gid = getgid ();
	(void) strcpy (username, pw -> pw_name);
    }

    if (uid == BBuid)
	return 1;

    q = b -> bb_leader;
    while (p = *q++)
	if (*p == '=') {
	    if ((gr = getgrnam (++p)) == NULL)
		continue;
	    if (gid == gr -> gr_gid)
		return 1;
	    r = gr -> gr_mem;
	    while (p = *r++)
		if (strcmp (username, p) == 0)
		    return 1;
	}
	else
	    if (strcmp (username, p) == 0)
		return 1;

    return 0;
}

/*  */

int     ldrchk (b)
register struct bboard  *b;
{
    if (b == NULL)
	return 0;

    if (*b -> bb_passwd == NULL)
	return 1;

    if (strcmp (b -> bb_passwd,
		crypt (getpass ("Password: "), b -> bb_passwd)) == 0)
	return 1;

    fprintf (stderr, "Sorry\n");
    return 0;
}

/*  */

struct bboard  *getbbcpy (bp)
register struct bboard  *bp;
{
    register char **p,
                  **q;
    register struct bboard *b;

    if (bp == NULL)
	return NULL;

    b = (struct bboard *) malloc ((unsigned) sizeof *b);
    if (b == NULL)
	return NULL;

    b -> bb_name = getcpy (bp -> bb_name);
    b -> bb_file = getcpy (bp -> bb_file);
    b -> bb_archive = getcpy (bp -> bb_archive);
    b -> bb_info = getcpy (bp -> bb_info);
    b -> bb_map = getcpy (bp -> bb_map);
    b -> bb_passwd = getcpy (bp -> bb_passwd);
    b -> bb_flags = bp -> bb_flags;
    b -> bb_count = bp -> bb_count;
    b -> bb_maxima = bp -> bb_maxima;
    b -> bb_date = getcpy (bp -> bb_date);
    b -> bb_addr = getcpy (bp -> bb_addr);
    b -> bb_request = getcpy (bp -> bb_request);
    b -> bb_relay = getcpy (bp -> bb_relay);

    for (p = bp -> bb_aka; *p; p++)
	continue;
    b -> bb_aka =
	q = (char **) calloc ((unsigned) (p - bp -> bb_aka + 1), sizeof *q);
    if (q == NULL)
	return NULL;
    for (p = bp -> bb_aka; *p; *q++ = getcpy (*p++))
	continue;
    *q = NULL;

    for (p = bp -> bb_leader; *p; p++)
	continue;
    b -> bb_leader =
	q = (char **) calloc ((unsigned) (p - bp -> bb_leader + 1), sizeof *q);
    if (q == NULL)
	return NULL;
    for (p = bp -> bb_leader; *p; *q++ = getcpy (*p++))
	continue;
    *q = NULL;

    for (p = bp -> bb_dist; *p; p++)
	continue;
    b -> bb_dist = 
	q = (char **) calloc ((unsigned) (p - bp -> bb_dist + 1), sizeof *q);
    if (q == NULL)
	return NULL;
    for (p = bp -> bb_dist; *p; *q++ = getcpy (*p++))
	continue;
    *q = NULL;

    b -> bb_next = bp -> bb_next;
    b -> bb_link = bp -> bb_link;
    b -> bb_chain = bp -> bb_chain;

    return b;
}

/*  */

int     getbbdist (bb, action)
register struct bboard  *bb;
register int     (*action) ();
{
    register int    result;
    register char **dp;

    BBErrors[0] = NULL;
    for (dp = bb -> bb_dist; *dp; dp++)
	if (result = getbbitem (bb, *dp, action))
	    return result;

    return result;
}

char    *getbberr () {
    return (BBErrors[0] ? BBErrors : NULL);
};

/*  */

static int  getbbitem (bb, item, action)
register struct bboard  *bb;
register char   *item;
register int     (*action) ();
{
    register int    result;
    register char  *cp,
                   *dp,
                   *hp,
                   *np;
    char    mbox[BUFSIZ],
            buffer[BUFSIZ],
            file[BUFSIZ],
            host[BUFSIZ],
            prf[BUFSIZ];
    register FILE *fp;

    switch (*item) {
	case '*': 
	    switch (*++item) {
		case '/': 
		    hp = item;
		    break;

		case NULL: 
		    if ((cp = rindex (bb -> bb_file, '/')) == NULL || *++cp == NULL)
			(void) strcpy (prf, ""), cp = bb -> bb_file;
		    else
			(void) sprintf (prf, "%.*s", cp - bb -> bb_file, bb -> bb_file);
		    if ((dp = index (cp, '.')) == NULL)
			dp = cp + strlen (cp);
		    (void) sprintf (file, "%s.%.*s%s", prf, dp - cp, cp, DSTFILE);
		    hp = file;
		    break;

		default: 
		    (void) sprintf (file, "%s/%s", BBDir, item);
		    hp = file;
		    break;
	    }

	    if ((fp = fopen (hp, "r")) == NULL)
		return bblose ("unable to read file %s", hp);
	    while (fgets (buffer, sizeof buffer, fp)) {
		if (np = index (buffer, '\n'))
		    *np = NULL;
		if (result = getbbitem (bb, buffer, action)) {
		    (void) fclose (fp);
		    (void) bblose ("error with file %s, item %s", hp, buffer);
		    return result;
		}
	    }
	    (void) fclose (fp);
	    return OK;

	default: 
	    if (hp = rindex (item, '@')) {
		*hp++ = NULL;
		(void) strcpy (mbox, item);
		(void) strcpy (host, hp);
		*--hp = '@';
	    }
	    else {
		(void) sprintf (mbox, "%s%s", DISTADR, bb -> bb_name);
		(void) strcpy (host, item);
	    }
	    if (result = (*action) (mbox, host))
		(void) bblose ("action (%s, %s) returned 0%o", mbox, host, result);
	    return result;
    }
}

/*  */

/* VARARGS1 */

static int  bblose (fmt, a, b, c)
char   *fmt,
       *a,
       *b,
       *c;
{
    if (BBErrors[0] == NULL)
	(void) sprintf (BBErrors, fmt, a, b, c);

    return NOTOK;
}

/*  */

void	make_lower (s1, s2)
register char   *s1,
		*s2;
{
    if (s1 == NULL || s2 == NULL)
	return;

    for (; *s2; s2++)
	*s1++ = isupper (*s2) ? tolower (*s2) : *s2;
    *s1 = NULL;
}

/*  */

static char *bbskip (p, c)
register char  *p,
		c;	
{
    if (p == NULL)
	return NULL;

    while (*p && *p != c)
	p++;
    if (*p)
	*p++ = NULL;

    return p;
}


static	char   *getcpy (s)
register char   *s;
{
    register char  *p;

    if (s == NULL)
	return NULL;

    if (p = malloc ((unsigned) (strlen (s) + 1)))
	(void) strcpy (p, s);
    return p;
}
> bb_flags = bp -> bb_flags;
    b -> bb_count = bpmmdf/src/bboards/lock.c   444      0     12       13602  3653247041  10276 /* lock.c - universal locking routines */

#ifndef	MMDFONLY
#include "../h/strings.h"
#else	MMDFONLY
#include "strings.h"
#include "mmdfonly.h"
#endif	MMDFONLY
#include <stdio.h>
#include "mts.h"
#include <sys/types.h>
#include <sys/stat.h>


#define	NOTOK	(-1)
#define	OK	0

#define	NULLCP	((char *) 0)

#ifdef	SYS5
#define	index	strchr
#define	rindex	strrchr
#endif	SYS5


extern int  errno;

/*  */

int	lkopen (file, access)
register char   *file;
register int     access;
{
    mts_init ("mts");
    switch (lockstyle) {
	case LOK_UNIX:
#ifdef	BSD42
	    return f_lkopen (file, access);
#endif	BSD42

	default:
	    return b_lkopen (file, access);
	}
}

/*  */

static int  b_lkopen (file, access)
register char   *file;
register int     access;
{
    register int    i,
                    j;
    long    curtime;
    char    curlock[BUFSIZ],
            tmplock[BUFSIZ];
    struct stat st;

    if (stat (file, &st) == NOTOK)
	return NOTOK;
    lockname (curlock, tmplock, file, (int) st.st_dev, (int) st.st_ino);

    for (i = 0;;)
	switch (lockit (tmplock, curlock)) {
	    case OK: 
		if ((i = open (file, access)) == NOTOK) {
		    j = errno;
		    (void) unlink (curlock);
		    errno = j;
		}
		timerON (curlock, i);
		return i;

	    case NOTOK: 
		if (stat (curlock, &st) == NOTOK) {
		    if (i++ > 5)
			return NOTOK;
		    sleep (5);
		    break;
		}

		i = 0;
		(void) time (&curtime);
		if (curtime < st.st_ctime + 60L)
		    sleep (5);
		else
		    (void) unlink (curlock);
		break;
	}
}


static int  lockit (tmp, file)
register char   *tmp,
	        *file;
{
    register int    fd;

    if ((fd = creat (tmp, 0400)) == NOTOK)
	return NOTOK;
    (void) close (fd);

    fd = link (tmp, file);
    (void) unlink (tmp);

    return (fd != NOTOK ? OK : NOTOK);
}

/*  */

static  lockname (curlock, tmplock, file, dev, ino)
register char   *curlock,
	        *tmplock,
	        *file;
register int     dev,
		 ino;
{
    register char  *bp,
                   *cp;

    bp = curlock;
    if ((cp = rindex (file, '/')) == NULL || *++cp == NULL)
	cp = file;
    if (lockldir == NULL || *lockldir == NULL) {
	if (cp != file) {
	    (void) sprintf (bp, "%.*s", cp - file, file);
	    bp += strlen (bp);
	}
    }
    else {
	(void) sprintf (bp, "%s/", lockldir);
	bp += strlen (bp);
    }

    switch (lockstyle) {
	case LOK_BELL: 
	default: 
	    (void) sprintf (bp, "%s.lock", cp);
	    break;

	case LOK_MMDF: 
	    (void) sprintf (bp, "LCK%05d.%05d", dev, ino);
	    break;
    }

    if (tmplock) {
	if ((cp = rindex (curlock, '/')) == NULL || *++cp == NULL)
	    (void) strcpy (tmplock, ",LCK.XXXXXX");
	else
	    (void) sprintf (tmplock, "%.*s,LCK.XXXXXX",
		cp - curlock, curlock);
	(void) unlink (mktemp (tmplock));
    }
}

/*  */

#ifdef	BSD42

#include <sys/file.h>

static int  f_lkopen (file, access)
register char   *file;
register int     access;
{
    register int    fd,
                    i,
		    j;

    for (i = 0; i < 5; i++) {
	if ((fd = open (file, access | O_NDELAY)) == NOTOK)
	    return NOTOK;
	if (flock (fd, LOCK_EX | LOCK_NB) != NOTOK)
	    return fd;
	j = errno;
	(void) close (fd);

	sleep (5);
    }

    (void) close (fd);
    errno = j;
    return NOTOK;
}
#endif	BSD42

/*  */

/* ARGSUSED */

int     lkclose (fd, file)
register int     fd;
register char   *file;
{
    char    curlock[BUFSIZ];
    struct stat st;

    if (fd == NOTOK)
	return OK;
    switch (lockstyle) {
	case LOK_UNIX: 
#ifdef	BSD42
	    break;
#endif	BSD42

	default: 
	    if (fstat (fd, &st) != NOTOK) {
		lockname (curlock, NULLCP, file, (int) st.st_dev, (int) st.st_ino);
		(void) unlink (curlock);
		timerOFF (fd);
	    }
    }

    return (close (fd));
}


/*  */

FILE	*lkfopen (file, mode)
register char   *file,
 	        *mode;
{
    register int    fd;
    register FILE  *fp;

    if ((fd = lkopen (file, strcmp (mode, "r") ? 2 : 0)) == NOTOK)
	return NULL;

    if ((fp = fdopen (fd, mode)) == NULL) {
	(void) close (fd);
	return NULL;
    }

    return fp;
}


/* ARGSUSED */

int	lkfclose (fp, file)
register FILE	*fp;
register char	*file;
{
    char    curlock[BUFSIZ];
    struct stat st;

    if (fp == NULL)
	return OK;

    switch (lockstyle) {
	case LOK_UNIX: 
#ifdef	BSD42
	    break;
#endif	BSD42

	default: 
	    if (fstat (fileno (fp), &st) != NOTOK) {
		lockname (curlock, NULLCP, file, (int) st.st_dev, (int) st.st_ino);
		(void) unlink (curlock);
	    }
    }

    return (fclose (fp));
}

/*  */

#include <signal.h>

#define	NSECS	((unsigned) 20)


struct lock {
    int		 l_fd;
    char	*l_lock;
    struct lock *l_next;
};
#define	NULLP	((struct lock *) 0)

static struct lock *l_top = NULLP;


/* ARGSUSED */

static alrmser (sig)
int	sig;
{
    register int    j;
    register char  *cp;
    register struct lock   *lp;

#ifndef	BSD42
    (void) signal (SIGALRM, alrmser);
#endif	BSD42

    for (lp = l_top; lp; lp = lp -> l_next)
	if (*(cp = lp -> l_lock) && (j = creat (cp, 0400)) != NOTOK)
	    (void) close (j);

    (void) alarm (NSECS);
}

/*  */

static timerON (lock, fd)
char   *lock;
int	fd;
{
    register struct lock   *lp;

    if ((lp = (struct lock *) malloc ((unsigned) (sizeof *lp))) == NULLP)
	return;			/* XXX */

    lp -> l_fd = fd;
    if ((lp -> l_lock = malloc ((unsigned) (strlen (lock) + 1))) == NULLCP) {
	free ((char *) lp);
	return;			/* XXX */
    }
    (void) strcpy (lp -> l_lock, lock);
    lp -> l_next = NULLP;

    if (l_top)
	lp -> l_next = l_top -> l_next;
    else {
	(void) signal (SIGALRM, alrmser);/* perhaps SIGT{STP,TIN,TOU} */
	(void) alarm (NSECS);
    }
    l_top = lp;
}


static timerOFF (fd)
int	fd;
{
    register struct lock   *pp,
                           *lp;

    (void) alarm (0);

    if (l_top) {
	for (pp = lp = l_top; lp; pp = lp, lp = lp -> l_next)
	    if (lp -> l_fd == fd)
		break;
	if (lp) {
	    if (lp == l_top)
		l_top = lp -> l_next;
	    else
		pp -> l_next = lp -> l_next;

	    free (lp -> l_lock);
	    free ((char *) lp);
	}
    }

    if (l_top)
	(void) alarm (NSECS);
}
    dev,
		 ino;
{
    register char  *bp,
                   *cp;

    bp = curlock;
    if ((cp = rindex (file, '/')) == NULmmdf/src/bboards/mmdfonly.c   444      0     12         346  3653247042  11135 /* mmdfonly.c - subroutines for stand-alone BBoards MMDF-II channel */


/* VARARGS2 */

void admonish (what, fmt, a, b, c, d, e, f)
char   *what,
       *fmt,
       *a,
       *b,
       *c,
       *d,
       *e,
       *f;
{
}
p -> l_next;
	    else
		pp -> l_next = lp -> l_next;

	    free (lp -> l_lock);
	    free ((char *) lp);
	}
    }

    if (l_top)
	(void) alarm (NSECS);
}
    dev,
		 ino;
{
    register char  *bp,
                   *cp;

    bp = curlock;
    if ((cp = rindex (file, '/')) == NULmmdf/src/bboards/mmdfonly.h   444      0     12        1202  3653247042  11152 /* mmdfonly.h - include file for stand-alone BBoards MMDF-II channel */


#ifdef	V4_2BSD
#define	BSD42
#endif	V4_2BSD
					/* manifest constants */
#define	NOTOK	(-1)
#define	OK	0

#define	NULLCP	((char *) 0)

#define	MAXFOLDER	2000


					/* no mtstailor/.mh_profile here */
#define	mts_init(n)
#define	m_gmprot()	0644


					/* maildrop delimiters */
#define	mmdlm1	delim1
#define	mmdlm2	delim2

extern char *delim1;
extern char *delim2;


					/* locking parameters */
#define	lockstyle	LOK_UNIX
#define	lockldir	lckdfldir

extern char *lckdfldir;


					/* MH subroutines/stubs */
void	admonish ();
int	stringdex ();
char   *r1bindex ();
", cp - file, file);
	    bp += strlen (bp);
	}
    }
    else {
	(void) sprintf (bp, "%s/", lockldir);
	bp += strlen (bp);
    }

    switch (lockstyle) {
	case LOK_BELL: 
	default: 
	    (void) sprintf (bp, "%s.lock", cp);
	    break;

	case LOK_MMDF: 
	    (void) sprintf (bp, "LCK%05d.%05d", dev, ino);
	    break;
    }

    if (tmplock) {
	if ((cp = rindex (curlock, '/')) == mmdf/src/bboards/mts.h   444      0     12        3414  3653247042  10137 /* mts.h - definitions for the mail system */


/* Local and UUCP Host Name */

char   *LocalName (), *SystemName (), *UucpChan ();


/* Mailboxes */

extern char *mmdfldir,
            *mmdflfil,
            *uucpldir,
            *uucplfil;

#define	MAILDIR	(mmdfldir && *mmdfldir ? mmdfldir : getenv ("HOME"))
#define	MAILFIL	(mmdflfil && *mmdflfil ? mmdflfil : getusr ())
#define	UUCPDIR	(uucpldir && *uucpldir ? uucpldir : getenv ("HOME"))
#define	UUCPFIL	(uucplfil && *uucplfil ? uucplfil : getusr ())

char   *getusr (), *getfullname ();

char   *getenv ();


/* Separators */

extern char *mmdlm1,
            *mmdlm2;

#define	isdlm1(s)	(strcmp (s, mmdlm1) == 0)
#define	isdlm2(s)	(strcmp (s, mmdlm2) == 0)


/* Filters */

extern char *umincproc;


/* Locking Directory */

#define	LOK_UNIX	0
#define	LOK_BELL	1
#define	LOK_MMDF	2

#ifndef	MMDFONLY
extern int   lockstyle;
#endif	MMDFONLY
extern char *lockldir;

int	lkopen (), lkclose ();
FILE   *lkfopen ();
int	lkfclose ();

/*  */

/* MTS specific variables */

#ifdef	MHMTS
extern char *Mailqdir;
extern char *TMailqdir;
extern int Syscpy;
extern char *Overseer;
extern char *Mailer;
extern char *Fromtmp;
extern char *Msgtmp;
extern char *Errtmp;
extern int Tmpmode;
extern char *Okhosts;
extern char *Okdests;
#endif	MHMTS

#ifdef	MMDFMTS
#endif	MMDFMTS

#ifdef	SENDMTS
extern char *hostable;
extern char *sendmail;
#endif SENDMTS


/* SMTP/POP stuff */

extern char *servers;
extern char *pophost;


/* BBoards-specific variables */

extern char *bb_domain;


/* POP BBoards-specific variables */

#ifdef	BPOP
extern char *popbbhost;
extern char *popbbuser;
extern char *popbblist;
#endif	BPOP


/* MailDelivery */

extern char *maildelivery;


/* Aliasing Facility (doesn't belong here) */

extern int Everyone;
extern char *NoShell;
ccess)
register char   *file;
register int     access;
{
    register int    fd,
                    i,
		    j;

    for (i = 0; i < 5; i++) {
	if ((fd = open (file, access | O_NDELAY)) == NOTOK)
	    return NOTOK;
	if (flock (fd, LOCK_EX | LOmmdf/src/bboards/qu2bb_send.c   444      0     12        6636  3653247043  11365 #ifndef	POP
/* qu2bb_send.c - manager for qu --> bb */
#else	POP
/* qu2po_send.c - manager for qu --> po */
#endif	POP


#include "util.h"
#include "mmdf.h"
#include "phs.h"
#include "ch.h"

/*  */

extern LLog *logptr;
extern char *supportaddr;

struct rp_construct rp_hend  = { /* end of host list */
    RP_NOOP,
    'e', 'n', 'd', ' ', 'o', 'f', ' ', 'h', 'o', 's', 't', ' ',
    'i', 'g', 'n', 'o', 'r', 'e', 'd', 
    NULL
};

struct rp_construct rp_aend = {	/* end of address list */
    RP_OK,
#ifndef	POP
    'b', 'b', 'o', 'a', 'r', 'd', 's', ' ', 'e', 'n', 'd', ' ',
#else	POP
    'p', 'o', 'p', ' ', 'e', 'n', 'd', ' ',
#endif	POP
    'o', 'f', ' ', 'a', 'd', 'd', 'r', ' ', 'l', 'i', 's', 't',
    NULL
};

struct rp_construct rp_badr = {	/* no such bboard */
    RP_USER,
    'u', 'n', 'k', 'n', 'o', 'w', 'n', ' ',
#ifndef	POP
    'b', 'b', 'o', 'a', 'r', 'd',
#else	POP
    'p', 'o', 'p', ' ', 's', 'u', 'b', 's', 'c', 'r', 'i', 'b', 'e', 'r',
#endif	POP
    NULL
};

struct rp_construct rp_err = {	/* error, retry later */
    RP_AGN,
    'u', 'n', 'k', 'n', 'o', 'w', 'n', ' ', 'e', 'r', 'r', 'o', 'r',
    NULL
};

/*  */

qu2bb_send (chanptr)
Chan *chanptr;
{
    short   result;
    char    info[LINESIZE],
            sender[LINESIZE];

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "qu2bb_send()");
#endif

    if (rp_isbad (result = qu_pkinit ()))
	return result;
    if (rp_isbad (result = bb_sbinit ()))
	return result;

    while (rp_gval ((result =
	    qu_rinit (info, sender, chanptr -> ch_apout))) == RP_OK) {
#ifdef	DEBUG
	ll_log (logptr, LLOGGEN, "info=%s sender=%s", info, sender);
#endif
	if (rp_isbad (result = bb_winit (info, sender)))
	    return result;
	if (rp_isbad (result = qu2bb_each (sender)))
	    return result;
	qu_rend();
    }
    qu_rend();

    if (rp_gval (result) != RP_DONE) {
	ll_log (logptr, LLOGTMP, "not DONE [%s]", rp_valstr (result));
	return RP_RPLY;
    }

    qu_pkend ();
    bb_sbend ();

    return result;
}

/*  */
LOCFUN
qu2bb_each (sender)
char   *sender;
{
    short   result;
    char    adr[LINESIZE],
            host[LINESIZE];
    RP_Buf replyval;

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "qu2bb_each(sender='%s')", sender);
#endif

    FOREVER {			/* loop through the addresses */
	if (rp_isbad (result = qu_radr (host, adr)))
	    return result;
#ifdef	RP_HOK
	if (rp_gval (result) == RP_HOK)	{/* no-op the sub-list indication */
	    qu_wrply ((struct rp_bufstruct *) &rp_hend, rp_conlen (rp_hend));
	    continue;
	}
#endif	RP_HOK
	if (rp_gval (result) == RP_DONE) {
	    qu_wrply ((RP_Buf *) &rp_aend, rp_conlen (rp_aend));
	    return RP_OK;
	}

	switch (replyval.rp_val = bb_wtadr (host, adr)) {
	    case RP_AOK: 
	    case RP_OK: 
		switch (replyval.rp_val = bb_txtcpy ()) {
		    case RP_MOK: 
			replyval.rp_line[0] = NULL;
			break;
		    default: 
			ll_log (logptr, LLOGFAT, "unknown error [%s]",
				rp_valstr (replyval.rp_val));
			blt (&rp_err, (char *) &replyval, sizeof rp_err);
			break;
		}
		break;

	    case RP_USER: 
#ifndef	POP
		ll_log (logptr, LLOGFAT, "unknown bboard '%s'", adr);
#else	POP
		ll_log (logptr, LLOGFAT, "unknown pop subscriber '%s'", adr);
#endif	POP
		blt (&rp_badr, (char *) &replyval, sizeof rp_badr);
		break;

	    default: 
		ll_log (logptr, LLOGFAT, "unknown error [%s]",
			rp_valstr (replyval.rp_val));
		blt (&rp_err, (char *) &replyval, sizeof rp_err);
		break;
	}

	qu_wrply (&replyval,
		(sizeof replyval.rp_val) + strlen (replyval.rp_line));
    }
}
sult;

    while (rp_gval ((result =
	    qu_rinit (info, sender, chanptr -> ch_apout))) == RP_OK)mmdf/src/bboards/r1bindex.c   444      0     12         402  3653247043  11016 /* r1bindex.c - right plus 1 or beginning index */


char *r1bindex(str, chr)
register char *str;
register int chr;
{
    register char  *cp;

    for (cp = str; *cp; cp++)
	continue;
    --cp;
    while (cp >= str && *cp != chr)
	--cp;
    return (++cp);
}
	    return result;
#ifdef	RP_HOK
	if (rp_gval (result) == RP_HOK)	{/* no-op the sub-list indication */
	    qu_wrply ((struct rp_bufstruct *) &rp_hend, rp_conlen (rp_hend));
	    continue;
	}
#endif	RP_HOK
	if (rp_gval (result) == RP_DONE) {
	    qu_wrpmmdf/src/bboards/strindex.c   444      0     12         337  3653247043  11151 /* strindex.c - "unsigned" lexical index */


int  stringdex (p1, p2)
register char  *p1,
               *p2;
{
    register char  *p;

    for (p = p2; *p; p++)
	if (uprf (p, p1))
	    return (p - p2);

    return (-1);
}
= chr)
	--cp;
    return (++cp);
}
	    return result;
#ifdef	RP_HOK
	if (rp_gval (result) == RP_HOK)	{/* no-op the sub-list indication */
	    qu_wrply ((struct rp_bufstruct *) &rp_hend, rp_conlen (rp_hend));
	    continue;
	}
#endif	RP_HOK
	if (rp_gval (result) == RP_DONE) {
	    qu_wrpmmdf/src/bboards/strings.h   444      0     12        1336  3653247044  11030 /* strings.h - define standard string functions */

#ifndef	_STRINGS		/* once-only... */
#define	_STRINGS

#ifdef	SYS5
#define	index	strchr
#define	rindex	strrchr
#endif	SYS5

char   *index ();
char   *mktemp ();
char   *rindex ();
#ifndef	SYS5
char   *sprintf ();
#else	SYS5
int     sprintf ();
#endif	SYS5
char   *strcat ();
int     strcmp ();
char   *strcpy ();
int	strlen ();
char   *strncat ();
int     strncmp ();
char   *strncpy ();

char   *getenv ();
char   *calloc (), *malloc (), *realloc ();

#ifdef	SYS5
#include <memory.h>
#define	bcopy(b1,b2,length)	(void) memcpy (b2, b1, length)
#define	bcpy(b1,b2,length)	memcmp (b1, b2, length)
#define	bzero(b,length)		(void) memset (b, 0, length)
#endif	SYS5

#endif	not _STRINGS
			replyval.rp_line[0] = NULL;
			break;
		    default: 
			ll_log (logptr, LLOGFAT, "unknown error [%s]",
				rp_valstr (replyval.rp_val));
			blt (&rp_err, (char *) &replyval, sizeof rp_err);
			break;
		}
		break;

	    case RP_USER: 
#ifndef	POP
		ll_log (logptr, LLOGFAT, "unknown bboammdf/src/bboards/uprf.c   444      0     12         342  3653247044  10262 /* uprf.c - "unsigned" lexical prefix  */


uprf (c1, c2)
register char  *c1,
               *c2;
{
    register int    c;

    while (c = *c2++)
	if ((c | 040) != (*c1 | 040))
	    return 0;
	else
	    c1++;

    return 1;
}
x ();
#ifndef	SYS5
char   *sprintf ();
#else	SYS5
int     sprintf ();
#endif	SYS5
char   *strcat ();
int     strcmp ();
char   *strcpy ();
int	strlen ();
char   *strncat ();
int     strncmp ();
char   *strncpy ();

char   *getenv ();
char   *calloc (), *malloc (), *realloc ();

#ifdef	mmdf/src/Makefile   444      0     12        2330  3671116727   7210 #	MMDF Program Source Subtree Makefile
#

#
# Programs that live in subdirectories, and have makefiles of their own.
#
SUBDIR=	tools submit deliver local list delay smtp phone pobox \
	uucp prog bboards pop

#
#  This should list all the subdirectories that make clean works in.
#  This probably should not be altered unless you add/delete a channel.
#
ALLSUBDIRS=	tools submit deliver local list delay smtp phone pobox \
		uucp niftp bboards ean prog pop

all:
	for dir in ${SUBDIR}; do \
		(cd $${dir}; gen ${MFLAGS} -${MAKEFLAGS}); done

install:
	for dir in ${SUBDIR}; do \
		(cd $${dir}; gen ${MFLAGS} -${MAKEFLAGS} install); done

dist:
	-touch `ls */x*|grep -v '\.'`
	echo echo MMDF Distribution made `date` > install.sh
	for dir in ${SUBDIR}; do \
		echo cd $${dir} >> install.sh; \
		( cd $${dir}; \
		  gen -n ${MFLAGS} -${MAKEFLAGS} install) >> install.sh; \
		echo cd .. >> install.sh; \
	done
	tar cvf mmdfdist.tar install.sh */x*

depend:
	for dir in ${SUBDIR}; do \
		(cd $${dir}; gen ${MFLAGS} -${MAKEFLAGS} depend); done

lint:
	for dir in ${SUBDIR}; do \
		(cd $${dir}; gen ${MFLAGS} -${MAKEFLAGS} lint); done

clean:
	-rm -f make.out core
	for dir in ${ALLSUBDIRS}; do \
		(cd $${dir}; make -f Makefile.real clean); done
error [%s]",
			rp_valstr (replyval.rp_val));
		blt (&rp_err, (char *) &replyval, sizeof rp_err);
		break;
	}

	qu_wrply (&replyval,
		(sizeof replyval.rp_val) + strlen (replyval.rp_line));
    }
}
sult;

    while (rp_gval ((result =
	    qu_rinit (info, sender, chanptr -> ch_apout))) == RP_OK)mmdf/src/delay/   755      0     12           0  3657740066   6570 mmdf/src/delay/Makefile.real   444      0     12        2144  3635174142  11226 #
#  List Channel
#

MODULES =	ch_delay qu2ds_send

OBJECTS =	ch_delay.o qu2ds_send.o

SOURCES =	ch_delay.c qu2ds_send.c

real-default:	delay

install:	$(CHANDIR)/delay

$(CHANDIR)/delay  :   xdelay
	     cp xdelay $(CHANDIR)/delay
	    -$(CHOWN) $(MMDFLOGIN) $(CHANDIR)/delay
	    -chmod 0$(PGMPROT) $(CHANDIR)/delay
	    -@ls -ls $(CHANDIR)/delay
	    -@echo "delay channel installed normally"; echo ""

delay:	xdelay
xdelay:	$(OBJECTS) $(MMDFLIBS)
	$(CC) $(LDFLAGS) -o $@ $(OBJECTS) $(MMDFLIBS) $(SYSLIBS)

lint:   ;  $(LINT) $(LFLAGS) $(SOURCES) $(LLIBS)

clean:
	-rm -f xdelay *.o x.c makedep eddep make.out errs


# DO NOT DELETE THIS LINE -- make depend uses it
ch_delay.o: ch_delay.c
ch_delay.o: ch_delay.c
ch_delay.o: ../../h/util.h
ch_delay.o: ../../h/mmdf.h
ch_delay.o: /usr/include/signal.h
ch_delay.o: ../../h/phs.h
ch_delay.o: ../../h/ch.h
qu2ds_send.o: qu2ds_send.c
qu2ds_send.o: ../../h/util.h
qu2ds_send.o: ../../h/mmdf.h
qu2ds_send.o: ../../h/phs.h
qu2ds_send.o: ../../h/ch.h
qu2ds_send.o: ../../h/ap.h
# DEPENDENCIES MUST END AT END OF FILE
# IF YOU PUT STUFF HERE IT WILL GO AWAY
# see make depend above
one

clean:
	-rm -f make.out core
	for dir in ${ALLSUBDIRS}; do \
		(cd $${dir}; make -f Makefile.real clean); done
error [%s]",
			rp_valstr (replyval.rp_val));
		blt (&rp_err, (char *) &replyval, sizeof rp_err);
		break;
	}

	qu_wrply (&replyval,
		(sizeof replyval.rp_val) + strlen (replyval.rp_line));
    }
}
sult;

    while (rp_gval ((result =
	    qu_rinit (info, sender, chanptr -> ch_apout))) == RP_OK)mmdf/src/delay/ch_delay.c   444      0     12        6400  3620510565  10554 #include "util.h"
#include "mmdf.h"
/*
 *     MULTI-CHANNEL MEMO DISTRIBUTION FACILITY  (MMDF)
 *
 *
 *     Copyright (C) 1979,1980,1981  University of Delaware
 *
 *     Department of Electrical Engineering
 *     University of Delaware
 *     Newark, Delaware  19711
 *
 *     Phone:  (302) 738-1163
 *
 *
 *     This program module was developed as part of the University
 *     of Delaware's Multi-Channel Memo Distribution Facility (MMDF).
 *
 *     Acquisition, use, and distribution of this module and its listings
 *     are subject restricted to the terms of a license agreement.
 *     Documents describing systems using this module must cite its source.
 *
 *     The above statements must be retained with all copies of this
 *     program and may not be removed without the consent of the
 *     University of Delaware.
 *
 *
 *     version  -1    David H. Crocker    March   1979
 *     version   0    David H. Crocker    April   1980
 *     version  v7    David H. Crocker    May     1981
 *     version   1    David H. Crocker    October 1981
 *
 */

/*                  DELAYED PROCESSING CHANNEL                          */
/*                  hacked up from the list channel Phil UCL Nov85      */

#include <signal.h>
#include "phs.h"
#include "ch.h"

extern char *dupfpath();
extern LLog *logptr;

Chan *chanptr;
char obuf[BUFSIZ];           /* Buffer for status printfs */

/*      MAIN MAIN MAIN MAIN MAIN MAIN MAIN MAIN MAIN MAIN MAIN MAIN     */

main (argc, argv)
int       argc;
char   *argv[];
{
    short retval;

    mmdf_init (argv[0]);
    setbuf( stdout, obuf );

#ifdef RUNALON
    logptr -> ll_fd = 1;
    ll_init (logptr);
#endif

    siginit ();
    signal (SIGINT, SIG_IGN);     /* always ignore interrupts             */

    if ((chanptr = ch_nm2struct (argv[0])) == (Chan *) NOTOK)
    {
	ll_log (logptr, LLOGTMP, "delayproc (%s) unknown channel", argv[0]);
	exit (RP_PARM);
    }
    retval = ch_delay (argc, argv);
    ll_close (logptr);
    exit (retval);
}
/***************  (ch_) DELAYED SUBMISSION DELIVERY  ***************** */

ch_delay (argc, argv)              /* send to internet                   */
int       argc;
char   *argv[];
{
    ch_llinit (chanptr);
#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "ch_delay()");
#endif

    if (rp_isbad (qu_init (argc, argv)))
	return (RP_NO);           /* problem setting-up for deliver     */

    phs_note (chanptr, PHS_WRSTRT);

    if (rp_isbad (qu2ds_send ()))
	return (RP_NO);           /* send the batch of outgoing mail    */

    phs_note (chanptr, PHS_WREND);

    qu_end (OK);                  /* done with Deliver function         */

    return (RP_OK);               /* NORMAL RETURN                      */
}

/**/

/* VARARGS2 */
err_abrt (code, fmt, b, c, d)     /* terminate ourself                  */
short     code;
char    fmt[],
	b[],
	c[],
	d[];
{
#ifdef DEBUG
    char linebuf[LINESIZE];
#endif

    qu_end (NOTOK);

#ifdef DEBUG
    if (rp_gbval (code) == RP_BNO || logptr -> ll_level >= LLOGBTR)
    {                         /* don't worry about minor stuff      */
	sprintf (linebuf, "%s%s", "err [ ABEND (%s) ]\t", fmt);
	ll_log (logptr, LLOGFAT, linebuf, rp_valstr (code), b, c, d);
	abort ();
    }
#endif
    ll_close (logptr);            /* in case of cycling, close neatly   */

    exit (code);
}
pen (file, strcmp (mode, "r") ? 2 : 0)) == NOTOK)
	return NULL;

    if ((fp = fdopen (fd, mode)) == NULL) {
	(void) close (fd);
	return NULL;
    }

    return fp;
}


/* ARGSUSED */

int	lkfclose (fp, file)
register FILE	*fp;
register char	*file;
{
    cmmdf/src/delay/gen   555      0     12          57  3620510566   7302 make -f ../../Makefile.com -f Makefile.real $*
                 DELAYED PROCESSING CHANNEL                          */
/*                  hacked up from the list channel Phil UCL Nov85      */

#include <signal.h>
#include "phs.h"
#include "ch.h"

extern char *dupfpath();
extern LLog *logptr;

Chan *chanptr;
char obuf[BUFSIZ];           /* Buffer for status printfs */

/*      MAIN MAIN MAIN MAIN MAIN MAIN MAIN MAIN MAIN MAIN MAIN MAIN     */

main (argc, argv)
int       argc;
char   *argv[];
{
    short rmmdf/src/delay/qu2ds_send.c   444      0     12       20637  3657737763  11130 #
/*
 *     MULTI-CHANNEL MEMO DISTRIBUTION FACILITY  (MMDF)
 *
 *     Copyright (C) 1979,1980,1981  University of Delaware
 *
 *     Department of Electrical Engineering
 *     University of Delaware
 *     Newark, Delaware  19711
 *
 *     Phone:  (302) 738-1163
 *
 *     This program module was developed as part of the University
 *     of Delaware's Multi-Channel Memo Distribution Facility (MMDF).
 *
 *     Acquisition, use, and distribution of this module and its listings
 *     are subject restricted to the terms of a license agreement.
 *     Documents describing systems using this module must cite its source.
 *
 *     The above statements must be retained with all copies of this
 *     program and may not be removed without the consent of the
 *     University of Delaware.
 *
 *
 *     version  -1    David H. Crocker    March   1979
 *     version   0    David H. Crocker    April   1980
 *     version  v7    David H. Crocker    May     1981
 *     version   1    David H. Crocker    October 1981
 *
 */
/*                SEND FROM DELIVER TO SUBMIT
 *
 *  Feb 83  Doug Kingston       Initial version of list processor
 *  Nov 85  Phil Cockcroft      Hacked into a delay channel
 *  Apr 86  Craig Partridge     Diddled for efficiency reasons
 */

#include "util.h"
#include "mmdf.h"
#include "phs.h"
#include "ch.h"
#include "ap.h"
#include "ns.h"

extern struct ll_struct   *logptr;
extern Chan *chanptr;

extern char *rindex();
extern char *strdup();
extern char *multcat();
extern char *blt();

LOCVAR int nadrs;
LOCVAR char sender[ADDRSIZE];

LOCVAR struct rp_construct
	rp_bdrem =
{
    RP_BHST, 'B', 'a', 'd', ' ', 'r', 'e', 's', 'p', 'o', 'n', 's', 'e',
    ' ', 'f', 'r', 'o', 'm', ' ', 's', 'u', 'b', 'm', 'i', 't', '\0'
},
	rp_adr =
{
    RP_AOK, 'a', 'd', 'd', 'r', 'e', 's', 's', ' ', 'o', 'k', '\0'
},
	rp_gdtxt =
{
    RP_MOK, 't', 'e', 'x', 't', ' ', 's', 'e', 'n', 't', ' ', 'o', 'k', '\0'
},
	rp_nadr =
{
    RP_BTNO, 'N', 'o', ' ', 'v', 'a', 'l', 'i', 'd', ' ', 
    'a', 'd', 'r', 'e', 's', 's', 'e', 's', '\0'
},
	rp_noop =
{
    RP_NOOP, 's', 'u', 'b', '-', 'l', 'i', 's', ' ', 'n', 'o', 't', ' ',
    's', 'p', 'e', 'c', 'i', 'a', 'l', '\0'
};

/**/

qu2ds_send ()             /* overall mngmt for batch of msgs    */
{
    int		started = FALSE;
    short       result;
    char        info[LINESIZE];

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "qu2ds_send ()");
#endif

    if (rp_isbad (result = qu_pkinit ()))
	return (result);

    /*
     *  While there are messages to process ...
     */
    for(;;){                /* get initial info for new message   */
	result = qu_rinit (info, sender, chanptr -> ch_apout);

	switch (rp_gval(result)) {

	    case RP_NS:		/* keep plugging */
	    case RP_FIO:	/* couldn't open this message file */
		qu_rend();
		continue;

	    case RP_OK:
		break;

	    default:
		goto done;
	}

	/* make sure we have a submit there */
	if ((!started) && (rp_isbad(mm_init())  || rp_isbad(mm_sbinit()))) {
	    /* do we want to tell deliver what happened? */
	    goto done;
	}
	started = TRUE;
	    
	/* now send message */
	switch(rp_gval(result = qu2ds_each ())) {

	    case RP_OK:		/* good! */
	    case RP_MECH:	/* bad queue file */
		break;

	    case RP_BTNO:	/* submit was cranky */
		started = FALSE;
		break;

	    case RP_BNO:	/* deliver hassled us */
	    default:		/* something worse happened */
		goto done;
	}

	qu_rend();
    }

done:
    qu_rend();

    if (rp_gval (result) != RP_DONE)
    {
	ll_log (logptr, LLOGFAT, "not DONE (%s)", rp_valstr (result));
	return (RP_RPLY);         /* catch protocol errors              */
    }

    /* shut down submit nicely */
    if (started) {
	mm_sbend();
	mm_end(OK);
    }


    qu_pkend ();                  /* done getting messages              */
    return (result);
}
/**/

LOCFUN
	qu2ds_each ()             /* send copy of text per address      */
{
    struct rp_bufstruct thereply;
    short   result;
    int     len;
    char    host[ADDRSIZE];
    char    adr[ADDRSIZE];
    char    *dchan, *dhost;
    char    info[ADDRSIZE];
    char    *index();
    int     ndone = 0;

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "qu2ds_each()");
#endif

    nadrs = 0;

    /*
     *  For every address in the message ...
     */
    while( 1 ) {
	result = qu_radr (host, adr);
	if (rp_isbad (result))
	    return (RP_BNO);      /* deliver goofed */

	switch (rp_gval (result))
	{
	    case RP_HOK:          /* end of sub-list */
		qu_wrply ((RP_Buf *)&rp_noop, sizeof rp_noop);
		break;

	    case RP_DONE:         /* end of full address list           */
		/* only send text if got some addresses done */
		if(ndone != 0)
		    qu2ds_txtcpy(&thereply);
		else 
		    blt((char *)&rp_nadr, (char *)&thereply, sizeof(rp_nadr));

		qu_wrply(&thereply, sizeof (thereply.rp_val)
					+ strlen (thereply.rp_line));

		if(ndone == 0 || rp_isbad(thereply.rp_val)) {
		    mm_end(NOTOK);
		    return(RP_BTNO);
		}
		return (RP_OK);         /* END for this message */

	    default:            /* actually have an address */

		if (nadrs++ == 0)
		{
		    if((dhost = index(host, '&')) != NULL){
			*dhost++ = 0;
			dchan = host;
			if(*dchan == '\0'){ /* no channel must be local */
			    dchan = 0;
			    sprintf(info, "vmjlk%d*",NS_DELTIME);
			}
			else if(*dhost == '\0')
			    sprintf(info, "vmjlk%d*",NS_DELTIME);
			else
			{
			    /* reset the host as before
			    * dchan is already set */
			    sprintf(info, "vmjlh%s*k%d", dhost,NS_DELTIME);
			}

		    if (domsg)
			strcat(info,"W");
#ifdef DEBUG
		    ll_log (logptr, LLOGBTR, "d2s dchan = '%s' info = '%s'",
				dchan ? dchan : "NULL", info);
#endif
		    } else {
#ifdef DEBUG
			ll_log (logptr, LLOGFAT, "Missing '&' in delay");
#endif
			return(RP_MECH);
		    }

		    /* tell submit about this message */
		    if (rp_isbad (mm_winit (dchan, info, sender))
			  || rp_isbad (mm_rrply(&thereply, &len))) {
			mm_end (NOTOK);
			printx ("Error in submit startup\n");
			fflush (stdout);
			qu_wrply((RP_Buf *)&rp_bdrem, sizeof rp_bdrem);
			return(RP_BTNO);
		    }

		    switch (rp_gbval (thereply.rp_val)) {
		    case RP_BNO:
		    case RP_BTNO:
			mm_end (NOTOK);
			printx ("Error in submit startup (%s)\n", thereply.rp_line);
			fflush (stdout);
			qu_wrply(&thereply, len);
			return(RP_BTNO);

		    default:
			break;
		    }

		    if (rp_isbad (mm_wadr ("", adr))
			  || rp_isbad (mm_rrply(&thereply, &len))) {
			mm_end (NOTOK);
			printx ("Error in passing address\n");
			fflush (stdout);
			qu_wrply((RP_Buf *)&rp_bdrem, sizeof rp_bdrem);
			return(RP_BTNO);
		    }
		} /* naddrs = 0 */
		else
		{
		    if (rp_isbad (mm_wadr ("", adr))
			   || rp_isbad (mm_rrply(&thereply, &len)))
		    {
			mm_end (NOTOK);
			printx ("Error in passing address\n");
			fflush (stdout);
			qu_wrply((RP_Buf *)&rp_bdrem, sizeof rp_bdrem);
			return(RP_BTNO);
		    }
		}  /* naddrs != 0 */
	} /* switch */

	switch (rp_gval (thereply.rp_val))
	{                 /* was address acceptable?            */
	    case RP_AOK:
		qu_wrply((RP_Buf *)&rp_adr, sizeof  rp_adr);
		ndone++;
		continue;

	    case RP_NAUTH:	/* errg !! */
		thereply.rp_val = RP_USER;
		break;

	    case RP_PARM:
	    case RP_USER:
	    case RP_NO:
	    case RP_NS:
		break;    /* report failure and continue        */

	    case RP_RPLY:
		ll_log (logptr, LLOGTMP, "unusual return: (%s)%s",
			rp_valstr (thereply.rp_val), thereply.rp_line);
		break;
	}
	/* tell deliver about address */
	qu_wrply (&thereply, len);
    }
}

/**/

LOCFUN
	qu2ds_txtcpy (rp)     /* copy the text of the message       */
RP_Buf *rp;
{
    int       len;
    short     result;
    char      buffer[BUFSIZ];

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "qu2ds_txtcpy()");
#endif

    qu_rtinit (0L);

    if (rp_isbad (result = mm_waend())) {
	blt ((char *)&rp_bdrem, (char *) rp, sizeof rp_bdrem);
	return;
    }

    printx ("sending...:");
    fflush (stdout);

    len = sizeof(buffer);
    while ((rp_gval (result = qu_rtxt (buffer, &len))) == RP_OK)
    {
	result = mm_wtxt (buffer, len);
	if (rp_isbad (result))
	    break;
	printx (".");
	fflush (stdout);
	if (rp_gval (result) != RP_OK) {
	    blt ((char *)&rp_bdrem, (char *) rp, sizeof rp_bdrem);
	    return;
	}
    	len = sizeof(buffer);
    }

    if (rp_isbad (result) || rp_gval (result) != RP_DONE) {
	blt ((char *)&rp_bdrem, (char *) rp, sizeof rp_bdrem);
	return;
    }

    if (rp_isbad(mm_wtend()) || rp_isbad(mm_rrply(rp,&len))) {
	rp->rp_val = RP_RPLY;
	strcpy (rp->rp_line, "Unknown Problem");
	return;
    }
    
    blt ((char *)&rp_gdtxt, (char *)rp, sizeof rp_gdtxt);
}
oop);
		break;

	    case RP_DONE:         /* end of full address list           */
		/* only senmmdf/src/niftp/   755      0     12           0  3664425311   6601 mmdf/src/niftp/ni_mail.c   444      0     12       10627  3620510546  10463 #include "util.h"
#include "mmdf.h"

/*  Main routine called by Q NIFTP process to invoke            */
/*  Q NIFTP channel of MMDF                                     */
/*                                                              */
/*  Note that the files are prefixed nn_                        */
/*  wheras the routines are prefixed ni_                        */
/*  This is to distinguish between linked and unlinked files    */
/*                                                              */
/*      Steve Kille     August 1982                             */

#define RETRY_TIME      5       /* Delay between retries in secs*/


extern char *cmddfldir;
				/* Default directory for NIFTP  */
				/* to MMDF routine              */

char nichan[] = "ni_niftp";


extern int   ipcs_print;

extern char *dupfpath ();


LOCVAR char tmpstring[LINESIZE];
				/* static buffer for return message */

/**/

ni_mail (fname, queue, TSname, max_tries, rpstring)
char  fname[];                  /* File to be transferred       */
char  queue[];                  /* name of NIFTP queue          */
char  TSname[];                 /* TS name of calling host      */
int   max_tries;                /* Max number of retries        */
				/* If 0 mailer will detach and  */
				/* Not return                   */
char  **rpstring;               /* Where to stuff result string */
				/* This might be passed to the  */
				/* remote NIFTP                 */
{
    int   pid;                  /* for fork                     */
    int   status;
    char  chpath[LINESIZE];     /* Full path of NIFTP channel   */
    int   no_tries;             /* Number of tries to deliver   */
    int   fd;                   /* temp file pointer            */



    if (ipcs_print > 0)
	printf ("ni_mail (f='%s', q='%s', TS='%s', n='%d')\n", fname,
		queue, TSname, max_tries);


    *rpstring = tmpstring;
		/* point to line buffer                         */


    getfpath (nichan, cmddfldir, chpath);
					/* get path of channel process  */


				/* Now try to submit the mail to the chan */
				/* In the case of partial failure repeat  */
				/* as specified by max_tries              */
    if (ipcs_print > 0)
	printf ("execl (%s, %s, %s, %s, %s)", chpath, nichan, queue,
		fname, TSname);


    no_tries = 0;               /* First attempt                          */
    FOREVER                     /* no test here as we must enter once     */
    {
	if ((pid = fork ()) == NOTOK)
	{
	    status = RP_AGN;    /* Should be temporary                    */
	    continue;
	}

	switch (pid)
	{
	    case 0:            /* In the child                            */
				/* close off any open files  NIFTP leaves   */
				/* lying around                             */
		for (fd = 0; fd < _NFILE; fd++)
			close (fd);
				/* open standard output                    */
		open ("/dev/null", 1);

		execl (chpath, nichan, queue, fname, TSname, (char *)0);
				/* If we get here something hs screwed      */
		exit (RP_MECH);
		break;

	   default:
		if (max_tries < 1)      /* don't wait around              */
		{
		    sprintf (tmpstring,
				"Not waiting for MMDF to transfer mail");
		    return (OK);
		}

		status = pgmwait (pid); /* hang around for MMDF            */
					/* but not any of its children     */
					/* should protect this with alarms */
					/* But UCL being the place it is...*/

		if (status == 0)
					/* Fatal crash                  */
		{
		    sprintf (tmpstring, "MMDF crashed - aarrg");
		    return (NOTOK);
		}

		if (rp_isgood (status))
					/* Success                         */
		{
		    sprintf (tmpstring, "Mail transferred correctly (%s)",
						rp_valstr (status));
		    return (OK);
		}

		if ((rp_gbval (status)) ==  RP_BNO)
					/* Complete disaster so go home     */
		{
		    switch (status)
		    {
			case  RP_NDEL:
				sprintf (tmpstring, "No valid recipients" );
				return (OK);
				break;
			default:
				sprintf (tmpstring,
					"Fatal error on mail transfer (%s)",
					rp_valstr (status));
				return (NOTOK);
		     }
		}
	}
					/* Do we need to make any more tries*/
	if (no_tries++ >= max_tries)
	    break;
					/* Otherwise sleep a while and then */
					/* Try again                        */
	sleep (RETRY_TIME);
   }

					/*  We have tried as many times as  */
					/*  allowed                         */

    sprintf (tmpstring, "Repeated temporary MMDF failure (%s) after %d attempts",
			      rp_valstr (status), no_tries);
    return (NOTOK);
}


*/


    getfpath (nichan, cmddfldir, chpath);
					/* get path of channel process  */


				/* Now try tmmdf/src/niftp/qn_rdmail.c   444      0     12       30404  3664425212  11017 /*                                                                      */
/*                  CH_QNIFTP: NIFTP MAIL RECEIVER CHANNEL              */
/*                                                                      */
/*      Developed originally by Chris Bennett - May 1981                */
/*                                                                      */
/*      Rewritten completely by Steve Kille - August 1982               */

#include "util.h"
#include "mmdf.h"
#include "ap.h"
#include "ch.h"
#include "dm.h"

extern struct ll_struct   *logptr;

extern Chan *curchan;

extern Table *tb_nm2struct();
extern Chan *ch_nm2struct();
extern Domain *dm_v2route();
extern char *ap_dmflip();
extern char *ap_p2s();

extern qn_rdchar ();

LOCVAR  FILE    *qn_fp;         /* File pointer for current file        */
LOCVAR  long    curpos;         /* Current position in file             */
LOCVAR  char    eoheader;       /* Have we reached end of JNT header    */
LOCVAR  char    adr_keyend[] = "\r\n,\377";
				/* Set of characters terminating JNT    */
				/* Mail address                         */
LOCVAR  char    *ack_adr;       /* acknowlege-to address                */

LOCVAR  char    qn_nichanlist [LINESIZE]; /* list of valid channels     */

/*
*/
qn_init (ni_queue)              /* Initialise channel                   */
				/* No-op                                */
char    *ni_queue;              /* NIFTP queue associated with channel  */
{
    Table *tb_niftp;

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "qn_init (%s)", ni_queue);
#endif

    if ((tb_niftp = tb_nm2struct ("niftp.nets")) == (Table *) NOTOK)
    {
	ll_err (logptr, LLOGTMP, "Failure to locate NIFTP net table");
	return (RP_NO);
    }
				/* Extract name of current channel      */
				/* from list of NIFTP channels          */
    if (tb_k2val (tb_niftp, TRUE, ni_queue, qn_nichanlist)!=OK)
    {
	ll_err (logptr, LLOGFAT, "Unknown NIFTP queue \"%s\"", ni_queue );
	return (RP_PARM);
				/* all NIFTP channels should be in the  */
				/* mapping table - if not then error    */
    }

    return (RP_OK);
}

qn_end (state)                  /* Terminate channel                    */
int state;                      /* No-op                                */
{
#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "qn_end (%d)", state);
#endif
    return (RP_OK);
}

qn_pkinit ()                    /* Initialise for batch submission      */
				/* Not now, but perhaps later on again  */
				/* when NIFTP is working properly       */
				/* No-op                                */
{
#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "qn_pkinit ()");
#endif
    return (RP_OK);
}

qn_pkend ()                     /* End batch submission                 */
				/* No-op                                */
{
#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "qn_pkend ()");
#endif
    return (RP_OK);
}

/*
*/
				/* Initialise for processing message    */
qn_rinit (fname, TSname, info, sender)
char    *fname;                 /* Name of file containing JNT message  */
char    *TSname;                /* TS name of calling host              */
char    *info;                  /* place to stuff info                  */
char    *sender;                /* place to stuff name of message sender*/
{
     char       *reverse;       /* Pointer to reverse ordered domain    */
     char       tmpstr [LINESIZE];
     char       official [LINESIZE];
     Dmn_route  route;
     char       chan_buf [LINESIZE];
     int        argc, n;
     char       *argv[20];      /* lots of channels */

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "qn_rinit (%s, %s)", fname, TSname );
#endif

    rtn_init ();
    ack_adr = (char *) 0;

    reverse = ap_dmflip (TSname);
    official [0] = 0;

    strcpy (chan_buf, qn_nichanlist);
    if ((argc = str2arg (chan_buf, 20, argv, (char *)0)) == NOTOK)
    {
	ll_err (logptr, LLOGFAT, "qn_rinit: str2arg failed '%s'", chan_buf);
	return (RP_LIO);
    }
    for ( n = 0 ; n < argc ; n++)
    {
	if ((curchan = ch_nm2struct (argv[n])) == (Chan *) NOTOK)
	{
	    ll_err (logptr, LLOGTMP, "qn_rinit: Bad channel '%s'", argv[n]);
	    continue;
	}

			/* See if we know it, NRS order first    */
	if (tb_k2val (curchan -> ch_table, TRUE, reverse, tmpstr)==OK)
	{
	    /* should check return value ?? */
	    dm_v2route (reverse, official, &route);
	    sprintf (info, "h%s*", official);
	    break;
	}
	else if (tb_k2val (curchan -> ch_table, TRUE, TSname, tmpstr)==OK)
	{
	    /* should check return value ?? */
	    dm_v2route (TSname, official, &route);
	    sprintf (info, "h%s*", official);
	    break;
	}
    }
				/* Strip "," and "*" from host name     */
				/* as it may be TS name with funnies    */
				/* we might as well munge this!!        */
    if (official [0] == '\0')
    {
	ll_err (logptr, LLOGTMP, "qnrinit: Unknown source address '%s'",TSname);
	return (RP_NDEL);
    }

#ifdef DEBUG
    ll_log (logptr, LLOGFTR, "We are channel '%s'", curchan -> ch_name);
#endif
    free (reverse);
    ch_llinit(curchan);

#ifdef DEBUG
    ll_log (logptr, LLOGFTR, "Info string is \"%s\"", info);
#endif

				/* Now open up the message file         */

    if ((qn_fp = fopen (fname, "r")) == NULL )
    {
	ll_err (logptr, LLOGTMP, "can't open file '%s'",
			    fname);
	return (RP_FIO);
    }

				/* Extract sender name from text        */
				/* This is pretty ugly, but the best    */
				/* That JNT mail can do                 */
				/* If there is no sender field valid    */
				/* dont worry about it                  */
    sender [0] = '\0';
    if (rp_isbad(hdr_sender (sender, &ack_adr, qn_fp, official)))
	return (RP_NDEL);
				/* now return to the start of the file  */

    ll_log (logptr, LLOGFST, "sender: '%s'", sender);

    fseek (qn_fp, 0L, 0);
    if (ferror (qn_fp))
    {
	ll_err (logptr, LLOGTMP, "Fail to seek on file '%s'", fname);
	return (RP_FIO);
    }
    curpos = 0;                 /* mark postition                       */

    eoheader = FALSE;           /* ready to read JNT header             */

    return (RP_OK);
}
/*
*/
extern char *rindex ();

qn_radr (host, adr)             /* extract next address from file       */
				/* might get confused by very badly     */
				/* damaged files                        */
char    *host;
char    *adr;
{
    char        *strptr;
    char        *cp;
    AP_ptr      local,          /* pointers for parse coponents         */
		domain,
		route;
    AP_ptr      ptr;
    AP_ptr      ap;

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "qn_radr ()");
#endif

    if ((ptr = ap_pinit (qn_rdchar)) == (AP_ptr) NOTOK)
    {
	ll_err (logptr, LLOGTMP, "Problem initialising for parse");
	return (RP_LIO);
    }

#ifdef DEBUG
     ll_log (logptr, LLOGFTR, "Parse corectly initialised");
#endif

    switch ( ap_1adr ())
    {
	case NOTOK:
	    ap_free (ptr);
	    ll_log (logptr, LLOGFAT, "Bad format address in JNT header");
	    return (RP_HUH);
	case DONE:
	    ap_free (ptr);
	    return (RP_DONE);   /* end of address list                  */
    }

				/* Now analyse address                  */

    ap_t2parts (ptr, (AP_ptr *) 0, (AP_ptr *) 0, &local, &domain, &route);
				/* remove any trailing DLIT          */
    if (route == (AP_ptr) 0)
    {
	if (domain ->  ap_obtype == APV_DLIT)
	{
	    ap_free (domain);
	    domain = (AP_ptr) 0;
	}
    }
    else
    {
	if (route -> ap_obtype == APV_DLIT)
	{
	    ap = route;
	    route = ap -> ap_chain;
	    if (route -> ap_obtype != APV_DLIT ||
		route ->  ap_obtype != APV_DOMN)
	    route = (AP_ptr) 0;
	    ap_free (ap);
	}
    }

    strptr = ap_p2s ((AP_ptr) 0, (AP_ptr) 0, local, domain, route);
    if(strptr == (char *)MAYBE)
	return(RP_NS);

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "Full address is '%s'", strptr);
#endif
    strcpy (adr, strptr);
    cp = rindex (adr, '@');
    if (cp == (char *) 0)
	host [0] = '\0';
    else
    {
	*cp  = '\0';
	strcpy (host, ++cp);
    }
    free (strptr);

    ll_log (logptr, LLOGFST, "JNT Address h='%s', a='%s'", host, adr);
    return (RP_AOK);
}


/*
*/
qn_raend ()                      /* Reached end of address list          */
				/* skip ofver any LWSP before 733 header*/
{
    int         c;

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "qn_raend ()");
#endif

    FOREVER
    {
	if ((c = getc (qn_fp)) == EOF)
	{
	    ll_err (logptr, LLOGTMP, "JNT mail header with no body" );
	    return (RP_NO);
	}
	curpos++;
	if (!isspace (c))       /* reached start of 733 header          */
	{
	    fseek (qn_fp, --curpos, 0);
				/* set back one character               */
	    if (ferror (qn_fp))
		return (RP_FIO);
	    return (RP_OK);
	}
    }
}
/*
*/
qn_wrply (reply, adr, host)     /* Used to mark status of address       */
				/* If reply is bad, passed downwards    */
				/* to build up list of erroneous        */
				/* addresses to be passed back          */
struct rp_bufstruct  *reply;
char    *adr,
	*host;
{
    char   line[LINESIZE];
#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "qn_wrply ()");
#endif

    if (rp_isgood (reply -> rp_val))
    {
	sprintf (line, "%s@%s", adr, host);
	return (rtn_adr (line, TRUE));
    }
    else
    {
	strcpy (line, reply -> rp_line);
	return (rtn_adr (line, FALSE));
    }

}
/*
*/
qn_rtxt (buffer, len)             /* read buffered block of text        */
char   *buffer;                   /* where to stuff next part of text   */
int    *len;                      /* where to stuff length of part      */
{
#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "qn_rtxt ()");
#endif


    switch (*len = gcread (qn_fp, buffer, *len - 1, "\n\000\377"))
    {                             /* raw read of the file               */
	case NOTOK:
	    ll_err (logptr, LLOGFAT, "File read error");
	    return (RP_FIO);

	case OK:                  /* end of message; not treated as EOF */
#ifdef DEBUG
	    ll_log (logptr, LLOGBTR, "qn_rtxt (DONE)");
#endif
	    return (RP_DONE);
    }
    switch (*len)
    {
	case 0:
	    return (RP_DONE);

	case 1:
	    if (isnull (buffer[0]))
		return (RP_DONE);
    }
    buffer[*len] = '\0';          /*   so it can be ll_loged as a string  */
#ifdef DEBUG
    ll_log (logptr, LLOGFTR, "\"%s\"", buffer);
#endif
    return (RP_OK);
}
/**/
extern char *supportaddr;

qn_rtend (sender, fname, alright)
				/* finished reading text - close file      */
				/* send off error message                  */
char    *sender;        /* sender of message                               */
char    *fname;         /* full pathname of file                           */
int     alright;
{
    int retval;
#ifdef debug
    ll_log (logptr, LLOGBTR, "qn_rtend ()");
#endif

    retval = RP_OK;
    if (rp_isgood (alright))
    {
	if (ack_adr  != (char *) 0)
	{
	    rtn_ack (ack_adr, qn_fp);
				/* don't care if this fails             */
	    free (ack_adr);
	}

	if (rtn_errmsg (sender, qn_fp) == NOTOK)
	{
	    ll_log (logptr,  LLOGTMP, "Failed to send error mesage to '%s'",
				sender);
	    if (rtn_errmsg (supportaddr, qn_fp) == NOTOK)
	    {
		ll_log (logptr, LLOGTMP, "Failed to send error message to spport addr");
		retval = RP_NO;
	    }
	}
				/* fire off error message                  */
				/* pass error upwards if it fails          */
    }


    if ((qn_fp != NULL) && (fclose (qn_fp) == NOTOK)) /* close file */
    {
	ll_err (logptr, LLOGFAT, "Failure to close file '%s'", fname);
	return (RP_FIO);
    }

    if (unlink (fname) == NOTOK)
				/* delete file                            */
    {
	ll_err (logptr, LLOGFAT, "Failure to unlink file '%s'", fname);
	return (RP_FOPN);
    }

    return (retval);
}
/**/

LOCFUN
qn_rdchar ()                    /* read chars from jnt header           */
				/* stop when we reach blank line        */
{
    int         c;

#ifdef DEBUG
   ll_log (logptr, LLOGMAX, "qn_rdchar ()");
#endif

    if (eoheader)
	return (EOF);

    if ((c = getc (qn_fp)) == EOF)
    {
	ll_err (logptr, LLOGTMP, "Premature end of JNT Mail Header");
	return (EOF);
    }
    curpos++;

    if (c != '\n')
    {
#ifdef DEBUG
    ll_log (logptr, LLOGMAX, "Returning (%c)", c);
#endif
	return (c);
    }

    while (isspace (c = getc (qn_fp)) && c != '\n')
	curpos++;

    curpos++;
    if (c == '\n')              /* note NIFTP changes <CRLF> sequence */
    {
#ifdef DEBUG
    ll_log (logptr, LLOGMAX, "Returning EOF");
#endif
	eoheader = TRUE;
	return (EOF);
    }
				/* end of JNT header                   */

#ifdef DEBUG
    ll_log (logptr, LLOGMAX, "Returning (%c)", c);
#endif

    return (c);                 /* Just a newline                       */
}
f 733 header          */
	{
	    fseek (qn_fp, --curpos, 0);
				/* set back one character               */
	    if (ferror (qn_fp))
		return (RP_FIO);
	    return (RP_OK);
	}
    }
}
/*
*/
qn_wrply (reply, adr, host)     /* Used to mark status of addmmdf/src/niftp/hdr_proc.c   444      0     12       22600  3664425214  10652 #include "util.h"
#include "mmdf.h"
#include "hdr.h"
#include "ch.h"
#include "ap.h"
/*
 *     MULTI-CHANNEL MEMO DISTRIBUTION FACILITY  (MMDF)
 *
 *
 *     Copyright (C) 1979,1980,1981  University of Delaware
 *
 *     Department of Electrical Engineering
 *     University of Delaware
 *     Newark, Delaware  19711
 *
 *     Phone:  (302) 738-1163
 *
 *
 *     This program module was developed as part of the University
 *     of Delaware's Multi-Channel Memo Distribution Facility (MMDF).
 *
 *     Acquisition, use, and distribution of this module and its listings
 *     are subject restricted to the terms of a license agreement.
 *     Documents describing systems using this module must cite its source.
 *
 *     The above statements must be retained with all copies of this
 *     program and may not be removed without the consent of the
 *     University of Delaware.
 *
 *
 *     version  -1    David H. Crocker    March   1979
 *     version   0    David H. Crocker    April   1980
 *     version  v7    David H. Crocker    May     1981
 *     version   1    David H. Crocker    October 1981
 *
 */
/* This module has bits extracted from submit modules                   */
/* It is used by the Q NIFTP channel to extract a sender address        */
/* From the text                                                        */
/* This is really a pretty ugly way to do things - but I didnt write    */
/* the protocol                                                         */
/*                                                                      */
/*      Steve Kille             August 1982                             */
/*      Steve Kille             Feb 83          extensive rehack        */


extern struct ll_struct *logptr;

extern Chan *curchan;

extern char *index ();
extern char *compress ();
extern char *ap_p2s();
extern AP_ptr ap_new();

extern char *locmachine;
extern char *locname;
extern char *locdomain;
/*
*/
				/* This is used to get the sender name  */
				/* Passes over JNT header and then      */
				/* examines the 822 header fields       */
				/* Takes Sender field if it exists      */
				/* If not takes first From field        */
hdr_sender (sender, ack, hdr_fp, viahost)
char    *sender;                /* Where to stuff name of sender        */
char    **ack;                  /* ack address - note double indirection*/
FILE    *hdr_fp;                /* File being examined                  */
char    *viahost;               /* Host message receoved from           */
{
    char        line[LINESIZE];         /* temp buffer                  */
    char        name[LINESIZE];         /* Name of header field         */
    char        contents[LINESIZE];     /* Contents of header field     */
    int         gotfrom;                /* Got address from From        */
    int         len;
    int         donevia;
    char        *p;
    AP_ptr      ap_ack,
		ap_sender,
		jntroute,
		ap,
		domain,
		route,
		mbox;

#ifdef DEBUG
   ll_log (logptr, LLOGBTR, "hdr_sender (%s)", viahost);
#endif

    gotfrom = FALSE;
    ap_sender = (AP_ptr) 0;
    sender [0] = '\0';
    jntroute = (AP_ptr) 0;

				/* We are at start of file              */
				/* Therefore skip JNT mail header       */
    while ((len = gcread (hdr_fp, line, LINESIZE, "\n\377")) > 1)
    {
	line [len - 1] = '\0';
	for (p = line ; isspace(*p) ;p++);
	if (*p == '\0')
	    break;
    }
    if (len == NOTOK)
    {
	ll_err (logptr, LLOGFAT, "Can't find 733 header");
	return (RP_NO);
    }



				/* Now process header lines             */
   FOREVER
   {
				/* Get a line                           */
	if ((len = gcread (hdr_fp, line, LINESIZE, "\n\377")) < 1)
	{
	    ll_log (logptr, LLOGTMP, "Message has no body" );
				/* someone MIGHT want to do this so     */
				/* just log and pass on through         */
	    if (gotfrom)        /* drop down and return adr             */
		break;
	    return (RP_NO);
	}
	line [len] = '\0';
	ll_log (logptr, LLOGFTR, "gcread (in header) = '%s' (%d)", line, len);

	switch (hdr_parse (line, name, contents))
	{
	    case        HDR_NAM:
				/* No name so contine through header    */
		continue;

	    case        HDR_EOH:
				/* End of header                        */
		if (gotfrom)    /* if we have From address pass it back */
		       break;
		return (RP_NO);

	    case        HDR_NEW:
	    case        HDR_MOR:
				/* Real field so check it               */
		if (lexequ (name, "Via"))
		{
		    if ((p = index (contents, ';')) == 0)
		    {
			ll_log (logptr, LLOGTMP, "Illegal via: field '%s'",
				contents);
			continue;
		    }
		    *p = '\0';
		    compress (contents, contents);
		    /*
		     * must take comments out of via field, ( to get info
		     * correct )
		     * ( delete RFC822 comments from via field )
		     */
		    while ((p = index (contents, '(')) != NULL)
		    {
			char    *q;
			if ((q = index(p, ')')) == NULL)
			{
			     ll_log(logptr, LLOGTMP,
				"Illegal comment in via: field '%s'", contents);
			     break;
			}
			strcpy(p, q+1);         /* zap () comment */
		    }
		    if(p != NULL)               /* only non null if no ')' */
			continue;
		    compress (contents, contents);
#ifdef DEBUG
		    ll_log (logptr, LLOGFTR, "%s: %s", name, contents);
#endif
		    ap = ap_new (APV_DOMN, contents);
		    ap -> ap_chain = jntroute;
		    jntroute = ap;
		}

		if (lexequ (name, "acknowledge-to"))
		    if (*ack == (char *) 0)
		    {
			ap_ack = ap_s2tree (contents);
			if (ap_ack != (AP_ptr) NOTOK)
			{
			    if(ap_t2s (ap_ack, ack) == (AP_ptr)MAYBE)
				return(RP_NS);
#ifdef DEBUG
			    ll_log (logptr, LLOGFTR, "Ack field '%s", &ack);
#endif
			    ap_sqdelete (ap_ack, (AP_ptr) 0);
			}
		    }

		if (lexequ (name, "Sender"))
		{
		    ap_sender = ap_s2tree (contents);
		    if (ap_sender == (AP_ptr) NOTOK)
		    {
			ll_log (logptr, LLOGFTR, "Illegal sender field: %s",
					contents);
			return (RP_NO);
		    }
		    break;      /* now build route */
		}

		if (lexequ (name, "From") && !gotfrom)
		{
		    if ((ap_sender = ap_s2tree (contents)) !=
					(AP_ptr) NOTOK)
			gotfrom = TRUE;
		}
		continue;
	}
	break;          /* always fall out of loop */
    }
				/* build on the route from the trace */
    ap_t2parts (ap_sender, (AP_ptr *) 0, (AP_ptr *) 0,
		&mbox, &domain, &route);

    donevia = FALSE;
    FOREVER
    {
	if (jntroute != (AP_ptr) 0)
	{
	    ap = jntroute;
	    jntroute = jntroute -> ap_chain;
	}
	else if (donevia)
	    break;
	else
	{
	    donevia = TRUE;
	    if (viahost [0] == '\0')
	    {	char *vh;
	    	vh = isnull(*locmachine) ? locname : locmachine;
		if (curchan->ch_lname) vh = curchan->ch_lname;
		ap = ap_new (APV_DOMN, vh);
	    }
	    else
		ap = ap_new (APV_DOMN, viahost);
	}

	if (domain == (AP_ptr) 0)
	    domain = ap;
	else
	{
	    ap -> ap_chain = route;
	    route = ap;
	}
    }

				/* now normalise the domains        */
    if(ap_dmnormalize (domain, (Chan *) NULL) == MAYBE)
	return(RP_NS);
    for (ap = route; ap != (AP_ptr) 0; ap = ap -> ap_chain)
	if(ap_dmnormalize (ap, (Chan *) NULL) == MAYBE){
		ap_free (route);	/* I suspect this is not needed */
		return(RP_NS);
	}

				/* eliminating redundant entries     */
    if (route != (AP_ptr) 0)
    {
	for (ap = route; ap -> ap_chain != (AP_ptr) 0; )
		ap = ap -> ap_chain;

	if (lexequ (domain -> ap_obvalue, ap -> ap_obvalue))
	{
		ap -> ap_obtype = APV_NIL;
		free (ap -> ap_obvalue);
		ap -> ap_obvalue = (char *) 0;
		if (ap == route)
		{
			ap_free (route);
			route = (AP_ptr) 0;
		}
	}
    }
    p = ap_p2s ((AP_ptr) 0, (AP_ptr) 0, mbox, domain, route);
    if(p == (char *)MAYBE)
	return(RP_NS);
    strcpy (sender, p);
    free (p);
    ll_log (logptr, LLOGFTR, "Sender = '%s'", sender);
    return (RP_OK);
}
/*
*/
/* basic processing of incoming header lines */
LOCFUN
hdr_parse (src, name, contents)   /* parse one header line              */
    register char *src;           /* a line of header text              */
    char *name,                   /* where to put field's name          */
	 *contents;               /* where to put field's contents      */
{
    extern char *compress ();
    char linetype;
    register char *dest;

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "hdr_parse(%s)", src);
#endif

    if (isspace (*src))
    {                             /* continuation text                  */
#ifdef DEBUG
	ll_log (logptr, LLOGFTR, "cmpnt more");
#endif
	if (*src == '\n')
	    return (HDR_EOH);
	linetype = HDR_MOR;
    }
    else
    {                             /* copy the name                      */
	linetype = HDR_NEW;
	for (dest = name; *dest = *src++; dest++)
	{
	    if (*dest == ':')
		break;            /* end of the name                    */
	    if (*dest == '\n')
	    {                     /* oops, end of the line              */
		*dest = '\0';
		return (HDR_NAM);
	    }
	}
	*dest = '\0';
	compress (name, name);    /* strip extra & trailing spaces      */
#ifdef DEBUG
	ll_log (logptr, LLOGFTR, "cmpnt name '%s'", name);
#endif
    }

    for (dest = contents; isspace (*src); )
	if (*src++ == '\n')       /* skip leading white space           */
	{                         /* unfulfilled promise, no contents   */
	    *dest = '\0';
	    return ((linetype == HDR_MOR) ? HDR_EOH : linetype);
	}                          /* hack to fix up illegal spaces      */

    while ((*dest = *src) != '\n' && *src != 0)
	     src++, dest++;       /* copy contents and then, backup     */
    while (isspace (*--dest));    /*   to eliminate trailing spaces     */
    *++dest = '\0';

    return (linetype);
}
ack != (AP_ptr) NOTOK)
			{
			    if(ap_t2s (ap_ack, ack) == (AP_ptr)MAYBE)
				return(RP_NS);
#ifdef DEBUG
			    ll_log (logpmmdf/src/niftp/ni_niftp.c   444      0     12       10274  3620510547  10660 #include "util.h"
#include "mmdf.h"

/*
 *     MULTI-CHANNEL MEMO DISTRIBUTION FACILITY  (MMDF)
 *
 *
 *     Copyright (C) 1979,1980,1981  University of Delaware
 *
 *     Department of Electrical Engineering
 *     University of Delaware
 *     Newark, Delaware  19711
 *
 *     Phone:  (302) 738-1163
 *
 *
 *     This program module was developed as part of the University
 *     of Delaware's Multi-Channel Memo Distribution Facility (MMDF).
 *
 *     Acquisition, use, and distribution of this module and its listings
 *     are subject restricted to the terms of a license agreement.
 *     Documents describing systems using this module must cite its source.
 *
 *     The above statements must be retained with all copies of this
 *     program and may not be removed without the consent of the
 *     University of Delaware.
 *
 *
 *     version  -1    David H. Crocker    March   1979
 *     version   0    David H. Crocker    April   1980
 *     version  v7    David H. Crocker    May     1981
 *     version   1    David H. Crocker    October 1981
 *
/*
 *      Modified by Steve Kille from ch_local  August 1982
 *
 */
#include <signal.h>
#include "ch.h"

extern LLog *logptr;

Chan    *curchan;               /* Loaction for validating channel      */

/*
 *  CH_QNIFTP  --  Channel for reception of NIFTP/ JNT mail
 *
 *  This module is made to look like a channel although it is not really
 *
 *  The module is called by the NIFTP, but does not communicate
 *  with it except for the final exit value.  The module interacts
 *  with submit, and passes messages to it.
 *
 *  The major advantage of looking like a channel is that messages
 *  can pretent to submit that they have come in over a channel.
 *  The facilities of submit can then be used for management and
 *  other things (e.g. stamping Via: fields).
 *
 *  It should be called by the NIFTP with an execl of the form
 *  execl( path, name, niftp-queue, fname, TSname) where the parameters are
 *  character strings.  fname is the file containing the JNT mail
 *  format message, and TSname is the calling address of the host.
 *  MMDF will try to identify this as a real host.
 *  niftp-queue is the NIFTP queue name.  This is mapped into an
 *  MMDF channel name
 *
 *
 *
 */

/*      MAIN MAIN MAIN MAIN MAIN MAIN MAIN MAIN MAIN MAIN MAIN MAIN     */

main (argc, argv)
int     argc;
char   *argv[];
{
    extern char *dupfpath ();
    short retval;

    umask(0);
    mmdf_init (argv[0]);
    siginit ();

    retval = ni_niftp (argc, argv);

#ifdef DEBUG
    ll_log (logptr, LLOGFTR, "Exiting with value (%s)", rp_valstr (retval));
#endif

    ll_close (logptr);              /* clean log end, if cycled  */
    exit (retval);
}
/***************  (ch_) NIFTP  MAIL DELIVERY  ************************ */

ni_niftp (argc, argv)             /* send to NIFTP channel               */
int     argc;
char   *argv[];
{
    short       result;
    ll_log (logptr, LLOGFST, "ni_niftp (q='%s', f='%s', TS='%s')",
		argv[1], argv[2], argv[3]);

    if (rp_isbad (result = qn_init (argv[1])))
	return (result);           /* problem setting-up NIFTP reception */
				   /* This checks channel validity       */
				   /* based on NIFTP queue name          */
    if (rp_isbad (result = mm_init ()))
	return (result);         /* problem initialising submit          */

    if (rp_isbad (result = qn2mm_send (argv[2], argv[3])))
	return (result);           /* transfer the mail to submit        */

    qn_end (OK);                  /* done with Deliver function         */
    mm_end (OK);

    return (RP_OK);               /* NORMAL RETURN                      */
}

/**/

/* VARARGS2 */

err_abrt (code, fmt, b, c, d)     /* terminate ourself                  */
short     code;
char    fmt[],
	b[],
	c[],
	d[];
{
    char linebuf[LINESIZE];

    qn_end (NOTOK);
    mm_end (NOTOK);
    if (rp_isbad (code))
    {
#ifdef DEBUG
	if (rp_gbval (code) == RP_BNO || logptr -> ll_level >= LLOGBTR)
	{                         /* don't worry about minor stuff      */
	    sprintf (linebuf, "%s%s", "[ABEND:  %s]\t", fmt);
	    ll_log (logptr, LLOGFAT, linebuf, rp_valstr (code), b, c, d);
	    abort ();
	}
#endif
    }
    ll_close (logptr);           /* in case of cycling, close neatly   */
    exit (code);
}


The facilities of submit can then be used for management and
 *  other things (e.g. stamping Via: fields).
 *
 *  It should be called by the NIFTP with an execl of the form
 *  execl( path, name, niftp-queue, fname, TSname) where the parameters are
 *  character strings.  fname is the file containing the JNT mail
 *  formammdf/src/niftp/pn_wtmail.c   444      0     12       40412  3657737733  11063 #include "util.h"
#include "mmdf.h"
/*                                                                      */
/*                  CH_NIFTP: NIFTP MAIL DONOR CHANNEL                  */
/*                                                                      */
/*         This module does all the hard work for the donor channel     */
/*              Chris Bennett University College London 24/2/81         */
/*                                                                      */
/*      Rewritten to incorporate new release MMDF stuff and             */
/*      to generally streamline the code                                */
/*              Steve Kille             July 1982                       */
/*      SEK  Feb 84     Rewrite to reformat sender                      */
/*
/*                  CH_NIFTP: NIFTP MAIL DONOR CHANNEL                  */

#include <signal.h>
#include "ch.h"
#include "ap.h"
#include "jnt.h"                /* JNT Mail return value definition     */

extern struct ll_struct   *logptr;

extern char     *pn_quedir;
				/* Location of JNTmail queues for NIFTP */

extern int      ap_outtype;     /* Default output format                */

extern Chan  *chanptr;          /* Current channel                      */


char    *pn_chname;             /* Name of channel we are called as     */
char    *pn_sender;             /* Sender of the message being processed*/
char    *pn_msgfile;            /* name of file with message in         */
char    pn_fname[ LINESIZE ];   /* Name of file for NIFTP output        */
int     pn_fd;                  /* fd of NIFTP ouput file               */
char    pn_curhost[ LINESIZE ]; /* Host being processed                 */
short   firstadr;               /* Boolean - first address for host     */

char    jnt_hedsep[] = "\n\n";
				/* Separatpr between header and text    */
				/* for JNT mail                         */
char    jnt_adrsep[] = ",\n"; /* Separator between addresses          */
				/* for JNT mail                         */

LOCVAR  int inheader;           /* flags for text copy                  */
LOCVAR  int donesender;

				/* This lot need to be glbal to allow   */
				/* the structure to be initialised      */
LOCVAR  char TSname [LINESIZE];
				/* The TS address to be handed down     */
LOCVAR char    senderbuf [LINESIZE];
LOCVAR char    *vararray[] = {
	"file", pn_fname,
	"host", pn_curhost,
	"adr",  TSname,
	"sender", senderbuf,
	0, 0
};


extern char *index ();
extern char *blt ();
extern char *ap_p2s();

/*
*/


pn_init ()                        /* session initialization             */
{
#ifdef DEBUG
	ll_log (logptr, LLOGBTR, "pn_init ()");
#endif
	return (RP_OK);
}

pn_end (state)                   /* end of session                     */
int     state;
{
#ifdef DEBUG
	ll_log (logptr, LLOGBTR, "pn_end (%d)", state);
#endif
	return (RP_OK);               /* no-op                          */
}

pn_sbinit ()                      /* ready for submission               */
{
#ifdef DEBUG
	ll_log (logptr, LLOGBTR, "pn_sbinit()");
#endif
	return (RP_OK);
}

pn_sbend ()                       /* end of submission                  */
{
#ifdef DEBUG
	ll_log (logptr, LLOGBTR, "pn_sbend ()");
#endif

	return (RP_OK);               /* no-op                          */
}

/*            DELIVER MESSAGE TO NIFTP DONOR                          */




pn_winit (chname, sender, msgfile)/* initialize for one message         */
char    chname[],                 /* what channel coming in from        */
	sender[],                 /* return address for error msgs      */
	msgfile[];                /* name of file with msg text         */
{
    extern char *strdup ();
    AP_ptr ap;
    AP_ptr local,
	domain,
	route;

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "pn_winit");
#endif

    if (pn_chname != (char *) 0)
	free (pn_chname);
    pn_chname = strdup (chname);
    if (pn_sender != (char *) 0)
	free (pn_sender);
    ap = ap_s2tree (sender);
    if (ap != (AP_ptr) NOTOK)
    {
	ap_t2parts (ap, (AP_ptr *) 0, (AP_ptr *) 0, &local, &domain, &route);
	pn_sender = ap_p2s ((AP_ptr) 0, (AP_ptr) 0, local, domain, route);
    }
			/* ap_outtype will have corect value*/

    else
	ll_log (logptr, LLOGTMP,  "Illegal sender '%s'", sender);

    if (pn_msgfile != (char *) 0)
	free (pn_msgfile);
    pn_msgfile = strdup (msgfile);

    ll_log (logptr, LLOGFTR, "c='%s', s='%s', f='%s'",
	    pn_chname, (pn_sender==(char *)MAYBE)?"":pn_sender, pn_msgfile);

    if(pn_sender == (char *)MAYBE)
	return (RP_NS);
    return (RP_OK);
}


/*
*/

pn_wainit ()                           /* generate and open file        */
				       /* needed befor we can start     */
				       /* and get ready for next set of */
				       /* addresses                     */
{
    extern char *mktemp ();     /* Stuff for filename genration         */
    static char template[] = "ni.aXXXXXX";
    short tries;
    extern char *multcpy ();

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "pn_wainit ()" );
#endif

    firstadr = TRUE;            /* Ready for first host         */


/* File name generation extracted from submit.
 * make the unique part of the queue entry file name.  if mktemp
 * can't do it, goose the value of the first char in the filename
 * part.  mktemp uses qf_mfname, rather than qf_filnam, so that its
 * stat() can accurately tell if the name is unique.  the goosing
 * strategy permits up to 500 messages under the same process id.
 * mmdf will probably fall apart before that limit is hit.
 */
    for (tries = 25; tries-- > 0; template[strlen (pn_quedir) + 4]++)
    {                             /* template[4] limited to lower case  */
	multcpy (pn_fname, pn_quedir, "/", template, (char *)0);
				  /* add standard part of unique name   */
	if (strcmp (mktemp(pn_fname), "/") != 0)
	    break;                /* add unique part                    */
    }
#ifdef DEBUG
    ll_log (logptr, LLOGFTR, "JNT mail file is = \"%s\"", pn_fname);
#endif


    if (rp_isbad (pn_fd = creat (pn_fname, 0444)))
    {
	ll_err (logptr, LLOGBTR, "Failure to open file" );
	return (RP_FCRT);
    }

    return (RP_OK);
}
/*
/*      Process individual address                                      */
/*      Produce JNT style address from host and address and place       */
/*      into file.  "," separator if not first one                      */

extern      AP_ptr ap_s2tree ();

pn_wadr (host, adr)
char    *host;          /* host official name                           */
char    *adr;           /* rest of address / route                      */
{
    char        *jntadr;	/* 733 /JNT format address              */
    int         len;

#ifdef DEBUG
	ll_log (logptr, LLOGBTR, "pn_wadr (h= '%s' a= '%s')", host, adr);
#endif

    jntadr = adr;
    ll_log (logptr, LLOGFST, "JNT address = '%s'", jntadr);

    if (firstadr)
    {
	firstadr = FALSE;
	strcpy (pn_curhost, host);
    }
    else
    {
	len = strlen (jnt_adrsep);
	if (write (pn_fd, jnt_adrsep, len) != len)
	{
	    ll_err (logptr, LLOGBTR, "File write error (separator)" );
	    return (RP_FIO);
	}
    }
    len = strlen(jntadr);
    if (write (pn_fd, jntadr, len) != len)
    {
	ll_err (logptr, LLOGBTR, "File write error" );
	return (RP_FIO);
    }

    return (RP_AOK );
}

/*
*/
LOCVAR  char partialbuf [LINESIZE];

pn_wtinit()
				/* End of addres list for this host     */
				/* Write separtator between address     */
				/* and start of body                    */
{
	int     len;
#ifdef DEBUG
	ll_log (logptr, LLOGBTR, "pn_wtinit ()");
#endif

	inheader  = TRUE;
	donesender = FALSE;
	partialbuf [0] = '\0';
	ap_outtype = chanptr -> ch_apout;
	len = strlen (jnt_hedsep);
	if (write (pn_fd, jnt_hedsep, len) != len)
	{
	    ll_err (logptr, LLOGBTR, "Failure writing separator" );
	    return (RP_FIO);
	}

	return(RP_OK);
}
/*
*/

#ifndef VIATRACE

LOCVAR char *txt_sender = "Sender: ";
LOCVAR char *txt_original = "Original-";
				/* Write block of text to file          */
pn_wtxt (buffer, len)
char *buffer;
int len;
{
    int retval;
    char  *front;
    char  *back;
    char  csav;
#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "pn_wttxt()");
#endif
				/* whilst in header split into lines */
				/*for reformatting                   */

    if (inheader)
    {
	back = index (buffer, '\n');
	if (back == (char *) 0)
	{
	    strcat (partialbuf, buffer);
	    return (RP_OK);
	}
	back++;
	csav = *back;
	*back = '\0';
	strcat (partialbuf, buffer);
	if (rp_isbad (retval = pn_whdr (partialbuf, strlen (partialbuf))))
	   return (retval);
	*back = csav;


	while ((front = index (back, '\n')) != (char *) 0)
	{
	    front++;
	    csav = *front;
	    *front = '\0';
	    if (rp_isbad (retval = pn_whdr (back, strlen (back))))
		return (retval);
	    *front = csav;
	    back = front;
	}
	if (!inheader)  /* reached end of header                */
	   return (pn_wtxt (back, strlen (back)));
	strcpy (partialbuf, back);
	return (RP_OK);
    }
    else
    {
	if (write (pn_fd, buffer, len) != len)
	{
		ll_err (logptr, LLOGBTR, "Failure writing text to file" );
		return (RP_FIO);
	}
    }
    return(RP_OK);
}


/* */

			/* take  single line and mungle it      */

LOCFUN pn_whdr (buffer, len)
char *buffer;
int  len;
{
	int namlen;
	char *cp;
	char name [LINESIZE];
	AP_ptr  ap,
		local,
		domain,
		route;

#ifdef DEBUG
	ll_log (logptr , LLOGFTR, "pn_whdr ('%s',%d)", buffer,  len);
#endif

    if (inheader)
    {
	if (buffer [0] == '\n')
	{
	     inheader = FALSE;
	     if (!donesender)
	     {
#ifdef DEBUG
		ll_log (logptr, LLOGBTR, "Outputting new sender '%s'",
				pn_sender);
#endif
		namlen = strlen (txt_sender);
		if (write (pn_fd, txt_sender, namlen) != namlen)
		{
		    ll_err (logptr, LLOGBTR, "Failure writing text to file" );
		    return (RP_FIO);
		}
		namlen = strlen (pn_sender);
		if (write (pn_fd, pn_sender, namlen) != namlen)
		{
		    ll_err (logptr, LLOGBTR, "Failure writing text to file" );
		    return (RP_FIO);
		}
		if (write (pn_fd, "\n\n", 2) != 2)
		{
		    ll_err (logptr, LLOGBTR, "Failure writing text to file" );
		    return (RP_FIO);
		}
	    }
	    else
		if (write (pn_fd, "\n", 1) != 1)
		{
		    ll_err (logptr, LLOGBTR, "Failure writing text to file" );
		    return (RP_FIO);
		}
	    return (RP_OK);
	}

				/* Now get name of line                 */
				/* just output continuations at once    */
	if (!isspace (buffer [0]) &&
		(cp = index (buffer, ':')) != (char *) 0)
	{
	    namlen = cp - buffer;
	    blt (buffer, name, namlen);
	    name [namlen]= '\0';
	    if (lexequ (name, "via"))
	    {
		namlen = strlen (txt_original);
		if (write (pn_fd, txt_original, namlen) != namlen)
		{
		    ll_err (logptr, LLOGBTR, "Failure writing text to file" );
		    return (RP_FIO);
		}
	    }

	    if (lexequ (name, "from"))
	    {
		ap = ap_s2tree (cp + 1);
		if (ap  != (AP_ptr) NOTOK)
		{
		    ap_t2parts (ap, (AP_ptr *) 0, (AP_ptr *) 0,
				&local, &domain, &route);
		    cp = ap_p2s ((AP_ptr) 0, (AP_ptr) 0,
				local, domain, route);
		    if(cp == (char *)MAYBE)
			return (RP_NS);
		    if (lexequ (pn_sender, cp))
		    {
			donesender = TRUE;
#ifdef DEBUG
			ll_log (logptr, LLOGFTR, "Found matching From '%s'",
				cp);
#endif
		    }
		    free (cp);
		}
	    }

	    if (lexequ (name, "sender"))
	    {
		ap = ap_s2tree (cp + 1);
		if (ap  != (AP_ptr) NOTOK)
		{
		    ap_t2parts (ap, (AP_ptr *) 0, (AP_ptr *) 0,
				&local, &domain, &route);
		    cp = ap_p2s ((AP_ptr) 0, (AP_ptr) 0,
				local, domain, route);
		    if(cp == (char *)MAYBE)
			return (RP_NS);
		    if (lexequ (pn_sender, cp))
		    {
			donesender = TRUE;

#ifdef DEBUG
		       ll_log (logptr, LLOGFTR, "Found matching Sender '%s'",
				cp);
#endif
		    }
		    else
		    {
			donesender = FALSE;
					/* in case a matchin From: field */
			namlen = strlen (txt_original);
			if (write (pn_fd, txt_original, namlen) != namlen)
			{
			    ll_err (logptr, LLOGBTR,
				"Failure writing text to file" );
			    return (RP_FIO);
			}
		    }
		    free (cp);
		}
	    }
	}
    }
    if (write (pn_fd, buffer, len) != len)
    {
	ll_err (logptr, LLOGBTR, "Failure writing text to file" );
	return (RP_FIO);
    }
    return(RP_OK);
}

#else VIATRACE

/*
*/
				/* Write block of text to file          */
				/* simple non-munging version           */
pn_wtxt (buffer, len)
char *buffer;
int len;
{
#ifdef DEBUG
	ll_log (logptr, LLOGBTR, "pn_wttxt()");
#endif

	if (write (pn_fd, buffer, len) != len)
	{
	    ll_err (logptr, LLOGBTR, "Failure writing text to file" );
	    return (RP_FIO);
	}

	return(RP_OK);
}
#endif VIATRACE
/*
/*      End of text                                                     */
/*      Close file and pass to NIFTP                                    */
/*      The host name is translated into the standard UCL TS address    */
/*      of the form TSservice/hostaddres/service (e.g. tcp/isid/mail    */


pn_wtend (tmp_result)
int     tmp_result;
{
int     i;
char    *p,*q;
int     pid;                    /* Process id for fork                  */
int     status;                 /* Status for wait                      */
char    buf [LINESIZE];
int     argc;
char    *argv[50];
extern  char *expand();

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "pn_wtend ()");
#endif

    if (close(pn_fd) == NOTOK)         /* close file                   */
    {
	ll_err (logptr, LLOGBTR, "Failure to close file" );
	return (RP_LIO);
    }

				/* if status so far is bad unlink file  */
				/* and pass on                          */
    if (rp_isbad (tmp_result))
    {
	if (unlink (pn_fname) == NOTOK) /* delete file */
	{
	    ll_err (logptr, LLOGFAT, "Failure to unlink file '%s'", pn_fname);
	    return (RP_FOPN);
	}
	ll_log (logptr, LLOGBTR, "Failure (%s) for file '%s'",
		rp_valstr (tmp_result), pn_fname);
	return (tmp_result);
    }

				/* Now extract the TS address           */
    if (tb_k2val (chanptr->ch_table, TRUE,
	(chanptr->ch_host == NORELAY ? pn_curhost :  chanptr->ch_host),
	TSname)!=OK)
    {
	ll_err (logptr,  LLOGBTR, "TS address not found for '%s'",
							     pn_curhost );
	return (RP_PARM);
    }

				/* 'quote' chars in sender              */
    for (p=pn_sender, q=senderbuf; *p != '\0';)
    {
	 switch (*p)
	 {
	    case '\"':
	    case '\\':
		*q++ = '\\';
		*q++ = *p++;
		break;
	    default:
		*q++ = *p++;
		break;
	}
    }
    *q = '\0';
				/* Now build execl string using         */
				/* ch_ipcsstring as format              */
    expand  (buf, chanptr -> ch_confstr, vararray);
    ll_log (logptr, LLOGFST, "execl (%s)", buf);

    argc = cstr2arg (buf, 50, argv, ' ');

#ifdef DEBUG
    ll_log (logptr, LLOGFTR, "n=(%d) 0='%s', 1='%s', 2='%s', 3='%s', 4='%s'",
		argc, argv[0], argv[1], argv[2], argv[3], argv[4]);
#endif


    if  ((pid = fork()) == NOTOK)
    {
	ll_err (logptr, LLOGTMP, "Unable to fork for %s", argv[0]);
	if (unlink (pn_fname) == NOTOK) /* delete file */
	{
	    ll_err (logptr, LLOGFAT, "Failure to unlink file '%s'", pn_fname);
	    return (RP_FOPN);
	}
#ifdef DEBUG
	ll_log (logptr, LLOGFTR, "File: %s deleted", pn_fname);
#endif
	return (RP_NET);
    }
    if (pid == 0)               /* In child so do execl                 */
    {

ll_log (logptr, LLOGFTR, "In the child");
				/* Close down all of the pipes to child  */

	signal (SIGHUP, SIG_IGN);
	signal (SIGINT, SIG_IGN);
	signal (SIGQUIT, SIG_IGN);

	for (i = _NFILE - 1; i > 0; i--)
		close (i);
	open("/dev/null", 2);
	open("/dev/null", 1);

	execv (argv[0], &argv[1]);

	ll_log (logptr, LLOGTMP, "Unable to execl %s", argv[0]);
	exit (-1);
    }

    alarm (300);                 /* The NIFTP is not file size          */
				 /* dependent so 5 mins is ample        */
    status = pgmwait (pid);      /* Parent waits for child              */
    alarm (0);

    if (status != JNT_OK)
    {                           /* Killed by alarm or general failure   */
	if (unlink (pn_fname) == NOTOK) /* delete file */
	{
	    ll_err (logptr, LLOGFAT, "Failure to unlink file '%s'", pn_fname);
	    return (RP_FOPN);
	}
#ifdef DEBUG
	ll_log (logptr, LLOGFTR, "File: %s deleted", pn_fname);
#endif
	switch (status)
	{
	    case JNT_TEMP:
	    default:            /* temporary error                      */
		ll_log (logptr, LLOGTMP,
		"Temporary failure (%d) for file: '%s' - requeued, h='%s'",
			status, pn_fname, pn_curhost);
		return (RP_DHST);


	    case JNT_PERM:      /* permanent error                     */
		ll_log (logptr, LLOGTMP,
		    "Permanent USP failure (%d) - rejected", status);
		return (RP_NDEL);
	}
    }


    ll_log (logptr, LLOGFST, "Result is GOOD (%d)", status);
				 /* For NIFTP evidence                  */

    return(RP_MOK);
}
VIATRACE
/*
/*      End of text                                                     */
/*      Close file and pass to NIFTP                                    */
/*      The host name is translated into the standard UCL TS address    */
/*      mmdf/src/niftp/rtn_proc.c   444      0     12       16500  3620510550  10670 #include "util.h"
#include "mmdf.h"

/*
 *     MULTI-CHANNEL MEMO DISTRIBUTION FACILITY  (MMDF)
 *
 *
 *     Copyright (C) 1979,1980,1981  University of Delaware
 *
 *     Department of Electrical Engineering
 *     University of Delaware
 *     Newark, Delaware  19711
 *
 *     Phone:  (302) 738-1163
 *
 *
 *     This program module was developed as part of the University
 *     of Delaware's Multi-Channel Memo Distribution Facility (MMDF).
 *
 *     Acquisition, use, and distribution of this module and its listings
 *     are subject restricted to the terms of a license agreement.
 *     Documents describing systems using this module must cite its source.
 *
 *     The above statements must be retained with all copies of this
 *     program and may not be removed without the consent of the
 *     University of Delaware.
 *
 *
 *     version  -1    David H. Crocker    March   1979
 *     version   0    David H. Crocker    April   1980
 *     version  v7    David H. Crocker    May     1981
 *     version   1    David H. Crocker    October 1981
 *
 */
/* for channel Q NIFTP returning list of duff addresses to sender       */
/*                                                                      */
/*      Steve Kille     August 1982                                     */

#include "msg.h"
#include "adr_queue.h"


extern char *multcat();
extern struct ll_struct *logptr;

extern char *supportaddr;        /* supoport address for mail messages */
extern char *locname;
extern char *locdomain;
#ifdef UCL_TSNAME
extern char TSname[];
#endif UCL_TSNAME

LOCVAR char rtn_sndel[] = "Failed mail";


struct rtn_str
{
    struct rtn_str  *r_link;
    char            *r_buf;
};

LOCVAR struct rtn_str
	*rtn_bad = (struct rtn_str *) 0,
	*rtn_ok = (struct rtn_str *) 0;
/**/

rtn_init ()
				/* clean up any debris                  */
{
    struct rtn_str *ptr;

    while (rtn_ok != 0)
    {
	ptr = rtn_ok;
	free (ptr -> r_buf);
	rtn_ok = rtn_ok -> r_link;
	free ( (char *)ptr);
    }

    while (rtn_bad != 0)
    {
	ptr = rtn_bad;
	free (ptr -> r_buf);
	rtn_bad = rtn_bad -> r_link;
	free ( (char *)ptr);
    }
}

/**/

rtn_adr (adr, good)
char    *adr;
{
    struct rtn_str *rptr;
    extern char *malloc();

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "rtn_adr (%s, %d)", adr, good);
#endif

    rptr = (struct rtn_str *) malloc (sizeof (struct rtn_str));
    if (rptr == (struct rtn_str *) 0)
	return (RP_NO);

    rptr -> r_buf = multcat ("\t", adr, "\n", (char *)0);
    if (rptr ->  r_buf == (char *) NOTOK)
	return (RP_NO);

    if (good)
    {
	rptr -> r_link =  rtn_ok;
	rtn_ok = rptr;
    }
    else
    {
	rptr -> r_link = rtn_bad;
	rtn_bad = rptr;
    }

    return (RP_OK);
}



/**/

rtn_errmsg (sender, rtn_fp)     /* Fire of list ofbad addresses to sender */
char    *sender;                /* sender of message                    */
FILE    *rtn_fp;                /* pointer to message file              */
{

    if (rtn_bad  == (struct rtn_str *) 0)    /* No error addresses built up */
	return (OK);


#ifdef DEBUG
    ll_log (logptr, LLOGPTR, "rtn_errmsg (%s)", sender);
#endif

				/* If sender not specified              */
				/* generate default                     */
				/* May throw away later if this becomes */
				/* a problem                            */
    if (sender[0] == '\0')
	strcpy (sender, supportaddr);

    if (rtn_mlinit (rtn_sndel, sender) != OK)
	return (NOTOK);           /* set up for returning               */

    rtn_nogood ();               /* give list of bad addresses     */

    rtn_cite (rtn_fp, 500);

    return (ml_end (OK));
}
/**/

rtn_ack (ackadr, rtn_fp)     /* Fire of list ofbad addresses to ackadr */
char    *ackadr;                /* ackadr of message                    */
FILE    *rtn_fp;                /* pointer to message file              */
{



#ifdef DEBUG
    ll_log (logptr, LLOGPTR, "rtn_ack (%s)", ackadr);
#endif

				/* If ackadr not specified              */
				/* generate default                     */
				/* May throw away later if this becomes */
				/* a problem                            */
    if (ackadr == (char *) 0 || ackadr[0] == '\0')
	return (OK);    /* no address to ack to */

    if (rtn_mlinit ("Message Acknowledgement", ackadr) != OK)
	return (NOTOK);           /* set up for returning               */

    rtn_good ();
    rtn_nogood ();

    rtn_cite (rtn_fp, 25);

    return (ml_end (OK));
}
/**/

LOCFUN
    rtn_nogood ()
			/*  handle bad addresses                        */
{
    struct rtn_str *ptr;

    if (rtn_bad == 0)
	return;

#ifdef	UCL_TSNAME
    if(*TSname == '+') {
	ml_txt("\nWARNING: Cannot reverse translate: <");
	ml_txt(TSname);
	ml_txt("> from the NRS database\n");
    }
#endif	UCL_TSNAME
    ml_txt ("\nYour message was not delivered to the following addresses: \n\n");

    for (ptr = rtn_bad; ptr != 0; ptr = ptr -> r_link) {
	to80 (ptr -> r_buf);
	ml_txt (ptr -> r_buf);
    }

    ml_txt ("\n\n");
}

to80(from)
char *from;
{
	register char   *p;
	char    *lastspace = NULL, *lastnl = from;

	for(p = from ; *p ; p++){
		if(*p != ' ' && *p != '\t')
			continue;
		if( p - lastnl > 80){
			if(lastspace != NULL){
				*lastspace = '\n';
				lastnl = lastspace;
				if(p - lastnl <= 80){
					 lastspace = p;
					continue;
				}
			}
			lastspace = NULL;
			*p = '\n';
			lastnl = p;
			continue;
		}
		lastspace = p;
	}
	if(p - lastnl > 80 && lastspace != NULL)
		*lastspace = '\n';
}

LOCFUN
    rtn_good ()
			/* handle good addresses for ack        */
{
    struct rtn_str *ptr;

    if (rtn_ok == 0)
	return;

    ml_txt ("\nYour message was accepted by: ");
    ml_txt (locname);
    ml_txt (".");
    ml_txt (locdomain);
    ml_txt ("\n    for delivery to the following addresses:\n\n");

    for (ptr = rtn_ok; ptr != 0; ptr = ptr -> r_link) {
	to80 (ptr -> r_buf);
	ml_txt (ptr -> r_buf);
    }

    ml_txt ("\n\n");
}


/**/

LOCFUN
	rtn_mlinit (subject, sender)
char    subject[];
char    sender[];
{
    extern char *sitesignature;

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "rtn_mlinit (%s)", subject);
#endif

    if (isnull (sender[0]))     /* no return address                  */
    {
	ll_log (logptr, LLOGTMP, "no return address");
	return (NOTOK);
    }
    if (ml_1adr (NO, NO, sitesignature, subject, sender) != OK)
    {                             /* no return, no Sender:              */
	ll_err (logptr, LLOGTMP, "ml_1adr");
	return (NOTOK);
    }
    return (OK);
}
/**/

rtn_cite (rtn_fp, count)
FILE    *rtn_fp;
int     count;
{
    short     lines;
    char    linebuf[LINESIZE];

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "rtn_cite ()");
#endif

    ml_txt ("\n    Your message begins as follows:\n\n");

    rewind (rtn_fp);

				/* Skip JNT mail header                 */
    while (fgets (linebuf, sizeof linebuf, rtn_fp) != NULL)
    {
	if (linebuf[0] == '\n')
	    break;
    }

    while (fgets (linebuf, sizeof linebuf, rtn_fp) != NULL)
    {
	ml_txt (linebuf);         /* do headers                           */
	if (linebuf[0] == '\n')
	    break;
    }

    if (ferror (rtn_fp))
	err_abrt (RP_FIO, "Error reading text for return-to-msg_sender.");

    if (!feof (rtn_fp))
    {
	for (lines = count; --lines > 0 &&
		fgets (linebuf, sizeof linebuf, rtn_fp) != NULL;)
	{
	    if (linebuf[0] == '\n')
		lines++;          /* truly blank lines don't count      */
	    ml_txt (linebuf);
	}
	if (!feof (rtn_fp))       /* if more, give an elipses           */
	    ml_txt ("...\n");
    }
}

el, sender) != OK)
	return (NOTOK);           /* set up for returning               */

    rtn_nogood ();               /* give list of bad addresses     */

    rtn_cite (rtn_fp, 500);

    mmdf/src/niftp/Makefile.real   444      0     12        5340  3635174330  11250 #
#   niftp (jntmail) channel
#
MODULES =       hdr_proc ni_mail ni_niftp pn_wtmail qn2mm_send \
		qn_rdmail rtn_proc qu2pn_send

OBJECTS =       hdr_proc.o ni_mail.o ni_niftp.o pn_wtmail.o qn2mm_send.o \
		qn_rdmail.o rtn_proc.o qu2pn_send.o

SOURCES =       hdr_proc.c ni_mail.c ni_niftp.c pn_wtmail.c qn2mm_send.c \
		qn_rdmail.c rtn_proc.c qu2pn_send.c

real-default:   niftp ni_niftp

install:        $(CHANDIR)/$(MMPREF)niftp $(LIBDIR)/$(MMPREF)ni_niftp

$(CHANDIR)/$(MMPREF)niftp:      xniftp
	cp xniftp $(CHANDIR)/$(MMPREF)niftp
	-$(CHOWN) daemon $(CHANDIR)/$(MMPREF)niftp
	-chmod 04$(PGMPROT) $(CHANDIR)/$(MMPREF)niftp
	-@ls -ls $(CHANDIR)/$(MMPREF)niftp
	-@echo "niftp installed normally"; echo ""

niftp:  xniftp
xniftp: ch_niftp.o qu2pn_send.o pn_wtmail.o $(MMDFLIBS)
	    $(CC) $(LDFLAGS) -o $@ ch_niftp.o qu2pn_send.o \
		pn_wtmail.o $(MMDFLIBS) $(SYSLIBS)


$(LIBDIR)/$(MMPREF)ni_niftp:      xni_niftp
	cp xni_niftp $(LIBDIR)/$(MMPREF)ni_niftp
	-$(CHOWN) mmdf $(LIBDIR)/$(MMPREF)ni_niftp
	-chmod 04$(PGMPROT) $(LIBDIR)/$(MMPREF)ni_niftp
	-@ls -ls $(LIBDIR)/$(MMPREF)ni_niftp
	-@echo "ni_niftp installed normally"; echo ""

ni_niftp:  xni_niftp
xni_niftp: ni_niftp.o qn2mm_send.o qn_rdmail.o hdr_proc.o rtn_proc.o  \
		$(MMDFLIBS)
	    $(CC) $(LDFLAGS) -o $@ ni_niftp.o qn2mm_send.o qn_rdmail.o \
		hdr_proc.o rtn_proc.o $(MMDFLIBS) $(SYSLIBS)



lint:   ;  $(LINT) $(LFLAGS) $(SOURCES) $(LLIBS)

clean:
	-rm -f xni_niftp xniftp *.o x.c makedep eddep make.out errs


# DO NOT DELETE THIS LINE -- make depend uses it
deliver.o: deliver.c
hdr_proc.o: hdr_proc.c
hdr_proc.o: ../../h/util.h
hdr_proc.o: ../../h/mmdf.h
hdr_proc.o: ../../h/hdr.h
hdr_proc.o: ../../h/ch.h
hdr_proc.o: ../../h/ap.h
ni_mail.o: ni_mail.c
ni_mail.o: ../../h/util.h
ni_mail.o: ../../h/mmdf.h
ni_niftp.o: ni_niftp.c
ni_niftp.o: ../../h/util.h
ni_niftp.o: ../../h/mmdf.h
ni_niftp.o: /usr/include/signal.h
ni_niftp.o: ../../h/ch.h
pn_wtmail.o: pn_wtmail.c
pn_wtmail.o: ../../h/util.h
pn_wtmail.o: ../../h/mmdf.h
pn_wtmail.o: /usr/include/signal.h
pn_wtmail.o: ../../h/ch.h
pn_wtmail.o: ../../h/ap.h
pn_wtmail.o: ../../h/jnt.h
qn2mm_send.o: qn2mm_send.c
qn2mm_send.o: ../../h/util.h
qn2mm_send.o: ../../h/mmdf.h
qn2mm_send.o: ../../h/ch.h
qn_rdmail.o: qn_rdmail.c
qn_rdmail.o: ../../h/util.h
qn_rdmail.o: ../../h/mmdf.h
qn_rdmail.o: ../../h/ap.h
qn_rdmail.o: ../../h/ch.h
qn_rdmail.o: ../../h/dm.h
rtn_proc.o: rtn_proc.c
rtn_proc.o: ../../h/util.h
rtn_proc.o: ../../h/mmdf.h
rtn_proc.o: ../../h/msg.h
rtn_proc.o: ../../h/adr_queue.h
qu2pn_send.o: qu2pn_send.c
qu2pn_send.o: ../../h/util.h
qu2pn_send.o: ../../h/mmdf.h
qu2pn_send.o: ../../h/ch.h
qu2pn_send.o: ../../h/phs.h
qu2pn_send.o: ../../h/ap.h
# DEPENDENCIES MUST END AT END OF FILE
# IF YOU PUT STUFF HERE IT WILL GO AWAY
# see make depend above
 while (fgets (linebuf, sizeof linebuf, rtn_fp) != NULL)
    {
	ml_txt (linebuf);         /* do headers                           */
	if (linebuf[0] == '\n')
	    break;
    }

    if (ferror (rtn_fp))
	err_abrt (RP_FIO, "Error reading text for return-to-msg_sender.");

    if (!feof (rtmmdf/src/niftp/gen   555      0     12          57  3620510550   7315 make -f ../../Makefile.com -f Makefile.real $*
ANDIR)/$(MMPREF)niftp
	-chmod 04$(PGMPROT) $(CHANDIR)/$(MMPREF)niftp
	-@ls -ls $(CHANDIR)/$(MMPREF)niftp
	-@echo "niftp installed normally"; echo ""

niftp:  xniftp
xniftp: ch_niftp.o qu2pn_send.o pn_wtmail.o $(MMDFLIBS)
	    $(CC) $(LDFLAGS) -o $@ ch_niftp.o qu2pn_send.o \
		pn_wtmail.o $(MMDFLIBS) $(SYSLIBS)


$(LIBDIR)/$(MMPREF)ni_niftp:      xni_niftp
	cp xni_niftp $(LIBDIR)/$(MMPREF)ni_niftp
	-$(CHOWN) mmdf $(LIBDIR)/$(MMPREF)ni_niftp
	-chmod 04$(PGMPROT) mmdf/src/niftp/ch_niftp.c   444      0     12        7171  3620510551  10621 #include "util.h"
#include "mmdf.h"

/*
 *     MULTI-CHANNEL MEMO DISTRIBUTION FACILITY  (MMDF)
 *
 *
 *     Copyright (C) 1979,1980,1981  University of Delaware
 *
 *     Department of Electrical Engineering
 *     University of Delaware
 *     Newark, Delaware  19711
 *
 *     Phone:  (302) 738-1163
 *
 *
 *     This program module was developed as part of the University
 *     of Delaware's Multi-Channel Memo Distribution Facility (MMDF).
 *
 *     Acquisition, use, and distribution of this module and its listings
 *     are subject restricted to the terms of a license agreement.
 *     Documents describing systems using this module must cite its source.
 *
 *     The above statements must be retained with all copies of this
 *     program and may not be removed without the consent of the
 *     University of Delaware.
 *
 *
 *     version  -1    David H. Crocker    March   1979
 *     version   0    David H. Crocker    April   1980
 *     version  v7    David H. Crocker    May     1981
 *     version   1    David H. Crocker    October 1981
 *
/*
 *      Modified by Steve Kille from ch_local  July 1982
 *      Mar 83  Steve Kille     Upgrade for new mmdf
 *
 */
#include <signal.h>
#include "ch.h"
#include "phs.h"

extern LLog *logptr;
extern	Chan	*ch_nm2struct();

Chan    *chanptr;               /* Loaction for validating channel      */
/*
 *  CH_PNIFTP  --  Channel for transmission on NIFTP/ JNT mail
 *
 *  This channel produces a JNT Mail format file for each host
 *  and then calls the NIFTP to transmit the file.
 *
 */

/*      MAIN MAIN MAIN MAIN MAIN MAIN MAIN MAIN MAIN MAIN MAIN MAIN     */

main (argc, argv)
int     argc;
char   *argv[];
{
    extern char *dupfpath ();
    short retval;

    mmdf_init (argv[0]);
    siginit ();
    signal (SIGINT, SIG_IGN);          /* always ignore interrupts         */



    if ((chanptr = ch_nm2struct (argv[0])) == (Chan *) NOTOK)
	err_abrt (RP_PARM, "unknown channel name '%s'", argv[0]);
				    /* find out who I am                  */
				    /* NIFTP channel can have several     */
				    /* names                              */

    retval = ch_niftp (argc, argv);
    ll_close (logptr);              /* clean log end, if cycled  */
    exit (retval);
}
/***************  (ch_) NIFTP  MAIL DELIVERY  ************************ */

ch_niftp (argc, argv)             /* send to NIFTP channel               */
int     argc;
char   *argv[];
{
    ch_llinit (chanptr);
#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "ch_niftp ()");
#endif

    if (rp_isbad (qu_init (argc, argv)))
	return (RP_NO);           /* problem setting-up for deliver     */
    if (rp_isbad (pn_init ()))
	return (RP_NO);

    phs_note (chanptr, PHS_CNSTRT);
    phs_note (chanptr, PHS_CNGOT);

    if (rp_isbad (qu2pn_send (argv[0])))
	return (RP_NO);           /* send the batch of outgoing mail    */

    qu_end (OK);                  /* done with Deliver function         */
    pn_end (OK);

    phs_end (chanptr, RP_OK);

    return (RP_OK);               /* NORMAL RETURN                      */
}

/**/

/* VARARGS2 */

err_abrt (code, fmt, b, c, d)     /* terminate ourself                  */
short     code;
char    fmt[],
	b[],
	c[],
	d[];
{
    char linebuf[LINESIZE];

    qu_end (NOTOK);
    pn_end (NOTOK);
    if (rp_isbad (code))
    {
#ifdef DEBUG
	if (rp_gbval (code) == RP_BNO || logptr -> ll_level >= LLOGBTR)
	{                         /* don't worry about minor stuff      */
	    sprintf (linebuf, "%s%s", "[ABEND:  %s]\t", fmt);
	    ll_log (logptr, LLOGFAT, linebuf, rp_valstr (code), b, c, d);
	    abort ();
	}
#endif
    }
    ll_close (logptr);           /* in case of cycling, close neatly   */
    exit (code);
}

init ("Message Acknowledgement", ackadr) != OK)
	return (NOTOK);           /* set up for returning               */

    rtn_good ();
    rtn_nogood ();

    rtn_cite (rtn_fp, 25);

    return (ml_end (OK));
}
/**/

LOCFUN
    rtn_nogood ()
			/*  handle bad addresses                        */
{
    struct rtn_str *ptr;

    if (rtn_bad == 0)
	return;

#ifdef	UCL_TSNAME
    if(*TSname ==mmdf/src/niftp/test_chan.c   444      0     12        2044  3620510551  10771 /*   Routine to test nn_mail.o                                  */
/*   Note that this does not use any MMDF libraries etc         */
/*   This is to emulate linking into the NIFTP                  */
/*                                                              */
/*      Steve Kille             August 1982                     */

int ipcs_print = 7;

main ()
{
    char        fname[50];
    char        queue[50];
    char        TSname[50];
    int         maxtries;
    char        *result;
    int         tmp;

    printf ( "\nInput filename: " );
    scanf ( "%s", fname);
    printf ( "Input queue: " );
    scanf ( "%s", queue );
    printf ( "TSname: " );
    scanf ("%s", TSname );
    printf ( "maxtries: " );
    scanf ( "%d", &maxtries );

    printf ( "\n\nni_mail (%s, %s, %s, %d, , )\n", fname, queue, TSname,
		maxtries, result);

    tmp = ni_mail (fname, queue, TSname, maxtries, &result);

    if (tmp == 0)
	printf ("\nResult is good");
    else
	printf ("\nResult is bad");

    printf ("\n\nResult string is: '%s'\n", result);

}

 *
/*
 *      Modified by Steve Kille from ch_local  July 1982
 *      Mar 83  Steve Kille     Upgrade for new mmdf
 *
 */
#include <signal.h>
#include "ch.h"
#include "phs.h"

extern LLog *logptr;
extern	Chan	*ch_nm2struct();

Chan    *chanptr;               /* Loaction for validating channel      */
/*
 *  CH_PNIFTP  --  Channel for transmission on NIFTP/ JNT mail
 *
 *  This channel produces a JNT Mail format file for each host
 *  and then calls the NIFTP to transmitmmdf/src/niftp/qu2pn_send.c   444      0     12       17051  3620510553  11125 #include "util.h"
#include "mmdf.h"

/*
 *     MULTI-CHANNEL MEMO DISTRIBUTION FACILITY  (MMDF)
 *
 *
 *     Copyright (C) 1979,1980,1981  University of Delaware
 *
 *     Department of Electrical Engineering
 *     University of Delaware
 *     Newark, Delaware  19711
 *
 *     Phone:  (302) 738-1163
 *
 *
 *     This program module was developed as part of the University
 *     of Delaware's Multi-Channel Memo Distribution Facility (MMDF).
 *
 *     Acquisition, use, and distribution of this module and its listings
 *     are subject restricted to the terms of a license agreement.
 *     Documents describing systems using this module must cite its source.
 *
 *     The above statements must be retained with all copies of this
 *     program and may not be removed without the consent of the
 *     University of Delaware.
 *
 *
 *     version  -1    David H. Crocker    March   1979
 *     version   0    David H. Crocker    April   1980
 *     version  v7    David H. Crocker    May     1981
 *     version   1    David H. Crocker    October 1981
 *
 */
/*                  SEND FROM DELIVER TO NIFTP CHANNEL
 *
 *      Adapted by Steve Kille from module to communicate with local channel
 *      July 1982
 *
 *      Mar 82  Steve Kille    Modify to use new MMDF - in particular the new
 *                                sub-list structuring
 *
 */

#include "ch.h"
#include "phs.h"
#include "ap.h"

extern struct ll_struct   *logptr;
extern char *qu_msgfile;          /* name of file containing msg text   */
extern Chan *chanptr;

LOCVAR int nadrs;               /* to keep a count of the addresses     */

LOCVAR struct rp_construct
			    rp_adr  =
{
    RP_AOK, 'a', 'd', 'd', 'r', 'e', 's', 's', ' ', 'o', 'k', '\0'
},

			    rp_hend     =
{
    RP_MOK, 'P', ' ', 'N', 'i', 'f', 't', 'p', ' ', 'e', 'n', 'd', ' ',
     'o', 'f', ' ', 'a',  'd', 'd', 'r', ' ', 'l', 'i', 's', 't', '\0'
};

LOCVAR struct rp_construct
			    rp_temp     =
{
    RP_LIO, 'P', ' ', 'N', 'i', 'f', 't', 'p', ',', ' ', 't', 'e', 'm',
     'p', ' ', 'f', 'a', 'i', 'l', 'u', 'r', 'e', '\0'
};

LOCVAR struct rp_construct
			    rp_perm     =
{
    RP_NDEL, 'P', ' ', 'N', 'i', 'f', 't', 'p', ' ',
    'p', 'e', 'r', 'm', 'a', 'n', 'a', 'n', 't', ' ',
    'f', 'a', 'i', 'l', 'u', 'r', 'e', '\0'
};

LOCVAR struct rp_construct
			    rp_bhst     =
{
    RP_BHST, 'B', 'a', 'd', ' ', 'H', 'o', 's', 't', '\0'
};


LOCVAR struct rp_construct
			   rp_noadr     =
{
    RP_NDEL, 'N', 'o', ' ', 'v', 'a', 'l', 'i', 'd', ' ', 'a', 'd', 'd',
    'r', 'e', 's', 's', 's', 'e', 's', '\0'
};

LOCVAR struct rp_construct
			  rp_noop       =
{
    RP_NOOP, 'E', 'n', 'd', ' ', 'o', 'f', ' ', 'h', 'o', 's', 't', ' ',
    'i', 'g', 'n', 'o', 'r', 'e', 'd', '\0'
};

/**/

qu2pn_send (chname)                /* overall mngmt for batch of msgs    */
char    chname [];
{
    short     result;
    char    info[LINESIZE],
	    sender[LINESIZE];

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "qu2pn_send ()");
#endif

    if (rp_isbad (result = qu_pkinit ()))
	return (result);

    if (rp_isbad (result = pn_sbinit ()))
    {
	printx ("problem with niftp submission... ");
	return (result);
    }

    for(;;){                /* get initial info for new message   */
	result = qu_rinit (info, sender, chanptr -> ch_apout);
	if(rp_gval(result) == RP_NS){
	    qu_rend();
	    continue;
	}
	else if(rp_gval(result) != RP_OK)
	    break;
	if (rp_isbad (result =
		    pn_winit (chname, sender, qu_msgfile)))
	    return (result);      /* ready to write message out         */

	if (rp_isbad (result = qu2pn_each ()))
				/* Now process the addresses and text   */
				/* for this message                     */
	    return (result);
	qu_rend();
    }
    qu_rend();

    if (rp_gval (result) != RP_DONE)
    {
	ll_log (logptr, LLOGTMP, "not DONE (%s)", rp_valstr (result));
	return (RP_RPLY);         /* catch protocol errors              */
    }

    qu_pkend ();                  /* done getting messages              */
    if (rp_isbad (result = pn_sbend ()))
				  /* done sending messages              */
    {
	printx ("bad ending for NIFTP delivery... ");
	fflush (stdout);
    }

    return (result);
}
/**/


LOCFUN
	qu2pn_each ()             /* send copy of text per address      */
{
    short     result;
    short     txt_result;
    char    host[LINESIZE];
    char    adr[LINESIZE];

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "qu2sm_each()");
#endif

    phs_note (chanptr, PHS_WRSTRT);

    nadrs = 0;
    if (rp_isbad (result = pn_wainit()))
	return (result);

    FOREVER                     /* iterate thru list                    */
    {
	result = qu_radr (host, adr);
	if (rp_isbad (result))
	    return (result);      /* get address from Deliver           */

/*  check for end of address sub-list.  different
 *  sub-lists reference different hosts to connect to.
 */
	switch (rp_gval (result))
	{
	    case RP_HOK:          /* end of sub-list */
		if (chanptr -> ch_host != NORELAY)
		{
				/* send all addresses to relay at once */
		    qu_wrply ((struct rp_bufstruct *) &rp_noop,
						rp_conlen (rp_noop));
		    continue;
		}
	    case RP_DONE:         /* end of list so fire it off */
#ifdef DEBUG
	ll_log (logptr, LLOGFTR, "End of list or sublist (%s)",
			rp_valstr (result));
#endif

		if (nadrs == 0)
		{
		    pn_wtend (RP_NO);  /* terminate current message    */
		    qu_wrply ((struct rp_bufstruct *) &rp_noadr,
				sizeof rp_noadr);
					/* assume strange temp error    */
		}
		else
		{
		    nadrs = 0;      /* reset, in case this is only a hend */
		    if (rp_isbad (txt_result = qu2pn_txtcpy ()))
		    {
					/* Tell other side of problem    */
			if (rp_gbval (txt_result) == RP_BTNO)
					/*  temp error                  */
			    qu_wrply ((struct rp_bufstruct *) &rp_temp,
				rp_conlen (rp_temp));
			else
			    qu_wrply ((struct rp_bufstruct *) &rp_perm,
				rp_conlen (rp_perm));
					/* no reason to abort though    */
		    }
		    else
			qu_wrply ((struct rp_bufstruct *) &rp_hend,
				rp_conlen (rp_hend));
		}
		if (rp_gval (result) == RP_DONE)
		    return (RP_OK);
				/* end of list - so return          */
				/* otherwise sort prepare for another one */
		if (rp_isbad (result = pn_wainit ()))
		    return (result);
		continue;

	    default:            /* actually have an address */
		if (rp_isbad (result = pn_wadr (host, adr)))
		{
		    pn_wtend (result);  /* clean up and get out         */
		    return (result);
		}

		nadrs++;

		qu_wrply ((struct rp_bufstruct *) &rp_adr, rp_conlen (rp_adr));
		continue;
	}
    }
    /* NOTREACHED */
}

/**/

LOCFUN
	qu2pn_txtcpy ()             /* copy the text of the message       */
{
    short   result;
    int     len;
    char    buffer[BUFSIZ];

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "qu2pn_txtcpy()");
#endif

    phs_note (chanptr, PHS_WRMSG);

    if (rp_isbad (result = pn_wtinit ()))
	return (result);

    qu_rtinit (0L);              /* ready to read the text             */
    len = sizeof(buffer);
    while ((rp_gval (result = qu_rtxt (buffer, &len))) == RP_OK)
    {                             /* send the text                      */
	if (rp_isbad (result = pn_wtxt (buffer, len)))
	    break;
	if (rp_gval (result) != RP_OK)
	{
	    result = RP_RPLY;
	    break;
	}
	len = sizeof(buffer);
    }


    if (rp_isgood (result) && rp_gval (result) != RP_DONE)
	result = RP_RPLY;        /* didn't get it all across?          */

    if (rp_isbad (result = pn_wtend (result)))
	return (result);          /* flag end of message                */
				  /* and then it can be sent off        */

    phs_note (chanptr, PHS_WREND);

    return (RP_MOK);               /* got the text out                   */
}
es and text   */
				/* for this message                     */
	    return (result);
	qu_rend();
    }
    qu_rend();

    if (rp_gval (result) != RP_DONE)
    {
	ll_log (logptr, LLOGTMP, "not DONE (%s)", rp_valstr (result));
	return (RP_RPLY);         /* catch protocol errors              */
    }

    qu_pkend ();                  /* done getting messages              */
    if (rp_isbad (result = pn_sbend ()))
				  /* done sending messages              */
    {
mmdf/src/niftp/qn2mm_send.c   444      0     12       17514  3637630225  11125 #include "util.h"
#include "mmdf.h"

/*
 *     MULTI-CHANNEL MEMO DISTRIBUTION FACILITY  (MMDF)
 *
 *
 *     Copyright (C) 1979,1980,1981  University of Delaware
 *
 *     Department of Electrical Engineering
 *     University of Delaware
 *     Newark, Delaware  19711
 *
 *     Phone:  (302) 738-1163
 *
 *
 *     This program module was developed as part of the University
 *     of Delaware's Multi-Channel Memo Distribution Facility (MMDF).
 *
 *     Acquisition, use, and distribution of this module and its listings
 *     are subject restricted to the terms of a license agreement.
 *     Documents describing systems using this module must cite its source.
 *
 *     The above statements must be retained with all copies of this
 *     program and may not be removed without the consent of the
 *     University of Delaware.
 *
 *
 *     version  -1    David H. Crocker    March   1979
 *     version   0    David H. Crocker    April   1980
 *     version  v7    David H. Crocker    May     1981
 *     version   1    David H. Crocker    October 1981
 *
 */
/*                  SEND FROM NIFTP TO SUBMIT                           */
/*                                                                      */
/*      Adapted from ph2mm_send.c                                       */
/*                                                                      */
/*      Steve Kille     August 1982                                     */
/*                                                                      */
/*      Note that for now, this module only handles one message at      */
/*      a time.  The overhead associated with Scanning a directory      */
/*      is not worthwhile.  Eventually the NIFTP may pass pointers      */
/*      too multiple files                                              */

/*
 * Mar 83  Steve Kille  Adapt for new MMDF
 *                      Changes to remove MMDF dependancies freom NIFTP
 */

#include "ch.h"

extern struct ll_struct   *logptr;

extern Chan *curchan;

LOCVAR int qn_gotone;

qn2mm_send (fname, TSname)
				/* Send message(s) to submit            */
    char fname[];               /* name of message file                 */
    char TSname[];              /* TS name (calling address) of source  */
				/* host - to be translated to host name */
{
    short     result;
    char    info[LINESIZE];
    char    sender[LINESIZE];
    struct rp_bufstruct thereply;
    int len;

#ifdef DEBUG
    ll_log (logptr, LLOGPTR, "qn2mm_send");
#endif

    if (rp_isbad (result = qn_pkinit ()))
	return (result);

    if (rp_isbad (result = mm_sbinit ()))
	return (result);


    if (rp_gval (result = qn_rinit (fname, TSname, info, sender)) != RP_OK){
	if(rp_gval(result) != RP_FIO)
	    qn_rtend (sender, fname, RP_NO);	/* this deletes the message */
	return(result);
    }
    /* 
     * For now we only go through this loop
     * once - multiple submissions later perhaps (but I doubt it)
     */

    strcat (info, "v");
    if (rp_isbad (result = mm_winit (curchan -> ch_name, info, sender)))
	return (result);      /* ready to process a message         */

    if (rp_isbad (result = mm_rrply (&thereply, &len)))
	return (result);      /* how did remote like it?            */

    switch (rp_gbval (thereply.rp_val))
    {			  /* was source acceptable?            */
	case RP_BNO:
    	case RP_BTNO:
	    return (thereply.rp_val);
    }

    if (rp_isbad (result = qn2mm_admng ()))
    {
	qn_rtend (sender, fname, result);
	return (result);
    }

    if (!qn_gotone)		/* stop here if no good addresses     */
				/* send the address list              */
    {
	if (sender [0] == '\0')
	{
	    ll_log (logptr, LLOGTMP,  "Filewith no valid addresseses and no return adr");
	    qn_rtend (sender, fname, RP_NDEL);
	    return (RP_NDEL);
	}
	return (qn_rtend (sender,  fname, RP_OK));
    }

				/* send the message text              */
    if (rp_isbad (result = qn2mm_txmng (sender, fname)))
    {
	qn_rtend (sender, fname, result);
	return (result);
    }

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "done with submission");
#endif

    if (rp_gval (result) == RP_DONE)
	return (RP_OK);
    return (result);
}
/**/

LOCFUN
	qn2mm_admng ()             /* send address list                  */
{
    struct rp_bufstruct thereply;
    int       len;
    short     result;
    char    host[LINESIZE];
    char    adr[LINESIZE];

    qn_gotone = FALSE;
    FOREVER                       /* iterate thru list                    */
    {                             /* we already have and adr              */
	result = qn_radr (host, adr);
	if (rp_isbad (result))
	    return (result);      /* get address from NIFTP file        */
	if (rp_gval (result) == RP_DONE)
	    break;                /* end of address list                */

#ifdef DEBUG
	ll_log (logptr, LLOGFTR, "'%s'@'%s'", adr, host);
#endif

	if (rp_isbad (result = mm_wadr (host, adr)))
	    return (result);      /* give to remote site                */

	if (rp_isbad (result = mm_rrply (&thereply, &len)))
	    return (result);      /* how did remote like it?            */

	switch (rp_gval (thereply.rp_val))
				  /* was address acceptable?            */
	{
	    case RP_AOK:          /* address ok, text not yet sent      */
		qn_wrply (&thereply, adr, host);
		qn_gotone = TRUE;
		break;

	    case RP_NO:           /* remaining acceptible responses     */
		thereply.rp_val = RP_NDEL;
	    case RP_USER:         /* unknown user                       */
	    case RP_NDEL:
	    case RP_AGN:
	    case RP_NOOP:
	    case RP_NS:
		qn_wrply (&thereply, adr, host);
		break;

	    default:              /* responses which force abort        */
		qn_wrply (&thereply, adr, host);
		ll_log (logptr, LLOGFAT,
			"reply err (%s)'%s'",
			rp_valstr (thereply.rp_val), thereply.rp_line);
		if (rp_isbad (thereply.rp_val))
		    return (thereply.rp_val);
		return (RP_RPLY);
	}
    }
    if (rp_isbad (result = qn_raend ()))
	return (result);        /* read end of address list            */
				 /* sets fp to beginning of text       */


    return (mm_waend ());        /* tell remote of address list end    */
}
/**/

LOCFUN
	qn2mm_txmng (sender, fname) /* send message text                  */
   char *sender;                /* Who sent the message - for error     */
				/* replies to go to                     */
    char        *fname;         /* Name of file containing message      */
				/* needed to unlink message             */
{
    struct rp_bufstruct thereply;
    int     len;
    short   result;
    char    buffer[BUFSIZ];

#ifdef DEBUG
    ll_log (logptr, LLOGPTR, "doing text");
#endif
    len = sizeof(buffer);
    while ((rp_gval (result = qn_rtxt (buffer, &len))) == RP_OK)
    {
	if (rp_isbad (result = mm_wtxt (buffer, len)))
	    return (result);
	len = sizeof(buffer);
    }
#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "done sending");
#endif

    if (rp_isbad (result))
	return (result);          /* problem with transmission          */
    if (rp_gval (result) != RP_DONE)
	return (RP_RPLY);         /* protocol error in reply code       */

    if (rp_isbad (result = mm_wtend ()))
	return (result);          /* flag end of message                */

    if (rp_isbad (result = mm_rrply (&thereply, &len)))
	return (result);          /* problem getting reply?             */
#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "got text reply");
#endif

    switch (rp_gval (thereply.rp_val))
    {                             /* was text acceptable?               */
	case RP_OK:
	case RP_MOK:              /* text was accepted                  */
	    break;

	default:                  /* responses which force abort        */
	    ll_log (logptr, LLOGFAT, "reply err (%s)'%s'",
			rp_valstr (thereply.rp_val), thereply.rp_line);
	    return (thereply.rp_val);
    }

				/* sent it out, so clean up read        */
    if (rp_isbad (result = qn_rtend (sender, fname, RP_OK)))
	return (result);          /* flag end of message                */

    return (RP_OK);
}
esult = qn2mm_txmng (sender, fname)))
    {
	qn_rtend (sender, fname, result);
	return (result);
    }

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "done with submission");
#endif

 mmdf/src/pop/   755      0     12           0  3657740274   6271 mmdf/src/pop/bboards.h   444      0     12        4427  3653247036  15134 1mmdf/src/bboards/bboards.hmmdf/src/pop/dropsbr.h   444      0     12        2167  3653247040  15224 1mmdf/src/bboards/dropsbr.hmmdf/src/pop/mmdfonly.h   444      0     12        1202  3653247042  15537 1mmdf/src/bboards/mmdfonly.hmmdf/src/pop/mts.h   444      0     12        3414  3653247042  13502 1mmdf/src/bboards/mts.hmmdf/src/pop/strings.h   444      0     12        1336  3653247044  15261 1mmdf/src/bboards/strings.hmmdf/src/pop/READ-ME   444      0     12        1124  3656173622   7303 
1.  In src/Makefile, add pop to SUBDIR, so it looks something like:

    SUBDIR= tools uip submit deliver local phone pobox bboards pop smtp uucp


2.  In mmdftailor, add these lines

MTBL	pop, file="pop", show="POP service"
MDMN	POP, show="POP Psuedo-domain", table="POP"
MCHN	pop, show="POP Delivery", que=pop, tbl=pop,
	pgm=pop, mod=reg, ap=822, host="pop"


3.   Create the table for the POP channel, one line:
	pop:		pop

4.    Run dbmbuild

5.    In your mmdf.start file, either add the pop channel to an
      existing background deliver, or give it one of its own:

	deliver -b -cpop &

ogptr, LLOGFAT,
			"reply err (%s)'%s'",
			rp_valstr (thereply.rp_val), thereply.rp_line);
		if (rp_isbad (thereply.rp_val))
		    return (thereply.rp_val);
		return (RP_RPLY);
	}
    }
    if (rp_isbad (result = qn_raend ()))
	return (result);        /* read end of address list            */
				 /* sets fp to beginning of text       */


    return (mm_waend ());        /* tell remote of address list end    */
}
/**/

LOmmdf/src/pop/qu2po_send.c   444      0     12        6636  3653247043  16177 1mmdf/src/bboards/qu2bb_send.cmmdf/src/pop/dropsbr.c   444      0     12       32363  3653247037  15241 1mmdf/src/bboards/dropsbr.cmmdf/src/pop/getbbent.c   444      0     12       33715  3655273070  15477 1mmdf/src/bboards/getbbent.cmmdf/src/pop/lock.c   444      0     12       13602  3653247041  13761 1mmdf/src/bboards/lock.cmmdf/src/pop/mmdfonly.c   444      0     12         346  3653247042  15515 1mmdf/src/bboards/mmdfonly.cmmdf/src/pop/po_wtmail.c   444      0     12       40066  3657737720  16054 1mmdf/src/bboards/bb_wtmail.cmmdf/src/pop/r1bindex.c   444      0     12         402  3653247043  15265 1mmdf/src/bboards/r1bindex.cmmdf/src/pop/strindex.c   444      0     12         337  3653247043  15544 1mmdf/src/bboards/strindex.cmmdf/src/pop/uprf.c   444      0     12         342  3653247044  13771 1mmdf/src/bboards/uprf.cmmdf/src/pop/ch_pop.c   444      0     12       10145  3653247036  15442 1mmdf/src/bboards/ch_bboards.cmmdf/src/pop/Makefile.real   444      0     12        3260  3656173624  10735 #
#   pop:	POP delivery channel transmission
#
MODULES =	ch_pop qu2po_send po_wtmail

OBJECTS =	ch_pop.o qu2po_send.o po_wtmail.o

SOURCES =	ch_pop.c qu2po_send.c po_wtmail.c 		getbbent.c dropsbr.c lock.c r1bindex.c strindex.c uprf.c 		mmdfonly.c

ZOTLIBS	=	getbbent.o dropsbr.o lock.o r1bindex.o strindex.o uprf.o 		mmdfonly.o

.c.o:;		$(CC) $(CFLAGS) -DPOP -DMMDFONLY -c .c

real-default:	pop

install:	$(CHANDIR)/$(MMPREF)pop

$(CHANDIR)/$(MMPREF)pop:	xpop
		-cp $(CHANDIR)/$(MMPREF)pop zxpop
		-chmod 0$(PGMPROT) zxpop
		cp xpop $(CHANDIR)/$(MMPREF)pop
		-$(CHOWN) root $(CHANDIR)/$(MMPREF)pop
		-chmod 04$(PGMPROT) $(CHANDIR)/$(MMPREF)pop
		-@ls -ls $(CHANDIR)/$(MMPREF)pop
		-@echo "POP installed normally"; echo ""

pop:		xpop
xpop:		$(OBJECTS) $(MMDFLIBS) $(ZOTLIBS)
		$(CC) $(LDFLAGS) -o  $(OBJECTS) $(MMDFLIBS) $(SYSLIBS) 			$(ZOTLIBS)

lint:;		$(LINT) $(LFLAGS) -DPOP -DMMDFONLY $(LLIBS) $(SOURCES)

clean:;		rm -f zxpop xpop *.o x.c makedep eddep make.out errs


# DO NOT DELETE THIS LINE -- make depend uses it
# DEPENDENCIES MUST END AT END OF FILE
ch_pop.o: ch_pop.c
ch_pop.o: ../../h/util.h
ch_pop.o: ../../h/mmdf.h
ch_pop.o: /usr/include/signal.h
ch_pop.o: ../../h/ch.h
ch_pop.o: ../../h/phs.h
qu2po_send.o: qu2po_send.c
qu2po_send.o: ../../h/util.h
qu2po_send.o: ../../h/mmdf.h
qu2po_send.o: ../../h/phs.h
qu2po_send.o: ../../h/ch.h
po_wtmail.o: po_wtmail.c
po_wtmail.o: ../../h/util.h
po_wtmail.o: ../../h/mmdf.h
po_wtmail.o: bboards.h
po_wtmail.o: ../../h/cnvtdate.h
po_wtmail.o: ../../h/ch.h
po_wtmail.o: ../../h/phs.h
po_wtmail.o: /usr/include/pwd.h
po_wtmail.o: /usr/include/sys/stat.h
# DEPENDENCIES MUST END AT END OF FILE
# IF YOU PUT STUFF HERE IT WILL GO AWAY
# see make depend above
DEBUG
    ll_log (logptr, LLOGBTR, "done sending");
#endif

    if (rp_isbad (result))
	return (result);          /* problem with transmission          */
    if (rp_gval (result) != RP_DONE)
	return (RP_RPLY);         /* protocol error in reply code       */

    if (rp_isbad (result = mm_wtend ()))
	return (result);          /* flagmmdf/src/pop/gen   444      0     12          55  3656173625   7007 make -f ../../Makefile.com -f Makefile.real 
#
MODULES =	ch_pop qu2po_send po_wtmail

OBJECTS =	ch_pop.o qu2po_send.o po_wtmail.o

SOURCES =	ch_pop.c qu2po_send.c po_wtmail.c 		getbbent.c dropsbr.c lock.c r1bindex.c strindex.c uprf.c 		mmdfonly.c

ZOTLIBS	=	getbbent.o dropsbr.o lock.o r1bindex.o strindex.o uprf.o 		mmdfonly.o

.c.o:;		$(CC) $(CFLAGS) -DPOP -DMMDFONLY -c .c

real-default:	pop

install:	$(CHANDIR)/$(MMPREF)pop

$(CHANDIR)/$(MMPREF)pop:	xpop
		-cp $(CHANDIR)/$(MMPREF)pop zxpop
		-chmod 0$(PGMPmmdf/src/tools/   755      0     12           0  3671074631   6624 mmdf/src/tools/cleanque.c   444      0     12       30317  3671073243  10672 /* $Header: cleanque.c,v 1.6 85/04/05 13:42:13 long Exp $ */
/*
 *     MULTI-CHANNEL MEMO DISTRIBUTION FACILITY  (MMDF)
 *     
 *
 *     Copyright (C) 1979,1980,1981  University of Delaware
 *     
 *     Department of Electrical Engineering
 *     University of Delaware
 *     Newark, Delaware  19711
 *
 *     Phone:  (302) 738-1163
 *     
 *     
 *     This program module was developed as part of the University
 *     of Delaware's Multi-Channel Memo Distribution Facility (MMDF).
 *     
 *     Acquisition, use, and distribution of this module and its listings
 *     are subject restricted to the terms of a license agreement.
 *     Documents describing systems using this module must cite its source.
 *
 *     The above statements must be retained with all copies of this
 *     program and may not be removed without the consent of the
 *     University of Delaware.
 *     
 *
 *     version  -1    David H. Crocker    March   1979
 *     version   0    David H. Crocker    April   1980
 *     version  v7    David H. Crocker    May     1981
 *     version   1    David H. Crocker    October 1981
 *
 */
#include "util.h"
#include "mmdf.h"
#include "msg.h"
#include "adr_queue.h"
#include "ch.h"
#include <sys/stat.h>

/*  QUECLEAN:  Clean up the mail queue directories                      */

/*  Jul 80 Dave Crocker     noaddr, correct the 2d multcpy
 *  Aug 80 Dave Crocker     fix orphaned
 *  Aug 80 Dave Crocker     fix name-handling for mclean
 *  Aug 81 Dave Crocker     test for chdir failures
 *  Mar 83 Doug Kingston    modified to use format independent directory
 *			    access routines a la 4.2BSD.  (libndir.a)
 */

/*#define RUNALON */

#define MINAGE  (long) (60 * 60 * 4)
				  /* at least four hours old             */

extern int warntime;    /* hours to wait before notification  */
extern int failtime;    /* hours to wait before returning msg */
extern int errno;
extern LLog msglog;
LLog *logptr = &msglog;

extern char *quedfldir,
	    *aquedir,
	    *squepref,
	    *tquedir,
	    *mquedir,
	    *supportaddr;

DIR *quep;                     /* for reading directory entries      */

struct stat    testnode;

time_t  curtime;              /* Current time in hours              */
int	elaphour;	      /* Message waiting time in hours      */

char    aname[LINESIZE],
	mname[LINESIZE];

int     effecid,                  /* system number of pgm/file's owner  */
	callerid;                 /* who invoked me?                    */

/**/

main (argc, argv)		  /* remove old queue files             */
int       argc;
char   *argv[];
{
    extern char *dupfpath ();
    extern time_t time ();

    mmdf_init (argv[0]);
    nice (-5);                 /* try to run faster, if root           */

    getwho (&callerid, &effecid); /* who am I and who is running me?    */
    mn_mmdf();			/* set up effective and group id's properly */

    if (argc == 2 && argv[1][0] == '-' && argv[1][1] == 'w')
	domsg = TRUE;

    time (&curtime);

    if (chdir (quedfldir) < OK || chdir (tquedir) < OK)
    {
	printx ("couldn't chdir to tquedir\n");
	ll_err (logptr, LLOGFAT, "couldn't chdir tquedir");
	exit (-1);
    }

    tclean ();			  /* clean out temporary files          */

    if (chdir (quedfldir) < OK)
    {
	printx ("couldn't chdir to quedfldir\n");
	ll_log (logptr, LLOGFAT, "couldn't chdir to quedfldir");
	exit (-1);
    }

    mclean ();			  /* get rid of orphaned text files     */

    if (chdir (quedfldir) < OK)
    {
	printx ("couldn't chdir to quedfldir\n");
	ll_log (logptr, LLOGFAT, "couldn't chdir to quedfldir");
	exit (-1);
    }

    aclean ();                    /* get rid of old queued messages     */
}
/* ***************  GET RID OF OLD TEMPORARY FILES  ****************  */

tclean ()			  /* clean out temporary files          */
{
    register struct direct *dp;
	
    if ((quep = opendir (".")) == NULL)
    {
	printx ("couldn't open tquedir\n");
	ll_log (logptr, LLOGFAT, "couldn't open tquedir");
	exit (-1);
    }

    while ((dp = readdir (quep)) != NULL)
	if (ismsg (dp) && minage (dp->d_name))
	{
	    printx ("removing temp file: %s\n", dp->d_name);
	    ll_log (logptr, LLOGGEN, "removing temp file: %s", dp->d_name);

#ifndef RUNALON
	    (void)  unlink (dp->d_name);
#endif
	}

    closedir (quep);
}
/* ***************  GET RID OF ORPHANED TEXT FILES  ****************  */

mclean ()			  /* get rid of orphaned text files     */
{
    register struct direct *dp;
	
    if ((quep = opendir (mquedir)) == NULL)
    {
	printx ("couldn't open quedfldir\n");
	ll_log (logptr, LLOGFAT, "couldn't open quedfldir");
	exit (-1);
    }

    while ((dp = readdir (quep)) != NULL)
	if (ismsg (dp))
	    mproc (dp->d_name);

    closedir (quep);
    quep = (DIR *) EOF;
}

mproc (filename)                /* check for & remove orphaned msgs   */
char    filename[];
{
    (void) sprintf (mname, "%s%s", mquedir, filename);
    (void) sprintf (aname, "%s%s", aquedir, filename);

    if (minage (mname) && stat (aname, &testnode) == -1)
    {                             /* old & no aquedir association       */
	printx ("removing old orphaned text file: %s\n", filename);
	ll_log (logptr, LLOGGEN, "removing text file: %s", filename);

#ifndef RUNALON
	(void)  unlink (mname);
#endif
    }
}

/**/

minage (filename)                /* is file more than x hours old?      */
char    filename[];
{
    if (stat (filename, &testnode) == -1)
	return (FALSE);           /* doesn't really exist               */

#ifdef RUNALON
    printx ("O? %s\n", filename);
#endif

    return (((curtime - testnode.st_mtime) > MINAGE) ? TRUE : FALSE);
				  /* two hours since modified?          */
}
/* **************  GET RID OF MESSAGES IN QUEUE TOO LONG **********  */

aclean ()
{
    Msg  themsg;
    char retadr[LINESIZE];
    register struct direct *dp;

    if ((quep = opendir (aquedir)) == NULL)
	    err_abrt (RP_FOPN, "can't open address queue");

    themsg.mg_null = '\0';
    while ((dp = readdir (quep)) != NULL)
	if (ismsg (dp))
	{
				  /* get queue entry name (msg name)   */
	    (void) strcpy (themsg.mg_mname, dp->d_name);
	    if (mq_rinit ((Chan *) 0, &themsg, retadr) != OK)
		continue;
	    elaphour = (int) ((curtime - themsg.mg_time) / (time_t) 3600);
#ifdef DEBUG
	    ll_log (logptr, LLOGFTR,
			    "%s (%d hrs)", themsg.mg_mname, elaphour);
#endif
	    if (elaphour > warntime) /* message is old                   */
	    {
		if (elaphour > failtime)
		{
                    /* old enough to return?            */
		    doreturn (&themsg, retadr);
		    deque (&themsg);
		}
		else if (!msg_warned(themsg.mg_stat))
		    dowarn (&themsg, retadr);
	    }
	    mq_rkill (OK);
	}

    closedir (quep);
    quep = (DIR *) EOF;
}

/**/

LOCFUN
	ismsg (theentry)         /* a processable message?             */
    register struct direct *theentry;
{
#ifdef DEBUG
    ll_log (logptr, LLOGFTR, "ovr_ismsg (name='%s')",
			theentry -> d_name);
#endif

/*  valid message:  entry allocated & name begins with "msg." */

    return ((theentry -> d_namlen < MSGNSIZE
		&& equal (theentry -> d_name, "msg.", 4)) ? TRUE : FALSE);
}

deque (themsg)
    Msg *themsg;
{
    struct adr_struct theadr;
    char curque[ADDRSIZE];

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "deque");
#endif

    curque[0] = '\0';   /* no queue in effect */

    mq_setpos (0L);	/* start at the beginning of the queue */
    while (mq_radr (&theadr) == OK)
    {
#ifdef DEBUG
	ll_log (logptr, LLOGFTR,
		    "(%c)%s:%s", theadr.adr_delv, theadr.adr_que, curque);
#endif
    	/*
    	 *  We have to dequeue from every queue since the return
    	 *  function already has marked all the addresses as "DONE".
    	 *  The msg_dequeue function was modified to ignore ENOENT
    	 *  so if the file is already gone, it is not an error.
    	 */
	if (!lexequ (curque, theadr.adr_que))
	{
	    (void) strcpy (curque, theadr.adr_que);
	    msg_dequeue (theadr.adr_que, themsg);
	}
    }
    msg_dequeue ((char *) 0, themsg);
}
/**/

LOCFUN
	msg_dequeue (theque, themsg) /* remove message from queue          */
	    char *theque;
	    Msg *themsg;
{
    char thename[LINESIZE];
#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "msg_deque (%s,%s)", 
		(theque == (char *) 0) ? "(Base entry)" : theque,
		themsg -> mg_mname);
#endif
#ifdef RUNALON
    return;
#endif

    if (theque == (char *) 0)
        (void) sprintf (thename, "%s%s", aquedir, themsg -> mg_mname);
    else
	(void) sprintf (thename, "%s%s/%s",
			squepref, theque, themsg -> mg_mname);

    if (unlink (thename) < OK && errno != ENOENT) {
	 /* this is real queue handle  */
	 ll_err (logptr, LLOGTMP, "Problem unlinking '%s' address: %s",
		    themsg -> mg_mname, thename);
    }

    if (theque == (char *) 0)
    {				  /* get rid of ALL the message */
	(void) sprintf (thename, "%s%s", mquedir, themsg -> mg_mname);
	if (unlink (thename) < OK) /* the text is just "baggage"         */
	    ll_err (logptr, LLOGTMP, "Problem unlinking %s text: '%s'",
	        themsg -> mg_mname, thename);
    }
}
/**/

dowarn (themsg, retadr)
	Msg *themsg;
	char retadr[];
{
#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "dowarn (%s, %s)", themsg -> mg_mname, retadr);
#endif

    printx ("%s:  delivery overdue; ", themsg -> mg_mname);
    (void) fflush (stdout);

    if (msg_nowarn (themsg -> mg_stat)) {
	printx ("warning not wanted\n");
    }
    else if (rtn_warn (themsg, retadr) == OK)
    {                     /* flag as already warned               */
	printx ("warning sent\n");
	ll_log (logptr, LLOGGEN, "warn *** Time warning (%s, %s)",
		    themsg -> mg_mname, retadr);
    }
    else
    {
	printx ("couldn't send warning\n");
	ll_err (logptr, LLOGTMP, "warn *** Couldn't time warn (%s, %s)",
		    themsg -> mg_mname, retadr);
    }
    (void) fflush (stdout);

    mq_rwarn ();
}

doreturn (themsg, retadr)
    Msg *themsg;
    char retadr[];
{
#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "doreturn (%s, %s)", themsg -> mg_mname, retadr);
#endif

    printx ("%s:  not delivered in time; waiting %d hrs;", themsg -> mg_mname, elaphour);
    (void) fflush (stdout);
    if (msg_noret (themsg -> mg_stat)) {
	printx (" error return not wanted\n");
    }    
    else if (rtn_time (themsg, retadr) == OK)
    {                         /* dequeue if couldn't notify   */
	printx (" returned\n");
	(void) fflush (stdout);
	ll_log (logptr, LLOGTMP, "ret *** Timeout return (%s, %s)",
		    themsg -> mg_mname, retadr);
    }
    else
    {                         /* dequeue if couldn't notify   */
	char orphanage[ADDRSIZE];

	(void) sprintf (orphanage, "Orphanage <%s>", supportaddr);
	printx (" couldn't return,\ntrying orphanage...");
	(void) fflush (stdout);
	if (rtn_time (themsg, orphanage) == OK)
	{
	    printx (" returned to orphanage.\n");
	    (void) fflush (stdout);
	}
	else
	{
	    ll_err (logptr, LLOGTMP, "ret *** Timeout couldn't return (%s,%s)",
		    themsg -> mg_mname, retadr);
	    dead_letter (themsg->mg_mname, "Timeout on delivery");
	}
    }
}


/**/

LOCFUN
        mn_mmdf ()		  /* setuid to mmdf: bypass being root  */
{				  /* get sys id for mmdf; setuid to it  */
    extern char *pathsubmit;     /* submit command file name           */
    extern char *cmddfldir;      /* directory w/mmdf commands          */
    char    temppath[LINESIZE];
    struct stat    statbuf;

#ifdef DEBUG
    ll_log (logptr, LLOGPTR, "mn_mmdf(); effec==%d",
		effecid );
#endif

/*  the following is a little strange, doing a stat on the object
 *  file, because setuid-on-execute does not work when the caller
 *  is root, as will happen when this is started by the rc file.
 *  hence, the effective id, from a getuid, will show root & not mmdf.
 *
 *  the goal is to have this process be name-independent of the caller,
 *  so that returned mail comes from mmdf and not the invoker.
 *
 *  in pickup mode, the id of the caller has to be retained, since
 *  pobox channels use that to determine access rights to mail.
 *
 *  All sets gid to mmdf's gid  --  <DPK@BRL>
 */

    if (effecid == 0)
    {
	getfpath (pathsubmit, cmddfldir, temppath);

	if (stat (temppath, &statbuf) < OK)
	    err_abrt (RP_LIO, "Unable to stat %s", temppath);
				  /* use "submit" to get mmdf id        */

	if (setgid (statbuf.st_gid) == NOTOK)
	    err_abrt (RP_LIO, "Can't setgid to mmdf (%d)", statbuf.st_gid);
	if (setuid (statbuf.st_uid) == NOTOK)
	    err_abrt (RP_LIO, "Can't setuid to mmdf (%d)", statbuf.st_uid);

	effecid = statbuf.st_uid; /* mostly needed for return mail      */
    }
}
);
    else
	(void) sprintf (thename, "%s%s/%s",
			squepref, theque, themsg -> mg_mname);

    if (unlink (thename) < OK && errno != ENOENT) {
	 /* this is real queue handle  */
	 ll_err (logptr, LLOGTMP, "Problem unlinking '%s' address: %s",
		    themsg -> mg_mname, thename);
    }

    if (theque == mmdf/src/tools/setup.c   444      0     12       20071  3671073245  10233 #include "util.h"
#include "mmdf.h"
/*
 *     MULTI-CHANNEL MEMO DISTRIBUTION FACILITY  (MMDF)
 *
 *
 *     Copyright (C) 1979,1980,1981  University of Delaware
 *
 *     Department of Electrical Engineering
 *     University of Delaware
 *     Newark, Delaware  19711
 *
 *     Phone:  (302) 738-1163
 *
 *
 *     This program module was developed as part of the University
 *     of Delaware's Multi-Channel Memo Distribution Facility (MMDF).
 *
 *     Acquisition, use, and distribution of this module and its listings
 *     are subject restricted to the terms of a license agreement.
 *     Documents describing systems using this module must cite its source.
 *
 *     The above statements must be retained with all copies of this
 *     program and may not be removed without the consent of the
 *     University of Delaware.
 *
 *
 *     version  -1    David H. Crocker    March   1979
 *     version   0    David H. Crocker    April   1980
 *     version  v7    David H. Crocker    May     1981
 *     version   1    David H. Crocker    October 1981
 *
 */
/*      make directories to be used by mmdf
 *
 *  Jan 82  D. Crocker          convert to !traceit before chmod of lockdir
 */
#include <pwd.h>
#include <sys/stat.h>
#include "ch.h"

extern char *mmdflogin;        /* login name for mmdf processes */

/* logs, programs, delivered mail */

extern char
	*logdfldir,
	*phsdfldir,
	*tbldfldir,
	*cmddfldir,
	*chndfldir,
	*mldfldir;

extern char     *lckdfldir,
	      *pn_quedir;

#ifdef JNTMAIL
int daemonuid,
    daemongid;
#define	DAEMON	"daemon"		/* Can be overridden by CC option */
char daemonname[]  = DAEMON;
#endif JNTMAIL

/* the queue */

extern int queprot;             /* protection on quedfldir[] parent */

extern char
	*quedfldir,
	*tquedir,
	*aquedir,
	*mquedir;

extern	int	ch_numchans;
extern	Chan	**ch_tbsrch;

int mmdfuid,
    mmdfgid;
int traceit;

/**/

main (argc, argv)
	int argc;
	char *argv[];
{
    extern struct passwd *getpwnam ();
    struct passwd *mmdfpwd;
    int realid,
	effecid;
    register int i;

    mmdf_init (argv[0]);
#ifdef JNTMAIL
    if ((mmdfpwd = getpwnam (daemonname)) == (struct passwd *) NULL)
    {
	printf ("No daemon entry in paswd file (%s)\n", daemonname);
	exit (NOTOK);
    }
    daemonuid = mmdfpwd -> pw_uid;
    daemongid = mmdfpwd -> pw_gid;
#endif


    while (argc-- > 1)
	if (argv[argc][0] == '-')
	    switch (argv[argc][1])
	    {
		case 'd':       /* debug              */
		case 'n':       /* like make's switch */
		case 't':       /* trace              */
		    traceit = TRUE;
		    break;

		default:
		    printf ("Unknown switch '%s'\n", argv[argc]);
		    (void) fflush (stdout);
		    exit (-1);
	    }

    getwho (&realid, &effecid);
    if (effecid != 0)
	printf ("   [Not running with superuser rights]\n");

    if (traceit)
	printf ("\n   [Trace only; no actions will be taken]\n");
    else
    {
	printf ("\n   [Intermediate directories will be created, if]\n");
	printf ("   [necessary.  No changes will be made to]\n");
	printf ("   [directories which already exist.]\n");
    }
    putchar ('\n');
    (void) fflush (stdout);

/*  get uid & gid for mmdf login, for setting directory ownerships */

    if ((mmdfpwd = getpwnam (mmdflogin)) == (struct passwd *) NULL)
    {
	printf ("***\t Login name '%s' not in password file.\n", mmdflogin);
	(void) fflush (stdout);
	exit (NOTOK);
    }
    mmdfuid = mmdfpwd -> pw_uid;
    mmdfgid = mmdfpwd -> pw_gid;

    printf ("Login '%s':  uid (%d), gid (%d)\n",
		mmdflogin, mmdfuid, mmdfgid);
    printf ("***\t Make sure Makefile's $(MMDFLOGIN) specifies this name.\n");
/**/

/*  create standard directories */

    printf ("\nCommand library directory '%s'\n\t[protected at 0711]\n", cmddfldir);
    if (my_drcreat (cmddfldir, 0711, mmdfuid, mmdfgid) >= 0)
    {
	printf ("***\t Make sure Makefile's $(LIBDIR) specifies this directory.\n");
	(void) fflush (stdout);
    }
    printf ("\nLogging directory '%s'\n\t[protected at 0711]\n", logdfldir);
    if (my_drcreat (logdfldir, 0711, mmdfuid, mmdfgid) >= 0)
    {
	printf ("***\t Check and run 'setlogs'.\n");
	printf ("***\t It should chdir to this directory.\n");
    }
    printf ("\nPhase (timestamping) directory '%s'\n\t[protected at 0711]\n", phsdfldir);
    my_drcreat (phsdfldir, 0711, mmdfuid, mmdfgid);

#ifndef V4_2BSD
    printf ("\nLocking directory '%s'\n\t[protected at 0777]\n", lckdfldir);
    my_drcreat (lckdfldir, 0777, mmdfuid, mmdfgid);
#endif V4_2BSD

#ifdef JNTMAIL
    printf ("\nNIFTP (JNT Mail) Spool directory '%s'\n\t[protected at 0700]\n",
		pn_quedir);
    my_drcreat (pn_quedir, 0700, daemonuid, daemongid);
#endif

    printf ("\nName table & telephone scripts '%s'\n\t[protected at 0711]\n", tbldfldir);
    my_drcreat (tbldfldir, 0711, mmdfuid, mmdfgid);

    printf ("\nChannel program directory '%s'\n\t[protected at 0700]\n", chndfldir);
    if (my_drcreat (chndfldir, queprot, mmdfuid, mmdfgid) >= 0)
    {
	printf ("***\t Make sure Makefile's $(CHANDIR) specifies this directory.\n");
	(void) fflush (stdout);
    }

    if (mldfldir != (char *)0 && !isnull (mldfldir[0]))
    {                           /* all mail delivered in common dir     */
	printf ("\nRecipient users' mailbox directory '%s'\n\t[protected at 0777]\n", mldfldir);
	my_drcreat (mldfldir, 0777, mmdfuid, mmdfgid);
    }
/**/

/*  create and set-up queue directory substructure */

    printf ("\nMail queue home directory '%s'\n\t[protected at 0777]\n", quedfldir);
    my_drcreat (quedfldir, 0777, mmdfuid, mmdfgid);
				 /* sub directories all open access      */

    printf ("\tChanging into queue home directory\n");
    if (chdir (quedfldir) < 0)
    {
	errprt (quedfldir);
	printf ("*** Cannot further process queue directories\n");
	(void) fflush (stdout);
	exit (-1);
    }
    printf ("\tLocking its parent [protected at %0o]\n", queprot);
    if (!traceit)
	if (chmod ("..", queprot) < 0)
		errprt ("quedfldir parent");
				      /* couldn't lock PARENT          */

    printf ("Temporary address directory '%s'\n\t[protected at 0777]\n", tquedir);
    my_drcreat (tquedir, 0777, mmdfuid, mmdfgid);

    printf ("Queued-address directory '%s'\n\t[protected at 0777]\n", aquedir);
    my_drcreat (aquedir, 0777, mmdfuid, mmdfgid);

    printf ("Message text directory '%s'\n\t[protected at 0777]\n", mquedir);
    my_drcreat (mquedir, 0777, mmdfuid, mmdfgid);

    for (i = 0; i < ch_numchans; i++) {
	char tmpname[64];

	if (ch_tbsrch[i] != (Chan *)NULL)
	{
	    (void) sprintf (tmpname, "q.%s", ch_tbsrch[i] -> ch_queue);
	    printf ("Queue directory '%s'\n\t[protected at 0777]\n", tmpname);
	    my_drcreat (tmpname, 0777, mmdfuid, mmdfgid);
	}
    }

    if (traceit)
	printf ("\n   [Trace only; no actions were taken.]\n");

    (void) fflush (stdout);
    exit (OK);
}
/**/

errprt (pref)
	char pref[];
{
    (void) fflush (stdout);
    perror (pref);
    (void) fflush (stderr);
}

my_drcreat (path, prot, uid, gid)
	char *path;
	int prot,
	    uid,
	    gid;
{
    extern struct passwd *getpwuid ();
    struct stat statbuf;
    struct passwd *pwdptr;
    register int retval;

    if (traceit)
	return (0);

    if (stat (path, &statbuf) >= OK)
    {                             /* already exists  */
	printf ("\t['%s' already exists.]\n", path);
	statbuf.st_mode &= 04777;
	if (statbuf.st_uid != uid)
	{
	    if ((pwdptr = getpwuid (statbuf.st_uid)) != NULL)
		printf ("***\t uid (%s) is not %s\n",
			    pwdptr -> pw_name, mmdflogin);
	    else
		printf ("UNREGISTERED owner uid (%d) is not %s\n",
			    statbuf.st_uid, mmdflogin);
	}
	if (statbuf.st_gid != gid)
	    printf ("***\t group id is (%d) and not (%d)\n",
			statbuf.st_gid, gid);

    	/* Allow liberal group permissions to ease management */
	if ((statbuf.st_mode&04707) != (prot&04707))
	    printf ("***\t protections (%o) should be (%o)\n",
			statbuf.st_mode, prot);
	return (OK);
    }

    printf ("\tCreate it? ");
    (void) fflush (stdout);
    switch (getchar ())
    {
	case 'Y':
	case 'y':
	    while (getchar () != '\n')
		continue;       /* DROP ON THROUGH */
	case '\n':
	    if ((retval = creatdir (path, prot, uid, gid)) < 0)
		errprt (path);
	    return (retval);
    }
    while (getchar () != '\n');
    return (NOTOK);
}

irectory.\n");
    }
    printf ("\nPhase (timestamping) directory '%s'\n\t[protected at 0711]\n", phsdfldir);
    my_drcreat (phsdfldir, 0711, mmdfuid, mmdfgid);

#ifndef V4_2BSD
    printf ("\nLocking directory '%s'\n\t[protected at 0777]\n", lckdfldir);
    my_drcreat (lckdfldir, 0777, mmdfuid, mmdfgid);
#endif V4_2BSD

#ifdef JNTMAIL
    printf ("\nNIFTP (JNT Mail) Spool directory '%s'\n\t[protected at 0700]\n",
		pn_quedir);
    my_drcreat (pn_qummdf/src/tools/nictable.c   444      0     12       14153  3671073247  10662 /*
 *                      N I C T A B L E . C
 *
 *                    (Revised as of 28 March)
 *
 *  Generates domain or channel tables from dhosts.txt
 *  (Based on code for dmtable.c from Steve Kille)
 *
 *    nictable <-D,-C,-T> [infile] [-d domain] [-t transport] [-s service]
 *      infile is location of dhosts.txt
 *      -D build a domain table
 *      -C build a channel table
 *      -T build a top table
 *      -d specifies domain to look for (e.g. ARPA or CSNET)
 *      -t specifies transport protocol (e.g. TCP)
 *      -s specifies service to look for (e.g. SMTP)
 *
 */
#include "util.h"
#include "mmdf.h"

char    *domain,                /* domain to add                        */
	*transport,             /* transport service to look for        */
	*service;               /* name of service to look for          */
int     domainlen = 0;
int     target = 0;
#define T_DOMAIN        1
#define T_CHANNEL       2
#define T_TOP           3

FILE    *infp = stdin;

char    entry[1000],            /* fully-assembled entry (multi-line) */
	linebuf[1000];          /* current line */

extern  char *index();
extern  char *rindex();
extern  char *strncpy();

main (argc, argv)
    int argc;
    char *argv[];
{
    mmdf_init (argv[0]);
    arg_init (argc, argv);

    doit ();
    exit (0);
}
/**/

arg_init (argc, argv)
    int argc;
    char *argv[];
{
    register int ind;

    if (argc < 2) {
	fputs ("Usage:  nictable <-D,-C,-T> [infile] [-d domain] [-t transport] [-s service]\n",
		stderr);
	exit(NOTOK);
    }

    for (ind = 1; ind < argc; ind++)
    {
	if (argv[ind][0] != '-')
	{
	    if ((infp = fopen (argv[ind], "r")) == NULL)
	    {
		fprintf (stderr,  "Unable to open '%s':", argv[ind]);
		perror ("");
		exit (NOTOK);
	    }
	}
	else
	    switch (argv[ind][1])
	    {
		case 'C':
		    target = T_CHANNEL;
		    break;

		case 'D':
		    target = T_DOMAIN;
		    break;

		case 'T':
		    target = T_TOP;
		    break;

		case 's':
		    service = argv[++ind];
		    break;

		case 't':
		    transport = argv[++ind];
		    break;

		case 'd':
		    domain = argv[++ind];
		    domainlen = strlen (domain);
		    break;
	    }
    }

    if (target == 0) {
	fputs ("nictable: you must specify target type (-C, -D, -T)\n", stderr);
	exit(NOTOK);
    }
}
/**/

#define NUMPARTS    75

doit ()
{
    int argc;
    char *argv[NUMPARTS];       /* fields of entry */

    fgets (linebuf, sizeof (linebuf), infp);
				/* load first line into buffer */
    while (getentry ())
    {
	argc = cstr2arg (entry, NUMPARTS, argv, ':');
	switch (argc)
	{
	    case -1:
		fprintf (stderr, "problem parsing entry '%s':", entry);
		continue;

	    case 0:
	    case 1:
	    case 2:
		continue;
	}

	if (!lexequ (argv[0], "host"))
	    continue;           /* only process host entries    */

	if (goodservice (argv [argc - 2]))
	    output (argc, argv);
    }
}
/**/

getentry ()
{
    if (linebuf[0] != '\0')
    {
	if (linebuf[0] != ';')
	    (void) strcpy (entry, linebuf);
	linebuf[0] = '\0';
    }
    else
	entry[0] = '\0';

    while (fgets (linebuf, sizeof (linebuf), infp) != NULL)
	switch (linebuf[0])
	{
	    case ';':           /* comment */
		break;

	    case ' ':           /* continuation */
	    case '\t':
		strcat (entry, linebuf);
		break;

	    default:            /* new entry */
		spstrip (entry);
		return (TRUE);
	}

    if (ferror (stdin))
    {
	perror ("problem reading host table");
	exit (NOTOK);
    }

    spstrip (entry);
    return ((entry[0] == '\0') ? FALSE : TRUE);
}
/**/

goodservice (str)      /* entry have right transport/service? */
char *str;
{
	int sargc;
	char *sargv[NUMPARTS];
	char *ts;               /* transport from entry         */
	char *sv;               /* service from entry           */
	int i;

	if (transport == (char *)0 && service == (char *)0)
		return (TRUE);

	sargc = cstr2arg (str, NUMPARTS, sargv, ',');
	for (i = 0; i < sargc; i++)
	{
	    ts = sargv [i];
	    if ((sv = index (ts, '/')) != 0)
		    *sv++ = '\0';

	    if (transport != (char *)0 && !lexequ (transport, ts))
		    continue;

	    if (sv == 0)
		return (TRUE);          /* implies all service  */

	    if (service != (char *)0 && (!lexequ (service, sv)))
		    continue;

	    return (TRUE);
	}
	return (FALSE);
}

gooddomain (str)      /* entry have right domain? */
char *str;
{
	if (domain == (char *)0)
		return (TRUE);
	if (lexequ (domain, str + strlen(str) - domainlen))
		return (TRUE);
	return (FALSE);
}
/**/
/*ARGSUSED*/
output (argc, argv)       /* output the list of host references */
    int argc;
    char *argv[];
{
    char *oargv[NUMPARTS];
    char *p;
    int i;
    int oargc;
    char buf [LINESIZE];

    if (target == T_CHANNEL) {
	if ((p = index (argv [2], ',')) != 0)
	    *p = '\0';
	oargc = cstr2arg (argv [1], NUMPARTS, oargv, ',');
	for (i = 0; i < oargc; i++)
	    printf ("%s:%s\n", argv [2], oargv [i]);
    } else if (target == T_DOMAIN) {
	oargc = cstr2arg (argv [2], NUMPARTS, oargv, ',');
	if (gooddomain (oargv[0]))
	{
	    if (domainlen) {
		(void) strncpy (buf, oargv[0], strlen(oargv[0])-domainlen-1);
		buf [strlen(oargv[0]) - domainlen - 1]  = '\0';
	    } else {  /* KLUDGE: No domain specified, wing it! */
		(void) strcpy (buf, oargv[0]);
		if (p = rindex(buf, '.'))
		    *p = '\0';
	    }
	    printf ("%s:%s\n", buf, oargv [0]);
	    for (i = 1; i < oargc; i++)
		if ((index(oargv[i], '.') == (char *) 0)
			&& !lexequ (buf, oargv[i]))
		    printf ("%s:%s\n", oargv  [i], oargv [0]);
	}
    } else if (target == T_TOP) {
	oargc = cstr2arg (argv [2], NUMPARTS, oargv, ',');
	if (gooddomain (oargv[0]))
	{
	    for (i = 1; i < oargc; i++)
		if ((index(oargv[i], '.') != (char *) 0)
			&& !gooddomain (oargv[i]))
		    printf ("%s:%s\n", oargv  [i], oargv [0]);
	}
	else
	{
	    for (i = 0; i < oargc; i++)
		if (index(oargv[i], '.')  != (char *) 0)
		    printf ("%s:%s\n", oargv  [i], oargv [0]);
	}
    } else {
	/* Invalid target */
	fprintf (stderr, "Internal nictable error, target is %d\n", target);
	exit (NOTOK);
    }

}

spstrip (str)           /* strip LWSP chars                     */
char *str;
{
	char *src, *dest;

	for (src = dest = str; *src != '\0';)
		if (!isspace (*src))
			*dest++ = *src++;
		else
			src++;
	*dest ='\0';
}
	    }
    }

    if (target == 0) {
	fputs ("nictable: you must specify target type (-C, -D, -T)\n", stderr);
	exit(NOTOK);
    }
}
/**/

#define NUMPARTS    75

doit ()
{
    int argc;
    char *argv[NUMPARTS];       /* fields of entry */

    fgets (linebuf, sizeof (linebuf), infp);
				/* load first line into buffer */
    while (getentry ())
    {
	argc = cstr2arg (entry, NUMPARTS, argv, ':');
	smmdf/src/tools/Makefile.real   444      0     12       22705  3653507601  11315 #
#	Makefile for MMDF maintenance programs
#
MODULES = 	amp checkup checkque cleanque checkaddr nictable \
		dbmbuild dbmedit dbmlist setup mailid rem_chans fixtai

real-default:	amp checkup cleanque checkque checkaddr \
		nictable dbmbuild dbmedit dbmlist setup mailid \
		rem_chans fixtai

install:	inst-checkup inst-checkque inst-dbmbuild inst-dbmedit \
		inst-dbmlist inst-nictable inst-cleanque inst-setup \
		inst-mailid inst-checkaddr inst-rem_chans inst-fixtai \
		inst-amp

lint:	l-amp l-checkup l-cleanque l-checkque l-dbmbuild l-dbmedit l-dbmlist \
	l-nictable l-setup l-mailid l-checkaddr l-rem_chans l-fixtai

#
#   amp:       test address-mapping code (coerce to RFC733 or RFC822)
#
inst-amp:	$(LIBDIR)/$(MMPREF)amp

$(LIBDIR)/$(MMPREF)amp  :  xamp
	cp xamp $(LIBDIR)/$(MMPREF)amp
	-$(CHOWN) $(MMDFLOGIN) $(LIBDIR)/$(MMPREF)amp
	-chmod 0$(PGMPROT) $(LIBDIR)/$(MMPREF)amp
	-@ls -ls $(LIBDIR)/$(MMPREF)amp
	-@echo "amp installed normally"; echo ""

amp:	xamp
xamp:	amp.o $(MMDFLIBS)
	$(CC) $(LDFLAGS) -o $@ amp.o $(MMDFLIBS) $(SYSLIBS)

l-amp:
	$(LINT) $(LFLAGS) amp.c $(LLIBS)

#
#   checkup:     verify mmdf directory and file setup
#
inst-checkup  :   $(LIBDIR)/$(MMPREF)checkup

$(LIBDIR)/$(MMPREF)checkup  :   xcheckup
	cp xcheckup $(LIBDIR)/$(MMPREF)checkup
	-$(CHOWN) $(MMDFLOGIN) $(LIBDIR)/$(MMPREF)checkup
	-chmod 0$(PGMPROT) $(LIBDIR)/$(MMPREF)checkup
	-@ls -ls $(LIBDIR)/$(MMPREF)checkup
	-@echo "checkup installed normally"; echo ""

checkup:	xcheckup
xcheckup:	checkup.o $(MMDFLIBS)
	$(CC) $(LDFLAGS) -o $@ checkup.o $(MMDFLIBS) $(SYSLIBS)

l-checkup:
	$(LINT) $(LFLAGS) checkup.c $(LLIBS)

#
#   checkque:     summarize status of mail queue
#
inst-checkque:	$(LIBDIR)/$(MMPREF)checkque

$(LIBDIR)/$(MMPREF)checkque:	xcheckque
	cp xcheckque $(LIBDIR)/$(MMPREF)checkque
	-$(CHOWN) $(MMDFLOGIN) $(LIBDIR)/$(MMPREF)checkque
	-chmod 04$(PGMPROT) $(LIBDIR)/$(MMPREF)checkque
	-@ls -ls $(LIBDIR)/$(MMPREF)checkque
	-@echo "checkque installed normally"; echo ""

checkque:	xcheckque
xcheckque:	checkque.o $(MMDFLIBS)
	$(CC) $(LDFLAGS) -o $@ checkque.o $(MMDFLIBS) $(SYSLIBS)

l-checkque:
	$(LINT) $(LFLAGS) checkque.c $(LLIBS)

#
#   dbmbuild:     build the MMDF dbm database
#
inst-dbmbuild  :   $(TBLDIR)/$(MMPREF)dbmbuild

$(TBLDIR)/$(MMPREF)dbmbuild  :   xdbmbuild
	cp xdbmbuild $(TBLDIR)/$(MMPREF)dbmbuild
	-$(CHOWN) $(MMDFLOGIN) $(TBLDIR)/$(MMPREF)dbmbuild
	-chmod 0$(PGMPROT) $(TBLDIR)/$(MMPREF)dbmbuild
	-@ls -ls $(TBLDIR)/$(MMPREF)dbmbuild
	-@echo "dbmbuild installed normally"; echo ""

dbmbuild:	xdbmbuild
xdbmbuild:	dbmbuild.o $(MMDFLIBS)
	    $(CC) $(LDFLAGS) -o $@ dbmbuild.o $(MMDFLIBS) $(SYSLIBS)

l-dbmbuild:
	$(LINT) $(LFLAGS) dbmbuild.c $(LLIBS)

#
#   dbmedit:     incrementally change contents of MMDF database
#
inst-dbmedit  :   $(TBLDIR)/$(MMPREF)dbmedit

$(TBLDIR)/$(MMPREF)dbmedit  :   xdbmedit
	cp xdbmedit $(TBLDIR)/$(MMPREF)dbmedit
	-$(CHOWN) $(MMDFLOGIN) $(TBLDIR)/$(MMPREF)dbmedit
	-chmod 0$(PGMPROT) $(TBLDIR)/$(MMPREF)dbmedit
	-@ls -ls $(TBLDIR)/$(MMPREF)dbmedit
	-@echo "dbmedit installed normally"; echo ""

dbmedit:	xdbmedit
xdbmedit:	dbmedit.o $(MMDFLIBS)
	    $(CC) $(LDFLAGS) -o $@ dbmedit.o $(MMDFLIBS) $(SYSLIBS)

l-dbmedit:
	$(LINT) $(LFLAGS) dbmedit.c $(LLIBS)

#
#   dbmlist:     dump the contents of the DBM based mail database
#
inst-dbmlist:	$(TBLDIR)/$(MMPREF)dbmlist

$(TBLDIR)/$(MMPREF)dbmlist:	xdbmlist
	cp xdbmlist $(TBLDIR)/$(MMPREF)dbmlist
	-$(CHOWN) $(MMDFLOGIN) $(TBLDIR)/$(MMPREF)dbmlist
	-chmod 0$(PGMPROT) $(TBLDIR)/$(MMPREF)dbmlist
	-@ls -ls $(TBLDIR)/$(MMPREF)dbmlist
	-@echo "dbmlist installed normally"; echo ""

dbmlist:	xdbmlist
xdbmlist:	dbmlist.o $(MMDFLIBS)
	    $(CC) $(LDFLAGS) -o $@ dbmlist.o $(MMDFLIBS) $(SYSLIBS)

l-dbmlist:
	$(LINT) $(LFLAGS) dbmlist.c $(LLIBS)

#
#   nictable:     summarize status of mail queue
#
inst-nictable  :   $(TBLDIR)/$(MMPREF)nictable

$(TBLDIR)/$(MMPREF)nictable  :   xnictable
	cp xnictable $(TBLDIR)/$(MMPREF)nictable
	-$(CHOWN) $(MMDFLOGIN) $(TBLDIR)/$(MMPREF)nictable
	-chmod 0$(PGMPROT) $(TBLDIR)/$(MMPREF)nictable
	-@ls -ls $(TBLDIR)/$(MMPREF)nictable
	-@echo "nictable installed normally"; echo ""

nictable:	xnictable
xnictable:	nictable.o $(MMDFLIBS)
	$(CC) $(LDFLAGS) -o $@ nictable.o $(MMDFLIBS) $(SYSLIBS)

l-nictable:
	$(LINT) $(LFLAGS) nictable.c $(LLIBS)

#
#   checkaddr:	Check addresses from lists or as arguments
#
inst-checkaddr:	$(LIBDIR)/$(MMPREF)checkaddr

$(LIBDIR)/$(MMPREF)checkaddr  :   xcheckaddr
	cp xcheckaddr $(LIBDIR)/$(MMPREF)checkaddr
	-$(CHOWN) $(MMDFLOGIN) $(LIBDIR)/$(MMPREF)checkaddr
	-chmod 0$(PGMPROT) $(LIBDIR)/$(MMPREF)checkaddr
	-@ls -ls $(LIBDIR)/$(MMPREF)checkaddr
	-@echo "checkaddr installed normally"; echo ""

checkaddr:	xcheckaddr
xcheckaddr:	checkaddr.o $(MMDFLIBS)
	$(CC) $(LDFLAGS) -o $@ checkaddr.o $(MMDFLIBS) $(SYSLIBS)

l-checkaddr:
	$(LINT) $(LFLAGS) checkaddr.c $(LLIBS)

#
#   mailid:	Program to return mailid of invoker or specified userid
#
inst-mailid:	$(LIBDIR)/$(MMPREF)mailid

$(LIBDIR)/$(MMPREF)mailid  :   xmailid
	cp xmailid $(LIBDIR)/$(MMPREF)mailid
	-$(CHOWN) $(MMDFLOGIN) $(LIBDIR)/$(MMPREF)mailid
	-chmod 0$(PGMPROT) $(LIBDIR)/$(MMPREF)mailid
	-@ls -ls $(LIBDIR)/$(MMPREF)mailid
	-@echo "mailid installed normally"; echo ""

mailid:	xmailid
xmailid:	mailid.o $(MMDFLIBS)
	$(CC) $(LDFLAGS) -o $@ mailid.o $(MMDFLIBS) $(SYSLIBS)

l-mailid:
	$(LINT) $(LFLAGS) mailid.c $(LLIBS)

#
#   cleanque:   Remove old/dangling files from message queue directories
#
inst-cleanque    :   $(LIBDIR)/$(MMPREF)cleanque

$(LIBDIR)/$(MMPREF)cleanque  :   xcleanque
	cp xcleanque $(LIBDIR)/$(MMPREF)cleanque
	-$(CHOWN) root $(LIBDIR)/$(MMPREF)cleanque
	-chmod 04$(PGMPROT) $(LIBDIR)/$(MMPREF)cleanque
	-@echo "***  make sure /etc/rc & overnight cron run this program"
	-@ls -ls $(LIBDIR)/$(MMPREF)cleanque
	-@echo "cleanque installed normally"; echo ""

cleanque:	xcleanque
xcleanque:	cleanque.o $(MMDFLIBS)
	$(CC) $(LDFLAGS) -o $@ cleanque.o $(MMDFLIBS) $(SYSLIBS)

l-cleanque:
	$(LINT) $(LFLAGS) cleanque.c $(LLIBS)

#
#   fixtai:   Extract the fixed part of an mmdf tailor file
#

inst-fixtai    :   $(LIBDIR)/$(MMPREF)fixtai

$(LIBDIR)/$(MMPREF)fixtai  :   xfixtai
	    cp xfixtai $(LIBDIR)/$(MMPREF)fixtai
	    -$(CHOWN) $(MMDFLOGIN) $(LIBDIR)/$(MMPREF)fixtai
	    -chmod 0$(PGMPROT) $(LIBDIR)/$(MMPREF)fixtai
	    -@ls -ls $(LIBDIR)/$(MMPREF)fixtai
	    -@echo "fixtai installed normally"; echo ""

fixtai:   xfixtai
xfixtai:  fixtai.o $(MMDFLIBS)
	    $(CC) $(LDFLAGS) -o $@ fixtai.o $(MMDFLIBS) 

l-fixtai:
	$(LINT) $(LFLAGS) fixtai.c $(LLIBS)


#
#   rem_chans:   Comment out the channel defs from an mmdf tailor file
#
inst-rem_chans    :   $(LIBDIR)/$(MMPREF)rem_chans

$(LIBDIR)/$(MMPREF)rem_chans  :   xrem_chans
	    cp xrem_chans $(LIBDIR)/$(MMPREF)rem_chans
	    -$(CHOWN) $(MMDFLOGIN) $(LIBDIR)/$(MMPREF)rem_chans
	    -chmod 0$(PGMPROT) $(LIBDIR)/$(MMPREF)rem_chans
	    -@ls -ls $(LIBDIR)/$(MMPREF)rem_chans
	    -@echo "rem_chans installed normally"; echo ""

rem_chans:   xrem_chans
xrem_chans:  rem_chans.o $(MMDFLIBS)
	    $(CC) $(LDFLAGS) -o $@ rem_chans.o $(MMDFLIBS)

l-rem_chans:
	$(LINT) $(LFLAGS) rem_chans.c $(LLIBS)

#
#   setup:     build mmdf directories
#
inst-setup  :   $(LIBDIR)/$(MMPREF)setup

$(LIBDIR)/$(MMPREF)setup  :   xsetup
	cp xsetup $(LIBDIR)/$(MMPREF)setup
	-$(CHOWN) $(MMDFLOGIN) $(LIBDIR)/$(MMPREF)setup
	-chmod 0$(PGMPROT) $(LIBDIR)/$(MMPREF)setup
	-@ls -ls $(LIBDIR)/$(MMPREF)setup
	-@echo "setup installed normally"; echo ""

setup:	xsetup
xsetup:	setup.o $(MMDFLIBS)
	$(CC) $(LDFLAGS) -o $@ setup.o $(MMDFLIBS) $(SYSLIBS)

l-setup:
	$(LINT) $(LFLAGS) setup.c $(LLIBS)


clean:
	-rm -f x* *.o makedep eddep make.out errs


# DO NOT DELETE THIS LINE -- make depend uses it

amp.o: amp.c
amp.o: /usr/include/stdio.h
amp.o: ../../h/util.h
amp.o: ../../h/mmdf.h
amp.o: ../../h/ch.h
amp.o: ../../h/ap.h
checkup.o: checkup.c
checkup.o: ../../h/util.h
checkup.o: ../../h/mmdf.h
checkup.o: /usr/include/pwd.h
checkup.o: /usr/include/sys/stat.h
checkup.o: ../../h/ch.h
checkup.o: ../../h/d_proto.h
checkup.o: ../../h/d_structs.h
checkup.o: ../../h/gettys.h
checkup.o: ../../h/dm.h
checkque.o: checkque.c
checkque.o: ../../h/util.h
checkque.o: ../../h/mmdf.h
checkque.o: /usr/include/sys/stat.h
checkque.o: /usr/include/utmp.h
checkque.o: ../../h/ch.h
checkque.o: ../../h/msg.h
checkque.o: ../../h/adr_queue.h
checkque.o: ../../h/phs.h
cleanque.o: cleanque.c
cleanque.o: ../../h/util.h
cleanque.o: ../../h/mmdf.h
cleanque.o: ../../h/msg.h
cleanque.o: ../../h/adr_queue.h
cleanque.o: ../../h/ch.h
cleanque.o: /usr/include/sys/stat.h
checkaddr.o: checkaddr.c
checkaddr.o: ../../h/util.h
checkaddr.o: ../../h/mmdf.h
nictable.o: nictable.c
nictable.o: ../../h/util.h
nictable.o: ../../h/mmdf.h
dbmbuild.o: dbmbuild.c
dbmbuild.o: ../../h/util.h
dbmbuild.o: ../../h/mmdf.h
dbmbuild.o: ../../h/ch.h
dbmbuild.o: ../../h/dm.h
dbmbuild.o: ../../h/chdbm.h
dbmedit.o: dbmedit.c
dbmedit.o: ../../h/util.h
dbmedit.o: ../../h/mmdf.h
dbmedit.o: ../../h/chdbm.h
dbmlist.o: dbmlist.c
dbmlist.o: ../../h/util.h
dbmlist.o: ../../h/mmdf.h
dbmlist.o: ../../h/ch.h
dbmlist.o: ../../h/chdbm.h
setup.o: setup.c
setup.o: ../../h/util.h
setup.o: ../../h/mmdf.h
setup.o: /usr/include/pwd.h
setup.o: /usr/include/sys/stat.h
setup.o: ../../h/ch.h
mailid.o: mailid.c
mailid.o: /usr/include/stdio.h
mailid.o: /usr/include/pwd.h
rem_chans.o: rem_chans.c
rem_chans.o: ../../h/util.h
rem_chans.o: ../../h/cmd.h
fixtai.o: fixtai.c
fixtai.o: ../../h/util.h
fixtai.o: ../../h/cmd.h
fixtai.o: ../../h/ch.h
fixtai.o: /usr/include/sys/stat.h
fixtai.o: ../../h/ap.h
fixtai.o: ../../h/conf.h
# DEPENDENCIES MUST END AT END OF FILE
# IF YOU PUT STUFF HERE IT WILL GO AWAY
# see make depend above
cleanque
	-$(CHOWN) root $(LIBDIR)/$(MMPREF)cleanque
	-chmommdf/src/tools/dbmbuild.c   444      0     12       30242  3671073251  10653 /*
 *      Database builder for MMDF address database
 *
 *      Command line:
 *              dbmbuild [-flags] [outfile]
 *
 *              where flags are:
 *              d   -   debugging output
 *              v   -   verbose output
 *		k   -   keep going if a table is missing
 *              n   -   make a new dbm file from scratch
 *
 *      output-file is name of EXISTING dbm file to insert entires.
 *      Note it is two files, an output-file.dir and an output-file.pag.
 *
 *      No arguments defaults to  -n.
 */

#include "util.h"
#include "mmdf.h"
#include "ch.h"
#include "dm.h"
#include "chdbm.h"

extern Table **tb_list;  /* order tables searched         */
extern Domain **dm_list; /* ordr domain tables searched  */
extern Chan  **ch_tbsrch;  /* order chans searched         */

extern char *blt();
extern char *multcat();
extern char *locname;
extern char *tbldfldir;
extern char *tbldbm;
extern char *lckdfldir;

typedef struct
{
    char *dptr;
    int dsize;
} datum;

extern datum fetch ();

typedef struct dbmentry
{
    char dbmvalue[1];
} Entry;

int error;
int Debug;
int Verbose;
int newflag;
int keepgoing;
char dbfile[128];
char *dblock;
int lckfd;

char dbmstr[] = "DBM$$";
			/* string for locking the database */

/*
 * Main procedure.
 * process arguments, set flags and invoke file processing.
 * clean up and exit properly.
 */

main(argc, argv)
char **argv;
{
    int ind;
    char *p;
    char *outfile;
    register Table  *tblptr;

    mmdf_init (argv[0]);
    argv++; argc--;
    outfile = (char *)0;

    if (argc == 0)
	newflag++;

    while(argc-- > 0)
    {
	if (Debug)
	    fprintf (stderr, "arg='%s'\n", *argv);
	p = *argv++;
	if(*p == '-')
	{
	    while(*++p)
		switch(*p)
		{
		    case 'O':	/* Obsolete, ignored */
			    break;

		    case 'd':
			    Debug++;
			    break;

		    case 'k':
			    keepgoing++;
			    break;

		    case 'n':
			    newflag++;
			    break;

		    case 'v':
			    Verbose++;
			    break;

		    default:
			    fprintf (stderr,"Unknown flag %c\n",*p);
			    break;
		}
	}
	else
	{
	     outfile = p;
	     break;
	}
    }

    if (outfile == 0)           /* use default database */
    {
	if (tbldbm == 0 || tbldbm[0] == '\0')
	{
	    fprintf (stderr, "no default data base, in 'tbldbm' variable\n");
	    exit (NOTOK);
	}
	outfile = tbldbm;
    }

    /*
     *  Check for existence first
     */
    error = 0;
    if (argc <= 0) {
	for (ind = 0; dm_list[ind] != (Domain *)0; ind++)
	    error += check(dm_list[ind] -> dm_table);

	for (ind = 0; ch_tbsrch[ind] != (Chan *) 0; ind++)
	{
	    error += check(ch_tbsrch[ind] -> ch_table);

	    if ((tblptr = ch_tbsrch[ind] -> ch_insource) != (Table *) 0)
		error += check(tblptr);
	    if ((tblptr = ch_tbsrch[ind] -> ch_outsource) != (Table *) 0)
		error += check(tblptr);
	    if ((tblptr = ch_tbsrch[ind] -> ch_indest) != (Table *) 0)
		error += check(tblptr);
	    if ((tblptr = ch_tbsrch[ind] -> ch_outdest) != (Table *) 0)
		error += check(tblptr);
	}

	for (ind = 0; (tblptr = tb_list[ind]) != (Table *) 0; ind++)
	    error += check (tblptr);
    } else {
	while (argc-- > 0)
	{
	    if ((tblptr = tb_nm2struct (*argv++)) == (Table *) NOTOK)
	    {
		fprintf (stderr, "Table '%s' is unknown\n", *--argv);
		error++;
	    };
	    error += check(tblptr);
	}
    }

    if (error) {
	fprintf (stderr, "Some tables are missing.  dbmbuild aborted.\n");
    	exit(9);
    }

    getfpath (outfile, tbldfldir, dbfile);
    dblock = multcat (dbfile, ".lck", (char *)0);
    (void) close( creat( dblock, 0444 ));

    if ((lckfd = lk_open(dblock, 0, (char *)0, (char *)0, 10)) < 0) {
	perror (dblock);
	fprintf (stderr, "Data base %s is locked.  Try again later.\n",
		 dbfile);
	exit (NOTOK);
    }

    if (!theinit (newflag))
	cleanup (-1);

    if (argc <= 0)
    {
	/*
	 *  Now process for real!
	 */
	for (ind = 0; dm_list[ind] != (Domain *)0; ind++)
	{
	    if (Debug)
		fprintf (stderr, "Domain '%s'\n", dm_list[ind] -> dm_show);
	    process (dm_list[ind] -> dm_table);
	}

	for (ind = 0; ch_tbsrch[ind] != (Chan *) 0; ind++)
	{
	    if (Debug)
		fprintf (stderr, "Chan '%s'\n", ch_tbsrch[ind] -> ch_show);
	    process (ch_tbsrch[ind] -> ch_table);

	    if ((tblptr = ch_tbsrch[ind] -> ch_insource) != (Table *) 0)
	    {
		if (Debug)
		    fprintf (stderr, "Source inbound filter\n");
		if (Verbose || Debug)
		    putc ('\t', stderr);
		process (tblptr);
	    }
	    if ((tblptr = ch_tbsrch[ind] -> ch_outsource) != (Table *) 0)
	    {
		if (Debug)
		    fprintf (stderr, "Source outbound filter\n");
		if (Verbose || Debug)
		    putc ('\t', stderr);
		process (tblptr);
	    }
	    if ((tblptr = ch_tbsrch[ind] -> ch_indest) != (Table *) 0)
	    {
		if (Debug)
		    fprintf (stderr, "Destination inbound filter\n");
		if (Verbose || Debug)
		    putc ('\t', stderr);
		process (tblptr);
	    }
	    if ((tblptr = ch_tbsrch[ind] -> ch_outdest) != (Table *) 0)
	    {
		if (Debug)
		    fprintf (stderr, "Destination outbound filter\n");
		if (Verbose || Debug)
		    putc ('\t', stderr);
		process (tblptr);
	    }
	}

	for (ind = 0; (tblptr = tb_list[ind]) != (Table *) 0; ind++)
	{                           /* in case table used by chan */
	    if (tblptr -> tb_fp != (FILE *) EOF)
	    {
		if (Debug)
		    fprintf (stderr, "Table '%s'\n", tblptr -> tb_name);
		process (tblptr);
	    }
	}
    }
    else
	while (argc-- > 0)
	{
	    if ((tblptr = tb_nm2struct (*argv++)) == (Table *) NOTOK)
	    {
		 (void) fflush (stdout);
		 fprintf (stderr, "Table '%s' is unknown\n", *--argv);
		 cleanup (-1);
	    };
	    process (tblptr);
	}

    cleanup (theend (newflag));
}

/**/

theinit (new)               /* initialize the dbm files */
int new;
{
    char tmpfile[100];

    if (new)                /* start with fresh files */
    {
	if (Verbose || Debug)
	    fprintf (stderr, "Temporary database:  %s$\n", dbfile);
	(void) sprintf (tmpfile, "%s$.pag", dbfile);
	if(Debug)
	    fprintf (stderr, "creating '%s'\n", tmpfile);

	if (close (creat (tmpfile, 0644)) < 0)
	{                       /* create and/or zero the file */
	    fprintf (stderr, "could not creat '%s':  ", tmpfile);
	    perror ("");
	    cleanup (-1);
	}
	chmod (tmpfile, 0644);  /* in case umask screwed us */

	(void) sprintf (tmpfile, "%s$.dir", dbfile);
	if(Debug)
	    fprintf (stderr, "creating '%s'\n", tmpfile);

	if (close (creat (tmpfile, 0644)) < 0)
	{                       /* create and/or zero the file */
	    fprintf (stderr, "could not creat '%s':  ", tmpfile);
	    perror ("");
	    cleanup (-1);
	}
	chmod (tmpfile, 0644);  /* in case umask screwed us */
	(void) sprintf (tmpfile, "%s$", dbfile);
	return (dbfinit (tmpfile));
    }

    return (dbfinit (dbfile));
}

theend (new)               /* cleanup the dbm files */
int new;
{
    char fromfile[100];
    char tofile[100];

    dbfclose();

    if (new)                /* started with fresh files */
    {
	if (Verbose || Debug)
	    fprintf(stderr, "Moving to database:  %s\n", dbfile);

	(void) sprintf (fromfile, "%s$.pag", dbfile);
	(void) sprintf (tofile, "%s.pag", dbfile);
	if (Debug)
	    fprintf (stderr, "moving '%s'\n", fromfile);

	(void) unlink (tofile);

	if ((link (fromfile, tofile ) < 0) || (unlink (fromfile) < 0))
	{                       /* create and/or zero the file */
	    fprintf (stderr, "could not link to '%s':  ", tofile);
	    perror ("");
	    cleanup (-1);
	}

	(void) sprintf (fromfile, "%s$.dir", dbfile);
	(void) sprintf (tofile, "%s.dir", dbfile);
	if(Debug)
	    fprintf (stderr, "moving '%s'\n", fromfile);

	(void) unlink (tofile);

	if ((link (fromfile, tofile ) < 0) || (unlink (fromfile) < 0))
	{                       /* create and/or zero the file */
	    fprintf (stderr, "could not link to '%s':  ", tofile);
	    perror ("");
	    cleanup (-1);
	}

	return (TRUE);
    }

    return (TRUE);
}

/*
 * Initialize the dbm file.
 * Fetch the local name datum
 * Init to 1 and store it if there is no datum by that name.
 */

dbfinit(filename)
char *filename;
{
	if(dbminit(filename) < 0)
	{
	    fprintf (stderr, "could not initialize data base '%s'", filename);
	    perror("");
	    return (0);
	}
	return (1);
}

/*
 * Close the dbm datafile.
 * We can't close the file because the library does not provide the call...
 */

dbfclose()
{
    return (1);
}

/*
 *      Process a sequential file and insert items into the database
 *      Opens argument assumes database is initialized.
 */

process (tblptr)
    Table *tblptr;
{
    datum key, value;
    char tbkey[LINESIZE],
	 tbvalue[LINESIZE];
    char *cp;

    if (Verbose || Debug)
	fprintf (stderr, tblptr -> tb_fp == (FILE *)EOF ?
		"%s (%s) already done\n" : "%s (%s)\n",
		tblptr -> tb_show, tblptr -> tb_name);
    if (tblptr -> tb_fp == (FILE *)EOF)
	return;

#ifdef NAMESERVER
    if ((tblptr -> tb_flags & TB_SRC) == TB_NS) {
    	if (Debug || Verbose)
	    fprintf (stderr, "   (Nameserver table)\n");
	return;
    }
#endif NAMESERVER
    if (!tb_open (tblptr, TRUE)) /* gain access to a channel table */
    {
	fprintf (stderr, "could not open table \"%s\" (%s, file = '%s'):\n\t",
			tblptr -> tb_show, tblptr -> tb_name,
			tblptr -> tb_file);
	perror("");
	tblptr -> tb_fp = (FILE *) EOF;		/* Don't try again */
    	if (keepgoing)
    	    return;
    	cleanup(-1);
    }

    while (tb_read (tblptr, tbkey, tbvalue))
    {                           /* read a table's record        */
	switch (tbkey[0])
	{
	    case '\0':
	    case '#':
	    case ';':
		continue;       /* empty lines and comments ok */
	}

	if(tbvalue[0] == '\0')
	{                       /* empty value field            */
	    value.dptr = "";
	    value.dsize = 1;
	}
	else
	{
	    value.dptr = tbvalue;
	    value.dsize = strlen (tbvalue) + 1;
	}
    	for (cp = tbkey; *cp != 0; cp++)
    	    *cp = uptolow(*cp);			/* All keys are lower case */
	key.dptr = tbkey;
	key.dsize = strlen (tbkey) + 1;
	if(Debug)
	    fprintf (stderr, "(%d)'%s': (%d)'%s'\n",
		     key.dsize, key.dptr, value.dsize, value.dptr);
	install(key, value, tblptr -> tb_name);
    }

    if (ferror (tblptr -> tb_fp))
    {
	fprintf (stderr, "i/o error with table %s (%s, file = %s):  ",
			    tblptr -> tb_show, tblptr -> tb_name,
			    tblptr -> tb_file);
	perror ("");
    }
    tb_close (tblptr);
    tblptr -> tb_fp = (FILE *) EOF;		/* Don't try again */
}

/*
 * Install a datum into the database..
 * Fetch entry first to see if we have to append
 * name for building entry.
 */

install(key, value, tbname)
datum key, value;
char tbname[];
{
    datum old;
    char newentry[ENTRYSIZE];
    register char *p;

    p = newentry;
    old = fetch (key);
    if (old.dptr != NULL) {
	if (Debug) {
	    fprintf (stderr, "\tFound old entry\n\t");
	    prdatum (old);
	}

	p = blt (old.dptr, p, old.dsize - 1);
	*p++ = FS;			/* end with a record seperator */
    }
    p = blt (tbname, p, strlen (tbname));
    *p++ = ' ';
    p = blt (value.dptr, p, value.dsize);

    old.dptr = newentry;
    old.dsize = p - newentry;
    if (Debug) {
	fprintf (stderr, "\tNew datum\n\t");
	prdatum (old);
    }
    if (store (key, old) < 0) {          /* put the datum back */
	fprintf (stderr, "dbmbuild, install:  store failed; key='%s', value=",
								key.dptr);
	prdatum (old);
	cleanup (NOTOK);
    }
    return (1);
}
/*
 *      Print a datum.
 *      Takes the datum argument and prints it so that we can read
 *      it as either a key or an entry.
 */

prdatum(value)
datum value;
{
	int cnt;
	char data[512];

	blt (value.dptr, data, value.dsize);

	for (cnt = 0; cnt < value.dsize; cnt++) {
		if (value.dptr[cnt] >= ' ' && value.dptr[cnt] <= '~')
			putc (value.dptr[cnt], stderr);
		else
			fprintf (stderr, "<\\%03o>", value.dptr[cnt]);
	}
	putc ('\n', stderr);
}

check (tblptr)
Table *tblptr;
{
#ifdef NAMESERVER
    if ((tblptr -> tb_flags & TB_SRC) == TB_NS)
	return(0);
#endif NAMESERVER
    if (tblptr -> tb_fp == (FILE *)EOF)
	return(1);		/* We already know its bad */
    if (!tb_open (tblptr, TRUE)) /* gain access to a channel table */
    {
	fprintf (stderr, "could not open table \"%s\" (%s, file = '%s'):\n\t",
			tblptr -> tb_show, tblptr -> tb_name,
			tblptr -> tb_file);
	perror("");
	tblptr -> tb_fp = (FILE *) EOF;
	return(1);
    }
    tb_close (tblptr);
    tblptr -> tb_fp = (FILE *) NULL;
    return(0);
}

cleanup (exitval)
int     exitval;
{
	lk_close (lckfd, dblock, (char *)0, (char *)0);
	exit (exitval == TRUE? 0 : 1);	/* TRUE is non-zero */
}
sert items into the database
 *      Opens argument assumes database is initialized.
 */

process (tblptr)
    Table *tblptr;
{
    datum key, value;
    char tbkey[LINESIZE],
	 tbvalue[LINESIZE];
    char *cp;

    if (Verbose || Debug)
	fprintf (stderr, tblptr -> tb_fp == (FILE *)EOF ?
		"%s (%s) already done\n" : "%s (%s)\n",
		tblptr -> tb_showmmdf/src/tools/checkaddr.c   444      0     12        2732  3671073252  10765 /*
 *		C H E C K A D D R . C
 *
 *	Checks to determine if an address(s) is valid to mail to.
 */

#include "util.h"
#include "mmdf.h"

extern	char *rindex();
extern  char *supportaddr;

main (argc, argv)
int	argc;
char	**argv;
{
	struct rp_bufstruct thereply;
	char	linebuf[LINESIZE];
	char	*cp;
	int	i;
	char	*subargs = "vm";

	mmdf_init( argv[0] );

	if (argc > 1 && strcmp (argv[1], "-w") == 0) {
		subargs = "vmW";
		argc--;
		argv++;
	}

	if (rp_isbad(mm_init()) || rp_isbad(mm_sbinit()) ||
	    rp_isbad(mm_winit ((char *)0, subargs, supportaddr)) ||
	    rp_isbad(mm_rrply( &thereply, &i )) ||
	    rp_isbad(thereply.rp_val)) {
		printf("Cannot initialize mail system.\n");
		exit(9);
	}
		
	if (argc > 1) {
		for (i = 1; i < argc; i++)
			verify( argv[i] );
	} else {
		while (fgets (linebuf, LINESIZE, stdin)) {
			if (cp = rindex(linebuf, '\n'))
				*cp-- = 0;
			verify( linebuf );
		}
	}
	mm_end (NOTOK);
	exit (0);
}

verify (addr)
char	*addr;
{
	struct rp_bufstruct thereply;
	int	len;

	printf("%s: ", addr);
	(void) fflush(stdout);

	if (rp_isbad (mm_wadr ((char *)0, addr))) {
		if( rp_isbad( mm_rrply( &thereply, &len ))) {
			printf ("Mail system problem\n");
			mm_end (NOTOK);
			exit( 8 );
		} else
			printf ("%s\n", thereply.rp_line);
	} else {
		if( rp_isbad( mm_rrply( &thereply, &len ))) {
			printf ("Mail system problem\n");
			mm_end (NOTOK);
			exit (7);
		} else if( rp_isbad( thereply.rp_val ))
			printf ("%s\n", thereply.rp_line);
		else
			printf ("OK\n");
	}
}
 tbvalue;
	    value.dsize = strlen (tmmdf/src/tools/checkup.c   444      0     12       72772  3671073256  10536 /*
 *     MULTI-CHANNEL MEMO DISTRIBUTION FACILITY  (MMDF)
 *
 *
 *     Copyright (C) 1979,1980,1981  University of Delaware
 *
 *     Department of Electrical Engineering
 *     University of Delaware
 *     Newark, Delaware  19711
 *
 *     Phone:  (302) 738-1163
 *
 *
 *     This program module was developed as part of the University
 *     of Delaware's Multi-Channel Memo Distribution Facility (MMDF).
 *
 *     Acquisition, use, and distribution of this module and its listings
 *     are subject restricted to the terms of a license agreement.
 *     Documents describing systems using this module must cite its source.
 *
 *     The above statements must be retained with all copies of this
 *     program and may not be removed without the consent of the
 *     University of Delaware.
 *
 *
 *     version  -1    David H. Crocker    March   1979
 *     version   0    David H. Crocker    April   1980
 *     version  v7    David H. Crocker    May     1981
 *     version   1    David H. Crocker    October 1981
 *
 */
/*      check directory, file, and program conditions, for MMDF
 *
 *  Jan 82  D. Crocker    Cleaned up protection-setting, a bit more
 *                        Temporarily ignore group id mismatches
 *  Jun 82  S. Manion     Major functional upgrade, such as verifying
 *                        necessary alias entries exist.
 *  Feb 84  D. Long       Check for slave in cmddfldir...not /usr/mmdf/lib.
 *  Mar 84  D. Long       Report the line type of dialports.
 */

#include "util.h"
#include "mmdf.h"
#include <pwd.h>
#include <sys/stat.h>
#include "ch.h"
#ifndef NODIAL
#include "d_proto.h"
#include "d_structs.h"
#endif NODIAL
#include "gettys.h"
#include "dm.h"

#define MAXARG 50

/*  maximum number of output lines to be queued at once  */
#define         QUESIZ          20

/*  the maximum number of path names that can be remembered as
 *  having been checked already.  See chkpath()
*/
#define         MAXPATH         15

/*  various verbosity levels  */
#define         PERROR             01   /*  perror() should be called */
#define         ANYLEVEL          070   /*  any bit in this field */
#define         LEVEL1            010   /*  fatal */
#define         LEVEL1P      LEVEL1|PERROR
#define         LEVEL3            020   /*  major section */
#define         LEVEL4            030   /*  sub-section */
#define         LEVEL5            040
#define         LEVEL6            050
#define         LEVEL6_5          060   /*  warning [..] messages */
#define         LEVEL7            070   /* nitty gritty junk */
#define         LEVEL0       LEVEL1|PERROR

#define         BACKGROUND      LEVEL6  /* default verbosity */
#define         FINAL           0
#define         PARTIAL         1

extern char *dupfpath();
extern char *strncpy();
extern int  sys_nerr;
extern char *sys_errlist[];
extern int  errno;

extern LLog msglog;
extern Chan **ch_tbsrch;       /* defined channels */
extern Table **tb_list;        /* full list of known tables */

extern Domain **dm_list;
extern char *mmdflogin;        /* login name for mmdf processes */
extern char *mmtailor;        /* external tailoring information */


extern char     *lckdfldir,
	       *pn_quedir;

#ifdef JNTMAIL
int daemonuid,
    daemongid;
#define DAEMON  "daemon";       /* Can be overridden at compile time */
char daemonname[]  = DAEMON;
#endif JNTMAIL

extern char	*locname, *locdomain;

extern char
		*logdfldir,
		*phsdfldir,
		*tbldfldir,
		*tbldbm,
		*cmddfldir,
		*chndfldir,
		*mldfldir;

extern char
		*quedfldir,
		*tquedir,
		*aquedir,
		*mquedir,
		*squepref;

extern char
		*pathdeliver,
		*pathsubmit,
		*pathpkup,
		*pathmail;

extern char *tai_eptr;

extern int queprot;             /* protection on quedfldir[] parent */

extern struct ll_struct	msglog,
			chanlog;

#ifndef NODIAL
extern struct ll_struct	ph_log;
extern struct dialports *d_prts;
extern struct directlines *d_lines;
#endif NODIAL
extern struct passwd *getpwuid ();
extern struct passwd *getpwnam ();

struct stat statbuf;

struct prtque  {
    int q_level;
    char q_text[126];
};

int mmdfuid,
    mmdfgid;
int rootgid;
int verbosity = BACKGROUND;
char hdrfmt[] = "\n%-24s: %s\n";
char subhdrfmt[] = "    %-20s: %s\n";
char probfmt[] = "%-18s: %s\n";
char errflg;                    /* error during mmdf_init */
char *errstr();
/**/

main (argc, argv)
  int argc;
  char *argv[];
{
    char tmpfile[FILNSIZE];
    struct passwd *pwdptr;
    int ind;

    /*  check for the verbosity flag  */
    flaginit (argc, argv);

    que (BACKGROUND, "\n**  Asterisks indicate potentially serious anomolies.\n");
    que (LEVEL6_5, "[ Information which is bracketed is advisory. ]\n");
    qflush (LEVEL0);

    chktai ();  /* is the tailoring file there ? */

    mmdf_init (argv[0]);
    if (errflg)
	endit (NOTOK);
    ll_hdinit (&msglog, argv[0]);
    msglog.ll_file = dupfpath (msglog.ll_file, logdfldir);

#ifdef JNTMAIL
    if ((pwdptr = getpwnam (daemonname)) == (struct passwd *) NULL)
    {
      que (LEVEL1, hdrfmt, "** No /etc/passwd login", daemonname);
      endit (NOTOK);
    }
    daemonuid = pwdptr -> pw_uid;
    daemongid = pwdptr -> pw_gid;
#endif JNTMAIL

    que (BACKGROUND, hdrfmt, "Default local name", locname);
    que (LEVEL7, subhdrfmt, "", "this is your 'official' host name");
    qflush (LEVEL7);

    que (BACKGROUND, hdrfmt, "Default domain name", locdomain);
    que (LEVEL7, subhdrfmt, "", "this is your 'official' domain name");
    qflush (LEVEL7);

    /*  get uid & gid for mmdf login, for setting directory ownerships */
    if ((pwdptr = getpwnam (mmdflogin)) == (struct passwd *) NULL)
    {
	que (LEVEL1, hdrfmt, "** No /etc/passwd login", mmdflogin);
	endit (NOTOK);
    }
    mmdfuid = pwdptr -> pw_uid;
    mmdfgid = pwdptr -> pw_gid;

    que (LEVEL3, "\nMMDF login %-11s  : uid (%d), gid (%d)\n",
		mmdflogin, mmdfuid, mmdfgid);
    chkalias (mmdflogin);
    que (LEVEL7, subhdrfmt, "", "alias this to root or systems staff");
    qflush (LEVEL7);
    qflush (LEVEL3);

    /* check for hashed table, if required */
    if (tbldbm != (char *) 0)
    {
	que (BACKGROUND, hdrfmt, "MMDF DBM database", tbldbm);
	que (LEVEL7, hdrfmt, "", "compiled by dbmbuild");
	qflush (LEVEL7);

	getfpath (tbldbm, tbldfldir, tmpfile);
	strcat (tmpfile, ".dir");
	que (LEVEL7, subhdrfmt, "data directory", tmpfile);
	chkfile (tmpfile, 0644, 0664, mmdfuid, mmdfgid, mmdflogin);

	getfpath (tbldbm, tbldfldir, tmpfile);
	strcat (tmpfile, ".pag");
	que (LEVEL7, subhdrfmt, "data pages", tmpfile);
	chkfile (tmpfile, 0644, 0664, mmdfuid, mmdfgid, mmdflogin);
	qflush (LEVEL7);
    }

    que (BACKGROUND, hdrfmt, "Trouble report address", "Postmaster");
    que (LEVEL7, subhdrfmt, "", "alias this to root or systems staff");
    qflush (LEVEL7);
    chkalias ("Postmaster");    /*  make sure the name is aliased  */
    qflush (LEVEL0);

    if ((pwdptr = getpwuid (0)) != (struct passwd *) NULL)
	rootgid = pwdptr -> pw_gid;

    que (LEVEL3, hdrfmt, "Checking directories", "");

    /*  standard directories */
    que (LEVEL4, subhdrfmt, "Logging directory", logdfldir);
    if (chkfile (logdfldir, 0711, 0755, mmdfuid, mmdfgid, mmdflogin) >= OK)
	chklog ();
    qflush (LEVEL4);

    que (LEVEL4, "\n");
    que (LEVEL4, subhdrfmt, "Phase (timestamps)", phsdfldir);
    chkfile (phsdfldir, 0711, 0755, mmdfuid, mmdfgid, mmdflogin);
    qflush (LEVEL4);

#ifndef V4_2BSD
    que (LEVEL4, "\n");
    que (LEVEL4, subhdrfmt, "Locking directory", lckdfldir);
    chkfile (lckdfldir, 0777, 0777, mmdfuid, mmdfgid, mmdflogin);
    qflush (LEVEL4);
#endif V4_2BSD

#ifdef JNTMAIL
    que (LEVEL4, "\n");
    que (LEVEL4, subhdrfmt, "NIFTP spool directory", pn_quedir);
    chkfile (pn_quedir, 0700, 0700, daemonuid, daemongid, daemonname);
    qflush (LEVEL4);
#endif

    que (LEVEL4, "\n");
    que (LEVEL4, subhdrfmt, "Tables & scripts", tbldfldir);
    if (chkfile (tbldfldir, 0711, 0755, mmdfuid, mmdfgid, mmdflogin) >= OK)
	chktab ();
    qflush (LEVEL4);

    que (LEVEL4, hdrfmt, "MMDF Commands", cmddfldir);
    if (chkfile (cmddfldir, 0711, 0755, mmdfuid, mmdfgid, mmdflogin) >= OK)
	chkcmd ();
    qflush (LEVEL4);

    que (LEVEL4, hdrfmt, "Channel programs", chndfldir);
    if (chkfile (chndfldir, 0500, 0770, mmdfuid, mmdfgid, mmdflogin) >= OK)
	chkchan ();
    qflush (LEVEL4);

    if (isstr(mldfldir))
    {                           /* all mail delivered in common dir     */
	que (LEVEL4, hdrfmt, "Shared receipt directory", mldfldir);
	chkfile (mldfldir, 0777, 0777, mmdfuid, mmdfgid, mmdflogin);
	qflush (LEVEL4);
    }
    qflush (LEVEL0);

    /*  queue directory substructure */
    que (LEVEL4, hdrfmt, "Mail queue home", quedfldir);
    if (chkpath (FINAL, quedfldir, 0777, 0777, mmdfuid, mmdfgid, mmdflogin) < OK)
	que (LEVEL1, "cannot check queue further\n");
    else
    {
	que (LEVEL7, "\t(changing into queue home directory...)\n");
	if (chdir (quedfldir) < OK)
	{
	    que (LEVEL1, probfmt, "cannot chdir", errstr());
	    que (LEVEL1, "unable to check queue directory tree further\n");
	}
	else
	{
	    qflush (LEVEL7);
	    que (LEVEL5, subhdrfmt, "Queue lock", "home's parent directory");
	    if (stat ("..", &statbuf) < OK)
		que (LEVEL1P, "cannot stat home queue parent, ");
	    else
	    {
		statbuf.st_mode &= 06707;
		if (statbuf.st_mode != queprot)
		    que (LEVEL1, "Wrong mode        : (0%o) should be (0%o)\n",
				statbuf.st_mode, queprot);
	    }
	    qflush (LEVEL5);

	    que (LEVEL5, subhdrfmt, "Address-building", tquedir);
	    chkpath (FINAL, tquedir, 0777, 0777, mmdfuid, mmdfgid, mmdflogin);
	    qflush (LEVEL5);

	    que (LEVEL5, subhdrfmt, "Queued addresses", aquedir);
	    chkpath (FINAL, aquedir, 0777, 0777, mmdfuid, mmdfgid, mmdflogin);
	    qflush (LEVEL5);

	    que (LEVEL5, subhdrfmt, "Message text", mquedir);
	    chkpath (FINAL, mquedir, 0777, 0777, mmdfuid, mmdfgid, mmdflogin);
	    qflush (LEVEL5);

	    que (LEVEL5, "Queueing directories:\n");
	    for (ind = 0; ch_tbsrch[ind] != (Chan *) 0; ind++)
	    {
	    	static char path[64];

	    	(void) sprintf (path, "%s%s",
		    squepref, ch_tbsrch[ind] -> ch_queue);
		que (LEVEL5, subhdrfmt, ch_tbsrch[ind] -> ch_show, path);
		chkpath (FINAL, path, 0777, 0777, mmdfuid, mmdfgid, mmdflogin);
		qflush (LEVEL5);
	    }
	}
    }
    qflush (LEVEL0);

#ifndef NODIAL
    que (LEVEL4, hdrfmt, "Dial out ports", "");
    chk_dprts ();               /*  check the dial out ports  */
    qflush (LEVEL0);

    que (LEVEL4, hdrfmt, "Direct-connect lines", "");
    chk_dirlin ();              /*  check the direct lines  */
    qflush (LEVEL0);
#endif NODIAL

    endit (OK);
}
/**/

flaginit (argc, argv)
int argc;
char *argv[];
{
    register int argind, charind;

    for (argind = 1;  argind < argc;  argind++)
	if (argv[argind][0] == '-')
	    for (charind = 1;  argv[argind][charind] != '\0';  charind++)
		switch (argv[argind][charind])
		{
		    case 'p':           /* only note problems */
			verbosity = LEVEL1;
			break;

		    case 'v':
			verbosity = atoi (&argv[argind][++charind]);
			if (verbosity < 0)
			    verbosity = LEVEL1;
			if (verbosity == 0)
			    verbosity = LEVEL7;
			while (isdigit (argv[argind][charind]))
			    charind++;
			charind--;
			break;

		    default:
			printf ("Unknown flag %c\n", argv[argind][charind]);
			printf ("Usage:  %s [-p][-v<verbosity>]\n", argv[0]);
			exit (NOTOK);
		}
}
/**/

#ifndef NODIAL
chk_dprts ()
{
    struct ttys data;
    char desc[30];
    register int index, result;

    for (index = 0;  d_prts[index].p_port != NULL;  index++)
    {
	/*  check the dial out lines  */
	(void) sprintf (desc, "0%o %.25s", d_prts[index].p_speed,
	    d_prts[index].p_ltype);
	que (LEVEL5, subhdrfmt, d_prts[index].p_port, desc);
	qflush (LEVEL5);

	/*  verify that the device exists  */
	if (stat (d_prts[index].p_port, &statbuf) < 0)
	    que (LEVEL1, probfmt, "No port", errstr());
	else
	    que (LEVEL7, "\tPort exists\n");
	qflush (LEVEL7);

	/*  make sure it is not enabled  */
	result = getttynam (&data, &d_prts[index].p_port[5]);
	if (result == BADDATA)
	    que (LEVEL1, "No listing        : Port not in /etc/ttys\n");
	else
	    if (data.t_valid == 0)
		que (LEVEL7, "\tPort is set correctly (disabled)\n");
	    else
		que (LEVEL1, "Wrong state      : Port should not be enabled\n");
	qflush (LEVEL7);

	/*  make sure the lines dialer exists  */
	if (stat (d_prts[index].p_acu, &statbuf) < 0)
	    que (LEVEL1P, "No dialer         : (%s)\n", d_prts[index].p_acu);
	else
	    que (LEVEL7, "\tDialer (%s) exists\n", d_prts[index].p_acu);

	qflush (LEVEL5);
    }
    if (index == 0)
    {
	que (LEVEL5, subhdrfmt, "None", "");
	qflush (LEVEL5);
    }
}
/**/

chk_dirlin()
{ 
    register int index, ret;
    struct ttys ttybuf;

    for (index = 0;  d_lines[index].l_name != 0;  index++)
    {
	que (LEVEL5, subhdrfmt, d_lines[index].l_name, d_lines[index].l_tty);
	qflush (LEVEL5);

	/*  verify that the device exists  */
	if (stat (d_lines[index].l_tty, &statbuf) < 0)
	    que (LEVEL1, probfmt, "No line", errstr());
	else
	    que (LEVEL7, "\tLine exists\n");
	qflush (LEVEL7);

	/*  Is the line disabled?  */
	ret = getttynam (&ttybuf, &d_lines[index].l_tty[5]);
	if (ret == BADDATA)
	    que (LEVEL1, "No listing        : Line not in /etc/ttys\n");
	else
	    if (ttybuf.t_valid == 0)
		que (LEVEL7, "\tLine setup correctly (disabled)\n");
	    else
		que (LEVEL1,
			"Wrong state       : Line must NOT be enabled for terminal login\n");

	qflush (LEVEL5);
    }
    if (index == 0)
    {
	que (LEVEL5, subhdrfmt, "None", "");
	qflush (LEVEL5);
    }
}
#endif NODIAL
/**/

chklog ()
{

    que (LEVEL7, "\n(changing into log directory...)\n");
    qflush (LEVEL6);

    if (chdir (logdfldir) < OK)
    {
	que (LEVEL1, probfmt, "cannot chdir", errstr());
	return;
    }

    que (LEVEL6, subhdrfmt, "message-level log", msglog.ll_file);
    chkfile (msglog.ll_file, 0622, 0666, mmdfuid, mmdfgid, mmdflogin);
    qflush (LEVEL6);

    que (LEVEL6, subhdrfmt, "channel log", chanlog.ll_file);
    chkfile (chanlog.ll_file, 0622, 0666, mmdfuid, mmdfgid, mmdflogin);
    qflush (LEVEL6);

#ifndef NODIAL
    que (LEVEL6, subhdrfmt, "phone (link) log", ph_log.ll_file);
    chkfile (ph_log.ll_file, 0622, 0666, mmdfuid, mmdfgid, mmdflogin);
    qflush (LEVEL6);
#endif NODIAL
}
/**/

chktab ()
{
    int didone;
    register int ind, n;

    que (LEVEL7, "\n(changing into table directory...)\n");
    qflush (LEVEL6);

    if (chdir (tbldfldir) < OK)
    {
	que (LEVEL1, probfmt, "cannot chdir", errstr());
	return;
    }

    que (LEVEL4, "\nChannel name tables & associated system entries:\n");
    for (ind = 0; ch_tbsrch[ind] != (Chan *) 0; ind++)
    {
	if (ind > 0)
	    que (LEVEL5, "\n");
	que (LEVEL5, subhdrfmt, ch_tbsrch[ind] -> ch_show,
			ch_tbsrch[ind] -> ch_name);
	que (LEVEL6, subhdrfmt, "Queue name", ch_tbsrch[ind] -> ch_queue);
	qflush (LEVEL6);

	/*  verify that ch_name is legal, ie, less than 8 chars
	 *  long, and containing only alphanumerics or -.
	 */
	for (n = 0;  ch_tbsrch[ind] -> ch_name[n] != '\0';  n++)
	    if( ((isalpha (ch_tbsrch[ind] -> ch_name[n]) == 0) &&
		 (ch_tbsrch[ind] -> ch_name[n] != '-'      )   ) ||
		(n > 8                                          )   )
		break;
	/*  make sure the loop terminated normally  */
	if (n > 8)
	    que (LEVEL1, "Long name         : '%s' should be 8 or fewer characters\n",
			ch_tbsrch[ind] -> ch_name);
	else
	    if (ch_tbsrch[ind] -> ch_name[n] != '\0')
		que (LEVEL1, "Illegal char      : (%c) in '%s'\n",
		    ch_tbsrch[ind] -> ch_name[n], ch_tbsrch[ind] -> ch_name);

	que (LEVEL6, subhdrfmt, "  Local host name",
				    ch_tbsrch[ind] -> ch_lname);
	que (LEVEL6, subhdrfmt, "  Local domain name",
				    ch_tbsrch[ind] -> ch_ldomain);
	qflush (LEVEL6);

	cktable (ch_tbsrch[ind] -> ch_table, "Channel table");

	if (ch_tbsrch[ind] -> ch_indest != (Table *) 0)
	    cktable (ch_tbsrch[ind] -> ch_indest, "Destination filter");

	if (ch_tbsrch[ind] -> ch_outdest != (Table *) 0)
	    cktable (ch_tbsrch[ind] -> ch_outdest, "Destination filter");

	if (ch_tbsrch[ind] -> ch_insource != (Table *) 0)
	    cktable (ch_tbsrch[ind] -> ch_insource, "Source filter");

	if (ch_tbsrch[ind] -> ch_outsource != (Table *) 0)
	    cktable (ch_tbsrch[ind] -> ch_outsource, "Source filter");

	if (ch_tbsrch[ind] -> ch_known != (Table *) 0)
	    cktable (ch_tbsrch[ind] -> ch_known, "Table of known hosts");

	if (ch_tbsrch[ind] -> ch_script != 0)
	{
	    que (LEVEL6, subhdrfmt, "Dialing script",
				    ch_tbsrch[ind] -> ch_script);
	    if (stat (ch_tbsrch[ind] -> ch_script, &statbuf) < OK)
		que (LEVEL1, probfmt, "cannot stat", errstr());
	    qflush (LEVEL6);
	}

	if (ch_tbsrch[ind] -> ch_trans != (char *) DEFTRANS)
	{
	    que (LEVEL6, subhdrfmt, "Phone transcript",
				ch_tbsrch[ind] -> ch_trans );
	    qflush (LEVEL6);
	}
	if (ch_tbsrch[ind] -> ch_login != NOLOGIN)
	    chklogin (ch_tbsrch[ind]);
	qflush (LEVEL5);
    }
    qflush (LEVEL4);

    que (LEVEL4, hdrfmt, "Domain tables", "");
    for (didone = FALSE, ind = 0; dm_list[ind] != (Domain *) 0; ind++)
    {
	if (dm_list[ind] -> dm_table -> tb_fp == (FILE *) NOTOK)
	    continue;
	didone = TRUE;
	que (LEVEL5, subhdrfmt,
	     isstr(dm_list[ind] -> dm_name) ? dm_list[ind] -> dm_name : "(root)",
	     dm_list[ind] -> dm_show);

	/*  verify that spec is legal, containing only alphanumerics or -.
	 */
	for (n = 0;  dm_list[ind] -> dm_name[n] != '\0';  n++)
	    if( ((isalpha (dm_list[ind] -> dm_name[n]) == 0) &&
		 (dm_list[ind] -> dm_name[n] != '-'      )  &&
		 (dm_list[ind] -> dm_name[n] != '.'      )   ) )
		break;

	/*  make sure the loop terminated normally  */
	if (dm_list[ind] -> dm_name[n] != '\0')
	    que (LEVEL1, "Illegal char      : (%c) in '%s'\n",
		dm_list[ind] -> dm_name[n], dm_list[ind] -> dm_name);

	cktable(dm_list[ind] -> dm_table, "");
	qflush (LEVEL5);
    }
    if (!didone)
    {
	que (LEVEL5, subhdrfmt, "None", "");
	qflush (LEVEL5);
    }

    que (LEVEL4, hdrfmt, "Additional tables", "");
    for (didone = FALSE, ind = 0; tb_list[ind] != (Table *) 0; ind++)
    {
	if (tb_list[ind] -> tb_fp == (FILE *) NOTOK)
	    continue;           /* already done */

	didone = TRUE;
	que (LEVEL5, subhdrfmt, tb_list[ind] -> tb_show,
				tb_list[ind] -> tb_name);

	/*  verify that spec is legal, ie, less than 8 chars
	 *  long, and containing only alphanumerics or -.
	 */
	for (n = 0;  tb_list[ind] -> tb_name[n] != '\0';  n++)
	    if( ((isalpha (tb_list[ind] -> tb_name[n]) == 0) &&
		 (tb_list[ind] -> tb_name[n] != '-'      )   &&
		 (tb_list[ind] -> tb_name[n] != '.'      )   ) )
		break;

	/*  make sure the loop terminated normally  */
	if (tb_list[ind] -> tb_name[n] != '\0')
	    que (LEVEL1, "Illegal char      : (%c) in '%s'\n",
		tb_list[ind] -> tb_name[n], tb_list[ind] -> tb_name);

    	cktable (tb_list[ind], "");
	qflush (LEVEL5);
    }
    if (!didone)
    {
	que (LEVEL5, subhdrfmt, "None", "");
	qflush (LEVEL5);
    }
    qflush (LEVEL4);
}
/**/

cktable(tb, title)
register Table *tb;
register char *title;
{
    struct stat statbuf;

    tb -> tb_fp = (FILE *) NOTOK;	/* flag this table as processed */
    if ((tb -> tb_flags&TB_SRC) == TB_NS) {
	que (LEVEL6, subhdrfmt, title, "(via nameserver)");
    	return;
    } else {
	que (LEVEL6, subhdrfmt, title, tb -> tb_file);
	if (stat (tb -> tb_file, &statbuf) < OK)
	    que (LEVEL1, probfmt, "cannot stat", errstr());
    }
    qflush (LEVEL6);
}

chklogin (chanptr)
Chan *chanptr;
{
    register char *name;
    struct passwd *ret, *getpwnam ();
    char slaveloc[FILNSIZE];
    int mode;

    /*  transfer to a more accessable variable  */
    name = chanptr -> ch_login;

    /*  verify that there is a login for this channel  */
    ret = getpwnam (name);
    if (ret == NULL)
    {
	que (LEVEL1, "No login          : for '%s'\n", name);
	return;
    }

    que (LEVEL6, subhdrfmt, "Login name", name);
    chkalias (name);            /*  make sure the name is aliased  */
    qflush (LEVEL6);

    /*  make sure the login program is the standard slave program  */
    if ((initstr("pobox", chanptr -> ch_ppath, 5) != (-1)) &&
	((chanptr -> ch_access & DLVRPSV) != 0        )   )
	{
	strcat(strcpy(slaveloc,cmddfldir), "/slave");
	if (strcmp (ret -> pw_shell, slaveloc) != 0)
	    que (LEVEL1, "Bad login prog    : '%s' is not phonenet slave\n",
			    ret -> pw_shell);
	else
	    que (LEVEL6, subhdrfmt, "Login program", ret -> pw_shell);
	qflush (LEVEL6);
    }
    else
	que (LEVEL6_5, "\t[ Login program : %s ]\n", ret -> pw_shell);
    qflush (LEVEL6_5);

    /*  verify that the login directory exists and check protections  */
    if (stat (ret -> pw_dir, &statbuf) < OK)
    {
	que (LEVEL1P, "Bad login dir     : Couldn't stat '%s'; ",
			ret -> pw_dir);
	return;
    }

    mode = statbuf.st_mode & 04777;
    if ((mode&06707) != 0701)
	que (LEVEL1, "Wrong mode        : (0%o) on login directory; should be 711\n",
			mode, ret -> pw_dir);
}
/**/

chkalias (name)
char *name;
{
    char alias[LINESIZE];

    if (aliasfetch(TRUE, name, alias, 1) == OK) {
	que (LEVEL6, subhdrfmt, "Alias value", alias);
    } else if (aliasfetch(TRUE, name, alias, 0) == OK) {
	que (LEVEL1, "Bypassable alias  : '%s' (** normally not bypassable)\n",
		alias);
    } else {
	que (LEVEL1, "No mail alias     : for address '%s'\n", name);
    }
    qflush (LEVEL6);
}



/**/

chkcmd ()
{
    que (LEVEL7, "\n(changing into command library...)\n");
    qflush (LEVEL7);

    if (chdir (cmddfldir) < OK)
    {
	que (LEVEL1, probfmt, "cannot chdir", errstr());
	return;
    }

    que (LEVEL6, subhdrfmt, "Posting/submission", pathsubmit);
    chkfile (pathsubmit, 04711, 04755, mmdfuid, mmdfgid, mmdflogin);
    qflush (LEVEL6);

    que (LEVEL6, subhdrfmt, "Delivery management", pathdeliver);
    chkfile (pathdeliver, 04711, 04755, 0, rootgid, "root");
    qflush (LEVEL6);

    if (strcmp (pathdeliver, pathpkup) != 0)
    {
	que (LEVEL6, subhdrfmt, "P.O. Box retrieval", pathpkup);
	chkfile (pathpkup, 04711, 04755, 0, rootgid, "root");
	qflush (LEVEL6);
    }

    que (LEVEL6, subhdrfmt, "V6Mail (for notices)",
		pathmail);
    chkfile (pathmail, 0711, 0755, mmdfuid, mmdfgid, mmdflogin);
    qflush (LEVEL6);

    que (LEVEL6, subhdrfmt, "cleanque", "Queue garbage cleaner");
    chkfile ("cleanque", 04711, 04755, 0, rootgid, "root");
    qflush (LEVEL6);

    que (LEVEL6, subhdrfmt, "checkque", "Queue status checker");
    chkfile ("checkque", 04711, 04755, mmdfuid, mmdfgid, mmdflogin);
    qflush (LEVEL6);

    que (LEVEL6, subhdrfmt, "setlogs", "Log cleanup shellfile");
    chkfile ("setlogs", 0700, 0755, mmdfuid, mmdfgid, mmdflogin);
    qflush (LEVEL6);
}
/**/

chkchan ()
{
    register int ind;
    register Chan *chan;

    que (LEVEL7, "\n(changing into channel directory...)\n");
    qflush (LEVEL7);

    que (BACKGROUND,"\t[ Some channels, such as Local, need to be setuid ]\n");
    qflush (BACKGROUND);

    if (chdir (chndfldir) < OK)
	que (LEVEL1, probfmt, "cannot chdir", errstr());
    else
    {
	for (ind = 0; (chan = ch_tbsrch[ind]) != (Chan *) 0; ind++)
	{
	    que (LEVEL5, subhdrfmt, chan -> ch_show, "");
	    if (chan -> ch_ppath[0] == '\0')
		que (LEVEL1, "No channel prog\n");
	    else
	    {
		que (LEVEL6, subhdrfmt, "Channel program",
				    chan -> ch_ppath);
		if (stat (chan -> ch_ppath, &statbuf) < OK)
		    que (LEVEL1, probfmt, "Can't stat prog", errstr());
		qflush (LEVEL6);
	    }
	    if (chan -> ch_logfile) {
	    	chan -> ch_logfile = dupfpath(chan -> ch_logfile, logdfldir);
		que (LEVEL6, subhdrfmt, "logfile", chan -> ch_logfile);
		chkfile (chan -> ch_logfile, 0622, 0666, mmdfuid, mmdfgid, mmdflogin);
		qflush (LEVEL6);
	    }
	    qflush (LEVEL5);
	}
    }
}
/**/

chkfile (dirptr, maxprot, minprot, owner, group, ownname)
register char *dirptr;
int maxprot, minprot, owner, group;
char ownname[];
{
    register char *nptr;
    char partpath[128];

    /*  verify that I have been given a real path name  */
    if (!isstr(dirptr))
    {
	que (LEVEL1, "useless filename\n");
	return (NOTOK);
    }

    /*  step through the path, stopping at /'s to check the
     *  directory.
     */
    nptr = partpath;
    while ((*nptr++ = *dirptr++) != '\0')
	/*  Note that this checks the NEXT char, not the one just
	 *  transfered.
	 */
	if (*dirptr == '/')
	{
	    *nptr = '\0';
	    if (chkpath (PARTIAL, partpath, maxprot, minprot, owner, group, ownname) != OK)
		return (NOTOK);
	}

    /*  At this point the entire pathname has been transfered.
     *  Check it (errors here are more serious than above.)
     */
    if (chkpath(FINAL, partpath, maxprot, minprot, owner, group, ownname) != OK)
	return (NOTOK);
    return (OK);
}
/**/

/*  Note that a return of OK from this routine indicates only
 */
/*ARGSUSED*/
chkpath (control, name, maxprot, minprot, owner, group, ownname)
int control;
char *name;
int maxprot, minprot, owner, group;
char *ownname;
{
    struct passwd *pwdptr;
    static char pathdone[MAXPATH][50];
    register int n;

    /*  First, must check to see if the dir is statable.
     *  A failure here must return NOTOK even if this dir
     *  has been checked before.
     */
    if (stat (name, &statbuf) < OK)
    {
	que (LEVEL1, probfmt, "unable to stat", errstr());
    	if (control == PARTIAL)
	    que (LEVEL1, "Unable to proceed checking this path.\n");
	return (NOTOK);
    }

    /*  If this path has already been checked, then don't
     *  do it again.  The repeated error msgs are annoying.
     */
    for (n = 0;  ((n < MAXPATH) && (pathdone[n][0] != '\0'));  n++)
	if (strcmp (name, pathdone[n]) == 0)
	    return (OK);

    /*  save this name as one that has been checked  */
    if (n < MAXPATH)
	(void) strcpy (pathdone[n], name);

    /*  verify that the owner is correct  */
    if (statbuf.st_uid != owner)
    {
	if ((pwdptr = getpwuid (statbuf.st_uid)) != NULL)
	{
	    if (control == FINAL)
		que (LEVEL1, "Wrong owner       : (%s) for '%s'; should be %s\n",
			pwdptr -> pw_name, name, ownname);
	    else
		que (LEVEL6_5, "\t[ Wrong owner   : (%s) for '%s'; should be %s ]\n",
			pwdptr -> pw_name, name, ownname);
	}
	else
	{
	    if (control == FINAL)
		que (LEVEL1, "Wrong owner      : (uid %d) for '%s'; should be %s\n",
				statbuf.st_uid, name, ownname);
	    else
		que (LEVEL6_5, "\t[ Wrong owner  : (uid %d) for '%s'; should be %s ]\n",
				statbuf.st_uid, name, ownname);
	}
	qflush (LEVEL6_5);
    }

    /* 
     *  Ingore bits that are site choice and not critical to operation.
     */
    if (control == PARTIAL) {
    	minprot |= 0111;
    	maxprot |= 0111;
    }
    statbuf.st_mode &= 06707;
    minprot &= 06707;
    maxprot &= 06707;
    if ((statbuf.st_mode&(~minprot)) || 	/* Any extra bit on? */
        (statbuf.st_mode&maxprot) != maxprot)	/* Any bits missing? */
    {
	if (control == FINAL)
	    que (LEVEL1, "Wrong mode        : (0%o) for '%s'; should be (0%o to 0%o)\n",
			statbuf.st_mode, name, maxprot, minprot);
	else
	    que (LEVEL6_5, "\t[ Wrong mode    : (0%o) for '%s'; should be (0%o to 0%o) ]\n",
			statbuf.st_mode, name, maxprot, minprot);
	qflush (LEVEL6_5);
    }
    return (OK);
}

chktai ()
{
    if (mmtailor != (char *) 0)
    {
	if (stat (mmtailor, &statbuf) < OK)
	{
	    que (LEVEL1, "No tailor file   : %s", mmtailor);
	    endit (NOTOK);
	}
	else
	    que (LEVEL4, hdrfmt, "Tailor file", mmtailor);
	qflush (LEVEL4);
    }
}
/**/

int prtmax;
struct prtque prtque[QUESIZ];

qflush (control)
int control;
{
    register int index, prtany;

    /*  determine if any of the queue should be output */
    for (prtany = index = 0;  index < prtmax;  index++)
	if (prtque[index].q_level <= verbosity)
	{
	    prtany = 1;
	    break;
	}

    if (prtany != 0)        /*  go through and print appropriate lines  */
    {
	for (index = 0;  index < prtmax;  index++)
		printf ("%s", prtque[index].q_text);
	prtmax = 0;            /* start over */
    }
    else
    {
	for (index = 0;     /* remove excess only */
		(index < prtmax) && (prtque[index].q_level < control);
		index++)
	    ;
	prtmax = index;
    }
    return;
}

/*VARARGS1*/
que (control, msg, arga, argb, argc, argd)
int control;
char *msg, *arga, *argb, *argc, *argd;
{
    char tmp[128];

    if (prtmax < (QUESIZ-1))
	format (prtque[prtmax].q_text, control, msg, arga, argb, argc, argd);
    else
    {
	qflush (LEVEL0);
	printf ("Recompile this program with larger QUESIZ.\n");
	printf ("Queue overflow on:\n  ");
	format (tmp, control, msg, arga, argb, argc, argd);
	printf (tmp);
	exit (NOTOK);
    }
    prtque[prtmax].q_level = control & ~PERROR;
    if (prtque[prtmax].q_level <= verbosity)
	qflush (prtque[prtmax++].q_level);
    else
	prtmax++;
    return;
}
/**/

/*VARARGS2*/
format (buff, control, msg, arga, argb, argc, argd)
char buff[];
int control;
char *msg, *arga, *argb, *argc, *argd;
{
    register int n;

    switch (control&~PERROR)
    {
	case LEVEL1:
	    (void) strncpy (buff, "**    ", 6);
	    n = 6;
	    break;

	default:
	    n = 0;
    }

    (void) sprintf (&buff[n], msg, arga, argb, argc, argd);

    if ((control & PERROR) && (errno > 0)) {
    	n = strlen(buff);
	(void) sprintf(&buff[n], " (%s)\n", errstr());
    }

    return;
}

char *
errstr()
{
    static char buff[64];

    if (errno > sys_nerr || errno < 0)
	(void) sprintf (buff, "Errno %d", errno);
    else
	(void) strcpy(buff, sys_errlist[errno]);
    return (buff);
}
/**/
/*ARGSUSED*/
post_tai (argc, argv)           /* let user know about unknowns tailor info */
	int argc;
	char *argv[];
{
    char errline[LINESIZE];

    arg2vstr (0, sizeof errline, errline, argv);
    if (tai_eptr == (char *) 0)
    {
	if (errno == 0)
	    que (LEVEL1, "Unknown tailor    : '%s'\n", errline);
	else
	    que (LEVEL1, "Tailor error      : '%s'\n", errline);
    }
    else                        /* problem with subfield, not field name */
	que (LEVEL1, "Tailor error      : with '%s' in line: '%s'\n",
		    tai_eptr, errline);

    if (errno > 0 && errno <= sys_nerr)
	que (LEVEL1, "                  : [ %s ]\n", sys_errlist[errno]);

    errflg = TRUE;

    return (YES);   /* we like everthing */
}

endit (type)
    int type;
{
    qflush (LEVEL0);
    (void) fflush (stdout);
    exit (type);
}
me);
	mmdf/src/tools/amp.c   444      0     12        4524  3657737722   7650 /*
 * This program acts as a filter which reformats message headers.
 * Normally the standard input is copied to the standard output,
 * with errors indicated by non-zero exit status.
 *      Arguments can be given, with the following meanings:
 *              % amp <channame> <output file> <top debug> <low debug>
 *      The 1st argument if present is taken to be a channel name
 *              to normalize with respect to.  If this is -, the
 *              NULL channel will be used.
 *      The 2nd argument if present specifies output file to use
 *              instead of standard output; useful when debugging.
 *              "-" will get std output anyway.
 *      A 3rd argument, if present, turns on top-level debug output,
 *              which always goes to the std output.
 *      A 4th argument, if present, turns on low-level debug output.
 *
 *      Hacked 12Mar81 by KLH @ SRI.  Uses Dave Crocker's RFC733
 *      address parsing package.  No warranty, express or implied,
 *      shall apply to this horrible kludge.
 */

#include <stdio.h>
#include "util.h"
#include "mmdf.h"
#include "ch.h"
#include "ap.h"

#if DEBUG > 1
extern char tdebug;                   /* Top-level debug flag */
extern char debug;                /* Low-level debug flag */
#endif

extern LLog     *logptr;
extern int ap_outtype;
extern Chan *ch_nm2struct();
extern long amp_hdr();

/**/

main (argc, argv)
int     argc;
char   *argv[];
{
    register int   c;
    int     ofd;
    FILE * outf;
    Chan *chanptr;

    mmdf_init (argv[0]);
    if (argc < 2 || (chanptr = ch_nm2struct(argv[1])) == (Chan *)NOTOK) {
	chanptr = (Chan *)NULL;
	ap_outtype = AP_822;
    } else
	ap_outtype = chanptr->ch_apout;

    if (argc < 3 || (argv[2][0] == '-' && argv[2][1] == '\0')
	    || ((ofd = creat ("amp.out", 0400)) < 0))
	outf = stdout;            /* Output to std output */
    else
	outf = fdopen (ofd, "w");

#if DEBUG > 1
    if (argc >= 4)
	tdebug++;                 /* Top-level debugging */
    if (argc == 5)
	debug++;                  /* Low-level debugging */
#endif
    if (argc == 5)
	logptr->ll_level = LLOGFTR;

    switch (amp_hdr (chanptr, stdin, outf)) {
    case NOTOK:
    case MAYBE:
	fprintf (stderr, "\nReformat failed.\n");
	exit (-1);
    default:
	/* Reformat succeeded, Copy message text */
	while ((c = getchar ()) != EOF)
	    putc (c, outf);
    }
    exit (0);
}
].q_level < control);
		index++)
	    ;
	prtmax = index;
    }
    return;
}

/*VARARGS1*/
que (control, msg, arga, argb, argc, argd)
int control;
char *msg, *arga, *argb, mmdf/src/tools/fixtai.c   444      0     12       21322  3671073260  10354 /*****************************************************************************
* Program to suck up the 'mchan' lines from the tailoring file and produce   *
* a suitable block of structure initializers for inclusion in conf_chan.c    *
*                                                                            *
* This program relies on having an existing installation with all channels   *
* defined in a tailoring file (whose name is supplied as the first argument).*
* Pre-compiled channels and tables are ignored.                              *
*                                                                            *
* This program uses mmdf_init to read in the (alternate) tailor file and     *
* then passes the channel and table structure arrays to an internal          *
* structure-initializer-printer.  This output is then #included into         *
* conf_chan.c and forms the basis for the next installation of MMDF.         *
*                                                                            *
* Fri Apr 13 13:07:49 EST 1984, bpc (BBN)                                    *
* Tue Nov 27 11:45:21 EST 1984, dbl (BBN)                                    *
/*****************************************************************************
* Added processing of 'mtbl' lines, also.                                    *
* Mon Apr 23 09:59:59 EST 1984, bpc                                          *
*****************************************************************************/

#include "util.h"
#include "cmd.h"
#include "ch.h"
#include <sys/stat.h>
#include "ap.h"
#include "conf.h"

#define MAXARG 50

extern int errno;
extern char * malloc();
extern int tb_numtables;
extern int ch_numchans;
extern Table **tb_list;
extern Chan **ch_tbsrch;
extern char *mmtailor;

/**/
main (argc, argv)
    char * argv[];
{
    if (argc != 2)
    {
	fprintf (stderr, "usage: %s tailoringfile_name\n", argv[0]);
	exit (1);
    }

    (void) strcpy(mmtailor, argv[1]);  /* use alternate tailor file */
    tb_numtables = 0;           /* zero the pre-compiled table array */
    tb_list[0] = (Table *) 0;
    ch_numchans = 0;            /* zero the pre-compiled channel array */
    ch_tbsrch[0] = (Chan *) 0;

    mmdf_init(argv[0]);
    dump_all();
    exit (0);
}

/*****************************************************************************
*                            | d u m p _ a l l |                             *
*                             *****************                              *
* this routine is the real point of all this: it is the routine to           *
* pass over the channels and tables we've defined and appropriately          *
* initialize them                                                            *
*****************************************************************************/
dump_all ()
{
    dump_tables();
    printf ("/*\f*/\n");
    dump_channels();
}

/*****************************************************************************
*                         | d u m p _ t a b l e s |                          *
*                          ***********************                           *
* Print out all of the pre-initialized tables.                               *
*****************************************************************************/
dump_tables()
{
    register i;

    printf ("LOCVAR Table _tblist[]");
    printf ("\t/* Data blocks for the pre-initialized tables */\n");
    printf ("\t=\n");
    printf ("{\n");
    for (i = 0; i < tb_numtables; i++)
      set_tb (tb_list[i]);
    printf ("};\n");
    printf ("LOCVAR Table * tblist[NUMTABLES+1]\t/* all known tables */\n");
    printf ("\t=\n");
    printf ("{\n");
    for (i = 0; i < tb_numtables; i++)
      printf ("    &_tblist[%d],\n", i);
    printf ("    (Table *) 0\n};\n");
    printf ("int\ttb_numtables = %d;\t/* Number of preinitialized tables */\n",
		tb_numtables);
}

/*****************************************************************************
*                       | d u m p _ c h a n n e l s |                        *
*                        ***************************                         *
* Now print out all the channel definitions.  The 'table' pointer for        *
* each channel will simply be the same pointer to the _tblist array          *
* that tblist uses                                                           *
*****************************************************************************/
dump_channels()
{
    register i;

    printf ("LOCVAR Chan _chsrch[]");
    printf ("\t/* Data blocks for the pre-initialized channels */\n");
    printf ("\t=\n");
    printf ("{\n");
    for (i = 0; i < ch_numchans; i += 1)
      set_ch (ch_tbsrch[i]);
    printf ("};\n");
    printf ("LOCVAR Chan * chsrch[NUMCHANS+1]\t/* order chan tables searched */\n");
    printf ("\t=\n");
    printf ("{\n");
    for (i = 0; i < ch_numchans; i += 1)
      printf ("    &_chsrch[%d],\n", i);
    printf ("    (Chan *) 0\n};\n");
    printf ("int\tch_numchans = %d;\t/* Number of preinitialized channels */\n",
		ch_numchans);
}
/**/
/*****************************************************************************
*                              | s e t _ t b |                               *
*                               *************                                *
* Set the initialization for one entry of a table                            *
*****************************************************************************/
set_tb (tptr)
    register Table * tptr;
{
    printf ("    { ");
    str_print (tptr->tb_name);
    printf (", ");
    str_print (tptr->tb_show);
    printf (", ");
    str_print (tptr->tb_file);
    printf (", (FILE *)0, 0L, 0%o },\n", (int)tptr->tb_flags);
}
/*****************************************************************************
*                         | t a b l e _ p r i n t |                          *
*                          ***********************                           *
* Given a table pointer, print it out as an pointer into the array of table  *
* structures.                                                                *
*****************************************************************************/
table_print (tptr)
    Table * tptr;
{
    register i;

    if (tptr == (Table *) 0)
	printf ("(Table *)0");
    else
    {
        for (i = 0; i < tb_numtables; i += 1)
	    if (tb_list[i] == tptr)
	    {
		printf ("&_tblist[%d]", i);
	        return;
	    }
        fprintf (stderr, "Can't locate table %o\n", tptr);
	(void) fflush (stdout);
        abort ();
    }
}
/*****************************************************************************
*                           | s t r _ p r i n t |                            *
*                            *******************                             *
* Print out a string if non-null, 0 otherwise                                *
*****************************************************************************/
str_print (ptr)
    char * ptr;
{
    if (ptr == (char *)0)
	printf ("(char *)0");
    else
	printf ("\"%s\"", ptr);
}
/**/
/*****************************************************************************
*                              | s e t _ c h |                               *
*                               *************                                *
* Initialize one channel                                                     *
*****************************************************************************/
set_ch (cptr)
    register Chan * cptr;
{
    printf ("    { ");
    str_print (cptr->ch_name);
    printf (", ");
    str_print (cptr->ch_show);
    printf (", ");
    table_print (cptr->ch_table);
    printf (", ");
    str_print (cptr->ch_queue);
    printf (", ");
    printf ("\n\t");
    printf ("0%o, ", cptr->ch_access);
    str_print (cptr->ch_ppath);
    printf (", ");
    str_print (cptr->ch_lname);
    printf (", ");
    str_print (cptr->ch_ldomain);
    printf (", ");
    str_print (cptr->ch_host);
    printf (", ");
    printf ("\n\t");
    str_print (cptr->ch_login);
    printf (", %d, ", (int)cptr->ch_poltime);
    str_print (cptr->ch_trans);
    printf (", ");
    str_print (cptr->ch_script);
    printf (", ");
    printf ("\n\t");
    table_print (cptr->ch_outsource);
    printf (", ");
    table_print (cptr->ch_insource);
    printf (", ");
    table_print (cptr->ch_outdest);
    printf (", ");
    table_print (cptr->ch_indest);
    printf (", ");
    table_print (cptr->ch_known);
    printf (", ");
    printf ("\n\t");
    str_print (cptr->ch_confstr);
    printf (", ");
    printf ("0%o, ", cptr->ch_apout);
    printf ("0%o, ", (int)cptr->ch_auth);
    printf ("(Cache *)0,"); /* ch_dead */
    printf ("\n\t");
    printf ("0%o, ", cptr->ch_ttl);
    str_print (cptr->ch_logfile);
    printf (", ");
    printf ("0%o", cptr->ch_loglevel);
    printf (" },\n");
}
ched */\n");
    printf ("\t=\n");
    printf ("{\n");
    for (i = 0; i < ch_numchans; i += 1)
      printf ("    &_chsrch[%d],\n", i);
    printf ("    (Chan *) 0\n};\n");
    printf ("int\tch_numchans = %d;\t/* Number of preinitialized channels */\n",
		ch_numchans);
}
/**/
/**********************mmdf/src/tools/dbmlist.c   444      0     12        2650  3620510542  10502 #include "util.h"
#include "mmdf.h"

/*
 *      Print contents of MMDF address database
 *
 */

#include "ch.h"
#include "chdbm.h"

extern char *blt();
extern char *locname;
extern char *tbldfldir;
extern char *tbldbm;

typedef struct
{
    char *dptr;
    int dsize;
} datum;

extern datum fetch (),
	     firstkey (),
	     nextkey ();

datum key,
	value;

char dbfile[128];

/*
 * Main procedure.
 * process arguments, set flags and invoke file processing.
 * clean up and exit properly.
 */

main(argc, argv)
char **argv;
{
    char tmp[100];
    char *path;

    mmdf_init (argv[0]);
    for (path = 0, argv++; argc-- > 1; argv++) 
    {
	if (argv[0][0] == '-')
	    switch (argv[0][1])
	    {
		default: ;
	    }
	else
	    path = argv[0];
    }
    getfpath ((path == 0) ? tbldbm : path, tbldfldir, dbfile);

    if(dbminit(dbfile) < 0)
    {
	    perror("tblprint, fileinit");
	    exit (-1);
    }

    for (key = firstkey (); key.dptr != NULL; key = nextkey (key))
    {
	blt (key.dptr, tmp, key.dsize);
	tmp[key.dsize] = '\0';
	printf ("%-15s:  ", tmp);      /* key string */
	value = fetch (key);
	prdatum (value);
    }
    exit (0);
}

prdatum(val)
datum val;
{
    register int cnt;
    register char *dbptr;

    for (dbptr = val.dptr, cnt = val.dsize - 1; cnt-- > 0; dbptr++)
    {
	if (*dbptr >= ' ' && *dbptr <= '~')
	    fputc (*dbptr, stdout);
	else
	    fprintf (stdout, "<\\%03o>", *dbptr);
    }
    fputs ("\n",stdout);
}
f ("    { ");
    str_print (tptr->tb_name);
    printf (", ");
    str_print (tptr->tb_mmdf/src/tools/gen   555      0     12          57  3620510543   7337 make -f ../../Makefile.com -f Makefile.real $*
 Print contents of MMDF address database
 *
 */

#include "ch.h"
#include "chdbm.h"

extern char *blt();
extern char *locname;
extern char *tbldfldir;
extern char *tbldbm;

typedef struct
{
    char *dptr;
    int dsize;
} datum;

extern datum fetch (),
	     firstkey (),
	     nextkey ();

datum key,
	value;

char dbfile[128];

/*
 * Main procedure.
 * process arguments, set flags and invoke file processing.
 * clean up and exit properly.
 */

main(argc, argvmmdf/src/tools/checkque.c   444      0     12       47541  3671073262  10675 /*
 *     MULTI-CHANNEL MEMO DISTRIBUTION FACILITY  (MMDF)
 *
 *
 *     Copyright (C) 1979,1980,1981  University of Delaware
 *
 *     Department of Electrical Engineering
 *     University of Delaware
 *     Newark, Delaware  19711
 *
 *     Phone:  (302) 738-1163
 *
 *
 *     This program module was developed as part of the University
 *     of Delaware's Multi-Channel Memo Distribution Facility (MMDF).
 *
 *     Acquisition, use, and distribution of this module and its listings
 *     are subject restricted to the terms of a license agreement.
 *     Documents describing systems using this module must cite its source.
 *
 *     The above statements must be retained with all copies of this
 *     program and may not be removed without the consent of the
 *     University of Delaware.
 *     
 *
 *     version  -1    David H. Crocker    March   1979
 *     version   0    David H. Crocker    April   1980
 *     version  v7    David H. Crocker    May     1981
 *     version   1    David H. Crocker    October 1981
 *
 */
/*  CHECKQUE:  Peruse status of mail queue
 *
 *  Oct 81  D. Crocker          Initial coding
 *  Nov 81  D. Crocker          message name must begin "msg."
 *                              show hours since last contact
 *                              show size of aquedir directory
 *  Apr 82  D. Crocker          show last pickup in elapsed hours, also
 *  Jun 82  S. Manion           added flags s,p,z,f, and t.
 *                              rearanged output format.
 *  Jul 82  D. Crocker          convert to mq_ package
 *  Mar 83  Doug Kingston       changed to use format independent directory
 *                              access routines a la 4.2BSD.  (libndir.a)
 *  Sep 84  D. Rockwell		-c only looks at relevant queues
 */

#include "util.h"
#include "mmdf.h"
#include <sys/stat.h>
#include <utmp.h>
#include "ch.h"
#include "msg.h"
#include "adr_queue.h"
#include "phs.h"

#define WARNTIME    (60L * 60L * 24) /* 1 day */

extern time_t time();

time_t  curtime;              /* Current time secconds              */
time_t	toolate;              /* Time to wait before panicing       */

extern char *quedfldir,            /* home directory for mmdf            */
	    *mquedir,            /* subordinate message directory       */
	    *squepref,		 /* subordinate queue prefix */
	    *aquedir;            /* subordinate address directory      */

extern Chan **ch_tbsrch;          /* full list of channels              */

extern int ch_numchans;           /* number of channels which are known */

struct ch_chstat
{
    int ch_nummsgs;
    long ch_bytes;
    time_t  dstrt,
	    dmsg,
	    ddone,
	    pstrt,
	    pmsg,
	    pdone,
	    oldest,          /* oldest message in queue */
	    llog;
    char oldmsg[15];         /* name of oldest msg */
}       ch_stat[NUMCHANS];
long msglen,                 /*  length of the current message  */
     totblks;                /*  number of blocks (1K) of msg data   */

DIR *ovr_dirp;                  /* handle on the queue directory      */

int msg_total;                  /* total in the queue                   */
long msg_dirsiz;                /* total spaces in aquedir              */

char    msg_sender[ADDRSIZE + 1]; /* return address of current message  */

int ssflag,                       /* print site summary lines only      */
    nzflag,                       /* print only non-zero sites          */
    pcflag,                       /* print only problem channels        */
    fflag,                        /* print name of first queued msg     */
    chflag;                       /* print only specified channels      */

int nchan;
Chan *chcodes[NUMCHANS];

char    bufout[BUFSIZ];

/*****  (mn_)  MAIN MAIN MAIN MAIN MAIN MAIN MAIN MAIN MAIN MAIN  ***** */

main (argc, argv)
int       argc;
char   *argv[];
{
    extern char *dupfpath ();

    mmdf_init (argv[0]);
    setbuf (stdout, bufout);
    if (ch_numchans > NUMCHANS)
    {
	printf("%s: Struct ch_stat[NUMCHANS] not large enough - recompile\n",
		    argv[0]);
	exit(-1);
    }

    siginit ();                   /* standard interrupt initialization  */
				  /* distinguish different delivers     */
    flaginit(argc, argv);         /*  initialize the flags  */

    mn_dirinit ();                /* get right working directory        */

    ovr_queue ();                 /* do the entire mail queue           */

    ch_callstat ();
    ch_report ();

    exit (RP_OK);
}

flaginit(argc, argv)      /*  initialize flags  */
int argc;
char **argv;
{
    register int argind, charind;

    chflag = fflag = ssflag = pcflag = nzflag = 0;
    nchan = 0;
    /*NOSTRICT*/
    toolate = WARNTIME;
    for (argind = 1;  argind < argc;  argind++)
    {
	if (argv[argind][0] != '-')
	{
	    /*  if the chflag is not set, then this arg is meaningless  */
	    if (chflag == 0)
		continue;

	    /*  Is there room for the char?  */
	    if(nchan == NUMCHANS)
	    {
		printf("Can only specify %d channels.\n", NUMCHANS);
		continue;
	    }

	    /*  Convert from the ch_spec form to structure pointer */

	    if ((chcodes[nchan++] = ch_nm2struct (argv[argind])) ==
			(Chan *) NOTOK)
	    {
		printf("Unknown channel: %s\n", argv[argind]);
		(void) fflush (stdout);
	    }
	    continue;

	}

	for(charind = 1;  argv[argind][charind] != '\0';  charind++)  {
	    switch(argv[argind][charind])  {
		case 'c':       /*  channel flag  */
		    chflag++;
		    break;

		case 'p':       /*  problem channel flag  */
		    pcflag++;
		    break;

		case 's':       /*  site summary flag  */
		    ssflag++;
		    break;

		case 'z':       /*  non-zero flag  */
		    nzflag++;
		    break;

		case 'f':       /*  print name of first queued msg  */
		    fflag++;
		    break;

		case 't':       /*  set time on problem channels  */
		    /*  toolate is seconds to start with  */
		    toolate = atoi(&argv[argind][++charind]);
		    if(toolate == 0L)  {
			printf("Need a number after the 't'.\n");
			exit(-1);
		    }
		    /*  check for a trailing M to indicate minutes  */
		    while( (argv[argind][charind] >= '0') &&
			   (argv[argind][charind] <= '9')    )
			charind++;
		    if( (argv[argind][charind] == 'm') ||
			(argv[argind][charind] == 'M')   )
			/*NOSTRICT*/
			toolate *= 60L;
		    else  {
			/*  this char is not part of this flag  */
			charind--;
			/*NOSTRICT*/
			toolate *=  60L * 60L;
		    }
		    /*  At this point charind must index the last position
		     *  actually used by this flag;  it does.
		     */
		    break;

		default:        /*  abzorb unrecognized flags  */
		    printf(
"Usage: %s [-z] [-s] [-p] [-f] [-t<age>[m]] [-c <channel name(s)>]\n",
						argv[0]);
		    printf("\t-c <channel names>\n");
		    printf("\t\tspecifies the channel names to look at\n");
		    printf("\t-f  ->  print name of oldest queued msg\n");
		    printf("\t-p  ->  print only the problem channels\n");
		    printf("\t-s  ->  summary lines only\n");
		    printf("\t-t<age>[m]\n");
		    printf(
"\t\tspecifies the age for problem channels in hours or minutes\n");
		    printf("\t-z  ->  non zero channels only\n");
		    exit(-1);
	    }
	}
    }
    (void) fflush(stdout);
    return(0);
}


LOCFUN
	mn_dirinit ()             /* get to the working directory       */
{
    if (chdir (quedfldir) < OK)
    {
	printf ("can't chdir to %s\n", quedfldir);
	exit (-1);
    }
}

/************  (ovr_)  OVERALL SEQUENCING MANAGEMENT  *************** */

LOCFUN
	ovr_ismsg (entry)         /* a processable message?             */
    register struct direct *entry;
{
    return ((entry -> d_namlen < MSGNSIZE
	  && equal (entry -> d_name, "msg.", 4)) ? TRUE : FALSE );
}

ovr_queue ()                      /* Process entire message queue       */
{
    char qname[128];
    register int index;

    time (&curtime);              /* Get current time (in hours)        */

    if (chflag != 0)
    {
	for (index = 0; index < nchan; index++)
	{
	    (void) strcpy(qname, squepref);
	    strcat(qname, chcodes[index]->ch_queue);
	    ovr_aqueue(qname);
	}
    } else
	ovr_aqueue(aquedir);
}

ovr_aqueue (ch)                      /* Process a single message queue   */
register char *ch;                   /* queue directory name             */
{
    struct stat statbuf;
    register struct direct *dp;

    stat (ch, &statbuf);
    msg_dirsiz += st_gsize(&statbuf);

    if ((ovr_dirp = opendir (ch)) == NULL)
    {
	printf ("can't open queue %s\n", ch);
	return;
    }

    while ((dp = readdir (ovr_dirp)) != NULL)
    {                         /* straight linear processing         */
	if (ovr_ismsg (dp))
	    msg_proc (dp -> d_name);
    }
    closedir(ovr_dirp);
}
/****************  (msg_)  HANDLE A SINGLE MESSAGE  ***************** */

extern int    errno;                /* system error number                */

/*  msg_cite() is defined in adr_queue.h                                */

msg_proc (thename)                /* get, process & release a msg       */
    char thename[];
{
    Msg curmsg;
    struct stat statbuf;
    char msgpath[28];

    (void) sprintf (msgpath, "%s%s", mquedir, thename);
    if (stat (msgpath, &statbuf) < 0) {
	printf ("\n*** %s: Couldn't stat;  error %d.\n", thename, errno);
	(void) fflush (stdout);
	msglen = 0;
    } else {
	msglen = st_gsize (&statbuf);
	if (msglen == 0)
	{
	    printf ("\n*** %s: msg length of zero.\n", thename);
	    (void) fflush (stdout);
	} else {
	    totblks += ((msglen+1023L)/1024L);
	    msg_total++;
	}
    }

    /*
     *  Check for real anomalies!
     */
    (void) strcpy (curmsg.mg_mname, thename);

    if (mq_rinit ((Chan *) 0, &curmsg, msg_sender) == OK)
    {
	if (adr_each (&curmsg) == 0)
	{
	    printf ("\n*** %s: msg exists (%d chars), but goes nowhere.\n",
			thename, msglen);
	    (void) fflush (stdout);
	}
	mq_rkill (OK);
    }
}
/************  (adr_)  INDIVIDUAL ADDR DELIVERY ATTEMPT  ************ */

adr_each (themsg)                 /* do each address                    */
Msg *themsg;
{
    struct adr_struct newadr;
    Chan *curch,
	 *newch;
    register int naddrs;


    for (naddrs = 0, curch = (Chan *) 0, mq_setpos (0L);;)
	switch (mq_radr (&newadr))
	{
	    default:              /* should not get this                */
		printf ("\n*** %s: bad return from mq_radr\n", themsg -> mg_mname);
		exit (NOTOK);

	    case NOTOK:           /* rest of list must be skipped       */
	    case DONE:            /* end of list                        */
		return (naddrs);

	    case OK:              /* normal addr acquisition            */
		naddrs++;
		if ((newch = ch_qu2struct (newadr.adr_que)) == (Chan *) NOTOK)
		{
		    printf ("\n*** Unknown channel queue ('%s') in message '%s'\n",
				newadr.adr_que, themsg -> mg_mname);
		    (void) fflush (stdout);
		}
		else
		{
		    if ((newadr.adr_delv != ADR_DONE) && (curch != newch))
		    {
			curch = newch;
			ch_newmsg (curch, themsg);
		    }
		}
	}
}
/**/

ch_newmsg (thech, themsg)
    Chan *thech;
    Msg  *themsg;
{
    register int chind;

    for (chind = 0; chind < ch_numchans; chind++)
	if (ch_tbsrch[chind] == thech)
	{
	    ch_stat[chind].ch_nummsgs++;
	    ch_stat[chind].ch_bytes += msglen;
	    if (themsg -> mg_time < ch_stat[chind].oldest ||
		    ch_stat[chind].oldest == 0)
	    {
		ch_stat[chind].oldest = themsg -> mg_time;
		(void) strcpy (ch_stat[chind].oldmsg, themsg -> mg_mname);
	    }
	    return;
	}

    printf ("\n*** Could not locate channel ('%s') in table\n",
		thech -> ch_name);
    (void) fflush (stdout);
}
/**/

ch_callstat ()
{
    extern struct utmp *getllnam ();
    struct utmp *llogptr;
    register int chind;

    for (chind = 0; chind < ch_numchans; chind++)
    {
	if (ch_tbsrch[chind] -> ch_login != NOLOGIN)
	{
	    setllog ();         /* setup for checking last logins */
	    if ((llogptr = getllnam (ch_tbsrch[chind] -> ch_login)) != NULL)
		/*NOSTRICT*/
		ch_stat[chind].llog = llogptr -> ut_time;
	}

	ch_stat[chind].dstrt = phs_get (ch_tbsrch[chind], PHS_WRSTRT);
	ch_stat[chind].dmsg  = phs_get (ch_tbsrch[chind], PHS_WRMSG);
	ch_stat[chind].ddone = phs_get (ch_tbsrch[chind], PHS_WREND);
	ch_stat[chind].pstrt = phs_get (ch_tbsrch[chind], PHS_RESTRT);
	ch_stat[chind].pmsg  = phs_get (ch_tbsrch[chind], PHS_REMSG);
	ch_stat[chind].pdone = phs_get (ch_tbsrch[chind], PHS_REEND);
    }

    endllog ();
}
/**/

ch_report ()
    {
    register int chind;
    register int index;
    register Chan *chptr;
    char *tptr;
    char *ctime();
    time_t lasttime, logtime, delstart, delmsg, deldone, 
	   pickstrt, pickmsg, pickdone, age;
    int prtpos, prtflag;

    tptr = ctime (&curtime);
    tptr[16] = '\0';
    printf ("\n%s:  %d queued msgs / %ld byte queue directory\n",
		tptr, msg_total, msg_dirsiz);
    printf("\t\t   %ld Kbytes in msg dir\n\n", totblks);

    prtflag = 0;
    for (chind = 0; chind < ch_numchans; chind++)
    {
	/*  If the non-zero flag is set, then make sure the number
	 *  of messages on this channel is non-zero.
	 */
	if (nzflag != 0)
	    if (ch_stat[chind].ch_nummsgs == 0)
		continue;

	/*  An often used variable  */
	chptr = ch_tbsrch[chind];

	/*  If specific channels were requested, make sure this is
	 *  one of them.
	 */
	if (chflag != 0)
	{
	    for (index = 0;  index < nchan;  index++)
		if (chptr == chcodes[index])
		    break;
	    if (index >= nchan)
		continue;
	}

	/*  find time information  */
	lasttime = (time_t) 0;
	if (ch_tbsrch[chind] -> ch_login != NOLOGIN)
	{
	    logtime = ch_stat[chind].llog;
	    if( (lasttime == (time_t) 0) ||
		(logtime > lasttime    )   )
	    {
		lasttime = logtime;
		prtpos = 0;
	    }
	}

	delstart = ch_stat[chind].dstrt;
	if ( (lasttime == (time_t) 0) ||
	     (delstart > lasttime   )   )
	{
	    lasttime = delstart;
	    prtpos = 1;
	}

	delmsg = ch_stat[chind].dmsg;
	if (delmsg > lasttime)
	{
	    lasttime = delmsg;
	    prtpos = 2;
	}

	deldone = ch_stat[chind].ddone;
	if (deldone > lasttime)
	{
	    lasttime = deldone;
	    prtpos = 3;
	}

	pickstrt = ch_stat[chind].pstrt;
	if (pickstrt > lasttime)
	{
	    lasttime = pickstrt;
	    prtpos = 4;
	}

	pickmsg = ch_stat[chind].pmsg;
	if (pickmsg > lasttime)
	{
	    lasttime = pickmsg;
	    prtpos = 5;
	}

	pickdone = ch_stat[chind].pdone;
	if (pickdone > lasttime)
	{
	    lasttime = pickdone;
	    prtpos = 6;
	}

	/*  how long ago was the contact?  */
	if(lasttime != (time_t) 0)
	    age = curtime - lasttime;
	else
	    age = 0;

	/*  if problem channel flag is set, check age  */
	if (pcflag != 0)
	{
	    if (age < toolate)
		continue;
	    else
		prtflag++;
	}

	prtinfo(chind, logtime, delstart, delmsg, deldone, pickstrt, 
                                pickmsg, pickdone, age, prtpos);
    }
    if ((pcflag != 0 ) &&
	(prtflag == 0)   )
	printf("No problems with any channels.\n");
}


prtinfo(chind, logtime, delstart, delmsg, deldone, 
	pickstrt, pickmsg, pickdone, age, prtpos)
  int chind, prtpos;
  time_t logtime, delstart, delmsg, deldone, pickstrt, pickmsg, pickdone, age;
{
    register Chan *chptr;
    char *tptr, *ctime();
    time_t ageh;
    int temp;

    chptr = ch_tbsrch[chind];


    /*  print the site summary line  */
    if(ssflag == 0)
	putchar('\n');
    else
    {
	/*  print a star to indicate overdue channels  */
	if ( (age != (time_t) 0) &&
	     (age > toolate    )   )
	    printf("* ");
	else
	    printf("  ");
    }
    /*NOSTRICT*/
    temp = (ch_stat[chind].ch_bytes + 500) / 1000;
    if( (ch_stat[chind].ch_bytes != 0) &&
	(temp == 0                   )   )
	temp++;
    /*NOSTRICT*/
    ageh = age / (60L * 60L);
    printf ("%3d msg%s %4d Kb ",
		    ch_stat[chind].ch_nummsgs,
		    ((ch_stat[chind].ch_nummsgs == 1) ? " " : "s"),
		    temp);
    if (ssflag != 0)
    {
	if ((age != (time_t) 0))
	    printf ("%5ld hour%s ", ageh, ((ageh == 1)  ?  " " : "s"));
	else
	if (chptr -> ch_login == NOLOGIN)
	    printf ("   inactive ");
	else
	    printf ("   no login ");
    }

	printf ("(%-8s) %-11s :  %s\n",
		    chptr -> ch_queue, chptr -> ch_name, chptr -> ch_show);

    /*  If only a summary was requested, then go on  */
    if (ssflag != 0)
	{
	/*  print oldest file if specifically requested  */
	if(fflag != 0)
	    printf("    oldest msg:  %s\n", ch_stat[chind].oldmsg);
	return;
    }


    if (chptr -> ch_login != NOLOGIN)
    {
	printf ("\t\t  %-8s login        :  ", chptr -> ch_login);

	if (logtime == (time_t) 0)
	    printf ("no login date\n");
	else
	{
	    tptr = ctime (&logtime);
	    tptr[16] = '\0';
	    printf ("%s", tptr);
	    if(prtpos == 0)
	    {
		printf(" / %ld hour%s",
			ageh,
			(ageh == 1)  ?  "" : "s");
		overdue(age);
	    }
	    putchar('\n');
	}
    }

    if (delstart == (time_t) 0)
        printf ("\t\t  No deliver start\n");
    else
    {
        tptr = ctime (&delstart);
        tptr[16] = '\0';
        printf ("\t\t  deliver start         :  %s", tptr);
        if(prtpos == 1)
        {
            printf(" / %ld hour%s",
    	        ageh,
    	        (ageh == 1)  ?  "" : "s");
            if (ch_stat[chind].ch_nummsgs > 0)
    	    overdue(age);
        }
        putchar('\n');
    }
    
    if (delmsg == (time_t) 0)
        printf ("\t\t  No message delivered\n");
    else
    {
        tptr = ctime (&delmsg);
        tptr[16] = '\0';
        printf ("\t\t  deliver message       :  %s", tptr);
        if(prtpos == 2)
        {
            printf(" / %ld hour%s",
    	        ageh,
    	        (ageh == 1)  ?  "" : "s");
            if (ch_stat[chind].ch_nummsgs > 0)
    	    overdue(age);
        }
        putchar('\n');
    }
    
    if (deldone == (time_t) 0)
        printf ("\t\t  No deliver end\n");
    else
    {
        tptr = ctime (&deldone);
        tptr[16] = '\0';
        printf ("\t\t  deliver end           :  %s", tptr);
        if(prtpos == 3)
	    {
            printf(" / %ld hour%s",
    	        ageh,
    	        (ageh == 1)  ?  "" : "s");
    	if (ch_stat[chind].ch_nummsgs > 0)
		    overdue(age);
	    }
        incomplete(delstart, deldone);
        putchar('\n');
            
    }

    if (pickstrt == (time_t) 0)
        printf ("\t\t  No pickup start\n");
    else
    {
        tptr = ctime (&pickstrt);
        tptr[16] = '\0';
        printf ("\t\t  pickup start          :  %s", tptr);
        if(prtpos == 4)
        {
            printf(" / %ld hour%s",
    	            ageh,
    	            (ageh == 1)  ?  "" : "s");
            overdue(age);
        }
        putchar('\n');
    }

    if (pickstrt == (time_t) 0)
        printf ("\t\t  No message pickup\n");
    else
    {
        tptr = ctime (&pickstrt);
        tptr[16] = '\0';
        printf ("\t\t  pickup message        :  %s", tptr);
        if(prtpos == 5)
        {
            printf(" / %ld hour%s",
    	            ageh,
    	            (ageh == 1)  ?  "" : "s");
            overdue(age);
        }
        putchar('\n');
    }
    
    if (pickdone == (time_t) 0)
        printf ("\t\t  No pickup end\n");
    else
    {
        tptr = ctime (&pickdone);
        tptr[16] = '\0';
        printf ("\t\t  pickup done           :  %s", tptr);
        if(prtpos == 6)
            printf (" / %ld hour%s", (curtime - pickdone) / 3600L,
    	            (((curtime - pickdone) / 3600L) == 1) ? "" : "s");
        incomplete(pickstrt, pickdone);
        if(overdue(age) == 0)
            /*  Failure to complete a pickup within twice the
             *  normal 'problem time' is a special problem;
             *  use the normal compare routine, but halve the
             *  'age'.
             */
            overdue((curtime - pickdone)/2);
    putchar('\n');
    }        
    /*  print oldest file in queue if requested  */
    if ( (fflag != 0) && (ch_stat[chind].oldest != (time_t) 0) )
	printf ("\t\t  oldest msg            :  %s\n", ch_stat[chind].oldmsg);


    if (ch_stat[chind].ch_nummsgs != 0 &&
	    ch_stat[chind].oldest < (curtime - toolate))
    {
	    tptr = ctime (&(ch_stat[chind].oldest));
	    tptr[16] = '\0';

	    printf ("  *** WAITING **  First message         :  %s\n", tptr);
    }
}



overdue(age)
  time_t age;
    {

    if (age == (time_t) 0 || age <= toolate)
	return(0);

    printf ("\n  *** OVERDUE **");
    return(1);
}

incomplete(strt, done)
  time_t strt, done;
    {

    if ( strt != (time_t) 0 && strt > done)
	printf ("\n  *** INCOMPLETE **");

    return;
}
   }


    if (chptr -> ch_login != NOLOGIN)
    {
	printf ("\t\t  %-8s login        :  ", chptr -> ch_login);

	if (logtime == (time_t) 0)
	    printf ("no lommdf/src/tools/mailid.c   444      0     12        1452  3620510543  10303 /*
 *	M A I L I D . C
 *
 *	This program is designed to be used by other programs and/or
 *	shell scripts to obtain the mail id for a given person.
 *	If no userid is give, the userid of the current real uid
 *	is assumed.
 */
#include <stdio.h>
#include <pwd.h>

main (argc, argv)
int	argc;
char	**argv;
{
	register char *user;
	register char *mailid;
	char *getmailid();

	mmdf_init(argv[0]);
	if (argc < 2) {
		register struct passwd *pw;
		struct passwd *getpwuid();

		if ((pw = getpwuid (getuid())) == NULL) {
			fprintf (stderr, "prmailid: No user with uid %d.\n",
				 getuid());
			exit (9);
		}
		user = pw->pw_name;
	} else
		user = argv[1];

	if ((mailid = getmailid(user)) == NULL) {
		fprintf (stderr, "prmailid: No mailid for user '%s'.\n",
			 user);
		exit (8);
	}
	puts(mailid);
	exit (0);
}
\t  No deliver start\n");
    else
    {
        tptr = ctime (&delstart);
        tptr[16] = '\0';
        printf ("\t\t  deliver start         :  %s", tptr);
        if(prtpos == 1)
        {
            printf("mmdf/src/tools/rem_chans.c   444      0     12       11114  3620510543  11017 /*****************************************************************************
* Comment the channel lines out of a tailoring file.  This is intended to    *
* be run after fixtai has extraced the channel information into a            *
* static-initialization.  The looked-for commands MUST agree with            *
* the ones processed by fixtai.  The comment convention MUST agree           *
* with the one used in the uncomment program used just before fixtai         *
* is run                                                                     *
*                                                                            *
* Another subtlety is that the various functional pieces must agree          *
* with the corresponding pieces in fixtai, even though they have been        *
* recoded and may not look the same (e.g., cmdsrch, tai_token,               *
* lexequ).                                                                   *
*                                                                            *
* Wed May 30 08:42:42 EDT 1984, bpc                                          *
*****************************************************************************/

#include "util.h"
#include "cmd.h"

#define     COMMENT_CHAR    ';'
#define     COMMENT     ";:;"   /* Our special comment character */

extern int errno;
extern char *malloc();

FILE * tai_handle;      /* for reading the tailoring file */
#define MAXLINE     500 /* Max length of a line in the tailoring file */
char tai_line[MAXLINE];

/*
** These next MUST agree with the corresponding data in fixtai
*/
#define MMTBL           42
#define MMCHAN          43
Cmd mycmdtab[] =
{
    "mchn",        MMCHAN,     1,
    "mchan",       MMCHAN,     1,
    "mtable",      MMTBL,      1,
    "mtbl",        MMTBL,      1,
    0,             0,          0
};
/**/
main (argc, argv)
    char * argv[];
{
    if (argc != 2)
    {
	fprintf (stderr, "usage: %s tailoringfile_name\n", argv[0]);
	exit (1);
    }
    tai_handle = fopen (argv[1], "r");
    if (tai_handle == NULL)
    {
	fprintf (stderr, "Can't access tailoring file '%s'", argv[1]);
	exit (1);
    }
    read_line ();
    for ( ; ; )
    {
	char token[100];    /* First token on the line */
	
	if (tai_token (tai_line, token) != OK
	                        || mycmdsrch (token, mycmdtab) != OK)
	{
	    printf ("%s", tai_line);
	    read_line ();
	}
	else
	    do 	        /* It is one of our commands, so 'remove' it */
	    {
	        printf ("%s%s", COMMENT, tai_line);
	        read_line ();
	    } while (tai_token (tai_line, token) != OK);
    }
}
/**/
mycmdsrch (str, cmd)        /* find command. return token reference */
    char str[];                 /* test string  */
    register Cmd *cmd;          /* table of known commands */
{
    for ( ; (cmd -> cmdname) != (char *) 0; cmd++)
	if (lexequ (str, cmd -> cmdname))
	    return (OK);        /* got a hit */ 
    return (NOTOK);
}


/*****************************************************************************
*                           | r e a d _ l i n e |                            *
*                            *******************                             *
* Read the next line from the tailoring file.  If we hit EOF, just           *
* quit                                                                       *
*****************************************************************************/
read_line ()
{
    if (fgets (tai_line, MAXLINE-1, tai_handle) == NULL)
	exit (0);
}
/*/*****************************************************************************
*                           | t a i _ t o k e n |                            *
*                            *******************                             *
* Find the first token on the supplied line - copy it out as                 *
* requested                                                                  *
*****************************************************************************/
tai_token (strp, tokenp)
    register char * strp;
    register char * tokenp;
{
    if (!isalnum (*strp))
	return (NOTOK);
    for ( ; ; )
	switch (*strp)
	{
	    default:        /* just copy it                     */
		*tokenp++ = *strp++;
		break;

	    case '\\':      /* quote next character             */
	    case '=':   /* make '=' prefix to pair  */
	    case '\"':      /* beginning or end of string       */
		return (NOTOK); /* No commands we care about will
				   include this stuff */

	    case ' ':
	    case '\t':
	    case '\n':
	    case '\r':
	    case ',':
	    case ';':
	    case ':':
	    case '/':
	    case '|':
	    case '.':
	    case '\0':
		*tokenp = '\0';
		return (OK);
	}
}
\t  pickup done           :  %s", tptr);
        if(prtpos == 6)
            printf (" / %ld hour%s", (curtime - pickdone) / 3600L,
    	            (((curtime - pickdone) / 3600L) == 1) ? "" : "s");
        incomplete(pickstrt, pickdone);
        if(overdue(age) == 0)
            /*  Failure to complete a pickup within twice the
             *  normal 'problem time' is a special problem;
             *  use the normal compare routimmdf/src/tools/dbmedit.c   444      0     12       20236  3671073264  10507 #include "util.h"
#include "mmdf.h"
#include "chdbm.h"

/*
 * Edit the mmdf dbm database - Phil Cockcroft UCL
 * From an Idea by Peter Collinson
 *      Should really use the mmdf routines but it is easier to write
 *      your own than to find the right ones.
 */

extern char *tbldbm;
extern char *tbldfldir;
extern char *dupfpath();
extern char *multcat();
extern char *gets();

typedef struct  {
	char    *dptr;
	int     dsize;
} datum;

extern  datum   fetch();
datum   dcons();

char    ibuf[LINESIZE];
#define MARGS   100
char    *argv[MARGS];                /* max number of args on a line */
int     argc;

int     help(), add(), del(), change(), print(), quit();

struct  cmds    {
	char    *cm_name;
	int     (*cm_func)();
	char    *cm_help;
} cmds[] = {
	"help", help,   "Print out help text",
	"add",  add,    "Add an entry",
	"delete", del,  "Delete an entry",
	"change", change, "Change an entry",
	"print", print, "Print an entry",
	"quit", quit,   "Quit the program",
	"h",    help,   0,              /* single character aliases */
	"a",    add,    0,
	"d",    del,    0,
	"c",    change, 0,
	"p",    print,  0,
	"q",    quit,   0,
	"?",    help,   0,
}, *curcmd;

int	verbose;

/*
 * dbm expanded structure
 */

#define MDB     50

char	*dblock;
int     ndbents;

struct  db      {
	char    *d_table;
	char    *d_value;
} dbs[MDB], *lastdb;

main(ac, av)
char **av;
{
	mmdf_init(av[0]);

	dblock = multcat (dupfpath (tbldbm, tbldfldir), ".lck", (char *)0);
	(void) close( creat( dblock, 0444 ));

	while (ac > 1 && av[1][0] == '-') {
		if (lexequ(av[1], "-v")) {
			verbose++;
			ac--;
			av++;
		} else if (lexequ(av[1], "-d")) {
			if (ac < 3) {
				fprintf(stderr, "dbmedit: missing database path\n");
				exit(NOTOK);
			}
			tbldbm = av[2];
			ac -= 2;
			av += 2;
			dblock = multcat (tbldbm, ".lck", (char *)0);
			(void) close( creat( dblock, 0444 ));
		} else {
			fprintf(stderr, "dbmedit: unknown argument '%s'\n", av[1]);
			exit(NOTOK);
		}
	}
	if(!isstr(tbldbm)) {
		fprintf(stderr, "dbmedit: cannot find database path\n");
		exit(NOTOK);
	}
	dbfinit(tbldbm);

	/* if got command line arguments process these instead */
	if(ac > 1){
		avfix(ac, av);
		if(findcmd() < 0)
			exit(1);
		exit((*curcmd->cm_func)());
	}

	for(;;){
		printf("dbmedit> ");
		(void) fflush(stdout);
		if (gets(ibuf) == NULL)
			exit(0);
		if (!isstr(ibuf))
			continue;
		if ((argc = sstr2arg(ibuf, MARGS, argv, " \t")) == 0)
			continue;
		if(findcmd() < 0)
			continue;
		(*curcmd->cm_func)();
	}
}

avfix(ac, av)
register char   **av;
{
	register char   **ap;

	for(av++, ac--, ap = argv; ac ; ac--, argc++)
		*ap++ = *av++;
}

findcmd()
{
	register struct cmds *cp;

	for(cp = cmds ; cp < &cmds[sizeof(cmds)/sizeof(cmds[0])] ; cp++)
		if(lexequ(cp->cm_name, argv[0])){
			curcmd = cp;
			return(0);
		}
	fprintf(stderr, "dbmedit: unknown command '%s'\n", argv[0]);
	return(-1);
}

help()
{
	register struct cmds *cp;

	for(cp = cmds ; cp < &cmds[sizeof(cmds)/sizeof(cmds[0])] ; cp++)
		if(cp->cm_help != 0)
			printf("%s\t\t%s\n", cp->cm_name, cp->cm_help);
	return(0);
}

datum
dcons(value)
char    *value;
{
	datum d;

	d.dptr = value;
	d.dsize = strlen(value)+1;
	return(d);
}

dssplit(str)
register char   *str;
{
	register struct db      *dp;
	static  char    ti[512];
	char    *strcpy();

	ndbents = 0;
	str = strcpy(ti, str);
	for(dp = dbs ; dp < &dbs[sizeof(dbs)/sizeof(dbs[0])]; dp++){
		if(*str == 0)
			break;
		dp->d_table = str;
		while(*str && *str != ' ')
			str++;
		*str++ = 0;
		dp->d_value = str;
		while(*str && *str != FS)
			str++;
		ndbents++;
		if(*str == FS)
			*str++ = 0;
	}
	lastdb = dbs + ndbents;
}

add()
{
	datum   d;
	register struct db      *dp;

	if(argc != 4){
		fprintf(stderr, "Usage: add key table value\n");
		return(9);
	}
	d = fetch(dcons(argv[1]));
	if(d.dptr == 0){
		if (verbose)
			printf("Creating new key: \"%s\" table: \"%s\" value: \"%s\"\n",
						argv[1], argv[2], argv[3]);
		lastdb = dbs;
		goto newalias;
	}
	dssplit(d.dptr);
	if (verbose)
	    for(dp = dbs ; dp < lastdb ; dp++){
		if(lexequ(dp->d_table, argv[2])){
		    fprintf(stderr,
	     "Warning: key \"%s\" has existing value \"%s\" in table \"%s\"\n",
					argv[1], dp->d_value, argv[2]);
		}
	    }

newalias:;
	dp = lastdb++;
	dp->d_table = argv[2];
	dp->d_value = argv[3];
	return(dscons());
}

del()
{
	datum   d;
	register struct db      *dp;
	int     changed = 0;
	int	lckfd;

	if (argc < 2 || argc > 4) {
		fprintf(stderr, "Usage: delete key [table [value] ]\n");
		return(9);
	}

	d = fetch(dcons(argv[1]));
	if (d.dptr == 0) {
		fprintf(stderr, "Key \"%s\" not found in database\n", argv[1]);
		return(99);
	}
	if (argc == 2) {	/* all entries */
		if (verbose)
			printf("Deleting key \"%s\"\n", argv[1]);
		goto delall;
	}
	dssplit(d.dptr);
	for(dp = dbs ; dp < lastdb ; dp++) {
		if(dp->d_table && lexequ(dp->d_table, argv[2]) 
		   && ( (argc == 3) || lexequ(dp->d_value, argv[3]) )) {
			if (verbose)
			    printf("Deleting \"%s\" from table \"%s\" (value: \"%s\")\n",
					argv[1], argv[2], dp->d_value);
			changed++;
			dp->d_table = 0;
		}
	}
	if(!changed){
	    if (argc == 3)
		fprintf(stderr,
			"\"%s\" does not have a value in table \"%s\"\n",
						argv[1], argv[2]);
	    else
		fprintf(stderr,
		    "\"%s\" does not have a value of \"%s\" in table \"%s\"\n",
						argv[1], argv[3], argv[2]);
	    return(99);
	}
	if(lastdb - changed <= dbs) {
		if (verbose)
			printf("All values deleted for \"%s\" - deleting key\n",
								argv[1]);
delall:		if ((lckfd = lk_open(dblock, 0, (char *)0, (char *)0, 10)) < 0) {
			perror(dblock);
			printf("Database %s is locked.  Try again later.\n",
				 tbldbm);
			return(1);
		}
		if(delete(dcons(argv[1])) < 0)
			fprintf(stderr, "Delete failed\n");
		lk_close (lckfd, dblock, (char *)0, (char *)0);
		return(0);
	}
	return(dscons());
}

change()
{
	datum   d;
	register struct db      *dp;
	int	newvalpos;

	if(argc < 4 || argc > 5) {
		fprintf(stderr,
			"Usage: change key table [oldvalue] newvalue\n");
		return(9);
	}

	newvalpos = (argc == 4) ? 3 : 4;
	d = fetch(dcons(argv[1]));
	if(d.dptr == 0) {
		if (verbose)
			fprintf(stderr, "key \"%s\" not found in database\n",
				argv[1]);
		if (argc == 4)
			return(add());
		return(0);
	}
	dssplit(d.dptr);
	for(dp = dbs ; dp < lastdb ; dp++)
		if(lexequ(dp->d_table, argv[2])){
			if ((argc == 5) && !lexequ(dp->d_value, argv[3]))
				continue;
			if (verbose)
				printf("changing \"%s\" to \"%s\"\n",
						dp->d_value, argv[newvalpos]);
			dp->d_value = argv[newvalpos];
			break;
		}

	if(dp >= lastdb){  /* no change made */
		if (argc == 4)
			return(add());
		else {
			fprintf(stderr, 
				"No entry found for key \"%s\" in table \"%s\" with value \"%s\"\n",
				argv[1], argv[2], argv[3]);
			return(0);
		}
	}
	return(dscons());
}

print()
{
	datum   d;
	register struct db      *dp;

	if(argc < 2){
		fprintf(stderr, "Usage: print key [table]\n");
		return(9);
	}

	d = fetch(dcons(argv[1]));
	if(d.dptr == 0){
		fprintf(stderr, "key \"%s\" not found in database\n", argv[1]);
		return(99);
	}
	dssplit(d.dptr);
	for(dp = dbs ; dp < lastdb ; dp++)
		if (argc == 2 || lexequ(dp->d_table, argv[2]) ||
		    lexequ("*", argv[2]))
			printf("key \"%s\": table \"%s\": value \"%s\"\n",
				argv[1], dp->d_table, dp->d_value);
	return(0);
}

dscons()
{
	char    tbuf[ENTRYSIZE];
	datum   d;
	register struct db      *dp;
	register char   *p, *q;
	int     changed = 0;
	int	lckfd;

	for(p = tbuf, dp = dbs ; dp <lastdb ; dp++){
		if((q = dp->d_table) == 0)
			continue;
		if(p != tbuf)
			*(p-1) = FS;
		changed++;
		while(*p++ = *q++);
		*(p-1) = ' ';
		for(q = dp->d_value ; *p++ = *q++;);
	}
	if (!changed) {
		fprintf(stderr, "dbmedit: internal error 'no change'\n");
		return(99);
	}
	d = dcons(tbuf);

	if ((lckfd = lk_open(dblock, 0, (char *)0, (char *)0, 10)) < 0) {
		perror (dblock);
		printf("Database %s is locked.  Try again later.\n", tbldbm);
		return(1);
	}
	if (store(dcons(argv[1]), d) < 0) {
		fprintf(stderr, "dbmedit: cannot store new value for '%s'\n", argv[1]);
	}
	lk_close (lckfd, dblock, (char *) 0, (char *)0);
	return(0);
}

/*
 * Initialize the dbm file.
 */

dbfinit(filename)
char *filename;
{
	if(dbminit(filename) < 0) {
	    fprintf (stderr, "could not initialize data base '%s'", filename);
	    perror("");
	    exit(99);
	}
	return (0);
}

quit()
{
	exit(0);
}
	dp->d_table = argv[2];
	dp->d_value = argv[3];
	return(dscons());
}

del()
{
	datum   d;
	register struct db      *dp;
	int     changed = 0;
	int	lckfd;

	if (argc < 2 || argc > 4) {
		fprintf(stderr, "Usage: delete key [table [value] ]\n");
		return(9);
	}

	d = fetch(dcons(argv[1]));
	if (d.dptr == 0) {
		fprintf(stderr, "Key \"%s\" not found in datmmdf/src/tools/.emacs_15451   644      0     12          54  3646577003  10503 /mnt/mmdf2b/mmdf/src/tools/dbmbuild.c10915
ot initialize data base '%s'", filename);
	    perror("");
	    exit(99);
	}
	return (0);
}

quit()
{
	exit(0);
}
	dp->d_table = argv[2];
	dp->d_value = argv[3];
	return(dscons());
}

del()
{
	datum   d;
	register struct db      *dp;
	int     changed = 0;
	int	lckfd;

	if (argc < 2 || argc > 4) {
		fprintf(stderr, "Usage: delete key [table [value] ]\n");
		return(9);
	}

	d = fetch(dcons(argv[1]));
	if (d.dptr == 0) {
		fprintf(stderr, "Key \"%s\" not found in datmmdf/src/uucp/   755      0     12           0  3671074626   6444 mmdf/src/uucp/ch_uucp.c   444      0     12        6671  3671073241  10323 #include "util.h"
#include "mmdf.h"
#include "ch.h"
#include "phs.h"
#include <signal.h>

/*
 *			C H _ U U C P . C
 *
 *		Take message and feed a request to UUX
 *
 *  qu2uu_send does the interesting work.  This interface was developed
 *  for MMDF by Doug Kingston at the US Army Ballistics Research Lab,
 *  Aberdeen, Maryland.    <dpk@brl>
 *
 *		    Original Version 21 Oct 81
 *
 *			Revision History
 *			================
 *
 */

/*
 *     MULTI-CHANNEL MEMO DISTRIBUTION FACILITY  (MMDF)
 *     
 *
 *     Department of Electrical Engineering
 *     University of Delaware
 *     Newark, Delaware  19711
 *
 *     Phone:  (302) 738-1163
 *     
 *
 */

extern char *dupfpath ();
extern LLog *logptr;

Chan	*curchan;	/* what channel am I?           */

/*      MAIN MAIN MAIN MAIN MAIN MAIN MAIN MAIN MAIN MAIN MAIN MAIN     */

main (argc, argv)
short     argc;
char   *argv[];
{
    short retval;

    mmdf_init (argv[0]);
    siginit ();
    signal (SIGINT, SIG_IGN);	  /* always ignore interrupts             */

    if ((curchan = ch_nm2struct (argv[0])) == (Chan *) NOTOK)
	err_abrt (RP_PARM, "unknown channel name '%s'", argv[0]);

    retval = ch_uucp (argc, argv);
    ll_close (logptr);
#ifdef DEBUG
    ll_log (logptr, LLOGPTR, "exit value is %d.", retval);
#endif
    exit (retval);
}
/***************  (ch_) UUCP MAIL DELIVERY  ************************ */

ch_uucp (argc, argv)		  /* send to remote machine via UUCP   */
short     argc;
char   *argv[];
{
    ch_llinit (curchan);
#ifdef DEBUG
    ll_log (logptr, LLOGPTR, "ch_uucp ()");
#endif

    if (rp_isbad (qu_init (argc, argv)))
	return (RP_NO);		  /* problem setting-up for deliver     */

    if (rp_isbad (uu_init ()))
	return (RP_NO);		  /* problem with uucp startup */

    phs_note (curchan, PHS_CNSTRT);     /* make a timestamp */
    phs_note (curchan, PHS_CNGOT);      /* make a timestamp */
    if (rp_isbad (qu2uu_send ()))
	return (RP_NO);		  /* send the batch of outgoing mail    */

    qu_end (OK);		  /* done with Deliver function         */
    phs_end  (curchan, RP_OK);    /* note end of session */

#ifdef DEBUG
    ll_log (logptr, LLOGFTR, "ch_uucp: Normal Return!");
#endif
    return (OK);		  /* NORMAL RETURN                      */
}

/**/
/*VARARGS2*/
err_abrt (code, fmt, b, c, d)     /* terminate ourself                  */
short     code;
char	*fmt, *b, *c, *d;
{
    char linebuf[LINESIZE];

    qu_end (NOTOK);
    if (rp_isbad (code))
    {
#ifdef DEBUG
	if (rp_gbval (code) == RP_BNO || logptr -> ll_level >= LLOGTMP)
	{                         /* don't worry about minor stuff      */
	    sprintf (linebuf, "%s%s", "err [ ABEND (%s) ]\t", fmt);
	    ll_log (logptr, LLOGFAT, linebuf, rp_valstr (code), b, c, d);
	}
#endif
    }
    ll_close (logptr);           /* in case of cycling, close neatly   */
    exit (code);
}

/**/
/*ARGSUSED*/
LOCFUN
	uu_init()
{
#ifdef notdef
#define	UU_CARGS	8
    char    *argv[UU_CARGS];
    int    argc;
#endif notdef

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "uu_init ()");
#endif
#ifdef notdef
    if (isstr(curchan->ch_confstr)) {
	argc = sstr2arg(curchan->ch_confstr, UU_CARGS, argv, " \t");
	while (argc < UU_CARGS) {
	    if (prefix("naddrs=", argv[argc], 7)) {
		if ((uu_naddrs = atoi(argv[argc]+7)) < 1)
		    uu_naddrs = 1;
#ifdef DEBUG
		ll_log (logptr, LLOGFTR, "uu_naddrs now %d", uu_naddrs);
	    } else {
		ll_log (logptr, LLOGTMP, "unknown uucp confstr '%s'", argv[argc]);
#endif
	    }
	    argc++;
	}
    }
#endif notdef
    return (RP_OK);
}
[1]));
	if(d.dptr == 0) {
		if (verbose)
			fprintf(stderr, "key \"%s\"mmdf/src/uucp/rmail.c   444      0     12       66165  3655234556  10037 /*
 *                              R M A I L . C
 *
 *      Developed from the Berkeley mail program of the same name
 *      by Mike Obrien at RAND to run with the MMDF mail system.
 *      Rewritten by Doug Kingston, US Army Ballistics Research Laboratory
 *      Hacked a lot by Steve Bellovin (smb@unc)
 *
 *      This program runs SETUID to Opr so that it can set effective and
 *      real [ug]ids to mmdflogin.
 *
 *      Steve Kille -  Aug 84
 *              Take machete to it.  Use protocol mode,
 *              and always go thru submit - phew
 *      Lee McLoughlin Oct 84.
 *              Address munges some header lines into 822 format.
 *	Peter Collinson Dec 85.
 *		Lee's version was dependent on 
 *		(a) the uucp channel
 *		(b) the domain tables associated with the uucp channel
 *		This version costs more cycles (but there is generally
 *		only one of these running on a machine at a time) and
 *		uses mmdf's tables to dis-assemble bang routes converting
 *		them into a domain address.
 *	
 */
#undef  RUNALON

#include "util.h"
#include "mmdf.h"
#include "ap.h"
#include "ch.h"
#include "dm.h"				/* needed for host_equal */
#include <pwd.h>
#include <signal.h>
#include <sys/stat.h>

#define VERSION "3.0"

#define NAMESZ  256      /* Limit on component name size. LMCL was 64 */


char     debug;

extern  char    *locname;
extern  char    *Uchan;
extern  char    *mmdflogin;
extern  char    *sitesignature;
extern	char	*supportaddr;
extern  LLog    *logptr;

extern char *index();
extern char *rindex();
extern char *getenv();	/* get the Accounting system from the environment */
extern char *strdup();
extern char *malloc();
extern char *compress();

FILE *rm_msgf;             /* temporary out for message text */
Chan *chanptr;
int Tmpmode = 0600;
char *Msgtmp = "/tmp/rmail.XXXXXX";
char rm_date[LINESIZE];    /* date of origination from uucp header */
char rm_from[LINESIZE];    /* accumulated path of sender */
char origsys[NAMESZ];   /* originating system */
char origpath[LINESIZE];/* path from us to originating system */
char Mailsys[LINESIZE]; /* Mail system signature */
char tmpline[LINESIZE]; /* Temporary buffer for continuations and such */
char usetmp = 0;        /* Wether tmpline should be used */
#define ungetline(s) (strcpy(tmpline,s),usetmp=1)
char linebuf[LINESIZE]; /* scratchpad */
char nextdoor[LINESIZE];/* The site nextdoor who sent the mail */
char uchan[LINESIZE];	/* Channel to use for submission */
#define MAXADRS 6       /* Max no. of adrs to output on a single line. */

main(argc, argv)
char **argv;
{
	char    fromwhom[NAMESZ];       /* user on remote system */
	char    *fromptr;
	char    sys[NAMESZ];            /* an element in the uucp path */
	char    *cp, *d;
	struct passwd *pw, *getpwnam(), *getpwuid();
	int     fd;

	mmdf_init (argv[0]);

	if( argc > 2 && strcmp( argv[ 1 ], "-d" ) == 0 ){
		debug = 1;
		argc--;
		argv++;
	}

	if (argc < 2) {
		fprintf(stderr, "Usage: rmail user [user ...]\n");
		exit(1);
	}
	umask (0);

	/* First, change effective and real UID/GID into Mailsys owner */
	if ((pw = getpwnam (mmdflogin)) == NULL) {
		fprintf (stderr, "Cannot find mmdflogin\n");
		exit (99);
	}
	setgid (pw->pw_gid);
	setuid (pw->pw_uid);

	sprintf(Mailsys, "%s <%s@%s>", sitesignature, mmdflogin, locname);



	/* Create temp file for body of message */
	mktemp (Msgtmp);
	if ((fd = creat(Msgtmp, Tmpmode)) < 0)
		bomb ("Can't create %s\n", Msgtmp);
	close( fd );

	if ((rm_msgf = fopen(Msgtmp, "r+")) == NULL)
		bomb ("Can't reopen %s\n", Msgtmp);

	unlink (Msgtmp);

	/* Unravell the From and >From lines at the head of the message
	 * to work who sent it, the path it took to get here and when
	 * it was sent.
	 * Some or all of this info may be ignore depending on the 822 header
	 * info given.
	 */
	for (;;) {
		if( gets( linebuf ) == NULL )
			break;
		if(   strncmp(linebuf, "From ", 5)
		   && strncmp(linebuf, ">From ", 6) )
			break;

		cp = index (linebuf, ' ');      /* start of name */
		fromptr = ++cp;
		cp = index (cp, ' ');           /* cp at end of name */
		*cp++ = 0;                      /* term. name, cp at date */
		strcpy (fromwhom, fromptr);
		while( isspace( *cp ) ) cp++;   /* Skip any ws */
		/* The date is the rest of the line ending at \0 or remote */
		d = rm_date;
		while( *cp && strncmp( cp, " remote from ", 13 ) )
			*d++ = *cp++;
		*d = '\0';

		for (;;) {
			cp = index(cp+1, 'r');
			if (cp == NULL) {
				cp = rindex(fromwhom, '!');
				if (cp != NULL) {
					char *p;
					*cp = '\0';
					p = rindex(fromwhom, '!');
					if (p != NULL) strcpy(origsys, p+1);
					else strcpy(origsys, fromwhom);
					strcat(rm_from, fromwhom);
					strcat(rm_from, "!");
					strcpy(fromwhom, cp+1);
					goto out;
				}
				/*
				 * Nothing coherent found - so look in
				 * environment for ACCTSYS
				 */
				if ((cp = getenv("ACCTSYS")) && *cp)
				{	strcpy(origsys, cp);
					strcat(rm_from, cp);
					strcat(rm_from, "!");
					goto out;
				}
				cp = "remote from somewhere";
			}
			if (strncmp(cp, "remote from ", 12) == 0)
				break;
		}

		sscanf(cp, "remote from %s", sys);
		strcat(rm_from, sys);
		strcpy(origsys, sys);           /* Save for quick ref. */
		strcat(rm_from, "!");
out:;
	}
	if( fromwhom[0] == '\0' )               /* No from line, illegal */
		bomb ("No from lines in message\n");

	ungetline( linebuf );

	/* Complete the from field */
	strcat( rm_from, fromwhom );
	
	/* A uk special - see if the first two components of the constructed */
	/* bang address are in fact the same site. If so replace by their */
	/* official name */
	domain_cross(rm_from);

	/* Save a copy of the path to the original site.
	 * This is all the the path we were given - the user name after
	 * the last !.  NOTE: We keep the trailing !, hence the +1.
	 */
	strcpy( origpath, rm_from );
	*(rindex( origpath, '!' ) + 1) = '\0';

	/* Savepath is given a copy of the immediate neighbour */
	if( (d = index( rm_from, '!' )) != NULL ){
		*d = '\0';
		strcpy( nextdoor, rm_from );
		*d = '!';
	}
	else    strcpy( nextdoor, rm_from );
	
	/* find the channel depending in the nextdoor site */
	set_channel(nextdoor);
	ch_llinit(chanptr);

	/* Convert the from to Arpa format and leave it in from */
	fromcvt( rm_from, fromwhom );
	strcpy( rm_from, fromwhom );

	if( debug )
		printf( "from=%s, origpath=%s, date=%s\n",
			rm_from, origpath, rm_date );

	msgfix( rm_from, rm_date );

	xsubmit( rm_from, argv, argc );

	exit( 0 );
}

/*
 * Munge the message header.
 * All header lines with addresses in are munged into 822 format.
 * If no "From:" line is given supply one based on the UUCP Froms, do the
 * same if no "Date:".
 */
typedef	struct
{	char	*hname;
	int	hfound;
} Hdrselect;

Hdrselect hdrselect[] = {
	"date",	0,		/* this is a little naughty - we need */
				/* to register that we have had this */
				/* but not address munge it */
				/* so it has token 0 */
	"from",	0,
	"to",	0,
	"cc",	0,
	"bcc",	0,
	"sender",	0,
	"reply-to",	0,
	"resent-from",	0,
	"resent-sender",0,
	"resent-to",	0,
	0,	0
#define HDATE	0
#define HFROM	1
#define HTO	2
#define HCC	3
#define HBCC	4
#define HSENDER	5			
#define HREPLY	6
#define HRFROM	7
#define HRSENDER	8			
#define HRTO	9
};

/* Is this header in the list of those to have their addresses munged? */
/* return header value offset value */
/* notice that it returns 0 for the date header */
shouldmunge( name )
char *name;
{
	register Hdrselect *h;

	if( debug )
		printf( "in shouldmunge with %s\n", name );

	for( h = hdrselect; h->hname != NULL; h++ )
		if( lexequ( h->hname, name ) )
		{	h->hfound++;
			return( h - hdrselect );
		}
	return( 0 );
}

/* If this is a header line then grab the name of the header and stuff it
 * into name then return a pointer to the actually body of the line.
 * Otherwise return NULL.
 * NOTE: A header is a line that begins with a word formed of alphanums and
 * dashes (-) then possibly some whitespace then a colon (:).
 */
char *
grabheader( s, name )
register char *s, *name;
{	char tmpbuf[LINESIZE];
	/* Copy the name into name */
	while( isalpha( *s ) || isdigit( *s ) || *s == '-' )
		*name++ = *s++;
	*name = '\0';

	/* Skip any whitespace */
	while( isspace( *s ) )
		*s++;

	/* This is a header if the next char is colon */
	if( *s == ':' )
	{	s++;
		/* 
		 * This is probably illegal but we fail horribly if it
		 * happens - so we will guard against it
		 */
		if (*s != ' ' && *s != '\t' && *s != '\0')
		{	/* we need to add a space */
			strcpy(tmpbuf, s);
			sprintf(s, " %s", tmpbuf);
		}
		return( s );  /* Return a pointer to the rest of the line */
	}
	else    return( NULL );
}

msgfix( from, date )
char *from, *date;
{
	register char *s;
	char *rest;
	char *lkp;
	char name[ LINESIZE ];
	char tmpbuf[ LINESIZE];
	int haveheader = 0;    /* Do I have a header? */
	int headertoken;
	char *grabline();

	/* Loop through all the headers */
	while( 1 )
	{
		if( debug )
			printf( "in msgfix about to grabline\n" );

		s = grabline();

		if( debug )
			printf( "got %s\n", s );

		/* Is this the end of the header? */
		if( *s == '\0' || feof( stdin ) )
			break;


		/* Is this a continuation line? */
		if( haveheader && isspace( *s ) ){
			/* Note: Address munged headers handled specially */
			fprintf( rm_msgf, "%s\n", s );
		}
		else {

			/* Grab the header name from the line */
			if( (rest = grabheader( s, name )) == NULL )
				/* Not a header therefore all headers done */
				break;

			haveheader = 1;

			/* Should I address munge this? */
			if( headertoken = shouldmunge( name ) )
			{	char *finalstr;
				finalstr = "\n";
				fprintf( rm_msgf, "%s: ", name );
				/*
				 * deal specially with From lines
				 * the parser loses comments so we retain
				 * them specially here
				 */
				if (headertoken == HFROM
				 || headertoken == HRFROM)
				{	if (lkp = index(rest, '<'))
					{	*lkp = '\0';
						if (*rest == ' ')
							fprintf( rm_msgf, "%s<", rest+1);
						else
							fprintf( rm_msgf, "%s<", rest);
						*lkp = '<';
						finalstr = ">\n";
						strcpy(tmpbuf, lkp);
						/* notice that we copy from the < *
						/* to start with a space */
						tmpbuf[0] = ' ';
						rest = tmpbuf;
						lkp = rindex(rest, '>');
						if (lkp) *lkp = '\0';
						
					}
					if (lkp = index(rest, '('))
					{	*lkp = '\0';
						sprintf(tmpbuf, " (%s\n", lkp+1);
						finalstr = tmpbuf;
					}
				}
				hadr_munge( rest );
				fputs( finalstr, rm_msgf );
			}
			else    fprintf( rm_msgf, "%s\n", s);
		}
	}

	/* No From: line was given, create one based on the From's */
	if( hdrselect[HFROM].hfound == 0 ){
		fprintf( rm_msgf, "From: %s\n", from );
	}

	/* No Date: line was given, create one based on the From's */
	if( hdrselect[HDATE].hfound == 0 ){
		datecvt( date, tmpbuf );        /* Convert from uucp -> Arpa */
		fprintf( rm_msgf, "Date: %s\n", tmpbuf );
	}

	/* Copy the rest of the file, if there is any more */
	if( !feof( stdin ) ){
		/* If the first line of the message isn't blank
		 * output the blank separator line.
		 */
		if( *s )
			putc( '\n', rm_msgf );
		do {
			fprintf( rm_msgf, "%s\n", s );
			s = grabline();
		} while( !feof( stdin ) );
	}
}

char *
grabline()
{
	if( debug )
		printf( "in grabline " );

	/* Grab the next line.
	 * Remembering this might be the tmpline
	 */
	if( usetmp ){
		if( debug )
			printf( "using tmpline " );
		strcpy( linebuf, tmpline );
		usetmp = 0;
	}
	else    gets( linebuf );

	/* Anything wrong? */
	if( ferror( stdin ) )
		fputs( "\n  *** Problem during receipt from UUCP ***\n", rm_msgf )
;

	if( debug )
		printf( "returning %s\n ", linebuf );

	return( linebuf );

}

char *next = NULL;      /* Where nextchar gets the next char from */
char adrs[ LINESIZE ];  /* Partly munged addresses go here */
nextchar()
{
	register char *s;       /* scratch pointer. */

	if( next != NULL && *next != '\0' ){
		if( debug )
			printf( "<%c>", *next );
		return( *next++ );
	}

	/* The last buffer is now empty, fill 'er up */
	next = grabline();

	if( feof( stdin ) || !isspace( *next ) || *next == '\0' ){
		/* Yipee! We've reached the end of the address list. */
		ungetline( next );
		next = NULL;                    /* Force it to read buffer */
		return( -1 );
	}

	/* Zap excess whitespace */
	compress( next, adrs );

	/* This may be a slightly duff list generated by some of the
	 * UNIX mailers eg: "x y z" rather than "x,y,z"
	 * If it is in this ^^^^^^^ convert to  ^^^^^^^ 
	 * NOTE: This won't handle list: "x<x@y> y<y@z>" conversions!
	 */
	if( index( adrs, ' ' ) != NULL &&
	    index( adrs, ',' ) == NULL &&
	    index( adrs, '<' ) == NULL &&
	    index( adrs, '(' ) == NULL &&
	    index( adrs, '"' ) == NULL ){
		for( s = adrs; *s; s++ )
			if( *s == ' ' )
				*s = ',';
	}
	next = adrs;
	/* This a continuation line so return in a seperator just in case */
	return( ',' );
}


/* Munge and address list thats part of a header line.
 * Try and get the ap_* (MMDF) routines to do as much as possible for
 * us.
 */
hadr_munge( list )
char *list;
{
	AP_ptr the_addr;        /* ---> THE ADDRESS <--- */
	int adrsout = 0;        /* How many address have been output */

	ungetline( list );

	while( 1 ){
		AP_ptr ap_pinit();

		the_addr = ap_pinit( nextchar );

		if( the_addr == (AP_ptr)NOTOK )
			bomb( "cannot initialise address parser!" );

		ap_clear();

		switch( ap_1adr() ){
		case NOTOK:
			/* Something went wrong!! */
			ap_sqdelete( the_addr, (AP_ptr)0 );
			ap_free( the_addr );
			/* Emergency action in case of parser break down:
			 * print out the rest of the line (I hope).
			 */
			fprintf( rm_msgf, "%s", tmpline );
			continue;
		case OK:
			/* I've got an address to output! */
			/* output a comma seperator between the addresses
			 * and a newline every once in a while to make
			 * sure the lines aren't too long
			 */
			if( adrsout++ )
			    fprintf( rm_msgf, ",%s ", adrsout%MAXADRS?"":"\n");

			/* Munge will do all the work to output it */
			munge( the_addr );

			/* Reclaim the space */
			ap_sqdelete( the_addr, (AP_ptr)0 );
			ap_free( the_addr );

			if( debug )
				printf( "munged and space freed\n" );
			break;
		case DONE:
			if( debug )
				printf( "hadr_munge all done\n" );

			/* Reclaim the space */
			ap_sqdelete( the_addr, (AP_ptr)0 );
			ap_free( the_addr );

			return;

		}
	}
}

/* We now have a single address in the_addr to output.
 */
munge( the_addr )
AP_ptr the_addr;
{
	char adr[ LINESIZE ];
	char *s, *frees;
	AP_ptr local, domain, route;
	char *ap_p2s();

	if( debug )
		printf( "in munge with: " );

	/* Find where the important bits begin in the tree */
	ap_t2parts( the_addr, (AP_ptr *)0, (AP_ptr *)0,
			&local, &domain, &route );

	/* Convert from the tree back into a string */
	frees = s = ap_p2s( (AP_ptr)0, (AP_ptr)0, local, domain, route );

	if( debug )
		printf( "%s\n", s );

	/* Is it a uucp style address? */
	if ( domain == (AP_ptr) 0  && index (s, '!') != (char *) 0) {
		char adr2[ LINESIZE ];

		strcpy( adr, s );

		/* Stick the path it took to get here at the start.
		 * But try to avoid any duplicates by overlapping
		 * the matching parts of the address. Eg:
		 *  adr = 'x!y!z'  path = 'a!b!x!y!'
		 *  adr2 = 'a!b!x!y!z'
		 */
		for( s = origpath; *s; s++ ){
			/* Does it match? */
			if( strncmp( adr, s, strlen( s ) ) == 0 ){
				char c = *s;
				*s = '\0';
				strcpy( adr2, origpath );
				strcat( adr2, adr );
				*s = c;
				break;
			}
		}

		/* Did I just scan the whole path without finding a match! */
		if( *s == '\0' ){
			/* Append the adr to the path */
			strcpy( adr2, origpath );
			strcat( adr2, adr );
		}

		/* Munge the address into its shortest form and print it */
		fromcvt( adr2, adr );
		fprintf( rm_msgf, "%s", adr );

		if( debug )
			printf( "uucp munge gives %s\n", adr );
	}
	else {
		char *p;
		char *at;
		char *official;
		char *get_official();
		char *fmt;
		char *brace;
		AP_ptr norm;
		extern int ap_outtype;

		/* Normalise the address */
		ap_outtype = AP_733;    /* Hmm. Maybe should be from channel */
		norm = ap_normalize( nextdoor, (char *) 0, the_addr, chanptr );

		/* Convert it back in to a string and output it */
		ap_t2s( norm, &p );
		if (debug)
			printf("Normalised address: %s\n", p);
		/* Look for the last address component and re-write to the */
		/* official form. There are probably better ways of doing this */
		at = rindex(p, '@');
		fmt = "%s";
		if (at)
		{	brace = index(at+1, '>');
			if (brace) *brace = '\0';
			official = get_official(at+1, (int *)0);
			if (official)
			{	*at = '\0';
				fmt = brace ? "%s@%s>" : "%s@%s";
			}
			else if (brace)
				*brace = '>';
		}
		fprintf( rm_msgf, fmt, p , official);

		if( debug )
		{	printf( "arpa munge gives ");
			printf( fmt, p, official );
			printf( "\n");
		}

		free( p );
	}
	free( frees );
}



/*
 *      datecvt()  --  convert UNIX ctime() style date to ARPA style
 *                      (change done in place)
 */
datecvt (date, newdate)
char *date;
char *newdate;
{
	/* LMCL: Changed the default timezone, when none given, to be GMT */
	/*      012345678901234567890123456789          */
	/*      Wed Nov 18 22:23:17 1981        Unix    */
	/*      Wed, 18 Nov 81 22:23:45 GMT     ARPA    */
	/* or
	/*      Wed Nov 18 22:23:17 EDT 1981    Unix    */
	/*      Wed, 18 Nov 81 22:23:45 EDT     ARPA    */
	/* or even (LMCL yet again) */
	/*      Mon Apr 23 16:50 BST 1984       Unix    */

	if (isdigit(date[0]) || date[3] == ',' || isdigit(date[5]))
		strcpy (newdate, date);         /* Probably already ARPA */
	else if (isdigit(date[20]))
		sprintf( newdate, "%.3s, %.2s %.3s %.2s %.2s:%.2s:%.2s GMT",
		    date, date+8, date+4, date+22, date+11, date+14, date+17);
	else if (isalpha(date[17]))     /* LMCL. Bum Unix format */
		sprintf( newdate, "%.3s, %.2s %.3s %.2s %.2s:%.2s:00 GMT",
		    date, date+8, date+4, date+23, date+11, date+14 );
	else
		sprintf( newdate, "%.3s, %.2s %.3s %.2s %.2s:%.2s:%.2s %.3s",
		    date, date+8, date+4, date+26,
		    date+11, date+14, date+17, date+20);
}

/**/

msg_return (addr, dest, reason)
char *addr, *dest, *reason;
{
	ml_init (YES, NO, "UUCP (rmail)", "Failed Message");
	ml_adr (addr);
	ml_aend ();
	ml_tinit();
	ml_txt ("Your message was not delivered to:\n    ");
	ml_txt (dest);
	ml_txt ("\nFor the following reason:\n    ");
	ml_txt (reason);
	ml_txt ("\n\n--------------- Returned Mail ---------------\n\n");
	rewind (rm_msgf);
	ml_file (rm_msgf);
	ml_txt ("--------------- End of Returned Mail -------------\n");
	if (ml_end (OK)  != OK)
	{
		ml_init (YES, NO, "UUCP (rmail)", "Failed Err Message");
		ml_adr (supportaddr);
		ml_aend ();
		ml_tinit();
		ml_txt ("Error message failed to:\n    ");
		ml_txt (addr);
		ml_txt ("\n\nWhen failing to send to:\n    ");
		ml_txt (dest);
		ml_txt ("\nFor the following reason:\n    ");
		ml_txt (reason);
		ml_txt ("\n\n----------- Returned Mail --------------\n\n");
		rewind (rm_msgf);
		ml_file (rm_msgf);
		ml_txt ("-------- End of Returned Mail ---------------\n");
		if (ml_end (OK)  != OK)
		    bomb ("Failed to return error message to overseer");
	}
}

/*  */

xsubmit( from, argv, argc)
char *from;
char **argv;
int argc;
{
	char subargs[LINESIZE];
	char buf[LINESIZE];
	struct rp_bufstruct thereply;
	register char *p, *q;
	int len;

	rewind (rm_msgf);

	if( debug ){
		for( argv++; --argc > 0; argv++ )
			printf( "arg = %s\n", *argv );
		while( fgets( buf, LINESIZE-1, rm_msgf ) != NULL )
			printf( "T=%s", buf );
		if( ferror( rm_msgf ) )
			bomb( "Error reading messge file" );

		exit( 0 );
	}

	if (rp_isbad (mm_init()) || rp_isbad (mm_sbinit ()))
		bomb ("Failed to initialise submit");

	sprintf(subargs, "lvmti%s*h", uchan);

	if (nextdoor[0]) {
		q = subargs + strlen(subargs);
		for (p = nextdoor; *p; ) {
			if (!isprint(*p)) {
				p++;
				continue;
			}
			if (*p == '\'') *q++ = '\\';
			*q++ = *p++;
		}
		*q = '\0';
	}
	strcat(subargs, "*");

	if (rp_isbad (mm_winit ((char *) 0, subargs, from)))
		bomb ("mm_winit (%s, %s) failed", subargs, from);
	if (rp_isbad (mm_rrply (&thereply, &len)))
		bomb ("Failed to read address reply");
	if (rp_isbad (thereply.rp_val))
		bomb ("Initialization failure '%s'", thereply.rp_line);

	for (argv++; --argc > 0; argv++ )
	{
	    char *name;

	    name = *argv;
	    if (rp_isbad (mm_wadr("", *argv)))
		bomb ("failed to write address '%s'", name);

	    if (rp_isbad (mm_rrply (&thereply, &len)))
		bomb ("Failed to read address reply");

	    switch (rp_gval(thereply.rp_val))
	    {
		case RP_AOK:
		    break;
		case RP_USER:
		    msg_return (from, name, thereply.rp_line);
		    break;      /* LMCL: Was return */
		default:
		    bomb ("Address check failure '%s' (%s)", thereply.rp_line,
			rp_valstr (thereply.rp_val));
	    }
	}

	if (rp_isbad (mm_waend()))
	    bomb ("Failed to write address end");

	rewind (rm_msgf);
	while (fgets (buf, LINESIZE-1, rm_msgf) != NULL)
	    if (rp_isbad (mm_wtxt (buf, strlen (buf))))
		 bomb ("mm_wtxt failure");
	if (ferror (rm_msgf))
		bomb ("Error reading messge file");

	if (rp_isbad (mm_wtend ()))
		bomb ("mm_wtend error");

	if (rp_isbad (mm_rrply (&thereply, &len)))
		bomb ("mm_rrply at end of message");

	if (rp_isbad (thereply.rp_val))
		bomb ("Bad text reply val '%s' (%s)", thereply.rp_line,
				rp_valstr (thereply.rp_val));

	mm_sbend();
	return;
}


/*VARARGS1*/
bomb (fmt, a, b, c)
char *fmt, *a, *b, *c;
{
	ll_log (logptr, LLOGFAT, fmt, a, b, c);
	ll_close (logptr);
	fputs ("rmail: ", stderr);
	fprintf (stderr, fmt, a, b, c);
	putc ('\n', stderr);
	exit (99);
}

/*
 *      fromcvt()  --  trys to convert the UUCP style path into
 *                      an ARPA style name.
 */
fromcvt (from, newfrom)
char *from, *newfrom;
{
	register char *cp;
	register char *sp;
	register char *off;
	char	*at;
	char	*atoff;
	char	atstore[LINESIZE];
	char	buf[LINESIZE];
	char	*get_official();
	int	saveit;
	
	if( debug )
		printf( "fromcvt on %s\n", from );

	strcpy (buf, from);
	cp = rindex (buf, '!');
	if (cp == 0) {
		strcpy (newfrom, from);
		return;
	}
	/*
	 *	look for @site at the end of the name
	 */
	atoff = NULL;
	if ((at = index(cp, '@')) != NULL)
	{	/* got one - is it followed by a ! ? */
		if (index(at+1, '!') != NULL)
			 at = NULL;
		else
		{	/* look up the official name of the at site */
			atoff = get_official(at+1, &saveit);
			if (atoff)
			{	if (saveit)
				{	strcpy(atstore, atoff);
					atoff = atstore;
				}
			}
		}
	}
	*cp = 0;
	while( sp = rindex (buf, '!') )
	{	/*
		 * scan the path backwards looking for hosts that we
		 * know about
		 */
		if (off = get_official(sp+1, &saveit))
		{	if (atoff && lexequ(atoff, off))
				strcpy(newfrom, cp+1);
			else	sprintf(newfrom, "%s@%s", cp+1, off);
			return;
		}
		else
		if (at && lexequ(at+1, sp+1))
		{	strcpy(newfrom, cp+1);
			return;
		}
		*cp = '!';
		cp = sp;
		*cp = 0;
	}
	if (off = get_official(buf, (int *)0))
		sprintf(newfrom, "%s@%s", cp+1, off);
	else
		sprintf(newfrom, "%s@%s", cp+1, buf);
}

/*
 *	This piece added by Peter Collinson (UKC)
 *	rather than just making rmail submit into the uucp channel which
 *	is hard coded. First look up in a table 'rmail.chans' for entries
 *	of the form
 *	nextdoor:channel
 *	If the name exists then use that channel otherwise just default to
 *	the uucp channel. This is for authorisation purposes really
 */
set_channel(nxt)
char *nxt;
{	Table *tb_rmchans;
	char tuchan[LINESIZE];
#ifdef DEBUG
	ll_log(logptr, LLOGBTR, "set_channel (%s)", nxt);
#endif
	/* set default */
	strcpy(uchan, Uchan);
	
	if ((tb_rmchans = tb_nm2struct ("rmail.chans")) != (Table *) NOTOK)
	{	/* well we have the table */
		/* Extract name of current channel      */
		/* from list of channels          */
		if (tb_k2val (tb_rmchans, TRUE, nxt, tuchan) == OK)
			strcpy(uchan, tuchan);
	}

	/* Get the channel attributes */
	if (( chanptr = ch_nm2struct (uchan)) == (Chan *)NOTOK )
	{	/* fail safe - not sure that we should do this */
		/* find the default channel */
		if (( chanptr = ch_nm2struct (Uchan)) == (Chan *)NOTOK )
			bomb ("Cannot look up channel '%s'\n", uchan);
	}
}

/*
 *	Check if the from machine which we have constructed actually
 *	contains two references to the same machine of the form
 *	site.uucp!site.??.uk!something
 *	if so replace by the single official name
 */
domain_cross(route)
char *route;
{	char *second;
	char *endsecond;
	char *official;
	char *host_equal();
	
	second = index(route, '!');
	if (second == (char *)0)
		return;
	second++;
	endsecond = index(second, '!');
	if (endsecond == (char *)0)
		return;
	second[-1] = '\0';
	*endsecond = '\0';
	if (official = host_equal(route, second))
	{	strcpy(route, official);
		*endsecond = '!';
		strcat(route, endsecond);
	}
	else
	{	second[-1] = '!';
		*endsecond = '!';
	}
}

/*
 *	host_equal
 *	a routine to see whether the strings which are input
 *	are in fact the same host
 *	Returns the official name if they are and a null pointer if not
 */
char *
host_equal(n1, n2)
char *n1, *n2;
{	static char res1[LINESIZE];
	char	*o1, *o2;
	int	saveit;
	char	*get_official();
	
	o1 = get_official(n1, &saveit);
	if (o1 == NULL)
		return (NULL);
	if (saveit)
	{	strcpy(res1, o1);
		o1 = res1;
	}
	o2 = get_official(n2, &saveit);
	if (o2 == NULL)
		return (NULL);
	if (lexequ(o1, o2) == TRUE)
		return (o1);
	return (NULL);
}

/*
 *	These routines perform some caching on the host name/official
 *	name pair in and attempt to reduce overheads
 */
typedef struct
{	char	*o_host;		/* Host name */
	char	*o_official;		/* Official name */
} Official;

Official *off_cache;			/* pointer to the base of the cache */
#define OFFICIALSIZE	50		/* Chunk size for the cache */
int	off_size;			/* current size of the cache */
int	off_used;			/* current set which are used */
/*
 *	get a host from the domain tables
 *	if the cache has fallen over due to lack of memory
 *	then the saveit switch will be set showing that the
 *	address is in static memory
 */
char *
get_official(host, saveit)
char *host;
int *saveit;				/* set if the cache is not working */
{	register Official *off;
	static char res[LINESIZE];
	Domain *dmn;
	Dmn_route rbuf;
	Dmn_route *route = &rbuf;
	Domain *dm_v2route();
	int sadummy;
		
	if (debug)
		printf("get_official called for %s\n", host);
	if (saveit == (int *)0)
		saveit = &sadummy;
	*saveit = 0;			/* assume cache is working */
	
	if (off_size == 0)
	{	off_cache = (Official *)malloc(OFFICIALSIZE*sizeof(Official));
		off_size = OFFICIALSIZE;
		if (debug)
			printf("Cache%sset - %d items\n",
				off_cache ? " " : " not " ,
				off_cache ? off_size : 0 );
	}
	/*
	 * If we have managed to get space for the cache 
	 * search it for the name
	 */
	if (off_cache == (Official *)0)
		*saveit = 1;
	else
	{	for (off = off_cache; off < &off_cache[off_used]; off++)
			if (*host == *off->o_host && lexequ(host, off->o_host))
			{	if (debug)
					printf("Cache hit - %s\n", off->o_official);
				return (off->o_official);
			}
	}
	/*
	 * Bad news - we must do something to get the information
	 */
	dmn = dm_v2route(host, res, route);
	if (dmn == (Domain *) NOTOK)
	{	if (debug)
			printf("%s not in tables\n", host);
		return (NULL);
	}
	/*
	 * we have a hit in the tables
	 * save it in the cache
	 */
	if (off_used >= off_size)
	{	off_size += OFFICIALSIZE;
		/*NOSTRICT*/
		off_cache = (Official *)realloc(off_cache,off_size * sizeof(Official));
		if (debug)
		{	if (off_cache)
				printf("Cache grown to %d items\n", off_size);
			else	printf("Cache grow failed\n");
		}
	}
	if (off_cache)
	{	off = &off_cache[off_used];
		if ((off->o_host = strdup(host)) &&
		    (off->o_official = strdup(res)))
		{	off_used++;
			if (debug)
			{	printf("%s - %s stored in cache - cachesize %d\n",
					off->o_host,
					off->o_official,
					off_used);
			}
			return (off->o_official);
		}	
	}
	*saveit = 1;
	if (debug)
		printf("%s - %s not cached\n", host, res);
	return (res);
}
, tuchan);
	}

	/* Get the channel attributes */
	if (( chanptr = ch_nm2struct (uchan)) == (Chan *)NOTOK )
	{	/* fail safe - not sure that we should do this */
		/* find the default channel */
		if (( chanptr = ch_nm2struct (Uchan)) == (Chan *)NOTOK )
			bomb ("Cannot look up channel '%s'\n", uchan);
	}
}

/*
 *	Check if the from machine which we have constructed actually
 *	contains two refemmdf/src/uucp/uu_wtmail.c   444      0     12       13471  3622772400  10716 /*
 *                      U U _ W T M A I L . C
 *
 *      Part of the UUCP channel of the MMDF Mail system developed
 *      by Doug Kingston at the US Army, Ballistics Research Laboratory.
 *                              <DPK@BRL>
 *
 *      Low-level IO to handle mail delivery to the UUCP
 *      queuing system.  Calls are made from qu2uu_send.c.
 *
 *                  Original Version November 1981
 */

#include "util.h"
#include "mmdf.h"
#include "ch.h"
#include "ap.h"

extern struct ll_struct *logptr;
extern Chan *curchan;
extern char *Uuxstr;
extern int errno;
extern int pbroke;

extern int ap_outtype;
extern AP_ptr ap_s2tree ();

/*
 *      -----  Variables Local to this Module  -----
 */
static FILE     *uucpf;                   /* used by POPEN(III) */
static char     nextnode[LINESIZE];
static char     who[LINESIZE];

/**/

/*
 *      uu_wtadr() takes the given host and address and generates
 *      a valid uucp style address and verifies that the given host
 *      is in the mapping tables.  If so, it then invokes UUCP with
 *      the appropriate arguments.
 */
uu_wtadr (host, adr, sender, realfrom)
	char    *host, *adr, *sender, *realfrom;
{
    char        *index(), *rindex();
    FILE        *popen();
    char        *bangptr;
    time_t      timenow;
    char        linebuf[LINESIZE];
    char        *atp, *percentp, *lp;
    AP_ptr      ap, local, domain;

    ap_outtype = AP_733;        /* LMCL */
    /*
     *	Added by Peter C temporarily to munge host names with underscores
     *  and remove any suffix.
     *  Nasty kludge
     */
    for (atp = host; atp = index(atp, '_');)
    	*atp = '.';
#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "uu_wtadr()");
    ll_log (logptr, LLOGFTR, "host='%s', adr='%s', sender='%s', realfrom='%s'",
		host, adr, sender, realfrom );
    ll_log (logptr, LLOGFTR, "ap_outtype = %o", ap_outtype);
#endif

    strcpy(nextnode,"");
    strcpy(who,"");

    if ((ap = ap_s2tree (adr)) == (AP_ptr) NOTOK)
    {
	ll_log (logptr, LLOGTMP, "Failure to parse address '%s'", adr);
	return (RP_PARM);
    }
    ap_t2parts(ap, (AP_ptr *)0, (AP_ptr *)0, &local, &domain, (AP_ptr *)0);
    lp = ap_p2s( (AP_ptr)0, (AP_ptr)0, local, domain, (AP_ptr)0);

    atp = index (lp, '@');
    if (atp != (char *)0)
	*atp++ = '\0';
    if (lexequ(atp, host))
	atp = (char *)0;	/* don't make path-to-foo!foo.uucp!user */
	
    percentp = rindex (lp, '%');
    if (percentp != (char *) 0) {
	*percentp = '\0';
    	if (atp)
	    sprintf (adr, "%s!%s!%s", atp, ++percentp, lp);
    	else
	    sprintf (adr, "%s!%s", ++percentp, lp);
    } else if (atp) {
	sprintf (adr, "%s!%s", atp, lp);
    } else
	strcpy(adr, lp);
    free (lp);

    ll_log (logptr, LLOGFST, "address = '%s'", adr);

    if (!isstr(host))
	    strcpy(who, adr);
    else {
	    switch(tb_k2val (curchan -> ch_table, TRUE, host, nextnode)) {
	    case NOTOK:
		return (RP_USER);       /* No such host */
	    case MAYBE:
	    	return (RP_NS);
	    }
	    sprintf(who, nextnode, adr);
    }

    /* Extract first host name for destination */
    if ((bangptr=index (who, '!')) != NULL)
    {
	/* There is at least one relay machine */
	*bangptr++ = '\0';
	strcpy (nextnode, who);
	strcpy(who, bangptr);
    }
    else strcpy(nextnode, "");

    sprintf (linebuf, "%s %s!rmail \\(%s%s\\)",
		Uuxstr, nextnode, *who=='~' ? "\\\\" : "", who);

#ifdef DEBUG
    ll_log (logptr, LLOGFTR, "calling uux with <%s>", linebuf);
#endif

    printx ("Queuing UUCP mail for %s via %s...\n",
		who, nextnode);

    if ((uucpf = popen (linebuf, "w")) == NULL) {
	ll_log (logptr, LLOGFAT, "can't popen UUX (errno %d)", errno);
	return (RP_AGN);
    }

    time (&timenow);
    fprintf (uucpf, "From %s %.24s remote from %s\n",
		realfrom, ctime(&timenow), curchan->ch_lname);

#ifdef DEBUG
    ll_log (logptr, LLOGFTR, "Done uu_wtadr().");
#endif
    return (RP_OK);
}
/**/

/*
 *      UU_TXTCPY()
 *
 *      The special function of this guy is to grap the "From" line
 *      from the message header and move it to the first line and to
 *      put in standard UUCP form.  There are programs that depend
 *      on this line on less sophisticated systems.  I hate to louse
 *      up a perfectly good RFC733 letter but that's life!
 *
 *      Too refresh your memory, when this module is called, the "From"
 *      line has already been written out onto the pipe.  We now want
 *      copy the rest of the header out onto pipe a line at a time
 *      so that we cat remove the original From line.
 *
 *      SEK - have changed this.  Do not mungle now so we
 *      can use the deliver reformatting.
 *      It seems preferable to leave the orginal From: line
 *      Other sendmail and MMDF systems will prefer this
 *      Older systems will have to lump it
 */

uu_txtcpy()
{
    int     nread;
    char    buffer[BUFSIZ];

#ifdef DEBUG
    ll_log (logptr, LLOGFTR," uu_txtcpy()");
#endif

    qu_rtinit (0L);             /* ready to read the text             */

    nread = sizeof(buffer);
    while (!pbroke && (rp_gval (qu_rtxt (buffer, &nread)) == RP_OK))
    {                             /* send the text                      */
	if (fwrite (buffer, sizeof *buffer, nread, uucpf) == 0) {
	    ll_log (logptr, LLOGFAT, "write on pipe error (errno %d)", errno);
	    ll_log (logptr, LLOGFAT, "pclose returned %d", pclose (uucpf));
	    return (RP_LIO);
	}
    	nread = sizeof(buffer);

    }

    fflush(uucpf);              /* see if the pipe broke */
    if (pbroke) {
	ll_log (logptr, LLOGFAT, "pipe broke -- probably bad host");
	pclose(uucpf);
	return (RP_LIO);
    }

    return (RP_MOK);              /* got the text out                   */
}

/*
 *      uu_wttend()  --  Cleans up after the UUCP
 */
uu_wttend()
{
	if (pclose (uucpf) != 0)
		return (RP_LIO);
	return (RP_MOK);
}

/**/

/*
 *      LOWERFY()  -  convert string to lower case
 */
lowerfy (strp)
	char *strp;
{
	while (*strp = uptolow (*strp))
		strp++;
}
ttype = %o", ap_outtype);
#endif

    strcpy(nextnode,"");
    strcpy(who,"");

    if ((ap = ap_s2tree (adr)) == (AP_ptr) NOTOK)
    {
	ll_log (logptr, LLOGTMP, "Failure to parse address '%s'", adr)mmdf/src/uucp/Makefile.real   444      0     12        4054  3635174275  11115 #
#   uucp:   uucp (direct) delivery channel transmission
#

MODULES =	ch_uucp qu2uu_send uu_wtmail rmail

OBJECTS =	ch_uucp.o qu2uu_send.o uu_wtmail.o

SOURCES =	ch_uucp.c qu2uu_send.c uu_wtmail.c

real-default:	xuucp xrmail

install:	inst-uucp inst-rmail

inst-uucp:	$(CHANDIR)/$(MMPREF)uucp
$(CHANDIR)/$(MMPREF)uucp:	xuucp
	cp xuucp $(CHANDIR)/$(MMPREF)uucp
	-$(CHOWN) $(MMDFLOGIN) $(CHANDIR)/$(MMPREF)uucp
	-chmod 04$(PGMPROT) $(CHANDIR)/$(MMPREF)uucp
	-@ls -ls $(CHANDIR)/$(MMPREF)uucp
	-@echo "uucp installed normally"; echo ""

uucp:	xuucp
xuucp:	$(OBJECTS) $(MMDFLIBS)
	$(CC) $(LDFLAGS) -o $@ $(OBJECTS) $(MMDFLIBS) $(SYSLIBS)

inst-rmail:	/usr/bin/rmail
/usr/bin/rmail:	xrmail
	-@echo "***** Please install rmail by hand in /bin or /usr/bin."
	: cp xrmail /usr/bin/rmail
	: -$(CHOWN) root /usr/bin/rmail
	: -chmod 04$(PGMPROT) /usr/bin/rmail
	: -@ls -ls /usr/bin/rmail
	: -@echo "rmail installed normally"; echo ""

rmail:	xrmail
xrmail:	rmail.o $(MMDFLIBS)
	$(CC) $(LDFLAGS) -o $@ rmail.o $(MMDFLIBS) $(SYSLIBS)

lint:	l-uucp l-rmail

l-uucp:
	$(LINT) $(LFLAGS) $(SOURCES) $(LLIBS)

l-rmail:
	$(LINT) $(LFLAGS) rmail.c $(LLIBS)

clean:
	-rm -f xuucp xrmail *.o x.c makedep eddep make.out errs


# DO NOT DELETE THIS LINE -- make depend uses it
deliver.o: deliver.c
ch_uucp.o: ch_uucp.c
ch_uucp.o: ../../h/util.h
ch_uucp.o: ../../h/mmdf.h
ch_uucp.o: ../../h/ch.h
ch_uucp.o: ../../h/phs.h
ch_uucp.o: /usr/include/signal.h
qu2uu_send.o: qu2uu_send.c
qu2uu_send.o: ../../h/util.h
qu2uu_send.o: ../../h/mmdf.h
qu2uu_send.o: ../../h/ch.h
qu2uu_send.o: /usr/include/signal.h
qu2uu_send.o: ../../h/ap.h
qu2uu_send.o: ../../h/phs.h
uu_wtmail.o: uu_wtmail.c
uu_wtmail.o: ../../h/util.h
uu_wtmail.o: ../../h/mmdf.h
uu_wtmail.o: ../../h/ch.h
uu_wtmail.o: ../../h/ap.h
rmail.o: rmail.c
rmail.o: ../../h/util.h
rmail.o: ../../h/mmdf.h
rmail.o: ../../h/ap.h
rmail.o: ../../h/ch.h
rmail.o: ../../h/dm.h
rmail.o: /usr/include/pwd.h
rmail.o: /usr/include/signal.h
rmail.o: /usr/include/sys/stat.h
# DEPENDENCIES MUST END AT END OF FILE
# IF YOU PUT STUFF HERE IT WILL GO AWAY
# see make depend above
o);
	return (RP_AGN);
    }

    time (&timenow);
    fprintf (uucpf, "From %s %.24s remote from %s\n",
		realfrom, ctime(&timenow), curchan->ch_lname);

#ifdef DEBUG
    ll_log (logptr, LLOGFTR, "Done uu_wtadr().");
#endif
    return (RP_OK);
}
/**/

/*
 *      UU_TXTCPY()
 *
 *      The special function of this guy is to grap the "From" line
 *      from the message header and move it to the first line and to
 *      put in standard UUCP form.  There are programmdf/src/uucp/gen   555      0     12          57  3620510537   7156 make -f ../../Makefile.com -f Makefile.real $*
ansmission
#

MODULES =	ch_uucp qu2uu_send uu_wtmail rmail

OBJECTS =	ch_uucp.o qu2uu_send.o uu_wtmail.o

SOURCES =	ch_uucp.c qu2uu_send.c uu_wtmail.c

real-default:	xuucp xrmail

install:	inst-uucp inst-rmail

inst-uucp:	$(CHANDIR)/$(MMPREF)uucp
$(CHANDIR)/$(MMPREF)uucp:	xuucp
	cp xuucp $(CHANDIR)/$(MMPREF)uucp
	-$(CHOWN) $(MMDFLOGIN) $(CHANDIR)/$(MMPREF)uucp
	-chmod 04$(PGMPROT) $(CHANDIR)/$(MMPREF)uucp
	-@ls -ls $(CHANDIR)/$(MMPREF)uucp
	-@echo "uucp installmmdf/src/uucp/qu2uu_send.c   444      0     12       22062  3620510537  10775 #include "util.h"
#include "mmdf.h"
#include "ch.h"
#include <signal.h>
#include "ap.h"
#include "phs.h"

/*
 *                      Q U 2 U U _ S E N D . C
 *
 *                  SEND FROM DELIVER TO UUCP PROGRAMS
 *
 *  The UUCP channel developed for MMDF at the US Army
 *  Ballistics Research Lab by Doug Kingston.    <dpk@brl>
 *
 *                     Original Version 21 Oct 81
 */

/*
 *     MULTI-CHANNEL MEMO DISTRIBUTION FACILITY  (MMDF)
 *
 *
 *     Department of Electrical Engineering
 *     University of Delaware
 *     Newark, Delaware  19711
 *
 *     Phone:  (302) 738-1163
 *
 *
 */

extern	char	*malloc();
extern	char	*rindex();
extern  char    *strdup();
extern  char    *ap_p2s();
extern  char    *multcat();
extern	char	*blt();

extern struct ll_struct   *logptr;
extern char *qu_msgfile;          /* name of file containing msg text   */
extern Chan      *curchan;  /* Who we are */


int pbroke;             /* Set if SIGPIPE occurs (in qu2uu_each) */

LOCVAR struct rp_construct
	rp_aend =
{
    RP_OK, 'u', 'u', 'c', 'p', ' ', 'e', 'n', 'd', ' ', 'o', 'f', ' ', 'a',
    'd', 'd', 'r', ' ', 'l', 'i', 's', 't', '\0'
},
	rp_mok =
{
    RP_MOK, 'm', 'o', 'k', '\0'
},
	rp_noop =
{
    RP_NOOP, 's', 'u', 'b', '-', 'l', 'i', 's', ' ', 'n', 'o', 't', ' ',
    's', 'p', 'e', 'c', 'i', 'a', 'l', '\0'
},
	rp_ns =
{
    RP_NS, 't', 'e', 'm', 'p', ' ', 'n', 'a', 'm', 'e', 's', 'e', 'r', 'v',
    'e', 'r', ' ', 'f', 'a', 'i', 'l', 'u', 'r', 'e', '\0'
},
	rp_err =
{
    RP_NO, 'u', 'n', 'k', 'n', 'o', 'w', 'n', ' ', 'e', 'r', 'r', 'o', 'r', '\0'
},
	rp_pipe =
{
    RP_NO, 'u', 'u', 'x', ' ', 'p', 'i', 'p', 'e', ' ',
    'b', 'r', 'o', 'k', 'e', ' ', '(', 's', 'y', 's', 't', 'e', 'm', ' ',
    'u', 'n', 'k', 'n', 'o', 'w', 'n', '?', ')', '\0'
},
	rp_bhost =
{
    RP_USER, 'b', 'a', 'd', ' ', 'h', 'o', 's', 't', ' ', 'n', 'a',
    'm', 'e', '\0'
};

/**/

qu2uu_send ()                     /* overall mngmt for batch of msgs    */
{
    short   result;
    char    *findfrom();
    char    *realfrom;
    char    info[LINESIZE],
	    sender[ADDRSIZE];

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "qu2uu_send ()");
#endif

    if (rp_isbad (result = qu_pkinit ()))
	return (result);

    /*
     *  AP_SAME == NO header munging.  We need to read the header
     *  ourselves and the munging just gets in the way.
     */
    for(;;){        /* get initial info for new message   */
	result = qu_rinit (info, sender, curchan -> ch_apout);
	if(rp_gval(result) == RP_NS){
	    qu_rend();
	    continue;
	}
	if(rp_gval(result) == RP_DONE)
	    break;
	if (rp_gval(result) == RP_FIO)          /* Can't open message file */
	    continue;
	else if (rp_gval(result) != RP_OK)      /* Some other error */
	    break;
	phs_note (curchan, PHS_WRSTRT);

	printx ("\r\nReformatting message from %s (%s)\r\n", sender, info);
	realfrom = findfrom (sender);
	if(realfrom == (char *)MAYBE){
	    result = RP_NS;
	    break;
	}
	printx ("Realfrom is '%s'\r\n", realfrom);

	if (rp_isbad (result = qu2uu_each (sender, realfrom)))
	    return (result);      /* process each adrlst/text pair     */
	qu_rend();
    }
    qu_rend();

    if (rp_gval (result) == RP_NS)
    {
	ll_log (logptr, LLOGTMP, "Nameserver failure");
	return (RP_NS);           /* catch protocol errors              */
    }
    if (rp_gval (result) != RP_DONE)
    {
	ll_log (logptr, LLOGTMP, "not DONE (%s)", rp_valstr (result));
	return (RP_RPLY);         /* catch protocol errors              */
    }

    qu_pkend ();                  /* done getting messages              */
    phs_note (curchan, PHS_WREND);

    return (result);
}
/**/

LOCFUN
	qu2uu_each (sender, realfrom)   /* generate a UUX for an address */
	char *sender;
	char      *realfrom;
{
    RP_Buf    replyval;
    short     result;
    char      host[ADDRSIZE];
    char      adr[ADDRSIZE];
    extern    brpipe();
    int       (*oldsig)();

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "qu2uu_each()");
#endif

    FOREVER                       /* iterate thru list                    */
    {                             /* we already have and adr              */
	result = qu_radr (host, adr);
	if (rp_isbad (result))
	    return (result);      /* get address from Deliver           */
	if (rp_gval (result) == RP_HOK)
	{
	    qu_wrply ((RP_Buf *) &rp_noop, sizeof rp_noop);
	    continue;
	}
	else if (rp_gval (result) == RP_DONE)
	{
	    qu_wrply ((RP_Buf *) &rp_aend, sizeof rp_aend);
	    return (RP_OK);       /* end of address list                */
	}

	/*
	 *  Following function takes     (name+host)     and
	 *  looks up a!b!c!host then puts "a" in host and
	 *  "b!c!name" in the adr.
	 */
	pbroke = 0;
	oldsig = signal(SIGPIPE, brpipe);
	replyval.rp_val = uu_wtadr (host, adr, sender, realfrom);
	switch (replyval.rp_val)
	{
	    case RP_AOK:
	    case RP_OK:
#ifdef DEBUG
		ll_log (logptr, LLOGFTR, "Calling txtcpy()\n");
#endif
		replyval.rp_val = uu_txtcpy();
		break;

	    case RP_USER:
		ll_log (logptr, LLOGFAT, "host (%s) not in table", host);
		blt((char *)&rp_bhost, (char *) &replyval, sizeof rp_bhost);
		break;

	    case RP_NS:
		ll_log (logptr, LLOGTMP, "nameserver failure in host lookup");
		blt((char *)&rp_ns, (char *) &replyval, sizeof rp_ns);
		break;

	    default:
		ll_log (logptr,LLOGFAT,"unknown error (0%o)", replyval.rp_val);
		blt((char *)&rp_err, (char *) &replyval, sizeof rp_err);
		replyval.rp_val = RP_NO;
	}
	if (replyval.rp_val != RP_MOK) {
		qu_wrply (&replyval, sizeof(replyval.rp_val)
			  + strlen(replyval.rp_line));
	} else {
	    replyval.rp_val = uu_wttend();
	    switch (replyval.rp_val) {
		    case RP_AOK:
		    case RP_OK:
		    case RP_MOK:
			qu_wrply ((RP_Buf *)&rp_mok, sizeof rp_mok);
			break;

		    case RP_USER:
			ll_log (logptr, LLOGFAT, "host (%s) not in table", host);
			qu_wrply ((RP_Buf *)&rp_bhost, sizeof rp_bhost);
			break;

		    case RP_LIO:
			ll_log (logptr,LLOGTMP,"uux pipe broke (unknown?)");
			qu_wrply ((RP_Buf *)&rp_pipe, sizeof rp_pipe);
			break;

		    default:
			ll_log (logptr,LLOGFAT,"unknown error on close (0%o)", replyval.rp_val);
			qu_wrply ((RP_Buf *)&rp_err, sizeof rp_err);
	    }
	}
	signal(SIGPIPE, oldsig);
    }
}

brpipe()
{
	pbroke = 1;
	signal(SIGPIPE, SIG_IGN);
}

/********/

/*
 *      This function probably should be an address parser
 *      but for now this will have to do.  -DPK-
 *
 *      Simple address parser use inserted by smb.
 */
extern int ap_outtype;
extern AP_ptr ap_normalize ();

char *
findfrom (sender)
char *sender;
{
	int     aptypesav;
	char    *adr;
	register char    *p;
	AP_ptr  ap;
	AP_ptr  local,
		domain,
		route;
	char    *MakeUucpFrom();

/* SEK have axed looking at top of file.  */
/* This may not be wise - but very much neater */
/* Delver has no business being given UUCP style messsages */

	if ((ap = ap_s2tree (sender)) == (AP_ptr) NOTOK)
	{
	    ll_log (logptr, LLOGTMP, "Failure to parse address '%s'", sender);
	    return (strdup (sender));
	}

	ap = ap_normalize (curchan -> ch_lname, curchan -> ch_ldomain,
		ap, curchan);
	if(ap == (AP_ptr)MAYBE)
	    return( (char *)MAYBE);
	ap_t2parts (ap, (AP_ptr *) 0, (AP_ptr *) 0, &local, &domain, &route);
	aptypesav = ap_outtype;
	ap_outtype = AP_733;
	adr = ap_p2s ((AP_ptr) 0, (AP_ptr) 0, local, domain, route);
	if(adr == (char *)MAYBE){
	    ap_outtype = aptypesav;
	    return(adr);
	}
	if ((route == (AP_ptr) 0))
	{
		p = multcat (curchan -> ch_lname, ".",
				curchan -> ch_ldomain, (char *)0);
		if (lexequ (p, domain -> ap_obvalue))
		{
			free (adr);
			adr = strdup (local -> ap_obvalue);
		}
		free (p);
	}
	ll_log (logptr, LLOGFST, "sender = '%s'", adr);
	ap_outtype = aptypesav;

	lowerfy(adr);
	return(MakeUucpFrom(adr));
}

/*
 * This added by pc (UKC) to generate correct 'From' lines with
 * `!' separated routes for uucp sites
 * the rules
 * 	a@b   -> b!a
 *	a%b@c -> c!b!a
 *	etc
 *	a%b%c%d%e@x -> x!e!d!c!b!a
 *	This is done by a call to a recursive routine which I hope is OK
 */
static
char *
MakeUucpFrom(adr)
char *adr;
{	char *new;
	char *localname;
	register char *site;
	
	/*NOSTRICT*/
	if ((new = malloc(strlen(adr)+1)) == (char *)0)
		return ((char *)NOTOK);
	/*
	 * Can we assume that this is a legal 733 address ?
	 * look for the first site
	 */
	site = rindex(adr, '@');
	if (site)
	{	*site++ = '\0';
		/*
		 * some input channels (notably ni_niftp) will add the
		 * name of the local machine into this address
		 * so we look for it and delete it if found
		 */
		localname = multcat(curchan->ch_lname, ".", curchan->ch_ldomain, 0);
		/*
		 * if not the same then put back a % to let ScanUucpFrom work
		 */
		if (!lexequ (localname, site))
			site[-1] = '%';
		free(localname);
	}
	ScanUucpFrom(new, adr);
	free(adr);
#ifdef DEBUG
	ll_log (logptr, LLOGBTR, "sender (From line) = '%s'", new);
#endif
  	return(new);
}

static
ScanUucpFrom(new, adr)
register char *new;
register char *adr;
{	register char *site;
	char	*rindex();

	/*
	 * This presumes that the address we are scanning is somewhat
	 * legal - but the @ has been replaced by a %
	 */
	site = rindex(adr, '%');
	if (site == (char *)0)
	{	(void) strcpy(new, adr);
		return;
	}
	*site++ = '\0';
	(void) strcpy(new, site);
	new += strlen(site);
	*new++ = '!';
	*new = '\0';
	ScanUucpFrom(new, adr);
}
kup");
		blt((char *)&rp_ns, (char *) &replyval, sizeof rp_ns);
		break;

	    default:
		ll_log (logptr,LLOGFAT,"unknown error (0%o)", replyval.rp_val);
		blt((char *)&rp_err, (char *) &replyval, sizeof rp_err);
		replyval.rp_val = RP_NO;
	}
	if (replyval.rp_val != RP_MOK) {
		qu_wrply (&replyval, sizeof(replyval.rp_val)
			  + strlen(replyval.rp_line));
	} else {
	    replyval.rp_val = uu_wttend();
	    switch (replyval.rp_val) {
		    case RP_AOK:
		    cmmdf/src/shellfiles/   755      0     12           0  3635165255   7621 mmdf/src/shellfiles/setlogs.sh   555      0     12         467  3620510554  11700 #!/bin/sh

if test "$1" = erase
then
	> msg.log
	> chan.log
	> ph.log
	> ph.trn
	> omsg.log
	> ochan.log
	> oph.log
	> oph.trn
fi

cd /usr/mmdf/log
mv msg.log  omsg.log
mv chan.log ochan.log
mv ph.log   oph.log
mv ph.trn   oph.trn
> msg.log
> chan.log
> ph.log
> ph.trn
chmod 662 msg.log chan.log ph.log ph.trn
qu (p, domain -> ap_obvalue))
		{
			free (adr);
			adr = strdup (local -> ap_obvalue);
		}
		free (p);
	}
	ll_log (logptr, LLOGFST, "sender = '%s'", adr);
	ap_outtype = aptypesav;

	lowerfy(adr);
	retmmdf/src/shellfiles/prune.sh   555      0     12         415  3620510554  11342 #! /bin/sh
#
#  Prune - Go through all the messages in the queue and
#          delete all the deadwood entries.
#
PATH=/usr/brl/bin:/usr/ucb:/bin:/usr/bin
export PATH

cd /usr/mmdf/lock/home/addr
for i in msg.*
do
flock $i ed $i << DONE
3,\$g/^. \* /d
f
w
q
DONE
done

chmod 662 msg.log chan.log ph.log ph.trn
qu (p, domain -> ap_obvalue))
		{
			free (adr);
			adr = strdup (local -> ap_obvalue);
		}
		free (p);
	}
	ll_log (logptr, LLOGFST, "sender = '%s'", adr);
	ap_outtype = aptypesav;

	lowerfy(adr);
	retmmdf/src/shellfiles/makearpa.sh   555      0     12         443  3620510554  11773 #! /bin/sh
: This shell file makes a new smtp host table, based upon
: the NIC host table.
nictable -C < /etc/nic-table > NEWsmtp
mv -f smtp smtp.bak; mv -f NEWsmtp smtp

: Here, we make a new Arpa Domain table.
nictable -D < /etc/nic-table > NEWarpa
mv -f arpa arpa.bak; mv -f NEWarpa arpa
n.log ph.log ph.trn
qu (p, domain -> ap_obvalue))
		{
			free (adr);
			adr = strdup (local -> ap_obvalue);
		}
		free (p);
	}
	ll_log (logptr, LLOGFST, "sender = '%s'", adr);
	ap_outtype = aptypesav;

	lowerfy(adr);
	retmmdf/src/shellfiles/doit.sh   555      0     12         337  3620510554  11153 #! /bin/sh
PATH=.:/bin:/usr/bin
export PATH
umask 002		# vital! -Mike
# to do everything, run this file.
echo "Processing new MMDF tables for `hostname`"
/bin/sh makearpa
/bin/sh makemailids
/bin/sh makealias
dbmbuild -Onv
 < /etc/nic-table > NEWarpa
mv -f arpa arpa.bak; mv -f NEWarpa arpa
n.log ph.log ph.trn
qu (p, domain -> ap_obvalue))
		{
			free (adr);
			adr = strdup (local -> ap_obvalue);
		}
		free (p);
	}
	ll_log (logptr, LLOGFST, "sender = '%s'", adr);
	ap_outtype = aptypesav;

	lowerfy(adr);
	retmmdf/src/shellfiles/turkeys.sh   555      0     12         176  3620510555  11724 checkmsgs | sed -e "/Message msg/d" -e "/Return address/d" \
	-e 's/ (via .*)$//' -e 's/^.*@//' | sort | uniq -c | sort -n -r
MMDF tables for `hostname`"
/bin/sh makearpa
/bin/sh makemailids
/bin/sh makealias
dbmbuild -Onv
 < /etc/nic-table > NEWarpa
mv -f arpa arpa.bak; mv -f NEWarpa arpa
n.log ph.log ph.trn
qu (p, domain -> ap_obvalue))
		{
			free (adr);
			adr = strdup (local -> ap_obvalue);
		}
		free (p);
	}
	ll_log (logptr, LLOGFST, "sender = '%s'", adr);
	ap_outtype = aptypesav;

	lowerfy(adr);
	retmmdf/src/shellfiles/makealias.sh   555      0     12        2036  3620510555  12162 #!/bin/sh
if test ! -r alias.local
then
	echo "Cannot read 'alias.local'"
	exit 1
elif test ! -r alias.global
then
	echo "Cannot read 'alias.global'"
	exit 1
fi

trap 'rm -f /tmp/$$.* newalias 2> /dev/null; echo --Aborted--; exit' 1 2 3 15
set -u
umask 077
LOCALHOST=`hostname`
echo "Building alias file for host '${LOCALHOST}'"

#
#  Put editor commands to convert YOUR hostname into the canonical form
#
cat > /tmp/$$.sed <<DONE
s/[ 	]*#.*//
/^$/d
s/@BRL-VGR/@brl-vgr/
s/@Brl-Vgr/@brl-vgr/
s/@VGR/@brl-vgr/
s/@Vgr/@brl-vgr/
s/@vgr/@brl-vgr/
s/@/ @/
DONE

cat > /tmp/$$.awk <<DONE
{
	if( \$3 == "" || \$3 == "@$LOCALHOST" ) {
		if( \$1 != \$2 )
			printf( "%s\t%s%s\n", \$1, \$2, \$3 )
	} else
		printf( "%s\t%s%s\n", \$1, \$2, \$3 )
}
DONE

echo -n "Processing alias.local"
sed -f /tmp/$$.sed alias.local | awk -f /tmp/$$.awk > newalias

echo -n " and alias.global"
sed -f /tmp/$$.sed alias.global | awk -f /tmp/$$.awk >> newalias

echo "."
rm /tmp/$$.*
chmod 644 newalias
mv -f aliases aliases.bak
mv -f newalias aliases
echo "New aliases file built"
 for the first site
	 */
	site = rindex(adr, '@');
	if (site)
	{	*site++ = '\0';
		/*
		 * some input channels (notably ni_niftp) will add the
		 * name of the local machine into this address
		 * so we look for it and delete it if found
		 */
		localname = multcat(curchan->ch_lname, ".", curchan->ch_ldomain, 0);
		/*
		 * if not the same then put back a % to let ScanUucpFrom work
		 */
		if (!lexequ (localname, site))
			site[-1] = '%';
		free(localname);
	}
	ScanUucpFrom(new,mmdf/src/shellfiles/makemailids.sh   555      0     12         700  3620510555  12467 #! /bin/sh
#
#	makemailids.sh
#
#	Generates the mailids and users files from /etc/passwd.
#	/etc/passwd is expected to contain an "<mailid>" entry in
#	the GCOS field.  This field may eventually contain more than
#	one mailid.
#
PATH=/bin:/usr/bin:.

ed - /etc/passwd <<DONE
v/</d
g/:.*</s//	/
g/>.*/s///
w users.new
g/\(.*\)	\(.*\)/s//\2	\1/
w mailids.new
q
DONE
mv users users.bak
mv users.new users
mv mailids mailids.bak
mv mailids.new mailids
d
s/@BRL-VGR/@brl-vgr/
s/@Brl-Vgr/@brl-vgr/
s/@VGR/@brl-vgr/
s/@mmdf/src/shellfiles/setlogs   555      0     12        1041  3620510555  11275 cd /usr/mmdf/log
mv msg.log  omsg.log
mv chan.log ochan.log
mv ph.log   oph.log
mv ph.trn   oph.trn
cp /dev/null msg.log
cp /dev/null chan.log
cp /dev/null ph.log
cp /dev/null ph.trn
chmod 622 msg.log chan.log ph.log ph.trn
/etc/chown mmdf msg.log chan.log ph.log ph.trn
(grep "end msg" omsg.log; grep "[rl]in msg" omsg.log) | \
	sort >> /usa/dcrocker/mmdf.misc/msg.log
ls -ls /usa/dcrocker/mmdf.misc/msg.log
(grep "[rw]msg" ochan.log; grep "end " ochan.log) | \
	sort >> /usa/dcrocker/mmdf.misc/chan.log
ls -ls /usa/dcrocker/mmdf.misc/chan.log
.log
mv chan.log ochan.log
mv ph.log   oph.log
mv ph.trn   oph.trn
cp /dev/null msg.log
cp /dev/null chan.log
cp /dev/null ph.log
cp /dev/null ph.trn
chmod 622 msg.log chan.log ph.log ph.trn
/etc/chown mmdf msg.log chan.log ph.log ph.trn
(grep "end msg" omsg.log; grep "[rl]in msg" omsg.log) | \
	sort >> /usa/dcrocker/mmdf.misc/msg.log
ls -ls /usa/dcrocker/mmdf.misc/msg.log
(grep "[rw]msg" ochan.log; grep "end " ochan.log) | \
	sort >> /usa/dcrocker/mmdf.misc/chan.log
ls -ls mmdf/src/prog/   755      0     12           0  3671117107   6427 mmdf/src/prog/Makefile.real   444      0     12        4607  3635174434  11111 #
#   prog:   prog (direct) delivery channel transmission
#
MODULES	= ch_prog qu2pr_send pr_wtmail pr2mm_send pr_errmsg

SOURCES	= ch_prog.c qu2pr_send.c pr_wtmail.c pr2mm_send.c pr_errmsg.c

real-default:	sendprog recvprog

install:	inst-sendprog inst-recvprog

lint:		l-sendprog l-recvprog

sendprog:	xsendprog
xsendprog:	ch_prog.o qu2pr_send.o pr_wtmail.o $(MMDFLIBS)
	$(CC) $(LDFLAGS) -o $@ ch_prog.o qu2pr_send.o pr_wtmail.o $(MMDFLIBS) $(SYSLIBS)

l-sendprog:
	$(LINT) $(LFLAGS) ch_prog.c qu2pr_send.c pr_wtmail.c $(LLIBS)

inst-sendprog:	$(CHANDIR)/sendprog
$(CHANDIR)/sendprog:	xsendprog
	cp xsendprog $(CHANDIR)/sendprog
	-$(CHOWN) $(MMDFLOGIN) $(CHANDIR)/sendprog
	-chmod $(PGMPROT) $(CHANDIR)/sendprog
	-@ls -ls $(CHANDIR)/sendprog
	-@echo "sendprog installed normally"; echo ""

recvprog:	xrecvprog
xrecvprog:	pr2mm_send.o pr_errmsg.o
	$(CC) $(LDFLAGS) -o $@ pr2mm_send.o pr_errmsg.o $(MMDFLIBS) $(SYSLIBS)


l-recvprog:
	$(LINT) $(LFLAGS) pr2mm_send.c pr_errmsg.c $(LLIBS)

inst-recvprog:	$(CHANDIR)/recvprog
$(CHANDIR)/recvprog:	xrecvprog
	cp xrecvprog $(CHANDIR)/recvprog
	-$(CHOWN) $(MMDFLOGIN) $(CHANDIR)/recvprog
	-chmod $(PGMPROT) $(CHANDIR)/recvprog
	-@ls -ls $(CHANDIR)/recvprog
	-@echo "recvprog installed normally"; echo ""

clean:
	-rm -f xsendprog xrecvprog *.o x.c core makedep eddep make.log 


# DO NOT DELETE THIS LINE -- make depend uses it

ch_prog.o: ch_prog.c
ch_prog.o: ../../h/util.h
ch_prog.o: ../../h/mmdf.h
ch_prog.o: ../../h/ch.h
ch_prog.o: ../../h/phs.h
ch_prog.o: /usr/include/signal.h
qu2pr_send.o: qu2pr_send.c
qu2pr_send.o: ../../h/util.h
qu2pr_send.o: ../../h/mmdf.h
qu2pr_send.o: ../../h/ch.h
qu2pr_send.o: /usr/include/signal.h
qu2pr_send.o: ../../h/ap.h
qu2pr_send.o: ../../h/phs.h
pr_wtmail.o: pr_wtmail.c
pr_wtmail.o: ../../h/util.h
pr_wtmail.o: ../../h/mmdf.h
pr_wtmail.o: ../../h/ch.h
pr_wtmail.o: ../../h/ap.h
pr2mm_send.o: pr2mm_send.c
pr2mm_send.o: /usr/include/stdio.h
pr2mm_send.o: /usr/include/errno.h
pr2mm_send.o: /usr/include/sys/wait.h
pr2mm_send.o: /usr/include/sys/file.h
pr2mm_send.o: /usr/include/signal.h
pr2mm_send.o: ../../h/util.h
pr2mm_send.o: ../../h/mmdf.h
pr2mm_send.o: ../../h/ch.h
pr2mm_send.o: ../../h/ap.h
pr_errmsg.o: pr_errmsg.c
pr_errmsg.o: /usr/include/stdio.h
pr_errmsg.o: /usr/include/sys/file.h
pr_errmsg.o: ../../h/util.h
pr_errmsg.o: ../../h/mmdf.h
# DEPENDENCIES MUST END AT END OF FILE
# IF YOU PUT STUFF HERE IT WILL GO AWAY
# see make depend above
lyval.rp_line));
	} else {
	    replyval.rp_val = uu_wttend();
	    switch (replyval.rp_val) {
		    case RP_AOK:
		    cmmdf/src/prog/ch_prog.c   444      0     12        4343  3620510556  10302 /*
 *	MULTI-CHANNEL MEMO DISTRIBUTION FACILITY  (MMDF)
 *
 *	Department of Electrical Engineering
 *	University of Delaware
 *	Newark, Delaware  19711
 *
 *
 *	Program Channel: Take message and feed a request to a program
 *
 *
 *	C H _ P R O G . C
 *	=================
 *
 *	main program (qu2pr_send does the interesting work)
 *
 *	J.B.D.Pardoe
 *	University of Cambridge Computer Laboratory
 *	October 1985
 *	
 *	based on the UUCP channel by Doug Kingston (US Army Ballistics 
 *	Research Lab, Aberdeen, Maryland: <dpk@brl>)
 *
 */

#include "util.h"
#include "mmdf.h"
#include "ch.h"
#include "phs.h"
#include <signal.h>


extern struct ll_struct chanlog;
extern char logdfldir[];
struct ll_struct  *logptr = &chanlog;

Chan *chan;


/*
 * main
 * ====
 */
main (argc, argv)
    short   argc;
    char   *argv[];
{
    char *dupfpath ();
    short retval;

    mmdf_init (argv[0]);
    siginit ();
    signal (SIGINT, SIG_IGN); 

    if ((chan = ch_nm2struct (argv[0])) == (Chan *) NOTOK)
	err_abrt (RP_PARM, "unknown channel name '%s'", argv[0]);

    retval = ch_prog (argc, argv);
    ll_close (logptr);

#ifdef DEBUG
    ll_log (logptr, LLOGPTR, "exit value is %d.", retval);
#endif

    exit (retval);
}

/*
 * ch_prog
 * =======
 */
ch_prog (argc, argv)
    short   argc;
    char   *argv[];
{
    ch_llinit (chan);
#ifdef DEBUG
    ll_log (logptr, LLOGPTR, "ch_prog ()");
#endif

    if (rp_isbad (qu_init (argc, argv)))
	return (RP_NO);

    lowerfy (chan->ch_lname);

    phs_note (chan, PHS_CNSTRT);
    phs_note (chan, PHS_CNGOT);

    if (rp_isbad (qu2pr_send ()))
	return (RP_NO);		  

    qu_end (OK);
    phs_end  (chan, RP_OK);

#ifdef DEBUG
    ll_log (logptr, LLOGFTR, "ch_prog: Normal Return!");
#endif
    return (OK);
}


err_abrt (code, f, a0, a1, a2)
    short code;
    char  *f, *a0, *a1, *a2;
{
    char buf [LINESIZE];

    qu_end (NOTOK);

#ifdef DEBUG
    if (rp_isbad (code)) {
	if (rp_gbval (code) == RP_BNO || logptr->ll_level >= LLOGTMP) {
	    /* don't worry about minor stuff */
	    sprintf (buf, "%s%s", "err [ ABEND (%s) ]\t", f);
	    ll_log (logptr, LLOGFAT, buf, rp_valstr (code), a0, a1, a2);
	    abort (code);
	}
    }
#endif DEBUG

    ll_close (logptr);           /* in case of cycling, close neatly   */
    exit (code);
}
le.h
pr_errmsg.o: ../../h/util.h
pr_errmsg.o: ../../h/mmdf.h
# DEPENDENCIES MUST END AT END OF FILE
# IF YOU PUT STUFF HERE IT WILL GO AWAY
# see make depend above
lyval.rp_line));
	} else {
	    replyval.rp_val = uu_wttend();
	    switch (replyval.rp_val) {
		    case RP_AOK:
		    cmmdf/src/prog/pr2mm_send.c   444      0     12       30704  3657737736  10774 /*
 *	MULTI-CHANNEL MEMO DISTRIBUTION FACILITY  (MMDF)
 *
 *	Department of Electrical Engineering
 *	University of Delaware
 *	Newark, Delaware  19711
 *
 *
 *	Program Channel (inbound): Pass message into MMDF
 *
 *
 *	P R 2 M M _ S E N D . C
 *	=======================
 *
 *	pr2mm_send [-M?] [-D] [-c <channel>] [-h|-v <host>]
 *						[-s <sender>] [address...]
 *
 *	specify -h if host in US order, -v (for Via:) if in UK order!
 *	specify -Mj if input is JNT mail file.
 *	-Ms is reserved for Batch-SMTP input format (implementation coming)
 *	(default mode: give recipients on command line)
 *
 *	J.B.D.Pardoe
 *	University of Cambridge Computer Laboratory
 *	October 1985
 *	
 */

#include <stdio.h>
#include <errno.h>
#ifdef V4_2BSD
#include <sys/wait.h>
#include <sys/file.h>
#endif V4_2BSD
#include <signal.h>
#include "util.h"
#include "mmdf.h"
#include "ch.h"
#include "ap.h"


#define EX_OK	0 /* everything successful */
#define EX_ADDR	1 /* bad addresses */
#define EX_MECH	2 /* more than just bad addresses */
#define EX_FAIL	3 /* the program failed to behave */

#define PR_COMM '#'
#define PR_ERR  '*'

/* 
 * Define  SAFEFORK  if  you  want  to  be ultra careful that no mail is
 * lost;  we try and notify the  sender  or  support  that  we  couldn't
 * deliver  some  mail,  but  if  we  can't then the mail will disappear
 * without trace.  Other problems such as  invalid  arguments  may  also
 * cause  this.  If  you  define  SAFEFORK the process doing the work is
 * monitored by another so  that  (as  long  as  mail  can  be  sent  to
 * support) some notification of the error is made.
 */
#define SAFEFORK 


#define TRY(OPERATION, WHAT) { \
    int rp  = OPERATION; \
    if (rp_isbad (rp)) terminate (WHAT, rp); \
}

#define invalid_address(ADR, RPLY) { \
    reportf ("address: %s\nproblem: %s\n", ADR, (RPLY)->rp_line); \
}


extern FILE *errmsg_file;
extern long msg_start;
extern char *mmdflogin, *supportaddr;
extern int errno;
extern char *ap_dmflip ();
extern int ap_outtype;


static char 
    *basic[]  = {"success", "partial", "temporary", "error"},
    *domain[] = {"syntax", "general", "transfer", "authentication", 
                 "mail", "file system", "<illegal>", "<illegal>"};


int prog_debug;
char *sender;

char prog_mode;



terminate (s, rp)
   char *s; int rp;
{
    printf ("%cfailed to %s\n%c  %s [%03o = %s: %s %o]\n", 
	PR_ERR, s, PR_ERR, rp_valstr (rp), rp & 0377,
	basic[rp_gbbit (rp)], domain[rp_gcbit (rp)], rp_gsbit (rp));
    mm_end (rp);
    exit (EX_MECH);
}	


/* *
 * monitoring of process which does the work
 */


#ifdef SAFEFORK

/*
 * monitor a forked process which does the work
 */

char *signame (sig)
    int sig;
{
    extern char *sys_siglist[];
    static char buff[24];

    if (sig >= 0 && sig < NSIG) {
	return (sys_siglist [sig]);
    } else {
	sprintf (buff, "unknown signal %d", sig);
	return (buff);
    }
}


main (argc, argv) 
    int argc; char **argv;
{
    char *divider = "-------------------------------------------------------";
    int child_pid, pp[2];
    char **argv_old = argv;

    mmdf_init (*argv);
    
    if (pipe (pp) != 0 || (child_pid = fork ()) == -1) {
	mopen (supportaddr, "Problems with `recvprog' program");
	mprintf ("Failed to start up mail reception program.\n");
	mprintf ("The arguments were:\n");
	while (argc-- != 0) mprintf ("  %s", *argv++);
	mprintf ("\n\nThe message follows\n\%s\n", divider);
	minclude (stdin);
	exit (mclose () == 0 ? EX_MECH : EX_FAIL);
    }
    
    if (child_pid == 0) {
	dup2 (pp[1], 1);
	dup2 (pp[1], 2);
	close (pp[0]);
	child (argc, argv);
    } else {
	int rc, stopped;
	int do_msg = 0;
#ifdef V4_2BSD
	union wait status;
#else
    	int status;
#endif V4_2BSD

	close (pp[1]);

	do {
	    rc = wait (&status);
	} while (rc != child_pid && (rc != -1 || errno == EINTR));

	if (rc != child_pid) {
	    int err = errno;	/* save errno in case open changes... */
	    mopen (supportaddr, "Problems with `recvprog' program");
	    mprintf ("Failed to collect child's status (pid %d, rc %d, \
error %d).\n\n", child_pid, rc, err);
	    do_msg = 1;
#ifdef WIFSTOPPED
	} else if ((stopped = WIFSTOPPED (status)) || WIFSIGNALED (status)) {
	    mopen (supportaddr, "Problem with `recvprog' program");
	    mprintf ("recvprog was %sed by a signal: %s\n\n",
		    (stopped? "stopp":"kill"), signame (status.w_termsig));
	    do_msg = 1;
	} else if (status.w_retcode != EX_OK && status.w_retcode != EX_ADDR) {
	    int mech = (status.w_retcode == EX_MECH);
#else
	} else if ((status>>8)&0xff != EX_OK && (status>>8)&0xff != EX_ADDR) {
	    int mech = ((status>>8)&0xff == EX_MECH);
#endif WIFSTOPPED
	    mopen (supportaddr, (mech? 
		"recvprog exited with EX_MECH":
		"recvprog exited with unexpected status"));
	    if (!mech) 
		mprintf ("recvprog exited with return/status 0x%x.\n\n", 
							status);
	    do_msg = 1;
	}

	if (do_msg) {
	    FILE *f;
	    mprintf ("The arguments were:\n");
	    while (argc-- != 0) mprintf ("  %s", *argv++);

	    mprintf ("\n\nThe program said the following:\n%s\n", divider);
	    f = fdopen (pp[0], "r");
	    if (f == NULL) 
		mprintf ("[FAILED TO OPEN PIPE]\n"); 
	    else 
		minclude (f);

	    mprintf ("\n%s\n\nThe message follows%s:",
		divider, (fseek (stdin, 0L, 0) < 0) ? 
				" (couldn't rewind the file)":"");
	    mprintf ("\n%s\n", divider);
	    minclude (stdin);
	    exit (mclose () == 0 ? EX_MECH : EX_FAIL);
	}

	if (strcmp (*(argv_old+1), "-D") == 0) {
	    char buff [512]; int n;
	    while ((n = read (pp[0], buff, 512)) > 0) write (1, buff, n);
	}
	exit (status.w_retcode);
    }
}
#endif SAFEFORK		


/* *
 * pass a message into MMDF
 */


#ifdef SAFEFORK
#define MAIN child
#else
#define MAIN main
#endif SAFEFORK


#define HT_NONE 0 /* no host specified */
#define HT_HOST 1 /* host specified by -h */
#define HT_VIA  2 /* host specified by -v */


MAIN (argc, argv)
    int argc; char **argv;
{
    char *channel, *host, *arg;
    struct rp_bufstruct rply; int rply_len;
    int rp, ex = EX_OK, n, hosttype = HT_NONE;
    char buf[BUFSIZ], subargs[32]; 

    errmsg_file = NULL;
    sender = 0;
    prog_mode = '\0';

#ifndef SAFEFORK
    mmdf_init (*argv);
#endif SAFEFORK

    TRY (mm_init (), "initialize MMDF");
    TRY (mm_sbinit (), "initialize submission");

    while (--argc > 0 && *(arg = *++argv) == '-') {
	char opt = *++arg;
	char **where = 0;
	switch (opt) {
	    case 'D': prog_debug = 1;   break;
	    case 'c': where = &channel; break;
	    case 's': where = &sender;  break;

	    case 'h': 
	    case 'v': 
		if (hosttype != HT_NONE) {
		    printf ("%chost specified twice\n", PR_ERR);
		    exit (EX_MECH);
		}
		hosttype = (opt == 'h' ? HT_HOST : HT_VIA);
		where = &host; break;

	    case 'M':
		prog_mode = *arg; break;

	    default:
		printf ("%cunknown argument -%c\n", PR_ERR, *arg);
		exit (EX_MECH);
	}
	if (where != 0) {
	    if (--argc <= 0) {
		printf ("%cno value for -%c\n", PR_ERR, *arg);
		exit (EX_MECH);
	    }
	    *where = *++argv;
	}
    }

    if (prog_mode == 'j' && argc > 0) {		/* JNT mail mode */
	printf ("%c-Mj and recipients", PR_ERR);
	exit (EX_MECH);
    } else if (prog_mode == 's' && argc > 0) {	/* Batch-SMTP mail mode */
	printf ("%c-Ms and recipients", PR_ERR);
	exit (EX_MECH);
    } else if (argc == 0) {
	printf ("%cno recipients\n", PR_ERR);
	exit (EX_MECH);
    }
	    
    strcpy (subargs, "tmlv");
    if (channel) {
    	Chan *curchan;

	if ((curchan = ch_nm2struct (channel)) == (Chan *) NOTOK)
	    err_abrt (RP_PARM, "unknown channel name '%s'", argv[0]);
    	ch_llinit(curchan);
	sprintf (buf, "%si%s*", subargs, channel);
	strcpy (subargs, buf);
    }
    if (hosttype != HT_NONE) {
	sprintf (buf, "%sh%s*", subargs, 
			    (hosttype == HT_VIA ? ap_dmflip (host) : host));
	strcpy (subargs, buf);
    }
    if (!sender) {
	strcat (subargs, "s");
    }

    if (prog_mode == 'j') {
	ex = JNTextract_recipients (&argc, &argv);
	if (ex != EX_OK) {
	    if (errmsg_file != NULL) {
		errmsg_send ();
	    }
	    exit (ex);
	}
    } else if (prog_mode == 's') {
    	/* Batch SMTP - not implemented yet */
    	printf ("%cBatch SMTP not supported yet", PR_ERR);
    	exit(EX_MECH);
    }

#ifdef DEBUG
    if (prog_debug) {
	printf ("%csubargs: %s", PR_COMM, subargs);
	printf ("; sender %s", sender ? sender : "(unspecified)");
	printf ("; %d recipient(s)\n", argc);
    }
#endif

    TRY (mm_winit ((char *) 0, subargs, sender), "initialize for message");
    TRY (mm_rrply (&rply, &rply_len), "get result of mm_winit");

    switch (rp_gbval (rply.rp_val))
    {			  /* was source acceptable?            */
	case RP_BNO:
    	case RP_BTNO:
	    terminate(rply.rp_line, rply.rp_val);
    }
#ifdef DEBUG
    if (prog_debug) {
	printf ("%csubmitting addresses\n", PR_COMM); fflush (stdout);
    }
#endif
    while (argc--) { 
	char *adr = *argv++;
#ifdef DEBUG
	if (prog_debug) {
	    printf ("%c  %s:\t", PR_COMM, adr); fflush (stdout);
	}
#endif DEBUG
	TRY (mm_wadr ((char *) 0, adr), "write address");
	TRY (mm_rrply (&rply, &rply_len), "get result of writing address");
#ifdef DEBUG
	if (prog_debug) {
	    printf ("[%o] %s\n", rply.rp_val & 0377, rply.rp_line); 
	    fflush (stdout);
	}
#endif DEBUG
	switch (rp = rp_gval (rply.rp_val)) {
	    case RP_AOK:
		break;
	    case RP_USER:
		invalid_address (adr, &rply);
		break;
	    default:
		{
		    char buff [128];
		    sprintf (buff, "understand reply to %s:\n%c%s",
						adr, PR_ERR, rply.rp_line);
		    terminate (buff, rp);
		}
	}
    }

    TRY (mm_waend (), "finish writing addresses");

    while (n = fread (buf, sizeof (char), BUFSIZ, stdin)) {
	TRY (mm_wtxt (buf, n), "write text");
    }

    TRY (mm_wtend (), "finish writing text");
    TRY (mm_rrply (&rply, &rply_len), "get result of submission");

    switch (rp = rp_gval (rply.rp_val)) {
	case RP_MOK:
	    break;

	case RP_NDEL:
	    if (errmsg_file == NULL) {
		printf ("%csubmit returned NDEL but no error message\n",
		    PR_ERR);
		exit (EX_MECH);
	    }
	    break;

	default:
	    if (errmsg_file != NULL) {
		printf ("%csending error message\n", PR_COMM);
		errmsg_send ();
	    }
	    terminate ("understand reply from submit", rp);
	    break;
    }

    mm_sbend ();
    mm_end (RP_OK);

    if (errmsg_file != NULL) {
	errmsg_send ();
	ex = EX_ADDR;
    }
    exit (ex);
	
}


/* *
 * reading of addresses from JNT mail file header
 *
 * (Strictly we should be using the MMDF parsing routines in this
 * code, but I'm fed up trying to get the buggers to work!!  This 
 * works more or less...!)
 */

JNTextract_recipients (argc, argv)
    int *argc; char ***argv;
{
    struct alist {
	struct alist *ad_next;
	char ad_addr[1]; /* well, as long as we need */
    } *alist = 0;
    int naddrs = 0;
    char **pp;

    rewindable_msg ();

    for (;;) {
	int ch;
	int lit  = 0; /* in a domain literal */
	int lay  = 0; /* just had layout */
	enum { D_NO, D_ADR, D_END } done = D_NO;
	char buff[ADDRSIZE];
	register char *p;
	struct alist *al;

	p = &buff[0];

	while (done == D_NO) {
	    enum { T_CHAR, T_SEP, T_LAY } type = T_CHAR; /* char type */
	    switch (ch = getchar ()) {
		case '\\' :
		    *p++ = '\\'; ch = getchar (); break;
		case '[' :
		    lit = 1; break;
		case ']' :
		    lit = 0; break;
		case ',' :
		    if (!lit) type = T_SEP; break;
		case '\r' :
		    ch = getchar ();
		    if (ch == '\n') {
			type = T_LAY;
		    } else {
			ungetc (ch, stdin);
			ch = '\r';
		    }
		    break;
		case '\n' :
		    type =T_LAY; break;
		case EOF :
		    reportf ("%cJNT file ended prematurely\n", PR_COMM);
		    return (EX_MECH); 
		default :
		    break;
	    }
	    
	    switch (type) {
		case T_SEP :
		    done = D_ADR; break;
		case T_CHAR :
		    *p++ = ch;
		    lay = 0; 
		    break;
		case T_LAY :
		    if (lay) done = D_END; else lay = 1;
		    break;
	    }
	}

	*p = '\0';
	al = (struct alist *) malloc (
	    sizeof (*al) - sizeof ((*al).ad_addr) + 
	    strlen (buff) + 1);
	al->ad_next = alist; alist = al;
	strcpy (al->ad_addr, buff);
	naddrs++;
#ifdef DEBUG
	if (prog_debug) printf ("%caddress %d: %s%s\n", 
		    PR_COMM, naddrs, buff, (done==D_END? " !":""));
#endif
	if (done == D_END) break;
    }

    msg_start = ftell (stdin);
    
    *argc = naddrs;
    *argv = pp = (char **) malloc (naddrs * sizeof (char *));

    while (naddrs-- > 0) {
	*pp++ = alist->ad_addr; alist = alist->ad_next;
    }
    return (EX_OK);
}

/* *
 * report an invalid address or other problem
 */

    
reportf (f, a0, a1, a2, a3, a4, a5, a6, a7)
    char *f;
{
#ifdef DEBUG
    if (prog_debug) {
	printf (f, a0, a1, a2, a3, a4, a5, a6, a7);
    }
#endif

    if (errmsg_file == NULL) errmsg_open ();
    fprintf (errmsg_file, f, a0, a1, a2, a3, a4, a5, a6, a7);
    fprintf (errmsg_file, "\n");
}

_line, rply.rp_val);
    }
#ifdef DEBUG
    if (prog_debug) mmdf/src/prog/pr_errmsg.c   444      0     12       11377  3657737740  10726 /*
 *	MULTI-CHANNEL MEMO DISTRIBUTION FACILITY  (MMDF)
 *
 *	Department of Electrical Engineering
 *	University of Delaware
 *	Newark, Delaware  19711
 *
 *
 *	Program Channel (inbound): Pass message into MMDF
 *
 *
 *	P R _ E R R M S G . C
 *	=====================
 *
 *	send message to report error
 *
 *	J.B.D.Pardoe
 *	University of Cambridge Computer Laboratory
 *	October 1985
 *	
 */


#include <stdio.h>
#include <sys/file.h>
#include "util.h"
#include "mmdf.h"


#define EX_OK	0 /* everything successful */
#define EX_ADDR	1 /* bad addresses */
#define EX_MECH	2 /* interaction with MMDF failed */

#define PR_COMM '#'
#define PR_ERR  '*'


FILE *errmsg_file;
long msg_start;

extern char *sender;
extern char *sitesignature, *mmdflogin, *supportaddr, *locname;
extern errno, prog_debug;

static char sender_buf[256];


errmsg_open () 
{
    extern long getpid ();
    char buf[80]; 
    
    rewindable_msg ();

    if (!sender) {
	/* find a return address: order of preference	*/
	/* is Resent_From > Sender > From		*/

	int prio = 0;
	long msg_pos = ftell (stdin);

	rewind_msg ();

	while (gets (buf) != NULL && buf[0] != '\0') {
	    int newprio; register char *p;

	    if (lexnequ (buf, "From:", 5)) {
		p = &buf[5]; newprio = 1;
	    } else if (lexnequ (buf, "Sender:", 7)) {
		p = &buf[7]; newprio = 2;
	    } else if (lexnequ (buf, "Resent-From:", 12)) {
		p = &buf[12]; newprio = 3;
	    } else {
		newprio = 0;
	    }
	    
	    if (newprio > prio) {
		while (*p == ' ' || *p == '\t') p++;
		strcpy (sender_buf, p);
		sender = sender_buf;
		prio = newprio;
	    }
	}
	fseek (stdin, msg_pos, 0);
    }

    sprintf (buf, "/tmp/rcvprg.eXXXXXX");
    mktemp(buf);
    errmsg_file = fopen (buf, "w+");
    if (errmsg_file == NULL) {
	printf ("%ccouldn't open errmsg\n", PR_ERR);
	exit (EX_MECH);
    }
    unlink (buf);
}


errmsg_send ()
{
    int rc;
#ifdef DEBUG
    if (prog_debug) {
	printf ("%csending error message to %s\n", PR_COMM,
				sender?sender:"MMDF support");
    }
#endif

    if (!sender) {
	mail_support ("(unknown sender)");
	return;
    }    

    mopen (sender, "Failed Mail");
    mprintf ("It was not possible to fully deliver ");
    mprintf ("your message\n(included below) for the ");
    mprintf ("reason(s) given below\n\n");
    minclude_errmsg ();
    rc = mclose ();

    if (rc != 0) {
#ifdef DEBUG
	if (prog_debug)
	    printf ("%cfailed: trying MMDF support\n", PR_ERR);
#endif DEBUG
	mail_support (sender);
    }
}


static mail_support (sender)
    char *sender;
{
    mopen (mmdflogin, "Unreturned Failed Mail");
    mprintf ("Unable to return message to %s\n\n", sender);
    minclude_errmsg ();
    if (mclose () != 0) {
	printf ("%cCouldn't mail %s about mail from %s\n",
	    PR_ERR, supportaddr, sender);
	exit (EX_MECH);
    }
}


rewindable_msg ()
{
    long getpid ();
    char buf[1024];
    int f, n;

    if (fseek (stdin, msg_start, 0) != -1) return; /* rewindable! */

    sprintf (buf, "/tmp/rcvprg.mXXXXXX");
    mktemp(buf);
#ifdef V4_2BSD
    if ((f = open (buf, O_RDWR|O_CREAT|O_EXCL, 0640)) < 0) {
#else
creat close open
#endif V4_2BSD
	printf ("%cfailed to open message file %s\n", PR_ERR, buf);
	exit (EX_MECH);
    }
    unlink (buf);

    while ((n = read (0, buf, 1024)) > 0) {
	if ((n = write (f, buf, n)) < 0) break;
    }
    if (n < 0) {
	printf ("%ci/o error on message\n", PR_ERR);
	exit (EX_MECH);
    }
    close (0); dup (f); close (f);
}


rewind_msg ()
{
    if (fseek (stdin, msg_start, 0) < 0) {
	printf ("%cfailed to rewind message: %d\n", PR_ERR, errno);
	exit (EX_MECH);
    }
    return (OK);
}	    


lexnequ (str1, str2, n)
    register char *str1, *str2;
{
    extern char chrcnv[];
    while (chrcnv[*str1] == chrcnv[*str2++])
	if (--n == 0 || *str1++ == 0) return (TRUE);
    return (FALSE);
}



/* *
 * Simple Message Sending
 */

static msuccess;

mopen (to, subj)
    char *to, *subj;
{
    char buff[64];
    sprintf (buff, "%s <%s@%s>", sitesignature, mmdflogin, locname);
    msuccess = (ml_1adr (NO, YES, buff, subj, to) == OK);
}


mprintf (f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9)
    char *f;
{
    char buff[128];
    if (!msuccess) return;
    sprintf (buff, f, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9);
    msuccess = (ml_txt (buff) == OK);
}


minclude (f)
    FILE *f;
{
    if (!msuccess) return;
    msuccess = (ml_file (f) == OK);
}


mclose ()
{
    if (msuccess) {
	msuccess = (ml_end (OK) == OK);
    } else {
	ml_end (NOTOK);
    }
    return (msuccess? 0 : -1);
}



minclude_errmsg ()
{
    FILE *msg;
    char *divider = 
	"\n-------------------------------------------------------\n\n";

    msuccess = (
	rewind (errmsg_file)		!= -1   &&
	ml_file (errmsg_file)		== OK   &&
	ml_txt (divider)		== OK   &&
	rewind_msg ()			!= -1   &&
	(msg = fdopen (0, "r"))		!= NULL &&
	ml_file (msg) 			== OK   &&
	ml_txt (divider)	 	== OK
    );
}
nder:", 7)) {
		p = &buf[7]; newprio = 2;
	    } else if (lexnequ (buf, "Resent-From:", 12)) {
		p = &buf[12]; newprio = 3;
	    } else {
		newprio = 0;
	    }
	    
	    if (newprio > prio) {
		while (*p == ' ' || *p == '\t') p++;
		strcpy (sender_buf, p);mmdf/src/prog/pr_wtmail.c   444      0     12       14436  3657737741  10724 /*
 *	MULTI-CHANNEL MEMO DISTRIBUTION FACILITY  (MMDF)
 *
 *	Department of Electrical Engineering
 *	University of Delaware
 *	Newark, Delaware  19711
 *
 *
 *	Program Channel: Take message and feed a request to a program
 *
 *
 *	P R _ W T M A I L . C
 *	=====================
 *
 *	?
 *
 *	J.B.D.Pardoe
 *	University of Cambridge Computer Laboratory
 *	October 1985
 *	
 *	based on the UUCP channel by Doug Kingston (US Army Ballistics 
 *	Research Lab, Aberdeen, Maryland: <dpk@brl>)
 *
 */

#include "util.h"
#include "mmdf.h"
#include "ch.h"
#include "ap.h"

extern struct ll_struct *logptr;
extern Chan *chan;
extern int errno;
extern int pipebroken;
extern char *locdomain, *locname;

extern int ap_outtype;
extern AP_ptr ap_s2tree ();

/*
 * local variables
 */

static FILE *pipefd;
static char hostadr[ADDRSIZE];

pr_wtadr (host, adr, from)
    char *host, *adr, *from;
{
    char        *index (), *rindex ();
    FILE        *popen();
    char        *confstr;
    char        command[LINESIZE];
    char	local[ADDRSIZE];
    AP_ptr      ap, ap_local, ap_domain, ap_route;
    int		ap_outtype_save;


    ap_outtype = chan->ch_apout;

#ifdef DEBUG
    printx (
	"pr_wtadr:\n  host: %s\n  addr: %s\n  from: %s\n  ap: 0%o\n",
		host, adr, from, ap_outtype);
#endif DEBUG


/*  get the host address from the channel table  */

    strcpy (hostadr, "");
    if (isstr (host)) {
	if (tb_k2val (chan->ch_table, TRUE, host, hostadr) != OK)
	    return (RP_USER);       /* No such host */
    }


/*  flip the host name if necessary  */

    if (ap_outtype & AP_BIG) { /* is this the correct test? (JBDP) */
	host = ap_dmflip (host);
#ifdef DEBUG
	printx ("  host flipped: %s\n", host);
#endif DEBUG
    }


/*  reformat the `from' address  */

    ap_outtype_save = ap_outtype;
    if ((ap = ap_s2tree (from)) == (AP_ptr) NOTOK) {
	ll_log (logptr, LLOGTMP, "Failure to parse address `%s'", from);
	return (RP_PARM);
    }
    ap_t2parts (ap, (AP_ptr *)0, (AP_ptr *)0, &ap_local, &ap_domain, &ap_route);
    from = ap_p2s ((AP_ptr)0, (AP_ptr)0, ap_local, ap_domain, ap_route);
    ap_outtype = ap_outtype_save;
#ifdef DEBUG
    printx ("  reformatted From: %s\n", from);
#endif DEBUG


/*  extract the `local' part of the address  */

    if ((ap = ap_s2tree (adr)) == (AP_ptr) NOTOK) {
	ll_log (logptr, LLOGTMP, "Failure to parse address `%s'", adr);
	return (RP_PARM);
    }
    ap_t2parts (ap,
	/* group  */ (AP_ptr *) 0,
	/* name   */ (AP_ptr *) 0,
	/* local  */ &ap_local,
	/* domain */ (AP_ptr *) 0,
	/* route  */ (AP_ptr *) 0);

/* who are we ? */

    sprintf (local, "%s.%s", 
	(isstr(chan->ch_lname) ? chan->ch_lname : locname),
	(isstr(chan->ch_ldomain) ? chan->ch_ldomain : locdomain));

/*  set the variables; generate the command and execute  */

    set_var ("from", from);
    set_var ("local", (ap_outtype & AP_BIG ? ap_dmflip (local) : local));
    set_var ("to", adr);
    set_var ("to.user", ap_local->ap_obvalue);
    set_var ("to.host", host);

    if (!isstr(chan->ch_confstr)) {
	confstr = hostadr;
    } else {
	confstr = chan->ch_confstr;
	set_var ("to.address", hostadr);
    }
    expand_vars (confstr, command);

    printx ("queuing mail for %s via %s\n\t[%s]:\n\t%s\n",
		adr, host, hostadr, command);

    if ((pipefd = popen (command, "w")) == NULL) {
	ll_log (logptr, LLOGFAT, "popen failed: %d", errno);
#ifdef DEBUG
	printx ("*** popen failed: %d\n", errno);
#endif DEBUG
	return (RP_AGN);
    }

#ifdef DEBUG
    ll_log (logptr, LLOGFTR, "Done pr_wtadr().");
#endif
    return (RP_OK);
}


/*
 * pr_txtcpy ()    --  copy the message body down pipefd
 * ============
 */
pr_txtcpy ()
{
    int  nread;
    char buffer [BUFSIZ];

#ifdef DEBUG
    ll_log (logptr, LLOGFTR, "pr_txtcpy ()");
#endif

    qu_rtinit (0L);             /* ready to read the text */

    nread = sizeof(buffer);
    while (!pipebroken && (rp_gval (qu_rtxt (buffer, &nread)) == RP_OK)) {
	if (fwrite (buffer, sizeof (*buffer), nread, pipefd) == 0) {
	    ll_log (logptr, LLOGFAT, "write on pipe error (errno %d)", errno);
	    ll_log (logptr, LLOGFAT, "pclose returned %d", pclose (pipefd));
	    return (RP_LIO);
	}
	nread = sizeof(buffer);
    }

    fflush (pipefd);
    if (pipebroken) {
	ll_log (logptr, LLOGFAT, "pipe broke -- probably bad host");
	pclose (pipefd);
	return (RP_LIO);
    }

    return (RP_MOK); /* got the text out */
}

/*
 * pr_wttend ()  --  Cleans up after the program
 */
pr_wttend ()
{
    return ((pclose (pipefd) != 0) ? RP_LIO : RP_MOK);
}


/*
 * variables 
 * =========
 */

struct var { char *var_name, *var_value} variables [] = {
    { "from",		(char *)0 },
    { "local",		(char *)0 },
    { "to",		(char *)0 },
    { "to.user",	(char *)0 },
    { "to.host",	(char *)0 },
    { "to.address",	(char *)0 },
    { (char *)0,	(char *)0 }
};


set_var (name, value)
    char *name, *value;
{
    register struct var *v;
    
    for (v = &variables[0]; v->var_name != (char *)0; v++) {
	if (strcmp (v->var_name, name) == 0) {
	    v->var_value = value;
	    return;
	}
    }
#ifdef DEBUG
    printx ("*** invalid variable $(%s)\n", name);
#endif DEBUG
    err_abrt (RP_MECH, "invalid variable $(%s)", name);
}


expand_vars (p, q)
    register char *p, *q;
{
    register char ch;

    while ((ch = *p++) != '\0') {
	if (ch == '$') {
	    char name [10];
	    register char *s;
	    register struct var *v;

	    if (*p++ != '(') {
#ifdef DEBUG
		printx ("*** missing `('\n");
#endif DEBUG
		err_abrt (RP_MECH, "missing `('");
	    }
	    s = &name[0];
	    while ((ch = *p++) != ')') {
		if (ch == '\0') {
#ifdef DEBUG
		    printx ("*** missing `)'\n");
#endif DEBUG
		    err_abrt (RP_MECH, "missing `)'");
		}
		*s++ = uptolow (ch);
	    }
	    *s = '\0';

	    s = (char *)0;
	    for (v = &variables[0]; v->var_name != (char *)0; v++) {
		if (strcmp (v->var_name, name) == 0) {
		    s = v->var_value;
		    if (s == (char *)0) {
#ifdef DEBUG
			printx ("*** variable $(%s) unset\n", name);
#endif
			err_abrt (RP_MECH, "variable $(%s) unset", name);
		    }
		    while (*q++ = *s++) ;
		    --q;
		    break;
		}
	    }
	    if (s == (char *)0) {
#ifdef DEBUG
		printx ("*** invalid variable $(%s)\n", name);
#endif DEBUG
		err_abrt (RP_MECH, "invalid variable $(%s)", name);
	    }

	} else {

	    *q++ = ch;

	}
    }
    *q = '\0';
}




/*
 * lowerfy ()  -  convert string to lower case
 */
lowerfy (strp)
    char *strp;
{
    while (*strp = uptolow (*strp)) strp++;
}

 address `%s'", adr);
	return (RP_PARM);
    }
    ap_t2parts (ap,
	/* group  */ (AP_ptr *) 0,
	/* name   */ (AP_ptr *) 0,
	/* local  */ &ap_local,
	/* domain */ (AP_ptr *) 0,
	/* route  */ (AP_ptr *) 0);

/* who are we ? */

mmdf/src/prog/qu2pr_send.c   444      0     12       11045  3657737743  11003 /*
 *	MULTI-CHANNEL MEMO DISTRIBUTION FACILITY  (MMDF)
 *
 *	Department of Electrical Engineering
 *	University of Delaware
 *	Newark, Delaware  19711
 *
 *
 *	Program Channel: Take message and feed a request to a program
 *
 *
 *	Q U 2 P R _ S E N D . C
 *	========================
 *
 *	?
 *
 *	J.B.D.Pardoe
 *	University of Cambridge Computer Laboratory
 *	October 1985
 *	
 *	based on the UUCP channel by Doug Kingston (US Army Ballistics 
 *	Research Lab, Aberdeen, Maryland: <dpk@brl>)
 *
 */

#include "util.h"
#include "mmdf.h"
#include "ch.h"
#include <signal.h>
#include "ap.h"
#include "phs.h"

extern	char	*strdup();
extern	char	*ap_p2s();
extern	char	*multcat();

extern struct ll_struct   *logptr;
extern char *qu_msgfile;          /* name of file containing msg text   */
extern Chan      *chan;  /* Who we are */


int pipebroken; /* set if SIGPIPE occurs */

LOCVAR struct rp_construct
	rp_aend =
{
    RP_OK, 'p', 'r', 'o', 'g', ' ', 'e', 'n', 'd', ' ', 'o', 'f', ' ', 'a',
    'd', 'd', 'r', ' ', 'l', 'i', 's', 't', '\0'
},
	rp_mok =
{
    RP_MOK, 'm', 'o', 'k', '\0'
},
	rp_noop =
{
    RP_NOOP, 's', 'u', 'b', '-', 'l', 'i', 's', ' ', 'n', 'o', 't', ' ',
    's', 'p', 'e', 'c', 'i', 'a', 'l', '\0'
},
	rp_err =
{
    RP_NO, 'u', 'n', 'k', 'n', 'o', 'w', 'n', ' ', 'e', 'r', 'r', 'o', 'r', '\0'
},
	rp_pipe =
{
    RP_NET, 'p', 'r', 'o', 'g', 'r', 'a', 'm', ' ', 'f', 'a', 'i', 'l', 'e', 'd', '\0'
},
	rp_bhost =
{
    RP_USER, 'b', 'a', 'd', ' ', 'h', 'o', 's', 't', ' ', 'n', 'a',
    'm', 'e', '\0'
};


/* 
 * qu2pr_send : overall management for batch of msgs
 * ==========
 */
qu2pr_send ()
{
    short rp;
    char  info[LINESIZE], sender[ADDRSIZE];

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "qu2pr_send ()");
#endif

    if (rp_isbad (rp = qu_pkinit ()))
	return (rp);

    /*
     *  AP_SAME == NO header munging.  We need to read the header
     *  ourselves and the munging just gets in the way.
     */
    while (rp_gval ((rp = qu_rinit (info, sender, chan->ch_apout))) != RP_DONE)
    {                             /* get initial info for new message   */
	if (rp_gval (rp) == RP_FIO)          /* Can't open message file */
		continue;
	else if (rp_gval (rp) != RP_OK)      /* Some other error */
		break;
	phs_note (chan, PHS_WRSTRT);

	if (rp_isbad (rp = qu2pr_each (sender))) return (rp);
	qu_rend ();
    }
    qu_rend ();

    if (rp_gval (rp) != RP_DONE)
    {
	ll_log (logptr, LLOGTMP, "not DONE (%s)", rp_valstr (rp));
	return (RP_RPLY);         /* catch protocol errors              */
    }

    qu_pkend ();
    phs_note (chan, PHS_WREND);

    return (rp);
}

/**/
/*
 * qu2pr_each : execute command for one address
 * ==========
 */

static qu2pr_each (sender)
    char *sender;
{
    RP_Buf    rply;
    short     rp;
    char      host [ADDRSIZE];
    char      adr  [ADDRSIZE];
    extern    brpipe ();
    int       (*oldsig) ();

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "qu2pr_each()");
#endif

    for (;;) /* ..all addresses */
    {                        
	rp = qu_radr (host, adr);
	if (rp_isbad (rp)) return (rp);

	if (rp_gval (rp) == RP_HOK) {
	    qu_wrply ((RP_Buf *) &rp_noop, sizeof rp_noop);
	    continue;
	} else if (rp_gval (rp) == RP_DONE) {
	    qu_wrply ((RP_Buf *) &rp_aend, sizeof rp_aend);
	    return (RP_OK); /* end of address list */
	}

	pipebroken = 0;
	oldsig = signal (SIGPIPE, brpipe);

	rply.rp_val = pr_wtadr (host, adr, sender);
	switch (rply.rp_val) {
	    case RP_AOK:
	    case RP_OK:
#ifdef DEBUG
		ll_log (logptr, LLOGFTR, "Calling txtcpy()\n");
#endif
		rply.rp_val = pr_txtcpy();
		break;

	    case RP_USER:
		ll_log (logptr, LLOGFAT, "host (%s) not in table", host);
		blt(&rp_bhost, (char *) &rply, sizeof rp_bhost);
		break;

	    default:
		ll_log (logptr,LLOGFAT,"unknown error (0%o)", rply.rp_val);
		blt(&rp_err, (char *) &rply, sizeof rp_err);
		rply.rp_val = RP_NO;
	}
	if (rply.rp_val != RP_MOK) {
	    qu_wrply (&rply, sizeof(rply.rp_val) + strlen(rply.rp_line));
	} else {
	    rply.rp_val = pr_wttend ();
	    switch (rply.rp_val) {
		case RP_AOK:
		case RP_OK:
		case RP_MOK:
		    qu_wrply (&rp_mok, sizeof rp_mok);    
		    break;

		case RP_USER:
		    ll_log (logptr, LLOGFAT, "host (%s) not in table", host);
		    qu_wrply (&rp_bhost, sizeof rp_bhost);
		    break;

		case RP_LIO:
		    ll_log (logptr,LLOGTMP,"prx pipe broke");
		    qu_wrply (&rp_pipe, sizeof rp_pipe);
		    break;

		default:
		    ll_log (logptr,LLOGFAT,"unknown error on close (0%o)", rply.rp_val);
		    qu_wrply (&rp_err, sizeof rp_err);
	    }
	}
	signal (SIGPIPE, oldsig);
    }
}


brpipe ()
{
    pipebroken = 1;
    signal (SIGPIPE, SIG_IGN);
}
atch of msgs
 * ==========
 */
qu2pr_send ()
{
    short rp;
    char  info[LINESIZE], sender[ADDRSIZE];

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "qu2pr_send ()");
#endif

    if (rp_isbad (rp = qu_pkinit ()))
	return (rp);

    /*
     *  AP_SAME == NO header munging.  We need to read the header
     *  ourselves and the munging just gets in the way.
     */
    while (rp_gval ((rp = qu_rinit (info, sender, chan->ch_apout))) != RP_DONE)
    {                          mmdf/src/prog/NOTES   444      0     12        3555  3620510560   7327 Date:    Tue, 7 Jan 86 15:22:04 GMT
From: Julian Pardoe <jbdp@cl.cam.ac.uk>
Subject: The `prog' channel

The  outgoing side (sendprog) consists of the following (fairly standard)
modules:
    ch_prog  qu2pr_send pr_wtmail

The  incoming  side (recvprog) consists of a main module pr2mm_send and a
module pr_errmsg which contains various bits  of  code  associated  with
the  reporting  of errors.  Its main failing is that is does no logging,
but with SAFEFORK #defined it does a good job at ensuring  failures  are
reported.

There  are  probably  several  ways  the channel could be improved.  Its
most obvious failing is that sendprog has no understanding  of  the  code
returned  by  the called program,  currently treating all non-zero codes
as temporary errors (if I've done it correctly).

Knowledge  of  these  could  most  easily  be  built  in  by  adding the
possibility of a configuration file,  specified by  having  `<'  as  the
first  letter  of  the configuration parameter.  This file would contain
the command line and further information.  I could imagine  one  reading
something like:

    # configuration file for the example channel
    command /bin/program -s $(from) $(to.local)

    #define return code values: code(s) MMDF_error descriptive_string
    error 0   OK   message delivered
    error 1   PARM invalid parameter
    error 2-9 AGN  service not available

    # norelay: implies that this program can deliver mail only
    # to its ultimate destination (often set when $(to.local) is
    # specified as this means we're throwing away all host names)
    norelay 


Julian Pardoe
-------------

University of Cambridge         Tel:     +44 223 352435 ext. 265
        Computer Laboratory     Arpa:    <@ucl-cs: jbdp@cl.cam.ac.uk>
Corn Exchange Street            Janet:   jbdp@UK.AC.Cam.CL
CAMBRIDGE, CB2 3QG              UUCP:    mcvax!ukc!cl-jenny!jbdp
Great Britain
with
the  reporting  of errors.  Its main failing is that is does no logging,
but with SAFEFORK #defined it does a good job at ensuring  failures  mmdf/src/prog/gen   555      0     12          57  3620510560   7145 make -f ../../Makefile.com -f Makefile.real $*

    norelay 


Julian Pardoe
-------------

University of Cambridge         Tel:     +44 223 352435 ext. 265
        Computer Laboratory     Arpa:    <@ucl-cs: jbdp@cl.cam.ac.uk>
Corn Exchange Street            Janet:   jbdp@UK.AC.Cam.CL
CAMBRIDGE, CB2 3QG              UUCP:    mcvax!ukc!cl-jenny!jbdp
Great Britain
with
the  reporting  of errors.  Its main failing is that is does no logging,
but with SAFEFORK #defined it does a good job at ensuring  failures  mmdf/src/smtp/   755      0     12           0  3671074632   6450 mmdf/src/smtp/sm_wtmail.c   444      0     12       36772  3671073266  10734 /*
 *     MULTI-CHANNEL MEMO DISTRIBUTION FACILITY  (MMDF)
 *
 *
 *     Copyright (C) 1979,1980,1981  University of Delaware
 *
 *     Department of Electrical Engineering
 *     University of Delaware
 *     Newark, Delaware  19711
 *
 *     Phone:  (302) 738-1163
 *
 *
 *     This program module was developed as part of the University
 *     of Delaware's Multi-Channel Memo Distribution Facility (MMDF).
 *
 *     Acquisition, use, and distribution of this module and its listings
 *     are subject restricted to the terms of a license agreement.
 *     Documents describing systems using this module must cite its source.
 *
 *     The above statements must be retained with all copies of this
 *     program and may not be removed without the consent of the
 *     University of Delaware.
 *     
 *
 *     version  -1    David H. Crocker    March   1979
 *     version   0    David H. Crocker    April   1980
 *     version  v7    David H. Crocker    May     1981
 *     version   1    David H. Crocker    October 1981
 *
 */
/*                  MAIL-COMMANDS FOR SMTP MAIL                      */

/*  Oct 82 Dave Crocker   derived from arpanet ftp/ncp channel
 *
 *      -------------------------------------------------
 *
 *  Feb 83 Doug Kingston  major rewrite, some fragments kept
 */

#include "util.h"
#include "mmdf.h"
#include "ch.h"
#include <signal.h>
#include "phs.h"
#include "ap.h"
#include "dm.h"
#include "smtp.h"

extern LLog     *logptr;
extern Chan     *chanptr;
extern char     *blt();
extern char     *strdup();
extern char     *strncpy ();

char    *sm_curname;

struct sm_rstruct sm_rp;            /* save last reply obtained           */
LOCVAR Chan     *sm_chptr;      /* structure for channel that we are  */
FILE    *sm_rfp, *sm_wfp;
LOCVAR char     sm_rnotext[] = "No reply text given";
LOCVAR  char    netobuf[BUFSIZ];
LOCVAR  char    netibuf[BUFSIZ];

/**/

sm_wfrom (sender)
char    *sender;
{
	char    linebuf[LINESIZE];

	sprintf (linebuf, "MAIL FROM:<%s>", sender);
	if (rp_isbad (sm_cmd (linebuf, SM_STIME)))
	    return (RP_DHST);

	switch( sm_rp.sm_rval ) {
	    case 250:
		break;          /* We're off and running! */

	    case 500:
	    case 501:
	    case 552:
		return( sm_rp.sm_rval = RP_PARM );

	    case 421:
	    case 450:
	    case 451:
	    case 452:
		return( sm_rp.sm_rval = RP_AGN);

	    default:
		return( sm_rp.sm_rval = RP_BHST);
	}
	return( RP_OK );
}

sm_wto (host, adr)         /* send one address spec to local     */
char    host[];                   /* "next" location part of address    */
char    adr[];                    /* rest of address                    */
{
    char linebuf[LINESIZE];

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "sm_wto(%s, %s)", host, adr);
#endif

    sprintf (linebuf, "RCPT TO:<%s>", adr);
    if (rp_isbad (sm_cmd (linebuf, SM_TTIME)))
	return (RP_DHST);

    switch (sm_rp.sm_rval)
    {
	case 250:
	case 251:
	    sm_rp.sm_rval = RP_AOK;
	    break;

	case 421:
	case 450:
	case 451:
	case 452:
	    sm_rp.sm_rval = RP_AGN;
	    break;

	case 550:
	case 551:
	case 552:
	case 553:
	case 554:               /* BOGUS: sendmail is out of spec! */
	    sm_rp.sm_rval = RP_USER;
	    break;

	case 500:
	case 501:
	    sm_rp.sm_rval = RP_PARM;
	    break;

	default:
	    sm_rp.sm_rval = RP_RPLY;
    }
    return (sm_rp.sm_rval);
}

sm_init (curchan)                 /* session initialization             */
Chan *curchan;                    /* name of channel                    */
{
#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "sm_init ()");
#endif
    sm_chptr = curchan;
    phs_note (sm_chptr, PHS_CNSTRT);
    return (RP_OK);               /* generally, a no-op                 */
}

/**/

LOCFUN
	sm_irdrply ()             /* get net reply & stuff into sm_rp   */
{
    static char sep[] = "; ";     /* for sticking multi-lines together  */
    short     len,
	    tmpreply,
	    retval;
    char    linebuf[LINESIZE];
    char    tmpmore;
    register char  *linestrt;     /* to bypass bad initial chars in buf */
    register short    i;
    register char   more;         /* are there continuation lines?      */

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "sm_irdrply ()");
#endif

newrply: 
    for (more = FALSE, sm_rp.sm_rgot = FALSE, sm_rp.sm_rlen = 0;
	    rp_isgood (retval = sm_rrec (linebuf, &len));)
    {                             /* 1st col in linebuf gets reply code */
	printx("<-(%s)\r\n", linebuf);
	fflush( stdout );

	for (linestrt = linebuf;  /* skip leading baddies, probably     */
		len > 0 &&        /*  from a lousy Multics              */
		    (!isascii ((char) *linestrt) ||
			!isdigit ((char) *linestrt));
		linestrt++, len--);

	tmpmore = FALSE;          /* start fresh                        */
	tmpreply = atoi (linestrt);
	blt (linestrt, sm_rp.sm_rstr, 3);       /* Grab reply code      */
	if ((len -= 3) > 0)
	{
	    linestrt += 3;
	    if (len > 0 && *linestrt == '-')
	    {
		tmpmore = TRUE;
		linestrt++;
		if (--len > 0)
		    for (; len > 0 && isspace (*linestrt); linestrt++, len--);
	    }
	}

	if (more)                 /* save reply value from 1st line     */
	{                         /* we at end of continued reply?      */
	    if (tmpreply != sm_rp.sm_rval || tmpmore)
		continue;
	    more = FALSE;         /* end of continuation                */
	}
	else                      /* not in continuation state          */
	{
	    sm_rp.sm_rval = tmpreply;
	    more = tmpmore;   /* more lines to follow?              */

	    if (len <= 0)
	    {                     /* fake it, if no text given          */
		blt (sm_rnotext, linestrt = linebuf,
		       (sizeof sm_rnotext) - 1);
		len = (sizeof sm_rnotext) - 1;
	    }
	}

	if ((i = min (len, (LINESIZE - 1) - sm_rp.sm_rlen)) > 0)
	{                         /* if room left, save the human text  */
	    blt (linestrt, &sm_rp.sm_rstr[sm_rp.sm_rlen], i);
	    sm_rp.sm_rlen += i;
	    if (more && sm_rp.sm_rlen < (LINESIZE - 4))
	    {                     /* put a separator between lines      */
		blt (sep, &(sm_rp.sm_rstr[sm_rp.sm_rlen]), (sizeof sep) - 1);
		sm_rp.sm_rlen += (sizeof sep) - 1;
	    }
	}
#ifdef DEBUG
	else
	    ll_log (logptr, LLOGFTR, "skipping");
#endif

	if (!more)
	{
#ifdef DEBUG
	    ll_log (logptr, LLOGBTR, "(%u)%s", sm_rp.sm_rval, sm_rp.sm_rstr);
#endif
	    if (sm_rp.sm_rval < 100)
		goto newrply;     /* skip info messages                 */

	    sm_rp.sm_rgot = TRUE;
	    return (RP_OK);
	}
    }
    return (retval);              /* error return                       */
}

sm_rpcpy (rp, len)           /* return arpanet command reply       */
RP_Buf *rp;      /* where to put it                    */
short    *len;                      /* its length                         */
{
    if( sm_rp.sm_rgot == FALSE )
	return( RP_RPLY );

    rp -> rp_val = sm_rp.sm_rval;
    *len = sm_rp.sm_rlen;
    blt (sm_rp.sm_rstr, rp -> rp_line, sm_rp.sm_rlen + 1);
    sm_rp.sm_rgot = FALSE;        /* flag as empty                      */

    return (RP_OK);
}
/**/

LOCFUN
	sm_rrec (linebuf, len)   /* read a reply record from net       */
char   *linebuf;                  /* where to stuff text                */
short    *len;                      /* where to stuff length              */
{
    extern int errno;
#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "sm_rrec ()");
#endif

    *len = 0;                     /* for clean logging if nothing read  */
    linebuf[0] = '\0';

    fgets (linebuf, LINESIZE, sm_rfp);
    *len = strlen (linebuf);

    if (ferror (sm_rfp) || feof (sm_rfp))
    {                             /* error or unexpected eof            */
	printx ("problem reading from net, ");
	fflush (stdout);
	ll_err(logptr, LLOGTMP, "netread:  ret=%d, fd=%d",
		*len, fileno (sm_rfp));
	sm_nclose (NOTOK);         /* since it won't work anymore        */
	return (RP_BHST);
    }
    if (linebuf[*len - 1] != '\n')
    {
	ll_log (logptr, LLOGTMP, "net input overflow");
	while (getc (sm_rfp) != '\n'
		&& !ferror (sm_rfp) && !feof (sm_rfp));
    }
    else
	if (linebuf[*len - 2] == '\r')
	    *len -= 1;            /* get rid of crlf or just lf         */

    linebuf[*len - 1] = '\0';
#ifdef DEBUG
    ll_log (logptr, LLOGFTR, "(%u)'%s'", *len, linebuf);
#endif
    return (RP_OK);
}
/**/

sm_cmd (cmd, time)              /* Send a command */
char    *cmd;
int     time;                   /* Max time for sending and getting reply */
{
    short     retval;

    ll_log (logptr, LLOGPTR, "sm_cmd (%s)", cmd);

    printx("->(%s)\r\n", cmd);
    fflush( stdout );

    if (setjmp(timerest)) {
	ll_log (logptr, LLOGGEN, "sm_cmd(): host died?");
	sm_nclose (NOTOK);
	return (sm_rp.sm_rval = RP_DHST);
    }
    s_alarm( (unsigned) time );
    fwrite (cmd, sizeof (char), strlen(cmd), sm_wfp);
    fputs ("\r\n", sm_wfp);
    fflush (sm_wfp);

    if (ferror (sm_wfp))
    {
	s_alarm ( 0 );
	ll_log (logptr, LLOGGEN, "sm_cmd(): host died?");
	sm_nclose (NOTOK);
	return (sm_rp.sm_rval = RP_DHST);
    }

    if (rp_isbad (retval = sm_irdrply ())) {
	s_alarm( 0 );
	return( sm_rp.sm_rval = retval );
    }
    s_alarm( 0 );
    return (RP_OK);
}
/**/

sm_wstm (buf, len)            /* write some message text out        */
char    *buf;                 /* what to write                      */
register int    len;              /* how long it is                     */
{
    static char lastchar = 0;
    short     retval;
    register char  *bufptr;
    register char   newline;

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "sm_wstm () (%u)'%s'", len, buf ? buf : "");
#endif

    if (buf == 0 && len == 0)
    {                             /* end of text                        */
	if (lastchar != '\n')     /* make sure it ends cleanly          */
	    fputs ("\r\n", sm_wfp);
	if (ferror (sm_wfp))
	    return (RP_DHST);
	lastchar = 0;             /* reset for next message             */
	retval = RP_OK;
    }
    else
    {
	newline = (lastchar == '\n') ? TRUE : FALSE;
	for (bufptr = buf; len--; bufptr++)
	{
	    switch (*bufptr)      /* cycle through the buffer           */
	    {
		case '\n':        /* Telnet requires crlf               */
		    newline = TRUE;
		    putc ('\r', sm_wfp);
		    break;

		case '.':         /* Insert extra period at beginning   */
		    if (newline)
			putc ('.', sm_wfp);
				  /* DROP ON THROUGH                    */
		default: 
		    newline = FALSE;
	    }
	    putc ((lastchar = *bufptr), sm_wfp);
	    if (ferror (sm_wfp))
		return (RP_DHST);
				  /* finally send the data character    */
	}
	retval = ferror(sm_wfp) ? RP_DHST : RP_OK;
    }

    return (retval);
}

/**/

union Haddru {
	long hnum;
	char hbyte[4];
};

sm_hostid (hostnam, addr, first)  /* addresses to try if sending to hostnam */
char    *hostnam;                 /* name of host */
union Haddru *addr;
int	first;			  /* first try on this name ?*/
{
    int     argc;
    register long    n;
    char   *argv[20];
    char    numstr[50];
    int     rval;
    int     tlookup=1;		/* used table lookup? */

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "sm_gethostid (%s)", hostnam);
#endif

    for (;;) 
    {
	/* look for [x.x.x.x] format -- don't need table lookup */

	if (hostnam[0] == '[')
	{
	    tlookup = 0;
	    (void) strncpy(numstr,hostnam,sizeof(numstr)-1);
	}
	else if ((rval=tb_k2val(sm_chptr->ch_table,first,hostnam,numstr))!=OK)
	{
	    /* No such host */
	    if (first)
		ll_log (logptr, LLOGTMP, "channel '%s' unknown host '%s'",
			sm_chptr -> ch_name, hostnam);

	    if (rval == MAYBE)
		return(RP_NS);

	    return(RP_BHST);
	}

	ll_log ( logptr, LLOGFTR, "Trying to break down %s", numstr) ;

	/* watch out for quoted and bracketed strings */
	argc = cstr2arg ((numstr[0]=='"' || numstr[0]=='[') ? &numstr[1]:numstr,
		20, argv, '.');

	ll_log ( logptr, LLOGFTR, "%d fields, '%s'", argc, argv[0] );


	switch (argc)               /* what form is hostnum in?             */
	{
	    case 4:                 /* dot-separated                        */
		n = atoi (argv[0]);
		n = (n<<8) | atoi (argv[1]);
		n = (n<<8) | atoi (argv[2]);
		addr->hnum = (n<<8) | atoi (argv[3]);
		return(RP_OK);
		break;

	    default:
		ll_log (logptr, LLOGTMP,
				"channel '%s' host %s' has bad address format",
				sm_chptr -> ch_name, hostnam);
		if (!tlookup)
		{
		    /* bad hostname in brackets */
		    return(RP_NO);
		}
		break;
	}
    } /* end for */
    
    /* NOTREACHED */
}

sm_nopen( hostnam )
char    *hostnam;
{
    Pip     fds;
    short   retval;
    char    linebuf[LINESIZE];
    union   Haddru haddr;
    unsigned atime;
    int     first;
    int	    rval;

    ll_log (logptr, LLOGPTR, "[ %s ]", hostnam);

    printx ("trying...\n");
    fflush (stdout);
    first = 1;
    atime = SM_ATIME;

    /* keep coming here until connected or no more addresses */
retry:

    if ((rval = sm_hostid(hostnam, &haddr, first)) != RP_OK)
    {
	switch (rval)
	{
	    case RP_NS:
		printx("\tno answer from nameserver\n");
		break;

	    case RP_NO:
		printx("\tbad hostname format\n");
		break;

	    default:
		/* some non-fatal error */
		rval = RP_BHST;
		break;
	}
	fflush(stdout);
	return(rval);
    }

    if (first)
	first = 0;

#ifdef DEBUG
    ll_log (logptr, LLOGFTR, "trying %u.%u.%u.%u",
		      haddr.hbyte[3]&0xff, haddr.hbyte[2]&0xff,
		      haddr.hbyte[1]&0xff, haddr.hbyte[0]&0xff);
#endif

    /* tell them who we are trying */
    printx("\tconnecting to [%u.%u.%u.%u]...",
			haddr.hbyte[3]&0xff, haddr.hbyte[2]&0xff,
			haddr.hbyte[1]&0xff, haddr.hbyte[0]&0xff);
    fflush(stdout);

    /* SMTP is on socket 25 */
    retval = tc_uicp (haddr.hnum, 25L, SM_OTIME, &fds);

    if (retval != RP_OK)
    {
	/* common event, so LLOGGEN (not TMP) */
        if (retval == RP_TIME) {
            ll_err (logptr, LLOGGEN, "%s (%8lx) open timeout", hostnam, haddr.hnum);
            printx (" timeout...\n");
        } else {
            ll_err (logptr, LLOGGEN, "%s (%8lx) no open", hostnam, haddr.hnum);
            printx (" can't...\n");
        }
	fflush (stdout);
	goto retry;	/* can't reach -- try someone else */
    }
#ifdef DEBUG
    else
	ll_log (logptr, LLOGFTR,
		    "fdr = %d,fdw = %d", fds.pip.prd, fds.pip.pwrt);
#endif

    sm_curname = strdup(hostnam);
    phs_note (sm_chptr, PHS_CNGOT);
    if ((sm_rfp = fdopen (fds.pip.prd, "r")) == NULL ||
	(sm_wfp = fdopen (fds.pip.pwrt, "w")) == NULL) {
	printx (" can't fdopen!\n");
	fflush(stdout);
	return (RP_LIO);	/* new address won't fix this problem */
    }
    printx (" open.\n");
    fflush (stdout);

    setbuf (sm_wfp, netobuf);
    setbuf (sm_rfp, netibuf);

    if (setjmp(timerest)) {
	sm_nclose (NOTOK);
	goto retry;		/* too slow, try someone else */
    }
    s_alarm (atime);

    atime -= SM_ATINC;
    if (atime < SM_ATMIN)
	atime = SM_ATMIN;

    if (rp_isbad (retval = sm_irdrply ())) {
	s_alarm (0);
	sm_nclose (NOTOK);
	goto retry;		/* problem reading -- try someone else */
    }
    s_alarm (0);

    if( sm_rp.sm_rval != 220 )
    {
	sm_nclose (NOTOK);
	goto retry;
    }

    if (sm_chptr -> ch_confstr)
	sprintf (linebuf, "HELO %s", sm_chptr -> ch_confstr);
    else
	sprintf (linebuf, "HELO %s.%s", sm_chptr -> ch_lname,
				    sm_chptr -> ch_ldomain);
    if (rp_isbad (sm_cmd( linebuf, SM_HTIME )) || sm_rp.sm_rval != 250 ) {
	sm_nclose (NOTOK);
	goto retry;		/* try more intelligent host? */
    }
    return (RP_OK);
}
/**/

sm_nclose (type)                /* end current connection             */
short     type;                 /* clean or dirty ending              */
{
	if (type == OK) {
		sm_cmd ("QUIT", SM_QTIME);
	} else {
		printx ("\r\nDropping connection\r\n");
		fflush (stdout);
	}
	if (sm_curname)
		free(sm_curname);
	sm_curname = 0;
	if (setjmp(timerest)) {
		return;
	}
	s_alarm (15);
	if (sm_rfp != NULL)
		fclose (sm_rfp);
	if (sm_wfp != NULL)
		fclose (sm_wfp);
	s_alarm (0);
	sm_rfp = sm_wfp = NULL;
	phs_note (sm_chptr, PHS_CNEND);
}
 fieldmmdf/src/smtp/Makefile.real   444      0     12        6403  3657737750  11133 #
#   smtp:    ArpaNet-based channel transmission
#
MODULES = 	ch_smtp qu2sm_send sm_wtmail tcp.${SYSTEM} \
		smtpsrvr smtpd.${SYSTEM}

real-default:	smtp smtpd smtpsrvr

install: inst-smtp inst-smtpd inst-smtpsrvr

lint:	l-smtp l-smtpd l-smtpsrvr

inst-smtp:	$(CHANDIR)/smtp
$(CHANDIR)/smtp:	xsmtp
	cp xsmtp $(CHANDIR)/smtp
	-$(CHOWN) $(MMDFLOGIN) $(CHANDIR)/smtp
	-chmod 0$(PGMPROT) $(CHANDIR)/smtp
	-@ls -ls $(CHANDIR)/smtp
	-@echo "smtp channel installed normally"; echo ""

smtp: xsmtp
xsmtp:	ch_smtp.o qu2sm_send.o sm_wtmail.o tcp.${SYSTEM}.o $(MMDFLIBS)
	$(CC) $(LDFLAGS) -o $@ \
		ch_smtp.o qu2sm_send.o sm_wtmail.o tcp.${SYSTEM}.o \
		$(MMDFLIBS) $(SYSLIBS)

l-smtp:
	$(LINT) $(LFLAGS) $(TCPINCL) \
		ch_smtp.c qu2sm_send.c sm_wtmail.c tcp.${SYSTEM}.c $(LLIBS)

inst-smtpsrvr: $(CHANDIR)/smtpsrvr

$(CHANDIR)/smtpsrvr:  xsmtpsrvr
	cp xsmtpsrvr $(CHANDIR)/smtpsrvr
	-$(CHOWN) $(MMDFLOGIN) $(CHANDIR)/smtpsrvr
	-chmod 0$(PGMPROT) $(CHANDIR)/smtpsrvr
	-@ls -ls $(CHANDIR)/smtpsrvr
	-@echo "smtp server installed normally"; echo " "


smtpsrvr : xsmtpsrvr
xsmtpsrvr: smtpsrvr.o $(MMDFLIBS) 
	$(CC) $(LDFLAGS) -o $@ \
		smtpsrvr.o $(MMDFLIBS) $(SYSLIBS)

l-smtpsrvr:
	$(LINT) $(LFLAGS) smtpsrvr.c $(LLIBS)


inst-smtpd: $(CHANDIR)/smtpd

$(CHANDIR)/smtpd:  xsmtpd
	cp xsmtpd $(CHANDIR)/smtpd
	-$(CHOWN) $(MMDFLOGIN) $(CHANDIR)/smtpd
	-chmod 0$(PGMPROT) $(CHANDIR)/smtpd
	-@ls -ls $(CHANDIR)/smtpd
	-@echo "smtp daemon installed normally"; echo " "
		
smtpd: xsmtpd
xsmtpd: smtpd.o $(MMDFLIBS)
	$(CC) $(LDFLAGS) -o $@ \
		smtpd.o $(MMDFLIBS) $(SYSLIBS)

smtpd.o:	smtpd.${SYSTEM}.c
	$(CC) $(CFLAGS) -c smtpd.${SYSTEM}.c
	mv smtpd.${SYSTEM}.o smtpd.o

l-smtpd:
	$(LINT) $(LFLAGS) smtpd.${SYSTEM}.c $(LLIBS)

clean:
	-rm -f x* *.o x.c makedep eddep make.out errs


# DO NOT DELETE THIS LINE -- make depend uses it
ch_smtp.o: ch_smtp.c
ch_smtp.o: ch_smtp.c
ch_smtp.o: ../../h/util.h
ch_smtp.o: ../../h/mmdf.h
ch_smtp.o: /usr/include/signal.h
ch_smtp.o: ../../h/phs.h
ch_smtp.o: ../../h/ch.h
qu2sm_send.o: qu2sm_send.c
qu2sm_send.o: ../../h/util.h
qu2sm_send.o: ../../h/mmdf.h
qu2sm_send.o: ../../h/ch.h
qu2sm_send.o: ../../h/ap.h
qu2sm_send.o: ../../h/smtp.h
sm_wtmail.o: sm_wtmail.c
sm_wtmail.o: ../../h/util.h
sm_wtmail.o: ../../h/mmdf.h
sm_wtmail.o: ../../h/ch.h
sm_wtmail.o: /usr/include/signal.h
sm_wtmail.o: ../../h/phs.h
sm_wtmail.o: ../../h/ap.h
sm_wtmail.o: ../../h/dm.h
sm_wtmail.o: ../../h/smtp.h
tcp.4.2.o: tcp.4.2.c
tcp.4.2.o: ../../h/util.h
tcp.4.2.o: ../../h/mmdf.h
tcp.4.2.o: /usr/include/sys/socket.h
tcp.4.2.o: /usr/include/netinet/in.h
tcp.4.2.o: /usr/include/netdb.h
smtpsrvr.o: smtpsrvr.c
smtpsrvr.o: ../../h/util.h
smtpsrvr.o: ../../h/mmdf.h
smtpsrvr.o: ../../h/ch.h
smtpsrvr.o: ../../h/ap.h
smtpsrvr.o: ../../h/phs.h
smtpsrvr.o: ../../h/smtp.h
smtpsrvr.o: /usr/include/stdio.h
smtpsrvr.o: /usr/include/signal.h
smtpsrvr.o: /usr/include/sys/stat.h
smtpsrvr.o: /usr/include/errno.h
smtpsrvr.o: ../../h/ns.h
smtpd.4.2.o: smtpd.4.2.c
smtpd.4.2.o: ../../h/util.h
smtpd.4.2.o: /usr/include/signal.h
smtpd.4.2.o: /usr/include/sys/socket.h
smtpd.4.2.o: /usr/include/netinet/in.h
smtpd.4.2.o: /usr/include/netdb.h
smtpd.4.2.o: /usr/include/sys/wait.h
smtpd.4.2.o: /usr/include/sys/ioctl.h
smtpd.4.2.o: ../../h/ns.h
# DEPENDENCIES MUST END AT END OF FILE
# IF YOU PUT STUFF HERE IT WILL GO AWAY
# see make depend above
e)
		free(sm_curname);
	sm_curname = 0;
	if (setjmp(timerest)) {
		return;
	}
	s_alarm (15);
	if (sm_rfp != NULL)
		fclose (sm_rfp);
	if (sm_wfp != NULL)
		fclose (sm_wfp);
	s_alarm (0);
	sm_rfp = sm_wfp = NULL;
	phs_note (sm_chptr, PHS_CNEND);
}
 fieldmmdf/src/smtp/ch_smtp.c   444      0     12        6422  3620510561  10326 #include "util.h"
#include "mmdf.h"
/*
 *     MULTI-CHANNEL MEMO DISTRIBUTION FACILITY  (MMDF)
 *     
 *
 *     Copyright (C) 1979,1980,1981  University of Delaware
 *     
 *     Department of Electrical Engineering
 *     University of Delaware
 *     Newark, Delaware  19711
 *
 *     Phone:  (302) 738-1163
 *     
 *     
 *     This program module was developed as part of the University
 *     of Delaware's Multi-Channel Memo Distribution Facility (MMDF).
 *     
 *     Acquisition, use, and distribution of this module and its listings
 *     are subject restricted to the terms of a license agreement.
 *     Documents describing systems using this module must cite its source.
 *
 *     The above statements must be retained with all copies of this
 *     program and may not be removed without the consent of the
 *     University of Delaware.
 *     
 *
 *     version  -1    David H. Crocker    March   1979
 *     version   0    David H. Crocker    April   1980
 *     version  v7    David H. Crocker    May     1981
 *     version   1    David H. Crocker    October 1981
 *
 */

/*                  ARPANET MAIL CHANNEL                                */

#include <signal.h>
#include "phs.h"
#include "ch.h"

extern char *dupfpath();
extern LLog *logptr;

Chan *chanptr;
char obuf[BUFSIZ];		/* Buffer for status printfs */

/*      MAIN MAIN MAIN MAIN MAIN MAIN MAIN MAIN MAIN MAIN MAIN MAIN     */

main (argc, argv)
int       argc;
char   *argv[];
{
    short retval;

    mmdf_init (argv[0]);
    setbuf( stdout, obuf );

#ifdef RUNALON
    logptr -> ll_fd = 1;
    ll_init (logptr);
#endif
    siginit ();
    signal (SIGINT, SIG_IGN);     /* always ignore interrupts             */

    if ((chanptr = ch_nm2struct (argv[0])) == (Chan *) NOTOK)
    {
	ll_log (logptr, LLOGTMP, "ch_smtp (%s) unknown channel", argv[0]);
	exit (RP_PARM);
    }
    retval = ch_smtp (argc, argv);
    ll_close (logptr);
    exit (retval);
}
/***************  (ch_) LOCAL MAIL DELIVERY  ************************ */

ch_smtp (argc, argv)              /* send to internet                   */
int       argc;
char   *argv[];
{
#ifndef RUNALON
    ch_llinit (chanptr);
#endif
#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "ch_smtp()");
#endif

    if (rp_isbad (qu_init (argc, argv)))
	return (RP_NO);		  /* problem setting-up for deliver     */
    
    if (rp_isbad (sm_init (chanptr)))
	return (RP_NO);

    phs_note (chanptr, PHS_WRSTRT);

    if (rp_isbad (qu2sm_send ()))
	return (RP_NO);		  /* send the batch of outgoing mail    */

    phs_note (chanptr, PHS_WREND);

    qu_end (OK);                  /* done with Deliver function         */

    return (RP_OK);		  /* NORMAL RETURN                      */
}

/**/

/* VARARGS2 */
err_abrt (code, fmt, b, c, d)     /* terminate ourself                  */
short     code;
char    fmt[],
        b[],
        c[],
        d[];
{
#ifdef DEBUG
    char linebuf[LINESIZE];
#endif

    qu_end (NOTOK);

#ifdef DEBUG
    if (rp_gbval (code) == RP_BNO || logptr -> ll_level >= LLOGBTR)
    {                         /* don't worry about minor stuff      */
	sprintf (linebuf, "%s%s", "err [ ABEND (%s) ]\t", fmt);
	ll_log (logptr, LLOGFAT, linebuf, rp_valstr (code), b, c, d);
	abort ();
    }
#endif
    ll_close (logptr);            /* in case of cycling, close neatly   */

    exit (code);
}
rname);
	sm_curname = 0;
	if (setjmp(timerest)) {
		return;
	}
	s_alarm (15);
	if (sm_rfp != NULL)
		fclose (sm_rfp);
	if (sm_wfp != NULL)
		fclose (sm_wfp);
	s_alarm (0);
	sm_rfp = sm_wfp = NULL;
	phs_note (sm_chptr, PHS_CNEND);
}
 fieldmmdf/src/smtp/smtpd.4.1.c   444      0     12        4735  3656410734  10340 /* Server SMTP daemon */

#include "sys/types.h"
#include "signal.h"
#include "netlib.h"
#include "con.h"
#include <stdio.h>

char *Channel;
char *Server;
int  Port;
int  Maxconnections = 4;
char *Network;
int numconnections = 0;

extern int errno;

struct con openparam;

main (argc, argv)
int argc;
char **argv;
{
	register  netfid;
	struct netstate statparam;
	char *us, *them;
	char *getuc ();
	int i;
	int pid;
	int alarmtrap() ;

	if (argc != 6)
	{
		printf ("Usage:  %s server channels port maxconns network\n", argv[0]);
		exit(1);
	}

	Server = argv[1];
	Channel = argv[2];
	Port = atoi(argv[3]);
	Maxconnections = atoi(argv[4]);
	Network = argv[5];

	signal (SIGALRM, alarmtrap);

	dup2 (1, 2);			/* make log file standard error */
	while (1)
	{
		openparam.c_mode = CONTCP;
		openparam.c_lport = Port;
		openparam.c_rbufs = 2;	/* Ask for 2K receive buffer */
		if (isbadhost(openparam.c_lcon = gethost(Network)))
		    {
		    printf ("SMTP daemon cannot find network '%s'", Network);
		    exit (1);
		    }

		while ((netfid = open ("/dev/net/net", &openparam)) < 0)
			{
			printf ("Return of %d from open, errno = %d\n", netfid, errno); 
			fflush (stdout);
			sleep (60);
			}

		ioctl (netfid, NETSETE, 0);

/*		ioctl (netfid, NETGETS, &statparam);

		printf ("Connection from %s\n", hostfmt(openparam.c_fcon,1));
*/
		if(( pid = fork()) < 0 ) {
/*			ll_log( "could not fork (%d)", errno);  */
			(void) close( netfid );
		} else if( pid == 0 ) {
			/*
			 *  Child
			 */

/*			us = getuc (thisname());	/* who are we? */
			us = getuc (Network);  /* daemon knows correct name */
			ioctl (netfid, NETGETS, &statparam);
			them = hostname (statparam.n_fcon); /* who are they? */
			dup2 (netfid, 0);
			dup2 (netfid, 1);
			close (netfid);
			execl (Server, Server, them, us, Channel, (char *)0);
			exit (1);
		}


		/*
		 *  Parent
		 */
		close (netfid);
		numconnections++;

		/*
		 *  This code collects ZOMBIES and implements load
		 *  limiting by staying in the do loop while the
		 *  Maxconnections active.
		 */
		if( numconnections > 1 || Maxconnections == 1 )
			do {
				alarm( 2 );
				if( wait( &i ) > 0 )
					numconnections--;
				alarm( 0 );
			} while( numconnections >= Maxconnections );

	}

}

/*
 * Uppercase a string in place. Return pointer to
 * null at end.
 */
char *
getuc(s)
    char *s;
{
    register char *p,
		  c;
    for (p = s; c = *p; p++)
    {
	if (c <= 'z' && c >= 'a')
	    *p -= ('a' - 'A');
    }
    return(s);
}

alarmtrap ()
{

	signal (SIGALRM, alarmtrap);
}
work);
		    exit (1);
		    }

		wmmdf/src/smtp/tcp.v7.c   444      0     12        2235  3643667702  10027 /*
 *		T C P _ U I C P . C
 *
 *	Open a TCP/SMTP connection using Berkeley 4.2 style networking.
 *
 *	Doug Kingston, 3 Jan 83 at BRL.
 */
#include "util.h"		/* includes <sys/types.h>, and others */
#include "mmdf.h"
#include <NET/longid.h>
#include <NET/net_types.h>
#include <NET/socket.h>
#include <NET/in.h>

extern LLog *logptr;
extern int errno;

tc_uicp (addr, sock, timeout, fds)
long addr;
long sock;	/* IGNORED */	/* absolute socket number       */
int timeout;			/* time to wait for open        */
Pip *fds;
{
	register int skt;
	struct sockaddr_in saddr;

	saddr.sin_family = AF_INET;
	saddr.sin_port = htons(IPPORT_SMTP);
	saddr.sin_addr.s_addr = htonl(addr);

	skt = socket( SOCK_STREAM, 0, (struct sockaddr_in *) 0, SO_DONTLINGER );
	if( skt < 0 ) {
		ll_log( logptr, LLOGFST, "Can't get socket (%d)", errno);
		return( RP_LIO );
	}

	if (setjmp(timerest)) {
	    close(skt);
	    return ( RP_TIME );
	}

	s_alarm( timeout );
	if( connect(skt, &saddr) < 0 ) {
		close( skt );
		s_alarm( 0 );
		ll_log( logptr, LLOGFST, "Can't get connection (%d)", errno);
		return( RP_DHST );
	}
	s_alarm( 0 );
	fds -> pip.prd = skt;
	fds -> pip.pwrt = dup(skt);
	return (RP_OK);
}
 (60);
			}

		ioctl (netfid, NETSETE, 0);

/*		ioctl (netfid, NETGETS, &statparam);

		printf ("Connection from %s\n", hostfmt(openparam.c_fcon,1));
*/
		if(( pid = fork()) < 0 ) {
/*			ll_log( "could not fork (%d)", errno);  */
			(void) close( netfid );
		} else if( pid == 0 ) {
			/*
			 *  Child
			 */

/*			us = getuc (thisname());	/* who are we? mmdf/src/smtp/smtpsrvr.c   444      0     12       44623  3657737753  10646 /*
 *                      S M T P S R V R . C
 *
 *              SMTP Mail Server for MMDF under 4.2bsd
 *
 * Eric Shienbrood (BBN) 3 Apr 81 - SMTP server, based on BBN FTP server
 * Modified to talk to MMDF Jun 82 by Donn Neuhengen.
 * Doug Kingston, BRL: Took a machete to large chunks of unnecessary code.
 *      Reimplemented the interface to MMDF and used 4.2BSD style networking.
 *      Usage:  smtpsrvr <them> <us> <channel>
 */

#include "util.h"
#include "mmdf.h"
#include "ch.h"
#include "ap.h"
#include "phs.h"
#include "smtp.h"
#include <stdio.h>
#include <signal.h>
#include <sys/stat.h>
#include <errno.h>

#include "ns.h"

#define BYTESIZE        8

/* mmdf configuration variables */
extern LLog *logptr;
extern char *supportaddr;
extern int errno;               /* Has Unix error codes */

extern char *getline();
extern char *index();
extern char *rindex();
extern char *malloc();
extern char *strdup();

char *progname, *us, *them, *channel;   /* Arguments to program */
char *sender = 0;                       /* address of mail sender */
Chan *chanptr;                          /* pointer to incoming channel */

#define BUFL 600                /* length of buf */
char    buf[BUFL];              /* general usage */
char    netbuf[BUFL];           /* the place that has the valid characters */
int     netcount = 0;           /* number of valid characters in netbuf */
char    *netptr  = netbuf;      /* next character to come out of netbuf */
char    *arg;                   /* zero if no argument - pts to comm param */
int     dont_mung;              /* used by getline() to limit munging */
int     numrecipients = 0;      /* number of valid recipients accepted */
int     stricked;               /* force rejection of non validated hosts */
#ifdef NODOMLIT
int	themknown=TRUE;		/* do we have symbolic name for them? */
#endif NODOMLIT
char    *addrfix();

/* character defines */
#define CR      '\r'    /* carriage return */
#define LF      '\n'    /* line feed */
#define CNULL   '\0'    /* null */


/****************************************************************
 *                                                              *
 *      C O M M A N D   D I S P A T C H   T A B L E             *
 *                                                              *
 ****************************************************************/

int helo(), mail(), quit(), help(), rcpt(), confirm();
int data(), rset(), reject();

struct comarr           /* format of the command table */
{
	char *cmdname;          /* ascii name */
	int (*cmdfunc)();       /* command procedure to call */
} commands[] = {
	"helo", helo,           "noop", confirm,
	"mail", mail,           "data", data,
	"rcpt", rcpt,           "help", help,
	"quit", quit,           "rset", rset,
	NULL, NULL
};


/*
 *              M A I N
 *
 *      Takes commands from the assumed network connection (file desc 0)
 *      under the assumption that they follow the ARPA network mail
 *      transfer protocol RFC 788 and appropriate modifications.
 *      Command responses are returned via file desc 1.
 *
 *      There is a small daemon waiting for connections to be
 *      satisfied on socket 25 from any host.  As connections
 *      are completed by the ncpdaemon, the returned file descriptor
 *      is setup as the standard input (file desc 0) and standard
 *      output (file desc 1) and this program is executed to
 *      deal with each specific connection.  This allows multiple
 *      server connections, and minimizes the system overhead
 *      when connections are not in progress.  File descriptors
 *      zero and one are used to ease production debugging and
 *      to allow the forking of other relavent Unix programs
 *      with comparative ease.
 *
 *              while commands can be gotten
 *                      find command procedure
 *                              call command procedure
 *                              get next command
 *                      command not found
 *
 */
main (argc, argv)
int argc;
char **argv;
{
	register struct comarr *comp;
	char    replybuf[128];
	char *i;
	long atime;
	Chan *curchan;
	char    tmp_buf[LINESIZE];
	char    tmpstr[LINESIZE];
	char    *Ags[20];
	int     n, Agc;

	progname = argv[0];
	mmdf_init( progname );

	if (argc != 4){
		ll_log( logptr, LLOGFAT, "wrong number of args!" );
		exit(NOTOK);
	}

	/* force rejection of unknown hosts */
	if (*progname == 'r')
		stricked++;

	/*
	 * first look up address of sender. Used to be in server but
	 * now must be in this routine so that we can send a reject
	 * reply.
	 */
	them = argv[1];
	us = argv[2];
				/* A numeric address - don't like it */
	if(*them >= '0' && *them <= '9')
	    if ( stricked){
		ll_log (logptr, LLOGTMP, "smtpsrvr can't lookup '%s'", them);
		sprintf(replybuf,
		       "421 %s: Cannot resolve your address. '%s'\r\n",us,them);
		netreply (replybuf);
		exit (-1);
	    }
	    else {   /* make into [x.x.x.x] format */
		them = strdup(them);
	        strcpy(tmpstr, them);  
		sprintf(them, "[%s]", tmpstr);
#ifdef NODOMLIT
		themknown = FALSE;
#endif NODOMLIT
	    }
	    
	/*
	 * found out who you are I might even believe you.
	 */

	/*
	 * the channel arg is now a comma seperated list of channels
	 * useful for multiple sources ( As on UCL's ether )
	 */
	strcpy (tmp_buf, argv[3]);
	Agc = str2arg (tmp_buf, 20, Ags, (char *)0);
	channel = Ags[Agc-1];
	for(chanptr = (Chan *)0, n = 0 ; n < Agc ; n++){
		if ( (curchan = ch_nm2struct(Ags[n])) == (Chan *) NOTOK) {
			ll_log (logptr, LLOGTMP, "smtpsrvr (%s) bad channel",
				Ags[n]);
			continue;
		}
		/*
		 * Is this a valid host for this channel ?
		 */
		switch(tb_k2val (curchan -> ch_table, TRUE, them, tmpstr)){
		default:        /* Either NOTOK or MAYBE */
			if ((n != (Agc-1)) || stricked)
				continue;
			/* fall through so we get some channel name to use */
		case OK:
			chanptr = curchan;
			channel = curchan -> ch_name;
			break;
		}
		break;
	}
	time(&atime);

	if (chanptr == (Chan *) 0){
		ll_log (logptr, LLOGTMP, "smtpsrvr (%s) no channel for host",
								    them);
		sprintf (replybuf,
			"421 %s: Your name, '%s', is unknown to us.\r\n",
			us, them);
		netreply (replybuf);
		exit (-1);
	}
	ch_llinit (chanptr);
	ll_log( logptr, LLOGGEN, "OPEN: %s %.19s (%s)",
			them, ctime(&atime), channel);
	phs_note(chanptr, PHS_RESTRT);

	mmdfstart();

	/* say we're listening */
	sprintf (replybuf, "220 %s Server SMTP (Complaints/bugs to:  %s)\r\n",
			us, supportaddr);
	netreply (replybuf);

nextcomm:
	while (i = getline())
	{
		if (i == (char *)NOTOK)         /* handle error ??? */
			byebye( 1 );

		/* find and call command procedure */
		comp = commands;
		while( comp->cmdname != NULL)   /* while there are names */
		{
		    if (strcmp(buf, comp->cmdname) == 0) /* a winner */
		    {
			(*comp->cmdfunc)();     /* call comm proc */
			goto nextcomm;          /* back for more */
		    }
		    comp++;             /* bump to next candidate */
		}
		netreply("500 Unknown or unimplemented command\r\n" );
		ll_log(logptr, LLOGBTR, "unknown command '%s'", buf);
	}
	byebye(0);
}

/*name:
	getline

function:
	get commands from the standard input terminated by <cr><lf>.
	afix a pointer( arg ) to any arguments passed.
	ignore carriage returns
	map UPPER case to lower case.
	manage the netptr and netcount variables

algorithm:
	while we havent received a line feed and buffer not full

		if netcount is zero or less
			get more data from net
				error: return 0
		check for delimiter character
			null terminate first string
			set arg pointer to next character
		check for carriage return
			ignore it
		if looking at command name
			convert upper case to lower case

	if command line (not mail)
		null terminate last token
	manage netptr

returns:
	0 for EOF
	-1 when an error occurs on network connection
	ptr to last character (null) in command line

globals:
	dont_mung
	netcount=
	netptr =
	buf=
*/

char *
getline()
{
	register char *inp;     /* input pointer in netbuf */
	register char *outp;    /* output pointer in buf */
	register int c;         /* temporary char */
	extern char *progname;

	inp = netptr;
	outp = buf;
	arg = 0;

	do
	{
		if( --netcount <= 0 )
		{
			if (setjmp(timerest)) {
				ll_log( logptr, LLOGTMP,
					"%s net input read error (%d)",
					them, errno);
				return((char *)NOTOK);
			}
			s_alarm (NTIMEOUT);
			netcount = read (0, netbuf, 512);
			s_alarm( 0 );
			if (netcount == 0)      /* EOF */
				return( 0 );
			if (netcount < 0) {     /* error */
				ll_log( logptr, LLOGTMP,
					"%s net input read error (%d)",
					them, errno);
				return((char *)NOTOK);
			}
			inp = netbuf;
		}
		c = *inp++ & 0377;

		if (c == '\r' ||        /* ignore CR */
		    c >= 0200)          /* or any telnet codes that */
			continue;       /*  try to sneak through */
		if (dont_mung == 0 && arg == NULL)
		{
			/* if char is a delim afix token */
			if (c == ' ' || c == ',')
			{
				c = CNULL;       /* make null term'ed */
				arg = outp + 1; /* set arg ptr */
			}
			else if (c >= 'A' && c <= 'Z')
			/* do case mapping (UPPER -> lower) */
				c += 'a' - 'A';
		}
		*outp++ = c;
	} while( c != '\n' && outp < &buf[BUFL] );

	if( dont_mung == 0 )
		*--outp = 0;            /* null term the last token */

	/* scan off blanks in argument */
	if (arg) {
		while (*arg == ' ')
			arg++;
		if (*arg == '\0')
			arg = 0;        /* if all blanks, no argument */
	}
	if (dont_mung == 0)
		ll_log( logptr, LLOGFTR, "'%s', '%s'", buf,
			arg == 0 ? "<noarg>" : arg );

	/* reset netptr for next trip in */
	netptr = inp;
	/* return success */
	return (outp);
}

/*
 *  Process the HELO command
 */
helo()
{
	char replybuf[128];

	if(arg == 0 || !lexequ(arg, them))
		sprintf(replybuf, "250 %s - you are a charlatan\r\n", us);
	else 
		sprintf (replybuf, "250 %s\r\n", us);
	netreply (replybuf);
}

/*
 *      mail
 *
 *      handle the MAIL command  ("MAIL from:<user@host>")
 */
mail()
{
	char    replybuf[128];
	char    info[128];
	struct rp_bufstruct thereply;
	int	len;
	AP_ptr  domain, route, mbox, themap, ap_sender;

	if (arg == 0 || *arg == 0) {
		netreply("501 No argument supplied\r\n");
		return;
	} else if( sender ) {
		netreply("503 MAIL command already accepted\r\n");
		return;
	} else if (!equal(arg, "from:", 5)) {
		netreply("501 No sender named\r\n");
		return;
	}

	/* Scan FROM: parts of arg */
	sender = index (arg, ':') + 1;
	sender = addrfix( sender );
	/*
	 * If the From part is not the same as where it came from
	 * then add on the extra part of the route.
	 */

#ifdef NODOMLIT
	if(themknown && ((ap_sender = ap_s2tree(sender)) != (AP_ptr)NOTOK)){
#else
	if ((ap_sender = ap_s2tree(sender)) != (AP_ptr)NOTOK){
#endif NODOMLIT
		/*
		 * this must be a bit of a sledge hammer approach ??
		 */
		ap_t2parts(ap_sender, (AP_ptr *)0, (AP_ptr *)0,
						&mbox, &domain, &route);
		themap = ap_new(APV_DOMN, them);
		if(ap_dmnormalize(themap, (Chan *)0) == MAYBE)
			goto tout;
		if(route != (AP_ptr)0){
			/*
			 * only normalize the bits that we need
			 */
			if(ap_dmnormalize(route, (Chan *)0) == MAYBE)
				goto tout;
			if(lexequ(themap->ap_obvalue, route->ap_obvalue))
				ap_free(themap);
			else {
				themap->ap_chain = route;
				route = themap;
			}
		}
		else if(domain != (AP_ptr)0) {
			if(ap_dmnormalize(domain, (Chan *)0) == MAYBE)
				goto tout;
			if(lexequ(themap->ap_obvalue, domain->ap_obvalue))
				ap_free(themap);
			else
				route = themap;
		}
		sender = ap_p2s((AP_ptr)0, (AP_ptr)0, mbox, domain, route);
		if(sender == (char *)MAYBE){    /* help !! */
	tout:;
			netreply("451 Nameserver timeout during parsing\r\n");
			return;
		}
		strcpy(arg, sender);
		free(sender);
		sender = arg;
	}

	/* Supply necessary flags, "tiCHANNEL" will be supplied by winit */
	if (*sender == NULL) {
		/* No return mail */
		sprintf( info, "mvqdh%s*k%d*", them, NS_NETTIME );
		sender = "Orphanage";           /* Placeholder */
	} else
		sprintf( info, "mvdh%s*k%d*", them, NS_NETTIME );

	if( rp_isbad( mm_winit(channel, info, sender))) {
		netreply("451 Temporary problem initializing\r\n");
		mm_end( NOTOK );
		mmdfstart();
	} else if( rp_isbad( mm_rrply( &thereply, &len ))) {
		netreply( "451 Temporary problem initializing\r\n" );
		mm_end( NOTOK );
		mmdfstart();
	} else if( rp_gbval( thereply.rp_val ) == RP_BNO) {
		sprintf (replybuf, "501 %s\r\n", thereply.rp_line);
		netreply (replybuf);
		mm_end( NOTOK );
		mmdfstart();
	} else if( rp_gbval( thereply.rp_val ) == RP_BTNO) {
		sprintf (replybuf, "451 %s\r\n", thereply.rp_line);
		netreply (replybuf);
		mm_end( NOTOK );
		mmdfstart();
	} else
		netreply("250 OK\r\n");
	numrecipients = 0;
}

/*
 *  Process the RCPT command  ("RCPT TO:<forward-path>")
 */
rcpt()
{
	register char *p;
	struct rp_bufstruct thereply;
	char    replybuf[128];
	int     len;

	/* parse destination arg */
	if( sender == (char *)0 ) {
		netreply("503 You must give a MAIL command first\r\n");
		return;
	} else if (arg == (char *)0 || !equal(arg, "to:", 3)) {
		netreply("501 No recipient named.\r\n");
		return;
	}
	p = index( arg, ':' ) + 1;
	p = addrfix( p );

	if (setjmp(timerest)) {
		netreply( "451 Mail system problem\r\n" );
		return;
	}
	s_alarm (DTIMEOUT);
	if( rp_isbad( mm_wadr( (char *)0, p ))) {
		if( rp_isbad( mm_rrply( &thereply, &len )))
			netreply( "451 Mail system problem\r\n" );
		else {
			sprintf (replybuf, "451 %s\r\n", thereply.rp_line);
			netreply (replybuf);
		}
	} else {
		if( rp_isbad( mm_rrply( &thereply, &len )))
			netreply("451 Mail system problem\r\n");
		else if( rp_gbval( thereply.rp_val ) == RP_BNO) {
			sprintf (replybuf, "550 %s\r\n", thereply.rp_line);
			netreply (replybuf);
		}
		else if( rp_gbval( thereply.rp_val ) == RP_BTNO) {
			sprintf (replybuf, "451 %s\r\n", thereply.rp_line);
			netreply (replybuf);
		}
		else {
			netreply("250 Recipient OK.\r\n");
			numrecipients++;
		}
	}
	s_alarm (0);
}

/*
 *      ADDRFIX()  --  This function takes the SMTP "path" and removes
 *      the leading and trailing "<>"'s which would make the address
 *      illegal to RFC822 mailers.  Note that although the spec states
 *      that the angle brackets are required, we will accept addresses
 *      without them.   (DPK@BRL, 4 Jan 83)
 */
char *
addrfix( addrp )
char *addrp;
{
	register char   *cp;

	if( cp = index( addrp, '<' )) {
		addrp = ++cp;
		if( cp = rindex( addrp, '>' ))
			*cp = 0;
	}
	compress (addrp, addrp);
#ifdef DEBUG
	ll_log( logptr, LLOGFTR, "addrfix(): '%s'", addrp );
#endif
	return( addrp );
}

/*
 *  Process the DATA command.  Send text to MMDF.
 */
data()
{
	register char *p, *bufptr;
	time_t  tyme;
	int     errflg, werrflg;
	struct rp_bufstruct thereply;
	int     len, msglen;

	errflg = werrflg = msglen = 0;
	if (numrecipients == 0) {
		netreply("503 No recipients have been specified.\r\n");
		return;
	}

	if (setjmp(timerest)) {
		netreply( "451 Mail system problem\r\n" );
		return;
	}
	s_alarm (DTIMEOUT);
	if( rp_isbad(mm_waend())) {
		netreply("451 Unknown mail system trouble.\r\n");
		return;
	}
	s_alarm (0);

	netreply ("354 Enter Mail, end by a line with only '.'\r\n");

	dont_mung = 1;      /* tell getline only to drop cr */
#ifdef DEBUG
	ll_log( logptr, LLOGFTR, "... body of message ..." );
#endif
	while (1) {             /* forever */
		if ((p = getline()) == 0) {
			p = "\n***Sender closed connection***\n";
			mm_wtxt( p , strlen(p) );
			errflg++;
			break;
		}
		if (p == (char *)NOTOK) {
			p = "\n***Error on net connection***\n";
			mm_wtxt( p , strlen(p) );
			if (!errflg++)
				ll_log(logptr, LLOGTMP,
					"netread error from host %s", them);
			break;
		}

		/* are we done? */
		if (buf[0] == '.')
			if (buf[1] == '\n')
				break;          /* yep */
			else
				bufptr = &buf[1];       /* skip leading . */
		else
			bufptr = &buf[0];
		/* If write error occurs, stop writing but keep reading. */
		if (!werrflg) {
			if (setjmp(timerest)) {
				netreply( "451 Mail system problem\r\n" );
				return;
			}
			s_alarm (DTIMEOUT);
			msglen += (len = p-bufptr);
			if( rp_isbad(mm_wtxt( bufptr, len ))) {
				werrflg++;
				ll_log( logptr, LLOGTMP, "error from submit");
			}
			s_alarm (0);
		}
	}
	dont_mung = 0;  /* set getline to normal operation */
#ifdef DEBUG
	ll_log( logptr, LLOGBTR, "Finished receiving text." );
#endif

	if (werrflg) {
		netreply("451-Mail trouble (write error to mailsystem)\r\n");
		netreply("451 Please try again later.\r\n");
		byebye( 1 );
	}
	if (errflg) {
	    time (&tyme);
	    byebye(1);
	}

	if (setjmp(timerest)) {
		netreply( "451 Mail system problem\r\n" );
		return;
	}
	s_alarm (DTIMEOUT);
	if( rp_isbad(mm_wtend()) || rp_isbad( mm_rrply( &thereply, &len)))
		netreply("451 Unknown mail trouble, try later\r\n");
	else if( rp_isgood(thereply.rp_val)) {
		sprintf (buf, "250 %s\r\n", thereply.rp_line);
		netreply (buf);
		phs_msg(chanptr, numrecipients, (long) msglen);
	}
	else if( rp_gbval(thereply.rp_val) == RP_BNO) {
		sprintf (buf, "554 %s\r\n", thereply.rp_line);
		netreply (buf);
	}
	else {
		sprintf (buf, "451 %s\r\n", thereply.rp_line);
		netreply (buf);
	}
	s_alarm (0);
	sender = (char *) 0;
	numrecipients = 0;
}

/*
 *  Process the RSET command
 */
rset()
{
	mm_end( NOTOK );
	sender = (char *)0;
	mmdfstart();
	confirm();
}

mmdfstart()
{
	if( rp_isbad( mm_init() ) || rp_isbad( mm_sbinit() )) {
		ll_log( logptr, LLOGFAT, "can't reinitialize mail system" );
		netreply("421 Server can't initialize mail system (mmdf)\r\n");
		byebye( 2 );
	}
	numrecipients = 0;
}

/*
 *  handle the QUIT command
 */
quit()
{
	time_t  timenow;

	time (&timenow);
	sprintf (buf, "221 %s says goodbye to %s at %.19s.\r\n",
		us, them, ctime(&timenow));
	netreply(buf);
	byebye( 0 );
}

byebye( retval )
int retval;
{
	if (retval == OK) {
		mm_sbend();
		phs_note(chanptr, PHS_REEND);
	}
	mm_end( retval == 0 ? OK : NOTOK );
	exit( retval );
}

/*
 *  Reply that the current command has been logged and noted
 */
confirm()
{
	netreply("250 OK\r\n");
}

/*
 *  Process the HELP command by giving a list of valid commands
 */
help()
{
	register i;
	register struct comarr *p;
	char    replybuf[128];

	netreply("214-The following commands are accepted:\r\n214-" );
	for(p = commands, i = 1; p->cmdname; p++, i++) {
		sprintf (replybuf, "%s%s", p->cmdname, ((i%10)?" ":"\r\n214-") );
		netreply (replybuf);
	}
	sprintf (replybuf, "\r\n214 Send complaints/bugs to:  %s\r\n", supportaddr);
	netreply (replybuf);
}

/*
 *  Send appropriate ascii responses over the network connection.
 */

netreply(string)
char *string;
{
	if (setjmp(timerest)) {
		byebye( 1 );
	}
	s_alarm (NTIMEOUT);
	if(write(1,string, strlen(string)) < 0){
		s_alarm( 0 );
		ll_log( logptr, LLOGFST,
			"(netreply) error (%d) in writing [%s] ...",
			errno, string);
		byebye( 1 );
	}
	s_alarm( 0 );
#ifdef DEBUG
	ll_log( logptr, LLOGFTR, "%s", string);
#endif DEBUG
}
numrecipients == 0) {
		netreply("503 No recipients have been specified.\r\n");
		return;
	}

	if (setjmp(timmmdf/src/smtp/smtpd.v7.c   444      0     12       10613  3622772021  10373 /*
 *		S M T P D . C
 *
 *	For 4.1a style networking on a PDP-11.
 */

#include <stdio.h>
#include <signal.h>
#include <sys/types.h>
#include <NET/longid.h>
#include <NET/net_types.h>
#include <NET/socket.h>
#include <NET/in.h>

extern	int errno;

struct sockaddr_in addr = { AF_INET };
struct sockaddr_in rmtaddr;	

char	*Smtpserver;		/* Actual smtp server process, full path */
char	*Channel;
int	Maxconnections = 4;	/* Maximum simultaneous connections */
int	numconnections = 0;	/* Number of currently active connections */
int	debug = 0;		/* If nonzero, give verbose output on stderr */
char	errbuf[BUFSIZ];		/* Logging will be line buffered */
char	programid[40];		/* identification string for log entries */

main (argc, argv)
int	argc;
char	**argv;
{
	register int	skt;
	int	pid;
	int	i;
	int	alarmtrap();
	char	*them;
	char	thishost[32];
	char	workarea[32];

	setbuf( stderr, errbuf );
	sprintf( programid, "smtpd(%5.5d): ", getpid());
	gethostname( thishost, sizeof(thishost));	/* current hostname */

	/* Parse args; args will override configuration file... */
	arginit( argc, argv );

	/* Alarms will be used to avoid getting trapped in a wait call */
	signal( SIGALRM, &alarmtrap );

	while (1)
	{
		/*
		 *  Create a socket to accept a SMTP connection on.
		 *  The doloop is due to some sort of IPC bug in 4.1a.
		 */
		if (debug) log("opening socket...");
		i = 60;
		addr.sin_port = htons(IPPORT_SMTP);   /* swap bytes */
		do{
			skt = socket( SOCK_STREAM, 0, &addr,
				      SO_ACCEPTCONN | SO_KEEPALIVE );
			if( debug ) log( "socket returned %d", skt);
			if( skt < 0 )
				sleep( 5 );
		} while( skt < 0 && i-- > 0 );
		if( skt < 0 ) {
			log( "can't open socket (errno %d)",errno);
			continue;
		}
		if( debug ) log( "socket open on %d", skt );

		/*
		 *  Accept a connection.
		 */
		if( accept( skt, &rmtaddr )) {
			if( debug ) log( "accept error (%d)", errno);
			close( skt );
			sleep(1);
			continue;
		}

		if(( pid = fork()) < 0 ) {
			log( "could not fork (%d)", errno);
			close( skt );
		} else if( pid == 0 ) {
			/*
			 *  Child
			 */
			them = raddr( rmtaddr.sin_addr );
			if( them == (-1) )  {
				/* This should produce a dotted quad */
				sprintf( workarea, "%u.%u.%u.%u",
					rmtaddr.sin_addr.s_net&0xff,
					rmtaddr.sin_addr.s_host&0xff,
					rmtaddr.sin_addr.s_lh&0xff,
					rmtaddr.sin_addr.s_impno&0xff);
				them = &workarea[0];
			}
			log( "%s started", them);

			dup2( skt, 0 );
			dup2( skt, 1 );
			if( skt > 1 )
				close( skt );
			execl (Smtpserver, "smtpsrvr", them,
				    thishost, Channel, (char *)0);
			log( "server exec error (%d)", errno);
			exit (99);
		}

		/*
		 *  Parent
		 */
		close( skt );
		numconnections++;

		/*
		 *  This code collects ZOMBIES and implements load
		 *  limiting by staying in the do loop while the
		 *  Maxconnections active.
		 */
		if( numconnections > 1 || Maxconnections == 1 )
			do {
				alarm( 2 );
				if( wait( &i ) > 0 )
					numconnections--;
				alarm( 0 );
			} while( numconnections >= Maxconnections );
	}
}

alarmtrap() {
	signal( SIGALRM, &alarmtrap );
	return;
}

arginit( argc, argv )
int	argc;
char	**argv;
{
	register int	i;
	register char	*arg;

	if( argc == 1 ) {
		log( "Usage: smtpd [-d] [-n #connections] smtpserver channel" );
		exit( 1 );
	}

	for( i = 1; i < argc; i++ ) {
		if( *argv[i] != '-' )
			break;
		arg = argv[i];
		arg++;
		switch( *arg ) {
		case 'd':
			debug++;
			break;

		case 'n':
			if( ++i == argc ) {
				log( "missing number of connections" );
				exit( 99 );
			}
			Maxconnections = atoi( argv[i] );
			if( Maxconnections <= 0 ) {
				log( "Bad number of connections '%s'",
				    argv[i] );
				exit( 99 );
			}
			log( "Maxconnection now %d", Maxconnections );
			break;
		}
	}

	if( i < argc ) {
		if( access( argv[i], 01 ) < 0 )	{	/* execute privs? */
			log( "Cannot access server program '%s'", argv[i] );
			exit( 99 );
		}
		Smtpserver = argv[i];
		if( debug ) log( "server is '%s'", Smtpserver );
	} else {
		log( "Smtpserver program not specified!" );
		log( "Usage: smtpd [-d] [-n #connections] smtpserver channel" );
		exit( 99 );
	}

	if( ++i < argc ) {
		Channel = argv[i];
		if( debug ) log( "channel is '%s'", Smtpserver );
	} else {
		log( "Channel not specified!" );
		log( "Usage: smtpd [-d] [-n #connections] smtpserver channel" );
		exit( 99 );
	}
}

/* VARARGS */
log( fmt, a, b, c, d )
char *fmt;
{
	fputs( programid, stderr );
	fprintf( stderr, fmt, a, b, c, d );
	fputc( '\n', stderr );
	fflush( stderr );
}
eading . */
		else
			bufptr = &buf[0];
		/* If write error occurs, stop writing but keep reading. */
		if (!werrflg)mmdf/src/smtp/tcp.5.2.c   444      0     12        2040  3643667701   7770 #include <fcntl.h>
#include "util.h"
#include "mmdf.h"
#include "netlib.h"
#include "net/con.h"

#define NETFILE "/dev/net"

/* open a tcp connect.  based on code from bbn */


tc_uicp (addr, socket, timeout, fds)
	long addr;
	long socket;            /* absolute socket number       */
	int timeout;            /* time to wait for open        */
	Pip *fds;
{
    struct con openparam, netcon;
    int sock;
    register int fd;

    sock = socket;

    openparam.c_fcon = addr;
    mkanyhost(openparam.c_lcon);

    openparam.c_lport = 0;
    openparam.c_fport = sock;
    openparam.c_timeo = timeout;
    openparam.c_mode = (CONACT | CONTCP);
    openparam.c_sbufs =
    openparam.c_rbufs = 1;
    openparam.c_proto = 0;

    netcon.c_mode = (CONTCP | CONOBLOK);
    if ((fd = open (NETFILE, O_RDWR)) < 0)
	return (RP_LIO);

    if (ioctl(fd, NETOPEN, &netcon) <0) {
	close(fd);
	return(RP_DHST);
    }

    if (ioctl (fd, NETSETE, NULL) < 0) {
	close(fd);
	return (RP_LIO);
    }

    fds -> pip.prd = fd;
    fds -> pip.pwrt = fd;
    return (RP_OK);
}
em = raddr( rmtaddr.sin_addr );
			if( them == (-1) )  {
				/* This should produce a dotted quad */
				sprintf( workarea, "%u.%u.%u.%u",
					rmtaddr.sin_addr.s_net&0xff,
					rmtaddr.sin_addr.s_host&0xff,
					rmtaddr.sin_addr.s_lh&0xff,
					rmtaddr.sin_addr.s_impno&0xff);
				them = &workarea[0];
			}
			log( "%s started", them);

			dup2( skt, 0 );
			dup2( skt, 1 );
			if( skt > 1 )
				close( skt );
			execl (Smtpserver, "smtpsrvr", them,
				    thishost, Channel, (cmmdf/src/smtp/gen   555      0     12          57  3620510563   7164 make -f ../../Makefile.com -f Makefile.real $*
t = sock;
    openparam.c_timeo = timeout;
    openparam.c_mode = (CONACT | CONTCP);
    openparam.c_sbufs =
    openparam.c_rbufs = 1;
    openparam.c_proto = 0;

    netcon.c_mode = (CONTCP | CONOBLOK);
    if ((fd = open (NETFILE, O_RDWR)) < 0)
	return (RP_LIO);

    if (ioctl(fd, NETOPEN, &netcon) <0) {
	close(fd);
	return(RP_DHST);
    }

    if (ioctl (fd, NETSETE, NULL) < 0) {
	close(fd);
	return (RP_LIO);
    }

    fds -> pip.prd = fd;
    fds -> pip.pmmdf/src/smtp/smtpd.5.2.c   444      0     12        5116  3656410740  10331 /* Server SMTP daemon */

#include <sys/types.h>
#include <fcntl.h>
#include <signal.h>
#include "netlib.h"
#include "net/con.h"
#include <stdio.h>

char *Channel;
char *Server;
int  Port;
int  Maxconnections = 4;
char *Network;
int numconnections = 0;

extern int errno;

struct con openparam;

main (argc, argv)
int argc;
char **argv;
{
	register  netfid;
	struct netstate statparam;
	char *us, *them;
	char *getuc ();
	int i;
	int pid;
	int alarmtrap() ;

	if (argc != 6)
	{
		printf ("Usage:  %s server channels port maxconns network\n", argv[0]);
		exit(1);
	}

	Server = argv[1];
	Channel = argv[2];
	Port = atoi(argv[3]);
	Maxconnections = atoi(argv[4]);
	Network = argv[5];

	signal (SIGALRM, alarmtrap);

	close (2);
	dup (1);			/* make log file standard error */
	while (1)
	{
		openparam.c_mode = CONTCP;
		openparam.c_lport = Port;
		openparam.c_rbufs = 2;	/* Ask for 2K receive buffer */
		if (isbadhost(openparam.c_lcon = gethost(Network)))
		    {
		    printf ("SMTP daemon cannot find network '%s'", Network);
		    exit (1);
		    }

		while ((netfid = open ("/dev/net", O_RDWR)) < 0
			|| ioctl(netfid, NETOPEN, &openparam) < 0)
			{
			close (netfid);
			printf ("Return of %d from open, errno = %d\n", netfid, errno); 
			fflush (stdout);
			sleep (60);
			}

		ioctl (netfid, NETSETE, O_RDONLY);

/*		ioctl (netfid, NETGETS, &statparam);

		printf ("Connection from %s\n", hostfmt(openparam.c_fcon,1));
*/
		if(( pid = fork()) < 0 ) {
/*			ll_log( "could not fork (%d)", errno);  */
			(void) close( netfid );
		} else if( pid == 0 ) {
			/*
			 *  Child
			 */

/*			us = getuc (thisname());	/* who are we? */
			us = getuc (Network);  /* daemon knows correct name */
			ioctl (netfid, NETGETS, &statparam);
			them = hostname (statparam.n_fcon); /* who are they? */
			close (0);
			dup (netfid);
			close(1);
			dup (netfid);
			close (netfid);
			execl (Server, Server, them, us, Channel, (char *)0);
			exit (1);
		}


		/*
		 *  Parent
		 */
		close (netfid);
		numconnections++;

		/*
		 *  This code collects ZOMBIES and implements load
		 *  limiting by staying in the do loop while the
		 *  Maxconnections active.
		 */
		if( numconnections > 1 || Maxconnections == 1 )
			do {
				alarm( 2 );
				if( wait( &i ) > 0 )
					numconnections--;
				alarm( 0 );
			} while( numconnections >= Maxconnections );

	}

}

/*
 * Uppercase a string in place. Return pointer to
 * null at end.
 */
char *
getuc(s)
    char *s;
{
    register char *p,
		  c;
    for (p = s; c = *p; p++)
    {
	if (c <= 'z' && c >= 'a')
	    *p -= ('a' - 'A');
    }
    return(s);
}

alarmtrap ()
{

	signal (SIGALRM, alarmtrap);
}
			|| ioctl(netfid, NETOPEN, &openparam) < 0)
			{
			close (netfid);
			printf ("Return of %d from open, errno = %d\n", netfid, errno); 
			fflush (stdout);
			sleep (60);
			}

		ioctl (netfid, NETSETE, O_RDONLY);

/*		ioctl (netfid, NETGETS, &statparam);

		printf ("Connection from %s\n", hostfmt(openparam.c_fcon,1));
*/
		if(( pid = fork()) < 0 ) {
/*			ll_log( "could not fork (%d)", errno);  */
			(void) close( netfid );
		} mmdf/src/smtp/tcp.4.2.c   444      0     12        3100  3671073267   7764 /*
 *		T C P . 4 . 2 . C
 *
 *	Open a TCP/SMTP connection using Berkeley 4.2 style networking.
 *
 *	Doug Kingston, 3 Jan 83 at BRL.
 */
#include "util.h"		/* includes <sys/types.h>, and others */
#include "mmdf.h"
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>

extern LLog *logptr;
extern int errno;

/*ARGSUSED*/
tc_uicp (addr, sock, timeout, fds)
long addr;
long sock;	/* IGNORED */	/* absolute socket number       */
int timeout;			/* time to wait for open        */
Pip *fds;
{
	register int skt;
	struct sockaddr_in haddr;
	short	smtpport = 0;
#ifdef notdef
	int	on = 1;
#endif

	if (smtpport == 0) {
		struct servent *sp;

		if ((sp = getservbyname("smtp", "tcp")) == NULL)
			return (RP_NO);
		smtpport = (short)sp->s_port;
	}

	haddr.sin_family = AF_INET;
	haddr.sin_port = smtpport;
	haddr.sin_addr.s_addr = htonl(addr);

	skt = socket( AF_INET, SOCK_STREAM, 0 );
	if( skt < 0 ) {
		ll_log( logptr, LLOGFST, "Can't get socket (%d)", errno);
		return( RP_LIO );
	}

#ifdef notdef
	if (setsockopt (skt, SOL_SOCKET, SO_DONTLINGER, &on, sizeof on)) {
		ll_log( logptr, LLOGFST, "Can't set socket options (%d)", errno);
		close (skt);
		return( RP_LIO );
	}
#endif notdef
	
        if (setjmp(timerest)) {
            close(skt);
            return ( RP_TIME );
        }
        
        s_alarm( (unsigned) timeout );
	if( connect(skt, &haddr, sizeof haddr) < 0 ) {
		close( skt );
		s_alarm( 0 );
		ll_log( logptr, LLOGFST, "Can't get connection (%d)", errno);
		return( RP_DHST );
	}
	s_alarm( 0 );
	fds -> pip.prd = skt;
	fds -> pip.pwrt = dup(skt);
	return (RP_OK);
}
gister int	i;
	register char	*arg;

	if( argc == 1 ) {
		log( "Usage: smtpd [-d] [-n #connections] smtpserver channel" );
		exit( 1 );
	}

	for( i = 1; i < argc; i++ ) {
		if( *argv[i] != '-' )
			break;
		arg = argv[i];
		arg++;
		switch( *arg ) {
		case 'd':
			debug++;
			break;

		case 'n':
			if( ++i == argc ) {
				log( "missing number of connections" );
				exit( 99 );
			}
			Maxconnections = atoi( argv[i] );
			if( Maxconnections <= 0 mmdf/src/smtp/qu2sm_send.c   444      0     12       27713  3657737761  11026 /*
 *     MULTI-CHANNEL MEMO DISTRIBUTION FACILITY  (MMDF)
 *
 *     Copyright (C) 1979,1980,1981  University of Delaware
 *
 *     Department of Electrical Engineering
 *     University of Delaware
 *     Newark, Delaware  19711
 *
 *     Phone:  (302) 738-1163
 *
 *
 *     This program module was developed as part of the University
 *     of Delaware's Multi-Channel Memo Distribution Facility (MMDF).
 *
 *     Acquisition, use, and distribution of this module and its listings
 *     are subject restricted to the terms of a license agreement.
 *     Documents describing systems using this module must cite its source.
 *
 *     The above statements must be retained with all copies of this
 *     program and may not be removed without the consent of the
 *     University of Delaware.
 *
 *
 *     version  -1    David H. Crocker    March   1979
 *     version   0    David H. Crocker    April   1980
 *     version  v7    David H. Crocker    May     1981
 *     version   1    David H. Crocker    October 1981
 *
 */
/*                SEND FROM DELIVER TO ARPANET MACHINE
 *
 *  Jun 81  D. Crocker    Officially added SRI's header-transform code
 *                        Removed MLFL-related code
 *                        Add HDRMOD conditional compilation for code
 *                        that transforms offnet addresses
 *                        Moved unlink just after reopen, in d2a_send()
 *  Jul 81  D. Crocker    Only call am_cend if txtcpy was successful
 *  Aug 81  D. Crocker    d2a_send() had fclose's w/out fp validity test
 *
 *      -----------------------------------------------------------
 *
 *  Feb 83  Doug Kingston       *** Total Rewrite, Some Fragments Used ***
 *  Nov 83  Steve Kille         Add throughput and UCL changes
 */

#include "util.h"
#include "mmdf.h"
#include "ch.h"
#include "ap.h"
#include "smtp.h"

extern struct ll_struct   *logptr;
extern Chan *chanptr;
extern char *supportaddr;

extern char *sm_curname;

extern long qu_msglen;
extern long sm_hostid();
extern struct sm_rstruct sm_rp;
extern char *strdup();
extern char *strncpy();
extern char *ap_p2s();
extern char *blt();
extern time_t time();

extern int ap_outtype;

LOCVAR time_t start_time;
LOCVAR short sm_fromsent;
LOCVAR int nadrs;
LOCVAR char *sender = (char *)NULL;

LOCVAR struct rp_construct
	rp_bdrem =
{
    RP_BHST, 'B', 'a', 'd', ' ', 'r', 'e', 's', 'p', 'o', 'n', 's', 'e',
    ' ', 'f', 'r', 'o', 'm', ' ', 'r', 'e', 'm', 'o', 't', 'e', ' ',
    's', 'i', 't', 'e', '\0'
}          ,
	rp_adr  =
{
    RP_AOK, 'a', 'd', 'd', 'r', 'e', 's', 's', ' ', 'o', 'k', '\0'
},
	rp_gdtxt =
{
    RP_MOK, 't', 'e', 'x', 't', ' ', 's', 'e', 'n', 't', ' ', 'o', 'k', '\0'
},
	rp_noop =
{
    RP_NOOP, 's', 'u', 'b', '-', 'l', 'i', 's', ' ', 'n', 'o', 't', ' ',
    's', 'p', 'e', 'c', 'i', 'a', 'l', '\0'
},
	rp_dhst =
{
    RP_DHST, 'R', 'e', 'm', 'o', 't', 'e', ' ', 'h', 'o', 's', 't', ' ',
    'u', 'n', 'a', 'v', 'a', 'i', 'l', 'a', 'b', 'l', 'e', '\0'
},
	rp_kild =
{
    RP_DHST, 'n', 'o', ' ', 'v', 'a', 'l', 'i', 'd', ' ', 'a', 'd', 'd',
    'r', 'e', 's', 's', 'e', 's', '\0'
};

/**/

qu2sm_send ()             /* overall mngmt for batch of msgs    */
{
    short   result;
    char    info[LINESIZE];
    char    sendbuf[ADDRSIZE];

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "qu2sm_send ()");
#endif

    if (rp_isbad (result = qu_pkinit ()))
	return (result);

    sm_curname = (char *) 0;

    /*
     *  While there are messages to process ...
     */
    for(;;){
	AP_ptr  loctree, domtree, routree, sendtree;
	AP_ptr  ap_s2tree(), ap_normalize();

	result = qu_rinit (info, sendbuf, chanptr -> ch_apout);
	if(rp_gval(result) == RP_NS){
	    qu_rend();
	    continue;
	}
	else if(rp_gval(result) == RP_DONE)
	    break;
	if (rp_gval(result) == RP_FIO)          /* Can't open message file */
		continue;
	else if (rp_gval(result) != RP_OK)      /* Some other error */
		break;

#ifdef DEBUG
	ll_log (logptr, LLOGBTR, "from qu_rinit (%s)", sender);
#endif
	if (sender != (char *)NULL)
		free (sender);

	if ( sendbuf[0] == '\0' ||
	    (sendtree = ap_s2tree( sendbuf )) == (AP_ptr) NOTOK) {
		printx("return address unparseable, using Orphanage\n");
		fflush(stdout);
		sender = strdup (supportaddr);
	} else {
		sendtree = ap_normalize (chanptr -> ch_lname,
				chanptr  -> ch_ldomain, sendtree, chanptr);
		if(sendtree == (AP_ptr)MAYBE){
		    result = RP_NS;
		    goto bugout;
		}
		ap_t2parts (sendtree, (AP_ptr *)0, (AP_ptr *)0, &loctree, &domtree, &routree);
		sender = ap_p2s ((AP_ptr)0, (AP_ptr)0, loctree, domtree, routree);
		if(sender == (char *)MAYBE){
		    result = RP_NS;
		    goto bugout;
		}
		ap_outtype = chanptr -> ch_apout;
		ap_sqdelete( sendtree, (AP_ptr) 0 );
		ap_free( sendtree );
	}

	if (rp_isbad (result = qu2sm_each ()))
	    break;

	qu_rend();              /* Cleans up after header conversion... */
    }
    qu_rend();

    if (sm_curname != (char *) 0)
	sm_nclose (OK);

    if (rp_gval(result) != RP_DONE)
    {
	ll_log (logptr, LLOGFAT, "not DONE (%s)", rp_valstr (result));
	return (RP_RPLY);         /* catch protocol errors              */
    }

    qu_pkend ();                  /* done getting messages              */
    return (result);

bugout:
    qu_rend();
    if (sm_curname != (char *) 0)
	sm_nclose (OK);
    qu_pkend ();                  /* done getting messages              */
    return (result);
}
/**/

LOCFUN
	qu2sm_each ()             /* send copy of text per address      */
{
    RP_Buf  thereply;
    short   result;
    short   len;
    char    host[ADDRSIZE];
    char    adr[ADDRSIZE];
    char     *newname;

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "qu2sm_each()");
#endif

    time (&start_time);

    sm_fromsent = FALSE;
    nadrs = 0;

    /*
     *  For every address in the message ...
     */
    while( 1 ) {
	result = qu_radr (host, adr);
	if (rp_isbad (result))
	    return (result);      /* get address from Deliver           */

/*  check for end of address sub-list.  if channel is relay, then
 *  sub-list reference is not meaningful.  otherwise, different
 *  sub-lists reference different hosts to connect to.
 */
	switch (rp_gval (result))
	{
	    case RP_HOK:          /* end of sub-list */
		if (chanptr -> ch_host != NORELAY)
		{                 /* different host refs => diff dests  */
		    qu_wrply ((RP_Buf *) &rp_noop, sizeof rp_noop);
		    break;
		}                 /* else DROP ON THROUGH */
	    case RP_DONE:         /* end of full address list           */
		if (nadrs == 0)
		{
		    /*
		     *  Theoretically we would break the connection here since
		     *  the next host is a different host, except that it may
		     *  be flaged and the host after that might be this one.
		     *  Basic rule: don't break a connection until you have to.
		     */
		    qu_wrply ((RP_Buf *) &rp_kild, sizeof rp_kild);

		    if (sm_curname != (char *) 0)
		    {
			if (rp_isbad (sm_cmd ("RSET", SM_RTIME)))
			    break;
/********* should be:   if (sm_rp.sm_rval != 250) *******/
			if (sm_rp.sm_rval < 200 || sm_rp.sm_rval > 299)
			    sm_nclose( NOTOK ); /* Give up on it for now */
		    }
		}
		else if (sm_curname != (char *) 0)
		{
		    nadrs = 0;      /* reset, in case this is only a hend */
		    qu2sm_txtcpy(&thereply);
		    ll_log(logptr, LLOGFTR, "txtcpy reply (%o) (%s)",
			    thereply.rp_val, thereply.rp_line);
		    qu_wrply (&thereply, sizeof (thereply.rp_val)
			      + strlen(thereply.rp_line));
		} else {
		    /* Some addrs were send, but connection went bad */
		    qu_wrply((RP_Buf *)&rp_bdrem, sizeof rp_bdrem);
		}
		sm_fromsent = FALSE;
		if (rp_gval (result) == RP_HOK)
		    continue;           /* more to process  */

		return (RP_OK);         /* END for this message */

	    default:            /* actually have an address */
		if(( newname =  (chanptr->ch_host == NORELAY ?
		   host : chanptr->ch_host)) == (char *) 0)
		{
		    qu_wrply ((RP_Buf *)&rp_dhst, sizeof rp_dhst);
		    break;
		}

		if (!lexequ (sm_curname, newname))
		{
		    if (sm_curname != (char *) 0)
			sm_nclose (OK);

		    if( rp_isbad(sm_nopen( newname)))
		    {
			qu_wrply((RP_Buf *)&rp_dhst, sizeof rp_dhst);
			break;
		    }

		}

		if( sm_fromsent == FALSE ) {
		    if (rp_isbad (sm_wfrom (sender))) {
			if (rp_isbad (sm_rpcpy(&thereply, &len)))
			    qu_wrply((RP_Buf *)&rp_bdrem, sizeof rp_bdrem);
			else
			    qu_wrply (&thereply, len);
			break;
		    }
		    sm_fromsent = TRUE;
		}

		if (sm_wto (host, adr) == RP_DHST
		  || rp_isbad (sm_rpcpy (&thereply, &len))) {
		    qu_wrply ((RP_Buf *) &rp_bdrem, sizeof rp_bdrem);
		    break;
		}
		switch (rp_gval (thereply.rp_val))
		{                 /* was address acceptable?            */
		    case RP_AOK:
			nadrs++;
			qu_wrply ((RP_Buf *) &rp_adr, sizeof rp_adr);
			continue;

		    case RP_PARM:
		    case RP_USER:
		    case RP_NO:
			break;    /* report failure and continue        */

		    case RP_RPLY:
			ll_log (logptr, LLOGFAT, "unusual return: (%s)%s",
				rp_valstr (thereply.rp_val), thereply.rp_line);
			break;    /* notify deliver */
		}

		qu_wrply (&thereply, len);
				  /* notify deliver */
	}
    }
    /* NOTREACHED */
}
/**/

LOCFUN
	qu2sm_txtcpy (rp)     /* copy the text of the message       */
RP_Buf *rp;
{
    int       len;
    short     result;
    char      buffer[BUFSIZ];
    long      bytecount;
    time_t    start_txt,
		end_txt,
		time_eff,
		time_txt;

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "qu2sm_txtcpy()");
#endif

    qu_rtinit (0L);
    sm_fromsent = FALSE;
    nadrs = 0;
    if (rp_isbad (sm_cmd ("DATA", SM_DTIME))) {
	blt ((char *)&rp_bdrem, (char *) rp, sizeof rp_bdrem);
	return;
    }

    switch( sm_rp.sm_rval ) {
    case 354:
	break;          /* Go ahead and send mail */

    case 500:
    case 501:
    case 503:
    case 554:
	rp->rp_val = RP_NDEL;
	if (sm_rp.sm_rgot)
		strncpy (rp->rp_line, sm_rp.sm_rstr, sm_rp.sm_rlen);
	else
		strcpy (rp->rp_line, "Unknown Problem");
	return;

    case 421:
    case 451:
    default:
	rp->rp_val = RP_AGN;
	if (sm_rp.sm_rgot)
		strncpy (rp->rp_line, sm_rp.sm_rstr, sm_rp.sm_rlen);
	else
		strcpy (rp->rp_line, "Unknown Problem");
	return;
    }

    printx ("sending...:");
    fflush (stdout);

    bytecount = 0;
    time (&start_txt);

    len = sizeof(buffer);
    while ((rp_gval (result = qu_rtxt (buffer, &len))) == RP_OK)
    {
	if (setjmp(timerest)) {
	    break;
	}
	s_alarm (SM_BTIME);
	result = sm_wstm (buffer, len);
	s_alarm (0);
	bytecount = bytecount + len;
	if (rp_isbad (result))
	    break;
	printx (".");
	fflush (stdout);
	if (rp_gval (result) != RP_OK) {
	    blt ((char *)&rp_bdrem, (char *) rp, sizeof rp_bdrem);
	    return;
	}
	len = sizeof(buffer);
    }

    bytecount = bytecount +  len;

    if (rp_isbad (result) || rp_gval (result) != RP_DONE) {
	blt ((char *)&rp_bdrem, (char *) rp, sizeof rp_bdrem);
	return;
    }

    /* Kludge: the sm_wstm function will make sure the last char is a NL */
    if (setjmp(timerest)) {
	blt ((char *)&rp_bdrem, (char *) rp, sizeof rp_bdrem);
	return;
    }
    s_alarm (SM_TTIME);
    result = sm_wstm ((char *)0, 0);    /* MAGIC options */
    s_alarm( 0 );
    if (rp_isbad (result)) {
	blt ((char *)&rp_bdrem, (char *) rp, sizeof rp_bdrem);
	return;
    }

    printx (" ");
    if( rp_isbad( sm_cmd( ".", SM_PTIME + (3*nadrs) ))) {
	rp->rp_val = RP_BHST;
	strcpy (rp->rp_line, "Bad response to final dot");
	return;
    }

    time (&end_txt);
    time_txt = end_txt - start_txt;
    time_eff = end_txt - start_time;

    ll_log (logptr, LLOGFST,
		"tpt: data = (%ld) bytes/sec, eff =  (%ld) bytes/sec",
		bytecount / (time_txt ? time_txt : 1), 
		bytecount / (time_eff ? time_eff : 1));


    time (&start_time);

    switch( sm_rp.sm_rval ) {
    case 250:
    case 251:
	blt ((char *)&rp_gdtxt, (char *)rp, sizeof rp_gdtxt);
	return;

    case 552:
    case 554:
	rp->rp_val = RP_NDEL;
	if (sm_rp.sm_rgot)
		strncpy (rp->rp_line, sm_rp.sm_rstr, sm_rp.sm_rlen);
	else
		strcpy (rp->rp_line, "Unknown Problem");
	return;

    case 421:
    case 451:
    case 452:
    default:
	rp->rp_val = RP_AGN;
	if (sm_rp.sm_rgot)
		strncpy (rp->rp_line, sm_rp.sm_rstr, sm_rp.sm_rlen);
	else
		strcpy (rp->rp_line, "Unknown Problem");
	return;
    }
}
st, sizeof rp_dhst);
			break;
		    }

		}

		if( smmmdf/src/smtp/smtpd.4.2.c   444      0     12       12634  3671073270  10353 /*
 *                      S M T P D . C
 *
 *  Server SMTP daemon, for 4.1a/4.2 BSD by DPK at BRL, 1 Jan 82 (Sigh...)
 *
 *              R E V I S I O N   H I S T O R Y
 *
 *      02/02/83  MJM   Modified to be more robust.
 *
 *      03/18/83  DPK   Modified for 4.1c.
 */

#include "util.h"
#include <signal.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <sys/wait.h>
#include <sys/ioctl.h>

#include "ns.h"

extern  int errno;

struct sockaddr_in addr;

char    *Smtpserver = 0;        /* Actual smtp server process, full path */
char    *Channel = 0;           /* Logical channel mail will arrive on */
int     Maxconnections = 4;     /* Maximum simultaneous connections */
int     numconnections = 0;     /* Number of currently active connections */
int     debug = 0;              /* If nonzero, give verbose output on stderr */
char    errbuf[BUFSIZ];         /* Logging will be line buffered */
char    programid[40];          /* identification string for log entries */
int     stricked;               /* force full validity of source address */

main (argc, argv)
int     argc;
char    **argv;
{
	register int    skt;
	int     pid;
	int     i;
	char    thishost[64];
	char    workarea[32];
	struct  servent *sp;
	struct  hostent *hp;

	setbuf( stderr, errbuf );
	sprintf( programid, "smtpd(%5.5d): ", getpid());
	gethostname( thishost, sizeof(thishost));       /* current hostname */
	if ((sp = getservbyname("smtp", "tcp")) == NULL) {
		fprintf(stderr, "Cannot find service smtp/tcp\n");
		exit(-1);
	}
#ifdef  NAMESERVER
	/* don't wait forever! */
	ns_settimeo(NS_NETTIME);
#endif  NAMESERVER

	/*
	 * try to get full name for thishost
	 */
	if( (hp = gethostbyname(thishost)) != NULL)
	    strcpy(thishost, hp->h_name);

	/* Parse args; args will override configuration file... */
	arginit( argc, argv );

	addr.sin_addr.s_addr = AF_INET;
	addr.sin_addr.s_addr = INADDR_ANY;
	addr.sin_port = (short)sp->s_port;

	if( (i = open("/dev/tty", 2)) >= 0) {
		(void) ioctl(i, TIOCNOTTY, (char *)0);
		(void) close(i);
	}

	/*
	 *  Create a socket to accept a SMTP connection on.
	 *  The doloop is due to some sort of IPC bug in 4.1a.
	 */
	if (debug) log("opening socket...");
	i = 60;
	do{
		skt = socket( AF_INET, SOCK_STREAM, 0, 0 );
		if( debug ) log( "socket returned %d", skt);
		if( skt < 0 )
			sleep( 5 );
	} while( skt < 0 && i-- > 0 );
	if( skt < 0 ) {
		if (debug) log( "can't open socket (errno %d)", errno);
		exit (99);
	}
	if( debug ) log( "socket open on %d", skt );

	if (bind (skt, (char *)&addr, sizeof addr, 0) < 0) {
		if (debug) log( "can't bind socket (errno %d)", errno);
		exit (98);
	}
	listen (skt, Maxconnections+1);

	while (1)
	{
		struct sockaddr_in rmtaddr;
		int     len_rmtaddr = sizeof rmtaddr;
		int     tmpskt;
		int     status;
		extern	char *inet_ntoa();

		/*
		 *  Accept a connection.
		 */
		if(( tmpskt = accept( skt, &rmtaddr, &len_rmtaddr, 0 )) < 0) {
			if( debug ) log( "accept error (%d)", errno);
			sleep(1);
			continue;
		}

		/* We have a valid open connection, start a server... */
		if(( pid = fork()) < 0 ) {
			if (debug) log( "could not fork (%d)", errno);
			close( tmpskt );
		} else if( pid == 0 ) {
			/*
			 *  Child
			 */
			char    *rmt;

			hp = gethostbyaddr(&rmtaddr.sin_addr,
					sizeof(rmtaddr.sin_addr), AF_INET);
			if((hp == NULL) || !isstr(hp->h_name)) {
				strcpy(workarea, inet_ntoa(rmtaddr.sin_addr));
				rmt = workarea;
			}
			else rmt = hp->h_name;

			if (debug) log( "%s started", rmt);
			dup2( tmpskt, 0 );
			dup2( tmpskt, 1 );
			if( tmpskt > 1 )
				close( tmpskt );
			execl (Smtpserver, stricked? "rsmtpsrvr" : "smtpsrvr",
					rmt, thishost, Channel, (char *)0);
			if (debug) log( "server exec error (%d)", errno);
			exit (99);
		}

		/*
		 *  Parent
		 */
		close( tmpskt );
		numconnections++;

		/*
		 *  This code collects ZOMBIES and implements load
		 *  limiting by staying in the do loop while the
		 *  Maxconnections active.
		 */
		while (wait3 (&status, numconnections < Maxconnections
		    ? WNOHANG : 0, 0) > 0)
			numconnections--;
	}
}

arginit( argc, argv )
int     argc;
char    **argv;
{
	register int    i;
	register char   *arg;

	if( argc == 1 ) {
		log( "Usage: %s [-d] [-n #connections] smtpserver channels",
			argv[0]);
		exit( 1 );
	}

	for( i = 1; i < argc; i++ ) {
		if( *argv[i] != '-' )
			break;
		arg = argv[i];
		arg++;
		switch( *arg ) {
		case 'd':
			debug++;
			break;

		case 'f':       /* force correctness of addresses */
			stricked++;
			break;

		case 'n':
			if( ++i == argc ) {
				if (debug) log( "missing number of connections" );
				exit( 99 );
			}
			Maxconnections = atoi( argv[i] );
			if( Maxconnections <= 0 ) {
				if (debug) log( "Bad number of connections '%s'",
				    argv[i] );
				exit( 99 );
			}
			if (debug) log( "Maxconnection now %d", Maxconnections );
			break;
		}
	}

	if( i < argc ) {
		if( access( argv[i], 01 ) < 0 ) {       /* execute privs? */
			if (debug) log( "Cannot access server program '%s'", argv[i] );
			exit( 99 );
		}
		Smtpserver = argv[i];
		if( debug ) log( "server is '%s'", Smtpserver );
	} else {
		if (debug) log( "Smtpserver program not specified!" );
		exit( 99 );
	}

	if( ++i < argc ) {
		Channel = argv[i];
		if( debug ) log( "channel is '%s'", Channel);
	} else {
		if (debug) log( "Channel not specified!" );
		exit (99);
	}
}

/* VARARGS */
log( fmt, a, b, c, d )
char *fmt;
{
	fputs( programid, stderr );
	fprintf( stderr, fmt, a, b, c, d );
	fputc( '\n', stderr );
	fflush( stderr );
}
;
		exit (99);
	}
	if( debug ) log( "socket open on %d", skt );

	if (bind (skt, (char *)&addr, sizemmdf/src/smtp/tcp.4.1.c   444      0     12        1625  3643667675  10010 #include "util.h"
#include "mmdf.h"
#include "netlib.h"
#include "con.h"

#define NETFILE "/dev/net/net"

/* open a tcp connect.  based on code from bbn */


tc_uicp (addr, socket, timeout, fds)
	long addr;
	long socket;            /* absolute socket number       */
	int timeout;            /* time to wait for open        */
	Pip *fds;
{
    struct con openparam;
    int sock;
    register int fd;

    sock = socket;

    openparam.c_fcon = addr;
    mkanyhost(openparam.c_lcon);

    openparam.c_lport = 0;
    openparam.c_fport = sock;
    openparam.c_timeo = timeout;
    openparam.c_mode = (CONACT | CONTCP);
    openparam.c_sbufs =
	openparam.c_rbufs = 1;
    openparam.c_proto = 0;

    fd = open (NETFILE, &openparam);
    if (fd < 0)
	return (RP_DHST);

    if (ioctl (fd, NETSETE, NULL) < 0) {
	close(fd);
	return (RP_LIO);
    }

    fds -> pip.prd = fd;
    fds -> pip.pwrt = fd;
    return (RP_OK);
}
ait3 (&status, numconnections < Maxconnections
		    ? WNOHANG : 0, 0) > 0)
			numconnections--;
	}
}

argimmdf/src/smtp/smtpd.4.3.c   444      0     12        4340  3656410737  10335 /*
 *			S M T P D . C
 *
 *	Server process that is designed to be called by INETD,
 *	the Internet Daemon of Daemons.  It calls SMTPSRVR
 *	with the proper arguments.
 *
 *		R E V I S I O N   H I S T O R Y
 */

#include "util.h"
#include <signal.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>

#include "ns.h"

extern	int errno;
extern	char *inet_ntoa();

struct sockaddr_in addr;

int	facist = 0;		/* If set, tell smtpsrvr to dump unknowns */
char	*Smtpserver = 0;	/* Actual smtp server process, full path */
char	*Channel = 0;		/* Logical channel mail will arrive on */
char	obuf[BUFSIZ];		/* Logging will be line buffered */

main (argc, argv)
int	argc;
char	**argv;
{
	register int	skt;
	register struct hostent *hp;
	char	thishost[32];
	char	workarea[32];
	char *rmthost;
	struct sockaddr_in rmtaddr;
	int	len_rmtaddr = sizeof rmtaddr;
	int	status;
	int	on = 1;

#ifdef NAMESERVER
	/* don't lose connection because NS takes too long */
	ns_settimeo(NS_NETTIME);
#endif
	setbuf( stdout, obuf );
	gethostname( thishost, sizeof(thishost));	/* current hostname */

	/* Parse args; args will override configuration file... */
	arginit( argc, argv );

	if (getpeername (0, &rmtaddr, &len_rmtaddr) < 0)
		bomb( "getpeername failed (errno %d)",errno);

	(void) setsockopt (0, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof on);

	hp = gethostbyaddr ( &rmtaddr.sin_addr,
		sizeof(rmtaddr.sin_addr), AF_INET );
	if ((hp == NULL) || !isstr(hp->h_name)) {
		strcpy(workarea, inet_ntoa(rmtaddr.sin_addr));
		rmthost = workarea;
	} else
		rmthost = hp->h_name;


	execl (Smtpserver, facist ? "rsmtpsrvr" : "smtpsrvr",
		rmthost, thishost, Channel, (char *)0);
	bomb( "server exec error (%d)", errno);
}

arginit( argc, argv )
int	argc;
char	**argv;
{
	register char	*arg;

again:	if( argc < 3 )
		bomb( "Usage: %s [-f] smtpserver channels", argv[0] );
	if( strcmp(argv[1], "-f") == 0 ) {
		facist++;
		argc--;
		argv++;
		goto again;
	}
	if( access( argv[1], 01 ) < 0 )		/* execute privs? */
		bomb( "Cannot access server program '%s'", argv[1] );
	Smtpserver = argv[1];
	Channel = argv[2];
}

/* VARARGS */
bomb( fmt, a, b, c, d )
char *fmt;
{
	fputs( "451 ", stdout );
	fprintf( stdout, fmt, a, b, c, d );
	fputs( "\r\n", stdout );
	fflush( stdout );
	exit (99);
}
ed!" );
		exit (99);
	}
}

/* VARARGS */
log( fmt, a, b, c, d )
char *fmt;
{
	fputs( programid, stderr );
	fprintf( stderr, fmt, a, b, c, d );
	fputc( '\n', stderr );
	fflush( stderr );
}
;
		exit (99);
	}
	if( debug ) log( "socket open on %d", skt );

	if (bind (skt, (char *)&addr, sizemmdf/src/smtp/smtpd.2.9.c   444      0     12       13022  3656410733  10352 /*
 *                      S M T P D A E M O N . C
 *
 *  Server SMTP daemon, for 4.1a/4.2 BSD by DPK at BRL, 1 Jan 82 (Sigh...)
 *
 *              R E V I S I O N   H I S T O R Y
 *
 *      02/02/83  MJM   Modified to be more robust.
 *
 *      03/18/83  DPK   Modified for 4.1c.
 */

#include "util.h"
#include <signal.h>
#include <netdb.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/wait.h>
#include <sys/ioctl.h>

extern  int errno;

struct sockaddr_in addr;

char    *Smtpserver = 0;        /* Actual smtp server process, full path */
char    *Channel = 0;           /* Logical channel mail will arrive on */
int     Maxconnections = 4;     /* Maximum simultaneous connections */
int     numconnections = 0;     /* Number of currently active connections */
int     debug = 0;              /* If nonzero, give verbose output on stderr */
char    errbuf[BUFSIZ];         /* Logging will be line buffered */
char    programid[40];          /* identification string for log entries */
int     stricked;               /* force full validity of source address */

main (argc, argv)
int     argc;
char    **argv;
{
	register int    skt;
	int     pid;
	int     i;
	char    thishost[64];
	char    workarea[32];
	struct  servent *sp;
	struct  hostent *hp;

	setbuf( stderr, errbuf );
	sprintf( programid, "smtpdae(%5.5d): ", getpid());
	gethostname( thishost, sizeof(thishost));       /* current hostname */
	if ((sp = getservbyname("smtp", "tcp")) == NULL) {
		fprintf(stderr, "Cannot find service smtp/tcp\n");
		exit(-1);
	}

	/*
	 * try to check if name is complete.
	 */
	if( (hp = gethostbyname(thishost)) != NULL)
	    strcpy(thishost, hp->h_name);

	/* Parse args; args will override configuration file... */
	arginit( argc, argv );

	addr.sin_family = AF_INET;
	addr.sin_addr.s_addr = INADDR_ANY;
	addr.sin_port = (short)sp->s_port;

	disconnect ();          /* close off contact with controlling TTY */

	while (1)
	{
		struct sockaddr_in rmtaddr;
		int     tmpskt;
		int     status;

		/*
		 *  Create a socket to accept a SMTP connection on.
		 */

		if (debug) log("opening socket...");
		skt = socket(SOCK_STREAM, 0, &addr, SO_ACCEPTCONN);
		if( skt < 0 ) {
			if (debug) log( "can't open socket (errno %d)", errno);
			exit (99);
		}
		if( debug ) log( "socket open on %d", skt );
		/*
		 *  Accept a connection.
		 */
		if(accept(skt, &rmtaddr) < 0) {
			if( debug ) log( "accept error (%d)", errno);
			sleep(1);
			continue;
		}

		/* We have a valid open connection, start a server... */
		if(( pid = fork()) < 0 ) {
			if (debug) log( "could not fork (%d)", errno);
			close( skt );
		} else if( pid == 0 ) {
			/*
			 *  Child
			 */
			char    *rmt;

			hp = gethostbyaddr(&rmtaddr.sin_addr,
					sizeof(rmtaddr.sin_addr), AF_INET);
			if((hp == NULL) || !isstr(hp->h_name))
				rmt = inet_ntoa(rmtaddr.sin_addr);
			else
				rmt = hp->h_name;

			if (debug) log( "%s started", rmt);
			dup2( skt, 0 );
			dup2( skt, 1 );
			if( skt > 1 )
				close( skt );
			execl (Smtpserver, stricked? "rsmtpsrvr" : "smtpsrvr",
						rmt, thishost, Channel, 0);
			if (debug) log( "server exec error (%d)", errno);
			exit (99);
		}

		/*
		 *  Parent
		 */
		close( skt );
		numconnections++;

		/*
		 *  This code collects ZOMBIES and implements load
		 *  limiting by staying in the do loop while the
		 *  Maxconnections active.
		 */
		while (wait2 (&status, numconnections < Maxconnections
		    ? WNOHANG : 0, 0) > 0)
			numconnections--;
	}
}

arginit( argc, argv )
int     argc;
char    **argv;
{
	register int    i;
	register char   *arg;

	if( argc == 1 ) {
		log( "Usage: %s [-d] [-n #connections] smtpserver channel", 
			argv[0]);
		exit( 1 );
	}

	for( i = 1; i < argc; i++ ) {
		if( *argv[i] != '-' )
			break;
		arg = argv[i];
		arg++;
		switch( *arg ) {
		case 'd':
			debug++;
			break;

		case 'f':       /* force correctness of addresses */
			stricked++;
			break;

		case 'n':
			if( ++i == argc ) {
				if (debug) log( "missing number of connections" );
				exit( 99 );
			}
			Maxconnections = atoi( argv[i] );
			if( Maxconnections <= 0 ) {
				if (debug) log( "Bad number of connections '%s'",
				    argv[i] );
				exit( 99 );
			}
			if (debug) log( "Maxconnection now %d", Maxconnections );
			break;
		}
	}

	if( i < argc ) {
		if( access( argv[i], 01 ) < 0 ) {       /* execute privs? */
			if (debug) log( "Cannot access server program '%s'", argv[i] );
			exit( 99 );
		}
		Smtpserver = argv[i];
		if( debug ) log( "server is '%s'", Smtpserver );
	} else {
		if (debug) log( "Smtpserver program not specified!" );
		exit( 99 );
	}

	if( ++i < argc ) {
		Channel = argv[i];
		if( debug ) log( "channel is '%s'", Channel);
	} else {
		if (debug) log( "Channel not specified!" );
		exit (99);
	}
}

/* VARARGS */
log( fmt, a, b, c, d )
char *fmt;
{
	fputs( programid, stderr );
	fprintf( stderr, fmt, a, b, c, d );
	fputc( '\n', stderr );
	fflush( stderr );
}
/*
**  DISCONNECT -- remove our connection with any foreground process
**
**      Parameters:
**              fulldrop -- if set, we should also drop the controlling
**                      TTY if possible -- this should only be done when
**                      setting up the daemon since otherwise UUCP can
**                      leave us trying to open a dialin, and we will
**                      wait for the carrier.
**
**      Returns:
**              none
**
**      Side Effects:
**              Trys to insure that we are immune to vagaries of
**              the controlling tty.
*/

disconnect ()
{
	int fd;

	if( (fd = open("/dev/tty", 2)) >= 0){
		(void) ioctl(fd, TIOCNOTTY, 0);
		(void) close(fd);
	}
	errno = 0;
}
 to check if name is complete.
	 */
	if( (hp = gethostbyname(thishost)) != NULL)
	    strcpy(thishost, hp->h_name);

	/* Parse args; args will override configuration file... */
	arginit( argc, argv );

	addr.sin_family = AF_INET;
	addr.sin_addr.s_addr = INADDR_ANY;
	addr.sin_port = (short)sp->s_port;

	disconnect ();          /* close off contact with controlling TTY */

	while (1)
	{
		struct sockaddr_in rmtaddr;
		int     tmpskt;
		int     status;

		/*
		 *  Create a socket to accept a mmdf/src/smtp/smtpd.sun.c   444      0     12        7347  3656410742  10644 /*
 *   SMTPD.SUN.C
 *
 *	Special version for braindamaged SUN version of INETD.
 *
 *	Server process that is designed to be called by SUN/INETD,
 *	the stupid Internet Daemon of Daemons.  It calls SMTPSRVR
 *	with the proper arguments.
 *
 *	Expects arguments in form:
 *
 *	(1) smtpd sourcehost.sourceport
 *		for SUN
 *
 *	(2) smtpd - smtpserver channel
 *		for others or smart suns (better to use smtpd.4.3.c)
 *
 *		R E V I S I O N   H I S T O R Y
 *   
 */


#include "util.h"
#include <signal.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>

extern int errno;
extern char *cmddfldir;
extern char *rindex(), *sprintf();
extern void bomb();	/* advance declaration -- routine in this file */

char localhost[256];
char remotehost[256];
char smtpserv[256] = "";
char *channel =  "smtp";	/* not configurable on SUN */

main(argc,argv)
int argc;
char **argv;
{
    register int valid = 0;
    int opt;

    if (argc >= 2)
    {

	/* now which kind of inetd called us? */

	if (*argv[1] == '-') 
	    valid = setup1(argc-2,argv+2);
	else 
	    valid = setup2(argc-1,argv+1);
    }

    if (!valid)
    {
	/* bomb calls exit() */
	bomb("Usage: %s [- smtpsrv channels] [rmthost.port]", argv[0]);
    }

    if (smtpserv[0] == '\0')
    {
	/* only if not user configured... */
	mmdf_init();
	(void) sprintf(smtpserv,"%s/smtpsrvr",chndfldir);
    }

    opt = 1;
    (void) setsockopt (0, SOL_SOCKET, SO_KEEPALIVE, (char *)&opt, sizeof(opt));

    execl(smtpserv,"smtpsrvr",remotehost,localhost,channel,(char *)0);

    /* bomb calls exit() */
    bomb("server exec error (%d)",errno);
}

setup1(count,args)
int count;
char **args;
{
    register int i;
    register struct hostent *he;
    struct sockaddr_in rmt;
    int size;

    for(i=0; i < count; i++)
    {
	switch(i)
	{
	    case 0:
		(void) strcpy(smtpserv,args);
		break;

	    case 1:
		channel = args[i];
		break;

	    default:
		break;
	}
    }

    /* now learn remote address */
    size = sizeof(rmt);
    if (getpeername(0,&rmt,&size) != 0)
	return(0);

    he = gethostbyaddr((char *)&rmt.sin_addr,sizeof(rmt.sin_addr),rmt.sin_family);

    if ((he == NULL) || !isstr(he->h_name))
    {
	char *cp = (char *)&rmt.sin_addr.s_addr;

	(void)sprintf(remotehost,"%u.%u.%u.%u",
		(int)cp[0],(int)cp[1],(int)cp[2],(int)cp[3]);
    }
    else
	(void)strcpy(remotehost,he->h_name);

     /* now local host name */
     size = sizeof(localhost);
     if (gethostname(localhost,&size) != 0)
	(void)strcpy(localhost,"LocalHost");


    return(1);
}

setup2(count,args)
int count;
char **args;
{
    register char *cp;
    register struct hostent *he;
    int size;
    struct in_addr in;

    if (count != 1)
	return(0);

    /* strip off trailing port value */
    if ((cp = rindex(args[0],'.')) == 0)
	return(0);

    *cp = 0;

    /* now get symbolic name if we can */

    (void) sscanf(args[0],"%x",&in.s_addr);
    he = gethostbyaddr((char *)&in,4,AF_INET);

    if ((he != 0) && isstr(he->h_name))
	(void)strcpy(remotehost,he->h_name);
    else
	(void)strcpy(remotehost,args[0]);

    printf("remotehost = %s\n",remotehost);

     size = sizeof(localhost);
     if (gethostname(localhost,&size) != 0)
	(void)strcpy(localhost,"LocalHost");

    printf("localhost = %s\n",localhost);

    return(1);
}

/**************************************************************************/
/* this works for both SUN and VAX -- more creativity may be required w/  */
/* other types of machines.                                               */
/**************************************************************************/

/* VARARGS1 */
void bomb(fmt,args)
char *fmt;
int args;
{
    char buf[BUFSIZ];

    setbuf(stdout,buf);

    printf("451 ");

    _doprnt(fmt,&args,stdout);
    printf("\r\n");

    (void) fflush(stdout);

    exit(99);
}
unt; i++)
    {
	switch(i)
	{
	    case 0:
		(void) strcpy(smtpserv,args);
		break;

	    case 1:
		channel = args[i];
		break;

	    default:
		break;
	}
    }

    /* now learn remote address */
    size = sizeof(rmt);
    if (getpeername(0,&rmt,&size) != 0)
	return(0);

    he mmdf/src/smtp/.emacs_15451   644      0     12          54  3635564045  10326 /mnt/mmdf2b/mmdf/src/smtp/sm_wtmail.c13264
(rmt.sin_addr),rmt.sin_family);

    if ((he == NULL) || !isstr(he->h_name))
    {
	char *cp = (char *)&rmt.sin_addr.s_addr;

	(void)sprintf(remotehost,"%u.%u.%u.%u",
		(int)cp[0],(int)cp[1],(int)cp[2],(int)cp[3]);
    }
    else
	(void)strcpy(remotehost,he->h_name);

     /* now local host name */
     size = sizeof(localhost);
     if (gethostname(localhost,&size) != 0)
	(void)strcpy(localhost,"LocalHost");


    return(1);
}

setup2(count,args)
int count;
char mmdf/src/submit/   755      0     12           0  3671074626   6773 mmdf/src/submit/gen   555      0     12          57  3620510527   7504 make -f ../../Makefile.com -f Makefile.real $*
t.sin_addr),rmt.sin_family);

    if ((he == NULL) || !isstr(he->h_name))
    {
	char *cp = (char *)&rmt.sin_addr.s_addr;

	(void)sprintf(remotehost,"%u.%u.%u.%u",
		(int)cp[0],(int)cp[1],(int)cp[2],(int)cp[3]);
    }
    else
	(void)strcpy(remotehost,he->h_name);

     /* now local host name */
     size = sizeof(localhost);
     if (gethostname(localhost,&size) != 0)
	(void)strcpy(localhost,"LocalHost");


    return(1);
}

setup2(count,args)
int count;
char mmdf/src/submit/hdr_parse.c   444      0     12        5622  3620510527  11163 #include "util.h"
#include "mmdf.h"
#include "hdr.h"
/*
 *     MULTI-CHANNEL MEMO DISTRIBUTION FACILITY  (MMDF)
 *     
 *
 *     Copyright (C) 1979,1980,1981  University of Delaware
 *     
 *     Department of Electrical Engineering
 *     University of Delaware
 *     Newark, Delaware  19711
 *
 *     Phone:  (302) 738-1163
 *     
 *     
 *     This program module was developed as part of the University
 *     of Delaware's Multi-Channel Memo Distribution Facility (MMDF).
 *     
 *     Acquisition, use, and distribution of this module and its listings
 *     are subject restricted to the terms of a license agreement.
 *     Documents describing systems using this module must cite its source.
 *
 *     The above statements must be retained with all copies of this
 *     program and may not be removed without the consent of the
 *     University of Delaware.
 *     
 *
 *     version  -1    David H. Crocker    March   1979
 *     version   0    David H. Crocker    April   1980
 *     version  v7    David H. Crocker    May     1981
 *     version   1    David H. Crocker    October 1981
 *
 */

extern struct ll_struct *logptr;

/* basic processing of incoming header lines */

hdr_parse (src, name, contents)   /* parse one header line              */
    register char *src;           /* a line of header text              */
    char *name,                   /* where to put field's name          */
	 *contents;               /* where to put field's contents      */
{
    extern char *compress ();
    char linetype;
    register char *dest;

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "hdr_parse()");
#endif

    if (isspace (*src))
    {                             /* continuation text                  */
#ifdef DEBUG
	ll_log (logptr, LLOGFTR, "cmpnt more");
#endif
	if (*src == '\n')
	    return (HDR_EOH);
	linetype = HDR_MOR;
    }
    else
    {                             /* copy the name                      */
	linetype = HDR_NEW;
	for (dest = name; *dest = *src++; dest++)
	{
	    if (*dest == ':')
		break;            /* end of the name                    */
	    if (*dest == '\n')
	    {                     /* oops, end of the line              */
		*dest = '\0';
		return (HDR_NAM);
	    }
	}
	*dest = '\0';
	compress (name, name);    /* strip extra & trailing spaces      */
#ifdef DEBUG
	ll_log (logptr, LLOGFTR, "cmpnt name '%s'", name);
#endif
    }

    for (dest = contents; isspace (*src); )
	if (*src++ == '\n')       /* skip leading white space           */
	{                         /* unfulfilled promise, no contents   */
	    *dest = '\0';
	    return ((linetype == HDR_MOR) ? HDR_EOH : linetype);
	}                          /* hack to fix up illegal spaces      */

    while ((*dest = *src) != '\n' && *src != 0)
	     src++, dest++;       /* copy contents and then, backup     */
    while (isspace (*--dest));    /*   to eliminate trailing spaces     */
    *++dest = '\0';

    return (linetype);
}

arginit( argc, argv )
int     argc;
char    **argv;
{
	register int    i;
	register char   *arg;

	if( argc =mmdf/src/submit/lnk_submit.c   444      0     12       16752  3620510530  11403 #include "util.h"
#include "mmdf.h"
#include "ch.h"
#include "adr_queue.h"
#include "msg.h"
/*
 *     MULTI-CHANNEL MEMO DISTRIBUTION FACILITY  (MMDF)
 *     
 *
 *     Copyright (C) 1979,1980,1981  University of Delaware
 *     
 *     Department of Electrical Engineering
 *     University of Delaware
 *     Newark, Delaware  19711
 *
 *     Phone:  (302) 738-1163
 *     
 *     
 *     This program module was developed as part of the University
 *     of Delaware's Multi-Channel Memo Distribution Facility (MMDF).
 *     
 *     Acquisition, use, and distribution of this module and its listings
 *     are subject restricted to the terms of a license agreement.
 *     Documents describing systems using this module must cite its source.
 *
 *     The above statements must be retained with all copies of this
 *     program and may not be removed without the consent of the
 *     University of Delaware.
 *     
 *
 *     version  -1    David H. Crocker    March   1979
 *     version   0    David H. Crocker    April   1980
 *     version  v7    David H. Crocker    May     1981
 *     version   1    David H. Crocker    October 1981
 *
 */

/*      Jun 80  D. Crocker      RP_USER -> RP_NDEL on no valid users
 *      Nov 80  D. Crocker      Reduce verbosity if more than 12 addrs.
 *      Jun 82  D. Crocker      null chan -> source spec
 *      Jul 82  D. Crocker      qf_ -> mq_
 */
extern struct ll_struct *logptr;
extern Chan **ch_tbsrch;         /* for sorting addrs by channel       */
extern char *strdup ();
extern char *mgt_return;         /* sender's return address            */
extern short tracing;
extern int  lnk_listsize;

struct lnk_struct
{
    struct lnk_struct   *lnk_nxt; /* where the next entry is            */
    struct ch_struct    *lnk_ch;  /* what delivery pgm to run           */
    short     lnk_chpri;            /* "priority" of chan in ch_tbsrch[]  */
    char   *lnk_host;             /* host part of address               */
    char   *lnk_mbox;             /* mailbox part of address            */
};

short  lnk_nadrs;            /* current number of addresses        */
short  lnk_nentries;         /* current number of entries in list  */

/* ***************  (lnk_)  ADDRESS LINKED-LIST PRIMITIVES  *********** */

LOCVAR struct lnk_struct    lnk_strt;
				  /* 1st address in list                */

lnk_adinfo (thechan, hostr, mbox) /* given constituents, add to list    */
Chan *thechan;                 /* internal chan name/code            */
char *hostr,                   /* official name of host              */
     *mbox;                    /* name of mailbox                    */
{
    extern char *ut_alloc ();
    register struct lnk_struct *tstadr;
    register short ch_ind;

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "lnk_adinfo ()");
#endif

    /*NOSTRICT*/
    tstadr = (struct lnk_struct *) ut_alloc (sizeof (struct lnk_struct));
    if ((tstadr -> lnk_ch = thechan) == (Chan *) 0)
	ch_ind = -1;            /* really a source specification        */
    else
    {
#ifdef DEBUG
	ll_log (logptr, LLOGFTR, "lnk_adinfo (%s(->%s), %s, %s)",
		thechan -> ch_name, thechan -> ch_queue,
		hostr, mbox);
#endif
    	if (tracing)
	    printf("queueing for %s: via '%s': '%s'\n",
		thechan -> ch_name, hostr, mbox);
	for (ch_ind = 0; ch_tbsrch[ch_ind] != 0 &&
		ch_tbsrch[ch_ind] != thechan; ch_ind++);
			      /* chan's "priority" is its position  */
    }
    tstadr -> lnk_chpri = ch_ind;
    tstadr -> lnk_host = strdup (hostr);
    tstadr -> lnk_mbox = strdup (mbox);

    return (lnk_insrt (tstadr));
}
/**/

LOCFUN
        lnk_insrt (newadr)	  /* insert addr into list, sorted      */
register struct lnk_struct *newadr;
{
    register struct lnk_struct *curadr,
                               *lastptr;

#ifdef DEBUG
    ll_log (logptr, LLOGFTR, "lnk_insrt ()");
#endif
    for (curadr = lnk_strt.lnk_nxt, lastptr = &lnk_strt;
	    curadr != 0; lastptr = curadr, curadr = curadr -> lnk_nxt)
    {				  /* skip until new entry is "smaller"  */
	switch (lnk_cmpr (newadr, curadr))
	{
	    case 1: 		  /* newadr > curadr                    */
		continue;

	    case 0: 		  /* duplicate entries are ignored      */
		return (OK);      /* noop                               */
	}
	break;			  /* returned -1 => curadr > newadr     */
    }				  /* otherwise tack on end of list      */
    lnk_nentries++;               /* any kind of entry                  */
    if (newadr -> lnk_ch != (Chan *) 0)
	lnk_nadrs++;              /* actual addresses                   */
    lastptr -> lnk_nxt = newadr;  /* insert after last one smaller      */
    newadr -> lnk_nxt = curadr;   /*    than new one                    */
    return (DONE);                /* really did something               */
}

/*
 *	In order to avoid O(n*2) sorting behavior, this comparison
 *	routine will cause the addresses to be sorted in reverse
 *	alphabetical order.  The address list is reversed before
 *	being written out.
 */
LOCFUN
        lnk_cmpr (adr1, adr2)     /* determine sort relationship        */
register struct lnk_struct *adr1,
                           *adr2;
{				  /* sort on chan, host, then mailbox   */
    register short    adr_rel;      /* relationship between addresses     */

    adr_rel = adr2 -> lnk_chpri - adr1 -> lnk_chpri;
				  /* compare "priorities" of channels   */
    if (adr_rel < 0)
	return (-1);		  /* chan2 > chan1                      */

    if (adr_rel > 0)
	return (1);		  /* chan1 > chan2                      */

    if ((adr_rel = lexrel (adr2 -> lnk_host, adr1 -> lnk_host)) != 0)
	return (adr_rel);	  /* host spec was different            */

    return (lexrel (adr2 -> lnk_mbox, adr1 -> lnk_mbox));
				  /* well, maybe different mailboxes    */
}
/**/

LOCFUN struct lnk_struct   *
                            lnk_1free (adr)
				  /* free storage for an entry          */
register struct lnk_struct *adr;
{
    register struct lnk_struct *nxtadr;

    if (adr == 0)
	return (0);		  /* ain't nothin' here                 */
    free (adr -> lnk_mbox);
    free (adr -> lnk_host);
    nxtadr = adr -> lnk_nxt;
    free ((char *) adr);
    return (nxtadr);
}

lnk_freeall ()                    /* step through list and free storage */
{
    register struct lnk_struct *curadr;

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "lnk_freeall()");
#endif
    for (curadr = lnk_strt.lnk_nxt; curadr != 0; curadr = lnk_1free (curadr));
    lnk_strt.lnk_nxt = 0;         /* indicate end-of-list at base       */
    lnk_nadrs = 0;
}

lnk_filall (flags)         /* copy the list out to the file      */
{
    register struct lnk_struct *prev;
    register struct lnk_struct *cur;
    register struct lnk_struct *next;

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "lnk_filall:  %d addrs...", lnk_nadrs);
#endif

    if (lnk_nadrs <= 0 || lnk_strt.lnk_nxt == 0)
	err_msg (RP_NDEL, "no addressees!");

    if (lnk_nadrs > lnk_listsize)      /* long lists => less verbosity     */
	mq_adinit (NO, flags | ADR_CITE, mgt_return);
    else                            /* initialize address list queue    */
	mq_adinit (YES, flags, mgt_return); /* permit delay warning message */

    /*  Invert the list to get it back in alphabetical order  */
    prev = lnk_strt.lnk_nxt;
    cur = prev->lnk_nxt;
    prev->lnk_nxt = 0;
    while (cur != 0) {
	next = cur->lnk_nxt;
    	cur->lnk_nxt = prev;
    	prev = cur;
    	cur = next;
    }
    lnk_strt.lnk_nxt = prev;

				/* step through list & store entries  */
    for (cur = lnk_strt.lnk_nxt; cur != 0; cur = cur -> lnk_nxt)
	if (cur -> lnk_ch != (Chan *) 0)
	    mq_adwrite (cur -> lnk_ch, cur -> lnk_host, cur -> lnk_mbox);
}
	}

	if( ++i < argc ) mmdf/src/submit/Makefile.real   444      0     12        4662  3635174026  11443 #
#   Makefile for submit:     Accept, check, & queue mail from users
#

MODULES =	submit hdr_parse adr_submit lnk_submit \
		mq_wtmail mgt_submit auth_submit

OBJECTS =	submit.o hdr_parse.o adr_submit.o lnk_submit.o \
		mq_wtmail.o mgt_submit.o auth_submit.o

SOURCES =	submit.c hdr_parse.c adr_submit.c auth_submit.c \
		lnk_submit.c mq_wtmail.c mgt_submit.c

real-default:	submit
submit:	xsubmit
xsubmit:	$(OBJECTS) $(MMDFLIBS)
	    $(CC) $(LDFLAGS) -o $@ $(OBJECTS) $(MMDFLIBS) $(SYSLIBS)

install:	$(LIBDIR)/$(MMPREF)submit

$(LIBDIR)/$(MMPREF)submit:	xsubmit
	    cp xsubmit $(LIBDIR)/$(MMPREF)submit
	    -$(CHOWN) $(MMDFLOGIN) $(LIBDIR)/$(MMPREF)submit
	    -chmod 04$(PGMPROT) $(LIBDIR)/$(MMPREF)submit
	    -@ls -ls $(LIBDIR)/$(MMPREF)submit
	    -@echo "submit installed normally"; echo ""

lint:
	$(LINT) $(LFLAGS) $(SOURCES) $(LLIBS)

clean:
	-rm -f xsubmit *.o x.c makedep eddep make.out errs


# DO NOT DELETE THIS LINE -- make depend uses it

submit.o: submit.c
submit.o: ../../h/util.h
submit.o: ../../h/mmdf.h
submit.o: /usr/include/sys/stat.h
submit.o: /usr/include/pwd.h
submit.o: ../../h/nexec.h
submit.o: ../../h/ap.h
submit.o: ../../h/ch.h
submit.o: ../../h/adr_queue.h
submit.o: ../../h/msg.h
submit.o: ../../h/hdr.h
hdr_parse.o: hdr_parse.c
hdr_parse.o: ../../h/util.h
hdr_parse.o: ../../h/mmdf.h
hdr_parse.o: ../../h/hdr.h
adr_submit.o: adr_submit.c
adr_submit.o: ../../h/util.h
adr_submit.o: ../../h/mmdf.h
adr_submit.o: /usr/include/pwd.h
adr_submit.o: ../../h/ch.h
adr_submit.o: ../../h/dm.h
adr_submit.o: ../../h/ap.h
lnk_submit.o: lnk_submit.c
lnk_submit.o: ../../h/util.h
lnk_submit.o: ../../h/mmdf.h
lnk_submit.o: ../../h/ch.h
lnk_submit.o: ../../h/adr_queue.h
lnk_submit.o: ../../h/msg.h
mq_wtmail.o: mq_wtmail.c
mq_wtmail.o: ../../h/util.h
mq_wtmail.o: ../../h/mmdf.h
mq_wtmail.o: ../../h/ch.h
mq_wtmail.o: ../../h/adr_queue.h
mgt_submit.o: mgt_submit.c
mgt_submit.o: ../../h/util.h
mgt_submit.o: ../../h/mmdf.h
mgt_submit.o: ../../h/ch.h
mgt_submit.o: /usr/include/pwd.h
mgt_submit.o: ../../h/cnvtdate.h
mgt_submit.o: ../../h/ap.h
mgt_submit.o: ../../h/dm.h
mgt_submit.o: ../../h/msg.h
mgt_submit.o: ../../h/hdr.h
mgt_submit.o: ../../h/ns.h
auth_submit.o: auth_submit.c
auth_submit.o: ../../h/util.h
auth_submit.o: ../../h/mmdf.h
auth_submit.o: ../../h/cmd.h
auth_submit.o: ../../h/ch.h
auth_submit.o: ../../h/dm.h
auth_submit.o: ../../h/ap.h
# DEPENDENCIES MUST END AT END OF FILE
# IF YOU PUT STUFF HERE IT WILL GO AWAY
# see make depend above
ee (adr -> lnk_host);
    nxtadr = adr -> lnk_nxt;
    free ((char *) adr);
  mmdf/src/submit/mq_wtmail.c   444      0     12       20172  3671073232  11226 #include "util.h"
#include "mmdf.h"

/*
 *     MULTI-CHANNEL MEMO DISTRIBUTION FACILITY  (MMDF)
 *     
 *
 *     Copyright (C) 1979,1980,1981  University of Delaware
 *     
 *     Department of Electrical Engineering
 *     University of Delaware
 *     Newark, Delaware  19711
 *
 *     Phone:  (302) 738-1163
 *     
 *     
 *     This program module was developed as part of the University
 *     of Delaware's Multi-Channel Memo Distribution Facility (MMDF).
 *     
 *     Acquisition, use, and distribution of this module and its listings
 *     are subject restricted to the terms of a license agreement.
 *     Documents describing systems using this module must cite its source.
 *
 *     The above statements must be retained with all copies of this
 *     program and may not be removed without the consent of the
 *     University of Delaware.
 *     
 *
 *     version  -1    David H. Crocker    March   1979
 *     version   0    David H. Crocker    April   1980
 *     version  v7    David H. Crocker    May     1981
 *     version   1    David H. Crocker    October 1981
 *
 */
/* *****************  (mq_)  CREATE QUEUED MESSAGES   **************** */

/*
 *  Nov 81 Dave Crocker     mq_msinit make message name begin "msg."
 *                          mq_adinit permit no-warning specification
 */

#include "ch.h"
#include "adr_queue.h"
#include <sys/file.h>

extern char *multcpy();

extern LLog *logptr;
extern Chan **ch_tbsrch;
extern char *tquedir,            /* temp directory for address list    */
	    *aquedir,            /* actual address list queue          */
	    *squepref,           /* string to preface sub-queue name   */
	    *mquedir,            /* message text queue                 */
	    *supportaddr;	 /* where orphaned messages will go    */

FILE *mq_mffp;                   /* pointerto message text file buffer */
char    *mq_munique;              /* unique-part of mquedir file name   */
				  /*  public, for use as msg's name     */

LOCVAR char *mq_tunique,          /* unique-part of tquedir file name   */
	    *mq_aunique,          /* unique-part of aquedir file name   */
	    mq_tfname[FILNSIZE],  /* full pathname to tquedir file      */
	    mq_afname[FILNSIZE],  /* full pathname to aquedir file      */
	    mq_mfname[FILNSIZE];  /* full pathname to mquedir file      */
LOCVAR FILE *mq_tffp;            /* buffer to temporary address file   */
/**/

mq_winit ()                       /* initialize to write queued msgs    */
{                                 /* set-up for queue file names        */
#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "mq_init ()");
#endif

    /* multcpy used to get pointer to end of base strings */

    mq_tunique = multcpy (mq_tfname, tquedir, (char *)0);
    mq_aunique = multcpy (mq_afname, aquedir, (char *)0);
    mq_munique = multcpy (mq_mfname, mquedir, (char *)0);
}
/**/

mq_creat ()                      /* initialize for new message         */
{
    static char template[128];
    int fd;

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "mq_creat ()");
#endif
/* make the unique part of the queue entry file name.
 * modelled in behaviour on mktemp() but mktemp() iterates through
 * all the possibilities -- which means too many namei()'s get done
 * so we fake it locally.  allows a maximum of 676 messages per submit...
 */

    /* fake like mktemp */
    if (*mq_munique == '\0') {                             

	/* get pid */
	sprintf(template,"msg.aa%05.5d",getpid());

	/* append template to standard part of name */
	strcpy (mq_munique, template);
    }

    while (access(mq_mfname,F_OK) == 0) {
	if (mq_munique[5] >= 'z') {
	    if (++mq_munique[4] > 'z')
		break;	/* error caught below */
	    mq_munique[5] = 'a';
	}
	else
	    mq_munique[5]++;
    }
#ifdef DEBUG
    ll_log (logptr, LLOGFTR, "mname = \"%s\"", mq_mfname);
#endif

    if ((fd = creat(mq_mfname, 0444)) < 0 ||
	(mq_mffp = fdopen (fd, "w")) == NULL)
	err_abrt (RP_FCRT, "Can't create text file to be queued.");
				  /* open it now, to reserve the name   */

    (void) strcpy (mq_aunique, mq_munique);
    (void) strcpy (mq_tunique, mq_munique);

#ifdef DEBUG
    ll_log (logptr, LLOGFTR, "tname = \"%s\"", mq_tfname);
#endif
}

/* initialize adddress queue          */
mq_adinit (dowarn, msgflgs, retadr)
    int dowarn;                  /* permit warning message?            */
    int msgflgs;                 /* misc. bits                         */
    char *retadr;                /* return address (NULL or char pointer */
{
    time_t clockdate;            /* variable for clock/date  */
    register Chan **chanptr;
    int fd;

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "mq_adinit ()");
#endif

    for (chanptr = ch_tbsrch; *chanptr != 0; chanptr++)
	(*chanptr) -> ch_access &= ~MQ_INQ;
				  /* reset "used" bit, for channel      */

    if ((fd = creat(mq_tfname, 0666)) < 0 ||
	(mq_tffp = fdopen (fd, "w")) == NULL)
	err_abrt (RP_FCRT, "Can't create address file to be queued.");

    time(&clockdate);             /* store current time, for queue sort */

/* Creation, Delivery status, Flags(retcite, for now), return addr */

    if (retadr && *retadr)	/* Paranoid */
	fprintf(mq_tffp, "%ld%c%d\n%s\n",
		(long)clockdate, ((dowarn) ? ADR_MAIL : ADR_DONE),
		msgflgs, retadr);
    else
	fprintf(mq_tffp, "%ld%c%d\n%s (Orphanage)\n",
		(long)clockdate, ((dowarn) ? ADR_MAIL : ADR_DONE),
		msgflgs, supportaddr);
}

mq_adwrite (achan, ahost, ambox)  /* put an address in the queue */
    register Chan *achan;
    register char *ahost;
    char    *ambox;
{
    char linebuf[LINESIZE];
    static char tmpchr[2] = ADR_CLR;
    static char typchr[2] = ADR_MAIL;
#ifdef DEBUG
    ll_log (logptr, LLOGFTR, "%s %8s/%s", achan -> ch_queue, ahost, ambox);
#endif

/* this format conforms to the structure specified in adr_queue.h       */

    arg2lstr (0, LINESIZE, linebuf, tmpchr, typchr, achan -> ch_queue,
	ahost, ambox, (char *)0);
    fprintf (mq_tffp, "%s\n", linebuf);
    achan -> ch_access |= MQ_INQ; /* message goes into this sub-queue   */
}

mq_txinit ()                      /* get ready to write message text    */
{                                 /* text file already opened           */
}
/**/

mq_eomsg ()                       /* done submitting message            */
{
    Chan **chanptr;
    char subname[LINESIZE];

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "mq_eomsg ()");
#endif

    fflush (mq_mffp);
    if (ferror (mq_mffp))
	err_abrt (RP_FIO, "Problem writing %s", mq_mfname);

    fclose (mq_mffp);
    mq_mffp = (FILE *) NULL;    /* mark it as unusable */

    fflush (mq_tffp);
    if (ferror (mq_tffp))
	err_abrt (RP_FIO, "Problem writing %s", mq_tfname);
    fclose (mq_tffp);
    mq_tffp = (FILE *) NULL;    /* mark it as unusable */

    for (chanptr = ch_tbsrch; *chanptr != 0; chanptr++)
	if ((*chanptr) -> ch_access & MQ_INQ)
	{
#ifdef DEBUG
	    ll_log (logptr, LLOGFTR,
			"linking to sub-queue '%s'", (*chanptr) -> ch_queue);
#endif
	    sprintf (subname, "%s%s/%s",
				squepref, (*chanptr) -> ch_queue, mq_munique);
	    if (link (mq_tfname, subname) == NOTOK)
	    {                         /* add it to the subqueue         */
		sprintf (subname, "%s%s",
				squepref, (*chanptr) -> ch_queue);
		err_abrt (RP_FCRT, "sub-directory '%s' missing", subname);
	    }
	}

#ifdef DEBUG
    ll_log (logptr, LLOGFTR,
	    "linking to '%s', unlinking '%s'", mq_afname, mq_tfname);
#endif

    if (link (mq_tfname, mq_afname) == NOTOK)
	err_abrt (RP_FCRT, "unable to move file to mail queue");
    unlink (mq_tfname);           /* IGNORE ANY ERROR HERE              */
}
/**/

mq_clear ()                       /* error termination of queue's files */
{
#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "mq_clear ()");
#endif

    if (mq_tffp != (FILE *) EOF && mq_tffp != (FILE *) NULL)
    {                             /* address file                       */
	fclose (mq_tffp);
	mq_tffp = (FILE *) NULL;
#ifdef DEBUG
	ll_log (logptr, LLOGFTR, "unlinking '%s'", mq_tfname);
#endif
	unlink (mq_tfname);
    }

    if (mq_mffp != (FILE *) EOF && mq_mffp != (FILE *) NULL)
    {                             /* message text file                  */
	fclose (mq_mffp);
	mq_mffp = (FILE *) NULL;
#ifdef DEBUG
	ll_log (logptr, LLOGFTR, "unlinking '%s'", mq_mfname);
#endif
	unlink (mq_mfname);
    }
}
ze adddress queue          */
mq_adinit (dowarn, msgflgs, retadr)
    int dowarn;                  /* permit warning message?            */
    int msgflgs;                 /* misc. bits                         */
    char *retadr;                /* return address (NULL or char pointer */
{
    time_t clockdate;            /* variable for clock/date  */
    register Chan **chanptr;
    immdf/src/submit/mgt_submit.c   444      0     12       65342  3642056273  11422 /*
 *     MULTI-CHANNEL MEMO DISTRIBUTION FACILITY  (MMDF)
 *
 *
 *     Copyright (C) 1979,1980,1981  University of Delaware
 *
 *     Department of Electrical Engineering
 *     University of Delaware
 *     Newark, Delaware  19711
 *
 *     Phone:  (302) 738-1163
 *
 *
 *     This program module was developed as part of the University
 *     of Delaware's Multi-Channel Memo Distribution Facility (MMDF).
 *
 *     Acquisition, use, and distribution of this module and its listings
 *     are subject restricted to the terms of a license agreement.
 *     Documents describing systems using this module must cite its source.
 *
 *     The above statements must be retained with all copies of this
 *     program and may not be removed without the consent of the
 *     University of Delaware.
 *
 *
 *     version  -1    David H. Crocker    March   1979
 *     version   0    David H. Crocker    April   1980
 *     version  v7    David H. Crocker    May     1981
 *     version   1    David H. Crocker    October 1981
 *
 */
#include "util.h"
#include "mmdf.h"
#include "ch.h"
#include <pwd.h>
#include "cnvtdate.h"
#include "ap.h"
#include "dm.h"
#include "msg.h"
#include "hdr.h"
#include "ns.h"

/*  enforce management policies on message submission   */

/*  currently, we are only concerned about author authenticity and
 *  format conformance.
 *
 *  Jun 82  D. Crocker      add adruid, for proc/file address protection
 *  Jul 82  D. Crocker      add mgt_hops, checking for looping
 *                          fix use of From, over Sender, for return addr
 *                          add use of Reply-to, over From, fro return addr
 *  Jul 82  A. Chang (suny) mgt_author require mbox==username
 */

extern Chan **ch_tbsrch;           /* complete list of channels          */
extern FILE *mq_mffp;
extern char *ch_dflnam;            /* default channel name             */

extern struct passwd *getpwmid ();
extern struct ll_struct *logptr;
extern char *index ();
extern char *multcat();
extern Chan *ch_nm2struct();
extern Domain *dm_v2route();

extern long tx_msize;             /* char count of message text         */

extern short pro_vfadr;

extern int    effecid,
	    userid,
	    adruid,
	    maxhops,
	    mgt_addid,
	    dlv_flgs;

extern char *adr_orgspec;         /* original mailbox specification     */
extern char *adr_fulldmn;	  /* used for the delay channel 	*/
extern char *blt();
extern char *compress ();

short       lnk_nadrs;            /* number of addressees for message   */

extern char *mq_munique,          /* name of the message                */
	    *prm_dupval (),
	    *strdup (),
	    *locmachine,
	    *locname,
	    *locdomain,
	    nlnultrm[],           /* termination break-set for ut_stdin */
	    *mailid,
	    *username;

char *mgt_txsrc = NULL;           /* some text for Source-Info:         */
char *mgt_return = NULL;          /* return address                     */
int	mgt_gotid;		  /* have seen a message-id: field	*/
int     mgt_inalias;              /* is address in alias file           */

short   mgt_s2return = FALSE;     /* get return from Sender             */

char	mgt_dlname[] = "delay";	  /* name of the delay channel		*/
int     mgt_nodelay = FALSE;      /* don't use delay channel if true    */
int     mgt_amdelay = FALSE;      /* I really am the delay channel      */

LOCVAR struct ch_hstchan
{
    Chan *mgt_achan;              /* channel relay coming in on         */
    char *mgt_ahost;              /* official host name on channel      */
}   mgt_vchan;                    /* caller is coming in from chan x    */

LOCVAR Chan *mgt_chdfl;
LOCVAR short   mgt_trust,        /* pass on author authentication?     */
#define NRMFROM 0                 /*    normal checking                 */
#define FLGFROM 1                 /*    flag as unauthenticated         */
#define OKFROM  2                 /*    ignore validity                 */
	mgt_rt2sndr,              /* if a return mail message is needed */
				  /*    send it to submittor            */
	mgt_rtgot,                /* got the return address             */
	mgt_remail,               /* this was remailed                  */
	mgt_okauthor,             /* an author field was acceptable     */
	mgt_fmcnt,                /* number of from fields seen         */
	mgt_sncnt,                /* number of sender fields seen       */

	mgt_doloc,                /* send "immediate" channels, if any  */
	mgt_donet,                /* send "network" channels, if any    */
	mgt_loops,                /* number of times through this site  */
	mgt_hops;                 /* number of hops message has gone    */
/**/

mgt_init ()
{
#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "mgt_init ()");
#endif
    if (ch_dflnam[0] != '\0')
	if ((mgt_chdfl = ch_nm2struct (ch_dflnam)) == (Chan *) NOTOK)
	    err_abrt (RP_PARM, "'%s' invalid default channel name", ch_dflnam);

    return (RP_OK);
}

mgt_minit ()                      /* initialize for new message         */
{
    register Chan **chanptr;

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "mgt_minit ()");
#endif

    if (mgt_txsrc)                /* if previously set                  */
	free (mgt_txsrc);
    if (mgt_vchan.mgt_ahost)      /*  "     "       "                   */
	free (mgt_vchan.mgt_ahost);
    if (mgt_return)
	free (mgt_return);

    mgt_vchan.mgt_achan =  (Chan *) 0;
    mgt_vchan.mgt_ahost =  (char *) 0;
    mgt_txsrc = (char *) 0;
    mgt_return = (char *) 0;

    mgt_trust = NRMFROM;

    mgt_remail =
	mgt_rt2sndr =
	mgt_s2return =
	mgt_rtgot =
	mgt_okauthor = FALSE;
    mgt_fmcnt =
	mgt_sncnt =
	mgt_gotid =
	mgt_loops =
	mgt_hops = 0;

    for (chanptr = ch_tbsrch; *chanptr != 0; chanptr++)
	(*chanptr) -> ch_access &= ~DLVRDID;
				  /* reset "used" bit, for channel      */
    return (RP_OK);
}
/**/

char *
	mgt_parm (theparm)
register char *theparm;
{
    char *ptr;

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "mgt_parm ()");
#endif
    switch (*theparm)
    {
	case '-':
	    break;

	case 'h':                 /* hostname for relay source          */
	    theparm = prm_dupval (++theparm, &mgt_vchan.mgt_ahost);
	    break;                /* just save the info, for now        */

	case 'i':                 /* relaying Indirectly                */
	    theparm = prm_dupval (++theparm, &ptr);
	    mgt_forward (ptr);
	    free (ptr);
	    break;                /* certify legality of relaying       */

	case 'l':                 /* send local now, if have any        */
	    mgt_doloc = TRUE;
	    break;

	case 'n':                 /* send net now, if have any          */
	    mgt_donet = TRUE;
	    break;

	case 'r':                 /* get RETURN from login info         */
	    mgt_rt2sndr = TRUE;   /*   and not explicit specification   */
	    break;

	case 's':                 /* get RETURN from Sender info        */
	    mgt_s2return = TRUE;
	    break;

	case 't':                 /* TRUST me; don't authenticate       */
	    mgt_trust = OKFROM;
	    break;

	case 'u':                 /* (close to 't'); no authentication  */
	    mgt_trust = FLGFROM;
	    break;

	case 'd':                 /* don't use delay channel            */
	    mgt_nodelay = TRUE;
	    break;

	case 'j':                 /* I really am the delay channel      */
	    mgt_amdelay = TRUE;
	    mgt_nodelay = TRUE;
	    break;

	case 'k':
	    theparm = prm_dupval (++theparm, &ptr);
#ifdef NAMESERVER
	    ns_settimeo(atoi(ptr));
#endif
	    free(ptr);
	    break;

	default:
	    return ((char *) NOTOK);    /* not a management parameter   */
    }

    return (theparm);
}
/**/

mgt_pend ()                       /* end of parameters                  */
{
    AP_ptr rtntree;
    AP_ptr local, domain, route;
    char rtnbuf[ADDRSIZE];
    register short len;

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "mgt_pend ()");
#endif

    mgt_source();

    if (mgt_rt2sndr)              /* Use sender for return address      */
    {
	mgt_return = multcat(mailid, "@", locname, ".", locdomain, (char *)0);
	mgt_rtgot = TRUE;
    }
    else {                         /* submittor specifying return        */
	if (!mgt_s2return)         /* take msg directly                  */
	{
	    register char *cp;

	    if ((len = ut_stdin (rtnbuf, LINESIZE, nlnultrm, NOTOK)) <= 0)
		err_abrt (RP_LIO, "error copying return address");

	    /* Suck up leading white space */
	    for (cp = rtnbuf; isspace(*cp); cp++);

	    if (*cp == '\n')
		*cp = NULL;
	    if (*cp == NULL)
		/*   Special Case:  Null return address --> Quiet flag */
		dlv_flgs |= ADR_NORET;
	    else if (len > (sizeof(rtnbuf) - 1))
		err_msg (RP_LIO, "return address too long");
	    else if ((rtntree = ap_s2tree (cp)) == (AP_ptr) NOTOK)
		err_msg (RP_PARM, "Problem with return address '%s'", rtnbuf);
	    else {
		rtntree = ap_normalize (mgt_vchan.mgt_ahost, (char *) 0,
							rtntree, (Chan *)0);
		if(rtntree == (AP_ptr)MAYBE)
		    err_msg (RP_NS, "Nameserver timeout for '%s'", rtnbuf);
		ap_t2parts(rtntree, (AP_ptr *)0, (AP_ptr *)0, &local,
							&domain, &route);
		mgt_return = ap_p2s( (AP_ptr) 0, (AP_ptr) 0, local, domain,
								route);
		ap_sqdelete (rtntree, (AP_ptr) 0);
		ap_free (rtntree);   /* delete full string                 */
		if(mgt_return == (char *)MAYBE)
		    err_msg (RP_NS, "Nameserver timeout for '%s'", rtnbuf);
		mgt_rtgot = TRUE;
	    }
	}
    }

    if (mgt_vchan.mgt_achan == (Chan *) 0)
	auth_init (username, (mgt_trust == NRMFROM));
    else
	auth_init ((mgt_return ? mgt_return : ""), (mgt_trust == NRMFROM));

    if(pro_vfadr)
	pro_reply (RP_OK, "Sender is '%s'", mgt_return ? mgt_return : "");
#ifdef DEBUG
    ll_log (logptr, LLOGFTR, "mgt_return: '%s'", mgt_return ? mgt_return : "");
#endif
}
/**/

#define AOKNUM 50

mgt_aok (thechan, hostr, mbox, parm) /* is address acceptable for sending  */
Chan *thechan;                    /* internal chan name/code            */
char *hostr,                      /* official name of host              */
     *mbox,                       /* name of mailbox                    */
     *parm;                       /* local-mailbox parameter            */
{
    register struct passwd *pwdptr;

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "mgt_aok (%s, %s, %s, %s)", thechan->ch_name,
		hostr, mbox, parm);
#endif

/* if address maps to file or process, login uid must meet the following
 * restrictions.
 */

    if (thechan == mgt_chdfl)    /* "local" address              */
    {
	switch (parm[0]) 
	{
	case '/':           /* file destination                 */
	case '|':           /* process destination              */
	    if ((pwdptr = getpwmid (mbox)) == (struct passwd *) NULL)
	    {
		ll_err (logptr, LLOGTMP,
			    "user '%s' not in passwd file", mbox);
		return (FALSE); /* can't deliver to non-persons */
	    }
#ifdef DEBUG
	    ll_log (logptr, LLOGFTR, "caller uid (%d), parm uid (%d)",
			adruid, pwdptr -> pw_uid);
#endif
	    if (!mgt_inalias)
				/* only accept from alias file    */
				/* unless 'insecure'              */
#ifdef INSECURE
		/*
		 *  Only do this code if you are willing to take the
		 *  implied security risk.  I do not trust this (DPK).
		 *
		 *  Must be ((your own login id or privelidged user)
		 *  and locally originated) (SEK - note de Morgan!!)
		 */
		if (((adruid != pwdptr -> pw_uid) && (adruid != 0)) ||
			(mgt_vchan.mgt_achan != (Chan *) 0))
#endif
			return (FALSE);
	}
    }

/* is this address subject to immediate transmission, if requested?
 * the check, here, is more for convenience than for enforcement,
 * since Deliver will have to redo the check.  this is just to
 * avoid calling Deliver unnecessarily.
 */

    if (! (thechan -> ch_access & (DLVRPSV | DLVRBAK)))
    {                             /* an active, foreground-able chan?   */
	if ((thechan -> ch_access & DLVRIMM) || mgt_donet)
	    thechan -> ch_access |= DLVRDID;  /* channel may be run now */
    }

    if (mgt_vchan.mgt_achan == (Chan *) 0)
	return (auth_user (mgt_chdfl -> ch_lname, hostr,
						mgt_chdfl, thechan));
    else
	return (auth_user (mgt_vchan.mgt_ahost, hostr,
					mgt_vchan.mgt_achan, thechan));
				/* apply control policies relating to */
				/* channel access                     */
}

mgt_aend ()
{                                 /* record some stats                  */
    char sizstr[11];

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "mgt_aend ()");
#endif

    if (lnk_nadrs <= 0)
	err_msg (RP_NDEL, "No valid addresses");

    sprintf (sizstr, "%ld", tx_msize);
				  /* ll_log can't handle longs          */
    if (mgt_vchan.mgt_achan == 0) /* local submission                   */
	ll_log (logptr, LLOGBST, "lin %s (%d, %s) %s %s",
		mq_munique, lnk_nadrs, sizstr, mgt_chdfl->ch_queue, mailid);
    else
    {                             /* claiming to be a relay             */
	if (!mgt_s2return &&       /* hack past possible bad parsing     */
	    mgt_return != NULL && !isnull (*mgt_return) &&
	    !index(mgt_return, '@'))/* add host if necessary if not given */
	{
	    char *cp;

	    cp = multcat(mgt_return, "@", mgt_vchan.mgt_ahost, (char *)0);
	    free (mgt_return);
	    mgt_return = cp;
	}

	ll_log (logptr, LLOGBST, "rin %s (%d, %s) %s %s %s",
		mq_munique, lnk_nadrs, sizstr,
		mgt_vchan.mgt_achan -> ch_queue, mgt_vchan.mgt_ahost,
		mgt_return);

    }
    auth_end ();
    return (RP_OK);
}

/**/

mgt_source ()                     /* ready to process headers */
{
    char linebuf[LINESIZE];

    if (mgt_vchan.mgt_achan != 0)
    {
	if (mgt_vchan.mgt_ahost == 0)
	{                         /* no source host named               */
	    if (mgt_vchan.mgt_achan -> ch_host == NORELAY)
	    {                     /* not a single relay for the chan    */
		if(mgt_amdelay && PRIV_USER(userid))
			mgt_vchan.mgt_ahost = strdup(adr_fulldmn);
		else	/* cheat here should do an err_abrt with RP_NS ?? */
		    if (tb_k2val (mgt_vchan.mgt_achan -> ch_table, TRUE,
			 	username, linebuf) != OK)
				  /* derive it from login name          */
			err_abrt(RP_PARM,"'%s' not an authorized %s (%s) relay",
				username,
				mgt_vchan.mgt_achan -> ch_table -> tb_name,
				username, mgt_vchan.mgt_achan -> ch_name);
		else 
		    mgt_vchan.mgt_ahost = strdup (linebuf);
	    }
	    else			/* take name from ch_tai structure    */
		mgt_vchan.mgt_ahost = strdup (mgt_vchan.mgt_achan -> ch_host);
	}
	else if (!PRIV_USER(userid))	/* trying to name host explicitly     */
		err_abrt (RP_PARM,
			    "only root, mmdf and daemons may name source host");
    }
}

mgt_hinit ()                     /* ready to process headers */
{
    if (mgt_vchan.mgt_achan != 0)
	mgt_via();
}

/*
 *  Authentication of "Resent" messages is quit a bit looser than
 *  authentication of original messages.  This is due to the fact
 *  that a message might be resent several times, each time gaining
 *  a new set of Resent- headers.  The last set wins.   (DPK, 17 July 84)
 */
mgt_hdr (name, contents, hdr_state)
    char *name,
	contents[];
    int hdr_state;
{
    int ind,
	argc;
    char *argv[25];

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "mgt_hdr ()");
#endif

    if (prefix ("Resent-", name))
    {
	name += 7;
	goto doremail;
    }
    if (prefix ("Remailed-", name))
    {
	name += 9;
	goto doremail;
    }
    if (prefix ("Redistributed-", name))
    {
	name += 14;
doremail:
	if (!mgt_remail)
	{
	    mgt_sncnt = mgt_fmcnt = 0;
	    mgt_remail = TRUE;
	}
	if (lexequ ("Sender", name))
	    goto dosn;
	if (lexequ ("From", name))
	    goto dofm;
	if (lexequ ("Reply-to", name))
	    goto dorplyto;
    }

    if (lexequ ("Sender", name))
    {
	if (!mgt_remail)
	{
dosn:
	    mgt_sncnt++;
	    if (mgt_trust == NRMFROM)
		mgt_okauthor = mgt_author (contents);

	    if (mgt_s2return) {   /* use Sender field for return addr   */
		compress (contents, contents);
		mgt_return = strdup(contents);
		mgt_rtgot = TRUE;
	    }

	}
    }
    else
    if (lexequ ("From", name))
    {
	if (!mgt_remail)
	{
dofm:
	    mgt_fmcnt++;

	    /* Sender overrides; contents of From     */
	    if (mgt_trust == NRMFROM)
		if (mgt_remail || mgt_sncnt == 0)
		    mgt_okauthor = mgt_author (contents);

	    if (mgt_s2return && !mgt_rt2sndr && !mgt_rtgot) {
		compress (contents, contents);
		mgt_return = strdup(contents);
	    }                     /* get return from From, if no Sender */
	}
    }
    else
    if (lexequ ("Reply-To", name))
    {
	if (!mgt_remail)
	{
dorplyto:
	    if (!mgt_s2return && !mgt_rt2sndr && !mgt_rtgot)
	    {
		compress (contents, contents);
		mgt_return = strdup(contents);;
		mgt_rtgot = TRUE;  /* From doesn't set it, but Reply-to does */
	    }
	}
    }
    else
    if (lexequ ("Via", name))   /* old rfc733 pseudo-standard   */
    {
	/*
	 * Only inc mgt_hops if this is the first line of the trace
	 */
	if (hdr_state != HDR_MOR && ++mgt_hops > maxhops)
	{
	    if (mgt_vchan.mgt_achan != (Chan *) 0)
		ll_log (logptr, LLOGTMP, "reject on hop count from: %s %s %s",
			mgt_vchan.mgt_achan -> ch_name, mgt_vchan.mgt_ahost,
			mgt_return);
	    else
		ll_log (logptr, LLOGTMP, "reject last hop:  %s", contents);
	    err_msg (RP_NDEL, "Message has travelled too many hops");
	}

	argc = str2arg (contents, 25, argv, (char *) 0);
#ifdef DEBUG
	ll_log (logptr, LLOGFTR, "from '%s'; contents '%s'",
	    mgt_vchan.mgt_ahost, argv[0]);
#endif

	if (mgt_vchan.mgt_achan != (Chan *) 0 &&
	    lexequ (mgt_vchan.mgt_ahost, argv[0]) &&
	    ++mgt_loops > 2)
	{
	    ll_log (logptr, LLOGTMP, "reject on loop from:  %s %s %s",
			mgt_vchan.mgt_achan -> ch_name, mgt_vchan.mgt_ahost,
			mgt_return);
	    err_msg (RP_NDEL,
			"Message has been through this site (%s) already",
			locname);
	}
    }
    if (lexequ ("Received", name)) /* RFC822 standard   */
    {
	/*
	 * Only inc mgt_hops if this is the first line of the trace
	 */
	if (hdr_state != HDR_MOR && ++mgt_hops > maxhops)
	{
	    if (mgt_vchan.mgt_achan != (Chan *) 0)
		ll_log (logptr, LLOGTMP, "reject on hop count from: %s %s %s",
			mgt_vchan.mgt_achan -> ch_name, mgt_vchan.mgt_ahost,
			mgt_return);
	    else
		ll_log (logptr, LLOGTMP, "reject last hop:  %s", contents);
	    err_msg (RP_NDEL, "Message has travelled too many hops");
	}

	argc = str2arg (contents, 25, argv, (char *) 0);
	for (ind = 0; ind < argc - 2; ind += 2)
	    if (lexequ (argv[ind], "by"))
		break;          /* found the field naming the receiver */

	if (ind++ < argc)
	{                       /* compare our name with 'received' name */
#ifdef DEBUG
	    ll_log (logptr, LLOGFTR, "from '%s'; contents '%s'",
		    mgt_vchan.mgt_ahost, argv[ind]);
#endif

	    if (mgt_vchan.mgt_achan != (Chan *) 0 &&
		    lexequ (mgt_vchan.mgt_achan -> ch_lname, argv[ind]) &&
		    ++mgt_loops > 2)
	    {
		ll_log (logptr, LLOGTMP, "reject on loop from:  %s %s %s",
			mgt_vchan.mgt_achan -> ch_name,
			mgt_vchan.mgt_achan -> ch_lname,
			mgt_return);
		err_msg (RP_NDEL,
			"Message has been through this site (%s) too many times",
			mgt_vchan.mgt_achan -> ch_lname);
	    }
	}
    }
    if (lexequ ("Message-Id", name))
    {
	mgt_gotid++;
    }
    return (RP_OK);     /* no management policies, for this one         */
}
/**/

mgt_hend ()
{
#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "mgt_hend ()");
    ll_log (logptr, LLOGFTR, "uid=%d,effec=%d, fmcnt=%d,okauth=%d",
				userid, effecid, mgt_fmcnt, mgt_okauthor);
#endif

    if (!mgt_remail) {
	if (mgt_sncnt == 0 && mgt_fmcnt > 1 && mgt_trust == NRMFROM)
	    err_msg (RP_PARM, "Too many From: lines");
	if (mgt_sncnt > 1 && mgt_trust == NRMFROM)
	    err_msg (RP_PARM, "Too many Sender: lines");
    }

    if (mgt_trust == OKFROM)     /* pure trust requested               */
    {
	if (!PRIV_USER(userid) && mgt_vchan.mgt_achan == 0)
				  /* trust root, mmdf, and relays      */
	    mgt_trust = FLGFROM; /* otherwise note, but honor          */
    }

    if (mgt_addid && !mgt_gotid)
	mgt_messageid ();

    if (mgt_trust == FLGFROM || mgt_txsrc != 0)
	mgt_srcinfo ();           /* this must follow the above "if"    */

    if (mgt_trust != NRMFROM)
	return (RP_OK);           /* not checking for these errors      */

    if (!mgt_okauthor)
	err_msg (RP_PARM, "No valid author specification present");

    return (RP_OK);
}
/**/

LOCFUN
	mgt_forward (channame)    /* certify source as relay site       */
    char channame[];
{                                 /* map username to "host" on chan     */
    register Chan *thechan;       /* chan claiming to come from         */

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "mgt_forward (%s)", channame);
#endif

    if ((thechan = ch_nm2struct (channame)) == (Chan *) NOTOK)
	err_abrt (RP_PARM, "Channel '%s' does not exist", channame);

    mgt_vchan.mgt_achan = thechan;
}

/**/

LOCFUN
	mgt_author (contents)     /* does component contain only the    */
    char contents[];
{                                 /* address of the actual sender?      */
    struct passwd *pwdptr;
    AP_ptr  locptr,               /* local & domain parts               */
	    dmnptr,
	    aptr;
    int retval = TRUE;

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "mgt_author(%s)", contents);
#endif

    /* do not normalize.  evaluate as is */
    if ((aptr = ap_s2tree (contents)) == (AP_ptr) NOTOK
	|| ap_t2parts (aptr, (AP_ptr *) 0, (AP_ptr *) 0,
		&locptr, &dmnptr, (AP_ptr *) 0) != (AP_ptr) OK) {
	retval = FALSE;         /* only allowed one address in the field */
	goto cleanup;
    }

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "checking: %s, uid %d, %s", locptr -> ap_obvalue,
		userid, dmnptr ? dmnptr -> ap_obvalue : "No Domain");
#endif
    if ((pwdptr = getpwmid (locptr -> ap_obvalue)) == (struct passwd *)NULL
      || pwdptr -> pw_uid != userid)
      retval = FALSE;
    /*
     *  If a domain was specified, make sure it was the proper one.
     */
    if (retval == TRUE && dmnptr != (AP_ptr) 0) {
	Dmn_route dmnroute;
	char tmpbuf[LINESIZE];

#ifdef DEBUG
	ll_log (logptr, LLOGBTR, "mgt_author, v2route(%s)",dmnptr->ap_obvalue);
#endif
	switch ((int)dm_v2route (dmnptr -> ap_obvalue, tmpbuf, &dmnroute)) {
	case MAYBE:
	    retval = FALSE;
	    break;
	case OK:                /* Can't happen ??  -DPK- */
	    retval = TRUE;
	    break;
	default:
#ifdef DEBUG
	    ll_log (logptr, LLOGBTR, "mgt_author, h2chan(%s)", dmnroute.dm_argv[0]);
#endif
	    if (ch_h2chan (dmnroute.dm_argv[0], 1) != OK)
		retval = FALSE;
	    break;
	case NOTOK:
#ifdef DEBUG
	    ll_log (logptr, LLOGBTR, "mgt_author, h2chan(%s)", dmnptr->ap_obvalue);
#endif
	    if (ch_h2chan (dmnptr -> ap_obvalue, 1) != OK)
		retval = FALSE;
	    break;
	}
    }

cleanup:
    if (aptr && aptr != (AP_ptr)NOTOK) {
	ap_sqdelete (aptr, (AP_ptr) 0);
	ap_free (aptr);
    }

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "mgt_author, returns %d", retval);
#endif
    return (retval);
}
/**/

LOCFUN
	mgt_messageid ()            /* add Message-ID: */
{
    char buf[32];
#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "mgt_messageid ()");
#endif

    if (isstr(locmachine))
	fprintf(mq_mffp, "Message-ID:  <%s.%s@%s.%s.%s>\n",
	    cnvtdate(TIMCOM, buf), &mq_munique[4], locmachine,
	    locname, locdomain);
    else
	fprintf(mq_mffp, "Message-ID:  <%s.%s@%s.%s>\n",
	    cnvtdate(TIMCOM, buf), &mq_munique[4], locname, locdomain);
}

LOCFUN
	mgt_srcinfo ()            /* add Source-Info field, maybe       */
{
#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "mgt_srcinfo ()");
#endif

    fputs ("Source-Info:  ", mq_mffp);
    if (mgt_trust == FLGFROM)
    {                             /* note the lack of authentication    */
	fputs ("From (or Sender) name not authenticated.\n", mq_mffp);
	if (mgt_txsrc != 0)       /* indent second line of text         */
	    fputs ("     ", mq_mffp);
    }
    if (mgt_txsrc != 0)           /* user wants to say something        */
    {
	fputs (mgt_txsrc, mq_mffp);
	if (mgt_txsrc[strlen (mgt_txsrc) - 1] != '\n')
	    putc ('\n', mq_mffp);
    }
}

/**/

LOCFUN
	mgt_via ()                /* note fact of relaying              */
{
    extern char *cnvtdate ();
    char    thedate[LINESIZE];
    int len;
#ifdef VIATRACE
    char    *p;
#endif

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "mgt_via ()");
#endif

/* Received: by hostname.net; date                                      */
/*  e.g.:                                                               */
/* Received: by Rand-Unix.ArpaNet; 21 Jan 79 12:40 EDT                  */

    if (mgt_vchan.mgt_achan -> ch_login != NOLOGIN)
				  /* single login authorized to relay   */
	if (!PRIV_USER(userid))	  /* caller is not special id           */
	    if (!lexequ (mgt_vchan.mgt_achan -> ch_login, username))
				  /* caller not the authorized id       */
		err_abrt (RP_USER, "%s not authorized to submit mail as %s",
			    username, mgt_vchan.mgt_achan -> ch_name);

    cnvtdate (TIMSHRT, thedate);  /* net name & short date/time         */

/*  if mail always goes through a relay, for this channel, then the
 *  host/chan combination is a constant and source reference is not
 *  useful; only the receiver will be listed.  The channel name is
 *  used to specify the transfer link.
 */

#ifdef VIATRACE
				/* needed for standard UK sites */
				/* note reversed domain ordering*/
    len = mgt_rcv (0, "Via:");
    if (mgt_vchan.mgt_achan -> ch_host == NORELAY)
	p = ap_dmflip (mgt_vchan.mgt_ahost);
    else
	p  = ap_dmflip (mgt_vchan.mgt_achan -> ch_host);
    len = mgt_rcv (len, " %s;", p);
    free (p);
#else VIATRACE

    len = mgt_rcv (0, "Received:");

    if(mgt_amdelay != TRUE){    /* special string for Delay channel */
	if (mgt_vchan.mgt_achan -> ch_host == NORELAY)
	    len = mgt_rcv (len, "from %s", mgt_vchan.mgt_ahost);
	else
	    len = mgt_rcv (len, "from %s", mgt_vchan.mgt_achan -> ch_host);
    }

#ifndef UCL
    if (isstr (locmachine))
	len = mgt_rcv (len, "by %s.%s.%s", locmachine,
		mgt_vchan.mgt_achan -> ch_lname,
		mgt_vchan.mgt_achan -> ch_ldomain);
    else
	len = mgt_rcv (len, "by %s.%s",
		mgt_vchan.mgt_achan -> ch_lname,
		mgt_vchan.mgt_achan -> ch_ldomain);
    if (adr_orgspec != (char *) 0)
	len = mgt_rcv (len, "for %s", adr_orgspec);
    len = mgt_rcv (len, "id %s;", &mq_munique[4]);
			/* skip the "msg." preface */
#else UCL
    if (isstr (locmachine))
	len = mgt_rcv (len, "by %s.%s.%s ", locmachine, locname, locdomain);
    else
	len = mgt_rcv (len, "by %s.%s ", locname, locdomain);
			/* SEK use local rather than channel pref names? */
			/* structure ch_show to suit tase               */
    if(mgt_amdelay == TRUE)
	len = mgt_rcv (len, " with Delay channel");
    else
	len = mgt_rcv (len, " %s", mgt_vchan.mgt_achan -> ch_show);
    len = mgt_rcv (len, " id %s;", &mq_munique[4]);
			/* skip the "msg." preface */
#endif UCL

#endif VIATRACE
    (void) mgt_rcv (len, "%s\n", thedate);
}

/* VARARGS2 */

LOCVAR
mgt_rcv (curlen, fmt, val1, val2, val3)
    int curlen;
    char *fmt, *val1, *val2, *val3;
{
    char linebuf[LINESIZE];
    int len;

    sprintf (linebuf, fmt, val1, val2, val3);
    len = strlen (linebuf);
    if ((curlen + len) > 77)
    {
	fputs ("\n          ", mq_mffp);
	curlen = 10;
    }
    else
	if (curlen > 0)
	    putc (' ', mq_mffp);
    fputs (linebuf, mq_mffp);
    return (curlen + len);
}

/* generate a delay channel 'host parameter' */

char    *
mgt_dstgen()
{
	char	*multcat();
	return(multcat(mgt_vchan.mgt_achan == NULL ?
			mgt_dlname : mgt_vchan.mgt_achan->ch_name,
			"&", mgt_vchan.mgt_ahost, (char *)0));
}
fdef DEBUG
    ll_log (logptr, LLOGBTR, "mgt_srcinfo ()");
#endif

    fputs ("Source-Info:  ", mq_mffp);
    if (mgt_trust == FLGFROM)
    {                             /* note the lack of authentication    */
	fputs ("From (or Sender) name not authenticated.\n", mq_mffp);
	if (mgt_txmmdf/src/submit/submit.c   444      0     12      113730  3671073235  10565 #include "util.h"
#include "mmdf.h"

/*
 *     MULTI-CHANNEL MEMO DISTRIBUTION FACILITY  (MMDF)
 *
 *
 *     Copyright (C) 1979,1980,1981  University of Delaware
 *
 *     Department of Electrical Engineering
 *     University of Delaware
 *     Newark, Delaware  19711
 *
 *     Phone:  (302) 738-1163
 *
 *
 *     This program module was developed as part of the University
 *     of Delaware's Multi-Channel Memo Distribution Facility (MMDF).
 *
 *     Acquisition, use, and distribution of this module and its listings
 *     are subject restricted to the terms of a license agreement.
 *     Documents describing systems using this module must cite its source.
 *
 *     The above statements must be retained with all copies of this
 *     program and may not be removed without the consent of the
 *     University of Delaware.
 *
 *
 *     version  -1    David H. Crocker    March   1979
 *     version   0    David H. Crocker    April   1980
 *     version  v7    David H. Crocker    May     1981
 *     version   1    David H. Crocker    October 1981
 *
 */
/*  SUBMIT:  Mail enqueuer
 *
 *  May, 80 Dave Crocker    fix switch-setting in prm_ & pro_
 *                          add eof check after text copy
 *                          adr_ghost return PAST initial string ->
 *                            modify use of pointer to be -1
 *  Jun, 80 Dave Crocker    fix adr_ghost return to be 1 past
 *                          mn_init, change fout to be 2, not dup(1)
 *                          hdr_psfrom handle FLGFROM properly
 *  Jul, 80 Dave Crocker    prm_parse, remove debug & fix default
 *  Sep, 80 Jim Lieb        Conversion to V7 and standard io
 *                            protocol replies go to stderr
 *  Oct, 80 Dave Crocker    Minor mods to make V6 also (again) work
 *  Nov, 80 Dave Crocker    Split off adr_submit, qf_submit, mgt_submit,
 *                            & hdr_parse, for easier policy changes
 *  Nov, 81 Dave Crocker    Switches case-insensitive
 *  Jun 82  Dave Crocker    Add adruid proc/file address protection
 *  Jul 82  Dave Crocker    qf_ -> mq_
 */

#include <sys/stat.h>
#include <pwd.h>
#include "nexec.h"
#include "ap.h"
#include "ns.h"

extern LLog msglog;
LLog *logptr = &msglog;
extern struct ap_prevstruct   *ap_fle; /* parse state top of stack       */

extern char *locname;
extern char *locdomain;
extern char *locmachine;
extern char *quedfldir;            /* directory w/mail queue directories */
extern char *cmddfldir;            /* directory w/mmdf commands          */
extern char *logdfldir;            /* directory w/mmdf log files	 */
extern char *supportaddr;
extern char *sitesignature;

jmp_buf retloc;                   /* address for longjmp()              */
int     userid,                   /* id of user running me              */
	effecid,                  /* id providing access priviledges    */
	adruid;                   /* authorized addr, for file access   */

char    *username;                /* string login name of user running me */
char    *mailid;                  /* string mailid of user running me     */

short   earlyret,                 /* caller already got return status   */
	readadrs,                 /* will be directly fed address list  */
	tracing;		  /* enable submission tracing on fd2   */

extern char *mgt_parm();
extern char *dupfpath();
extern char *strdup();
extern char *multcat();
extern char *getmailid();
extern char *malloc();
extern char *strdup();
extern struct passwd *getpwuid();
extern ap_flget();

extern char *namdeliver;      /* name of mailer process             */
extern char *pathdeliver;     /* file path to mailer proc.          */
extern int  *regfdary;
extern int  errno;
extern int  sys_nerr;
extern char *sys_errlist[];

char *prm_dupval();

/* *** constants  and temporaries *** */
char	nltrm[] = "\n\377",		/* for gcread() */
	nlnultrm[] = "\n\000\377",
	nultrm[] = "\000\377";

/* *** pro_ *** */
short   pro_doing = TRUE,         /* protocol-mode interaction vs. one- */
				  /*    message "batch" submission?     */
	pro_vfadr;                /* in protocol mode, indicate         */
				  /*    acceptance of each address      */
/**/
/* *** mq_ *** */

extern FILE *mq_mffp;             /* pointerto message text file buffer */
extern char *mq_munique;          /* unique part of message queue name  */

/* *** ch_ *** */
#include "ch.h"
extern Chan **ch_tbsrch;

/* *** adr_ *** */
#include "adr_queue.h"
#include "msg.h"
extern char *adr_fulldmn;       /* SEK - name for 'full' domain name    */
extern char *adr_fmc;           /* name of 'full' machine               */

/* *** tx_ *** */
long tx_msize;

/* *** hdr_ *** */
#include "hdr.h"
#define MAXACMPT    10

char   *hdr_cmpt[MAXACMPT];       /* where to extract addresses from    */

short     hdr_numadrcmpt;         /* number of components to search     */
				  /*    for addresses in msg text       */
short   hdr_extractadr;           /* tx_stormsg extract addrs?          */

/* *** mgt_ *** */
extern char   *mgt_txsrc;         /* text to add to Source-Info cmpnt   */
extern int    mgt_inalias;        /* SEK - is address extracted from    */
				  /* alias file                         */

/* *** auth_ *** */
extern char auth_msg[];         /* message of reason for auth refusal   */

/* *** dlv_ *** */
short   dlv_watch;                /* user want to watch the send?       */
int	dlv_flgs;                 /* flags to store into first line     */
				  /*  of address list (e.g. RETCITE)    */

/* *** ut_ *** */
char   *ut_alloc ();
short   ut_eofhit,                /* eof encountered on ut_stdin        */
	ut_msgend;                /* null/eof encountered on ut_stdin   */

/*******  (mn_)  MAIN MAIN MAIN MAIN MAIN MAIN MAIN MAIN MAIN  ****** */

LOCVAR	char *mn_aptr;          /* current position on buffer   */
LOCVAR	char mn_abuf[LINESIZE]; /* address buffer               */

LOCFUN
	mn_adrin ()            /* main   input given to alst_proc    */
{
    if (mn_aptr == (char *)1) {
#ifdef DEBUG
	    ll_log (logptr, LLOGFTR, "mn_adrin need newline");
#endif
	    if (ut_stdin(mn_abuf, sizeof(mn_abuf), nltrm, NOTOK) <= 0
	       || strequ ("!\n", mn_abuf))
		mn_aptr = (char *) 0;   /* disable future reading     */
	    else
		mn_aptr = mn_abuf;
    }
    if (mn_aptr == (char *)0) {
#ifdef DEBUG
	    ll_log (logptr, LLOGBTR, "mn_adrin eof or err");
#endif
	    return (0);                 /* permanent eof                */
    }

    switch (*mn_aptr) {
	case '\n':
	case '\0':                      /* end on bang/newline/bang */
	    mn_aptr = (char *)1;      /* indicate we need another line */
	    return (0);                 /* note end of this address     */
    }

    return (*mn_aptr++);
}

/*
 *  Function to re-sync input.  This is needed for the interactive
 *  input function because the other side is expecting exactly one
 *  reply per line sent.  alst_proc might parse two addresses in one line.
 */
mn_arset()
{
	mn_aptr = (char *)1;
}

mn_aread ()
{
    mn_aptr = (char *)1;      /* flag as needing a new line */
    mgt_inalias = FALSE;      /* reading addresses from input          */

    (void) alst_proc (mn_adrin, TRUE, mn_arset, (char **) 0);

    mn_aptr = (char *) 0;
}

/**/

main (argc, argv)                 /*   MAIN MAIN MAIN MAIN MAIN MAIN    */
int       argc;
char   *argv[];
{
    umask(0);
    mmdf_init (argv[0]);
#ifdef NAMESERVER
    ns_settimeo(NS_UIPTIME);	/* set an initial timeout */
#endif 
    mn_usinit ();                 /* initial info on who is running us  */
    mn_init (argc, argv);         /* parse args, alloc buffers.         */
    mn_dirinit ();                /* chdir into quedfldir                 */
    mgt_init ();
    mq_winit ();                  /* info on queue directories          */

    for (setjmp (retloc);       /* return here, after err_msg         */
	    !ut_eofhit;)          /* no more input?                     */
    {
	if (pro_doing)
	    if (pro_init () != OK)
		break;            /* all done                           */

	mq_creat ();              /* set-up the queue files including   */
				  /*    status info in address list     */

	if (readadrs)            /* explicit address list being given? */
	    mn_aread ();

	if (ut_msgend)            /* can't do much with only addr list  */
	    err_abrt (RP_EOF, "Input ended prior to processing text");

	tx_stormsg ();            /* queue & authenticate message       */

	if (pro_doing && ut_eofhit)
	    err_abrt (RP_EOF, "Premature eof during text copy");

	mgt_aend ();             /* perform any ending address policy  */

	lnk_filall (dlv_flgs);
				  /* put address list into its file     */

	lnk_freeall ();

	mq_eomsg ();              /* move to real queue directory       */

#ifdef DEBUG
	ll_log (logptr, LLOGPTR, "msg done -- do deliver?");
#endif

	dlv_invoke ();            /* Maybe give the message a mailer    */
				  /* Will acknowledge message queueing */
    }
    exit (RP_OK);
}
/**/

mn_init (argc, argv)              /* basic process initialization       */
int       argc;
char   *argv[];
{
    register short     tmp;

    prm_init ();                  /* set default values                 */
    for (tmp = 1; tmp < argc; tmp++)
    {                             /* parse any arguments                */
	if (*argv[tmp] == '-')
	{
	    pro_doing = FALSE;    /* presence of arguments => one-shot  */
	    prm_parse (&argv[tmp][1]);
	}                         /* parse the switch string            */
	else
	    mgt_parm (argv[tmp]); /* let management module analyze it   */
    }

    if (!pro_doing)               /* protocol mode will do its own      */
	prm_end ();               /* cleanup & evaluate state           */
}
/**/

mn_usinit ()                      /* get info on who is running me      */
{
    register char *midp;
    register struct passwd *pw;

    getwho (&userid, &effecid);   /* who is running me?                 */
#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "user=%d, effec=%d", userid, effecid);
#endif

    if((pw = getpwuid(userid)) == NULL
      || (midp = getmailid(pw->pw_name)) == NULL)
	err_abrt (RP_LIO, "No login/mailid for UID %d, call Support.",
		userid);

    username = strdup (pw -> pw_name);
    mailid = strdup (midp);

    if (effecid == userid)        /* being run by MMDF                  */
	adruid = 0;               /* full privileges                    */
    else
	adruid = userid;          /* uid for file/process redirection   */
#ifdef  UCL
    if (adruid == 2)            /* fix for authorization for UCL        */
	adruid = 0;
#endif
}

mn_dirinit ()                     /* current loc? chdir to home; setuid */
{
    if (chdir (quedfldir) == NOTOK) /* change wdir into mail queues       */
	err_abrt (RP_LIO, "Unable to change directory.");

    setuid (userid);              /* so we have the user's privileges   */
				  /*   to access address files          */
}
/*******************  (prm_) USER PARAMETER HANDLING  *************** */

prm_init ()                       /* set default user-settable vals     */
{

    mgt_minit ();                /* initialize management settings     */

    while (hdr_numadrcmpt-- > 0)   /* free any previous component names  */
	free (hdr_cmpt[hdr_numadrcmpt]);

    hdr_numadrcmpt =
	dlv_flgs = 0;

    readadrs =
	hdr_extractadr =
	pro_vfadr =
	dlv_watch =
	earlyret =  FALSE;

	adr_fulldmn = multcat (locname, ".", locdomain, (char *)0);
	if (locmachine && isstr (locmachine))
	    adr_fmc = multcat (locmachine, ".", locname, ".", locdomain, (char *)0);
	else
	    adr_fmc = strdup (adr_fulldmn);
}

prm_end ()                        /* evaluate final settings            */
{
    if (!pro_doing)               /* no protocol => a unix "command"    */
	domsg = TRUE;

    if (!hdr_extractadr)           /* not building list from msg text,   */
	readadrs = TRUE;         /*    so take from explicit list      */

    mgt_pend ();                  /* end of parm specifications         */
}
/**/

LOCFUN
	prm_parse (parmstrt)      /* parse spec & set switches          */
    char *parmstrt;               /* note beginning of parm             */
{
    register char  *parmptr;      /* pointer to text of specification   */

    parmptr = parmstrt;
    FOREVER
	switch (*parmptr++)       /* read until end of list             */
    {
	case '\0':
	case '\n':
	    return;               /* all done                           */
	case ' ':
	case '\t':                /* skip linear white space            */
	case '-':                 /* switch-char got through            */
	    continue;

	case 'L':           /* Specify alternate logfile, SU/MMDF only */
	    if (adruid == 0 && *parmptr) {
	    	char *cp;
                
                switch (*(parmptr = prm_dupval(parmptr, &cp)))
                {                     
                    case ',':     /* skip to the last char processed    */
                        err_msg (RP_PARM, "Invalid parameter character ','");
                
                    case '*':
                        parmptr++;
                }
		ll_log (logptr, LLOGFST, "Logfile is '%s'", cp);
		logptr->ll_file = dupfpath(cp, logdfldir);
	    }
	    break;

	case 'V':               /* Specify alternate level, SU/MMDF only */
	    if (adruid == 0 && *parmptr) {
	    	char *cp;
		int level;
                
                switch (*(parmptr = prm_dupval(parmptr, &cp)))
                {                     
                    case ',':     /* skip to the last char processed    */
                        err_msg (RP_PARM, "Invalid parameter character ','");
                
                    case '*':
                        parmptr++;
                }
		ll_log (logptr, LLOGFST, "Loglevel '%s'", cp);
		if ((level = tai_llev (1, &cp)) > 0)
		    logptr->ll_level = level;
	    }
    	    break;

    	case 'W':
    	    tracing = TRUE;
    	    break;

	case 'b':                 /* deliver to mailbox AND tty (obsolete) */
	    break;

	case 'c':                 /* only CITATION in returm mail, not  */
	    dlv_flgs |= ADR_CITE;
	    break;                /*   full copy of text                */

	case 'd':                 /* don't force through DELAY channel */
	    mgt_parm ("d");
	    break;

	case 'f':                 /* add FROM text to Source-Info field */
	    switch (*(parmptr = prm_dupval (parmptr, &mgt_txsrc)))
	    {
		case ',':
		case '*':
		    parmptr++;
	    }
	    break;

	case 'h':                 /* hostname of relay source           */
	    switch (*(parmptr = mgt_parm (&parmptr[-1])))
	    {                     /* let management module process it   */
		case ',':         /* skip to the last char processed    */
		    err_msg (RP_PARM, "Invalid parameter character ','");

		case '*':
		    parmptr++;
	    }
	    break;

	case 'i':                 /* RELAYING, "via" us, "INDIRECTLY"   */
	    switch (*(parmptr = mgt_parm (&parmptr[-1])))
	    {                     /* let management module process it   */
		case ',':         /* skip to the last char processed    */
		    err_msg (RP_PARM, "Invalid parameter character ','");

		case '*':
		    parmptr++;
	    }
	    break;

	case 'j':                 /* we are the delay channel !!        */
	    mgt_parm ("j");
	    break;

	case 'k':                 /* Set nameserver timeouts */
	    switch (*(parmptr = mgt_parm (&parmptr[-1])))
	    {                     /* let management module process it   */
		case ',':         /* skip to the last char processed    */
		    err_msg (RP_PARM, "Invalid parameter character ','");

		case '*':
		    parmptr++;
	    }
	    break;

	case 'l':                 /* send LOCAL mail immediately        */
	    mgt_parm ("l");       /* fork a mailer for it               */
	    break;

	case 'm':                 /* send just to MAILBOX (obsolete option) */
	    break;

	case 'n':                 /* send "NET" mail immediately        */
	    mgt_parm ("n");       /* fork a mailer for it               */
	    break;

	case 'q':                 /* do not return a copy on errors     */
	    dlv_flgs |= ADR_NORET;
	    break;

	case 'r':                 /* send RETURN mail to invoker        */
	    mgt_parm ("r");       /*    and not explicit specification  */
	    break;

	case 's':                 /* get RETURN from Sender info        */
	    mgt_parm ("s");       /*    and not explicit specification  */
	    break;

	case 't':                 /* TRUST me; don't authenticate       */
	    mgt_parm ("t");
	    break;

	case 'u':                 /* (close to 't'); no authentication  */
	    mgt_parm ("u");
	    break;

	case 'v':                 /* Protocol mode; VERIFY each address */
	    pro_vfadr = TRUE;     /*    as received                     */
	    break;

	case 'w':                 /* user wants to WATCH a delivery try */
	    dlv_watch = TRUE;
	    break;

	case 'g':                 /* GET cmpts addrs AND explicit ones  */
	    readadrs = TRUE;     /* DROP ON TRHOUGH                    */
	case 'x':                 /* EXTRACT addrs from cmpnts          */
	    hdr_extractadr = TRUE; /* 'x' [name *(',' name)] '*'         */
	    while (hdr_numadrcmpt < MAXACMPT)
		switch (*(parmptr = prm_dupval (parmptr,
				&(hdr_cmpt[hdr_numadrcmpt++]))))
		{
		    case ',':
			parmptr++;
			continue;

		    case '*':
		    case '\n':
			parmptr++;

		    case '\0':
			goto cmptend;
		}
    cmptend:
	    hdr_cmpt[hdr_numadrcmpt] = 0;
	    break;

	case 'z':                 /* do not send non-delivery warnings  */
	    dlv_flgs |= ADR_NOWARN;
	    break;

	default:
	    err_msg (RP_PARM, "Invalid parameter character '%c' in '%s'",
			parmptr[-1], parmstrt);
    }
}
/**/

char *
	prm_dupval (start, into)  /* get text; dup to into; return ptr  */
				  /*     to last char in orig str       */
char   *start;                    /* beginning of text to parse         */
char  **into;                     /* where to put dup'd str             */
{
    register char   tchar;
    register char  *strptr;

    for (strptr = start;; strptr++)
	switch (*strptr)
	{
	    case '*':
	    case ',':
	    case '\0':
	    case '\n':
		tchar = *strptr;
		*strptr = '\0';
		*into = strdup (start);
		*strptr = tchar;
		return (strptr);  /* pass back pointer to last char     */
	}
}
/**************  (pro_)  PROTOCOL WITH CALLER  **************** */

pro_init ()                       /* read switches in protocol mode     */
{
    char linebuf[LINESIZE];

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "pro_init ()");
#endif

    ut_msgend = FALSE;

    if (ut_stdin (linebuf, sizeof linebuf, nlnultrm, NOTOK) == OK)
	return (NOTOK);          /* done before we began.  tsk. tsk.   */

    prm_init ();
    prm_parse (linebuf);         /* parse & use switches               */
    prm_end ();
    return (OK);
}
/**/

/* VARARGS2 */
pro_reply (code, fmt, b, c, d) /* inform user of a status            */
short     code;                     /* value from mmdfrply.h              */
char   *fmt,
       *b,
       *c,
       *d;
{
    register char  *errchar;

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "pro_reply(%s)", rp_valstr (code));
    ll_log (logptr, LLOGFTR, fmt, b, c, d);
#endif

    if (pro_doing)                /* print each field, for exactness    */
	putc(rp_gval (code), stderr);
    else
    {                             /* assume human watching              */
	if (rp_isgood (code))  /* keep silent during success         */
	    return;
	fprintf(stderr, "submit: ");      /* and noisy on errors                */
    }
    if (rp_isbad (code))
	switch (rp_gval (code))
	{
	    case RP_HUH:          /* not if it was a user error         */
	    case RP_PARM:
	    case RP_USER:
		break;

	    default:
		if (errno > 0 && errno < sys_nerr)
				  /* a system-call error; have to dance */
		{                 /* around ending \n in sys_errlist    */
		    fprintf (stderr, "[ ");
		    for (errchar = sys_errlist[errno];
			    !isnull (*errchar) && *errchar != '\n';
			    errchar++)
			putc(*errchar, stderr);
		    fprintf (stderr, " ] ");
		}
	}
    fprintf(stderr, fmt, b, c, d);
    putc('\n', stderr);
    fflush(stderr);
}
/*****************  (alst_)  PROCESS AN ADDRESS LIST  *************** */

alst_proc (inproc, errabrt, resync, badptr)  /* process text stream of addrs */
int     (*inproc) ();           /* the function which gets address     */
				/*    chars; decides when finished    */
int     errabrt;                /* abort on error?                    */
int     (*resync) ();           /* the function that resyncs input      */
char    **badptr;               /* place to stuff list of bad addresses */
{
    AP_ptr  local,             /* beginning of local-part */
	    domain,            /* basic domain reference */
	    route;             /* beginning of 733 forward routing */
    AP_ptr  theptr;
    int     retval;
    int     anybad;
    char    *msgptr;
    char    *addrp;
    char    *cp;
    int     inalias;
    char    *oldbad;

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "alst_proc ()");
#endif
    anybad = RP_AOK;
    inalias = mgt_inalias;      /* SEK hold value at this level */

    FOREVER
    {
	if ((theptr = ap_pinit (inproc)) == (AP_ptr) NOTOK)
	    err_abrt (LLOGTMP, "problem initializing for address parse");
			      /* problem during parse               */

	switch( retval = ap_1adr ())
	{                   /* get next address                         */
	    case NOTOK:
		ap_sqdelete (theptr, (AP_ptr) 0);
		ap_free (theptr);
		if (!errabrt)           /* Keep on trucking...     */
		    anybad = RP_USER;
		else if (pro_vfadr)    /* just tell caller & continue  */
		    err_gen (RP_USER, "Unable to parse address");
		else              /* kiss off the whole message         */
		    err_msg (RP_USER, "Unable to parse address");
		if (resync != 0)
			(*resync)();
		continue;       /*  Go get another address...  */

	    case DONE:
#ifdef DEBUG
		ll_log( logptr, LLOGFTR, "alst_proc() done");
#endif
		ap_sqdelete (theptr, (AP_ptr) 0);
		ap_free (theptr);
		return (anybad);
	}
	mgt_inalias  = inalias;         /* take iniial value */

	if(ap_t2s (theptr, &addrp) != (AP_ptr)MAYBE) {
#ifdef DEBUG
	    ll_log (logptr, LLOGBTR, "canonical addr: '%s'", addrp);
#endif
    	    if (tracing)
    		printf("%s:  ", addrp);
	    ap_t2parts (theptr, (AP_ptr *) 0, (AP_ptr*) 0,
				    &local, &domain, &route);
	    if (theptr -> ap_obtype == APV_DTYP &&
		lexequ (theptr -> ap_obvalue, "include"))
	    {
		Chan    *lrval;
		/*
		 *  Handle :include:/foo/bar@host, :include:file, <file, etc.
		 */
		if (domain && (domain -> ap_obvalue)
		  && (lrval = ch_h2chan (domain -> ap_obvalue, 1))
						    != (Chan *) OK)
		{
		    if(lrval == (Chan *)MAYBE)
			retval = RP_NS;
		    else {
			cp = multcat (":Include:", local -> ap_obvalue, (char *)0);
			free (local  -> ap_obvalue);
			local -> ap_obvalue = cp;
			retval = adr_check (local, domain, route);
		    }
		}
		else
		    retval = alst_gfil (local -> ap_obvalue);
	    }
	    else                      /* regular address */
		retval = adr_check (local, domain, route);

	    if(rp_gval(retval) == RP_NS)  /* can we use the delay channel */
		retval = adr_dsubmit(addrp);
	}
    	else
    	    retval = RP_NS;

	if (errabrt)              /* we care about this address         */
	{
	    if (rp_isbad (retval))
	    {                     /* error                              */
		switch (rp_gval (retval))
		{
		    case RP_NS:
			if (tracing) 
			    printf("Nameserver Timeout\n");
			msgptr = "(%s) Nameserver Timeout in \"%s\"";
			if (pro_vfadr)    /* just tell caller & continue */
			    err_gen(RP_NS, msgptr, rp_valstr(retval), addrp);
			else              /* kiss off the whole message */
			    err_msg(RP_NS, msgptr, rp_valstr(retval), addrp);
			goto bugout1;

		    case RP_USER:
			msgptr = "(%s) Unknown user name in \"%s\"";
			break;

		    case RP_BHST:
			msgptr = "(%s) Unknown host/domain name in \"%s\"";
			break;

		    case RP_FOPN:
			msgptr = "(%s) Unable to open address file \"%s\"";
			break;

		    case RP_NAUTH:
			msgptr = "(%s) Bad authorization for address \"%s\":%s";
			break;

		    default:
			msgptr = "(%s) Cannot process address \"%s\"";
		}
		if (tracing)
		    printf("Bad address\n");
		if (pro_vfadr)    /* just tell caller & continue        */
		    err_gen (RP_USER, msgptr, rp_valstr (retval),
						      addrp, auth_msg);
		else              /* kiss off the whole message         */
		    err_msg (RP_USER, msgptr, rp_valstr (retval),
						      addrp, auth_msg);
	    }
	    else if (pro_vfadr) {
		/* it's ok and we want to know that, too   */
		if (pro_doing) {
		    if(rp_gval(retval) == RP_DOK)
			pro_reply (RP_DOK, "Possibly a nice address \"%s\"",
								     addrp);
		    else
			pro_reply (RP_AOK, "Nice address \"%s\"", addrp);

		}
		else
		    printf ("%s: OK\n", addrp);
	    }
	}
	else if (rp_isbad (retval)) {
	    switch (retval)
	    {
		case RP_NAUTH:
		case RP_NS:
		    anybad = retval;
		    break;

		default:
		    anybad = RP_USER;
		    break;
	    }

	    if (badptr) {
	        if (*badptr == (char *)0)
	            *badptr = multcat(addrp, "\n", (char *) 0);
	        else {
	            oldbad = *badptr;
	            *badptr = multcat(oldbad, addrp, "\n", (char *) 0);
	            free (oldbad);
	        }
	    }
	}

bugout1:
	ap_sqdelete (theptr, (AP_ptr) 0);
	ap_free (theptr);
	free (addrp);
	if (resync != 0)
		(*resync)();
    }
    /* NOTREACHED */
}
/*******************  ADDRESS FILE INPUT  *************************** */

alst_gfil (file)                 /* read file address list             */
    char    file[];               /* basic name of file                 */
{
    struct stat statbuf;
    int oadruid;                 /* push old adruid onto stack */
    static char themsg[] = "Can't get addresses from file '%s'";
    char *badlist = (char *) 0;

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "alst_gfil (%s)", file);
#endif
    if (tracing)
	printf("Including file %s\n", file);

    oadruid = adruid;

    if (ap_fpush (file) != OK)
    {                           /* could not open the file */
	ll_err (logptr, LLOGGEN, themsg, file);
	return (RP_FOPN);
    }

    fstat (fileno (ap_fle -> ap_curfp), &statbuf);

    if (effecid == statbuf.st_uid)
	adruid = 0;             /* full privileges                    */
    else                        /* uid for file/process redirection   */
	adruid = statbuf.st_uid;

#ifdef DEBUG
    ll_log (logptr, LLOGFTR, "adruid for file = %d", adruid);
#endif

    mgt_inalias = FALSE;        /* getting addresses from file          */

    if (rp_isbad (alst_proc (ap_flget, FALSE, (int (*)())0 , &badlist))) {
	struct  passwd *pw;
	char    *cp;
	char    linebuf[LINESIZE];

	ll_log (logptr, LLOGTMP, "Bad address in list-file %s", file);
	if (tracing)
	    printf ("Bad address in file %s\n", file);
	sprintf (linebuf, "%s %s", locname, sitesignature);
	ml_init (NO, NO, linebuf, "Bad address in file");
	ml_adr (supportaddr);
	if( (pw = getpwuid (statbuf.st_uid))
	 && (cp = getmailid (pw->pw_name)) )
		ml_adr (cp);
	ml_aend();
	ml_tinit();
	sprintf (linebuf, "Found bad address in the file '%s'.\n\n", file);
	ml_txt (linebuf);
	if (badlist) {
	    sprintf (linebuf, "There were problems with:\n");
	    ml_txt (linebuf);
	    ml_txt (badlist);
	    free (badlist);
	    sprintf (linebuf, 
	"\nThe remaining addresses in the file were used for submission.\n\n");
	    ml_txt (linebuf);
	}
	if (ml_end(OK) != OK)
	    ll_log (logptr, LLOGFAT, "Can't send to supportaddr");
    }
    ap_fpop ();

#ifdef DEBUG
    ll_log (logptr, LLOGFTR, "done w/file");
#endif
    adruid = oadruid;
    return (RP_AOK);
}
/****************  (tx_)  TRANSFER MESSAGE TEXT  ******************** */

tx_stormsg ()                     /* store message text into queue      */
{                                 /* perform required processing        */
    struct stat statbuf;
    short len,
	retval;
    char linebuf[LINESIZE],       /* input line                         */
	 thename[LINESIZE];       /* name of the component              */
    register char   lastchar;     /* to make sure message end with \n   */

#ifdef DEBUG
    ll_log (logptr, LLOGPTR, "tx_stormsg ()");
#endif

    mgt_hinit ();

    for (mq_txinit (), lastchar = '\n';
	 (len = ut_stdin (linebuf, sizeof linebuf, nlnultrm, NOTOK)) != OK &&
	 (retval = hdr_proc (linebuf, thename)) > HDR_EOH;
	 fputs (linebuf, mq_mffp))
	    lastchar = linebuf[len - 1];
				  /* process headers & copy to file     */

    if (lastchar != '\n')         /* msgs must end with newline         */
	putc ('\n', mq_mffp);

    mgt_hend ();		  /* whatever policies apply overall    */
				  /* includes Source-Info & Via         */

    putc ('\n', mq_mffp);         /* put out the blank line             */

    if (retval == HDR_NAM)        /* put out illegal first body line    */
	fputs (linebuf, mq_mffp); /*  if any                            */

    if (!ut_msgend)               /* nothing to copy                    */
    {
    	char buf[BUFSIZ];	  /* Lets be efficient now		*/

	for (lastchar = '\n';     /* copy the body                      */
	  (len = ut_stdin (buf, sizeof buf, nultrm, OK)) > 0;
	  fwrite (buf, sizeof(char), len, mq_mffp))
	    lastchar = buf[len - 1];

	if (lastchar != '\n')     /* msgs must end with newline         */
	    putc ('\n', mq_mffp);
    }
    fflush (mq_mffp);
    fstat (fileno (mq_mffp), &statbuf);
    tx_msize = st_gsize (&statbuf);
}
/*******************  (hdr_)  PROCESS A HEADER  ********************* */

LOCVAR char *hdr_atxt;             /* hdr_in() passes to alst()      */

LOCFUN
	hdr_in ()               /* adrs extracted from component text */
{
    if (hdr_atxt == (char *) 0)  /* nothing to give it                 */
	return (EOF);

    switch (*hdr_atxt)
    {
	case '\0':
	case '\n':            /* end of string, DROP ON THROUGH     */
	    hdr_atxt = (char *) 0;
	    return (0);
    }
    return (*hdr_atxt++);
}
/**/

hdr_proc (theline, name)          /* process one header line            */
    char theline[],               /* the text                           */
	 *name;                   /* old or new name of header          */
{
    char contents[LINESIZE];      /* where to put header contents       */
    int  thestate;

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "hdr_proc (%s)", theline);
#endif

    switch (thestate = hdr_parse (theline, name, contents))
    {
	case HDR_NAM:
	    return (HDR_NAM);

	case HDR_EOH:
	    return (HDR_EOH);

	case HDR_NEW:
	case HDR_MOR:
	    break;
    }

    if (!mgt_hdr (name, contents, thestate))
	err_msg (RP_HUH, "Problem with component '%s'", name);

    if (hdr_extractadr && hdr_numadrcmpt > 0 && hdr_isacmpt (name))
    {                             /* add the addresses                  */
	hdr_atxt = contents;
	mgt_inalias = FALSE;      /* getting addresses from header      */
	(void) alst_proc (hdr_in, TRUE, (int (*)())0, (char **) 0);
    }

    return (HDR_OK);
}

LOCFUN
	hdr_isacmpt (name)        /* a component on extraction list?    */
register char   name[];           /* name of the test component         */
{
    register short    entry;

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "hdr_isacmpt(%s)", name);
#endif
    for (entry = 0; entry < hdr_numadrcmpt && hdr_cmpt[entry]; entry++)
	if (lexequ (name, hdr_cmpt[entry]))
	    return (TRUE);
    return (FALSE);
}
/******************  (dlv_)  INVOKE DELIVER PROCESS  *************** */

dlv_invoke ()                     /* maybe try immediate transmission   */
{
    Chan    **chanptr;
    char    temppath[LINESIZE];
    char dochans[LINESIZE];
    int     oldpid;               /* what channels to send              */
    int     proctyp,              /* type of process invocation         */
	    dowait;               /* parent status after child invoke   */

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "dlv_invoke ()");
#endif

    strcpy (dochans, "-c");       /* switch, indicating chans to run    */

    for (proctyp = dowait = 0, chanptr = ch_tbsrch; *chanptr != 0; chanptr++)
	if ((*chanptr) -> ch_access & DLVRDID)
	{
#ifdef DEBUG
	    ll_log (logptr, LLOGFTR, "do: %s (%s)",
		(*chanptr) -> ch_show, (*chanptr) -> ch_name);
#endif
	    strcat (dochans, (*chanptr) -> ch_name);
	    strcat (dochans, ",");
	    proctyp = SPNEXEC;    /* default to detached send           */
	}
    dochans[strlen (dochans) - 1] = '\0';	/* zap last comma */

    if (proctyp == 0)             /* nothing to send immediately        */
    {
	pro_reply (RP_MOK, "Submitted & queued (%s)", mq_munique);
	earlyret = TRUE;          /* note that we already ok'd          */
	return;
    }
    if (dlv_watch)                /* watch immediate transmissions?     */
    {
	proctyp = FRKEXEC;        /* do it via attached child           */
	dowait = FRKWAIT | FRKPIGO;
				  /* parent not interruptable til end   */
	regfdary[1] = 1;
    }
    else
    {
	pro_reply (RP_MOK, "Submitted & immediates started (%s)", mq_munique);
	earlyret = TRUE;          /* note that we already ok'd          */
	regfdary[1] = open ("/dev/null", 1);
				  /* no output to caller                */
#ifdef DEBUG
	ll_log (logptr, LLOGFTR, "null opened");
#endif
    }

    getfpath (pathdeliver, cmddfldir, temppath);

#ifdef DEBUG
    ll_log (logptr, LLOGBTR,
		"%s (%s, %s)", temppath, mq_munique, dochans);
#endif
    ll_close (logptr);        /* free it for deliver                */

    oldpid = getpid ();

    if (nexecl (proctyp, dowait|FRKCIGO|FRKERRR , regfdary,
	    temppath, namdeliver, mq_munique, "-d",
	    dochans, (dlv_watch) ? "-w" : (char *)0, (char *)0) == NOTOK)
	if (getpid () != oldpid)      /* problem with the child             */
	{
	    printx ("Problem running delivery process\n");
	    ll_err (logptr, LLOGTMP,
			"Unable to run %s '%s'", namdeliver, temppath);
	    exit (RP_MECH);
	}

    if (regfdary[1] != 1)         /* it was opened on /dev/null         */
	close (regfdary[1]);
    if (dlv_watch)            /* if not already noted               */
	pro_reply (RP_MOK, "Done mailing (%s)", mq_munique);

}
/*****************  (err_)  GENERAL ERROR-HANDLING  ***************** */

/* VARARGS2 */
err_abrt (code, fmt, b, c, d)  /* terminate the process              */
short     code;                     /* a mmdfrply.h termination code      */
char   *fmt,
       *b,
       *c,
       *d;
{
    mq_clear();                 /* Just to be sure. */
    if (setjmp (retloc) == 0)   /* hack; force err_msg return to here */
	err_msg (code, fmt, b, c, d);
    exit (code);                  /* pass the reply code to caller      */
}


/* VARARGS2 */
err_msg (code, fmt, b, c, d)   /* end processing for current message */
short     code;                     /* see err_abrt explanation           */
char   *fmt,
       *b,
       *c,
       *d;
{
#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "err_msg ()");
#endif
    err_gen (code, fmt, b, c, d); /* includes sending reply             */
    if (!ut_eofhit)               /* get rid of rest of input stream    */
	ut_suckup ();
    mq_clear ();                  /* eliminate temporary files          */
    lnk_freeall();                /* wipe out the current address list  */
    if (pro_doing)                /* more messages ok in protocl mode   */
	longjmp (retloc, TRUE);
    else
	printx ("submit: message submission aborted\n");
    exit (code);                  /* otherwise, goodbye                 */
}

/* VARARGS2 */
err_gen (code, fmt, b, c, d)   /* standard error processing          */
short     code;                /* see err_abrt, for explanation      */
char   *fmt,
       *b,
       *c,
       *d;
{
    char newfmt[LINESIZE];        /* for printf                         */

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "err_gen(%s)", rp_valstr (code));
#endif
    if (rp_isgood (code))         /* it wasn't an error                 */
	return;

    switch (rp_gval (code))       /* log the error?                     */
    {
	case RP_HUH:              /* not if it was a user error         */
	case RP_PARM:
	case RP_USER:
	    sprintf (newfmt, "%s %s", "xin", fmt);
	    ll_log (logptr, LLOGFST, newfmt, b, c, d);
	    break;

	default:
	    sprintf (newfmt, "%s  %s", "err [ ABEND (%s) ] ", fmt);
	    ll_err (logptr, LLOGFAT, newfmt, rp_valstr (code), b, c, d);
    }

    if (!earlyret)                /* haven't already sent reply         */
	pro_reply (code, fmt, b, c, d);
}
/*******************  (ut_)  UTILITY ROUTINES  ********************** */

ut_stdin (buffer, buflen, brkset, longok) /* read from primary input    */
register char  *buffer;           /* where to put null-ended input      */
int	buflen;			  /* length of buffer */
char    *brkset;                  /* what characters to end on          */
int     longok;                   /* long lines are allowed if == OK    */
{
    register int      len;        /* number of characters read          */

/*  brkset must always include null ('\000'), which is the end-of-message
 *  indicator.  if null is the terminator, len is decremented.  i.e.,
 *  null is not allowed to pass as an input character, tho its occurrence
 *  is noted by ut_msgend.
 */

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "ut_stdin (brk='%s')", brkset);
#endif

    if (ut_msgend || ut_eofhit)
    {
#ifdef DEBUG
	ll_log (logptr, LLOGBTR, "(eof terminated last read)");
#endif
	return (0);              /* signal eof, for this read          */
    }

    len = gcread (stdin, buffer, buflen - 1, brkset);
    switch (len)
    {
	case -1:
	    err_abrt (RP_LIO, "Error reading data from control stream");

	case 0:
#ifdef DEBUG
	    ll_log (logptr, LLOGBTR, "EOF");
#endif
	    ut_msgend =
		ut_eofhit = TRUE;
	    buffer[0] = '\0';
	    return(0);
    }
    if (longok != OK && len == (buflen - 1))
	err_abrt (RP_NO, "Input line to submit longer than %d characters",
			buflen - 2);

    if (isnull (buffer[len - 1])) {
	--len;            /* strip null off end                 */
	ut_msgend = TRUE; /* also note end of message         */
    } else
	buffer[len] = '\0';

#ifdef DEBUG
    ll_log (logptr, LLOGFTR, "returning: \"%s\"", buffer);
#endif
    return (len);
}
/**/

ut_suckup ()                      /* skip rest of message input stream  */
{                                 /*    to null (not eof)               */
    register int c;

    if (!ut_msgend && !ut_eofhit)
    {
	while ((c = getc (stdin)) != EOF)
	    if (isnull (c) && pro_doing)
	    {
		ut_msgend = TRUE;
		return;
	    }
	ut_eofhit = TRUE;
    }
}

char   *
	ut_alloc (numbytes)       /* allocate some memory               */
unsigned  numbytes;                 /* duh... number of bytes to allocate */
{
    register char  *thebytes;

    if ((thebytes = malloc (numbytes)) == NULL)
	err_abrt (RP_MECH, "No more storage available");
    return (thebytes);
}
 /* see err_abrt explanation           *mmdf/src/submit/adr_submit.c   444      0     12       61645  3671073240  11376 #include "util.h"
#include "mmdf.h"
/*
 *     MULTI-CHANNEL MEMO DISTRIBUTION FACILITY  (MMDF)
 *
 *
 *     Copyright (C) 1979,1980,1981  University of Delaware
 *
 *     Department of Electrical Engineering
 *     University of Delaware
 *     Newark, Delaware  19711
 *
 *     Phone:  (302) 738-1163
 *
 *
 *     This program module was developed as part of the University
 *     of Delaware's Multi-Channel Memo Distribution Facility (MMDF).
 *
 *     Acquisition, use, and distribution of this module and its listings
 *     are subject restricted to the terms of a license agreement.
 *     Documents describing systems using this module must cite its source.
 *
 *     The above statements must be retained with all copies of this
 *     program and may not be removed without the consent of the
 *     University of Delaware.
 *
 *
 *     version  -1    David H. Crocker    March   1979
 *     version   0    David H. Crocker    April   1980
 *     version  v7    David H. Crocker    May     1981
 *     version   1    David H. Crocker    October 1981
 *
 */

/*  SUBMIT ADDRESS HANDLING                                             */

/*  Apr, 81 Dave Crocker    adr_check able to handle foo<addr> form
 *  Jun, 81 Dave Crocker    adr_check copy test addr, before parsing
 *                          loc_alsrch check for increment to lnk_nadrs
 *                          in case alias entry had no good addresses
 *  Jul 81 Dave Crocker     use separate variable to detect no valid
 *                          alias entries
 *                          fixed alias no-valid-address handling
 *                          lnk_nadrs no longer referenced
 *  May 82 Dave Crocker     adr_ghost use locname, if host field null
 *                          adr_check use chan name as host, if none given
 *                          adr_check added call to loc_gsrch
 *                          loc_g* routines added to use group names as list
 *  Jun 82 Dave Crocker     prevent alias cycling by saving source specs
 *  Aug 82 Dave Crocker     adr_check returns bad user/host distinction
 *  Dec 82 Doug Kingston    fixed error in design of lc_afin(), and lc_gfin().
 *  Feb 83 Doug Kingston    Modified adr_local to recurse through adr_check
 *                          when the local string contains a '%', '.'.
 *  Apr 83 Steve Kille      Add code to transparently deliver between machines
 *                              This uses a table of user to machine matches
 *                              Could use aliases or password instead
 *  May 83 Steve Kille      Change adr_check to handle domain routes
 */

#include <pwd.h>
#include "ch.h"
#include "dm.h"
#include "ap.h"

#define MAXLOOPS 10

extern struct ll_struct *logptr;
extern char *ch_dflnam;
extern char *locname;
extern char *supportaddr;
extern char *sitesignature;
extern char *locmachine;               /* local machine name           */
extern int mgt_inalias;
extern int ap_outtype;
extern short tracing;

char *adr_fulldmn;              /* Name of 'full' domain                */
char *adr_fmc;                  /* name of 'full' machie                */
char *adr_orgspec;              /* original mailbox string              */

extern char *blt();
extern char *index();
extern char *rindex();
extern char mgt_dlname[];	/* name of the delay channel */
extern char *ap_p2s();

/***************  (adr_) PROCESS A SINGLE ADDRESS  *****************  */

LOCVAR char adr_gotone;		/* got at least one valid address     */
LOCVAR char adr_level = 0;	/* Level adr_check/adr_local recursion */

adr_check (local, domain, route) /* check & save an address            */
    AP_ptr  local,             /* beginning of local-part */
	    domain,            /* basic domain reference */
	    route;             /* beginning of 733 forward routing */
{
    extern Domain *dm_v2route ();
    Dmn_route dmrt;
    Dmn_route tmpdmrt;
    AP_ptr hptr;               /* 'next' host */
    Chan   *thechan;
    char    tstline[LINESIZE],
	    official[LINESIZE],
	    tmpstr[LINESIZE];
    int    i;
    int    nextchan;
    AP_ptr ap;
    char   *cp = (char *)0;
    int loopcnt = 0;
    int retval;

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "adr_check (loc='%s', dom='%s', rt='%s')",
	  local -> ap_obvalue,
	  domain != (AP_ptr) 0 ? domain -> ap_obvalue : "[NIL]",
	  route != (AP_ptr) 0 ? route -> ap_obvalue : "[NIL]");
#endif

    /* Limit the number of time we loop looking up routes to domains */
    /* (Sort of a cop-out for foo.bar where foo isn't in the channel table */
    for (thechan = (Chan *) NOTOK; ; loopcnt++)
    {
	if (loopcnt > MAXLOOPS)
		return (RP_BHST);
	/*
	 * This if-then-else clause selects the nxt domain to be
	 * evaluated or calls adr_local if there are none left.
	 */
	if (route != (AP_ptr) 0) {	/* have explicit routing info */
	    hptr = route;		/* a list of fields             */
	    FOREVER
	    {
		switch (route -> ap_ptrtype) {
		    case APP_NIL:
		    case APP_NXT:
			route = (AP_ptr) 0;
			break;  /* no more route */

		    case APP_ETC:
			route = route -> ap_chain;
			if(route == (AP_ptr)0)
				break;
			switch (route -> ap_obtype) {
			    case APV_DLIT:
			    case APV_DOMN:
				break;

			    case APV_CMNT:
				continue;

			    default:
				route = (AP_ptr) 0;
				break;  /* no more route */
			}
		}
		break; /* no route */
	    }
	}
	else                    /* just use the primary reference */
	if (domain != (AP_ptr) 0) {	/* domain ref is a single field */
	    hptr = domain;
	    domain = (AP_ptr) 0;
	}
	else {
#ifdef DEBUG
	    ll_log (logptr, LLOGFTR, "local address");
#endif
	    if(adr_level++ == 0)
		adr_orgspec = NULL;
	    retval = adr_local (local -> ap_obvalue);
	    adr_level--;
	    return (retval);
	}

/*
 *  a single domain reference is evaluated
 */
#ifdef DEBUG
	ll_log (logptr, LLOGFTR, "testing '%s'", hptr -> ap_obvalue);
#endif

	if (thechan != (Chan *) NOTOK) {
	    /* Last time through was channel reference */
	    /* Check the specified table this time */
#ifdef DEBUG
	    ll_log (logptr, LLOGFTR, "chan specified, checking '%s'",
		thechan -> ch_show);
#endif
	    /* SEK if lookup fails, may still be domain */
	    /* reference, so fall into code down below */
	    retval = tb_k2val(thechan->ch_table,TRUE,hptr->ap_obvalue, tstline);
	    if(retval == MAYBE)
		return(RP_NS);
	    else if(retval == OK)
	    {
		strcpy (tstline, hptr -> ap_obvalue);
		strcpy (official, tstline); /* no domain */
		goto storeit;
	    }
	}

	if (thechan == (Chan *) NOTOK)
	{
	    if (lexequ (hptr -> ap_obvalue, locname) ||
		lexequ (hptr -> ap_obvalue, adr_fulldmn) ||
		lexequ (hptr -> ap_obvalue, locmachine) ||
		lexequ (hptr -> ap_obvalue, adr_fmc)) {
#ifdef DEBUG
		 ll_log (logptr, LLOGFTR, "loc ref '%s' found", hptr ->
			ap_obvalue);
#endif
				/* SEK shortcut normalised address      */
		 continue;
	    }
	}

	switch ((int)dm_v2route (hptr -> ap_obvalue, official, &dmrt)) {
	    case MAYBE:
		return (RP_NS);
				  /* obtain a host reference            */
	    case OK:              /* 'tis us                            */
#ifdef DEBUG
		ll_log (logptr, LLOGFTR, "local domain reference");
#endif
		continue;         /* go to next domain reference        */

	    case NOTOK:		  /* Not a valid hostname */
				  /* SEK: first check for explicit       */
				  /* channel refs (somehow)              */
		if (cp = rindex(hptr -> ap_obvalue, '#')) {
		    *cp = 0;
		    if ((thechan = ch_nm2struct (hptr -> ap_obvalue))
						!= (Chan *) NOTOK) {
#ifdef DEBUG
			ll_log (logptr, LLOGFTR, "explicit chan spec '%s'",
				thechan -> ch_name);
#endif
			cp = 0;
			continue;
		    }
		}
		if ((thechan = ch_nm2struct("badhosts")) != (Chan *)NOTOK) {
		    strcpy (tstline, thechan->ch_host ? thechan->ch_host : "");
		    strcpy (official, hptr -> ap_obvalue);
		    goto storeit;
		}

		/*
		 * Bad name - really not known, and no place to send it.
		 */
		return (RP_BHST);

	    default:
		 if (thechan != (Chan *) NOTOK) {
#ifdef DEBUG
		    ll_log (logptr, LLOGFTR, "chan specified, checking '%s'",
			thechan -> ch_show);
#endif
		    if (dmrt.dm_argc != 1)
			return (RP_BHST);
		    switch(tb_k2val (thechan -> ch_table, TRUE,
				dmrt.dm_argv[0], tstline)) {
		    case MAYBE:
			return (RP_NS);
		    case OK:
			break;
		    default:
			return (RP_BHST);
		    }
		    strcpy (tstline, dmrt.dm_argv[0]);
		    goto storeit;                /* store it */
		}

		for (i = 0; i < (dmrt.dm_argc - 1); i++) {
				/* algorithm - first component in table is */
				/* last component in route, but before     */
				/* route in  address.                      */
				/* Thus take route components from the     */
				/* left, and add to front of explictit     */
				/* route                                   */
		    if (domain == (AP_ptr) 0) {
#ifdef DEBUG
		       ll_log (logptr,LLOGFTR,"Adding local (1) component '%s'",
					dmrt.dm_argv[i]);
#endif
			domain = ap_new (APV_DOMN, dmrt.dm_argv[i]);
		    }
		    else {
#ifdef DEBUG
		       ll_log (logptr, LLOGFTR, "Adding route component 1 '%s'",
					dmrt.dm_argv[i]);
#endif
			ap = ap_new (APV_DOMN, dmrt.dm_argv[i]);
			if (route != (AP_ptr) 0)
			    ap -> ap_ptrtype = APP_ETC;
			ap -> ap_chain = route;
			route = ap;
		    }
		}
#ifdef DEBUG
		ll_log (logptr, LLOGFTR, "Checking '%s' in channel tables",
				dmrt.dm_argv [dmrt.dm_argc - 1]);
#endif
		switch ((int)(thechan = ch_h2chan
			  (dmrt.dm_argv [dmrt.dm_argc -1], 1)))
		{
		    case MAYBE:
				/* NS failure */
				return(RP_NS);
		    case OK:      /* 'tis us                            */
				/* SEK first check for self to avoid    */
				/* loops due to references to           */
				/* non-existent local subdomains        */
			if (dmrt.dm_argc > 1)
			   if (lexequ (dmrt.dm_argv [dmrt.dm_argc - 2],
				official))
			    {
				/* Check first to see if previous entry is */
				/* in channel tables, for handling locmachine */
				switch ((int)(thechan = ch_h2chan
				  (dmrt.dm_argv [dmrt.dm_argc -2], 1)))
				{
				    case MAYBE:
					return(RP_NS);
				    case OK:
				    case NOTOK:
#ifdef DEBUG
					ll_log (logptr, LLOGTMP,
					  "Found unknown subdomain '%s'",
					  hptr -> ap_obvalue);
#endif
					return (RP_BHST);

				   default:
					/* Found host ref               */
					strcpy (tstline, dmrt.dm_argv [dmrt.dm_argc  - 2]);
					strcpy (official, tstline);
					/* Now remove first component of */
					/* route                         */
					if (route == (AP_ptr) 0) {
						ap_free (domain);
						domain = (AP_ptr) 0;
					}
					else {
						ap = route;
						route = ap -> ap_chain;
						ap_free (ap);
					}
					goto storeit;
				}
			    }
#ifdef DEBUG
			ll_log (logptr, LLOGFTR, "local host reference");
#endif
			thechan = (Chan *) NOTOK;               /* DPK */
			continue; /* go to next domain reference        */

		    case NOTOK:   /* hmmm, unknown                      */
			switch((int)dm_v2route (dmrt.dm_argv[dmrt.dm_argc -1],
				tmpstr, &tmpdmrt)) {
			case NOTOK:
			    return (RP_BHST);
			case MAYBE:
			    return (RP_NS);
			}
			thechan = (Chan *) NOTOK;
			ap = ap_new (APV_DOMN, dmrt.dm_argv[dmrt.dm_argc -1]);
			if (route != (AP_ptr) 0)
			    ap -> ap_ptrtype = APP_ETC;
			ap -> ap_chain = route;
			route = ap;
			continue;
		    default:
			strcpy (tstline,
				dmrt.dm_argv [dmrt.dm_argc - 1]);
			if (dmrt.dm_argc > 1)
			    strcpy(official, tstline);
				/* make sure official has full domain   */
			goto storeit;
		}
	}
    	/*NOTREACHED*/
    }

/*
 *  validated non-reflexive address is enqueued
 */
storeit:

     if (domain == (AP_ptr) 0)
     {
#ifdef DEBUG
	 ll_log (logptr, LLOGFTR, "Adding local (2) component '%s'", official);
#endif
	 domain = ap_new (APV_DOMN, official);
     } else {
#ifdef BOTHEND
				/* SEK - at this point we pay for not */
				/* ap_normalize earlier, and must clean up */
				/* set thechan to map any looped local refs */
	if(ap_dmnormalize (domain,  thechan) == MAYBE){
		retval = RP_NS;
		goto bugout;
	}

	for(ap = route ; ap != (AP_ptr)0 ; ap = ap->ap_chain){
		switch(ap->ap_obtype){
		    case APV_DOMN:
			if(ap_dmnormalize (ap, thechan) == MAYBE){
				retval = RP_NS;
				goto bugout;
			}
		    case APV_DLIT:
		    case APV_CMNT:
			continue;
		}
		break; /* no more route */
	}
#endif BOTHEND
#ifdef DEBUG
	ll_log (logptr, LLOGFTR, "Adding route component 2 '%s'", official);
#endif
	ap = ap_new (APV_DOMN, official);
	if (route != (AP_ptr) 0)
	    ap -> ap_ptrtype = APP_ETC;
	ap -> ap_chain = route;
	route = ap;
     }
    /* there should be some code in here to delete spurious extra components
     * in the domain and the route, this can do nasty things on some sites
     */


    cp = ap_p2s ((AP_ptr) 0, (AP_ptr) 0, local, domain, route);
    if(cp == (char *)MAYBE){
	retval = RP_NS;
	goto bugout;
    }

    auth_uinit (cp);
    nextchan = 2;
    retval = RP_OK;

    while (mgt_aok (thechan, tstline, cp, "") == 0) {
	if((thechan = ch_h2chan(tstline, nextchan++)) == (Chan *)NOTOK){
		auth_bad();	/* build an error message */
		retval = RP_NAUTH;
#ifdef DEBUG
		ll_log(logptr, LLOGFTR, "No authorized route");
#endif
		break;
	}
	else if(thechan == (Chan *)MAYBE){
		retval = RP_NS;
		break;
	}
    }

    if(rp_gval(retval) == RP_OK) {
	free(cp);

	/* put the address into a form that can be used */
	i = ap_outtype;                 /* save this setting */
	ap_outtype = thechan -> ch_apout;
	cp = ap_p2s ((AP_ptr) 0, (AP_ptr) 0, local, domain, route);
	ap_outtype = i;
	if(cp != (char *)MAYBE){
#ifdef DEBUG
	    ll_log (logptr, LLOGFTR, "linking for chan '%s'",thechan -> ch_name);
#endif
	    lnk_adinfo (thechan, tstline, cp);
	    adr_gotone = TRUE;
	    retval = RP_AOK;
	}
	else
	    retval = RP_NS;
    }

    auth_uend ();

bugout:
    if (domain != (AP_ptr) 0)
    {
	ap_sqdelete (domain, (AP_ptr) 0);
	ap_free (domain);
    }
    if (route != (AP_ptr) 0)
    {
	ap_sqdelete (route, (AP_ptr) 0);
	ap_free (route);
    }
    if (cp && cp != (char *)MAYBE)
	free (cp);
    return (retval);
}
/**/

adr_dsubmit(addr)
char	*addr;
{
    Chan    *thechan;
    extern  int mgt_nodelay;
    char    *mgt_dstgen();
    char    *xcp;

    if(mgt_nodelay == TRUE) /* can't use the delay channel */
	return(RP_NS);

    if(addr == (char *)MAYBE)	/* is this check needed ?? */
	return(RP_NS);

    if( (thechan = ch_nm2struct(mgt_dlname)) == (Chan *)NOTOK)
	return(RP_NS);      /* delay channel does not exist */

#ifdef DEBUG
    ll_log (logptr, LLOGFTR, "linking for chan '%s'",thechan -> ch_name);
#endif

    lnk_adinfo (thechan, (xcp = mgt_dstgen()), addr);
    free(xcp);
    adr_gotone = TRUE;
    return(RP_DOK);
}

adr_local (local)                       /* process host-less reference */
    char *local;
{
    Chan *thechan;
    char    qualstr[LINESIZE],
	    tmpstr[LINESIZE];
    char    old_gotone;           /* stacked value of adr_gotone        */
    char    *cp;
    int     retval, bypass = 0;

    strcpy (tmpstr, local);

    /*
     *  Important Note:
     *      The order of the following conditions determines
     *      precedence for the separators @, ., %, and !.
     *      This assumes standard (from K&R) evaluation.
     *      If your compiler is non-standard, you're screwed.
     */
    if ( (cp = rindex (tmpstr, '@')) != 0
	  || (cp = rindex (tmpstr, '%')) != 0
	  || (cp = index (tmpstr, '!')) != 0
#ifndef JNTMAIL
	  || (cp = rindex (tmpstr, '.')) != 0
#endif
	)
				/* SEK handle quoted @, and quoted or   */
				/* unquoted . or %.  If JNT Mail, do not*/
				/* handle ".".  In this case this is    */
				/* only needed for quoted @ and %       */
    {
	AP_ptr  adrtree;
	AP_ptr  loctree, domtree, routree;
	int     rtnval;

#ifdef DEBUG
	ll_log (logptr, LLOGFTR, "recursing on local part: '%s'", local);
#endif
	/*
	 *  We recurse after changing the % to an `@` after
	 *  running ap_normalize on the resultant string.
	 */
	if (*cp != '!')
	    *cp = '@';
	else
	{
	    char   tbuf [LINESIZE];
	    *cp++ = '\0';
	    sprintf (tbuf, "%s@%s", cp, tmpstr);
	    strcpy (tmpstr, tbuf);
	}
	if ((adrtree = ap_s2tree (tmpstr)) != (AP_ptr) NOTOK) {
	    if(ap_normalize ((char *) 0, (char *) 0, adrtree, (Chan *) 0) ==
						(AP_ptr)MAYBE){
		ap_sqdelete (adrtree, (AP_ptr) 0);
		ap_free (adrtree);   /* delete full tree */
		return(RP_NS);
	    }
	    ap_t2parts (adrtree, (AP_ptr *)0, (AP_ptr *)0,
			&loctree, &domtree, &routree);
	    rtnval = adr_check( loctree, domtree, routree );  /* RECURSE */
	    ap_sqdelete (adrtree, (AP_ptr) 0);
	    ap_free (adrtree);   /* delete full tree */
	    return( rtnval );
	}
    }

    if (ch_dflnam[0] == '\0') /* no local delivery && no hostname   */
	return (RP_BHST);     /*    => address not legal            */

    if ((thechan = ch_nm2struct (ch_dflnam)) == (Chan *) NOTOK)
    {
	ll_log (logptr, LLOGFTR, "default channel '%s' unknown",
		    ch_dflnam);
	return (RP_BHST);
    }
    strcpy (tmpstr, local);
			    /* local will be untouched version    */
    qualstr[0] = '\0';
    adr_gparm (tmpstr, qualstr);

    if (bypass = (local[0] == '~')) {    /* bypass alias search   */
	strcpy (tmpstr, &tmpstr[1]);
	strcpy (local, &local[1]);      /* need both if qualstr     */
    }

    old_gotone = adr_gotone;
    if ((retval = lc_search (tmpstr, qualstr, bypass)) != RP_NO)
    {
#ifdef DEBUG
	ll_log (logptr, LLOGFTR, "alias = %s", local);
#endif
	if (adr_gotone)   /* in alias file & has valid addrs    */
	    return (RP_AOK);

	adr_gotone = old_gotone;
	if (retval == RP_NAUTH) {
	    ll_log (logptr, LLOGTMP, "*** No authorized addrs: alias '%s'", tmpstr);
	    return (RP_NAUTH);
	}
	if (retval == RP_NS)
	    return (RP_NS);

	ll_log (logptr, LLOGTMP, "*** No valid addrs: alias '%s'", tmpstr);
	return (RP_USER);
    }
    adr_gotone = old_gotone;

    if (lc_pwsrch (tmpstr))    /* not even a login name.  too bad  */
    {
#ifdef DEBUG
	ll_log (logptr, LLOGFTR, "local addr='%s',parm='%s'",
		    tmpstr, qualstr);
#endif
	auth_uinit (tmpstr);
	if (qualstr[0] == '\0' && mgt_aok (thechan, "", tmpstr, ""))
	{                       /* try it without the argument      */
	    lnk_adinfo (thechan, "", tmpstr);
				/* acceptable -> insert into list   */
				/* source string NOT used           */
	    adr_gotone = TRUE;
	    auth_uend ();
	    return (RP_AOK);
	}
	else if (mgt_aok (thechan, "", tmpstr, qualstr))
	{                       /* have to check the parm, too      */
	    lnk_adinfo (thechan, "", local);
				/* acceptable -> insert into list   */
				/* note that SOURCE string is used  */
	    adr_gotone = TRUE;
	    auth_uend ();
	    return (RP_AOK);
	}
	auth_bad ();
	auth_uend ();
	ll_log (logptr, LLOGFST, "Local address '%s' not authorized",
			tmpstr);
	return (RP_NAUTH);
    }

    /*
     *  Last chance:  See if we have a forwarding channel
     */
    if( (thechan = ch_nm2struct("badusers")) != (Chan *)NOTOK) {
	if (!mgt_aok (thechan, "", local, ""))
	    return(RP_NO);

#ifdef DEBUG
	ll_log (logptr, LLOGFTR, "linking for chan '%s'", thechan -> ch_name);
#endif

	lnk_adinfo (thechan,
	    thechan->ch_host ? thechan->ch_host : "", local);
	adr_gotone = TRUE;
	return(RP_OK);
    }

    ll_log (logptr, LLOGFST, "Unknown user '%s'", tmpstr);

    return (RP_USER);         /* bad ref        */
}
/**/

adr_gparm (buf, to)               /* get local-mailbox parameter        */
    char   *buf;                  /* the buffer holding the text        */
    char   *to;                   /* put parameter into here            */
{
    register char  *strptr;

    for (strptr = buf ;; strptr++)
	 switch (*strptr)
	 {
	    case '\0':
		to[0] = '\0';
		return (FALSE);

	    case '/':
	    case '|':
	    case '=':
		strcpy (to, strptr);
		*strptr = '\0'; /* terminate the mailbox portion */
		return (TRUE);
	 }

     /* NOTREACHED */
}

/**************  (lc_)  LOCAL NAME TABLE SEARCHING  **************** */

extern Alias *al_list;            /* list of the aliases tables */

LOCVAR
struct lc_alstruct               /* previous aposinfo's are stored on  */
{                                 /*    lc_alsrch's stack when         */
				  /*    lc_alsrch needs to recurse     */
    char   *abufpos;              /* current position in alias buf      */
    char    aliasbuf[LINESIZE];   /* address-part of alias entry        */
}                  *lc_cralias;

LOCFUN
lc_afin ()                /* alst_proc input routine for file   */
{                         /* parse already-read line            */
    char    c;

    switch (c = *(lc_cralias->abufpos))
    {
	case 0377:
	case 0:
	    return( 0 );

	case '\n':
	    c = ',';
    }
    lc_cralias->abufpos++;
    return ( c );
}
/**/
lc_search (mbox, qualstr, bypass)
char	*mbox;
char	*qualstr;
int	bypass;
{
    register Alias *alp;
    struct lc_alstruct *oldalias, newalias;
    char *oldspec;
    int	oldinalias;
    register int retval;
    int badretval = RP_NO;
    char *badlist = (char *) 0;
    char tmpbuf[LINESIZE];

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "lc_search(%s, %d)", mbox, bypass);
#endif
    if ((qualstr[0] != 0) && (qualstr[0] != '='))
	return(RP_NO);  /* Skip the alias search for user/file, user|prog */

    if (adr_orgspec != NULL)		/* we have been here before ... */
	if(lexequ(mbox, adr_orgspec))	/* this is getting boring ... */
	     return (RP_NO);		/* Pretend we didn't find the alias */

    /*
     *  This could almost use the library routine aliasfetch() except
     *  we need the al_flags information down below (AL_TRUSTED?).
     */
    newalias.abufpos = newalias.aliasbuf;
    for (alp = al_list; alp != (Alias *)0; alp = alp->al_next) {
	if (bypass && !(alp->al_flags & AL_NOBYPASS))
	    continue;
	if ((retval = tb_k2val (alp->al_table, TRUE, mbox,
				newalias.abufpos)) == OK)
	    break;
	if (retval == MAYBE)
	    badretval = RP_NS;	/* NS timeout? */
    }
    if (alp == (Alias *)0)
	return (badretval);

    strcpy (tmpbuf, mbox);
    strcat (tmpbuf, qualstr);

    if (lnk_adinfo ((Chan *) 0, "", tmpbuf) == OK) {
	adr_gotone = TRUE;
	return (RP_AOK);       /* already did this alias              */
    }

#ifdef DEBUG
    ll_log(logptr, LLOGFTR, "lc_search, newalias '%s'", newalias.abufpos);
#endif
    if (tracing)
	printf("alias: %s => %s\n", mbox, newalias.abufpos);
    if (qualstr[0] != '\0')
    {
	char *p;
				/* Found alias for username.  If username   */
				/* was part of "user=foo", check that alias */
				/* was just a simple username (otherwise    */
				/* alias=foo will make no sense).           */
	if ((index (newalias.aliasbuf, '|') != 0) ||
	    (index (newalias.aliasbuf, '/') != 0) ||
	    (index (newalias.aliasbuf, ',') != 0)) {
	    ll_log (logptr, LLOGTMP, "Illegal to use = in alias '%s:%s'",
			mbox,  newalias.aliasbuf);
	    return (RP_NO);
	}
	if ((p = rindex (newalias.aliasbuf, '@')) == 0)
	    strcat (newalias.aliasbuf, qualstr);
	else {
	    *p++ = '\0';
	    sprintf (tmpbuf, "%s%s@%s", newalias.aliasbuf, qualstr, p);
	    strcpy (newalias.aliasbuf, tmpbuf);
	}
    }

    /* so save previous info on stack     */
    oldspec = adr_orgspec;
    oldalias = lc_cralias;
    oldinalias = mgt_inalias;

    adr_orgspec = mbox;
    lc_cralias = &newalias;  /*   to allow processing new list     */
    mgt_inalias = (alp->al_flags & AL_TRUSTED ? TRUE : FALSE);

    adr_gotone = FALSE;
    if (rp_isbad (retval=alst_proc(lc_afin, FALSE, (int (*)()) 0, &badlist))) {
	char        linebuf[LINESIZE];

	ll_log (logptr, LLOGTMP, "Bad address in alias %s", mbox);
	if (tracing)
	    printf ("Bad address in alias %s\n", mbox);
	sprintf (linebuf, "%s %s", locname, sitesignature);
	ml_1adr (NO, NO, linebuf, "Bad address in alias", supportaddr);
	sprintf (linebuf, "Found bad address in alias '%s'.\n", mbox);
	ml_txt (linebuf);
	if (retval == RP_NAUTH)
	    ml_txt ("    (Authorization problem with valid address)\n");
	sprintf (linebuf, "The alias was '%s'.\n\n", newalias.aliasbuf);
	ml_txt (linebuf);
	if (badlist) {
	    sprintf (linebuf, "There were problems with:\n");
	    ml_txt (linebuf);
	    ml_txt (badlist);
	    free (badlist);
	    sprintf (linebuf, 
	"\nThe remaining addresses in the alias were used for submission.\n\n");
	    ml_txt (linebuf);
	}
	if (ml_end(OK) != OK)
	    ll_log (logptr, LLOGFAT, "Can't send to supportaddr");
    }
    adr_orgspec = oldspec;
    lc_cralias = oldalias;   /* pop previous info off stack        */
    mgt_inalias = oldinalias;

    if ((retval == RP_NAUTH) || (retval == RP_NS))
	return (retval);
    return (RP_AOK);
}

lc_pwsrch (name)                 /* search login names (password file) */
char  *name;                      /* search key                         */
{
    extern struct passwd *getpwmid ();
    extern int errno;
    char namebuf[LINESIZE];
    register int ind;

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "lc_pwsrch(%s)", name);
#endif

    errno = 0;

    if (getpwmid (name) != NULL)   /* case-independent name search       */
	return (TRUE);            /*   found an entry                   */

    if (errno != 0)
	ll_log (logptr, LLOGTMP, "Strange errno after pwsrch failure (%d)",
					errno);

    if ((ind = strindex ("|", name)) >= 0 ||
	(ind = strindex ("/", name)) >= 0   )
    {                             /* piped msg or stored into file      */
	blt (name, namebuf, ind);
	namebuf[ind] = '\0';
#ifdef DEBUG
	ll_log (logptr, LLOGBTR, "lc_pwsrch base part (%s)", namebuf);
#endif
	return ((getpwmid (namebuf) != NULL) ? TRUE : FALSE);
    }

    return (FALSE);               /* return failure                     */
}
     *  we need the al_flags information down below (AL_TRUSTED?).
     */
    newalias.abummdf/src/submit/auth_submit.c   444      0     12       62115  3620510532  11554 #include "util.h"
#include "mmdf.h"
#include "cmd.h"
#include "ch.h"
#include "dm.h"
#include "ap.h"

/*
 *  This module applies authorization on the basis of channel
 *  sending host, receiving host,  sender, and recipient.
 *
 *  Initial coding Steve Kille.  Jan 84
 */

extern struct ll_struct *logptr;

extern long tx_msize;
extern int adruid;

extern char     *authfile;              /* file with warning message    */
extern char     *authrequest;           /* where to send error request to */

extern char *locdomain;
extern char *locname;
extern char *adr_fulldmn;
extern char *supportaddr;
extern char *mgt_return;

Table *authtable;

char auth_msg [LINESIZE*2];

				/* space for auth error message   */
				/* to be passed upwards by submit */
extern char *logdfldir;

extern struct ll_struct authlog;
struct ll_struct *alogptr = &authlog;

char *user_string = "username";

extern char *index();
extern char *rindex();
extern long atol();
extern char *dupfpath();
extern char *strdup();
extern char *multcat();

char *get_route();


/*
 *  Structures needed for by user authorization
 */

				/* values for user auth */

#define AUTH_SEND 1             /* send only            */
#define AUTH_RECV 2             /* receive only         */
#define AUTH_BOTH 3             /* send + receive       */
#define AUTH_LIST 4             /* mailing list         */
#define AUTH_BAD  -1            /* not authorized       */
#define AUTH_EXPIRE -2          /* authorization expired*/

#define AUTH_NCHANS 15          /* max channels can have */

struct auth_struct
{
    int         a_type;         /* auth type            */
    int         a_nchans;       /* number of channels   */
    Chan        *a_chans [AUTH_NCHANS];
				/* channel list         */
    char        *a_addr;        /* the address          */
    char        *a_reason;      /* rason for expiry     */
};

LOCVAR
struct auth_struct auth_sender, /* sender authorization         */
		   auth_rcvr;   /* current receiver auth        */


LOCVAR
int  auth_do;                   /* is this mesage being authorized?     */
				/* prevents logging on local and other  */
				/* uninteresting traffic                */

LOCVAR
char *route_in;                 /* route assocaited with incoming       */
				/* address                              */
LOCVAR
char *route_out;

LOCVAR
char *cn_in, *cn_out;           /* storage for channel names            */
				/* needed for bad auth logging          */


/**/
				/* Authorization initialisation policy  */
				/* Store data on sender                 */

auth_init (sender, trust)
char *sender;                   /* Message sender (normalized)          */
int trust;                      /* can we trust this? (NRMFROM)         */
{
     char *p;
#ifdef DEBUG
     ll_log (logptr, LLOGBTR, "auth_init (%s)", sender);
#endif

     auth_sender.a_type = AUTH_BAD;
     auth_do = FALSE;
			      /* SEK this is needed so that WARN/LOG  */
			      /* work correctly for host auth         */

     if (alogptr -> ll_file[0] != '/')
	 alogptr -> ll_file = dupfpath (alogptr -> ll_file, logdfldir);

				/* strip  local domain for loocup      */
     auth_sender.a_addr= strdup (sender);
     auth_sender.a_nchans = 0;    /* make sure this has safe value        */
     auth_sender.a_reason = (char *) 0;

     if (isstr(sender))
	 route_in = get_route (sender);
     else
	 route_in = (char *)0;

     if ((authtable = tb_nm2struct ("auth")) == (Table *) NOTOK)
	return;

     if ( auth_sender.a_addr && auth_sender.a_addr[0] != '@')
     {
	if ((p = index (auth_sender.a_addr, '@')) != (char *) 0)
	   if (lexequ (p + 1, adr_fulldmn))
		*p = '\0';
     }
				  /* Table exists, so look up sender      */
     if (!trust && (adruid != 0))
				/* This can't be authorized             */
	auth_sender.a_type = AUTH_BAD;
     else
	auth_fetch (&auth_sender);
}
/**/
			/* initialise user check, as user may be */
			/*  checked in the context of a number of */
			/* channels                               */
auth_uinit (adr)
char *adr;               /* address being authorized             */
{
    char        *p,
		*q;
#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "auth_uinit (%s)", adr);
#endif
    auth_msg [0] = '\0';
    route_out = (char *) 0;

    if (authtable == (Table *) NOTOK)
    {
	auth_rcvr.a_addr = strdup (adr);
	auth_rcvr.a_type= AUTH_BAD;
			/* Assume no authorization as default   */
	return;
    }

			/* this jiggling is to map local names */
			/* on other local machines to just      */
			/* user names                           */
			/* e.g.  ~foo@44c.ucl-cs.ac.uk -> foo   */
    if (adr[0] != '@')
    {
	p = index (adr, '@');
	if (p != (char *) 0)
	{
	    q = index (p + 1, '.');
	    if (lexequ (p+1, adr_fulldmn) ||
		    ((q != 0) && lexequ (q+1, adr_fulldmn)))
	    {
		*p = '\0';
		if (adr[0] == '~')
		    auth_rcvr.a_addr = strdup (&adr[1]);
		else
		    auth_rcvr.a_addr = strdup (adr);
		*p= '@';
	    }
	    else
		auth_rcvr.a_addr = strdup (adr);
	}
	else
	    auth_rcvr.a_addr = strdup (adr);
    }
    else
	auth_rcvr.a_addr = strdup (adr);


				/* Do not look up until needed - just mark */
    auth_rcvr.a_nchans = -1;
    auth_rcvr.a_reason = (char *) 0;
}

				/* clean up user structures             */
auth_uend ()
{
#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "auth_uend (), %d", authtable);
#endif
    free (auth_rcvr.a_addr);
    if (authtable != (Table *) NOTOK)
    {
	if (auth_rcvr.a_reason != (char *) 0)
	    free (auth_rcvr.a_reason);
    }
    if (route_out != (char *) 0)
	free (route_out);
}


/**/
				/* Perform checks for a given address  */
LOCVAR char *a_fmt = "i='%s' o='%s' a='%s' r='%s' r='%s'";
LOCVAR char *h_fmt = "i='%s' o='%s' a='%s' r='%s' hi='%s' ho='%s'";

#define AOKNUM 50

auth_user (h_in, h_out, chan_in, chan_out)
char *h_in;                     /* host (not domain) directly from      */
char *h_out;                    /* host (not domain) being connected to */
Chan *chan_in;                  /* incoming channel                     */
Chan *chan_out;                 /* outgoing channel                     */
{
    char ch_in_auth;            /* user auth needed for incomin chan    */
    char ch_out_auth;           /* ditto for outgoing chan              */
    char in_hostanduser;         /* flag if both host AND user auth needed */
    char out_hostanduser;        /* flag if both host AND user auth needed */
    int gotinbound;
    int gotoutbound;
    char linebuf [LINESIZE];
    int argc;
    char *argv[AOKNUM];
    int ind;
    char *in_auth_str;
    char *out_auth_str;
    char *host_in;
    char *host_out;

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "auth_user (hin='%s', hout='%s', cin='%s', cout='%s'",
		h_in, h_out, chan_in -> ch_show, chan_out -> ch_show);
#endif
    ch_in_auth = chan_in -> ch_auth & CH_IN_AUTH;
    ch_out_auth = chan_out -> ch_auth & CH_OUT_AUTH;

    /* return immediately if no checks needed */
    if ((ch_in_auth == 0) && (ch_out_auth == 0))
	return (TRUE);

    cn_in = chan_in -> ch_name;
    cn_out = chan_out -> ch_name;
    in_hostanduser = FALSE;
    out_hostanduser = FALSE;

    if ((chan_out -> ch_auth & CH_DHO) == CH_DHO)
    {
	if (route_out == (char*) 0)
	    route_out = get_route (auth_rcvr.a_addr);
	host_out = route_out;
    }
    else
	host_out = h_out;

    if ((chan_in -> ch_auth & CH_DHO) == CH_DHO)
	host_in = route_in;
    else
	host_in = h_in;

    if ((chan_out -> ch_auth & CH_HAU) == CH_HAU)
	out_hostanduser = TRUE;
    if ((chan_in -> ch_auth & CH_HAU) == CH_HAU)
	in_hostanduser = TRUE;

				/* initallialy check hostwise agreements */
				/* log any messages so authorized        */

				/* 1 outbound host, ch_out              */
				/* h_out:c_in/h_in                      */
    if (chan_out -> ch_outdest != (Table *) 0)
    {
	if (tb_k2val (chan_out -> ch_outdest, TRUE, host_out, linebuf)==OK)
	{                         /* this destination is listed         */
	    argc = str2arg (linebuf, AOKNUM, argv, (char *)0);
	    if (argc == 0)        /* authorized for all senders         */
	    {
		auth_log (h_fmt, chan_in -> ch_name, chan_out -> ch_name,
			auth_rcvr.a_addr, "OH", "", host_out);
		if (out_hostanduser)
		    goto douser;
		return (TRUE);
	    }
	    for (ind = 0; ind < argc; ind++)
				  /* all hosts on channel authorized?  */
		if (lexequ (chan_in -> ch_name, argv[ind]))
		{
		    auth_log (h_fmt, chan_in -> ch_name, chan_out -> ch_name,
				auth_rcvr.a_addr, "CH", "", host_out);
		    if (out_hostanduser)
			goto douser;
		    return (TRUE); /*  this pair is authorized      */
		}
	    for (ind = 0; ind < argc; ind++)
				       /* host, itself, authorized?    */
		if (lexequ (host_in, argv[ind]))
		{
		    auth_log (h_fmt, chan_in -> ch_name, chan_out -> ch_name,
			auth_rcvr.a_addr, "HH", host_in, host_out);
		    if (out_hostanduser)
			goto douser;
		    return (TRUE);
		}
	}
    }

				/* 2 inbound hosts, ch_in               */
				/* h_in:c_out/h_out                     */

    if (chan_in -> ch_insource != (Table *) 0)
    {
	if (tb_k2val (chan_in -> ch_insource, TRUE, host_in, linebuf)==OK)
	{                         /* this destination is listed         */
	    argc = str2arg (linebuf, AOKNUM, argv, (char *)0);
	    if (argc == 0)        /* authorized for all senders         */
	    {
		auth_log (h_fmt, chan_in -> ch_name, chan_out -> ch_name,
			auth_rcvr.a_addr, "IH", host_in, "");
		if (in_hostanduser)
		    goto douser;
		return (TRUE);
	    }
	    for (ind = 0; ind < argc; ind++)
				  /* all hosts on channel authorized?  */
		if (lexequ (chan_out -> ch_name, argv[ind]))
		{
		    auth_log (h_fmt, chan_in -> ch_name, chan_out -> ch_name,
				auth_rcvr.a_addr, "HC", host_in, "");
		    if (in_hostanduser)
			goto douser;
		    return (TRUE); /*  this pair is authorized      */
		}
	    for (ind = 0; ind < argc; ind++)
				       /* host, itself, authorized?    */
		if (lexequ (host_out, argv[ind]))
		{
		    auth_log (h_fmt, chan_in -> ch_name, chan_out -> ch_name,
			auth_rcvr.a_addr, "HH", host_in, host_out);
		    if (in_hostanduser)
			goto douser;
		    return (TRUE);
		}
	}
    }


					/* 3 inbound hosts, ch_out      */
					/* c_in:h_out                   */
					/* h_in:h_out                   */


    if (chan_out -> ch_outsource != (Table *) 0)
    {
	if (tb_k2val (chan_out -> ch_outsource, TRUE,
					chan_in -> ch_name, linebuf)==OK)
	{                         /* source channel is listed           */
	    argc = str2arg (linebuf, AOKNUM, argv, (char *)0);
	    if (argc == 0)        /* authorized for all receivers       */
	    {
		auth_log (h_fmt, chan_in -> ch_name, chan_out -> ch_name,
				auth_rcvr.a_addr, "CC", "", "");
		if (out_hostanduser)
		    goto douser;
		return (TRUE);
	    }
	    for (ind = 0; ind < argc; ind++)
		if (lexequ (host_out, argv[ind]))
		{
		    auth_log (h_fmt, chan_in -> ch_name, chan_out -> ch_name,
			auth_rcvr.a_addr, "CH", "", host_out);
		    if (out_hostanduser)
			goto douser;
		    return (TRUE); /* this channel is authorized        */
		}
	}
	if (tb_k2val (chan_out -> ch_outsource, TRUE,
				host_in, linebuf)==OK)
	{                         /* this source is listed              */
	    argc = str2arg (linebuf, AOKNUM, argv, (char *)0);
	    if (argc == 0)        /* authorized for all receivers       */
	    {
		auth_log (h_fmt, chan_in -> ch_name, chan_out -> ch_name,
			auth_rcvr.a_addr, "IH", host_in, "");
		if (out_hostanduser)
		    goto douser;
		return (TRUE);
	    }
	    for (ind = 0; ind < argc; ind++)
		if (lexequ (host_out, argv[ind]))
		{
		    auth_log (h_fmt, chan_in -> ch_name, chan_out -> ch_name,
			auth_rcvr.a_addr, "HH", host_in, host_out);
		    if (out_hostanduser)
			goto douser;
		    return (TRUE); /* this pair is authorized           */
		}
	}
    }

				/* 4, outbound hosts, ch_in               */
				/* h_out:h_in                             */
				/* c_out:h_in                             */

    if (chan_in -> ch_indest != (Table *) 0)
    {
	if (tb_k2val (chan_in -> ch_indest, TRUE,
					chan_out -> ch_name, linebuf)==OK)
	{                         /* source channel is listed           */
	    argc = str2arg (linebuf, AOKNUM, argv, (char *)0);
	    if (argc == 0)        /* authorized for all receivers       */
	    {
		auth_log (h_fmt, chan_in -> ch_name, chan_out -> ch_name,
				auth_rcvr.a_addr, "CC", "", "");
		if (in_hostanduser)
		    goto douser;
		return (TRUE);
	    }
	    for (ind = 0; ind < argc; ind++)
		if (lexequ (host_in, argv[ind]))
		{
		    auth_log (h_fmt, chan_in -> ch_name, chan_out -> ch_name,
			auth_rcvr.a_addr, "HC", host_in, "");
		    if (in_hostanduser)
			goto douser;
		    return (TRUE); /* this channel is authorized        */
		}
	}
	if (tb_k2val (chan_in -> ch_indest, TRUE,
				host_out, linebuf)==OK)
	{                         /* this source is listed              */
	    argc = str2arg (linebuf, AOKNUM, argv, (char *)0);
	    if (argc == 0)        /* authorized for all receivers       */
	    {
		auth_log (h_fmt, chan_in -> ch_name, chan_out -> ch_name,
			auth_rcvr.a_addr, "OH", "", host_out);
		if (in_hostanduser)
		    goto douser;
		return (TRUE);
	    }
	    for (ind = 0; ind < argc; ind++)
		if (lexequ (host_in, argv[ind]))
		{
		    auth_log (h_fmt, chan_in -> ch_name, chan_out -> ch_name,
			auth_rcvr.a_addr, "HH", host_in, host_out);
		    if (in_hostanduser)
			goto douser;
		    return (TRUE); /* this pair is authorized           */
		}
	}
    }

#ifdef DEBUG
    ll_log (logptr, LLOGFTR, "Checking auth:  need in =%o out =%o",
		ch_in_auth, ch_out_auth);
#endif

    if (in_hostanduser || out_hostanduser)
    {
	auth_log ("NO HOST AUTH i='%s' o='%s' s='%s' r='%s' msg='%s'",
	   cn_in, cn_out, auth_sender.a_addr, auth_rcvr.a_addr, auth_msg);
	return (FALSE);
    }


douser:
    in_auth_str = "";
    out_auth_str = "";

    if (ch_in_auth == 0)
	gotinbound = TRUE;
    else
	gotinbound = FALSE;

    if (ch_out_auth ==  0)
	gotoutbound = TRUE;
    else
	gotoutbound = FALSE;


			/* lookup value if needed               */
    if ((auth_rcvr.a_nchans < 0) && (authtable != (Table *) NOTOK))
    {
	auth_rcvr.a_nchans = 0;
	auth_fetch (&auth_rcvr);
    }


			/* now see if we can find any user authorization */
			/* if recipeint is authorized list, charge by pref */
    if (auth_rcvr.a_type == AUTH_LIST)
    {
	if (!gotinbound)
	    if (gotinbound = auth_ok (chan_in, &auth_rcvr, AUTH_RECV))
	    {
		in_auth_str = "IL";
		if (gotoutbound)
		{
		     auth_log (a_fmt, chan_in -> ch_name, chan_out -> ch_name,
			auth_rcvr.a_addr, in_auth_str, out_auth_str);
		    return (TRUE);
		}
	    }
	if (!gotoutbound)
	    if (gotoutbound = auth_ok (chan_out, &auth_rcvr, AUTH_RECV))
	    {
		out_auth_str ="OL";
		if (gotinbound)
		{
		     auth_log (a_fmt, chan_in -> ch_name, chan_out -> ch_name,
			auth_rcvr.a_addr, in_auth_str, out_auth_str);
		    return (TRUE);
		}
	    }
    }
			/* Now  check sender                            */
    if (!gotinbound)
	if (gotinbound = auth_ok (chan_in, &auth_sender, AUTH_SEND))
	{
	    in_auth_str = "IS";
	    if (gotoutbound)
	    {
		 auth_log (a_fmt, chan_in -> ch_name, chan_out -> ch_name,
		    auth_rcvr.a_addr, in_auth_str, out_auth_str);
		return (TRUE);
	    }
	}
	else if (auth_rcvr.a_type == AUTH_LIST)
	    switch (ch_in_auth)
	    {
		case  CH_IN_BLOCK:
		    return (FALSE);
		case  CH_IN_WARN:
		    auth_warn ();
		default:
		    in_auth_str = "*I";
		    if (gotoutbound)
		    {
			auth_log (a_fmt, chan_in -> ch_name,
			    chan_out -> ch_name,
			    auth_rcvr.a_addr, in_auth_str, out_auth_str);
			return (TRUE);
		    }
		    gotinbound = TRUE;
	    }

    if (!gotoutbound)
	if (gotoutbound = auth_ok (chan_out, &auth_sender, AUTH_SEND))
	{
	    out_auth_str ="OS";
	    if (gotinbound)
	    {
		 auth_log (a_fmt, chan_in -> ch_name, chan_out -> ch_name,
			auth_rcvr.a_addr, in_auth_str, out_auth_str);
		return (TRUE);
	    }
	}
	else if (auth_rcvr.a_type == AUTH_LIST)
	    switch (ch_out_auth)
	    {
		case  CH_OUT_BLOCK:
		    return (FALSE);
		case  CH_OUT_WARN:
		    auth_warn ();
		default:
		    out_auth_str = "*O";
		    auth_log (a_fmt, chan_in -> ch_name, chan_out -> ch_name,
			auth_rcvr.a_addr, in_auth_str, out_auth_str);
		    return (TRUE);
	    }

				/* if not done aready, check receiver   */
    if (auth_rcvr.a_type == AUTH_LIST)
	 return (FALSE);


    if (!gotinbound)
	if (gotinbound = auth_ok (chan_in, &auth_rcvr, AUTH_RECV))
	{
	    in_auth_str = "IR";
	    if (gotoutbound)
	    {
		 auth_log (a_fmt, chan_in -> ch_name, chan_out -> ch_name,
			auth_rcvr.a_addr, in_auth_str, out_auth_str);
		return (TRUE);
	    }
	}
	else
	    switch (ch_in_auth)
	    {
		case  CH_IN_BLOCK:
		    return (FALSE);
		case  CH_IN_WARN:
		    auth_warn ();
		default:
		    in_auth_str = "*I";
		    if (gotoutbound)
		    {
			  auth_log (a_fmt, chan_in -> ch_name,
				chan_out -> ch_name,
				auth_rcvr.a_addr, in_auth_str, out_auth_str);
			  return (TRUE);
		    }
		    gotinbound = TRUE;
	    }

    if (!gotoutbound)
	if (gotoutbound = auth_ok (chan_out, &auth_rcvr, AUTH_RECV))
	{
	    out_auth_str  = "OR";
	    if (gotinbound)
	    {
		 auth_log (a_fmt, chan_in -> ch_name, chan_out -> ch_name,
			auth_rcvr.a_addr, in_auth_str, out_auth_str);
		 return (TRUE);
	    }
	}
	else
	    switch (ch_out_auth)
	    {
		case  CH_OUT_BLOCK:
		    return (FALSE);
		case  CH_OUT_WARN:
		    auth_warn ();
		default:
		    auth_log (a_fmt, chan_in -> ch_name, chan_out -> ch_name,
			auth_rcvr.a_addr, in_auth_str, "*O");
		    return (TRUE);
	    }

			/* SEK shouldn't get here - I hope              */
#ifdef DEBUG
    ll_log (logptr, LLOGTMP,  "Auth_user - did the impossible");
#endif
    return (FALSE);
}
/**/
LOCFUN auth_warn ()
{
    FILE *fp;
    char buf [LINESIZE];
#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "auth_warn ()");
#endif
    if (!isstr(mgt_return))
	return;
     sprintf (buf, "Authorization Warning Daemon <%s>", authrequest);
     ml_1adr (NO, YES, buf, "Authorization Warning",
		mgt_return);
     ml_txt ("\nNot authorized to send mail to address: ");
     ml_txt (auth_rcvr.a_addr);
     ml_txt ("\n\n");
     if ((fp = fopen (authfile, "r")) == 0)
     {
	ml_txt ("Message  sent anyhow\n\n");
	ll_log (logptr, LLOGTMP,  "Auth warning file '%s' not found",
		authfile);
     }
     else
	 ml_file (fp);
     if (ml_end (OK) != OK)
	ll_log (logptr, LLOGTMP, "Failed to send auth warning to '%s'",
		auth_sender.a_addr);
}
/**/
				/* what to do if bad address            */
auth_bad ()
{
#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "auth_bad()");
#endif
				/* build string for submit to pass up */
    if (auth_sender.a_type == AUTH_EXPIRE)
    {
	if (auth_sender.a_reason  == (char *) 0)
	    sprintf (auth_msg, "sender '%s' no longer authorized for address '%s'",
		auth_sender.a_addr, auth_rcvr.a_addr);
	else
	    sprintf (auth_msg, "sender '%s' no authorization for address '%s' for reason: (%s)",
		auth_sender.a_addr,
		auth_rcvr.a_addr,
		auth_sender.a_reason);
    }
    else if (auth_rcvr.a_type == AUTH_EXPIRE)
    {
	if (auth_rcvr.a_reason == (char *) 0)
	    sprintf (auth_msg, "recipient '%s' no longer authorized",
			auth_rcvr.a_addr);
	else
	    sprintf (auth_msg, "recipient '%s' no authorization for reason: (%s)",
		auth_rcvr.a_addr,
		auth_rcvr.a_reason);
    }
    else
	sprintf (auth_msg, "sender '%s' not authorized to send to address '%s'",
		     auth_sender.a_addr, auth_rcvr.a_addr);

    auth_log ("BAD AUTH i='%s' o='%s' s='%s' r='%s' msg='%s'",
	cn_in, cn_out, auth_sender.a_addr, auth_rcvr.a_addr, auth_msg);
#ifdef  UCL
    strcat (auth_msg, " : send textless msg to auto-mailbox ");
    strcat (auth_msg, authrequest);
    strcat (auth_msg, " for info");
#else   UCL
    strcat (auth_msg, " : send queries to ");
    strcat (auth_msg, authrequest);
#endif
}


/**/
				/* Perform any authorization ending policy */

auth_end ()
{
#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "auth_end()");
#endif
    if (auth_do)
	auth_log ("END size='%ld', sender='%s'", tx_msize, auth_sender.a_addr);
    if (auth_sender.a_addr != (char *)0)
	free (auth_sender.a_addr);
    if (auth_sender.a_reason != (char *) 0)
	free (auth_sender.a_reason);
    if (route_in != (char *)0)
	free (route_in);
}

/**/

				/* check if chan matches auth           */
LOCFUN
auth_ok (chan, auth, direction)
Chan    *chan;
struct auth_struct *auth;
int     direction;              /* send or receive                      */
{
    int i;

#ifdef DEBUG
    ll_log (logptr, LLOGFTR, "auth_ok (c='%s', u='%s', d='%s')",
	chan -> ch_name, auth -> a_addr,
	(direction == AUTH_RECV) ? "receive" : "send");
#endif

    if (auth  -> a_type < 0)
	return (FALSE);

    if (direction == AUTH_RECV)
    {
	switch (auth -> a_type)
	{
	    case AUTH_SEND:
		return (FALSE);
	}
    }
    else
    {
	switch (auth -> a_type)
	{
	    case AUTH_RECV:
	    case AUTH_LIST:
		return (FALSE);
	}
    }

    for (i = 0; i < auth -> a_nchans; i++)
	if (chan == auth -> a_chans [i])
	{
#ifdef DEBUG
	    ll_log (logptr,  LLOGFTR, "Got valid authorization");
#endif
	    return (TRUE);
	}
    return (FALSE);
}

/**/

LOCVAR Cmd
	authmap [] =
{
	"both", AUTH_BOTH,      0,
	"send", AUTH_SEND,      0,
	"recv", AUTH_RECV,      0,
	"list", AUTH_LIST,      0,
	"expire", AUTH_EXPIRE,  0,
	0,      0,              0,
};


				/* Get user authorization from table        */
LOCFUN
auth_fetch (auth)
struct auth_struct *auth;       /* where to put the data                    */
{
    char        buf [LINESIZE];
    char        *argv [AUTH_NCHANS + 1];
    int         i,
		argc;
#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "auth_fetch (%s)", auth -> a_addr);
#endif
				/* implictly auth table exists             */
    if (tb_k2val (authtable, TRUE, auth -> a_addr, buf)!=OK)
    {
	auth -> a_type = AUTH_BAD;
	return;
    }

#ifdef DEBUG
    ll_log (logptr, LLOGFTR, "user '%s' has auth '%s'", auth -> a_addr, buf);
#endif
				/* authorization ha form of type  */
				/* followed by a list of channels */
    if ((argc = str2arg (buf, AUTH_NCHANS + 1, argv, (char *) 0))
		== NOTOK)
    {
	ll_log (logptr, LLOGTMP, "User '%s' has more that %d chans",
			auth -> a_addr, AUTH_NCHANS);
	auth -> a_type = AUTH_BAD;
	return;
    }
				/* get auth type                */
    switch (auth -> a_type = cmdsrch (argv[0], 0, authmap))
    {
	case AUTH_BOTH:
	case AUTH_SEND:
	case AUTH_RECV:
	case AUTH_LIST:
	    break;
	case  AUTH_EXPIRE:
	    break;
	default:
	    auth -> a_type = AUTH_EXPIRE;
	    auth -> a_reason = strdup (argv [0]);
#ifdef DEBUG
	    ll_log (logptr, LLOGFTR, "User '%s' expire reason '%s'",
			auth -> a_addr, argv[0]);
#endif
	    break;
    }


			/* now fill in the chans                */
    auth -> a_nchans = 0;
    for (i = 1; i < argc; i++)
    {
	if ((auth -> a_chans [auth -> a_nchans] = ch_nm2struct (argv[i]))
		== (Chan *) NOTOK)
	    ll_log (logptr, LLOGBTR,
		"Unknown channel name '%s' for user '%s'", argv[i], auth -> a_addr);
	else
	     auth -> a_nchans++;
    }
#ifdef  DEBUG
    ll_log (logptr, LLOGFTR, "%d channels in total type = %d",
		auth ->  a_nchans, auth -> a_type);
#endif
}

/**/

				/* this loggin might be done differnetly */
				/* later                                 */

extern char *mq_munique;

				/* Log authorization info                  */
/*VARARGS1*/
LOCFUN
auth_log (format, a1, a2, a3, a4, a5, a6, a7, a8, a9)
char *format,
	*a1, *a2, *a3, *a4, *a5, *a6, *a7, *a8, *a9;
{
	char fbuf [LINESIZE];

	auth_do = TRUE;         /* we now care about this message       */
	sprintf (fbuf, "%%s: %s", format);
	ll_log (&authlog, LLOGFST, fbuf, mq_munique, a1, a2, a3, a4, a5,
		a6, a7, a8, a9);
}

/**/

/*
 * get_route is used to determine the route assocaited with
 * a given address.  This allows authorization on the whole
 * route and not just the connect host.
 * It works by:
 * 1) breaking the address into parts
 * 2) remove the username from the local part, assiming a mix of percent
 *      and UUCP routes.
 *      The username starts right of the rightmost "!", and is
 *      terminated by "%".
 * 3) Put it all back together again
 */
char *get_route (adr)
char *adr;
{
    char        *p, *q, *r;
    AP_ptr      local,          /* pointers for parse coponents         */
		domain,
		route;
    AP_ptr      ap;

#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "get_route (%s)", adr);
#endif

    ap = ap_s2tree (adr);

    ap_t2parts (ap, (AP_ptr *) 0, (AP_ptr *) 0, &local, &domain, &route);

#ifdef DEBUG
   ll_log (logptr, LLOGFTR, "local part = '%s'", local -> ap_obvalue);
#endif
				/* jiggle the local part                */
    if ((p = rindex (local -> ap_obvalue, '!')) == (char *) 0)
    {
	if ((p = index (local -> ap_obvalue, '%')) != (char *) 0)
	{
	     q = multcat (p, user_string, (char *)0);
	     free (local -> ap_obvalue);
	     local -> ap_obvalue = q;
	}
	else
	{
				/* local part really sseems to be user */
	    free (local -> ap_obvalue);
	    local -> ap_obvalue = strdup (user_string);
	}
    }
    else
    {
				/* p points to last "!"                 */
	q = index (p, '%');
	p++;
	*p = '\0';
	if (q != (char *) 0)
	    r = multcat (local -> ap_obvalue, user_string, q, (char *)0);
	else
	    r  = multcat (local -> ap_obvalue, user_string, (char *)0);
	*p =  '%';
	free (local -> ap_obvalue);
	local -> ap_obvalue = r;
    }

#ifdef DEBUG
   ll_log (logptr, LLOGFTR, "Munged local part = '%s'", local -> ap_obvalue);
#endif

    p = ap_p2s ((AP_ptr) 0, (AP_ptr) 0, local, domain, route);

    ap_sqdelete (ap, (AP_ptr)0);
    ap_free (ap);

#ifdef DEBUG
    ll_log (logptr,LLOGFTR, "Auth ROUTE is '%s'",(p==(char*)MAYBE)?"MAYBE":p);
#endif
    return (p);
}
    i,
		argc;
#ifdef DEBUG
    ll_log (logptr, LLOGBTR, "auth_fetch (%s)", auth -> a_addr);
#endif
				/* implictly auth table exists             */
    if (tb_k2val (authtable, TRUE, auth -> a_addr, buf)!=OK)
    {
	auth -> a_type = AUTH_BAD;
	return;
    }

#ifdef DEBUG
    ll_log (logptr, LLOGFTR, "user '%s' has auth '%s'", auth -> a_addr, buf);
#endif
				/* authorization ha form of type  */
				/* followed by a list of channemmdf/conf/   755      0     12           0  3652760052   5620 mmdf/conf/vgr/   755      0     12           0  3671074637   6426 mmdf/conf/vgr/chan.c   444      0     12        6062  3620510310   7543 #include "util.h"
#include "mmdf.h"
#include "ch.h"
#include "dm.h"

char    *ch_dflnam = "local";

Alias	*al_list = (Alias *)0;

/*******************  MAIL IDENTIFIER  SELECTION  ***********************/

/*
 *	The global mid_enable controls the use of an alternate
 *	mail name system refered to as Mail-Ids.  When mail-ids are
 *	in use, login-ids and mail-ids are nolong bound to one another
 *	by the system account database, and are bound only by mapping
 *	tables.  The tables "mailids" and "users" will always be present
 *	though they may not be used.  "mailids" maps a mailid to a
 *	system account.  The table "users" maps a username to a mailid.
 */

int	mid_enable = 0;

Table
	tb_mailids =		/* Used in getpwmid() */
{
    "mailids", "Mail-ID to Username Mappings", "mailids", 0, 0l
},
	tb_users =		/* Used in getmailid() */
{
    "users", "Username to Mail-ID Mappings", "users", 0, 0l
};


/*
 *  The following set of structures provides a complete list of channels
 *  known to MMDF.
 *
 *  It is followed by two arrays that list the channels in useful orders.
 *
 *  ch_tbsrch[] is a full list, of all known channels, and is primarily
 *  used to guide the ch_table routines when treating the hostname space as
 *  flat (linear).  It also is used by lnk_submit, for sorting the address
 *  linked-list.
 *
 *  THE ORDER IS CRITICAL.  A hostname may appear in several tables, but
 *  the first table checked will cause the hit.
 *
 *  One nice aspect of this is that, for example, you can have a standard
 *  arpanet host table, but have some of its hosts actually get mail on a
 *  different channel.  Simply place that table's entry earlier in the
 *  ch_tbsrch[] list.
 *
 *  ch_exsrch[] is a list of channels that are processed by Deliver
 *  (usually just when it does standard, default daemon processing).
 *  Therefore, it need contain only those channels that are active and are
 *  to be processed by the daemon.  Again, order is important.  Channels
 *  are processed in the order listed.  This means that, typically, you
 *  want ch_sloc to be first, unless you have the strange view that foreign
 *  mail is more important than local...
 */

LOCVAR Chan *chsrch[NUMCHANS+1] = {	/* Order chan tables searched */
	(Chan *) 0
};
Chan **ch_tbsrch = chsrch;

LOCVAR Chan *exsrch[NUMCHANS+1] = {	/* Order of active chan execution */
	(Chan *) 0
};
Chan **ch_exsrch = exsrch;
Chan * ch_ptrlist[NUMCHANS + 1];  /* can hold pointers to chans */

LOCVAR Table *tblist[NUMTABLES+1] = {	/* All known tables */
    (Table *) 0
};
Table **tb_list = tblist;

LOCVAR Domain *dmlist[NUMDOMAINS+1];
Domain **dm_list = dmlist;         /* all known domains */

int     tb_maxtables = NUMTABLES; /* max number of slots */
int     ch_maxchans = NUMCHANS;   /* max number of slots */
int     dm_maxtables = NUMDOMAINS; /* max number of slots */

/* These next entries should reflect the ACTUAL number of slots in use  */
int     tb_numtables = 0;         /* actual number of tables */
int     ch_numchans = 0;          /* actual number of channels */
int     dm_numtables = 0;         /* actual number of domains */
ptr, LLOGFTR, "User '%s' expire reason '%s'",
			auth -> a_addr, argv[0]);
#endif
	    break;
    }


			/* now fill in the chans                */
    auth -> a_nchans = 0;
    for (i = 1; i < argc; i++)
    {
	if ((auth -> a_chans [auth -> a_nchans] = ch_nm2struct (argv[i]))
		== (Chan *) NOTOK)
	    ll_log (logptr, LLOGBTR,
		"Unknown channel name '%s' for user '%s'", argv[i], auth -> a_addr);
	else
	     auth -> a_nchans++;
    }
#ifdef  DEBUG
    ll_logmmdf/conf/vgr/conf.c   444      0     12       20357  3620510311   7603 #include "util.h"
#include "mmdf.h"
#include "ap_norm.h"

/*            COMPILE-TIME CONFIGURATION FILE WITH SITE-DEPENDENT INFORMATION
 *
 *      This file contains general host-dependent variable info that
 *   the message system needs to have.
 */

char *mmtailor = "/usr/mmdf/mmdftailor";
				/* location of external tailoring file  */

/* ************************  PUBLIC NAMES  ****************************** */

char
	*locname = "BRL-VGR",	/* Generic name for local host          */
	*locdomain = "ARPA",	/* Name of domain that is local to us */
	*sitesignature = "Mail System (MMDF)",
				/* in signature field of return-mail  */
	*mmdflogin = "mmdf",	/* login name for mmdf         */
	*supportaddr = "MMDF@BRL.ARPA";
				/* where to send bug reports, etc. */
/* *****************  DEFAULT BASE DIRECTORIES  *********************** */

/*  The following pathnames define the default working directories for
 *  MMDF activities.  Specific files usually will be referenced relative
 *  to these directories.  To override the default, the specific filename
 *  should be specified as an anchored pathname, beginning with a slash.
 *
 *  The following pathnames must be anchored.
 */

char	*cmddfldir = "/usr/mmdf";
			      /* contains MMDF commands, such as      */
			      /* submit, deliver, and queclean        */
char	*logdfldir = "/usr/mmdf/log";
			      /* contains highly volatile files, such */
			      /* as logs and check-point markers      */
char	*phsdfldir = "/usr/mmdf/log/phase";
			      /* contains timestamp files             */
char	*tbldfldir = "/usr/mmdf/table";
			      /* contains sticky files, such as       */
			      /* name tables & dialing scripts        */
char	*tbldbm = "/usr/mmdf/table/mmdfdbm";
			      /* dbm() hash of name tables      */
char	*quedfldir = "/usr/mmdf/lock/home";
			      /* contains queued mail files, in       */
			      /* subordinate directories              */
			      /* (also see below)                     */
char	*chndfldir = "/usr/mmdf/chans";
				/* contains the channel programs        */
				/* (ch_*) called by deliver             */
char	*lckdfldir = "/tmp/mmdf";
				/* Directory for lock files SEK           */


/* ***************  DEFAULT LOG LOCATIONS & SETTINGS  ***************** */

char    mlogloc[] = "msg.log";
char    chlogloc[] = "chan.log";

/*  It is common for specific using processes to tailor the second
 *  field, which is the log entry label, so that you can tell which
 *  incarnation of the process made a set of entries.
 *
 *  These structures conform to ll_log.h
 */

struct ll_struct
		    msglog =      /* deliver, submit, & queclean          */
{
    mlogloc, "XX-0000", LLOGFST, 40, LLOGSOME, 0
},

		   chanlog =      /* log for channels & user agents       */
{
    chlogloc, "UA-0000", LLOGFST, 40, LLOGSOME, 0
};

/* Needed for user authorisation logging        */

struct ll_struct authlog =
{
    "auth.log", "AU-0000", LLOGFTR, 40, LLOGSOME, 0
};

/* ****************  MESSAGE QUEUE SUBSTRUCTURE  ********************** */

/*  The queue has a top-level, locking directory, which only trusted
 *  processes can get through.  Under that is a base working directory.
 *  Below that are three directories which contain queued messages data
 *
 *  quedfldir[] specifies the top-level and base directories.
 */

int     queprot = 0700;           /* protection on quedfldir parent      */

/* the next five are relative to quedfldir[] */

char
	*deadletter = "DeadLetters",
				/* Actually NOT a directory, where	*/
				/* unreturned letters go to die.	*/
	*tquedir = "tmp/",	/* sub-directory for builing            */
				/* address list file for msg            */
	*aquedir = "addr/",	/* sub-dir with files of address        */
				/* lists for queued mail; files are     */
				/* linked into here from tquedir        */
				/* as last act of queuing msg           */
	*squepref = "q.",	/* preface sub-queue name with this	*/
	*mquedir = "msg/";	/* sub-dir containing text of           */
				/* queued messages; file names          */
				/* are same as in aquedir               */

/*  The following two parameters establish thresholds for queue residency
 *  limits.  If a message does not move out of the queue by warntime,
 *  a notice may be sent to the originator.  If it is still in the queue
 *  by failtime, it is removed from the queue and may be returned to the
 *  originator.
 */

int
	warntime = 60,
	failtime = 252;


/* ******************  COMMAND NAMES & LOCATIONS  *********************** */

char
	*namsubmit = "submit",      /* who to submit mail through         */
	*pathsubmit = "submit",     /* location relative to cmddfldir[]   */

	*namdeliver = "deliver",    /* name of delivery overseer          */
	*pathdeliver = "deliver",   /* location relative to cmddfldir[]   */

	*nampkup = "pickup",        /* name of pickup overseer            */
	*pathpkup = "deliver",      /* location relative to cmddfldir[]   */

	*nammail = "mmdfmail",      /* the mmdf simple mail-sender        */
	*pathmail = "v6mail";       /* needs to handle special switches   */
				    /* probably must be MMDF's uip/mail   */

/* *********************  SUBMIT TAILORING  ************************** */

int     maxhops = 20;               /* number of Via fields permitted   */
int	mgt_addid = 0;		    /* if set, add message-id if necessary */
int	lnk_listsize = 12;	    /* if more than this many addresses,
				     * then do not send warning and only
				     * send citation on return
				     */


/*   *********************  DELIVER TAILORING  ************************** */

int	maxqueue = 300;		  /* maximum size of sortable queue       */
int	mailsleep = 600;          /* seconds between queue sweeps by a    */
				  /* background (-b) Deliver              */

/*   *********************  ADDRESS TRANSFORMATION  ********************* */


/*  Format:
 *
 *  Original host name, New hostname, String appended to mailbox
 *
 *      e.g.        "DCrocker at UDel-EE"
 *      maps to     "DCrocker.EE at UDel"
 *
 *  Note the usage, for Darcom.  It is designed to bypass address
 *  mapping, so that "foo at bar" does not go out as "foo.bar at relay"
 */

LOCVAR struct ap_hstab hstab[] =
{
    0, 0, 0,
};
struct ap_hstab *ap_exhstab = hstab;

/* *****************  LOCAL DELIVERY TAILORING  ************************* */

/*  Obviously, the following is only needed if there is local delivery,
 *  rather than pure relaying (a rare, but permitted mode).  Simplest
 *  determinor is ch_okloc in conf_chan.c
 */

int     sentprotect = 0600;         /* protection on mail files             */

char
	*dlvfile = ".maildelivery", /* User specified delivery instructions */
	*sysdlvfile = (char *)0,  /* if non-zero, the default dlvfile */
	*mldfldir = { 0 },	/* directory to contain users' file     */
				/* for receiving local mail.  if this   */
				/* spec is empty, recipient's login     */
				/* directory will be used.              */
	*mldflfil = "mailbox",    /* file to receive new mail             */
				  /* relative to mldfldir[] or to login   */
				  /*   dir if mldfldir[] is empty         */
				  /* if this spec empty, user's login     */
				  /* name will be name of file            */
	*delim1 = "\001\001\001\001\n",
	*delim2 = "\001\001\001\001\n";
				  /* add to begin and end of messages     */
				  /* NOTE:  to avoid author spoofin,      */
				  /* lo_wtmail() catches mail that        */
				  /* has either of these strings in it    */
				  /* and changes the first char           */
				  /* (it increments its ascii value)      */


/* *****************  UUCP CHANNEL TAILORING  *********************** */

char	*Uchan = "uucp";		/* default channel name */
char    *Uuxstr = "uux - -r";		/* command string to start UUX */

/******************* NIFTP CHANNEL TAILORING  *************************/

char	*pn_quedir   = "/usr/spool/jntmail";
				/* Location of JNTmail queues for NIFTP */

/******************* MULTIPLE HOST TAILORING **************************
 *
 *  This variable is initialised to be the name of the local machine
 *  It is used to transparently forward between the UCL machines
 */
char	*locmachine = "BRL-VGR";


/******************* AUTHORIZATION TAILORING **************************/
char	*authrequest = "mmdf@BRL";
				/* authorisation request address        */
char	*authfile = "/usr/mmdf/warning";
				/* warning letter - full pathname       */

 it is removed from the queue and may be returned to the
 *  originator.
 */

int
	warntime = 60,
	failtime = 252;


/* ******************  COMMAND NAMES & LOCATIONS  *********************** */

char
	*namsubmit = "submit",      /* who to submit mail through         */
	*pmmdf/conf/vgr/conf.h   444      0     12        2732  3623122357   7601 #
/*          This contains the most site-dependent definitions             */

#define USERSIZE    8		/* max length of a user system name   */
#define	MAILIDSIZ  64		/* max length of a mailid,
				 * must be at least USERSIZE.		*/
#define MSGNSIZE   14           /* max length of a message file name  */
#define FILNSIZE   64           /* max length of full file name       */
#define ADDRSIZE  256		/* max length of an address           */
#define LINESIZE  256		/* max length of a line/record        */

#define	NUMCHANS   20		/* max number of channels */
#define NUMTABLES  30		/* max number of tables */
#define NUMDOMAINS 20		/* max number of domains */

/************  DIAL IN/OUT (CH_PHONE / SLAVE) TAILORING  *****************/

#define DL_TRIES    2             /* number of re-connects OK for phone */
#define DL_RCVSZ    255           /* longest line receivable            */
#define DL_XMTSZ    255           /* longest line sendable              */

/****************  JNTMAIL Tailoring *******************/

#undef	JNTMAIL
#undef	BOTHEND
#undef	VIATRACE

/**************** Include File Tailoring ****************************/

#include <time.h>
#include <sys/dir.h>

/********************* Privileged UIDs *************************/

/*
 *  This can be a macro as give here or you can provide a function.
 *  It should always authorize root and mmdf.
 *  The global variable effecid is assumed to have the MMDF uid in it.
 */

#define	PRIV_USER(x) ((x)==0 || (x)==1 || (x)==effecid)
char
	*dlvfile = ".maildelivery", /* Ummdf/conf/vgr/conf_dial.c   444      0     12        6724  3622765432  10600 #include "util.h"
#include "ll_log.h"
#include "d_proto.h"
#include "d_structs.h"

/* **********************  DIALING TAILOR INFO  **********************  */

/*  modem speed support definitions  */

# define  BELL103        0177     /*  bell 103  0 to 300 baud  */
# define  VA825          0177     /*  vadic 825  0 to 300 baud  */
# define  VA3415         0400     /*  vadic 3415  1200 baud  */

/*
 *     structure defining the available dial-out ports.
 */

#define NUMPRTS         20
LOCVAR struct dialports  theprts[NUMPRTS] =
  {
  0,             0,                0,           0,    0, 0,      0,   0
  };
struct dialports  *d_prts = theprts;
int d_maxprts = NUMPRTS;
int d_numprts = 0;

#define NUMLINES        10
LOCVAR struct directlines  thelines[NUMLINES] =
  {
  0,              0,             0,                 0, 0
  };

int d_maxlines = NUMLINES;
int d_numlines = 0;
struct directlines  *d_lines = thelines;


/*
 *     this is the path name of the file on which outgoing calls are
 *     logged.  it should be owned by the same user as the outgoing
 *     ports and autodialer devices.
 */

char  *d_calllog  =  "/usr/mmdf/log/dial_log";

/*
 *	this is the name of the programs will actually do dialing
 *	it is called as "dial <line> <number>".  It knows which
 *	acus are attached to which lines.
 */

char	*d_dialprog = "/usr/bin/dial";

/* ***************  ILLEGAL CHARACTER TAILORING  ************************* */

/*  The following sets the default illegal-character arrays for both
    dial-out (phone) and dial-in (slave) connections.
 */


unsigned short d_lrill[8] =       /* illegal receive chars              */
{				  /* see d_slvconn & d_masconn          */
    0177777,                      /* nul soh stx etx eot enq ack bel    */
				  /* bs  ht  nl  vt  np  cr  so  si     */
    0177777,                      /* dle dc1 dc2 dc3 dc4 nak syn etb    */
				  /* can em  sub esc fs  gs  rs  us     */
    0000010,                      /* sp  ! " # $ % & ' ( ) * + , - . /  */
    0000000,                      /* 0 1 2 3 4 5 6 7 8 9 : ; < = > ?    */
    0000001,                      /* @ A B C D E F G H I J K L M N O    */
    0010000,                      /* P Q R S T U V W X Y Z [ \\] ^ _    */
    0000000,                      /* ` a b c d e f g h i j k l m n o    */
    0100000                       /* p q r s t u v w x y z { | } ~ del  */
};

unsigned short d_lxill[8] =       /* illegal send chars                 */
{				  /* see d_slvconn & d_masconn          */
    0177777,                      /* nul soh stx etx eot enq ack bel    */
				  /* bs  ht  nl  vt  np  cr  so  si     */
    0177777,                      /* dle dc1 dc2 dc3 dc4 nak syn etb    */
				  /* can em  sub esc fs  gs  rs  us     */
    0000010,                      /* sp  ! " # $ % & ' ( ) * + , - . /  */
    0000000,                      /* 0 1 2 3 4 5 6 7 8 9 : ; < = > ?    */
    0000001,                      /* @ A B C D E F G H I J K L M N O    */
    0010000,                      /* P Q R S T U V W X Y Z [ \\] ^ _    */
    0000000,                      /* ` a b c d e f g h i j k l m n o    */
    0100000                       /* p q r s t u v w x y z { | } ~ del  */
};

/*************************** PHONE LOGGING ***************************/

struct ll_struct    ph_log =       /* dial-out link-level package log    */
{
    "ph.log", "PH-0000", LLOGFST, 25, LLOGSOME, 0
};

char *def_trn = "ph.trn";          /* dial-out link-level i/o transcript */
				  /* does not use ll_log package        */

 arrays for both
    dial-out (phone) and dimmdf/conf/vgr/Makefile.com   444      0     12        7327  3657737645  10750 #
#	Common Sub-Makefile for the MMDF System
#
HOST		= vgr
SYSTEM		= 4.2
MMPREF		=
LIBDIR		= /usr/mmdf
CHANDIR		= /usr/mmdf/chans
TBLDIR		= /usr/mmdf/table
BINDIR		= /usr/brl/bin
RCVDIR		= /usr/mmdf/rcvmail

#
#  Defines used during installation
#
CHOWN		= /etc/chown
MMDFLOGIN	= mmdf
ROOTLOGIN	= root
PGMPROT 	= 755

#
#  Configuration Defines
#
#  The paths below are relative from the directories below this one.
#  Define DOASSIGN to CONFIGDEFS if you want the d_assign code
#  Define SECURETTY if you run with terminals write protected (mode 711)
#  Define DEBUG to include extensive calls to the logging package
#  Define D_LOG if you want phone channel logging
#  Define D_DBGLOG if you want phone channel debugging output
#  Define RUNALONE for standalone testing (seldom used)
#  Define V4_2BSD if you are compiling for 4.2BSD
#  Define NODIAL if you do not want the dial package (no phone channel)
#  Define SYS5 if you have a SystemV Unix or something similar (Sys3)
#  Define ALTOS if you have an Altos, does some depraved things.
#  Define NODUP2 if you don't a dup2() syscall or subroutine.
#  Define NOFCNTL and NODUP2 if you don't have fcntl(x, F_DUPFD) or dup2()
#  Define -Drindex=strrchr -Dindex=strchr if you are on a SYS5 system
#  Define NAMESERVER if you want the nameserver lookup code
#  Define UUCPLOCK if you want UUCP/tip/cu-style locking for dial-out lines
#  Define NODOMLIT if you don't want literals like [10.0.0.52] in messages
#
CONFIGDEFS	= -DSECURETTY -DDEBUG=1 -DV4_2BSD -DNAMESERVER -DNODOMLIT
CFLAGS		= -O -I../../h $(CONFIGDEFS)
LDFLAGS 	=
MMDFLIBS 	= ../../lib/libmmdf.a
SYSLIBS		= -lresolv -ldbm
LINT		= lint
LFLAGS		= -phbvxacL -I../../h $(CONFIGDEFS)
LLIBS		= ../../lib/llib-lmmdf.ln
AR		= ar


#  Specify either ch_tbdbm (for DBM based tables) or ch_tbseq for
#  sequential IO based tables.
CH_TB	= ch_tbdbm

#  Specify one of the nameserver modules or the fake module
#  if you do not intend to support nameservers (4.2, fake)
TB_NS	= 4.2

#  Specify tai_???.o and lk_lock???.o
LOCALUTIL = tai_file.o lk_lock.4.2.o


default:	real-default

#
#  special case dependencies
#
../../h/mmdf.h:	../../h/conf.h
	-touch ../../h/mmdf.h
.PRECIOUS: ../../h/mmdf.h

#
#  #include dependencies
#
#  Two versions are supplied.  One for sites with cc -M (4.3BSD)
#  and one for those that don't have it.  Comment out the one
#  you do not want.

#  This one is for sites without cc -M
#depend:
#	cat </dev/null >x.c
#	for i in $(MODULES); do \
#		(echo $$i.o: $$i.c >>makedep; \
#		/bin/grep '^#[ 	]*include' x.c $$i.c | sed \
#			-e 's,c:[^"]*"\./\([^"]*\)".*,o: \1,' \
#			-e 's,c:[^"]*"/\([^"]*\)".*,o: /\1,' \
#			-e 's,c:[^"]*"\([^"]*\)".*,o: ../../h/\1,' \
#			-e 's,c:[^<]*<\(.*\)>.*,o: /usr/include/\1,' \
#			>>makedep); done
#	echo '/^# DO NOT DELETE THIS LINE/+2,$$d' >eddep
#	echo '$$r makedep' >>eddep
#	echo 'w' >>eddep
#	cp Makefile.real Makefile.bak
#	ed - Makefile.real < eddep
#	rm eddep makedep x.c
#	echo '# DEPENDENCIES MUST END AT END OF FILE' >> Makefile.real
#	echo '# IF YOU PUT STUFF HERE IT WILL GO AWAY' >> Makefile.real
#	echo '# see make depend above' >> Makefile.real

#  This one id for sites with cc -M
depend:
	( for i in ${MODULES} ; do \
		${CC} -M ${CFLAGS} $$i.c ; done ) | \
	awk ' { if ($$1 != prev) { print rec; rec = $$0; prev = $$1; } \
		else { if (length(rec $$2) > 78) { print rec; rec = $$0; } \
		       else rec = rec " " $$2 } } \
	      END { print rec } ' > makedep
	echo '/^# DO NOT DELETE THIS LINE/+2,$$d' >eddep
	echo '$$r makedep' >>eddep
	echo 'w' >>eddep
	cp Makefile.real Makefile.bak
	ed - Makefile.real < eddep
	rm eddep makedep
	echo '# DEPENDENCIES MUST END AT END OF FILE' >> Makefile.real
	echo '# IF YOU PUT STUFF HERE IT WILL GO AWAY' >> Makefile.real
	echo '# see make depend above' >> Makefile.real
ther ch_tbdbm (for DBM based tables) or ch_tbseq for
#  sequential IO based tables.
CH_TB	= ch_tbdbm

#  Specify one of the nameserver modules or the fake module
#  if you do not intend to support nameservers (4.2, fake)
TB_NS	= 4.2

#  Specify tai_???.o and lk_lock???.o
LOCALUTIL = tai_file.o lkmmdf/conf/vgr/s_tailor.c   444      0     12         334  3620510311  10423 /*
 *	S _ T A I L O R . C
 *
 *  Contains strings used to tailor SEND to a particular
 *  site.  Also check s.h for tailorable defines.
 */

char	*dfleditor = "ed";
char	*dflveditor = "jove";
char	*dflchecker = "spell";
ites with cc -M (4.3BSD)
#  and one for those that don't have it.  Comment out the one
#  you do not want.

#  This one is for sites without cc -M
#depend:
#	cat </dev/null >x.c
#	for i in $(MODULES); do \
#		(echo $$i.o: $$i.c >>makedep; \
#		/bin/grep '^#[ 	]*include' x.c $$i.c | sed \
#		mmdf/conf/vgr/msgtailor.c   444      0     12        3114  3671073315  10645 /*
 *	M S G T A I L O R . C
 */

#define NOEXTERNS
#include "./msg.h"

#ifdef pdp11
#	define NMSGS	500
#else
#	define NMSGS	2000
#endif pdp11

char    *savmsgfn = "mbox";
char	*resendprog = "|/usr/brl/bin/resend ";
char	*sndname = "/usr/local/send"; /* Overridden by .msgrc sendprog */

char	*dflshell = "sh";	/* Overridden by getenv("SHELL"); */
char	*dfleditor = "jove";	/* Overridden by getenv("EDITOR"); */

int	pagesize = 22;		/* default line count */
int	linelength = 80;	/* default line size */

int	paging = OFF;		/* Internal paging of long messages */
int	verbose = ON;		/* Command completion (raw mode) */
int	wmsgflag = OFF;		/* Save messages when calling send */
int	keystrip = ON;		/* Strip header lines on display */
int	bdots = ON;		/* Binary box dots */
int	mdots = ON;		/* Mailbox dots */
int	bprint = ON;		/* Reading... messages */
int	quicknflag = OFF;	/* Turn off NEW before message prints */
int	quickexit = ON;		/* Don't stay in msg if mailbox is empty */
int	prettylist = ON;	/* Print message numbers in listings */
int	filoutflag = ON;	/* Filter text (display controls as ^X) */
int	binsavflag = ON;	/* Save binary box on exec's & pauses */
int	doctrlel = OFF;		/* Page on control-L */
#ifndef SYS5
long	timezone = 5*60*60;	/* Seconds West of GMT */
int	daylight = 1;		/* Do you use daylight savings time? */
				/* 0 = NO, 1 = YES */
char	*tzname[] = {"EST", "EDT",};
				/* Timezone strings, primary and daylight
				   savings time if used, in that order */
#endif SYS5

/***** Do not tailor below this line *****/
int	Nmsgs = NMSGS;
struct message *msgp[NMSGS];
struct message *mptr;
 sites with cc -M
depend:
	( for i in ${MODULES} ; do \
		${CC} -M ${CFLAGS} $$i.c ; done ) | \
	awk ' { if ($$1 != prev) { print rec; rec = $$0; prev = $$1; } \
		else { if (length(rec $$2) > 78) { print rec; rec = $$0; } \
		       else rec = rec " " $$2 } } \
	      END { print rec } ' > makedep
	echo '/^# DO NOT DELETE THIS LINE/+2,$$d' >eddep
	echo '$$r makedep' >>eddep
	echo 'w' >>eddep
	cp Makefile.real Makefile.bak
	ed - Makmmdf/conf/vgr/Makefile.lib   444      0     12        2220  3652767276  10721 #
#	Makefile for the MMDF library   (libmmdf.a)
#

#  The paths below are relative from the directories below this one.
#
#  The RANLIB define should be "sh libfix" for systems that don't have
#	the ranlib command.  Systems that do have ranlib should set
#	RANLIB to "ranlib".  "libfix" is a Bourne shell file that will
#	use lorder and tsort to reorder the library.
#
MAKE	= make
RANLIB	= ranlib

SUBDIRS = addr dial mmdf table util
ALLSUBDIRS = addr dial mmdf table util

default: remake reorder

remake:
	for dir in $(SUBDIRS); \
	  do (cd $${dir}; echo "Running make in $${dir}"; \
	      gen ${MFLAGS} -${MAKEFLAGS}); \
	  done

install: default
	@echo "Library is up to date, nothing else to do."

reorder: reorder-done

reorder-done: libmmdf.a
	-$(RANLIB) libmmdf.a
	-touch reorder-done

depend:
	for dir in $(SUBDIRS); \
	  do (cd $${dir}; gen ${MFLAGS} -${MAKEFLAGS} depend); done

lint:
	for dir in $(SUBDIRS); \
	  do (cd $${dir}; gen ${MFLAGS} -${MAKEFLAGS} lint); done
	cat */llib-l*.ln > llib-lmmdf.ln

clean:
	-rm -f *-made libmmdf.a make.out core reorder-done
	for dir in $(SUBDIRS); \
	  do (cd $${dir}; $(MAKE) -f Makefile.real clean); done

ALWAYS:
{CFLAGS} $$i.c ; done ) | \
	awk ' { if ($$1 != prev) { print rec; rec = $$0; prev = $$1; } \
		else { if (length(rec $$2) > 78) { print rec; rec = $$0; } \
		       else rec = rec " " $$2 } } \
	      END { print rec } ' > makedep
	echo '/^# DO NOT DELETE THIS LINE/+2,$$d' >eddep
	echo '$$r makedep' >>eddep
	echo 'w' >>eddep
	cp Makefile.real Makefile.bak
	ed - Makmmdf/conf/vgr/Makefile.src   444      0     12        2307  3657737646  10753 #	MMDF Program Source Subtree Makefile
#

#
# Programs that live in subdirectories, and have makefiles of their own.
#
SUBDIR=	tools submit deliver local list delay smtp phone pobox \
	uucp

#
#  This should list all the subdirectories that make clean works in.
#  This probably should not be altered unless you add/delete a channel.
#
ALLSUBDIRS=	tools submit deliver local list delay smtp phone pobox \
		uucp niftp bboards pop ean prog

all:
	for dir in ${SUBDIR}; do \
		(cd $${dir}; gen ${MFLAGS} -${MAKEFLAGS}); done

install:
	for dir in ${SUBDIR}; do \
		(cd $${dir}; gen ${MFLAGS} -${MAKEFLAGS} install); done

dist:
	-touch `ls */x*|grep -v '\.'`
	echo echo MMDF Distribution made `date` > install.sh
	for dir in ${SUBDIR}; do \
		echo cd $${dir} >> install.sh; \
		( cd $${dir}; \
		  gen -n ${MFLAGS} -${MAKEFLAGS} install) >> install.sh; \
		echo cd .. >> install.sh; \
	done
	tar cvf mmdfdist.tar install.sh */x*

depend:
	for dir in ${SUBDIR}; do \
		(cd $${dir}; gen ${MFLAGS} -${MAKEFLAGS} depend); done

lint:
	for dir in ${SUBDIR}; do \
		(cd $${dir}; gen ${MFLAGS} -${MAKEFLAGS} lint); done

clean:
	-rm -f make.out core
	for dir in ${ALLSUBDIRS}; do \
		(cd $${dir}; make -f Makefile.real clean); done
 print rec; rec = $$0; prev = $$1; } \
		else { if (length(rec $$2) > 78) { print rec; rec = $$0; } \
		       else rec = rec " " $$2 } } \
	      END { print rec } ' > makedep
	echo '/^# DO NOT DELETE THIS LINE/+2,$$d' >eddep
	echo '$$r makedep' >>eddep
	echo 'w' >>eddep
	cp Makefile.real Makefile.bak
	ed - Makmmdf/conf/vgr/s_tailor.h   444      0     12         471  3620510312  10433 /*
 *	Site Tunable Parameters
 */
#undef	SENTFILE yes	/* Define to have copies of mail sent put in ,sent */
#undef	FNCYPRNT yes	/* Define for fancy listing of message (paginated) */
#define	PWNAME yes	/* Define to get real name from /etc/passwd and to */
			/* treat 0 length signatures to get login name only */
dd/delete a channel.
#
ALLSUBDIRS=	tools submit deliver local list delay smtp phone pobox \
		uucp niftp bboards pop ean prog

all:
	for dir in ${SUBDIR}; do \
		(cd $${dir}; gen ${MFLAGS} -${MAKEFLAmmdf/conf/4.3vax/   755      0     12           0  3671074640   6645 mmdf/conf/4.3vax/Makefile.lib   444      0     12        2236  3652767212  14353 1mmdf/lib/Makefilemmdf/conf/4.3vax/Makefile.src   444      0     12        2330  3657737647  11175 #	MMDF Program Source Subtree Makefile
#

#
# Programs that live in subdirectories, and have makefiles of their own.
#
SUBDIR=	tools submit deliver local list delay smtp phone pobox \
	uucp prog bboards pop

#
#  This should list all the subdirectories that make clean works in.
#  This probably should not be altered unless you add/delete a channel.
#
ALLSUBDIRS=	tools submit deliver local list delay smtp phone pobox \
		uucp niftp bboards pop ean prog

all:
	for dir in ${SUBDIR}; do \
		(cd $${dir}; gen ${MFLAGS} -${MAKEFLAGS}); done

install:
	for dir in ${SUBDIR}; do \
		(cd $${dir}; gen ${MFLAGS} -${MAKEFLAGS} install); done

dist:
	-touch `ls */x*|grep -v '\.'`
	echo echo MMDF Distribution made `date` > install.sh
	for dir in ${SUBDIR}; do \
		echo cd $${dir} >> install.sh; \
		( cd $${dir}; \
		  gen -n ${MFLAGS} -${MAKEFLAGS} install) >> install.sh; \
		echo cd .. >> install.sh; \
	done
	tar cvf mmdfdist.tar install.sh */x*

depend:
	for dir in ${SUBDIR}; do \
		(cd $${dir}; gen ${MFLAGS} -${MAKEFLAGS} depend); done

lint:
	for dir in ${SUBDIR}; do \
		(cd $${dir}; gen ${MFLAGS} -${MAKEFLAGS} lint); done

clean:
	-rm -f make.out core
	for dir in ${ALLSUBDIRS}; do \
		(cd $${dir}; make -f Makefile.real clean); done
 $$0; prev = $$1; } \
		else { if (length(rec $$2) > 78) { print rec; rec = $$0; } \
		       else rec = rec " " $$2 } } \
	      END { print rec } ' > makedep
	echo '/^# DO NOT DELETE THIS LINE/+2,$$d' >eddep
	echo '$$r makedep' >>eddep
	echo 'w' >>eddep
	cp Makefile.real Makefile.bak
	ed - Makmmdf/conf/4.3vax/chan.c   444      0     12        6062  3620510313   7773 #include "util.h"
#include "mmdf.h"
#include "ch.h"
#include "dm.h"

char    *ch_dflnam = "local";

Alias	*al_list = (Alias *)0;

/*******************  MAIL IDENTIFIER  SELECTION  ***********************/

/*
 *	The global mid_enable controls the use of an alternate
 *	mail name system refered to as Mail-Ids.  When mail-ids are
 *	in use, login-ids and mail-ids are nolong bound to one another
 *	by the system account database, and are bound only by mapping
 *	tables.  The tables "mailids" and "users" will always be present
 *	though they may not be used.  "mailids" maps a mailid to a
 *	system account.  The table "users" maps a username to a mailid.
 */

int	mid_enable = 0;

Table
	tb_mailids =		/* Used in getpwmid() */
{
    "mailids", "Mail-ID to Username Mappings", "mailids", 0, 0l
},
	tb_users =		/* Used in getmailid() */
{
    "users", "Username to Mail-ID Mappings", "users", 0, 0l
};


/*
 *  The following set of structures provides a complete list of channels
 *  known to MMDF.
 *
 *  It is followed by two arrays that list the channels in useful orders.
 *
 *  ch_tbsrch[] is a full list, of all known channels, and is primarily
 *  used to guide the ch_table routines when treating the hostname space as
 *  flat (linear).  It also is used by lnk_submit, for sorting the address
 *  linked-list.
 *
 *  THE ORDER IS CRITICAL.  A hostname may appear in several tables, but
 *  the first table checked will cause the hit.
 *
 *  One nice aspect of this is that, for example, you can have a standard
 *  arpanet host table, but have some of its hosts actually get mail on a
 *  different channel.  Simply place that table's entry earlier in the
 *  ch_tbsrch[] list.
 *
 *  ch_exsrch[] is a list of channels that are processed by Deliver
 *  (usually just when it does standard, default daemon processing).
 *  Therefore, it need contain only those channels that are active and are
 *  to be processed by the daemon.  Again, order is important.  Channels
 *  are processed in the order listed.  This means that, typically, you
 *  want ch_sloc to be first, unless you have the strange view that foreign
 *  mail is more important than local...
 */

LOCVAR Chan *chsrch[NUMCHANS+1] = {	/* Order chan tables searched */
	(Chan *) 0
};
Chan **ch_tbsrch = chsrch;

LOCVAR Chan *exsrch[NUMCHANS+1] = {	/* Order of active chan execution */
	(Chan *) 0
};
Chan **ch_exsrch = exsrch;
Chan * ch_ptrlist[NUMCHANS + 1];  /* can hold pointers to chans */

LOCVAR Table *tblist[NUMTABLES+1] = {	/* All known tables */
    (Table *) 0
};
Table **tb_list = tblist;

LOCVAR Domain *dmlist[NUMDOMAINS+1];
Domain **dm_list = dmlist;         /* all known domains */

int     tb_maxtables = NUMTABLES; /* max number of slots */
int     ch_maxchans = NUMCHANS;   /* max number of slots */
int     dm_maxtables = NUMDOMAINS; /* max number of slots */

/* These next entries should reflect the ACTUAL number of slots in use  */
int     tb_numtables = 0;         /* actual number of tables */
int     ch_numchans = 0;          /* actual number of channels */
int     dm_numtables = 0;         /* actual number of domains */
rs.
 *
 *  ch_tbsrch[] is a full list, of all known channels, and is primarily
 *  used to guide the ch_table routines when treating the hostname space as
 *  flat (linear).  It also is used by lnk_submit, for sorting the address
 *  linked-list.
 *
 *  THE ORDER IS CRITICAL.  A hostname may appear in several tables, but
 *  the first table checked will cause the hit.
 *
 *  One nice aspect of this is that, for example, you can have a standard
 *  arpanet hommdf/conf/4.3vax/conf.c   444      0     12       20377  3620510314  10035 #include "util.h"
#include "mmdf.h"
#include "ap_norm.h"

/*            COMPILE-TIME CONFIGURATION FILE WITH SITE-DEPENDENT INFORMATION
 *
 *      This file contains general host-dependent variable info that
 *   the message system needs to have.
 */

char *mmtailor = "/usr/mmdf/mmdftailor";
				/* location of external tailoring file  */

/* ************************  PUBLIC NAMES  ****************************** */

char
	*locname = "Unknown-Vax",  /* Generic name for local host   */
	*locdomain = "ARPA",	/* Name of domain that is local to us */
	*sitesignature = "Mail System (MMDF)",
				/* in signature field of return-mail  */
	*mmdflogin = "mmdf",	/* login name for mmdf         */
	*supportaddr = "MMDF@Unknown-Vax.ARPA";
				/* where to send bug reports, etc. */
/* *****************  DEFAULT BASE DIRECTORIES  *********************** */

/*  The following pathnames define the default working directories for
 *  MMDF activities.  Specific files usually will be referenced relative
 *  to these directories.  To override the default, the specific filename
 *  should be specified as an anchored pathname, beginning with a slash.
 *
 *  The following pathnames must be anchored.
 */

char	*cmddfldir = "/usr/mmdf";
			      /* contains MMDF commands, such as      */
			      /* submit, deliver, and queclean        */
char	*logdfldir = "/usr/mmdf/log";
			      /* contains highly volatile files, such */
			      /* as logs and check-point markers      */
char	*phsdfldir = "/usr/mmdf/log/phase";
			      /* contains timestamp files             */
char	*tbldfldir = "/usr/mmdf/table";
			      /* contains sticky files, such as       */
			      /* name tables & dialing scripts        */
char	*tbldbm = "/usr/mmdf/table/mmdfdbm";
			      /* dbm() hash of name tables      */
char	*quedfldir = "/usr/mmdf/lock/home";
			      /* contains queued mail files, in       */
			      /* subordinate directories              */
			      /* (also see below)                     */
char	*chndfldir = "/usr/mmdf/chans";
				/* contains the channel programs        */
				/* (ch_*) called by deliver             */
char	*lckdfldir = "/tmp/mmdf";
				/* Directory for lock files SEK if needed */


/* ***************  DEFAULT LOG LOCATIONS & SETTINGS  ***************** */

char    mlogloc[] = "msg.log";
char    chlogloc[] = "chan.log";

/*  It is common for specific using processes to tailor the second
 *  field, which is the log entry label, so that you can tell which
 *  incarnation of the process made a set of entries.
 *
 *  These structures conform to ll_log.h
 */

struct ll_struct
		    msglog =      /* deliver, submit, & queclean          */
{
    mlogloc, "XX-0000", LLOGFST, 40, LLOGSOME, 0
},

		   chanlog =      /* log for channels & user agents       */
{
    chlogloc, "UA-0000", LLOGFST, 40, LLOGSOME, 0
};

/* Needed for user authorisation logging        */

struct ll_struct authlog =
{
    "auth.log", "AU-0000", LLOGFTR, 40, LLOGSOME, 0
};

/* ****************  MESSAGE QUEUE SUBSTRUCTURE  ********************** */

/*  The queue has a top-level, locking directory, which only trusted
 *  processes can get through.  Under that is a base working directory.
 *  Below that are three directories which contain queued messages data
 *
 *  quedfldir[] specifies the top-level and base directories.
 */

int     queprot = 0700;           /* protection on quedfldir parent      */

/* the next five are relative to quedfldir[] */

char
	*deadletter = "DeadLetters",
				/* Actually NOT a directory, where	*/
				/* unreturned letters go to die.	*/
	*tquedir = "tmp/",	/* sub-directory for builing            */
				/* address list file for msg            */
	*aquedir = "addr/",	/* sub-dir with files of address        */
				/* lists for queued mail; files are     */
				/* linked into here from tquedir        */
				/* as last act of queuing msg           */
	*squepref = "q.",	/* preface sub-queue name with this	*/
	*mquedir = "msg/";	/* sub-dir containing text of           */
				/* queued messages; file names          */
				/* are same as in aquedir               */

/*  The following two parameters establish thresholds for queue residency
 *  limits.  If a message does not move out of the queue by warntime,
 *  a notice may be sent to the originator.  If it is still in the queue
 *  by failtime, it is removed from the queue and may be returned to the
 *  originator.
 */

int
	warntime = 60,
	failtime = 252;


/* ******************  COMMAND NAMES & LOCATIONS  *********************** */

char
	*namsubmit = "submit",      /* who to submit mail through         */
	*pathsubmit = "submit",     /* location relative to cmddfldir[]   */

	*namdeliver = "deliver",    /* name of delivery overseer          */
	*pathdeliver = "deliver",   /* location relative to cmddfldir[]   */

	*nampkup = "pickup",        /* name of pickup overseer            */
	*pathpkup = "deliver",      /* location relative to cmddfldir[]   */

	*nammail = "mmdfmail",      /* the mmdf simple mail-sender        */
	*pathmail = "v6mail";       /* needs to handle special switches   */
				    /* probably must be MMDF's uip/mail   */

/* *********************  SUBMIT TAILORING  ************************** */

int     maxhops = 20;               /* number of Via fields permitted   */
int	mgt_addid = 0;		    /* if set, add message-id if necessary */
int	lnk_listsize = 12;	    /* if more than this many addresses,
				     * then do not send warning and only
				     * send citation on return
				     */


/*   *********************  DELIVER TAILORING  ************************** */

int	maxqueue = 300;		  /* maximum size of sortable queue       */
int	mailsleep = 600;          /* seconds between queue sweeps by a    */
				  /* background (-b) Deliver              */

/*   *********************  ADDRESS TRANSFORMATION  ********************* */


/*  Format:
 *
 *  Original host name, New hostname, String appended to mailbox
 *
 *      e.g.        "DCrocker at UDel-EE"
 *      maps to     "DCrocker.EE at UDel"
 *
 *  Note the usage, for Darcom.  It is designed to bypass address
 *  mapping, so that "foo at bar" does not go out as "foo.bar at relay"
 */

LOCVAR struct ap_hstab hstab[] =
{
    0, 0, 0,
};
struct ap_hstab *ap_exhstab = hstab;

/* *****************  LOCAL DELIVERY TAILORING  ************************* */

/*  Obviously, the following is only needed if there is local delivery,
 *  rather than pure relaying (a rare, but permitted mode).  Simplest
 *  determinor is ch_okloc in conf_chan.c
 */

int     sentprotect = 0600;         /* protection on mail files             */

char
	*dlvfile = ".maildelivery", /* User specified delivery instructions */
	*sysdlvfile = (char *)0,  /* if non-zero, the default dlvfile */
	*mldfldir = { 0 },	/* directory to contain users' file     */
				/* for receiving local mail.  if this   */
				/* spec is empty, recipient's login     */
				/* directory will be used.              */
	*mldflfil = "mailbox",    /* file to receive new mail             */
				  /* relative to mldfldir[] or to login   */
				  /*   dir if mldfldir[] is empty         */
				  /* if this spec empty, user's login     */
				  /* name will be name of file            */
	*delim1 = "\001\001\001\001\n",
	*delim2 = "\001\001\001\001\n";
				  /* add to begin and end of messages     */
				  /* NOTE:  to avoid author spoofin,      */
				  /* lo_wtmail() catches mail that        */
				  /* has either of these strings in it    */
				  /* and changes the first char           */
				  /* (it increments its ascii value)      */


/* *****************  UUCP CHANNEL TAILORING  *********************** */

char	*Uchan = "uucp";		/* default channel name */
char    *Uuxstr = "uux - -r";		/* command string to start UUX */

/******************* NIFTP CHANNEL TAILORING  *************************/

char	*pn_quedir   = "/usr/spool/jntmail";
				/* Location of JNTmail queues for NIFTP */

/******************* MULTIPLE HOST TAILORING **************************
 *
 *  This variable is initialised to be the name of the local machine
 *  It is used to transparently forward between the UCL machines
 */
char	*locmachine = "Vax1";


/******************* AUTHORIZATION TAILORING **************************/
char	*authrequest = "mmdf@Unknown-Vax.ARPA";
				/* authorisation request address        */
char	*authfile = "/usr/mmdf/warning";
				/* warning letter - full pathname       */

oved from the queue and may be returned to the
 *  originator.
 */

int
	warntime = 60,
	failtime = 252;


/* ******************  COMMAND NAMES & LOCATIONS  *********************** */

char
	*namsubmit = "submit",      /* who to submit mail through         mmdf/conf/4.3vax/conf.h   444      0     12        2732  3656424247  12366 1mmdf/h/conf.hmmdf/conf/4.3vax/conf_dial.c   444      0     12        6721  3622764040  11014 #include "util.h"
#include "ll_log.h"
#include "d_proto.h"
#include "d_structs.h"

/* **********************  DIALING TAILOR INFO  **********************  */

/*  modem speed support definitions  */

# define  BELL103        0177     /*  bell 103  0 to 300 baud  */
# define  VA825          0177     /*  vadic 825  0 to 300 baud  */
# define  VA3415         0400     /*  vadic 3415  1200 baud  */

/*
 *     structure defining the available dial-out ports.
 */

#define NUMPRTS         20
LOCVAR struct dialports  theprts[NUMPRTS] =
  {
  0,             0,                0,           0,    0, 0,      0,   0
  };
struct dialports  *d_prts = theprts;
int d_maxprts = NUMPRTS;
int d_numprts = 0;

#define NUMLINES        10
LOCVAR struct directlines  thelines[NUMLINES] =
  {
  0,              0,             0,                 0, 0
  };

int d_maxlines = NUMLINES;
int d_numlines = 0;
struct directlines  *d_lines = thelines;


/*
 *     this is the path name of the file on which outgoing calls are
 *     logged.  it should be owned by the same user as the outgoing
 *     ports and autodialer devices.
 */

char  *d_calllog  =  "/usr/mmdf/log/dial_log";

/*
 *	this is the name of the programs will actually do dialing
 *	it is called as "dial <line> <number>".  It knows which
 *	acus are attached to which lines.
 */

char	*d_dialprog = "/usr/bin/dial";

/* ***************  ILLEGAL CHARACTER TAILORING  ************************* */

/*  The following sets the default illegal-character arrays for both
    dial-out (phone) and dial-in (slave) connections.
 */


unsigned short d_lrill[8] =       /* illegal receive chars              */
{				  /* see d_slvconn & d_masconn          */
    0177777,                      /* nul soh stx etx eot enq ack bel    */
				  /* bs  ht  nl  vt  np  cr  so  si     */
    0177777,                      /* dle dc1 dc2 dc3 dc4 nak syn etb    */
				  /* can em  sub esc fs  gs  rs  us     */
    0000010,                      /* sp  ! " # $ % & ' ( ) * + , - . /  */
    0000000,                      /* 0 1 2 3 4 5 6 7 8 9 : ; < = > ?    */
    0000001,                      /* @ A B C D E F G H I J K L M N O    */
    0010000,                      /* P Q R S T U V W X Y Z [ \\] ^ _    */
    0000000,                      /* ` a b c d e f g h i j k l m n o    */
    0100000                       /* p q r s t u v w x y z { | } ~ del  */
};

unsigned short d_lxill[8] =       /* illegal send chars                 */
{				  /* see d_slvconn & d_masconn          */
    0177777,                      /* nul soh stx etx eot enq ack bel    */
				  /* bs  ht  nl  vt  np  cr  so  si     */
    0177777,                      /* dle dc1 dc2 dc3 dc4 nak syn etb    */
				  /* can em  sub esc fs  gs  rs  us     */
    0000010,                      /* sp  ! " # $ % & ' ( ) * + , - . /  */
    0000000,                      /* 0 1 2 3 4 5 6 7 8 9 : ; < = > ?    */
    0000001,                      /* @ A B C D E F G H I J K L M N O    */
    0010000,                      /* P Q R S T U V W X Y Z [ \\] ^ _    */
    0000000,                      /* ` a b c d e f g h i j k l m n o    */
    0100000                       /* p q r s t u v w x y z { | } ~ del  */
};

/************************** PHONE LOGGING **************************/

struct ll_struct    ph_log =       /* dial-out link-level package log    */
{
    "ph.log", "PH-0000", LLOGFST, 25, LLOGSOME, 0
};

char *def_trn = "ph.trn";          /* dial-out link-level i/o transcript */
				  /* does not use ll_log package        */
o handle special switches   */
				    /* probammdf/conf/4.3vax/msgtailor.c   444      0     12        3117  3671073316  11076 /*
 *	M S G T A I L O R . C
 */

#define NOEXTERNS
#include "./msg.h"

#ifdef pdp11
#	define NMSGS	500
#else
#	define NMSGS	2000
#endif pdp11

char    *savmsgfn = "mbox";
char	*resendprog = "|/usr/local/resend ";
char	*sndname = "/usr/local/send"; /* Overridden by .msgrc sendprog */

char	*dflshell = "sh";	/* Overridden by getenv("SHELL"); */
char	*dfleditor = "vi";	/* Overridden by getenv("EDITOR"); */

int	pagesize = 22;		/* default line count */
int	linelength = 80;	/* default line size */

int	paging = OFF;		/* Internal paging of long messages */
int	verbose = ON;		/* Command completion (raw mode) */
int	wmsgflag = OFF;		/* Save messages when calling send */
int	keystrip = ON;		/* Strip header lines on display */
int	bdots = ON;		/* Binary box dots */
int	mdots = ON;		/* Mailbox dots */
int	bprint = ON;		/* Reading... messages */
int	quicknflag = OFF;	/* Turn off NEW before message prints */
int	quickexit = ON;		/* Don't stay in msg if mailbox is empty */
int	prettylist = ON;	/* Print message numbers in listings */
int	filoutflag = ON;	/* Filter text (display controls as ^X) */
int	binsavflag = ON;	/* Save binary box on exec's & pauses */
int	doctrlel = OFF;		/* Page on control-L */
#ifndef SYS5
long	timezone = (-1)*60*60;	/* Seconds West of GMT */
int	daylight = 1;		/* Do you use daylight savings time? */
				/* 0 = NO, 1 = YES */
char	*tzname[] = {"+0100", "+0200",};
				/* Timezone strings, primary and daylight
				   savings time if used, in that order */
#endif SYS5

/***** Do not tailor below this line *****/
int	Nmsgs = NMSGS;
struct message *msgp[NMSGS];
struct message *mptr;
eceive chars              */
{				  /* see d_slvconn & d_masconn          */
    0177777,                      /* nul soh stx etx eot enq ack bel    */
				  /* bs  ht  nl  vt  np  cr  so  si     */
    0177777,                      /* dle dc1 dc2 dc3 dc4 nak syn etb    */
				  /* can em  sub esc fs  gs  rs  us     */
    0000010,                      /* sp  ! " # $ % & ' ( ) * + , - . /  */
    0000000,                      /* mmdf/conf/4.3vax/s_tailor.c   444      0     12         332  3620510315  15362 1mmdf/uip/send/s_tailor.cmmdf/conf/4.3vax/s_tailor.h   444      0     12         471  3620510315  15400 1mmdf/uip/send/s_tailor.hmmdf/conf/4.3vax/Makefile.com   444      0     12        7263  3657737650  11170 #
#	Common Sub-Makefile for the MMDF System
#
HOST		= 4.3vax
SYSTEM		= 4.2
MMPREF		=
LIBDIR		= /usr/mmdf
CHANDIR		= /usr/mmdf/chans
TBLDIR		= /usr/mmdf/table
BINDIR		= /usr/local
RCVDIR		= /usr/mmdf/rcvmail

#
#  Defines used during installation
#
CHOWN		= /etc/chown
MMDFLOGIN	= mmdf
ROOTLOGIN	= root
PGMPROT 	= 755

#
#  Configuration Defines
#
#  The paths below are relative from the directories below this one.
#  Define DOASSIGN to CONFIGDEFS if you want the d_assign code
#  Define SECURETTY if you run with terminals write protected (mode 711)
#  Define DEBUG to include extensive calls to the logging package
#  Define D_LOG if you want phone channel logging
#  Define D_DBGLOG if you want phone channel debugging output
#  Define RUNALONE for standalone testing (seldom used)
#  Define V4_2BSD if you are compiling for 4.2BSD
#  Define NODIAL if you do not want the dial package (no phone channel)
#  Define SYS5 if you have a SystemV Unix or something similar (Sys3)
#  Define ALTOS if you have an Altos, does some depraved things.
#  Define NODUP2 if you don't a dup2() syscall or subroutine.
#  Define NOFCNTL and NODUP2 if you don't have fcntl(x, F_DUPFD) or dup2()
#  Define -Drindex=strrchr -Dindex=strchr if you are on a SYS5 system
#  Define NAMESERVER if you want the nameserver lookup code
#  Define UUCPLOCK if you want UUCP/tip/cu-style locking for dial-out lines
#  Define NODOMLIT if you don't want literals like [10.0.0.52] in messages
#
CONFIGDEFS	= -DDEBUG=1 -DV4_2BSD -DNODOMLIT
CFLAGS		= -O -I../../h $(CONFIGDEFS)
LDFLAGS 	=
MMDFLIBS 	= ../../lib/libmmdf.a
SYSLIBS		= -ldbm
LINT		= lint
LFLAGS		= -bxah -I../../h $(CONFIGDEFS)
LLIBS		= ../../lib/llib-lmmdf.ln
AR		= ar


#  Specify either ch_tbdbm (for DBM based tables) or ch_tbseq for
#  sequential IO based tables.
CH_TB	= ch_tbdbm

#  Specify one of the nameserver modules or the fake module
#  if you do not intend to support nameservers (4.2, fake)
TB_NS	= fake

#  Specify tai_???.o and lk_lock???.o
LOCALUTIL = tai_file.o lk_lock.4.2.o


default:	real-default

#
#  special case dependencies
#
../../h/mmdf.h:	../../h/conf.h
	-touch ../../h/mmdf.h
.PRECIOUS: ../../h/mmdf.h

#
#  #include dependencies
#
#  Two versions are supplied.  One for sites with cc -M (4.3BSD)
#  and one for those that don't have it.  Comment out the one
#  you do not want.

#  This one is for sites without cc -M
#depend:
#	cat </dev/null >x.c
#	for i in $(MODULES); do \
#		(echo $$i.o: $$i.c >>makedep; \
#		/bin/grep '^#[ 	]*include' x.c $$i.c | sed \
#			-e 's,c:[^"]*"\./\([^"]*\)".*,o: \1,' \
#			-e 's,c:[^"]*"/\([^"]*\)".*,o: /\1,' \
#			-e 's,c:[^"]*"\([^"]*\)".*,o: ../../h/\1,' \
#			-e 's,c:[^<]*<\(.*\)>.*,o: /usr/include/\1,' \
#			>>makedep); done
#	echo '/^# DO NOT DELETE THIS LINE/+2,$$d' >eddep
#	echo '$$r makedep' >>eddep
#	echo 'w' >>eddep
#	cp Makefile.real Makefile.bak
#	ed - Makefile.real < eddep
#	rm eddep makedep x.c
#	echo '# DEPENDENCIES MUST END AT END OF FILE' >> Makefile.real
#	echo '# IF YOU PUT STUFF HERE IT WILL GO AWAY' >> Makefile.real
#	echo '# see make depend above' >> Makefile.real

#  This one id for sites with cc -M
depend:
	( for i in ${MODULES} ; do \
		${CC} -M ${CFLAGS} $$i.c ; done ) | \
	awk ' { if ($$1 != prev) { print rec; rec = $$0; prev = $$1; } \
		else { if (length(rec $$2) > 78) { print rec; rec = $$0; } \
		       else rec = rec " " $$2 } } \
	      END { print rec } ' > makedep
	echo '/^# DO NOT DELETE THIS LINE/+2,$$d' >eddep
	echo '$$r makedep' >>eddep
	echo 'w' >>eddep
	cp Makefile.real Makefile.bak
	ed - Makefile.real < eddep
	rm eddep makedep
	echo '# DEPENDENCIES MUST END AT END OF FILE' >> Makefile.real
	echo '# IF YOU PUT STUFF HERE IT WILL GO AWAY' >> Makefile.real
	echo '# see make depend above' >> Makefile.real
G **************************/

struct ll_struct    ph_log =       /* dial-out link-level package log    */
{
    "ph.log", "PH-0000", LLOGFST, 25, LLOGSOME, 0
};

char *def_trn = "ph.trn";          /* dial-out link-level i/o transcript */
				  /* does not use ll_log package        */
o handle special switches   */
				    /* probammdf/conf/test/   755      0     12           0  3671074635   6605 mmdf/conf/test/chan.c   444      0     12        6062  3620510315   7731 #include "util.h"
#include "mmdf.h"
#include "ch.h"
#include "dm.h"

char    *ch_dflnam = "local";

Alias	*al_list = (Alias *)0;

/*******************  MAIL IDENTIFIER  SELECTION  ***********************/

/*
 *	The global mid_enable controls the use of an alternate
 *	mail name system refered to as Mail-Ids.  When mail-ids are
 *	in use, login-ids and mail-ids are nolong bound to one another
 *	by the system account database, and are bound only by mapping
 *	tables.  The tables "mailids" and "users" will always be present
 *	though they may not be used.  "mailids" maps a mailid to a
 *	system account.  The table "users" maps a username to a mailid.
 */

int	mid_enable = 0;

Table
	tb_mailids =		/* Used in getpwmid() */
{
    "mailids", "Mail-ID to Username Mappings", "mailids", 0, 0l
},
	tb_users =		/* Used in getmailid() */
{
    "users", "Username to Mail-ID Mappings", "users", 0, 0l
};


/*
 *  The following set of structures provides a complete list of channels
 *  known to MMDF.
 *
 *  It is followed by two arrays that list the channels in useful orders.
 *
 *  ch_tbsrch[] is a full list, of all known channels, and is primarily
 *  used to guide the ch_table routines when treating the hostname space as
 *  flat (linear).  It also is used by lnk_submit, for sorting the address
 *  linked-list.
 *
 *  THE ORDER IS CRITICAL.  A hostname may appear in several tables, but
 *  the first table checked will cause the hit.
 *
 *  One nice aspect of this is that, for example, you can have a standard
 *  arpanet host table, but have some of its hosts actually get mail on a
 *  different channel.  Simply place that table's entry earlier in the
 *  ch_tbsrch[] list.
 *
 *  ch_exsrch[] is a list of channels that are processed by Deliver
 *  (usually just when it does standard, default daemon processing).
 *  Therefore, it need contain only those channels that are active and are
 *  to be processed by the daemon.  Again, order is important.  Channels
 *  are processed in the order listed.  This means that, typically, you
 *  want ch_sloc to be first, unless you have the strange view that foreign
 *  mail is more important than local...
 */

LOCVAR Chan *chsrch[NUMCHANS+1] = {	/* Order chan tables searched */
	(Chan *) 0
};
Chan **ch_tbsrch = chsrch;

LOCVAR Chan *exsrch[NUMCHANS+1] = {	/* Order of active chan execution */
	(Chan *) 0
};
Chan **ch_exsrch = exsrch;
Chan * ch_ptrlist[NUMCHANS + 1];  /* can hold pointers to chans */

LOCVAR Table *tblist[NUMTABLES+1] = {	/* All known tables */
    (Table *) 0
};
Table **tb_list = tblist;

LOCVAR Domain *dmlist[NUMDOMAINS+1];
Domain **dm_list = dmlist;         /* all known domains */

int     tb_maxtables = NUMTABLES; /* max number of slots */
int     ch_maxchans = NUMCHANS;   /* max number of slots */
int     dm_maxtables = NUMDOMAINS; /* max number of slots */

/* These next entries should reflect the ACTUAL number of slots in use  */
int     tb_numtables = 0;         /* actual number of tables */
int     ch_numchans = 0;          /* actual number of channels */
int     dm_numtables = 0;         /* actual number of domains */
*********  SUBMIT TAILORING  ************************** */

int     maxhops = 20;               /* number of Via fields permitted   */
int	mgt_addid = 0;		    /* if set, add message-id if necessary */
int	lnk_listsize = 12;	    /* if more than this many addresses,
				     * then do not send warning and only
				     * send citation on return
				     */


/*   *********************  DELIVER TAILORING  ************************** */

int	maxqueue = 300;		  /*mmdf/conf/test/conf.c   444      0     12       20647  3635352136  10005 #include "util.h"
#include "mmdf.h"
#include "ap_norm.h"

/*            COMPILE-TIME CONFIGURATION FILE WITH SITE-DEPENDENT INFORMATION
 *
 *      This file contains general host-dependent variable info that
 *   the message system needs to have.
 */

char *mmtailor = "/usr/src/newbrl/mmdf/testmmdf/mmdftailor";
				/* location of external tailoring file  */

/* ************************  PUBLIC NAMES  ****************************** */

char
	*locname = "BRL-VGR",	/* Generic name for local host          */
	*locdomain = "ARPA",	/* Name of domain that is local to us */
	*sitesignature = "Mail System (MMDF)",
				/* in signature field of return-mail  */
	*mmdflogin = "mmdf",	/* login name for mmdf         */
	*supportaddr = "MMDF@BRL-VGR.ARPA";
				/* where to send bug reports, etc. */
/* *****************  DEFAULT BASE DIRECTORIES  *********************** */

/*  The following pathnames define the default working directories for
 *  MMDF activities.  Specific files usually will be referenced relative
 *  to these directories.  To override the default, the specific filename
 *  should be specified as an anchored pathname, beginning with a slash.
 *
 *  The following pathnames must be anchored.
 */

char	*cmddfldir = "/usr/src/newbrl/mmdf/testmmdf";
			      /* contains MMDF commands, such as      */
			      /* submit, deliver, and queclean        */
char	*logdfldir = "/usr/src/newbrl/mmdf/testmmdf/log";
			      /* contains highly volatile files, such */
			      /* as logs and check-point markers      */
char	*phsdfldir = "/usr/src/newbrl/mmdf/testmmdf/log/phase";
			      /* contains timestamp files             */
char	*tbldfldir = "/usr/src/newbrl/mmdf/testmmdf/table";
			      /* contains sticky files, such as       */
			      /* name tables & dialing scripts        */
char	*tbldbm = "/usr/src/newbrl/mmdf/testmmdf/table/mmdfdbm";
			      /* dbm() hash of name tables      */
char	*quedfldir = "/usr/src/newbrl/mmdf/testmmdf/lock/home";
			      /* contains queued mail files, in       */
			      /* subordinate directories              */
			      /* (also see below)                     */
char	*chndfldir = "/usr/src/newbrl/mmdf/testmmdf/chans";
				/* contains the channel programs        */
				/* (ch_*) called by deliver             */
char	*lckdfldir = "/tmp";
				/* Directory for lock files SEK           */


/* ***************  DEFAULT LOG LOCATIONS & SETTINGS  ***************** */

char    mlogloc[] = "msg.log";
char    chlogloc[] = "chan.log";

/*  It is common for specific using processes to tailor the second
 *  field, which is the log entry label, so that you can tell which
 *  incarnation of the process made a set of entries.
 *
 *  These structures conform to ll_log.h
 */

struct ll_struct
		    msglog =      /* deliver, submit, & queclean          */
{
    mlogloc, "XX-0000", LLOGFST, 40, LLOGSOME, 0
},

		   chanlog =      /* log for channels & user agents       */
{
    chlogloc, "UA-0000", LLOGFST, 40, LLOGSOME, 0
};

/* Needed for user authorisation logging        */

struct ll_struct authlog =
{
    "auth.log", "AU-0000", LLOGFTR, 40, LLOGSOME, 0
};

/* ****************  MESSAGE QUEUE SUBSTRUCTURE  ********************** */

/*  The queue has a top-level, locking directory, which only trusted
 *  processes can get through.  Under that is a base working directory.
 *  Below that are three directories which contain queued messages data
 *
 *  quedfldir[] specifies the top-level and base directories.
 */

int     queprot = 0700;           /* protection on quedfldir parent      */

/* the next five are relative to quedfldir[] */

char
	*deadletter = "DeadLetters",
				/* Actually NOT a directory, where	*/
				/* unreturned letters go to die.	*/
	*tquedir = "tmp/",	/* sub-directory for builing            */
				/* address list file for msg            */
	*aquedir = "addr/",	/* sub-dir with files of address        */
				/* lists for queued mail; files are     */
				/* linked into here from tquedir        */
				/* as last act of queuing msg           */
	*squepref = "q.",	/* preface sub-queue name with this	*/
	*mquedir = "msg/";	/* sub-dir containing text of           */
				/* queued messages; file names          */
				/* are same as in aquedir               */

/*  The following two parameters establish thresholds for queue residency
 *  limits.  If a message does not move out of the queue by warntime,
 *  a notice may be sent to the originator.  If it is still in the queue
 *  by failtime, it is removed from the queue and may be returned to the
 *  originator.
 */

int
	warntime = 60,
	failtime = 252;


/* ******************  COMMAND NAMES & LOCATIONS  *********************** */

char
	*namsubmit = "submit",      /* who to submit mail through         */
	*pathsubmit = "submit",     /* location relative to cmddfldir[]   */

	*namdeliver = "deliver",    /* name of delivery overseer          */
	*pathdeliver = "deliver",   /* location relative to cmddfldir[]   */

	*nampkup = "pickup",        /* name of pickup overseer            */
	*pathpkup = "deliver",      /* location relative to cmddfldir[]   */

	*nammail = "mmdfmail",      /* the mmdf simple mail-sender        */
	*pathmail = "v6mail";       /* needs to handle special switches   */
				    /* probably must be MMDF's uip/mail   */

/* *********************  SUBMIT TAILORING  ************************** */

int     maxhops = 20;               /* number of Via fields permitted   */
int	mgt_addid = 0;		    /* if set, add message-id if necessary */
int	lnk_listsize = 12;	    /* if more than this many addresses,
				     * then do not send warning and only
				     * send citation on return
				     */


/*   *********************  DELIVER TAILORING  ************************** */

int	maxqueue = 100;		  /* maximum size of sortable queue       */
int	mailsleep = 600;          /* seconds between queue sweeps by a    */
				  /* background (-b) Deliver              */

/*   *********************  ADDRESS TRANSFORMATION  ********************* */


/*  Format:
 *
 *  Original host name, New hostname, String appended to mailbox
 *
 *      e.g.        "DCrocker at UDel-EE"
 *      maps to     "DCrocker.EE at UDel"
 *
 *  Note the usage, for Darcom.  It is designed to bypass address
 *  mapping, so that "foo at bar" does not go out as "foo.bar at relay"
 */

LOCVAR struct ap_hstab hstab[] =
{
    0, 0, 0,
};
struct ap_hstab *ap_exhstab = hstab;

/* *****************  LOCAL DELIVERY TAILORING  ************************* */

/*  Obviously, the following is only needed if there is local delivery,
 *  rather than pure relaying (a rare, but permitted mode).  Simplest
 *  determinor is ch_okloc in conf_chan.c
 */

int     sentprotect = 0600;         /* protection on mail files             */

char
	*dlvfile = ".maildelivery", /* User specified delivery instructions */
	*sysdlvfile = (char *)0,  /* if non-zero, the default dlvfile */
	*mldfldir = { 0 },	/* directory to contain users' file     */
				/* for receiving local mail.  if this   */
				/* spec is empty, recipient's login     */
				/* directory will be used.              */
	*mldflfil = "mailbox",    /* file to receive new mail             */
				  /* relative to mldfldir[] or to login   */
				  /*   dir if mldfldir[] is empty         */
				  /* if this spec empty, user's login     */
				  /* name will be name of file            */
	*delim1 = "\001\001\001\001\n",
	*delim2 = "\001\001\001\001\n";
				  /* add to begin and end of messages     */
				  /* NOTE:  to avoid author spoofin,      */
				  /* lo_wtmail() catches mail that        */
				  /* has either of these strings in it    */
				  /* and changes the first char           */
				  /* (it increments its ascii value)      */


/* *****************  UUCP CHANNEL TAILORING  *********************** */

char	*Uchan = "uucp";		/* default channel name */
char    *Uuxstr = "uux - -r";		/* command string to start UUX */

/******************* NIFTP CHANNEL TAILORING  *************************/

char	*pn_quedir   = "/usr/spool/jntmail";
				/* Location of JNTmail queues for NIFTP */

/******************* MULTIPLE HOST TAILORING **************************
 *
 *  This variable is initialised to be the name of the local machine
 *  It is used to transparently forward between the UCL machines
 */
char	*locmachine = "BRL-VGR";


/******************* AUTHORIZATION TAILORING **************************/
char	*authrequest = "Lamas@BRL-VGR";
				/* authorisation request address        */
char	*authfile = "/usr/src/newbrl/mmdf/testmmdf/warning";
				/* warning letter - full pathname       */

ueue and may be returned to the
 *  originator.
 */

int
	warntime = 60,
	failtime = 252;mmdf/conf/test/conf.h   444      0     12        2726  3623122355   7763 #
/*          This contains the most site-dependent definitions             */

#define USERSIZE    8		/* max length of a user system name   */
#define	MAILIDSIZ  64		/* max length of a mailid,
				 * must be at least USERSIZE.		*/
#define MSGNSIZE   14           /* max length of a message file name  */
#define FILNSIZE   64           /* max length of full file name       */
#define ADDRSIZE  256		/* max length of an address           */
#define LINESIZE  256		/* max length of a line/record        */

#define	NUMCHANS   20		/* max number of channels */
#define NUMTABLES  30		/* max number of tables */
#define NUMDOMAINS 20		/* max number of domains */

/************  DIAL IN/OUT (CH_PHONE / SLAVE) TAILORING  *****************/

#define DL_TRIES    2             /* number of re-connects OK for phone */
#define DL_RCVSZ    255           /* longest line receivable            */
#define DL_XMTSZ    255           /* longest line sendable              */

/****************  JNTMAIL Tailoring *******************/

#undef	JNTMAIL
#undef	BOTHEND
#undef	VIATRACE

/**************** Include File Tailoring ****************************/

#include <time.h>
#include <dir.h>

/********************* Privileged UIDs *************************/

/*
 *  This can be a macro as give here or you can provide a function.
 *  It should always authorize root and mmdf.
 *  The global variable effecid is assumed to have the MMDF uid in it.
 */

#define	PRIV_USER(x) ((x)==0 || (x)==1 || (x)==effecid)
		     * then do not send warning and onlymmdf/conf/test/conf_dial.c   444      0     12        6717  3622764534  10765 #include "util.h"
#include "ll_log.h"
#include "d_proto.h"
#include "d_structs.h"

/* **********************  DIALING TAILOR INFO  **********************  */

/*  modem speed support definitions  */

# define  BELL103        0177     /*  bell 103  0 to 300 baud  */
# define  VA825          0177     /*  vadic 825  0 to 300 baud  */
# define  VA3415         0400     /*  vadic 3415  1200 baud  */

/*
 *     structure defining the available dial-out ports.
 */

#define NUMPRTS         20
LOCVAR struct dialports  theprts[NUMPRTS] =
  {
  0,             0,                0,           0,    0, 0,      0,   0
  };
struct dialports  *d_prts = theprts;
int d_maxprts = NUMPRTS;
int d_numprts = 0;

#define NUMLINES        10
LOCVAR struct directlines  thelines[NUMLINES] =
  {
  0,              0,             0,                 0, 0
  };

int d_maxlines = NUMLINES;
int d_numlines = 0;
struct directlines  *d_lines = thelines;


/*
 *     this is the path name of the file on which outgoing calls are
 *     logged.  it should be owned by the same user as the outgoing
 *     ports and autodialer devices.
 */

char  *d_calllog  =  "/usr/mmdf/log/dial_log";

/*
 *	this is the name of the programs will actually do dialing
 *	it is called as "dial <line> <number>".  It knows which
 *	acus are attached to which lines.
 */

char	*d_dialprog = "/usr/bin/dial";

/* ***************  ILLEGAL CHARACTER TAILORING  ************************* */

/*  The following sets the default illegal-character arrays for both
    dial-out (phone) and dial-in (slave) connections.
 */


unsigned short d_lrill[8] =       /* illegal receive chars              */
{				  /* see d_slvconn & d_masconn          */
    0177777,                      /* nul soh stx etx eot enq ack bel    */
				  /* bs  ht  nl  vt  np  cr  so  si     */
    0177777,                      /* dle dc1 dc2 dc3 dc4 nak syn etb    */
				  /* can em  sub esc fs  gs  rs  us     */
    0000010,                      /* sp  ! " # $ % & ' ( ) * + , - . /  */
    0000000,                      /* 0 1 2 3 4 5 6 7 8 9 : ; < = > ?    */
    0000001,                      /* @ A B C D E F G H I J K L M N O    */
    0010000,                      /* P Q R S T U V W X Y Z [ \\] ^ _    */
    0000000,                      /* ` a b c d e f g h i j k l m n o    */
    0100000                       /* p q r s t u v w x y z { | } ~ del  */
};

unsigned short d_lxill[8] =       /* illegal send chars                 */
{				  /* see d_slvconn & d_masconn          */
    0177777,                      /* nul soh stx etx eot enq ack bel    */
				  /* bs  ht  nl  vt  np  cr  so  si     */
    0177777,                      /* dle dc1 dc2 dc3 dc4 nak syn etb    */
				  /* can em  sub esc fs  gs  rs  us     */
    0000010,                      /* sp  ! " # $ % & ' ( ) * + , - . /  */
    0000000,                      /* 0 1 2 3 4 5 6 7 8 9 : ; < = > ?    */
    0000001,                      /* @ A B C D E F G H I J K L M N O    */
    0010000,                      /* P Q R S T U V W X Y Z [ \\] ^ _    */
    0000000,                      /* ` a b c d e f g h i j k l m n o    */
    0100000                       /* p q r s t u v w x y z { | } ~ del  */
};

/************************* PHONE LOGGING *************************/

struct ll_struct    ph_log =       /* dial-out link-level package log    */
{
    "ph.log", "PH-0000", LLOGFST, 25, LLOGSOME, 0
};

char *def_trn = "ph.trn";          /* dial-out link-level i/o transcript */
				  /* does not use ll_log package        */
ng (a rare, but permitted mode).  Simplest
 *  demmdf/conf/test/Makefile.com   444      0     12        7353  3657737652  11126 #
#	Common Sub-Makefile for the MMDF System
#
HOST		= test
SYSTEM		= 4.2
MMPREF		=
LIBDIR		= /usr/src/brl/mmdf/testmmdf
CHANDIR		= /usr/src/brl/mmdf/testmmdf/chans
TBLDIR		= /usr/src/brl/mmdf/testmmdf/table
BINDIR		= /usr/src/brl/mmdf/testmmdf
RCVDIR		= /usr/src/brl/mmdf/testmmdf/rcvmail

#
#  Defines used during installation
#
CHOWN		= /etc/chown
MMDFLOGIN	= mmdf
ROOTLOGIN	= root
PGMPROT 	= 755

#
#  The paths below are relative from the directories below this one.
#  Define DOASSIGN to CONFIGDEFS if you want the d_assign code
#  Define SECURETTY if you run with terminals write protected (mode 711)
#  Define DEBUG to include extensive calls to the logging package
#  Define D_LOG if you want phone channel logging
#  Define D_DBGLOG if you want phone channel debugging output
#  Define RUNALONE for standalone testing (seldom used)
#  Define V4_2BSD if you are compiling for 4.2BSD
#  Define NODIAL if you do not want the dial package (no phone channel)
#  Define SYS5 if you have a SystemV Unix or something similar (Sys3)
#  Define ALTOS if you have an Altos, does some depraved things.
#  Define NODUP2 if you don't a dup2() syscall or subroutine.
#  Define NOFCNTL and NODUP2 if you don't have fcntl(x, F_DUPFD) or dup2()
#  Define -Drindex=strrchr -Dindex=strchr if you are on a SYS5 system
#  Define NAMESERVER if you want the nameserver lookup code
#  Define UUCPLOCK if you want UUCP/tip/cu-style locking for dial-out lines
#  Define NODOMLIT if you don't want literals like [10.0.0.52] in messages
#
CONFIGDEFS	= -DDEBUG=1 -DV4_2BSD -DNODOMLIT
CFLAGS		= -O -I../../h $(CONFIGDEFS)
LDFLAGS 	= 
MMDFLIBS 	= ../../lib/libmmdf.a
SYSLIBS		= -ldbm
LINT		= lint
LFLAGS		= -phbvxacL -I../../h $(CONFIGDEFS)
LLIBS		= ../../lib/llib-lmmdf.ln
AR		= ar


#  Specify either ch_tbdbm (for DBM based tables) or ch_tbseq for
#  sequential IO based tables.
CH_TB	= ch_tbdbm

#  Specify one of the nameserver modules or the fake module
#  if you do not intend to support nameservers (4.2, fake)
TB_NS	= 4.2

#  Specify tai_???.o and lk_lock???.o
LOCALUTIL = tai_file.o lk_lock.4.2.o


default:	real-default

#
#  special case dependencies
#
../../h/mmdf.h:	../../h/conf.h
	-touch ../../h/mmdf.h
.PRECIOUS: ../../h/mmdf.h

#
#  #include dependencies
#
#  Two versions are supplied.  One for sites with cc -M (4.3BSD)
#  and one for those that don't have it.  Comment out the one
#  you do not want.

#  This one is for sites without cc -M
depend:
	cat </dev/null >x.c
	for i in $(MODULES); do \
		(echo $$i.o: $$i.c >>makedep; \
		/bin/grep '^#[ 	]*include' x.c $$i.c | sed \
			-e 's,c:[^"]*"\./\([^"]*\)".*,o: \1,' \
			-e 's,c:[^"]*"/\([^"]*\)".*,o: /\1,' \
			-e 's,c:[^"]*"\([^"]*\)".*,o: ../../h/\1,' \
			-e 's,c:[^<]*<\(.*\)>.*,o: /usr/include/\1,' \
			>>makedep); done
	echo '/^# DO NOT DELETE THIS LINE/+2,$$d' >eddep
	echo '$$r makedep' >>eddep
	echo 'w' >>eddep
	cp Makefile.real Makefile.bak
	ed - Makefile.real < eddep
	rm eddep makedep x.c
	echo '# DEPENDENCIES MUST END AT END OF FILE' >> Makefile.real
	echo '# IF YOU PUT STUFF HERE IT WILL GO AWAY' >> Makefile.real
	echo '# see make depend above' >> Makefile.real

#  This one id for sites with cc -M
#depend:
#	( for i in ${MODULES} ; do \
#		${CC} -M ${CFLAGS} $$i.c ; done ) | \
#	awk ' { if ($$1 != prev) { print rec; rec = $$0; prev = $$1; } \
#		else { if (length(rec $$2) > 78) { print rec; rec = $$0; } \
#		       else rec = rec " " $$2 } } \
#	      END { print rec } ' > makedep
#	echo '/^# DO NOT DELETE THIS LINE/+2,$$d' >eddep
#	echo '$$r makedep' >>eddep
#	echo 'w' >>eddep
#	cp Makefile.real Makefile.bak
#	ed - Makefile.real < eddep
#	rm eddep makedep
#	echo '# DEPENDENCIES MUST END AT END OF FILE' >> Makefile.real
#	echo '# IF YOU PUT STUFF HERE IT WILL GO AWAY' >> Makefile.real
#	echo '# see make depend above' >> Makefile.real
zero, the default dlvfile */
	*mldfldir = { 0 },	/* directory to contain users' file     */
				/* for receiving local mail.  if this   */
				/* spec is empty, recipient's login     */
				/* directory will be used.              */
	*mldflfil = "mailbox",    /* file to receivmmdf/conf/test/s_tailor.c   444      0     12         334  3620510317  10612 /*
 *	S _ T A I L O R . C
 *
 *  Contains strings used to tailor SEND to a particular
 *  site.  Also check s.h for tailorable defines.
 */

char	*dfleditor = "ed";
char	*dflveditor = "jove";
char	*dflchecker = "spell";
 -Drindex=strrchr -Dindex=strchr if you are on a SYS5 system
#  Define NAMESERVER if you want the nameserver lookup code
#  Define UUCPLOCK if you want UUCP/tip/cu-style locking for dial-out lines
#  Define NODOMLIT if you don't want literals like [10.0.0.52] in messages
#
CONFIGDEFS	= -DDEBmmdf/conf/test/msgtailor.c   444      0     12        3114  3671073312  11023 /*
 *	M S G T A I L O R . C
 */

#define NOEXTERNS
#include "./msg.h"

#ifdef pdp11
#	define NMSGS	500
#else
#	define NMSGS	2000
#endif pdp11

char    *savmsgfn = "mbox";
char	*resendprog = "|/usr/brl/bin/resend ";
char	*sndname = "/usr/local/send"; /* Overridden by .msgrc sendprog */

char	*dflshell = "sh";	/* Overridden by getenv("SHELL"); */
char	*dfleditor = "jove";	/* Overridden by getenv("EDITOR"); */

int	pagesize = 22;		/* default line count */
int	linelength = 80;	/* default line size */

int	paging = OFF;		/* Internal paging of long messages */
int	verbose = ON;		/* Command completion (raw mode) */
int	wmsgflag = OFF;		/* Save messages when calling send */
int	keystrip = ON;		/* Strip header lines on display */
int	bdots = ON;		/* Binary box dots */
int	mdots = ON;		/* Mailbox dots */
int	bprint = ON;		/* Reading... messages */
int	quicknflag = OFF;	/* Turn off NEW before message prints */
int	quickexit = ON;		/* Don't stay in msg if mailbox is empty */
int	prettylist = ON;	/* Print message numbers in listings */
int	filoutflag = ON;	/* Filter text (display controls as ^X) */
int	binsavflag = ON;	/* Save binary box on exec's & pauses */
int	doctrlel = OFF;		/* Page on control-L */
#ifndef SYS5
long	timezone = 5*60*60;	/* Seconds West of GMT */
int	daylight = 1;		/* Do you use daylight savings time? */
				/* 0 = NO, 1 = YES */
char	*tzname[] = {"EST", "EDT",};
				/* Timezone strings, primary and daylight
				   savings time if used, in that order */
#endif SYS5

/***** Do not tailor below this line *****/
int	Nmsgs = NMSGS;
struct message *msgp[NMSGS];
struct message *mptr;
\([^"]*\)".*,o: /\1,' \
			-e 's,c:[^"]*"\([^"]*\)".*,o: ../../h/\1,' \
			-e 's,c:[^<]*<\(.*\)>.*,o: /usr/include/\1,' \
			>>makedep); done
	echo '/^# DO NOT DELETE THIS LINE/+2,$$d' >eddep
	echo '$$r makedep' >>eddep
	echo 'w' >>eddep
	cp Makefile.real Makefile.bak
	ed - Makefile.real < eddep
	rm eddep makedep x.c
	echo '# DEPENDENCIES MUST END AT END OF FILE' >> Makefile.real
	echo '# IF YOU PUT STUFF HERE IT WILL GO AWAY' >> Mammdf/conf/test/Makefile.lib   444      0     12        2220  3652767264  11077 #
#	Makefile for the MMDF library   (libmmdf.a)
#

#  The paths below are relative from the directories below this one.
#
#  The RANLIB define should be "sh libfix" for systems that don't have
#	the ranlib command.  Systems that do have ranlib should set
#	RANLIB to "ranlib".  "libfix" is a Bourne shell file that will
#	use lorder and tsort to reorder the library.
#
MAKE	= make
RANLIB	= ranlib

SUBDIRS = addr dial mmdf table util
ALLSUBDIRS = addr dial mmdf table util

default: remake reorder

remake:
	for dir in $(SUBDIRS); \
	  do (cd $${dir}; echo "Running make in $${dir}"; \
	      gen ${MFLAGS} -${MAKEFLAGS}); \
	  done

install: default
	@echo "Library is up to date, nothing else to do."

reorder: reorder-done

reorder-done: libmmdf.a
	-$(RANLIB) libmmdf.a
	-touch reorder-done

depend:
	for dir in $(SUBDIRS); \
	  do (cd $${dir}; gen ${MFLAGS} -${MAKEFLAGS} depend); done

lint:
	for dir in $(SUBDIRS); \
	  do (cd $${dir}; gen ${MFLAGS} -${MAKEFLAGS} lint); done
	cat */llib-l*.ln > llib-lmmdf.ln

clean:
	-rm -f *-made libmmdf.a make.out core reorder-done
	for dir in $(SUBDIRS); \
	  do (cd $${dir}; $(MAKE) -f Makefile.real clean); done

ALWAYS:
hould be "sh libfix" for systems that don't have
#	the ranlib command.  Systems that do have ranlib should set
#	RANLIB to "ranlib".  "libfix" is a Bourne shell file that will
#	use lorder and tsort to reorder the library.
#
MAKE	= make
RANLIB	= ranlib

SUBDIRS = addr dial mmdf table util
ALLSUBDIRS = addr dial mmdf table util

default: remake reorder

remake:
	for mmdf/conf/test/Makefile.src   444      0     12        2307  3657737653  11132 #	MMDF Program Source Subtree Makefile
#

#
# Programs that live in subdirectories, and have makefiles of their own.
#
SUBDIR=	tools submit deliver local list delay smtp phone pobox \
	uucp

#
#  This should list all the subdirectories that make clean works in.
#  This probably should not be altered unless you add/delete a channel.
#
ALLSUBDIRS=	tools submit deliver local list delay smtp phone pobox \
		uucp niftp bboards pop ean prog

all:
	for dir in ${SUBDIR}; do \
		(cd $${dir}; gen ${MFLAGS} -${MAKEFLAGS}); done

install:
	for dir in ${SUBDIR}; do \
		(cd $${dir}; gen ${MFLAGS} -${MAKEFLAGS} install); done

dist:
	-touch `ls */x*|grep -v '\.'`
	echo echo MMDF Distribution made `date` > install.sh
	for dir in ${SUBDIR}; do \
		echo cd $${dir} >> install.sh; \
		( cd $${dir}; \
		  gen -n ${MFLAGS} -${MAKEFLAGS} install) >> install.sh; \
		echo cd .. >> install.sh; \
	done
	tar cvf mmdfdist.tar install.sh */x*

depend:
	for dir in ${SUBDIR}; do \
		(cd $${dir}; gen ${MFLAGS} -${MAKEFLAGS} depend); done

lint:
	for dir in ${SUBDIR}; do \
		(cd $${dir}; gen ${MFLAGS} -${MAKEFLAGS} lint); done

clean:
	-rm -f make.out core
	for dir in ${ALLSUBDIRS}; do \
		(cd $${dir}; make -f Makefile.real clean); done
long	timezone = 5*60*60;	/* Seconds West of GMT */
int	daylight = 1;		/* Do you use daylight savings time? */
				/* 0 = NO, 1 = YES */
char	*tzname[] = {"EST", "EDT",};
				/* Timezone strings, primary and daylight
				   savings time if used, in that order */
#endif SYS5

/***** Do not tailor below this line *mmdf/conf/test/s_tailor.h   444      0     12         471  3620510321  10614 /*
 *	Site Tunable Parameters
 */
#undef	SENTFILE yes	/* Define to have copies of mail sent put in ,sent */
#undef	FNCYPRNT yes	/* Define for fancy listing of message (paginated) */
#define	PWNAME yes	/* Define to get real name from /etc/passwd and to */
			/* treat 0 length signatures to get login name only */
dd/delete a channel.
#
ALLSUBDIRS=	tools submit deliver local list delay smtp phone pobox \
		uucp niftp bboards pop ean prog

all:
	for dir in ${SUBDIR}; do \
		(cd $${dir}; gen ${MFLAGS} -${MAKEFLAmmdf/conf/csnet-relay/   755      0     12           0  3671074640  10050 mmdf/conf/csnet-relay/conf_dial.c   444      0     12        6712  3664425104  12220 #include "util.h"
#include "ll_log.h"
#include "d_proto.h"
#include "d_structs.h"

/* **********************  DIALING TAILOR INFO  **********************  */

/*  modem speed support definitions  */

# define  BELL103        0177     /*  bell 103  0 to 300 baud  */
# define  VA825          0177     /*  vadic 825  0 to 300 baud  */
# define  VA3415         0400     /*  vadic 3415  1200 baud  */

/*
 *     structure defining the available dial-out ports.
 */

#define NUMPRTS         20
LOCVAR struct dialports  theprts[NUMPRTS] =
  {
  0,             0,                0,           0,    0, 0,      0,   0
  };
struct dialports  *d_prts = theprts;
int d_maxprts = NUMPRTS;
int d_numprts = 0;

#define NUMLINES        10
LOCVAR struct directlines  thelines[NUMLINES] =
  {
  0,              0,             0,                 0, 0
  };

int d_maxlines = NUMLINES;
int d_numlines = 0;
struct directlines  *d_lines = thelines;


/*
 *     this is the path name of the file on which outgoing calls are
 *     logged.  it should be owned by the same user as the outgoing
 *     ports and autodialer devices.
 */

char  *d_calllog  =  "/mmdf/log/dial_log";

/*
 *	this is the name of the programs will actually do dialing
 *	it is called as "dial <line> <number>".  It knows which
 *	acus are attached to which lines.
 */

char	*d_dialprog = "/usr/bin/dial";

/* ***************  ILLEGAL CHARACTER TAILORING  ************************* */

/*  The following sets the default illegal-character arrays for both
    dial-out (phone) and dial-in (slave) connections.
 */


unsigned short d_lrill[8] =       /* illegal receive chars              */
{				  /* see d_slvconn & d_masconn          */
    0177777,                      /* nul soh stx etx eot enq ack bel    */
				  /* bs  ht  nl  vt  np  cr  so  si     */
    0177777,                      /* dle dc1 dc2 dc3 dc4 nak syn etb    */
				  /* can em  sub esc fs  gs  rs  us     */
    0000000,                      /* sp  ! " # $ % & ' ( ) * + , - . /  */
    0000000,                      /* 0 1 2 3 4 5 6 7 8 9 : ; < = > ?    */
    0000000,                      /* @ A B C D E F G H I J K L M N O    */
    0000000,                      /* P Q R S T U V W X Y Z [ \\] ^ _    */
    0000000,                      /* ` a b c d e f g h i j k l m n o    */
    0100000                       /* p q r s t u v w x y z { | } ~ del  */
};

unsigned short d_lxill[8] =       /* illegal send chars                 */
{				  /* see d_slvconn & d_masconn          */
    0177777,                      /* nul soh stx etx eot enq ack bel    */
				  /* bs  ht  nl  vt  np  cr  so  si     */
    0177777,                      /* dle dc1 dc2 dc3 dc4 nak syn etb    */
				  /* can em  sub esc fs  gs  rs  us     */
    0000000,                      /* sp  ! " # $ % & ' ( ) * + , - . /  */
    0000000,                      /* 0 1 2 3 4 5 6 7 8 9 : ; < = > ?    */
    0000000,                      /* @ A B C D E F G H I J K L M N O    */
    0000000,                      /* P Q R S T U V W X Y Z [ \\] ^ _    */
    0000000,                      /* ` a b c d e f g h i j k l m n o    */
    0100000                       /* p q r s t u v w x y z { | } ~ del  */
};

/************************ PHONE LOGGING ************************/

struct ll_struct    ph_log =       /* dial-out link-level package log    */
{
    "ph.log", "PH-0000", LLOGFST, 100, LLOGSOME, 0
};

char *def_trn = "ph.trn";          /* dial-out link-level i/o transcript */
				  /* does not use ll_log package        */
these strings in it    */
				  /* and changes the firmmdf/conf/csnet-relay/chan.c   444      0     12        6226  3620510322  11200 #include "util.h"
#include "mmdf.h"
#include "ch.h"
#include "dm.h"

char    *ch_dflnam = "local";

Alias	*al_list = (Alias *)0;

/*******************  MAIL IDENTIFIER  SELECTION  ***********************/

/*
 *	The global mid_enable controls the use of an alternate
 *	mail name system refered to as Mail-Ids.  When mail-ids are
 *	in use, login-ids and mail-ids are nolong bound to one another
 *	by the system account database, and are bound only by mapping
 *	tables.  The tables "mailids" and "users" will always be present
 *	though they may not be used.  "mailids" maps a mailid to a
 *	system account.  The table "users" maps a username to a mailid.
 */

int	mid_enable = 0;

Table
	tb_mailids =		/* Used in getpwmid() */
{
    "mailids", "Mail-ID to Username Mappings", "mailids", 0, 0l
},
	tb_users =		/* Used in getmailid() */
{
    "users", "Username to Mail-ID Mappings", "users", 0, 0l
};


/*
 *  The following set of structures provides a complete list of channels
 *  known to MMDF.
 *
 *  It is followed by two arrays that list the channels in useful orders.
 *
 *  ch_tbsrch[] is a full list, of all known channels, and is primarily
 *  used to guide the ch_table routines when treating the hostname space as
 *  flat (linear).  It also is used by lnk_submit, for sorting the address
 *  linked-list.
 *
 *  THE ORDER IS CRITICAL.  A hostname may appear in several tables, but
 *  the first table checked will cause the hit.
 *
 *  One nice aspect of this is that, for example, you can have a standard
 *  arpanet host table, but have some of its hosts actually get mail on a
 *  different channel.  Simply place that table's entry earlier in the
 *  ch_tbsrch[] list.
 *
 *  ch_exsrch[] is a list of channels that are processed by Deliver
 *  (usually just when it does standard, default daemon processing).
 *  Therefore, it need contain only those channels that are active and are
 *  to be processed by the daemon.  Again, order is important.  Channels
 *  are processed in the order listed.  This means that, typically, you
 *  want ch_sloc to be first, unless you have the strange view that foreign
 *  mail is more important than local...
 */

#include "chans.init"

/* INITCHANS
/*LOCVAR Chan *chsrch[NUMCHANS+1] = {	  /* Order chan tables searched */
/*	  (Chan *) 0
/*};
/**/
Chan **ch_tbsrch = chsrch;

LOCVAR Chan *exsrch[NUMCHANS+1] = {	/* Order of active chan execution */
	(Chan *) 0
};
Chan **ch_exsrch = exsrch;
Chan * ch_ptrlist[NUMCHANS + 1];  /* can hold pointers to chans */

/* INITCHANS
/*LOCVAR Table *tblist[NUMTABLES+1] = {	  /* All known tables */
/*    (Table *) 0
/*};
/**/
Table **tb_list = tblist;

LOCVAR Domain *dmlist[NUMDOMAINS+1];
Domain **dm_list = dmlist;         /* all known domains */

int     tb_maxtables = NUMTABLES; /* max number of slots */
int     ch_maxchans = NUMCHANS;   /* max number of slots */
int     dm_maxtables = NUMDOMAINS; /* max number of slots */

/* These next entries should reflect the ACTUAL number of slots in use  */

/* INITCHANS
/*int     tb_numtables = 0;         /* actual number of tables */
/*int     ch_numchans = 0;          /* actual number of channels */
/**/
int     dm_numtables = 0;         /* actual number of domains */
********** PHONE LOGGING ************************/

struct ll_struct    ph_log =       /* dial-out link-level package log    */
{
    "ph.log", "PH-0000", LLOGFST, 100, LLOGSOME, 0
};

char *def_trn = "ph.trn";          /* dial-out link-level i/o transcript */
				  /* does not use ll_log package        */
these strings in it    */
				  /* and changes the firmmdf/conf/csnet-relay/chans.init   444      0     12      171507  3664425117  12170 LOCVAR Table _tblist[]	/* Data blocks for the pre-initialized tables */
	=
{
    { "aliases", "aliases", "aliases", (FILE *)0, 0L, 00 },
    { "Top-Level", "Top Level Domain", "top", (FILE *)0, 0L, 00 },
    { "LIST", "LIST Psuedo-domain", "list", (FILE *)0, 0L, 00 },
    { "CSNET", "CSNET Domain", "csnet", (FILE *)0, 0L, 00 },
    { "ARPA", "ARPA Domain", "arpa", (FILE *)0, 0L, 00 },
    { "BITNET", "BITNET Domain", "bitnet", (FILE *)0, 0L, 00 },
    { "smtp", "SMTP Hosts", "smtp", (FILE *)0, 0L, 00 },
    { "smtpknown", "SMTP hosts", "smtpknown", (FILE *)0, 0L, 00 },
    { "siteknown", "hosts known to 733 sites", "siteknown", (FILE *)0, 0L, 00 },
    { "local", "local", "local", (FILE *)0, 0L, 00 },
    { "albany", "albany", "albany", (FILE *)0, 0L, 00 },
    { "alcoa-atc", "alcoa-atc", "alcoa-atc", (FILE *)0, 0L, 00 },
    { "apple", "apple", "apple", (FILE *)0, 0L, 00 },
    { "australia", "australia", "australia", (FILE *)0, 0L, 00 },
    { "arizona", "Arizona/TCP", "arizona", (FILE *)0, 0L, 00 },
    { "asu", "asu", "asu", (FILE *)0, 0L, 00 },
    { "bbnv1", "bbnv1", "bbnv1", (FILE *)0, 0L, 00 },
    { "bgsu", "bgsu", "bgsu", (FILE *)0, 0L, 00 },
    { "bostonu", "BostonU/TCP", "bostonu", (FILE *)0, 0L, 00 },
    { "boulder", "boulder", "boulder", (FILE *)0, 0L, 00 },
    { "brandeis", "brandeis", "brandeis", (FILE *)0, 0L, 00 },
    { "brown", "brown", "brown", (FILE *)0, 0L, 00 },
    { "bsu", "bsu", "bsu", (FILE *)0, 0L, 00 },
    { "btl", "btl", "btl", (FILE *)0, 0L, 00 },
    { "buffalo", "buffalo", "buffalo", (FILE *)0, 0L, 00 },
    { "canisius", "canisius", "canisius", (FILE *)0, 0L, 00 },
    { "carleton", "carleton", "carleton", (FILE *)0, 0L, 00 },
    { "case", "case", "case", (FILE *)0, 0L, 00 },
    { "cgi", "cgi", "cgi", (FILE *)0, 0L, 00 },
    { "chalmers", "Sweden/PhoneNet", "chalmers", (FILE *)0, 0L, 00 },
    { "clemson", "clemson", "clemson", (FILE *)0, 0L, 00 },
    { "colgate", "colgate", "colgate", (FILE *)0, 0L, 00 },
    { "colostate", "colostate", "colostate", (FILE *)0, 0L, 00 },
    { "ct", "ct", "ct", (FILE *)0, 0L, 00 },
    { "dartmouth", "dartmouth", "dartmouth", (FILE *)0, 0L, 00 },
    { "depaul", "depaul", "depaul", (FILE *)0, 0L, 00 },
    { "digital", "digital", "digital", (FILE *)0, 0L, 00 },
    { "duke", "duke", "duke", (FILE *)0, 0L, 00 },
    { "emory", "emory", "emory", (FILE *)0, 0L, 00 },
    { "france", "france", "france", (FILE *)0, 0L, 00 },
    { "fresno-state", "fresno-state", "fresno-state", (FILE *)0, 0L, 00 },
    { "gatech", "gatech", "gatech", (FILE *)0, 0L, 00 },
    { "germany", "germany", "germany", (FILE *)0, 0L, 00 },
    { "gmr", "gmr", "gmr", (FILE *)0, 0L, 00 },
    { "gte-labs", "gte-labs", "gte-labs", (FILE *)0, 0L, 00 },
    { "houston", "houston", "houston", (FILE *)0, 0L, 00 },
    { "hplabs", "HPLABS/TCP", "hplabs", (FILE *)0, 0L, 00 },
    { "hplb", "hplb", "hplb", (FILE *)0, 0L, 00 },
    { "hugo-test", "hugo-test", "hugo-test", (FILE *)0, 0L, 00 },
    { "ibm-sj", "IBM-SJ/TCP", "ibm-sj", (FILE *)0, 0L, 00 },
    { "icase", "icase", "icase", (FILE *)0, 0L, 00 },
    { "indiana", "indiana", "indiana", (FILE *)0, 0L, 00 },
    { "intel-isc", "intel-isc", "intel-isc", (FILE *)0, 0L, 00 },
    { "intel-sc", "intel-sc", "intel-sc", (FILE *)0, 0L, 00 },
    { "israel", "Israel/PhoneNet", "israel", (FILE *)0, 0L, 00 },
    { "iowa-state", "iowa-state", "iowa-state", (FILE *)0, 0L, 00 },
    { "nasa-jsc", "nasa-jsc", "nasa-jsc", (FILE *)0, 0L, 00 },
    { "kaist", "kaist", "kaist", (FILE *)0, 0L, 00 },
    { "kansas-state", "kansas-state", "kansas-state", (FILE *)0, 0L, 00 },
    { "kentvax", "kentvax", "kentvax", (FILE *)0, 0L, 00 },
    { "lsu", "lsu", "lsu", (FILE *)0, 0L, 00 },
    { "mcnc", "mcnc", "mcnc", (FILE *)0, 0L, 00 },
    { "mich-state", "mich-state", "mich-state", (FILE *)0, 0L, 00 },
    { "mtu", "mtu", "mtu", (FILE *)0, 0L, 00 },
    { "ncar", "ncar", "ncar", (FILE *)0, 0L, 00 },
    { "ncr", "ncr", "ncr", (FILE *)0, 0L, 00 },
    { "nmsu", "nmsu", "nmsu", (FILE *)0, 0L, 00 },
    { "nmt", "nmt", "nmt", (FILE *)0, 0L, 00 },
    { "northeastern", "northeastern", "northeastern", (FILE *)0, 0L, 00 },
    { "nsf", "nsf", "nsf", (FILE *)0, 0L, 00 },
    { "nsf-cs", "nsf-cs", "nsf-cs", (FILE *)0, 0L, 00 },
    { "nwu", "nwu", "nwu", (FILE *)0, 0L, 00 },
    { "odu", "odu", "odu", (FILE *)0, 0L, 00 },
    { "ohio-state", "Ohio-state/TCP", "ohio-state", (FILE *)0, 0L, 00 },
    { "okstate", "okstate", "okstate", (FILE *)0, 0L, 00 },
    { "oregon-grad", "oregon-grad", "oregon-grad", (FILE *)0, 0L, 00 },
    { "omnitor", "Omnitor/TCP", "omnitor", (FILE *)0, 0L, 00 },
    { "oregon-state", "oregon-state", "oregon-state", (FILE *)0, 0L, 00 },
    { "penn-state", "penn-state", "penn-state", (FILE *)0, 0L, 00 },
    { "pitt", "pitt", "pitt", (FILE *)0, 0L, 00 },
    { "portland", "portland", "portland", (FILE *)0, 0L, 00 },
    { "princeton", "princeton", "princeton", (FILE *)0, 0L, 00 },
    { "rit", "rit", "rit", (FILE *)0, 0L, 00 },
    { "rpics", "rpics", "rpics", (FILE *)0, 0L, 00 },
    { "scarolina", "scarolina", "scarolina", (FILE *)0, 0L, 00 },
    { "sju", "sju", "sju", (FILE *)0, 0L, 00 },
    { "slb-doll", "slb-doll", "slb-doll", (FILE *)0, 0L, 00 },
    { "slb-test", "slb-test", "slb-test", (FILE *)0, 0L, 00 },
    { "smu", "smu", "smu", (FILE *)0, 0L, 00 },
    { "sperry-csd", "sperry-csd", "sperry-csd", (FILE *)0, 0L, 00 },
    { "sperry-ctc", "sperry-ctc", "sperry-ctc", (FILE *)0, 0L, 00 },
    { "src", "src", "src", (FILE *)0, 0L, 00 },
    { "suny-bing", "suny-bing", "suny-bing", (FILE *)0, 0L, 00 },
    { "suny-sbcs", "suny-sbcs", "suny-sbcs", (FILE *)0, 0L, 00 },
    { "swri", "swri", "swri", (FILE *)0, 0L, 00 },
    { "syr", "syr", "syr", (FILE *)0, 0L, 00 },
    { "tamu", "tamu", "tamu", (FILE *)0, 0L, 00 },
    { "tektronix", "tektronix", "tektronix", (FILE *)0, 0L, 00 },
    { "temple", "temple", "temple", (FILE *)0, 0L, 00 },
    { "tennessee", "tennessee", "tennessee", (FILE *)0, 0L, 00 },
    { "ti-eg", "ti-eg", "ti-eg", (FILE *)0, 0L, 00 },
    { "ti-csl", "ti-csl", "ti-csl", (FILE *)0, 0L, 00 },
    { "toronto", "toronto", "toronto", (FILE *)0, 0L, 00 },
    { "tufts", "tufts", "tufts", (FILE *)0, 0L, 00 },
    { "tulane", "tulane", "tulane", (FILE *)0, 0L, 00 },
    { "uab", "uab", "uab", (FILE *)0, 0L, 00 },
    { "ubc", "ubc", "ubc", (FILE *)0, 0L, 00 },
    { "ucd", "ucd", "ucd", (FILE *)0, 0L, 00 },
    { "ucf", "ucf", "ucf", (FILE *)0, 0L, 00 },
    { "uchicago", "uchicago", "uchicago", (FILE *)0, 0L, 00 },
    { "uconn", "uconn", "uconn", (FILE *)0, 0L, 00 },
    { "ucsb", "ucsb", "ucsb", (FILE *)0, 0L, 00 },
    { "ucsc", "ucsc", "ucsc", (FILE *)0, 0L, 00 },
    { "udenver", "udenver", "udenver", (FILE *)0, 0L, 00 },
    { "uhcl", "uhcl", "uhcl", (FILE *)0, 0L, 00 },
    { "uiowa", "uiowa", "uiowa", (FILE *)0, 0L, 00 },
    { "ukans", "ukans", "ukans", (FILE *)0, 0L, 00 },
    { "uky", "uky", "uky", (FILE *)0, 0L, 00 },
    { "ufl", "ufl", "ufl", (FILE *)0, 0L, 00 },
    { "ulowell", "ulowell", "ulowell", (FILE *)0, 0L, 00 },
    { "umass-boston", "umass-boston", "umass-boston", (FILE *)0, 0L, 00 },
    { "umass-cs", "umass-cs", "umass-cs", (FILE *)0, 0L, 00 },
    { "umich", "umich", "umich", (FILE *)0, 0L, 00 },
    { "umiss", "umiss", "umiss", (FILE *)0, 0L, 00 },
    { "umn-cs", "umn-cs", "umn-cs", (FILE *)0, 0L, 00 },
    { "umn-duluth", "umn-duluth", "umn-duluth", (FILE *)0, 0L, 00 },
    { "unc", "unc", "unc", (FILE *)0, 0L, 00 },
    { "unh", "unh", "unh", (FILE *)0, 0L, 00 },
    { "unl", "unl", "unl", (FILE *)0, 0L, 00 },
    { "unlv", "unlv", "unlv", (FILE *)0, 0L, 00 },
    { "uoregon", "uoregon", "uoregon", (FILE *)0, 0L, 00 },
    { "upenn", "upenn", "upenn", (FILE *)0, 0L, 00 },
    { "usc-cse", "USC-CSE/TCP", "usc-cse", (FILE *)0, 0L, 00 },
    { "usf", "usf", "usf", (FILE *)0, 0L, 00 },
    { "usl", "usl", "usl", (FILE *)0, 0L, 00 },
    { "uta", "uta", "uta", (FILE *)0, 0L, 00 },
    { "utd-cs", "utd-cs", "utd-cs", (FILE *)0, 0L, 00 },
    { "utokyo-relay", "utokyo-relay", "utokyo-relay", (FILE *)0, 0L, 00 },
    { "uvm", "uvm", "uvm", (FILE *)0, 0L, 00 },
    { "uwmeecs", "uwmeecs", "uwmeecs", (FILE *)0, 0L, 00 },
    { "vanderbilt", "vanderbilt", "vanderbilt", (FILE *)0, 0L, 00 },
    { "vpi", "vpi", "vpi", (FILE *)0, 0L, 00 },
    { "virginia", "virginia", "virginia", (FILE *)0, 0L, 00 },
    { "wang-inst", "wang-inst", "wang-inst", (FILE *)0, 0L, 00 },
    { "waterloo", "waterloo", "waterloo", (FILE *)0, 0L, 00 },
    { "wfu", "wfu", "wfu", (FILE *)0, 0L, 00 },
    { "williams", "williams", "williams", (FILE *)0, 0L, 00 },
    { "wiscvm", "WISCVM/TCP", "wiscvm", (FILE *)0, 0L, 00 },
    { "wright", "wright", "wright", (FILE *)0, 0L, 00 },
    { "wsu", "wsu", "wsu", (FILE *)0, 0L, 00 },
    { "wwu", "wwu", "wwu", (FILE *)0, 0L, 00 },
    { "test-in", "Inbound phone test", "test-in", (FILE *)0, 0L, 00 },
    { "test-out", "Outbound phone test", "test-out", (FILE *)0, 0L, 00 },
    { "test-relay", "To Test-Relay", "test-relay", (FILE *)0, 0L, 00 },
};
LOCVAR Table * tblist[NUMTABLES+1]	/* all known tables */
	=
{
    &_tblist[0],
    &_tblist[1],
    &_tblist[2],
    &_tblist[3],
    &_tblist[4],
    &_tblist[5],
    &_tblist[6],
    &_tblist[7],
    &_tblist[8],
    &_tblist[9],
    &_tblist[10],
    &_tblist[11],
    &_tblist[12],
    &_tblist[13],
    &_tblist[14],
    &_tblist[15],
    &_tblist[16],
    &_tblist[17],
    &_tblist[18],
    &_tblist[19],
    &_tblist[20],
    &_tblist[21],
    &_tblist[22],
    &_tblist[23],
    &_tblist[24],
    &_tblist[25],
    &_tblist[26],
    &_tblist[27],
    &_tblist[28],
    &_tblist[29],
    &_tblist[30],
    &_tblist[31],
    &_tblist[32],
    &_tblist[33],
    &_tblist[34],
    &_tblist[35],
    &_tblist[36],
    &_tblist[37],
    &_tblist[38],
    &_tblist[39],
    &_tblist[40],
    &_tblist[41],
    &_tblist[42],
    &_tblist[43],
    &_tblist[44],
    &_tblist[45],
    &_tblist[46],
    &_tblist[47],
    &_tblist[48],
    &_tblist[49],
    &_tblist[50],
    &_tblist[51],
    &_tblist[52],
    &_tblist[53],
    &_tblist[54],
    &_tblist[55],
    &_tblist[56],
    &_tblist[57],
    &_tblist[58],
    &_tblist[59],
    &_tblist[60],
    &_tblist[61],
    &_tblist[62],
    &_tblist[63],
    &_tblist[64],
    &_tblist[65],
    &_tblist[66],
    &_tblist[67],
    &_tblist[68],
    &_tblist[69],
    &_tblist[70],
    &_tblist[71],
    &_tblist[72],
    &_tblist[73],
    &_tblist[74],
    &_tblist[75],
    &_tblist[76],
    &_tblist[77],
    &_tblist[78],
    &_tblist[79],
    &_tblist[80],
    &_tblist[81],
    &_tblist[82],
    &_tblist[83],
    &_tblist[84],
    &_tblist[85],
    &_tblist[86],
    &_tblist[87],
    &_tblist[88],
    &_tblist[89],
    &_tblist[90],
    &_tblist[91],
    &_tblist[92],
    &_tblist[93],
    &_tblist[94],
    &_tblist[95],
    &_tblist[96],
    &_tblist[97],
    &_tblist[98],
    &_tblist[99],
    &_tblist[100],
    &_tblist[101],
    &_tblist[102],
    &_tblist[103],
    &_tblist[104],
    &_tblist[105],
    &_tblist[106],
    &_tblist[107],
    &_tblist[108],
    &_tblist[109],
    &_tblist[110],
    &_tblist[111],
    &_tblist[112],
    &_tblist[113],
    &_tblist[114],
    &_tblist[115],
    &_tblist[116],
    &_tblist[117],
    &_tblist[118],
    &_tblist[119],
    &_tblist[120],
    &_tblist[121],
    &_tblist[122],
    &_tblist[123],
    &_tblist[124],
    &_tblist[125],
    &_tblist[126],
    &_tblist[127],
    &_tblist[128],
    &_tblist[129],
    &_tblist[130],
    &_tblist[131],
    &_tblist[132],
    &_tblist[133],
    &_tblist[134],
    &_tblist[135],
    &_tblist[136],
    &_tblist[137],
    &_tblist[138],
    &_tblist[139],
    &_tblist[140],
    &_tblist[141],
    &_tblist[142],
    &_tblist[143],
    &_tblist[144],
    &_tblist[145],
    &_tblist[146],
    &_tblist[147],
    &_tblist[148],
    &_tblist[149],
    &_tblist[150],
    &_tblist[151],
    &_tblist[152],
    &_tblist[153],
    (Table *) 0
};
int	tb_numtables = 154;	/* Number of preinitialized tables */
/**/
LOCVAR Chan _chsrch[]	/* Data blocks for the pre-initialized channels */
	=
{
    { "local", "local", &_tblist[9], "local", 
	050, "local", "csnet-relay", "arpa", (char *)0, 
	(char *)0, 8, (char *)0, (char *)0, 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, (Table *)0, 
	(char *)0, 00, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "xalbany", "xalbany", &_tblist[10], "albany", 
	060, "phone", "csnet-relay", "csnet", "albany", 
	(char *)0, 8, "albany.trn", "albany.script", 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, (Table *)0, 
	(char *)0, 02, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "xalcoa-atc", "xalcoa-atc", &_tblist[11], "alcoa-atc", 
	060, "phone", "csnet-relay", "csnet", "alcoa-atc", 
	(char *)0, 8, "alcoa-atc.trn", "alcoa-atc.script", 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, &_tblist[8], 
	(char *)0, 05, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "xapple", "xapple", &_tblist[12], "apple", 
	060, "phone", "csnet-relay", "csnet", "apple", 
	(char *)0, 8, "apple.trn", "apple.script", 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, &_tblist[8], 
	(char *)0, 05, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "xaustralia", "xaustralia", &_tblist[13], "australia", 
	062, "pobox", "csnet-relay", "csnet", "australia", 
	"ozmail", 8, "australia.trn", (char *)0, 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, &_tblist[8], 
	(char *)0, 05, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "arizona", "Arizona/TCP", &_tblist[14], "arizona", 
	040, "smtp", "csnet-relay", "arpa", "arizona", 
	(char *)0, 8, (char *)0, (char *)0, 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, (Table *)0, 
	(char *)0, 02, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "azpob", "azpob", &_tblist[14], "arizona", 
	062, "pobox", "csnet-relay", "csnet", "arizona", 
	"azmail", 8, "arizona.trn", (char *)0, 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, (Table *)0, 
	(char *)0, 02, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "azspob", "azspob", &_tblist[15], "asu", 
	062, "pobox", "csnet-relay", "csnet", "asu", 
	"azsmail", 8, "asu.trn", (char *)0, 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, &_tblist[8], 
	(char *)0, 05, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "bbnv1pob", "bbnv1pob", &_tblist[16], "bbnv1", 
	062, "pobox", "csnet-relay", "csnet", "bbnv1", 
	"bbnv1ml", 8, "bbnv1.trn", (char *)0, 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, (Table *)0, 
	(char *)0, 01, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "xbgsu", "xbgsu", &_tblist[17], "bgsu", 
	062, "pobox", "csnet-relay", "csnet", "bgsu", 
	"bgsumail", 8, "bgsu.trn", (char *)0, 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, &_tblist[8], 
	(char *)0, 05, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "bostonu", "BostonU/TCP", &_tblist[18], "bostonu", 
	040, "smtp", "csnet-relay", "arpa", "bostonu", 
	(char *)0, 8, (char *)0, (char *)0, 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, (Table *)0, 
	(char *)0, 02, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "xbostonu", "xbostonu", &_tblist[18], "bostonu", 
	060, "phone", "csnet-relay", "csnet", "bostonu", 
	(char *)0, 8, "bostonu.trn", "bostonu.script", 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, (Table *)0, 
	(char *)0, 02, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "bouldpob", "bouldpob", &_tblist[19], "boulder", 
	062, "pobox", "csnet-relay", "csnet", "boulder", 
	"bouldrml", 8, "boulder.trn", (char *)0, 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, &_tblist[8], 
	(char *)0, 05, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "xboulder", "xboulder", &_tblist[19], "boulder", 
	060, "phone", "csnet-relay", "csnet", "boulder", 
	(char *)0, 8, "boulder.trn", "boulder.script", 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, &_tblist[8], 
	(char *)0, 05, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "xbrandeis", "xbrandeis", &_tblist[20], "brandeis", 
	060, "phone", "csnet-relay", "csnet", "brandeis", 
	(char *)0, 8, "brandeis.trn", "brandeis.script", 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, &_tblist[8], 
	(char *)0, 05, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "udbrown", "udbrown", &_tblist[21], "brown", 
	062, "pobox", "csnet-relay", "csnet", "brown", 
	"brownml", 8, "brown.trn", (char *)0, 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, &_tblist[8], 
	(char *)0, 05, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "xbsu", "xbsu", &_tblist[22], "bsu", 
	060, "phone", "csnet-relay", "csnet", "bsu", 
	(char *)0, 8, "bsu.trn", "bsu.script", 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, (Table *)0, 
	(char *)0, 01, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "btlpob", "btlpob", &_tblist[23], "btl", 
	062, "pobox", "csnet-relay", "csnet", "btl", 
	"btlmail", 8, "btl.trn", (char *)0, 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, &_tblist[8], 
	(char *)0, 05, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "xbuffalo", "xbuffalo", &_tblist[24], "buffalo", 
	062, "pobox", "csnet-relay", "csnet", "buffalo", 
	"buffmail", 8, "buffalo.trn", (char *)0, 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, &_tblist[8], 
	(char *)0, 05, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "xcanisius", "xcanisius", &_tblist[25], "canisius", 
	062, "pobox", "csnet-relay", "csnet", "canisius", 
	"canmail", 8, "canisius.trn", (char *)0, 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, (Table *)0, 
	(char *)0, 02, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "xcarleton", "xcarleton", &_tblist[26], "carleton", 
	060, "phone", "csnet-relay", "csnet", "carleton", 
	(char *)0, 8, "carleton.trn", "carleton.script", 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, &_tblist[8], 
	(char *)0, 05, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "xcase", "xcase", &_tblist[27], "case", 
	060, "phone", "csnet-relay", "csnet", "case", 
	(char *)0, 8, "case.trn", "case.script", 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, &_tblist[8], 
	(char *)0, 05, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "cgipob", "cgipob", &_tblist[28], "cgi", 
	062, "pobox", "csnet-relay", "csnet", "cgi", 
	"cgimail", 8, "cgi.trn", (char *)0, 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, &_tblist[8], 
	(char *)0, 05, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "xchalmers", "Sweden/PhoneNet", &_tblist[29], "chalmers", 
	062, "pobox", "csnet-relay", "csnet", "chalmers", 
	"sumail", 8, "chalmers.trn", (char *)0, 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, (Table *)0, 
	(char *)0, 02, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "xclemson", "xclemson", &_tblist[30], "clemson", 
	060, "phone", "csnet-relay", "csnet", "clemson", 
	(char *)0, 8, "clemson.trn", "clemson.script", 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, (Table *)0, 
	(char *)0, 01, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "xcolgate", "xcolgate", &_tblist[31], "colgate", 
	062, "pobox", "csnet-relay", "csnet", "colgate", 
	"colgmail", 8, "colgate.trn", (char *)0, 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, &_tblist[8], 
	(char *)0, 05, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "xcolostate", "xcolostate", &_tblist[32], "colostate", 
	060, "phone", "csnet-relay", "csnet", "colostate", 
	(char *)0, 8, "colostate.trn", "colostate.script", 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, &_tblist[8], 
	(char *)0, 05, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "xct", "xct", &_tblist[33], "ct", 
	060, "phone", "csnet-relay", "csnet", "ct", 
	(char *)0, 8, "ct.trn", "ct.script", 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, &_tblist[8], 
	(char *)0, 05, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "dartpob", "dartpob", &_tblist[34], "dartmouth", 
	062, "pobox", "csnet-relay", "csnet", "dartmouth", 
	"dartmail", 8, "dartmouth.trn", (char *)0, 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, (Table *)0, 
	(char *)0, 02, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "xdartmouth", "xdartmouth", &_tblist[34], "dartmouth", 
	060, "phone", "csnet-relay", "csnet", "dartmouth", 
	(char *)0, 8, "dartmouth.trn", "dartmouth.script", 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, (Table *)0, 
	(char *)0, 02, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "xdepaul", "xdepaul", &_tblist[35], "depaul", 
	060, "phone", "csnet-relay", "csnet", "depaul", 
	(char *)0, 8, "depaul.trn", "depaul.script", 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, &_tblist[8], 
	(char *)0, 05, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "xdigital", "xdigital", &_tblist[36], "digital", 
	060, "phone", "csnet-relay", "csnet", "digital", 
	(char *)0, 8, "digital.trn", "digital.script", 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, &_tblist[8], 
	(char *)0, 05, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "xduke", "xduke", &_tblist[37], "duke", 
	060, "phone", "csnet-relay", "csnet", "duke", 
	(char *)0, 8, "duke.trn", "duke.script", 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, &_tblist[8], 
	(char *)0, 05, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "xemory", "xemory", &_tblist[38], "emory", 
	062, "pobox", "csnet-relay", "csnet", "emory", 
	"emrymail", 8, "emory.trn", (char *)0, 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, &_tblist[8], 
	(char *)0, 05, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "xfrance", "xfrance", &_tblist[39], "france", 
	062, "pobox", "csnet-relay", "csnet", "france", 
	"franmail", 8, "france.trn", (char *)0, 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, &_tblist[8], 
	(char *)0, 05, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "xfresno-state", "xfresno-state", &_tblist[40], "fresno-state", 
	060, "phone", "csnet-relay", "csnet", "fresno-state", 
	(char *)0, 8, "fresno-state.trn", "fresno-state.script", 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, (Table *)0, 
	(char *)0, 02, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "xgatech", "xgatech", &_tblist[41], "gatech", 
	060, "phone", "csnet-relay", "csnet", "gatech", 
	(char *)0, 8, "gatech.trn", "gatech.script", 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, (Table *)0, 
	(char *)0, 02, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "xgermany", "xgermany", &_tblist[42], "germany", 
	062, "pobox", "csnet-relay", "csnet", "germany", 
	"krlsmail", 8, "germany.trn", (char *)0, 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, &_tblist[8], 
	(char *)0, 05, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "gmrpob", "gmrpob", &_tblist[43], "gmr", 
	062, "pobox", "csnet-relay", "csnet", "gmr", 
	"gmrmail", 8, "gmr.trn", (char *)0, 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, &_tblist[8], 
	(char *)0, 05, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "xgte-labs", "xgte-labs", &_tblist[44], "gte-labs", 
	060, "phone", "csnet-relay", "csnet", "gte-labs", 
	(char *)0, 8, "gte-labs.trn", "gte-labs.script", 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, &_tblist[8], 
	(char *)0, 05, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "xhouston", "xhouston", &_tblist[45], "houston", 
	060, "phone", "csnet-relay", "csnet", "houston", 
	(char *)0, 8, "houston.trn", "houston.script", 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, &_tblist[8], 
	(char *)0, 05, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "hplabs", "HPLABS/TCP", &_tblist[46], "hplabs", 
	040, "smtp", "csnet-relay", "arpa", "hplabs", 
	(char *)0, 8, (char *)0, (char *)0, 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, &_tblist[7], 
	(char *)0, 02, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "hplbpob", "hplbpob", &_tblist[47], "hplb", 
	062, "pobox", "csnet-relay", "csnet", "hplb", 
	"hplbmail", 8, "hplb.trn", (char *)0, 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, (Table *)0, 
	(char *)0, 02, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "xhugo-test", "xhugo-test", &_tblist[48], "hugo-test", 
	062, "pobox", "csnet-relay", "csnet", "hugo-test", 
	"hugtmail", 8, "hugo-test.trn", (char *)0, 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, &_tblist[8], 
	(char *)0, 05, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "ibm-sj", "IBM-SJ/TCP", &_tblist[49], "ibm-sj", 
	040, "smtp", "csnet-relay", "arpa", "ibm.com", 
	(char *)0, 8, (char *)0, (char *)0, 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, &_tblist[7], 
	(char *)0, 02, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "xicase", "xicase", &_tblist[50], "icase", 
	062, "pobox", "csnet-relay", "csnet", "icase", 
	"icasmail", 8, "icase.trn", (char *)0, 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, &_tblist[8], 
	(char *)0, 05, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "icase", "ICASE/SMTP", &_tblist[50], "icase", 
	040, "smtp", "csnet-relay", "arpa", "icase.arpa", 
	(char *)0, 8, (char *)0, (char *)0, 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, &_tblist[7], 
	(char *)0, 02, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "indypob", "indypob", &_tblist[51], "indiana", 
	062, "pobox", "csnet-relay", "csnet", "indiana", 
	"indymail", 8, "indiana.trn", (char *)0, 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, (Table *)0, 
	(char *)0, 02, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "xintel-isc", "xintel-isc", &_tblist[52], "intel-isc", 
	062, "pobox", "csnet-relay", "csnet", "isc.intel.com", 
	"iscintml", 8, "intel-isc.trn", (char *)0, 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, (Table *)0, 
	(char *)0, 02, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "xintel-sc", "xintel-sc", &_tblist[53], "intel-sc", 
	060, "phone", "csnet-relay", "csnet", "sc.intel.com", 
	(char *)0, 8, "intel-sc.trn", "intel-sc.script", 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, (Table *)0, 
	(char *)0, 01, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "intel-scpob", "intel-scpob", &_tblist[53], "intel-sc", 
	062, "pobox", "csnet-relay", "csnet", "sc.intel.com", 
	"scintml", 8, "intel-sc.trn", (char *)0, 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, (Table *)0, 
	(char *)0, 01, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "isrpob", "Israel/PhoneNet", &_tblist[54], "israel", 
	062, "pobox", "csnet-relay", "csnet", "israel", 
	"isrmail", 8, "israel.trn", (char *)0, 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, &_tblist[8], 
	(char *)0, 05, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "israel", "Israel/TCP", &_tblist[54], "israel", 
	040, "smtp", "CSNET-RELAY", "ARPA", "israel", 
	(char *)0, 8, (char *)0, (char *)0, 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, (Table *)0, 
	(char *)0, 02, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "xiowa-state", "xiowa-state", &_tblist[55], "iowa-state", 
	060, "phone", "csnet-relay", "csnet", "iowa-state", 
	(char *)0, 8, "iowa-state.trn", "iowa-state.script", 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, (Table *)0, 
	(char *)0, 02, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "iowastpob", "iowastpob", &_tblist[55], "iowa-state", 
	062, "pobox", "csnet-relay", "csnet", "iowa-state", 
	"iowastml", 8, "iowa-state.trn", (char *)0, 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, (Table *)0, 
	(char *)0, 02, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "xjsc", "xjsc", &_tblist[56], "nasa-jsc", 
	062, "pobox", "csnet-relay", "csnet", "nasa-jsc", 
	"jscmail", 8, "nasa-jsc.trn", (char *)0, 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, &_tblist[8], 
	(char *)0, 05, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "xkaist", "xkaist", &_tblist[57], "kaist", 
	062, "pobox", "csnet-relay", "csnet", "kaist", 
	"kormail", 8, "kaist.trn", (char *)0, 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, &_tblist[8], 
	(char *)0, 05, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "kanstpob", "kanstpob", &_tblist[58], "kansas-state", 
	062, "pobox", "csnet-relay", "csnet", "kansas-state", 
	"kansmail", 8, "kansas-state.trn", (char *)0, 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, &_tblist[8], 
	(char *)0, 05, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "xkentvax", "xkentvax", &_tblist[59], "kentvax", 
	060, "phone", "csnet-relay", "csnet", "kentvax", 
	(char *)0, 8, "kentvax.trn", "kentvax.script", 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, &_tblist[8], 
	(char *)0, 05, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "xlsu", "xlsu", &_tblist[60], "lsu", 
	060, "phone", "csnet-relay", "csnet", "lsu", 
	(char *)0, 8, "lsu.trn", "lsu.script", 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, &_tblist[8], 
	(char *)0, 05, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "xmcnc", "xmcnc", &_tblist[61], "mcnc", 
	060, "phone", "csnet-relay", "csnet", "mcnc", 
	(char *)0, 8, "mcnc.trn", "mcnc.script", 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, (Table *)0, 
	(char *)0, 02, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "mcncpob", "mcncpob", &_tblist[61], "mcnc", 
	062, "pobox", "csnet-relay", "csnet", "mcnc", 
	"mcncmail", 8, "mcnc.trn", (char *)0, 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, (Table *)0, 
	(char *)0, 02, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "xmich-state", "xmich-state", &_tblist[62], "mich-state", 
	062, "pobox", "csnet-relay", "csnet", "mich-state", 
	"msmail", 8, "mich-state.trn", (char *)0, 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, &_tblist[8], 
	(char *)0, 05, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "xmtu", "xmtu", &_tblist[63], "mtu", 
	062, "pobox", "csnet-relay", "csnet", "mtu", 
	"mtumail", 8, "mtu.trn", (char *)0, 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, (Table *)0, 
	(char *)0, 02, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "xncar", "xncar", &_tblist[64], "ncar", 
	060, "phone", "csnet-relay", "csnet", "ncar", 
	(char *)0, 8, "ncar.trn", "ncar.script", 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, &_tblist[8], 
	(char *)0, 05, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "ncrpob", "ncrpob", &_tblist[65], "ncr", 
	062, "pobox", "csnet-relay", "csnet", "ncr", 
	"ncrmail", 8, "ncr.trn", (char *)0, 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, (Table *)0, 
	(char *)0, 02, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "xnmsu", "xnmsu", &_tblist[66], "nmsu", 
	060, "phone", "csnet-relay", "csnet", "nmsu", 
	(char *)0, 8, "nmsu.trn", "nmsu.script", 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, &_tblist[8], 
	(char *)0, 05, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "nmtpob", "nmtpob", &_tblist[67], "nmt", 
	062, "pobox", "csnet-relay", "csnet", "nmt", 
	"nmtmail", 8, "nmt.trn", (char *)0, 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, &_tblist[8], 
	(char *)0, 05, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "xnortheastern", "xnortheastern", &_tblist[68], "northeastern", 
	060, "phone", "csnet-relay", "csnet", "northeastern", 
	(char *)0, 8, "northeastern.trn", "northeastern.script", 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, (Table *)0, 
	(char *)0, 01, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "xnsf", "xnsf", &_tblist[69], "nsf", 
	062, "pobox", "csnet-relay", "csnet", "nsf", 
	"newnsfml", 8, "nsf.trn", (char *)0, 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, (Table *)0, 
	(char *)0, 01, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "xnsf-cs", "xnsf-cs", &_tblist[70], "nsf-cs", 
	062, "pobox", "csnet-relay", "csnet", "nsf-cs", 
	"nsfmail", 8, "nsf-cs.trn", (char *)0, 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, &_tblist[8], 
	(char *)0, 05, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "xnwu", "xnwu", &_tblist[71], "nwu", 
	060, "phone", "csnet-relay", "csnet", "nwu", 
	(char *)0, 8, "nwu.trn", "nwu.script", 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, &_tblist[8], 
	(char *)0, 05, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "xodu", "xodu", &_tblist[72], "odu", 
	060, "phone", "csnet-relay", "csnet", "odu", 
	(char *)0, 8, "odu.trn", "odu.script", 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, (Table *)0, 
	(char *)0, 02, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "odupob", "odupob", &_tblist[72], "odu", 
	062, "pobox", "csnet-relay", "csnet", "odu", 
	"odumail", 8, "odu.trn", (char *)0, 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, (Table *)0, 
	(char *)0, 02, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "ohio-state", "Ohio-state/TCP", &_tblist[73], "ohio-state", 
	040, "smtp", "csnet-relay", "arpa", "ohio-state", 
	(char *)0, 8, (char *)0, (char *)0, 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, &_tblist[7], 
	(char *)0, 02, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "okstatepob", "okstatepob", &_tblist[74], "okstate", 
	062, "pobox", "csnet-relay", "csnet", "a.cs.okstate.edu", 
	"okstmail", 8, "okstate.trn", (char *)0, 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, (Table *)0, 
	(char *)0, 02, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "xokstate", "xokstate", &_tblist[74], "okstate", 
	060, "phone", "csnet-relay", "csnet", "a.cs.okstate.edu", 
	(char *)0, 8, "okstate.trn", "okstate.script", 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, (Table *)0, 
	(char *)0, 02, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "ogcpob", "ogcpob", &_tblist[75], "oregon-grad", 
	062, "pobox", "csnet-relay", "csnet", "oregon-grad", 
	"ogcmmdf", 8, "oregon-grad.trn", (char *)0, 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, &_tblist[8], 
	(char *)0, 05, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "omnitor", "Omnitor/TCP", &_tblist[76], "omnitor", 
	040, "smtp", "csnet-relay", "arpa", "omnitor", 
	(char *)0, 8, (char *)0, (char *)0, 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, (Table *)0, 
	(char *)0, 02, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "xoregon-state", "xoregon-state", &_tblist[77], "oregon-state", 
	060, "phone", "csnet-relay", "csnet", "oregon-state", 
	(char *)0, 8, "oregon-state.trn", "oregon-state.script", 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, &_tblist[8], 
	(char *)0, 05, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "xpenn-state", "xpenn-state", &_tblist[78], "penn-state", 
	060, "phone", "csnet-relay", "csnet", "penn-state", 
	(char *)0, 8, "penn-state.trn", "penn-state.script", 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, &_tblist[8], 
	(char *)0, 05, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "pittpob", "pittpob", &_tblist[79], "pitt", 
	062, "pobox", "csnet-relay", "csnet", "pitt", 
	"pittmail", 8, "pitt.trn", (char *)0, 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, (Table *)0, 
	(char *)0, 02, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "portpob", "portpob", &_tblist[80], "portland", 
	062, "pobox", "csnet-relay", "csnet", "portland", 
	"portmail", 8, "portland.trn", (char *)0, 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, (Table *)0, 
	(char *)0, 02, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "xportland", "xportland", &_tblist[80], "portland", 
	060, "phone", "csnet-relay", "csnet", "portland", 
	(char *)0, 8, "portland.trn", "portland.script", 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, &_tblist[8], 
	(char *)0, 05, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "xprinceton", "xprinceton", &_tblist[81], "princeton", 
	060, "phone", "csnet-relay", "csnet", "princeton", 
	(char *)0, 8, "princeton.trn", "princeton.script", 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, &_tblist[8], 
	(char *)0, 05, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "xrit", "xrit", &_tblist[82], "rit", 
	060, "phone", "csnet-relay", "csnet", "rit", 
	(char *)0, 8, "rit.trn", "rit.script", 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, (Table *)0, 
	(char *)0, 02, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "xrpics", "xrpics", &_tblist[83], "rpics", 
	062, "pobox", "csnet-relay", "csnet", "rpics", 
	"rpimail", 8, "rpics.trn", (char *)0, 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, &_tblist[8], 
	(char *)0, 05, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "xscarolina", "xscarolina", &_tblist[84], "scarolina", 
	060, "phone", "csnet-relay", "csnet", "scarolina", 
	(char *)0, 8, "scarolina.trn", "scarolina.script", 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, &_tblist[8], 
	(char *)0, 05, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "xsju", "xsju", &_tblist[85], "sju", 
	060, "phone", "csnet-relay", "csnet", "sju.edu", 
	(char *)0, 8, "sju.trn", "sju.script", 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, (Table *)0, 
	(char *)0, 02, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "xslb-doll", "xslb-doll", &_tblist[86], "slb-doll", 
	062, "pobox", "csnet-relay", "csnet", "slb-doll", 
	"slbdmail", 8, "slb-doll.trn", (char *)0, 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, (Table *)0, 
	(char *)0, 02, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "xslb-test", "xslb-test", &_tblist[87], "slb-test", 
	062, "pobox", "csnet-relay", "csnet", "slb-test", 
	"shlumail", 8, "slb-test.trn", (char *)0, 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, (Table *)0, 
	(char *)0, 01, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "xsmu", "xsmu", &_tblist[88], "smu", 
	060, "phone", "csnet-relay", "csnet", "smu", 
	(char *)0, 8, "smu.trn", "smu.script", 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, &_tblist[8], 
	(char *)0, 05, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "eaganpob", "eaganpob", &_tblist[89], "sperry-csd", 
	062, "pobox", "csnet-relay", "csnet", "sperry-csd", 
	"eaganml", 8, "sperry-csd.trn", (char *)0, 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, (Table *)0, 
	(char *)0, 01, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "xsperry-ctc", "xsperry-ctc", &_tblist[90], "sperry-ctc", 
	060, "phone", "csnet-relay", "csnet", "sperry-ctc", 
	(char *)0, 8, "sperry-ctc.trn", "sperry-ctc.script", 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, &_tblist[8], 
	(char *)0, 05, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "xsrc", "xsrc", &_tblist[91], "src", 
	060, "phone", "csnet-relay", "csnet", "src", 
	(char *)0, 8, "src.trn", "src.script", 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, (Table *)0, 
	(char *)0, 01, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "xsuny-bing", "xsuny-bing", &_tblist[92], "suny-bing", 
	060, "phone", "csnet-relay", "csnet", "suny-bing", 
	(char *)0, 8, "suny-bing.trn", "suny-bing.script", 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, &_tblist[8], 
	(char *)0, 05, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "sunypob", "sunypob", &_tblist[93], "suny-sbcs", 
	062, "pobox", "csnet-relay", "csnet", "suny-sbcs", 
	"sunymail", 8, "suny-sbcs.trn", (char *)0, 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, &_tblist[8], 
	(char *)0, 05, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "xswri", "xswri", &_tblist[94], "swri", 
	060, "phone", "csnet-relay", "csnet", "swri", 
	(char *)0, 8, "swri.trn", "swri.script", 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, &_tblist[8], 
	(char *)0, 05, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "syracuse", "syracuse", &_tblist[95], "syr", 
	062, "pobox", "csnet-relay", "csnet", "syr", 
	"syrmail", 8, "syr.trn", (char *)0, 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, (Table *)0, 
	(char *)0, 02, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "xtamu", "xtamu", &_tblist[96], "tamu", 
	060, "phone", "csnet-relay", "csnet", "tamu", 
	(char *)0, 8, "tamu.trn", "tamu.script", 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, &_tblist[8], 
	(char *)0, 05, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "xtekpob", "xtekpob", &_tblist[97], "tektronix", 
	022, "pobox", "csnet-relay", "csnet", "tektronix", 
	"tekmail", 8, "tektronix.trn", (char *)0, 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, (Table *)0, 
	(char *)0, 02, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "xtektronix", "xtektronix", &_tblist[97], "tektronix", 
	060, "phone", "csnet-relay", "csnet", "tektronix", 
	(char *)0, 8, "tektronix.trn", "tektronix.script", 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, (Table *)0, 
	(char *)0, 02, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "xtemple", "xtemple", &_tblist[98], "temple", 
	060, "phone", "csnet-relay", "csnet", "temple", 
	(char *)0, 8, "temple.trn", "temple.script", 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, (Table *)0, 
	(char *)0, 01, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "xtennessee", "xtennessee", &_tblist[99], "tennessee", 
	060, "phone", "csnet-relay", "csnet", "tennessee", 
	(char *)0, 8, "tennessee.trn", "tennessee.script", 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, (Table *)0, 
	(char *)0, 01, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "tennpob", "tennpob", &_tblist[99], "tennessee", 
	062, "pobox", "csnet-relay", "csnet", "tennessee", 
	"tennmail", 8, "tennessee.trn", (char *)0, 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, (Table *)0, 
	(char *)0, 01, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "tiegpob", "tiegpob", &_tblist[100], "ti-eg", 
	062, "pobox", "csnet-relay", "csnet", "ti-eg", 
	"tiegmail", 8, "ti-eg.trn", (char *)0, 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, &_tblist[8], 
	(char *)0, 05, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "tipob", "tipob", &_tblist[101], "ti-csl", 
	062, "pobox", "csnet-relay", "csnet", "ti-csl", 
	"timail", 8, "ti-csl.trn", (char *)0, 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, &_tblist[8], 
	(char *)0, 05, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "torpob", "torpob", &_tblist[102], "toronto", 
	062, "pobox", "csnet-relay", "csnet", "toronto", 
	"tormail", 8, "toronto.trn", (char *)0, 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, &_tblist[8], 
	(char *)0, 05, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "xtufts", "xtufts", &_tblist[103], "tufts", 
	060, "phone", "csnet-relay", "csnet", "tufts", 
	(char *)0, 8, "tufts.trn", "tufts.script", 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, &_tblist[8], 
	(char *)0, 05, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "xtulane", "xtulane", &_tblist[104], "tulane", 
	062, "pobox", "csnet-relay", "csnet", "tulane", 
	"tulmail", 8, "tulane.trn", (char *)0, 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, &_tblist[8], 
	(char *)0, 05, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "uabpob", "uabpob", &_tblist[105], "uab", 
	062, "pobox", "csnet-relay", "csnet", "uab", 
	"uabmail", 8, "uab.trn", (char *)0, 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, &_tblist[8], 
	(char *)0, 05, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "ubcpob", "ubcpob", &_tblist[106], "ubc", 
	062, "pobox", "csnet-relay", "csnet", "ubc", 
	"ubcmail", 8, "ubc.trn", (char *)0, 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, (Table *)0, 
	(char *)0, 02, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "xucd", "xucd", &_tblist[107], "ucd", 
	060, "phone", "csnet-relay", "csnet", "ucd", 
	(char *)0, 8, "ucd.trn", "ucd.script", 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, (Table *)0, 
	(char *)0, 02, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "xucf", "xucf", &_tblist[108], "ucf", 
	060, "phone", "csnet-relay", "csnet", "ucf", 
	(char *)0, 8, "ucf.trn", "ucf.script", 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, &_tblist[8], 
	(char *)0, 05, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "xuchicago", "xuchicago", &_tblist[109], "uchicago", 
	060, "phone", "csnet-relay", "csnet", "uchicago", 
	(char *)0, 8, "uchicago.trn", "uchicago.script", 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, (Table *)0, 
	(char *)0, 02, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "xuconn", "xuconn", &_tblist[110], "uconn", 
	060, "phone", "csnet-relay", "csnet", "uconn", 
	(char *)0, 8, "uconn.trn", "uconn.script", 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, &_tblist[8], 
	(char *)0, 05, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "xucsb", "xucsb", &_tblist[111], "ucsb", 
	060, "phone", "csnet-relay", "csnet", "ucsb", 
	(char *)0, 8, "ucsb.trn", "ucsb.script", 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, &_tblist[8], 
	(char *)0, 05, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "xucsc", "xucsc", &_tblist[112], "ucsc", 
	060, "phone", "csnet-relay", "csnet", "ucsc", 
	(char *)0, 8, "ucsc.trn", "ucsc.script", 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, &_tblist[8], 
	(char *)0, 05, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "xudenver", "xudenver", &_tblist[113], "udenver", 
	060, "phone", "csnet-relay", "csnet", "udenver", 
	(char *)0, 8, "udenver.trn", "udenver.script", 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, (Table *)0, 
	(char *)0, 02, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "xuhcl", "xuhcl", &_tblist[114], "uhcl", 
	060, "phone", "csnet-relay", "csnet", "uhcl", 
	(char *)0, 8, "uhcl.trn", "uhcl.script", 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, &_tblist[8], 
	(char *)0, 05, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "uiowapob", "uiowapob", &_tblist[115], "uiowa", 
	062, "pobox", "csnet-relay", "csnet", "uiowa", 
	"iowamail", 8, "uiowa.trn", (char *)0, 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, &_tblist[8], 
	(char *)0, 05, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "ukanpob", "ukanpob", &_tblist[116], "ukans", 
	062, "pobox", "csnet-relay", "csnet", "ukans", 
	"ukanmail", 8, "ukans.trn", (char *)0, 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, (Table *)0, 
	(char *)0, 02, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "xukans", "xukans", &_tblist[116], "ukans", 
	060, "phone", "csnet-relay", "csnet", "ukans", 
	(char *)0, 8, "ukans.trn", "ukans.script", 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, &_tblist[8], 
	(char *)0, 05, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "xuky", "xuky", &_tblist[117], "uky", 
	062, "pobox", "csnet-relay", "csnet", "uky", 
	"ukymail", 8, "uky.trn", (char *)0, 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, (Table *)0, 
	(char *)0, 02, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "xufl", "xufl", &_tblist[118], "ufl", 
	060, "phone", "csnet-relay", "csnet", "ufl", 
	(char *)0, 8, "ufl.trn", "ufl.script", 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, &_tblist[8], 
	(char *)0, 05, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "xulowell", "xulowell", &_tblist[119], "ulowell", 
	060, "phone", "csnet-relay", "csnet", "ulowell", 
	(char *)0, 8, "ulowell.trn", "ulowell.script", 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, (Table *)0, 
	(char *)0, 01, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "xumass-boston", "xumass-boston", &_tblist[120], "umass-boston", 
	062, "pobox", "csnet-relay", "csnet", "umass-boston", 
	"umbmail", 8, "umass-boston.trn", (char *)0, 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, &_tblist[8], 
	(char *)0, 05, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "xumass-cs", "xumass-cs", &_tblist[121], "umass-cs", 
	062, "pobox", "csnet-relay", "csnet", "umass-cs", 
	"massmail", 8, "umass-cs.trn", (char *)0, 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, (Table *)0, 
	(char *)0, 01, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "xumich", "xumich", &_tblist[122], "umich", 
	062, "pobox", "csnet-relay", "csnet", "umich", 
	"michmail", 8, "umich.trn", (char *)0, 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, (Table *)0, 
	(char *)0, 02, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "xumiss", "xumiss", &_tblist[123], "umiss", 
	060, "phone", "csnet-relay", "csnet", "umiss", 
	(char *)0, 8, "umiss.trn", "umiss.script", 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, &_tblist[8], 
	(char *)0, 05, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "xumn-cs", "xumn-cs", &_tblist[124], "umn-cs", 
	062, "pobox", "csnet-relay", "csnet", "umn-cs", 
	"minnmail", 8, "umn-cs.trn", (char *)0, 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, &_tblist[8], 
	(char *)0, 05, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "xumn-duluth", "xumn-duluth", &_tblist[125], "umn-duluth", 
	060, "phone", "csnet-relay", "csnet", "umn-duluth", 
	(char *)0, 8, "umn-duluth.trn", "umn-duluth.script", 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, (Table *)0, 
	(char *)0, 02, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "uncpob", "uncpob", &_tblist[126], "unc", 
	062, "pobox", "csnet-relay", "csnet", "unc", 
	"uncmail", 8, "unc.trn", (char *)0, 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, (Table *)0, 
	(char *)0, 02, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "xunc", "xunc", &_tblist[126], "unc", 
	060, "phone", "csnet-relay", "csnet", "unc", 
	(char *)0, 8, "unc.trn", "unc.script", 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, (Table *)0, 
	(char *)0, 02, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "udunhpob", "udunhpob", &_tblist[127], "unh", 
	062, "pobox", "csnet-relay", "csnet", "unh", 
	"unhcsnet", 8, "unh.trn", (char *)0, 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, &_tblist[8], 
	(char *)0, 05, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "xunl", "xunl", &_tblist[128], "unl", 
	060, "phone", "csnet-relay", "csnet", "unl", 
	(char *)0, 8, "unl.trn", "unl.script", 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, (Table *)0, 
	(char *)0, 02, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "xunlv", "xunlv", &_tblist[129], "unlv", 
	060, "phone", "csnet-relay", "csnet", "unlv", 
	(char *)0, 8, "unlv.trn", "unlv.script", 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, &_tblist[8], 
	(char *)0, 02, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "xuoregon", "xuoregon", &_tblist[130], "uoregon", 
	062, "pobox", "csnet-relay", "csnet", "uoregon", 
	"uoremail", 8, "uoregon.trn", (char *)0, 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, &_tblist[8], 
	(char *)0, 05, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "xupenn", "xupenn", &_tblist[131], "upenn", 
	060, "phone", "csnet-relay", "csnet", "upenn", 
	(char *)0, 8, "upenn.trn", "upenn.script", 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, &_tblist[8], 
	(char *)0, 05, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "upenn", "UPenn/TCP", &_tblist[131], "upenn", 
	040, "smtp", "csnet-relay", "arpa", "upenn", 
	(char *)0, 8, (char *)0, (char *)0, 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, (Table *)0, 
	(char *)0, 01, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "usc-cse", "USC-CSE/TCP", &_tblist[132], "usc-cse", 
	040, "smtp", "csnet-relay", "arpa", "usc-cse", 
	(char *)0, 8, (char *)0, (char *)0, 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, (Table *)0, 
	(char *)0, 02, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "xusc-cse", "xusc-cse", &_tblist[132], "usc-cse", 
	060, "phone", "csnet-relay", "csnet", "usc-cse", 
	(char *)0, 8, "usc-cse.trn", "usc-cse.script", 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, (Table *)0, 
	(char *)0, 02, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "xusf", "xusf", &_tblist[133], "usf", 
	062, "pobox", "csnet-relay", "csnet", "usf", 
	"usfmail", 8, "usf.trn", (char *)0, 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, (Table *)0, 
	(char *)0, 02, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "uslpob", "uslpob", &_tblist[134], "usl", 
	062, "pobox", "csnet-relay", "csnet", "usl", 
	"uslamail", 8, "usl.trn", (char *)0, 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, &_tblist[8], 
	(char *)0, 05, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "xuta", "xuta", &_tblist[135], "uta", 
	060, "phone", "csnet-relay", "csnet", "uta", 
	(char *)0, 8, "uta.trn", "uta.script", 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, (Table *)0, 
	(char *)0, 01, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "xutd-cs", "xutd-cs", &_tblist[136], "utd-cs", 
	060, "phone", "csnet-relay", "csnet", "utd-cs", 
	(char *)0, 8, "utd-cs.trn", "utd-cs.script", 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, &_tblist[8], 
	(char *)0, 05, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "xtokyo", "xtokyo", &_tblist[137], "utokyo-relay", 
	062, "pobox", "csnet-relay", "csnet", "utokyo-relay", 
	"tokmail", 8, "utokyo-relay.trn", (char *)0, 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, (Table *)0, 
	(char *)0, 02, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "xuvm", "xuvm", &_tblist[138], "uvm", 
	060, "phone", "csnet-relay", "csnet", "uvm", 
	(char *)0, 8, "uvm.trn", "uvm.script", 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, &_tblist[8], 
	(char *)0, 05, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "xuwmeecs", "xuwmeecs", &_tblist[139], "uwmeecs", 
	060, "phone", "csnet-relay", "csnet", "uwmeecs", 
	(char *)0, 8, "uwmeecs.trn", "uwmeecs.script", 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, (Table *)0, 
	(char *)0, 02, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "xvanderbilt", "xvanderbilt", &_tblist[140], "vanderbilt", 
	060, "phone", "csnet-relay", "csnet", "vanderbilt", 
	(char *)0, 8, "vanderbilt.trn", "vanderbilt.script", 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, &_tblist[8], 
	(char *)0, 05, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "vpipob", "vpipob", &_tblist[141], "vpi", 
	062, "pobox", "csnet-relay", "csnet", "vpi", 
	"vpimail", 8, "vpi.trn", (char *)0, 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, (Table *)0, 
	(char *)0, 01, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "xvpi", "xvpi", &_tblist[141], "vpi", 
	060, "phone", "csnet-relay", "csnet", "vpi", 
	(char *)0, 8, "vpi.trn", "vpi.script", 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, (Table *)0, 
	(char *)0, 01, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "uvapob", "uvapob", &_tblist[142], "virginia", 
	062, "pobox", "csnet-relay", "csnet", "virginia", 
	"uvamail", 8, "virginia.trn", (char *)0, 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, &_tblist[8], 
	(char *)0, 05, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "xwang-inst", "xwang-inst", &_tblist[143], "wang-inst", 
	060, "phone", "csnet-relay", "csnet", "wang-inst", 
	(char *)0, 8, "wang-inst.trn", "wang-inst.script", 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, &_tblist[8], 
	(char *)0, 05, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "xwaterloo", "xwaterloo", &_tblist[144], "waterloo", 
	062, "pobox", "csnet-relay", "csnet", "waterloo", 
	"watmail", 8, "waterloo.trn", (char *)0, 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, &_tblist[8], 
	(char *)0, 05, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "xwfu", "xwfu", &_tblist[145], "wfu", 
	060, "phone", "csnet-relay", "csnet", "wfu", 
	(char *)0, 8, "wfu.trn", "wfu.script", 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, &_tblist[8], 
	(char *)0, 05, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "xwilliams", "xwilliams", &_tblist[146], "williams", 
	060, "phone", "csnet-relay", "csnet", "williams", 
	(char *)0, 8, "williams.trn", "williams.script", 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, &_tblist[8], 
	(char *)0, 05, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "wiscvm", "WISCVM/TCP", &_tblist[147], "wiscvm", 
	040, "smtp", "csnet-relay", "arpa", "wiscvm", 
	(char *)0, 8, (char *)0, (char *)0, 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, &_tblist[7], 
	(char *)0, 02, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "xwright", "xwright", &_tblist[148], "wright", 
	062, "pobox", "csnet-relay", "csnet", "wright", 
	"wrimail", 8, "wright.trn", (char *)0, 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, &_tblist[8], 
	(char *)0, 05, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "xwsu", "xwsu", &_tblist[149], "wsu", 
	062, "pobox", "csnet-relay", "csnet", "wsu", 
	"wsumail", 8, "wsu.trn", (char *)0, 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, &_tblist[8], 
	(char *)0, 05, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "xwwu", "xwwu", &_tblist[150], "wwu", 
	060, "phone", "csnet-relay", "csnet", "wwu", 
	(char *)0, 8, "wwu.trn", "wwu.script", 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, &_tblist[8], 
	(char *)0, 05, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "list", "List Channel", &_tblist[2], "list", 
	050, "list", "CSNET-RELAY", "ARPA", (char *)0, 
	(char *)0, 8, (char *)0, (char *)0, 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, (Table *)0, 
	(char *)0, 00, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "xtest-in", "Inbound phone test", &_tblist[151], "test-in", 
	062, "pobox", "csnet", "csnet", "test-in", 
	"testmail", -1, "test-in.trn", (char *)0, 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, &_tblist[8], 
	(char *)0, 05, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "xtest-out", "Outbound phone test", &_tblist[152], "test-out", 
	060, "phone", "csnet-relay", "csnet", "test-out", 
	(char *)0, 8, "test-out.trn", "test-out.script", 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, (Table *)0, 
	(char *)0, 01, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
    { "xtest-relay", "To Test-Relay", &_tblist[153], "test-relay", 
	060, "phone", "csnet-relay", "csnet", "test-relay", 
	(char *)0, 8, "test-relay.trn", "test-relay.script", 
	(Table *)0, (Table *)0, (Table *)0, (Table *)0, (Table *)0, 
	(char *)0, 02, 00, (Cache *)0,
	016040, "/usr/mmdf/log/chan.log", 031 },
};
LOCVAR Chan * chsrch[NUMCHANS+1]	/* order chan tables searched */
	=
{
    &_chsrch[0],
    &_chsrch[1],
    &_chsrch[2],
    &_chsrch[3],
    &_chsrch[4],
    &_chsrch[5],
    &_chsrch[6],
    &_chsrch[7],
    &_chsrch[8],
    &_chsrch[9],
    &_chsrch[10],
    &_chsrch[11],
    &_chsrch[12],
    &_chsrch[13],
    &_chsrch[14],
    &_chsrch[15],
    &_chsrch[16],
    &_chsrch[17],
    &_chsrch[18],
    &_chsrch[19],
    &_chsrch[20],
    &_chsrch[21],
    &_chsrch[22],
    &_chsrch[23],
    &_chsrch[24],
    &_chsrch[25],
    &_chsrch[26],
    &_chsrch[27],
    &_chsrch[28],
    &_chsrch[29],
    &_chsrch[30],
    &_chsrch[31],
    &_chsrch[32],
    &_chsrch[33],
    &_chsrch[34],
    &_chsrch[35],
    &_chsrch[36],
    &_chsrch[37],
    &_chsrch[38],
    &_chsrch[39],
    &_chsrch[40],
    &_chsrch[41],
    &_chsrch[42],
    &_chsrch[43],
    &_chsrch[44],
    &_chsrch[45],
    &_chsrch[46],
    &_chsrch[47],
    &_chsrch[48],
    &_chsrch[49],
    &_chsrch[50],
    &_chsrch[51],
    &_chsrch[52],
    &_chsrch[53],
    &_chsrch[54],
    &_chsrch[55],
    &_chsrch[56],
    &_chsrch[57],
    &_chsrch[58],
    &_chsrch[59],
    &_chsrch[60],
    &_chsrch[61],
    &_chsrch[62],
    &_chsrch[63],
    &_chsrch[64],
    &_chsrch[65],
    &_chsrch[66],
    &_chsrch[67],
    &_chsrch[68],
    &_chsrch[69],
    &_chsrch[70],
    &_chsrch[71],
    &_chsrch[72],
    &_chsrch[73],
    &_chsrch[74],
    &_chsrch[75],
    &_chsrch[76],
    &_chsrch[77],
    &_chsrch[78],
    &_chsrch[79],
    &_chsrch[80],
    &_chsrch[81],
    &_chsrch[82],
    &_chsrch[83],
    &_chsrch[84],
    &_chsrch[85],
    &_chsrch[86],
    &_chsrch[87],
    &_chsrch[88],
    &_chsrch[89],
    &_chsrch[90],
    &_chsrch[91],
    &_chsrch[92],
    &_chsrch[93],
    &_chsrch[94],
    &_chsrch[95],
    &_chsrch[96],
    &_chsrch[97],
    &_chsrch[98],
    &_chsrch[99],
    &_chsrch[100],
    &_chsrch[101],
    &_chsrch[102],
    &_chsrch[103],
    &_chsrch[104],
    &_chsrch[105],
    &_chsrch[106],
    &_chsrch[107],
    &_chsrch[108],
    &_chsrch[109],
    &_chsrch[110],
    &_chsrch[111],
    &_chsrch[112],
    &_chsrch[113],
    &_chsrch[114],
    &_chsrch[115],
    &_chsrch[116],
    &_chsrch[117],
    &_chsrch[118],
    &_chsrch[119],
    &_chsrch[120],
    &_chsrch[121],
    &_chsrch[122],
    &_chsrch[123],
    &_chsrch[124],
    &_chsrch[125],
    &_chsrch[126],
    &_chsrch[127],
    &_chsrch[128],
    &_chsrch[129],
    &_chsrch[130],
    &_chsrch[131],
    &_chsrch[132],
    &_chsrch[133],
    &_chsrch[134],
    &_chsrch[135],
    &_chsrch[136],
    &_chsrch[137],
    &_chsrch[138],
    &_chsrch[139],
    &_chsrch[140],
    &_chsrch[141],
    &_chsrch[142],
    &_chsrch[143],
    &_chsrch[144],
    &_chsrch[145],
    &_chsrch[146],
    &_chsrch[147],
    &_chsrch[148],
    &_chsrch[149],
    &_chsrch[150],
    &_chsrch[151],
    &_chsrch[152],
    &_chsrch[153],
    &_chsrch[154],
    &_chsrch[155],
    &_chsrch[156],
    &_chsrch[157],
    &_chsrch[158],
    &_chsrch[159],
    &_chsrch[160],
    &_chsrch[161],
    &_chsrch[162],
    &_chsrch[163],
    &_chsrch[164],
    (Chan *) 0
};
int	ch_numchans = 165;	/* Number of preinitialized channels */

    { "xtest-in", "Inbound phone test", &_tblist[151], "test-in", 
	062, "pobox", "csnet", "csnet", "test-in", 
	"testmail", -1, "test-in.trn", (char *)0, 
	(Table *)0, (Table *)0, (Tammdf/conf/csnet-relay/conf.c   444      0     12       20360  3657737654  11264 #include "util.h"
#include "mmdf.h"
#include "ap_norm.h"

/*            COMPILE-TIME CONFIGURATION FILE WITH SITE-DEPENDENT INFORMATION
 *
 *      This file contains general host-dependent variable info that
 *   the message system needs to have.
 */

char *mmtailor = "/mmdf/mmdftailor";
				/* location of external tailoring file  */

/* ************************  PUBLIC NAMES  ****************************** */

char
	*locname = "CSNET-RELAY",   /* Generic name for local host          */
	*locdomain = "ARPA",	/* Name of domain that is local to us */
	*sitesignature = "Mail System (MMDF)",
				/* in signature field of return-mail  */
	*mmdflogin = "mmdf",	/* login name for mmdf         */
	*supportaddr = "Postmaster@CSNET-RELAY.ARPA";
				/* where to send bug reports, etc. */
/* *****************  DEFAULT BASE DIRECTORIES  *********************** */

/*  The following pathnames define the default working directories for
 *  MMDF activities.  Specific files usually will be referenced relative
 *  to these directories.  To override the default, the specific filename
 *  should be specified as an anchored pathname, beginning with a slash.
 *
 *  The following pathnames must be anchored.
 */

char	*cmddfldir = "/mmdf/lib";
			      /* contains MMDF commands, such as      */
			      /* submit, deliver, and queclean        */
char	*logdfldir = "/usr/mmdf/log";
			      /* contains highly volatile files, such */
			      /* as logs and check-point markers      */
char	*phsdfldir = "/mmdf/phase";
			      /* contains timestamp files             */
char	*tbldfldir = "/mmdf/table";
			      /* contains sticky files, such as       */
			      /* name tables & dialing scripts        */
char	*tbldbm = "/mmdf/table/mmdfdbm";
			      /* dbm() hash of name tables      */
char	*quedfldir = "/mmdf/lock/home";
			      /* contains queued mail files, in       */
			      /* subordinate directories              */
			      /* (also see below)                     */
char	*chndfldir = "/mmdf/chans";
				/* contains the channel programs        */
				/* (ch_*) called by deliver             */
char	*lckdfldir = "/tmp/mmdf";
				/* Directory for lock files SEK           */


/* ***************  DEFAULT LOG LOCATIONS & SETTINGS  ***************** */

char    mlogloc[] = "msg.log";
char    chlogloc[] = "chan.log";

/*  It is common for specific using processes to tailor the second
 *  field, which is the log entry label, so that you can tell which
 *  incarnation of the process made a set of entries.
 *
 *  These structures conform to ll_log.h
 */

struct ll_struct
		    msglog =      /* deliver, submit, & queclean          */
{
    mlogloc, "XX-0000", LLOGFST, 100, LLOGSOME, 0
},

		   chanlog =      /* log for channels & user agents       */
{
    chlogloc, "UA-0000", LLOGFST, 100, LLOGSOME, 0
};

/* Needed for user authorisation logging        */

struct ll_struct authlog =
{
    "auth.log", "AU-0000", LLOGFTR, 40, LLOGSOME, 0
};

/* ****************  MESSAGE QUEUE SUBSTRUCTURE  ********************** */

/*  The queue has a top-level, locking directory, which only trusted
 *  processes can get through.  Under that is a base working directory.
 *  Below that are three directories which contain queued messages data
 *
 *  quedfldir[] specifies the top-level and base directories.
 */

int     queprot = 0700;           /* protection on quedfldir parent      */

/* the next five are relative to quedfldir[] */

char
	*deadletter = "DeadLetters",
				/* Actually NOT a directory, where	*/
				/* unreturned letters go to die.	*/
	*tquedir = "tmp/",	/* sub-directory for builing            */
				/* address list file for msg            */
	*aquedir = "addr/",	/* sub-dir with files of address        */
				/* lists for queued mail; files are     */
				/* linked into here from tquedir        */
				/* as last act of queuing msg           */
	*squepref = "q.",	/* preface sub-queue name with this	*/
	*mquedir = "msg/";	/* sub-dir containing text of           */
				/* queued messages; file names          */
				/* are same as in aquedir               */

/*  The following two parameters establish thresholds for queue residency
 *  limits.  If a message does not move out of the queue by warntime,
 *  a notice may be sent to the originator.  If it is still in the queue
 *  by failtime, it is removed from the queue and may be returned to the
 *  originator.
 */

int
	warntime = 95,
	failtime = 143;


/* ******************  COMMAND NAMES & LOCATIONS  *********************** */

char
	*namsubmit = "submit",      /* who to submit mail through         */
	*pathsubmit = "submit",     /* location relative to cmddfldir[]   */

	*namdeliver = "deliver",    /* name of delivery overseer          */
	*pathdeliver = "deliver",   /* location relative to cmddfldir[]   */

	*nampkup = "pickup",        /* name of pickup overseer            */
	*pathpkup = "deliver",      /* location relative to cmddfldir[]   */

	*nammail = "mmdfmail",      /* the mmdf simple mail-sender        */
	*pathmail = "v6mail";       /* needs to handle special switches   */
				    /* probably must be MMDF's uip/mail   */

/* *********************  SUBMIT TAILORING  ************************** */

int     maxhops = 20;               /* number of Via fields permitted   */
int	mgt_addid = 0;		    /* if set, add message-id if necessary */
int	lnk_listsize = 12;	    /* if more than this many addresses,
				     * then do not send warning and only
				     * send citation on return
				     */


/*   *********************  DELIVER TAILORING  ************************** */

int	maxqueue = 300;		  /* maximum size of sortable queue       */
int	mailsleep = 600;          /* seconds between queue sweeps by a    */
				  /* background (-b) Deliver              */

/*   *********************  ADDRESS TRANSFORMATION  ********************* */


/*  Format:
 *
 *  Original host name, New hostname, String appended to mailbox
 *
 *      e.g.        "DCrocker at UDel-EE"
 *      maps to     "DCrocker.EE at UDel"
 *
 *  Note the usage, for Darcom.  It is designed to bypass address
 *  mapping, so that "foo at bar" does not go out as "foo.bar at relay"
 */

LOCVAR struct ap_hstab hstab[] =
{
    0, 0, 0,
};
struct ap_hstab *ap_exhstab = hstab;

/* *****************  LOCAL DELIVERY TAILORING  ************************* */

/*  Obviously, the following is only needed if there is local delivery,
 *  rather than pure relaying (a rare, but permitted mode).  Simplest
 *  determinor is ch_okloc in conf_chan.c
 */

int     sentprotect = 0600;         /* protection on mail files             */

char
	*dlvfile = ".maildelivery", /* User specified delivery instructions */
	*sysdlvfile = (char *)0,  /* if non-zero, the default dlvfile */
	*mldfldir = { 0 },	/* directory to contain users' file     */
				/* for receiving local mail.  if this   */
				/* spec is empty, recipient's login     */
				/* directory will be used.              */
	*mldflfil = "mailbox",    /* file to receive new mail             */
				  /* relative to mldfldir[] or to login   */
				  /*   dir if mldfldir[] is empty         */
				  /* if this spec empty, user's login     */
				  /* name will be name of file            */
	*delim1 = "\001\001\001\001\n",
	*delim2 = "\001\001\001\001\n";
				  /* add to begin and end of messages     */
				  /* NOTE:  to avoid author spoofin,      */
				  /* lo_wtmail() catches mail that        */
				  /* has either of these strings in it    */
				  /* and changes the first char           */
				  /* (it increments its ascii value)      */


/* *****************  UUCP CHANNEL TAILORING  *********************** */

char	*Uchan = "uucp";		/* default channel name 	 */
char    *Uuxstr = "uux - -r";		/* command string to start UUX */

/******************* NIFTP CHANNEL TAILORING  *************************/

char	*pn_quedir   = "/usr/spool/jntmail";
				/* Location of JNTmail queues for NIFTP */

/******************* MULTIPLE HOST TAILORING **************************
 *
 *  This variable is initialised to be the name of the local machine
 *  It is used to transparently forward between the UCL machines
 */
char	*locmachine = { 0 };


/******************* AUTHORIZATION TAILORING **************************/
char	*authrequest = "CIC@CSNET-SH.ARPA";
				/* authorisation request address        */
char	*authfile = "/mmdf/lib/warning";
				/* warning letter - full pathname       */

removed from the queue and may be returned to the
 *  originator.
 */

int
	warntime = 95,
	failtime = 143;


/* ******************  COMMAND NAMES & LOCATIONS  *********************** */

char
	*namsubmit = "submit",      /* who to submit mail through         */
	*pathsubmmdf/conf/csnet-relay/conf.h   444      0     12        2740  3635350741  11233 #
/*          This contains the most site-dependent definitions             */

#define USERSIZE    8		/* max length of a user system name   */
#define	MAILIDSIZ  64		/* max length of a mailid,
				 * must be at least USERSIZE.		*/
#define MSGNSIZE   14           /* max length of a message file name  */
#define FILNSIZE   64           /* max length of full file name       */
#define ADDRSIZE  256		/* max length of an address           */
#define LINESIZE  256		/* max length of a line/record        */

#define	NUMCHANS   180		/* max number of channels */
#define NUMTABLES  190		/* max number of tables */
#define NUMDOMAINS 20		/* max number of domains */

/************  DIAL IN/OUT (CH_PHONE / SLAVE) TAILORING  *****************/

#define DL_TRIES    2             /* number of re-connects OK for phone */
#define DL_RCVSZ    255           /* longest line receivable            */
#define DL_XMTSZ    255           /* longest line sendable              */

/****************  JNTMAIL Tailoring *******************/

#undef	JNTMAIL
#undef	BOTHEND
#undef	VIATRACE

/**************** Include File Tailoring ****************************/

#include <sys/time.h>
#include <sys/dir.h>

/********************* Privileged UIDs *************************/

/*
 *  This can be a macro as give here or you can provide a function.
 *  It should always authorize root and mmdf.
 *  The global variable effecid is assumed to have the MMDF uid in it.
 */

#define	PRIV_USER(x) ((x)==0 || (x)==1 || (x)==effecid)
f a line/record        */

#defimmdf/conf/csnet-relay/Makefile.com   444      0     12        7262  3657737656  12400 #
#	Common Sub-Makefile for the MMDF System
#
HOST		= csnet-relay
SYSTEM		= 4.2
MMPREF		=
LIBDIR		= /mmdf/lib
CHANDIR		= /mmdf/chans
TBLDIR		= /mmdf/table
BINDIR		= /usr/local
RCVDIR		= /mmdf/rcvmail

#
#  Defines used during installation
#
CHOWN		= /etc/chown
MMDFLOGIN	= mmdf
ROOTLOGIN	= root
PGMPROT 	= 775

#
#  The paths below are relative from the directories below this one.
#  Define DOASSIGN to CONFIGDEFS if you want the d_assign code
#  Define SECURETTY if you run with terminals write protected (mode 711)
#  Define DEBUG to include extensive calls to the logging package
#  Define D_LOG if you want phone channel logging
#  Define D_DBGLOG if you want phone channel debugging output
#  Define RUNALONE for standalone testing (seldom used)
#  Define V4_2BSD if you are compiling for 4.2BSD
#  Define NODIAL if you do not want the dial package (no phone channel)
#  Define SYS5 if you have a SystemV Unix or something similar (Sys3)
#  Define ALTOS if you have an Altos, does some depraved things.
#  Define NODUP2 if you don't a dup2() syscall or subroutine.
#  Define NOFCNTL and NODUP2 if you don't have fcntl(x, F_DUPFD) or dup2()
#  Define -Drindex=strrchr -Dindex=strchr if you are on a SYS5 system
#  Define NAMESERVER if you want the nameserver lookup code
#  Define UUCPLOCK if you want UUCP/tip/cu-style locking for dial-out lines
#  Define NODOMLIT if you don't want literals like [10.0.0.52] in messages
#
CONFIGDEFS	= -DDEBUG=1 -DV4_2BSD -DNAMESERVER -DD_LOG -DD_DBGLOG -DNODOMLIT
CFLAGS		= -g -I../../h $(CONFIGDEFS)
LDFLAGS 	= 
MMDFLIBS 	= ../../lib/libmmdf.a
SYSLIBS		= -ldbm
LINT		= lint
LFLAGS		= -phbvxacL -I../../h $(CONFIGDEFS)
LLIBS		= ../../lib/llib-lmmdf.ln
AR		= ar


#  Specify either ch_tbdbm (for DBM based tables) or ch_tbseq for
#  sequential IO based tables.
CH_TB	= ch_tbdbm

#  Specify one of the nameserver modules or the fake module
#  if you do not intend to support nameservers (4.2, fake)
TB_NS	= 4.2

#  Specify tai_???.o and lk_lock???.o
LOCALUTIL = tai_file.o lk_lock.4.2.o


default:	real-default

#
#  special case dependencies
#
../../h/mmdf.h:	../../h/conf.h
	-touch ../../h/mmdf.h
.PRECIOUS: ../../h/mmdf.h

#
#  #include dependencies
#
#  Two versions are supplied.  One for sites with cc -M (4.3BSD)
#  and one for those that don't have it.  Comment out the one
#  you do not want.

#  This one is for sites without cc -M
depend:
	cat </dev/null >x.c
	for i in $(MODULES); do \
		(echo $$i.o: $$i.c >>makedep; \
		/bin/grep '^#[ 	]*include' x.c $$i.c | sed \
			-e 's,c:[^"]*"\./\([^"]*\)".*,o: \1,' \
			-e 's,c:[^"]*"/\([^"]*\)".*,o: /\1,' \
			-e 's,c:[^"]*"\([^"]*\)".*,o: ../../h/\1,' \
			-e 's,c:[^<]*<\(.*\)>.*,o: /usr/include/\1,' \
			>>makedep); done
	echo '/^# DO NOT DELETE THIS LINE/+2,$$d' >eddep
	echo '$$r makedep' >>eddep
	echo 'w' >>eddep
	cp Makefile.real Makefile.bak
	ed - Makefile.real < eddep
	rm eddep makedep x.c
	echo '# DEPENDENCIES MUST END AT END OF FILE' >> Makefile.real
	echo '# IF YOU PUT STUFF HERE IT WILL GO AWAY' >> Makefile.real
	echo '# see make depend above' >> Makefile.real

#  This one id for sites with cc -M
#depend:
#	( for i in ${MODULES} ; do \
#		${CC} -M ${CFLAGS} $$i.c ; done ) | \
#	awk ' { if ($$1 != prev) { print rec; rec = $$0; prev = $$1; } \
#		else { if (length(rec $$2) > 78) { print rec; rec = $$0; } \
#		       else rec = rec " " $$2 } } \
#	      END { print rec } ' > makedep
#	echo '/^# DO NOT DELETE THIS LINE/+2,$$d' >eddep
#	echo '$$r makedep' >>eddep
#	echo 'w' >>eddep
#	cp Makefile.real Makefile.bak
#	ed - Makefile.real < eddep
#	rm eddep makedep
#	echo '# DEPENDENCIES MUST END AT END OF FILE' >> Makefile.real
#	echo '# IF YOU PUT STUFF HERE IT WILL GO AWAY' >> Makefile.real
#	echo '# see make depend above' >> Makefile.real
utput
#  Define RUNALONE for standalone testing (seldom used)
#  Define V4_2BSD if you are compiling for 4.2BSD
#  Define NODIAL if you do not want the dial package (no phone channel)
#  Define SYS5 if you have a SystemV Unix or something similar (Sys3)
#  Define ALTOS if you have an Altos, does some depraved things.
#  Define NODUPmmdf/conf/csnet-relay/s_tailor.c   444      0     12         334  3657737657  12115 /*
 *	S _ T A I L O R . C
 *
 *  Contains strings used to tailor SEND to a particular
 *  site.  Also check s.h for tailorable defines.
 */

char	*dfleditor = "pen";
char	*dflveditor = "pen";
char	*dflchecker = "spell";
 78) { print rec; rec = $$0; } \
#		       else rec = rec " " $$2 } } \
#	      END { print rec } ' > makedep
#	echo '/^# DO NOT DELETE THIS LINE/+2,$$d' >eddep
#	echo '$$r makedep' >>eddep
#	echo 'w' >>eddep
#	cp Makefile.real Makefile.bak
#	ed - Makefile.real < eddep
#	rm eddep makedep
#	emmdf/conf/csnet-relay/msgtailor.c   444      0     12        3135  3671073317  12302 /*
 *	M S G T A I L O R . C
 */

#define NOEXTERNS
#include "./msg.h"

#ifdef pdp11
#	define NMSGS	500
#else
#	define NMSGS	2000
#endif pdp11

char    *savmsgfn = "savebox";
char	*resendprog = "|/usr/local/mmdf/resend ";
char	*sndname = "/usr/local/mmdf/send"; /* Overridden by .msgrc sendprog */

char	*dflshell = "sh";	/* Overridden by getenv("SHELL"); */
char	*dfleditor = "pen";    /* Overridden by getenv("EDITOR"); */

int	pagesize = 22;		/* default line count */
int	linelength = 80;	/* default line size */

int	paging = OFF;		/* Internal paging of long messages */
int	verbose = OFF;		/* Command completion (raw mode) */
int	wmsgflag = OFF;		/* Save messages when calling send */
int	keystrip = ON;		/* Strip header lines on display */
int	bdots = OFF;		/* Binary box dots */
int	mdots = OFF;		/* Mailbox dots */
int	bprint = OFF;		/* Reading... messages */
int	quicknflag = OFF;	/* Turn off NEW before message prints */
int	quickexit = ON;		/* Don't stay in msg if mailbox is empty */
int	prettylist = ON;	/* Print message numbers in listings */
int	filoutflag = ON;	/* Filter text (display controls as ^X) */
int	binsavflag = ON;	/* Save binary box on exec's & pauses */
int	doctrlel = OFF;		/* Page on control-L */
#ifndef SYS5
long	timezone = 5*60*60;	/* Seconds West of GMT */
int	daylight = 1;		/* Do you use daylight savings time? */
				/* 0 = NO, 1 = YES */
char	*tzname[] = {"EST", "EDT",};
				/* Timezone strings, primary and daylight
				   savings time if used, in that order */
#endif SYS5

/***** Do not tailor below this line *****/
int	Nmsgs = NMSGS;
struct message *msgp[NMSGS];
struct message *mptr;
NMSGS	500
#else
#	define NMSGS	2000
#endif pdp11

char    *savmsgfn = "savebox";
char	*resendprog = "|/usr/local/mmdf/resend ";
char	*sndname = "/usr/local/mmdf/send"; /* Overridden by .msgrc sendprog */

char	*dflshell = "sh";	/* Overridden by getenv("SHELL"); */
char	*dfleditor = "pen";    /* Overridden by getenv("EDITOR"); */

int	pagesize = 22;		/* default line count */
int	linelength = 80;	/* default line size mmdf/conf/csnet-relay/Makefile.lib   444      0     12        2220  3652767244  12344 #
#	Makefile for the MMDF library   (libmmdf.a)
#

#  The paths below are relative from the directories below this one.
#
#  The RANLIB define should be "sh libfix" for systems that don't have
#	the ranlib command.  Systems that do have ranlib should set
#	RANLIB to "ranlib".  "libfix" is a Bourne shell file that will
#	use lorder and tsort to reorder the library.
#
MAKE	= make
RANLIB	= ranlib

SUBDIRS = addr dial mmdf table util
ALLSUBDIRS = addr dial mmdf table util

default: remake reorder

remake:
	for dir in $(SUBDIRS); \
	  do (cd $${dir}; echo "Running make in $${dir}"; \
	      gen ${MFLAGS} -${MAKEFLAGS}); \
	  done

install: default
	@echo "Library is up to date, nothing else to do."

reorder: reorder-done

reorder-done: libmmdf.a
	-$(RANLIB) libmmdf.a
	-touch reorder-done

depend:
	for dir in $(SUBDIRS); \
	  do (cd $${dir}; gen ${MFLAGS} -${MAKEFLAGS} depend); done

lint:
	for dir in $(SUBDIRS); \
	  do (cd $${dir}; gen ${MFLAGS} -${MAKEFLAGS} lint); done
	cat */llib-l*.ln > llib-lmmdf.ln

clean:
	-rm -f *-made libmmdf.a make.out core reorder-done
	for dir in $(SUBDIRS); \
	  do (cd $${dir}; $(MAKE) -f Makefile.real clean); done

ALWAYS:
s & pauses */
int	doctrlel = OFF;		/* Page on control-L */
#ifndef SYS5
long	timezone = 5*60*60;	/* Seconds West of GMT */
int	daylight = 1;		/* Do you use daylight savings time? */
				/* 0 = NO, 1 = YES */
char	*tzname[] = {"EST", "EDT",};
				/* Timezone strings, primary and daylight
				   savings time if used, in that order */
#endif SYS5

/***** Do not tailor mmdf/conf/csnet-relay/Makefile.src   444      0     12        2300  3657737662  12372 #	MMDF Program Source Subtree Makefile
#

#
# Programs that live in subdirectories, and have makefiles of their own.
#
SUBDIR=	tools submit deliver local list delay smtp phone pobox 

#
#  This should list all the subdirectories that make clean works in.
#  This probably should not be altered unless you add/delete a channel.
#
ALLSUBDIRS=	tools submit deliver local list delay smtp phone pobox \
		uucp niftp bboards pop ean prog

all:
	for dir in ${SUBDIR}; do \
		(cd $${dir}; gen ${MFLAGS} -${MAKEFLAGS}); done

install:
	for dir in ${SUBDIR}; do \
		(cd $${dir}; gen ${MFLAGS} -${MAKEFLAGS} install); done

dist:
	-touch `ls */x*|grep -v '\.'`
	echo echo MMDF Distribution made `date` > install.sh
	for dir in ${SUBDIR}; do \
		echo cd $${dir} >> install.sh; \
		( cd $${dir}; \
		  gen -n ${MFLAGS} -${MAKEFLAGS} install) >> install.sh; \
		echo cd .. >> install.sh; \
	done
	tar cvf mmdfdist.tar install.sh */x*

depend:
	for dir in ${SUBDIR}; do \
		(cd $${dir}; gen ${MFLAGS} -${MAKEFLAGS} depend); done

lint:
	for dir in ${SUBDIR}; do \
		(cd $${dir}; gen ${MFLAGS} -${MAKEFLAGS} lint); done

clean:
	-rm -f make.out core
	for dir in ${ALLSUBDIRS}; do \
		(cd $${dir}; make -f Makefile.real clean); done
s should list all the subdirectories that make clean works in.
#  This probably should not be altered unless you add/delete a channel.
#
ALLSUBDIRS=	tools submit deliver local list delay smtp phone pobox \
		uucp niftp bboards pop ean prog

all:
	for dir in ${SUBDIR}; do \
		(cd $${dir}; gen ${MFLAGS} -${MAKEFLAGS}); dmmdf/conf/csnet-relay/s_tailor.h   444      0     12         471  3620510325  12067 /*
 *	Site Tunable Parameters
 */
#undef	SENTFILE yes	/* Define to have copies of mail sent put in ,sent */
#undef	FNCYPRNT yes	/* Define for fancy listing of message (paginated) */
#define	PWNAME yes	/* Define to get real name from /etc/passwd and to */
			/* treat 0 length signatures to get login name only */
te a channel.
#
ALLSUBDIRS=	tools submit deliver local list delay smtp phone pobox \
		uucp niftp bboards pop ean prog

all:
	for dir in ${SUBDIR}; do \
		(cd $${dir}; gen ${MFLAGS} -${MAKEFLAGS}); dmmdf/conf/uclvax2/   755      0     12           0  3671074637   7214 mmdf/conf/uclvax2/Makefile.com   444      0     12        7556  3657737663  11542 #
#       Common Sub-Makefile for the MMDF System
#
HOST            = uclvax2
SYSTEM          = 4.2
MMPREF          =
LIBDIR          = /usr/mmdf/lib
CHANDIR         = /usr/mmdf/lib/chans
TBLDIR          = /usr/mmdf/mmdftable
BINDIR          = /bin
RCVDIR          = /usr/mmdf/lib/rcvmail

#
#  Defines used during installation
#
CHOWN           = /bin/chug
MMDFLOGIN       = mmdf
ROOTLOGIN       = root
PGMPROT         = 755

#
#  Configuration Defines
#
#  The paths below are relative from the directories below this one.
#  Define DOASSIGN to CONFIGDEFS if you want the d_assign code
#  Define SECURETTY if you run with terminals write protected (mode 711)
#  Define DEBUG to include extensive calls to the logging package
#  Define D_LOG if you want phone channel logging
#  Define D_DBGLOG if you want phone channel debugging output
#  Define RUNALONE for standalone testing (seldom used)
#  Define V4_2BSD if you are compiling for 4.2BSD
#  Define NODIAL if you do not want the dial package (no phone channel)
#  Define SYS5 if you have a SystemV Unix or something similar (Sys3)
#  Define ALTOS if you have an Altos, does some depraved things.
#  Define NODUP2 if you don't a dup2() syscall or subroutine.
#  Define NOFCNTL and NODUP2 if you don't have fcntl(x, F_DUPFD) or dup2()
#  Define -Drindex=strrchr -Dindex=strchr if you are on a SYS5 system
#  Define NAMESERVER if you want the nameserver lookup code
#  Define UUCPLOCK if you want UUCP/tip/cu-style locking for dial-out lines
#  Define NODOMLIT if you don't want literals like [10.0.0.52] in messages
#
CONFIGDEFS	= -DDEBUG=1 -DV4_2BSD -DNODOMLIT
CFLAGS          = -O -I../../h $(CONFIGDEFS)
LDFLAGS         = -s
MMDFLIBS        = ../../lib/libmmdf.a
SYSLIBS         = -ldbm
LINT            = lint
LFLAGS          = -phbvxacL -I../../h $(CONFIGDEFS)
LLIBS		= ../../lib/llib-lmmdf.ln
AR              = ar


#  Specify either ch_tbdbm (for DBM based tables) or ch_tbseq for
#  sequential IO based tables.
CH_TB   = ch_tbdbm

#  Specify one of the nameserver modules or the fake module
#  if you do not intend to support nameservers (4.2, fake)
TB_NS	= 4.2

#  Specify tai_???.o and lk_lock???.o
LOCALUTIL = tai_file.o lk_lock.o dial_noop.o


default:        real-default

#
#  special case dependencies
#
../../h/mmdf.h: ../../h/conf.h
	-touch ../../h/mmdf.h
.PRECIOUS: ../../h/mmdf.h

#
#  #include dependencies
#
#  Two versions are supplied.  One for sites with cc -M (4.3BSD)
#  and one for those that don't have it.  Comment out the one
#  you do not want.

#  This one is for sites without cc -M
depend:
	cat </dev/null >x.c
	for i in $(MODULES); do \
		(echo $$i.o: $$i.c >>makedep; \
		/bin/grep '^#[ 	]*include' x.c $$i.c | sed \
			-e 's,c:[^"]*"\./\([^"]*\)".*,o: \1,' \
			-e 's,c:[^"]*"/\([^"]*\)".*,o: /\1,' \
			-e 's,c:[^"]*"\([^"]*\)".*,o: ../../h/\1,' \
			-e 's,c:[^<]*<\(.*\)>.*,o: /usr/include/\1,' \
			>>makedep); done
	echo '/^# DO NOT DELETE THIS LINE/+2,$$d' >eddep
	echo '$$r makedep' >>eddep
	echo 'w' >>eddep
	cp Makefile.real Makefile.bak
	ed - Makefile.real < eddep
	rm eddep makedep x.c
	echo '# DEPENDENCIES MUST END AT END OF FILE' >> Makefile.real
	echo '# IF YOU PUT STUFF HERE IT WILL GO AWAY' >> Makefile.real
	echo '# see make depend above' >> Makefile.real

#  This one id for sites with cc -M
#depend:
#	( for i in ${MODULES} ; do \
#		${CC} -M ${CFLAGS} $$i.c ; done ) | \
#	awk ' { if ($$1 != prev) { print rec; rec = $$0; prev = $$1; } \
#		else { if (length(rec $$2) > 78) { print rec; rec = $$0; } \
#		       else rec = rec " " $$2 } } \
#	      END { print rec } ' > makedep
#	echo '/^# DO NOT DELETE THIS LINE/+2,$$d' >eddep
#	echo '$$r makedep' >>eddep
#	echo 'w' >>eddep
#	cp Makefile.real Makefile.bak
#	ed - Makefile.real < eddep
#	rm eddep makedep
#	echo '# DEPENDENCIES MUST END AT END OF FILE' >> Makefile.real
#	echo '# IF YOU PUT STUFF HERE IT WILL GO AWAY' >> Makefile.real
#	echo '# see make depend above' >> Makefile.real
 i in $(MODULES); do \
		(echo $$i.o: $$i.c >>makedep; \
		/bin/grep '^#[ 	]*include' x.c $$i.c | sed \
			-e 's,c:[^"]*"\./\([^"]*\)".*,o: \1,' \mmdf/conf/uclvax2/chan.c   444      0     12        6365  3620510326  10346 #include "util.h"
#include "mmdf.h"
#include "ch.h"
#include "dm.h"

char    *ch_dflnam = "local";

Alias	*al_list = (Alias *)0;

/*******************  MAIL IDENTIFIER  SELECTION  ***********************/

/*
 *      The global mid_enable controls the use of an alternate
 *      mail name system refered to as Mail-Ids.  When mail-ids are
 *      in use, login-ids and mail-ids are nolong bound to one another
 *      by the system account database, and are bound only by mapping
 *      tables.  The tables "mailids" and "users" will always be present
 *      though they may not be used.  "mailids" maps a mailid to a
 *      system account.  The table "users" maps a username to a mailid.
 */

int     mid_enable = 0;

Table
	tb_mailids =            /* Used in getpwmid() */
{
    "mailids", "Mail-ID to Username Mappings", "mailids", 0, 0l
},
	tb_users =              /* Used in getmailid() */
{
    "users", "Username to Mail-ID Mappings", "users", 0, 0l
};

/*
 *  The following set of structures provides a complete list of channels
 *  known to MMDF.
 *
 *  It is followed by two arrays that list the channels in useful orders.
 *
 *  ch_tbsrch[] is a full list, of all known channels, and is primarily
 *  used to guide the ch_table routines when treating the hostname space as
 *  flat (linear).  It also is used by lnk_submit, for sorting the address
 *  linked-list.
 *
 *  THE ORDER IS CRITICAL.  A hostname may appear in several tables, but
 *  the first table checked will cause the hit.
 *
 *  One nice aspect of this is that, for example, you can have a standard
 *  arpanet host table, but have some of its hosts actually get mail on a
 *  different channel.  Simply place that table's entry earlier in the
 *  ch_tbsrch[] list.
 *
 *  ch_exsrch[] is a list of channels that are processed by Deliver
 *  (usually just when it does standard, default daemon processing).
 *  Therefore, it need contain only those channels that are active and are
 *  to be processed by the daemon.  Again, order is important.  Channels
 *  are processed in the order listed.  This means that, typically, you
 *  want ch_sloc to be first, unless you have the strange view that foreign
 *  mail is more important than local...
 */
/**/

LOCVAR Chan *chsrch[NUMCHANS+1] =       /* order chan tables searched         */
{
	(Chan *) 0
};
Chan **ch_tbsrch = chsrch;

LOCVAR Chan *exsrch[NUMCHANS+1] =     /* order of active chan execution     */
{
	(Chan *) 0
};
Chan **ch_exsrch = exsrch;

LOCVAR Table *tblist[NUMTABLES+1] =    /* all known tables                   */
{
    (Table *) 0
};
Table **tb_list = tblist;

Chan * ch_ptrlist[NUMCHANS + 1];  /* can hold pointers to chans         */

LOCVAR Domain *dmlist[NUMDOMAINS];
Domain **dm_list = dmlist;         /* all known domains                  */

int     tb_maxtables = NUMTABLES; /* max number of slots                */
int     ch_maxchans = NUMCHANS;   /* max number of slots                */
int     dm_maxtables = NUMDOMAINS; /* max number of slots                */

/* these should reflect the ACTUAL number of slots in use  */
int     tb_numtables = 0;         /* actual number of slots used        */
int     ch_numchans = 0;          /* actual number of channels          */
int     dm_numtables = 0;         /* actual number of slots used        */
real
#	echo '# IF YOU PUT STUFF HERE IT WILL GO AWAY' >> Makefile.real
#	echo '# see make depend above' >> Makefile.real
 i in $(MODULES); do \
		(echo $$i.o: $$i.c >>makedep; \
		/bin/grep '^#[ 	]*include' x.c $$i.c | sed \
			-e 's,c:[^"]*"\./\([^"]*\)".*,o: \1,' \mmdf/conf/uclvax2/conf.c   444      0     12       20761  3635351422  10404 /*            COMPILE-TIME CONFIGURATION FILE WITH SITE-DEPENDENT INFORMATION
 *
 *      This file contains general host-dependent variable info that
 *   the message system needs to have.
 */

#include "util.h"
#include "mmdf.h"
#include "ap_norm.h"

char *mmtailor = "/etc/mmdf/mmdftailor";
				/* location of external tailoring file  */

/* ************************  PUBLIC NAMES  ****************************** */

char
	*locname = "UCL-CS",   /* Generic name for local host          */
	*locdomain = "AC.UK",    /* Name of domain that is local to us */
	*sitesignature = "Mail System (MMDF)",
				  /* in signature field of return-mail  */
	*mmdflogin = "mmdf",            /* login name for mmdf         */
	*supportaddr = "mmdf@Ucl-Cs.AC.UK";
				/* where to send bug reports, etc.      */

/* *****************  DEFAULT BASE DIRECTORIES  *********************** */

/*  The following pathnames define the default working directories for
 *  MMDF activities.  Specific files usually will be referenced relative
 *  to these directories.  To override the default, the specific filename
 *  should be specified as an anchored pathname, beginning with a slash.
 *
 *  The following pathnames must be anchored.
 */

char
	*cmddfldir = "/usr/mmdf/lib",
			      /* contains MMDF commands, such as      */
			      /* submit, deliver, and queclean        */
	*logdfldir = "/usr/spool/mmdflogs",
			      /* contains highly volatile files, such */
			      /* as logs and check-point markers      */
	*phsdfldir = "/usr/spool/mmdflogs/phase",
			      /* contains timestamp files             */
	*tbldfldir = "/usr/mmdf/mmdftable",
			      /* contains sticky files, such as       */
			      /* name tables & dialing scripts        */
	*tbldbm = "/usr/spool/mmdfdbm",
			      /* dbm() hash of name tables      */
	*quedfldir = "/usr/spool/mmdflock/que",
			      /* contains queued mail files, in       */
			      /* subordinate directories              */
			      /* (also see below)                     */
	*chndfldir = "/usr/mmdf/lib/chans",
				  /* contains the channel programs        */
				  /* (ch_*) called by deliver             */
	*lckdfldir = "/tmp/mmdf";
				/* Directory for lock files SEK           */


/* ***************  DEFAULT LOG LOCATIONS & SETTINGS  ***************** */

char    mlogloc[] = "msg.log";
char    chlogloc[] = "chan.log";

/*  It is common for specific using processes to tailor the second
 *  field, which is the log entry label, so that you can tell which
 *  incarnation of the process made a set of entries.
 *
 *  These structures conform to ll_log.h
 */

struct ll_struct
		    msglog =      /* deliver, submit, & queclean          */
{
    mlogloc, "XX-0000", LLOGGEN, 40, LLOGSOME, 0
},

		   chanlog =      /* log for channels & user agents       */
{
    chlogloc, "UA-0000", LLOGGEN, 40, LLOGSOME, 0
};

/* Needed for user authorisation logging        */

struct ll_struct authlog =
{
    "auth.log", "AU-0000", LLOGGEN, 40, LLOGSOME, 0
};

/* ****************  MESSAGE QUEUE SUBSTRUCTURE  ********************** */

/*  The queue has a top-level, locking directory, which only trusted
 *  processes can get through.  Under that is a base working directory.
 *  Below that are three directories which contain queued messages data
 *
 *  quedfldir[] specifies the top-level and base directories.
 */

int     queprot = 0700;           /* protection on quedfldir parent      */

/* the next five are relative to quedfldir[] */

char
	*deadletter = "DeadLetters",
				/* File for dead letters                */
	*tquedir = "temp/",   /* sub-directory for builing            */
				  /* address list file for msg            */
	*aquedir = "addr/",   /* sub-dir with files of address        */
				  /* lists for queued mail; files are     */
				  /* linked into here from tquedir        */
				  /* as last act of queuing msg           */
	*squepref = "q.",    /* preface sub-queue name with this        */
	*mquedir = "msg/";   /* sub-dir containing text of           */
				  /* queued messages; file names          */
				  /* are same as in aquedir               */

/*  The following two parameters establish thresholds for queue residency
 *  limits.  If a message does not move out of the queue by warntime,
 *  a notice may be sent to the originator.  If it is still in the queue
 *  by failtime, it is removed from the queue and may be returned to the
 *  originator.
 */

int
	warntime = 36,
	failtime = 252;


/* ******************  COMMAND NAMES & LOCATIONS  *********************** */

char
	*namsubmit = "submit",      /* who to submit mail through         */
	*pathsubmit = "submit",     /* location relative to cmddfldir[]   */

	*namdeliver = "deliver",    /* name of delivery overseer          */
	*pathdeliver = "deliver",   /* location relative to cmddfldir[]   */

	*nampkup = "pickup",        /* name of pickup overseer            */
	*pathpkup = "deliver",      /* location relative to cmddfldir[]   */

	*nammail = "v6mail",      /* the mmdf simple mail-sender        */
	*pathmail = "/usr/mmdf/lib/v6mail";
				    /* needs to handle special switches   */
				    /* must be MMDF special mail          */

/* *********************  SUBMIT TAILORING  ************************** */

int     maxhops = 20;               /* number of Via fields permitted   */
int	mgt_addid = 0;		    /* if set, add message-id if necessary */
int	lnk_listsize = 12;	    /* if more than this many addresses,
				     * then do not send warning and only
				     * send citation on return
				     */


/*   *********************  DELIVER TAILORING  ************************** */

int     maxqueue = 100;           /* maximum size of sortable queue       */
int     mailsleep = 300;          /* seconds between queue sweeps by a    */
				  /* background (-b) Deliver              */

/*   *********************  ADDRESS TRANSFORMATION  ********************* */


/*  Format:
 *
 *  Original host name, New hostname, String appended to mailbox
 *
 *      e.g.        "DCrocker at UDel-EE"
 *      maps to     "DCrocker.EE at UDel"
 *
 *  Note the usage, for Darcom.  It is designed to bypass address
 *  mapping, so that "foo at bar" does not go out as "foo.bar at relay"
 */

LOCVAR struct ap_hstab hstab[] =
{
    0, 0, 0,
};
struct ap_hstab *ap_exhstab = hstab;

/* *****************  LOCAL DELIVERY TAILORING  ************************* */

/*  Obviously, the following is only needed if there is local delivery,
 *  rather than pure relaying (a rare, but permitted mode).  Simplest
 *  determinor is ch_okloc in conf_chan.c
 */

int     sentprotect = 0600;         /* protection on mail files             */

char
	*dlvfile = ".maildelivery", /* User specified delivery instructions */
	*sysdlvfile = (char *)0,  /* if non-zero, the default dlvfile */
	*mldfldir = {0},	  /* directory to contain users' file     */
				  /* for receiving local mail.  if this   */
				  /* spec is empty, recipient's login     */
				  /* directory will be used.              */
	*mldflfil = ".mail",      /* file to receive new mail             */
				  /* relative to mldfldir[] or to login   */
				  /*   dir if mldfldir[] is empty         */
				  /* if this spec empty, user's login     */
				  /* name will be name of file            */
	*delim1 = "\001\001\n",   /* Note short UCL delimiter - SEK       */
	*delim2 = "\001\001\n";
				  /* add to begin and end of messages     */
				  /* NOTE:  to avoid author spoofin,      */
				  /* lo_wtmail() catches mail that        */
				  /* has either of these strings in it    */
				  /* and changes the first char           */
				  /* (it increments its ascii value)      */


/* *****************  UUCP CHANNEL TAILORING  *********************** */

char    *Uchan = "uucp";            /* default channel name */
char    *Uuxstr = "uux - -r";       /* command string to start UUX */

/* **************** NIFTP CHANNEL TAILORING  ************************** */

char	*pn_quedir   = "/usr/spool/jntmail";
				/* Location of JNTmail queues for NIFTP */

/* ************ MULTIPLE HOST TAILORING *******************************  */
/*                                                                       */
/*  This variable is initialised to be the name of the local machine     */
/*  It is used to transparently forward between the UCL machines         */


char	*locmachine = "Ucl-Cs";
				/* variable into which this name is read */

/******************* AUTHORIZATION TAILORING **************************/
char  *authrequest = "authorisation@Ucl-Cs";
				/* authorisation request address        */
char *authfile = "/etc/mmdf/warning";
				/* warning letter - full pathname       */

to submit mail mmdf/conf/uclvax2/conf.h   444      0     12        3063  3623122571  10363 #
/*          This contains the most site-dependent definitions             */

#define USERSIZE    8           /* max length of a user system name   */
#define MAILIDSIZ  64           /* max length of a mailid,
				 * must be at least USERSIZE.           */
#define MSGNSIZE   14           /* max length of a message file name  */
#define FILNSIZE   64           /* max length of full file name       */
#define ADDRSIZE  256           /* max length of an address           */
#define LINESIZE  256           /* max length of a line/record        */

#define NUMCHANS   20           /* max number of channels */
#define NUMTABLES  30           /* max number of tables */
#define NUMDOMAINS 20           /* max number of domains */

/************  DIAL IN/OUT (CH_PHONE / SLAVE) TAILORING  *****************/

#define DL_TRIES    2             /* number of re-connects OK for phone */
#define DL_RCVSZ    255           /* longest line receivable            */
#define DL_XMTSZ    255           /* longest line sendable              */

/****************  JNTMAIL Tailoring *******************/

#define JNTMAIL
#define BOTHEND
#undef  VIATRACE

/**************** Include File Tailoring ****************************/

#include <sys/time.h>
#include <sys/dir.h>

/********************* Privileged UIDs *************************/

/*
 *  This can be a macro as give here or you can provide a function.
 *  It should always authorize root and mmdf.
 *  The global variable effecid is assumed to have the MMDF uid in it.
 */

#define	PRIV_USER(x) ((x)==0 || (x)==1 || (x)==2 || (x)==effecid)
ser specified delivery instructions */
	*sysdlvfile = (char *)0,  /* if non-zero, the default dlvfile */
	*mldfldir = {0},	  /* directory to contain users' file     */
				  /* for receiving local mail.  if this   */
				  /* spec is empty, recipient's login     */
				  /* directory will be used.              */
	*mldflfil = ".mail",      /* file to receive new mail             */
				  /* relative to mldfldir[] or to login   */
				  /*   dir if mldfldir[mmdf/conf/uclvax2/conf_dial.c   444      0     12        6716  3622765005  11363 #include "util.h"
#include "ll_log.h"
#include "d_proto.h"
#include "d_structs.h"

/* **********************  DIALING TAILOR INFO  **********************  */

/*  modem speed support definitions  */

# define  BELL103        0177     /*  bell 103  0 to 300 baud  */
# define  VA825          0177     /*  vadic 825  0 to 300 baud  */
# define  VA3415         0400     /*  vadic 3415  1200 baud  */

/*
 *     structure defining the available dial-out ports.
 */

#define NUMPRTS         20
LOCVAR struct dialports  theprts[NUMPRTS] =
  {
  0,             0,                0,           0,    0, 0,      0,   0
  };
struct dialports  *d_prts = theprts;
int d_maxprts = NUMPRTS;
int d_numprts = 0;

#define NUMLINES        10
LOCVAR struct directlines  thelines[NUMLINES] =
  {
  0,              0,             0,                 0, 0
  };

int d_maxlines = NUMLINES;
int d_numlines = 0;
struct directlines  *d_lines = thelines;


/*
 *     this is the path name of the file on which outgoing calls are
 *     logged.  it should be owned by the same user as the outgoing
 *     ports and autodialer devices.
 */

char  *d_calllog  =  "/usr/mmdf/log/dial_log";

/*
 *	this is the name of the programs will actually do dialing
 *	it is called as "dial <line> <number>".  It knows which
 *	acus are attached to which lines.
 */

char	*d_dialprog = "/usr/bin/dial";

/* ***************  ILLEGAL CHARACTER TAILORING  ************************* */

/*  The following sets the default illegal-character arrays for both
    dial-out (phone) and dial-in (slave) connections.
 */


unsigned short d_lrill[8] =       /* illegal receive chars              */
{				  /* see d_slvconn & d_masconn          */
    0177777,                      /* nul soh stx etx eot enq ack bel    */
				  /* bs  ht  nl  vt  np  cr  so  si     */
    0177777,                      /* dle dc1 dc2 dc3 dc4 nak syn etb    */
				  /* can em  sub esc fs  gs  rs  us     */
    0000010,                      /* sp  ! " # $ % & ' ( ) * + , - . /  */
    0000000,                      /* 0 1 2 3 4 5 6 7 8 9 : ; < = > ?    */
    0000001,                      /* @ A B C D E F G H I J K L M N O    */
    0010000,                      /* P Q R S T U V W X Y Z [ \\] ^ _    */
    0000000,                      /* ` a b c d e f g h i j k l m n o    */
    0100000                       /* p q r s t u v w x y z { | } ~ del  */
};

unsigned short d_lxill[8] =       /* illegal send chars                 */
{				  /* see d_slvconn & d_masconn          */
    0177777,                      /* nul soh stx etx eot enq ack bel    */
				  /* bs  ht  nl  vt  np  cr  so  si     */
    0177777,                      /* dle dc1 dc2 dc3 dc4 nak syn etb    */
				  /* can em  sub esc fs  gs  rs  us     */
    0000010,                      /* sp  ! " # $ % & ' ( ) * + , - . /  */
    0000000,                      /* 0 1 2 3 4 5 6 7 8 9 : ; < = > ?    */
    0000001,                      /* @ A B C D E F G H I J K L M N O    */
    0010000,                      /* P Q R S T U V W X Y Z [ \\] ^ _    */
    0000000,                      /* ` a b c d e f g h i j k l m n o    */
    0100000                       /* p q r s t u v w x y z { | } ~ del  */
};

/*********************** PHONE LOGGING *************************/

struct ll_struct    ph_log =       /* dial-out link-level package log    */
{
    "ph.log", "PH-0000", LLOGGEN, 25, LLOGSOME, 0
};

char *def_trn = "ph.trn";          /* dial-out link-level i/o transcript */
				  /* does not use ll_log package        */

dir[] or to login   */
				  /*   dir if mldfldir[mmdf/conf/uclvax2/s_tailor.c   444      0     12         334  3620510327  11220 /*
 *	S _ T A I L O R . C
 *
 *  Contains strings used to tailor SEND to a particular
 *  site.  Also check s.h for tailorable defines.
 */

char	*dfleditor = "ed";
char	*dflveditor = "jove";
char	*dflchecker = "spell";
 bel    */
				  /* bs  ht  nl  vt  np  cr  so  si     */
    0177777,                      /* dle dc1 dc2 dc3 dc4 nak syn etb    */
				  /* can em  sub esc fs  gs  rs  us     */
    0000010,                      /* sp  ! " # $ % & ' ( ) * + , - . /  */
    0000000,                      /* mmdf/conf/uclvax2/msgtailor.c   444      0     12        3114  3671073315  11433 /*
 *	M S G T A I L O R . C
 */

#define NOEXTERNS
#include "./msg.h"

#ifdef pdp11
#	define NMSGS	500
#else
#	define NMSGS	2000
#endif pdp11

char    *savmsgfn = "mbox";
char	*resendprog = "|/usr/brl/bin/resend ";
char	*sndname = "/usr/local/send"; /* Overridden by .msgrc sendprog */

char	*dflshell = "sh";	/* Overridden by getenv("SHELL"); */
char	*dfleditor = "jove";	/* Overridden by getenv("EDITOR"); */

int	pagesize = 22;		/* default line count */
int	linelength = 80;	/* default line size */

int	paging = OFF;		/* Internal paging of long messages */
int	verbose = ON;		/* Command completion (raw mode) */
int	wmsgflag = OFF;		/* Save messages when calling send */
int	keystrip = ON;		/* Strip header lines on display */
int	bdots = ON;		/* Binary box dots */
int	mdots = ON;		/* Mailbox dots */
int	bprint = ON;		/* Reading... messages */
int	quicknflag = OFF;	/* Turn off NEW before message prints */
int	quickexit = ON;		/* Don't stay in msg if mailbox is empty */
int	prettylist = ON;	/* Print message numbers in listings */
int	filoutflag = ON;	/* Filter text (display controls as ^X) */
int	binsavflag = ON;	/* Save binary box on exec's & pauses */
int	doctrlel = OFF;		/* Page on control-L */
#ifndef SYS5
long	timezone = 5*60*60;	/* Seconds West of GMT */
int	daylight = 1;		/* Do you use daylight savings time? */
				/* 0 = NO, 1 = YES */
char	*tzname[] = {"EST", "EDT",};
				/* Timezone strings, primary and daylight
				   savings time if used, in that order */
#endif SYS5

/***** Do not tailor below this line *****/
int	Nmsgs = NMSGS;
struct message *msgp[NMSGS];
struct message *mptr;
   0177777,                      /* dle dc1 dc2 dc3 dc4 nak syn etb    */
				  /* can em  sub esc fs  gs  rs  us     */
    0000010,                      /* sp  ! " # $ % & ' ( ) * + , - . /  */
    0000000,                      /* 0 1 2 3 4 5 6 7 8 9 : ; < = > ?    */
    0000001,                      /* @ A B C D E F G H I J K L M N O    */
    0010000,                      /* P Q R S T U V W X Y Z [ \\] ^ _    */
    0000000,   mmdf/conf/uclvax2/Makefile.lib   444      0     12        2220  3652767271  11502 #
#	Makefile for the MMDF library   (libmmdf.a)
#

#  The paths below are relative from the directories below this one.
#
#  The RANLIB define should be "sh libfix" for systems that don't have
#	the ranlib command.  Systems that do have ranlib should set
#	RANLIB to "ranlib".  "libfix" is a Bourne shell file that will
#	use lorder and tsort to reorder the library.
#
MAKE	= make
RANLIB	= ranlib

SUBDIRS = addr dial mmdf table util
ALLSUBDIRS = addr dial mmdf table util

default: remake reorder

remake:
	for dir in $(SUBDIRS); \
	  do (cd $${dir}; echo "Running make in $${dir}"; \
	      gen ${MFLAGS} -${MAKEFLAGS}); \
	  done

install: default
	@echo "Library is up to date, nothing else to do."

reorder: reorder-done

reorder-done: libmmdf.a
	-$(RANLIB) libmmdf.a
	-touch reorder-done

depend:
	for dir in $(SUBDIRS); \
	  do (cd $${dir}; gen ${MFLAGS} -${MAKEFLAGS} depend); done

lint:
	for dir in $(SUBDIRS); \
	  do (cd $${dir}; gen ${MFLAGS} -${MAKEFLAGS} lint); done
	cat */llib-l*.ln > llib-lmmdf.ln

clean:
	-rm -f *-made libmmdf.a make.out core reorder-done
	for dir in $(SUBDIRS); \
	  do (cd $${dir}; $(MAKE) -f Makefile.real clean); done

ALWAYS:
   */
				  /* can em  sub esc fs  gs  rs  us     */
    0000010,                      /* sp  ! " # $ % & ' ( ) * + , - . /  */
    0000000,                      /* 0 1 2 3 4 5 6 7 8 9 : ; < = > ?    */
    0000001,                      /* @ A B C D E F G H I J K L M N O    */
    0010000,                      /* P Q R S T U V W X Y Z [ \\] ^ _    */
    0000000,   mmdf/conf/uclvax2/Makefile.src   444      0     12        2307  3657737664  11541 #	MMDF Program Source Subtree Makefile
#

#
# Programs that live in subdirectories, and have makefiles of their own.
#
SUBDIR=	tools submit deliver local list delay smtp phone pobox \
	uucp

#
#  This should list all the subdirectories that make clean works in.
#  This probably should not be altered unless you add/delete a channel.
#
ALLSUBDIRS=	tools submit deliver local list delay smtp phone pobox \
		uucp niftp bboards pop ean prog

all:
	for dir in ${SUBDIR}; do \
		(cd $${dir}; gen ${MFLAGS} -${MAKEFLAGS}); done

install:
	for dir in ${SUBDIR}; do \
		(cd $${dir}; gen ${MFLAGS} -${MAKEFLAGS} install); done

dist:
	-touch `ls */x*|grep -v '\.'`
	echo echo MMDF Distribution made `date` > install.sh
	for dir in ${SUBDIR}; do \
		echo cd $${dir} >> install.sh; \
		( cd $${dir}; \
		  gen -n ${MFLAGS} -${MAKEFLAGS} install) >> install.sh; \
		echo cd .. >> install.sh; \
	done
	tar cvf mmdfdist.tar install.sh */x*

depend:
	for dir in ${SUBDIR}; do \
		(cd $${dir}; gen ${MFLAGS} -${MAKEFLAGS} depend); done

lint:
	for dir in ${SUBDIR}; do \
		(cd $${dir}; gen ${MFLAGS} -${MAKEFLAGS} lint); done

clean:
	-rm -f make.out core
	for dir in ${ALLSUBDIRS}; do \
		(cd $${dir}; make -f Makefile.real clean); done
  0000010,                      /* sp  ! " # $ % & ' ( ) * + , - . /  */
    0000000,                      /* 0 1 2 3 4 5 6 7 8 9 : ; < = > ?    */
    0000001,                      /* @ A B C D E F G H I J K L M N O    */
    0010000,                      /* P Q R S T U V W X Y Z [ \\] ^ _    */
    0000000,   mmdf/conf/uclvax2/s_tailor.h   444      0     12         471  3620510330  11221 /*
 *	Site Tunable Parameters
 */
#undef	SENTFILE yes	/* Define to have copies of mail sent put in ,sent */
#undef	FNCYPRNT yes	/* Define for fancy listing of message (paginated) */
#define	PWNAME yes	/* Define to get real name from /etc/passwd and to */
			/* treat 0 length signatures to get login name only */
dd/delete a channel.
#
ALLSUBDIRS=	tools submit deliver local list delay smtp phone pobox \
		uucp niftp bboards pop ean prog

all:
	for dir in ${SUBDIR}; do \
		(cd $${dir}; gen ${MFLAGS} -${MAKEFLAmmdf/conf/okstate/   755      0     12           0  3671074635   7300 mmdf/conf/okstate/Makefile.com   444      0     12        7532  3657737665  11624 #
#	Common Sub-Makefile for the MMDF System
#
HOST		= okstate
SYSTEM		= v7
MMPREF		=
LIBDIR		= /usr/mmdfii
CHANDIR		= /usr/mmdfii/chans
TBLDIR		= /usr/mmdfii/table
BINDIR		= /usr/local
RCVDIR		= /usr/mmdfii/rcvmail

#
#  Defines used during installation
#
CHOWN		= chown
ROOTLOGIN	= root
MMDFLOGIN	= mmdf
PGMPROT 	= 711

#
#  Configuration Defines
#
#  The paths below are relative from the directories below this one.
#  Define DOASSIGN to CONFIGDEFS if you want the d_assign code
#  Define SECURETTY if you run with terminals write protected (mode 711)
#  Define DEBUG to include extensive calls to the logging package
#  Define D_LOG if you want phone channel logging
#  Define D_DBGLOG if you want phone channel debugging output
#  Define RUNALONE for standalone testing (seldom used)
#  Define V4_2BSD if you are compiling for 4.2BSD
#  Define NODIAL if you do not want the dial package (no phone channel)
#  Define SYS5 if you have a SystemV Unix or something similar (Sys3)
#  Define ALTOS if you have an Altos, does some depraved things.
#  Define NODUP2 if you don't a dup2() syscall or subroutine.
#  Define NOFCNTL and NODUP2 if you don't have fcntl(x, F_DUPFD) or dup2()
#  Define -Drindex=strrchr -Dindex=strchr if you are on a SYS5 system
#  Define NAMESERVER if you want the nameserver lookup code
#  Define UUCPLOCK if you want UUCP/tip/cu-style locking for dial-out lines
#  Define NODOMLIT if you don't want literals like [10.0.0.52] in messages
#
# Lots of special crocks here for OKSTATE problems (this is not a good sample)
CONFIGDEFS	= -DDEBUG=1 -DD_LOG -DD_DBGLOG -DNODOMLIT
CFLAGS		= -m -O -I../../h $(CONFIGDEFS)
LDFLAGS 	= -k 8k
LDFLAGSB	= -k 16k
LDSHR		= -n
MMDFLIBS 	= ../../lib/dial/d_pglobals.o ../../lib/libmmdf.a
SYSLIBS		= -ldbm -lndir
LINT		= lint
LFLAGS		= -phbvxacL -I../../h $(CONFIGDEFS)
LLIBS		= ../../lib/llib-lmmdf.ln
AR		= ar


#  Specify either ch_tbdbm (for DBM based tables) or ch_tbseq for
#  sequential IO based tables.
CH_TB	= ch_tbdbm

#  Specify one of the nameserver modules or the fake module
#  if you do not intend to support nameservers (4.2, fake)
TB_NS	= fake

#  Specify tai_???.o and lk_lock???.o
LOCALUTIL = tai_file.o lk_lock.rand.o


default:	real-default

#
#  special case dependencies
#
../../h/mmdf.h:	../../h/conf.h
	-touch ../../h/mmdf.h
.PRECIOUS: ../../h/mmdf.h

#
#  #include dependencies
#
#  Two versions are supplied.  One for sites with cc -M (4.3BSD)
#  and one for those that don't have it.  Comment out the one
#  you do not want.

#  This one is for sites without cc -M
depend:
	cat </dev/null >x.c
	for i in $(MODULES); do \
		(echo $$i.o: $$i.c >>makedep; \
		/bin/grep '^#[ 	]*include' x.c $$i.c | sed \
			-e 's,c:[^"]*"\./\([^"]*\)".*,o: \1,' \
			-e 's,c:[^"]*"/\([^"]*\)".*,o: /\1,' \
			-e 's,c:[^"]*"\([^"]*\)".*,o: ../../h/\1,' \
			-e 's,c:[^<]*<\(.*\)>.*,o: /usr/include/\1,' \
			>>makedep); done
	echo '/^# DO NOT DELETE THIS LINE/+2,$$d' >eddep
	echo '$$r makedep' >>eddep
	echo 'w' >>eddep
	cp Makefile.real Makefile.bak
	ed - Makefile.real < eddep
	rm eddep makedep x.c
	echo '# DEPENDENCIES MUST END AT END OF FILE' >> Makefile.real
	echo '# IF YOU PUT STUFF HERE IT WILL GO AWAY' >> Makefile.real
	echo '# see make depend above' >> Makefile.real

#  This one id for sites with cc -M
#depend:
#	( for i in ${MODULES} ; do \
#		${CC} -M ${CFLAGS} $$i.c ; done ) | \
#	awk ' { if ($$1 != prev) { print rec; rec = $$0; prev = $$1; } \
#		else { if (length(rec $$2) > 78) { print rec; rec = $$0; } \
#		       else rec = rec " " $$2 } } \
#	      END { print rec } ' > makedep
#	echo '/^# DO NOT DELETE THIS LINE/+2,$$d' >eddep
#	echo '$$r makedep' >>eddep
#	echo 'w' >>eddep
#	cp Makefile.real Makefile.bak
#	ed - Makefile.real < eddep
#	rm eddep makedep
#	echo '# DEPENDENCIES MUST END AT END OF FILE' >> Makefile.real
#	echo '# IF YOU PUT STUFF HERE IT WILL GO AWAY' >> Makefile.real
#	echo '# see make depend above' >> Makefile.real
risation@Ucl-Cs";
				/* authorisation request address        */
char *authfile = "/etc/mmdf/warning";
				/* warning letter - full pathname       */

to submit mail mmdf/conf/okstate/chan.c   444      0     12        6062  3620510331  10422 #include "util.h"
#include "mmdf.h"
#include "ch.h"
#include "dm.h"

char    *ch_dflnam = "local";

Alias	*al_list = (Alias *)0;

/*******************  MAIL IDENTIFIER  SELECTION  ***********************/

/*
 *	The global mid_enable controls the use of an alternate
 *	mail name system refered to as Mail-Ids.  When mail-ids are
 *	in use, login-ids and mail-ids are nolong bound to one another
 *	by the system account database, and are bound only by mapping
 *	tables.  The tables "mailids" and "users" will always be present
 *	though they may not be used.  "mailids" maps a mailid to a
 *	system account.  The table "users" maps a username to a mailid.
 */

int	mid_enable = 0;

Table
	tb_mailids =		/* Used in getpwmid() */
{
    "mailids", "Mail-ID to Username Mappings", "mailids", 0, 0l
},
	tb_users =		/* Used in getmailid() */
{
    "users", "Username to Mail-ID Mappings", "users", 0, 0l
};


/*
 *  The following set of structures provides a complete list of channels
 *  known to MMDF.
 *
 *  It is followed by two arrays that list the channels in useful orders.
 *
 *  ch_tbsrch[] is a full list, of all known channels, and is primarily
 *  used to guide the ch_table routines when treating the hostname space as
 *  flat (linear).  It also is used by lnk_submit, for sorting the address
 *  linked-list.
 *
 *  THE ORDER IS CRITICAL.  A hostname may appear in several tables, but
 *  the first table checked will cause the hit.
 *
 *  One nice aspect of this is that, for example, you can have a standard
 *  arpanet host table, but have some of its hosts actually get mail on a
 *  different channel.  Simply place that table's entry earlier in the
 *  ch_tbsrch[] list.
 *
 *  ch_exsrch[] is a list of channels that are processed by Deliver
 *  (usually just when it does standard, default daemon processing).
 *  Therefore, it need contain only those channels that are active and are
 *  to be processed by the daemon.  Again, order is important.  Channels
 *  are processed in the order listed.  This means that, typically, you
 *  want ch_sloc to be first, unless you have the strange view that foreign
 *  mail is more important than local...
 */

LOCVAR Chan *chsrch[NUMCHANS+1] = {	/* Order chan tables searched */
	(Chan *) 0
};
Chan **ch_tbsrch = chsrch;

LOCVAR Chan *exsrch[NUMCHANS+1] = {	/* Order of active chan execution */
	(Chan *) 0
};
Chan **ch_exsrch = exsrch;
Chan * ch_ptrlist[NUMCHANS + 1];  /* can hold pointers to chans */

LOCVAR Table *tblist[NUMTABLES+1] = {	/* All known tables */
    (Table *) 0
};
Table **tb_list = tblist;

LOCVAR Domain *dmlist[NUMDOMAINS+1];
Domain **dm_list = dmlist;         /* all known domains */

int     tb_maxtables = NUMTABLES; /* max number of slots */
int     ch_maxchans = NUMCHANS;   /* max number of slots */
int     dm_maxtables = NUMDOMAINS; /* max number of slots */

/* These next entries should reflect the ACTUAL number of slots in use  */
int     tb_numtables = 0;         /* actual number of tables */
int     ch_numchans = 0;          /* actual number of channels */
int     dm_numtables = 0;         /* actual number of domains */
akedep' >>eddep
#	echo 'w' >>eddep
#	cp Makefile.real Makefile.bak
#	ed - Makefile.real < eddep
#	rm eddep makedep
#	echo '# DEPENDENCIES MUST END AT END OF FILE' >> Makefile.real
#	echo '# IF YOU PUT STUFF HERE IT WILL GO AWAY' >> Makefile.real
#	echo '# see make depend above' >> Makefile.real
risation@Ucl-Cs";
				/* authorisation request address        */
char *authfile = "/etc/mmdf/warning";
				/* warning letter - full pathname       */

to submit mail mmdf/conf/okstate/conf.c   444      0     12       20426  3635352257  10477 #include "util.h"
#include "mmdf.h"
#include "ap_norm.h"

/*            COMPILE-TIME CONFIGURATION FILE WITH SITE-DEPENDENT INFORMATION
 *
 *      This file contains general host-dependent variable info that
 *   the message system needs to have.
 */

char *mmtailor = "/usr/mmdfii/mmdftailor";
				/* location of external tailoring file  */

/* ************************  PUBLIC NAMES  ****************************** */

char
	*locname = "okstate",	/* Generic name for local host          */
	*locdomain = "CSNET",	/* Name of domain that is local to us */
	*sitesignature = "Mail System (MMDF)",
				/* in signature field of return-mail  */
	*mmdflogin = "mmdf",	/* login name for mmdf         */
	*supportaddr = "MMDF@okstate.csnet";
				/* where to send bug reports, etc. */
/* *****************  DEFAULT BASE DIRECTORIES  *********************** */

/*  The following pathnames define the default working directories for
 *  MMDF activities.  Specific files usually will be referenced relative
 *  to these directories.  To override the default, the specific filename
 *  should be specified as an anchored pathname, beginning with a slash.
 *
 *  The following pathnames must be anchored.
 */

char	*cmddfldir = "/usr/mmdfii";
			      /* contains MMDF commands, such as      */
			      /* submit, deliver, and queclean        */
char	*logdfldir = "/usr/mmdfii/log";
			      /* contains highly volatile files, such */
			      /* as logs and check-point markers      */
char	*phsdfldir = "/usr/mmdfii/log/phase";
			      /* contains timestamp files             */
char	*tbldfldir = "/usr/mmdfii/table";
			      /* contains sticky files, such as       */
			      /* name tables & dialing scripts        */
char	*tbldbm = "/usr/mmdfii/table/mmdfdbm";
			      /* dbm() hash of name tables      */
char	*quedfldir = "/usr/mmdfii/lock/home";
			      /* contains queued mail files, in       */
			      /* subordinate directories              */
			      /* (also see below)                     */
char	*chndfldir = "/usr/mmdfii/chans";
				/* contains the channel programs        */
				/* (ch_*) called by deliver             */
char	*lckdfldir = "/usr/mmdfii/lock/home/lock";
				/* Directory for lock files SEK           */


/* ***************  DEFAULT LOG LOCATIONS & SETTINGS  ***************** */

char    mlogloc[] = "msg.log";
char    chlogloc[] = "chan.log";

/*  It is common for specific using processes to tailor the second
 *  field, which is the log entry label, so that you can tell which
 *  incarnation of the process made a set of entries.
 *
 *  These structures conform to ll_log.h
 */

struct ll_struct
		    msglog =      /* deliver, submit, & queclean          */
{
    mlogloc, "XX-0000", LLOGFST, 40, LLOGSOME, 0
},

		   chanlog =      /* log for channels & user agents       */
{
    chlogloc, "UA-0000", LLOGFST, 40, LLOGSOME, 0
};

/* Needed for user authorisation logging        */

struct ll_struct authlog =
{
    "auth.log", "AU-0000", LLOGFTR, 40, LLOGSOME, 0
};

/* ****************  MESSAGE QUEUE SUBSTRUCTURE  ********************** */

/*  The queue has a top-level, locking directory, which only trusted
 *  processes can get through.  Under that is a base working directory.
 *  Below that are three directories which contain queued messages data
 *
 *  quedfldir[] specifies the top-level and base directories.
 */

int     queprot = 0700;           /* protection on quedfldir parent      */

/* the next five are relative to quedfldir[] */

char
	*deadletter = "DeadLetters",
				/* Actually NOT a directory, where	*/
				/* unreturned letters go to die.	*/
	*tquedir = "tmp/",	/* sub-directory for builing            */
				/* address list file for msg            */
	*aquedir = "addr/",	/* sub-dir with files of address        */
				/* lists for queued mail; files are     */
				/* linked into here from tquedir        */
				/* as last act of queuing msg           */
	*squepref = "q.",	/* preface sub-queue name with this	*/
	*mquedir = "msg/";	/* sub-dir containing text of           */
				/* queued messages; file names          */
				/* are same as in aquedir               */

/*  The following two parameters establish thresholds for queue residency
 *  limits.  If a message does not move out of the queue by warntime,
 *  a notice may be sent to the originator.  If it is still in the queue
 *  by failtime, it is removed from the queue and may be returned to the
 *  originator.
 */

int
	warntime = 60,
	failtime = 252;


/* ******************  COMMAND NAMES & LOCATIONS  *********************** */

char
	*namsubmit = "submit",      /* who to submit mail through         */
	*pathsubmit = "submit",     /* location relative to cmddfldir[]   */

	*namdeliver = "deliver",    /* name of delivery overseer          */
	*pathdeliver = "deliver",   /* location relative to cmddfldir[]   */

	*nampkup = "pickup",        /* name of pickup overseer            */
	*pathpkup = "deliver",      /* location relative to cmddfldir[]   */

	*nammail = "mmdfmail",      /* the mmdf simple mail-sender        */
	*pathmail = "v6mail";       /* needs to handle special switches   */
				    /* probably must be MMDF's uip/mail   */

/* *********************  SUBMIT TAILORING  ************************** */

int     maxhops = 20;               /* number of Via fields permitted   */
int	mgt_addid = 0;		    /* if set, add message-id if necessary */
int	lnk_listsize = 12;	    /* if more than this many addresses,
				     * then do not send warning and only
				     * send citation on return
				     */


/*   *********************  DELIVER TAILORING  ************************** */

int	maxqueue = 100;		  /* maximum size of sortable queue       */
int	mailsleep = 600;          /* seconds between queue sweeps by a    */
				  /* background (-b) Deliver              */

/*   *********************  ADDRESS TRANSFORMATION  ********************* */


/*  Format:
 *
 *  Original host name, New hostname, String appended to mailbox
 *
 *      e.g.        "DCrocker at UDel-EE"
 *      maps to     "DCrocker.EE at UDel"
 *
 *  Note the usage, for Darcom.  It is designed to bypass address
 *  mapping, so that "foo at bar" does not go out as "foo.bar at relay"
 */

LOCVAR struct ap_hstab hstab[] =
{
    0, 0, 0,
};
struct ap_hstab *ap_exhstab = hstab;

/* *****************  LOCAL DELIVERY TAILORING  ************************* */

/*  Obviously, the following is only needed if there is local delivery,
 *  rather than pure relaying (a rare, but permitted mode).  Simplest
 *  determinor is ch_okloc in conf_chan.c
 */

int     sentprotect = 0600;         /* protection on mail files             */

char
	*dlvfile = ".maildelivery", /* User specified delivery instructions */
	*sysdlvfile = (char *)0,  /* if non-zero, the default dlvfile */
	*mldfldir = "\0",	/* directory to contain users' file     */
				/* for receiving local mail.  if this   */
				/* spec is empty, recipient's login     */
				/* directory will be used.              */
	*mldflfil = ".mail",    /* file to receive new mail             */
				  /* relative to mldfldir[] or to login   */
				  /*   dir if mldfldir[] is empty         */
				  /* if this spec empty, user's login     */
				  /* name will be name of file            */
	*delim1 = "\001\001\001\001\n",
	*delim2 = "\001\001\001\001\n";
				  /* add to begin and end of messages     */
				  /* NOTE:  to avoid author spoofin,      */
				  /* lo_wtmail() catches mail that        */
				  /* has either of these strings in it    */
				  /* and changes the first char           */
				  /* (it increments its ascii value)      */


/* *****************  UUCP CHANNEL TAILORING  *********************** */

char	*Uchan = "uucp";		/* default channel name 	 */
char    *Uuxstr = "uux - -r";		/* command string to start UUX */

/******************* NIFTP CHANNEL TAILORING  *************************/

char	*pn_quedir   = "/usr/spool/jntmail";
				/* Location of JNTmail queues for NIFTP */

/******************* MULTIPLE HOST TAILORING **************************
 *
 *  This variable is initialised to be the name of the local machine
 *  It is used to transparently forward between the UCL machines
 */
char	*locmachine = "\0";


/******************* AUTHORIZATION TAILORING **************************/
char	*authrequest = "mmdf@okstate";
				/* authorisation request address        */
char	*authfile = "/usr/mmdfii/warning";
				/* warning letter - full pathname       */

 it is removed from the queue and may be returned to the
 *  originator.
 */

int
	warntime = 60,
	failtime = 252;


/* ******************  COMMAND NAMES & LOCATIONS  *********************** */

char
	*namsubmit = "submit",      /* whmmdf/conf/okstate/conf.h   444      0     12        2733  3623122355  10454 #
/*          This contains the most site-dependent definitions             */

#define USERSIZE    8		/* max length of a user system name   */
#define	MAILIDSIZ  64		/* max length of a mailid,
				 * must be at least USERSIZE.		*/
#define MSGNSIZE   14           /* max length of a message file name  */
#define FILNSIZE   64           /* max length of full file name       */
#define ADDRSIZE  256		/* max length of an address           */
#define LINESIZE  256		/* max length of a line/record        */

#define	NUMCHANS   20		/* max number of channels */
#define NUMTABLES  30		/* max number of tables */
#define NUMDOMAINS 20		/* max number of domains */

/************  DIAL IN/OUT (CH_PHONE / SLAVE) TAILORING  *****************/

#define DL_TRIES    2             /* number of re-connects OK for phone */
#define DL_RCVSZ    255           /* longest line receivable            */
#define DL_XMTSZ    255           /* longest line sendable              */

/****************  JNTMAIL Tailoring *******************/

#undef	JNTMAIL
#undef	BOTHEND
#undef	VIATRACE

/**************** Include File Tailoring ****************************/

#include <time.h>
#include <sys/ndir.h>

/********************* Privileged UIDs *************************/

/*
 *  This can be a macro as give here or you can provide a function.
 *  It should always authorize root and mmdf.
 *  The global variable effecid is assumed to have the MMDF uid in it.
 */

#define	PRIV_USER(x) ((x)==0 || (x)==1 || (x)==effecid)
ER TAILORING  ***********************mmdf/conf/okstate/conf_dial.c   444      0     12        6721  3622764343  11451 #include "util.h"
#include "ll_log.h"
#include "d_proto.h"
#include "d_structs.h"

/* **********************  DIALING TAILOR INFO  **********************  */

/*  modem speed support definitions  */

# define  BELL103        0177     /*  bell 103  0 to 300 baud  */
# define  VA825          0177     /*  vadic 825  0 to 300 baud  */
# define  VA3415         0400     /*  vadic 3415  1200 baud  */

/*
 *     structure defining the available dial-out ports.
 */

#define NUMPRTS         20
LOCVAR struct dialports  theprts[NUMPRTS] =
  {
  0,             0,                0,           0,    0, 0,      0,   0
  };
struct dialports  *d_prts = theprts;
int d_maxprts = NUMPRTS;
int d_numprts = 0;

#define NUMLINES        10
LOCVAR struct directlines  thelines[NUMLINES] =
  {
  0,              0,             0,                 0, 0
  };

int d_maxlines = NUMLINES;
int d_numlines = 0;
struct directlines  *d_lines = thelines;


/*
 *     this is the path name of the file on which outgoing calls are
 *     logged.  it should be owned by the same user as the outgoing
 *     ports and autodialer devices.
 */

char  *d_calllog  =  "/usr/mmdfii/log/dial_log";

/*
 *	this is the name of the programs will actually do dialing
 *	it is called as "dial <line> <number>".  It knows which
 *	acus are attached to which lines.
 */

char	*d_dialprog = "/usr/bin/dial";

/* ***************  ILLEGAL CHARACTER TAILORING  ************************* */

/*  The following sets the default illegal-character arrays for both
    dial-out (phone) and dial-in (slave) connections.
 */


unsigned short d_lrill[8] =       /* illegal receive chars              */
{				  /* see d_slvconn & d_masconn          */
    0177777,                      /* nul soh stx etx eot enq ack bel    */
				  /* bs  ht  nl  vt  np  cr  so  si     */
    0177777,                      /* dle dc1 dc2 dc3 dc4 nak syn etb    */
				  /* can em  sub esc fs  gs  rs  us     */
    0000000,                      /* sp  ! " # $ % & ' ( ) * + , - . /  */
    0000000,                      /* 0 1 2 3 4 5 6 7 8 9 : ; < = > ?    */
    0000000,                      /* @ A B C D E F G H I J K L M N O    */
    0000000,                      /* P Q R S T U V W X Y Z [ \\] ^ _    */
    0000000,                      /* ` a b c d e f g h i j k l m n o    */
    0100000                       /* p q r s t u v w x y z { | } ~ del  */
};

unsigned short d_lxill[8] =       /* illegal send chars                 */
{				  /* see d_slvconn & d_masconn          */
    0177777,                      /* nul soh stx etx eot enq ack bel    */
				  /* bs  ht  nl  vt  np  cr  so  si     */
    0177777,                      /* dle dc1 dc2 dc3 dc4 nak syn etb    */
				  /* can em  sub esc fs  gs  rs  us     */
    0000000,                      /* sp  ! " # $ % & ' ( ) * + , - . /  */
    0000000,                      /* 0 1 2 3 4 5 6 7 8 9 : ; < = > ?    */
    0000000,                      /* @ A B C D E F G H I J K L M N O    */
    0000000,                      /* P Q R S T U V W X Y Z [ \\] ^ _    */
    0000000,                      /* ` a b c d e f g h i j k l m n o    */
    0100000                       /* p q r s t u v w x y z { | } ~ del  */
};

/************************ PHONE LOGGING **************************/

struct ll_struct    ph_log =       /* dial-out link-level package log    */
{
    "ph.log", "PH-0000", LLOGFST, 25, LLOGSOME, 0
};

char *def_trn = "ph.trn";          /* dial-out link-level i/o transcript */
				  /* does not use ll_log package        */
    /* protection on mail files             */
mmdf/conf/okstate/s_tailor.c   444      0     12         334  3620510332  11302 /*
 *	S _ T A I L O R . C
 *
 *  Contains strings used to tailor SEND to a particular
 *  site.  Also check s.h for tailorable defines.
 */

char	*dfleditor = "ed";
char	*dflveditor = "jove";
char	*dflchecker = "spell";
dial <line> <number>".  It knows which
 *	acus are attached to which lines.
 */

char	*d_dialprog = "/usr/bin/dial";

/* ***************  ILLEGAL CHARACTER TAILORING  ************************* */

/*  The following sets the default illegal-character arrays for both
    dial-out (phone) and mmdf/conf/okstate/msgtailor.c   444      0     12        3114  3671073311  11515 /*
 *	M S G T A I L O R . C
 */

#define NOEXTERNS
#include "./msg.h"

#ifdef pdp11
#	define NMSGS	500
#else
#	define NMSGS	2000
#endif pdp11

char    *savmsgfn = "mbox";
char	*resendprog = "|/usr/brl/bin/resend ";
char	*sndname = "/usr/local/send"; /* Overridden by .msgrc sendprog */

char	*dflshell = "sh";	/* Overridden by getenv("SHELL"); */
char	*dfleditor = "jove";	/* Overridden by getenv("EDITOR"); */

int	pagesize = 22;		/* default line count */
int	linelength = 80;	/* default line size */

int	paging = OFF;		/* Internal paging of long messages */
int	verbose = ON;		/* Command completion (raw mode) */
int	wmsgflag = OFF;		/* Save messages when calling send */
int	keystrip = ON;		/* Strip header lines on display */
int	bdots = ON;		/* Binary box dots */
int	mdots = ON;		/* Mailbox dots */
int	bprint = ON;		/* Reading... messages */
int	quicknflag = OFF;	/* Turn off NEW before message prints */
int	quickexit = ON;		/* Don't stay in msg if mailbox is empty */
int	prettylist = ON;	/* Print message numbers in listings */
int	filoutflag = ON;	/* Filter text (display controls as ^X) */
int	binsavflag = ON;	/* Save binary box on exec's & pauses */
int	doctrlel = OFF;		/* Page on control-L */
#ifndef SYS5
long	timezone = 5*60*60;	/* Seconds West of GMT */
int	daylight = 1;		/* Do you use daylight savings time? */
				/* 0 = NO, 1 = YES */
char	*tzname[] = {"EST", "EDT",};
				/* Timezone strings, primary and daylight
				   savings time if used, in that order */
#endif SYS5

/***** Do not tailor below this line *****/
int	Nmsgs = NMSGS;
struct message *msgp[NMSGS];
struct message *mptr;

    0177777,                      /* dle dc1 dc2 dc3 dc4 nak syn etb    */
				  /* can em  sub esc fs  gs  rs  us     */
    0000000,                      /* sp  ! " # $ % & ' ( ) * + , - . /  */
    0000000,                      /* 0 1 2 3 4 5 6 7 8 9 : ; < = > ?    */
    0000000,                      /* @ A B C D E F G H I J K L M N O    */
    0000000,                      /* P Q R S T U V W X Y Z [ \\] ^ _    */
    0000000, mmdf/conf/okstate/Makefile.lib   444      0     12        2220  3652767254  11571 #
#	Makefile for the MMDF library   (libmmdf.a)
#

#  The paths below are relative from the directories below this one.
#
#  The RANLIB define should be "sh libfix" for systems that don't have
#	the ranlib command.  Systems that do have ranlib should set
#	RANLIB to "ranlib".  "libfix" is a Bourne shell file that will
#	use lorder and tsort to reorder the library.
#
MAKE	= make
RANLIB	= ranlib

SUBDIRS = addr dial mmdf table util
ALLSUBDIRS = addr dial mmdf table util

default: remake reorder

remake:
	for dir in $(SUBDIRS); \
	  do (cd $${dir}; echo "Running make in $${dir}"; \
	      gen ${MFLAGS} -${MAKEFLAGS}); \
	  done

install: default
	@echo "Library is up to date, nothing else to do."

reorder: reorder-done

reorder-done: libmmdf.a
	-$(RANLIB) libmmdf.a
	-touch reorder-done

depend:
	for dir in $(SUBDIRS); \
	  do (cd $${dir}; gen ${MFLAGS} -${MAKEFLAGS} depend); done

lint:
	for dir in $(SUBDIRS); \
	  do (cd $${dir}; gen ${MFLAGS} -${MAKEFLAGS} lint); done
	cat */llib-l*.ln > llib-lmmdf.ln

clean:
	-rm -f *-made libmmdf.a make.out core reorder-done
	for dir in $(SUBDIRS); \
	  do (cd $${dir}; $(MAKE) -f Makefile.real clean); done

ALWAYS:
	doctrlel = OFF;		/* Page on control-L */
#ifndef SYS5
long	timezone = 5*60*60;	/* Seconds West of GMT */
int	daylight = 1;		/* Do you use daylight savings time? */
				/* 0 = NO, 1 = YES */
char	*tzname[] = {"EST", "EDT",};
				/* Timezone strings, primary and daylight
				   savings time if used, in that order */
#endif SYS5

/***** Do not tailor below this line *mmdf/conf/okstate/Makefile.src   444      0     12        2307  3657737667  11632 #	MMDF Program Source Subtree Makefile
#

#
# Programs that live in subdirectories, and have makefiles of their own.
#
SUBDIR=	tools submit deliver local list delay smtp phone pobox \
	uucp

#
#  This should list all the subdirectories that make clean works in.
#  This probably should not be altered unless you add/delete a channel.
#
ALLSUBDIRS=	tools submit deliver local list delay smtp phone pobox \
		uucp niftp bboards pop ean prog

all:
	for dir in ${SUBDIR}; do \
		(cd $${dir}; gen ${MFLAGS} -${MAKEFLAGS}); done

install:
	for dir in ${SUBDIR}; do \
		(cd $${dir}; gen ${MFLAGS} -${MAKEFLAGS} install); done

dist:
	-touch `ls */x*|grep -v '\.'`
	echo echo MMDF Distribution made `date` > install.sh
	for dir in ${SUBDIR}; do \
		echo cd $${dir} >> install.sh; \
		( cd $${dir}; \
		  gen -n ${MFLAGS} -${MAKEFLAGS} install) >> install.sh; \
		echo cd .. >> install.sh; \
	done
	tar cvf mmdfdist.tar install.sh */x*

depend:
	for dir in ${SUBDIR}; do \
		(cd $${dir}; gen ${MFLAGS} -${MAKEFLAGS} depend); done

lint:
	for dir in ${SUBDIR}; do \
		(cd $${dir}; gen ${MFLAGS} -${MAKEFLAGS} lint); done

clean:
	-rm -f make.out core
	for dir in ${ALLSUBDIRS}; do \
		(cd $${dir}; make -f Makefile.real clean); done
long	timezone = 5*60*60;	/* Seconds West of GMT */
int	daylight = 1;		/* Do you use daylight savings time? */
				/* 0 = NO, 1 = YES */
char	*tzname[] = {"EST", "EDT",};
				/* Timezone strings, primary and daylight
				   savings time if used, in that order */
#endif SYS5

/***** Do not tailor below this line *mmdf/conf/okstate/s_tailor.h   444      0     12         471  3620510333  11312 /*
 *	Site Tunable Parameters
 */
#undef	SENTFILE yes	/* Define to have copies of mail sent put in ,sent */
#undef	FNCYPRNT yes	/* Define for fancy listing of message (paginated) */
#define	PWNAME yes	/* Define to get real name from /etc/passwd and to */
			/* treat 0 length signatures to get login name only */
dd/delete a channel.
#
ALLSUBDIRS=	tools submit deliver local list delay smtp phone pobox \
		uucp niftp bboards pop ean prog

all:
	for dir in ${SUBDIR}; do \
		(cd $${dir}; gen ${MFLAGS} -${MAKEFLAmmdf/conf/vu44/   755      0     12           0  3671074636   6431 mmdf/conf/vu44/Makefile.com   444      0     12        7326  3657737670  10751 #
#	Common Sub-Makefile for the MMDF System
#
HOST		= vu44
SYSTEM		= 5.2
MMPREF		=
LIBDIR		= /usr/mmdf
CHANDIR		= /usr/mmdf/chans
TBLDIR		= /usr/mmdf/table
BINDIR		= /usr/local
RCVDIR		= /usr/mmdf/rcvmail

#
#  Defines used during installation
#
CHOWN		= /bin/chown
MMDFLOGIN	= mmdf
ROOTLOGIN	= root
PGMPROT 	= 755

#
#  Configuration Defines
#
#  The paths below are relative from the directories below this one.
#  Define DOASSIGN to CONFIGDEFS if you want the d_assign code
#  Define SECURETTY if you run with terminals write protected (mode 711)
#  Define DEBUG to include extensive calls to the logging package
#  Define D_LOG if you want phone channel logging
#  Define D_DBGLOG if you want phone channel debugging output
#  Define RUNALONE for standalone testing (seldom used)
#  Define V4_2BSD if you are compiling for 4.2BSD
#  Define NODIAL if you do not want the dial package (no phone channel)
#  Define SYS5 if you have a SystemV Unix or something similar (Sys3)
#  Define ALTOS if you have an Altos, does some depraved things.
#  Define NODUP2 if you don't a dup2() syscall or subroutine.
#  Define NOFCNTL and NODUP2 if you don't have fcntl(x, F_DUPFD) or dup2()
#  Define -Drindex=strrchr -Dindex=strchr if you are on a SYS5 system
#  Define NAMESERVER if you want the nameserver lookup code
#  Define UUCPLOCK if you want UUCP/tip/cu-style locking for dial-out lines
#  Define NODOMLIT if you don't want literals like [10.0.0.52] in messages
#
CONFIGDEFS	= -DSYS5 -DNODIAL -DNODUP2 -Drindex=strrchr -Dindex=strchr -DNODOMLIT
CFLAGS		= -O -I../../h $(CONFIGDEFS)
LDFLAGS 	=
MMDFLIBS 	= ../../lib/libmmdf.a
SYSLIBS		= -ldbm -lndir
LINT		= lint
LFLAGS		= -bxah -I../../h $(CONFIGDEFS)
LLIBS		= ../../lib/llib-lmmdf.ln
AR		= ar


#  Specify either ch_tbdbm (for DBM based tables) or ch_tbseq for
#  sequential IO based tables.
CH_TB	= ch_tbdbm

#  Specify one of the nameserver modules or the fake module
#  if you do not intend to support nameservers (4.2, fake)
TB_NS	= fake

#  Specify tai_???.o and lk_lock???.o
LOCALUTIL = tai_file.o lk_lock.o


default:	real-default

#
#  special case dependencies
#
../../h/mmdf.h:	../../h/conf.h
	-touch ../../h/mmdf.h
.PRECIOUS: ../../h/mmdf.h

#
#  #include dependencies
#
#  Two versions are supplied.  One for sites with cc -M (4.3BSD)
#  and one for those that don't have it.  Comment out the one
#  you do not want.

#  This one is for sites without cc -M
depend:
	cat </dev/null >x.c
	for i in $(MODULES); do \
		(echo $$i.o: $$i.c >>makedep; \
		/bin/grep '^#[ 	]*include' x.c $$i.c | sed \
			-e 's,c:[^"]*"\./\([^"]*\)".*,o: \1,' \
			-e 's,c:[^"]*"/\([^"]*\)".*,o: /\1,' \
			-e 's,c:[^"]*"\([^"]*\)".*,o: ../../h/\1,' \
			-e 's,c:[^<]*<\(.*\)>.*,o: /usr/include/\1,' \
			>>makedep); done
	echo '/^# DO NOT DELETE THIS LINE/+2,$$d' >eddep
	echo '$$r makedep' >>eddep
	echo 'w' >>eddep
	cp Makefile.real Makefile.bak
	ed - Makefile.real < eddep
	rm eddep makedep x.c
	echo '# DEPENDENCIES MUST END AT END OF FILE' >> Makefile.real
	echo '# IF YOU PUT STUFF HERE IT WILL GO AWAY' >> Makefile.real
	echo '# see make depend above' >> Makefile.real

#  This one id for sites with cc -M
#depend:
#	( for i in ${MODULES} ; do \
#		${CC} -M ${CFLAGS} $$i.c ; done ) | \
#	awk ' { if ($$1 != prev) { print rec; rec = $$0; prev = $$1; } \
#		else { if (length(rec $$2) > 78) { print rec; rec = $$0; } \
#		       else rec = rec " " $$2 } } \
#	      END { print rec } ' > makedep
#	echo '/^# DO NOT DELETE THIS LINE/+2,$$d' >eddep
#	echo '$$r makedep' >>eddep
#	echo 'w' >>eddep
#	cp Makefile.real Makefile.bak
#	ed - Makefile.real < eddep
#	rm eddep makedep
#	echo '# DEPENDENCIES MUST END AT END OF FILE' >> Makefile.real
#	echo '# IF YOU PUT STUFF HERE IT WILL GO AWAY' >> Makefile.real
#	echo '# see make depend above' >> Makefile.real


char	*pn_quedir   = "/usr/spool/jntmail";
				/* Location of JNTmail queues for NIFTP */

/******************* MULTIPLE HOST TAILORING **************************
 *
 *  This variable is initialised to be the name of the local machine
 *  It is used to transparently forward between the UCL machinmmdf/conf/vu44/Makefile.lib   444      0     12        2224  3652767305  10722 #
#	Makefile for the MMDF library   (libmmdf.a)
#

#  The paths below are relative from the directories below this one.
#
#  The RANLIB define should be "sh libfix" for systems that don't have
#	the ranlib command.  Systems that do have ranlib should set
#	RANLIB to "ranlib".  "libfix" is a Bourne shell file that will
#	use lorder and tsort to reorder the library.
#
MAKE	= make
RANLIB	= libfix

SUBDIRS = addr mmdf table util
ALLSUBDIRS = addr mmdf table util

default: remake reorder

remake:
	for dir in $(SUBDIRS); \
	  do (cd $${dir}; echo "Running make in $${dir}"; \
	      gen ${MFLAGS} -${MAKEFLAGS}); \
	  done

install: default
	@echo "Library is up to date, nothing else to do."

reorder: reorder-done

reorder-done: libmmdf.a
	-$(RANLIB) libmmdf.a
	-touch reorder-done

depend:
	for dir in $(SUBDIRS); \
	  do (cd $${dir}; gen ${MFLAGS} -${MAKEFLAGS} depend); done

lint:
	for dir in $(SUBDIRS); \
	  do (cd $${dir}; gen ${MFLAGS} -${MAKEFLAGS} lint); done
	cat */llib-l*.ln > llib-lmmdf.ln

clean:
	-rm -f *-made libmmdf.a make.out core llib-lmmdf.ln reorder-done
	for dir in $(SUBDIRS); \
	  do (cd $${dir}; $(MAKE) -f Makefile.real clean); done

ALWAYS:
up2()
#  Define -Drindex=strrchr -Dindex=strchr if you are on a SYS5 system
#  Define NAMESERVER if you want the nameserver lookup code
#  Define UUCPLOCK if you want UUCP/tip/cu-style locking for dial-out lines
#  Define NODOMLIT if you don't want literals like [10.0.0.52] in messages
#
CONFIGDEFS	= -DSYS5 -DNODIAL -DNODUP2 -Drindex=strrchr -Dindex=strchr -DNODmmdf/conf/vu44/Makefile.src   444      0     12        2325  3657737671  10755 #	MMDF Program Source Subtree Makefile
#

#
# Programs that live in subdirectories, and have makefiles of their own.
#
SUBDIR=	tools submit deliver local list uucp # prog
# delay smtp phone pobox \
	

#
#  This should list all the subdirectories that make clean works in.
#  This probably should not be altered unless you add/delete a channel.
#
ALLSUBDIRS=	tools submit deliver local list uucp
#  delay smtp phone pobox \
#  niftp bboards pop ean prog

all:
	for dir in ${SUBDIR}; do \
		(cd $${dir}; gen ${MFLAGS} -${MAKEFLAGS}); done

install:
	for dir in ${SUBDIR}; do \
		(cd $${dir}; gen ${MFLAGS} -${MAKEFLAGS} install); done

dist:
	-touch `ls */x*|grep -v '\.'`
	echo echo MMDF Distribution made `date` > install.sh
	for dir in ${SUBDIR}; do \
		echo cd $${dir} >> install.sh; \
		( cd $${dir}; \
		  gen -n ${MFLAGS} -${MAKEFLAGS} install) >> install.sh; \
		echo cd .. >> install.sh; \
	done
	tar cvf mmdfdist.tar install.sh */x*

depend:
	for dir in ${SUBDIR}; do \
		(cd $${dir}; gen ${MFLAGS} -${MAKEFLAGS} depend); done

lint:
	for dir in ${SUBDIR}; do \
		(cd $${dir}; gen ${MFLAGS} -${MAKEFLAGS} lint); done

clean:
	-rm -f make.out core
	for dir in ${ALLSUBDIRS}; do \
		(cd $${dir}; make -f Makefile.real clean); done
YS5 system
#  Define NAMESERVER if you want the nameserver lookup code
#  Define UUCPLOCK if you want UUCP/tip/cu-style locking for dial-out lines
#  Define NODOMLIT if you don't want literals like [10.0.0.52] in messages
#
CONFIGDEFS	= -DSYS5 -DNODIAL -DNODUP2 -Drindex=strrchr -Dindex=strchr -DNODmmdf/conf/vu44/chan.c   444      0     12        6062  3620510334   7555 #include "util.h"
#include "mmdf.h"
#include "ch.h"
#include "dm.h"

char    *ch_dflnam = "local";

Alias	*al_list = (Alias *)0;

/*******************  MAIL IDENTIFIER  SELECTION  ***********************/

/*
 *	The global mid_enable controls the use of an alternate
 *	mail name system refered to as Mail-Ids.  When mail-ids are
 *	in use, login-ids and mail-ids are nolong bound to one another
 *	by the system account database, and are bound only by mapping
 *	tables.  The tables "mailids" and "users" will always be present
 *	though they may not be used.  "mailids" maps a mailid to a
 *	system account.  The table "users" maps a username to a mailid.
 */

int	mid_enable = 0;

Table
	tb_mailids =		/* Used in getpwmid() */
{
    "mailids", "Mail-ID to Username Mappings", "mailids", 0, 0l
},
	tb_users =		/* Used in getmailid() */
{
    "users", "Username to Mail-ID Mappings", "users", 0, 0l
};


/*
 *  The following set of structures provides a complete list of channels
 *  known to MMDF.
 *
 *  It is followed by two arrays that list the channels in useful orders.
 *
 *  ch_tbsrch[] is a full list, of all known channels, and is primarily
 *  used to guide the ch_table routines when treating the hostname space as
 *  flat (linear).  It also is used by lnk_submit, for sorting the address
 *  linked-list.
 *
 *  THE ORDER IS CRITICAL.  A hostname may appear in several tables, but
 *  the first table checked will cause the hit.
 *
 *  One nice aspect of this is that, for example, you can have a standard
 *  arpanet host table, but have some of its hosts actually get mail on a
 *  different channel.  Simply place that table's entry earlier in the
 *  ch_tbsrch[] list.
 *
 *  ch_exsrch[] is a list of channels that are processed by Deliver
 *  (usually just when it does standard, default daemon processing).
 *  Therefore, it need contain only those channels that are active and are
 *  to be processed by the daemon.  Again, order is important.  Channels
 *  are processed in the order listed.  This means that, typically, you
 *  want ch_sloc to be first, unless you have the strange view that foreign
 *  mail is more important than local...
 */

LOCVAR Chan *chsrch[NUMCHANS+1] = {	/* Order chan tables searched */
	(Chan *) 0
};
Chan **ch_tbsrch = chsrch;

LOCVAR Chan *exsrch[NUMCHANS+1] = {	/* Order of active chan execution */
	(Chan *) 0
};
Chan **ch_exsrch = exsrch;
Chan * ch_ptrlist[NUMCHANS + 1];  /* can hold pointers to chans */

LOCVAR Table *tblist[NUMTABLES+1] = {	/* All known tables */
    (Table *) 0
};
Table **tb_list = tblist;

LOCVAR Domain *dmlist[NUMDOMAINS+1];
Domain **dm_list = dmlist;         /* all known domains */

int     tb_maxtables = NUMTABLES; /* max number of slots */
int     ch_maxchans = NUMCHANS;   /* max number of slots */
int     dm_maxtables = NUMDOMAINS; /* max number of slots */

/* These next entries should reflect the ACTUAL number of slots in use  */
int     tb_numtables = 0;         /* actual number of tables */
int     ch_numchans = 0;          /* actual number of channels */
int     dm_numtables = 0;         /* actual number of domains */
ne id for sites with cc -M
#depend:
#	( for i in ${MODULES} ; do \
#		${CC} -M ${CFLAGS} $$i.c ; done ) | \
#	awk ' { if ($$1 != prev) { print rec; rec = $$0; prev = $$1; } \
#		else { if (length(rec $$2) > 78) { print rec; rec = $$0; } \
#		       else rec = rec " " $$2 } } \
#	      END { print rec } ' > makedep
#	echo '/^# DO NOT DELETE THIS LINE/+2,$$d' >eddep
#	echo '$$r makedep' >>eddep
#	echo 'w' >>eddep
#	cp Makefile.real Makefile.bak
#	ed - Makefilemmdf/conf/vu44/conf.c   444      0     12       20366  3620510335   7615 #include "util.h"
#include "mmdf.h"
#include "ap_norm.h"

/*            COMPILE-TIME CONFIGURATION FILE WITH SITE-DEPENDENT INFORMATION
 *
 *      This file contains general host-dependent variable info that
 *   the message system needs to have.
 */

char *mmtailor = "/usr/mmdf/mmdftailor";
				/* location of external tailoring file  */

/* ************************  PUBLIC NAMES  ****************************** */

char
	*locname = "vu44",  /* Generic name for local host   */
	*locdomain = "UUCP",	/* Name of domain that is local to us */
	*sitesignature = "Mail System (MMDF)",
				/* in signature field of return-mail  */
	*mmdflogin = "mmdf",	/* login name for mmdf         */
	*supportaddr = "Postmaster@vu44.UUCP";
				/* where to send bug reports, etc. */
/* *****************  DEFAULT BASE DIRECTORIES  *********************** */

/*  The following pathnames define the default working directories for
 *  MMDF activities.  Specific files usually will be referenced relative
 *  to these directories.  To override the default, the specific filename
 *  should be specified as an anchored pathname, beginning with a slash.
 *
 *  The following pathnames must be anchored.
 */

char	*cmddfldir = "/usr/mmdf";
			      /* contains MMDF commands, such as      */
			      /* submit, deliver, and queclean        */
char	*logdfldir = "/usr/mmdf/log";
			      /* contains highly volatile files, such */
			      /* as logs and check-point markers      */
char	*phsdfldir = "/usr/mmdf/log/phase";
			      /* contains timestamp files             */
char	*tbldfldir = "/usr/mmdf/table";
			      /* contains sticky files, such as       */
			      /* name tables & dialing scripts        */
char	*tbldbm = "/usr/mmdf/table/mmdfdbm";
			      /* dbm() hash of name tables      */
char	*quedfldir = "/usr/mmdf/lock/home";
			      /* contains queued mail files, in       */
			      /* subordinate directories              */
			      /* (also see below)                     */
char	*chndfldir = "/usr/mmdf/chans";
				/* contains the channel programs        */
				/* (ch_*) called by deliver             */
char	*lckdfldir = "/tmp/mmdf";
				/* Directory for lock files SEK if needed */


/* ***************  DEFAULT LOG LOCATIONS & SETTINGS  ***************** */

char    mlogloc[] = "msg.log";
char    chlogloc[] = "chan.log";

/*  It is common for specific using processes to tailor the second
 *  field, which is the log entry label, so that you can tell which
 *  incarnation of the process made a set of entries.
 *
 *  These structures conform to ll_log.h
 */

struct ll_struct
		    msglog =      /* deliver, submit, & queclean          */
{
    mlogloc, "XX-0000", LLOGFST, 40, LLOGSOME, 0
},

		   chanlog =      /* log for channels & user agents       */
{
    chlogloc, "UA-0000", LLOGFST, 40, LLOGSOME, 0
};

/* Needed for user authorisation logging        */

struct ll_struct authlog =
{
    "auth.log", "AU-0000", LLOGFTR, 40, LLOGSOME, 0
};

/* ****************  MESSAGE QUEUE SUBSTRUCTURE  ********************** */

/*  The queue has a top-level, locking directory, which only trusted
 *  processes can get through.  Under that is a base working directory.
 *  Below that are three directories which contain queued messages data
 *
 *  quedfldir[] specifies the top-level and base directories.
 */

int     queprot = 0700;           /* protection on quedfldir parent      */

/* the next five are relative to quedfldir[] */

char
	*deadletter = "DeadLetters",
				/* Actually NOT a directory, where	*/
				/* unreturned letters go to die.	*/
	*tquedir = "tmp/",	/* sub-directory for builing            */
				/* address list file for msg            */
	*aquedir = "addr/",	/* sub-dir with files of address        */
				/* lists for queued mail; files are     */
				/* linked into here from tquedir        */
				/* as last act of queuing msg           */
	*squepref = "q.",	/* preface sub-queue name with this	*/
	*mquedir = "msg/";	/* sub-dir containing text of           */
				/* queued messages; file names          */
				/* are same as in aquedir               */

/*  The following two parameters establish thresholds for queue residency
 *  limits.  If a message does not move out of the queue by warntime,
 *  a notice may be sent to the originator.  If it is still in the queue
 *  by failtime, it is removed from the queue and may be returned to the
 *  originator.
 */

int
	warntime = 48,
	failtime = 96;


/* ******************  COMMAND NAMES & LOCATIONS  *********************** */

char
	*namsubmit = "submit",      /* who to submit mail through         */
	*pathsubmit = "submit",     /* location relative to cmddfldir[]   */

	*namdeliver = "deliver",    /* name of delivery overseer          */
	*pathdeliver = "deliver",   /* location relative to cmddfldir[]   */

	*nampkup = "pickup",        /* name of pickup overseer            */
	*pathpkup = "deliver",      /* location relative to cmddfldir[]   */

	*nammail = "mmdfmail",      /* the mmdf simple mail-sender        */
	*pathmail = "v6mail";       /* needs to handle special switches   */
				    /* probably must be MMDF's uip/mail   */

/* *********************  SUBMIT TAILORING  ************************** */

int     maxhops = 40;               /* number of Via fields permitted   */
int	mgt_addid = 1;		    /* if set, add message-id if necessary */
int	lnk_listsize = 12;	    /* if more than this many addresses,
				     * then do not send warning and only
				     * send citation on return
				     */


/*   *********************  DELIVER TAILORING  ************************** */

int	maxqueue = 300;		  /* maximum size of sortable queue       */
int	mailsleep = 600;          /* seconds between queue sweeps by a    */
				  /* background (-b) Deliver              */

/*   *********************  ADDRESS TRANSFORMATION  ********************* */


/*  Format:
 *
 *  Original host name, New hostname, String appended to mailbox
 *
 *      e.g.        "DCrocker at UDel-EE"
 *      maps to     "DCrocker.EE at UDel"
 *
 *  Note the usage, for Darcom.  It is designed to bypass address
 *  mapping, so that "foo at bar" does not go out as "foo.bar at relay"
 */

LOCVAR struct ap_hstab hstab[] =
{
    0, 0, 0,
};
struct ap_hstab *ap_exhstab = hstab;

/* *****************  LOCAL DELIVERY TAILORING  ************************* */

/*  Obviously, the following is only needed if there is local delivery,
 *  rather than pure relaying (a rare, but permitted mode).  Simplest
 *  determinor is ch_okloc in conf_chan.c
 */

int     sentprotect = 0600;         /* protection on mail files             */

char
	*dlvfile = ".maildelivery", /* User specified delivery instructions */
	*sysdlvfile = (char *)0,  /* if non-zero, the default dlvfile */
	*mldfldir = { 0 },	/* directory to contain users' file     */
				/* for receiving local mail.  if this   */
				/* spec is empty, recipient's login     */
				/* directory will be used.              */
	*mldflfil = "mailbox",    /* file to receive new mail             */
				  /* relative to mldfldir[] or to login   */
				  /*   dir if mldfldir[] is empty         */
				  /* if this spec empty, user's login     */
				  /* name will be name of file            */
	*delim1 = "\001\001\001\001\n",
	*delim2 = "\001\001\001\001\n";
				  /* add to begin and end of messages     */
				  /* NOTE:  to avoid author spoofin,      */
				  /* lo_wtmail() catches mail that        */
				  /* has either of these strings in it    */
				  /* and changes the first char           */
				  /* (it increments its ascii value)      */


/* *****************  UUCP CHANNEL TAILORING  *********************** */

char	*Uchan = "uucp";		/* default channel name */
char    *Uuxstr = "uux - -r";		/* command string to start UUX */

/******************* NIFTP CHANNEL TAILORING  *************************/

char	*pn_quedir   = "/usr/spool/jntmail";
				/* Location of JNTmail queues for NIFTP */

/******************* MULTIPLE HOST TAILORING **************************
 *
 *  This variable is initialised to be the name of the local machine
 *  It is used to transparently forward between the UCL machines
 */
char	*locmachine = "laser";


/******************* AUTHORIZATION TAILORING **************************/
char	*authrequest = "Postmaster@vu44.UUCP";
				/* authorisation request address        */
char	*authfile = "/usr/mmdf/warning";
				/* warning letter - full pathname       */

moved from the queue and may be returned to the
 *  originator.
 */

int
	warntime = 48,
	failtime = 96;


/* ******************  COMMAND NAMES & LOCATIONS  *********************** */

char
	*namsubmit = "submit",      /* who to submit mail through         */
	*pathmmdf/conf/vu44/conf.h   444      0     12        2727  3623122360   7603 #
/*          This contains the most site-dependent definitions             */

#define USERSIZE    8		/* max length of a user system name   */
#define	MAILIDSIZ  64		/* max length of a mailid,
				 * must be at least USERSIZE.		*/
#define MSGNSIZE   14           /* max length of a message file name  */
#define FILNSIZE   64           /* max length of full file name       */
#define ADDRSIZE  256		/* max length of an address           */
#define LINESIZE  256		/* max length of a line/record        */

#define	NUMCHANS   20		/* max number of channels */
#define NUMTABLES  30		/* max number of tables */
#define NUMDOMAINS 20		/* max number of domains */

/************  DIAL IN/OUT (CH_PHONE / SLAVE) TAILORING  *****************/

#define DL_TRIES    2             /* number of re-connects OK for phone */
#define DL_RCVSZ    255           /* longest line receivable            */
#define DL_XMTSZ    255           /* longest line sendable              */

/****************  JNTMAIL Tailoring *******************/

#undef	JNTMAIL
#undef	BOTHEND
#undef	VIATRACE

/**************** Include File Tailoring ****************************/

#include <time.h>
#include <ndir.h>

/********************* Privileged UIDs *************************/

/*
 *  This can be a macro as give here or you can provide a function.
 *  It should always authorize root and mmdf.
 *  The global variable effecid is assumed to have the MMDF uid in it.
 */

#define	PRIV_USER(x) ((x)==0 || (x)==1 || (x)==effecid)
l switches   */
				    /* probably must mmdf/conf/vu44/conf_dial.c   444      0     12        6721  3622765666  10612 #include "util.h"
#include "ll_log.h"
#include "d_proto.h"
#include "d_structs.h"

/* **********************  DIALING TAILOR INFO  **********************  */

/*  modem speed support definitions  */

# define  BELL103        0177     /*  bell 103  0 to 300 baud  */
# define  VA825          0177     /*  vadic 825  0 to 300 baud  */
# define  VA3415         0400     /*  vadic 3415  1200 baud  */

/*
 *     structure defining the available dial-out ports.
 */

#define NUMPRTS         20
LOCVAR struct dialports  theprts[NUMPRTS] =
  {
  0,             0,                0,           0,    0, 0,      0,   0
  };
struct dialports  *d_prts = theprts;
int d_maxprts = NUMPRTS;
int d_numprts = 0;

#define NUMLINES        10
LOCVAR struct directlines  thelines[NUMLINES] =
  {
  0,              0,             0,                 0, 0
  };

int d_maxlines = NUMLINES;
int d_numlines = 0;
struct directlines  *d_lines = thelines;


/*
 *     this is the path name of the file on which outgoing calls are
 *     logged.  it should be owned by the same user as the outgoing
 *     ports and autodialer devices.
 */

char  *d_calllog  =  "/usr/mmdf/log/dial_log";

/*
 *	this is the name of the programs will actually do dialing
 *	it is called as "dial <line> <number>".  It knows which
 *	acus are attached to which lines.
 */

char	*d_dialprog = "/usr/bin/dial";

/* ***************  ILLEGAL CHARACTER TAILORING  ************************* */

/*  The following sets the default illegal-character arrays for both
    dial-out (phone) and dial-in (slave) connections.
 */


unsigned short d_lrill[8] =       /* illegal receive chars              */
{				  /* see d_slvconn & d_masconn          */
    0177777,                      /* nul soh stx etx eot enq ack bel    */
				  /* bs  ht  nl  vt  np  cr  so  si     */
    0177777,                      /* dle dc1 dc2 dc3 dc4 nak syn etb    */
				  /* can em  sub esc fs  gs  rs  us     */
    0000010,                      /* sp  ! " # $ % & ' ( ) * + , - . /  */
    0000000,                      /* 0 1 2 3 4 5 6 7 8 9 : ; < = > ?    */
    0000001,                      /* @ A B C D E F G H I J K L M N O    */
    0010000,                      /* P Q R S T U V W X Y Z [ \\] ^ _    */
    0000000,                      /* ` a b c d e f g h i j k l m n o    */
    0100000                       /* p q r s t u v w x y z { | } ~ del  */
};

unsigned short d_lxill[8] =       /* illegal send chars                 */
{				  /* see d_slvconn & d_masconn          */
    0177777,                      /* nul soh stx etx eot enq ack bel    */
				  /* bs  ht  nl  vt  np  cr  so  si     */
    0177777,                      /* dle dc1 dc2 dc3 dc4 nak syn etb    */
				  /* can em  sub esc fs  gs  rs  us     */
    0000010,                      /* sp  ! " # $ % & ' ( ) * + , - . /  */
    0000000,                      /* 0 1 2 3 4 5 6 7 8 9 : ; < = > ?    */
    0000001,                      /* @ A B C D E F G H I J K L M N O    */
    0010000,                      /* P Q R S T U V W X Y Z [ \\] ^ _    */
    0000000,                      /* ` a b c d e f g h i j k l m n o    */
    0100000                       /* p q r s t u v w x y z { | } ~ del  */
};

/************************** PHONE LOGGING **************************/

struct ll_struct    ph_log =       /* dial-out link-level package log    */
{
    "ph.log", "PH-0000", LLOGFST, 25, LLOGSOME, 0
};

char *def_trn = "ph.trn";          /* dial-out link-level i/o transcript */
				  /* does not use ll_log package        */
  */

char
	*dlvfile = ".maildelivery", /* Usermmdf/conf/vu44/msgtailor.c   444      0     12        3116  3671073313  10651 /*
 *	M S G T A I L O R . C
 */

#define NOEXTERNS
#include "./msg.h"

#ifdef pdp11
#	define NMSGS	500
#else
#	define NMSGS	200
#endif pdp11

char    *savmsgfn = "mbox";
char	*resendprog = "|/usr/local/resend ";
char	*sndname = "/usr/local/send"; /* Overridden by .msgrc sendprog */

char	*dflshell = "sh";	/* Overridden by getenv("SHELL"); */
char	*dfleditor = "vi";	/* Overridden by getenv("EDITOR"); */

int	pagesize = 22;		/* default line count */
int	linelength = 80;	/* default line size */

int	paging = OFF;		/* Internal paging of long messages */
int	verbose = ON;		/* Command completion (raw mode) */
int	wmsgflag = OFF;		/* Save messages when calling send */
int	keystrip = ON;		/* Strip header lines on display */
int	bdots = ON;		/* Binary box dots */
int	mdots = ON;		/* Mailbox dots */
int	bprint = ON;		/* Reading... messages */
int	quicknflag = OFF;	/* Turn off NEW before message prints */
int	quickexit = ON;		/* Don't stay in msg if mailbox is empty */
int	prettylist = ON;	/* Print message numbers in listings */
int	filoutflag = ON;	/* Filter text (display controls as ^X) */
int	binsavflag = ON;	/* Save binary box on exec's & pauses */
int	doctrlel = OFF;		/* Page on control-L */
#ifndef SYS5
long	timezone = (-1)*60*60;	/* Seconds West of GMT */
int	daylight = 1;		/* Do you use daylight savings time? */
				/* 0 = NO, 1 = YES */
char	*tzname[] = {"+0100", "+0200",};
				/* Timezone strings, primary and daylight
				   savings time if used, in that order */
#endif SYS5

/***** Do not tailor below this line *****/
int	Nmsgs = NMSGS;
struct message *msgp[NMSGS];
struct message *mptr;
receive chars              */
{				  /* see d_slvconn & d_masconn          */
    0177777,                      /* nul soh stx etx eot enq ack bel    */
				  /* bs  ht  nl  vt  np  cr  so  si     */
    0177777,                      /* dle dc1 dc2 dc3 dc4 nak syn etb    */
				  /* can em  sub esc fs  gs  rs  us     */
    0000010,                      /* sp  ! " # $ % & ' ( ) * + , - . /  */
    0000000,                      /* mmdf/conf/vu44/s_tailor.c   444      0     12         332  3620510335  10433 /*
 *	S _ T A I L O R . C
 *
 *  Contains strings used to tailor SEND to a particular
 *  site.  Also check s.h for tailorable defines.
 */

char	*dfleditor = "ex";
char	*dflveditor = "vi";
char	*dflchecker = "spell";
bdots = ON;		/* Binary box dots */
int	mdots = ON;		/* Mailbox dots */
int	bprint = ON;		/* Reading... messages */
int	quicknflag = OFF;	/* Turn off NEW before message prints */
int	quickexit = ON;		/* Don't stay in msg if mailbox is empty */
int	prettylist = ON;	/* Print message numbers in limmdf/conf/vu44/s_tailor.h   444      0     12         471  3620510336  10445 /*
 *	Site Tunable Parameters
 */
#undef	SENTFILE yes	/* Define to have copies of mail sent put in ,sent */
#undef	FNCYPRNT yes	/* Define for fancy listing of message (paginated) */
#define	PWNAME yes	/* Define to get real name from /etc/passwd and to */
			/* treat 0 length signatures to get login name only */
ding... messages */
int	quicknflag = OFF;	/* Turn off NEW before message prints */
int	quickexit = ON;		/* Don't stay in msg if mailbox is empty */
int	prettylist = ON;	/* Print message numbers in limmdf/conf/sitesetup   755      0     12        1350  3620510333   7645 : This shellfile must be run with a Bourne shell derivative, not csh.
if test $# != 1
then
	echo "Usage: $0 host"
	exit 1
fi

echo Removing old config files and linking in new ones
set -x
rm -f ../h/conf.h
rm -f ../Makefile.com ../lib/Makefile ../src/Makefile
rm -f ../uip/msg/msgtailor.c
rm -f ../uip/send/s_tailor.h ../uip/send/s_tailor.c
rm -f ../lib/mmdf/conf.o ../lib/mmdf/chan.o
if ln $1/conf.h ../h/conf.h
then
	touch ../h/conf.h
	ln $1/Makefile.com ../Makefile.com
	ln $1/Makefile.lib ../lib/Makefile
	ln $1/Makefile.src ../src/Makefile
	ln $1/msgtailor.c ../uip/msg/msgtailor.c
	ln $1/s_tailor.h ../uip/send/s_tailor.h
	ln $1/s_tailor.c ../uip/send/s_tailor.c
	exit 0
fi
echo '*** Please check that you supplied a valid host.'
exit 99
 West of GMT */
int	daylight = 1;		/* Do you use daylight savings time? */
				/* 0 = NO, 1 = YES */
char	*tzname[] = {"+0100", "+0200",};
				/* Timezone strings, primary and daylight
				   savings time if used, in that order */
#endif SYS5

/***** Do not tailor below this linemmdf/conf/bbn/   755      0     12           0  3671074636   6370 mmdf/conf/bbn/Makefile.com   444      0     12        7162  3635657314  10676 #
#	Common Sub-Makefile for the MMDF System
#
HOST		= bbn
SYSTEM		= 4.2
MMPREF		=
LIBDIR		= /usr/mmdf/lib
CHANDIR		= /usr/mmdf/chans
TBLDIR		= /usr/mmdf/table
BINDIR		= /usr/local/mmdf
RCVDIR		= /usr/local/mmdf

#
#  Defines used during installation
#
CHOWN		= /etc/chown
MMDFLOGIN	= mmdf
ROOTLOGIN	= root
PGMPROT 	= 775

#
#  The paths below are relative from the directories below this one.
#  Define DOASSIGN to CONFIGDEFS if you want the d_assign code
#  Define SECURETTY if you run with terminals write protected (mode 711)
#  Define DEBUG to include extensive calls to the logging package
#  Define D_LOG if you want phone channel logging
#  Define D_DBGLOG if you want phone channel debugging output
#  Define RUNALONE for standalone testing (seldom used)
#  Define V4_2BSD if you are compiling for 4.2BSD
#  Define NODIAL if you do not want the dial package (no phone channel)
#  Define SYS5 if you have a SystemV Unix or something similar (Sys3)
#  Define ALTOS if you have an Altos, does some depraved things.
#  Define NODUP2 if you don't a dup2() syscall or subroutine.
#  Define NOFCNTL and NODUP2 if you don't have fcntl(x, F_DUPFD) or dup2()
#  Define -Drindex=strrchr -Dindex=strchr if you are on a SYS5 system
#  Define NAMESERVER if you want the nameserver lookup code
#  Define UUCPLOCK if you want UUCP/tip/cu-style locking for dial-out lines
#
CONFIGDEFS	= -DDEBUG=1 -DV4_2BSD -DNAMESERVER -DD_LOG -DD_DBGLOG -DUUCPLOCK
CFLAGS		= -g -I../../h $(CONFIGDEFS)
LDFLAGS 	= 
MMDFLIBS 	= ../../lib/libmmdf.a
SYSLIBS		= -ldbm
LINT		= lint
LFLAGS		= -phbvxacL -I../../h $(CONFIGDEFS)
LLIBS		= ../../lib/llib-lmmdf.ln
AR		= ar


#  Specify either ch_tbdbm (for DBM based tables) or ch_tbseq for
#  sequential IO based tables.
CH_TB	= ch_tbdbm

#  Specify one of the nameserver modules or the fake module
#  if you do not intend to support nameservers (4.2, fake)
TB_NS	= 4.2

#  Specify tai_???.o and lk_lock???.o
LOCALUTIL = tai_file.o lk_lock.4.2.o


default:	real-default

#
#  special case dependencies
#
../../h/mmdf.h:	../../h/conf.h
	-touch ../../h/mmdf.h
.PRECIOUS: ../../h/mmdf.h

#
#  #include dependencies
#
#  Two versions are supplied.  One for sites with cc -M (4.3BSD)
#  and one for those that don't have it.  Comment out the one
#  you do not want.

#  This one is for sites without cc -M
depend:
	cat </dev/null >x.c
	for i in $(MODULES); do \
		(echo $$i.o: $$i.c >>makedep; \
		/bin/grep '^#[ 	]*include' x.c $$i.c | sed \
			-e 's,c:[^"]*"\./\([^"]*\)".*,o: \1,' \
			-e 's,c:[^"]*"/\([^"]*\)".*,o: /\1,' \
			-e 's,c:[^"]*"\([^"]*\)".*,o: ../../h/\1,' \
			-e 's,c:[^<]*<\(.*\)>.*,o: /usr/include/\1,' \
			>>makedep); done
	echo '/^# DO NOT DELETE THIS LINE/+2,$$d' >eddep
	echo '$$r makedep' >>eddep
	echo 'w' >>eddep
	cp Makefile.real Makefile.bak
	ed - Makefile.real < eddep
	rm eddep makedep x.c
	echo '# DEPENDENCIES MUST END AT END OF FILE' >> Makefile.real
	echo '# IF YOU PUT STUFF HERE IT WILL GO AWAY' >> Makefile.real
	echo '# see make depend above' >> Makefile.real

#  This one id for sites with cc -M
#depend:
#	( for i in ${MODULES} ; do \
#		${CC} -M ${CFLAGS} $$i.c ; done ) | \
#	awk ' { if ($$1 != prev) { print rec; rec = $$0; prev = $$1; } \
#		else { if (length(rec $$2) > 78) { print rec; rec = $$0; } \
#		       else rec = rec " " $$2 } } \
#	      END { print rec } ' > makedep
#	echo '/^# DO NOT DELETE THIS LINE/+2,$$d' >eddep
#	echo '$$r makedep' >>eddep
#	echo 'w' >>eddep
#	cp Makefile.real Makefile.bak
#	ed - Makefile.real < eddep
#	rm eddep makedep
#	echo '# DEPENDENCIES MUST END AT END OF FILE' >> Makefile.real
#	echo '# IF YOU PUT STUFF HERE IT WILL GO AWAY' >> Makefile.real
#	echo '# see make depend above' >> Makefile.real
 either ch_tbdbm (for DBM based tables) or ch_tbseq for
#  sequential IO based tables.
CH_TB	= ch_tbdbm

#  Specify one of the nameserver modules or the fake module
#  if you do not intend to support nameservers (4.2, fake)
TB_NS	= 4.2

#  Specify tai_???.o and lk_lock???.o
LOCALUTIL = tai_file.o lk_lock.4.2.o


default:	real-default

#
#  special case dependencies
#
../../h/mmdf.h:	../../h/confmmdf/conf/bbn/Makefile.lib   444      0     12        2220  3652766031  10650 #
#	Makefile for the MMDF library   (libmmdf.a)
#

#  The paths below are relative from the directories below this one.
#
#  The RANLIB define should be "sh libfix" for systems that don't have
#	the ranlib command.  Systems that do have ranlib should set
#	RANLIB to "ranlib".  "libfix" is a Bourne shell file that will
#	use lorder and tsort to reorder the library.
#
MAKE	= make
RANLIB	= ranlib

SUBDIRS = addr dial mmdf table util
ALLSUBDIRS = addr dial mmdf table util

default: remake reorder

remake:
	for dir in $(SUBDIRS); \
	  do (cd $${dir}; echo "Running make in $${dir}"; \
	      gen ${MFLAGS} -${MAKEFLAGS}); \
	  done

install: default
	@echo "Library is up to date, nothing else to do."

reorder: reorder-done

reorder-done: libmmdf.a
	-$(RANLIB) libmmdf.a
	-touch reorder-done

depend:
	for dir in $(SUBDIRS); \
	  do (cd $${dir}; gen ${MFLAGS} -${MAKEFLAGS} depend); done

lint:
	for dir in $(SUBDIRS); \
	  do (cd $${dir}; gen ${MFLAGS} -${MAKEFLAGS} lint); done
	cat */llib-l*.ln > llib-lmmdf.ln

clean:
	-rm -f *-made libmmdf.a make.out core reorder-done
	for dir in $(SUBDIRS); \
	  do (cd $${dir}; $(MAKE) -f Makefile.real clean); done

ALWAYS:
h(rec $$2) > 78) { print rec; rec = $$0; } \
#		       else rec = rec " " $$2 } } \
#	      END { print rec } ' > makedep
#	echo '/^# DO NOT DELETE THIS LINE/+2,$$d' >eddep
#	echo '$$r makedep' >>eddep
#	echo 'w' >>eddep
#	cp Makefile.real Makefile.bak
#	ed - Makefile.real < eddep
#	rm eddep makedep
#	echo '# DEPENDENCIES MUST END AT END OF FILE' >> Makefile.real
#	mmdf/conf/bbn/Makefile.src   444      0     12        2354  3657737672  10717 #	MMDF Program Source Subtree Makefile
#

#
# Programs that live in subdirectories, and have makefiles of their own.
#
SUBDIR=	tools submit deliver local list delay smtp phone pobox \
	uucp niftp prog bboards pop

#
#  This should list all the subdirectories that make clean works in.
#  This probably should not be altered unless you add/delete a channel.
#
ALLSUBDIRS=	tools submit deliver local list delay smtp phone pobox \
		uucp niftp bboards pop ean prog
# bboards+pop

all:
	for dir in ${SUBDIR}; do \
		(cd $${dir}; gen ${MFLAGS} -${MAKEFLAGS}); done

install:
	for dir in ${SUBDIR}; do \
		(cd $${dir}; gen ${MFLAGS} -${MAKEFLAGS} install); done

dist:
	-touch `ls */x*|grep -v '\.'`
	echo echo MMDF Distribution made `date` > install.sh
	for dir in ${SUBDIR}; do \
		echo cd $${dir} >> install.sh; \
		( cd $${dir}; \
		  gen -n ${MFLAGS} -${MAKEFLAGS} install) >> install.sh; \
		echo cd .. >> install.sh; \
	done
	tar cvf mmdfdist.tar install.sh */x*

depend:
	for dir in ${SUBDIR}; do \
		(cd $${dir}; gen ${MFLAGS} -${MAKEFLAGS} depend); done

lint:
	for dir in ${SUBDIR}; do \
		(cd $${dir}; gen ${MFLAGS} -${MAKEFLAGS} lint); done

clean:
	-rm -f make.out core
	for dir in ${ALLSUBDIRS}; do \
		(cd $${dir}; make -f Makefile.real clean); done
END { print rec } ' > makedep
#	echo '/^# DO NOT DELETE THIS LINE/+2,$$d' >eddep
#	echo '$$r makedep' >>eddep
#	echo 'w' >>eddep
#	cp Makefile.real Makefile.bak
#	ed - Makefile.real < eddep
#	rm eddep makedep
#	echo '# DEPENDENCIES MUST END AT END OF FILE' >> Makefile.real
#	mmdf/conf/bbn/chan.c   444      0     12        6062  3635172556   7534 #include "util.h"
#include "mmdf.h"
#include "ch.h"
#include "dm.h"

char    *ch_dflnam = "local";

Alias	*al_list = (Alias *)0;

/*******************  MAIL IDENTIFIER  SELECTION  ***********************/

/*
 *	The global mid_enable controls the use of an alternate
 *	mail name system refered to as Mail-Ids.  When mail-ids are
 *	in use, login-ids and mail-ids are nolong bound to one another
 *	by the system account database, and are bound only by mapping
 *	tables.  The tables "mailids" and "users" will always be present
 *	though they may not be used.  "mailids" maps a mailid to a
 *	system account.  The table "users" maps a username to a mailid.
 */

int	mid_enable = 0;

Table
	tb_mailids =		/* Used in getpwmid() */
{
    "mailids", "Mail-ID to Username Mappings", "mailids", 0, 0l
},
	tb_users =		/* Used in getmailid() */
{
    "users", "Username to Mail-ID Mappings", "users", 0, 0l
};


/*
 *  The following set of structures provides a complete list of channels
 *  known to MMDF.
 *
 *  It is followed by two arrays that list the channels in useful orders.
 *
 *  ch_tbsrch[] is a full list, of all known channels, and is primarily
 *  used to guide the ch_table routines when treating the hostname space as
 *  flat (linear).  It also is used by lnk_submit, for sorting the address
 *  linked-list.
 *
 *  THE ORDER IS CRITICAL.  A hostname may appear in several tables, but
 *  the first table checked will cause the hit.
 *
 *  One nice aspect of this is that, for example, you can have a standard
 *  arpanet host table, but have some of its hosts actually get mail on a
 *  different channel.  Simply place that table's entry earlier in the
 *  ch_tbsrch[] list.
 *
 *  ch_exsrch[] is a list of channels that are processed by Deliver
 *  (usually just when it does standard, default daemon processing).
 *  Therefore, it need contain only those channels that are active and are
 *  to be processed by the daemon.  Again, order is important.  Channels
 *  are processed in the order listed.  This means that, typically, you
 *  want ch_sloc to be first, unless you have the strange view that foreign
 *  mail is more important than local...
 */

LOCVAR Chan *chsrch[NUMCHANS+1] = {	/* Order chan tables searched */
	(Chan *) 0
};
Chan **ch_tbsrch = chsrch;

LOCVAR Chan *exsrch[NUMCHANS+1] = {	/* Order of active chan execution */
	(Chan *) 0
};
Chan **ch_exsrch = exsrch;
Chan * ch_ptrlist[NUMCHANS + 1];  /* can hold pointers to chans */

LOCVAR Table *tblist[NUMTABLES+1] = {	/* All known tables */
    (Table *) 0
};
Table **tb_list = tblist;

LOCVAR Domain *dmlist[NUMDOMAINS+1];
Domain **dm_list = dmlist;         /* all known domains */

int     tb_maxtables = NUMTABLES; /* max number of slots */
int     ch_maxchans = NUMCHANS;   /* max number of slots */
int     dm_maxtables = NUMDOMAINS; /* max number of slots */

/* These next entries should reflect the ACTUAL number of slots in use  */
int     tb_numtables = 0;         /* actual number of tables */
int     ch_numchans = 0;          /* actual number of channels */
int     dm_numtables = 0;         /* actual number of domains */
Makefile.real
#	echo '# see make depend above' >> Makefile.real
 either ch_tbdbm (for DBM based tables) or ch_tbseq for
#  sequential IO based tables.
CH_TB	= ch_tbdbm

#  Specify one of the nameserver modules or the fake module
#  if you do not intend to support nameservers (4.2, fake)
TB_NS	= 4.2

#  Specify tai_???.o and lk_lock???.o
LOCALUTIL = tai_file.o lk_lock.4.2.o


default:	real-default

#
#  special case dependencies
#
../../h/mmdf.h:	../../h/confmmdf/conf/bbn/conf.c   444      0     12       20366  3635352125   7563 #include "util.h"
#include "mmdf.h"
#include "ap_norm.h"

/*            COMPILE-TIME CONFIGURATION FILE WITH SITE-DEPENDENT INFORMATION
 *
 *      This file contains general host-dependent variable info that
 *   the message system needs to have.
 */

char *mmtailor = "/usr/mmdf/mmdftailor";
				/* location of external tailoring file  */

/* ************************  PUBLIC NAMES  ****************************** */

char
	*locname = "CSNET-DEV",	  /* Generic name for local host          */
	*locdomain = "ARPA",	/* Name of domain that is local to us */
	*sitesignature = "Mail System (MMDF)",
				/* in signature field of return-mail  */
	*mmdflogin = "mmdf",	/* login name for mmdf         */
	*supportaddr = "MMDF@CSNET-DEV.ARPA";
				/* where to send bug reports, etc. */
/* *****************  DEFAULT BASE DIRECTORIES  *********************** */

/*  The following pathnames define the default working directories for
 *  MMDF activities.  Specific files usually will be referenced relative
 *  to these directories.  To override the default, the specific filename
 *  should be specified as an anchored pathname, beginning with a slash.
 *
 *  The following pathnames must be anchored.
 */

char	*cmddfldir = "/usr/mmdf/lib";
			      /* contains MMDF commands, such as      */
			      /* submit, deliver, and queclean        */
char	*logdfldir = "/usr/mmdf/log";
			      /* contains highly volatile files, such */
			      /* as logs and check-point markers      */
char	*phsdfldir = "/usr/mmdf/phase";
			      /* contains timestamp files             */
char	*tbldfldir = "/usr/mmdf/table";
			      /* contains sticky files, such as       */
			      /* name tables & dialing scripts        */
char	*tbldbm = "/usr/mmdf/table/mmdfdbm";
			      /* dbm() hash of name tables      */
char	*quedfldir = "/usr/mmdf/lock/home";
			      /* contains queued mail files, in       */
			      /* subordinate directories              */
			      /* (also see below)                     */
char	*chndfldir = "/usr/mmdf/chans";
				/* contains the channel programs        */
				/* (ch_*) called by deliver             */
char	*lckdfldir = "/tmp";
				/* Directory for lock files SEK           */


/* ***************  DEFAULT LOG LOCATIONS & SETTINGS  ***************** */

char    mlogloc[] = "msg.log";
char    chlogloc[] = "chan.log";

/*  It is common for specific using processes to tailor the second
 *  field, which is the log entry label, so that you can tell which
 *  incarnation of the process made a set of entries.
 *
 *  These structures conform to ll_log.h
 */

struct ll_struct
		    msglog =      /* deliver, submit, & queclean          */
{
    mlogloc, "XX-0000", LLOGFST, 40, LLOGSOME, 0
},

		   chanlog =      /* log for channels & user agents       */
{
    chlogloc, "UA-0000", LLOGFST, 40, LLOGSOME, 0
};

/* Needed for user authorisation logging        */

struct ll_struct authlog =
{
    "auth.log", "AU-0000", LLOGFTR, 40, LLOGSOME, 0
};

/* ****************  MESSAGE QUEUE SUBSTRUCTURE  ********************** */

/*  The queue has a top-level, locking directory, which only trusted
 *  processes can get through.  Under that is a base working directory.
 *  Below that are three directories which contain queued messages data
 *
 *  quedfldir[] specifies the top-level and base directories.
 */

int     queprot = 0700;           /* protection on quedfldir parent      */

/* the next five are relative to quedfldir[] */

char
	*deadletter = "DeadLetters",
				/* Actually NOT a directory, where	*/
				/* unreturned letters go to die.	*/
	*tquedir = "tmp/",	/* sub-directory for builing            */
				/* address list file for msg            */
	*aquedir = "addr/",	/* sub-dir with files of address        */
				/* lists for queued mail; files are     */
				/* linked into here from tquedir        */
				/* as last act of queuing msg           */
	*squepref = "q.",	/* preface sub-queue name with this	*/
	*mquedir = "msg/";	/* sub-dir containing text of           */
				/* queued messages; file names          */
				/* are same as in aquedir               */

/*  The following two parameters establish thresholds for queue residency
 *  limits.  If a message does not move out of the queue by warntime,
 *  a notice may be sent to the originator.  If it is still in the queue
 *  by failtime, it is removed from the queue and may be returned to the
 *  originator.
 */

int
	warntime = 60,
	failtime = 252;


/* ******************  COMMAND NAMES & LOCATIONS  *********************** */

char
	*namsubmit = "submit",      /* who to submit mail through         */
	*pathsubmit = "submit",     /* location relative to cmddfldir[]   */

	*namdeliver = "deliver",    /* name of delivery overseer          */
	*pathdeliver = "deliver",   /* location relative to cmddfldir[]   */

	*nampkup = "pickup",        /* name of pickup overseer            */
	*pathpkup = "deliver",      /* location relative to cmddfldir[]   */

	*nammail = "mmdfmail",      /* the mmdf simple mail-sender        */
	*pathmail = "v6mail";       /* needs to handle special switches   */
				    /* probably must be MMDF's uip/mail   */

/* *********************  SUBMIT TAILORING  ************************** */

int     maxhops = 20;               /* number of Via fields permitted   */
int	mgt_addid = 0;		    /* if set, add message-id if necessary */
int	lnk_listsize = 12;	    /* if more than this many addresses,
				     * then do not send warning and only
				     * send citation on return
				     */


/*   *********************  DELIVER TAILORING  ************************** */

int	maxqueue = 300;		  /* maximum size of sortable queue       */
int	mailsleep = 600;          /* seconds between queue sweeps by a    */
				  /* background (-b) Deliver              */

/*   *********************  ADDRESS TRANSFORMATION  ********************* */


/*  Format:
 *
 *  Original host name, New hostname, String appended to mailbox
 *
 *      e.g.        "DCrocker at UDel-EE"
 *      maps to     "DCrocker.EE at UDel"
 *
 *  Note the usage, for Darcom.  It is designed to bypass address
 *  mapping, so that "foo at bar" does not go out as "foo.bar at relay"
 */

LOCVAR struct ap_hstab hstab[] =
{
    0, 0, 0,
};
struct ap_hstab *ap_exhstab = hstab;

/* *****************  LOCAL DELIVERY TAILORING  ************************* */

/*  Obviously, the following is only needed if there is local delivery,
 *  rather than pure relaying (a rare, but permitted mode).  Simplest
 *  determinor is ch_okloc in conf_chan.c
 */

int     sentprotect = 0600;         /* protection on mail files             */

char
	*dlvfile = ".maildelivery", /* User specified delivery instructions */
	*sysdlvfile = (char *)0,  /* if non-zero, the default dlvfile */
	*mldfldir = { 0 },	/* directory to contain users' file     */
				/* for receiving local mail.  if this   */
				/* spec is empty, recipient's login     */
				/* directory will be used.              */
	*mldflfil = "mailbox",    /* file to receive new mail             */
				  /* relative to mldfldir[] or to login   */
				  /*   dir if mldfldir[] is empty         */
				  /* if this spec empty, user's login     */
				  /* name will be name of file            */
	*delim1 = "\001\001\001\001\n",
	*delim2 = "\001\001\001\001\n";
				  /* add to begin and end of messages     */
				  /* NOTE:  to avoid author spoofin,      */
				  /* lo_wtmail() catches mail that        */
				  /* has either of these strings in it    */
				  /* and changes the first char           */
				  /* (it increments its ascii value)      */


/* *****************  UUCP CHANNEL TAILORING  *********************** */

char	*Uchan = "uucp";		/* default channel name */
char    *Uuxstr = "uux - -r";		/* command string to start UUX */

/******************* NIFTP CHANNEL TAILORING  *************************/

char	*pn_quedir   = "/usr/spool/jntmail";
				/* Location of JNTmail queues for NIFTP */

/******************* MULTIPLE HOST TAILORING **************************
 *
 *  This variable is initialised to be the name of the local machine
 *  It is used to transparently forward between the UCL machines
 */
char	*locmachine = { 0 };


/******************* AUTHORIZATION TAILORING **************************/
char	*authrequest = "mmdf@CSNET-DEV";
				/* authorisation request address        */
char	*authfile = "/usr/mmdf/warning";
				/* warning letter - full pathname       */

t is removed from the queue and may be returned to the
 *  originator.
 */

int
	warntime = 60,
	failtime = 252;


/* ******************  COMMAND NAMES & LOCATIONS  *********************** */

char
	*namsubmit = "submit",      /* who to submit mail through         *mmdf/conf/bbn/conf.h   444      0     12        2736  3635350623   7552 #
/*          This contains the most site-dependent definitions             */

#define USERSIZE    8		/* max length of a user system name   */
#define	MAILIDSIZ  64		/* max length of a mailid,
				 * must be at least USERSIZE.		*/
#define MSGNSIZE   14           /* max length of a message file name  */
#define FILNSIZE   64           /* max length of full file name       */
#define ADDRSIZE  256		/* max length of an address           */
#define LINESIZE  256		/* max length of a line/record        */

#define	NUMCHANS   20		/* max number of channels */
#define NUMTABLES  30		/* max number of tables */
#define NUMDOMAINS 20		/* max number of domains */

/************  DIAL IN/OUT (CH_PHONE / SLAVE) TAILORING  *****************/

#define DL_TRIES    2             /* number of re-connects OK for phone */
#define DL_RCVSZ    255           /* longest line receivable            */
#define DL_XMTSZ    255           /* longest line sendable              */

/****************  JNTMAIL Tailoring *******************/

#undef	JNTMAIL
#undef	BOTHEND
#undef	VIATRACE

/**************** Include File Tailoring ****************************/

#include <sys/time.h>
#include <sys/dir.h>

/********************* Privileged UIDs *************************/

/*
 *  This can be a macro as give here or you can provide a function.
 *  It should always authorize root and mmdf.
 *  The global variable effecid is assumed to have the MMDF uid in it.
 */

#define	PRIV_USER(x) ((x)==0 || (x)==1 || (x)==effecid)

char
	*dlvfile = ".maildelivery",mmdf/conf/bbn/conf_dial.c   444      0     12        6717  3635321167  10542 #include "util.h"
#include "ll_log.h"
#include "d_proto.h"
#include "d_structs.h"

/* **********************  DIALING TAILOR INFO  **********************  */

/*  modem speed support definitions  */

# define  BELL103        0177     /*  bell 103  0 to 300 baud  */
# define  VA825          0177     /*  vadic 825  0 to 300 baud  */
# define  VA3415         0400     /*  vadic 3415  1200 baud  */

/*
 *     structure defining the available dial-out ports.
 */

#define NUMPRTS         20
LOCVAR struct dialports  theprts[NUMPRTS] =
  {
  0,             0,                0,           0,    0, 0,      0,   0
  };
struct dialports  *d_prts = theprts;
int d_maxprts = NUMPRTS;
int d_numprts = 0;

#define NUMLINES        10
LOCVAR struct directlines  thelines[NUMLINES] =
  {
  0,              0,             0,                 0, 0
  };

int d_maxlines = NUMLINES;
int d_numlines = 0;
struct directlines  *d_lines = thelines;


/*
 *     this is the path name of the file on which outgoing calls are
 *     logged.  it should be owned by the same user as the outgoing
 *     ports and autodialer devices.
 */

char  *d_calllog  =  "/usr/mmdf/log/dial_log";

/*
 *	this is the name of the programs will actually do dialing
 *	it is called as "dial <line> <number>".  It knows which
 *	acus are attached to which lines.
 */

char	*d_dialprog = "/usr/bin/dial";

/* ***************  ILLEGAL CHARACTER TAILORING  ************************* */

/*  The following sets the default illegal-character arrays for both
    dial-out (phone) and dial-in (slave) connections.
 */


unsigned short d_lrill[8] =       /* illegal receive chars              */
{				  /* see d_slvconn & d_masconn          */
    0177777,                      /* nul soh stx etx eot enq ack bel    */
				  /* bs  ht  nl  vt  np  cr  so  si     */
    0177777,                      /* dle dc1 dc2 dc3 dc4 nak syn etb    */
				  /* can em  sub esc fs  gs  rs  us     */
    0000000,                      /* sp  ! " # $ % & ' ( ) * + , - . /  */
    0000000,                      /* 0 1 2 3 4 5 6 7 8 9 : ; < = > ?    */
    0000000,                      /* @ A B C D E F G H I J K L M N O    */
    0000000,                      /* P Q R S T U V W X Y Z [ \\] ^ _    */
    0000000,                      /* ` a b c d e f g h i j k l m n o    */
    0100000                       /* p q r s t u v w x y z { | } ~ del  */
};

unsigned short d_lxill[8] =       /* illegal send chars                 */
{				  /* see d_slvconn & d_masconn          */
    0177777,                      /* nul soh stx etx eot enq ack bel    */
				  /* bs  ht  nl  vt  np  cr  so  si     */
    0177777,                      /* dle dc1 dc2 dc3 dc4 nak syn etb    */
				  /* can em  sub esc fs  gs  rs  us     */
    0000010,                      /* sp  ! " # $ % & ' ( ) * + , - . /  */
    0000000,                      /* 0 1 2 3 4 5 6 7 8 9 : ; < = > ?    */
    0000001,                      /* @ A B C D E F G H I J K L M N O    */
    0010000,                      /* P Q R S T U V W X Y Z [ \\] ^ _    */
    0000000,                      /* ` a b c d e f g h i j k l m n o    */
    0100000                       /* p q r s t u v w x y z { | } ~ del  */
};

/************************* PHONE LOGGING *************************/

struct ll_struct    ph_log =       /* dial-out link-level package log    */
{
    "ph.log", "PH-0000", LLOGFST, 25, LLOGSOME, 0
};

char *def_trn = "ph.trn";          /* dial-out link-level i/o transcript */
				  /* does not use ll_log package        */
which outgoing calls are
 *     logged.  it shoulmmdf/conf/bbn/msgtailor.c   444      0     12        3042  3671073320  10604 /*
 *	M S G T A I L O R . C
 */

#define NOEXTERNS
#include "./msg.h"

#ifdef pdp11
#	define NMSGS	500
#else
#	define NMSGS	2000
#endif pdp11

char    *savmsgfn = "savebox";
char	*resendprog = "|/usr/local/mmdf/resend ";
char	*sndname = "/usr/local/mmdf/send"; /* Overridden by .msgrc sendprog */

char	*dflshell = "sh";	/* Overridden by getenv("SHELL"); */
char	*dfleditor = "pen";    /* Overridden by getenv("EDITOR"); */

int	pagesize = 22;		/* default line count */
int	linelength = 80;	/* default line size */

int	paging = OFF;		/* Internal paging of long messages */
int	verbose = OFF;		/* Command completion (raw mode) */
int	keystrip = ON;		/* Strip header lines on display */
int	bdots = OFF;		/* Binary box dots */
int	mdots = OFF;		/* Mailbox dots */
int	bprint = OFF;		/* Reading... messages */
int	quicknflag = OFF;	/* Turn off NEW before message prints */
int	quickexit = ON;		/* Don't stay in msg if mailbox is empty */
int	prettylist = ON;	/* Print message numbers in listings */
int	filoutflag = ON;	/* Filter text (display controls as ^X) */
int	binsavflag = ON;	/* Save binary box on exec's & pauses */
int	doctrlel = OFF;		/* Page on control-L */
#ifndef SYS5
long	timezone = 5*60*60;	/* Seconds West of GMT */
int	daylight = 1;		/* Do you use daylight savings time? */
				/* 0 = NO, 1 = YES */
char	*tzname[] = {"EST", "EDT",};
				/* Timezone strings, primary and daylight
				   savings time if used, in that order */
#endif SYS5

/***** Do not tailor below this line *****/
int	Nmsgs = NMSGS;
struct message *msgp[NMSGS];
struct message *mptr;
nsigned short d_lrill[8] =       /* illegal receive chars              */
{				  /* see d_slvconn & d_masconn          */
    0177777,                      /* nul soh stx etx eot enq ack bel    */
				  /* bs  ht  nl  vt  np  cr  so  si     */
    0177777,                      /* dle dc1 dc2 dc3 dc4 nak syn etb    */
				  /* can em  sub esc fs  gs  rs  us     */
    0000000,                      /* sp  ! " # $ % & ' ( ) * + , - . /  */
    0000000,                      /* mmdf/conf/bbn/s_tailor.c   444      0     12         334  3635172556  10413 /*
 *	S _ T A I L O R . C
 *
 *  Contains strings used to tailor SEND to a particular
 *  site.  Also check s.h for tailorable defines.
 */

char	*dfleditor = "pen";
char	*dflveditor = "pen";
char	*dflchecker = "spell";

char	*sndname = "/usr/local/mmdf/send"; /* Overridden by .msgrc sendprog */

char	*dflshell = "sh";	/* Overridden by getenv("SHELL"); */
char	*dfleditor = "pen";    /* Overridden by getenv("EDITOR"); */

int	pagesize = 22;		/* default line count */
int	linelength = 80;	/* default line size mmdf/conf/bbn/s_tailor.h   444      0     12         471  3635321377  10417 /*
 *	Site Tunable Parameters
 */
#undef	SENTFILE yes	/* Define to have copies of mail sent put in ,sent */
#undef	FNCYPRNT yes	/* Define for fancy listing of message (paginated) */
#define	PWNAME yes	/* Define to get real name from /etc/passwd and to */
			/* treat 0 length signatures to get login name only */
= "sh";	/* Overridden by getenv("SHELL"); */
char	*dfleditor = "pen";    /* Overridden by getenv("EDITOR"); */

int	pagesize = 22;		/* default line count */
int	linelength = 80;	/* default line size mmdf/samples/   755      0     12           0  3657737572   6357 mmdf/samples/uclvax2/   755      0     12           0  3635165133   7723 mmdf/samples/uclvax2/cron/   755      0     12           0  3635165132  10663 mmdf/samples/uclvax2/cron/44a.logs   444      0     12        1225  3620510453  12216 dir=/ftp/mailstats
f=`lday`
file="/arch/mailstats/44amsg/$f"
mkdir /arch/mailstats 2> /dev/null
mkdir /arch/mailstats/44amsg 2> /dev/null
cd /usr/spool/mmdflogs
(grep "end msg" msg.log;  grep "[rl]in msg" msg.log) > "$file"
ls -ls "$file"
mv msg.log  "$dir/msg/$f"
mv chan.log "$dir/chan/$f"
mv auth.log "$dir/auth/$f"
mv ph.log   oph.log
mv ph.trn   oph.trn
cp /dev/null msg.log
cp /dev/null chan.log
cp /dev/null ph.log
cp /dev/null ph.trn
cp /dev/null auth.log
chmod 666 msg.log chan.log ph.log ph.trn auth.log
find "$dir" -mtime +7 -exec rm {} ";" &
cd "$dir/auth"
if cat *:* >> `day`.44amlog
then
        rm *:*
	cp `day`.44amlog /arch/service/raw
fi
exit
ol/mmdflogs
(grep "end msg" msg.log;  grep "[rl]in msg" msg.log) > "$file"
ls -ls "$file"
mv msg.log  "$dir/msg/$f"
mv chan.log "$dir/chan/$f"
mv auth.log "$dir/auth/$f"
mv ph.log   oph.log
mv ph.trn   oph.trn
cp /dev/null msg.log
cp /dev/null chan.log
cp /dev/null ph.log
cp /dev/null ph.trn
cp /dev/null auth.log
chmod 666 msg.log chan.log ph.log ph.trn auth.lommdf/samples/uclvax2/cron/44a.night   444      0     12         473  3620510453  12347 set dir="/usr/spool/mmdflogs/misc"
set lg="$dir/night.log"
rm -f $lg
cd /lib/mmdf
/bin/sh /lib/mmdf/setlogs >& $lg
/common/mmdftable/send_to_vax >& $lg
cleanque >>& $lg
cd /common/mmdftable
dbmbuild >>& $lg
rm -f "$dir/dbmlist"
dbmlist >& "$dir/dbmlist"
cp /common/mmdftable/helplist /etc/mmdf/helplist >& $lg
exit
$f"
mv ph.log   oph.log
mv ph.trn   oph.trn
cp /dev/null msg.log
cp /dev/null chan.log
cp /dev/null ph.log
cp /dev/null ph.trn
cp /dev/null auth.log
chmod 666 msg.log chan.log ph.log ph.trn auth.lommdf/samples/uclvax2/cron/44a.start   444      0     12         220  3620510456  12364 cd /lib/mmdf
cleanque &
deliver -b -clist,local,niserc,nipss,niipss &
deliver -b -l30 -csring,satnet,tunnel &
usmtpdaemon  "smtp/" sring &
exit
 >& $lg
cleanque >>& $lg
cd /common/mmdftable
dbmbuild >>& $lg
rm -f "$dir/dbmlist"
dbmlist >& "$dir/dbmlist"
cp /common/mmdftable/helplist /etc/mmdf/helplist >& $lg
exit
$f"
mv ph.log   oph.log
mv ph.trn   oph.trn
cp /dev/null msg.log
cp /dev/null chan.log
cp /dev/null ph.log
cp /dev/null ph.trn
cp /dev/null auth.log
chmod 666 msg.log chan.log ph.log ph.trn auth.lommdf/samples/uclvax2/cron/44d.logs   444      0     12        1047  3620510456  12226 dir="/service/mailstats"
day=`lday`
file="$dir/stat/$day"
cd /usr/spool/mmdflogs
(grep "end msg" msg.log; grep "[rl]in msg" msg.log) | mail mmdf.arch.44d@44a
mv msg.log  "$dir/msg/$day"
mv chan.log "$dir/chan/$day"
mv auth.log "$dir/auth/$day"
mv ph.log   oph.log
mv ph.trn   oph.trn
cp /dev/null msg.log
cp /dev/null chan.log
cp /dev/null ph.log
cp /dev/null ph.trn
cp /dev/null auth.log
chmod 666 msg.log chan.log ph.log ph.trn auth.log
find "$dir" -mtime +5 -a -exec rm -f {} ";"
cd "$dir/auth"
if cat *:* >>`day`.44dmlog
then
        rm -f *:*
fi
ng of long messages */
int	verbose = OFF;		/* Command completion (raw mode) */
int	keystrip = ON;		/* Strip header lines on display */
int	bdots = OFF;		/* Binary box dots */
int	mdots = OFF;		/* Mailbox dots */
int	bprint = OFF;		/* Reading... messages */
int	quicknflag = OFF;	/* Turn off NEW before message prints */
int	quickexit = ON;		/* Don't stay in msg if mailbox is empty */
int	prettylist = ON;	/* Print message numbers in listings */
int	filoutflag = ON;	/* Filmmdf/samples/uclvax2/cron/44d.night   444      0     12        1060  3620510456  12366 set dir="/usr/spool/mmdflogs/misc"
set lg="$dir/night.log"
rm -f $lg
cd  /lib/mmdf
/bin/sh setlogs >& $lg
cleanque >>& $lg
cd /common/mmdftable >>& $lg
dbmbuild >>& $lg
rm -f "$dir/dbmlist"
cd /common/bin/mmdf
dbmlist >& "$dir/dbmlist"
cp /common/mmdftable/helplist /etc/mmdf/helplist >>& $lg
cp /common/mmdftable/hosts.txt /service/docs/arpa.sites >>& $lg
mail mail-check@ucl-cs@tunnel -s "test of Satnet loop" < /dev/null
mail mail-check@ucl-cs@nipss -s "test of PSS loop" < /dev/null
mail mail-check@ucl-cs@niserc -s "test of Sercnet loop" < /dev/null
exit
g messages */
int	verbose = OFF;		/* Command completion (raw mode) */
int	keystrip = ON;		/* Strip header lines on display */
int	bdots = OFF;		/* Binary box dots */
int	mdots = OFF;		/* Mailbox dots */
int	bprint = OFF;		/* Reading... messages */
int	quicknflag = OFF;	/* Turn off NEW before message prints */
int	quickexit = ON;		/* Don't stay in msg if mailbox is empty */
int	prettylist = ON;	/* Print message numbers in listings */
int	filoutflag = ON;	/* Filmmdf/samples/uclvax2/cron/44d.start   444      0     12         345  3620510456  12377 cd /lib/mmdf
cleanque &
deliver -b -clist,local,niserc,nipss,niipss,uucp &
deliver -b -l120 -T900 -ctunnel,satnet &
deliver -b -l30 -csring &
usmtpdaemon  "smtp/" sring &
smtpdaemon -n 3 "tcpx/25; 0.0.0.0.0,B,120:" satnet &
exit
mlist"
cp /common/mmdftable/helplist /etc/mmdf/helplist >>& $lg
cp /common/mmdftable/hosts.txt /service/docs/arpa.sites >>& $lg
mail mail-check@ucl-cs@tunnel -s "test of Satnet loop" < /dev/null
mail mail-check@ucl-cs@nipss -s "test of PSS loop" < /dev/null
mail mail-check@ucl-cs@nimmdf/samples/uclvax2/cron/mmdf.check   444      0     12         541  3620510457  12646 set mc=`mc`
( date; \
mc; \
echo "NIFTP temp dir - should be empty"; \
ls -l /usr/spool/nitemp; \
echo; \
echo "NIFTP spool dir - no old files"; \
ls -lt /usr/spool/jntmail; \
echo; \
echo; \
echo "Deadletter file - should not exist"; \
ls -l /usr/spool/mmdflock/que/DeadLetters; \
echo; \
echo; \
/lib/mmdf/checkque ) | mail mail-check -s "$mc status"
$lg
mail mail-check@ucl-cs@tunnel -s "test of Satnet loop" < /dev/null
mail mail-check@ucl-cs@nipss -s "test of PSS loop" < /dev/null
mail mail-check@ucl-cs@nimmdf/samples/uclvax2/cron/mmdf.table   444      0     12         633  3620510457  12662 #
set dir="/common/mmdftable"
set  ht="$dir/hosts.txt"
cd "$dir"
rm -f arpa
rm -f smtp.names
cp arpa.head arpa
cp smtp.head smtp.names
cd /common/bin/mmdf
dmtable $ht -t tcp -d arpa  >>& "$dir/arpa"
hstable $ht -t tcp >>& "$dir/smtp.names"
rm -f "$dir/all.names"
cp "$dir/all.head" "$dir/all.names"
fulltable >>& "$dir/all.names"
(/bin/sh mmdf.al.check) | mail mail-check -s "Alias file consistency check"
exit
 < /dev/null
mail mail-check@ucl-cs@nipss -s "test of PSS loop" < /dev/null
mail mail-check@ucl-cs@nimmdf/samples/uclvax2/cron/send_to_vax   444      0     12         556  3620510457  13166 set y = `cat /common/mmdftable/getfile.pa`

foreach x ( ac-uk aliases arpa auth ipss.names mailnet niftp.nets pss.names \
         serc.names smtp.names smtp.nets sring.names sweden top ucl-cs uucp )
        cpf /common/mmdftable/$x /usr/mmdf/mmdftable/$x@vax1 $y -arm >&/dev/null
        cpf /common/mmdftable/$x /usr/mmdf/mmdftable/$x@vax2 $y -arm >&/dev/null
end
check -s "Alias file consistency check"
exit
 < /dev/null
mail mail-check@ucl-cs@nipss -s "test of PSS loop" < /dev/null
mail mail-check@ucl-cs@nimmdf/samples/uclvax2/cron/update.misc   444      0     12         141  3620510457  13057 mc=`mc`
l=/lib/mmdf
cp $mc.logs $l/setlogs
cp $mc.night $l/mmdf.night
cp $mc.start $l/mmdf.start
t niftp.nets pss.names \
         serc.names smtp.names smtp.nets sring.names sweden top ucl-cs uucp )
        cpf /common/mmdftable/$x /usr/mmdf/mmdftable/$x@vax1 $y -arm >&/dev/null
        cpf /common/mmdftable/$x /usr/mmdf/mmdftable/$x@vax2 $y -arm >&/dev/null
end
check -s "Alias file consistency check"
exit
 < /dev/null
mail mail-check@ucl-cs@nipss -s "test of PSS loop" < /dev/null
mail mail-check@ucl-cs@nimmdf/samples/uclvax2/cron/vax1.logs   444      0     12           1  3620510460  12412 
c=`mc`
l=/lib/mmdf
cp $mc.logs $l/setlogs
cp $mc.night $l/mmdf.night
cp $mc.start $l/mmdf.start
t niftp.nets pss.names \
         serc.names smtp.names smtp.nets sring.names sweden top ucl-cs uucp )
        cpf /common/mmdftable/$x /usr/mmdf/mmdftable/$x@vax1 $y -arm >&/dev/null
        cpf /common/mmdftable/$x /usr/mmdf/mmdftable/$x@vax2 $y -arm >&/dev/null
end
check -s "Alias file consistency check"
exit
 < /dev/null
mail mail-check@ucl-cs@nipss -s "test of PSS loop" < /dev/null
mail mail-check@ucl-cs@nimmdf/samples/uclvax2/cron/vax1.night   444      0     12           1  3620510460  12557 
c=`mc`
l=/lib/mmdf
cp $mc.logs $l/setlogs
cp $mc.night $l/mmdf.night
cp $mc.start $l/mmdf.start
t niftp.nets pss.names \
         serc.names smtp.names smtp.nets sring.names sweden top ucl-cs uucp )
        cpf /common/mmdftable/$x /usr/mmdf/mmdftable/$x@vax1 $y -arm >&/dev/null
        cpf /common/mmdftable/$x /usr/mmdf/mmdftable/$x@vax2 $y -arm >&/dev/null
end
check -s "Alias file consistency check"
exit
 < /dev/null
mail mail-check@ucl-cs@nipss -s "test of PSS loop" < /dev/null
mail mail-check@ucl-cs@nimmdf/samples/uclvax2/cron/vax1.start   444      0     12         220  3620510460  12646 cd /lib/mmdf
cleanque &
deliver -b -clist,local,niserc,nipss,niipss &
deliver -b -l30 -csring,satnet,tunnel &
usmtpdaemon  "smtp/" sring &
exit
tp.names smtp.nets sring.names sweden top ucl-cs uucp )
        cpf /common/mmdftable/$x /usr/mmdf/mmdftable/$x@vax1 $y -arm >&/dev/null
        cpf /common/mmdftable/$x /usr/mmdf/mmdftable/$x@vax2 $y -arm >&/dev/null
end
check -s "Alias file consistency check"
exit
 < /dev/null
mail mail-check@ucl-cs@nipss -s "test of PSS loop" < /dev/null
mail mail-check@ucl-cs@nimmdf/samples/uclvax2/table/   755      0     12           0  3635165143  11013 mmdf/samples/uclvax2/table/44a-list.names   444      0     12          22  3620510461  13405 44a-list:44a-list
que &
deliver -b -clist,local,niserc,nipss,niipss &
deliver -b -l30 -csring,satnet,tunnel &
usmtpdaemon  "smtp/" sring &
exit
tp.names smtp.nets sring.names sweden top ucl-cs uucp )
        cpf /common/mmdftable/$x /usr/mmdf/mmdftable/$x@vax1 $y -arm >&/dev/null
        cpf /common/mmdftable/$x /usr/mmdf/mmdftable/$x@vax2 $y -arm >&/dev/null
end
check -s "Alias file consistency check"
exit
 < /dev/null
mail mail-check@ucl-cs@nipss -s "test of PSS loop" < /dev/null
mail mail-check@ucl-cs@nimmdf/samples/uclvax2/table/44a.names   444      0     12         151  3620510461  12457 ucl-cs:ucl-cs
ucl-cs.ac.uk:ucl-cs
ucl-cs.arpa:ucl-cs
44a:ucl-cs
44a.ac.uk:ucl-cs
44a.ucl-cs.ac.uk:ucl-cs
el &
usmtpdaemon  "smtp/" sring &
exit
tp.names smtp.nets sring.names sweden top ucl-cs uucp )
        cpf /common/mmdftable/$x /usr/mmdf/mmdftable/$x@vax1 $y -arm >&/dev/null
        cpf /common/mmdftable/$x /usr/mmdf/mmdftable/$x@vax2 $y -arm >&/dev/null
end
check -s "Alias file consistency check"
exit
 < /dev/null
mail mail-check@ucl-cs@nipss -s "test of PSS loop" < /dev/null
mail mail-check@ucl-cs@nimmdf/samples/uclvax2/table/44a.tailor   444      0     12       10461  3622770730  12724 MMSGLOG    level=GEN, size=40, stat=SOME
MCHANLOG   level=GEN, size=400, stat=SOME
PHLOG     level=GEN, size=40, stat=SOME
MSMTPLEV   PTR
MIPCSLEVEL 1

; special strings
MLOCMACHINE     44a
MLNAME    Ucl-Cs
MLDOMAIN  "AC.UK"
MSIG      "Memo Service (mmdf.2.84)"

; special directories
MTBLDIR    "/common/mmdftable"
MQUEDIR    "/usr/spool/mmdflock/que"

;  general tables
MTBL  name=aliases, file=aliases, display="User & list aliases"
MTBL  name=auth, file=auth, display="Authorisation file"


; Domain tables
MTBL    "Ucl-Cs",file="ucl-cs",show="Ucl-Cs domain table"
MDMN    "Ucl-Cs.AC.UK",show="Ucl-Cs.AC.UK domain",table="Ucl-Cs"
MTBL    "AC-UK",file="ac-uk",show="AC.UK Domain"
MDMN    "AC.UK",show="AC.UK Domain",table="AC-UK"
MTBL    "ARPA",file="arpa",show="ARPA Domain"
MDMN    "ARPA",show="ARPA Domain",table="ARPA"
MTBL    "SWEDEN",file="sweden",show="Swedish Domain"
MDMN    "SWEDEN",show="Swedish Domain",table="SWEDEN"
MTBL    "MAILNET",file="mailnet",show="Mailnet Domain"
MDMN    "MAILNET",show="Mailnet Domain",table="MAILNET"
MTBL    "UUCP",file="uucp",show="Usenet Domain"
MDMN    "UUCP",show="Usenet Domain",table="UUCP"
MDMN    "AC",show="AC.UK Domain (AC)",table="AC-UK",name="AC",dmn="AC.UK"
MDMN    "UK",show="AC.UK Domain (UK)",table="AC-UK",name="UK",dmn="AC.UK"
MTBL    "Top-Level",file="top",show="Top Level Domain"
MDMN    "Top-Level",show="Top level Domain",table="Top-Level",name="",dmn=""


;  channel definitions
MTBL  local     file="44a.names"
MCHN  local    name=local,que=local,tbl=local,show="Local (44a)",pgm=local,poll=0,mod=reg,ap=733,ap=big

MCHN  newslist  name=newslist,que=newslist,tbl=local,show="News feed from 44d",pgm=local,poll=0,mod=reg

MTBL  list     file="44a-list.names"
MCHN  list    name=list,que=list,tbl=list,show="via List-Channel",pgm=list,poll=0,mod=reg,host=Ucl-Cs.AC.UK

; MTBL  vaxlink    file="vaxlink.names"
; MCHN  vaxlink name=vaxlink,que=vaxlink,tbl=vaxlink,show="via Phonenet link to VAX",pgm=pobox,poll=0,mod=psv,user=vaxmmdf,host=Vax.Ucl-Cs.AC.UK

MTBL  sring    file="sring.names"
MCHN  sring name=sring,que=sring,tbl=sring,show="with SMTP",pgm=smtp,poll=0,mod=reg,confstr="%s/"

; SMTP channel stuff
MTBL  smtp      file="smtp.names"
; (going through rc tunnel)
; MTBL  tunnilsmtp      file="tunnil.names"
; MCHN  tunnil      name=tunnil,que=tunnil,tbl=tunnilsmtp,show="via IPSS-Tunnel (tunnel only hosts ) with SMTP",pgm=smtp,poll=0,mod=reg,ldomain="ARPA",confstr="tcpx/128.16.9.3,X;%s.25,B,200:",ap=733,ap=host,ldomain=ARPA,known=smtp
; (going over Satnet)
MCHN  satnet     name=satnet,que=satnet,tbl=smtp,show="via Satnet with SMTP",pgm=smtp,poll=0,mod=reg,ldomain="ARPA",confstr="tcpx/128.16.9.3,X;%s.25,B,200:",ap=733,ap=host,ldomain=ARPA,known=smtp,auth=inlog,auth=outlog
; (going through tunnel)
MCHN  tunnel      name=tunnel,que=tunnel,tbl=smtp,show="via IPSS-Tunnel with SMTP",pgm=smtp,poll=0,mod=reg,ldomain="ARPA",confstr="tcpx/14.0.0.9,X;%s.25,B,200:",ap=733,ap=host,ldomain=ARPA,known=smtp

MTBL  niserc    file="serc.names"
MCHN  niserc    name=niserc,que=niserc,tbl=niserc,show="via Janet with NIFTP",pgm=niftp,poll=0,mod=reg,ap=jnt,confstr="/usr/bin/cpf cpf -t -f\"$(SENDER)\" $(FILE) @$(ADR)",auth=inlog,auth=outlog

MTBL  nipss    file="pss.names"
MCHN  nipss    name=nipss,que=nipss,tbl=nipss,show="via PSS with NIFTP",pgm=niftp,poll=0,mod=reg,ap=jnt,confstr="/usr/bin/cpf cpf -t -f\"$(SENDER)\" $(FILE) @$(ADR)",auth=inlog,auth=outlog

MTBL  niipss    file="ipss.names"
MCHN  niipss    name=niipss,que=niipss,tbl=niipss,show="via IPSS with NIFTP",pgm=niftp,poll=0,mod=reg,host=ykxa,ap=733,ap=host,confstr="/usr/bin/cpf cpf -t -f\"$(SENDER)\" $(FILE) @$(ADR)",auth=inlog,auth=outlog


MTBL  xuucp    file="uucp.names"
MCHN  uucp name=uucp,que=uucp,tbl=xuucp,show="with UUCP",pgm=uucp,poll=0,mod=reg,ldomain=UUCP,ap=733,auth=inlog,auth=outlog

; Karsruhe special link
MTBL  karlsruh    file="karl.names"
MCHN  karlsruh name=karlsruh,que=karlsruh,tbl=karlsruh,show="via Phonenet link to Karlsruhe",pgm=pobox,poll=0,mod=psv,user=karlsruh,host=karlsruh,ap=733,ap=host,auth=inlog,auth=outlog


; Deliver tailoring
MWARNTIME  72
MFAILTIME  144
MSLEEP     300 ; daemon sleep time

;smtp server
MTBL  name="smtp.nets", file="smtp.nets", display="SMTP net to channel map"

MTBL  name="niftp.nets", file="niftp.nets", display="NIFTP net to channel map"

PHLOG      level=FST
MCHANLOG   level=FST
MMSGLOG    level=FST
e="ARPA"
MTBL    "SWEDEN",file="sweden",show="Swedish Domain"
MDMN    "SWEDEN",show="Swedish Domain",table="SWEDEN"
MTBL    "MAILNET",file="mailnet",show="Mailnet Domain"
MDMN    "MAILNET",show="Mailnet Domammdf/samples/uclvax2/table/44d-list.names   444      0     12          22  3620510461  13410 44d-list:44d-list
usr/bin/cpf cpf -t -f\"$(SENDER)\" $(FILE) @$(ADR)",auth=inlog,auth=outlog


MTBL  xuucp    file="uucp.names"
MCHN  uucp name=uucp,que=uucp,tbl=xuucp,show="with UUCP",pgm=uucp,poll=0,mod=reg,ldomain=UUCP,ap=733,auth=inlog,auth=outlog

; Karsruhe special link
MTBL  karlsruh    file="karl.names"
MCHN  karlsruh name=karlsruh,que=karlsruh,tbl=karlsruh,show="via Phonenet link to Karlsruhe",pgm=pobox,poll=0,mod=psv,user=karlsruh,host=karlsruh,ap=733,ap=host,auth=inlog,auth=outlog


; Deliver taimmdf/samples/uclvax2/table/44d.names   444      0     12         151  3620510462  12463 ucl-cs:ucl-cs
ucl-cs.ac.uk:ucl-cs
ucl-cs.arpa:ucl-cs
44d:ucl-cs
44d.ac.uk:ucl-cs
44d.ucl-cs.ac.uk:ucl-cs
p    file="uucp.names"
MCHN  uucp name=uucp,que=uucp,tbl=xuucp,show="with UUCP",pgm=uucp,poll=0,mod=reg,ldomain=UUCP,ap=733,auth=inlog,auth=outlog

; Karsruhe special link
MTBL  karlsruh    file="karl.names"
MCHN  karlsruh name=karlsruh,que=karlsruh,tbl=karlsruh,show="via Phonenet link to Karlsruhe",pgm=pobox,poll=0,mod=psv,user=karlsruh,host=karlsruh,ap=733,ap=host,auth=inlog,auth=outlog


; Deliver taimmdf/samples/uclvax2/table/44d.tailor   444      0     12        7276  3622770736  12727 MMSGLOG    level=GEN, size=40, stat=SOME
MCHANLOG   level=GEN, size=400, stat=SOME
PHLOG     level=GEN, size=40, stat=SOME
MSMTPLEV   PTR
MIPCSLEVEL 1

; special strings
MLOCMACHINE     44d
MLNAME    Ucl-Cs
MLDOMAIN  "AC.UK"
MSIG      "Memo Service (mmdf.6.84)"

; special directories
MTBLDIR    "/common/mmdftable"
MQUEDIR    "/usr/spool/mmdflock/que"

;  general tables
MTBL  name=aliases, file=aliases, display="User & list aliases"
MTBL  name=auth, file=auth, display="Authorisation file"

; Domain tables
MTBL    "Ucl-Cs",file="ucl-cs",show="Ucl-Cs domain table"
MDMN    "Ucl-Cs.AC.UK",show="Ucl-Cs.AC.UK domain",table="Ucl-Cs"
MTBL    "AC-UK",file="ac-uk",show="AC.UK Domain"
MDMN    "AC.UK",show="AC.UK Domain",table="AC-UK"
MTBL    "ARPA",file="arpa",show="ARPA Domain"
MDMN    "ARPA",show="ARPA Domain",table="ARPA"
MTBL    "SWEDEN",file="sweden",show="Swedish Domain"
MDMN    "SWEDEN",show="Swedish Domain",table="SWEDEN"
MTBL    "MAILNET",file="mailnet",show="Mailnet Domain"
MDMN    "MAILNET",show="Mailnet Domain",table="MAILNET"
MTBL    "UUCP",file=uucp,show="Usenet Domain"
MDMN    "UUCP",show="Usenet Domain",table="UUCP"
MDMN    "AC",show="AC.UK Domain (AC)",table="AC-UK",name="AC",dmn="AC.UK"
MDMN    "UK",show="AC.UK Domain (UK)",table="AC-UK",name="UK",dmn="AC.UK"
MTBL    "Top-Level",file="top",show="Top Level Domain"
MDMN    "Top-Level",show="Top level Domain",table="Top-Level",name="",dmn=""


;  channel definitions
MTBL  local     file="44d.names"
MCHN  local    name=local,que=local,tbl=local,show="Local (44d)",pgm=ch_local,poll=0,mod=reg,ap=733,ap=big

MTBL  list     file="44d-list.names"
MCHN  list    name=list,que=list,tbl=list,show="via List-Channel",pgm=ch_list,poll=0,mod=reg,host=Ucl-Cs.AC.UK

MTBL  sring    file="sring.names"
MCHN  sring name=sring,que=sring,tbl=sring,show="with SMTP",pgm=ch_smtp,poll=0,mod=reg,confstr="%s/"

; SMTP channel stuff
MTBL  smtp      file="smtp.names"
; (going through tunnel)
MCHN  tunnel      name=tunnel,que=tunnel,tbl=smtp,show="via IPSS-Tunnel with SMTP",pgm=ch_smtp,poll=0,mod=reg,ldomain="ARPA",confstr="tcpx/14.0.0.9,X;%s.25,B,200:",ap=733,ap=host,ldomain=ARPA,known=smtp,auth=inlog,auth=outlog
; (going over Satnet)
MCHN  satnet     name=satnet,que=satnet,tbl=smtp,show="via Satnet with SMTP",pgm=ch_smtp,poll=0,mod=reg,ldomain="ARPA",confstr="tcpx/128.16.9.3,X;%s.25,B,200:",ap=733,ap=host,ldomain=ARPA,known=smtp,auth=inlog,auth=outlog

MTBL  niserc    file="serc.names"
MCHN  niserc    name=niserc,que=niserc,tbl=niserc,show="via Janet with NIFTP",pgm=ch_niftp,poll=0,mod=reg,ap=jnt,confstr="/usr/bin/cpf cpf -t -f\"%%%%s\" %s @%%s",auth=inlog,auth=outlog

MTBL  nipss    file="pss.names"
MCHN  nipss    name=nipss,que=nipss,tbl=nipss,show="via PSS with NIFTP",pgm=ch_niftp,poll=0,mod=reg,ap=jnt,confstr="/usr/bin/cpf cpf -t -f\"%%%%s\" %s @%%s",auth=inlog,auth=outlog

MTBL  niipss    file="ipss.names"
MCHN  niipss    name=niipss,que=niipss,tbl=niipss,show="via IPSS with NIFTP",pgm=ch_niftp,poll=0,mod=reg,confstr="/usr/bin/cpf cpf -t -f\"%%%%s\" %s @%%s",ap=733,ap=host,host=ykxa,auth=inlog,auth=outlog

MCHN mailnet    name=mailnet,que=mailnet,tbl=smtp,show="via Mailnet with SMTP",pgm=ch_smtp,poll=0,mod=psv,ldomain=Mailnet,ap=733,ap=host,host="Mit-Multics.ARPA",lname=Ucl-Cs-Mailnet,auth=inlog,auth=outlog


MTBL  xuucp    file="uucp.names"
MCHN  uucp name=uucp,que=uucp,tbl=xuucp,show="with UUCP",pgm=ch_uucp,poll=0,mod=reg,ap=733,ap=host,auth=inlog,auth=outlog


; Deliver tailoring
MWARNTIME  72
MFAILTIME  144
MSLEEP     300 ; daemon sleep time

;smtp server
MTBL  name="smtp.nets", file="smtp.nets", display="SMTP net to channel map"

MTBL  name="niftp.nets", file="niftp.nets", display="NIFTP net to channel map"

PHLOG      level=FST
MCHANLOG   level=FST
MMSGLOG    level=FST
ipss,que=nipss,tbl=nipss,show="via PSS with NIFTP",pgm=niftp,poll=0,mod=reg,ap=jnt,confstr="/usr/bin/cpf cpf -t -f\"$(SENDER)\" $(FILE) @$(ADR)",auth=inlog,auth=outlog

MTBL  niipss    file="ipss.names"
MCHN  niipss    name=niipss,que=niipss,tbl=niipss,show="via IPSS with NIFTP",pgm=niftp,poll=0,mod=reg,host=ykxa,ap=733,mmdf/samples/uclvax2/table/README   444      0     12         343  3620510462  11726 jan 85

This contains configs for:  44a  44d and vax1
Machine specific files are in $mc*
Startup and cron entries are in the subdirectory cron
Tailor files are in $mc.tailor

Various long files have been shortened

Steve Kille
 "ARPA",file="arpa",show="ARPA Domain"
MDMN    "ARPA",show="ARPA Domain",table="ARPA"
MTBL    "SWEDEN",file="sweden",show="Swedish Domain"
MDMN    "SWEDEN",show="Swedish Domain",table="SWEDEN"
MTBL    "MAILNET",file="mailnet",show="Mailnet Domain"
MDMN    "MAILNET",show="Mailnet Domaimmdf/samples/uclvax2/table/ac-uk   444      0     12        7061  3620510462  12015 #       uk-domain-table
#       uk-local-names
ucl-cs:ucl-cs
44a:44a
44b:44b
44c:44c
44d:44d
44e:44e
vax1:vax1
vax2:vax2
ucl:ucl-cs
zuxa:ucl-cs
zuxa80:ucl-cs
research:ucl-cs
nss:ucl-cs
unknown-host:ucl-cs
2005100.ucl-cs.ftp.mail:ucl-cs
32.ucl-cs.ftp.mail:ucl-cs
000000000032.ucl-cs.ftp.mail:ucl-cs
5111600.ucl-cs.ftp.mail:ucl-cs
000005111600.ucl-cs.ftp.mail:ucl-cs
zuxa.ucl-cs.ftp:ucl-cs
zuxa.ucl-cs.ftp.mail:ucl-cs
zuxc.ucl-cs.ftp:ucl-cs
zuxc.ucl-cs.ftp.mail:ucl-cs
serc.zuxa.ucl-cs.ftp.mail:ucl-cs
pss.zuxa.ucl-cs.ftp.mail:ucl-cs
x25.234219200300.ucl-cs.ftp.mail:ucl-cs
x25.234219200300.uclcs.ftp.mail:ucl-cs
x25.234219200300.ucl-cs.ftp:ucl-cs
x25.234219200300.uclcs.ftp:ucl-cs
234219200300.ucl-cs.ftp.mail:ucl-cs
234219200300.uclcs.ftp.mail:ucl-cs
234219200300.ucl-cs.ftp:ucl-cs
234219200300.uclcs.ftp:ucl-cs
zuxc:ucl-cs
ucl-niftp:ucl-cs
ucl-tg:ucl-cs
pss-src-gtway:ucl-cs
pss-serc-gateway:ucl-cs
uclcs:ucl-cs
ucl-ean:ucl-ean.ac.uk
ean:ucl-ean.ac.uk
#
#       names-over-serc-ring
44d:44d/ftp
ucl-cs:ucl-cs
ucl:ucl-cs
ucl-cs(44d):ucl-44d
ucl-44d:ucl-44d
#
#       serc-names
aberdeen:aberdeen
abdn:aberdeen
abxa:aberdeen
aere:aere
harwell:aere
apca:apca
are-pn:are-pn
aston:aston
vaxa.aston:aston
baca:baca
bapa:bapa
bdga:bdga
bdgb:bdgb
bedford:bedford
bgxa:bgxa
bihi3:bihi3
bihi4:bihi4
biht:biht
idqb:idqb
idqc:idqc
iclkids:iclkids
iclp:iclkids
keca:keca
# ktda:ktda   see eagle
kwga:kwga
kwga:kwga
vax1.central.lancaster:vax1.central.lancaster
icl.central.lancaster:icl.central.lancaster
computing.lancaster:computing.lancaster
engineering.lancaster:engineering.lancaster
lancaster:vax1.central.lancaster
lanc:lancaster
vax1.cent.lanc:vax1.central.lancaster
icl.cent.lanc:icl.central.lancaster
comp.lanc:computing.lancaster
eng.lanc:engineering.lancaster
lava:lava
lawa:lawa
leva:leva
levc:levc
lhgu:lhgu
vax1.livuniv:vax1.livuniv
liverpool:vax1.livuniv
luva:vax1.livuniv
loughborough:loughborough
llga:llga
llgb:llgb
llia:llia
lnxa:lnxa
ltga:ltga
nmpa:nmpa
nnga:nnga
hcig.nott:hcig.nott
hcig.nottingham:hcig.nott
hcig:hcig.nott
nott:nott hcig.nott
nottingham:nott hcig.nott
nsga:nsga
nsxa:nsxa
vax1.ox:vax1.ox
vax2.ox:vax2.ox
vax3.ox:vax3.ox
prgv.ox:prgv.ox
prgg.ox:prgg.ox
prg.ox:prg.ox
sevax.prg.oxford:prgv.ox
segec.prg.oxford:prgg.ox
prg.oxford:prg.ox
qec:qec
cu.qec:qec
qmc:qmc
qmc-csvax:qmc-csvax
ccvax.qub:ccvax.qub
belfast:ccvax.qub
qubccvax:ccvax.qub
quva:quva
quxa:quxa
micro.rdg:micro.rdg
reading:micro.rdg
rega:rega
revs:revs
rgga:rgga
zuxa:zuxa
zuxb:zuxb
zuxc:zuxc
zuxu:zuxu
#   odd synonyms
abbot:ykdc
bishop:ykdd
cannon:ykdf
ercc:edxa
farmer:ykxb
ral:rlgb
rl:rlgb
york:ykxa
#
#       serc-hosts-relayed
alvey:alvey,rlgm
dlia:dlia,rlgb
rlia:rlia,rlgb
rlib:rlib,rlgb
rlid:rlid,rlgb
camphx:camphx,caga
caxa:camphx,caga
camring:camring,camjenny.ac.uk,caga
camjenny:camjenny,caga
camsteve:camsteve,caga
cavj:cavj
cuedvax:cuedvax,caga
ecsvax:ecsvax,edxa
rco:rco
edxd:rco
edinburgh:rco
ucl-cc:ucl-cc,caga
euclid:ucl-cc,caga
ykda:ykda,ykxa
yorkcom:yorkcom,ykxa
#
# ukc hosts
eagle:eagle
eagle.ukc:eagle
ktda:eagle
kent:eagle
ukc:ukc
emas.ukc:emas.ukc
#
#       pss-host-names
aucc:aucc
cardiff:cardiff
cardf:cardiff
essex:essex
gecd:gecd
hatfield:hatfield
rsre:rsre
st-andrews:st-andrews
stand:st-andrews
uwist:uwist
r-d.salford:r-d.salford
salford:r-d.salford
#
#       telex bits
# telex: "\"TELEX(ZUSK,ZUSK)\"" telex-service 44a
# ucl-telex: 28722 "\"TELEX(ZUSK,ZUSK)\"" telex-service 44a
#
#       teletex hosts
# ucl-teletex:ucl-teletex 44e
# logica-teletex:logica-teletex 44e
# gec-teletex:gec-teletex 44e
# btrl-teletex:btrl-teletex 44e
# logica:logica-teletex 44e
# btrl:btrl-teletex 44e
# gec:gec-teletex 44e
#
ail:ucl-cs
x25.234219200300.uclcs.ftp.mail:ucl-cs
x25.234219200300.ucl-cs.ftp:ucl-cs
x25.234219200300.uclcs.ftp:ucl-cs
234219200300.ucl-cs.ftp.mail:ucl-cs
234219200300.uclcs.ftp.mail:ucl-cs
234219200300.ucl-cs.ftp:ucl-cs
234219200300.uclcs.ftp:ucl-cs
zuxc:ucl-cs
ucl-niftp:ucl-cs
ucl-tg:ucl-cs
pss-src-gtway:ucl-cs
pss-serc-gateway:ucl-cs
uclcs:ucl-cs
ucl-ean:ucl-ean.ac.uk
ean:ucl-ean.ac.uk
#
#       names-over-serc-ring
44d:44d/ftp
ucl-cs:ucl-cs
ucl:ucl-cs
uclmmdf/samples/uclvax2/table/aliases   444      0     12       17560  3620510463  12464 #
#        aliases-of: aliases
admin:liaison
administration:liaison
cn:sg
engineers:hardware
fax:wap
faxgroup:wap
games:sg
guest:sg
killunix:sg
liason:liaison
lman:sg
manager:liaison
measure:measurements
netnews:liaison
netwiz:ucl-netwiz
newuser:sg
nfmaint:postmaster
no_login:sg
oper:sg
operator:sg
postperson:postmaster
karlsruh:postmaster
qftp-daemon:francis
qstate:francis
root:sg
sattime:satime
sman:sg
ss:sg
support:liaison
susmaster:jc
system:sg
tcptest:gateway
teaching:academic
ucl-liaison:liaison
up-owp:up-cwp
up-ucl:universe
up:universe
uucp:uucpmast
uucpmaster:uucpmast,francis
uucp-loop:uucpmaster,mail-check
uucpperson:uucpmaster
who:sg
#
# GROUPS CONTAINING GROUPS: NOTE ORDERING IS STRUCTURED
pss:service,mod
staff:research-outbound@44a-list,academic-outbound@44a-list,x_staff@44a,x_staff@44e
x_staff:mmdf|/lib/mmdf/bnewsinput staff
students:1styr,2ndyr,3rdyr,msc
msc:mscit,msccs
remote-users:uk-users,us-users
sat-out-list:liaison,gateway
uk-users:uk-us-accts,uk-uk-accts
us-users:us-us-accts,us-uk-accts
up-all:up-bt@rlgd,up-cucl@rlgd,up-logica@rlgd,up-lut@rlgd,up-mrc@rlgd,up-ral@rlgd,universe
#
#        functional-aliases: pipes+files
#       note- disabled at present
mmdf.arch.44b:mmdf|/common/bin/mmdf/mmdf.arch 44bmsg
mmdf.arch.44c:mmdf|/common/bin/mmdf/mmdf.arch 44cmsg
mmdf.arch.44d:mmdf|/common/bin/mmdf/mmdf.arch 44dmsg
mmdf.arch.44e:mmdf|/common/bin/mmdf/mmdf.arch 44emsg
mmdf.arch.vax1:mmdf|/common/bin/mmdf/mmdf.arch vax1msg
mmdf.arch.vax2:mmdf|/common/bin/mmdf/mmdf.arch vax2msg
dev-null:mmdf|"cat > /dev/null"
#
# mail table update for vax
# vax-aliases:mmdf|"cat > /usr/src/cmd/mmdf/newtable/xaliases"
# vax-names:mmdf|"cat >  /usr/src/cmd/mmdf/newtable/xall.names"
# vax-ucl:mmdf|"cat > /usr/src/cmd/mmdf/newtable/xucl-cs"
# vax-uk:mmdf|"cat > /usr/src/cmd/mmdf/newtable/xac-uk"
# vax-arpa:mmdf|"cat > /usr/src/cmd/mmdf/newtable/xarpa"
# vax-sweden:mmdf|"cat > /usr/src/cmd/mmdf/newtable/xsweden"
# vax-mailnet:mmdf|"cat >  /usr/src/cmd/mmdf/newtable/xmailnet"
#
#  News + bboard stuff
# inject on 1 mcs for now.   just on 44d for relaying
# BRL NEWSFEED
brl-news:news-list@44a
# WESTFIELD NEWSFEED
westfield-news:uucp-inject@44a
# KENT NEWSFEED
# uucp-news:uucp-inject@44a
uucp-inject:news|/usr/lib/news/uurec
news-list:news|/usr/lib/news/uurec@ucl-cs@newslist
general.wall:44a.general.wall@44a,44b.general.wall@44b,44c.general.wall@44c,44d.general.wall@44d,44e.general.wall@44e,vax1.general.wall@vax1,vax2.general.wall@vax2
44a.general.wall:loc-general.wall@44a,x_general@44a
44b.general.wall:loc-general.wall@44b
44c.general.wall:loc-general.wall@44c
44d.general.wall:loc-general.wall@44d
44e.general.wall:loc-general.wall@44e,x_general@44e
vax1.general.wall:loc-general.wall@vax1
vax2.general.wall:loc-general.wall@vax2
loc-general.wall:loc-general,root|/etc/wall.mmdf
general:44a.general@44a,44b.general@44b,44c.general@44c,44d.general@44d,44e.general@44e,vax1.general@vax1,vax2.general@vax2,general-outbound@44a-list
44a.general:loc-general@44a,x_general@44a
44b.general:loc-general@44b
44c.general:loc-general@44c
44d.general:loc-general@44d
44e.general:loc-general@44e,x_general@44e
vax1.general:v_loc-general@vax1
vax2.general:v_loc-general@vax2
general-outbound: :include:/etc/alias/general@44a
general-request:mmdf
x_general:mmdf|/lib/mmdf/bnewsinput general
loc-general:news|/usr/lib/news/recnews general
v_loc-general:dev-null
loc-unix:news|/usr/lib/news/recnews unix
#
#  GROUPS /LISTS  - UCL stuff
academic:academic-outbound@44a-list,x_academic@44a,x_academic@44e
x_academic:mmdf|/lib/mmdf/bnewsinput academic
academic-outbound: :include:/etc/alias/academic@44a
academic-request:mmdf
tutors:tutors-outbound@44c-list
tutors-outbound: :include:/etc/alias/tutors@44c
tutors-request:wshbrk
accountall:account
action:mitchell
advisers: :include:/etc/alias/advisers@44c
advisors:advisers
advisory: :include:/etc/alias/advisers@44c
advisers-request:jc
advisors-request:jc
bbcusers: :include:/etc/alias/bbcusers@44a
bugs: :include:/etc/alias/bugs@44a,x_bugs@44a,x_bugs@44e
x_bugs:mmdf|/lib/mmdf/bnewsinput bugs
coffee:coffee-outbound@44a-list,x_coffee@44a
x_coffee:mmdf|/lib/mmdf/bnewsinput coffee
coffee-outbound: :include:/etc/alias/coffee@44a
coffee-request:russel
codata:sg,wilbur,june,knight,bruce
codata-request:phil
rpc:mriddoch,fielding,jon,benjamin
rpc-request:benjamin
daemon:x_daemon-err@44a,phil
x_daemon-err:mmdf|/lib/mmdf/bnewsinput daemon-err
dcs:dcs-outbound@44a-list
dcs-outbound: :include:/etc/alias/dcs@44a
dcs-request:mmdf
dcxusers:cjk,kirstein,billt,soren,hugh,jack,ucl-cc
discmaster:action,francis,jc
.
.
.


#
#
#  Arpanet Mailing Lists
#
# These should have the following entries (for list foo)
# An attempt has been made to retain this order in all cases
#
# foo           pointing to the master list
# ucl-foo  to be added to the list:, pointing to
#       foo-outbound and x_foo on all machines which have the
#       notesfile
# x_foo         pointing to the notesfile pipe
# foo-outbound  pointing to the expansion file
# foo-request   pointing to the UCL maintaner's address
arms-d:arms-d@mit-mc
ucl-arms-d:arms-d-outbound@44d-list,x_arms-d@44a
x_arms-d:mmdf|/lib/mmdf/bnewsinput arms-d
arms-d-outbound: :include:/service/arpa/edaib/info.arms@44d
arms-d-request:d.plummer@edxa
com-x400-implementors:com-x400-implementors-outbound@44a-list,x_com-x400-implementors@44a
x_com-x400-implementors:mmdf|/lib/mmdf/bnewsinput com-x400-implementors
com-x400-implementors-outbound: :include:/etc/alias/com-x400-impl@44a
com-x400-implementors-request:steve
header-people:header-people@mit-mc
ucl-header-people:header-people-outbound@44d-list,x_header-people@44a
x_header-people:mmdf|/lib/mmdf/bnewsinput header-people
header-people-outbound: :include:/etc/alias/h-people@44d
header-people-request:steve
.
.
.
.
.


#
# News to Notesfile aliases
# NOTE: the name of the group is for outbound traffic
# This specifies relay to the News machine to a t_* address
# The t_ address calls an appropriate pipe
# The n_* address is for incoming news messages, which routes to
# an x_* address on al machines where the notesfile is stored
# The x_* address specifies input to a notesfile
# The reason for all this twiddling is that pipes must come from
# the alias file
ucl.test:t_ucl.test@44a
t_ucl.test:"news|/usr/lib/news/recnews ucl.test"
n_ucl.test:x_ucl.test@44a
x_ucl.test:mmdf|/lib/mmdf/bnewsinput ucl.test
uk.events:t_uk.events@44a
t_uk.events:"news|/usr/lib/news/recnews uk.events"
n_uk.events:x_uk.events@44a
x_uk.events:mmdf|/lib/mmdf/bnewsinput uk.events
uk.general:t_uk.general@44a
t_uk.general:"news|/usr/lib/news/recnews uk.general"
n_uk.general:x_uk.general@44a,jpo@nott
x_uk.general:mmdf|/lib/mmdf/bnewsinput uk.general
uk.net:t_uk.net@44a
t_uk.net:"news|/usr/lib/news/recnews uk.net"
n_uk.net:x_uk.net@44a,jpo@nott
x_uk.net:mmdf|/lib/mmdf/bnewsinput uk.net
uk.test:t_uk.test@44a
t_uk.test:"news|/usr/lib/news/recnews uk.test"
n_uk.test:x_uk.test@44a
x_uk.test:mmdf|/lib/mmdf/bnewsinput uk.test
uk.news.map:t_uk.news.map@44a
t_uk.news.map:"news|/usr/lib/news/recnews uk.news.map"
n_uk.news.map:x_uk.news.map@44a
x_uk.news.map:mmdf|/lib/mmdf/bnewsinput uk.news.map
.
.
.
.
.


#
#  INDIVIDUAL : ALIASES
alawson:lawson
amcdowell:mcdowell
aringsell:anita
bbacarisse:benjamin
bermudez:tony
benedict:bnixon
blacknest:blacknst
braden:braden@isia
bsegal:barbara
btuck:billt
bwilford:bruce
wilford:bruce
carbonell:raphael
cleung:leung
clloyd:lloyd
cole:robert
cooper:dlc@rlib
crompton:jc
clement:leung
cn:robert
cockcroft:phil
rabbit:phil
ceasteal:charles
ckennington:cjk
coulouris:george
crowcroft:jon
daniel:tom
dbrink:brink
.
.
.
.
.

#
#       User table Sectiom
#
#        Memebers-of-staff
msg:~msg@44a
account:~account@44a
angell:~angell@44c
alkalaj:~alkalaj@44a
anita:~anita@44a
awood:~awood@44c
atanu:~atanu@44a
barac:~barac@44e
barbara:~barbara@44e
bbn:~bbn@44a
benjamin:~benjamin@44e
billt:~billt@44d
bnixon:~bnixon@44c
brink:~brink@44d
bruce:~bruce@44a
btrl:~btrl@44e
burrows:~burrows@44d
cguy:~cguy@44c
ch:~ch@44d
chapman:~chapman@vax1
charles:~charles@44c
.
.
.


#
# end
ers@44a
bugs: :include:/etc/alias/bugs@44a,x_bugs@44a,x_bugs@44e
x_bugs:mmdf|/lib/mmdf/bnewsinput bugs
coffee:coffee-outbound@44a-list,x_coffee@mmdf/samples/uclvax2/table/all.head   444      0     12          45  3620510463  12421 44a:smtp44a
44a.ucl-cs.ac.uk:smtp44a
net:t_uk.net@44a
t_uk.net:"news|/usr/lib/news/recnews uk.net"
n_uk.net:x_uk.net@44a,jpo@nott
x_uk.net:mmdf|/lib/mmdf/bnewsinput uk.net
uk.test:t_uk.test@44a
t_uk.test:"news|/usr/lib/news/recnews uk.test"
n_uk.test:x_uk.test@44a
x_uk.test:mmdf|/lib/mmdf/bnewsinput uk.test
uk.news.map:t_uk.news.map@44a
t_uk.news.map:"news|/usr/lib/news/recnews uk.news.map"
n_uk.news.map:x_uk.news.map@44a
x_uk.news.map:mmdf|/lib/mmdf/bnewsinput uk.news.map
.
.
.
.
.


#
#  INDIVIDUAL : ALIAmmdf/samples/uclvax2/table/all.names   444      0     12         772  3620510463  12652 44a:smtp44a
44a.ucl-cs.ac.uk:smtp44a
ucl-cs:ucl-cs
ucl-cs.ac.uk:ucl-cs
ucl-cs.arpa:ucl-cs
44a:ucl-cs
44a.ac.uk:ucl-cs
44a.ucl-cs.ac.uk:ucl-cs
44a-list:44a-list
ucl-cs.arpa:128.16.9.3
44a:smtp44a
44b:smtp44b
44c:smtp44c
44d:smtp44d
44e:smtp44e
vax1:smtpvax1
vax2:smtpvax2
ucl-ean.ac.uk:smtpvax2
44a-list:smtp44a
44b-list:smtp44b
44c-list:smtp44c
44d-list:smtp44d
44e-list:smtp44e
vax1-list:smtpvax1
vax2-list:smtpvax2
ucl-cs.arpa:128.16.9.3
ucl-cs.arpa:14.0.0.9
ucl-cs:128.16.9.3
edn-noc.arpa:7.0.0.1
.
.
.
: ALIAmmdf/samples/uclvax2/table/arpa   444      0     12        1426  3620510463  11740 #
#        arpa-domain-table
#       note ucl and isid at top of file
#       WARNING THIS FILE IS MACHINE GENERATED
ucl-cs:ucl-cs.arpa
ucl-tg:ucl-cs.arpa
ucl-niftp:ucl-cs.arpa
ucl:ucl-cs.arpa
usc-isid:usc-isid.arpa
edn-noc:edn-noc.arpa
mit-multics:mit-multics.arpa 44a
anl-mcs:anl-mcs.arpa 44a
uci-750a:uci-750a.arpa 44a
mit-mc:mit-mc.arpa 44a
mit-ai:mit-ai.arpa 44a
ucla-ccn:ucla-ccn.arpa 44a
#
#
# ordinary TCP hosts
yuma:yuma.arpa
edn-vax:edn-vax.arpa
bbncca:bbncca.arpa
bbnccb:bbnccb.arpa
bbnccd:bbnccd.arpa
bbncck:bbncck.arpa
bbn-labs-b:bbn-labs-b.arpa
bbnlb:bbn-labs-b.arpa
labs-b:bbn-labs-b.arpa
bbnccg:bbnccg.arpa
bbncc2-tac:bbncc2-tac.arpa
bbncc2:bbncc2-tac.arpa
bbnccr:bbnccr.arpa
bbnccf:bbnccf.arpa
bbncci:bbncci.arpa
bbnh:bbnh.arpa
bbncd2:bbncd2.arpa
cdnoc:bbncd2.arpa
.
.
.
.
:jc
clement:leung
cn:robert
cockcroft:phil
rabbit:phil
ceasteal:charles
ckennington:cjk
coulouris:george
crowcroft:jon
daniel:tom
dbrink:brink
.
.
.
.
.

#
#       User table Sectiom
#
#        Memebers-of-staff
msg:~msg@44a
account:~mmdf/samples/uclvax2/table/arpa.head   444      0     12         644  3620510463  12621 #
#        arpa-domain-table
#       note ucl and isid at top of file
#       WARNING THIS FILE IS MACHINE GENERATED
ucl-cs:ucl-cs.arpa
ucl-tg:ucl-cs.arpa
ucl-niftp:ucl-cs.arpa
ucl:ucl-cs.arpa
usc-isid:usc-isid.arpa
edn-noc:edn-noc.arpa
mit-multics:mit-multics.arpa 44a
anl-mcs:anl-mcs.arpa 44a
uci-750a:uci-750a.arpa 44a
mit-mc:mit-mc.arpa 44a
mit-ai:mit-ai.arpa 44a
ucla-ccn:ucla-ccn.arpa 44a
#
#
# ordinary TCP hosts
yuma:yuma.arpa
edn-vax:edn-vax.arpa
bbncca:bbncca.arpa
bbnccb:bbnccb.arpa
bbnccd:bbnccd.arpammdf/samples/uclvax2/table/auth   444      0     12       10020  3620510464  11765 #
#       Authorisation File
#
#  -
#
#  1styr
afurse:both local,niserc
ajolly:both local,niserc
akirby:both local,niserc
akusytsc:both local,niserc
amoore:both local,niserc
bardzins:both local,niserc
benson:both local,niserc
bking:both local,niserc
bmorjari:both local,niserc
boswell:both local,niserc
brahim:both local,niserc
.
.
.
.


#
#  aforrest
aforrest:both local,niserc,tunnel,nipss
#
#  ahbond
ahbond:both local,niserc,tunnel,nipss
#
#  aherbert
aherbert:both local,niserc,tunnel,nipss
#
#  ajcole
ajcole:both local,niserc,tunnel,nipss
cbl6ac@leva.AC.UK:both local,niserc,tunnel,nipss
#
#  ash
@caga.AC.UK,MA11@camphx.AC.UK:both local,niserc,tunnel,nipss
@caga.AC.UK,mje2@camphx.AC.UK:both local,niserc,tunnel,nipss
ash:both local,niserc,tunnel,nipss
#
#  aston
aston:both local,niserc,tunnel,nipss
#
#  atoddpok
#
#  aucc
#
#  bin
mmdfa:both local,niserc,satnet,nipss,niipss
mmdfb:both local,niserc,satnet,nipss,niipss
#
#  bjswill
bjswill:both local,niserc,tunnel,nipss
#
#  blacknst
blacknst:both local,niserc,satnet,nipss
#
#  bmdiaz
bmdiaz:both local,niserc,tunnel,nipss
#
#  braden
#
#  bradshaw
bradshaw:both local,niserc,tunnel,nipss
#
#  bshackel
bshackel:both local,niserc,satnet,nipss
#
#  bwelham
#
#  camphx
@caga.AC.UK,jml4@camjenny.AC.UK:both local,niserc,tunnel,nipss
@caga.AC.UK,jml4@camphx.AC.UK:both local,niserc,tunnel,nipss
@caga.AC.UK,ljf1@camphx.AC.UK:both local,niserc,tunnel,nipss
@caga.AC.UK,nwa1@camphx.AC.UK:both local,niserc,tunnel,nipss
@caga.AC.UK,postmaster@camphx.AC.UK:both local,niserc,tunnel,nipss
@caga.AC.UK,rd100@camphx.AC.UK:both local,niserc,tunnel,nipss
@caga.AC.UK,rs12@camphx.AC.UK:both local,niserc,tunnel,nipss
@caga.AC.UK,tco1@camphx.AC.UK:both local,niserc,tunnel,nipss
@caga.AC.UK,vhc1@camphx.AC.UK:both local,niserc,tunnel,nipss
camphx:both local,niserc,tunnel,nipss
cpuc08@caga.AC.UK:both local,niserc,tunnel,nipss
line@caga.AC.UK:both local,niserc,tunnel,nipss
#
#  cbjones
cbjones:both local,niserc,tunnel,nipss
#
#  ccooper
@caga.AC.UK,@Camjenny.AC.UK,iml@camring.AC.UK:both local,niserc,tunnel,nipss
afe@rlgd.AC.UK:both local,niserc,tunnel,nipss
agw@rlgd.AC.UK:both local,niserc,tunnel,nipss
camb@rlgb.AC.UK:both local,niserc,tunnel,nipss
cja@rlgd.AC.UK:both local,niserc,tunnel,nipss
csc@rlgd.AC.UK:both local,niserc,tunnel,nipss
jwb@rlgd.AC.UK:both local,niserc,tunnel,nipss
rdp@rlgd.AC.UK:both local,niserc,tunnel,nipss
.
.
.
.

#
#  lists
1styr-outbound:recv satnet,niserc,nipss,local,niipss,tunnel,tunnil
1styr-request:send satnet,niserc,nipss,local,niipss,tunnel,tunnil
2ndyr-outbound:recv satnet,niserc,nipss,local,niipss,tunnel,tunnil
2ndyr-request:send satnet,niserc,nipss,local,niipss,tunnel,tunnil
3rdyr-outbound:recv satnet,niserc,nipss,local,niipss,tunnel,tunnil
3rdyr-request:send satnet,niserc,nipss,local,niipss,tunnel,tunnil
A-team-request:send satnet,niserc,nipss,local,niipss,tunnel,tunnil
a-team-outbound:recv satnet,niserc,nipss,local,niipss,tunnel,tunnil
academic-outbound:recv satnet,niserc,nipss,local,niipss,tunnel,tunnil
academic-request:send satnet,niserc,nipss,local,niipss,tunnel,tunnil
advisers-request:send satnet,niserc,nipss,local,niipss,tunnel,tunnil
advisors-request:send satnet,niserc,nipss,local,niipss,tunnel,tunnil
amethyst-users-request:send satnet,niserc,nipss,local,niipss,tunnel,tunnil
arms-d-outbound:recv satnet,niserc,nipss,local,niipss,tunnel,tunnil
arms-d-request:send satnet,niserc,nipss,local,niipss,tunnel,tunnil
.
.
.
.

,include,/44b/2ndyr/sweeney/viswat.list:both local,niserc
,include,/etc/alias/1styr:both local,niserc
,include,/etc/alias/2ndyr:both local,niserc
,include,/etc/alias/3rdyr:both local,niserc
,include,/etc/alias/a-team:both local,niserc
,include,/etc/alias/academic:both local,niserc
,include,/etc/alias/advisers:both local,niserc
,include,/etc/alias/arpa/uk-uk-accts:both local,niserc
,include,/etc/alias/bbcusers:both local,niserc
,include,/etc/alias/bugs:both local,niserc
,include,/etc/alias/coffee:both local,niserc
,include,/etc/alias/com-x400-impl:both local,niserc
.
.
.
.

#
#  young
young:both local,niserc,tunnel,nipss
#
#  zorn
zorn:both local,niserc,tunnel,nipss
b/mmdf/bnewsinput arms-d
arms-d-outbound: :include:/service/arpa/edaib/info.arms@44d
arms-d-request:d.plummer@edxa
com-x400-implementors:com-x400-implementors-outbound@44a-list,x_com-x400-implementors@44a
x_com-x400-implementors:mmdf|/lib/mmdf/bnewsinput com-x400-implementors
com-x400-implementors-outbound: :include:/etc/alias/com-x400-impl@44a
com-x400-implementors-request:steve
header-people:header-people@mit-mc
ucl-header-people:header-people-outbound@44d-list,x_header-people@44a
x_headermmdf/samples/uclvax2/table/helplist   444      0     12        3132  3620510464  12636 This list is the first pass at a help list.  I hope that it
will be improved to become more helpful!   Eventually, all the
lists should have brief descriptions, and all the more detailed
documents will be assembled in one place.

                                SEK


UCL INTERNAL LISTS

Note:  Other regularly used lists are in fact the sum of two or
more of these lists.  A user should join those sublists which
seem appropriate.  In particular:
        all = staff + students
        students = 1styr + 2ndyr + 3rdyr + msc
        msc = mscit + msccs
        staff = research + academic

A full explanation of these lists will be found in the UCL
directory (in preparation)

academic
advisors
bbcusers
bugs
coffee
dcs
external
gateway
graphics        :UCL graphics interest group
hog
indra
itcourse
liaison
measurements
mos
ms-users
mscit
msccs
research
research-students :UCL Phd and Mphi.l studentsstu
ringgp
sas             :UCL Stats and Alarm system developers
service
teletex
terminalwp      :UCL Terminal working party
uksat
wap
wicatusers
1styr
2ndyr
3rdyr


UNIVERSE LISTS  (see /docs/UP/UP??????)

universe
up-daily
up-grp
up-weekly
up-monthly
up-pic
up-piwp
up-cwp
up-x25wp
up-dcwp
up-sewp
up-dwp
up-twg
up-reps

NETWORK LISTS MANAGED AT UCL

mailgroup       :Items relevant to mail services in the UK
niftp-group     :obsolete???


ARPANET LIST EXPANSIONS

A full explanation and list of lists may be found in /service/docs/arpa.mlist

arms-d
header-people
human-nets
info-ai
info-graphics
info-micro
info-music
info-prolog
info-prolog-hackers
info-unix
info-vax
info-works
msggroup
telecom
tcp-ip
unix-wizards
al,niipss,tunnel,tunnil
3rdyr-outbound:recv satnet,niserc,nipss,local,niipss,tunnel,tunnil
3rdyr-request:send satnet,niserc,nipss,local,niipss,tunnel,tunnil
A-team-request:send satnet,niserc,nipss,local,niipss,tunnel,tunnil
a-team-outbound:recv satnet,niserc,nipss,local,niipss,tunnel,tunnil
academic-outbound:recv satnet,niserc,nipss,local,niipss,tunnel,tunnil
academic-request:send satnet,niserc,nipss,local,niipss,tunnemmdf/samples/uclvax2/table/ipss.names   444      0     12         204  3620510464  13047 #
# Hosts reached through IPSS
# Currently all indirectly through YORK
ykxa:ykxa/mail
qz:qz
oden:oden
qzcom:qzcom
chalmers:chalmers
e UCL
directory (in preparation)

academic
advisors
bbcusers
bugs
coffee
dcs
external
gateway
graphics        :UCL graphics interest group
hog
indra
itcourse
liaison
measurements
mos
ms-users
mscit
msccs
research
research-students :UCL Phd and Mphi.l studentsstu
ringgp
sas             :UCL Stats and Alarm system developers
service
teletex
terminalwp      :UCL Terminal working pmmdf/samples/uclvax2/table/karl.names   444      0     12          46  3620510464  13006 karlsruhe:karlsruhe
germany:karlsruhe
ntly all indirectly through YORK
ykxa:ykxa/mail
qz:qz
oden:oden
qzcom:qzcom
chalmers:chalmers
e UCL
directory (in preparation)

academic
advisors
bbcusers
bugs
coffee
dcs
external
gateway
graphics        :UCL graphics interest group
hog
indra
itcourse
liaison
measurements
mos
ms-users
mscit
msccs
research
research-students :UCL Phd and Mphi.l studentsstu
ringgp
sas             :UCL Stats and Alarm system developers
service
teletex
terminalwp      :UCL Terminal working pmmdf/samples/uclvax2/table/mailnet   444      0     12        2063  3620510464  12445 ucl-cs:ucl-cs
ucl-cs-mailnet:ucl-cs
carnegie:carnegie.mailnet,mit-multics.arpa
cornella:cornella.mailnet,mit-multics.arpa
dickinson:dickinson.mailnet,mit-multics.arpa
durham:durham.mailnet,mit-multics.arpa
educom:educom.mailnet,mit-multics.arpa
harvard:harvard.mailnet,mit-multics.arpa
iowa-state:iowa-state.mailnet,mit-multics.arpa
maccwisc:maccwisc.mailnet,mit-multics.arpa
newcastle:newcastle.mailnet,mit-multics.arpa
numac:newcastle.mailnet,mit-multics.arpa
njit-eies:njit-eies.mailnet,mit-multics.arpa
njit:njit-eies.mailnet,mit-multics.arpa
northwestern:northwestern.mailnet,mit-multics.arpa
oden:oden.mailnet,mit-multics.arpa
qucdn:qucdn.mailnet,mit-multics.arpa
qz-com:oden.mailnet,mit-multics.arpa
qzcom:oden.mailnet,mit-multics.arpa
rpi-mts:rpi-mts.mailnet,mit-multics.arpa
scal:scal.mailnet,mit-multics.arpa
stanford:stanford.mailnet,mit-multics.arpa
swarthmore:swarthmore.mailnet,mit-multics.arpa
uchicago:uchicago.mailnet,mit-multics.arpa
umich-mts:umich-mts.mailnet,mit-multics.arpa
union:union.mailnet,mit-multics.arpa
merit:umich-mts.mailnet,mit-multics.arpa
s
msggroup
telecom
tcp-ip
unix-wizards
al,niipss,tunnel,tunnil
3rdyr-outbound:recv satnet,niserc,nipss,local,niipss,tunnel,tunnil
3rdyr-request:send satnet,niserc,nipss,local,niipss,tunnel,tunnil
A-team-request:send satnet,niserc,nipss,local,niipss,tunnel,tunnil
a-team-outbound:recv satnet,niserc,nipss,local,niipss,tunnel,tunnil
academic-outbound:recv satnet,niserc,nipss,local,niipss,tunnel,tunnil
academic-request:send satnet,niserc,nipss,local,niipss,tunnemmdf/samples/uclvax2/table/niftp.nets   444      0     12         151  3620510465  13061 unix:sring
u:sring
serc:niserc
src:niserc
srcx:niserc
srcy:niserc
s:niserc
pss:nipss
p:nipss
ipss:niipss
mit-multics.arpa
dickinson:dickinson.mailnet,mit-multics.arpa
durham:durham.mailnet,mit-multics.arpa
educom:educom.mailnet,mit-multics.arpa
harvard:harvard.mailnet,mit-multics.arpa
iowa-state:iowa-state.mailnet,mit-multics.arpa
maccwisc:maccwisc.mailnet,mit-multics.arpa
newcastle:newcastle.mailnet,mit-multics.arpa
numac:newcastle.mailnet,mit-multics.arpa
njit-eies:njit-eies.mailnet,mit-multics.arpa
njit:mmdf/samples/uclvax2/table/pss.names   444      0     12         575  3620510465  12712 #
#        pss-host-names
#
#        44d-used-as-relay
ucl-cs:pss/ucl/mail
cardiff:cardiff/mail
essex:essex/mail
gecd:gecd/mail
hatfield:hatfield/mail
oden:oden/mail
qz:qz/mail
rsre:rsre/mail
st-andrews:st-andrews/mail
stand:st-andrews/mail
uwist:uwist/mail
aucc:aucc/mail
r-d.salford:salford/mail
iclkids:iclkids/mail
mrcb:mrcb/mail
mrc:mrc/mail
aere:aere/mail
are-pn:are-pn/mail
stle:newcastle.mailnet,mit-multics.arpa
numac:newcastle.mailnet,mit-multics.arpa
njit-eies:njit-eies.mailnet,mit-multics.arpa
njit:mmdf/samples/uclvax2/table/serc.names   444      0     12        1612  3620510465  13052 #
#        sercnet-host-table
#
#       last updated 23-August-84
#
#        relay ring-host-names
44a:smtp44a
44b:smtp44b
44c:smtp44c
44d:smtp44d
44e:smtp44e
#
# names which disagree with Serc naming
rco:edxd/mail
#
#         hosts-reached-driectly
aberdeen:aberdeen/mail
apca:apca/mail
aston:aston/mail
baca:baca/mail
bapa:bapa/mail
bdga:bdga/mail
bdgb:bdgb/mail
bedford:bedford/mail
keca:keca/mail
ktda:ktda/mail
eagle:eagle/mail
ukc:ukc.eagle/mail
emas.ukc:ukc.emas/mail
kwga:kwga/mail
vax1.central.lancaster:lanc.cent.vax1/mail
icl.central.lancaster:lanc.cent.icl/mail
computing.lancaster:lanc.comp/mail
engineering.lancaster:lanc.eng/mail
lava:lava/mail
lawa:lawa/mail
zuxb:zuxb/mail
zuxc:zuxc/mail
zuxu:zuxu/mail
zwga:zwga/mail
#
#       pseudo-hosts for trace info
#       as the mail and niftp do not agree as to which is the
#       official name!!
york:ykxa/mail
ercc:edxa/mail
ucl-cs:zuxa/mail
pa
uchicago:uchicago.mailnet,mit-multics.arpa
umich-mts:umich-mts.mailnet,mit-multics.arpa
union:union.mailnet,mit-mulmmdf/samples/uclvax2/table/smtp.head   444      0     12         443  3620510465  12660 #
#       SMTP channel table
#
#       Note ucl-cs and isid at top of tables
#       WARNING THISFILE IS MACHINE GENERATED !!!!!!!!!
ucl-cs.arpa:128.16.9.3
ucl-cs.arpa:14.0.0.9
ucl-cs:128.16.9.3
# stc.arpa:128.16.9.9
# usc-isid.arpa:10.0.0.27
edn-noc.arpa:7.0.0.1
#
#        smtp-host-names
on:aston/mail
baca:baca/mail
bapa:bapa/mail
bdga:bdga/mail
bdgb:bdgb/mail
bedford:bedford/mail
keca:keca/mail
ktda:ktda/mail
eagle:eagle/mail
ukc:ukc.eagle/mail
emas.ukc:ukc.emas/mail
kwga:kwga/mail
vax1.central.lancastermmdf/samples/uclvax2/table/smtp.names   444      0     12        1001  3620510465  13071 #
#       SMTP channel table
#
#       Note ucl-cs and isid at top of tables
#       WARNING THISFILE IS MACHINE GENERATED !!!!!!!!!
ucl-cs.arpa:128.16.9.3
ucl-cs.arpa:14.0.0.9
ucl-cs:128.16.9.3
# stc.arpa:128.16.9.9
# usc-isid.arpa:10.0.0.27
edn-noc.arpa:7.0.0.1
#
#        smtp-host-names
yuma.arpa:6.1.0.1
edn-vax.arpa:7.0.0.3
bbncca.arpa:8.0.0.2
bbnccb.arpa:8.1.0.2
bbnccd.arpa:8.3.0.2
bbncck.arpa:8.5.0.2
bbn-labs-b.arpa:8.7.0.2
bbn-labs-b.arpa:128.11.1.2
bbnccg.arpa:8.0.0.3
bbncc2-tac.arpa:8.2.0.3
.
.
.
.
lanc.cent.vax1/mail
icl.central.lancaster:lanc.cent.icl/mail
computing.lancaster:lanc.comp/mail
engineering.lancaster:lanc.eng/mail
lava:lava/mail
lawa:lawa/mail
zuxb:zuxb/mail
zuxc:zuxc/mail
zuxu:zuxu/mail
zwga:zwga/mail
#
#       pseudo-hosts for trace info
#       as the mail and niftp do not agree as to which is the
#       official name!!
york:ykxa/mail
ercc:edxa/mail
ucl-cs:zuxa/mail
pa
uchicago:uchicago.mailnet,mit-multics.arpa
umich-mts:umich-mts.mailnet,mit-multics.arpa
union:union.mailnet,mit-mulmmdf/samples/uclvax2/table/smtp.nets   444      0     12         174  3620510466  12732 14.0.0.9:tunnel
128.16.9.9:tunnel
128.16.9.3:satnet
44a:sring
44b:sring
44c:sring
44d:sring
44e:sring
vax1:sring
vax2:sring
!!!!!!!!
ucl-cs.arpa:128.16.9.3
ucl-cs.arpa:14.0.0.9
ucl-cs:128.16.9.3
# stc.arpa:128.16.9.9
# usc-isid.arpa:10.0.0.27
edn-noc.arpa:7.0.0.1
#
#        smtp-host-names
yuma.arpa:6.1.0.1
edn-vax.arpa:7.0.0.3
bbncca.arpa:8.0.0.2
bbnccb.arpa:8.1.0.2
bbnccd.arpa:8.3.0.2
bbncck.arpa:8.5.0.2
bbn-labs-b.arpa:8.7.0.2
bbn-labs-b.arpa:128.11.1.2
bbnccg.arpa:8.0.0.3
bbncc2-tac.arpa:8.2.0.3
.
.
.
.mmdf/samples/uclvax2/table/sring.names   444      0     12         434  3620510466  13222 #
#        ring-host-names
ucl-cs.arpa:128.16.9.3
44a:smtp44a
44b:smtp44b
44c:smtp44c
44d:smtp44d
44e:smtp44e
vax1:smtpvax1
vax2:smtpvax2
ucl-ean.ac.uk:smtpvax2
44a-list:smtp44a
44b-list:smtp44b
44c-list:smtp44c
44d-list:smtp44d
44e-list:smtp44e
vax1-list:smtpvax1
vax2-list:smtpvax2
-names
yuma.arpa:6.1.0.1
edn-vax.arpa:7.0.0.3
bbncca.arpa:8.0.0.2
bbnccb.arpa:8.1.0.2
bbnccd.arpa:8.3.0.2
bbncck.arpa:8.5.0.2
bbn-labs-b.arpa:8.7.0.2
bbn-labs-b.arpa:128.11.1.2
bbnccg.arpa:8.0.0.3
bbncc2-tac.arpa:8.2.0.3
.
.
.
.mmdf/samples/uclvax2/table/sweden   444      0     12         274  3620510466  12265 #
#       Swedish host names
# Note extra pseudo hop for now, as York drops the lat domain
# without checking.
qzcom:qzcom qzcom
qz:qzcom qzcom
oden:qzcom qzcom
chalmers:chalmers chalmers
mtp44b
44c-list:smtp44c
44d-list:smtp44d
44e-list:smtp44e
vax1-list:smtpvax1
vax2-list:smtpvax2
-names
yuma.arpa:6.1.0.1
edn-vax.arpa:7.0.0.3
bbncca.arpa:8.0.0.2
bbnccb.arpa:8.1.0.2
bbnccd.arpa:8.3.0.2
bbncck.arpa:8.5.0.2
bbn-labs-b.arpa:8.7.0.2
bbn-labs-b.arpa:128.11.1.2
bbnccg.arpa:8.0.0.3
bbncc2-tac.arpa:8.2.0.3
.
.
.
.mmdf/samples/uclvax2/table/top   444      0     12         517  3620510467  11603 uucp:ucb-vax.arpa
bitnet:wiscvm.arpa
mailnet:mit-multics.arpa
csnet:csnet-relay.arpa
dec:decwrl.arpa
cdn:ubc.csnet
cern:ucl-ean.ac.uk
# The domain for karlsruhe
germany:germany 44a
karlsruhe:germany 44a
karlsruh:germany 44a
# To stop sillies like ARPA.MULTICS.AC.UK
ucl-ean.uk:ean.vax2.ucl-cs.ac.uk
uk:xxxxxxxxxxxxxx
arpa:yyyyyyyyyyyy
a.arpa:8.0.0.2
bbnccb.arpa:8.1.0.2
bbnccd.arpa:8.3.0.2
bbncck.arpa:8.5.0.2
bbn-labs-b.arpa:8.7.0.2
bbn-labs-b.arpa:128.11.1.2
bbnccg.arpa:8.0.0.3
bbncc2-tac.arpa:8.2.0.3
.
.
.
.mmdf/samples/uclvax2/table/ucl-cs   444      0     12         332  3620510467  12162 44a:44a
44b:44b
44c:44c
44d:44d
44e:44e
vax1:vax1
vax2:vax2
vax:vax1
44a-list:44a-list
44b-list:44b-list
44c-list:44c-list
44d-list:44d-list
44e-list:44e-list
vax1-list:vax1-list
vax2-list:vax2-list
vax-list:vax1-list
y 44a
# To stop sillies like ARPA.MULTICS.AC.UK
ucl-ean.uk:ean.vax2.ucl-cs.ac.uk
uk:xxxxxxxxxxxxxx
arpa:yyyyyyyyyyyy
a.arpa:8.0.0.2
bbnccb.arpa:8.1.0.2
bbnccd.arpa:8.3.0.2
bbncck.arpa:8.5.0.2
bbn-labs-b.arpa:8.7.0.2
bbn-labs-b.arpa:128.11.1.2
bbnccg.arpa:8.0.0.3
bbncc2-tac.arpa:8.2.0.3
.
.
.
.mmdf/samples/uclvax2/table/uucp   444      0     12        1020  3620510467  11763 #
# European UUCP (Eunet) Names
# Permanent header
ucl-cs:ucl-cs
camjenny:camjenny,caga
camsteve:camsteve,caga
cuedvax:cuedvax,caga
# UUCP stuff from mkpath
ace:ace.uucp 44d
ariadne:ariadne.uucp 44d
axon:axon.uucp 44d
cernvax:cernvax.uucp 44d
cfg:cfg.uucp 44d
chalmers:chalmers.uucp 44d
cjn:cjn.uucp 44d
cki:cki.uucp 44d
cncaltu:cncaltu.uucp 44d
cnetslc:cnetslc.uucp 44d
crcad:crcad.uucp 44d
crcge1:crcge1.uucp 44d
crcge2:crcge2.uucp 44d
csbstand:csbstand.uucp 44d
csg:csg.uucp 44d
decsto:decsto.uucp 44d
decvax:decvax.uucp 44d
mail
icl.central.lancaster:lanc.cent.icl/mail
computing.lancaster:lanc.comp/mail
engineering.lancaster:lanc.eng/mail
lava:lava/mail
lawa:lawa/mail
zuxb:zuxb/mail
zuxc:zuxc/mail
zuxu:zuxu/mail
zwga:zwga/mail
#
#       pseudo-hosts for trace info
#       as the mail and niftp do not agree as to which is the
#       official name!!
york:ykxa/mail
ercc:edxa/mail
ucl-cs:zuxa/mail
pa
uchicago:uchicago.mailnet,mit-multics.arpa
umich-mts:umich-mts.mailnet,mit-multics.arpa
union:union.mailnet,mit-mulmmdf/samples/uclvax2/table/uucp.names   444      0     12        2216  3620510467  13075 ucl-cs.uucp:ucl-cs!%s
ace.uucp:kcl-cs!ukc!mcvax!ace!%s
ariadne.uucp:kcl-cs!ukc!mcvax!ariadne!%s
axon.uucp:kcl-cs!ukc!mcvax!enea!axon!%s
camjenny.uucp:camjenny!%s
camsteve.uucp:camjenny!camsteve!%s
cernvax.uucp:cernvax!%s
cfg.uucp:kcl-cs!ukc!cfg!%s
chalmers.uucp:kcl-cs!ukc!mcvax!enea!chalmers!%s
cjn.uucp:kcl-cs!ukc!mcvax!kvport!taycs!cjn!%s
cki.uucp:kcl-cs!ukc!mcvax!diku!cki!%s
cncaltu.uucp:kcl-cs!ukc!mcvax!inria!cncaltu!%s
cnetslc.uucp:kcl-cs!ukc!mcvax!vmucnam!cnetslc!%s
crcad.uucp:kcl-cs!ukc!mcvax!diku!crcad!%s
crcge1.uucp:kcl-cs!ukc!mcvax!vmucnam!crcge1!%s
crcge2.uucp:kcl-cs!ukc!mcvax!vmucnam!crcge1!crcge2!%s
csbstand.uucp:kcl-cs!glasgow!csbstand!%s
csg.uucp:kcl-cs!ukc!mcvax!ikogsmb!csg!%s
cuedvax.uucp:camjenny!camsteve!cuedvax!%s
decsto.uucp:kcl-cs!ukc!mcvax!enea!decsto!%s
decvax.uucp:kcl-cs!ukc!mcvax!decvax!%s
dice.uucp:kcl-cs!hwcs!edcaad!dice!%s
dido.uucp:kcl-cs!ukc!mcvax!enea!dido!%s
diku.uucp:kcl-cs!ukc!mcvax!diku!%s
dutentb.uucp:kcl-cs!ukc!mcvax!icnned!dutentb!%s
dutesta.uucp:kcl-cs!ukc!mcvax!dutesta!%s
dutinfa.uucp:kcl-cs!ukc!mcvax!dutinfd!dutinfa!%s
dutinfd.uucp:kcl-cs!ukc!mcvax!dutinfd!%s
echbull.uucp:kcl-cs!ukc!mcvax!vmucnam!echbull!%s
iserc,nipss,local,niipss,tunnel,tunnil
3rdyr-request:send satnet,niserc,nipss,local,niipss,tunnel,tunnil
A-team-request:send satnet,niserc,nipss,local,niipss,tunnel,tunnil
a-team-outbound:recv satnet,niserc,nipss,local,niipss,tunnel,tunnil
academic-outbound:recv satnet,niserc,nipss,local,niipss,tunnel,tunnil
academic-request:send satnet,niserc,nipss,local,niipss,tunnemmdf/samples/uclvax2/table/vax1-list.name   444      0     12          24  3620510467  13521 vax1-list:vax1-list
s
ace.uucp:kcl-cs!ukc!mcvax!ace!%s
ariadne.uucp:kcl-cs!ukc!mcvax!ariadne!%s
axon.uucp:kcl-cs!ukc!mcvax!enea!axon!%s
camjenny.uucp:camjenny!%s
camsteve.uucp:camjenny!camsteve!%s
cernvax.uucp:cernvax!%s
cfg.uucp:kcl-cs!ukc!cfg!%s
chalmers.uucp:kcl-cs!ukc!mcvax!enea!chalmers!%s
cjn.uucp:kcl-cs!ukc!mcvax!kvport!taycs!cjn!%s
cki.uucp:kcl-cs!ukc!mcvax!diku!cki!%s
cncaltu.uucp:kcl-cs!ukc!mcvax!inria!cncaltu!%s
cnetslc.uucp:kcl-cs!ukc!mcvax!vmucnam!cnetslc!%s
crcad.uucp:kcl-cs!ukc!mcvax!diku!crcmmdf/samples/uclvax2/table/vax1.names   444      0     12         154  3620510470  12751 ucl-cs:ucl-cs
ucl-cs.ac.uk:ucl-cs
ucl-cs.arpa:ucl-cs
vax1:ucl-cs
vax1.ac.uk:ucl-cs
vax1.ucl-cs.ac.uk:ucl-cs
l-cs!ukc!mcvax!enea!axon!%s
camjenny.uucp:camjenny!%s
camsteve.uucp:camjenny!camsteve!%s
cernvax.uucp:cernvax!%s
cfg.uucp:kcl-cs!ukc!cfg!%s
chalmers.uucp:kcl-cs!ukc!mcvax!enea!chalmers!%s
cjn.uucp:kcl-cs!ukc!mcvax!kvport!taycs!cjn!%s
cki.uucp:kcl-cs!ukc!mcvax!diku!cki!%s
cncaltu.uucp:kcl-cs!ukc!mcvax!inria!cncaltu!%s
cnetslc.uucp:kcl-cs!ukc!mcvax!vmucnam!cnetslc!%s
crcad.uucp:kcl-cs!ukc!mcvax!diku!crcmmdf/samples/uclvax2/table/vax1.tailor   444      0     12       11531  3622770743  13216 MCHANLOG   level=GEN, size=400, stat=SOME
MMSGLOG    level=GEN, size=40, stat=SOME
PHLOG      level=GEN, size=40, stat=SOME
MIPCSLEVEL 0

; special strings
MLOCMACHINE     vax1
MLNAME    Cs
MLDOMAIN  "Ucl.AC.UK"
MSIG      "Memo Service (mmdf.1.86)"

; special directories
MTBLDIR    "/usr/mmdf/mmdftable"
MQUEDIR    "/usr/spool/mmdflock/que"

;  general tables
MTBL  name=aliases, file=aliases, show="User & list aliases"
MTBL  name=auth, file=auth, show="Authorisation file"

; flow tables
MTBL  name=mod-auth, file=mod.auth, show="MOD Host control"

; Domain tables
MTBL    "Cs",file="ucl-cs",show="Cs.Ucl.AC.UK domain table"
MDMN    "Cs.Ucl.AC.UK",show="Cs.Ucl.AC.UK domain",table="Cs"
MTBL    "Ucl-AC-UK",file="uk-ac-ucl",show="Ucl.AC.UK domain table"
MDMN    "Ucl.AC.UK",show="Ucl.AC.UK domain",table="Ucl-AC-UK"
MTBL    "AC-UK",file="uk-ac",show="AC.UK Domain"
MDMN    "AC.UK",show="AC.UK Domain",table="AC-UK"
MTBL    "CO-UK",file="uk-co",show="CO.UK Domain"
MDMN    "CO.UK",show="CO.UK Domain",table="CO-UK"
MTBL    "MOD-UK",file="uk-mod",show="MOD.UK Domain"
MDMN    "MOD.UK",show="MOD.UK Domain",table="MOD-UK"
MTBL    "UK",file="uk",show="UK Domain"
MDMN    "UK",show="UK Domain",table="UK"
MDMN    "UCL",show="UCL.AC.UK Domain (UCL)",table="Ucl-AC-UK",name="Ucl",dmn="UCL.AC.UK"
MTBL    "UUCP",file="uucp",show="Usenet Domain"
MDMN    "UUCP",show="Usenet Domain",table="UUCP",lname="ucl-cs"
;  Note flags=ns and flags=domain
MTBL    "ARPA",show="ARPA Domain",flags=ns,flags=domain
MDMN    "ARPA",show="ARPA Domain",table="ARPA",lname="ucl-cs"
MTBL    "EDU",show="EDU Domain",flags=ns,flags=domain
MDMN    "EDU",show="EDU Domain",table="EDU"
MTBL    "COM",show="COM Domain",flags=ns,flags=domain
MDMN    "COM",show="COM Domain",table="COM"
MTBL    "GOV",show="GOV Domain",flags=ns,flags=domain
MDMN    "GOV",show="GOV Domain",table="GOV"
MTBL    "ORG",show="ORG Domain",flags=ns,flags=domain
MDMN    "ORG",show="ORG Domain",table="ORG"
MTBL    "MIL",show="MIL Domain",flags=ns,flags=domain
MDMN    "MIL",show="MIL Domain",table="MIL"
MTBL    "NET",show="NET Domain",flags=ns,flags=domain
MDMN    "NET",show="NET Domain",table="NET"
MTBL    "US",show="US Domain",flags=ns,flags=domain
MDMN    "US",show="US Domain",table="US"
MDMN    "AC",show="AC.UK Domain (AC)",table="AC-UK",name="AC",dmn="AC.UK"
MDMN    "CO",show="CO.UK Domain (CO)",table="CO-UK",name="CO",dmn="CO.UK"
MTBL    "Top-Level",file="top",show="Top Level Domain"
MDMN    "Top-Level",show="Top level Domain",table="Top-Level",name="",dmn=""


;  channel definitions
MTBL  local     file="vax1.names"
MCHN  local    name=local,que=local,tbl=local,show="Local (vax1)",pgm=local,poll=0,mod=reg,ap=733,ap=big,level=BST

MTBL  list     file="vax1-list.names"
MCHN  list    name=list,que=list,tbl=list,show="via List-Channel",pgm=list,poll=0,mod=reg,host=Cs.Ucl.AC.UK,level=FST

MTBL  ean file="ean.names"
MCHN  ean name=ean,que=ean,tbl=ean,show="via EAN",pgm=ean,poll=0,mod=reg,host="EAN.VAX2.CS.UCL.AC.UK",ap=733,auth=inlog,auth=outlog,level=FST

MTBL  ether file="ether.names"
MCHN  ether name=ether,que=ether,tbl=ether,show="via Ethernet with SMTP",pgm=smtp,poll=0,mod=reg,level=FST

MTBL  sring    file="sring.names"
MCHN  sring name=sring,que=sring,tbl=sring,show="with SMTP",pgm=uclsmtp,poll=0,mod=reg,confstr="%s/",level=FST

MTBL  niserc    file="janet.names"
MCHN  niserc    name=niserc,que=niserc,tbl=niserc,show="via Janet with NIFTP",pgm=niftp,poll=0,mod=reg,ap=jnt,confstr="/usr/bin/cpf cpf -t -f\"$(SENDER)\" $(FILE) @$(ADR)",auth=inlog,auth=outlog,level=PTR

MTBL  nipss    file="pss.names"
MCHN  nipss    name=nipss,que=nipss,tbl=nipss,show="via PSS with NIFTP",pgm=niftp,poll=0,mod=reg,ap=jnt,confstr="/usr/bin/cpf cpf -t -f\"$(SENDER)\" $(FILE) @$(ADR)",auth=inlog,auth=outlog,level=PTR

MTBL  xuucp     file="vax1.uucp.names"
MCHN  uucp      name=uucp,que=uucp,tbl=xuucp,show="with UUCP",pgm=dmuucp,poll=0,mod=reg,ap=733,lname=ucl-cs,ldomain=uucp,level=FTR

; SMTP channel stuff
MTBL  smtp      flags=ns,flags=channel

; (going over Satnet)
MCHN  satnet     name=satnet,que=satnet,tbl=smtp,show="via Satnet with SMTP",pgm=uclsmtp,poll=0,mod=reg,confstr="tcpx/128.16.9.3,X;%s.25,B,200:",ap=822,auth=inlog,auth=outblock,level=PTR

; (going through tunnel)
MCHN  tunnel      name=tunnel,que=tunnel,tbl=smtp,show="via IPSS-Tunnel with SMTP",pgm=uclsmtp,poll=0,mod=reg,confstr="tcpx/14.0.0.9,X;%s.25,B,200:",ap=822,known=smtp,auth=inlog,auth=outblock,level=PTR

;  Table delay must have a file
;  'host' must be set otherwise auth/submit screws up
MTBL  delay    file="vax1-list.names"
MCHN  delay    name=delay,que=delay,show="via Delay-Channel",pgm=delay,poll=0,mod=reg,level=FAT,host="Cs.ucl.ac.uk"

; Deliver tailoring
MWARNTIME  72
MFAILTIME  144
MSLEEP     300 ; daemon sleep time

MTBL  name="niftp.nets", file="niftp.nets", show="NIFTP net to channel map"

PHLOG      level=FST
MCHANLOG   level=FST
MMSGLOG    level=FST
AUTHLOG    level=FST, size=40, stat=SOME
ress is for incoming news messages, which routes to
# an x_* address on al machines where the notesfile is stored
# The x_* address specifies input to a notesfile
# Thmmdf/samples/vgr/   755      0     12           0  3635165147   7142 mmdf/samples/vgr/table/   755      0     12           0  3635165146  10230 mmdf/samples/vgr/table/arpa   444      0     12        1173  3620510471  11150 BRL-AOS:BRL-AOS.ARPA
AOS:BRL-AOS.ARPA
RUCKER-VAX:BRL-AOS.ARPA
BRL-CYBER:BRL-CYBER.ARPA
MFA:BRL-CYBER.ARPA
BRL-MIS:BRL-MIS.ARPA
BRL-ZAP:BRL-ZAP.ARPA
BRL-VGR:BRL-VGR.ARPA
VGR:BRL-VGR.ARPA
BRL-TAC2:BRL-TAC2.ARPA
BRL-HEP:BRL-HEP.ARPA
BRL-VAT:BRL-VAT.ARPA
VAT:BRL-VAT.ARPA
BRL-TAC1:BRL-TAC1.ARPA
BRL-TAC:BRL-TAC.ARPA
BRL:BRL.ARPA
BBNCCA:BBNCCA.ARPA
BBNCCB:BBNCCB.ARPA
BBNCCC:BBNCCC.ARPA
BBNCCD:BBNCCD.ARPA
BBN-LABS-ADMIN:BBN-LABS-ADMIN.ARPA
BBN-LABS:BBN-LABS-ADMIN.ARPA
BBNL:BBN-LABS-ADMIN.ARPA
BBNCCK:BBNCCK.ARPA
BBNCCG:BBNCCG.ARPA
BBNCC2-TAC:BBNCC2-TAC.ARPA
BBNCC2:BBNCC2-TAC.ARPA
BBNCCF:BBNCCF.ARPA
BBNCCP:BBNCCP.ARPA
BBNCCI:BBNCCI.ARPA
",flags=ns,flags=domain
MDMN    "US",show="US Domain",table="US"
MDMN    "AC",show="AC.UK Domain (AC)",table="AC-UK",name="AC",dmn="AC.UK"
MDMN    "CO",show="CO.UK Domain (CO)",table="CO-UK",name="CO",dmn="CO.UK"
MTBL    "Top-Level",file="top",show="Top Level Domain"
MDMN    "Top-Level",show="Top level Domain",table="Top-Level",name="",dmn=""


;  channel definitions
MTBL  local     filmmdf/samples/vgr/table/doit   555      0     12         337  3620510471  11150 #! /bin/sh
PATH=.:/bin:/usr/bin
export PATH
umask 002		# vital! -Mike
# to do everything, run this file.
echo "Processing new MMDF tables for `hostname`"
/bin/sh makearpa
/bin/sh makemailids
/bin/sh makealias
dbmbuild -Onv
P.ARPA
BRL-VAT:BRL-VAT.ARPA
VAT:BRL-VAT.ARPA
BRL-TAC1:BRL-TAC1.ARPA
BRL-TAC:BRL-TAC.ARPA
BRL:BRL.ARPA
BBNCCA:BBNCCA.ARPA
BBNCCB:BBNCCB.ARPA
BBNCCC:BBNCCC.ARPA
BBNCCD:BBNCCD.ARPA
BBN-LABS-ADMIN:BBN-LABS-ADMIN.ARPA
BBN-LABS:BBN-LABS-ADMIN.ARPA
BBNL:BBN-LABS-ADMIN.ARPA
BBNCCK:BBNCCK.ARPA
BBNmmdf/samples/vgr/table/list   444      0     12          67  3620510471  11141 list-processor	list-processor
list-proc	list-processor
# vital! -Mike
# to do everything, run this file.
echo "Processing new MMDF tables for `hostname`"
/bin/sh makearpa
/bin/sh makemailids
/bin/sh makealias
dbmbuild -Onv
P.ARPA
BRL-VAT:BRL-VAT.ARPA
VAT:BRL-VAT.ARPA
BRL-TAC1:BRL-TAC1.ARPA
BRL-TAC:BRL-TAC.ARPA
BRL:BRL.ARPA
BBNCCA:BBNCCA.ARPA
BBNCCB:BBNCCB.ARPA
BBNCCC:BBNCCC.ARPA
BBNCCD:BBNCCD.ARPA
BBN-LABS-ADMIN:BBN-LABS-ADMIN.ARPA
BBN-LABS:BBN-LABS-ADMIN.ARPA
BBNL:BBN-LABS-ADMIN.ARPA
BBNCCK:BBNCCK.ARPA
BBNmmdf/samples/vgr/table/local   444      0     12          46  3620510471  11255 brl-vgr.arpa	brl-vgr
vgr.arpa	brl-vgr
c	list-processor
# vital! -Mike
# to do everything, run this file.
echo "Processing new MMDF tables for `hostname`"
/bin/sh makearpa
/bin/sh makemailids
/bin/sh makealias
dbmbuild -Onv
P.ARPA
BRL-VAT:BRL-VAT.ARPA
VAT:BRL-VAT.ARPA
BRL-TAC1:BRL-TAC1.ARPA
BRL-TAC:BRL-TAC.ARPA
BRL:BRL.ARPA
BBNCCA:BBNCCA.ARPA
BBNCCB:BBNCCB.ARPA
BBNCCC:BBNCCC.ARPA
BBNCCD:BBNCCD.ARPA
BBN-LABS-ADMIN:BBN-LABS-ADMIN.ARPA
BBN-LABS:BBN-LABS-ADMIN.ARPA
BBNL:BBN-LABS-ADMIN.ARPA
BBNCCK:BBNCCK.ARPA
BBNmmdf/samples/vgr/table/mailids   444      0     12        3500  3620510471  11643 root	root
root	croot
dae	dae
bin	bin
who	who
mount	mount
init	init
uucp	uucp
ftp	ftp
backup	backup
mmdf	mmdf
harry	harry
learn	learn
notes	notes
news	news
steve	steve
alpha1	alpha1
charlie	charlie
geo	geo
psl	psl
rnj	rnj
mdqs	mdqs
vitali	vitali
bboards	bboards
dickmc	dickmc
sys	sys
doc	doc
osg	osg
tcp-ip	tcp-ip
dpk	dpk
mort	mort
exchange	exchange
earl	earl
mike	mike
sue	mjm
jcp	jcp
randy	randy
fish	fish
ron	ron
doug	doug
ingres	ingres
abc	abc
tom	tom
dll	dll
lax	lax
judy	judy
vann	vann
gwyn	gwyn
reschly	reschly
phd	phd
lee	lee
durf	durf
davisson	davisson
oz	oz
andr	andr
john	john
jerry	jerry
kinch	kinch
hawk	hawk
robert	robert
myra	myra
karen	karen
keith	keith
gary	gary
howard	howard
barb	barb
rsm	rsm
joanw	joanw
paula	paula
dougb	dougb
schlegel	schlegel
bobs	bobs
kermit	kermit
phil	phil
mckusick	mckusick
ginny	ginny
moss	moss
ace	ace
jill	jill
wildman	wildman
ryan	ryan
crimmins	crimmins
lacetera	lacetera
sarge	sarge
lkoke	lkoke
salkind	salkind
manuel	manuel
heiser	heiser
stay	stay
knapp	knapp
dickk	dickk
kirky	kirky
cae	cae
mas	mas
cmoore	cmoore
jra	jra
hans	hans
fsbrn	fsbrn
helfman	helfman
jeffh	jeffh
dumer	dumer
denn	denn
ge	ge
broome	broome
rich	rich
jwood	jwood
matt	matt
hugh	hugh
donnelly	donnelly
todd	todd
muehl	muehl
cohen	cohen
walinch	walinch
reamos	amos
bulmash	bulmash
karr	karr
schmidt	schmidt
gdw	gdw
jdk	jdk
jei	jei
rvm	rvm
gb	gb
dz	dz
cn	cn
mp	mp
dunigan	dunigan
glimm	glimm
thompson	thompson
motz	motz
rbn	rbn
merritt	merritt
andy	andy
rocchio	rocchio
unix-wizards-request	wiz
kille	kille
cain	cain
hanratty	hanratty
aic	aic
whh	whh
hwalt	hwalt
towson	towson
erim	erim
lgarn	lgarn
mills	mills
wouk	wouk
tcs	tcs
det	det
reilly	reilly
launer	launer
jjwu	jjwu
tde	tde
rck	rck
taus	taus
kjm	kjm
riddle	riddle
romanell	romanell
bmermag	bmermag
james	james
torek	torek
mmallott	mmallott
rucker	rucker
holly	holly
domae	domae
="via Janet with NIFTP",pgm=niftp,poll=0,mod=reg,ap=jnt,confstr="/usr/bin/cpf cpf -t -f\"$(SENDER)\" $(FILE) @$(ADR)",auth=inlog,auth=outlog,level=PTR

MTBL  nipss    file="pss.names"
MCHN  nimmdf/samples/vgr/table/makealias   555      0     12        2121  3620510472  12152 #!/bin/sh
if test ! -r alias.local
then
	echo "Cannot read 'alias.local'"
	exit 1
elif test ! -r alias.global
then
	echo "Cannot read 'alias.global'"
	exit 1
fi

trap 'rm -f /tmp/$$.* newalias 2> /dev/null; echo --Aborted--; exit' 1 2 3 15
set -u
umask 077
LOCALHOST=`hostname`
echo "Building alias file for host '${LOCALHOST}'"

#
#  Put editor commands to convert YOUR hostname into the canonical form
#
cat > /tmp/$$.sed <<DONE
s/[ 	]*#.*//
/^$/d
s/@BRL-VGR/@brl-vgr/
s/@Brl-Vgr/@brl-vgr/
s/@VGR/@brl-vgr/
s/@Vgr/@brl-vgr/
s/@vgr.brl.arpa/@brl-vgr/
s/@vgr.brl.mil/@brl-vgr/
s/@vgr/@brl-vgr/
s/@/ @/
DONE

cat > /tmp/$$.awk <<DONE
{
	if( \$3 == "" || \$3 == "@$LOCALHOST" ) {
		if( \$1 != \$2 )
			printf( "%s\t%s%s\n", \$1, \$2, \$3 )
	} else
		printf( "%s\t%s%s\n", \$1, \$2, \$3 )
}
DONE

echo -n "Processing alias.local"
sed -f /tmp/$$.sed alias.local | awk -f /tmp/$$.awk > newalias

echo -n " and alias.global"
sed -f /tmp/$$.sed alias.global | awk -f /tmp/$$.awk >> newalias

echo "."
rm /tmp/$$.*
chmod 644 newalias
mv -f aliases aliases.bak
mv -f newalias aliases
echo "New aliases file built"
man
jeffh	jeffh
dumer	dumer
denn	denn
ge	ge
broome	broome
rich	rich
jwood	jwood
matt	matt
hugh	hugh
donnelly	donnelly
todd	todd
muehl	muehl
cohen	cohen
walinch	walinch
reamos	amos
bulmash	bulmash
karr	karr
schmidt	schmidt
gdw	gdw
jdk	jdk
jei	jei
rvm	rvm
gb	gb
dz	dz
cn	cn
mp	mp
dunigan	dunigan
glimm	glimm
thompson	thompson
motz	motz
rbn	rbn
merritt	merritt
andy	andy
rocchio	rocchio
unix-wizards-request	wiz
kille	kille
cain	cain
mmdf/samples/vgr/table/makearpa   555      0     12         453  3620510472  11772 #! /bin/sh
: This shell file makes a new smtp host table, based upon
: the NIC host table.
nictable -C < /etc/nic-table > NEWsmtp
mv -f smtp smtp.bak; mv -f NEWsmtp smtp

: Here, we make a new Arpa Domain table.
nictable -D -d ARPA < /etc/nic-table > NEWarpa
mv -f arpa arpa.bak; mv -f NEWarpa arpa
file for host '${LOCALHOST}'"

#
#  Put editor commands to convert YOUR hostname into the canonical form
#
cat > /tmp/$$.sed <<DONE
s/[ 	]*#.*//
/^$/d
s/@BRL-VGR/@brl-vgr/
s/@Brl-Vgr/@brl-vgr/
s/@VGR/@brl-vgr/
s/@mmdf/samples/vgr/table/makemailids   555      0     12         700  3620510472  12464 #! /bin/sh
#
#	makemailids.sh
#
#	Generates the mailids and users files from /etc/passwd.
#	/etc/passwd is expected to contain an "<mailid>" entry in
#	the GCOS field.  This field may eventually contain more than
#	one mailid.
#
PATH=/bin:/usr/bin:.

ed - /etc/passwd <<DONE
v/</d
g/:.*</s//	/
g/>.*/s///
w users.new
g/\(.*\)	\(.*\)/s//\2	\1/
w mailids.new
q
DONE
mv users users.bak
mv users.new users
mv mailids mailids.bak
mv mailids.new mailids
d
s/@BRL-VGR/@brl-vgr/
s/@Brl-Vgr/@brl-vgr/
s/@VGR/@brl-vgr/
s/@mmdf/samples/vgr/table/rootdomain   444      0     12         222  3620510472  12353 uucp:		seismo.arpa
csnet:		csnet-relay.arpa
mailnet:	mit-multics.arpa
bitnet:		wiscvm.arpa
uk:		ucl-cs.arpa
dec:		decwrl.arpa
delaware:	udel.arpa
 in
#	the GCOS field.  This field may eventually contain more than
#	one mailid.
#
PATH=/bin:/usr/bin:.

ed - /etc/passwd <<DONE
v/</d
g/:.*</s//	/
g/>.*/s///
w users.new
g/\(.*\)	\(.*\)/s//\2	\1/
w mailids.new
q
DONE
mv users users.bak
mv users.new users
mv mailids mailids.bak
mv mailids.new mailids
d
s/@BRL-VGR/@brl-vgr/
s/@Brl-Vgr/@brl-vgr/
s/@VGR/@brl-vgr/
s/@mmdf/samples/vgr/table/smtp   444      0     12         755  3620510472  11176 BRL-AOS.ARPA:192.5.22.82
BRL-CYBER.ARPA:192.5.22.19
BRL-MIS.ARPA:192.5.22.17
BRL-ZAP.ARPA:192.5.23.16

BRL-TAC2.ARPA:128.20.2.2
BRL-HEP.ARPA:128.20.1.2
BRL-VAT.ARPA:128.20.0.2
BRL-TAC1.ARPA:128.20.2.1
BRL-TAC.ARPA:26.2.0.29
BRL.ARPA:26.0.0.29
YUMA.ARPA:6.1.0.1
BBNCCA.ARPA:8.0.0.2
BBNCCB.ARPA:8.1.0.2
BBNCCC.ARPA:8.2.0.2
BBNCCD.ARPA:8.3.0.2
BBN-LABS-ADMIN.ARPA:8.4.0.2
BBNCCK.ARPA:8.5.0.2
BBNCCG.ARPA:8.0.0.3
BBNCC2-TAC.ARPA:8.2.0.3
BBNCCF.ARPA:8.0.0.4
BBNCCP.ARPA:8.2.0.4
BBNCCI.ARPA:8.3.0.4
/@VGR/@brl-vgr/
s/@mmdf/samples/vgr/table/users   444      0     12         561  3620510473  11350 root	root
croot	root
dae	dae
bin	bin
who	who
mount	mount
init	init
uucp	uucp
ftp	ftp
backup	backup
mmdf	mmdf
harry	harry
learn	learn
notes	notes
news	news
steve	steve
alpha1	alpha1
charlie	charlie
geo	geo
psl	psl
rnj	rnj
mdqs	mdqs
vitali	vitali
bboards	bboards
dickmc	dickmc
sys	sys
doc	doc
osg	osg
tcp-ip	tcp-ip
dpk	dpk
mort	mort
exchange	exchange
earl	earl
mike	mike
BBNCCK.ARPA:8.5.0.2
BBNCCG.ARPA:8.0.0.3
BBNCC2-TAC.ARPA:8.2.0.3
BBNCCF.ARPA:8.0.0.4
BBNCCP.ARPA:8.2.0.4
BBNCCI.ARPA:8.3.0.4
/@VGR/@brl-vgr/
s/@mmdf/samples/vgr/table/brlnet   444      0     12         543  3620510473  11475 brl.arpa	"26.0.0.29"
brl-aos.arpa	"192.5.22.82"
brl-mis.arpa	"192.5.21.3"
brl-bmd.arpa	"192.5.21.1"
brl-vld.arpa	"192.5.21.2"
brl-vgr.arpa	"128.20.1.1"
brl-voc.arpa	"128.20.3.2"
brl-vat.arpa	"128.20.4.2"
brl-tbd.arpa	"128.20.0.3"
brl-tgr.arpa	"192.5.21.4"
brl-lfd.arpa	"128.20.0.4"
amsaa.arpa	"128.20.3.1"
hel-ace.arpa	"128.20.0.5"
crdc.arpa	"128.20.0.6"
arl
mike	mike
BBNCCK.ARPA:8.5.0.2
BBNCCG.ARPA:8.0.0.3
BBNCC2-TAC.ARPA:8.2.0.3
BBNCCF.ARPA:8.0.0.4
BBNCCP.ARPA:8.2.0.4
BBNCCI.ARPA:8.3.0.4
/@VGR/@brl-vgr/
s/@mmdf/samples/vgr/mmdftailor   444      0     12        4014  3620510473  11273 MLNAME	BRL
MLDOMAIN ARPA
MLOCMACHINE VGR
MMAILID 1
MSUPPORT "mmdf@brl.arpa"

; Table entries
MTBL	aliases,	file="aliases",		show="Local User Aliases"
MTBL	local,		file="local",		show="Local Host Aliases"
MTBL	brlnet,		file="brlnet",		show="BRLNET Hosts"
MTBL	rootdomain,	file="rootdomain",	show="Root Domain"
MTBL	smtp,		file="channel", flags=ns, show="SMTP Hosts via NS"
MTBL	arpa,		file="domain", flags=ns, show="ARPA Domains via NS"
MTBL	mil,		file="domain", flags=ns, show="MIL Domains via NS"
MTBL	edu,		file="domain", flags=ns, show="EDU Domains via NS"
MTBL	com,		file="domain", flags=ns, show="COM Domains via NS"
MTBL	gov,		file="domain", flags=ns, show="GOV Domains via NS"
MTBL	org,		file="domain", flags=ns, show="ORG Domains via NS"
MTBL	list,		file="list",		show="List Pseudo Hosts"
MTBL	uucpdom,	file="uucpdom",		show="UUCP Domain"
MTBL	uucpchn,	file="uucpchn",		show="UUCP Routings"
MTBL	bboards,	file="bboards",		show="BBoard Pseudo Hosts"

; Alias sources
ALIAS	table=aliases, trusted

; Channel entries
MCHN	local, show="Local Delivery", que=local, tbl=local,
	pgm=local, mod=imm
MCHN	brlnet, show="BRLNET", que=brlnet, tbl=brlnet
	pgm=smtp, mod=reg, known=smtp, ap=822
MCHN	smtp, show="SMTP/TCP", que=smtp, tbl=smtp
	pgm=smtp, mod=reg, ap=822, confstr="BRL-VGR.ARPA"
MCHN	list, show="Mailing List Processor", que=list, tbl=list
	pgm=list, mod=reg
MCHN	uucp, show="UUCP Channel (Test)", que=uucp, tbl=uucpchn
	pgm=uucp, mod=reg, lname="brl-vgr"
MCHN	bb, show="BBoards", que=bboards, tbl=bboards
	pgm=bboards, mod=reg

; Domain tables
MDMN    ARPA, show="ARPA Domain", table=arpa
MDMN    MIL, show="MIL Domain", table=mil
MDMN    EDU, show="EDU Domain", table=edu
MDMN    COM, show="COM Domain", table=com
MDMN    GOV, show="GOV Domain", table=gov
MDMN    ORG, show="ORG Domain", table=org
MDMN	LIST, show="List Pseudo Domain", table=list
MDMN	UUCP, show="UUCP Domain", table=uucpdom
MDMN	BB, show="List Pseudo Domain", table=bboards
MDMN	"", show="Root Domain", table=rootdomain

; Logging levels
MMSGLOG     level=BST
MCHANLOG    level=BST
nipss,que=nipss,tbl=nipss,show="via PSS with NIFTP",pgm=niftp,poll=0,mod=reg,ap=jnt,confstr="/usr/bin/cpf cpf -t -f\"$(SENDER)\" $(FILE) @$(ADR)",auth=inlog,auth=outlog,level=PTR

MTBL  xuucp     file="vax1.uucp.names"
MCHN  uucp      name=uucp,que=uucp,tbl=xuucp,show="with UUCP",pgm=dmuucp,poll=0,mod=reg,ap=733,lname=ucl-cs,ldomain=uucp,level=FTR

; SMTP channel stuff
MTBL  smtp      flags=ns,flags=channel

; (going over Satnet)
MCHN  satnet     name=satnet,que=satnet,tbl=smtp,show="via Satnet mmdf/samples/vgr/etc.nightly   555      0     12        4031  3620510473  11367 #! /bin/sh
#
# This shell file is to be run on the MASTER BRL Mail machine only, and
# causes automatic nightly updates on all BRL-maintained
# UNIX systems.
#
# Procedure:
#	1) Get a new table from the NIC
#	2) Update local MMDF tables
#	3) For all BRL hosts, transmit a copy of nic-table and alias.global,
#	   then update their host tables and MMDF tables.
#
# Mike Muuss, BRL, 09/10/83.
#
umask 002
PATH=/bin:/usr/bin:/usr/brl/bin:/usr/brl/sbin:/usr/ucb:/etc
export PATH

echo "/etc/nightly started at `date`" >/dev/console

if test `hostname` != brl-tgr
then
	echo "This shell file is to run on BRL-TGR only!"
	exit
fi

# Because this will print on the console, make it readable
stty cooked echo -nl

# First, update our (Bldg 394) PACX tables
update-pacx brl-tgr brl-vgr amsaa brl-vld brl-mis

# Get a fresh host table, and update local hashed tables.
/etc/fetch-nic-table	# If it fails, will leave old table intact.

cd /usr/mmdf/table
sh -x doit

#
# Update all "supported" VAXen
#
cd /etc
for i in brl-vgr brl-voc hel-ace amsaa brl-aos brl-vat hel-cpx brl-tbd
do
	echo -n -------------------------------------------- $i --\ 
	date
	rcp /etc/nic-table $i:/etc/nic-table

	# If the RCP failed (host down, probably), do not delay further.
	if test $? = 0
	then
rcp /usr/mmdf/table/alias.global $i:/usr/mmdf/table/alias.global
rcp /usr/mmdf/table/brlnet $i:/usr/mmdf/table/brlnet
rsh $i 'umask 2;cd /etc; /etc/htable /etc/nic-table; /etc/mkhosts /etc/hosts'
rsh $i 'cd /usr/mmdf/table; doit; echo NIGHTLY done `date` >/dev/console'
	fi
done

#
# Update all "supported" PDP-11s
#
cd /etc
for i in brl-bmd brl-vld brl-mis
do
	echo -n -------------------------------------------- $i --\ 
	date
	rcp /usr/mmdf/table/alias.global $i:/usr/mmdf/table/alias.global

	# If the RCP failed (host down, probably), do not delay further.
	if test $? = 0
	then
		rcp /usr/mmdf/table/brlnet $i:/usr/mmdf/table/brlnet
		rsh $i "umask 2;cd /usr/mmdf/table; doit; echo NIGHTLY done '`date`' >/dev/console"
	fi
done

echo -------------- All Done at `date`
stty raw -echo nl >/dev/console
ivery", que=local, tbl=local,
	pgm=local, mod=imm
MCHN	brlnet, show="BRLNET", que=brlnet, tbl=brlnet
	pgm=smtp, mod=reg, known=smtp, ap=822
MCHN	smtp, show="SMTP/TCP", que=smtp, tbl=smtp
	pgm=smtp, mod=reg, ap=822, confstr="BRL-VGR.ARPA"
MCHN	list, show="Mailing List Processor", que=list, tbl=list
	pgm=list, mod=reg
MCHN	uucp, show="UUCP Channel (Test)", que=uucp, tbl=uucpchn
	pgm=uucp, mod=reg, lname="brl-vgr"
MCHN	bb, show="BBoards", que=bboards, tbl=bboards
	pgm=bboards, mod=reg
mmdf/samples/vgr/etc.rc.local   444      0     12        1722  3620510473  11407 #/etc/ifconfig vv0 192.5.24.0 up -trailers
/etc/console << FROB
l
s
FROB
/bin/hostname brl-vgr
/etc/ifconfig pcl0  192.5.21.6 -trailers up
/etc/ifconfig imp0 128.20.1.1 up
/etc/ifconfig il0 192.5.23.1 up -trailers
#/etc/route add brlnet2 brl-voc 2
/etc/route add 0 192.5.21.5 3

/etc/savecore /s/crash					>/dev/console

				echo -n mail system:		>/dev/console
if [ -d /usr/mmdf ]; then
	(cd /usr/mmdf; deliver -b -clocal,brlnet,list &)
	echo -n ' local'					>/dev/console
	(cd /usr/mmdf; deliver -b -t24 -csmtp &)
	echo -n ' smtp'						>/dev/console
	(cd /usr/mmdf; deliver -b -csmtp &)
	echo -n ' smtp'						>/dev/console
fi
/etc/impmon							>/dev/console
	echo -n ' impmon'					>/dev/console
/etc/hmpserver							>/dev/console
	echo -n ' hmpserver'					>/dev/console
/etc/hmppoll tgr vgr amsaa voc brl				>/dev/console
	echo -n ' hmppoll'					>/dev/console
(cd /usr/local/lib/druid; druid &)				>/dev/console
	echo -n ' druid'					>/dev/console
echo '.'							>/dev/console
echo -------------- All Done at `date`
stty rammdf/samples/vgr/usr.lib.crontab   444      0     12        1053  3620510474  12143 0,10,20,30,40,50 * * * * /etc/dmesg - >>/usr/adm/messages
0,30 * * * * (echo "
                `date` `/usr/ucb/uptime`
" ) >/dev/console

1 4 * * * sh /usr/adm/newsyslog
30 4 * * * /etc/sa -s > /dev/null
15 4 * * * find /usr/preserve -mtime +7 -a -exec rm -f {} \;

1 6 * * * /etc/quotacheck -a

10 4,12,20 * * * /usr/brl/sbin/alias mmdf /usr/mmdf/cleanque
0 7,19 * * * /usr/brl/sbin/checkque -s | mail dpk -s "`hostname` Mail Status"

42 2,7 * * * /etc/fsck -n /dev/rhp9c > /dev/null
5 0,6,12,18 * * * /usr/brl/sbin/alias mmdf /usr/mmdf/lock/home/prune
r/adm/messages
0,30 * * * * (echo "
                `date` `/usr/ucb/uptime`
" ) >/dev/console

1 4 * * * sh /usr/adm/newsyslog
30 4 * * * /etc/sa -s > /dev/null
15 4 * * * find /usr/preserve -mtime +7 -a -exec rm -f {} \;

1 6 * * * /etc/quotacheck -a

10 4,12,20 * * * /usr/brl/sbin/alias mmdf /usr/mmdf/cleanque
0 7,19 * * * /usr/brl/sbin/checkque -s | mail dpk -s "`hostname` Mail Status"

42 2,7 * * * /etc/fsck -n /dev/rhp9c > /dev/null
5 0,6,12,18 * * * /usr/brlmmdf/samples/csnet-relay/   755      0     12           0  3635165153  10567 mmdf/samples/csnet-relay/table/   755      0     12           0  3635165152  11655 mmdf/samples/csnet-relay/table/arpa   444      0     12        1774  3620510474  12612 TESTCELL-NU:TESTCELL-NU.ARPA
BBN-RVAX:BBN-RVAX.ARPA
BBNCCE:BBNCCE.ARPA
BBNIOS:BBNIOS.ARPA
BBN-IOS:BBNIOS.ARPA
BBN6:BBN6.ARPA
CGCE1:BBN-CGCE1
CGCE2:BBN-CGCE2
CGCE3:BBN-CGCE3
CGCE4:BBN-CGCE4
BBNV:BBN-VAX.ARPA
BBN-VAX:BBN-VAX.ARPA
BBNV:BBN-VAX.ARPA
BBN-ECHO:BBN-ECHO.ARPA
BBN-LOKI:LOKI
CLXX:BBN-CLXX.ARPA
BBN-CLXX:BBN-CLXX.ARPA
CLXX:BBN-CLXX.ARPA
BBN-TENEXF:BBNF.ARPA
BBNF:BBNF.ARPA
BBN-TENEXF:BBNF.ARPA
BBN-TENEXG:BBNG.ARPA
BBNG:BBNG.ARPA
BBN-TENEXG:BBNG.ARPA
BBN:BBNA.ARPA
BBNC:BBNA.ARPA
BBNA:BBNA.ARPA
BBN:BBNA.ARPA
BBNC:BBNA.ARPA
KOREA:KOREA-EMH.ARPA
KOREA-EMH:KOREA-EMH.ARPA
KOREA:KOREA-EMH.ARPA
HAWAII:HAWAII-EMH.ARPA
HAWAII-EMH:HAWAII-EMH.ARPA
HAWAII:HAWAII-EMH.ARPA
BBNCC-DEMO:BBN-DEMO.ARPA
DEMO:BBN-DEMO.ARPA
BBN-DEMO:BBN-DEMO.ARPA
BBNCC-DEMO:BBN-DEMO.ARPA
DEMO:BBN-DEMO.ARPA
BBN-UUCP:BBNCCA.ARPA
BBNCCA:BBNCCA.ARPA
BBN-UUCP:BBNCCA.ARPA
WANG-NOC:WANGLINK-NOC.ARPA
WANG:WANGLINK-NOC.ARPA
WANGLINK-NOC:WANGLINK-NOC.ARPA
WANG-NOC:WANGLINK-NOC.ARPA
WANG:WANGLINK-NOC.ARPA
MBT-NOC:MBT-NOC.ARPA
DARCOM-HQ:DARCOM-HQ.ARPA
y rammdf/samples/csnet-relay/table/smtp   444      0     12        1325  3620510474  12642 TESTCELL-NU.ARPA:"24.0.0.9"
BBN-RVAX.ARPA:"8.1.0.16"
BBNCCE.ARPA:"8.4.0.5"
BBNIOS.ARPA:"8.1.0.38"
BBN6.ARPA:"8.1.0.10"
BBN-CGCE1:"128.11.0.5"
BBN-CGCE2:"128.11.0.6"
BBN-CGCE3:"128.11.0.7"
BBN-CGCE4:"128.11.0.8"
BBN-VAX.ARPA:"8.1.0.8"
BBN-VAX.ARPA:"10.1.0.82"
BBN-VAX.ARPA:"128.11.1.1"
BBN-ECHO.ARPA:"128.11.192.100"
BBN-ECHO.ARPA:"10.1.1.82"
BBN-ECHO.ARPA:"8.1.1.8"
LOKI:"128.11.0.100"
BBN-CLXX.ARPA:"128.11.0.3"
BBNF.ARPA:"192.1.2.66"
BBNG.ARPA:"192.1.2.67"
BBNG.ARPA:"10.1.0.5"
BBNA.ARPA:"8.5.0.4"
BBNA.ARPA:"192.1.2.68"
BBNA.ARPA:"10.3.0.5"
KOREA-EMH.ARPA:"26.0.0.117"
HAWAII-EMH.ARPA:"26.1.0.36"
BBN-DEMO.ARPA:"8.0.0.15"
BBNCCA.ARPA:"8.0.0.2"
WANGLINK-NOC.ARPA:"8.6.0.2"
MBT-NOC.ARPA:"8.6.0.8"
DARCOM-HQ.ARPA:"26.0.0.50"
N-VAX.ARPA:"8.1.0.8"
BBN-VAX.ARPA:"10.1.0.82"
BBN-VAX.ARPA:"128.11.1.1"
BBN-ECHO.ARPA:"128.11.192.100"
BBN-ECHO.ARPA:"10.1.1.82"
BBN-ECHO.ARPA:"8.1.1.8"
LOKI:"128.11.0.100"
BBN-CLXX.ARPA:"128.11.0.3"
BBNF.ARPA:"192.1.2.66"
BBNG.ARPA:"192.1.2.67"
BBNG.ARPA:"10.1.0.5"
BBNA.ARPA:"8.5.0.4"
BBNA.ARPA:"1mmdf/samples/csnet-relay/table/csnet   444      0     12        1112  3620510475  12766 COLGATE:COLGATE
COLGATE.CSNET:COLGATE
KAIST:KAIST.CSNET
CSKAIST:KAIST.CSNET
PAGODA:KAIST.CSNET
PAGODA2:KAIST.CSNET
ALCOA-SEI:ALCOA-SEI.CSNET
ALCOA:ALCOA-SEI.CSNET
SEI-ALCOA:ALCOA-SEI.CSNET
RICE:RICE.CSNET
MTU:MTU.CSNET
TUFTS:TUFTS.CSNET
NMSU:NMSU.CSNET
NMSU-CS:NMSU.CSNET
UVM:UVM.CSNET
UVM-CS:UVM.CSNET
VERMONT:UVM.CSNET
UVMCS:UVM.CSNET
DARTMOUTH:DARTMOUTH.CSNET
DARTVAX:DARTMOUTH.CSNET
CSNET-RELAY:CSNET-RELAY.CSNET
CSNET-DEV:CSNET-DEV.CSNET
CSNET3:CSNET-DEV.CSNET
UMICH-CIPRNET:UMICH-CIPRNET.CSNET
UMICH:UMICH-CIPRNET.CSNET
UMICH-CV:UMICH-CIPRNET.CSNET
UMICH-CIPR:UMICH-CIPRNET.CSNET
EA-EMH.ARPA
HAWAII:HAWAII-EMH.ARPA
HAWAII-EMH:HAWAII-EMH.ARPA
HAWAII:HAWAII-EMH.ARPA
BBNCC-DEMO:BBN-DEMO.ARPA
DEMO:BBN-DEMO.ARPA
BBN-DEMO:BBN-DEMO.ARPA
BBNCC-DEMO:BBN-DEMO.ARPA
DEMO:BBN-DEMO.ARPA
BBN-UUCP:BBNCCA.ARPA
BBNCCA:BBNCCA.ARPA
BBN-UUCP:BBNCCA.ARPA
WANG-NOC:WANGLINK-NOC.ARPA
WANG:WANGLINK-NOC.ARPA
WANGLINK-NOC:WANGLINK-NOC.ARPA
WANG-NOC:WANGLINK-NOC.ARPA
WANG:WANGLINK-NOC.ARPA
MBT-NOC:MBT-NOC.ARPA
DARCOM-HQ:DARCOM-HQ.ARPA
y rammdf/samples/csnet-relay/table/apple   444      0     12          36  3620510475  12717 apple:apple
apple.csnet:apple
COLGATE
KAIST:KAIST.CSNET
CSKAIST:KAIST.CSNET
PAGODA:KAIST.CSNET
PAGODA2:KAIST.CSNET
ALCOA-SEI:ALCOA-SEI.CSNET
ALCOA:ALCOA-SEI.CSNET
SEI-ALCOA:ALCOA-SEI.CSNET
RICE:RICE.CSNET
MTU:MTU.CSNET
TUFTS:TUFTS.CSNET
NMSU:NMSU.CSNET
NMSU-CS:NMSU.CSNET
UVM:UVM.CSNET
UVM-CS:UVM.CSNET
VERMONT:UVM.CSNET
UVMCS:UVM.CSNET
DARTMOUTH:DARTMOUTH.CSNET
DARTVAX:DARTMOUTH.CSNET
CSNET-RELAY:CSNET-RELAY.CSNET
CSNET-DEV:CSNET-DEV.CSNET
CSNET3:CSNET-DEV.CSNET
UMICH-CIPRNET:UMICH-CIPRNET.CSNET
UMICH:UMICH-mmdf/samples/csnet-relay/table/arizona   444      0     12         102  3620510475  13273 arizona:arizona
arizona.csnet:arizona
az:arizona
az.csnet:arizona
IST.CSNET
PAGODA:KAIST.CSNET
PAGODA2:KAIST.CSNET
ALCOA-SEI:ALCOA-SEI.CSNET
ALCOA:ALCOA-SEI.CSNET
SEI-ALCOA:ALCOA-SEI.CSNET
RICE:RICE.CSNET
MTU:MTU.CSNET
TUFTS:TUFTS.CSNET
NMSU:NMSU.CSNET
NMSU-CS:NMSU.CSNET
UVM:UVM.CSNET
UVM-CS:UVM.CSNET
VERMONT:UVM.CSNET
UVMCS:UVM.CSNET
DARTMOUTH:DARTMOUTH.CSNET
DARTVAX:DARTMOUTH.CSNET
CSNET-RELAY:CSNET-RELAY.CSNET
CSNET-DEV:CSNET-DEV.CSNET
CSNET3:CSNET-DEV.CSNET
UMICH-CIPRNET:UMICH-CIPRNET.CSNET
UMICH:UMICH-mmdf/samples/csnet-relay/table/umass-cs   444      0     12         332  3620510475  13370 umass-cs:umass-cs
umass-cs.csnet:umass-cs
umass:umass-cs
umass.csnet:umass-cs
umass-coins:umass-cs
umass-coins.csnet:umass-cs
umass-ece:umass-ece
umass-ece.csnet:umass-ece
umass-ecs:umass-ece
umass-ecs.csnet:umass-ece

TUFTS:TUFTS.CSNET
NMSU:NMSU.CSNET
NMSU-CS:NMSU.CSNET
UVM:UVM.CSNET
UVM-CS:UVM.CSNET
VERMONT:UVM.CSNET
UVMCS:UVM.CSNET
DARTMOUTH:DARTMOUTH.CSNET
DARTVAX:DARTMOUTH.CSNET
CSNET-RELAY:CSNET-RELAY.CSNET
CSNET-DEV:CSNET-DEV.CSNET
CSNET3:CSNET-DEV.CSNET
UMICH-CIPRNET:UMICH-CIPRNET.CSNET
UMICH:UMICH-mmdf/samples/csnet-relay/table/list   444      0     12         612  3620510475  12611 csnet-relay:csnet-relay
csnet-relay.arpa:csnet-relay
csnet-relay.csnet:csnet-relay
csnet1:csnet-relay
csnet1.arpa:csnet-relay
csnet1.csnet:csnet-relay
rand-relay:csnet-relay
rand-relay.arpa:csnet-relay
rand-relay.csnet:csnet-relay
mmcc:csnet-relay
mmcc.arpa:csnet-relay
mmcc.csnet:csnet-relay
csnet-pdn-gateway:csnet-relay
csnet-pdn-gateway.arpa:csnet-relay
csnet-pdn-gateway.csnet:csnet-relay
ELAY:CSNET-RELAY.CSNET
CSNET-DEV:CSNET-DEV.CSNET
CSNET3:CSNET-DEV.CSNET
UMICH-CIPRNET:UMICH-CIPRNET.CSNET
UMICH:UMICH-mmdf/samples/csnet-relay/table/local   444      0     12         612  3620510476  12731 csnet-relay:csnet-relay
csnet-relay.arpa:csnet-relay
csnet-relay.csnet:csnet-relay
csnet1:csnet-relay
csnet1.arpa:csnet-relay
csnet1.csnet:csnet-relay
rand-relay:csnet-relay
rand-relay.arpa:csnet-relay
rand-relay.csnet:csnet-relay
mmcc:csnet-relay
mmcc.arpa:csnet-relay
mmcc.csnet:csnet-relay
csnet-pdn-gateway:csnet-relay
csnet-pdn-gateway.arpa:csnet-relay
csnet-pdn-gateway.csnet:csnet-relay
ELAY:CSNET-RELAY.CSNET
CSNET-DEV:CSNET-DEV.CSNET
CSNET3:CSNET-DEV.CSNET
UMICH-CIPRNET:UMICH-CIPRNET.CSNET
UMICH:UMICH-mmdf/samples/csnet-relay/table/bitnet   444      0     12        1742  3620510476  13151 BBADMIN:BBADMIN.BITNET,WISCVM.ARPA
BBADMIN2:BBADMIN2.BITNET,WISCVM.ARPA
BB003:BB003.BITNET,WISCVM.ARPA
BKLYN:BKLYN.BITNET,WISCVM.ARPA
BMACADM:BMACADM.BITNET,WISCVM.ARPA
BM002:BM002.BITNET,WISCVM.ARPA
BROWNVM:BROWNVM.BITNET,WISCVM.ARPA
BX001:BX001.BITNET,WISCVM.ARPA
CCNY:CCNY.BITNET,WISCVM.ARPA
CUNYJES3:CUNYJES3.BITNET,WISCVM.ARPA
CUNYVM:CUNYVM.BITNET,WISCVM.ARPA
CUNYVMS1:CUNYVMS1.BITNET,WISCVM.ARPA
HUNTER:HUNTER.BITNET,WISCVM.ARPA
KB001:KB001.BITNET,WISCVM.ARPA
LEHMAN:LEHMAN.BITNET,WISCVM.ARPA
NJECNVM:NJECNVM.BITNET,WISCVM.ARPA
NJECNVS:NJECNVS.BITNET,WISCVM.ARPA
NY001:NY001.BITNET,WISCVM.ARPA
QB001:QB001.BITNET,WISCVM.ARPA
QUEENS:QUEENS.BITNET,WISCVM.ARPA
SI001:SI001.BITNET,WISCVM.ARPA
YALEADS:YALEADS.BITNET,WISCVM.ARPA
YALECS:YALECS.BITNET,WISCVM.ARPA
YALEMVS:YALEMVS.BITNET,WISCVM.ARPA
YALEVM:YALEVM.BITNET,WISCVM.ARPA
YORK:YORK.BITNET,WISCVM.ARPA
PSUMVS:PSUMVS.BITNET,WISCVM.ARPA
PSUPDP1:PSUPDP1.BITNET,WISCVM.ARPA
PSUVAX1:PSUVAX1.BITNET,WISCVM.ARPA
PSUVM:PSUVM.BITNET,WISCVM.ARPA
TNET,WISCVM.ARPA
NJECNVM:NJECNmmdf/samples/csnet-relay/table/top   444      0     12         100  3620510476  12431 uucp:ucb-vax.arpa
bitnet:wisc-ibm.arpa
mailnet:mit-multics.arpa
Y001.BITNET,WISCVM.ARPA
QB001:QB001.BITNET,WISCVM.ARPA
QUEENS:QUEENS.BITNET,WISCVM.ARPA
SI001:SI001.BITNET,WISCVM.ARPA
YALEADS:YALEADS.BITNET,WISCVM.ARPA
YALECS:YALECS.BITNET,WISCVM.ARPA
YALEMVS:YALEMVS.BITNET,WISCVM.ARPA
YALEVM:YALEVM.BITNET,WISCVM.ARPA
YORK:YORK.BITNET,WISCVM.ARPA
PSUMVS:PSUMVS.BITNET,WISCVM.ARPA
PSUPDP1:PSUPDP1.BITNET,WISCVM.ARPA
PSUVAX1:PSUVAX1.BITNET,WISCVM.ARPA
PSUVM:PSUVM.BITNET,WISCVM.ARPA
TNET,WISCVM.ARPA
NJECNVM:NJECNmmdf/samples/csnet-relay/script/   755      0     12           0  3635165153  12073 mmdf/samples/csnet-relay/script/apple.script   444      0     12         303  3620510476  14455 window 1 0
xmitpack 130
recvpack 130
dial 1200|<wats5>14085550000
xmit "\r\r\r"
recv "ogin: " 60
xmit "ACCOUNT\r"
recv "assword:" 45
xmit "PASSWORD\r"
recv "annel: " 120
xmit "pobox\n"
start
end
ADS.BITNET,WISCVM.ARPA
YALECS:YALECS.BITNET,WISCVM.ARPA
YALEMVS:YALEMVS.BITNET,WISCVM.ARPA
YALEVM:YALEVM.BITNET,WISCVM.ARPA
YORK:YORK.BITNET,WISCVM.ARPA
PSUMVS:PSUMVS.BITNET,WISCVM.ARPA
PSUPDP1:PSUPDP1.BITNET,WISCVM.ARPA
PSUVAX1:PSUVAX1.BITNET,WISCVM.ARPA
PSUVM:PSUVM.BITNET,WISCVM.ARPA
TNET,WISCVM.ARPA
NJECNVM:NJECNmmdf/samples/csnet-relay/script/brandeis.script   444      0     12         500  3620510477  15143 window 2 0
xmitpack 130
recvpack 130
dial 1200|<local>5550000
xmit "\r\x\r"
{
  alternate
	recv "ogin: " 15
  alternate
	replay
	recv "xx" 5
	log "ERROR: Phone answered at 300 baud--breaking"
	xmit "\#"
	recv "ogin: " 15
}
xmit "ACCOUNT\r"
recv "assword:" 30
xmit "PASSWORD\r"
recv "annel: " 60
xmit "pobox\n"
start
end
ORK:YORK.BITNET,WISCVM.ARPA
PSUMVS:PSUMVS.BITNET,WISCVM.ARPA
PSUPDP1:PSUPDP1.BITNET,WISCVM.ARPA
PSUVAX1:PSUVAX1.BITNET,WISCVM.ARPA
PSUVM:PSUVM.BITNET,WISCVM.ARPA
TNET,WISCVM.ARPA
NJECNVM:NJECNmmdf/samples/csnet-relay/script/tamu.script   444      0     12        2055  3620510477  14351 window 2 0
xmitpack 130
recvpack 130
dial 1200|<wats5>14095551111,1200|<wats5>14095550000
xmit "\r\x\x\r"
recv "sername: " 30
xmit "ACCOUNT\r"
{
  alternate
	recv "assword:" 30
  alternate	   
	replay
	recv "ailure" 5
	log "WARNING: login failed--redialing"
	dial 1200|<wats5>14095550000,1200|<wats5>14095551111
	xmit "\x\x\x\r\x\r"
	recv "sername: " 30
	xmit "ACCOUNT\r"
	recv "assword:" 30
  alternate
	replay
	recv "sername: " 5
	log "WARNING: starting over at Username: prompt"
	xmit "ACCOUNT\r"
	recv "assword:" 30
  alternate	   
	replay
	recv "ailure" 5
	log "WARNING: login failed--redialing"
	dial 1200|<wats5>14095550000,1200|<wats5>14095551111
	xmit "\x\x\x\r\x\r"
	recv "sername: " 30
	xmit "ACCOUNT\r"
	recv "assword:" 30
}
xmit "PASSWORD\r"
{
  alternate
	recv "annel: " 60
  alternate
	replay
	recv "ailure" 5
	log "WARNING: login/passwd failed--redialing"
	dial 1200|<wats5>14095551111,1200|<wats5>14095550000
	xmit "\x\x\r\x\r"
	recv "sername: " 30
	xmit "ACCOUNT\r"
	recv "assword:" 30
	xmit "PASSWORD\r"
	recv "annel: " 60
}
xmit "pobox\r"
start
end
bl=local,
	pgm=local, mod=imm
MCHN	brlnet, show="BRLNET", que=brlnet, tbl=brlnet
	pgm=smtp, mod=reg, known=smtp, ap=822
MCHN	smtp, show="SMTP/TCP", que=smtp, tbl=smtp
	pgm=smtp, mod=reg, ap=822, confstr="BRL-VGR.ARPA"
MCHN	list, show="Mailing List Processor", que=list, tbl=list
	pgm=list, mod=reg
MCHN	uucp, show="UUCP Channel (Test)", que=uucp, tbl=uucpchn
	pgm=uucp, mod=reg, lname="brl-vgr"
MCHN	bb, show="BBoards", que=bboards, tbl=bboards
	pgm=bboards, mod=reg
mmdf/samples/csnet-relay/mmdftailor   444      0     12       56333  3622770714  12766 MV6MAIL "/usr/local/v6mail"

; Domain tables
;:;MTBL    "ARPA", file="arpa", show="ARPA Domain"
MDMN	"ARPA", table="ARPA"

;:;MTBL    "CSNET", file="csnet", show="CSNET Domain"
MDMN	"CSNET", table="CSNET"

;:;MTBL    "BITNET", file="bitnet", show="BITNET Domain"
MDMN	"BITNET", table="BITNET"

;:;MTBL    "Top-Level", file="top", show="Top Level Domain"
MDMN    "Top-Level", table="Top-Level", name="", dmn=""

; Channel tables
;:;MTBL    "smtp", file="smtp", show="SMTP Hosts"
;:;
;:;; Channel entries
;:;MCHN local, que=local, tbl=local,
;:;     pgm="local", mod=imm, mod=send, lname=csnet-relay, ldomain=arpa
;:;MCHN	xalcoa-sei, que=alcoa-sei, tbl=alcoa-sei, trn="alcoa-sei.trn", ap=733,
;:;	pgm="phone", mod=reg, mod=send, mod=pick,
;:;	host=alcoa-sei, scr="alcoa-sei.script", ldomain=csnet,
;:;	lname=csnet-relay
;:;;MCHN	xalcoa, que=alcoa, tbl=alcoa, trn="alcoa.trn", ap=733,
;:;;	pgm="pobox", mod=psv, mod=send, mod=pick,
;:;;	host=alcoa, user=alcomail, ldomain=csnet, lname=csnet-relay
;:;MCHN	xapple, que=apple, tbl=apple, trn="apple.trn", ap=733,
;:;	pgm="phone", mod=reg, mod=send, mod=pick,
;:;	host=apple, scr="apple.script", ldomain=csnet,
;:;	lname=csnet-relay
;:;MCHN	azpob, que=arizona, tbl=arizona, trn="arizona.trn", ap=733,
;:;	pgm="pobox", mod=psv, mod=send, mod=pick,
;:;	host=arizona, user=azmail, ldomain=csnet, lname=csnet-relay
;:;MCHN	azspob, que=asu, tbl=asu, trn="asu.trn", ap=733,
;:;	pgm="pobox", mod=psv, mod=send, mod=pick,
;:;	host=asu, user=azsmail, ldomain=csnet, lname=csnet-relay
;:;MCHN	xbbn-rvax, que=bbn-rvax, tbl=bbn-rvax, trn="bbn-rvax.trn", ap=733,
;:;	pgm="phone", mod=reg, mod=send, mod=pick,
;:;	host=bbn-rvax, ldomain=csnet, scr="bbn-rvax.script",
;:;	lname=csnet-relay
;:;MCHN	xbostonu, que=bostonu, tbl=bostonu, trn="bostonu.trn", ap=822,
;:;	pgm="phone", mod=reg, mod=send, mod=pick,
;:;	scr="bostonu.script",
;:;	host=bostonu, ldomain=csnet, lname=csnet-relay
;:;MCHN	xboulder, que=boulder, tbl=boulder, trn="boulder.trn", ap=733,
;:;	pgm="phone", mod=reg, mod=send, mod=pick,
;:;	host=boulder, scr="boulder.script", ldomain=csnet,
;:;	lname=csnet-relay
;:;MCHN	xbrandeis, que=brandeis, tbl=brandeis, trn="brandeis.trn", ap=733,
;:;	pgm="phone", mod=reg, mod=send, mod=pick,
;:;	host=brandeis, scr="brandeis.script", ldomain=csnet,
;:;	lname=csnet-relay
;:;MCHN	udbrown, que=brown, tbl=brown, trn="brown.trn", ap=733,
;:;	pgm="pobox", mod=psv, mod=send, mod=pick,
;:;	host=brown, user=brownml, ldomain=csnet, lname=csnet-relay
;:;MCHN	xbrown-cogsci, que=brown-cogsci, tbl=brown-cogsci, trn="brown-cogsci.trn", ap=733,
;:;	pgm="phone", mod=reg, mod=send, mod=pick,
;:;	host=brown-cogsci, scr="brown-cogsci.script", ldomain=csnet,
;:;	lname=csnet-relay
;:;MCHN	btlpob, que=btl, tbl=btl, trn="btl.trn", ap=733, 
;:;	pgm="pobox", mod=psv, mod=send, mod=pick,
;:;	host=btl, user=btlmail, ldomain=csnet, lname=csnet-relay
;:;MCHN	xbuffalo, que=buffalo, tbl=buffalo, trn="buffalo.trn", ap=733,
;:;	pgm="pobox", mod=psv, mod=send, mod=pick,
;:;	host=buffalo, user=buffmail, ldomain=csnet, lname=csnet-relay
;:;MCHN	xcase, que=case, tbl=case, trn="case.trn", ap=733,
;:;	pgm="phone", mod=reg, mod=send, mod=pick,
;:;	host=case, scr="case.script", ldomain=csnet,
;:;	lname=csnet-relay
;:;MCHN	xclemson, que=clemson, tbl=clemson, trn="clemson.trn", ap=733,
;:;	pgm="phone", mod=reg, mod=send, mod=pick,
;:;	host=clemson, scr="clemson.script", ldomain=csnet,
;:;	lname=csnet-relay
MCHN	xcolgate, que=colgate, tbl=colgate, trn="colgate.trn", ap=733,
	pgm="pobox", mod=psv, mod=send, mod=pick,
	host=colgate, user=colgmail, ldomain=csnet, lname=csnet-relay
;:;MCHN	xcolostate, que=colostate, tbl=colostate, trn="colostate.trn", ap=733,
;:;	pgm="phone", mod=reg, mod=send, mod=pick,
;:;	host=colostate, scr="colostate.script", ldomain=csnet,
;:;	lname=csnet-relay
;:;MCHN	xct, que=ct, tbl=ct, trn="ct.trn", ap=733,
;:;	pgm="phone", mod=reg, mod=send, mod=pick,
;:;	host=ct, scr="ct.script", ldomain=csnet,
;:;	lname=csnet-relay
;:;MCHN	xdartmouth, que=dartmouth, tbl=dartmouth, trn="dartmouth.trn", ap=733,
;:;	pgm="phone", mod=reg, mod=send, mod=pick,
;:;	host=dartmouth, scr="dartmouth.script", ldomain=csnet,
;:;	lname=csnet-relay
;:;MCHN	xdepaul, que=depaul, tbl=depaul, trn="depaul.trn", ap=733,
;:;	pgm="phone", mod=reg, mod=send, mod=pick,
;:;	host=depaul, scr="depaul.script", ldomain=csnet,
;:;	lname=csnet-relay
;:;MCHN	xdigital, que=digital, tbl=digital, trn="digital.trn", ap=733,
;:;	pgm="phone", mod=reg, mod=send, mod=pick,
;:;	host=digital, scr="digital.script", ldomain=csnet,
;:;	lname=csnet-relay
;:;MCHN	xduke, que=duke, tbl=duke, trn="duke.trn", ap=733,
;:;	pgm="phone", mod=reg, mod=send, mod=pick,
;:;	host=duke, scr="duke.script", ldomain=csnet,
;:;	lname=csnet-relay
;:;MCHN	xemory, que=emory, tbl=emory, trn="emory.trn", ap=733,
;:;	pgm="pobox", mod=psv, mod=send, mod=pick,
;:;	host=emory, user=emrymail, ldomain=csnet,
;:;	lname=csnet-relay
;:;MCHN	xfrance, que=france, tbl=france, trn="france.trn", ap=733,
;:;	pgm="pobox", mod=psv, mod=send, mod=pick,
;:;	host=france, user=franmail, ldomain=csnet, lname=csnet-relay
;:;MCHN	xgatech, que=gatech, tbl=gatech, trn="gatech.trn", ap=733,
;:;	pgm="phone", mod=reg, mod=send, mod=pick,
;:;	host=gatech, scr="gatech.script", ldomain=csnet,
;:;	lname=csnet-relay
;:;MCHN	xgermany, que=germany, tbl=germany, trn="germany.trn", ap=733,
;:;	pgm="pobox", mod=psv, mod=send, mod=pick,
;:;	host=germany, user=krlsmail, ldomain=csnet, lname=csnet-relay
;:;MCHN	xgmr, que=gmr, tbl=gmr, trn="gmr.trn", ap=733,
;:;	pgm="phone", mod=reg, mod=send, mod=pick,
;:;	host=gmr, scr="gmr.script", ldomain=csnet,
;:;	lname=csnet-relay
;:;MCHN	xgte-labs, que=gte-labs, tbl=gte-labs, trn="gte-labs.trn", ap=733,
;:;	pgm="phone", mod=reg, mod=send, mod=pick,
;:;	host=gte-labs, scr="gte-labs.script", ldomain=csnet,
;:;	lname=csnet-relay
;:;MCHN	xhouston, que=houston, tbl=houston, trn="houston.trn", ap=733,
;:;	pgm="phone", mod=reg, mod=send, mod=pick,
;:;	host=houston, scr="houston.script", ldomain=csnet,
;:;	lname=csnet-relay
;:;MCHN	xhplabs, que=hplabs, tbl=hplabs, trn="hplabs.trn", ap=733,
;:;	pgm="phone", mod=reg, mod=send, mod=pick,
;:;	host=hplabs, scr="hplabs.script", ldomain=csnet,
;:;	lname=csnet-relay
;:;MCHN	xibm-sj, que=ibm-sj, tbl=ibm-sj, trn="ibm-sj.trn", ap=733,
;:;	pgm="phone", mod=reg, mod=send,  mod=pick, poll=-1,
;:;	host=ibm-sj, scr="ibm-sj.script", ldomain=csnet,
;:;	lname=csnet-relay
;:;MCHN	xicase, que=icase, tbl=icase, trn="icase.trn", ap=733,
;:;	pgm="pobox", mod=psv, mod=send, mod=pick,
;:;	host=icase, user=icasmail, ldomain=csnet, lname=csnet-relay
;:;MCHN	indypob, que=indiana, tbl=indiana, trn="indiana.trn", ap=822,
;:;	pgm="pobox", mod=psv, mod=send, mod=pick,
;:;	host=indiana, user=indymail, ldomain=csnet, lname=csnet-relay
;:;MCHN	isrpob, que=israel, tbl=israel, trn="israel.trn", ap=733,
;:;	pgm="pobox", mod=psv, mod=send, mod=pick,
;:;	host=israel, user=isrmail, ldomain=csnet, lname=csnet-relay
;:;MCHN	xiowa-state, que=iowa-state, tbl=iowa-state, trn="iowa-state.trn", ap=733,
;:;	pgm="phone", mod=reg, mod=send, mod=pick,
;:;	host=iowa-state, scr="iowa-state.script", ldomain=csnet,
;:;	lname=csnet-relay
;:;MCHN	xjhu, que=jhu, tbl=jhu, trn="jhu.trn", ap=733,
;:;	pgm="phone", mod=reg, mod=send, mod=pick,
;:;	host=jhu,  scr="jhu.script", lname=csnet-relay,
;:;	ldomain=csnet
MCHN	xkaist, que=kaist, tbl=kaist, trn="kaist.trn", ap=733,
	pgm="pobox", mod=psv, mod=send, mod=pick,
	host=kaist, user=kormail, ldomain=csnet, lname=csnet-relay
;:;MCHN	kanstpob, que=kansas-state, tbl=kansas-state, trn="kansas-state.trn", ap=733,
;:;	pgm="pobox", mod=psv, mod=send, mod=pick,
;:;	host=kansas-state, user=kansmail, ldomain=csnet, lname=csnet-relay
;:;MCHN	xkentvax, que=kentvax, tbl=kentvax, trn="kentvax.trn", ap=733,
;:;	pgm="phone", mod=reg, mod=send, mod=pick,
;:;	host=kentvax, scr="kentvax.script", ldomain=csnet,
;:;	lname=csnet-relay
;:;MCHN	xlsu, que=lsu, tbl=lsu, trn="lsu.trn", ap=733,
;:;	pgm="phone", mod=reg, mod=send, mod=pick,
;:;	host=lsu, scr="lsu.script", ldomain=csnet,
;:;	lname=csnet-relay
;:;MCHN	xlsu, que=lsu, tbl=lsu, trn="lsu.trn", ap=733,
;:;	pgm="pobox", mod=psv, mod=send, mod=pick,
;:;	host=lsu, user=lsumail, ldomain=csnet, lname=csnet-relay
;:;MCHN	xmcc, que=mcc, tbl=mcc, trn="mcc.trn", ap=733,
;:;	pgm="phone", mod=reg, mod=send, mod=pick,
;:;	host=mcc, scr="mcc.script", ldomain=csnet,
;:;	lname=csnet-relay
;:;MCHN	xmcnc, que=mcnc, tbl=mcnc, trn="mcnc.trn", ap=733,
;:;	pgm="phone", mod=reg, mod=send, mod=pick,
;:;	scr="mcnc.script",
;:;	host=mcnc, ldomain=csnet, lname=csnet-relay
;:;;MCHN	xmtu, que=mtu, tbl=mtu, trn="mtu.trn", ap=822,
;:;;	pgm="phone", mod=reg, mod=send, mod=pick,
;:;;	host=mtu, scr="mtu.script", ldomain=csnet,
;:;;	lname=csnet-relay
;:;MCHN	xmtu, que=mtu, tbl=mtu, trn="mtu.trn", ap=822,
;:;	pgm="pobox", mod=psv, mod=send, mod=pick,
;:;	host=mtu, user=mtumail, ldomain=csnet, lname=csnet-relay
;:;MCHN	xnmsu, que=nmsu, tbl=nmsu, trn="nmsu.trn", ap=733,
;:;	pgm="phone", mod=reg, mod=send, mod=pick,
;:;	host=nmsu, scr="nmsu.script", ldomain=csnet,
;:;	lname=csnet-relay
;:;MCHN	nmtpob, que=nmt, tbl=nmt, trn="nmt.trn", ap=733,
;:;	pgm="pobox", mod=psv, mod=send, mod=pick,
;:;	host=nmt, user=nmtmail, ldomain=csnet, lname=csnet-relay
;:;MCHN	xnortheastern, que=northeastern, tbl=northeastern, trn="northeastern.trn", ap=733,
;:;	pgm="phone", mod=reg, mod=send, mod=pick,
;:;	host=northeastern, scr="northeastern.script", ldomain=csnet,
;:;	lname=csnet-relay
;:;MCHN	xnsf-cs, que=nsf-cs, tbl=nsf-cs, trn="nsf-cs.trn", ap=733,
;:;	pgm="pobox", mod=psv, mod=send, mod=pick,
;:;	host=nsf-cs, user=nsfmail, ldomain=csnet, lname=csnet-relay
;:;MCHN	xnwu, que=nwu, tbl=nwu, trn="nwu.trn", ap=733,
;:;	pgm="phone", mod=reg, mod=send, mod=pick,
;:;	host=nwu, scr="nwu.script", ldomain=csnet,
;:;	lname=csnet-relay
;:;MCHN	xohio-state, que=ohio-state, tbl=ohio-state, trn="ohio-state.trn", ap=733,
;:;	pgm="phone", mod=reg, mod=send, mod=pick,
;:;	host=ohio-state, scr="ohio-state.script", ldomain=csnet,
;:;	lname=csnet-relay
;:;MCHN	xokstate, que=okstate, tbl=okstate, trn="okstate.trn", ap=733,
;:;	pgm="phone", mod=reg, mod=send, mod=pick,
;:;	host=okstate, scr="okstate.script", ldomain=csnet,
;:;	lname=csnet-relay
;:;MCHN	ogcpob, que=oregon-grad, tbl=oregon-grad, trn="oregon-grad.trn", ap=733,
;:;	pgm="pobox", mod=psv, mod=send, mod=pick,
;:;	host=oregon-grad, user=ogcmmdf, ldomain=csnet, lname=csnet-relay
;:;MCHN	xoregon-state, que=oregon-state, tbl=oregon-state, trn="oregon-state.trn", ap=733,
;:;	pgm="phone", mod=reg, mod=send, mod=pick,
;:;	host=oregon-state, scr="oregon-state.script", ldomain=csnet,
;:;	lname=csnet-relay
;:;MCHN	xpenn-state, que=penn-state, tbl=penn-state, trn="penn-state.trn", ap=733,
;:;	pgm="phone", mod=reg, mod=send, mod=pick,
;:;	host=penn-state, scr="penn-state.script", ldomain=csnet,
;:;	lname=csnet-relay
;:;MCHN	pittpob, que=pitt, tbl=pitt, trn="pitt.trn", ap=733,
;:;	pgm="pobox", mod=psv, mod=send, mod=pick,
;:;	host=pitt, user=pittmail, ldomain=csnet, lname=csnet-relay
;:;MCHN	xportland, que=portland, tbl=portland, trn="portland.trn", ap=733,
;:;	pgm="phone", mod=reg, mod=send, mod=pick,
;:;	host=portland, scr="portland.script", ldomain=csnet,
;:;	lname=csnet-relay
;:;MCHN	xprinceton, que=princeton, tbl=princeton, trn="princeton.trn", ap=733,
;:;	pgm="phone", mod=reg, mod=send, mod=pick,
;:;	host=princeton, scr="princeton.script", ldomain=csnet,
;:;	lname=csnet-relay
;:;MCHN	udqucis, que=qucis, tbl=qucis, trn="qucis.trn", ap=733,
;:;	pgm="pobox", mod=psv, mod=send, mod=pick,
;:;	host=qucis, user=qucsmail, ldomain=csnet, lname=csnet-relay
;:;MCHN	xrit, que=rit, tbl=rit, trn="rit.trn", ap=822,
;:;	pgm="phone", mod=reg, mod=send, mod=pick,
;:;	host=rit, scr="rit.script", ldomain=csnet,
;:;	lname=csnet-relay
;:;MCHN	rpipob, que=rpi, tbl=rpi, trn="rpi.trn", ap=733,
;:;	pgm="pobox", mod=psv, mod=send, mod=pick,
;:;	host=rpi, user=rpimail, ldomain=csnet, lname=csnet-relay
;:;MCHN	xscarolina, que=scarolina, tbl=scarolina, trn="scarolina.trn", ap=733,
;:;	pgm="phone", mod=reg, mod=send, mod=pick,
;:;	host=scarolina, scr="scarolina.script", ldomain=csnet,
;:;	lname=csnet-relay
;:;MCHN	xsmu, que=smu, tbl=smu, trn="smu.trn", ap=733,
;:;	pgm="phone", mod=reg, mod=send, mod=pick,
;:;	host=smu, scr="smu.script", ldomain=csnet,
;:;	lname=csnet-relay
;:;MCHN	xsrc, que=src, tbl=src, trn="src.trn", ap=733,
;:;	pgm="phone", mod=reg, mod=send, mod=pick,
;:;	host=src, scr="src.script", ldomain=csnet,
;:;	lname=csnet-relay
;:;MCHN	xsuny-bing, que=suny-bing, tbl=suny-bing, trn="suny-bing.trn", ap=733,
;:;	pgm="phone", mod=reg, mod=send, mod=pick,
;:;	host=suny-bing, scr="suny-bing.script", ldomain=csnet,
;:;	lname=csnet-relay
;:;MCHN	sunypob, que=suny-sbcs, tbl=suny-sbcs, trn="suny-sbcs.trn", ap=733,
;:;	pgm="pobox", mod=psv, mod=send, mod=pick,
;:;	host=suny-sbcs, user=sunymail, ldomain=csnet, lname=csnet-relay
;:;MCHN	syracuse, que=syr, tbl=syr, trn="syr.trn", ap=733,
;:;	pgm="pobox", mod=psv, mod=send, mod=pick,
;:;	host=syr, user=syrmail, ldomain=csnet, lname=csnet-relay
;:;MCHN	xtamu, que=tamu, tbl=tamu, trn="tamu.trn", ap=733,
;:;	pgm="phone", mod=reg, mod=send, mod=pick,
;:;	host=tamu, scr="tamu.script", ldomain=csnet,
;:;	lname=csnet-relay
;:;MCHN	xtektronix, que=tektronix, tbl=tektronix, trn="tektronix.trn", ap=733,
;:;	pgm="phone", mod=reg, mod=send, mod=pick,
;:;	host=tektronix, scr="tektronix.script", ldomain=csnet,
;:;	lname=csnet-relay
;:;MCHN	xtennessee, que=tennessee, tbl=tennessee, trn="tennessee.trn", ap=733,
;:;	pgm="phone", mod=reg, mod=send, mod=pick,
;:;	host=tennessee, scr="tennessee.script", ldomain=csnet,
;:;	lname=csnet-relay
;:;MCHN	tiegpob, que=ti-eg, tbl=ti-eg, trn="ti-eg.trn", ap=733,
;:;     pgm="pobox", mod=psv, mod=send, mod=pick, 
;:;	host=ti-eg, user=tiegmail, ldomain=csnet, lname=csnet-relay
;:;MCHN	tipob, que=ti-csl, tbl=ti-csl, trn="ti-csl.trn", ap=733,
;:;	pgm="pobox", mod=psv, mod=send, mod=pick, 
;:;	host=ti-csl, user=timail, ldomain=csnet, lname=csnet-relay
;:;MCHN	torpob, que=toronto, tbl=toronto, trn="toronto.trn", ap=733,
;:;	pgm="pobox", mod=psv, mod=send, mod=pick,
;:;	host=toronto, user=tormail, ldomain=csnet, lname=csnet-relay
;:;MCHN	torpob, que=toronto, tbl=toronto, trn="toronto.trn", ap=733,
;:;	pgm="phone", mod=reg, mod=send, mod=pick,
;:;	host=toronto, scr="toronto.script", ldomain=csnet, lname=csnet-relay
;:;MCHN	xtufts, que=tufts, tbl=tufts, trn="tufts.trn", ap=733,
;:;	pgm="phone", mod=reg, mod=send, mod=pick,
;:;	host=tufts, scr="tufts.script", ldomain=csnet,
;:;	lname=csnet-relay
;:;MCHN	xuab, que=uab, tbl=uab, trn="uab.trn", ap=733,
;:;	pgm="phone", mod=reg, mod=send, mod=pick,
;:;	host=uab, scr="uab.script", ldomain=csnet,
;:;	lname=csnet-relay
;:;MCHN	ubcpob, que=ubc, tbl=ubc, trn="ubc.trn", ap=822,
;:;	pgm="pobox", mod=psv, mod=send, mod=pick,
;:;	host=ubc, user=ubcmail, ldomain=csnet, lname=csnet-relay
;:;MCHN	xucf, que=ucf, tbl=ucf, trn="ucf.trn", ap=733,
;:;	pgm="phone", mod=reg, mod=send, mod=pick,
;:;	host=ucf, scr="ucf.script", ldomain=csnet,
;:;	lname=csnet-relay
;:;MCHN	xuchicago, que=uchicago, tbl=uchicago, trn="uchicago.trn", ap=733,
;:;	pgm="phone", mod=reg, mod=send, mod=pick,
;:;	host=uchicago, scr="uchicago.script", ldomain=csnet,
;:;	lname=csnet-relay
;:;MCHN	xuconn, que=uconn, tbl=uconn, trn="uconn.trn", ap=733,
;:;	pgm="phone", mod=reg, mod=send, mod=pick,
;:;	host=uconn, scr="uconn.script", ldomain=csnet,
;:;	lname=csnet-relay
;:;MCHN	xucsb, que=ucsb, tbl=ucsb, trn="ucsb.trn", ap=733,
;:;	pgm="phone", mod=reg, mod=send, mod=pick,
;:;	host=ucsb, scr="ucsb.script", ldomain=csnet,
;:;	lname=csnet-relay
;:;MCHN	xucsc, que=ucsc, tbl=ucsc, trn="ucsc.trn", ap=733,
;:;	pgm="phone", mod=reg, mod=send, mod=pick,
;:;	host=ucsc, scr="ucsc.script", ldomain=csnet,
;:;	lname=csnet-relay
;:;MCHN	xuhcl, que=uhcl, tbl=uhcl, trn="uhcl.trn", ap=733,
;:;	pgm="phone", mod=reg, mod=send, mod=pick,
;:;	host=uhcl, scr="uhcl.script", ldomain=csnet,
;:;	lname=csnet-relay
;:;MCHN	uiowapob, que=uiowa, tbl=uiowa, trn="uiowa.trn", ap=733,
;:;	pgm="pobox", mod=psv, mod=send, mod=pick,
;:;	host=uiowa, user=iowamail, ldomain=csnet, lname=csnet-relay
;:;MCHN	xukans, que=ukans, tbl=ukans, trn="ukans.trn", ap=733,
;:;	pgm="phone", mod=reg, mod=send, mod=pick,
;:;	host=ukans, scr="ukans.script", ldomain=csnet,
;:;	lname=csnet-relay
;:;MCHN	xufl, que=ufl, tbl=ufl, trn="ufl.trn", ap=733,
;:;	pgm="phone", mod=reg, mod=send, mod=pick,
;:;	host=ufl, scr="ufl.script", ldomain=csnet,
;:;	lname=csnet-relay
;:;MCHN	xuiuc, que=uiuc, tbl=uiuc, trn="uiuc.trn", ap=822,
;:;	pgm="phone", mod=reg, mod=send, mod=pick,
;:;	host=uiuc, scr="uiuc.script", ldomain=csnet,
;:;	lname=csnet-relay
;:;MCHN	xulowell, que=ulowell, tbl=ulowell, trn="ulowell.trn", ap=733,
;:;	pgm="phone", mod=reg, mod=send, mod=pick,
;:;	host=ulowell, scr="ulowell.script", ldomain=csnet,
;:;	lname=csnet-relay
;:;MCHN	xumass-boston, que=umass-boston, tbl=umass-boston, trn="umass-boston.trn", ap=733,
;:;	pgm="phone", mod=reg, mod=send, mod=pick,
;:;	host=umass-boston, scr="umass-boston.script", ldomain=csnet,
;:;	lname=csnet-relay
;:;MCHN	xumass-cs, que=umass-cs, tbl=umass-cs, trn="umass-cs.trn", ap=733,
;:;	pgm="pobox", mod=psv, mod=send, mod=pick,
;:;	host=umass-cs, user=massmail, ldomain=csnet, lname=csnet-relay
;:;MCHN	michpob, que=umich-ciprnet, tbl=umich-ciprnet, trn="umich-ciprnet.trn", ap=733,
;:;	pgm="pobox", mod=psv, mod=send, mod=pick,
;:;	host=umich-ciprnet, user=michmail, ldomain=csnet, lname=csnet-relay
;:;MCHN	xumiss, que=umiss, tbl=umiss, trn="umiss.trn", ap=733,
;:;	pgm="phone", mod=reg, mod=send, mod=pick,
;:;	host=umiss, scr="umiss.script", ldomain=csnet,
;:;	lname=csnet-relay
;:;MCHN	xumn-cs, que=umn-cs, tbl=umn-cs, trn="umn-cs.trn", ap=733,
;:;	pgm="phone", mod=reg, mod=send, mod=pick,
;:;	host=umn-cs, scr="umn-cs.script", ldomain=csnet,
;:;	lname=csnet-relay
;:;MCHN	uncpob, que=unc, tbl=unc, trn="unc.trn", ap=733,
;:;	pgm="pobox", mod=psv, mod=send, mod=pick,
;:;	host=unc, user=uncmail, ldomain=csnet, lname=csnet-relay
;:;MCHN uncpob, que=unc, tbl=unc, trn="unc.trn", ap=733,
;:;     pgm="phone", mod=reg, mod=send, mod=pick,
;:;     host=unc, ldomain=csnet, lname=csnet-relay, scr="unc.script"
;:;MCHN	udunhpob, que=unh, tbl=unh, trn="unh.trn", ap=733,
;:;	pgm="pobox", mod=psv, mod=send, mod=pick,
;:;	host=unh, user=unhcsnet, ldomain=csnet, lname=csnet-relay
;:;MCHN	xunl, que=unl, tbl=unl, trn="unl.trn", ap=733,
;:;	pgm="phone", mod=reg, mod=send, mod=pick,
;:;	host=unl, scr="unl.script", ldomain=csnet,
;:;	lname=csnet-relay
;:;MCHN	xunlv, que=unlv, tbl=unlv, trn="unlv.trn", ap=733,
;:;	pgm="phone", mod=reg, mod=send, mod=pick,
;:;	host=unlv,  scr="unlv.script", ldomain=csnet, 
;:;	lname=csnet-relay
;:;MCHN	xuoregon, que=uoregon, tbl=uoregon, trn="uoregon.trn", ap=733,
;:;	pgm="phone", mod=reg, mod=send, mod=pick,
;:;	host=uoregon, scr="uoregon.script", ldomain=csnet,
;:;	lname=csnet-relay
;:;MCHN	xupenn, que=upenn, tbl=upenn, trn="upenn.trn", ap=733,
;:;	pgm="phone", mod=reg, mod=send, mod=pick,
;:;	host=upenn, scr="upenn.script", ldomain=csnet,
;:;	lname=csnet-relay
;:;MCHN	xusc-cse, que=usc-cse, tbl=usc-cse, trn="usc-cse.trn", ap=733,
;:;	pgm="phone", mod=reg, mod=send, mod=pick,
;:;	host=usc-cse, scr="usc-cse.script", ldomain=csnet,
;:;	lname=csnet-relay
;:;MCHN	uslpob, que=usl, tbl=usl, trn="usl.trn", ap=733,
;:;	pgm="pobox", mod=psv, mod=send, mod=pick,
;:;	host=usl, user=uslamail, ldomain=csnet, lname=csnet-relay
;:;MCHN	xutd-cs, que=utd-cs, tbl=utd-cs, trn="utd-cs.trn", ap=733,
;:;	pgm="phone", mod=reg, mod=send, mod=pick,
;:;	host=utd-cs, scr="utd-cs.script", ldomain=csnet,
;:;	lname=csnet-relay
;:;MCHN	xuvm, que=uvm, tbl=uvm, trn="uvm.trn", ap=733,
;:;	pgm="phone", mod=reg, mod=send, mod=pick,
;:;	host=uvm, scr="uvm.script", ldomain=csnet,
;:;	lname=csnet-relay
;:;MCHN	xuwmeecs, que=uwmeecs, tbl=uwmeecs, trn="uwmeecs.trn", ap=733,
;:;	pgm="phone", mod=reg, mod=send, mod=pick,
;:;	host=uwmeecs, scr="uwmeecs.script", ldomain=csnet,
;:;	lname=csnet-relay
;:;MCHN	xvanderbilt, que=vanderbilt, tbl=vanderbilt, trn="vanderbilt.trn", ap=733,
;:;	pgm="phone", mod=reg, mod=send, mod=pick,
;:;	host=vanderbilt, scr="vanderbilt.script", ldomain=csnet,
;:;	lname=csnet-relay
;:;MCHN	xvpi, que=vpi, tbl=vpi, trn="vpi.trn", ap=733,
;:;	pgm="phone", mod=reg, mod=send, mod=pick,
;:;	host=vpi, scr="vpi.script", ldomain=csnet,
;:;	lname=csnet-relay
;:;MCHN	uvapob, que=virginia, tbl=virginia, trn="virginia.trn", ap=733,
;:;	pgm="pobox", mod=psv, mod=send, mod=pick,
;:;	host=virginia, user=uvamail, ldomain=csnet, lname=csnet-relay
;:;MCHN	xwang-inst, que=wang-inst, tbl=wang-inst, trn="wang-inst.trn", ap=733,
;:;	pgm="phone", mod=reg, mod=send, mod=pick,
;:;	host=wang-inst, scr="wang-inst.script", ldomain=csnet,
;:;	lname=csnet-relay
;:;MCHN	xwaterloo, que=waterloo, tbl=waterloo, trn="waterloo.trn", ap=733,
;:;	pgm="phone", mod=reg, mod=send, mod=pick,
;:;	host=waterloo, scr="waterloo.script", ldomain=csnet,
;:;	lname=csnet-relay
;:;MCHN	xwsu, que=wsu, tbl=wsu, trn="wsu.trn", ap=733,
;:;	pgm="phone", mod=reg, mod=send, mod=pick,
;:;	host=wsu, scr="wsu.script", ldomain=csnet,
;:;	lname=csnet-relay
;:;MCHN ucirvine, show="UCI/TCP", que=ucirvine, tbl=ucirvine, ap=822,
;:;     pgm="smtp", mod=reg, host=uci-750a, ldomain=arpa, mod=send,
;:;     lname=csnet-relay, known=arpa
;:;MCHN	udelrelay, show="UDEL/TCP", que=udelrelay, tbl=udelrelay, ap=822,
;:;	pgm="smtp", mod=reg, host=udel-relay, ldomain=arpa,
;:;	lname=csnet-relay, known=arpa
;:;MCHN	smtp, show="SMTP/TCP", que=smtp, tbl=smtp, ap=822, mod=send,
;:;	pgm="smtp", mod=reg, lname=csnet-relay, ldomain=arpa, known=smtp
;:;MCHN list, show="List Channel", que=list, tbl=list,
;:;     pgm=list, mod=imm, mod=send
;:;MCHN	xtest-in, show="Inbound phone test", que=test-in, tbl=test-in, ap=733,
;:;	trn="test-in.trn", pgm="pobox", mod=psv, mod=send, mod=pick, 
;:;	host=test-in, user=fooble, ldomain=csnet, lname=csnet
;:;MCHN	xtest-out, show="Outbound phone test", que=test-out, tbl=test-out, 
;:;	trn="test-out.trn", pgm="phone", mod=reg, mod=send, mod=pick,
;:;	host=test-out, scr="test-out.script", ldomain=csnet,
;:;     lname=csnet-relay, ap=733
;:;MCHN	xtest-relay, show="To Test-Relay", que=test-relay, tbl=test-relay, trn="test-relay.trn", ap=733,
;:;	pgm="phone", mod=reg, mod=send, mod=pick,
;:;	host=test-relay, scr="test-relay.script", ldomain=csnet,
;:;	lname=csnet-relay, ap=822
;:;
;:;; definitions of available dial-out lines (0400=1200, 0177=300)
DPRT "/dev/ttyd2", "/usr/spool/uucp/LCK..ttyd2", "/dev/cu0", 0400, "wats3", pref=08, suff="<"
DPRT "/dev/ttyd2", "/usr/spool/uucp/LCK..ttyd2", "/dev/cu0", 0177, "wats3", pref=48, suff="<"
DPRT "/dev/ttyd3", "/usr/spool/uucp/LCK..ttyd3", "/dev/cu0", 0400, "wats3", pref=06, suff="<"
DPRT "/dev/ttyd3", "/usr/spool/uucp/LCK..ttyd3", "/dev/cu0", 0177, "wats3", pref=46, suff="<"
DPRT "/dev/ttyd4", "/usr/spool/uucp/LCK..ttyd4", "/dev/cu0", 0400, "wats5", pref=04, suff="<"
DPRT "/dev/ttyd4", "/usr/spool/uucp/LCK..ttyd4", "/dev/cu0", 0177, "wats5", pref=44, suff="<"
DPRT "/dev/ttyd5", "/usr/spool/uucp/LCK..ttyd5", "/dev/cu0", 0400, "wats5", pref=02, suff="<"
DPRT "/dev/ttyd5", "/usr/spool/uucp/LCK..ttyd5", "/dev/cu0", 0177, "wats5", pref=42, suff="<"
DPRT "/dev/ttyd6", "/usr/spool/uucp/LCK..ttyd6", "/dev/cu0", 0400, "local", pref=00, suff="<"
DPRT "/dev/ttyd6", "/usr/spool/uucp/LCK..ttyd6", "/dev/cu0", 0177, "local", pref=40, suff="<"
DPRT "/dev/ttyd7", "/usr/spool/uucp/LCK..ttyd7", "/dev/cu0", 0400, "local", pref="0<", suff="<"
DPRT "/dev/ttyd7", "/usr/spool/uucp/LCK..ttyd7", "/dev/cu0", 0177, "local", pref="4<", suff="<"

D_LINE "ptymmdf", "/dev/ptymmdf", "/usr/spool/uucp/LCK..ptymmdf", 19200

; log of phone calls made
DACCT "/mmdf/log/dial_log"

;logging levels
MMSGLOG		level=FST
MCHANLOG	level=FST
PHLOG		level=FST
MSMTPLEV	FST
mod=reg, mod=send, mod=pick,
;:;	host=uvm, scr="uvm.script", ldomain=csnet,
;:;	lname=csnet-relay
;:;MCHN	xuwmeecs, que=uwmeecs, tbl=uwmeecs, trn="uwmeecs.trn", ap=733,
;:;	pgm="phone", mod=reg, mod=send, mod=pick,
;:;	host=uwmeecs, scr="uwmeecs.script", ldomain=csnet,
;:;	lname=csnet-relay
;mmdf/samples/4.3vax/   755      0     12           0  3635165156   7367 mmdf/samples/4.3vax/table/   755      0     12           0  3635165156  10456 mmdf/samples/4.3vax/table/aliases   444      0     12         736  3620510500  12050 #
#	Global aliases
#
guido		guido@boring.uucp
jaap		jaap@haring.uucp
jennifer	jennifer@boring.uucp
piet		piet@haring.uucp
postmaster	dpk
steven		steven@boring.uucp
jim		jim@bellcore.uucp
kingston	kingston@mcvax.uucp
#
#	Local aliases
#
root		jaap,dpk,piet
daemon		root
operator	root
bin		root
uucp		root
news		root
usenet		dpk
guest		root
ftp		root
nobody		root
acsnet		root
mmdf		dpk,piet
#
# Lists
#
mg1		:include:/usr/mmdf/lists/mg1,news//userfs1/archive/mg1
mg1-request	dpk
"wats3", pref=08, suff="<"
DPRT "/mmdf/samples/4.3vax/table/local   444      0     12         124  3620510501  11511 sering.uucp		sering
sering			sering
sering.mcvax.uucp 	sering
sering.cwi.nl		sering
er@boring.uucp
piet		piet@haring.uucp
postmaster	dpk
steven		steven@boring.uucp
jim		jim@bellcore.uucp
kingston	kingston@mcvax.uucp
#
#	Local aliases
#
root		jaap,dpk,piet
daemon		root
operator	root
bin		root
uucp		root
news		root
usenet		dpk
guest		root
ftp		root
nobody		root
acsnet		root
mmdf		dpk,piet
#
# Lists
#
mg1		:include:/usr/mmdf/lists/mg1,news//userfs1/archive/mg1
mg1-request	dpk
"wats3", pref=08, suff="<"
DPRT "/mmdf/samples/4.3vax/table/rootdomain   444      0     12         607  3620510501  12600 arpa		haring.uucp
bitnet		haring.uucp
cdn		haring.uucp
cern		haring.uucp
com		haring.uucp
csnet		haring.uucp
dec		haring.uucp
decnet		haring.uucp
desy		haring.uucp
edu		haring.uucp
haring		haring.uucp
junet		haring.uucp
mailnet		haring.uucp
mil		haring.uucp
net		haring.uucp
org		haring.uucp
oz		haring.uucp
sun		haring.uucp
uk		haring.uucp
uucp		haring.uucp
fun		haring.uucp
fake		foo.uucp

# Lists
#
mg1		:include:/usr/mmdf/lists/mg1,news//userfs1/archive/mg1
mg1-request	dpk
"wats3", pref=08, suff="<"
DPRT "/mmdf/samples/4.3vax/table/smtp   444      0     12         274  3620510501  11410 boring.uucp	7.0.0.1
haring.uucp	7.0.0.2
mcvax.uucp	7.0.0.2
turing.uucp	7.0.0.3
sering.uucp	7.0.0.4
zuring.uucp	7.0.0.5
krikkit.uucp	7.0.0.6
wug.mcvax.uucp	7.0.0.99
vod.mcvax.uucp	7.0.0.98
	haring.uucp
junet		haring.uucp
mailnet		haring.uucp
mil		haring.uucp
net		haring.uucp
org		haring.uucp
oz		haring.uucp
sun		haring.uucp
uk		haring.uucp
uucp		haring.uucp
fun		haring.uucp
fake		foo.uucp

# Lists
#
mg1		:include:/usr/mmdf/lists/mg1,news//userfs1/archive/mg1
mg1-request	dpk
"wats3", pref=08, suff="<"
DPRT "/mmdf/samples/4.3vax/table/uucpchn   444      0     12          63  3620510501  12046 sering.uucp	%s
foo.uucp	bar!foo!%s
bar.uucp	bar!%s
7.0.0.2
turing.uucp	7.0.0.3
sering.uucp	7.0.0.4
zuring.uucp	7.0.0.5
krikkit.uucp	7.0.0.6
wug.mcvax.uucp	7.0.0.99
vod.mcvax.uucp	7.0.0.98
	haring.uucp
junet		haring.uucp
mailnet		haring.uucp
mil		haring.uucp
net		haring.uucp
org		haring.uucp
oz		haring.uucp
sun		haring.uucp
uk		haring.uucp
uucp		haring.uucp
fun		haring.uucp
fake		foo.uucp

# Lists
#
mg1		:include:/usr/mmdf/lists/mg1,news//userfs1/archive/mg1
mg1-request	dpk
"wats3", pref=08, suff="<"
DPRT "/mmdf/samples/4.3vax/table/uucpdom   444      0     12         222  3620510501  12072 mcvax		mcvax.uucp
boring		boring.uucp
haring		haring.uucp
sering		sering.uucp
turing		turing.uucp
zuring		zuring.uucp
foo		foo.uucp
bar		bar.uucp
vax.uucp	7.0.0.99
vod.mcvax.uucp	7.0.0.98
	haring.uucp
junet		haring.uucp
mailnet		haring.uucp
mil		haring.uucp
net		haring.uucp
org		haring.uucp
oz		haring.uucp
sun		haring.uucp
uk		haring.uucp
uucp		haring.uucp
fun		haring.uucp
fake		foo.uucp

# Lists
#
mg1		:include:/usr/mmdf/lists/mg1,news//userfs1/archive/mg1
mg1-request	dpk
"wats3", pref=08, suff="<"
DPRT "/mmdf/samples/4.3vax/mmdftailor   444      0     12        2324  3620510500  11511 MLNAME	sering
MLDOMAIN UUCP
MLOCMACHINE sering
MSUPPORT "postmaster@sering.uucp"

; Table entries
MTBL	alias,		file="aliases",		show="Local Name Aliases"
MTBL	local,		file="local",		show="Local Host Aliases"
MTBL	smtp,		file="smtp",		show="SMTP Hosts"
MTBL	uucpchn,	file="uucpchn",		show="UUCP Routes"
MTBL	uucpdom,	file="uucpdom",		show="UUCP Domains"
MTBL	rootdomain,	file="rootdomain",	show="Root Domain"

ALIAS	table=alias, trusted

; Channel entries
MCHN	local, show="Local Delivery", que=local, tbl=local,
	pgm=local, mod=imm
MCHN	smtp, show="SMTP/TCP", que=smtp, tbl=smtp
	pgm=smtp, mod=reg, ap=822, confstr="sering.uucp"
MCHN	uucp, show="UUCP", que=uucp, tbl=uucpchn,
	pgm=uucp, mod=reg, ap=822, lname=sering
MCHN	list, show="List Processing", que=list, tbl=local,
	pgm=list, mod=reg
MCHN	badhosts, show="Bad Host Forwarding", que=badhosts, tbl=smtp
	pgm=smtp, mod=reg, ap=822, confstr="sering.uucp", host="haring.uucp"
MCHN	badusers, show="Bad User Forwarding", que=badusers, tbl=smtp
	pgm=smtp, mod=reg, ap=822, confstr="sering.uucp", host="haring.uucp"

; Domain tables
MDMN	"UUCP", show="UUCP Domain", table=uucpdom
MDMN	"", show="Root Domain", table=rootdomain

; Logging levels
MMSGLOG     level=FST
MCHANLOG    level=FST
", pref=00, suff="<"
DPRT "/dev/ttyd6", "/usr/spool/uucp/LCK..ttyd6", "/dev/cu0", 0177, "local", pref=40, suff="<"
DPRT "/dev/ttyd7", "/usr/spool/uucp/LCK..ttyd7", "/dev/cu0", 0400, "local", pref="0<", suff="<"
DPRT "/dev/ttyd7", "/usr/spool/uucp/LCK..ttyd7", "/dev/cu0", 0177, "local", pref="4<", summdf/samples/okstate/   755      0     12           0  3671072452  10012 mmdf/samples/okstate/README   444      0     12         522  3671072446  10737 A word of warning about this configuration is in order.  The UUCP domain and
channel tables are computed by pathalias(1) and are relative to the system
on which they are computed.  Since the actual text of the UUCP tables are
of no practical use to other sites, I have included only example entrys.

Mark Vasoll
Oklahoma State University
"UUCP Domains"
MTBL	rootdomain,	file="rootdomain",	show="Root Domain"

ALIAS	table=alias, trusted

; Channel entries
MCHN	local, show="Local Delivery", que=local, tbl=local,
mmdf/samples/okstate/mmdftailor   444      0     12        6173  3671072452  12165 ;  MMDF-II tailoring file for Oklahoma State University, CS Department
;  (a "typical" CSNET Phonenet site + UUCP)

MLNAME		"a"
MLDOMAIN	"cs.okstate.edu"

; Table entires
; Authorization tables

MTBL	auth,			file="authorization/auth",
				show="User Authorization"

MTBL	auth-csnet,		file="authorization/pobox",
				show="POBOX Authorization"

; Domain tables

MTBL	dom-cs.okstate.edu,	file="domains/cs.okstate.edu"
MTBL	dom-csnet,		file="domains/csnet"
MTBL	dom-uucp,		file="domains/uucp"
MTBL	dom-top,		file="domains/top"

; Channel tables

MTBL	ch-local,		file="channels/local"
MTBL	ch-okscc1,		file="channels/okscc1"
MTBL	ch-oksee1,		file="channels/oksee1"
MTBL	ch-oksma1,		file="channels/oksma1"
MTBL	ch-csnet,		file="channels/csnet"
MTBL	ch-uucp,		file="channels/uucp"
MTBL	ch-list,		file="channels/list"

; Domain entries

MDMN	"cs.okstate.edu", table="dom-cs.okstate.edu",
	show="CS.OKSTATE.EDU Domain"

MDMN	"okstate.edu", table="dom-okstate.edu",
	show="OKSTATE.EDU Domain"

MDMN	"csnet", table="dom-csnet", show="CSNET Domain"

MDMN	"uucp", table="dom-uucp", show="UUCP Domain"

MDMN    "Top-Level", table="dom-top", name="", dmn="",
	show="Top Level Domain"

; Channel entries

MCHN	local, que=local, tbl="ch-local", ap=822, pgm="local", mod=imm,
	mod=send, show="Local Channel"

MCHN	xokscc1, que=okscc1, tbl="ch-okscc1", trn="trn.xokscc1", ap=822,
	pgm="phone", mod=reg, mod=send, mod=pick, host=a.ucc.okstate.edu,
	ttl=20, poll=4, scr="scripts/okscc1", show="UCC VAX 11/780"

MCHN	xoksee1, que=oksee1, tbl="ch-oksee1", trn="trn.xoksee1", ap=822,
	pgm="phone", mod=reg, mod=send, mod=pick, host=rvax.ecen.okstate.edu,
	ttl=20, poll=12, scr="scripts/oksee1", show="ECEN VAX 11/750"

MCHN	xoksma1, que=oksma1, tbl="ch-oksma1", trn="trn.xoksma1", ap=822,
	pgm="phone", mod=imm, mod=send, mod=pick, host=nemo.math.okstate.edu,
	ttl=20, poll=0, scr="scripts/oksma1", show="MATH & STAT VAX 11/750"

MCHN	yoksma1, que=oksma1, tbl="ch-oksma1", trn="trn.yoksma1", ap=822,
	pgm="pobox", mod=psv, mod=send, mod=pick, host=nemo.math.okstate.edu,
	user=oksma1, show="MATH & STAT VAX 11/750"

MCHN	xpobox, que=csnet, tbl="ch-csnet", trn="trn.xpobox", ap=822,
	pgm="phone", mod=reg, mod=send, mod=pick, host=relay.cs.net, ttl=30
	poll=0, scr="scripts/relay", show="CSNET Phone",
	outsrc=auth-csnet, indest=auth-csnet, auth=inblock, auth=outblock

MCHN	pobox, que=csnet, tbl="ch-csnet", trn="trn.pobox", ap=822,
	pgm="pobox", mod=psv, mod=send, mod=pick, host=relay.cs.net,
	user=okscsnet, show="CSNET Channel",
	outsrc=auth-csnet, indest=auth-csnet, auth=inblock, auth=outblock

MCHN	uucp, que=uucp, tbl="ch-uucp", ap=822, pgm="dmuucp", mod=reg,
	lname=okstate, ldomain=uucp, show="UUCP Channel"

MCHN	list, que=list, tbl="ch-list", ap=822, pgm=list, mod=reg,
	show="List Expansion"

; Definitions of available direct out lines

D_LINE "modem-1200",	"/dev/tty2e", "/usr/spool/locks/LCK..tty2e", 1200
D_LINE "net-1200",	"/dev/tty38", "/usr/spool/locks/LCK..tty38", 1200
D_LINE "nemo",		"/dev/tty48", "/usr/spool/locks/LCK..tty48", 9600
D_LINE "net-1200",	"/dev/tty4e", "/usr/spool/locks/LCK..tty4e", 1200

;logging levels

MMSGLOG		level=FST
MCHANLOG	level=FAT
PHLOG		level=FAT
AUTHLOG		level=FST
	"a"
MLDOMAIN	"cs.okstate.edu"

; Table entires
; Authorization tables

MTBL	auth,			file="authorization/auth",
				show="User Authorization"

MTBL	auth-csnet,		file="authorization/pobox",
				show="POBOX Authorization"

; Domain tables

MTBL	dom-cs.okstate.edu,	file="domains/cs.okstate.edu"
MTBL	dom-csnet,		file="domains/csnet"
MTBL	dom-uucp,		file="domains/uucp"
MTBL	dom-top,		file="dmmdf/samples/okstate/table/   755      0     12           0  3671072504  11077 mmdf/samples/okstate/table/aliases   444      0     12       13433  3671072460  12553 #
# Aliases for Oklahoma State University, Computer Science (A.CS.OKSTATE.EDU)
#
# System support aliases
#
sysmgr:			vasoll
postmaster:		sysmgr
uucp-support:		sysmgr
usenet:			sysmgr
notes:			sysmgr
MMDF:			sysmgr
mmdf:			sysmgr
adm:			sysmgr
bin:			sysmgr
sys:			sysmgr
#
# Trashcan for UUCP failure messages from the Kermit Distribution Project,
# the logins for `pobox' pickup accounts and for various UUCP logins
#
trash:			daemon|/bin/cat
okscsnet:		trash
oksma1:			trash
daemon:			trash
uucp:			trash
uucpaa:			trash
uucpab:			trash
uucpac:			trash
uucpad:			trash
uucpae:			trash
uucpaf:			trash
uucpag:			trash
uucpah:			trash
uucpai:			trash
uucpaj:			trash
uucpak:			trash
uucpal:			trash
uucpam:			trash
uucpan:			trash
uucpao:			trash
uucpker:		trash
#
# Faculty member aliases from last name to user name
#
long:			anl
fisher:			ddf
grace:			dwg
hedrick:		geh
edgmand:		jje
chandler:		jpc
folk:			mjf
thoreson:		sat
#
# Map changed user names
#
george:			gregg
kermit:			gregg
bmjrm:			rick
#
# Map local forwarding addresses
#
ned:			ned@oksma1.csnet
ks:			ks@svo.uucp
#
# Local general purpose mailing lists
#
all:			all-outbound@a@list
all-outbound:		faculty,students
all-request:		vasoll
faculty:		faculty-outbound@a@list
faculty-outbound:	:include:/usr/local/lib/maillist/faculty
faculty-request:	geh
ta:			ta-outbound@a@list
ta-outbound:		:include:/usr/local/lib/maillist/ta
ta-request:		geh
students:		students-outbound@a@list
students-outbound:	:include:/usr/local/lib/maillist/students
students-request:	vasoll
guests:			guests-outbound@a@list
guests-outbound:	:include:/usr/local/lib/maillist/guests
guests-request:		vasoll
alumni:			alumni-outbound@a@list
alumni-outbound:	:include:/usr/local/lib/maillist/alumni
alumni-request:		geh
#
# OSU Phonenet liaisons mailing list
#
osu-pn-liaisons:		osu-pn-liaisons-outbound@a@list
osu-pn-liaisons-outbound:	:include:/usr/local/lib/maillist/osu-pn-liaisons
osu-pn-liaisons-request:	vasoll
#
# UUCP liaisons mailing list
#
uucp-liaisons:			uucp-liaisons-outbound@a@list
uucp-liaisons-outbound:		:include:/usr/local/lib/maillist/uucp-liaisons
uucp-liaisons-request:		vasoll
#
# Local special interest mailing lists
#
wizards:		wizards-outbound@a@list
wizards-outbound:	:include:/usr/local/lib/maillist/wizards
wizards-request:	vasoll
ada:			ada-outbound@a@list
ada-outbound:		:include:/usr/local/lib/maillist/ada
ada-request:		geh
#
# Local class mailing lists
#
# List containing the union of all class mailing lists.
all-classes:		all-classes-outbound@a@list
all-classes-outbound:	:include:/usr/local/lib/maillist/all-classes
all-classes-request:	vasoll
# 2113 TA list updated Fall 85
2113-ta:		2113-ta-outbound@a@list
2113-ta-outbound:	:include:/usr/local/lib/maillist/2113-ta
2113-ta-request:	kdavis
# 3301 list updated Fall 85
3301:			3301-outbound@a@list
3301-outbound:		:include:/usr/local/lib/maillist/3301
3301-request:		kdavis
# 3333 list updated Fall 85
3333:			3333-outbound@a@list
3333-outbound:		:include:/usr/local/lib/maillist/3333
3333-request:		geh
# 3431 list updated Spring 85
3431:			3431-outbound@a@list
3431-outbound:		:include:/usr/local/lib/maillist/3431
3431-request:		mjf,slee
# 3451 list updated Fall 85
3451:			3451-outbound@a@list
3451-outbound:		:include:/usr/local/lib/maillist/3451
3451-request:		ddf
# 4323 list updated Fall 85
4323:			4323-outbound@a@list
4323-outbound:		:include:/usr/local/lib/maillist/4323
4323-request:		ajm
# 4344 list updated Fall 85
4344:			4344-outbound@a@list
4344-outbound:		:include:/usr/local/lib/maillist/4344
4344-request:		ddf,vxt
# 4424 list updated Spring 86
4424:			4424-outbound@a@list
4424-outbound:		:include:/usr/local/lib/maillist/4424
4424-request:		mjf,slee
# 5113 list updated Spring 86
5113:			5113-outbound@a@list
5113-outbound:		:include:/usr/local/lib/maillist/5113
5113-request:		ddf
# 5313 list updated Fall 85
5313:			5313-outbound@a@list
5313-outbound:		:include:/usr/local/lib/maillist/5313
5313-request:		geh
# 5323 list updated Spring 86
5323:			5323-outbound@a@list
5323-outbound:		:include:/usr/local/lib/maillist/5323
5323-request:		sat
# 5413 list updated Fall 85
5413:			5413-outbound@a@list
5413-outbound:		:include:/usr/local/lib/maillist/5413
5413-request:		ddf
#
# Test mailing list
#
test:			test-outbound@a@list
test-outbound:		:include:/usr/local/lib/maillist/test
test-request:		vasoll
#
# Local redistribution lists of network-wide, privite mailing lists.
#
security-forum:			security-forum-outbound@a@list
security-forum-outbound:	:include:/usr/local/lib/maillist/security
security-forum-request:		ks
#
# Gateway from certain network mailing addresses to Notesfiles
# be added to a network mailing list on another system.  When the local
# channel processes this address, it will get stuffed into a notesfile
# by the nfmail command.  Unfortunately, this is currently a oneway
# street.
#
ailist:			ailist-outbound@a@list
ailist-outbound:	notes|/usr/spool/notes/.utilities/nfmail -z ailist
csnet-forum:		csnet-forum-outbound@a@list
csnet-forum-outbound:	notes|/usr/spool/notes/.utilities/nfmail -z csnet-forum
info-ada:		info-ada-outbound@a@list
info-ada-outbound:	notes|/usr/spool/notes/.utilities/nfmail -z info-ada
info-unix:		info-unix-outbound@a@list
info-unix-outbound:	notes|/usr/spool/notes/.utilities/nfmail -z info-unix
info-kermit:		info-kermit-outbound@a@list
info-kermit-outbound:	notes|/usr/spool/notes/.utilities/nfmail -z info-kermit
info-mathlib:		info-mathlib-outbound@a@list, ned@oksma1, duvall@oksma1
info-mathlib-outbound:	notes|/usr/spool/notes/.utilities/nfmail -z info-mathlib
texhax:			texhax-outbound@a@list,ned@oksma1
texhax-outbound:	notes|/usr/spool/notes/.utilities/nfmail -z texhax
unix-tex:		unix-tex-outbound@a@list
unix-tex-outbound:	notes|/usr/spool/notes/.utilities/nfmail -z unix-tex
unix-wizards:		unix-wizards-outbound@a@list
unix-wizards-outbound:	notes|/usr/spool/notes/.utilities/nfmail -z unix-wizards
nown=arpa
;:;MCHN	udelrelay, show="UDEL/TCP", que=udelrelay, tbl=udelrelay, ap=822,
;:;	pgm="smtp", mod=reg, host=udel-relay, ldomain=arpa,
;:;	lname=csnet-relay, known=arpa
;:;MCHN	smtp, show="SMTP/TCP", que=smtp, tbl=smtp, ap=8mmdf/samples/okstate/table/authorization/   755      0     12           0  3671072463  14003 mmdf/samples/okstate/table/authorization/auth   444      0     12         473  3671072461  14734 #
# Authorization file for Oklahoma State University, CS Department
# This system is the main mail gateway to OSU, we make agreements
# with various departments to provide access to the mail network.
#
# Per user authorization for SVO
#
ks@svo.uucp:both local,list,xokscc1,xoksee1,xoksma1,yoksma1,pobox,xpobox,uucp
/3301
3301-request:		kdavis
# 3333 list updated Fall 85
3333:			3333-outbound@a@list
3333-outbound:		:include:/usr/local/lib/maillist/3333
3333-request:		geh
# 3431 list updated Spring 85
3431:			3mmdf/samples/okstate/table/authorization/local   444      0     12         742  3671072462  15065 local:
list:
xokscc1:
xoksee1:
xoksma1:
yoksma1:
okstride:
okstride.csnet:
okscs1:
okscs1.csnet:
okscs2:
okscs2.csnet:
oksst1:
oksst1.csnet:
hpmae:
hpmae.csnet:
glmnhh:
glmnhh.uucp:
ea
ea.uucp
occrsh
occrsh.uucp
glmnhh
glmnhh.uucp
uok
uok.uucp
cemsa
cemsa.uucp
cemsb
cemsb.uucp
cemsc
cemsc.uucp
amne
amne.uucp
ie
ie.uucp
uoksun
uoksun.uucp
uokvax
uokvax.uucp
eecsa
eecsa.uucp
eecsb
eecsb.uucp
kepix
kepix.uucp
kenix
kenix.uucp
fremen
fremen.uucp
uokmet
uokmet.uucp
amber
amber.uucp
st updated Spring 85
3431:			3mmdf/samples/okstate/table/authorization/pobox   444      0     12         207  3671072463  15117 local:
list:
pobox:
xpobox:
xokscc1:
xoksee1:
xoksma1:
yoksma1:
okstride:
okstride.csnet:
thermo.chem.okstate.edu:
hpmae:
hpmae.csnet:
snet:
hpmae:
hpmae.csnet:
glmnhh:
glmnhh.uucp:
ea
ea.uucp
occrsh
occrsh.uucp
glmnhh
glmnhh.uucp
uok
uok.uucp
cemsa
cemsa.uucp
cemsb
cemsb.uucp
cemsc
cemsc.uucp
amne
amne.uucp
ie
ie.uucp
uoksun
uoksun.uucp
uokvax
uokvax.uucp
eecsa
eecsa.uucp
eecsb
eecsb.uucp
kepix
kepix.uucp
kenix
kenix.uucp
fremen
fremen.uucp
uokmet
uokmet.uucp
amber
amber.uucp
st updated Spring 85
3431:			3mmdf/samples/okstate/table/channels/   755      0     12           0  3671072472  12676 mmdf/samples/okstate/table/channels/csnet   444      0     12          32  3671072465  13755 relay.cs.net:relay.cs.net
:
xokscc1:
xoksee1:
xoksma1:
yoksma1:
okstride:
okstride.csnet:
thermo.chem.okstate.edu:
hpmae:
hpmae.csnet:
snet:
hpmae:
hpmae.csnet:
glmnhh:
glmnhh.uucp:
ea
ea.uucp
occrsh
occrsh.uucp
glmnhh
glmnhh.uucp
uok
uok.uucp
cemsa
cemsa.uucp
cemsb
cemsb.uucp
cemsc
cemsc.uucp
amne
amne.uucp
ie
ie.uucp
uoksun
uoksun.uucp
uokvax
uokvax.uucp
eecsa
eecsa.uucp
eecsb
eecsb.uucp
kepix
kepix.uucp
kenix
kenix.uucp
fremen
fremen.uucp
uokmet
uokmet.uucp
amber
amber.uucp
st updated Spring 85
3431:			3mmdf/samples/okstate/table/channels/list   444      0     12         162  3671072466  13641 a.cs.okstate.edu:a.cs.okstate.edu
a:a.cs.okstate.edu
okstate.csnet:a.cs.okstate.edu
okstate.uucp:a.cs.okstate.edu

hpmae:
hpmae.csnet:
snet:
hpmae:
hpmae.csnet:
glmnhh:
glmnhh.uucp:
ea
ea.uucp
occrsh
occrsh.uucp
glmnhh
glmnhh.uucp
uok
uok.uucp
cemsa
cemsa.uucp
cemsb
cemsb.uucp
cemsc
cemsc.uucp
amne
amne.uucp
ie
ie.uucp
uoksun
uoksun.uucp
uokvax
uokvax.uucp
eecsa
eecsa.uucp
eecsb
eecsb.uucp
kepix
kepix.uucp
kenix
kenix.uucp
fremen
fremen.uucp
uokmet
uokmet.uucp
amber
amber.uucp
st updated Spring 85
3431:			3mmdf/samples/okstate/table/channels/local   444      0     12         162  3671072467  13761 a.cs.okstate.edu:a.cs.okstate.edu
a:a.cs.okstate.edu
okstate.csnet:a.cs.okstate.edu
okstate.uucp:a.cs.okstate.edu

hpmae:
hpmae.csnet:
snet:
hpmae:
hpmae.csnet:
glmnhh:
glmnhh.uucp:
ea
ea.uucp
occrsh
occrsh.uucp
glmnhh
glmnhh.uucp
uok
uok.uucp
cemsa
cemsa.uucp
cemsb
cemsb.uucp
cemsc
cemsc.uucp
amne
amne.uucp
ie
ie.uucp
uoksun
uoksun.uucp
uokvax
uokvax.uucp
eecsa
eecsa.uucp
eecsb
eecsb.uucp
kepix
kepix.uucp
kenix
kenix.uucp
fremen
fremen.uucp
uokmet
uokmet.uucp
amber
amber.uucp
st updated Spring 85
3431:			3mmdf/samples/okstate/table/channels/okscc1   444      0     12         103  3671072470  14037 a.ucc.okstate.edu:a.ucc.okstate.edu
okscc1.csnet:a.ucc.okstate.edu
a.cs.okstate.edu
okstate.uucp:a.cs.okstate.edu

hpmae:
hpmae.csnet:
snet:
hpmae:
hpmae.csnet:
glmnhh:
glmnhh.uucp:
ea
ea.uucp
occrsh
occrsh.uucp
glmnhh
glmnhh.uucp
uok
uok.uucp
cemsa
cemsa.uucp
cemsb
cemsb.uucp
cemsc
cemsc.uucp
amne
amne.uucp
ie
ie.uucp
uoksun
uoksun.uucp
uokvax
uokvax.uucp
eecsa
eecsa.uucp
eecsb
eecsb.uucp
kepix
kepix.uucp
kenix
kenix.uucp
fremen
fremen.uucp
uokmet
uokmet.uucp
amber
amber.uucp
st updated Spring 85
3431:			3mmdf/samples/okstate/table/channels/oksee1   444      0     12         117  3671072471  14051 rvax.ecen.okstate.edu:rvax.ecen.okstate.edu
oksee1.csnet:rvax.ecen.okstate.edu
.edu
okstate.uucp:a.cs.okstate.edu

hpmae:
hpmae.csnet:
snet:
hpmae:
hpmae.csnet:
glmnhh:
glmnhh.uucp:
ea
ea.uucp
occrsh
occrsh.uucp
glmnhh
glmnhh.uucp
uok
uok.uucp
cemsa
cemsa.uucp
cemsb
cemsb.uucp
cemsc
cemsc.uucp
amne
amne.uucp
ie
ie.uucp
uoksun
uoksun.uucp
uokvax
uokvax.uucp
eecsa
eecsa.uucp
eecsb
eecsb.uucp
kepix
kepix.uucp
kenix
kenix.uucp
fremen
fremen.uucp
uokmet
uokmet.uucp
amber
amber.uucp
st updated Spring 85
3431:			3mmdf/samples/okstate/table/channels/oksma1   444      0     12         117  3671072471  14055 nemo.math.okstate.edu:nemo.math.okstate.edu
oksma1.csnet:nemo.math.okstate.edu
.edu
okstate.uucp:a.cs.okstate.edu

hpmae:
hpmae.csnet:
snet:
hpmae:
hpmae.csnet:
glmnhh:
glmnhh.uucp:
ea
ea.uucp
occrsh
occrsh.uucp
glmnhh
glmnhh.uucp
uok
uok.uucp
cemsa
cemsa.uucp
cemsb
cemsb.uucp
cemsc
cemsc.uucp
amne
amne.uucp
ie
ie.uucp
uoksun
uoksun.uucp
uokvax
uokvax.uucp
eecsa
eecsa.uucp
eecsb
eecsb.uucp
kepix
kepix.uucp
kenix
kenix.uucp
fremen
fremen.uucp
uokmet
uokmet.uucp
amber
amber.uucp
st updated Spring 85
3431:			3mmdf/samples/okstate/table/channels/uucp   444      0     12        1361  3671072472  13661 okstate.uucp:%s
okstride.csnet:okstride!%s
okscs2.csnet:okscs2!%s
thermo.chem.okstate.edu:okstride!%s
hpmae.csnet:hpmae!%s
tuvax.uucp:tuvax!%s
uokvax.uucp:uokvax!%s
uokmet.uucp:uokvax!uokmet!%s

[ about 6100 lines deleted here ]

houligan.uucp:cbosgd!brl-bmd!houligan!%s
birddog.uucp:cbosgd!brl-bmd!houligan!birddog:%s
kant.uucp:cbosgd!brl-bmd!houligan!kant:%s
cadsun.uucp:cbosgd!brl-bmd!houligan!cadsun:%s
frankenix.uucp:cbosgd!brl-bmd!houligan!frankenix:%s
nugipsy.uucp:cbosgd!brl-bmd!houligan!nugipsy:%s
smaug.uucp:cbosgd!brl-bmd!houligan!smaug:%s
pcpond.uucp:cbosgd!brl-bmd!pcpond!%s
delaware.uucp:cbosgd!brl-bmd!pcpond!delaware!%s
marketing.uucp:cbosgd!brl-bmd!marketing!%s
aatpdx.uucp:cbosgd!aatpdx!%s
aat.uucp:cbosgd!aat!%s
apc3b2.uucp:apc3b2!%s
pdated Fall 85
4323:			4323-outbound@a@list
4323-outbound:		:include:/usr/local/lib/maillist/4323
4323-request:		ajm
# 4344 list updated Fall 85
4344:			4344-outbound@a@list
4344-outbound:		:include:/usr/local/lib/maillist/4344
4344-request:		ddf,vxt
# 4424 list updated mmdf/samples/okstate/table/domains/   755      0     12           0  3671072502  12527 mmdf/samples/okstate/table/domains/cs.okstate.edu   444      0     12         271  3671072474  15357 a:a
okstate:a
a:a.cs.okstate.edu
b:b.cs.okstate.edu
c:c.cs.okstate.edu
d:d.cs.okstate.edu
e:e.cs.okstate.edu
f:f.cs.okstate.edu
g:g.cs.okstate.edu
h:h.cs.okstate.edu
i:i.cs.okstate.edu
okmet!%s

[ about 6100 lines deleted here ]

houligan.uucp:cbosgd!brl-bmd!houligan!%s
birddog.uucp:cbosgd!brl-bmd!houligan!birddog:%s
kant.uucp:cbosgd!brl-bmd!houligan!kant:%s
cadsun.uucp:cbosgd!brl-bmd!houligan!cadsun:%s
frankenix.uucp:cbosgd!brl-bmd!houligan!frankenix:%s
nugipsy.uucp:cbosgd!brl-bmd!houligan!nugipsy:%s
smaugmmdf/samples/okstate/table/domains/csnet   444      0     12         254  3671072476  13644 OKSTATE:A
OKLAHOMA-STATE:A
OKSCC1:A.UCC.OKSTATE.EDU
OKSCS2:OKSCS2.CSNET
OKSEE1:RVAX.ECEN.OKSTATE.EDU
OKSMA1:NEMO.MATH.OKSTATE.EDU
OKSTRIDE:OKSTRIDE.CSNET
HPMAE:HPMAE.CSNET
.okstate.edu
okmet!%s

[ about 6100 lines deleted here ]

houligan.uucp:cbosgd!brl-bmd!houligan!%s
birddog.uucp:cbosgd!brl-bmd!houligan!birddog:%s
kant.uucp:cbosgd!brl-bmd!houligan!kant:%s
cadsun.uucp:cbosgd!brl-bmd!houligan!cadsun:%s
frankenix.uucp:cbosgd!brl-bmd!houligan!frankenix:%s
nugipsy.uucp:cbosgd!brl-bmd!houligan!nugipsy:%s
smaugmmdf/samples/okstate/table/domains/okstate.edu   444      0     12         140  3671072477  14751 cs:a.cs.okstate.edu
math:nemo.math.okstate.edu
ecen:rvax.ecen.okstate.edu
ucc:a.ucc.okstate.edu
.EDU
OKSMA1:NEMO.MATH.OKSTATE.EDU
OKSTRIDE:OKSTRIDE.CSNET
HPMAE:HPMAE.CSNET
.okstate.edu
okmet!%s

[ about 6100 lines deleted here ]

houligan.uucp:cbosgd!brl-bmd!houligan!%s
birddog.uucp:cbosgd!brl-bmd!houligan!birddog:%s
kant.uucp:cbosgd!brl-bmd!houligan!kant:%s
cadsun.uucp:cbosgd!brl-bmd!houligan!cadsun:%s
frankenix.uucp:cbosgd!brl-bmd!houligan!frankenix:%s
nugipsy.uucp:cbosgd!brl-bmd!houligan!nugipsy:%s
smaugmmdf/samples/okstate/table/domains/top   444      0     12         561  3671072501  13320 thermo.chem.okstate.edu:thermo.chem.okstate.edu
okstate.edu:a.cs.okstate.edu
att.com:cbosgd.uucp
math:nemo.math.okstate.edu
ecen:rvax.ecen.okstate.edu
ucc:a.ucc.okstate.edu
arpa:relay.cs.net
bitnet:relay.cs.net
mailnet:relay.cs.net
csnet:relay.cs.net
edu:relay.cs.net
com:relay.cs.net
mil:relay.cs.net
net:relay.cs.net
gov:relay.cs.net
org:relay.cs.net
uk:relay.cs.net
ucp:cbosgd!brl-bmd!houligan!cadsun:%s
frankenix.uucp:cbosgd!brl-bmd!houligan!frankenix:%s
nugipsy.uucp:cbosgd!brl-bmd!houligan!nugipsy:%s
smaugmmdf/samples/okstate/table/domains/uucp   444      0     12         443  3671072503  13473 okstate:okstate.uucp
uokvax:uokvax.uucp
kant:kant.uucp
cadsun:cadsun.uucp

[ about 6100 lines deleted here ]

frankenix:frankenix.uucp
nugipsy:nugipsy.uucp
smaug:smaug.uucp
pcpond:pcpond.uucp
delaware:delaware.uucp
marketing:marketing.uucp
aatpdx:aatpdx.uucp
aat:aat.uucp
apc3b2:apc3b2.uucp
lay.cs.net
net:relay.cs.net
gov:relay.cs.net
org:relay.cs.net
uk:relay.cs.net
ucp:cbosgd!brl-bmd!houligan!cadsun:%s
frankenix.uucp:cbosgd!brl-bmd!houligan!frankenix:%s
nugipsy.uucp:cbosgd!brl-bmd!houligan!nugipsy:%s
smaugmmdf/samples/okstate/table/gen   444      0     12         162  3671072504  11635 TABLE=/usr/mmdf/table
$TABLE/dbmbuild -Onv
chmod 666 $TABLE/mmdf*
chown mmdf $TABLE/mmdf*
chgrp mmdf $TABLE/mmdf*
kenix:frankenix.uucp
nugipsy:nugipsy.uucp
smaug:smaug.uucp
pcpond:pcpond.uucp
delaware:delaware.uucp
marketing:marketing.uucp
aatpdx:aatpdx.uucp
aat:aat.uucp
apc3b2:apc3b2.uucp
lay.cs.net
net:relay.cs.net
gov:relay.cs.net
org:relay.cs.net
uk:relay.cs.net
ucp:cbosgd!brl-bmd!houligan!cadsun:%s
frankenix.uucp:cbosgd!brl-bmd!houligan!frankenix:%s
nugipsy.uucp:cbosgd!brl-bmd!houligan!nugipsy:%s
smaugmmdf/samples/okstate/table/scripts/   755      0     12           0  3671072510  12563 mmdf/samples/okstate/table/scripts/okscc1   444      0     12         726  3671072505  13745 xmitpack 130
recvpack 130
xmitill "\0\1\2\3\4\5\6\7\10\11\13\14\16\17"
xmitill "\20\21\22\23\24\25\26\27\30\31\32\33\34\35\36\37"
xmitill "\177"
recvill "\0\1\2\3\4\5\6\7\10\11\13\14\16\17"
recvill "\20\21\22\23\24\25\26\27\30\31\32\33\34\35\36\37"
recvill "\177"
window 2 1
dial net-1200
xmit "\x\r"
recv "NETWORK" 30
xmit "VAX\r"
recv "COM" 30
xmit "\x\r\x\r\x\r"
recv "name:" 30
xmit "FOO\r"
recv "ssword:" 30
xmit "BAR\r"
recv "annel: " 120
xmit "pobox\r"
start
end
p:cbosgd!brl-bmd!houligan!nugipsy:%s
smaugmmdf/samples/okstate/table/scripts/oksee1   444      0     12        1632  3671072507  13770 xmitpack 130
recvpack 130
xmitill "\0\1\2\3\4\5\6\7\10\11\13\14\16\17"
xmitill "\20\21\22\23\24\25\26\27\30\31\32\33\34\35\36\37"
xmitill "\177"
recvill "\0\1\2\3\4\5\6\7\10\11\13\14\16\17"
recvill "\20\21\22\23\24\25\26\27\30\31\32\33\34\35\36\37"
recvill "\177"
{
alternate
	dial net-1200
	xmit "\x\r"
	recv "NETWORK" 60
	xmit "EEVAX\r"
	recv "COM" 60
	xmit "\x\r\x\r\x\r"
	recv "name:" 60
	xmit "FOO\r"
	recv "ssword:" 60
	xmit "BAR\r"
	recv "annel: " 120
	xmit "pobox\r"
alternate
	dial modem-1200
	xmit "ATDT,7352\r"
	recv "CONNECT" 60
	xmit "\r\x\r\x\r\x\r"
	recv "name:" 60
	xmit "FOO\r"
	recv "ssword:" 60
	xmit "BAR\r"
	recv "annel: " 120
	xmit "pobox\r"
alternate
	dial modem-1200
	xmit "ATDT,7580\r"
	recv "CONNECT" 60
	xmit "\r\x\r\x\r\x\r"
	recv "name:" 60
	xmit "FOO\r"
	recv "ssword:" 60
	xmit "BAR\r"
	recv "annel: " 120
	xmit "pobox\r"
alternate
	log "We tried, but we couldn't do it!"
	abort
}
start
end
v "ssword:" 60
	xmit "BAR\r"
	recv "annel: " 120
	xmit "pobox\r"
alternate
	dial modem-1200
	xmit "ATDmmdf/samples/okstate/table/scripts/oksma1   444      0     12         771  3671072510  13751 xmitpack 255
recvpack 255
xmitill "\000\001\002\003\004\005\006\007\010\011\012\013\014\015\016\017"
xmitill "\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037"
xmitill "\177"
recvill "\000\001\002\003\004\005\006\007\010\011\012\013\014\015\016\017"
recvill "\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037"
recvill "\177"
window 2 1
dial nemo
xmit "\r\x\r\x\r\x\r"
recv "sername:" 30
xmit "FOO\r"
recv "ssword:" 30
xmit "BAR\r"
recv "hannel:" 60
xmit "pobox\r"
start
end
it "ATDmmdf/samples/okstate/table/scripts/relay   444      0     12        1247  3671072511  13712 xmitsize 255
recvsize 255
waitack 120
waitdata 300
xmitill "\0\1\2\3\4\5\6\7\10\11\13\14\16\17"
xmitill "\20\21\22\23\24\25\26\27\30\31\32\33\34\35\36\37"
xmitill "\177"
recvill "\0\1\2\3\4\5\6\7\10\11\13\14\16\17"
recvill "\20\21\22\23\24\25\26\27\30\31\32\33\34\35\36\37"
recvill "\177"
dial net-1200
xmit "\x\r"
recv "NETWORK" 30
xmit "X25\r"
recv "COM" 30
xmit "\r\x\r\x\r\x\r\x\r"
recv "*" 30
{
alternate
	xmit "C 1234567890\r"
	recv "com" 30
	xmit "\r"
	recv "ogin:" 4
alternate
	xmit "\020"
	recv "*" 30
	xmit "clr"
	xmit "C 1234567890\r"
	recv "com" 30
	xmit "\r"
	recv "ogin:" 4
}
xmit "FOO\r"
recv "ssword:" 60
xmit "BAR\r"
recv "hannel:" 240
xmit "mychan\r"
start
end
aatpdx.uucp:cbosgd!aatpdx!%s
aat.uucp:cbosgd!aat!%s
apc3b2.uucp:apc3b2!%s
pdated Fall 85
4323:			4323-outbound@a@list
4323-outbound:		:include:/usr/local/lib/maillist/4323
4323-request:		ajm
# 4344 list updated Fall 85
4344:			4344-outbound@a@list
4344-outbound:		:include:/usr/local/lib/maillist/4344
4344-request:		ddf,vxt
# 4424 list updated mmdf/samples/dial_scripts/   755      0     12           0  3656411040  11011 mmdf/samples/dial_scripts/Script   444      0     12         462  3620510507  12244 window 2 0
xmitpack 130
recvpack 130
xmitill "\0\1\2\3\5\6\7\10\13\14\16\17"
xmitill "\20\21\22\23\24\25\26\27\30\31\32\33\34\35"
xmitill "\36\37\134\177"
recvill "\0\1\2\3\5\6\7\10\13\14\16\17"
recvill "\20\21\22\23\24\25\26\27\30\31\32\33\34\35"
recvill "\36\37\134\177"
dial vld70
xmit "\x\x\x\r\x\r\x"
t "\x\r"
recv "NETWORK" 30
xmit "X25\r"
recv "COM" 30
xmit "\r\x\r\x\r\x\r\x\r"
recv "*" 30
{
alternate
	xmit "C 1234567890\r"
	recv "com" 30
	xmit "\r"
	recv "ogin:" 4
alternate
	xmit "\020"
	recv "*" 30
	mmdf/samples/dial_scripts/standard   444      0     12         422  3655234601  12602 xmitpack $1
recvpack $2
xmitill "\0\1\2\3\4\5\6\7\10\11\12\13\14\15\16\17"
xmitill "\20\21\22\23\24\25\26\27\30\31\32\33\34\35\36\37"
xmitill "\177"
recvill "\0\1\2\3\4\5\6\7\10\11\12\13\14\15\16\17"
recvill "\20\21\22\23\24\25\26\27\30\31\32\33\34\35\36\37"
recvill "\177"
ial vld70
xmit "\x\x\x\r\x\r\x"
t "\x\r"
recv "NETWORK" 30
xmit "X25\r"
recv "COM" 30
xmit "\r\x\r\x\r\x\r\x\r"
recv "*" 30
{
alternate
	xmit "C 1234567890\r"
	recv "com" 30
	xmit "\r"
	recv "ogin:" 4
alternate
	xmit "\020"
	recv "*" 30
	mmdf/samples/dial_scripts/script1   444      0     12         507  3655234602  12374 window 2 0
use standard 130 130
{
  alternate
	dial 1200|9:453-6436
	xmit "\r\r"
	xmit "\r\r\r\r"
	recv "ame:" 30
  alternate
	dial 300|9:453-6300
	xmit "\r"
	xmit "20\r"
	recv "ame:" 30
}
xmit "\x\x\manion\r"
recv "word:" 30
xmit "\aardwolf\r"
recv "% " 30
xmit "/usa/manion/mark\r"
recv "%" 10
xmit "logout\r"
log "done"
end
K" 30
xmit "X25\r"
recv "COM" 30
xmit "\r\x\r\x\r\x\r\x\r"
recv "*" 30
{
alternate
	xmit "C 1234567890\r"
	recv "com" 30
	xmit "\r"
	recv "ogin:" 4
alternate
	xmit "\020"
	recv "*" 30
	mmdf/samples/dial_scripts/script2   444      0     12         467  3655234603  12403 window 2 0
use standard 130 130
{
  alternate
	dial 1200|9:453-6436
#	xmit "\r\r"
	recv "ame:" 30
  alternate
	dial 300|9:453-6300
	xmit "\r"
	xmit "20\r"
	recv "ame:" 30
}
xmit "\x\x\manion\r"
recv "word:" 30
xmit "\aardwolf\r"
recv "% " 30
xmit "/usa/manion/mark\r"
recv "%" 10
xmit "logout\r"
log "done"
end

log "done"
end
K" 30
xmit "X25\r"
recv "COM" 30
xmit "\r\x\r\x\r\x\r\x\r"
recv "*" 30
{
alternate
	xmit "C 1234567890\r"
	recv "com" 30
	xmit "\r"
	recv "ogin:" 4
alternate
	xmit "\020"
	recv "*" 30
	mmdf/samples/dial_scripts/script3   444      0     12         533  3655234604  12377 window 2 0
use standard 130 130
{
  alternate
	dial 300|9:453-6436
	xmit "\r\r"
#	dial 300|9:453-6300
#	xmit "\r"
#	xmit "20\r"
	recv "ame:" 30
  alternate
	dial 1200|9:453-6436
	xmit "\r\r"
	recv "ame:" 30
}
xmit "\x\x\manion\r"
recv "word:" 30
xmit "\aardwolf\r"
recv "% " 30
xmit "/usa/manion/mark\r"
recv "%" 10
xmit "logout\r"
log "done"
end
ecv "COM" 30
xmit "\r\x\r\x\r\x\r\x\r"
recv "*" 30
{
alternate
	xmit "C 1234567890\r"
	recv "com" 30
	xmit "\r"
	recv "ogin:" 4
alternate
	xmit "\020"
	recv "*" 30
	mmdf/samples/dial_scripts/script4   444      0     12         534  3655234606  12403 window 2 0
use standard 130 130
{
  alternate
	dial 1200|9:453-6436
	xmit "\x\x\x\x\x\x\x\x\x\x\r\r"
#	xmit "\r\r\r\r"
	recv "ame:" 30
  alternate
	dial 300|9:453-6300
	xmit "\r"
	xmit "20\r"
	recv "ame:" 30
}
xmit "\x\x\manion\r"
recv "word:" 30
xmit "\aardwolf\r"
recv "% " 30
xmit "/usa/manion/mark\r"
recv "%" 10
xmit "logout\r"
log "done"
end
cv "COM" 30
xmit "\r\x\r\x\r\x\r\x\r"
recv "*" 30
{
alternate
	xmit "C 1234567890\r"
	recv "com" 30
	xmit "\r"
	recv "ogin:" 4
alternate
	xmit "\020"
	recv "*" 30
	mmdf/samples/vu44/   755      0     12           0  3635165167   7150 mmdf/samples/vu44/table/   755      0     12           0  3635165167  10237 mmdf/samples/vu44/table/aliases   444      0     12         407  3620510511  11624 mmdf		gregor
Postmaster	gregor
dpk		dpk@mcvax.UUCP
jennifer	jennifer@mcvax.UUCP
sjoerd		sjoerd@tjalk.UUCP
leovm		leovm@tjalk.UUCP
matty		matty@tjalk.UUCP
dick		dick@tjalk.UUCP
sape		sape@mcvax.UUCP
ast		ast@tjalk.UUCP
bal		bal@logger.UUCP
sater		sater@tjalk.UUCP
r"
recv "% " 30
xmit "/usa/manion/mark\r"
recv "%" 10
xmit "logout\r"
log "done"
end
cv "COM" 30
xmit "\r\x\r\x\r\x\r\x\r"
recv "*" 30
{
alternate
	xmit "C 1234567890\r"
	recv "com" 30
	xmit "\r"
	recv "ogin:" 4
alternate
	xmit "\020"
	recv "*" 30
	mmdf/samples/vu44/table/list   444      0     12          67  3620510511  11140 list-processor	list-processor
list-proc	list-processor
ifer	jennifer@mcvax.UUCP
sjoerd		sjoerd@tjalk.UUCP
leovm		leovm@tjalk.UUCP
matty		matty@tjalk.UUCP
dick		dick@tjalk.UUCP
sape		sape@mcvax.UUCP
ast		ast@tjalk.UUCP
bal		bal@logger.UUCP
sater		sater@tjalk.UUCP
r"
recv "% " 30
xmit "/usa/manion/mark\r"
recv "%" 10
xmit "logout\r"
log "done"
end
cv "COM" 30
xmit "\r\x\r\x\r\x\r\x\r"
recv "*" 30
{
alternate
	xmit "C 1234567890\r"
	recv "com" 30
	xmit "\r"
	recv "ogin:" 4
alternate
	xmit "\020"
	recv "*" 30
	mmdf/samples/vu44/table/local   444      0     12         113  3620510511  11267 vu44.UUCP	vu44
vu44		vu44
laser.vu44.UUCP	vu44
laser.UUCP	vu44
laser		vu44
UUCP
sjoerd		sjoerd@tjalk.UUCP
leovm		leovm@tjalk.UUCP
matty		matty@tjalk.UUCP
dick		dick@tjalk.UUCP
sape		sape@mcvax.UUCP
ast		ast@tjalk.UUCP
bal		bal@logger.UUCP
sater		sater@tjalk.UUCP
r"
recv "% " 30
xmit "/usa/manion/mark\r"
recv "%" 10
xmit "logout\r"
log "done"
end
cv "COM" 30
xmit "\r\x\r\x\r\x\r\x\r"
recv "*" 30
{
alternate
	xmit "C 1234567890\r"
	recv "com" 30
	xmit "\r"
	recv "ogin:" 4
alternate
	xmit "\020"
	recv "*" 30
	mmdf/samples/vu44/table/uucpchn   444      0     12         404  3620510512  11646 laser.uucp		%s
tjalk.uucp		tjalk!%s
vu44.uucp		tjalk!vu44!%s
vu45.uucp		tjalk!vu45!%s
logger.uucp		tjalk!logger!%s
vu60.uucp		tjalk!vu44!vu60!%s
botter.uucp		tjalk!botter!%s
ark.uucp		tjalk!ark!%s
klipper.uucp		tjalk!klipper!%s
mcvax.uucp		tjalk!vu44!mcvax!%s
CP
r"
recv "% " 30
xmit "/usa/manion/mark\r"
recv "%" 10
xmit "logout\r"
log "done"
end
cv "COM" 30
xmit "\r\x\r\x\r\x\r\x\r"
recv "*" 30
{
alternate
	xmit "C 1234567890\r"
	recv "com" 30
	xmit "\r"
	recv "ogin:" 4
alternate
	xmit "\020"
	recv "*" 30
	mmdf/samples/vu44/table/uucpdom   444      0     12         262  3620510512  11657 laser		laser.UUCP
tjalk		tjalk.UUCP
vu44		vu44.UUCP
botter		botter.UUCP
ark		ark.uUCP
klipper		klipper.UUCP
vu45		vu45.UUCP
vu60		vu60.UUCP
logger		logger.UUCP
mcvax		mcvax.UUCP
uucp		tjalk!ark!%s
klipper.uucp		tjalk!klipper!%s
mcvax.uucp		tjalk!vu44!mcvax!%s
CP
r"
recv "% " 30
xmit "/usa/manion/mark\r"
recv "%" 10
xmit "logout\r"
log "done"
end
cv "COM" 30
xmit "\r\x\r\x\r\x\r\x\r"
recv "*" 30
{
alternate
	xmit "C 1234567890\r"
	recv "com" 30
	xmit "\r"
	recv "ogin:" 4
alternate
	xmit "\020"
	recv "*" 30
	mmdf/samples/vu44/mmdftailor   444      0     12        1670  3623755302  11311 MLNAME	vu44
MLDOMAIN UUCP
MLOCMACHINE laser
MLCKDIR	"/usr/mmdf/tmp"

; Table entries
MTBL	alias,		file="aliases",		show="Local Name Aliases"
MTBL	local,		file="local",		show="Local Host Aliases"
MTBL	uucpchn,	file="uucpchn",		show="UUCP Routes"
MTBL	uucpdom,	file="uucpdom",		show="UUCP Domains"

ALIAS	table=alias, trusted, nobypass

; Channel entries
MCHN	local, show="Local Delivery", que=local, tbl=local,
	pgm=local, mod=imm
MCHN	uucp, show="UUCP", que=uucp, tbl=uucpchn,
	pgm=uucp, mod=reg, ap=822
MCHN	list, show="List Processing", que=list, tbl=local,
	pgm=list, mod=reg
MCHN	badhosts, show="Bad Host Forwarding", que=badhosts, tbl=uucpchn
	pgm=uucp, mod=reg, ap=822, host="mcvax.uucp"
MCHN	badusers, show="Bad User Forwarding", que=badusers, tbl=uucpchn
	pgm=uucp, mod=reg, ap=822, host="vu44.uucp"

; Domain tables
MDMN	"UUCP", show="UUCP Domain", table=uucpdom

; Logging levels
MMSGLOG     level=FST,size=20
MCHANLOG    level=FST,size=20


/usr/local/lib/maillist/4344
4344-request:		ddf,vxt
# 4424 list updated mmdf/samples/bbn/   755      0     12           0  3657737574   7122 mmdf/samples/bbn/mmdftailor   444      0     12        5763  3657737573  11300 MLDOMAIN "CS.NET"
MLNAME SH
MLOCMACHINE ""

ALIAS table=aliases, trusted

; ARPA
MTBL    "ARPA", file="ARPA", show="ARPA Domain (NS)", flags=ns, flags=domain, 
	flags=partial
MDMN	"ARPA", show="ARPA Domain", table="ARPA"

; EDU
MTBL    "EDU", file="EDU", show="EDU Domain (NS)", flags=ns, flags=domain
MDMN	"EDU", show="EDU Domain", table="EDU"

; COM
MTBL    "COM", file="COM", show="COM Domain (NS)", flags=ns, flags=domain
MDMN	"COM", show="COM Domain", table="COM"

; ORG
MTBL    "ORG", file="ORG", show="ORG Domain (NS)", flags=ns, flags=domain
MDMN	"ORG", show="ORG Domain", table="ORG"

; GOV
MTBL    "GOV", file="GOV", show="GOV Domain (NS)", flags=ns, flags=domain
MDMN	"GOV", show="GOV Domain", table="GOV"

; MIL
MTBL    "MIL", file="MIL", show="MIL Domain (NS)", flags=ns, flags=domain
MDMN	"MIL", show="MIL Domain", table="MIL"

; UK
MTBL    "UK", file="UK", show="UK Domain (NS)", flags=ns, flags=domain
MDMN	"UK", show="UK Domain", table="UK"

; IL
MTBL    "IL", fILe="IL", show="IL Domain (NS)", flags=ns, flags=domain
MDMN	"IL", show="IL Domain", table="IL"

; NET
MTBL    "NET", file="NET", show="NET Domain (NS)", flags=ns, flags=domain
MDMN	"NET", show="NET Domain", table="NET"

; US
MTBL    "US", file="US", show="US Domain (NS)", flags=ns, flags=domain
MDMN	"US", show="US Domain", table="US"

; AU
MTBL    "AU", file="AU", show="AU Domain (NS)", flags=ns, flags=domain
MDMN	"AU", show="AU Domain", table="AU"

; CS.NET
MTBL	"CS.NET", file="CS.NET", show="CS.NET Domain", flags=ns, flags=domain,
	flags=partial
MDMN	"CS.NET", show="CS.NET Domain", table="CS.NET"

; Top-Level
MTBL    "Top-Level", file="top.base", show="Top Level Domain"
MDMN    "Top-Level", show="Top Level Domain", table="Top-Level", 
                     name="", dmn=""
;BBOARDS
MTBL    "BBOARDS", file="bboards", show="BBoard Psuedo-hosts"
MDMN    "BBOARDS", show="BBoards Psuedo-domain", table="BBOARDS"

; LIST
MTBL    "LIST", file="list", show="LIST Psuedo-hosts"
MDMN    "LIST", show="LIST Psuedo-domain", table="LIST"

; CSNET
MTBL    "CSNET", file="csnet", show="CSNET Domain"
MDMN    "CSNET", show="CSNET Domain", table="CSNET"

; Channel entries
MCHN    local, show="Local Delivery", que=local, tbl=local,
        pgm=local, ap=822, mod=send, mod=imm

; SMTP 
MTBL    "SMTP", file="smtp", show="SMTP Hosts", flags=ns, flags=channel
MTBL    "SMTPKNOWN", file="smtpknown", show="SMTP Hosts", flags=ns, flags=root
MTBL	"NS", file="ns", show="Entire NS database", flags=ns, flags=channel
MCHN	smtp, que=smtp, tbl=smtp, pgm=smtp, mod=reg, ap=822,
	known=smtpknown, mod=send

MCHN    list, show="List Channel", que=list, tbl=list,
	pgm=list, mod=reg, mod=send

MCHN    bboards, show="BBoards Delivery", que=bboards, tbl=bboards,
	pgm=bboards, mod=reg, ap=822

; DELAY (table is irrelevant but required)
MCHN	delay, show="Delayed Delivery", que=delay, tbl=smtp,
	mod=reg, ap=822

;logging levels
MMSGLOG		level=FST
MCHANLOG	level=FST
PHLOG		level=FST

; hours until warning/failure messages
MWARNTIME       95
MFAILTIME       143

MSUPPORT "Postmaster@SH.CS.NET"
 flags=ns, flmmdf/samples/bbn/table/   755      0     12           0  3657737605  10204 mmdf/samples/bbn/table/bboards   444      0     12          20  3657737575  11564 bboards:bboards
CSNET Domain"
MDMN    "CSNET", show="CSNET Domain", table="CSNET"

; Channel entries
MCHN    local, show="Local Delivery", que=local, tbl=local,
        pgm=local, ap=822, mod=send, mod=imm

; SMTP 
MTBL    "SMTP", file="smtp", show="SMTP Hosts", flags=ns, flags=channel
MTBL    "SMTPKNOWN", file="smtpknown", show="SMTP Hosts", flags=ns, flags=root
MTBL	"NS", file="ns", show="Entire NS database", flags=ns, flags=channel
MCHN	smtp, que=smtp, tbl=smtp, pgm=smtp, mod=reg, ap=822,
	known=smtpknowmmdf/samples/bbn/table/csnet   444      0     12       40334  3657737601  11346 USF:USF.CSNET,CSNET-RELAY.ARPA
BSU:BSU.CSNET,CSNET-RELAY.ARPA
BALL-STATE:BSU.CSNET,CSNET-RELAY.ARPA
UMN-DULUTH:UMN-DULUTH.CSNET,CSNET-RELAY.ARPA
UMD-UA:UMD-UA.CSNET,CSNET-RELAY.ARPA
NCR:NCR.CSNET,CSNET-RELAY.ARPA
UTOKYO-RELAY:UTOKYO-RELAY.CSNET,CSNET-RELAY.ARPA
BBNV1:BBNV1.CSNET,CSNET-RELAY.ARPA
FRESNO-STATE:FRESNO-STATE.CSNET,CSNET-RELAY.ARPA
CSUFRES:FRESNO-STATE.CSNET,CSNET-RELAY.ARPA
SPERRY-CSD:SPERRY-CSD.CSNET,CSNET-RELAY.ARPA
SPERRY-EAGAN:SPERRY-CSD.CSNET,CSNET-RELAY.ARPA
SPERRY-CSD-EAGAN:SPERRY-CSD.CSNET,CSNET-RELAY.ARPA
CGI:CGI.CSNET,CSNET-RELAY.ARPA
CARNEGIE-GROUP:CGI.CSNET,CSNET-RELAY.ARPA
BUTEST:BUTEST.CSNET,CSNET-RELAY.ARPA
UKY:UKY.CSNET,CSNET-RELAY.ARPA
UKMA:UKY.CSNET,CSNET-RELAY.ARPA
ODU:ODU.CSNET,CSNET-RELAY.ARPA
XANTH:ODU.CSNET,CSNET-RELAY.ARPA
TEMPLE:TEMPLE.CSNET,CSNET-RELAY.ARPA
SLB-TEST:SLB-TEST.CSNET,CSNET-RELAY.ARPA
NSF:NSF.CSNET,CSNET-RELAY.ARPA
TITEST:TITEST.CSNET,CSNET-RELAY.ARPA
SLB-DOLL:SLB-DOLL.CSNET,CSNET-RELAY.ARPA
SCHLUMBERGER-DOLL:SLB-DOLL.CSNET,CSNET-RELAY.ARPA
SCHLUM-DOLL:SLB-DOLL.CSNET,CSNET-RELAY.ARPA
UCI-20A:UCI-20A.CSNET,UCI.EDU,CSNET-RELAY.ARPA
UCI-20B:UCI-20B.CSNET,UCI.EDU,CSNET-RELAY.ARPA
CANISIUS:CANISIUS.CSNET,CSNET-RELAY.ARPA
KLAATU:CANISIUS.CSNET,CSNET-RELAY.ARPA
WRIGHT:WRIGHT.CSNET,CSNET-RELAY.ARPA
SWRI:SWRI.CSNET,CSNET-RELAY.ARPA
NASA-JSC:NASA-JSC.CSNET,CSNET-RELAY.ARPA
JSC:NASA-JSC.CSNET,CSNET-RELAY.ARPA
TULANE:TULANE.CSNET,CSNET-RELAY.ARPA
WWU:WWU.CSNET,CSNET-RELAY.ARPA
SPERRY-CTC:SPERRY-CTC.CSNET,CSNET-RELAY.ARPA
SPERRY-RESTON:SPERRY-CTC.CSNET,CSNET-RELAY.ARPA
SPERRY-AI-CTC:SPERRY-CTC.CSNET,CSNET-RELAY.ARPA
ALCOA-ATC:ALCOA-ATC.CSNET,CSNET-RELAY.ARPA
MICH-STATE:MICH-STATE.CSNET,CSNET-RELAY.ARPA
MSU:MICH-STATE.CSNET,CSNET-RELAY.ARPA
AUSTRALIA:AUSTRALIA.CSNET,CSNET-RELAY.ARPA
UTA:UTA.CSNET,CSNET-RELAY.ARPA
UTEXARL:UTA.CSNET,CSNET-RELAY.ARPA
CARLETON:CARLETON.CSNET,CSNET-RELAY.ARPA
BGSU:BGSU.CSNET,CSNET-RELAY.ARPA
BOWLING-GREEN:BGSU.CSNET,CSNET-RELAY.ARPA
UDENVER:UDENVER.CSNET,CSNET-RELAY.ARPA
UDENV:UDENVER.CSNET,CSNET-RELAY.ARPA
ISIS:UDENVER.CSNET,CSNET-RELAY.ARPA
NCAR:NCAR.CSNET,CSNET-RELAY.ARPA
ALBANY:ALBANY.CSNET,CSNET-RELAY.ARPA
SUNYA:ALBANY.CSNET,CSNET-RELAY.ARPA
SUNY-A:ALBANY.CSNET,CSNET-RELAY.ARPA
WILLIAMS:WILLIAMS.CSNET,CSNET-RELAY.ARPA
CHALMERS:CHALMERS.CSNET,CSNET-RELAY.ARPA
UCD:UCD.CSNET,CSNET-RELAY.ARPA
HUJI:HUJI.CSNET,CSNET-RELAY.ARPA
WFU:WFU.CSNET,CSNET-RELAY.ARPA
WAKE-FOREST:WFU.CSNET,CSNET-RELAY.ARPA
COLGATE:COLGATE.CSNET,CSNET-RELAY.ARPA
KAIST:KAIST.CSNET,CSNET-RELAY.ARPA
PAGODA:KAIST.CSNET,CSNET-RELAY.ARPA
SDN:KAIST.CSNET,CSNET-RELAY.ARPA
BONGWHA:KAIST.CSNET,CSNET-RELAY.ARPA
ALCOA-SEI:ALCOA-SEI.CSNET,CSNET-RELAY.ARPA
ALCOA:ALCOA-SEI.CSNET,CSNET-RELAY.ARPA
SEI-ALCOA:ALCOA-SEI.CSNET,CSNET-RELAY.ARPA
RICE:RICE.CSNET,CSNET-RELAY.ARPA
MTU:MTU.CSNET,CSNET-RELAY.ARPA
TUFTS:TUFTS.CSNET,CSNET-RELAY.ARPA
NMSU:NMSU.CSNET,CSNET-RELAY.ARPA
NMSU-CS:NMSU.CSNET,CSNET-RELAY.ARPA
UVM:UVM.CSNET,CSNET-RELAY.ARPA
UVM-CS:UVM.CSNET,CSNET-RELAY.ARPA
VERMONT:UVM.CSNET,CSNET-RELAY.ARPA
UVMCS:UVM.CSNET,CSNET-RELAY.ARPA
DARTMOUTH:DARTMOUTH.CSNET,CSNET-RELAY.ARPA
DARTVAX:DARTMOUTH.CSNET,CSNET-RELAY.ARPA
CSNET-SH:CSNET-SH.CSNET
CSNET-CIC:CSNET-SH.CSNET
CSNET-RELAY:CSNET-RELAY.CSNET,CSNET-RELAY.ARPA
CSNET-DEV:CSNET-DEV.CSNET,CSNET-RELAY.ARPA
UMICH:UMICH.CSNET,CSNET-RELAY.ARPA
UMICH-CIPRNET:UMICH.CSNET,CSNET-RELAY.ARPA
UMICH-CV:UMICH.CSNET,CSNET-RELAY.ARPA
UMICH-CIPR:UMICH.CSNET,CSNET-RELAY.ARPA
CIPR:UMICH.CSNET,CSNET-RELAY.ARPA
NSF-CS:NSF-CS.CSNET,CSNET-RELAY.ARPA
ONYX:NSF-CS.CSNET,CSNET-RELAY.ARPA
NSFCS:NSF-CS.CSNET,CSNET-RELAY.ARPA
UNC:UNC.CSNET,CSNET-RELAY.ARPA
DOPEY:UNC.CSNET,CSNET-RELAY.ARPA
UNC-DOPEY:UNC.CSNET,CSNET-RELAY.ARPA
UNC-CH:UNC.CSNET,CSNET-RELAY.ARPA
UNCCH:UNC.CSNET,CSNET-RELAY.ARPA
CHAPEL-HILL:UNC.CSNET,CSNET-RELAY.ARPA
UNC-GRUMPY:UNC-GRUMPY.CSNET,CSNET-RELAY.ARPA
UNC-GRUMPEY:UNC-GRUMPY.CSNET,CSNET-RELAY.ARPA
GRUMPY:UNC-GRUMPY.CSNET,CSNET-RELAY.ARPA
GRUMPEY:UNC-GRUMPY.CSNET,CSNET-RELAY.ARPA
UNC-SLEEPY:UNC-SLEEPY.CSNET,CSNET-RELAY.ARPA
SLEEPY:UNC-SLEEPY.CSNET,CSNET-RELAY.ARPA
UNC-BASHFUL:UNC-BASHFUL.CSNET,CSNET-RELAY.ARPA
BASHFUL:UNC-BASHFUL.CSNET,CSNET-RELAY.ARPA
UNC-DOC:UNC-DOC.CSNET,CSNET-RELAY.ARPA
DOC:UNC-DOC.CSNET,CSNET-RELAY.ARPA
UNC-HAPPY:UNC-HAPPY.CSNET,CSNET-RELAY.ARPA
HAPPY:UNC-HAPPY.CSNET,CSNET-RELAY.ARPA
UNC-JOHN:UNC-JOHN.CSNET,CSNET-RELAY.ARPA
JOHN:UNC-JOHN.CSNET,CSNET-RELAY.ARPA
UNC-THORIN:UNC-THORIN.CSNET,CSNET-RELAY.ARPA
THORIN:UNC-THORIN.CSNET,CSNET-RELAY.ARPA
UNC-SUNNY0:UNC-SUNNY0.CSNET,CSNET-RELAY.ARPA
SUNNY0:UNC-SUNNY0.CSNET,CSNET-RELAY.ARPA
UNC-SUNNY1:UNC-SUNNY1.CSNET,CSNET-RELAY.ARPA
SUNNY1:UNC-SUNNY1.CSNET,CSNET-RELAY.ARPA
UWMEECS:UWMEECS.CSNET,CSNET-RELAY.ARPA
UWM-EECS:UWMEECS.CSNET,CSNET-RELAY.ARPA
UWM:UWMEECS.CSNET,CSNET-RELAY.ARPA
PITT:PITT.CSNET,CSNET-RELAY.ARPA
UPITT:PITT.CSNET,CSNET-RELAY.ARPA
PITTSBURGH:PITT.CSNET,CSNET-RELAY.ARPA
U-PITT:PITT.CSNET,CSNET-RELAY.ARPA
RPICS:RPICS.CSNET,CSNET-RELAY.ARPA
RENSSELAER:RPICS.CSNET,CSNET-RELAY.ARPA
RPI:RPICS.CSNET,CSNET-RELAY.ARPA
RIP:RPICS.CSNET,CSNET-RELAY.ARPA
TUTE:RPICS.CSNET,CSNET-RELAY.ARPA
RPI-CS:RPICS.CSNET,CSNET-RELAY.ARPA
RPICMP:RPICMP.CSNET,CSNET-RELAY.ARPA
RPICIE:RPICIE.CSNET,CSNET-RELAY.ARPA
SUNY-BING:SUNY-BING.CSNET,CSNET-RELAY.ARPA
SUNYBING:SUNY-BING.CSNET,CSNET-RELAY.ARPA
SUNY-SB:SUNY-SB.CSNET,CSNET-RELAY.ARPA
SUNY-SBCS:SUNY-SB.CSNET,CSNET-RELAY.ARPA
SUNY-STONY:SUNY-SB.CSNET,CSNET-RELAY.ARPA
SUNY:SUNY-SB.CSNET,CSNET-RELAY.ARPA
SB-CS:SUNY-SB.CSNET,CSNET-RELAY.ARPA
SBCS:SUNY-SB.CSNET,CSNET-RELAY.ARPA
STONY-BROOK:SUNY-SB.CSNET,CSNET-RELAY.ARPA
VIRGINIA:VIRGINIA.CSNET,CSNET-RELAY.ARPA
UVIRGIN:VIRGINIA.CSNET,CSNET-RELAY.ARPA
UVA:VIRGINIA.CSNET,CSNET-RELAY.ARPA
UVA-CS:VIRGINIA.CSNET,CSNET-RELAY.ARPA
UVACS:VIRGINIA.CSNET,CSNET-RELAY.ARPA
UVIRGINIA:VIRGINIA.CSNET,CSNET-RELAY.ARPA
SYR:SYR.CSNET,CSNET-RELAY.ARPA
SYR-CIS-AOS:SYR-CIS-AOS.CSNET,CSNET-RELAY.ARPA
SYR-NMR-AOS1:SYR-NMR-AOS1.CSNET,CSNET-RELAY.ARPA
SYR-ECE-UNIX:SYR-ECE-UNIX.CSNET,CSNET-RELAY.ARPA
SYR-SUTCASE:SYR-SUTCASE.CSNET,CSNET-RELAY.ARPA
UNH:UNH.CSNET,CSNET-RELAY.ARPA
UNH-CSVAX:UNH.CSNET,CSNET-RELAY.ARPA
UNH-CS:UNH.CSNET,CSNET-RELAY.ARPA
UNHCS:UNH.CSNET,CSNET-RELAY.ARPA
BROWN:BROWN.CSNET,CSNET-RELAY.ARPA
TORONTO:TORONTO.CSNET,CSNET-RELAY.ARPA
UTCSRI:TORONTO.CSNET,CSNET-RELAY.ARPA
UMCP-CS:UMCP-CS.CSNET,CSNET-RELAY.ARPA
UMCP:UMCP-CS.CSNET,CSNET-RELAY.ARPA
UMARYLAND-COLLEGE-PARK:UMCP-CS.CSNET,CSNET-RELAY.ARPA
UMARYLAND:UMCP-CS.CSNET,CSNET-RELAY.ARPA
UMARYLAND-CP:UMCP-CS.CSNET,CSNET-RELAY.ARPA
MARYLAND-CP:UMCP-CS.CSNET,CSNET-RELAY.ARPA
MARYLAND:UMCP-CS.CSNET,CSNET-RELAY.ARPA
UMD-CSD:UMD-CSD.CSNET,CSNET-RELAY.ARPA
UMD8:UMD-CSD.CSNET,CSNET-RELAY.ARPA
BTL:BTL.CSNET,CSNET-RELAY.ARPA
BTL-MH:BTL.CSNET,CSNET-RELAY.ARPA
RESEARCH:BTL.CSNET,CSNET-RELAY.ARPA
DIGITAL:DIGITAL.CSNET,CSNET-RELAY.ARPA
DEC-CSNET:DIGITAL.CSNET,CSNET-RELAY.ARPA
DEC-CRG:DIGITAL.CSNET,CSNET-RELAY.ARPA
UMASS-ECE:UMASS-ECE.CSNET,UMASS-CS.CSNET,CSNET-RELAY.ARPA
UMASS-ECS:UMASS-ECE.CSNET,UMASS-CS.CSNET,CSNET-RELAY.ARPA
UMASS-CS:UMASS-CS.CSNET,CSNET-RELAY.ARPA
UMASS:UMASS-CS.CSNET,CSNET-RELAY.ARPA
UMASS-COINS:UMASS-CS.CSNET,CSNET-RELAY.ARPA
UMASS-BOSTON:UMASS-BOSTON.CSNET,CSNET-RELAY.ARPA
UMB:UMASS-BOSTON.CSNET,CSNET-RELAY.ARPA
UMASS-BOSS:UMASS-BOSTON.CSNET,CSNET-RELAY.ARPA
UM-BOSTON:UMASS-BOSTON.CSNET,CSNET-RELAY.ARPA
UMBOSTON:UMASS-BOSTON.CSNET,CSNET-RELAY.ARPA
WANG-INST:WANG-INST.CSNET,CSNET-RELAY.ARPA
WANGINST:WANG-INST.CSNET,CSNET-RELAY.ARPA
WANG-IGS:WANG-INST.CSNET,CSNET-RELAY.ARPA
WANG-INSTITUTE:WANG-INST.CSNET,CSNET-RELAY.ARPA
BRANDEIS:BRANDEIS.CSNET,CSNET-RELAY.ARPA
TI-CSL:TI-CSL.CSNET,CSNET-RELAY.ARPA
TI:TI-CSL.CSNET,CSNET-RELAY.ARPA
ISRAEL:ISRAEL.CSNET,CSNET-RELAY.ARPA
HUMUS:HUMUS.CSNET,CSNET-RELAY.ARPA
WISDOM:WISDOM.CSNET,CSNET-RELAY.ARPA
TECH-SEL:TECH-SEL.CSNET,CSNET-RELAY.ARPA
TAURUS:TAURUS.CSNET,CSNET-RELAY.ARPA
BENGUS:BENGUS.CSNET,CSNET-RELAY.ARPA
WSU:WSU.CSNET,CSNET-RELAY.ARPA
WASH-STATE:WSU.CSNET,CSNET-RELAY.ARPA
UOREGON:UOREGON.CSNET,CSNET-RELAY.ARPA
UO:UOREGON.CSNET,CSNET-RELAY.ARPA
UO-VAX3:UOREGON.CSNET,CSNET-RELAY.ARPA
UNLV:UNLV.CSNET,CSNET-RELAY.ARPA
UNLV44:UNLV.CSNET,CSNET-RELAY.ARPA
UNLV34:UNLV.CSNET,CSNET-RELAY.ARPA
UNLV23:UNLV.CSNET,CSNET-RELAY.ARPA
ARIZONA:ARIZONA.CSNET,CSNET-RELAY.ARPA
AZ:ARIZONA.CSNET,CSNET-RELAY.ARPA
HPLABS:HPLABS.CSNET,CSNET-RELAY.ARPA
HP:HPLABS.CSNET,CSNET-RELAY.ARPA
HEWLETT-PACKARD:HPLABS.CSNET,CSNET-RELAY.ARPA
HP-LABS:HPLABS.CSNET,CSNET-RELAY.ARPA
HPVAX:HPLABS.CSNET,CSNET-RELAY.ARPA
HP-VAX:HPLABS.CSNET,CSNET-RELAY.ARPA
HPLABSD:HPLABS.CSNET,CSNET-RELAY.ARPA
HP-SATURN:HPLABS.CSNET,CSNET-RELAY.ARPA
HP-HULK:HP-HULK.CSNET,CSNET-RELAY.ARPA
HULK:HP-HULK.CSNET,CSNET-RELAY.ARPA
HP-THOR:HP-THOR.CSNET,CSNET-RELAY.ARPA
THOR:HP-THOR.CSNET,CSNET-RELAY.ARPA
HP-HEWEY:HP-HEWEY.CSNET,CSNET-RELAY.ARPA
HEWEY:HP-HEWEY.CSNET,CSNET-RELAY.ARPA
HP-VENUS:HP-HEWEY.CSNET,CSNET-RELAY.ARPA
HP-MERCURY:HP-MERCURY.CSNET,CSNET-RELAY.ARPA
HP-MARS:HP-MARS.CSNET,CSNET-RELAY.ARPA
HP-BRONZE:HP-BRONZE.CSNET,CSNET-RELAY.ARPA
HP-STEEL:HP-STEEL.CSNET,CSNET-RELAY.ARPA
TEKTRONIX:TEKTRONIX.CSNET,CSNET-RELAY.ARPA
TEK:TEKTRONIX.CSNET,CSNET-RELAY.ARPA
TEKCRD:TEKTRONIX.CSNET,CSNET-RELAY.ARPA
UCSB:UCSB.CSNET,CSNET-RELAY.ARPA
UCSBCSL:UCSB.CSNET,CSNET-RELAY.ARPA
UCSBCSIL:UCSB.CSNET,CSNET-RELAY.ARPA
UCSC:UCSC.CSNET,CSNET-RELAY.ARPA
SANTACRUZ:UCSC.CSNET,CSNET-RELAY.ARPA
UCSCCIS:UCSC.CSNET,CSNET-RELAY.ARPA
UTD-CS:UTD-CS.CSNET,CSNET-RELAY.ARPA
UTD:UTD-CS.CSNET,CSNET-RELAY.ARPA
DALLAS:UTD-CS.CSNET,CSNET-RELAY.ARPA
UT-DALLAS:UTD-CS.CSNET,CSNET-RELAY.ARPA
UTEXAS-DALLAS:UTD-CS.CSNET,CSNET-RELAY.ARPA
BOULDER:BOULDER.CSNET,CSNET-RELAY.ARPA
COLORADO:BOULDER.CSNET,CSNET-RELAY.ARPA
UCOLORADO:BOULDER.CSNET,CSNET-RELAY.ARPA
NMT:NMT.CSNET,CSNET-RELAY.ARPA
NMTVAX:NMT.CSNET,CSNET-RELAY.ARPA
CEREBUS:NMT.CSNET,CSNET-RELAY.ARPA
NEWMEXICOTECH:NMT.CSNET,CSNET-RELAY.ARPA
NEW-MEXICO-TECH:NMT.CSNET,CSNET-RELAY.ARPA
UKANS:UKANS.CSNET,CSNET-RELAY.ARPA
KU:UKANS.CSNET,CSNET-RELAY.ARPA
UKAN:UKANS.CSNET,CSNET-RELAY.ARPA
UKANSAS:UKANS.CSNET,CSNET-RELAY.ARPA
OREGON-GRAD:OREGON-GRAD.CSNET,CSNET-RELAY.ARPA
OGCVAX:OREGON-GRAD.CSNET,CSNET-RELAY.ARPA
OGC:OREGON-GRAD.CSNET,CSNET-RELAY.ARPA
IBM-TEST:IBM-TEST.CSNET,CSNET-RELAY.ARPA
UIUC:UIUC.CSNET,CSNET-RELAY.ARPA
UIUCDCS:UIUC.CSNET,CSNET-RELAY.ARPA
INDIANA:INDIANA.CSNET,CSNET-RELAY.ARPA
IUVAX:INDIANA.CSNET,CSNET-RELAY.ARPA
IUCS:INDIANA.CSNET,CSNET-RELAY.ARPA
USC-CSE:USC-CSE.CSNET,CSNET-RELAY.ARPA
USCVAX:USC-CSE.CSNET,CSNET-RELAY.ARPA
VANDERBILT:VANDERBILT.CSNET,CSNET-RELAY.ARPA
VAND:VANDERBILT.CSNET,CSNET-RELAY.ARPA
VANDY:VANDERBILT.CSNET,CSNET-RELAY.ARPA
VANDERBUILT:VANDERBILT.CSNET,CSNET-RELAY.ARPA
VANDERBIULT:VANDERBILT.CSNET,CSNET-RELAY.ARPA
VU:VANDERBILT.CSNET,CSNET-RELAY.ARPA
UAB:UAB.CSNET,CSNET-RELAY.ARPA
ALABAMA:UAB.CSNET,CSNET-RELAY.ARPA
ALABAMA-CS:UAB.CSNET,CSNET-RELAY.ARPA
UABCS:UAB.CSNET,CSNET-RELAY.ARPA
UMN:UMN.CSNET,CSNET-RELAY.ARPA
MN-CS:UMN.CSNET,CSNET-RELAY.ARPA
MINN-CS:UMN.CSNET,CSNET-RELAY.ARPA
MIN:UMN.CSNET,CSNET-RELAY.ARPA
MINN:UMN.CSNET,CSNET-RELAY.ARPA
UMNCSCI:UMN.CSNET,CSNET-RELAY.ARPA
UMN-CSCI:UMN.CSNET,CSNET-RELAY.ARPA
UMINN:UMN.CSNET,CSNET-RELAY.ARPA
UMIN:UMN.CSNET,CSNET-RELAY.ARPA
UMINN-CS:UMN.CSNET,CSNET-RELAY.ARPA
MINNCS:UMN.CSNET,CSNET-RELAY.ARPA
MNCS:UMN.CSNET,CSNET-RELAY.ARPA
UMN-CS:UMN.CSNET,CSNET-RELAY.ARPA
UCF:UCF.CSNET,CSNET-RELAY.ARPA
UCF-CS:UCF.CSNET,CSNET-RELAY.ARPA
UCFL-CS:UCF.CSNET,CSNET-RELAY.ARPA
UCFL:UCF.CSNET,CSNET-RELAY.ARPA
CENTRAL-FLORIDA:UCF.CSNET,CSNET-RELAY.ARPA
FLORIDA:UCF.CSNET,CSNET-RELAY.ARPA
EMORY:EMORY.CSNET,CSNET-RELAY.ARPA
OREGON-STATE:OREGON-STATE.CSNET,CSNET-RELAY.ARPA
ORSTCS:OREGON-STATE.CSNET,CSNET-RELAY.ARPA
UFL:UFL.CSNET,CSNET-RELAY.ARPA
UFLA:UFL.CSNET,CSNET-RELAY.ARPA
UF-CSV:UFL.CSNET,CSNET-RELAY.ARPA
UIOWA:UIOWA.CSNET,CSNET-RELAY.ARPA
IOWA:UIOWA.CSNET,CSNET-RELAY.ARPA
OHIO-STATE:OHIO-STATE.CSNET,CSNET-RELAY.ARPA
OSU-DBS:OHIO-STATE.CSNET,CSNET-RELAY.ARPA
OSU-CIS:OHIO-STATE.CSNET,CSNET-RELAY.ARPA
NWU:NWU.CSNET,CSNET-RELAY.ARPA
CT:CT.CSNET,CSNET-RELAY.ARPA
CTVAX:CT.CSNET,CSNET-RELAY.ARPA
TAMU:TAMU.CSNET,CSNET-RELAY.ARPA
TEXAM:TAMU.CSNET,CSNET-RELAY.ARPA
DEPAUL:DEPAUL.CSNET,CSNET-RELAY.ARPA
USL:USL.CSNET,CSNET-RELAY.ARPA
PORTLAND:PORTLAND.CSNET,CSNET-RELAY.ARPA
UBC:UBC.CSNET,CSNET-RELAY.ARPA
UBC-EAN:UBC.CSNET,CSNET-RELAY.ARPA
EAN:UBC.CSNET,CSNET-RELAY.ARPA
UMISS:UMISS.CSNET,CSNET-RELAY.ARPA
OLEMISS:UMISS.CSNET,CSNET-RELAY.ARPA
KANSAS-STATE:KANSAS-STATE.CSNET,CSNET-RELAY.ARPA
KANSTATE:KANSAS-STATE.CSNET,CSNET-RELAY.ARPA
DUKE:DUKE.CSNET,CSNET-RELAY.ARPA
DUKEVAX:DUKE.CSNET,CSNET-RELAY.ARPA
GATECH:GATECH.CSNET,CSNET-RELAY.ARPA
GA-TECH:GATECH.CSNET,CSNET-RELAY.ARPA
GEORGIA-TECH:GATECH.CSNET,CSNET-RELAY.ARPA
GIT:GATECH.CSNET,CSNET-RELAY.ARPA
GEORGIA:GATECH.CSNET,CSNET-RELAY.ARPA
GATCH:GATECH.CSNET,CSNET-RELAY.ARPA
PRINCETON:PRINCETON.CSNET,CSNET-RELAY.ARPA
PRINC:PRINCETON.CSNET,CSNET-RELAY.ARPA
PRIN:PRINCETON.CSNET,CSNET-RELAY.ARPA
PRINU:PRINCETON.CSNET,CSNET-RELAY.ARPA
VLSIVAX:VLSIVAX.CSNET,CSNET-RELAY.ARPA
PRINCETON-VLSI:VLSIVAX.CSNET,CSNET-RELAY.ARPA
PRIN-VLSI:VLSIVAX.CSNET,CSNET-RELAY.ARPA
UPENN:UPENN.CSNET,CSNET-RELAY.ARPA
PENN:UPENN.CSNET,CSNET-RELAY.ARPA
UPENN-CS:UPENN.CSNET,CSNET-RELAY.ARPA
UPENN-CIS:UPENN.CSNET,CSNET-RELAY.ARPA
PENN-CIS:UPENN.CSNET,CSNET-RELAY.ARPA
PENN-CS:UPENN.CSNET,CSNET-RELAY.ARPA
UPENN-750:UPENN-750.CSNET,CSNET-RELAY.ARPA
PENN-750:UPENN-750.CSNET,CSNET-RELAY.ARPA
UPENN750:UPENN-750.CSNET,CSNET-RELAY.ARPA
PENN750:UPENN-750.CSNET,CSNET-RELAY.ARPA
SCAROLINA:SCAROLINA.CSNET,CSNET-RELAY.ARPA
CAROLINA:SCAROLINA.CSNET,CSNET-RELAY.ARPA
SCAR:SCAROLINA.CSNET,CSNET-RELAY.ARPA
BUFFALO:BUFFALO.CSNET,CSNET-RELAY.ARPA
BUFFALO-CS:BUFFALO.CSNET,CSNET-RELAY.ARPA
BUFCS:BUFFALO.CSNET,CSNET-RELAY.ARPA
SUNY-BUF:BUFFALO.CSNET,CSNET-RELAY.ARPA
CASE:CASE.CSNET,CSNET-RELAY.ARPA
CASE-WESTERN:CASE.CSNET,CSNET-RELAY.ARPA
CWRU:CASE.CSNET,CSNET-RELAY.ARPA
PENN-STATE:PENN-STATE.CSNET,CSNET-RELAY.ARPA
PSUVAX1:PENN-STATE.CSNET,CSNET-RELAY.ARPA
CLEMSON:CLEMSON.CSNET,CSNET-RELAY.ARPA
VPI:VPI.CSNET,CSNET-RELAY.ARPA
VPISU:VPI.CSNET,CSNET-RELAY.ARPA
VATECH:VPI.CSNET,CSNET-RELAY.ARPA
VPIVAX1:VPI.CSNET,CSNET-RELAY.ARPA
VTCS:VPI.CSNET,CSNET-RELAY.ARPA
VT:VPI.CSNET,CSNET-RELAY.ARPA
KENTVAX:KENTVAX.CSNET,CSNET-RELAY.ARPA
KENT:KENTVAX.CSNET,CSNET-RELAY.ARPA
KSU:KENTVAX.CSNET,CSNET-RELAY.ARPA
HOUSTON:HOUSTON.CSNET,CSNET-RELAY.ARPA
UH:HOUSTON.CSNET,CSNET-RELAY.ARPA
ASU:ASU.CSNET,CSNET-RELAY.ARPA
ARIZONA-STATE:ASU.CSNET,CSNET-RELAY.ARPA
ASU-VAX:ASU.CSNET,CSNET-RELAY.ARPA
ASUVAX:ASU.CSNET,CSNET-RELAY.ARPA
GMR:GMR.CSNET,CSNET-RELAY.ARPA
GM-RESEARCH:GMR.CSNET,CSNET-RELAY.ARPA
GM-LABS:GMR.CSNET,CSNET-RELAY.ARPA
TENNESSEE:TENNESSEE.CSNET,CSNET-RELAY.ARPA
UTK:TENNESSEE.CSNET,CSNET-RELAY.ARPA
UTENN:TENNESSEE.CSNET,CSNET-RELAY.ARPA
TENNESSE:TENNESSEE.CSNET,CSNET-RELAY.ARPA
UHCL:UHCL.CSNET,CSNET-RELAY.ARPA
APOLLO:UHCL.CSNET,CSNET-RELAY.ARPA
UHCL-APOLLO:UHCL.CSNET,CSNET-RELAY.ARPA
BOSTONU:BOSTONU.CSNET,CSNET-RELAY.ARPA
BU:BOSTONU.CSNET,CSNET-RELAY.ARPA
BU-CS:BOSTONU.CSNET,CSNET-RELAY.ARPA
BOSTONU-CS:BOSTONU.CSNET,CSNET-RELAY.ARPA
MCNC:MCNC.CSNET,CSNET-RELAY.ARPA
LSU:LSU.CSNET,CSNET-RELAY.ARPA
LSUCSC:LSU.CSNET,CSNET-RELAY.ARPA
GTE-LABS:GTE-LABS.CSNET,CSNET-RELAY.ARPA
GTEL:GTE-LABS.CSNET,CSNET-RELAY.ARPA
COLOSTATE:COLOSTATE.CSNET,CSNET-RELAY.ARPA
CSU:COLOSTATE.CSNET,CSNET-RELAY.ARPA
OKSTATE:OKSTATE.CSNET,CSNET-RELAY.ARPA
OKLAHOMA-STATE:OKSTATE.CSNET,CSNET-RELAY.ARPA
OKSMA1:OKSMA1.CSNET,CSNET-RELAY.ARPA
TI-TEST:TI-TEST.CSNET,CSNET-RELAY.ARPA
SRC:SRC.CSNET,CSNET-RELAY.ARPA
UCONN:UCONN.CSNET,CSNET-RELAY.ARPA
CARCVAX:UCONN.CSNET,CSNET-RELAY.ARPA
WATERLOO:WATERLOO.CSNET,CSNET-RELAY.ARPA
WATMATH:WATERLOO.CSNET,CSNET-RELAY.ARPA
TEST-RELAY:TEST-RELAY.CSNET,CSNET-RELAY.ARPA
ICASE:ICASE.CSNET,CSNET-RELAY.ARPA
LANGLEY:ICASE.CSNET,CSNET-RELAY.ARPA
BUFFALO-TEST:BUFFALO-TEST.CSNET,CSNET-RELAY.ARPA
UCHICAGO:UCHICAGO.CSNET,CSNET-RELAY.ARPA
GARGOYLE:UCHICAGO.CSNET,CSNET-RELAY.ARPA
UNL:UNL.CSNET,CSNET-RELAY.ARPA
UNLCS:UNL.CSNET,CSNET-RELAY.ARPA
FERGVAX:UNL.CSNET,CSNET-RELAY.ARPA
GERMANY:GERMANY.CSNET,CSNET-RELAY.ARPA
KARLSRUHE:GERMANY.CSNET,CSNET-RELAY.ARPA
UKA:GERMANY.CSNET,CSNET-RELAY.ARPA
DFN:GERMANY.CSNET,CSNET-RELAY.ARPA
SMU:SMU.CSNET,CSNET-RELAY.ARPA
SMU-CSE:SMU.CSNET,CSNET-RELAY.ARPA
ULOWELL:ULOWELL.CSNET,CSNET-RELAY.ARPA
BROWN-COGSCI:BROWN-COGSCI.CSNET,CSNET-RELAY.ARPA
BRN-COG:BROWN-COGSCI.CSNET,CSNET-RELAY.ARPA
COGNET1:BROWN-COGSCI.CSNET,CSNET-RELAY.ARPA
BROWNCOG:BROWN-COGSCI.CSNET,CSNET-RELAY.ARPA
APPLE:APPLE.CSNET,CSNET-RELAY.ARPA
TI-EG:TI-EG.CSNET,CSNET-RELAY.ARPA
IOWA-STATE:IOWA-STATE.CSNET,CSNET-RELAY.ARPA
ISUCS1:IOWA-STATE.CSNET,CSNET-RELAY.ARPA
FRANCE:FRANCE.CSNET,CSNET-RELAY.ARPA
RIT:RIT.CSNET,CSNET-RELAY.ARPA
RITCV:RIT.CSNET,CSNET-RELAY.ARPA
TEST-IN:TEST-IN.CSNET,CSNET-RELAY.ARPA
TEST-OUT:TEST-OUT.CSNET,CSNET-RELAY.ARPA
HUGO-TEST:HUGO-TEST.CSNET,CSNET-RELAY.ARPA
RAND-TEST:RAND-TEST.CSNET,CSNET-RELAY.ARPA
T,CSNET-RELAY.ARPA
GATCH:GATECH.CSNET,CSNET-RELAY.ARPA
PRINCETON:PRINCETON.CSNET,CSNET-RELAY.ARPA
PRINC:PRINCETON.CSNET,CSNET-RELAY.ARPA
PRIN:PRINCETON.CSNET,CSNET-RELAY.ARPA
PRINU:PRINCETON.CSNET,CSNET-RELAY.ARPA
VLSIVAX:VLSIVAX.CSNET,CSNET-RELAY.ARPA
PRINCETON-VLSI:VLSIVAX.CSNET,CSNET-RELAmmdf/samples/bbn/table/list   444      0     12          67  3657737602  11125 list-processor:list-processor
list-proc:list-processor
:BUFFALO.CSNET,CSNET-RELAY.ARPA
BUFFALO-CS:BUFFALO.CSNET,CSNET-RELAY.ARPA
BUFCS:BUFFALO.CSNET,CSNET-RELAY.ARPA
SUNY-BUF:BUFFALO.CSNET,CSNET-RELAY.ARPA
CASE:CASE.CSNET,CSNET-RELAY.ARPA
CASE-WESTERN:CASE.CSNET,CSNET-RELAY.ARPA
CWRU:CASE.CSNET,CSNET-RELAY.ARPA
PENN-STATE:PENN-STATE.CSNET,CSNET-RELAY.ARPA
PSUVAX1:PENN-STATE.CSNET,CSNET-RELAY.ARPA
CLEMSON:CLEMSON.CSNET,CSNET-RELAY.ARPA
VPI:VPI.CSNET,CSNET-RELAY.ARPA
VPISU:VPI.CSNET,CSNET-RELAY.ARPA
VATECH:VPmmdf/samples/bbn/table/local   444      0     12         275  3657737603  11266 sh:sh
csnet-sh:sh
csnet-sh.arpa:sh
csnet-sh.csnet:sh
sh.cs.net:sh
csnet2:sh
csnet2.arpa:sh
csnet2.csnet:sh
csnet2.bbn.com:sh
csnet-cic:sh
csnet-cic.csnet:sh
csnet-cic.arpa:sh
cic.cs.net:sh
CSNET-RELAY.ARPA
CASE:CASE.CSNET,CSNET-RELAY.ARPA
CASE-WESTERN:CASE.CSNET,CSNET-RELAY.ARPA
CWRU:CASE.CSNET,CSNET-RELAY.ARPA
PENN-STATE:PENN-STATE.CSNET,CSNET-RELAY.ARPA
PSUVAX1:PENN-STATE.CSNET,CSNET-RELAY.ARPA
CLEMSON:CLEMSON.CSNET,CSNET-RELAY.ARPA
VPI:VPI.CSNET,CSNET-RELAY.ARPA
VPISU:VPI.CSNET,CSNET-RELAY.ARPA
VATECH:VPmmdf/samples/bbn/table/top.base   444      0     12         174  3657737604  11706 uucp:seismo.css.gov
bitnet:wiscvm.wisc.edu
mailnet:mit-multics.arpa
csnet:csnet-relay.arpa
sh:sh.cs.net
sh.cs.net:sh.cs.net

csnet-cic:sh
csnet-cic.csnet:sh
csnet-cic.arpa:sh
cic.cs.net:sh
CSNET-RELAY.ARPA
CASE:CASE.CSNET,CSNET-RELAY.ARPA
CASE-WESTERN:CASE.CSNET,CSNET-RELAY.ARPA
CWRU:CASE.CSNET,CSNET-RELAY.ARPA
PENN-STATE:PENN-STATE.CSNET,CSNET-RELAY.ARPA
PSUVAX1:PENN-STATE.CSNET,CSNET-RELAY.ARPA
CLEMSON:CLEMSON.CSNET,CSNET-RELAY.ARPA
VPI:VPI.CSNET,CSNET-RELAY.ARPA
VPISU:VPI.CSNET,CSNET-RELAY.ARPA
VATECH:VPmmdf/samples/bbn/table/aliases   444      0     12        7354  3657737605  11644 # AllST:    ALPHABETICAL ALIASES FOR LISTS

csnet-ec:	ec-full
csnet-forum-request:	cic
csnet-tc:	tc
forum:		csnet-forum
implementers:	implementors
mailer-daemon:	postmaster
root:		wizards
status:		status@csnet-relay.arpa
tc:		tc-outbound@list-processor
test-list:	test-list-outbound@list-processor

# BLISTS: BBOARDS AND LISTS TO BE MOVED TO THE BBN ALIAS DATABASE

arms-d-bboard:          arms-d@bboards
arpa-mhs-bboard:        arpa-mhs@bboards
bbn-db-sig-bboard:	bbn-db-sig@bboards
bboard-bboard:          bboard@bboards
bpa-bboard:             bpa@bboards
cic-announce-bboard:	cic-announce@bboards
cic-licenses:		cic-licenses-bboard
cic-licenses-bboard:    cic-licenses@bboards
cic-log-bboard:         cic-log@bboards
cic-reports:		cic-reports-bboard
cic-reports-bboard:	cic-reports@bboards
cic-staff-bboard:       cic-staff@bboards
cic-tapes:		cic-tapes-bboard
cic-tapes-bboard:       cic-tapes@bboards
csnet-forum-bboard:	csnet-forum@bboards
domain-people-bboard:	domain-people@bboards
english-bboard:		english@bboards
food-bboard:		food@bboards
header-people-bboard:   header-people@bboards
info-futures-bboard:    info-futures@bboards
info-ibmpc-bboard:      info-ibmpc@bboards
info-mac-bboard:        info-mac@bboards
info-receipts:          info-receipts@bboards
info-vax-bboard:        info-vax@bboards
laser-lovers-bboard:    laser-lovers@bboards
mhs_implementation-bboard:  mhs_implementation@bboards
mailgroup-bboard:       mailgroup@bboards
mmdf:			postmaster
mmdf2-bboard:           mmdf2@bboards
mmdf2-bugs-bboard:      mmdf2-bugs@bboards
namedroppers-bboard:    namedroppers@bboards
pmdf-protocol:		pmdf-protocol@csnet-relay.arpa
pmdf-protocol-bboard:	pmdf-protocol@bboards
rfc-bboard:             rfc@bboards
serial-ip-bboard:       serial-ip@bboards
soft-eng-bboard:	soft-eng@bboards
space-bboard:		space@bboards
techies-bboard:		techies@bboards
unix-pmdf-bboard:	unix-pmdf@bboards
unix-review-bboard:	unix-review@bboards
xni-list:		xni-list@csnet-relay.arpa
xni-list-bboard:	xni-list@bboards

# LISTS:    TOPOLOGICAL EXPANSION LISTS

implementors:	< /csnet/edmiston/mail-lists/implementors
registrar:      registrar|/usr/central/bin/mcatch
techies:	< /csnet/edmiston/mail-lists/techies
test-list-outbound:	< /csnet/edmiston/mail-lists/test-list
test-list-request: long
wizards:        < /csnet/edmiston/mail-lists/wizards

# ALINDIV:      ALPHA ALIASES FOR INDIVIDUALS

kay:		alan
kennedy:	ken
mcilroy:        doug
landweber:	lhl
lefaivre:	rickl
litzkow:	mike
rick:           adrion
ruttenberg: 	stan
selinger:	pat
vandam:		avd
vyssotsky:	clf

# RELINDIV:     ALPHA RELAY ADDRESS FOR INDIVIDUALS

adrion: 	adrion@nsf-cs,~adrion
alan:		alan@usc-isib.arpa
avd:		avd@brown
baskett:        baskett@decwrl.dec.com
birnbaum:	birnbaum@hplabs.arpa
browne:		browne@r20.utexas.edu
clf:		clf.panther@btl
comer:		dec@purdue.edu
denning:	denning@riacs.arpa
dodd:           dodd@gmr
donn:           donn@crys.wisc.edu
doug:           doug@btl
farber:		farber@huey.udel.edu
feldman:	feldman@rochester.arpa
galler:		galler@umich-mts.mailnet
gear:           gear@a.cs.uiuc.edu
harrison:	harrison@ucbvax.berkeley.edu
hearn:		hearn@rand-unix.arpa
ken:		ken@rice.edu
kirton:		decvax!mulga!trlsasb.oz!kirton@bbncca.arpa
leiner:		leiner@riacs.arpa
lhl:		lhl@crys.wisc.edu
mike: 		mike@crys.wisc.edu
miller:		miller@gatech
mr-protocol:    obrien@rand-unix.arpa
nicholson: 	nich@rsch.wisc.edu
obrien:         obrien@rand-unix.arpa
pat:		pat@ibm-sj.arpa
perry:          perry@ipto.arpa
rashid:         rashid@spice.cs.cmu.edu
riesenfeld:     riesenfeld@utah-20.arpa
rickl:		rickl@tektronix
saltzer:	saltzer@mit-multics.arpa
solomon:        solomon@crys.wisc.edu
weaver:         acw@virginia

# LLIST		ALPHA LOCAL MAILBOXES

csnet-forum:	~csnet-forum,cic
forwarder:      root|/usr/local/lib/ns/bin/mcatch
info-server:    info
GSCI.CSNET,CSNET-RELAY.ARPA
BROWNCOG:BROWN-COGSCI.CSNET,CSNET-RELAY.ARPA
APPLE:APPLE.CSNET,CSNET-RELAY.ARPA
TI-EG:TI-EG.CSNET,CSNET-RELAY.ARPA
IOWA-STATE:IOWA-STATE.CSNET,CSNET-RELAY.ARPA
ISUCS1:IOWA-STATE.CSNET,CSNET-RELAY.ARPA
FRANCE:FRANCE.CSNET,CSNET-RELAY.ARPA
RIT:RIT.CSmmdf/README   444      0     12        6251  3670403202   5631 
README -- MMDFII, Release B (beta), April 22nd, 1986

*****************************************************************************
* This is a beta release of MMDFIIb.  We believe it is quite stable or we   *
* would not have put it out with 4.3 but users should still be aware that   *
* it is beta-test.  The most critical error(s) are listed at the end of     *
* this file.                                                                *
*****************************************************************************

The first thing you should do is look at the administrators guide
in doc/administrators.  There is a makefile there to make an nroff
or troff version of "Installing and Operating MMDFII".  Read it.

For a current status report on MMDFII, including information on
how to get an up-to-date buglist and updates to the 4.3 distribution,
you should mail a request to the CSNET Info Server (info@sh.cs.net).
Simply cut out the following text and mail it to info@sh.cs.net for
the current status.

------cut-here------
request: mmdf
topic: status
request: end
-----cut-here-------

You should also join the MMDFII mailing list.  The list
is mmdf2@relay.cs.net.  Send requests to join/drop/etc to
mmdf2-request@relay.cs.net.  DO NOT CALL BERKELEY with MMDF2
questions.  Send bug reports to mmdf2-bugs@relay.cs.net.

Thanks and much credit is due to Doug Kingston, Phil Cockcroft,
Steve Kille, Howard Walter, and others who put in considerable time
and effort into this release.

Dan Long
Craig Partridge
CSNET Coordination and Information Center (BBN Laboratories)
cic@sh.cs.net

--------------------------

Comments and Bug list:

1.  The program channel is new and has not been widely used.  Expect
portability problems.

2.   4.2 and 4.3 sites should be aware that MMDFII is a complete mail
system, and as such, replaces sendmail et. al.  As a result you no
longer need the following files:

    /usr/lib/sendmail.*
    /usr/ucb/newaliases
    /etc/aliases
    /etc/delivermail
    /etc/comsat
    /usr/ucb/biff
    /usr/ucb/mailq

The following programs are replaced by MMDFII (you may want to save them)

    /bin/rmail
    /bin/mail
    /usr/lib/sendmail		MMDFII has a fake sendmail that fools
				most applications

    /usr/ucb/Mail		There are MMDFII versions that work
    /usr/ucb/mail		with MMDFII.   Most people seem to prefer
				to use MMDFII's own user interface (msg/send)
				or MH (6.4 or later).

3.  Internet sites which want to interact with nameservers and use MX
RR's should make sure that NAMESERVER is defined in Makefile.com.

4.  There are lot of bugs.  Many of them are so minor they have been
around since the early days of MMDFIIa and no one has bothered to fix them.
The most critical ones are:

    -  dotted quads aren't parsed properly.  As a result we
    don't put them into return addresses.  This occasionally
    means a user may get a message with a return address
    that he/she can't reply to.

    - the routines controlling timeouts may not be quite in
    sync with the BIND resolver routines distributed with 4.3.
    As a result, timeouts and delays may be slightly off.  This
    sometimes has unfortunate performance implications.  (A fix
    for this is expected shortly).

arder:      root|/usr/local/lib/ns/bin/mcatch
info-server:    info
GSCI.CSNET,CSNET-RELAY.ARPA
BROWNCOG:BROWN-COGSCI.CSNET,CSNET-RELAY.ARPA
APPLE:APPLE.CSNET,CSNET-RELAY.ARPA
TI-EG:TI-EG.CSNET,CSNET-RELAY.ARPA
IOWA-STATE:IOWA-STATE.CSNET,CSNET-RELAY.ARPA
ISUCS1:IOWA-STATE.CSNET,CSNET-RELAY.ARPA
FRANCE:FRANCE.CSNET,CSNET-RELAY.ARPA
RIT:RIT.CSmmdf/Makefile   444      0     12         763  3620510307   6373 #	MMDF Root Makefile
#
#
SUBDIR=	lib src uip

all:
	for dir in ${SUBDIR}; do \
		(cd $${dir}; make ${MFLAGS} -${MAKEFLAGS}); done

depend:
	for dir in ${SUBDIR}; do \
		(cd $${dir}; make ${MFLAGS} -${MAKEFLAGS} depend); done

lint:
	for dir in ${SUBDIR}; do \
		(cd $${dir}; make ${MFLAGS} -${MAKEFLAGS} lint); done

install:
	for dir in ${SUBDIR}; do \
		(cd $${dir}; make ${MFLAGS} -${MAKEFLAGS} install); done

clean:
	-rm -f make.out
	for dir in ${SUBDIR}; do \
		(cd $${dir}; make clean); done
s.

------cutmmdf/Makefile.com   444      0     12        7135  3642260666   7206 #
#	Common Sub-Makefile for the MMDF System
#
HOST		= 4.3vax
SYSTEM		= 4.2
MMPREF		=
LIBDIR		= /usr/mmdf
CHANDIR		= /usr/mmdf/chans
TBLDIR		= /usr/mmdf/table
BINDIR		= /usr/local
RCVDIR		= /usr/mmdf/rcvmail

#
#  Defines used during installation
#
CHOWN		= /etc/chown
MMDFLOGIN	= mmdf
ROOTLOGIN	= root
PGMPROT 	= 755

#
#  Configuration Defines
#
#  The paths below are relative from the directories below this one.
#  Define DOASSIGN to CONFIGDEFS if you want the d_assign code
#  Define SECURETTY if you run with terminals write protected (mode 711)
#  Define DEBUG to include extensive calls to the logging package
#  Define D_LOG if you want phone channel logging
#  Define D_DBGLOG if you want phone channel debugging output
#  Define RUNALONE for standalone testing (seldom used)
#  Define V4_2BSD if you are compiling for 4.2BSD
#  Define NODIAL if you do not want the dial package (no phone channel)
#  Define SYS5 if you have a SystemV Unix or something similar (Sys3)
#  Define ALTOS if you have an Altos, does some depraved things.
#  Define NODUP2 if you don't a dup2() syscall or subroutine.
#  Define NOFCNTL and NODUP2 if you don't have fcntl(x, F_DUPFD) or dup2()
#  Define -Drindex=strrchr -Dindex=strchr if you are on a SYS5 system
#  Define NAMESERVER if you want the nameserver lookup code
#  Define UUCPLOCK if you want UUCP/tip/cu-style locking for dial-out lines
#
CONFIGDEFS	= -DDEBUG=1 -DV4_2BSD
CFLAGS		= -O -I../../h $(CONFIGDEFS)
LDFLAGS 	=
MMDFLIBS 	= ../../lib/libmmdf.a
SYSLIBS		= -ldbm
LINT		= lint
LFLAGS		= -bxah -I../../h $(CONFIGDEFS)
LLIBS		= ../../lib/llib-lmmdf.ln
AR		= ar


#  Specify either ch_tbdbm (for DBM based tables) or ch_tbseq for
#  sequential IO based tables.
CH_TB	= ch_tbdbm

#  Specify one of the nameserver modules or the fake module
#  if you do not intend to support nameservers (4.2, fake)
TB_NS	= fake

#  Specify tai_???.o and lk_lock???.o
LOCALUTIL = tai_file.o lk_lock.4.2.o


default:	real-default

#
#  special case dependencies
#
../../h/mmdf.h:	../../h/conf.h
	-touch ../../h/mmdf.h
.PRECIOUS: ../../h/mmdf.h

#
#  #include dependencies
#
#  Two versions are supplied.  One for sites with cc -M (4.3BSD)
#  and one for those that don't have it.  Comment out the one
#  you do not want.

#  This one is for sites without cc -M
#depend:
#	cat </dev/null >x.c
#	for i in $(MODULES); do \
#		(echo $$i.o: $$i.c >>makedep; \
#		/bin/grep '^#[ 	]*include' x.c $$i.c | sed \
#			-e 's,c:[^"]*"\./\([^"]*\)".*,o: \1,' \
#			-e 's,c:[^"]*"/\([^"]*\)".*,o: /\1,' \
#			-e 's,c:[^"]*"\([^"]*\)".*,o: ../../h/\1,' \
#			-e 's,c:[^<]*<\(.*\)>.*,o: /usr/include/\1,' \
#			>>makedep); done
#	echo '/^# DO NOT DELETE THIS LINE/+2,$$d' >eddep
#	echo '$$r makedep' >>eddep
#	echo 'w' >>eddep
#	cp Makefile.real Makefile.bak
#	ed - Makefile.real < eddep
#	rm eddep makedep x.c
#	echo '# DEPENDENCIES MUST END AT END OF FILE' >> Makefile.real
#	echo '# IF YOU PUT STUFF HERE IT WILL GO AWAY' >> Makefile.real
#	echo '# see make depend above' >> Makefile.real

#  This one id for sites with cc -M
depend:
	( for i in ${MODULES} ; do \
		${CC} -M ${CFLAGS} $$i.c ; done ) | \
	awk ' { if ($$1 != prev) { print rec; rec = $$0; prev = $$1; } \
		else { if (length(rec $$2) > 78) { print rec; rec = $$0; } \
		       else rec = rec " " $$2 } } \
	      END { print rec } ' > makedep
	echo '/^# DO NOT DELETE THIS LINE/+2,$$d' >eddep
	echo '$$r makedep' >>eddep
	echo 'w' >>eddep
	cp Makefile.real Makefile.bak
	ed - Makefile.real < eddep
	rm eddep makedep
	echo '# DEPENDENCIES MUST END AT END OF FILE' >> Makefile.real
	echo '# IF YOU PUT STUFF HERE IT WILL GO AWAY' >> Makefile.real
	echo '# see make depend above' >> Makefile.real
= /usr/mmdf
CHANDIR		= /usr/mmdf/chans
TBLDIR		= /usr/mmdf/table
BINDIR		= /usr/local
RCVDIR		= /usr/mmdf/rcvmail

#
#  Defines used during installation
#
CHOWN		= /etc/chown
MMDFLOGIN	= mmdf
ROOTLOGIN	= root
PGMPROT 	= 755

#
#  Configuration Defines
#
#  The paths below are relative from the directories below this one.
#  Define DOASSIGN to CONFIGDEFS if you want the d_assign code
#  Define SECURETTY if you run wimmdf/VERSION   444      0     12          36  3671105114   5756 MMDF IIb: 4.3 Initial Release
Makefile.real
	echo '# see make depend above' >> Makefile.real
= /usr/mmdf
CHANDIR		= /usr/mmdf/chans
TBLDIR		= /usr/mmdf/table
BINDIR		= /usr/local
RCVDIR		= /usr/mmdf/rcvmail

#
#  Defines used during installation
#
CHOWN		= /etc/chown
MMDFLOGIN	= mmdf
ROOTLOGIN	= root
PGMPROT 	= 755

#
#  Configuration Defines
#
#  The paths below are relative from the directories below this one.
#  Define DOASSIGN to CONFIGDEFS if you want the d_assign code
#  Define SECURETTY if you run wih
.PRECIOUS: ../../h/mmdf.h

#
#  #include dependencies
#
#  Two versions are supplied.  One for sites with cc -M (4.3BSD)
#  and one for those that don't have it.  Comment out the one
#  you do not want.

#  This one is for sites without cc -M
#depend:
#	cat </dev/null >x.c
#	for i in $(MODULES); do \
#		(echo $$i.o: $$i.c >>makedep; \
#		/bin/grep '^#[ 	]*include' x.c $$i.c | sed \
#			-e 's,c:[^"]*"\./\([^"]*\)".*,o: \1,' \
#			-e 's,c:[^"]*"/\([^"]*\)".*,o: /\1,' \
#			-e 's,c:[^"]*"\([^"]*\)".*,o: ../../h/\1,' \
#			-e 's,c:[^<]*<\(.*\)>.*,o: /usr/include/\1,' \
#			>>makedep); done
#	echo '/^# DO NOT DELETE THIS LINE/+2,$$d' >eddep
#	echo '$$r makedep' >>eddep
#	echo 'w' >>eddep
#	cp Makefile.real Makefile.bak
#	ed - Makefile.real < eddep
#	rm eddep makedep x.c
#	echo '# DEPENDENCIES MUST END AT END OF FILE' >> Makefile.real
#	echo '# IF YOU PUT STUFF HERE IT WILL GO AWAY' >> Makefile.real
#	echo '# see make depend above' >> Makefile.real

#  This one id for sites with cc -M
depend:
	( for i in ${MODULES} ; do \
		${CC} -M ${CFLAGS} $$i.c ; done ) | \
	awk ' { if ($$1 != prev) { print rec; rec = $$0; prev = $$1; } \
		else { if (length(rec $$2) > 78) { print rec; rec = $$0; } \
		       else rec = rec " " $$2 } } \
	      END { print rec } ' > makedep
	echo '/^# DO NOT DELETE THIS LINE/+2,$$d' >eddep
	echo '$$r makedep' >>eddep
	echo 'w' >>eddep
	cp Makefile.real Makefile.bak
	ed - Makefile.real < eddep
	rm eddep makedep
	echo '# DEPENDENCIES MUST END AT END OF FILE' >> Makefile.real
	echo '# IF YOU PUT S