1 # include <stdio.h> 2 # include <ctype.h> 3 # include <errno.h> 4 # include "dlvrmail.h" 5 6 static char SccsId[] = "@(#)collect.c 3.1 03/04/81"; 7 8 /* 9 ** MAKETEMP -- 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 char *MsgId; /* message-id, determined or created */ 41 long MsgSize; /* size of message in bytes */ 42 char *Date; /* UNIX-style origination date */ 43 44 char * 45 maketemp() 46 { 47 register FILE *tf; 48 char buf[MAXFIELD+1]; 49 register char *p; 50 char c; 51 extern int errno; 52 register HDR *h; 53 HDR **hp; 54 extern bool isheader(); 55 extern char *newstr(); 56 extern char *xalloc(); 57 char *fname; 58 char *fvalue; 59 extern char *index(), *rindex(); 60 char *xfrom; 61 extern char *hvalue(); 62 extern char *makemsgid(); 63 struct hdrinfo *hi; 64 65 /* 66 ** Create the temp file name and create the file. 67 */ 68 69 mktemp(InFileName); 70 close(creat(InFileName, 0600)); 71 if ((tf = fopen(InFileName, "w")) == NULL) 72 { 73 syserr("Cannot create %s", InFileName); 74 return (NULL); 75 } 76 77 /* try to read a UNIX-style From line */ 78 if (fgets(buf, sizeof buf, stdin) == NULL) 79 return (NULL); 80 if (strncmp(buf, "From ", 5) == 0) 81 { 82 eatfrom(buf); 83 fgets(buf, sizeof buf, stdin); 84 } 85 86 /* 87 ** Copy stdin to temp file & do message editting. 88 ** To keep certain mailers from getting confused, 89 ** and to keep the output clean, lines that look 90 ** like UNIX "From" lines are deleted in the header, 91 ** and prepended with ">" in the body. 92 */ 93 94 for (; !feof(stdin); !feof(stdin) && fgets(buf, sizeof buf, stdin)) 95 { 96 /* see if the header is over */ 97 if (!isheader(buf)) 98 break; 99 100 /* get the rest of this field */ 101 while ((c = getc(stdin)) == ' ' || c == '\t') 102 { 103 p = &buf[strlen(buf)]; 104 *p++ = c; 105 if (fgets(p, sizeof buf - (p - buf), stdin) == NULL) 106 break; 107 } 108 if (c != EOF) 109 ungetc(c, stdin); 110 111 MsgSize += strlen(buf); 112 113 /* 114 ** Snarf header away. 115 */ 116 117 /* strip off trailing newline */ 118 p = rindex(buf, '\n'); 119 if (p != NULL) 120 *p = '\0'; 121 122 /* find canonical name */ 123 fname = buf; 124 p = index(buf, ':'); 125 fvalue = &p[1]; 126 while (isspace(*--p)) 127 continue; 128 *++p = '\0'; 129 makelower(fname); 130 131 /* strip field value on front */ 132 if (*fvalue == ' ') 133 fvalue++; 134 135 /* search header list for this header */ 136 for (hp = &Header, h = Header; h != NULL; hp = &h->h_link, h = h->h_link) 137 { 138 if (strcmp(fname, h->h_field) == 0 && flagset(H_CONCAT|H_DEFAULT, h->h_flags)) 139 break; 140 } 141 if (h == NULL) 142 { 143 /* create a new node */ 144 # ifdef DEBUG 145 if (Debug) 146 printf("new field '%s', value '%s'\n", fname, fvalue); 147 # endif DEBUG 148 *hp = h = (HDR *) xalloc(sizeof *h); 149 h->h_field = newstr(fname); 150 h->h_value = NULL; 151 h->h_link = NULL; 152 h->h_flags = 0; 153 154 /* see if it is a known type */ 155 for (hi = HdrInfo; hi->hi_field != NULL; hi++) 156 { 157 if (strcmp(hi->hi_field, h->h_field) == 0) 158 { 159 h->h_flags = hi->hi_flags; 160 break; 161 } 162 } 163 } 164 else if (flagset(H_DEFAULT, h->h_flags)) 165 { 166 /* overriding default, throw out old value */ 167 # ifdef DEBUG 168 if (Debug) 169 printf("overriding '%s', old='%s', new='%s'\n", 170 fname, h->h_value, fvalue); 171 # endif DEBUG 172 free(h->h_value); 173 h->h_value = NULL; 174 } 175 176 /* do something with the value */ 177 if (h->h_value == NULL) 178 { 179 # ifdef DEBUG 180 if (Debug) 181 printf("installing '%s: %s'\n", fname, fvalue); 182 # endif DEBUG 183 h->h_value = newstr(fvalue); 184 } 185 else 186 { 187 register int len; 188 189 /* concatenate the two values */ 190 # ifdef DEBUG 191 if (Debug) 192 printf("concat '%s: %s' with '%s'\n", fname, 193 h->h_value, fvalue); 194 # endif DEBUG 195 len = strlen(h->h_value) + strlen(fvalue) + 2; 196 p = xalloc(len); 197 strcpy(p, h->h_value); 198 strcat(p, ","); 199 strcat(p, fvalue); 200 free(h->h_value); 201 h->h_value = p; 202 } 203 } 204 205 # ifdef DEBUG 206 if (Debug) 207 printf("EOH\n"); 208 # endif DEBUG 209 210 /* throw away a blank line */ 211 if (buf[0] == '\n') 212 fgets(buf, sizeof buf, stdin); 213 214 /* 215 ** Collect the body of the message. 216 */ 217 218 for (; !feof(stdin); !feof(stdin) && fgets(buf, sizeof buf, stdin) != NULL) 219 { 220 /* check for end-of-message */ 221 if (!IgnrDot && buf[0] == '.' && (buf[1] == '\n' || buf[1] == '\0')) 222 break; 223 224 /* Hide UNIX-like From lines */ 225 if (strncmp(buf, "From ", 5) == 0) 226 { 227 fputs(">", tf); 228 MsgSize++; 229 } 230 MsgSize += strlen(buf); 231 fputs(buf, tf); 232 if (ferror(tf)) 233 { 234 if (errno == ENOSPC) 235 { 236 freopen(InFileName, "w", tf); 237 fputs("\nMAIL DELETED BECAUSE OF LACK OF DISK SPACE\n\n", tf); 238 syserr("Out of disk space for temp file"); 239 } 240 else 241 syserr("Cannot write %s", InFileName); 242 freopen("/dev/null", "w", tf); 243 } 244 } 245 fclose(tf); 246 247 /* 248 ** Find out some information from the headers. 249 ** Examples are who is the from person, the date, the 250 ** message-id, etc. 251 */ 252 253 /* from person */ 254 xfrom = hvalue("sender"); 255 if (xfrom == NULL) 256 xfrom = hvalue("from"); 257 258 /* date message originated */ 259 /* we don't seem to have a good way to do canonical conversion .... 260 p = hvalue("date"); 261 if (p != NULL) 262 Date = newstr(arpatounix(p)); 263 .... so we will ignore the problem for the time being */ 264 if (Date == NULL) 265 { 266 auto long t; 267 extern char *ctime(); 268 269 time(&t); 270 Date = newstr(ctime(&t)); 271 } 272 273 /* message id */ 274 MsgId = hvalue("message-id"); 275 if (MsgId == NULL) 276 MsgId = makemsgid(); 277 278 if (freopen(InFileName, "r", stdin) == NULL) 279 syserr("Cannot reopen %s", InFileName); 280 281 # ifdef DEBUG 282 if (Debug) 283 { 284 printf("----- collected header -----\n"); 285 for (h = Header; h != NULL; h = h->h_link) 286 printf("%s: %s\n", capitalize(h->h_field), h->h_value); 287 printf("----------------------------\n"); 288 } 289 # endif DEBUG 290 return (ArpaFmt ? xfrom : NULL); 291 } 292 /* 293 ** EATFROM -- chew up a UNIX style from line and process 294 ** 295 ** This does indeed make some assumptions about the format 296 ** of UNIX messages. 297 ** 298 ** Parameters: 299 ** fm -- the from line. 300 ** 301 ** Returns: 302 ** none. 303 ** 304 ** Side Effects: 305 ** extracts what information it can from the header, 306 ** such as the Date. 307 */ 308 309 char *MonthList[] = 310 { 311 "Jan", "Feb", "Mar", "Apr", "May", "Jun", 312 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", 313 NULL 314 }; 315 316 eatfrom(fm) 317 char *fm; 318 { 319 register char *p; 320 register char **dt; 321 322 /* find the date part */ 323 p = fm; 324 while (*p != '\0') 325 { 326 /* skip a word */ 327 while (*p != '\0' && *p != ' ') 328 *p++; 329 while (*p == ' ') 330 *p++; 331 if (!isupper(*p) || p[3] != ' ' || p[13] != ':' || p[16] != ':') 332 continue; 333 334 /* we have a possible date */ 335 for (dt = MonthList; *dt != NULL; dt++) 336 if (strncmp(*dt, p, 3) == 0) 337 break; 338 339 if (*dt != NULL) 340 break; 341 } 342 343 if (*p != NULL) 344 { 345 /* we have found a date */ 346 Date = xalloc(25); 347 strncpy(Date, p, 25); 348 Date[24] = '\0'; 349 } 350 } 351 /* 352 ** HVALUE -- return value of a header. 353 ** 354 ** Parameters: 355 ** field -- the field name. 356 ** 357 ** Returns: 358 ** pointer to the value part. 359 ** NULL if not found. 360 ** 361 ** Side Effects: 362 ** sets the H_USED bit in the header if found. 363 */ 364 365 char * 366 hvalue(field) 367 char *field; 368 { 369 register HDR *h; 370 371 for (h = Header; h != NULL; h = h->h_link) 372 { 373 if (strcmp(h->h_field, field) == 0) 374 { 375 h->h_flags |= H_USED; 376 return (h->h_value); 377 } 378 } 379 return (NULL); 380 } 381 /* 382 ** MAKEMSGID -- Compute a message id for this process. 383 ** 384 ** This routine creates a message id for a message if 385 ** it did not have one already. If the MESSAGEID compile 386 ** flag is set, the messageid will be added to any message 387 ** that does not already have one. Currently it is more 388 ** of an artifact, but I suggest that if you are hacking, 389 ** you leave it in -- I may want to use it someday if 390 ** duplicate messages turn out to be a problem. 391 ** 392 ** Parameters: 393 ** none. 394 ** 395 ** Returns: 396 ** a message id. 397 ** 398 ** Side Effects: 399 ** none. 400 */ 401 402 char * 403 makemsgid() 404 { 405 auto long t; 406 extern char *MyLocName; 407 extern char *ArpaHost; 408 static char buf[50]; 409 410 time(&t); 411 sprintf(buf, "<%ld.%d.%s@%s>", t, getpid(), MyLocName, ArpaHost); 412 return (buf); 413 } 414 /* 415 ** ISHEADER -- predicate telling if argument is a header. 416 ** 417 ** Parameters: 418 ** s -- string to check for possible headerness. 419 ** 420 ** Returns: 421 ** TRUE if s is a header. 422 ** FALSE otherwise. 423 ** 424 ** Side Effects: 425 ** none. 426 */ 427 428 bool 429 isheader(s) 430 register char *s; 431 { 432 if (!isalnum(*s)) 433 return (FALSE); 434 while (!isspace(*s) && *s != ':') 435 s++; 436 while (isspace(*s)) 437 s++; 438 return (*s == ':'); 439 } 440