11392Seric # include <stdio.h> 21392Seric # include <ctype.h> 31439Seric # include <errno.h> 4*2969Seric # include "postbox.h" 51392Seric 6*2969Seric static char SccsId[] = "@(#)collect.c 3.2 03/07/81"; 71392Seric 81392Seric /* 9*2969Seric ** COLLECT -- read & parse message header & make temp file. 101392Seric ** 111392Seric ** Creates a temporary file name and copies the standard 121392Seric ** input to that file. While it is doing it, it looks for 131392Seric ** "From:" and "Sender:" fields to use as the from-person 141392Seric ** (but only if the -a flag is specified). It prefers to 151392Seric ** to use the "Sender:" field. 161392Seric ** 171392Seric ** MIT seems to like to produce "Sent-By:" fields instead 181392Seric ** of "Sender:" fields. We used to catch this, but it turns 191392Seric ** out that the "Sent-By:" field doesn't always correspond 201392Seric ** to someone real ("___057", for instance), as required by 211392Seric ** the protocol. So we limp by..... 221392Seric ** 231392Seric ** Parameters: 241875Seric ** none 251392Seric ** 261392Seric ** Returns: 271875Seric ** Name of temp file. 281392Seric ** 291392Seric ** Side Effects: 301392Seric ** Temp file is created and filled. 311392Seric ** 321392Seric ** Called By: 331392Seric ** main 341392Seric ** 351392Seric ** Notes: 361392Seric ** This is broken off from main largely so that the 371392Seric ** temp buffer can be deallocated. 381392Seric */ 391392Seric 402900Seric char *MsgId; /* message-id, determined or created */ 411624Seric long MsgSize; /* size of message in bytes */ 422900Seric char *Date; /* UNIX-style origination date */ 431397Seric 441392Seric char * 45*2969Seric collect() 461392Seric { 471392Seric register FILE *tf; 481392Seric char buf[MAXFIELD+1]; 491392Seric register char *p; 501392Seric char c; 511439Seric extern int errno; 522900Seric register HDR *h; 532900Seric HDR **hp; 542900Seric extern bool isheader(); 552900Seric extern char *newstr(); 562900Seric extern char *xalloc(); 572900Seric char *fname; 582900Seric char *fvalue; 592900Seric extern char *index(), *rindex(); 602900Seric char *xfrom; 612900Seric extern char *hvalue(); 622900Seric extern char *makemsgid(); 632900Seric struct hdrinfo *hi; 641392Seric 651392Seric /* 661392Seric ** Create the temp file name and create the file. 671392Seric */ 681392Seric 691392Seric mktemp(InFileName); 701392Seric close(creat(InFileName, 0600)); 711392Seric if ((tf = fopen(InFileName, "w")) == NULL) 721392Seric { 731392Seric syserr("Cannot create %s", InFileName); 741392Seric return (NULL); 751392Seric } 761392Seric 772900Seric /* try to read a UNIX-style From line */ 782900Seric if (fgets(buf, sizeof buf, stdin) == NULL) 792900Seric return (NULL); 802900Seric if (strncmp(buf, "From ", 5) == 0) 812900Seric { 822900Seric eatfrom(buf); 832900Seric fgets(buf, sizeof buf, stdin); 842900Seric } 852900Seric 861392Seric /* 871392Seric ** Copy stdin to temp file & do message editting. 881392Seric ** To keep certain mailers from getting confused, 891392Seric ** and to keep the output clean, lines that look 901392Seric ** like UNIX "From" lines are deleted in the header, 911392Seric ** and prepended with ">" in the body. 921392Seric */ 931392Seric 942900Seric for (; !feof(stdin); !feof(stdin) && fgets(buf, sizeof buf, stdin)) 951392Seric { 962900Seric /* see if the header is over */ 972900Seric if (!isheader(buf)) 982900Seric break; 992900Seric 1002900Seric /* get the rest of this field */ 1012900Seric while ((c = getc(stdin)) == ' ' || c == '\t') 1021392Seric { 1032900Seric p = &buf[strlen(buf)]; 1042900Seric *p++ = c; 1052900Seric if (fgets(p, sizeof buf - (p - buf), stdin) == NULL) 1062900Seric break; 1071392Seric } 1082900Seric if (c != EOF) 1092900Seric ungetc(c, stdin); 1101392Seric 1112900Seric MsgSize += strlen(buf); 1121392Seric 1132900Seric /* 1142900Seric ** Snarf header away. 1152900Seric */ 1162900Seric 1172900Seric /* strip off trailing newline */ 1182900Seric p = rindex(buf, '\n'); 1192900Seric if (p != NULL) 1202900Seric *p = '\0'; 1212900Seric 1222900Seric /* find canonical name */ 1232900Seric fname = buf; 1242900Seric p = index(buf, ':'); 1252900Seric fvalue = &p[1]; 1262900Seric while (isspace(*--p)) 1272900Seric continue; 1282900Seric *++p = '\0'; 1292900Seric makelower(fname); 1302900Seric 1312900Seric /* strip field value on front */ 1322900Seric if (*fvalue == ' ') 1332900Seric fvalue++; 1342900Seric 1352900Seric /* search header list for this header */ 1362900Seric for (hp = &Header, h = Header; h != NULL; hp = &h->h_link, h = h->h_link) 1371392Seric { 138*2969Seric if (strcmp(fname, h->h_field) == 0 && bitset(H_DEFAULT, h->h_flags)) 1392900Seric break; 1402900Seric } 1412900Seric if (h == NULL) 1422900Seric { 1432900Seric /* create a new node */ 1442900Seric *hp = h = (HDR *) xalloc(sizeof *h); 1452900Seric h->h_field = newstr(fname); 1462900Seric h->h_value = NULL; 1472900Seric h->h_link = NULL; 1482900Seric h->h_flags = 0; 1492900Seric 1502900Seric /* see if it is a known type */ 1512900Seric for (hi = HdrInfo; hi->hi_field != NULL; hi++) 1521392Seric { 1532900Seric if (strcmp(hi->hi_field, h->h_field) == 0) 1542900Seric { 1552900Seric h->h_flags = hi->hi_flags; 1562900Seric break; 1572900Seric } 1581392Seric } 1592900Seric } 160*2969Seric else if (bitset(H_DEFAULT, h->h_flags)) 1612900Seric { 1622900Seric /* overriding default, throw out old value */ 1632900Seric free(h->h_value); 1642900Seric h->h_value = NULL; 1651392Seric } 1661392Seric 1672900Seric /* do something with the value */ 1682900Seric if (h->h_value == NULL) 1691392Seric { 1702900Seric h->h_value = newstr(fvalue); 1711392Seric } 1722900Seric else 1732900Seric { 1742900Seric register int len; 1751392Seric 1762900Seric /* concatenate the two values */ 1772900Seric len = strlen(h->h_value) + strlen(fvalue) + 2; 1782900Seric p = xalloc(len); 1792900Seric strcpy(p, h->h_value); 1802900Seric strcat(p, ","); 1812900Seric strcat(p, fvalue); 1822900Seric free(h->h_value); 1832900Seric h->h_value = p; 1841392Seric } 1852900Seric } 1861392Seric 1872900Seric # ifdef DEBUG 1882900Seric if (Debug) 1892900Seric printf("EOH\n"); 1902900Seric # endif DEBUG 1912900Seric 1922900Seric /* throw away a blank line */ 1932900Seric if (buf[0] == '\n') 1942900Seric fgets(buf, sizeof buf, stdin); 1952900Seric 1962900Seric /* 1972900Seric ** Collect the body of the message. 1982900Seric */ 1992900Seric 2002900Seric for (; !feof(stdin); !feof(stdin) && fgets(buf, sizeof buf, stdin) != NULL) 2012900Seric { 2022900Seric /* check for end-of-message */ 2032900Seric if (!IgnrDot && buf[0] == '.' && (buf[1] == '\n' || buf[1] == '\0')) 2042900Seric break; 2052900Seric 2062900Seric /* Hide UNIX-like From lines */ 2072900Seric if (strncmp(buf, "From ", 5) == 0) 2081392Seric { 2092900Seric fputs(">", tf); 2102900Seric MsgSize++; 2111392Seric } 2121624Seric MsgSize += strlen(buf); 2131392Seric fputs(buf, tf); 2141392Seric if (ferror(tf)) 2151392Seric { 2161439Seric if (errno == ENOSPC) 2171439Seric { 2181439Seric freopen(InFileName, "w", tf); 2191439Seric fputs("\nMAIL DELETED BECAUSE OF LACK OF DISK SPACE\n\n", tf); 2201439Seric syserr("Out of disk space for temp file"); 2211439Seric } 2221439Seric else 2231439Seric syserr("Cannot write %s", InFileName); 2241439Seric freopen("/dev/null", "w", tf); 2251392Seric } 2261392Seric } 2271392Seric fclose(tf); 2282900Seric 2292900Seric /* 2302900Seric ** Find out some information from the headers. 2312900Seric ** Examples are who is the from person, the date, the 2322900Seric ** message-id, etc. 2332900Seric */ 2342900Seric 2352900Seric /* from person */ 2362900Seric xfrom = hvalue("sender"); 2372900Seric if (xfrom == NULL) 2382900Seric xfrom = hvalue("from"); 2392900Seric 2402900Seric /* date message originated */ 2412900Seric /* we don't seem to have a good way to do canonical conversion .... 2422900Seric p = hvalue("date"); 2432900Seric if (p != NULL) 2442900Seric Date = newstr(arpatounix(p)); 2452900Seric .... so we will ignore the problem for the time being */ 2462900Seric if (Date == NULL) 2472900Seric { 2482900Seric auto long t; 2492900Seric extern char *ctime(); 2502900Seric 2512900Seric time(&t); 2522900Seric Date = newstr(ctime(&t)); 2532900Seric } 2542900Seric 2552900Seric /* message id */ 2562900Seric MsgId = hvalue("message-id"); 2572900Seric if (MsgId == NULL) 2582900Seric MsgId = makemsgid(); 2592900Seric 2601392Seric if (freopen(InFileName, "r", stdin) == NULL) 2611392Seric syserr("Cannot reopen %s", InFileName); 2622900Seric 2632900Seric # ifdef DEBUG 2642900Seric if (Debug) 2652900Seric { 2662900Seric printf("----- collected header -----\n"); 2672900Seric for (h = Header; h != NULL; h = h->h_link) 2682900Seric printf("%s: %s\n", capitalize(h->h_field), h->h_value); 2692900Seric printf("----------------------------\n"); 2702900Seric } 2712900Seric # endif DEBUG 2722900Seric return (ArpaFmt ? xfrom : NULL); 2731392Seric } 2741392Seric /* 2752900Seric ** EATFROM -- chew up a UNIX style from line and process 2762900Seric ** 2772900Seric ** This does indeed make some assumptions about the format 2782900Seric ** of UNIX messages. 2792900Seric ** 2802900Seric ** Parameters: 2812900Seric ** fm -- the from line. 2822900Seric ** 2832900Seric ** Returns: 2842900Seric ** none. 2852900Seric ** 2862900Seric ** Side Effects: 2872900Seric ** extracts what information it can from the header, 2882900Seric ** such as the Date. 2892900Seric */ 2902900Seric 2912900Seric char *MonthList[] = 2922900Seric { 2932900Seric "Jan", "Feb", "Mar", "Apr", "May", "Jun", 2942900Seric "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", 2952900Seric NULL 2962900Seric }; 2972900Seric 2982900Seric eatfrom(fm) 2992900Seric char *fm; 3002900Seric { 3012900Seric register char *p; 3022900Seric register char **dt; 3032900Seric 3042900Seric /* find the date part */ 3052900Seric p = fm; 3062900Seric while (*p != '\0') 3072900Seric { 3082900Seric /* skip a word */ 3092900Seric while (*p != '\0' && *p != ' ') 3102900Seric *p++; 3112900Seric while (*p == ' ') 3122900Seric *p++; 3132900Seric if (!isupper(*p) || p[3] != ' ' || p[13] != ':' || p[16] != ':') 3142900Seric continue; 3152900Seric 3162900Seric /* we have a possible date */ 3172900Seric for (dt = MonthList; *dt != NULL; dt++) 3182900Seric if (strncmp(*dt, p, 3) == 0) 3192900Seric break; 3202900Seric 3212900Seric if (*dt != NULL) 3222900Seric break; 3232900Seric } 3242900Seric 3252900Seric if (*p != NULL) 3262900Seric { 3272900Seric /* we have found a date */ 3282900Seric Date = xalloc(25); 3292900Seric strncpy(Date, p, 25); 3302900Seric Date[24] = '\0'; 3312900Seric } 3322900Seric } 3332900Seric /* 3342900Seric ** HVALUE -- return value of a header. 3352900Seric ** 3362900Seric ** Parameters: 3372900Seric ** field -- the field name. 3382900Seric ** 3392900Seric ** Returns: 3402900Seric ** pointer to the value part. 3412900Seric ** NULL if not found. 3422900Seric ** 3432900Seric ** Side Effects: 3442900Seric ** sets the H_USED bit in the header if found. 3452900Seric */ 3462900Seric 3472900Seric char * 3482900Seric hvalue(field) 3492900Seric char *field; 3502900Seric { 3512900Seric register HDR *h; 3522900Seric 3532900Seric for (h = Header; h != NULL; h = h->h_link) 3542900Seric { 3552900Seric if (strcmp(h->h_field, field) == 0) 3562900Seric { 3572900Seric h->h_flags |= H_USED; 3582900Seric return (h->h_value); 3592900Seric } 3602900Seric } 3612900Seric return (NULL); 3622900Seric } 3632900Seric /* 3641392Seric ** MAKEMSGID -- Compute a message id for this process. 3651392Seric ** 3661392Seric ** This routine creates a message id for a message if 3671392Seric ** it did not have one already. If the MESSAGEID compile 3681392Seric ** flag is set, the messageid will be added to any message 3691392Seric ** that does not already have one. Currently it is more 3701392Seric ** of an artifact, but I suggest that if you are hacking, 3711392Seric ** you leave it in -- I may want to use it someday if 3721392Seric ** duplicate messages turn out to be a problem. 3731392Seric ** 3741392Seric ** Parameters: 3751392Seric ** none. 3761392Seric ** 3771392Seric ** Returns: 3782900Seric ** a message id. 3791392Seric ** 3801392Seric ** Side Effects: 3812900Seric ** none. 3821392Seric */ 3831392Seric 3842900Seric char * 3851392Seric makemsgid() 3861392Seric { 3871392Seric auto long t; 3881392Seric extern char *MyLocName; 3891392Seric extern char *ArpaHost; 3902900Seric static char buf[50]; 3911392Seric 3921392Seric time(&t); 3932900Seric sprintf(buf, "<%ld.%d.%s@%s>", t, getpid(), MyLocName, ArpaHost); 3942900Seric return (buf); 3951392Seric } 3962900Seric /* 3972900Seric ** ISHEADER -- predicate telling if argument is a header. 3982900Seric ** 3992900Seric ** Parameters: 4002900Seric ** s -- string to check for possible headerness. 4012900Seric ** 4022900Seric ** Returns: 4032900Seric ** TRUE if s is a header. 4042900Seric ** FALSE otherwise. 4052900Seric ** 4062900Seric ** Side Effects: 4072900Seric ** none. 4082900Seric */ 4092900Seric 4102900Seric bool 4112900Seric isheader(s) 4122900Seric register char *s; 4132900Seric { 4142900Seric if (!isalnum(*s)) 4152900Seric return (FALSE); 4162900Seric while (!isspace(*s) && *s != ':') 4172900Seric s++; 4182900Seric while (isspace(*s)) 4192900Seric s++; 4202900Seric return (*s == ':'); 4212900Seric } 422