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