1 # include <stdio.h> 2 # include <ctype.h> 3 # include <errno.h> 4 # include "sendmail.h" 5 6 static char SccsId[] = "@(#)collect.c 3.8 03/28/81"; 7 8 /* 9 ** COLLECT -- read & parse message header & make temp file. 10 ** 11 ** Creates a temporary file name and copies the standard 12 ** input to that file. While it is doing it, it looks for 13 ** "From:" and "Sender:" fields to use as the from-person 14 ** (but only if the -a flag is specified). It prefers to 15 ** to use the "Sender:" field. 16 ** 17 ** MIT seems to like to produce "Sent-By:" fields instead 18 ** of "Sender:" fields. We used to catch this, but it turns 19 ** out that the "Sent-By:" field doesn't always correspond 20 ** to someone real ("___057", for instance), as required by 21 ** the protocol. So we limp by..... 22 ** 23 ** Parameters: 24 ** none 25 ** 26 ** Returns: 27 ** Name of temp file. 28 ** 29 ** Side Effects: 30 ** Temp file is created and filled. 31 ** 32 ** Called By: 33 ** main 34 ** 35 ** Notes: 36 ** This is broken off from main largely so that the 37 ** temp buffer can be deallocated. 38 */ 39 40 long MsgSize; /* size of message in bytes */ 41 42 char * 43 collect() 44 { 45 register FILE *tf; 46 char buf[MAXFIELD+1]; 47 register char *p; 48 char c; 49 extern int errno; 50 extern bool isheader(); 51 extern char *newstr(); 52 extern char *xalloc(); 53 extern char *index(), *rindex(); 54 char *xfrom; 55 extern char *hvalue(); 56 extern char *strcpy(), *strcat(), *mktemp(); 57 HDR *h; 58 59 /* 60 ** Create the temp file name and create the file. 61 */ 62 63 mktemp(InFileName); 64 close(creat(InFileName, 0600)); 65 if ((tf = fopen(InFileName, "w")) == NULL) 66 { 67 syserr("Cannot create %s", InFileName); 68 return (NULL); 69 } 70 71 /* try to read a UNIX-style From line */ 72 if (fgets(buf, sizeof buf, stdin) == NULL) 73 return (NULL); 74 if (strncmp(buf, "From ", 5) == 0) 75 { 76 eatfrom(buf); 77 fgets(buf, sizeof buf, stdin); 78 } 79 80 /* 81 ** Copy stdin to temp file & do message editting. 82 ** To keep certain mailers from getting confused, 83 ** and to keep the output clean, lines that look 84 ** like UNIX "From" lines are deleted in the header, 85 ** and prepended with ">" in the body. 86 */ 87 88 for (; !feof(stdin); !feof(stdin) && fgets(buf, sizeof buf, stdin)) 89 { 90 /* see if the header is over */ 91 if (!isheader(buf)) 92 break; 93 94 /* get the rest of this field */ 95 while ((c = getc(stdin)) == ' ' || c == '\t') 96 { 97 p = &buf[strlen(buf)]; 98 *p++ = c; 99 if (fgets(p, sizeof buf - (p - buf), stdin) == NULL) 100 break; 101 } 102 if (c != EOF) 103 ungetc(c, stdin); 104 105 MsgSize += strlen(buf); 106 107 /* 108 ** Snarf header away. 109 */ 110 111 if (bitset(H_EOH, chompheader(buf, 0))) 112 break; 113 } 114 115 # ifdef DEBUG 116 if (Debug) 117 printf("EOH\n"); 118 # endif DEBUG 119 120 /* throw away a blank line */ 121 if (buf[0] == '\n') 122 fgets(buf, sizeof buf, stdin); 123 124 /* 125 ** Collect the body of the message. 126 */ 127 128 for (; !feof(stdin); !feof(stdin) && fgets(buf, sizeof buf, stdin) != NULL) 129 { 130 /* check for end-of-message */ 131 if (!IgnrDot && buf[0] == '.' && (buf[1] == '\n' || buf[1] == '\0')) 132 break; 133 134 /* Hide UNIX-like From lines */ 135 if (strncmp(buf, "From ", 5) == 0) 136 { 137 fputs(">", tf); 138 MsgSize++; 139 } 140 MsgSize += strlen(buf); 141 fputs(buf, tf); 142 if (ferror(tf)) 143 { 144 if (errno == ENOSPC) 145 { 146 freopen(InFileName, "w", tf); 147 fputs("\nMAIL DELETED BECAUSE OF LACK OF DISK SPACE\n\n", tf); 148 syserr("Out of disk space for temp file"); 149 } 150 else 151 syserr("Cannot write %s", InFileName); 152 freopen("/dev/null", "w", tf); 153 } 154 } 155 fclose(tf); 156 157 /* 158 ** Find out some information from the headers. 159 ** Examples are who is the from person & the date. 160 */ 161 162 /* from person */ 163 xfrom = hvalue("sender"); 164 if (xfrom == NULL) 165 xfrom = hvalue("from"); 166 167 /* date message originated */ 168 p = hvalue("date"); 169 if (p != NULL) 170 { 171 define('a', p); 172 /* we don't have a good way to do canonical conversion .... 173 define('d', newstr(arpatounix(p))); 174 .... so we will ignore the problem for the time being */ 175 } 176 177 if (freopen(InFileName, "r", stdin) == NULL) 178 syserr("Cannot reopen %s", InFileName); 179 180 # ifdef DEBUG 181 if (Debug) 182 { 183 printf("----- collected header -----\n"); 184 for (h = Header; h != NULL; h = h->h_link) 185 printf("%s: %s\n", capitalize(h->h_field), h->h_value); 186 printf("----------------------------\n"); 187 } 188 # endif DEBUG 189 return (ArpaFmt ? xfrom : NULL); 190 } 191 /* 192 ** EATFROM -- chew up a UNIX style from line and process 193 ** 194 ** This does indeed make some assumptions about the format 195 ** of UNIX messages. 196 ** 197 ** Parameters: 198 ** fm -- the from line. 199 ** 200 ** Returns: 201 ** none. 202 ** 203 ** Side Effects: 204 ** extracts what information it can from the header, 205 ** such as the date. 206 */ 207 208 char *MonthList[] = 209 { 210 "Jan", "Feb", "Mar", "Apr", "May", "Jun", 211 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", 212 NULL 213 }; 214 215 eatfrom(fm) 216 char *fm; 217 { 218 register char *p; 219 register char **dt; 220 221 /* find the date part */ 222 p = fm; 223 while (*p != '\0') 224 { 225 /* skip a word */ 226 while (*p != '\0' && *p != ' ') 227 *p++; 228 while (*p == ' ') 229 *p++; 230 if (!isupper(*p) || p[3] != ' ' || p[13] != ':' || p[16] != ':') 231 continue; 232 233 /* we have a possible date */ 234 for (dt = MonthList; *dt != NULL; dt++) 235 if (strncmp(*dt, p, 3) == 0) 236 break; 237 238 if (*dt != NULL) 239 break; 240 } 241 242 if (*p != NULL) 243 { 244 char *q; 245 246 /* we have found a date */ 247 q = xalloc(25); 248 strncpy(q, p, 25); 249 q[24] = '\0'; 250 define('d', q); 251 } 252 } 253 /* 254 ** HVALUE -- return value of a header. 255 ** 256 ** Only "real" fields (i.e., ones that have not been supplied 257 ** as a default) are used. 258 ** 259 ** Parameters: 260 ** field -- the field name. 261 ** 262 ** Returns: 263 ** pointer to the value part. 264 ** NULL if not found. 265 ** 266 ** Side Effects: 267 ** sets the H_USED bit in the header if found. 268 */ 269 270 char * 271 hvalue(field) 272 char *field; 273 { 274 register HDR *h; 275 276 for (h = Header; h != NULL; h = h->h_link) 277 { 278 if (!bitset(H_DEFAULT, h->h_flags) && strcmp(h->h_field, field) == 0) 279 { 280 h->h_flags |= H_USED; 281 return (h->h_value); 282 } 283 } 284 return (NULL); 285 } 286 /* 287 ** ISHEADER -- predicate telling if argument is a header. 288 ** 289 ** Parameters: 290 ** s -- string to check for possible headerness. 291 ** 292 ** Returns: 293 ** TRUE if s is a header. 294 ** FALSE otherwise. 295 ** 296 ** Side Effects: 297 ** none. 298 */ 299 300 bool 301 isheader(s) 302 register char *s; 303 { 304 if (!isalnum(*s)) 305 return (FALSE); 306 while (!isspace(*s) && *s != ':') 307 s++; 308 while (isspace(*s)) 309 s++; 310 return (*s == ':'); 311 } 312 /* 313 ** CHOMPHEADER -- process and save a header line. 314 ** 315 ** Called by collect and by readcf to deal with header lines. 316 ** 317 ** Parameters: 318 ** line -- header as a text line. 319 ** stat -- bits to set in the h_flags field. 320 ** 321 ** Returns: 322 ** flags for this header. 323 ** 324 ** Side Effects: 325 ** The header is saved on the header list. 326 */ 327 328 chompheader(line, stat) 329 char *line; 330 int stat; 331 { 332 register char *p; 333 extern int errno; 334 register HDR *h; 335 HDR **hp; 336 extern bool isheader(); 337 extern char *newstr(); 338 extern char *xalloc(); 339 char *fname; 340 char *fvalue; 341 extern char *index(), *rindex(); 342 struct hdrinfo *hi; 343 extern char *strcpy(), *strcat(), *mktemp(); 344 345 /* strip off trailing newline */ 346 p = rindex(line, '\n'); 347 if (p != NULL) 348 *p = '\0'; 349 350 /* find canonical name */ 351 fname = line; 352 p = index(line, ':'); 353 fvalue = &p[1]; 354 while (isspace(*--p)) 355 continue; 356 *++p = '\0'; 357 makelower(fname); 358 359 /* strip field value on front */ 360 if (*fvalue == ' ') 361 fvalue++; 362 363 /* search header list for this header */ 364 for (hp = &Header, h = Header; h != NULL; hp = &h->h_link, h = h->h_link) 365 { 366 if (strcmp(fname, h->h_field) == 0 && bitset(H_DEFAULT, h->h_flags)) 367 break; 368 } 369 370 /* see if it is a known type */ 371 for (hi = HdrInfo; hi->hi_field != NULL; hi++) 372 { 373 if (strcmp(hi->hi_field, fname) == 0) 374 break; 375 } 376 377 /* if this means "end of header" quit now */ 378 if (bitset(H_EOH, hi->hi_flags)) 379 return (hi->hi_flags); 380 381 /* create/fill in a new node */ 382 if (h == NULL) 383 { 384 /* create a new node */ 385 *hp = h = (HDR *) xalloc(sizeof *h); 386 h->h_field = newstr(fname); 387 h->h_value = NULL; 388 h->h_link = NULL; 389 h->h_flags = hi->hi_flags | stat; 390 h->h_mflags = hi->hi_mflags; 391 } 392 else 393 h->h_flags &= ~H_DEFAULT; 394 if (h->h_value != NULL) 395 free(h->h_value); 396 h->h_value = newstr(fvalue); 397 398 return (h->h_flags); 399 } 400