[comp.os.minix] Wmail 2.6

jdoss@attctc.Dallas.TX.US (Joe M. Doss, Jr.) (11/09/89)

In article <2148@prune.bbn.com> rsalz@bbn.com (Rich Salz) writes:
>In <4465@ast.cs.vu.nl> ast@cs.vu.nl (Andy Tanenbaum) writes:
>
>From the Makefile:
>install:
>		...
>		chown root.root $(BIN)/wmail
>		chmod 4555 $(BIN)/wmail
>
>Okay, so the sucker runs setuid root.  GREAT!  Look at the
>dead_letter() function.  It does, basically fopen(getenv("DEADLETTER"), "w").
>
>So, all I need to is "DEADLETTER=/etc/passwd ; export DEADLETTER" and
>arrange to fail a mail message of the right stuff.  Yes, it does chown()
>the file, so I can't just write my own /bin/su...
>	/r$

I haven't looked at this latest version yet, but the previous version
didn't do any access() checking on saved mail files, either. Something like:

[1]& s /etc/passwd

worked from any account.  I fixed it with minor changes by creating a group
called "mail" and making the program setgid mail, no setuid.  I changed 
/usr/spool/mail to be group mail with group write-bit set, and all the
mail-files group mail with group write permission.  About the only harm one
could do that way is wipe out another user's mail file.  I'll make the few
changes to the newer version and post a patch in the next few days.

This method with /bin/mail setgid mail is the way mail is done on this 
System-V machine, so I can't see a problem with the concept, at least.
My implementation may have missed something obvious, but I've been using
it about a week without a single problem.  It certainly beats the security
hole the mailer has as posted.

==============================================================================
Disclaimer: The opinions above are my own, not my employer's

Joe M. Doss, Jr.                               jmdst!jdoss@attctc.dallas.tx.us
==============================================================================

billm@hpnmdla.HP.COM (Bill Marlin) (11/09/89)

/ hpnmdla:comp.os.minix / ast@cs.vu.nl (Andy Tanenbaum) /  2:52 am  Nov  8, 1989 /
: This is a shar archive.  Extract with sh, not csh.
: This archive ends with exit, so do not worry about trailing junk.
: --------------------------- cut here --------------------------
PATH=/bin:/usr/bin:/usr/ucb
echo Extracting 'Makefile'
sed 's/^X//' > 'Makefile' << '+ END-OF-FILE ''Makefile'
X#
X#     Makefile for W-MAIL Local Mail Agent (MINIX)
X#
X#   Makefile @(#)1.4        MicroWalt W-MAIL V2.6
X#
X# Define UMAILER if your remote mailer understands the
X# "-i datafile" option. Otherwise "< datafile" will be used.
X#
X# Define V7MAIL if you do not want the "To:"-line to be
X# included in the message header.
X#
XPROG	=	/usr/lib/uucp
XBIN	=	/usr/bin
X
XOPTS	=	-DUMAILER
XOPSYS   =	MINIX
X
XCFLAGS	=	-D$(OPSYS) -DUWALT -S $(OPTS)
XLDFLAGS =	-i
X
XOBJS	=	wmmain.s wmread.s wmcreate.s wminteract.s \
X		wmdeliver.s wmtime.s
XSRCS	=	wmmain.c wmread.c wmcreate.c wminteract.c \
X		wmdeliver.c wmtime.c
XOTHERS	=	README wmail.doc wmail Makefile wmail.h
X
X
Xwmail:		$(OBJS)
X		cc $(LDFLAGS) -o wmail $(OBJS)
X		@chmem =20000 wmail >/dev/null
X
Xinstall:
X		@echo 'Copying files...'
X		@rm -f $(BIN)/wmail $(BIN)/lmail $(BIN)/Mail \
X			$(BIN)/mailx $(BIN)/mail
X		@cp wmail $(BIN)
X		@echo 'Setting up links...'
X		@ln $(BIN)/wmail $(BIN)/lmail
X		@ln $(BIN)/wmail $(BIN)/mail
X		@ln $(BIN)/wmail $(BIN)/Mail
X		@ln $(BIN)/wmail $(BIN)/mailx
X		@echo 'Setting up permissions...'
X		chown root.root $(BIN)/wmail
X		chmod 4555 $(BIN)/wmail
X
Xshar:
X		@rm -f wmail.shar
X		@shar -C -u -v -o wmail.shar $(OTHERS) $(SRCS)
X
Xtar:
X		@rm -f wmail.tar
X		@tar cfv wmail.tar $(OTHERS) $(SRCS)
X
Xwmmain.s:	wmail.h wmmain.c
X		cc $(CFLAGS) wmmain.c
X
Xwmread.s:	wmail.h wmread.c
X		cc $(CFLAGS) wmread.c
X
Xwmcreate.s:	wmail.h wmcreate.c
X		cc $(CFLAGS) wmcreate.c
X
Xwminteract.s:	wmail.h wminteract.c
X		cc $(CFLAGS) wminteract.c
X
Xwmdeliver.s:	wmail.h wmdeliver.c
X		cc $(CFLAGS) wmdeliver.c
X
Xwmtime.s:	wmtime.c
X		cc $(CFLAGS) wmtime.c
X
+ END-OF-FILE Makefile
chmod 'u=rw,g=r,o=r' 'Makefile'
set `wc -c 'Makefile'`
count=$1
case $count in
1576)	:;;
*)	echo 'Bad character count in ''Makefile' >&2
		echo 'Count should be 1576' >&2
esac
echo Extracting 'README'
sed 's/^X//' > 'README' << '+ END-OF-FILE ''README'
X		W-MAIL Local Mail Agent (MINIX)
X
X		     MicroWalt W-MAIL V2.6
X		     =====================
X
XThis archive contains the sources of the W-MAIL program, which
Xis a MINIX version of the MAILX program available on most UNIXes.
X
XIt does not need the presence of UUCP in order to run. However,
Xfor non-local mail delivery a remote mailer (like MicroWalt UMAIL)
Xis required to be present.
X
XRevision History:
X2.5	10/10/89	Initial posting.
X
X2.6	11/07/89	General fixes and a cleanup.
X	- The Makefile-macro 'OTHERS' now contains 'wmail.h'
X	  as well. This caused incomplete archives to be
X	  distributed. Also, the parameters to 'shar' and 'tar'
X	  have been adapted to the new tar(1) and shar(1) programs.
X
X	- Fixed '.' problem in edit_mail().
X
X	- The strings 'findbuff', 'lbuff' and 'inbuff' have been
X	  moved to the routines in which they are used.
X
X	- The bug in the more() code has been fixed. This bug
X	  cause gargabe to be printed in some circumstances.
X
X	- The dead_letter() function no longer puts the file
X	  in the user's HOME directory. It now looks if there
X	  is an environmental variable "DEADLETTER"; if it 
X	  exists its value will be used as the file name. 
X	  Otherwise, the message will be dumped in the file
X	  "dead.letter" in the current directory.
X
X	- All references to 'NIL' have been changed into 'NULL'.
X	  It was a bit confusing to have both around.
X
X	- The code to scan message-headers has been changed
X	  radically. All message fields are now stored in the
X	  'LETTER' structure dynamically. This saves a lot of
X	  memory, and thus allows for larger mailboxes.
X
X	- The 'reply' bug has been fixed. This bug was caused
X	  by the UMAIL mailer, because it generated the wrong
X	  type of "From_" header. The header is now scanned by
X	  a separate routine in 'ummain.c' called 'old_hdr()'.
X
X	- The source has been edited to make it conform to the
X	  new MINIX Style Sheet as posted by Andrew Tanenbaum.
X
X	- The version-number has been increased to 2.6.
X
X
X
XOne last note:
X	If you are compiling this program, and you get a lot of
X	"illegal conversion of int to pointer" errors, please make
X	sure that your header files in /usr/include have the
X	"extern" declarations. Do NOT add them to this program...
X
XEnjoy!
X
X+-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-+
X| MINIX User Group (Holland)   UUCP: hp4nl!kyber!minixug!waltje	|
X| c/o Fred van Kempen,		 or: minixug!waltje@kyber.UUCP	|
X| Hoefbladhof  27						|
X| 2215 DV  VOORHOUT						|
X| The Netherlands	"A good programmer knows his Sources"	|
X+-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-+
X
+ END-OF-FILE README
chmod 'u=rw,g=r,o=r' 'README'
set `wc -c 'README'`
count=$1
case $count in
2591)	:;;
*)	echo 'Bad character count in ''README' >&2
		echo 'Count should be 2591' >&2
esac
echo Extracting 'wmail.doc'
sed 's/^X//' > 'wmail.doc' << '+ END-OF-FILE ''wmail.doc'
XWMAIL(1)             MINIX User's Manual               WMAIL(1)
X
X
XNAME     
X     wmail - read or send electronic mail.
X
XSYNOPSIS     
X     wmail [-epqrv] [-f mailbox]
X     wmail [-dvt] [-i inputfile] [-s subject] <user> ...
X     lmail [-v] [-i inputfile] <user> ...
X
XDESCRIPTION     
X     Wmail is  used  to read  or create messages. Wmail  only
X     knows  about  local  users; messages to  non-local users
X     will be handed to the Remote Mailer of the system.
X
X     Wmail actually consists  of two  parts; namely  the user
X     agent (the part that reads or creates a message) and the
X     local  delivery  agent, which takes care of delivering a
X     message to the mailbox of a local user.
X
X     When  Wmail  is  called  without the 'user' argument, it
X     will read  the  contents  of  the  users's  mailbox  and
X     display a summary of messages. This summary contains one
X     line for each message indicating message number, sender,
X     date of creation and the subject of the message.
X     Wmail will  then display a prompt (containing the number
X     of the  current message), and waits for the user to type
X     a command. A  list  of this commands can be requested by
X     typing the '?' command.
X
X     When  the program is called with the 'user' argument, it
X     is assumed that the calling user wants to send a message
X     to that  user. Depending  on the name by which Wmail was
X     called ('lmail' or something else), the message  will be
X     generated and then delivered to the user.
X     If  the program  name is  'lmail', then the program only
X     delivers  the  message  to  the indicated  user. This is
X     used by intelligent mail routing programs that cannot do
X     local message delivery themselves.
X
X     Otherwise, if  the  name was not 'lmail', the message is
X     prepended with  a message  header,  which  contains  the
X     following lines:
X
X	From <user> <date>
X	To: <adressee>
X	Subject: <subject of message>
X	<empty line>
X
X     The 'To:' and 'Subject:' lines  are  optional, but often
X     they  are  present  in  the  message. After this message
X     header, there is the message itself.
X
X     After the message has been completed (by typing a '.' on     
X     an  empty  line or by typing an EOT (CTRL-D) character),
X     Wmail checks if a  signature file exists. This is a file
X     which  contains a standard  end-of-letter  text for that
X     user. Wmail  checks  the user's HOME  directory  for the
X     presence of a '.signature' file. If that file exists, it
X     is appended to the message.
X
X				-- 1 --
X
X
X
XWMAIL(1)             MINIX User's Manual               WMAIL(1)
X
X
X     If that file does not exist, Wmail checks if there is an
X     environment variable called 'SIGNATURE'. If that exists,
X     its value is assumed to be the path name of a  signature
X     file which will be appended to the message if it exists.
X
X     Finally,  the message is delivered to the adressed user.
X     Note, that  if  the adressee  is  not  a local user, the
X     message  will be passed on to the 'rmail' program, which
X     handles all remote-mail requests.
X     If the adressee  is just a local user, Wmail will do the
X     work  itself. If  any  errors  occur  during  the  local
X     delivery, the message is stored in a file so that it can
X     be resent. If an environmental variable "DEADLETTER"  is
X     defined, its value will be used as the file name. Other-
X     wise, the name 'dead.letter' will be used in the current
X     directory. Also, some diagnostics will be printed on the
X     terminal screen.
X
XOPTIONS
X     Some  command-line  options  are available to alter  the
X     "standard" behaviour  of the program. While  functioning
X     as a mail reader, the following options areavailable:
X
X	-e		Only check for the presence of mail.
X			When  this  option  is  given,  Wmail
X			checks the user's mailbox and returns
X			to the command level with  one of the
X			following exit values:
X
X				0  There is mail.
X				1  No mail.
X				2  Error.
X			
X			This  feature  can  be  used  in  the
X			system profile to warn the user if he
X			or she has mail.
X
X	-f filename	Use 'filename' as the mailbox instead
X			of the standard system mailbox.
X
X	-p		Print  the messages;  no  interactive
X			selection is needed.
X
X	-q		Quit after an interrupt occurs.
X			Usually Wmail catches all  interrupt;
X			this option may be  given to override
X			the interrupt catching mechanism.
X
X	-r		Print mail in Reverse order.
X			This option  is recognized for compa-
X			tibility with older programs only. It
X                        does not do anything, as the messages
X                        are usually selected by the user.
X
X
X
X
X
X
X				-- 2 --
X
X
X
XWMAIL(1)             MINIX User's Manual               WMAIL(1)
X
X
X    When  used  as a  message  creating  agent, the following
X    options are available:
X
X	-d		Deliver; call  the  message  delivery
X			software  immediately  after queueing
X			the message. This  is only valid  for
X			mail  that  needs to  be delivered by
X			some other mailer, e.g. Remote Mail.
X
X	-i infile	Input; use file 'infile' as  the mes-
X			sage file instead of  standard input.
X
X	-t		Include  all  adressees  in the 'To:'
X			line of the message header; not only
X			the first adressed user.
X
X	-s subject	Use 'subject'  as  the  value of  the
X			'Subject:' line of the message header
X			and do not ask for it later.  When an
X			empty subject (only NEWLINE) is typed
X			no 'Subject:'  line  will be inserted
X			in the message header.
X
X	-v		Verbose; generate some useful  output
X			while processing mail.
X
X     The  'user'  argument  identifies  the  user to send the
X     message to. If it  contains any  '!' or  '@' characters,
X     the  adressee is assumed  to be on  a remote system, and
X     the message will  be  handed  over to the remote mailer.
X     Otherwise it  is assumed  to be  an existing local user,
X     and  the  '/etc/passwd'  file  will  be checked  for the
X     existence  of that  user. If the  user is  non-existant,
X     a  diagnostic  message is  printed  and the  message  is
X     saved in the 'dead.letter' file.
X
XAUTHOR     
X     Peter S. Housel (first draft of mail program)
X     Fred van Kempen, waltje@kyber.UUCP
X
XFILES     
X     /etc/passwd		check existence of users
X     /usr/spool/mail/user   	maildrop file
X     /usr/spool/mail/user.lock  lock for maildrop files
X     /tmp/wm*      		temporary file
X     $HOME/dead.letter		message after delivery error
X
XSEE ALSO    
X     umail.doc, "MINIX/UUCP Reference Manual"
X
X
X
X
X
X
X
X
X
X
X				-- 3 --
X
X
X
+ END-OF-FILE wmail.doc
chmod 'u=rw,g=r,o=r' 'wmail.doc'
set `wc -c 'wmail.doc'`
count=$1
case $count in
6511)	:;;
*)	echo 'Bad character count in ''wmail.doc' >&2
		echo 'Count should be 6511' >&2
esac
echo Extracting 'wmail.h'
sed 's/^X//' > 'wmail.h' << '+ END-OF-FILE ''wmail.h'
X/*
X * WMAIL -	MicroWalt Extended Mail Agent.
X *		This is the MicroWalt Mail Agent; which is derived
X *		from  the  "Mini-Mail" written by Peter S. Housel.
X *		This version  has a better  user-interface, and it
X *		also "knows" about things like forwarding, replies
X *		etc. Overall, it looks like the Mail (uppercase M)
X *		on our local DYNIX(tm) (==BSD) system...
X *		The paging-code (for paging letters on the screen)
X *		was taken from "more.c", by Brandon Allbery.
X *
X *		D E F I N I T I O N S
X *
X * Author:	Fred van Kempen, MicroWalt Corporation
X */
X#include <sys/types.h>
X#include <setjmp.h>
X
X
X#define VERSION			"2.6 (11/07/89)"
X
X#define DROPNAME 	     "/usr/spool/mail/%s"   /* User Mailbox */
X#define LOCKNAME	"/usr/spool/mail/%s.lock"   /* lockfile for box */
X#define MAILTEMP		"/tmp/mailXXXXXX"   /* temp. file */
X#define COPYTEMP		"/tmp/mcpyXXXXXX"   /* temp update file */
X#define SHELL		      		"/bin/sh"
X#define RMAIL			 "/usr/bin/rmail"   /* the Internet Mailer */
X#define SAVEFILE		     	   "mbox"   /* default mailsave */
X#define SIGNATURE		     ".signature"
X#define DEADLETTER		    "dead.letter"
X#define PROMPT				 "[%d]& "
X
X#ifndef PATHLEN
X#	define PATHLEN	128
X#endif PATHLEN
X
X#ifndef TRUE
X#	define FALSE	  0
X#	define TRUE	  1
X#endif
X
X#define LOCKWAIT	  5		/* seconds to wait after collision */
X#define LOCKTRIES	  4		/* maximum number of collisions */
X#define MAXRCPT		100		/* maximum number of recipients */
X#define LINELEN		512
X
X#define UNREAD		  1		/* 'not read yet' status */
X#define DELETED		  2		/* 'deleted' status */
X#define READ		  3		/* 'has been read' status */
X
X
Xtypedef struct _letter{
X  struct _letter *prev;		/* linked letter list: previous letter */
X  struct _letter *next;		/* linked letter list: next letter */
X  off_t location;		/* location within mailbox file */
X  int status;			/* letter status */
X  int seqno;			/* letter number */
X  char *subject;		/* subject of message */
X  char *sender;			/* sender of message */
X  char date[32];		/* date of message */
X} LETTER;
X#define NIL_LET	(LETTER *)NULL
X
X
Xextern char *Version;
Xextern int remote;			/* use RMAIL to deliver (if any) */
Xextern int loclink;			/* LMAIL: local delivery only! */
Xextern int old_uid, old_gid;		/* UID/GID of calling user */
Xextern int printmode;			/* print-and-exit mode */
Xextern int immediate;			/* send remote immediately! */
Xextern int quitmode;			/* take interrupts */
Xextern int usedrop;			/* read the maildrop (no -f given) */
Xextern int verbose;			/* pass "-v" flag on to mailer */
Xextern int needupdate;			/* need to update mailbox */
Xextern int sayall;			/* include line with all recipients */
Xextern int checkonly;			/* only chack if there is mail */
Xextern char sender[PATHLEN];		/* who sent the message? */
Xextern char forward[PATHLEN];		/* who is the message forwarded to? */
Xextern char adressee[PATHLEN];		/* current recipient */
Xextern char recipients[PATHLEN];	/* also to... users */
Xextern char mailbox[PATHLEN];		/* user's mailbox/maildrop */
Xextern char subject[PATHLEN];		/* subject of message */
Xextern char msg_temp[PATHLEN];		/* temporary file */
Xextern FILE *infp;			/* current message input file */
Xextern FILE *boxfp;			/* mailbox file */
Xextern char *progname;			/* program name */
Xextern jmp_buf printjump;		/* for quitting out of letters */
Xextern LETTER *firstlet, *lastlet;	/* message pointers for readbox() */
Xextern int numlet, nextlet, seqno;	/* number of active letters */
Xextern unsigned oldmask;		/* saved umask() */ 
X
X
Xextern char *getenv();			/* these are in libc.a */
Xextern FILE *fdopen();
Xextern long ftell();
X
X
Xextern void onint(/* void */);
Xextern char *basename(/* char *name */);
Xextern char *basepath(/* char *name */);
Xextern char *whoami(/* void */);
Xextern void showlet(/* LETTER *let */);
Xextern void printlet(/* LETTER *let, FILE *tofp */);
Xextern void printall(/* void */);
Xextern void dead_letter(/* void */);
Xextern char *find_string(/* LETTER *let, char *text */);
Xextern void updatebox(/* void */);
Xextern void interact(/* void */);
Xextern void send_remote(/* char *name */);
Xextern FILE *edit_mail(/* void */);
Xextern int chk_box(/* void */);
Xextern int deliver(/* int count, char *vec[] */);
Xextern char *xtime(/* void */);
X
+ END-OF-FILE wmail.h
chmod 'u=rw,g=r,o=r' 'wmail.h'
set `wc -c 'wmail.h'`
count=$1
case $count in
4199)	:;;
*)	echo 'Bad character count in ''wmail.h' >&2
		echo 'Count should be 4199' >&2
esac
echo Extracting 'wmcreate.c'
sed 's/^X//' > 'wmcreate.c' << '+ END-OF-FILE ''wmcreate.c'
X/*
X * WMAIL -	MicroWalt Extended Mail Agent.
X *		This is the MicroWalt Mail Agent; which is derived
X *		from  the  "Mini-Mail" written by Peter S. Housel.
X *		This version  has a better  user-interface, and it
X *		also "knows" about things like forwarding, replies
X *		etc. Overall, it looks like the Mail (uppercase M)
X *		on our local DYNIX(tm) (==BSD) system...
X *		The paging-code (for paging letters on the screen)
X *		was taken from "more.c", by Brandon Allbery.
X *
X *		M A I L    G E N E R A T I O N    M O D U L E
X *
X * Author:	Fred van Kempen, MicroWalt Corporation
X *
X * Revisions:
X *		11/07/89 FvK	Edited a little for the new MSS.
X */
X#include <stdio.h>
X#include <string.h>
X#include <pwd.h>
X#include "wmail.h"
X
X
X/*
X * Create a message, including the Mail-header and the final signature.
X * If this is a link to LMAIL and/or if the message is read from a file
X * leave out the signature.
X * This routine copies lines of text from 'stdin' to 'tempfp', until an
X * EOF is typed, or a line containing only a '.'.
X * We complicate things by not setting a line limit.
X * Define V7MAIL to skip the "To:"-field.
X */
XFILE *edit_mail(void)
X{
X  char cpbuff[1024];			/* copy buffer */
X  char tmp[PATHLEN];
X  register FILE *tempfp, *sigfp;
X  register char *sp, *bp;
X  register int c;
X  int state, done;
X
X  if ((tempfp = fopen(msg_temp, "w")) == (FILE *)NULL) {
X	fprintf(stderr, "%s: cannot create temp file \"%s\"\n",
X							progname, msg_temp);
X	return((FILE *)NULL);
X  }
X
X  /* 
X   * Create the header. This has the form:
X   *
X   *  From <user> <date>
X   *  Subject: <text>	(if -s on command line or stdin)
X   *  To: <userlist>	(all recipients)
X   */
X  if (loclink == FALSE) {	/* only create header if not LMAIL! */
X	fprintf(tempfp, "From %s %s\n", sender, xtime());
X	if (subject[0]=='\0') {
X		if (isatty(fileno(infp))) {
X			fprintf(stderr, "Subject: ");
X			gets(subject);
X       			sp = strrchr(subject, '\n');
X			if (sp != NULL) *sp = '\0';
X			fprintf(stderr, "\n");
X       		}
X	}
X#ifndef V7MAIL
X	fprintf(tempfp, "To: %s\n", recipients);
X#endif
X	if (subject[0] != '\0') fprintf(tempfp, "Subject: %s\n", subject);
X
X	fputc('\n', tempfp);
X	done = FALSE; 
X	state = 1;	/* start out in 'newline' state */
X	do {
X      	    if ((c = fgetc(infp)) != EOF) {	
X		switch(c) {
X			case '\n':	/* mark this newline */
X				if (state == 0) state++;
X				  else if (state == 2) done = TRUE;
X
X				fputc(c, tempfp);
X				break;
X			case '.':	/* was previous a newline? */
X				if (state == 1) state++;
X				  else fputc(c, tempfp);
X				break;
X			default:	/* clear 'newline' flag */
X				if (state == 2) fputc('.', tempfp);
X				state = 0;
X				fputc(c, tempfp);
X		}
X      	    } else done = TRUE;
X  	} while (done == FALSE);
X
X	/*
X	 * Add a .signature file after the message.
X	 * Skip this if infp is a file.
X	 */
X	if (isatty(fileno(infp))) {
X		sp = getenv("SIGNATURE");
X		if (sp == NULL) {
X			sp = getenv("HOME");
X	      		if (sp != NULL) {
X                		strcpy(tmp, sp);
X                		if (strlen(tmp) > 1) strcat(tmp, "/");
X               		} else strcpy(tmp, "");
X              		strcat(tmp, SIGNATURE);
X	 	} else strcpy(tmp, sp);
X
X		if ((sigfp = fopen(tmp, "r")) != (FILE *)NULL) {
X			while ((c = fgetc(sigfp)) != EOF) fputc(c, tempfp);
X	        	fclose(sigfp);
X		}
X  	}
X  } else {	/* Fast file copy for local delivery. */
X	  /* Copy temp. file to mailbox. */
X	  while (TRUE) {
X		if (fgets(cpbuff, sizeof(cpbuff), infp) == (char *)NULL)
X								      break;
X		fwrite(cpbuff, sizeof(char), strlen(cpbuff), tempfp);
X	  }
X    }
X
X  if (ferror(tempfp) || fclose(tempfp)) {
X	fprintf(stderr, "%s: could not copy letter to temporary file\n",
X								progname);
X      	return((FILE *)NULL);
X  }
X
X  fclose(tempfp);
X  tempfp = fopen(msg_temp, "r");
X  return(tempfp);
X}
X
+ END-OF-FILE wmcreate.c
chmod 'u=rw,g=r,o=r' 'wmcreate.c'
set `wc -c 'wmcreate.c'`
count=$1
case $count in
3741)	:;;
*)	echo 'Bad character count in ''wmcreate.c' >&2
		echo 'Count should be 3741' >&2
esac
echo Extracting 'wmdeliver.c'
sed 's/^X//' > 'wmdeliver.c' << '+ END-OF-FILE ''wmdeliver.c'
X/*
X * WMAIL -	MicroWalt Extended Mail Agent.
X *		This is the MicroWalt Mail Agent; which is derived
X *		from  the  "Mini-Mail" written by Peter S. Housel.
X *		This version  has a better  user-interface, and it
X *		also "knows" about things like forwarding, replies
X *		etc. Overall, it looks like the Mail (uppercase M)
X *		on our local DYNIX(tm) (==BSD) system...
X *		The paging-code (for paging letters on the screen)
X *		was taken from "more.c", by Brandon Allbery.
X *
X *		L O C A L    D E L I V E R Y    M O D U L E
X *
X * Author:	Fred van Kempen, MicroWalt Corporation
X *
X * Revisions:
X *		11/07/89 FvK	Edited a little for the new MSS.
X *				Fixed "dead.letter" pathname.
X *
X * To Do:
X *		- Aliases.
X */
X#include <sys/stat.h>
X#include <stdio.h>
X#include <string.h>
X#include <signal.h>
X#include <errno.h>
X#include <pwd.h>
X#include "wmail.h"
X
X
Xextern int errno;
X
X
X/*
X * Update the mail-box file.
X */
Xvoid updatebox(void)
X{
X  char cpbuff[1024];		/* copy buffer */
X  char lockname[PATHLEN];	/* maildrop lock */
X  char copytemp[PATHLEN];	/* temporary copy file */
X  FILE *copyfp;			/* fp for tempfile */
X  LETTER *let;			/* current letter */
X  int locktries = 0;		/* tries when box is locked */
X  int c;
X
X  strcpy(copytemp, COPYTEMP);
X  mktemp(copytemp);
X
X  sprintf(lockname, LOCKNAME, sender);
X  
X  /* Create a new mailbox-file. */
X  if ((copyfp = fopen(copytemp, "w")) == (FILE *)NULL) {
X	fprintf(stderr, "%s: cannot create temp file \"%s\"\n",
X						progname, copytemp);
X	return;
X  }
X    
X  /* Copy letters from old file to new file. */
X  for (let = firstlet; let != NIL_LET; let = let->next) {
X	if (let->status != DELETED) printlet(let, copyfp);
X  }
X
X  if ((copyfp = freopen(copytemp, "r", copyfp)) == (FILE *)NULL) {
X	sprintf(cpbuff, "%s: temporary file write error", progname);
X	perror(cpbuff);
X	if (usedrop) unlink(copytemp); 
X	return;
X  }
X
X  /* Shut off signals during the update. */
X  signal(SIGINT, SIG_IGN);
X  signal(SIGHUP, SIG_IGN);
X  signal(SIGQUIT, SIG_IGN);
X
X  if (usedrop) while(link(mailbox, lockname) != 0) {
X	if (++locktries >= LOCKTRIES) {
X		fprintf(stderr, "%s: could not lock maildrop for update\n",
X								progname);
X		return;
X	}
X	sleep(LOCKWAIT);
X  }
X
X  if ((boxfp = freopen(mailbox, "w", boxfp)) == (FILE *)NULL) {
X	sprintf(cpbuff, "%s: could not reopen maildrop\n", progname);
X       	fprintf(stderr, "%sMail may have been lost; look in %s\n",
X							cpbuff, copytemp);
X       	unlink(lockname);
X       	return;
X  }
X
X  /* Copy temp. file to mailbox. */
X  while (TRUE) {
X	if (fgets(cpbuff, sizeof(cpbuff), copyfp) == (char *)NULL) break;
X	fwrite(cpbuff, sizeof(char), strlen(cpbuff), boxfp);
X  }
X  fflush(copyfp); fflush(boxfp);
X  fclose(boxfp); fclose(copyfp);
X  unlink(copytemp); 
X
X  if (usedrop) unlink(lockname);
X}
X
X
X/*
X * Send a message to a remote user.
X * Define UMAILER if your mailer knows about the "-i datafile" option.
X */
Xvoid send_remote(name)
Xchar *name;
X{
X  char cmdbuff[128];
X
X#ifdef UMAILER		/* faster than redirecting! */
X  sprintf(cmdbuff, "exec %s -i%s %s %s %s >/dev/null", RMAIL, msg_temp,
X#else
X  sprintf(cmdbuff, "exec %s <%s %s %s %s >/dev/null", RMAIL, msg_temp,
X#endif UMAILER
X	immediate ? "-n" : "", verbose ? "-v" : "", adressee);
X  system(cmdbuff);				/* call the mailer! */
X}
X
X
X/*
X * Deliver a message to a user.
X * First of all, check if we can use a more intelligent mailer
X * for this job. If not, deliver it !
X */
Xint deliver(count, vec)
Xint count; char *vec[];
X{
X  int (*sigint)(), (*sighup)(), (*sigquit)();	/* saved signal state */
X  char cpbuff[1024];			/* copy buffer */
X  char lockname[PATHLEN];		/* maildrop lock */
X  FILE *mailfp;				/* fp for mail */
X  struct stat stb;			/* for checking drop modes, owners */
X  struct passwd *pw;			/* sender and recipent */
X  int errs = 0;				/* count of errors */
X  int dropfd;				/* file descriptor for user's drop */
X  int created = FALSE;			/* true if we created the maildrop */
X  int locktries;			/* tries when box is locked */
X  register int i, c;
X
X  if (count > MAXRCPT) {
X	fprintf(stderr, "%s: too many recipients\n", progname);
X	return(-1);
X  }
X
X  strcpy(recipients, "");
X  if (sayall) {
X	for (i=0; i<count; i++) {
X		strcat(recipients, vec[i]);
X		/* RFC-822: separate with comma */
X		strcat(recipients, ",");
X  	}
X  	recipients[strlen(recipients)-1] = '\0';  /* kill last comma */
X  } else strcat(recipients, vec[0]);     
X
X  mailfp = edit_mail();	/* input the message */
X
X  /* Shut off signals during the delivery. */
X  sigint = signal(SIGINT, SIG_IGN);
X  sighup = signal(SIGHUP, SIG_IGN);
X  sigquit = signal(SIGQUIT, SIG_IGN);
X
X  /*
X   * We have the message, now deliver it to all recipients!
X   * Do this on a per-user basis!
X   */
X  for (i = 0; i < count; ++i) {
X	if (count > 1) rewind(mailfp);		/* rewind data-file */
X	strcpy(adressee, vec[i]); 		/* get name of adressee */
X	forward[0] = '\0';			/* Clear 'Forward' flag */
X
X      	/* OK, 'adressee' is the recipient. Check if local. */
X      	if (strchr(adressee, '!') || strchr(adressee, '@')) {
X		remote = TRUE;			/* the guy is remote... */
X        	send_remote(adressee);		/* call RMAIL for this... */
X		continue;
X       	}
X
X	/* Hmm, it is a local user. Do we know him? */
X      	if ((pw = getpwnam(adressee)) == (struct passwd *)NULL) {
X		fprintf(stderr, "%s: user %s unknown\n", progname, adressee);
X		++errs;
X		dead_letter();
X		continue;
X       	} else {
X	   	sprintf(mailbox, DROPNAME, adressee);
X 	       	sprintf(lockname, LOCKNAME, adressee);
X	}
X
X	/* OK, we know him. Check if we have to forward his mail. */
X      	if (chk_box() == 1) {		/* forward messages to 'forward' */
X		strcpy(adressee, forward);
X
X        	/* We now have the final adressee.
X		 * Check for remote users again.
X		 */
X		if (strchr(adressee, '!') || strchr(adressee, '@')) {
X			remote = TRUE;
X          		send_remote(adressee);		/* No, call RMAI */
X	  		continue;
X         	} else {	/* Check if this guy is known. */
X		       	if ((pw = getpwnam(adressee)) ==
X						(struct passwd *)NULL) {
X		   		fprintf(stderr,
X					"%s: forward-user %s unknown\n",
X							progname, adressee);
X		   		++errs;
X				dead_letter();
X		   		continue;
X	          	} else {
X		         	sprintf(mailbox, DROPNAME, adressee);
X	 	          	sprintf(lockname, LOCKNAME, adressee);
X		        }
X		}
X	}
X
X      	/*
X       	 * We now have a local user 'adressee' who exists.
X         * Lock the maildrop while we're messing with it. Races are possible
X         * (though not very likely) when we have to create the maildrop, but
X         * not otherwise. If the box is already locked, wait awhile and try
X         * again.
X         */
X      	locktries = 0;
X	created = 0;
Xtrylock:
X      	if (link(mailbox, lockname) != 0) {
X		if (errno == ENOENT) {	/* user doesn't have a drop yet */
X			if ((dropfd = creat(mailbox, 0600)) < 0) {
X				fprintf(stderr,
X				 "%s: could not create maildrop for %s\n",
X		      					  progname, vec[i]);
X				++errs;
X				dead_letter();
X				continue;
X          		}
X	  		++created;
X	  		goto trylock;
X         	} else {   /* somebody else has it locked, it seems - wait */
X	  	 	if (++locktries >= LOCKTRIES) {	
X				fprintf(stderr,
X				  "%s: could not lock maildrop for %s\n",
X		      					progname, vec[i]);
X				++errs;
X				dead_letter();
X	      	   		continue;
X	     	  	}
X	   	 	sleep(LOCKWAIT);
X	   	 	goto trylock;
X	  	}
X	}
X
X      	if (created) {
X        	chown(mailbox, pw->pw_uid, pw->pw_gid);
X        	boxfp = fdopen(dropfd, "a");
X       	} else boxfp = fopen(mailbox, "a");
X
X      	if (boxfp==(FILE *)NULL || stat(mailbox, &stb) < 0) {
X		fprintf(stderr, "%s: serious maildrop problems for %s\n",
X							progname, vec[i]);
X        	unlink(lockname);
X        	++errs;
X		dead_letter();
X        	continue;
X       	}
X
X      	if (stb.st_uid != pw->pw_uid || (stb.st_mode & S_IFMT) != S_IFREG) {
X		fprintf(stderr, "%s: mailbox for user %s is illegal\n",
X							progname, vec[i]);
X		unlink(lockname);
X		++errs;
X		dead_letter();
X		continue;
X       	}
X
X	/* Copy temp. file to mailbox. */
X	while (TRUE) {
X		if (fgets(cpbuff, sizeof(cpbuff), mailfp) == (char *)NULL)
X								      break;
X		fwrite(cpbuff, sizeof(char), strlen(cpbuff), boxfp);
X	}
X
X	/* To make sure! */
X	fputc('\n', boxfp);
X      	if (ferror(boxfp) || fclose(boxfp) != 0) {
X		fprintf(stderr, "%s: error delivering to user %s",
X							progname, vec[i]);
X		perror("");
X		dead_letter();
X		++errs;
X       	}
X
X      	unlink(lockname);
X  }
X
X  fclose(mailfp);
X
X  /* Put signals back the way they were. */
X  signal(SIGINT, sigint);
X  signal(SIGHUP, sighup);
X  signal(SIGQUIT, sigquit);
X
X  return((errs == 0) ? FALSE : TRUE);
X}
X
X
X/* 
X * Save the current message to file 'dead.letter'.
X * This is sometimes needed when delivery fails.
X *
X * If the environmental variable "DEADLETTER" is defined,
X * use its value as the file name. Otherwise, use DEADLETTER
X * in the current directory.
X */
Xvoid dead_letter(void)
X{
X  char cpbuff[1024];
X  char fname[PATHLEN];
X  char *sp;
X  register FILE *inf, *outf;
X
X  fname[0] = '\0';
X  inf = fopen(msg_temp, "r");
X  if ((sp = getenv("DEADLETTER")) != NULL) strcpy(fname, sp);
X    else strcpy(fname, DEADLETTER);
X
X  inf = fopen(msg_temp, "r");
X  if (inf == (FILE *)NULL) {
X	fprintf(stderr, "%s: cannot open \"%s\"\n", progname, msg_temp);
X	return;
X  }
X
X  outf = fopen(fname, "w");
X  if (outf == (FILE *)NULL) {
X	fprintf(stderr, "%s: cannot create \"%s\"\n", progname, fname);
X	return;
X  }
X
X  /* Copy temp. file to dead.letter. */
X  while (TRUE) {
X	if (fgets(cpbuff, sizeof(cpbuff), inf) == (char *)NULL) break;
X	fwrite(cpbuff, sizeof(char), strlen(cpbuff), outf);
X  }
X
X  fclose(inf);
X  fclose(outf);
X
X  chown(fname, old_uid, old_gid);
X  fprintf(stderr, "%s: dumped message on file \"%s\"\n", progname, fname);
X}
+ END-OF-FILE wmdeliver.c
chmod 'u=rw,g=r,o=r' 'wmdeliver.c'
set `wc -c 'wmdeliver.c'`
count=$1
case $count in
9643)	:;;
*)	echo 'Bad character count in ''wmdeliver.c' >&2
		echo 'Count should be 9643' >&2
esac
echo Extracting 'wminteract.c'
sed 's/^X//' > 'wminteract.c' << '+ END-OF-FILE ''wminteract.c'
X/*
X * WMAIL -	MicroWalt Extended Mail Agent.
X *		This is the MicroWalt Mail Agent; which is derived
X *		from  the  "Mini-Mail" written by Peter S. Housel.
X *		This version  has a better  user-interface, and it
X *		also "knows" about things like forwarding, replies
X *		etc. Overall, it looks like the Mail (uppercase M)
X *		on our local DYNIX(tm) (==BSD) system...
X *		The paging-code (for paging letters on the screen)
X *		was taken from "more.c", by Brandon Allbery.
X *
X *		I N T E R A C T I O N    M O D U L E
X *
X * Author:	Fred van Kempen, MicroWalt Corporation
X *
X * Revisions:
X *		11/07/89 FvK	Edited a little for the new MSS.
X *
X * To Do:
X *		- Builtin escapes (~i and friends)
X */
X#include <stdio.h>
X#include <ctype.h>
X#include <string.h>
X#include <signal.h>
X#include <unistd.h>
X#include "wmail.h"
X
X
X/*
X * Send a reply to a message.
X */
Xstatic int do_reply(let)
XLETTER *let;
X{
X  char repuser[512];
X  char *who[2];
X
X  strcpy(repuser, let->sender);
X  who[0] = &repuser[0];
X  who[1] = (char *)NULL;
X  sprintf(subject, "Re: %s", let->subject);
X
X  printf("\n*** From: %s %-24.24s\n", sender, xtime());
X  printf("*** To: %s\n", who[0]);
X  printf("*** Subject: %s\n\n", subject);
X
X  deliver(1, who);
X}
X
X
X/*
X * Execute a shell (or a command only)
X */
Xstatic void do_shell(command)
Xchar *command;
X{
X  int status, pid;
X  char *shell;
X
X  if ((shell = getenv("SHELL")) == NULL) shell = SHELL;
X
X  if ((pid = fork()) == 0) {
X	setgid(old_gid);	/* UUCP or USER */
X	setuid(old_uid);	/* UUCP or USER */
X	umask(oldmask);
X
X	execl(shell, shell, "-c", command, NULL);
X	fprintf(stderr, "%s: cannot exec shell", progname);
X	exit(127);
X  } else {
X	  if (pid > 0) wait(&status);
X	    else fprintf(stderr, "%s: could not fork", progname);
X  }
X}
X
X
X/*
X * Goto a specific letter.
X */
Xstatic LETTER *goto_letter(num)
Xregister int num;
X{
X  register LETTER *let;
X
X  let = firstlet;
X  while (let != NIL_LET) {
X	if (let->seqno == num) return(let);
X	let = let->next;
X  }
X  return(NIL_LET);
X}
X
X
X/*
X * Skip the header of the current message.
X * Do this by just updating the 'boxfp' pointer...
X */
Xstatic off_t skiphead(void)
X{
X  char skbuf[1024];
X  off_t count;
X
X  count = (off_t) 0;
X  while (fgets(skbuf, sizeof(skbuf), boxfp) != NULL) {
X	count += (off_t) strlen(skbuf);
X      	if (skbuf[0] == '\n') break;  /* end of header */
X  }
X  return(count);
X}
X
X
X/*
X * Save the current letter to a disk-file.
X */
Xvoid savelet(let, savefile, withhead)
XLETTER *let;
Xchar *savefile;
Xint withhead;
X{
X  off_t curr, limit, oldpos;
X  register char *bp;
X  FILE *savefp;
X  int c;
X
X  bp = savefile;
X  while (*bp && *bp!='\n') bp++;
X  *bp = '\0';
X
X  if ((savefp = fopen(savefile, "a")) == (FILE *)NULL) {
X	fprintf(stderr, "%s: cannot append to savefile \"%s\"\n",
X							progname, savefile);
X	return;
X  }
X
X  oldpos = ftell(boxfp);
X  fseek(boxfp, (curr = let->location), SEEK_SET);
X  limit = (let->next != NIL_LET) ? let->next->location : -1L;
X
X  if (withhead == 0) curr += skiphead();	/* skip the message header */
X  while(curr != limit && (c = fgetc(boxfp)) != EOF) {
X	fputc(c, savefp);
X      	++curr;
X  }
X  fflush(savefp);
X  fseek(boxfp, oldpos, SEEK_SET);
X
X  if ((ferror(savefp) != 0) | (fclose(savefp) != 0)) {
X	fprintf(stderr, "%s: savefile write error:", progname);
X  }
X
X  chown(savefile, old_uid, old_gid);
X}
X
X
X/*
X * Give a list of possible Interact Commands.
X */
Xstatic void do_help(void)
X{
X  printf("\n   ** W-MAIL Commands **\n\n");
X  printf("?\tThis help\n");
X  printf("!\tShell Command Escape\n");
X  printf("-\tPrevious letter\n");
X  printf("+\tNext letter\n");
X  printf("<ENTER>\tNext letter\n");
X  printf("d\tDelete current letter\n");
X  printf("i\tDisplay a summary of letters\n");
X  printf("p\tPrint a letter again\n");
X  printf("q\tQuit, update mailbox\n");
X  printf("r\tReply to the current letter\n");
X  printf("s\tSave current letter\n");
X  printf("t\tType a letter, no paging\n");
X  printf("w\tSave letter without header\n");
X  printf("x\tExit, do not update mailbox\n");
X  printf("\n");
X}
X
X
X/*
X * Give a summary of letters./
X */
Xstatic void summary(void)
X{
X  register LETTER *let;
X  register int i;
X  register char *sp;
X
X  let = firstlet;
X  printf(" No.  Sender           Date            Subject\n");
X  printf(" ----------------------------------------------");
X  printf("--------------------------------\n");
X
X  for (i=0; i<numlet; i++) {
X	sp = let->date;
X	if (strchr(sp, ',')) sp += 5;	/* xtime() or ctime() ?? */
X	  else sp += 4;
X	printf("%c%c %-3.3d  %-8.8s  %-15.15s  \"%.40s\"\n",
X		(nextlet == let->seqno) ? '>': ' ',
X		(let->status == DELETED) ? '*': ' ',
X 		let->seqno, basepath(let->sender), sp, let->subject);
X        let = let->next;
X  }
X  printf("\n");
X}
X
X
X/*
X * Interactively read the mail-box.
X * This routine has to be improved a lot.
X */
Xvoid interact(void)
X{
X  char input[512];			/* user input line */
X  LETTER *let, *templet, dummy;		/* current and next letter */
X  int interrupted = FALSE;		/* SIGINT hit during letter print */
X  char *savefile, *p;
X  int i, temp;
X
X  if (firstlet == NIL_LET) {
X	printf("No mail for %s.\n", sender);
X      	return;
X  }
X
X  printf("W-MAIL %s.  Type ? for Help.\n", Version);
X  printf("\"%s\": %d message(s)\n\n", mailbox, numlet);
X
X  nextlet = 1;
X  dummy.seqno = nextlet;
X  dummy.next = firstlet;
X  let = &dummy;
X
X  summary();			/* Show a summary of all the letters. */
X
X  while(TRUE) {
X	nextlet = let->seqno;
X
X       	if (!quitmode) {
X		interrupted = setjmp(printjump);
X		signal(SIGINT, onint);
X       	}
X
X      	if (interrupted = TRUE) printf("\n");
X      	printf(PROMPT, let->seqno);
X      	fflush(stdout);
X
X      	if (fgets(input, sizeof(input), stdin) == NULL) break;
X
X      	if (!quitmode) signal(SIGINT, SIG_IGN);
X
X	/*
X	 * Look at the first character of the command line.
X	 * This should be improved!
X	 */
X      	switch(input[0]) {
X		case '!':	/* Shell escape. */
X			do_shell(&input[1]);
X			continue;
X		case '?':	/* Type some help. */
X			do_help();
X			continue;
X		case '-':	/* Show previous letter. */
X			if (let->prev != NIL_LET) let = let->prev;
X			  else printf("Top of mailbox\n");
X			continue;
X		case '+':	/* Show next letter. */
X		case '\n':
X			if (let->next != NIL_LET) {
X				let = let->next;
X				if (!interrupted) {
X		            		if (let->status != DELETED)
X							let->status = READ;
X			    		printlet(let, stdout);
X		           	}
X			} else printf("At EOF\n");
X			continue;
X		case 'd':	/* Delete current letter. */
X			let->status = DELETED;
X			if (let->next != NIL_LET) let = let->next;
X			needupdate = TRUE;
X			continue;
X		case 'i':	/* Show letter summary */
X			summary();
X			continue;
X		case 'p':	/* Print a letter. */
X			if (!interrupted) printlet(let, stdout);
X			continue;
X		case 'q':	/* Update mailbox and quit. */
X			return;
X		case 'r':	/* Reply to a message. */
X			do_reply(let);
X			break;
X		case 's':	/* Save message to disk. */
X			savefile = &input[1];
X			if (*savefile != '\0') {
X			  	while (*savefile==' ' || *savefile=='\t')
X								savefile++; 
X			} else savefile = SAVEFILE;
X			savelet(let, savefile, TRUE);
X			continue;
X		case 't':	/* Type (no paging) current letter. */
X			temp = printmode;
X			printmode = TRUE;
X			if (!interrupted) printlet(let, stdout);
X			printmode = temp;
X			continue;
X		case 'w':	/* Write (without header) message to disk. */
X			savefile = &input[1];
X			if (*savefile != '\0') {
X			  	while (*savefile==' ' || *savefile=='\t')
X								savefile++; 
X			} else savefile = SAVEFILE;
X			savelet(let, savefile, FALSE);
X			continue;
X		case 'x':	/* Abort, do not update mailbox. */
X			exit(0);
X		default:
X			if (isdigit(input[0])) {
X				templet = goto_letter(atoi(input));
X				if (templet != NIL_LET) {
X					let = templet;
X					printlet(let, stdout);
X			   	} else printf("Illegal message-number\n");
X			} else printf("Illegal command\n");
X			continue;
X  	}
X  }   
X}
X
+ END-OF-FILE wminteract.c
chmod 'u=rw,g=r,o=r' 'wminteract.c'
set `wc -c 'wminteract.c'`
count=$1
case $count in
7742)	:;;
*)	echo 'Bad character count in ''wminteract.c' >&2
		echo 'Count should be 7742' >&2
esac
echo Extracting 'wmmain.c'
sed 's/^X//' > 'wmmain.c' << '+ END-OF-FILE ''wmmain.c'
X/*
X * WMAIL -	MicroWalt Extended Mail Agent.
X *		This is the MicroWalt Mail Agent; which is derived
X *		from  the  "Mini-Mail" written by Peter S. Housel.
X *		This version  has a better  user-interface, and it
X *		also "knows" about things like forwarding, replies
X *		etc. Overall, it looks like the Mail (uppercase M)
X *		on our local DYNIX(tm) (==BSD) system...
X *		The paging-code (for paging letters on the screen)
X *		was taken from "more.c", by Brandon Allbery.
X *
X * Author:	Fred van Kempen, MicroWalt Corporation
X *
X * Revisions:
X *		11/07/89 FvK	Edited a little for the new MSS.
X *				Allocate the "From_" path dynamically;
X *				it uses too much space now.
X *				Fixed the "reply" bug that was caused
X *				by a badly-decoded "From_" line.
X */
X#include <sys/stat.h>
X#include <stdio.h>
X#include <alloc.h>
X#include <string.h>
X#include <errno.h>
X#include <pwd.h>
X#include <unistd.h>
X#include "wmail.h"
X
X
Xchar *Version = VERSION;
Xint remote = FALSE;			/* use RMAIL to deliver (if any) */
Xint loclink = FALSE;			/* LMAIL: local delivery only! */
Xint old_uid, old_gid;			/* UID/GID of calling user */
Xint printmode = FALSE;			/* print-and-exit mode */
Xint immediate = FALSE;			/* send remote immediately! */
Xint quitmode = FALSE;			/* take interrupts */
Xint usedrop = TRUE;			/* read the maildrop (no -f given) */
Xint verbose = FALSE;			/* pass "-v" flag on to mailer */
Xint needupdate = FALSE;			/* need to update mailbox */
Xint sayall = FALSE;			/* include line with all recipients */
Xint checkonly = FALSE;			/* only chack if there is mail */
Xchar sender[PATHLEN];			/* who sent the message? */
Xchar forward[PATHLEN];			/* who is the message forwarded to? */
Xchar adressee[PATHLEN];			/* current recipient */
Xchar recipients[PATHLEN];		/* also to... users */
Xchar mailbox[PATHLEN];			/* user's mailbox/maildrop */
Xchar subject[PATHLEN];			/* subject of message */
Xchar msg_temp[PATHLEN];			/* temporary file */
XFILE *infp = (FILE *)NULL;		/* current message input file */
XFILE *boxfp = (FILE *)NULL;		/* mailbox file */
Xchar *progname;				/* program name */
Xjmp_buf printjump;			/* for quitting out of letters */
XLETTER *firstlet, *lastlet;		/* message pointers for readbox() */
Xint numlet, nextlet, seqno;		/* number of active letters */
Xunsigned oldmask;			/* saved umask() */ 
X
X
Xextern int getopt(), optind;		/* from the GETOPT(3) package */
Xextern char *optarg;
Xextern int errno;			/* from STDIO */
X
X
Xvoid onint(void)
X{
X  longjmp(printjump, TRUE);
X}
X
X
X/*
X * Chop off the last (file) part of a filename.
X */
Xchar *basename(name)
Xregister char *name;
X{
X  register char *p;
X
X  p = strrchr(name, '/');
X  if (p == NULL) return(name);	/* no pathname */
X    else return(p + 1);
X}
X
X
X/*
X * Chop off the last (user) part of a UUCP path-name.
X * This is needed for the summary() routine.
X */
Xchar *basepath(name)
Xregister char *name;
X{
X  register char *p;
X
X  p = strrchr(name, '!');
X  if (p == NULL) return(name);	/* no pathname */
X    else return(p + 1);
X}
X
X
X/*
X * return ASCII text of our login-name.
X */
Xchar *whoami(void)
X{
X  register struct passwd *pw;
X
X  if ((pw = getpwuid(getuid())) != (struct passwd *)NULL)
X						return(pw->pw_name);
X    else return("nobody");
X}
X
X
X/*
X * Find the given entry in the mail-header
X * Search for the first occurence of string 'text' in the header.
X * Copy the text following it into the 'let' structure.
X * Return buffer if found, else NULL.
X */
Xchar *find_string(let, text)
XLETTER *let;
Xchar *text;
X{
X  static char findbuff[128];
X  static char inbuff[512];
X  off_t curr, limit;
X  register char *sp;
X  int all;
X
X  fseek(boxfp, let->location, SEEK_SET);
X  limit = (off_t) -1L;
X  if (let->next != NIL_LET) limit = let->next->location;
X
X  all = FALSE;
X  curr = let->location;
X  while (curr != limit && all==FALSE) {
X	if (fgets(inbuff, sizeof(inbuff), boxfp) == NULL) all = TRUE;
X      	if (inbuff[0] == '\0') all = TRUE; /* end-of-header */
X
X      	if (!strncmp(inbuff, text, strlen(text))) {
X		sp = &inbuff[0];		/* remove '\n' */
X      		while (*sp && *sp!='\n') sp++;
X      		*sp = '\0';
X		sp = &inbuff[0] + strlen(text);	/* copy to static buff */
X		strcpy(findbuff, sp);
X		return(findbuff);		/* return adress of buff */
X       	}
X
X	curr += (off_t) strlen(inbuff);		/* update message offset */
X
X	if (all==FALSE && limit > 0L)		/* quit if past message */
X		if (curr >= limit) all = TRUE;
X  }
X  return(NULL);
X}
X
X
X/*
X * Check is the first line of the mailbox contains a line like
X *
X *	Forward to XXXX
X *
X * then all mail for the calling user is being forwarded
X * to user XXXX. Return a 1 value if this is the case.
X * Otherwise, return 0 (or -1 for error).
X */
Xint chk_box(void)
X{
X  char xbuf[128];
X  FILE *fp;
X  char *bp;
X
X  if (access(mailbox, 4) < 0 || 
X    (fp = fopen(mailbox, "r")) == (FILE *)NULL) {
X	if (usedrop && errno==ENOENT) return(-1);
X     	fprintf(stderr, "%s: cannot access mailbox ", progname);
X      	perror(mailbox);
X      	exit(1);
X  }
X
X  bp = fgets(xbuf, sizeof(xbuf), fp);
X  fclose(fp);
X
X  if (bp!=NULL && !strncmp(xbuf, "Forward to ", 11)) {
X	strcpy(forward, strrchr(xbuf, ' ') + 1);	/* get username */
X	forward[strlen(forward)-1] = '\0';		/* remove \n */
X	return(1);
X  }
X  return(0);
X}
X
X
X/*
X * Decode an old-style (V6/V7) mail header.
X * This is a line like:
X *
X *	From <user> <date> [remote from <host>]
X *
X * We want to find out the <user>, <date> and possible <remote> fields.
X */
Xstatic void old_hdr(let, text)
XLETTER *let;			/* which letter? */
Xchar *text;			/* message header text */
X{
X  register char *bp, *sp, *cp;
X  int i;
X
X  /* First, mark the end of the 'user' field. */
X  sp = text;
X
X  /* skip until <date> field */
X  while (*sp && *sp!=' ' && *sp!='\t') sp++;
X  *sp++ = '\0';			/* mark end, 'text' is now <user> */
X
X  /*
X   * SP now contains <date> and (possible) <remote> fields.
X   * Parse line to seek out "remote from".
X   */
X  cp = sp;			/* save the Date-pointer */
X  while (TRUE) {
X	bp = strchr(sp++, 'r');
X	if (bp != NULL) {	/* we found an 'r' */
X		if (!strncmp(bp, "remote from ", 12)) break;
X        } else break;
X  }
X
X  if (bp != NULL) {			/* host found --> remote mail */
X	sp = strrchr(bp, ' ');		/* start of "remote from" text */
X	*(bp - 1) = '\0';		/* mark end-of-date */
X	strcpy(let->date, cp);		/* set date */
X	i = strlen(++sp);		/* set length of hostname */
X  } else {
X	  strcpy(let->date, cp);	/* set date */
X	  i = 0;			/* no hostname! */
X  }
X  let->sender = (char *)malloc(strlen(text) + i + 5);
X  if (let->sender == NULL) {		/* no memory left! */
X	fprintf(stderr, "%s: out of memory.\n", progname);
X	exit(1);
X  }
X  if (i > 0) sprintf(let->sender, "%s!%s", sp, text);
X    else strcpy(let->sender, text);
X}
X
X
X/* 
X * Read the contents of the Mail-Box into memory.
X */
Xstatic int readbox(void)
X{
X  static char lbuff[512];
X  register LETTER *let;
X  register char *sp, *bp;
X  off_t current;
X 
X  firstlet = lastlet = NIL_LET;
X  numlet = 0;
X  seqno = 1;
X
X  if (chk_box() == 1) return(1);	/* mail is being forwarded... */
X
X  if ((boxfp = fopen(mailbox, "r")) == (FILE *)NULL) {
X	if (usedrop && errno==ENOENT) return(-1);
X      	fprintf(stderr, "%s: cannot access mailbox ", progname);
X      	perror(mailbox);
X      	exit(1);
X  }
X
X  /*
X   * Determine where all messages start.
X   * This should be done with an index file in the future!
X   */
X  current = (off_t) 0;
X  while(fgets(lbuff, sizeof(lbuff), boxfp) != NULL) {
X	current = ftell(boxfp);
X      	if (!strncmp(lbuff, "From ", 5)) {
X		if ((let = (LETTER *)malloc(sizeof(LETTER))) == NIL_LET) {
X			fprintf(stderr, "%s: out of memory.\n", progname);
X			exit(1);
X	 	}
X        	if (lastlet == NIL_LET) {
X			firstlet = let;
X			let->prev = NIL_LET;
X	 	} else {
X	 		let->prev = lastlet;
X	     	 	lastlet->next = let;
X	    	}
X		lastlet = let;
X		let->next = NIL_LET;
X
X		let->status = UNREAD;
X		let->location = current - (off_t) strlen(lbuff);
X		let->seqno = seqno++;
X		numlet++;
X	}
X  }
X
X  /*
X   * We now know where the messages are, read message headers.
X   */
X  let = firstlet;
X  while (let != NIL_LET) {
X	sp = find_string(let, "From ");		/* Find the "From_" field. */
X	if (sp == NULL) {
X		fprintf(stderr, "%s: no \"From\"-line.\n", progname);
X		exit(-1);
X	}
X	old_hdr(let, sp);			/* decode it */
X
X	/* Find the "Subject" field, if any... */
X	sp = find_string(let, "Subject: ");
X	if (sp == NULL) sp = "<none>";
X
X	/* ..and stuff it in memory. */
X	let->subject = (char *)malloc(strlen(sp) + 1);
X	if (let->subject == NULL) {		/* no memory left! */
X		fprintf(stderr, "%s: out of memory.\n", progname);
X		exit(1);
X	}
X	strcpy(let->subject, sp);
X
X	let = let->next;
X  }
X}
X
X
X/*
X * Check if there is any mail for the calling user.
X * Return 0 if there is mail, or 1 for NO MAIL.
X */
Xstatic int chk_mail(void)
X{
X  FILE *fp;
X  char temp[512];
X
X  if ((fp = fopen(mailbox, "r")) != (FILE *)NULL) {
X	if (fgets(temp, sizeof(temp), fp) == NULL) {
X		fclose(fp);	/* empty mailbox, no mail! */
X		return(1);
X	}
X      	if (!strncmp(temp, "Forward to ", 11)) {
X		fclose(fp);	/* FORWARD line in mailbox */
X		return(2);	/* so no mail. */
X	}
X	fclose(fp);	/* another line, so we have mail! */
X	return(0);
X  }
X  return(1);
X}
X
X
Xstatic void usage(void)
X{
X  fprintf(stderr, "Usage:\n");
X  fprintf(stderr, "\t%s [-epqrv] [-f file]\n", progname);
X  fprintf(stderr, "\t%s [-dtv] [-i file] [-s subject] user ...\n", progname);
X  fprintf(stderr, "\t%s [-lv] [-i file] user\n\n", progname);
X}
X
X
Xmain(argc, argv)
Xint argc; char *argv[];
X{
X  int c, st;
X
X  strcpy(sender, whoami());
X  old_uid = getuid();			/* get calling user and save */
X  old_gid = getgid();
X  setuid(geteuid());			/* set UID to ROOT (SU) */
X  setgid(getegid());			/* set GID to ROOT (SU) */
X
X  progname = basename(argv[0]);		/* how are we called? */
X  if (*progname == 'l') {
X	remote = FALSE;			/* 'lmail' link? */
X	loclink = TRUE;
X  }
X  strcpy(msg_temp, MAILTEMP);
X  mktemp(msg_temp);			/* name the temp file */
X  oldmask = umask(077);			/* change umask for security */
X  infp = stdin;				/* set INPUT to stdin */
X  
X  while ((c = getopt(argc, argv, "def:i:lpqrs:tv")) != EOF) switch(c) {
X	case 'd':	/* Deliver immediately. */
X		immediate++;
X		break;
X	case 'e':	/* Only check for mail, do not read it. */
X		checkonly++;
X		break;
X	case 'f':	/* use another mailbox. */
X		setuid(old_uid);
X		setgid(old_gid);
X		usedrop = FALSE;
X		strncpy(mailbox, optarg, PATHLEN - 1);
X		break;
X	case 'i':	/* Use another input-file. */
X		infp = fopen(optarg, "r");
X		if (infp == (FILE *)NULL) {
X			fprintf(stderr,
X				"%s: cannot open %s\n", progname, optarg);
X			exit(-1);
X		}
X		break;
X	case 'l':	/* Specify 'local' mail; same as 'lmail'. */
X		loclink = TRUE;
X		break;
X	case 'p':	/* Print all messages and exit. */
X		printmode++;
X		break;
X	case 'q':	/* Abort if SIGINT received. */
X		quitmode++;
X		break;
X	case 'r':	/* Show messages in reverse order. */
X		break;	/* This is only present for comp. with binmail! */
X	case 's':	/* Specify "subject" line. */
X		strcpy(subject, optarg);
X		break;
X	case 't':	/* Show all adressees in "To:" line. */
X		sayall++;
X		break;
X	case 'v':	/* Turn on debugging. */
X		verbose++;
X		break;
X	default:
X		usage();
X		exit(2);
X  }
X
X  if (optind >= argc) {
X	if (usedrop) sprintf(mailbox, DROPNAME, sender);
X
X	if (checkonly) {
X		st = chk_mail();
X		exit(st);
X	}
X
X	if ((st = readbox()) == 1) {
X		fprintf(stderr, "Your mail is being forwarded to %s.\n",
X								forward);
X		exit(1);
X	 } else {
X		 st = 0;
X	 	 if (printmode) printall();
X		   else interact();
X
X		 if (needupdate) updatebox();
X	}
X    } else st = deliver(argc - optind, argv + optind);
X
X  unlink(msg_temp);
X  exit(st);    
X}
+ END-OF-FILE wmmain.c
chmod 'u=rw,g=r,o=r' 'wmmain.c'
set `wc -c 'wmmain.c'`
count=$1
case $count in
11449)	:;;
*)	echo 'Bad character count in ''wmmain.c' >&2
		echo 'Count should be 11449' >&2
esac
echo Extracting 'wmread.c'
sed 's/^X//' > 'wmread.c' << '+ END-OF-FILE ''wmread.c'
X/*
X * WMAIL -	MicroWalt Extended Mail Agent.
X *		This is the MicroWalt Mail Agent; which is derived
X *		from  the  "Mini-Mail" written by Peter S. Housel.
X *		This version  has a better  user-interface, and it
X *		also "knows" about things like forwarding, replies
X *		etc. Overall, it looks like the Mail (uppercase M)
X *		on our local DYNIX(tm) (==BSD) system...
X *		The paging-code (for paging letters on the screen)
X *		was taken from "more.c", by Brandon Allbery.
X *
X *		R E A D    M A I L    M O D U L E
X *
X * Author:	Fred van Kempen, MicroWalt Corporation
X *
X * Revisions:
X *		11/07/89 FvK	Edited a little for the new MSS.
X *
X * To Do:
X *		- TERMCAP/TERMINFO use !
X */
X#include <stdio.h>
X#include <string.h>
X#include <sgtty.h>
X#include <unistd.h>
X#include "wmail.h"
X
X
Xstatic char mobuf[128];		/* output buffer */
Xstatic int mline;		/* current terminal line */
Xstatic int mcol;		/* current terminal column */
Xstatic int mobc;		/* position in output buffer (== chars in) */
Xstatic int misdone;		/* flag: return EOF next read even if not */
X
X
X/*
X * Write one line of text to the screen.
X * Return 1 if a Quit command was given.
X */
Xstatic int lwrite(fd, buf, len)
Xint fd;
Xchar *buf;
Xunsigned len;
X{
X  unsigned here, start;
X  int cmd;
X
X  start = 0;
X  here = 0;
X  while (here != len) {
X	cmd = '\0';
X      	switch (buf[here++]) {
X		case '\n':
X			mcol = 0;
X			if (++mline == 23) {
X				fflush(stdout);
X		     		write(1, buf + start, here - start);
X		     		fflush(stdout);
X		     		fprintf(stderr,
X					"\007\033[7m--More--\033[0m");
X		     		do {
X				    read(fd, &cmd, 1);
X       				} while (strchr(" \r\nqQ'nN", cmd)
X							== (char *)NULL);
X		        	fprintf(stderr, "\r\033[K");
X		        	mline = 0;
X		        	start = here;
X 	  		}
X			break;
X		case '\r':
X			mcol = 0;
X			break;
X		case '\b': 
X			if (mcol != 0) mcol--;
X			  else {
X				mline--;
X				mcol = 80 - 1;
X			}
X			break;
X		case '\t':
X			do {
X			    mcol++;
X			} while (mcol % 8 != 0);
X			break;
X		default:   
X			if (buf[here-1] < ' ' || (buf[here-1] & 0x80))
X		  					 buf[here-1] = '?';
X			if (++mcol == 80) {
X				mcol = 0;
X		     		if (++mline == 23) {
X		       			fflush(stdout);
X		       			write(1, buf + start, here - start);
X		       			fflush(stdout);
X		       			fprintf(stderr, 
X						"\007\033[7m--More--\033[0m");
X		       			do {
X			   		    read(fd, &cmd, 1);
X       			  		} while (strchr(" \r\nqQ'nN", cmd)
X							== (char *)NULL);
X		       			fprintf(stderr, "\r\033[K");
X		       			mline = 0;
X		       			start = here;
X		      		}
X		    	}
X	}
X      	switch (cmd) {
X		case '\0':
X			break;
X		case ' ':
X			mline = 0;
X		   	break;
X		case '\r':
X		case '\n':
X			mline = 23 - 1;
X		   	break;
X		case 'q':
X		case 'Q':
X			misdone = 1;
X		   	return(1);
X		case 'n':
X		case 'N':
X			misdone = 1;
X			return(1);
X		default:
X			break;
X	}
X  }
X  if (here != start) {
X	fflush(stdout);
X	write(1, buf + start, here - start);
X	fflush(stdout);
X  }
X}
X
X
X/*
X * Display the given letter on the screen.
X * Do this on a per-page basis...
X */
Xvoid showlet(let)
XLETTER *let;
X{
X  struct sgttyb ttymode;
X  off_t curr, limit;
X  int c, fd, st;
X
X  if ((fd = open("/dev/tty", 0)) == -1) {
X	fprintf(stderr, "%s: cannot open /dev/tty\n", progname);
X	return;
X  }
X  ioctl(fd, TIOCGETP, &ttymode);
X  ttymode.sg_flags |= CBREAK;
X  ttymode.sg_flags &= ~ECHO;
X  ioctl(fd, TIOCSETP, &ttymode);
X
X  fseek(boxfp, (curr = let->location), SEEK_SET);
X  limit = (let->next != NIL_LET) ? let->next->location : -1L;
X
X  st = mline = mcol = mobc = misdone = 0;
X  printf("Message %d:\n", let->seqno);
X  while(curr != limit && ((c = fgetc(boxfp)) != EOF) && st==0) {
X	if (mobc == 128) {
X		st = lwrite(fd, mobuf, 128);
X        	mobc = 0;
X       	}
X      	mobuf[mobc++] = (char) c;
X      	++curr;
X  }
X  if (st == 0) lwrite(fd, mobuf, mobc);
X  mobc = 0;
X  fflush(stdout);
X
X  ttymode.sg_flags &= ~CBREAK;
X  ttymode.sg_flags |= ECHO;
X  ioctl(fd, TIOCSETP, &ttymode);
X  close(fd);
X}
X
X
X/*
X * Print the contents of letter 'let' to file 'tofp'
X */
Xvoid printlet(let, tofp)
XLETTER *let;
XFILE *tofp;
X{
X  off_t curr, limit, oldpos;
X  int c;
X
X  if (tofp==stdout && !printmode) {
X	showlet(let);
X	return;
X  }
X
X  oldpos = ftell(boxfp);
X  fseek(boxfp, (curr = let->location), SEEK_SET);
X  limit = (let->next != NIL_LET) ? let->next->location : -1L;
X
X  if (tofp == stdout) printf("Message %d:\n", let->seqno);
X  while(curr != limit && (c = fgetc(boxfp)) != EOF) {
X	fputc(c, tofp);
X	++curr;
X  }
X  fflush(tofp);
X  fseek(boxfp, oldpos, SEEK_SET);
X}
X
X
X/*
X * Print all letters and quit.
X */
Xvoid printall(void)
X{
X  LETTER *let;
X
X  let = firstlet;
X  if (let == NIL_LET) {
X	fprintf(stderr, "No mail for %s.\n", sender);
X	return;
X  }
X
X  while(let != NIL_LET) {
X      	printlet(let, stdout);
X      	let = let->next;
X  }
X}
+ END-OF-FILE wmread.c
chmod 'u=rw,g=r,o=r' 'wmread.c'
set `wc -c 'wmread.c'`
count=$1
case $count in
4717)	:;;
*)	echo 'Bad character count in ''wmread.c' >&2
		echo 'Count should be 4717' >&2
esac
echo Extracting 'wmtime.c'
sed 's/^X//' > 'wmtime.c' << '+ END-OF-FILE ''wmtime.c'
X/*
X * XTIME-	Create ASCII string of the given time.
X *		This file contains a modified version of the ctime(3)
X *		function from the MINIX C library. The format of the
X *		new string is:
X *
X *			Tue, Nov  7 89 20:09:00\0
X *
X */
X#include <time.h>
X
X
Xstatic int days_per_month[] = {
X	31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
X};
Xstatic char *months[] = { 
X	"Jan", "Feb", "Mar", "Apr", "May", "Jun",
X	"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
X};
Xstatic char *days[] = {
X	"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
X};
X
X#define	MIN	60L		/* # seconds in a minute */
X#define	HOUR	(60 * MIN)	/* # seconds in an hour */
X#define	DAY	(24 * HOUR)	/* # seconds in a day */
X#define	YEAR	(365 * DAY)	/* # seconds in a year */
X
X
Xchar *xtime(void)
X{
X  static char xtmbuf[30];
X  struct tm tm;
X  long t, year;
X
X  time(&t);
X  tm.tm_year = 0;
X  tm.tm_mon = 0;
X  tm.tm_mday = 1;
X  tm.tm_hour = 0;
X  tm.tm_min = 0;
X  tm.tm_sec = 0;
X
X  /* t is elapsed time in seconds since Jan 1, 1970. */
X  tm.tm_wday = (int) (t/DAY + 4L) % 7;	/* Jan 1, 1970 is 4th wday */
X  while (t >= (year=((tm.tm_year%4)==2) ? YEAR+DAY : YEAR)) {
X	tm.tm_year += 1;
X	t -= year;
X  }
X  tm.tm_year += 1970;
X
X  /* t is now the offset into the current year, in seconds. */
X  tm.tm_yday = (t/DAY);		/* day # of the year, Jan 1 = 0 */
X
X  days_per_month[1] = 28;
X  if ((tm.tm_year % 4) == 0)	/* check for leap year */
X		days_per_month[1]++;
X
X  /* Compute month. */
X  while (t >= (days_per_month[tm.tm_mon] * DAY))
X		t -= days_per_month[tm.tm_mon++] * DAY;
X
X  /* Month established, now compute day of the month. */
X  while (t >= DAY) {
X	t -= DAY;
X	tm.tm_mday++;
X  }
X
X  /* Day established, now do hour. */
X  while (t >= HOUR) {
X	t -= HOUR;
X	tm.tm_hour++;
X  }
X
X  /* Hour established, now do minute. */
X  while (t >= MIN) {
X	t -= MIN;
X	tm.tm_min++;
X  }
X
X  /* Residual time is # seconds. */
X  tm.tm_sec = (int) t;
X
X  /* Generate output in ASCII in xtmbuf. */
X  sprintf(xtmbuf, "%s, %2.2d %s %2.2d %02d:%02d:%02d",
X	days[tm.tm_wday], tm.tm_mday, months[tm.tm_mon], 
X	    tm.tm_year - 1900, tm.tm_hour, tm.tm_min, tm.tm_sec); 
X  return(xtmbuf);
X}
+ END-OF-FILE wmtime.c
chmod 'u=rw,g=r,o=r' 'wmtime.c'
set `wc -c 'wmtime.c'`
count=$1
case $count in
2088)	:;;
*)	echo 'Bad character count in ''wmtime.c' >&2
		echo 'Count should be 2088' >&2
esac
exit 0
----------

hacker@isadora.ikp.liu.se (Goran Larsson [Hacker of Hackefors]) (11/10/89)

Wmail 2.6 (Posted for F. van Kempen; I haven't looked at it)
                                     ^^^^^^^^^^^^^^^^^^^^^^
I think you should have looked at it, because this program is loaded
with bugs and the author did not run it through lint or a spelling
checker before handing it over to you.

[1]
"adress" and "adressee" in source and doc. Use double "d" instead.

[2]
Some warnings like unsigned/signed clashes:
> fwrite, arg. 3 used inconsistently
> fwrite, arg. 3 used inconsistently
> fwrite, arg. 3 used inconsistently
> fwrite, arg. 3 used inconsistently
> lwrite, arg. 3 used inconsistently
> malloc, arg. 1 used inconsistently
> malloc value used inconsistently
> malloc, arg. 1 used inconsistently
> malloc value used inconsistently
> malloc value used inconsistently
> read, arg. 2 used inconsistently
> read, arg. 2 used inconsistently
> umask, arg. 1 used inconsistently

[3]
Global string "adressee" used instead of the argument "name"!
> "wmdeliver.c": warning: argument name unused in function send_remote

[4]
Some unused variables:
> "wmcreate.c": warning: bp unused in function edit_mail
> "wmdeliver.c": warning: c unused in function updatebox
> "wmdeliver.c": warning: c unused in function deliver
> "wminteract.c": warning: i unused in function interact
> "wminteract.c": warning: p unused in function interact
> "wmmain.c": warning: bp unused in function readbox

[5]
Some ugly bugs:
> "wmmain.c": warning: function readbox has return(e); and return;
> "wmread.c": warning: function lwrite has return(e); and return;

Both should have "return 0;" at the end.

[6]
"misdone" is set to 1 if the user types q, Q, n or N at the --More-- prompt,
but the code in "showlet()" never checks this. Thus it is impossible to
interrupt the printing of a mail with q, Q, n or N.

[7]
At the beginning of "interract()":
      	if (interrupted = TRUE) printf("\n");
This sets "interrupted" to true and then none of <NL>, +, t, or p works.
I don't know what this variable should do; if the "=" had been a "==", then
the code would have set "interrupted" at SIGINT and after that
none of <NL>, +, t, or p would have worked, WHY?

[8]
The first time the [1]& prompt displays, some commands does not work:

    [1]& p
    Message 1:			(empty!?!?)
    [1]& +			(does not work!)
    [1]& -			(correct)
    Top of Mailbox
    [1]& p
    Message 1:			(there it is!)
    From .....
    .....
    [1]&

[9]
Was this program ever tested?

  !       _
  ! !    Goran Larsson  [The Hacker of Hackefors]
--+-!    Hackefors, Linkoping, SWEDEN (See)  +46 13-155535 (Hear)
  !-+--  ...!uunet!sunic!liuida!prodix!isadora!hacker (UUCP)
  ! !    hacker@isadora.ikp.liu.se (Internet)
    !                                                    Mmh, Yes

ast@cs.vu.nl (Andy Tanenbaum) (11/11/89)

In article <527@isadora.ikp.liu.se> hacker@isadora.ikp.liu.se writes:
>Wmail 2.6 (Posted for F. van Kempen; I haven't looked at it)
>                                     ^^^^^^^^^^^^^^^^^^^^^^
>I think you should have looked at it, because this program is loaded
>with bugs and the author did not run it through lint or a spelling
>checker before handing it over to you.

I find this a difficult business.  I posted this for him because he has
email access and no news access.  If he had had news access, he would
have posted it himself, and you could flame him all you want for being
a lousy programmer, and I wouldn't be involved.  I suppose I could have
simply refused to post post on the grounds that I won't post anything
that I haven't checked out myself, but all that would have done is
prevented the net from having a mail program, something that there is
interest in.  I thought it was a reasonable compromise to post it with
the message saying although I was the guy who type Pnews, it wasn't
my code.

Thus, I accept responsibility for things I write myself, and if I blow
it, can rightly expect to get dumped on for it.  But I don't think it
is entirely fair to blame me for not verifying programs when I am only
posting them as a favor to someone else without news access.  
If the consensus of the group is that I should not post anything that
I cannot personally vouch for, I will just say point blank "no" to requests
for favors of this kind, since I don't have the time to test software unless
I happen to have a personal interest in it.  At the moment I have no
interest in any software that is not in P1003.

Andy Tanenbaum (ast@cs.vu.nl)

P.S.  I know Fred, and he is quite eager and ethusiastic.  He runs a big
bulletin board using MINIX.   He has made many changes to MINIX in the
area of getty, init, login, etc. to make MINIX more solid for use as a
public timesharing system.  I may incorporate some of these. 

sampson@attctc.Dallas.TX.US (Steve Sampson) (11/12/89)

Some people want all the work done for them.  Rather than fixing the Lint
bugs and reposting, they find it easier to throw acid on the thing...

If you think you got a good idea, don't like what's posted, fix it.  You
didn't get charged for it, so why complain.

nall@loligo.cc.fsu.edu (John Nall) (11/12/89)

In 4510@ast.cs.vu.nl Andy Tannenbaum writes:
>
>   If the consensus of the group is that I should not post anything
>   that I cannot personally vouch for, I will just say point blank "nO"
>   to requests for favors of this kind....

IMHO you should keep on doing such favors, Andy.  I think that the
message sufficiently warned that you had not tested the program.  It
is often useful to have stuff, even if it has bugs in it, just to see
some of the techniques used.

======================================================================
John Nall                              Internet:  nall@nu.cs.fsu.edu
Computer Science Department            Florida State University
    "Today, a Moon Moth -- tomorrow, a Sea Dragon Conquerer!!"