1 /* 2 * Copyright (c) 1983 Eric P. Allman 3 * Copyright (c) 1988 Regents of the University of California. 4 * All rights reserved. 5 * 6 * %sccs.include.redist.c% 7 */ 8 9 #ifndef lint 10 static char sccsid[] = "@(#)collect.c 5.15 (Berkeley) 12/15/92"; 11 #endif /* not lint */ 12 13 # include <errno.h> 14 # include "sendmail.h" 15 16 /* 17 ** COLLECT -- read & parse message header & make temp file. 18 ** 19 ** Creates a temporary file name and copies the standard 20 ** input to that file. Leading UNIX-style "From" lines are 21 ** stripped off (after important information is extracted). 22 ** 23 ** Parameters: 24 ** smtpmode -- if set, we are running SMTP: give an RFC821 25 ** style message to say we are ready to collect 26 ** input, and never ignore a single dot to mean 27 ** end of message. 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(smtpmode, e) 38 bool smtpmode; 39 register ENVELOPE *e; 40 { 41 register FILE *tf; 42 bool ignrdot = smtpmode ? FALSE : IgnrDot; 43 char buf[MAXFIELD], buf2[MAXFIELD]; 44 register char *workbuf, *freebuf; 45 register int workbuflen; 46 extern char *hvalue(); 47 extern bool isheader(), flusheol(); 48 49 /* 50 ** Create the temp file name and create the file. 51 */ 52 53 e->e_df = newstr(queuename(e, 'd')); 54 if ((tf = dfopen(e->e_df, "w")) == NULL) 55 { 56 syserr("Cannot create %s", e->e_df); 57 NoReturn = TRUE; 58 finis(); 59 } 60 (void) chmod(e->e_df, FileMode); 61 62 /* 63 ** Tell ARPANET to go ahead. 64 */ 65 66 if (smtpmode) 67 message("354", "Enter mail, end with \".\" on a line by itself"); 68 69 /* 70 ** Try to read a UNIX-style From line 71 */ 72 73 if (sfgets(buf, MAXFIELD, InChannel) == NULL) 74 goto readerr; 75 fixcrlf(buf, FALSE); 76 # ifndef NOTUNIX 77 if (!SaveFrom && strncmp(buf, "From ", 5) == 0) 78 { 79 if (!flusheol(buf, InChannel)) 80 goto readerr; 81 eatfrom(buf, e); 82 if (sfgets(buf, MAXFIELD, InChannel) == NULL) 83 goto readerr; 84 fixcrlf(buf, FALSE); 85 } 86 # endif /* NOTUNIX */ 87 88 /* 89 ** Copy InChannel to temp file & do message editing. 90 ** To keep certain mailers from getting confused, 91 ** and to keep the output clean, lines that look 92 ** like UNIX "From" lines are deleted in the header. 93 */ 94 95 workbuf = buf; /* `workbuf' contains a header field */ 96 freebuf = buf2; /* `freebuf' can be used for read-ahead */ 97 for (;;) 98 { 99 /* first, see if the header is over */ 100 if (!isheader(workbuf)) 101 { 102 fixcrlf(workbuf, TRUE); 103 break; 104 } 105 106 /* if the line is too long, throw the rest away */ 107 if (!flusheol(workbuf, InChannel)) 108 goto readerr; 109 110 /* it's okay to toss '\n' now (flusheol() needed it) */ 111 fixcrlf(workbuf, TRUE); 112 113 workbuflen = strlen(workbuf); 114 115 /* get the rest of this field */ 116 for (;;) 117 { 118 if (sfgets(freebuf, MAXFIELD, InChannel) == NULL) 119 goto readerr; 120 121 /* is this a continuation line? */ 122 if (*freebuf != ' ' && *freebuf != '\t') 123 break; 124 125 if (!flusheol(freebuf, InChannel)) 126 goto readerr; 127 128 /* yes; append line to `workbuf' if there's room */ 129 if (workbuflen < MAXFIELD-3) 130 { 131 register char *p = workbuf + workbuflen; 132 register char *q = freebuf; 133 134 /* we have room for more of this field */ 135 fixcrlf(freebuf, TRUE); 136 *p++ = '\n'; 137 workbuflen++; 138 while(*q != '\0' && workbuflen < MAXFIELD-1) 139 { 140 *p++ = *q++; 141 workbuflen++; 142 } 143 *p = '\0'; 144 } 145 } 146 147 e->e_msgsize += workbuflen; 148 149 /* 150 ** The working buffer now becomes the free buffer, since 151 ** the free buffer contains a new header field. 152 ** 153 ** This is premature, since we still havent called 154 ** chompheader() to process the field we just created 155 ** (so the call to chompheader() will use `freebuf'). 156 ** This convolution is necessary so that if we break out 157 ** of the loop due to H_EOH, `workbuf' will always be 158 ** the next unprocessed buffer. 159 */ 160 161 { 162 register char *tmp = workbuf; 163 workbuf = freebuf; 164 freebuf = tmp; 165 } 166 167 /* 168 ** Snarf header away. 169 */ 170 171 if (bitset(H_EOH, chompheader(freebuf, FALSE, e))) 172 break; 173 } 174 175 if (tTd(30, 1)) 176 printf("EOH\n"); 177 178 if (*workbuf == '\0') 179 { 180 /* throw away a blank line */ 181 if (sfgets(buf, MAXFIELD, InChannel) == NULL) 182 goto readerr; 183 } 184 else if (workbuf == buf2) /* guarantee `buf' contains data */ 185 (void) strcpy(buf, buf2); 186 187 /* 188 ** Collect the body of the message. 189 */ 190 191 do 192 { 193 register char *bp = buf; 194 195 fixcrlf(buf, TRUE); 196 197 /* check for end-of-message */ 198 if (!ignrdot && buf[0] == '.' && (buf[1] == '\n' || buf[1] == '\0')) 199 break; 200 201 /* check for transparent dot */ 202 if (OpMode == MD_SMTP && bp[0] == '.' && bp[1] == '.') 203 bp++; 204 205 /* 206 ** Figure message length, output the line to the temp 207 ** file, and insert a newline if missing. 208 */ 209 210 e->e_msgsize += strlen(bp) + 1; 211 fputs(bp, tf); 212 fputs("\n", tf); 213 if (ferror(tf)) 214 tferror(tf, e); 215 } while (sfgets(buf, MAXFIELD, InChannel) != NULL); 216 217 readerr: 218 if (fflush(tf) != 0) 219 tferror(tf, e); 220 (void) fclose(tf); 221 222 /* An EOF when running SMTP is an error */ 223 if ((feof(InChannel) || ferror(InChannel)) && OpMode == MD_SMTP) 224 { 225 int usrerr(), syserr(); 226 # ifdef LOG 227 if (RealHostName != NULL && LogLevel > 0) 228 syslog(LOG_NOTICE, 229 "collect: unexpected close on connection from %s: %m\n", 230 e->e_from.q_paddr, RealHostName); 231 # endif 232 (feof(InChannel) ? usrerr: syserr) 233 ("collect: unexpected close, from=%s", e->e_from.q_paddr); 234 235 /* don't return an error indication */ 236 e->e_to = NULL; 237 e->e_flags &= ~EF_FATALERRS; 238 239 /* and don't try to deliver the partial message either */ 240 finis(); 241 } 242 243 /* 244 ** Find out some information from the headers. 245 ** Examples are who is the from person & the date. 246 */ 247 248 eatheader(e); 249 250 /* 251 ** Add an Apparently-To: line if we have no recipient lines. 252 */ 253 254 if (hvalue("to", e) == NULL && hvalue("cc", e) == NULL && 255 hvalue("bcc", e) == NULL && hvalue("apparently-to", e) == NULL) 256 { 257 register ADDRESS *q; 258 259 /* create an Apparently-To: field */ 260 /* that or reject the message.... */ 261 for (q = e->e_sendqueue; q != NULL; q = q->q_next) 262 { 263 if (q->q_alias != NULL) 264 continue; 265 if (tTd(30, 3)) 266 printf("Adding Apparently-To: %s\n", q->q_paddr); 267 addheader("apparently-to", q->q_paddr, e); 268 } 269 } 270 271 if ((e->e_dfp = fopen(e->e_df, "r")) == NULL) 272 syserr("Cannot reopen %s", e->e_df); 273 } 274 /* 275 ** FLUSHEOL -- if not at EOL, throw away rest of input line. 276 ** 277 ** Parameters: 278 ** buf -- last line read in (checked for '\n'), 279 ** fp -- file to be read from. 280 ** 281 ** Returns: 282 ** FALSE on error from sfgets(), TRUE otherwise. 283 ** 284 ** Side Effects: 285 ** none. 286 */ 287 288 bool 289 flusheol(buf, fp) 290 char *buf; 291 FILE *fp; 292 { 293 register char *p = buf; 294 bool printmsg = TRUE; 295 char junkbuf[MAXLINE]; 296 extern char *sfgets(); 297 298 while (strchr(p, '\n') == NULL) 299 { 300 if (printmsg) 301 usrerr("header line too long"); 302 printmsg = FALSE; 303 if (sfgets(junkbuf, MAXLINE, fp) == NULL) 304 return (FALSE); 305 p = junkbuf; 306 } 307 308 return (TRUE); 309 } 310 /* 311 ** TFERROR -- signal error on writing the temporary file. 312 ** 313 ** Parameters: 314 ** tf -- the file pointer for the temporary file. 315 ** 316 ** Returns: 317 ** none. 318 ** 319 ** Side Effects: 320 ** Gives an error message. 321 ** Arranges for following output to go elsewhere. 322 */ 323 324 tferror(tf, e) 325 FILE *tf; 326 register ENVELOPE *e; 327 { 328 if (errno == ENOSPC) 329 { 330 (void) freopen(e->e_df, "w", tf); 331 fputs("\nMAIL DELETED BECAUSE OF LACK OF DISK SPACE\n\n", tf); 332 usrerr("452 Out of disk space for temp file"); 333 } 334 else 335 syserr("collect: Cannot write %s", e->e_df); 336 (void) freopen("/dev/null", "w", tf); 337 } 338 /* 339 ** EATFROM -- chew up a UNIX style from line and process 340 ** 341 ** This does indeed make some assumptions about the format 342 ** of UNIX messages. 343 ** 344 ** Parameters: 345 ** fm -- the from line. 346 ** 347 ** Returns: 348 ** none. 349 ** 350 ** Side Effects: 351 ** extracts what information it can from the header, 352 ** such as the date. 353 */ 354 355 # ifndef NOTUNIX 356 357 char *DowList[] = 358 { 359 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", NULL 360 }; 361 362 char *MonthList[] = 363 { 364 "Jan", "Feb", "Mar", "Apr", "May", "Jun", 365 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", 366 NULL 367 }; 368 369 eatfrom(fm, e) 370 char *fm; 371 register ENVELOPE *e; 372 { 373 register char *p; 374 register char **dt; 375 376 if (tTd(30, 2)) 377 printf("eatfrom(%s)\n", fm); 378 379 /* find the date part */ 380 p = fm; 381 while (*p != '\0') 382 { 383 /* skip a word */ 384 while (*p != '\0' && *p != ' ') 385 p++; 386 while (*p == ' ') 387 p++; 388 if (!isupper(*p) || p[3] != ' ' || p[13] != ':' || p[16] != ':') 389 continue; 390 391 /* we have a possible date */ 392 for (dt = DowList; *dt != NULL; dt++) 393 if (strncmp(*dt, p, 3) == 0) 394 break; 395 if (*dt == NULL) 396 continue; 397 398 for (dt = MonthList; *dt != NULL; dt++) 399 if (strncmp(*dt, &p[4], 3) == 0) 400 break; 401 if (*dt != NULL) 402 break; 403 } 404 405 if (*p != NULL) 406 { 407 char *q; 408 extern char *arpadate(); 409 410 /* we have found a date */ 411 q = xalloc(25); 412 (void) strncpy(q, p, 25); 413 q[24] = '\0'; 414 define('d', q, e); 415 q = arpadate(q); 416 define('a', newstr(q), e); 417 } 418 } 419 420 # endif /* NOTUNIX */ 421