1 # include <stdio.h>
2 # include <ctype.h>
3 # include <errno.h>
4 # include "sendmail.h"
5 
6 static char	SccsId[] = "@(#)collect.c	3.11	08/09/81";
7 
8 /*
9 **  COLLECT -- read & parse message header & make temp file.
10 **
11 **	Creates a temporary file name and copies the standard
12 **	input to that file.  While it is doing it, it looks for
13 **	"From:" and "Sender:" fields to use as the from-person
14 **	(but only if the -a flag is specified).  It prefers to
15 **	to use the "Sender:" field.
16 **
17 **	MIT seems to like to produce "Sent-By:" fields instead
18 **	of "Sender:" fields.  We used to catch this, but it turns
19 **	out that the "Sent-By:" field doesn't always correspond
20 **	to someone real ("___057", for instance), as required by
21 **	the protocol.  So we limp by.....
22 **
23 **	Parameters:
24 **		none
25 **
26 **	Returns:
27 **		Name of temp file.
28 **
29 **	Side Effects:
30 **		Temp file is created and filled.
31 **
32 **	Called By:
33 **		main
34 **
35 **	Notes:
36 **		This is broken off from main largely so that the
37 **		temp buffer can be deallocated.
38 */
39 
40 long	MsgSize;		/* size of message in bytes */
41 
42 char *
43 collect()
44 {
45 	register FILE *tf;
46 	char buf[MAXFIELD+1];
47 	register char *p;
48 	char c;
49 	extern bool isheader();
50 	char *xfrom;
51 	extern char *hvalue();
52 	extern char *mktemp();
53 	extern char *capitalize();
54 # ifdef DEBUG
55 	HDR *h;
56 # endif
57 
58 	/*
59 	**  Create the temp file name and create the file.
60 	*/
61 
62 	(void) mktemp(InFileName);
63 	(void) close(creat(InFileName, 0600));
64 	if ((tf = fopen(InFileName, "w")) == NULL)
65 	{
66 		syserr("Cannot create %s", InFileName);
67 		return (NULL);
68 	}
69 
70 	/* try to read a UNIX-style From line */
71 	if (fgets(buf, sizeof buf, stdin) == NULL)
72 		return (NULL);
73 	if (strncmp(buf, "From ", 5) == 0)
74 	{
75 		eatfrom(buf);
76 		(void) fgets(buf, sizeof buf, stdin);
77 	}
78 
79 	/*
80 	**  Copy stdin to temp file & do message editting.
81 	**	To keep certain mailers from getting confused,
82 	**	and to keep the output clean, lines that look
83 	**	like UNIX "From" lines are deleted in the header,
84 	**	and prepended with ">" in the body.
85 	*/
86 
87 	for (; !feof(stdin); !feof(stdin) && fgets(buf, sizeof buf, stdin))
88 	{
89 		/* see if the header is over */
90 		if (!isheader(buf))
91 			break;
92 
93 		/* get the rest of this field */
94 		while ((c = getc(stdin)) == ' ' || c == '\t')
95 		{
96 			p = &buf[strlen(buf)];
97 			*p++ = c;
98 			if (fgets(p, sizeof buf - (p - buf), stdin) == NULL)
99 				break;
100 		}
101 		if (!feof(stdin))
102 			(void) ungetc(c, stdin);
103 
104 		MsgSize += strlen(buf);
105 
106 		/*
107 		**  Snarf header away.
108 		*/
109 
110 		if (bitset(H_EOH, chompheader(buf, FALSE)))
111 			break;
112 	}
113 
114 # ifdef DEBUG
115 	if (Debug)
116 		printf("EOH\n");
117 # endif DEBUG
118 
119 	/* throw away a blank line */
120 	if (buf[0] == '\n')
121 		(void) fgets(buf, sizeof buf, stdin);
122 
123 	/*
124 	**  Collect the body of the message.
125 	*/
126 
127 	for (; !feof(stdin); !feof(stdin) && fgets(buf, sizeof buf, stdin) != NULL)
128 	{
129 		/* check for end-of-message */
130 		if (!IgnrDot && buf[0] == '.' && (buf[1] == '\n' || buf[1] == '\0'))
131 			break;
132 
133 		/* Hide UNIX-like From lines */
134 		if (strncmp(buf, "From ", 5) == 0)
135 		{
136 			fputs(">", tf);
137 			MsgSize++;
138 		}
139 		MsgSize += strlen(buf);
140 		fputs(buf, tf);
141 		if (ferror(tf))
142 		{
143 			if (errno == ENOSPC)
144 			{
145 				(void) freopen(InFileName, "w", tf);
146 				fputs("\nMAIL DELETED BECAUSE OF LACK OF DISK SPACE\n\n", tf);
147 				syserr("Out of disk space for temp file");
148 			}
149 			else
150 				syserr("Cannot write %s", InFileName);
151 			(void) freopen("/dev/null", "w", tf);
152 		}
153 	}
154 	(void) fclose(tf);
155 
156 	/*
157 	**  Find out some information from the headers.
158 	**	Examples are who is the from person & the date.
159 	*/
160 
161 	/* from person */
162 	xfrom = hvalue("sender");
163 	if (xfrom == NULL)
164 		xfrom = hvalue("from");
165 
166 	/* full name of from person */
167 	p = hvalue("full-name");
168 	if (p != NULL)
169 		define('x', p);
170 
171 	/* date message originated */
172 	p = hvalue("date");
173 	if (p != NULL)
174 	{
175 		define('a', p);
176 		/* we don't have a good way to do canonical conversion ....
177 		define('d', newstr(arpatounix(p)));
178 		.... so we will ignore the problem for the time being */
179 	}
180 
181 	if (freopen(InFileName, "r", stdin) == NULL)
182 		syserr("Cannot reopen %s", InFileName);
183 
184 # ifdef DEBUG
185 	if (Debug)
186 	{
187 		printf("----- collected header -----\n");
188 		for (h = Header; h != NULL; h = h->h_link)
189 			printf("%s: %s\n", capitalize(h->h_field), h->h_value);
190 		printf("----------------------------\n");
191 	}
192 # endif DEBUG
193 	return (ArpaFmt ? xfrom : NULL);
194 }
195 /*
196 **  EATFROM -- chew up a UNIX style from line and process
197 **
198 **	This does indeed make some assumptions about the format
199 **	of UNIX messages.
200 **
201 **	Parameters:
202 **		fm -- the from line.
203 **
204 **	Returns:
205 **		none.
206 **
207 **	Side Effects:
208 **		extracts what information it can from the header,
209 **		such as the date.
210 */
211 
212 char	*MonthList[] =
213 {
214 	"Jan", "Feb", "Mar", "Apr", "May", "Jun",
215 	"Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
216 	NULL
217 };
218 
219 eatfrom(fm)
220 	char *fm;
221 {
222 	register char *p;
223 	register char **dt;
224 
225 	/* find the date part */
226 	p = fm;
227 	while (*p != '\0')
228 	{
229 		/* skip a word */
230 		while (*p != '\0' && *p != ' ')
231 			*p++;
232 		while (*p == ' ')
233 			*p++;
234 		if (!isupper(*p) || p[3] != ' ' || p[13] != ':' || p[16] != ':')
235 			continue;
236 
237 		/* we have a possible date */
238 		for (dt = MonthList; *dt != NULL; dt++)
239 			if (strncmp(*dt, p, 3) == 0)
240 				break;
241 
242 		if (*dt != NULL)
243 			break;
244 	}
245 
246 	if (*p != NULL)
247 	{
248 		char *q;
249 
250 		/* we have found a date */
251 		q = xalloc(25);
252 		strncpy(q, p, 25);
253 		q[24] = '\0';
254 		define('d', q);
255 	}
256 }
257 /*
258 **  HVALUE -- return value of a header.
259 **
260 **	Only "real" fields (i.e., ones that have not been supplied
261 **	as a default) are used.
262 **
263 **	Parameters:
264 **		field -- the field name.
265 **
266 **	Returns:
267 **		pointer to the value part.
268 **		NULL if not found.
269 **
270 **	Side Effects:
271 **		sets the H_USED bit in the header if found.
272 */
273 
274 char *
275 hvalue(field)
276 	char *field;
277 {
278 	register HDR *h;
279 
280 	for (h = Header; h != NULL; h = h->h_link)
281 	{
282 		if (!bitset(H_DEFAULT, h->h_flags) && strcmp(h->h_field, field) == 0)
283 		{
284 			h->h_flags |= H_USED;
285 			return (h->h_value);
286 		}
287 	}
288 	return (NULL);
289 }
290 /*
291 **  ISHEADER -- predicate telling if argument is a header.
292 **
293 **	Parameters:
294 **		s -- string to check for possible headerness.
295 **
296 **	Returns:
297 **		TRUE if s is a header.
298 **		FALSE otherwise.
299 **
300 **	Side Effects:
301 **		none.
302 */
303 
304 bool
305 isheader(s)
306 	register char *s;
307 {
308 	if (!isalnum(*s))
309 		return (FALSE);
310 	while (!isspace(*s) && *s != ':')
311 		s++;
312 	while (isspace(*s))
313 		s++;
314 	return (*s == ':');
315 }
316 /*
317 **  CHOMPHEADER -- process and save a header line.
318 **
319 **	Called by collect and by readcf to deal with header lines.
320 **
321 **	Parameters:
322 **		line -- header as a text line.
323 **		def -- if set, this is a default value.
324 **
325 **	Returns:
326 **		flags for this header.
327 **
328 **	Side Effects:
329 **		The header is saved on the header list.
330 */
331 
332 chompheader(line, def)
333 	char *line;
334 	bool def;
335 {
336 	register char *p;
337 	register HDR *h;
338 	HDR **hp;
339 	extern bool isheader();
340 	char *fname;
341 	char *fvalue;
342 	struct hdrinfo *hi;
343 
344 	/* strip off trailing newline */
345 	p = rindex(line, '\n');
346 	if (p != NULL)
347 		*p = '\0';
348 
349 	/* find canonical name */
350 	fname = line;
351 	p = index(line, ':');
352 	fvalue = &p[1];
353 	while (isspace(*--p))
354 		continue;
355 	*++p = '\0';
356 	makelower(fname);
357 
358 	/* strip field value on front */
359 	if (*fvalue == ' ')
360 		fvalue++;
361 
362 	/* search header list for this header */
363 	for (hp = &Header, h = Header; h != NULL; hp = &h->h_link, h = h->h_link)
364 	{
365 		if (strcmp(fname, h->h_field) == 0 && bitset(H_DEFAULT, h->h_flags))
366 			break;
367 	}
368 
369 	/* see if it is a known type */
370 	for (hi = HdrInfo; hi->hi_field != NULL; hi++)
371 	{
372 		if (strcmp(hi->hi_field, fname) == 0)
373 			break;
374 	}
375 
376 	/* if this means "end of header" quit now */
377 	if (bitset(H_EOH, hi->hi_flags))
378 		return (hi->hi_flags);
379 
380 	/* create/fill in a new node */
381 	if (h == NULL)
382 	{
383 		/* create a new node */
384 		*hp = h = (HDR *) xalloc(sizeof *h);
385 		h->h_field = newstr(fname);
386 		h->h_value = NULL;
387 		h->h_link = NULL;
388 		h->h_flags = hi->hi_flags;
389 		h->h_mflags = hi->hi_mflags;
390 	}
391 	if (def)
392 		h->h_flags |= H_DEFAULT;
393 	else
394 		h->h_flags &= ~H_CHECK;
395 	if (h->h_value != NULL)
396 		free(h->h_value);
397 	h->h_value = newstr(fvalue);
398 
399 	return (h->h_flags);
400 }
401