[comp.mail.misc] PC-NFS Lifeline & 386i

jwd@phil.indiana.edu (Jon Dunn) (03/08/90)

Does anyone have a copy of PC-NFS Lifeline's popd mail server
which runs on a Sun386i under SunOS 4.0.2?  I attempted to
fix the version of popd.c that came with Lifeline to look in users'
home directories for mail, but that didn't seem to do the job.
NFSMAIL still cannot read mail from the server, although it
can send mail fine.

I'd appreciate any help anyone could give me with this problem.
Thanks!

Jon Dunn
jwd@phil.indiana.edu

geoff@hinode.East.Sun.COM (Geoff Arnold @ Sun BOS - R.H. coast near the top) (03/08/90)

Quoth jwd@phil.indiana.edu (Jon Dunn) (in <570@cica.cica.indiana.edu>):
#
#Does anyone have a copy of PC-NFS Lifeline's popd mail server
#which runs on a Sun386i under SunOS 4.0.2?  I attempted to
#fix the version of popd.c that came with Lifeline to look in users'
#home directories for mail, but that didn't seem to do the job.
#NFSMAIL still cannot read mail from the server, although it
#can send mail fine.

Here you are:


#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create the files:
#	popd_386i.c
# This archive created: Thu Mar  8 07:52:03 1990
export PATH; PATH=/bin:$PATH
if test -f 'popd_386i.c'
then
	echo shar: will not over-write existing file "'popd_386i.c'"
else
sed 's/^X//' << \SHAR_EOF > 'popd_386i.c'
X/*
X * popd - Post Office Protocol server
X */
X/*
X * This server implements the POP2 protocol described in RFC937.
X * It should work with any system derived from Unix 4.2/4.3 BSD.
X * It adds two small features to the standard protocol:
X *
X * (1) a user may check for the existence of mail without providing
X *     a password. This speeds up the process considerably.
X * (2) the non-standard command "save" is supported. If a "save"
X *     command is issued, any message deleted from the user's mail
X *     spool file is saved in the "save" file (by default this is
X *     named by appending ".bak" to the spool file). 
X *
X * The following script may be used to "age" backup files. Cut the
X * text out of this file, remove the leading " * " from each line,modify
X * as desired, install in /etc or /usr/local, and arrange for it to
X * be executed periodically via "/crontab":
X *
X *
X * 
X * #!/bin/csh
X * #
X * #       /etc/flushmailbackup
X * #
X * # The following script is designed to flush out the backup mailboxes
X * # left around by "POP2" when running in "backup" mode. It assumes
X * # that it is probably sufficient to keep today's backups plus yesterday
X * # and the day before. It should be run out of "crontab" every night
X * # at around 2 AM. The current backup mailbox is "username.bak",
X * # yesterday's is "username.-1" and the day before's is "username.-2".
X * #
X * set tmpf = /tmp/flshmbkup$$
X * cd /usr/spool/mail
X * #
X * # remove any ".-2" files
X * #
X * rm -f $tmpf
X * find . -name \*.-2 -print > $tmpf
X * if(`cat $tmpf|wc -l` != 0) then
X *         rm `cat $tmpf`
X * endif
X * #
X * # rename any ".-1" files to be ".-2"
X * #
X * rm -f $tmpf
X * find . -name \*.-1 -print > $tmpf
X * if(`cat $tmpf|wc -l` != 0) then
X *         foreach i(`cat $tmpf`)
X *         mv $i `basename $i .-1`.-2
X *         end
X * endif
X * #
X * # rename any ".bak" files to be ".-1"
X * #
X * rm -f $tmpf
X * find . -name \*.bak -print > $tmpf
X * if(`cat $tmpf|wc -l` != 0) then
X *         foreach i(`cat $tmpf`)
X *         mv $i `basename $i .bak`.-1
X *         end
X * endif
X * rm -f $tmpf
X *
X * # END OF SCRIPT flushmailbackup
X */
X#include <sys/types.h>
X#include <sys/stat.h>
X#include <sys/file.h>
X#include <signal.h>
X#include <errno.h>
X#include <stdio.h>
X#include <netinet/in.h>
X#include <netdb.h>
X#include <sgtty.h>
X#include <sys/socket.h>
X#include <pwd.h>
X
X#define null 0
X#define helo 1
X#define fold 2
X#define raed 3
X#define retr 4
X#define acks 5
X#define ackd 6
X#define nack 7
X#define quit 8
X#define	save 9
X#define NKEYS 9
X
X#define NACK 0
X#define ACK 1
X
X#define DEFAULT_POP2_PORT	109 /* per the RFC */
X
Xstruct key_word {
X	char	*key;
X	int	val;
X	int	args;
X	} key, keytab[] = {
X		{ "null", null, 0 } ,
X		{ "helo", helo, 2 } ,
X		{ "fold", fold, 1 } ,
X		{ "read", raed, 1 } ,
X		{ "retr", retr, 0 } ,
X		{ "acks", acks, 0 } ,
X		{ "ackd", ackd, 0 } ,
X		{ "nack", nack, 0 } ,
X		{ "quit", quit, 0 } ,
X		{ "save", save, 1 } ,		/* Non standard */
X	};
X
Xint state_tab [4][NKEYS] = {
X	{1,5,5,5,5,5,5,4,5},
X	{5,1,2,5,5,5,5,4,1},
X	{5,1,2,3,5,5,5,4,1},
X	{5,5,5,5,2,2,2,4,5}
X	};
X
X/******************************************************************************
X                State Diagram corresponding to the state transition table
X                represented by the variable state_tab[][]
X
X                             |
X                             | helo 
X                             |
X                            \|/
X                       --------------      quit           -----------
X              ------->|      1       |-----------------> |     4     |
X       fold  |         --------------                     -----------
X    or save  |         |   |      /|\                         /|\
X              ---------    |       |                           |
X                           |       |                           |
X                      read |       | fold or save              |
X                          \|/      |                           |
X                       --------------      quit                |
X              ------->|      2       |-------------------------
X       read  |         --------------                          |
X             |         |   |      /|\                          |
X              ---------    |       | ack(s/d)                  |
X                      retr |       |                           |
X                          \|/      |                           |
X                       --------------      quit                |
X                      |      3       |-------------------------
X                       --------------           
X                                                 ---------
X                                                |    5    |
X                                                |  error  |
X                                                 ---------
X*******************************************************************************/ 
X        
Xint	myargc, cur_msg, msg_cnt, cur_msg_len;
Xchar	myargv[4][30];
Xint	cur_state = 0;
Xint	debug = 0;
Xstruct	passwd *pwd;
X#define	MAXMSGS	1000
Xchar	mailbox[128];
X#ifdef SUN386I
X#define	MAILDIR "/var/spool/mail/"
X#else
X#define	MAILDIR "/usr/spool/mail/"
X#endif
Xchar	savebox[128];
X#define	SAVEFILE ".bak"
Xint	savefd = -1;
Xchar	tmplate[] = "/tmp/popXXXXX";
Xchar	mailtmp[16];
XFILE	*ftmp, *fmail;
Xint	nmsg, nlines;
Xint	opened;
X
Xstruct	sockaddr_in server_name;
X
Xint fd_stdin;
X
Xextern int errno;
X
Xstruct message {
X	long	headp;
X	int	deleted;
X	int	lines;
X};
X
Xstruct message msg[MAXMSGS+2];
X	
Xint result, flags;
Xint oursock, hissock;
Xchar	line[BUFSIZ];
X
Xmain ()
X{
X	struct servent *sp;
X	int s;
X
X	strcpy (line, "+ POP2 Unix Server on ");
X	gethostname(&line[strlen(line)], 1024-strlen(line));
X	strcat (line, "\r\n");
X
X	if (debug) setbuf (stdout, NULL);
X
X	/* find pop's socket number */
X	if (((sp = getservbyname("pop", "tcp")) == NULL) &&
X	  ((sp = getservbyname("pop2", "tcp")) == NULL)) {
X		server_name.sin_port = htons(DEFAULT_POP2_PORT);
X	} else
X		server_name.sin_port = sp->s_port;
X	
X	if (debug) server_name.sin_port = htons(1025);
X	else {
X		if (fork())
X			exit(0);
X		if ((s = open("/dev/tty", 2)) > 0) {
X			ioctl(s, TIOCNOTTY, 0);
X			close(s);
X		}
X	}
X	
X	/* now create socket for receives */
Xagain:
X	oursock = socket(AF_INET, SOCK_STREAM, 0, 0);
X	if (oursock < 0) {
X		perror("popd: socket");;
X		sleep(5);
X		goto again;
X	}
X	
X	/* name us as the pop server */
X	while (bind(oursock,
X		(caddr_t)&server_name, sizeof (server_name), 0) < 0) {
X		perror("pop: bind");
X		sleep(5);
X	}
X	
X	/* allow connections... */
X	listen(oursock, 10);
X	for (;;) {
X	    hissock = accept (oursock, 0, 0);
X	    if (!fork()) {
X		if (fork() == 0) exit(do_pop());
X		else exit(0);
X	    }
X	    else {
X		wait(0); /* collect zombie */
X		close(hissock);
X	    }
X	}
X}
X
X/* here to do all the real protocol work... */
X/* assumes that socket is already open and we're about to prompt */
Xdo_pop ()
X{
X    if (debug) printf ("(%d) starting fork\n", hissock);
X    /* provide initial prompt */
X    net_out(line);
X    
X    /* now get right down to business */
X    while ((result = parse()) > 0) ;
X    close (hissock);
X    if (debug) printf("(%d) closing...\n", hissock);
X    return (result);
X}
X
X/* Main parser - guides us through states and checks protocol compliance */
Xparse ()
X{
X	int token, next_state, i, slen;
X	token = lexical();
X	if (debug) printf ("(%d)token: %d\n", hissock, token);
X	if (token < 0) return (-1);
X	next_state = state_tab [cur_state] [token-1];
X	if (debug) printf ("(%d)next state: %d\n", hissock, next_state);
X	
X	/* things to do when we enter a state */
X	switch (next_state) {
X		/* about to open folder */
X		case 1:
X			switch (token) {
X			case helo:
X				strcpy(mailbox, MAILDIR);
X				strcat(mailbox, myargv[1]);
X#ifdef SUN386I
X				setupmailbox(myargv[1]); /* may overwrite */
X#endif
X				if (myargc == 2 && strlen(myargv[2])) {
X				    if (check_user(myargv[1], myargv[2])) 
X					return (-1);
X				    msg_cnt = openit(mailbox);
X				} else
X				    msg_cnt = checkit(mailbox);
X				sprintf (line, "#%d\r\n",msg_cnt);
X				net_out (line);
X				break;
X			case fold:
X				strcpy (mailbox, myargv[1]);
X				msg_cnt = openit(mailbox);
X				sprintf (line, "#%d\r\n",msg_cnt);
X				net_out (line);
X				break;
X			case save:
X				if (myargc == 1 && strlen(myargv[1]))
X					strcpy (savebox, myargv[1]);
X				else {
X					strcpy (savebox, mailbox);
X					strcat (savebox, SAVEFILE);
X				}
X				if (openbk(savebox)) sprintf (line,
X					"+ OK, saving to %s\r\n", savebox);
X				else sprintf (line, 
X					"- Can't access file %s\r\n", savebox);
X				net_out (line);
X				break;
X			default:
X				net_out ("- unexpected...\r\n");
X				break;
X			}
X
X			if (debug) printf("(%d) mailbox '%s'\n", hissock,
X						mailbox);
X			break;
X
X		case 2:	if (!opened) {
X				net_out ("- Error...no open mailbox\r\n");
X				return(-1);
X			}
X			switch (token) {
X				case raed: 	if (myargc != 0) {
X						    cur_msg = atoi (myargv[1]);
X						}
X						if ( cur_msg < 1 ) {
X					        net_out ("- ill msg num\r\n");
X						    return (-1);
X						}
X						break;
X				case ackd:	msg[cur_msg].deleted++;
X				case acks:	cur_msg++;
X						break;
X				default:	null;
X			}
X			
X			cur_msg_len = msglen (cur_msg);
X			sprintf (line, "=%d\r\n",cur_msg_len);
X			net_out (line);
X			break;
X		case 3: if (!cur_msg_len) {
X				net_out ("- zero length message\r\n");
X				return (-1);
X			}
X			outmsg (cur_msg);
X			break;
X		case 4:	net_out ("+ OK Exiting...\r\n");
X			closeit ();
X			return (0);
X		case 5:	net_out ("- Syntax Error...\r\n");
X			return (-1);
X		/* nothing else ever expected... */
X		default: break;
X	}
X	cur_state = next_state;
X	return (1);
X    }
X
X/*
X * Check userid and password
X */
Xcheck_user (user, passwd)
Xchar *user, *passwd;
X{
X	char *namep, *crypt();
X	struct passwd *getpwnam();
X
X	pwd = getpwnam(user);
X	if (debug) printf ("(%d): checking user info\n", hissock);
X	if (!pwd) {
X		net_out ("- logon incorrect\r\n");
X		return (-1);
X	}
X	namep = crypt(passwd, pwd->pw_passwd);
X	if (strcmp(namep, pwd->pw_passwd)) {
X		net_out ("- logon incorrect\r\n");
X		return (-1);
X	}
X
X	/* got valid user here, become him... */
X	if (debug) printf ("(%d): logon ok, setting UID\n", hissock);
X	setgid (pwd->pw_gid);
X	initgroups(user, pwd->pw_gid);
X	setuid (pwd->pw_uid);
X	if (debug) printf ("(%d): logged on and ready\n", hissock);
X	/* make filenames relative to home directory */
X	chdir (pwd->pw_dir);
X}
X
X
X/* perform lexical analysis on incomming stuff */
Xlexical ()
X{
X	int result = 0;
X	char inline[100];	/* main input comes here */
X	int i;
X
X	for (i = 0; i < 3; i++) myargv[i][0] = '\0';
X	myargc = -1;
X	while (myargc < 0) {
X		if (my_gets (inline) < 0) return (-1);
X		if (debug) printf ("(%d)saw: %s\n",hissock, inline );
X		myargc = sscanf (inline, "%s%s%s",myargv[0],myargv[1],
X						    myargv[2]);
X		if (debug) printf ("(%d)scan: %d args\n", hissock, myargc);
X		myargc--;
X	}
X	key = keytab[whatkey(myargv[0])];
X	if (debug) printf ("(%d)key: %d\n", hissock, key.val);
X	if (key.val == null) {
X			sprintf (line, "- Invalid command '%s'\r\n", myargv[0]);
X			net_out(line);
X			return (-1);
X		}
X	if (key.args && ((key.args - myargc) > 1)) {
X		sprintf (line, "- wna for '%s'\r\n", myargv[0]);
X		net_out(line);
X		return (-1);
X	}
X	return (key.val);
X}
X
X
X/* Now determine which keyword we've got... */
Xwhatkey (s)
X  char s[4];
X  {
X	int i;
X  /* First convert input to lower case... */
X	for (i = 0; i < 4; i++) {
X		s[i] = (('A' <= s[i]) && ('Z' >= s[i])) ?
X			    s[i] + 040 : s[i];
X		}
X
X	for (i = NKEYS; i > 0; i--) {
X		if (!strncmp (keytab[i].key, s, 4)) return (keytab[i].val);
X		}
X		
X	return (null);
X}		
X
X/*
X * check the mailbox for existence of mail.
X */
Xcheckit(mailbox)
Xchar *mailbox;
X{
X	struct stat statb;
X
X	if (stat(mailbox, &statb) == 0 && statb.st_size > 0) return(1);
X	return(0);
X}
X
X/*
X * open the specified mailbox
X */
Xopenit(mailbox)
Xchar *mailbox;
X{
X	struct stat statb;
X
X	if (opened) closeit();
X	nmsg = 0; nlines = 0;
X	strcpy(mailtmp, tmplate);
X	mktemp(mailtmp);
X	unlink(mailtmp);
X	if ((ftmp = fopen(mailtmp, "w")) == NULL) {
X		if (debug) printf("Can't open temp file %s\n", mailtmp);
X		return(0);
X	}
X
X	if ((fmail = fopen(mailbox, "r")) == NULL) {
X		if (debug) printf("Can't open mailbox %s\n", mailbox);
X		fclose(ftmp); unlink(mailtmp);
X		return(0);
X	}
X	if (fstat(fileno(fmail), &statb) != 0 || statb.st_size == 0) {
X		if (debug) printf("mail file empty\n");
X		fclose(ftmp); unlink(mailtmp);
X		fclose(fmail);
X		return(0);
X	}
X	
X	flock(fileno(fmail), LOCK_EX);
X	while (fgets(line, BUFSIZ, fmail) != NULL) {
X		if (strncmp("From ", line, 5) == 0) {
X			msg[nmsg++].lines = nlines;
Xif (debug) printf("msg[%d].lines = %d\n", nmsg-1,msg[nmsg-1].lines);
X			nlines = 0;
X			msg[nmsg].headp = ftell(fmail) - strlen(line);
Xif (debug) printf("msg[%d].headp = %d\n", nmsg, msg[nmsg].headp);
X		} else nlines++;
X		fputs(line, ftmp);
X	}
X	msg[nmsg].lines = nlines;
Xif (debug) printf("msg[%d].lines = %d\n", nmsg,msg[nmsg].lines);
X	msg[nmsg+1].headp = ftell(fmail);
Xif (debug) printf("msg[%d].headp = %d\n", nmsg+1, msg[nmsg+1].headp);
X	flock(fileno(fmail), LOCK_UN);
X	fclose(fmail);
X	fclose(ftmp);
X	ftmp = fopen(mailtmp, "r");
X	cur_msg = 1;
X
X	opened++;
X	return (nmsg);
X}
X
Xcloseit()
X{
X
X	struct stat statb;
X	int changed = 0;
X	int nlines;
X	int f, i, j;
X
X	if (!opened) return;
X	for (i = 1; i <= nmsg; i++) 
X		if (msg[i].deleted) {
X			changed++;
X			break;
X		}
X	if (!changed) {
X		fclose(fmail);
X		fclose(ftmp);
X		unlink(mailtmp);
X		opened = 0;
X		return;
X	}
X
X	signal(SIGINT, SIG_IGN);
X	signal(SIGHUP, SIG_IGN);
X	signal(SIGQUIT, SIG_IGN);
X	f = open(mailbox, 0);
X	flock(f, LOCK_EX);
X	fstat(f, &statb);
X	if (statb.st_size != msg[nmsg+1].headp) {
X		/*
X		 * there is new mail
X		 */
X		if ((fmail = fopen(mailbox, "r")) == NULL) return;
X		fseek(fmail, msg[nmsg+1].headp, 0);
X		fclose(ftmp);
X		ftmp = fopen(mailtmp, "a");
X		fseek(ftmp, msg[nmsg+1].headp, 0);
X		nlines = 0;
X		while(fgets(line, BUFSIZ, fmail) != NULL) {
X			nlines++;
X			fputs(line, ftmp);
X		}
X		nmsg++;
X		msg[nmsg].lines = nlines - 1;
X		msg[nmsg+1].headp = ftell(ftmp);
X		fclose(fmail);
X		fclose(ftmp);
X		ftmp = fopen(mailtmp, "r");
X	}
X	if ((fmail = fopen(mailbox, "w")) != NULL) {
X		if (savefd >= 0) flock(savefd, LOCK_EX);
X		for (i = 1; i <= nmsg; i++) {
X			if (!msg[i].deleted || savefd >= 0) {
X				fseek(ftmp, msg[i].headp, 0);
X				for (j = 0; j <= msg[i].lines; j++) {
X					fgets(line, BUFSIZ, ftmp);
X					if (msg[i].deleted) 
X					    write(savefd, line, strlen(line));
X					else fputs(line, fmail);
X				}
X			}
X		}
X		if (savefd >= 0) flock(savefd, LOCK_SH);
X		fclose(fmail);
X	}
X	flock(f, LOCK_UN);
X	close(f);
X	fclose(ftmp);
X	unlink(mailtmp);
X	opened = 0;
X}
X
X
X/* 
X * Compute a message's length. <lf> counts as <cr><lf>
X */
Xmsglen (n)
X{
X	struct message *m = &msg[n];
X	int len;
X
X	if (n > nmsg) return(0);
X	len = msg[n+1].headp - m->headp;
X	fseek(ftmp, m->headp, 0);
X	fgets(line, BUFSIZ, ftmp);
X	len -= strlen(line);
X	len += m->lines;
X	return (len);
X}
X
X/* 
X * Output msg to primary, convert to net standard ascii.
X * We are already positioned at the first line.
X */
Xoutmsg (n)
X{
X	int i;
X	char *s;
X
X	for (i = 0; i < msg[n].lines; i++) {
X		s = fgets(line, BUFSIZ, ftmp);
X		s += (strlen(line) - 1);
X		strcpy(s, "\r\n");
X		net_out(line);
X	}
X}	
X
X/* write a string to the net */
Xnet_out (s)
Xchar *s;
X{
X	write (hissock, s, strlen(s));
X	if (debug) printf ("(%d) sent: %s\n", hissock, s);
X}
X
X/* something to read an entire line from the network */
Xmy_gets (s)
Xchar *s;
X{
X    int i = 0;
X    char c;
X
X    while ((c = my_getc()) != '\n') {
X	if (c == EOF) return (-1);
X    	if (c != '\r') s[i++] = c;
X    }
X    s[i] = 0;
X    if (debug) printf ("(%d) rcvd: %s\n", hissock, s);
X    return (0);
X}
X
Xchar	ibuf[1024];
Xint	inptr, ilen;
X/* get one character of input from the network... */
Xmy_getc ()
X{
X    for (;;) {
X	if (inptr < ilen) return (ibuf[inptr++]);
X	ilen = read(hissock, ibuf, 1024);
X	inptr = 0;
X	if (ilen == 0) return (-1);
X    }
X}
X
X/*
X * open a backup file for deleted messages
X */
Xopenbk(savefile)
Xchar *savefile;
X{
X	if (savefd >= 0) close(savefd);
X	if ((savefd = open(savefile, O_CREAT|O_WRONLY|O_APPEND, 0600)) < 0) {
X		if (debug) printf("Can't access file %s\n", savefile);
X		return(0);
X	}
X	flock(savefd, LOCK_SH);
X	return (1);
X}
X
X
X#ifdef SUN386I
X
Xsetupmailbox(user)
Xchar *user;
X{
X    static char *ypdomain;
X    static char policyname [] = "mail_delivery";
X    static char policyvalue [] = "spool_area";
X    char *homeinfo;
X    int homeinfolen;
X
X   if (!ypdomain && yp_get_default_domain (&ypdomain) != 0) {
X        return; /* without disturbing mailbox */
X    }
X        /* For compatibility with older networks, let mail be delivered
X         * outside of home directories.  MASSIVELY DISCOURAGED.
X         */
X    if (yp_match (ypdomain, "policies",
X            policyname, sizeof policyname - 1,
X            &homeinfo, &homeinfolen) == 0) {
X        homeinfo [homeinfolen] = '\0';
X        (void) free (homeinfo);
X        if (strcmp (homeinfo, policyvalue) == 0) {
X             return; /* without disturbing mailbox */
X        }
X    }    
X
X/* XXX ought to check auto.home here */
X
X	sprintf(mailbox, "/home/%s/mail/inbox", user);
X}
X#endif
SHAR_EOF
fi # end of overwriting check
#	End of shell archive
exit 0

Geoff Arnold, PC-NFS architect, Sun Microsystems. (geoff@East.Sun.COM)
-------------------
The Bible is not my Book and Christianity is not my religion. I could never give
assent to the long complicated statements of Christian dogma." (Abraham Lincoln)