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