head.c revision 216370
1/*
2 * Copyright (c) 1980, 1993
3 *	The Regents of the University of California.  All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 4. Neither the name of the University nor the names of its contributors
14 *    may be used to endorse or promote products derived from this software
15 *    without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30#ifndef lint
31#if 0
32static char sccsid[] = "@(#)head.c	8.2 (Berkeley) 4/20/95";
33#endif
34#endif /* not lint */
35#include <sys/cdefs.h>
36__FBSDID("$FreeBSD: head/usr.bin/mail/head.c 216370 2010-12-11 08:32:16Z joel $");
37
38#include "rcv.h"
39#include "extern.h"
40
41/*
42 * Mail -- a mail program
43 *
44 * Routines for processing and detecting headlines.
45 */
46
47/*
48 * See if the passed line buffer is a mail header.
49 * Return true if yes.  Note the extreme pains to
50 * accomodate all funny formats.
51 */
52int
53ishead(linebuf)
54	char linebuf[];
55{
56	struct headline hl;
57	char parbuf[BUFSIZ];
58
59	if (strncmp(linebuf, "From ", 5) != 0)
60		return (0);
61	parse(linebuf, &hl, parbuf);
62	if (hl.l_date == NULL) {
63		fail(linebuf, "No date field");
64		return (0);
65	}
66	if (!isdate(hl.l_date)) {
67		fail(linebuf, "Date field not legal date");
68		return (0);
69	}
70	/*
71	 * I guess we got it!
72	 */
73	return (1);
74}
75
76/*ARGSUSED*/
77void
78fail(linebuf, reason)
79	const char *linebuf, *reason;
80{
81
82	/*
83	if (value("debug") == NULL)
84		return;
85	fprintf(stderr, "\"%s\"\nnot a header because %s\n", linebuf, reason);
86	*/
87}
88
89/*
90 * Split a headline into its useful components.
91 * Copy the line into dynamic string space, then set
92 * pointers into the copied line in the passed headline
93 * structure.  Actually, it scans.
94 */
95void
96parse(line, hl, pbuf)
97	char line[], pbuf[];
98	struct headline *hl;
99{
100	char *cp, *sp;
101	char word[LINESIZE];
102
103	hl->l_from = NULL;
104	hl->l_tty = NULL;
105	hl->l_date = NULL;
106	cp = line;
107	sp = pbuf;
108	/*
109	 * Skip over "From" first.
110	 */
111	cp = nextword(cp, word);
112	/*
113	 * Check for missing return-path.
114	 */
115	if (isdate(cp)) {
116		hl->l_date = copyin(cp, &sp);
117		return;
118	}
119	cp = nextword(cp, word);
120	if (strlen(word) > 0)
121		hl->l_from = copyin(word, &sp);
122	if (cp != NULL && strncmp(cp, "tty", 3) == 0) {
123		cp = nextword(cp, word);
124		hl->l_tty = copyin(word, &sp);
125	}
126	if (cp != NULL)
127		hl->l_date = copyin(cp, &sp);
128}
129
130/*
131 * Copy the string on the left into the string on the right
132 * and bump the right (reference) string pointer by the length.
133 * Thus, dynamically allocate space in the right string, copying
134 * the left string into it.
135 */
136char *
137copyin(src, space)
138	char *src;
139	char **space;
140{
141	char *cp, *top;
142
143	top = cp = *space;
144	while ((*cp++ = *src++) != '\0')
145		;
146	*space = cp;
147	return (top);
148}
149
150/*
151 * Test to see if the passed string is a ctime(3) generated
152 * date string as documented in the manual.  The template
153 * below is used as the criterion of correctness.
154 * Also, we check for a possible trailing time zone using
155 * the tmztype template.
156 *
157 * If the mail file is created by Sys V (Solaris), there are
158 * no seconds in the time. If the mail is created by another
159 * program such as imapd, it might have timezone as
160 * <-|+>nnnn (-0800 for instance) at the end.
161 */
162
163/*
164 * 'A'	An upper case char
165 * 'a'	A lower case char
166 * ' '	A space
167 * '0'	A digit
168 * 'O'	A digit or space
169 * 'p'	A punctuation char
170 * 'P'	A punctuation char or space
171 * ':'	A colon
172 * 'N'	A new line
173 */
174
175static char *date_formats[] = {
176	"Aaa Aaa O0 00:00:00 0000",	   /* Mon Jan 01 23:59:59 2001 */
177	"Aaa Aaa O0 00:00:00 AAA 0000",	   /* Mon Jan 01 23:59:59 PST 2001 */
178	"Aaa Aaa O0 00:00:00 0000 p0000",  /* Mon Jan 01 23:59:59 2001 -0800 */
179	"Aaa Aaa O0 00:00 0000",	   /* Mon Jan 01 23:59 2001 */
180	"Aaa Aaa O0 00:00 AAA 0000",	   /* Mon Jan 01 23:59 PST 2001 */
181	"Aaa Aaa O0 00:00 0000 p0000",	   /* Mon Jan 01 23:59 2001 -0800 */
182	NULL
183};
184
185int
186isdate(date)
187	char date[];
188{
189	int i;
190
191	for(i = 0; date_formats[i] != NULL; i++) {
192		if (cmatch(date, date_formats[i]))
193			return (1);
194	}
195	return (0);
196}
197
198/*
199 * Match the given string (cp) against the given template (tp).
200 * Return 1 if they match, 0 if they don't
201 */
202int
203cmatch(cp, tp)
204	char *cp, *tp;
205{
206
207	while (*cp != '\0' && *tp != '\0')
208		switch (*tp++) {
209		case 'a':
210			if (!islower((unsigned char)*cp++))
211				return (0);
212			break;
213		case 'A':
214			if (!isupper((unsigned char)*cp++))
215				return (0);
216			break;
217		case ' ':
218			if (*cp++ != ' ')
219				return (0);
220			break;
221		case '0':
222			if (!isdigit((unsigned char)*cp++))
223				return (0);
224			break;
225		case 'O':
226			if (*cp != ' ' && !isdigit((unsigned char)*cp))
227				return (0);
228			cp++;
229			break;
230		case 'p':
231			if (!ispunct((unsigned char)*cp++))
232				return (0);
233			break;
234		case 'P':
235			if (*cp != ' ' && !ispunct((unsigned char)*cp))
236				return (0);
237			cp++;
238			break;
239		case ':':
240			if (*cp++ != ':')
241				return (0);
242			break;
243		case 'N':
244			if (*cp++ != '\n')
245				return (0);
246			break;
247		}
248	if (*cp != '\0' || *tp != '\0')
249		return (0);
250	return (1);
251}
252
253/*
254 * Collect a liberal (space, tab delimited) word into the word buffer
255 * passed.  Also, return a pointer to the next word following that,
256 * or NULL if none follow.
257 */
258char *
259nextword(wp, wbuf)
260	char *wp, *wbuf;
261{
262	int c;
263
264	if (wp == NULL) {
265		*wbuf = '\0';
266		return (NULL);
267	}
268	while ((c = *wp++) != '\0' && c != ' ' && c != '\t') {
269		*wbuf++ = c;
270		if (c == '"') {
271 			while ((c = *wp++) != '\0' && c != '"')
272 				*wbuf++ = c;
273 			if (c == '"')
274 				*wbuf++ = c;
275			else
276				wp--;
277 		}
278	}
279	*wbuf = '\0';
280	for (; c == ' ' || c == '\t'; c = *wp++)
281		;
282	if (c == '\0')
283		return (NULL);
284	return (wp - 1);
285}
286