Xref: utzoo comp.protocols.nfs:751 comp.mail.misc:3049 Path: utzoo!attcan!uunet!ns-mx!iowasp!ceres!zaphod.mps.ohio-state.edu!wuarchive!texbell!texsun!newstop!east!hinode!geoff From: geoff@hinode.East.Sun.COM (Geoff Arnold @ Sun BOS - R.H. coast near the top) Newsgroups: comp.protocols.nfs,comp.mail.misc Subject: Re: PC-NFS Lifeline & 386i Message-ID: <1667@east.East.Sun.COM> Date: 8 Mar 90 12:49:42 GMT References: <570@cica.cica.indiana.edu> Sender: news@east.East.Sun.COM Reply-To: geoff@hinode.East.Sun.COM (Geoff Arnold @ Sun BOS - R.H. coast near the top) Followup-To: comp.protocols.nfs Organization: Sun Microsystems PC-NFS Engineering Lines: 740 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 X#include X#include X#include X#include X#include X#include X#include X#include X#include X#include 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. counts as 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)