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.c h ` opendir £] "