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