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