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)