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