[comp.mail.mh] POP for mh-6.6.

jepeway@utkcs2.cs.utk.edu (Chris Jepeway) (10/20/88)

I'm trying to get POP running under MH v6r6.
The POP server is a Vax 8200 running Ultrix 2.2,
and the clients are Sun 3/60's and 3/260's
running Sun-OS 3.5.  The trouble I'm having
is that the POP server on the Vax thinks
the entire contents of /var/spool/mail/whoever
is a single message.  Here's the Vax's mtstailor file--

mmdfldir:	/var/spool/mail
servers:	localhost \01localnet
mmdelim1:\nFrom\ 
mmdelim2:\nFrom\ 

The characters after the final slashes on the mmdelim
lines are spaces.  I can't figure what's going on,
but trust that someone else has a solution.

Thanks Ever,
C. Jepeway

dempsey@handel.colostate.edu. (Steve Dempsey) (10/25/88)

In article <598@utkcs2.cs.utk.edu> jepeway@utkcs2.cs.utk.edu (Chris Jepeway) writes:
>I'm trying to get POP running under MH v6r6.
>The POP server is a Vax 8200 running Ultrix 2.2,
>and the clients are Sun 3/60's and 3/260's
>running Sun-OS 3.5.  The trouble I'm having
>is that the POP server on the Vax thinks
>the entire contents of /var/spool/mail/whoever
>is a single message.  Here's the Vax's mtstailor file--
>
>mmdfldir:	/var/spool/mail
>servers:	localhost \01localnet
>mmdelim1:\nFrom\ 
>mmdelim2:\nFrom\ 

The obvious answer!  It really *SHOULD* work this way, right?

>
>The characters after the final slashes on the mmdelim
>lines are spaces.  I can't figure what's going on,
>but trust that someone else has a solution.
>
>Thanks Ever,
>C. Jepeway

[I've been rather hesitant to post a rogue patch to such a large and popular
software package, but figure there must be MANY (hundreds of, even) people
out there who could *really* use this (!) ]

The above configuration would seem to be technically correct.  The problem
with MH is that the POP server is normally used to handle mailboxes created
by the POP delivery agent (mail comes in addressed to user@pop).  This is when
those 'mmdelim'' strings are inserted into the maildrop file.  The normal V7
mailbox format says that new messages begin with a line that looks like:

   From sender[@wherever] FORMATTED_DATE\n

Unfortunately, the part of the POP server that reads maildrops is not set
up to handle this.  Lines are read from the file using fgets(3S), which
will never be able to deal with a line that looks like:

   [anything or NULL]\n[data][anything else to be ignores]
   
as was attempted above.  The newline not being at the end is the kicker.
Unless you know how simple-minded the maildrop reader is, it is not ovbious
why `mmdelim: \nFrom ' fails.

I cannot imagine that nobody ever thought about using POP this way before!
When I discovered MH, it was the *instant* solution to all our mail problems.
Or so I thought.  This odd behavior of the POP server was the only flaw, and
I set out to fix it immediately.

Since we have sources, I was able to discover how the old 4.[23]BSD mailer
handled the message delimiter.  The solution I came up with involved
duplication of a few of the MH library routines with minor changes, and
borrowing some of the BSD code to detect the beginning of a new message.

Because the problem is almost trivial, and the borrowed code is nothing
fancy, I decided that I would just grab the parts I needed to make the POP
server work.  If I had not been lazy, the necessary code could have been
implemented in about 10 lines of C, but I thought I might as well do it the
right way, and use some robust(?) code.  The following is a context diff
between the new and old versions of .../mh-6.6/support/pop/popser.c.

Part of the change is almost a duplication of mbx_read() routine, and the rest
is to parse the beginning of a new message.  With this patch, the postoffice
(POP server) is able to read /usr/spool/mail/whoever properly with no other
changes to the delivery agent or any other part of MH.

We've been using this for several weeks without a hitch.  It's really been
the answer to our mail problems.  Before MH, things were VERY difficult, and
now mail is a breeze.  Just for the record, I've included the options I used
on the server and one of the clients.

I've no no experience with generating patches, so please excuse the raw diff.
It should not be difficult to install, but if you have trouble, I can mail
out the entire file to those who request it.

P.S  I also have managed to get a client version of MH6.6 running under
     HPUX V5.17.  This also is a relatively small patch that I'd be willing
     to mail to interested parties.  The official update will be submitted
     when I find the time to really clean it up .... some day.
-------------------------------------------------------------------------------
 /\             \Steve Dempsey,  Center For   \steved@longs.LANCE.ColoState.Edu
 \/ _|/ _       _\Computer Assisted Engineering\dempsey@handel.CS.ColoState.Edu
 /\  | (_) | |_(_)\Colorado State University    \...!ncar!handel!dempsey
/_/_/(_/\_/ V   \_ \Fort Collins, CO  80523      \(303)-491-0630

------------------------------- Options ---------------------------------------

Server is a VAX780 running 4.3XINU [BSD+NFS].
Client is a VS2000 running Ultrix2.2, both use same options.

version: MH 6.6 #1[UCI] (ics) of Tue May 24 15:51:53 PDT 1988
options: [BSD42] [BSD43] [BERK] [TTYD] [DUMB] [MHE] [NETWORK] [BIND]
         [RPATHS] [ATZ] [SBACKUP='"#"'] [DPOP] [MHRC] [RPOP] [OVERHEAD]
         [WHATNOW] [SENDMTS] [SMTP] [POP] [BPOP]

-------------------------------- Diffs ----------------------------------------

<-  popser.c
 -> popser.c.orig

646c646
<     if ((i = pmbx_read (dp, pos, &rp, debug)) <= 0)
---
>     if ((i = mbx_read (dp, pos, &rp, debug)) <= 0)
1359,1762d1358
< 
< /* dropsbr.c - read from a mailbox - pop server version */
< 
< /* ALMOST IDENTICAL to mbx_read */
< 
< int	pmbx_read (fp, pos, drops, noisy)
< register FILE  *fp;
< register long	pos;
< struct drop **drops;
< int	noisy;
< {
<     register int    len,
<                     size;
<     long    ld1,
<             ld2;
<     register char  *bp;
<     char    buffer[BUFSIZ];
<     register struct drop   *cp,
<                            *dp,
<                            *ep,
<                            *pp;
< 
<     /* get drop storage */
<     pp = (struct drop  *) calloc ((unsigned) (len = MAXFOLDER), sizeof *dp);
< 
<     if (debug)
< 	padvise (NULLCP, LOG_DEBUG, "pmbx_read (%d, %ld, %d, %d)",
< 		fp, pos,drops,noisy);
< 
<     if (pp == NULL) {
< 	if (noisy)
< 	    admonish (NULLCP, "unable to allocate drop storage");
< 	return NOTOK;
<     }
< 
<     /* get sizes of msg delimiters */
<     ld1 = (long) strlen (mmdlm1);
<     ld2 = (long) strlen (mmdlm2);
< 
<     /* rewind drop file */
<     (void) fseek (fp, pos, 0);
< 
<     if (debug)
< 	padvise (NULLCP, LOG_DEBUG, "rewind maildrop");
< 
<     /* read a buffer */
<     for (ep = (dp = pp) + len - 1; fgets (buffer, sizeof buffer, fp);) {
< 	size = 0;
< 
< 	/* if beginning of msg then mark it */
< 
< 	if (p_ishead(buffer)) /*(strcmp (buffer, mmdlm1) == 0)*/ {
< 	    /* (don't) inc pos to msg start, mark it */
< 	    /*pos += ld1;*/
< 	    dp -> d_start = pos;
< 	    pos += strlen(buffer);  /* inc pos after marking head */
< 	}
< 	else {
< 	    /* didn't find it; mark it anyway */
< 	    dp -> d_start = pos, pos += (long) strlen (buffer);
< 
< 	    /* count newlines and inc size if any found */
< 	    for (bp = buffer; *bp; bp++, size++)
< 		if (*bp == '\n')
< 		    size++;
< 	}
< 
< 	/* read more lines... */
< 	while (fgets (buffer, sizeof buffer, fp) != NULL)
< 
< 	    /* found end? */
< 	    if (p_ishead(buffer)) /*(strcmp (buffer, mmdlm2) == 0)*/ {
< 
< 		/* out of loop */
< 	        (void) fseek (fp, pos, 0);
< 		break;
< 
<             }
< 	    else {
< 		/* add buffer size to pos */
< 		pos += (long) strlen (buffer);
< 
< 		/* count newlines.... */
< 		for (bp = buffer; *bp; bp++, size++)
< 		    if (*bp == '\n')
< 			size++;
< 	    }
< 
< 	if (dp -> d_start != pos) {
< 	    /* do this if pos was actually incremented; got some text */
< 	    dp -> d_id = 0;
< 	    dp -> d_size = size;  /* save the stuff we got */
< 	    dp -> d_stop = pos;
< 	    dp++;
< 	}
< 
< 	/* (don't) advance pos */
< 	/* pos += ld2; */
< 
< 	/* need more storage.... */
< 	if (dp >= ep) {
< 	    register int    curlen = dp - pp;
< 
< 	    cp = (struct drop  *) realloc ((char *) pp,
< 		                    (unsigned) (len += MAXFOLDER) * sizeof *pp);
< 	    if (cp == NULL) {
< 		if (noisy)
< 		    admonish (NULLCP, "unable to allocate drop storage");
< 		free ((char *) pp);
< 		return 0;
< 	    }
< 	    dp = cp + curlen, ep = (pp = cp) + len - 1;
< 	}
<     }
< 
<     /* return unused stuff */
<     if (dp == pp)
< 	free ((char *) pp);
<     else
< 	*drops = pp;
<     return (dp - pp);
< }
< 
< /*
<  * The remainder of this file adapted from:
<  *
<  *	head.c	5.2 (Berkeley) 6/21/85
<  */
< 
< struct p_hdline {
< 	char	*l_from;	/* The name of the sender */
< 	char	*l_tty;		/* His tty string (if any) */
< 	char	*l_date;	/* The entire date string */
< };
< 
< /*
<  *
<  * See if position in a file is a mail header.
<  * Return true if yes.  Note the extreme pains to
<  * accommodate all funny formats.
<  */
< 
< #define	NOSTR		((char *) 0)	/* Null string pointer */
< 
< p_ishead(buffer)
< char buffer[];
< {
< register char *cp;
< struct p_hdline hl;
< char linebuf[BUFSIZ];
< char parbuf[BUFSIZ];
< 
< char * p_copyin();
< char * p_copy();
< 
< 	strcpy(linebuf,buffer);
< 	cp = linebuf;
< 
< 	if (linebuf[0]=='F')
< 		  padvise (NULLCP, LOG_DEBUG, "ishead: '%s'",linebuf);
< 
< 	if (strncmp("From ", cp, 5) != 0)
< 		return(0);
< 
< 	padvise (NULLCP, LOG_DEBUG, "Fromline...");
< 
< 	/* get full header */
< 	p_parse(cp, &hl, parbuf);
< 
< 	if (hl.l_from == NOSTR || hl.l_date ==  NOSTR) {
< 		  padvise (NULLCP, LOG_DEBUG, "Fromline...NODATE");
< 		return(0);
< 		}
< 
< 	if (!p_isdate(hl.l_date)) {
< 		  padvise (NULLCP, LOG_DEBUG, "Fromline...BADDATE %s",
< 			hl.l_date);
< 		return(0);
< 		}
< 
< 	/* I guess we got it! */
< 	padvise (NULLCP, LOG_DEBUG, "got a head.. ");
< 
< 	return(1);
< }
< 
< /*
<  * Split a headline into its useful components.
<  * Copy the line into dynamic string space, then set
<  * pointers into the copied line in the passed headline
<  * structure.  Actually, it scans.
<  */
< 
< p_parse(line, hl, pbuf)
< 	char line[], pbuf[];
< 	struct p_hdline *hl;
< {
< 	register char *cp, *dp;
< 	char *sp;
< 	char word[BUFSIZ];
< 	char * p_nextword();
< 
< 	hl->l_from = NOSTR;
< 	hl->l_tty = NOSTR;
< 	hl->l_date = NOSTR;
< 	cp = line;
< 	sp = pbuf;
< 
< 	/*
< 	 * Skip the first "word" of the line, which should be "From"
< 	 * anyway.
< 	 */
< 	cp = p_nextword(cp, word);
< 	dp = p_nextword(cp, word);
< 	if (!(strcmp(word, "")==0))
< 		hl->l_from = p_copyin(word, &sp);
< 
< 	/* UNLIKELY */
< 	if (strncmp(dp, "tty", 3) == 0) {
< 		cp = p_nextword(dp, word);
< 		hl->l_tty = p_copyin(word, &sp);
< 		if (cp != NOSTR)
< 			hl->l_date = p_copyin(cp, &sp);
< 	}
< 
< 	/* USUAL */
< 	else
< 		if (dp != NOSTR)
< 			hl->l_date = p_copyin(dp, &sp);
< }
< 
< /*
<  * Copy the string on the left into the string on the right
<  * and bump the right (reference) string pointer by the length.
<  * Thus, dynamically allocate space in the right string, copying
<  * the left string into it.
<  */
< 
< char *
< p_copyin(src, space)
< 	char src[];
< 	char **space;
< {
< 	register char *cp, *top;
< 	register int s;
< 
< 	s = strlen(src);
< 	cp = *space;
< 	top = cp;
< 	strcpy(cp, src);
< 	cp += s + 1;
< 	*space = cp;
< 	return(top);
< }
< 
< /*
<  * Collect a liberal (space, tab delimited) word into the word buffer
<  * passed.  Also, return a pointer to the next word following that,
<  * or (empty) if none follow.
<  */
< 
< char *
< p_nextword(wp, wbuf)
< 	char wp[], wbuf[];
< {
< 	register char *cp, *cp2;
< 
< 	if ((cp = wp) == NOSTR) {
< 		p_copy("", wbuf);
< 		return(NOSTR);
< 	}
< 	cp2 = wbuf;
< 	while (!any(*cp, " \t") && *cp != '\0')
< 		if (*cp == '"') {
<  			*cp2++ = *cp++;
<  			while (*cp != '\0' && *cp != '"')
<  				*cp2++ = *cp++;
<  			if (*cp == '"')
<  				*cp2++ = *cp++;
<  		} else
<  			*cp2++ = *cp++;
< 	*cp2 = '\0';
< 	while (any(*cp, " \t"))
< 		cp++;
< 	if (*cp == '\0')
< 		return(NOSTR);
< 	return(cp);
< }
< 
< /*
<  * Copy str1 to str2, return pointer to null in str2.
<  */
< 
< char *
< p_copy(str1, str2)
< 	char *str1, *str2;
< {
< 	register char *s1, *s2;
< 
< 	s1 = str1;
< 	s2 = str2;
< 	while (*s1)
< 		*s2++ = *s1++;
< 	*s2 = 0;
< 	return(s2);
< }
< 
< #define	L	1		/* A lower case char */
< #define	S	2		/* A space */
< #define	D	3		/* A digit */
< #define	O	4		/* An optional digit or space */
< #define	C	5		/* A colon */
< #define	N	6		/* A new line */
< #define U	7		/* An upper case char */
< 
< /*example          T h u   S e p   2 9   1 5 : 2 0 : 1 9   1 9 8 8 */
< char p_ctypes[] = {U,L,L,S,U,L,L,S,O,D,S,D,D,C,D,D,C,D,D,S,D,D,D,D,0};
< 
< /* example         T h u   S e p   2 9   1 5 : 2 0 : 1 9   M S T   1 9 8 8 */
< char p_tmztyp[] = {U,L,L,S,U,L,L,S,O,D,S,D,D,C,D,D,C,D,D,S,U,U,U,S,D,D,D,D,0};
< 
< p_isdate(date)
< 	char date[];
< {
< 	register char *cp;
< 
< 	cp = date;
< 	if (p_cmatch(cp, p_ctypes))
< 		return(1);
< 
< 	return(p_cmatch(cp, p_tmztyp));
< }
< 
< /*
<  * Match the given string against the given template.
<  * Return 1 if they match, 0 if they don't
<  */
< 
< p_cmatch(str, temp)
< 	char str[], temp[];
< {
< 	register char *cp, *tp;
< 	register int c;
< 
< 	cp = str;
< 	tp = temp;
< 	while (*cp != '\0' && *tp != 0) {
< 		c = *cp++;
< 		switch (*tp++) {
< 		case L:
< 			if (c < 'a' || c > 'z')
< 				return(0);
< 			break;
< 
< 		case U:
< 			if (c < 'A' || c > 'Z')
< 				return(0);
< 			break;
< 
< 		case S:
< 			if (c != ' ')
< 				return(0);
< 			break;
< 
< 		case D:
< 			if (!isdigit(c))
< 				return(0);
< 			break;
< 
< 		case O:
< 			if (c != ' ' && !isdigit(c))
< 				return(0);
< 			break;
< 
< 		case C:
< 			if (c != ':')
< 				return(0);
< 			break;
< 
< 		case N:
< 			if (c != '\n')
< 				return(0);
< 			break;
< 		}
< 	}
< 	if ((*cp != '\0' && *cp != '\n') || *tp != 0)
< 		return(0);
< 	return(1);
< }
< 
< any(ch, str)
< 	char *str;
< {
< 	register char *f;
< 	register c;
< 
< 	f = str;
< 	c = ch;
< 	while (*f)
< 		if (c == *f++)
< 			return(1);
< 	return(0);
< }
<