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