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.14 (Berkeley) 11/14/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 char junkbuf[MAXLINE], *sfgets(); 294 register char *p = buf; 295 296 while (strchr(p, '\n') == NULL) { 297 if (sfgets(junkbuf,MAXLINE,fp) == NULL) 298 return(FALSE); 299 p = junkbuf; 300 } 301 302 return(TRUE); 303 } 304 /* 305 ** TFERROR -- signal error on writing the temporary file. 306 ** 307 ** Parameters: 308 ** tf -- the file pointer for the temporary file. 309 ** 310 ** Returns: 311 ** none. 312 ** 313 ** Side Effects: 314 ** Gives an error message. 315 ** Arranges for following output to go elsewhere. 316 */ 317 318 tferror(tf, e) 319 FILE *tf; 320 register ENVELOPE *e; 321 { 322 if (errno == ENOSPC) 323 { 324 (void) freopen(e->e_df, "w", tf); 325 fputs("\nMAIL DELETED BECAUSE OF LACK OF DISK SPACE\n\n", tf); 326 usrerr("452 Out of disk space for temp file"); 327 } 328 else 329 syserr("collect: Cannot write %s", e->e_df); 330 (void) freopen("/dev/null", "w", tf); 331 } 332 /* 333 ** EATFROM -- chew up a UNIX style from line and process 334 ** 335 ** This does indeed make some assumptions about the format 336 ** of UNIX messages. 337 ** 338 ** Parameters: 339 ** fm -- the from line. 340 ** 341 ** Returns: 342 ** none. 343 ** 344 ** Side Effects: 345 ** extracts what information it can from the header, 346 ** such as the date. 347 */ 348 349 # ifndef NOTUNIX 350 351 char *DowList[] = 352 { 353 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", NULL 354 }; 355 356 char *MonthList[] = 357 { 358 "Jan", "Feb", "Mar", "Apr", "May", "Jun", 359 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", 360 NULL 361 }; 362 363 eatfrom(fm, e) 364 char *fm; 365 register ENVELOPE *e; 366 { 367 register char *p; 368 register char **dt; 369 370 if (tTd(30, 2)) 371 printf("eatfrom(%s)\n", fm); 372 373 /* find the date part */ 374 p = fm; 375 while (*p != '\0') 376 { 377 /* skip a word */ 378 while (*p != '\0' && *p != ' ') 379 p++; 380 while (*p == ' ') 381 p++; 382 if (!isupper(*p) || p[3] != ' ' || p[13] != ':' || p[16] != ':') 383 continue; 384 385 /* we have a possible date */ 386 for (dt = DowList; *dt != NULL; dt++) 387 if (strncmp(*dt, p, 3) == 0) 388 break; 389 if (*dt == NULL) 390 continue; 391 392 for (dt = MonthList; *dt != NULL; dt++) 393 if (strncmp(*dt, &p[4], 3) == 0) 394 break; 395 if (*dt != NULL) 396 break; 397 } 398 399 if (*p != NULL) 400 { 401 char *q; 402 extern char *arpadate(); 403 404 /* we have found a date */ 405 q = xalloc(25); 406 (void) strncpy(q, p, 25); 407 q[24] = '\0'; 408 define('d', q, e); 409 q = arpadate(q); 410 define('a', newstr(q), e); 411 } 412 } 413 414 # endif /* NOTUNIX */ 415