head.c revision 216564
160812Sps/*
260786Sps * Copyright (c) 1980, 1993
3161478Sdelphij *	The Regents of the University of California.  All rights reserved.
460786Sps *
560786Sps * Redistribution and use in source and binary forms, with or without
660786Sps * modification, are permitted provided that the following conditions
760786Sps * are met:
860786Sps * 1. Redistributions of source code must retain the above copyright
960786Sps *    notice, this list of conditions and the following disclaimer.
1060786Sps * 2. Redistributions in binary form must reproduce the above copyright
1160786Sps *    notice, this list of conditions and the following disclaimer in the
1260786Sps *    documentation and/or other materials provided with the distribution.
1360786Sps * 4. Neither the name of the University nor the names of its contributors
1460786Sps *    may be used to endorse or promote products derived from this software
1560786Sps *    without specific prior written permission.
1660786Sps *
1760786Sps * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
1889022Sps * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1989022Sps * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2089022Sps * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2160786Sps * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2260786Sps * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2360786Sps * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2460786Sps * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25161478Sdelphij * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2660786Sps * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2760786Sps * SUCH DAMAGE.
2860786Sps */
2960786Sps
3060786Sps#ifndef lint
3160786Sps#if 0
3260786Spsstatic char sccsid[] = "@(#)head.c	8.2 (Berkeley) 4/20/95";
3360786Sps#endif
3460786Sps#endif /* not lint */
3560786Sps#include <sys/cdefs.h>
3660786Sps__FBSDID("$FreeBSD: head/usr.bin/mail/head.c 216564 2010-12-19 16:25:23Z charnier $");
3760786Sps
3860786Sps#include "rcv.h"
3960786Sps#include "extern.h"
4060786Sps
4160786Sps/*
4260812Sps * Mail -- a mail program
4360786Sps *
4460786Sps * Routines for processing and detecting headlines.
4560786Sps */
4660786Sps
4760786Sps/*
4860786Sps * See if the passed line buffer is a mail header.
4960786Sps * Return true if yes.  Note the extreme pains to
5060786Sps * accomodate all funny formats.
5160786Sps */
5260786Spsint
5360786Spsishead(char linebuf[])
5460786Sps{
5560786Sps	struct headline hl;
5660786Sps	char parbuf[BUFSIZ];
5760786Sps
5863131Sps	if (strncmp(linebuf, "From ", 5) != 0)
5960786Sps		return (0);
6060786Sps	parse(linebuf, &hl, parbuf);
6160786Sps	if (hl.l_date == NULL) {
6260786Sps		fail(linebuf, "No date field");
6360786Sps		return (0);
6460786Sps	}
6560786Sps	if (!isdate(hl.l_date)) {
6660786Sps		fail(linebuf, "Date field not legal date");
67128348Stjr		return (0);
6860786Sps	}
6960786Sps	/*
7060786Sps	 * I guess we got it!
7160786Sps	 */
72161478Sdelphij	return (1);
7360786Sps}
7460786Sps
7560786Spsvoid
7660786Spsfail(const char *linebuf __unused, const char *reason __unused)
7760786Sps{
7860786Sps
7960786Sps	/*
8060786Sps	if (value("debug") == NULL)
8160786Sps		return;
8260786Sps	fprintf(stderr, "\"%s\"\nnot a header because %s\n", linebuf, reason);
8360786Sps	*/
8460786Sps}
8560786Sps
8660786Sps/*
8760786Sps * Split a headline into its useful components.
8860786Sps * Copy the line into dynamic string space, then set
8960786Sps * pointers into the copied line in the passed headline
9060786Sps * structure.  Actually, it scans.
9160786Sps */
9260786Spsvoid
9360786Spsparse(char line[], struct headline *hl, char pbuf[])
9460786Sps{
9560786Sps	char *cp, *sp;
9660786Sps	char word[LINESIZE];
9760786Sps
9860786Sps	hl->l_from = NULL;
9960786Sps	hl->l_tty = NULL;
10060786Sps	hl->l_date = NULL;
10160786Sps	cp = line;
10260786Sps	sp = pbuf;
10360786Sps	/*
10460786Sps	 * Skip over "From" first.
10560786Sps	 */
10660786Sps	cp = nextword(cp, word);
10760786Sps	/*
10860786Sps	 * Check for missing return-path.
10960786Sps	 */
11060786Sps	if (isdate(cp)) {
11160786Sps		hl->l_date = copyin(cp, &sp);
11260786Sps		return;
11360786Sps	}
11460786Sps	cp = nextword(cp, word);
11560786Sps	if (strlen(word) > 0)
11660786Sps		hl->l_from = copyin(word, &sp);
11760786Sps	if (cp != NULL && strncmp(cp, "tty", 3) == 0) {
11860786Sps		cp = nextword(cp, word);
11960786Sps		hl->l_tty = copyin(word, &sp);
12060786Sps	}
12160786Sps	if (cp != NULL)
12260786Sps		hl->l_date = copyin(cp, &sp);
12360786Sps}
12460786Sps
12560786Sps/*
12660786Sps * Copy the string on the left into the string on the right
12760786Sps * and bump the right (reference) string pointer by the length.
12860786Sps * Thus, dynamically allocate space in the right string, copying
12960786Sps * the left string into it.
13060786Sps */
13160786Spschar *
13260786Spscopyin(char *src, char **space)
13360786Sps{
13460786Sps	char *cp, *top;
13560786Sps
13660786Sps	top = cp = *space;
13760786Sps	while ((*cp++ = *src++) != '\0')
13860786Sps		;
13960786Sps	*space = cp;
14060786Sps	return (top);
14160786Sps}
14260786Sps
14360786Sps/*
14460786Sps * Test to see if the passed string is a ctime(3) generated
14560786Sps * date string as documented in the manual.  The template
14660786Sps * below is used as the criterion of correctness.
14760786Sps * Also, we check for a possible trailing time zone using
14860786Sps * the tmztype template.
14960786Sps *
15060786Sps * If the mail file is created by Sys V (Solaris), there are
15160786Sps * no seconds in the time. If the mail is created by another
15260786Sps * program such as imapd, it might have timezone as
15360786Sps * <-|+>nnnn (-0800 for instance) at the end.
15460786Sps */
15560786Sps
15660786Sps/*
15760786Sps * 'A'	An upper case char
15860786Sps * 'a'	A lower case char
15960786Sps * ' '	A space
16060786Sps * '0'	A digit
16160786Sps * 'O'	A digit or space
16260786Sps * 'p'	A punctuation char
16360786Sps * 'P'	A punctuation char or space
16460786Sps * ':'	A colon
16560786Sps * 'N'	A new line
16660786Sps */
16760786Sps
16860786Spsstatic char *date_formats[] = {
16960786Sps	"Aaa Aaa O0 00:00:00 0000",	   /* Mon Jan 01 23:59:59 2001 */
17060786Sps	"Aaa Aaa O0 00:00:00 AAA 0000",	   /* Mon Jan 01 23:59:59 PST 2001 */
17160786Sps	"Aaa Aaa O0 00:00:00 0000 p0000",  /* Mon Jan 01 23:59:59 2001 -0800 */
17260786Sps	"Aaa Aaa O0 00:00 0000",	   /* Mon Jan 01 23:59 2001 */
17360786Sps	"Aaa Aaa O0 00:00 AAA 0000",	   /* Mon Jan 01 23:59 PST 2001 */
17460786Sps	"Aaa Aaa O0 00:00 0000 p0000",	   /* Mon Jan 01 23:59 2001 -0800 */
17560786Sps	NULL
17660786Sps};
17760786Sps
17860786Spsint
17960786Spsisdate(char date[])
18060786Sps{
18160786Sps	int i;
18260786Sps
18360786Sps	for(i = 0; date_formats[i] != NULL; i++) {
18460786Sps		if (cmatch(date, date_formats[i]))
18560786Sps			return (1);
18660786Sps	}
18760786Sps	return (0);
18860786Sps}
18960786Sps
19060786Sps/*
19160786Sps * Match the given string (cp) against the given template (tp).
19260786Sps * Return 1 if they match, 0 if they don't
193128348Stjr */
19460786Spsint
19560786Spscmatch(char *cp, char *tp)
19660786Sps{
19760786Sps
19860786Sps	while (*cp != '\0' && *tp != '\0')
19960786Sps		switch (*tp++) {
20060786Sps		case 'a':
20160786Sps			if (!islower((unsigned char)*cp++))
20260786Sps				return (0);
20360786Sps			break;
20460786Sps		case 'A':
20560786Sps			if (!isupper((unsigned char)*cp++))
20660786Sps				return (0);
20760786Sps			break;
20860786Sps		case ' ':
20960786Sps			if (*cp++ != ' ')
21060786Sps				return (0);
21160786Sps			break;
21260786Sps		case '0':
213128348Stjr			if (!isdigit((unsigned char)*cp++))
21460786Sps				return (0);
21560786Sps			break;
216128348Stjr		case 'O':
21760786Sps			if (*cp != ' ' && !isdigit((unsigned char)*cp))
21860786Sps				return (0);
21960786Sps			cp++;
22060786Sps			break;
22160786Sps		case 'p':
22260786Sps			if (!ispunct((unsigned char)*cp++))
223128348Stjr				return (0);
22489022Sps			break;
22589022Sps		case 'P':
226128348Stjr			if (*cp != ' ' && !ispunct((unsigned char)*cp))
22760786Sps				return (0);
22860786Sps			cp++;
22960786Sps			break;
23060786Sps		case ':':
23160786Sps			if (*cp++ != ':')
23260786Sps				return (0);
23360786Sps			break;
23460786Sps		case 'N':
23560786Sps			if (*cp++ != '\n')
23660786Sps				return (0);
23760786Sps			break;
23860786Sps		}
23960786Sps	if (*cp != '\0' || *tp != '\0')
24060786Sps		return (0);
24160786Sps	return (1);
24260786Sps}
24360786Sps
24460786Sps/*
24560786Sps * Collect a liberal (space, tab delimited) word into the word buffer
24660786Sps * passed.  Also, return a pointer to the next word following that,
24760786Sps * or NULL if none follow.
24860786Sps */
24960786Spschar *
25060786Spsnextword(char *wp, char *wbuf)
25160786Sps{
25260786Sps	int c;
25360786Sps
25460786Sps	if (wp == NULL) {
25560786Sps		*wbuf = '\0';
25660786Sps		return (NULL);
25760786Sps	}
25860786Sps	while ((c = *wp++) != '\0' && c != ' ' && c != '\t') {
25960786Sps		*wbuf++ = c;
26060786Sps		if (c == '"') {
26160786Sps 			while ((c = *wp++) != '\0' && c != '"')
26260786Sps 				*wbuf++ = c;
26360786Sps 			if (c == '"')
26460786Sps 				*wbuf++ = c;
26560786Sps			else
26660786Sps				wp--;
26760786Sps 		}
26860786Sps	}
26960786Sps	*wbuf = '\0';
27060786Sps	for (; c == ' ' || c == '\t'; c = *wp++)
27160786Sps		;
27260786Sps	if (c == '\0')
27360786Sps		return (NULL);
27460786Sps	return (wp - 1);
27560786Sps}
27660786Sps