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