fix need for double space between ESMTP args on MAIL command
[unix-history] / usr / src / usr.sbin / sendmail / src / collect.c
CommitLineData
d185cb11 1/*
dc45ba8c 2 * Copyright (c) 1983 Eric P. Allman
31de980b
KB
3 * Copyright (c) 1988, 1993
4 * The Regents of the University of California. All rights reserved.
bee79b64 5 *
417f7a11 6 * %sccs.include.redist.c%
bee79b64 7 */
d185cb11
DF
8
9#ifndef lint
90f66104 10static char sccsid[] = "@(#)collect.c 8.9 (Berkeley) %G%";
bee79b64 11#endif /* not lint */
d185cb11 12
b8020f57 13# include <errno.h>
96faada8 14# include "sendmail.h"
cb590f52 15
cb590f52 16/*
a530c75f 17** COLLECT -- read & parse message header & make temp file.
cb590f52
EA
18**
19** Creates a temporary file name and copies the standard
7338e3d4
EA
20** input to that file. Leading UNIX-style "From" lines are
21** stripped off (after important information is extracted).
cb590f52
EA
22**
23** Parameters:
17df0fcb
EA
24** from -- the person we think it may be from. If
25** there is a "From" line, we will replace
26** the name of the person by this. If NULL,
27** do no such replacement.
cb590f52
EA
28**
29** Returns:
17df0fcb
EA
30** Name of the "from" person extracted from the
31** arpanet header.
cb590f52
EA
32**
33** Side Effects:
34** Temp file is created and filled.
dc39c568 35** The from person may be set.
cb590f52
EA
36*/
37
17df0fcb
EA
38maketemp(from)
39 char *from;
cb590f52
EA
40{
41 register FILE *tf;
6f121a53 42 bool ignrdot = smtpmode ? FALSE : IgnrDot;
07b49560 43 char buf[MAXLINE], buf2[MAXLINE];
a909e430 44 register char *workbuf, *freebuf;
fe3849ea 45 bool inputerr = FALSE;
1a12c7d6 46 extern char *hvalue();
a909e430 47 extern bool isheader(), flusheol();
17df0fcb 48 extern char *index();
cb590f52
EA
49
50 /*
51 ** Create the temp file name and create the file.
52 */
53
f4318163
EA
54 e->e_df = queuename(e, 'd');
55 e->e_df = newstr(e->e_df);
6f7f4e2c 56 if ((tf = dfopen(e->e_df, O_WRONLY|O_CREAT, FileMode)) == NULL)
cb590f52 57 {
a4076aed 58 syserr("Cannot create %s", e->e_df);
e863b1fa
EA
59 NoReturn = TRUE;
60 finis();
cb590f52
EA
61 }
62
d6b27179
EA
63 /*
64 ** Tell ARPANET to go ahead.
65 */
66
6f121a53 67 if (smtpmode)
b6edea3d 68 message("354 Enter mail, end with \".\" on a line by itself");
d6b27179 69
74c5fe7c
EA
70 /*
71 ** Try to read a UNIX-style From line
72 */
73
8e948497
EA
74 if (sfgets(buf, MAXLINE, InChannel, TimeOuts.to_datablock,
75 "initial message read") == NULL)
a909e430 76 goto readerr;
2768afe3 77 fixcrlf(buf, FALSE);
a4758674 78# ifndef NOTUNIX
d6b27179 79 if (!SaveFrom && strncmp(buf, "From ", 5) == 0)
1a12c7d6 80 {
a909e430
KB
81 if (!flusheol(buf, InChannel))
82 goto readerr;
a4076aed 83 eatfrom(buf, e);
8e948497
EA
84 if (sfgets(buf, MAXLINE, InChannel, TimeOuts.to_datablock,
85 "message header read") == NULL)
a909e430 86 goto readerr;
2768afe3 87 fixcrlf(buf, FALSE);
1a12c7d6 88 }
f3d8f6d6 89# endif /* NOTUNIX */
1a12c7d6 90
cb590f52 91 /*
14a39063 92 ** Copy InChannel to temp file & do message editing.
cb590f52
EA
93 ** To keep certain mailers from getting confused,
94 ** and to keep the output clean, lines that look
4bac339c 95 ** like UNIX "From" lines are deleted in the header.
cb590f52
EA
96 */
97
a909e430
KB
98 workbuf = buf; /* `workbuf' contains a header field */
99 freebuf = buf2; /* `freebuf' can be used for read-ahead */
100 for (;;)
cb590f52 101 {
f9e990ef
EA
102 char *curbuf;
103 int curbuffree;
104 register int curbuflen;
105 char *p;
106
a909e430
KB
107 /* first, see if the header is over */
108 if (!isheader(workbuf))
109 {
110 fixcrlf(workbuf, TRUE);
60b869a2 111 break;
a909e430 112 }
60b869a2 113
76f46f56 114 /* if the line is too long, throw the rest away */
a909e430
KB
115 if (!flusheol(workbuf, InChannel))
116 goto readerr;
76f46f56 117
a909e430
KB
118 /* it's okay to toss '\n' now (flusheol() needed it) */
119 fixcrlf(workbuf, TRUE);
2768afe3 120
f9e990ef
EA
121 curbuf = workbuf;
122 curbuflen = strlen(curbuf);
07b49560 123 curbuffree = MAXLINE - curbuflen;
f9e990ef 124 p = curbuf + curbuflen;
1a12c7d6
EA
125
126 /* get the rest of this field */
a909e430 127 for (;;)
cb590f52 128 {
f9e990ef
EA
129 int clen;
130
8e948497
EA
131 if (sfgets(freebuf, MAXLINE, InChannel,
132 TimeOuts.to_datablock,
133 "message header read") == NULL)
7378b013
EA
134 {
135 freebuf[0] = '\0';
136 break;
137 }
a909e430
KB
138
139 /* is this a continuation line? */
140 if (*freebuf != ' ' && *freebuf != '\t')
1a12c7d6 141 break;
a909e430
KB
142
143 if (!flusheol(freebuf, InChannel))
144 goto readerr;
145
f9e990ef 146 fixcrlf(freebuf, TRUE);
07b49560 147 clen = strlen(freebuf) + 1;
f9e990ef
EA
148
149 /* if insufficient room, dynamically allocate buffer */
150 if (clen >= curbuffree)
a909e430 151 {
f9e990ef
EA
152 /* reallocate buffer */
153 int nbuflen = ((p - curbuf) + clen) * 2;
154 char *nbuf = xalloc(nbuflen);
155
07b49560
EA
156 p = nbuf + curbuflen;
157 curbuffree = nbuflen - curbuflen;
158 bcopy(curbuf, nbuf, curbuflen);
f9e990ef
EA
159 if (curbuf != buf && curbuf != buf2)
160 free(curbuf);
161 curbuf = nbuf;
a909e430 162 }
07b49560
EA
163 *p++ = '\n';
164 bcopy(freebuf, p, clen - 1);
165 p += clen - 1;
f9e990ef 166 curbuffree -= clen;
07b49560 167 curbuflen += clen;
cb590f52 168 }
f9e990ef 169 *p++ = '\0';
cb590f52 170
f9e990ef 171 e->e_msgsize += curbuflen;
a909e430
KB
172
173 /*
174 ** The working buffer now becomes the free buffer, since
175 ** the free buffer contains a new header field.
176 **
177 ** This is premature, since we still havent called
178 ** chompheader() to process the field we just created
179 ** (so the call to chompheader() will use `freebuf').
180 ** This convolution is necessary so that if we break out
181 ** of the loop due to H_EOH, `workbuf' will always be
182 ** the next unprocessed buffer.
183 */
184
185 {
186 register char *tmp = workbuf;
187 workbuf = freebuf;
188 freebuf = tmp;
189 }
1a12c7d6
EA
190
191 /*
192 ** Snarf header away.
193 */
194
f9e990ef 195 if (bitset(H_EOH, chompheader(curbuf, FALSE, e)))
355a2a04 196 break;
f9e990ef
EA
197
198 /*
199 ** If the buffer was dynamically allocated, free it.
200 */
201
202 if (curbuf != buf && curbuf != buf2)
203 free(curbuf);
a909e430 204 }
1a12c7d6 205
9678c96d 206 if (tTd(30, 1))
1a12c7d6 207 printf("EOH\n");
1a12c7d6 208
a909e430
KB
209 if (*workbuf == '\0')
210 {
211 /* throw away a blank line */
8e948497
EA
212 if (sfgets(buf, MAXLINE, InChannel, TimeOuts.to_datablock,
213 "message separator read") == NULL)
a909e430
KB
214 goto readerr;
215 }
216 else if (workbuf == buf2) /* guarantee `buf' contains data */
217 (void) strcpy(buf, buf2);
1a12c7d6
EA
218
219 /*
220 ** Collect the body of the message.
221 */
cb590f52 222
fe3849ea 223 for (;;)
1a12c7d6 224 {
cbdb7357 225 register char *bp = buf;
71326571 226
6e64d395 227 fixcrlf(buf, TRUE);
2768afe3 228
1a12c7d6 229 /* check for end-of-message */
6f121a53 230 if (!ignrdot && buf[0] == '.' && (buf[1] == '\n' || buf[1] == '\0'))
1a12c7d6
EA
231 break;
232
cbdb7357 233 /* check for transparent dot */
198d9e9c
EA
234 if ((OpMode == MD_SMTP || OpMode == MD_DAEMON) &&
235 bp[0] == '.' && bp[1] == '.')
cbdb7357
EA
236 bp++;
237
71326571
EA
238 /*
239 ** Figure message length, output the line to the temp
240 ** file, and insert a newline if missing.
241 */
242
a4076aed 243 e->e_msgsize += strlen(bp) + 1;
cbdb7357 244 fputs(bp, tf);
6e64d395 245 fputs("\n", tf);
cb590f52 246 if (ferror(tf))
a4076aed 247 tferror(tf, e);
fe3849ea
EA
248 if (sfgets(buf, MAXLINE, InChannel, TimeOuts.to_datablock,
249 "message body read") == NULL)
250 goto readerr;
251 }
a909e430 252
fe3849ea
EA
253 if (feof(InChannel) || ferror(InChannel))
254 {
a909e430 255readerr:
7378b013
EA
256 if (tTd(30, 1))
257 printf("collect: read error\n");
fe3849ea
EA
258 inputerr = TRUE;
259 }
260
83f057f2 261 if (fflush(tf) != 0)
a4076aed 262 tferror(tf, e);
eb1ada3c
EA
263 if (fsync(fileno(tf)) < 0 || fclose(tf) < 0)
264 {
265 syserr("cannot sync message data to disk (%s)", e->e_df);
266 finis();
267 }
1a12c7d6 268
0e33b011 269 /* An EOF when running SMTP is an error */
198d9e9c 270 if (inputerr && (OpMode == MD_SMTP || OpMode == MD_DAEMON))
b65a52af 271 {
5973222c 272 char *host;
fe3849ea 273 char *problem;
1c7897ef 274
5973222c
EA
275 host = RealHostName;
276 if (host == NULL)
277 host = "localhost";
278
fe3849ea
EA
279 if (feof(InChannel))
280 problem = "unexpected close";
281 else if (ferror(InChannel))
282 problem = "I/O error";
283 else
284 problem = "read timeout";
2e15a2d8 285# ifdef LOG
5973222c 286 if (LogLevel > 0 && feof(InChannel))
ab4889ea 287 syslog(LOG_NOTICE,
fe3849ea
EA
288 "collect: %s on connection from %s, sender=%s: %m\n",
289 problem, host, e->e_from.q_paddr);
2e15a2d8 290# endif
90f66104
EA
291 if (feof(InChannel))
292 usrerr("451 collect: %s on connection from %s, from=%s",
293 problem, host, e->e_from.q_paddr);
294 else
295 syserr("451 collect: %s on connection from %s, from=%s",
fe3849ea 296 problem, host, e->e_from.q_paddr);
b65a52af
EA
297
298 /* don't return an error indication */
a4076aed
EA
299 e->e_to = NULL;
300 e->e_flags &= ~EF_FATALERRS;
1023828f 301 e->e_flags |= EF_CLRQUEUE;
b65a52af
EA
302
303 /* and don't try to deliver the partial message either */
fe3849ea
EA
304 if (InChild)
305 ExitStat = EX_QUIT;
b65a52af
EA
306 finis();
307 }
0e33b011 308
1a12c7d6
EA
309 /*
310 ** Find out some information from the headers.
2f0c5bd8 311 ** Examples are who is the from person & the date.
1a12c7d6
EA
312 */
313
959cf51d 314 eatheader(e, !requeueflag);
9678c96d 315
f50419dd
EA
316 /* collect statistics */
317 if (OpMode != MD_VERIFY)
318 markstats(e, (ADDRESS *) NULL);
319
75a2bcaf
EA
320 /*
321 ** Add an Apparently-To: line if we have no recipient lines.
322 */
1a12c7d6 323
a4076aed
EA
324 if (hvalue("to", e) == NULL && hvalue("cc", e) == NULL &&
325 hvalue("bcc", e) == NULL && hvalue("apparently-to", e) == NULL)
f0375650
EA
326 {
327 register ADDRESS *q;
328
329 /* create an Apparently-To: field */
330 /* that or reject the message.... */
a4076aed 331 for (q = e->e_sendqueue; q != NULL; q = q->q_next)
f0375650 332 {
755d533c
EA
333 if (q->q_alias != NULL)
334 continue;
9678c96d 335 if (tTd(30, 3))
f0375650 336 printf("Adding Apparently-To: %s\n", q->q_paddr);
b6919a63 337 addheader("Apparently-To", q->q_paddr, e);
f0375650
EA
338 }
339 }
340
213020d2
EA
341 /* check for message too large */
342 if (MaxMessageSize > 0 && e->e_msgsize > MaxMessageSize)
343 {
344 usrerr("552 Message exceeds maximum fixed size (%ld)",
345 MaxMessageSize);
346 }
347
a4076aed 348 if ((e->e_dfp = fopen(e->e_df, "r")) == NULL)
e0539260
EA
349 {
350 /* we haven't acked receipt yet, so just chuck this */
a4076aed 351 syserr("Cannot reopen %s", e->e_df);
e0539260
EA
352 finis();
353 }
1a12c7d6
EA
354}
355\f/*
a909e430
KB
356** FLUSHEOL -- if not at EOL, throw away rest of input line.
357**
358** Parameters:
359** buf -- last line read in (checked for '\n'),
360** fp -- file to be read from.
361**
362** Returns:
363** FALSE on error from sfgets(), TRUE otherwise.
364**
365** Side Effects:
366** none.
367*/
368
369bool
370flusheol(buf, fp)
371 char *buf;
372 FILE *fp;
373{
a909e430 374 register char *p = buf;
569baf4f
EA
375 bool printmsg = TRUE;
376 char junkbuf[MAXLINE];
a909e430 377
569baf4f
EA
378 while (strchr(p, '\n') == NULL)
379 {
380 if (printmsg)
b6edea3d 381 usrerr("553 header line too long");
569baf4f 382 printmsg = FALSE;
8e948497
EA
383 if (sfgets(junkbuf, MAXLINE, fp, TimeOuts.to_datablock,
384 "long line flush") == NULL)
569baf4f 385 return (FALSE);
a909e430
KB
386 p = junkbuf;
387 }
388
569baf4f 389 return (TRUE);
a909e430
KB
390}
391\f/*
83f057f2
EA
392** TFERROR -- signal error on writing the temporary file.
393**
394** Parameters:
395** tf -- the file pointer for the temporary file.
396**
397** Returns:
398** none.
399**
400** Side Effects:
401** Gives an error message.
402** Arranges for following output to go elsewhere.
403*/
404
a4076aed 405tferror(tf, e)
83f057f2 406 FILE *tf;
a4076aed 407 register ENVELOPE *e;
83f057f2
EA
408{
409 if (errno == ENOSPC)
410 {
a4076aed 411 (void) freopen(e->e_df, "w", tf);
83f057f2
EA
412 fputs("\nMAIL DELETED BECAUSE OF LACK OF DISK SPACE\n\n", tf);
413 usrerr("452 Out of disk space for temp file");
414 }
415 else
a4076aed 416 syserr("collect: Cannot write %s", e->e_df);
83f057f2
EA
417 (void) freopen("/dev/null", "w", tf);
418}
419\f/*
1a12c7d6
EA
420** EATFROM -- chew up a UNIX style from line and process
421**
422** This does indeed make some assumptions about the format
423** of UNIX messages.
424**
425** Parameters:
426** fm -- the from line.
427**
428** Returns:
429** none.
430**
431** Side Effects:
432** extracts what information it can from the header,
2f0c5bd8 433** such as the date.
1a12c7d6
EA
434*/
435
a4758674
EA
436# ifndef NOTUNIX
437
823c5e31
EA
438char *DowList[] =
439{
440 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", NULL
441};
442
1a12c7d6
EA
443char *MonthList[] =
444{
445 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
446 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
447 NULL
448};
449
a4076aed 450eatfrom(fm, e)
1a12c7d6 451 char *fm;
a4076aed 452 register ENVELOPE *e;
1a12c7d6
EA
453{
454 register char *p;
455 register char **dt;
456
9678c96d 457 if (tTd(30, 2))
823c5e31 458 printf("eatfrom(%s)\n", fm);
823c5e31 459
1a12c7d6
EA
460 /* find the date part */
461 p = fm;
462 while (*p != '\0')
463 {
464 /* skip a word */
465 while (*p != '\0' && *p != ' ')
34fe0a9b 466 p++;
1a12c7d6 467 while (*p == ' ')
34fe0a9b 468 p++;
2bee003d
EA
469 if (!(isascii(*p) && isupper(*p)) ||
470 p[3] != ' ' || p[13] != ':' || p[16] != ':')
1a12c7d6
EA
471 continue;
472
473 /* we have a possible date */
823c5e31 474 for (dt = DowList; *dt != NULL; dt++)
1a12c7d6
EA
475 if (strncmp(*dt, p, 3) == 0)
476 break;
823c5e31
EA
477 if (*dt == NULL)
478 continue;
1a12c7d6 479
823c5e31
EA
480 for (dt = MonthList; *dt != NULL; dt++)
481 if (strncmp(*dt, &p[4], 3) == 0)
482 break;
1a12c7d6
EA
483 if (*dt != NULL)
484 break;
485 }
486
54d35e43 487 if (*p != '\0')
1a12c7d6 488 {
2f0c5bd8 489 char *q;
e863b1fa 490 extern char *arpadate();
2f0c5bd8 491
1a12c7d6 492 /* we have found a date */
2f0c5bd8 493 q = xalloc(25);
03388044 494 (void) strncpy(q, p, 25);
2f0c5bd8 495 q[24] = '\0';
e863b1fa 496 q = arpadate(q);
a4076aed 497 define('a', newstr(q), e);
1a12c7d6
EA
498 }
499}
a4758674 500
f3d8f6d6 501# endif /* NOTUNIX */