1 # include <errno.h> 2 # include "sendmail.h" 3 4 SCCSID(@(#)collect.c 3.32 01/10/82); 5 6 /* 7 ** COLLECT -- read & parse message header & make temp file. 8 ** 9 ** Creates a temporary file name and copies the standard 10 ** input to that file. While it is doing it, it looks for 11 ** "From:" and "Sender:" fields to use as the from-person 12 ** (but only if the -a flag is specified). It prefers to 13 ** to use the "Sender:" field. 14 ** 15 ** MIT seems to like to produce "Sent-By:" fields instead 16 ** of "Sender:" fields. We used to catch this, but it turns 17 ** out that the "Sent-By:" field doesn't always correspond 18 ** to someone real ("___057", for instance), as required by 19 ** the protocol. So we limp by..... 20 ** 21 ** Parameters: 22 ** sayok -- if set, give an ARPANET style message 23 ** to say we are ready to collect input. 24 ** 25 ** Returns: 26 ** none. 27 ** 28 ** Side Effects: 29 ** Temp file is created and filled. 30 ** The from person may be set. 31 */ 32 33 long MsgSize; /* size of message in bytes */ 34 35 collect(sayok) 36 bool sayok; 37 { 38 register FILE *tf; 39 char buf[MAXFIELD+1]; 40 register char *p; 41 char *xfrom; 42 extern char *hvalue(); 43 extern char *mktemp(); 44 static char tempfname[40]; 45 extern char *macvalue(); 46 47 /* 48 ** Create the temp file name and create the file. 49 */ 50 51 strcpy(tempfname, QueueDir); 52 strcat(tempfname, "/dfaXXXXXX"); 53 (void) mktemp(tempfname); 54 (void) close(creat(tempfname, 0600)); 55 if ((tf = fopen(tempfname, "w")) == NULL) 56 { 57 syserr("Cannot create %s", tempfname); 58 NoReturn = TRUE; 59 finis(); 60 } 61 InFileName = tempfname; 62 63 /* 64 ** Create the Mail-From line if we want to. 65 */ 66 67 if (Smtp && macvalue('s') != NULL) 68 { 69 char xbuf[50]; 70 71 (void) sprintf(xbuf, "Mail-From: %s$s received by $i at $b", 72 macvalue('r') == NULL ? "" : "$r host "); 73 (void) expand(xbuf, buf, &buf[sizeof buf - 1]); 74 (void) chompheader(buf, FALSE); 75 } 76 77 /* 78 ** Tell ARPANET to go ahead. 79 */ 80 81 if (sayok) 82 message("354", "Enter mail, end with \".\" on a line by itself"); 83 84 /* 85 ** Try to read a UNIX-style From line 86 */ 87 88 if (fgets(buf, sizeof buf, stdin) == NULL) 89 return; 90 fixcrlf(buf, FALSE); 91 # ifndef NOTUNIX 92 if (!SaveFrom && strncmp(buf, "From ", 5) == 0) 93 { 94 eatfrom(buf); 95 (void) fgets(buf, sizeof buf, stdin); 96 fixcrlf(buf, FALSE); 97 } 98 # endif NOTUNIX 99 100 /* 101 ** Copy stdin to temp file & do message editing. 102 ** To keep certain mailers from getting confused, 103 ** and to keep the output clean, lines that look 104 ** like UNIX "From" lines are deleted in the header, 105 ** and prepended with ">" in the body. 106 */ 107 108 for (; !feof(stdin); !feof(stdin) && fgets(buf, sizeof buf, stdin) != NULL) 109 { 110 register char c; 111 extern bool isheader(); 112 113 fixcrlf(buf, FALSE); 114 115 /* see if the header is over */ 116 if (!isheader(buf)) 117 break; 118 119 /* get the rest of this field */ 120 while ((c = getc(stdin)) == ' ' || c == '\t') 121 { 122 p = &buf[strlen(buf)]; 123 *p++ = c; 124 if (fgets(p, sizeof buf - (p - buf), stdin) == NULL) 125 break; 126 fixcrlf(p, FALSE); 127 } 128 if (!feof(stdin)) 129 (void) ungetc(c, stdin); 130 131 MsgSize += strlen(buf); 132 133 /* 134 ** Snarf header away. 135 */ 136 137 if (bitset(H_EOH, chompheader(buf, FALSE))) 138 break; 139 } 140 141 # ifdef DEBUG 142 if (Debug) 143 printf("EOH\n"); 144 # endif DEBUG 145 146 /* throw away a blank line */ 147 if (buf[0] == '\n') 148 { 149 (void) fgets(buf, sizeof buf, stdin); 150 fixcrlf(buf, FALSE); 151 } 152 153 /* 154 ** Collect the body of the message. 155 */ 156 157 for (; !feof(stdin); !feof(stdin) && fgets(buf, sizeof buf, stdin) != NULL) 158 { 159 register int i; 160 register char *bp = buf; 161 162 fixcrlf(buf, FALSE); 163 164 /* check for end-of-message */ 165 if (!IgnrDot && buf[0] == '.' && (buf[1] == '\n' || buf[1] == '\0')) 166 break; 167 168 /* check for transparent dot */ 169 if (Smtp && *bp == '.') 170 bp++; 171 172 # ifndef NOTUNIX 173 /* Hide UNIX-like From lines */ 174 if (strncmp(bp, "From ", 5) == 0) 175 { 176 fputs(">", tf); 177 MsgSize++; 178 } 179 # endif NOTUNIX 180 181 /* 182 ** Figure message length, output the line to the temp 183 ** file, and insert a newline if missing. 184 */ 185 186 i = strlen(bp); 187 MsgSize += i; 188 fputs(bp, tf); 189 if (bp[i - 1] != '\n') 190 fputs("\n", tf); 191 if (ferror(tf)) 192 { 193 if (errno == ENOSPC) 194 { 195 (void) freopen(InFileName, "w", tf); 196 fputs("\nMAIL DELETED BECAUSE OF LACK OF DISK SPACE\n\n", tf); 197 usrerr("452 Out of disk space for temp file"); 198 } 199 else 200 syserr("collect: Cannot write %s", InFileName); 201 (void) freopen("/dev/null", "w", tf); 202 } 203 } 204 (void) fclose(tf); 205 206 /* 207 ** Find out some information from the headers. 208 ** Examples are who is the from person & the date. 209 */ 210 211 if (!QueueRun) 212 { 213 /* adjust total priority by message priority */ 214 MsgPriority = MsgSize; 215 p = hvalue("priority"); 216 if (p != NULL) 217 MsgPriority -= priencode(p) * WKPRIFACT; 218 } 219 220 /* from person */ 221 xfrom = hvalue("sender"); 222 if (xfrom == NULL) 223 xfrom = OrigFrom; 224 if (ArpaMode) 225 setfrom(xfrom, (char *) NULL); 226 227 /* full name of from person */ 228 p = hvalue("full-name"); 229 if (p != NULL) 230 define('x', p); 231 else 232 { 233 register char *q; 234 235 /* 236 ** Try to extract the full name from a general From: 237 ** field. We take anything which is a comment as a 238 ** first choice. Failing in that, we see if there is 239 ** a "machine readable" name (in <angle brackets>); if 240 ** so we take anything preceeding that clause. 241 ** 242 ** If we blow it here it's not all that serious. 243 */ 244 245 p = hvalue("original-from"); 246 if (p == NULL) 247 p = OrigFrom; 248 q = index(p, '('); 249 if (q != NULL) 250 { 251 int parenlev = 0; 252 253 for (p = q; *p != '\0'; p++) 254 { 255 if (*p == '(') 256 parenlev++; 257 else if (*p == ')' && --parenlev <= 0) 258 break; 259 } 260 if (*p == ')') 261 { 262 *p = '\0'; 263 if (*++q != '\0') 264 define('x', newstr(q)); 265 *p = ')'; 266 } 267 } 268 else if ((q = index(p, '<')) != NULL) 269 { 270 char savec; 271 272 while (*--q == ' ') 273 continue; 274 while (isspace(*p)) 275 p++; 276 savec = *++q; 277 *q = '\0'; 278 if (*p != '\0') 279 define('x', newstr(p)); 280 *q = savec; 281 } 282 } 283 284 /* date message originated */ 285 p = hvalue("posted-date"); 286 if (p == NULL) 287 p = hvalue("date"); 288 if (p != NULL) 289 { 290 define('a', p); 291 /* we don't have a good way to do canonical conversion .... 292 define('d', newstr(arpatounix(p))); 293 .... so we will ignore the problem for the time being */ 294 } 295 296 if ((TempFile = fopen(InFileName, "r")) == NULL) 297 syserr("Cannot reopen %s", InFileName); 298 299 # ifdef DEBUG 300 if (Debug) 301 { 302 HDR *h; 303 extern char *capitalize(); 304 305 printf("----- collected header -----\n"); 306 for (h = Header; h != NULL; h = h->h_link) 307 printf("%s: %s\n", capitalize(h->h_field), h->h_value); 308 printf("----------------------------\n"); 309 } 310 # endif DEBUG 311 return; 312 } 313 /* 314 ** EATFROM -- chew up a UNIX style from line and process 315 ** 316 ** This does indeed make some assumptions about the format 317 ** of UNIX messages. 318 ** 319 ** Parameters: 320 ** fm -- the from line. 321 ** 322 ** Returns: 323 ** none. 324 ** 325 ** Side Effects: 326 ** extracts what information it can from the header, 327 ** such as the date. 328 */ 329 330 # ifndef NOTUNIX 331 332 char *DowList[] = 333 { 334 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", NULL 335 }; 336 337 char *MonthList[] = 338 { 339 "Jan", "Feb", "Mar", "Apr", "May", "Jun", 340 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", 341 NULL 342 }; 343 344 eatfrom(fm) 345 char *fm; 346 { 347 register char *p; 348 register char **dt; 349 350 # ifdef DEBUG 351 if (Debug > 1) 352 printf("eatfrom(%s)\n", fm); 353 # endif DEBUG 354 355 /* find the date part */ 356 p = fm; 357 while (*p != '\0') 358 { 359 /* skip a word */ 360 while (*p != '\0' && *p != ' ') 361 *p++; 362 while (*p == ' ') 363 *p++; 364 if (!isupper(*p) || p[3] != ' ' || p[13] != ':' || p[16] != ':') 365 continue; 366 367 /* we have a possible date */ 368 for (dt = DowList; *dt != NULL; dt++) 369 if (strncmp(*dt, p, 3) == 0) 370 break; 371 if (*dt == NULL) 372 continue; 373 374 for (dt = MonthList; *dt != NULL; dt++) 375 if (strncmp(*dt, &p[4], 3) == 0) 376 break; 377 if (*dt != NULL) 378 break; 379 } 380 381 if (*p != NULL) 382 { 383 char *q; 384 extern char *arpadate(); 385 386 /* we have found a date */ 387 q = xalloc(25); 388 strncpy(q, p, 25); 389 q[24] = '\0'; 390 define('d', q); 391 q = arpadate(q); 392 define('a', newstr(q)); 393 } 394 } 395 396 # endif NOTUNIX 397 /* 398 ** PRIENCODE -- encode external priority names into internal values. 399 ** 400 ** Parameters: 401 ** p -- priority in ascii. 402 ** 403 ** Returns: 404 ** priority as a numeric level. 405 ** 406 ** Side Effects: 407 ** none. 408 */ 409 410 struct prio 411 { 412 char *pri_name; /* external name of priority */ 413 int pri_val; /* internal value for same */ 414 }; 415 416 static struct prio Prio[] = 417 { 418 "alert", PRI_ALERT, 419 "quick", PRI_QUICK, 420 "first-class", PRI_FIRSTCL, 421 "normal", PRI_NORMAL, 422 "second-class", PRI_SECONDCL, 423 "third-class", PRI_THIRDCL, 424 NULL, PRI_NORMAL, 425 }; 426 427 priencode(p) 428 char *p; 429 { 430 register struct prio *pl; 431 extern bool sameword(); 432 433 for (pl = Prio; pl->pri_name != NULL; pl++) 434 { 435 if (sameword(p, pl->pri_name)) 436 break; 437 } 438 return (pl->pri_val); 439 } 440