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