1 # include <errno.h>
2 # include "sendmail.h"
3 
4 SCCSID(@(#)collect.c	3.44		08/08/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 	char *xfrom;
40 	extern char *hvalue();
41 	extern char *mktemp();
42 	static char tempfname[40];
43 	extern char *macvalue();
44 	register HDR *h;
45 	extern HDR *hrvalue();
46 
47 	/*
48 	**  Create the temp file name and create the file.
49 	*/
50 
51 	(void) strcpy(tempfname, QueueDir);
52 	(void) strcat(tempfname, "/dfXXXXXX");
53 	(void) mktemp(tempfname);
54 	if ((tf = dfopen(tempfname, "w")) == NULL)
55 	{
56 		syserr("Cannot create %s", tempfname);
57 		NoReturn = TRUE;
58 		finis();
59 	}
60 	(void) chmod(tempfname, 0600);
61 	CurEnv->e_df = tempfname;
62 
63 	/*
64 	**  Create the Mail-From line if we want to.
65 	*/
66 
67 	if (Smtp && macvalue('s') != NULL)
68 	{
69 		char xbuf[50];
70 
71 		(void) sprintf(xbuf, "Mail-From: %s$s received by $i at $b",
72 			macvalue('r') == NULL ? "" : "$r host ");
73 		expand(xbuf, buf, &buf[sizeof buf - 1], CurEnv);
74 		(void) chompheader(buf, FALSE);
75 	}
76 
77 	/*
78 	**  Tell ARPANET to go ahead.
79 	*/
80 
81 	if (sayok)
82 		message("354", "Enter mail, end with \".\" on a line by itself");
83 
84 	/*
85 	**  Try to read a UNIX-style From line
86 	*/
87 
88 	if (fgets(buf, sizeof buf, InChannel) == NULL)
89 		return;
90 	fixcrlf(buf, FALSE);
91 # ifndef NOTUNIX
92 	if (!SaveFrom && strncmp(buf, "From ", 5) == 0)
93 	{
94 		eatfrom(buf);
95 		(void) fgets(buf, sizeof buf, InChannel);
96 		fixcrlf(buf, FALSE);
97 	}
98 # endif NOTUNIX
99 
100 	/*
101 	**  Copy InChannel to temp file & do message editing.
102 	**	To keep certain mailers from getting confused,
103 	**	and to keep the output clean, lines that look
104 	**	like UNIX "From" lines are deleted in the header,
105 	**	and prepended with ">" in the body.
106 	*/
107 
108 	for (; !feof(InChannel); !feof(InChannel) && fgets(buf, sizeof buf, InChannel) != NULL)
109 	{
110 		register char c;
111 		extern bool isheader();
112 
113 		fixcrlf(buf, FALSE);
114 
115 		/* see if the header is over */
116 		if (!isheader(buf))
117 			break;
118 
119 		/* get the rest of this field */
120 		while ((c = getc(InChannel)) == ' ' || c == '\t')
121 		{
122 			p = &buf[strlen(buf)];
123 			*p++ = c;
124 			if (fgets(p, sizeof buf - (p - buf), InChannel) == NULL)
125 				break;
126 			fixcrlf(p, FALSE);
127 		}
128 		if (!feof(InChannel))
129 			(void) ungetc(c, InChannel);
130 
131 		CurEnv->e_msgsize += strlen(buf);
132 
133 		/*
134 		**  Snarf header away.
135 		*/
136 
137 		if (bitset(H_EOH, chompheader(buf, FALSE)))
138 			break;
139 	}
140 
141 # ifdef DEBUG
142 	if (tTd(30, 1))
143 		printf("EOH\n");
144 # endif DEBUG
145 
146 	/* throw away a blank line */
147 	if (buf[0] == '\n')
148 	{
149 		(void) fgets(buf, sizeof buf, InChannel);
150 		fixcrlf(buf, FALSE);
151 	}
152 
153 	/*
154 	**  Collect the body of the message.
155 	*/
156 
157 	for (; !feof(InChannel); !feof(InChannel) && fgets(buf, sizeof buf, InChannel) != NULL)
158 	{
159 		register int i;
160 		register char *bp = buf;
161 
162 		fixcrlf(buf, FALSE);
163 
164 		/* check for end-of-message */
165 		if (!IgnrDot && buf[0] == '.' && (buf[1] == '\n' || buf[1] == '\0'))
166 			break;
167 
168 		/* check for transparent dot */
169 		if (Smtp && *bp == '.')
170 			bp++;
171 
172 # ifndef NOTUNIX
173 		/* Hide UNIX-like From lines */
174 		if (strncmp(bp, "From ", 5) == 0)
175 		{
176 			fputs(">", tf);
177 			CurEnv->e_msgsize++;
178 		}
179 # endif NOTUNIX
180 
181 		/*
182 		**  Figure message length, output the line to the temp
183 		**  file, and insert a newline if missing.
184 		*/
185 
186 		i = strlen(bp);
187 		CurEnv->e_msgsize += i;
188 		fputs(bp, tf);
189 		if (bp[i - 1] != '\n')
190 			fputs("\n", tf);
191 		if (ferror(tf))
192 		{
193 			if (errno == ENOSPC)
194 			{
195 				(void) freopen(CurEnv->e_df, "w", tf);
196 				fputs("\nMAIL DELETED BECAUSE OF LACK OF DISK SPACE\n\n", tf);
197 				usrerr("452 Out of disk space for temp file");
198 			}
199 			else
200 				syserr("collect: Cannot write %s", CurEnv->e_df);
201 			(void) freopen("/dev/null", "w", tf);
202 		}
203 	}
204 	(void) fclose(tf);
205 
206 	/*
207 	**  Find out some information from the headers.
208 	**	Examples are who is the from person & the date.
209 	*/
210 
211 	/* message id */
212 	h = hrvalue("message-id");
213 	if (h == NULL)
214 		syserr("No Message-Id spec");
215 	else if (bitset(H_DEFAULT, h->h_flags))
216 	{
217 		(void) expand(h->h_value, buf, &buf[sizeof buf - 1], CurEnv);
218 		MsgId = newstr(buf);
219 	}
220 	else
221 		MsgId = h->h_value;
222 # ifdef DEBUG
223 	if (tTd(30, 1))
224 		printf("Message-Id: %s\n", MsgId);
225 # endif DEBUG
226 
227 	/* message priority */
228 	if (!QueueRun)
229 	{
230 		/* adjust total priority by message priority */
231 		CurEnv->e_msgpriority = CurEnv->e_msgsize;
232 		p = hvalue("priority");
233 		if (p != NULL)
234 			CurEnv->e_class = priencode(p);
235 		else
236 			CurEnv->e_class = PRI_NORMAL;
237 		CurEnv->e_msgpriority -= CurEnv->e_class * WKPRIFACT;
238 	}
239 
240 	/* special handling */
241 	p = hvalue("special-handling");
242 	if (p != NULL)
243 		spechandling(p);
244 
245 	/* from person */
246 	xfrom = hvalue("sender");
247 	if (xfrom == NULL)
248 		xfrom = CurEnv->e_origfrom;
249 	if (ArpaMode)
250 		setfrom(xfrom, (char *) NULL);
251 
252 	/* full name of from person */
253 	p = hvalue("full-name");
254 	if (p != NULL)
255 		define('x', p);
256 	else
257 	{
258 		extern char *getxpart();
259 
260 		/*
261 		**  Try to extract the full name from a general From:
262 		**  field.  We take anything which is a comment as a
263 		**  first choice.  Failing in that, we see if there is
264 		**  a "machine readable" name (in <angle brackets>); if
265 		**  so we take anything preceeding that clause.
266 		**
267 		**  If we blow it here it's not all that serious.
268 		*/
269 
270 		p = hvalue("original-from");
271 		if (p == NULL)
272 			p = CurEnv->e_origfrom;
273 		p = getxpart(p);
274 		if (p != NULL)
275 			define('x', newstr(p));
276 	}
277 
278 	/* date message originated */
279 	p = hvalue("posted-date");
280 	if (p == NULL)
281 		p = hvalue("date");
282 	if (p != NULL)
283 	{
284 		define('a', p);
285 		/* we don't have a good way to do canonical conversion ....
286 		define('d', newstr(arpatounix(p)));
287 		.... so we will ignore the problem for the time being */
288 	}
289 
290 	if (hvalue("to") == NULL && hvalue("cc") == NULL &&
291 	    hvalue("bcc") == NULL && hvalue("apparently-to") == NULL)
292 	{
293 		register ADDRESS *q;
294 
295 		/* create an Apparently-To: field */
296 		/*    that or reject the message.... */
297 		for (q = CurEnv->e_sendqueue; q != NULL; q = q->q_next)
298 		{
299 			if (q->q_alias != NULL)
300 				continue;
301 # ifdef DEBUG
302 			if (tTd(30, 3))
303 				printf("Adding Apparently-To: %s\n", q->q_paddr);
304 # endif DEBUG
305 			addheader("apparently-to", q->q_paddr, CurEnv);
306 		}
307 	}
308 
309 	/* check for hop count overflow */
310 	if (HopCount > MAXHOP)
311 		syserr("Too many hops (%d max); probably forwarding loop", MAXHOP);
312 
313 	if ((TempFile = fopen(CurEnv->e_df, "r")) == NULL)
314 		syserr("Cannot reopen %s", CurEnv->e_df);
315 
316 # ifdef DEBUG
317 	if (tTd(30, 2))
318 	{
319 		HDR *h;
320 		extern char *capitalize();
321 
322 		printf("----- collected header -----\n");
323 		for (h = CurEnv->e_header; h != NULL; h = h->h_link)
324 			printf("%s: %s\n", capitalize(h->h_field), h->h_value);
325 		printf("----------------------------\n");
326 	}
327 # endif DEBUG
328 
329 	/*
330 	**  Log collection information.
331 	*/
332 
333 # ifdef LOG
334 	if (LogLevel > 1)
335 		syslog(LOG_INFO, "%s: from=%s, size=%ld, class=%d\n", MsgId,
336 		       CurEnv->e_from.q_paddr, CurEnv->e_msgsize,
337 		       CurEnv->e_msgpriority);
338 # endif LOG
339 	return;
340 }
341 /*
342 **  EATFROM -- chew up a UNIX style from line and process
343 **
344 **	This does indeed make some assumptions about the format
345 **	of UNIX messages.
346 **
347 **	Parameters:
348 **		fm -- the from line.
349 **
350 **	Returns:
351 **		none.
352 **
353 **	Side Effects:
354 **		extracts what information it can from the header,
355 **		such as the date.
356 */
357 
358 # ifndef NOTUNIX
359 
360 char	*DowList[] =
361 {
362 	"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", NULL
363 };
364 
365 char	*MonthList[] =
366 {
367 	"Jan", "Feb", "Mar", "Apr", "May", "Jun",
368 	"Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
369 	NULL
370 };
371 
372 eatfrom(fm)
373 	char *fm;
374 {
375 	register char *p;
376 	register char **dt;
377 
378 # ifdef DEBUG
379 	if (tTd(30, 2))
380 		printf("eatfrom(%s)\n", fm);
381 # endif DEBUG
382 
383 	/* find the date part */
384 	p = fm;
385 	while (*p != '\0')
386 	{
387 		/* skip a word */
388 		while (*p != '\0' && *p != ' ')
389 			*p++;
390 		while (*p == ' ')
391 			*p++;
392 		if (!isupper(*p) || p[3] != ' ' || p[13] != ':' || p[16] != ':')
393 			continue;
394 
395 		/* we have a possible date */
396 		for (dt = DowList; *dt != NULL; dt++)
397 			if (strncmp(*dt, p, 3) == 0)
398 				break;
399 		if (*dt == NULL)
400 			continue;
401 
402 		for (dt = MonthList; *dt != NULL; dt++)
403 			if (strncmp(*dt, &p[4], 3) == 0)
404 				break;
405 		if (*dt != NULL)
406 			break;
407 	}
408 
409 	if (*p != NULL)
410 	{
411 		char *q;
412 		extern char *arpadate();
413 
414 		/* we have found a date */
415 		q = xalloc(25);
416 		strncpy(q, p, 25);
417 		q[24] = '\0';
418 		define('d', q);
419 		q = arpadate(q);
420 		define('a', newstr(q));
421 	}
422 }
423 
424 # endif NOTUNIX
425 /*
426 **  PRIENCODE -- encode external priority names into internal values.
427 **
428 **	Parameters:
429 **		p -- priority in ascii.
430 **
431 **	Returns:
432 **		priority as a numeric level.
433 **
434 **	Side Effects:
435 **		none.
436 */
437 
438 struct prio
439 {
440 	char	*pri_name;	/* external name of priority */
441 	int	pri_val;	/* internal value for same */
442 };
443 
444 static struct prio	Prio[] =
445 {
446 	"alert",		PRI_ALERT,
447 	"quick",		PRI_QUICK,
448 	"first-class",		PRI_FIRSTCL,
449 	"normal",		PRI_NORMAL,
450 	"second-class",		PRI_SECONDCL,
451 	"third-class",		PRI_THIRDCL,
452 	"junk",			PRI_JUNK,
453 	NULL,			PRI_NORMAL,
454 };
455 
456 priencode(p)
457 	char *p;
458 {
459 	register struct prio *pl;
460 	extern bool sameword();
461 
462 	for (pl = Prio; pl->pri_name != NULL; pl++)
463 	{
464 		if (sameword(p, pl->pri_name))
465 			break;
466 	}
467 	return (pl->pri_val);
468 }
469 /*
470 **  SPECHANDLE -- do special handling
471 **
472 **	Parameters:
473 **		p -- pointer to list of special handling words.
474 **
475 **	Returns:
476 **		none.
477 **
478 **	Side Effects:
479 **		Sets flags as indicated by p.
480 */
481 
482 struct handling
483 {
484 	char	*han_name;		/* word to get this magic */
485 	int	han_what;		/* what to do, see below */
486 };
487 
488 /* modes for han_what */
489 # define	HAN_NONE	0	/* nothing special */
490 # define	HAN_RRECEIPT	1	/* give return receipt */
491 
492 struct handling	Handling[] =
493 {
494 	"return-receipt-requested",	HAN_RRECEIPT,
495 	NULL,				HAN_NONE
496 };
497 
498 spechandling(p)
499 	register char *p;
500 {
501 	register char *w;
502 	register struct handling *h;
503 	extern bool sameword();
504 
505 	while (*p != '\0')
506 	{
507 		/* collect a word to compare to */
508 		while (*p != '\0' && (*p == ',' || isspace(*p)))
509 			p++;
510 		if (*p == '\0')
511 			break;
512 		w = p;
513 		while (*p != '\0' && *p != ',' && !isspace(*p))
514 			p++;
515 		if (*p != '\0')
516 			*p++ = '\0';
517 
518 		/* scan the special handling table */
519 		for (h = Handling; h->han_name != NULL; h++)
520 			if (sameword(h->han_name, w))
521 				break;
522 
523 		/* see if we can do anything interesting */
524 		switch (h->han_what)
525 		{
526 		  case HAN_NONE:	/* nothing to be done */
527 			break;
528 
529 		  case HAN_RRECEIPT:	/* give return receipt */
530 			CurEnv->e_retreceipt = TRUE;
531 # ifdef DEBUG
532 			if (tTd(30, 3))
533 				printf(">>> Return receipt requested\n");
534 # endif DEBUG
535 			break;
536 
537 		  default:
538 			syserr("spechandling: handling %d (%s)", h->han_what, w);
539 		}
540 	}
541 }
542