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