1 # include <errno.h>
2 # include "sendmail.h"
3 
4 SCCSID(@(#)collect.c	3.48		08/17/82);
5 
6 /*
7 **  COLLECT -- read & parse message header & make temp file.
8 **
9 **	Creates a temporary file name and copies the standard
10 **	input to that file.  While it is doing it, it looks for
11 **	"From:" and "Sender:" fields to use as the from-person
12 **	(but only if the -a flag is specified).  It prefers to
13 **	to use the "Sender:" field.
14 **
15 **	MIT seems to like to produce "Sent-By:" fields instead
16 **	of "Sender:" fields.  We used to catch this, but it turns
17 **	out that the "Sent-By:" field doesn't always correspond
18 **	to someone real ("___057", for instance), as required by
19 **	the protocol.  So we limp by.....
20 **
21 **	Parameters:
22 **		sayok -- if set, give an ARPANET style message
23 **			to say we are ready to collect input.
24 **
25 **	Returns:
26 **		none.
27 **
28 **	Side Effects:
29 **		Temp file is created and filled.
30 **		The from person may be set.
31 */
32 
33 collect(sayok)
34 	bool sayok;
35 {
36 	register FILE *tf;
37 	char buf[MAXFIELD+1];
38 	register char *p;
39 	extern char *hvalue();
40 	extern char *mktemp();
41 	static char tempfname[60];
42 	extern char *macvalue();
43 
44 	/*
45 	**  Create the temp file name and create the file.
46 	*/
47 
48 	(void) strcpy(tempfname, QueueDir);
49 	(void) strcat(tempfname, "/dfXXXXXX");
50 	(void) mktemp(tempfname);
51 	if ((tf = dfopen(tempfname, "w")) == NULL)
52 	{
53 		syserr("Cannot create %s", tempfname);
54 		NoReturn = TRUE;
55 		finis();
56 	}
57 	(void) chmod(tempfname, 0600);
58 	CurEnv->e_df = tempfname;
59 
60 	/*
61 	**  Create the Received: line if we want to.
62 	*/
63 
64 	if (Smtp && macvalue('s') != NULL)
65 	{
66 		char xbuf[50];
67 
68 		/* this should be in the config file */
69 		(void) sprintf(xbuf, "Received: from $s by $i with SMTP; $b");
70 		expand(xbuf, buf, &buf[sizeof buf - 1], CurEnv);
71 		(void) chompheader(buf, FALSE);
72 	}
73 
74 	/*
75 	**  Tell ARPANET to go ahead.
76 	*/
77 
78 	if (sayok)
79 		message("354", "Enter mail, end with \".\" on a line by itself");
80 
81 	/*
82 	**  Try to read a UNIX-style From line
83 	*/
84 
85 	if (fgets(buf, sizeof buf, InChannel) == NULL)
86 		return;
87 	fixcrlf(buf, FALSE);
88 # ifndef NOTUNIX
89 	if (!SaveFrom && strncmp(buf, "From ", 5) == 0)
90 	{
91 		eatfrom(buf);
92 		(void) fgets(buf, sizeof buf, InChannel);
93 		fixcrlf(buf, FALSE);
94 	}
95 # endif NOTUNIX
96 
97 	/*
98 	**  Copy InChannel to temp file & do message editing.
99 	**	To keep certain mailers from getting confused,
100 	**	and to keep the output clean, lines that look
101 	**	like UNIX "From" lines are deleted in the header,
102 	**	and prepended with ">" in the body.
103 	*/
104 
105 	for (; !feof(InChannel); !feof(InChannel) && fgets(buf, sizeof buf, InChannel) != NULL)
106 	{
107 		register char c;
108 		extern bool isheader();
109 
110 		/* if the line is too long, throw the rest away */
111 		if (index(buf, '\n') == NULL)
112 		{
113 			while ((c = getc(InChannel)) != '\n')
114 				continue;
115 			/* give an error? */
116 		}
117 
118 		fixcrlf(buf, FALSE);
119 
120 		/* see if the header is over */
121 		if (!isheader(buf))
122 			break;
123 
124 		/* get the rest of this field */
125 		while ((c = getc(InChannel)) == ' ' || c == '\t')
126 		{
127 			p = &buf[strlen(buf)];
128 			*p++ = c;
129 			if (fgets(p, sizeof buf - (p - buf), InChannel) == NULL)
130 				break;
131 			fixcrlf(p, FALSE);
132 		}
133 		if (!feof(InChannel))
134 			(void) ungetc(c, InChannel);
135 
136 		CurEnv->e_msgsize += strlen(buf);
137 
138 		/*
139 		**  Snarf header away.
140 		*/
141 
142 		if (bitset(H_EOH, chompheader(buf, FALSE)))
143 			break;
144 	}
145 
146 # ifdef DEBUG
147 	if (tTd(30, 1))
148 		printf("EOH\n");
149 # endif DEBUG
150 
151 	/* throw away a blank line */
152 	if (buf[0] == '\n')
153 	{
154 		(void) fgets(buf, sizeof buf, InChannel);
155 		fixcrlf(buf, FALSE);
156 	}
157 
158 	/*
159 	**  Collect the body of the message.
160 	*/
161 
162 	for (; !feof(InChannel); !feof(InChannel) && fgets(buf, sizeof buf, InChannel) != NULL)
163 	{
164 		register int i;
165 		register char *bp = buf;
166 
167 		fixcrlf(buf, FALSE);
168 
169 		/* check for end-of-message */
170 		if (!IgnrDot && buf[0] == '.' && (buf[1] == '\n' || buf[1] == '\0'))
171 			break;
172 
173 		/* check for transparent dot */
174 		if (Smtp && *bp == '.')
175 			bp++;
176 
177 # ifndef NOTUNIX
178 		/* Hide UNIX-like From lines */
179 		if (strncmp(bp, "From ", 5) == 0)
180 		{
181 			fputs(">", tf);
182 			CurEnv->e_msgsize++;
183 		}
184 # endif NOTUNIX
185 
186 		/*
187 		**  Figure message length, output the line to the temp
188 		**  file, and insert a newline if missing.
189 		*/
190 
191 		i = strlen(bp);
192 		CurEnv->e_msgsize += i;
193 		fputs(bp, tf);
194 		if (bp[i - 1] != '\n')
195 			fputs("\n", tf);
196 		if (ferror(tf))
197 		{
198 			if (errno == ENOSPC)
199 			{
200 				(void) freopen(CurEnv->e_df, "w", tf);
201 				fputs("\nMAIL DELETED BECAUSE OF LACK OF DISK SPACE\n\n", tf);
202 				usrerr("452 Out of disk space for temp file");
203 			}
204 			else
205 				syserr("collect: Cannot write %s", CurEnv->e_df);
206 			(void) freopen("/dev/null", "w", tf);
207 		}
208 	}
209 	(void) fclose(tf);
210 
211 	/*
212 	**  Find out some information from the headers.
213 	**	Examples are who is the from person & the date.
214 	*/
215 
216 	eatheader();
217 
218 	/*
219 	**  Add an Apparently-To: line if we have no recipient lines.
220 	*/
221 
222 	if (hvalue("to") == NULL && hvalue("cc") == NULL &&
223 	    hvalue("bcc") == NULL && hvalue("apparently-to") == NULL)
224 	{
225 		register ADDRESS *q;
226 
227 		/* create an Apparently-To: field */
228 		/*    that or reject the message.... */
229 		for (q = CurEnv->e_sendqueue; q != NULL; q = q->q_next)
230 		{
231 			if (q->q_alias != NULL)
232 				continue;
233 # ifdef DEBUG
234 			if (tTd(30, 3))
235 				printf("Adding Apparently-To: %s\n", q->q_paddr);
236 # endif DEBUG
237 			addheader("apparently-to", q->q_paddr, CurEnv);
238 		}
239 	}
240 
241 	/* check for hop count overflow */
242 	if (HopCount > MAXHOP)
243 		syserr("Too many hops (%d max); probably forwarding loop", MAXHOP);
244 
245 	if ((TempFile = fopen(CurEnv->e_df, "r")) == NULL)
246 		syserr("Cannot reopen %s", CurEnv->e_df);
247 
248 	/*
249 	**  Log collection information.
250 	*/
251 
252 # ifdef LOG
253 	if (LogLevel > 1)
254 		syslog(LOG_INFO, "%s: from=%s, size=%ld, class=%d\n", MsgId,
255 		       CurEnv->e_from.q_paddr, CurEnv->e_msgsize,
256 		       CurEnv->e_class);
257 # endif LOG
258 	return;
259 }
260 /*
261 **  EATFROM -- chew up a UNIX style from line and process
262 **
263 **	This does indeed make some assumptions about the format
264 **	of UNIX messages.
265 **
266 **	Parameters:
267 **		fm -- the from line.
268 **
269 **	Returns:
270 **		none.
271 **
272 **	Side Effects:
273 **		extracts what information it can from the header,
274 **		such as the date.
275 */
276 
277 # ifndef NOTUNIX
278 
279 char	*DowList[] =
280 {
281 	"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", NULL
282 };
283 
284 char	*MonthList[] =
285 {
286 	"Jan", "Feb", "Mar", "Apr", "May", "Jun",
287 	"Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
288 	NULL
289 };
290 
291 eatfrom(fm)
292 	char *fm;
293 {
294 	register char *p;
295 	register char **dt;
296 
297 # ifdef DEBUG
298 	if (tTd(30, 2))
299 		printf("eatfrom(%s)\n", fm);
300 # endif DEBUG
301 
302 	/* find the date part */
303 	p = fm;
304 	while (*p != '\0')
305 	{
306 		/* skip a word */
307 		while (*p != '\0' && *p != ' ')
308 			*p++;
309 		while (*p == ' ')
310 			*p++;
311 		if (!isupper(*p) || p[3] != ' ' || p[13] != ':' || p[16] != ':')
312 			continue;
313 
314 		/* we have a possible date */
315 		for (dt = DowList; *dt != NULL; dt++)
316 			if (strncmp(*dt, p, 3) == 0)
317 				break;
318 		if (*dt == NULL)
319 			continue;
320 
321 		for (dt = MonthList; *dt != NULL; dt++)
322 			if (strncmp(*dt, &p[4], 3) == 0)
323 				break;
324 		if (*dt != NULL)
325 			break;
326 	}
327 
328 	if (*p != NULL)
329 	{
330 		char *q;
331 		extern char *arpadate();
332 
333 		/* we have found a date */
334 		q = xalloc(25);
335 		strncpy(q, p, 25);
336 		q[24] = '\0';
337 		define('d', q);
338 		q = arpadate(q);
339 		define('a', newstr(q));
340 	}
341 }
342 
343 # endif NOTUNIX
344