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