11392Seric # include <stdio.h> 21392Seric # include <ctype.h> 31439Seric # include <errno.h> 42969Seric # include "postbox.h" 51392Seric 6*2988Seric static char SccsId[] = "@(#)collect.c 3.3 03/07/81"; 71392Seric 81392Seric /* 92969Seric ** 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 * 452969Seric 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; 64*2988Seric extern char *strcpy(), *strcat(), *mktemp(); 651392Seric 661392Seric /* 671392Seric ** Create the temp file name and create the file. 681392Seric */ 691392Seric 701392Seric mktemp(InFileName); 711392Seric close(creat(InFileName, 0600)); 721392Seric if ((tf = fopen(InFileName, "w")) == NULL) 731392Seric { 741392Seric syserr("Cannot create %s", InFileName); 751392Seric return (NULL); 761392Seric } 771392Seric 782900Seric /* try to read a UNIX-style From line */ 792900Seric if (fgets(buf, sizeof buf, stdin) == NULL) 802900Seric return (NULL); 812900Seric if (strncmp(buf, "From ", 5) == 0) 822900Seric { 832900Seric eatfrom(buf); 842900Seric fgets(buf, sizeof buf, stdin); 852900Seric } 862900Seric 871392Seric /* 881392Seric ** Copy stdin to temp file & do message editting. 891392Seric ** To keep certain mailers from getting confused, 901392Seric ** and to keep the output clean, lines that look 911392Seric ** like UNIX "From" lines are deleted in the header, 921392Seric ** and prepended with ">" in the body. 931392Seric */ 941392Seric 952900Seric for (; !feof(stdin); !feof(stdin) && fgets(buf, sizeof buf, stdin)) 961392Seric { 972900Seric /* see if the header is over */ 982900Seric if (!isheader(buf)) 992900Seric break; 1002900Seric 1012900Seric /* get the rest of this field */ 1022900Seric while ((c = getc(stdin)) == ' ' || c == '\t') 1031392Seric { 1042900Seric p = &buf[strlen(buf)]; 1052900Seric *p++ = c; 1062900Seric if (fgets(p, sizeof buf - (p - buf), stdin) == NULL) 1072900Seric break; 1081392Seric } 1092900Seric if (c != EOF) 1102900Seric ungetc(c, stdin); 1111392Seric 1122900Seric MsgSize += strlen(buf); 1131392Seric 1142900Seric /* 1152900Seric ** Snarf header away. 1162900Seric */ 1172900Seric 1182900Seric /* strip off trailing newline */ 1192900Seric p = rindex(buf, '\n'); 1202900Seric if (p != NULL) 1212900Seric *p = '\0'; 1222900Seric 1232900Seric /* find canonical name */ 1242900Seric fname = buf; 1252900Seric p = index(buf, ':'); 1262900Seric fvalue = &p[1]; 1272900Seric while (isspace(*--p)) 1282900Seric continue; 1292900Seric *++p = '\0'; 1302900Seric makelower(fname); 1312900Seric 1322900Seric /* strip field value on front */ 1332900Seric if (*fvalue == ' ') 1342900Seric fvalue++; 1352900Seric 1362900Seric /* search header list for this header */ 1372900Seric for (hp = &Header, h = Header; h != NULL; hp = &h->h_link, h = h->h_link) 1381392Seric { 1392969Seric if (strcmp(fname, h->h_field) == 0 && bitset(H_DEFAULT, h->h_flags)) 1402900Seric break; 1412900Seric } 1422900Seric if (h == NULL) 1432900Seric { 1442900Seric /* create a new node */ 1452900Seric *hp = h = (HDR *) xalloc(sizeof *h); 1462900Seric h->h_field = newstr(fname); 1472900Seric h->h_value = NULL; 1482900Seric h->h_link = NULL; 1492900Seric h->h_flags = 0; 1502900Seric 1512900Seric /* see if it is a known type */ 1522900Seric for (hi = HdrInfo; hi->hi_field != NULL; hi++) 1531392Seric { 1542900Seric if (strcmp(hi->hi_field, h->h_field) == 0) 1552900Seric { 1562900Seric h->h_flags = hi->hi_flags; 1572900Seric break; 1582900Seric } 1591392Seric } 1602900Seric } 1612969Seric else if (bitset(H_DEFAULT, h->h_flags)) 1622900Seric { 1632900Seric /* overriding default, throw out old value */ 1642900Seric free(h->h_value); 1652900Seric h->h_value = NULL; 1661392Seric } 1671392Seric 1682900Seric /* do something with the value */ 1692900Seric if (h->h_value == NULL) 1701392Seric { 1712900Seric h->h_value = newstr(fvalue); 1721392Seric } 1732900Seric else 1742900Seric { 175*2988Seric register unsigned len; 1761392Seric 1772900Seric /* concatenate the two values */ 1782900Seric len = strlen(h->h_value) + strlen(fvalue) + 2; 1792900Seric p = xalloc(len); 1802900Seric strcpy(p, h->h_value); 1812900Seric strcat(p, ","); 1822900Seric strcat(p, fvalue); 1832900Seric free(h->h_value); 1842900Seric h->h_value = p; 1851392Seric } 1862900Seric } 1871392Seric 1882900Seric # ifdef DEBUG 1892900Seric if (Debug) 1902900Seric printf("EOH\n"); 1912900Seric # endif DEBUG 1922900Seric 1932900Seric /* throw away a blank line */ 1942900Seric if (buf[0] == '\n') 1952900Seric fgets(buf, sizeof buf, stdin); 1962900Seric 1972900Seric /* 1982900Seric ** Collect the body of the message. 1992900Seric */ 2002900Seric 2012900Seric for (; !feof(stdin); !feof(stdin) && fgets(buf, sizeof buf, stdin) != NULL) 2022900Seric { 2032900Seric /* check for end-of-message */ 2042900Seric if (!IgnrDot && buf[0] == '.' && (buf[1] == '\n' || buf[1] == '\0')) 2052900Seric break; 2062900Seric 2072900Seric /* Hide UNIX-like From lines */ 2082900Seric if (strncmp(buf, "From ", 5) == 0) 2091392Seric { 2102900Seric fputs(">", tf); 2112900Seric MsgSize++; 2121392Seric } 2131624Seric MsgSize += strlen(buf); 2141392Seric fputs(buf, tf); 2151392Seric if (ferror(tf)) 2161392Seric { 2171439Seric if (errno == ENOSPC) 2181439Seric { 2191439Seric freopen(InFileName, "w", tf); 2201439Seric fputs("\nMAIL DELETED BECAUSE OF LACK OF DISK SPACE\n\n", tf); 2211439Seric syserr("Out of disk space for temp file"); 2221439Seric } 2231439Seric else 2241439Seric syserr("Cannot write %s", InFileName); 2251439Seric freopen("/dev/null", "w", tf); 2261392Seric } 2271392Seric } 2281392Seric fclose(tf); 2292900Seric 2302900Seric /* 2312900Seric ** Find out some information from the headers. 2322900Seric ** Examples are who is the from person, the date, the 2332900Seric ** message-id, etc. 2342900Seric */ 2352900Seric 2362900Seric /* from person */ 2372900Seric xfrom = hvalue("sender"); 2382900Seric if (xfrom == NULL) 2392900Seric xfrom = hvalue("from"); 2402900Seric 2412900Seric /* date message originated */ 2422900Seric /* we don't seem to have a good way to do canonical conversion .... 2432900Seric p = hvalue("date"); 2442900Seric if (p != NULL) 2452900Seric Date = newstr(arpatounix(p)); 2462900Seric .... so we will ignore the problem for the time being */ 2472900Seric if (Date == NULL) 2482900Seric { 2492900Seric auto long t; 2502900Seric extern char *ctime(); 2512900Seric 2522900Seric time(&t); 2532900Seric Date = newstr(ctime(&t)); 2542900Seric } 2552900Seric 2562900Seric /* message id */ 2572900Seric MsgId = hvalue("message-id"); 2582900Seric if (MsgId == NULL) 2592900Seric MsgId = makemsgid(); 2602900Seric 2611392Seric if (freopen(InFileName, "r", stdin) == NULL) 2621392Seric syserr("Cannot reopen %s", InFileName); 2632900Seric 2642900Seric # ifdef DEBUG 2652900Seric if (Debug) 2662900Seric { 2672900Seric printf("----- collected header -----\n"); 2682900Seric for (h = Header; h != NULL; h = h->h_link) 2692900Seric printf("%s: %s\n", capitalize(h->h_field), h->h_value); 2702900Seric printf("----------------------------\n"); 2712900Seric } 2722900Seric # endif DEBUG 2732900Seric return (ArpaFmt ? xfrom : NULL); 2741392Seric } 2751392Seric /* 2762900Seric ** EATFROM -- chew up a UNIX style from line and process 2772900Seric ** 2782900Seric ** This does indeed make some assumptions about the format 2792900Seric ** of UNIX messages. 2802900Seric ** 2812900Seric ** Parameters: 2822900Seric ** fm -- the from line. 2832900Seric ** 2842900Seric ** Returns: 2852900Seric ** none. 2862900Seric ** 2872900Seric ** Side Effects: 2882900Seric ** extracts what information it can from the header, 2892900Seric ** such as the Date. 2902900Seric */ 2912900Seric 2922900Seric char *MonthList[] = 2932900Seric { 2942900Seric "Jan", "Feb", "Mar", "Apr", "May", "Jun", 2952900Seric "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", 2962900Seric NULL 2972900Seric }; 2982900Seric 2992900Seric eatfrom(fm) 3002900Seric char *fm; 3012900Seric { 3022900Seric register char *p; 3032900Seric register char **dt; 3042900Seric 3052900Seric /* find the date part */ 3062900Seric p = fm; 3072900Seric while (*p != '\0') 3082900Seric { 3092900Seric /* skip a word */ 3102900Seric while (*p != '\0' && *p != ' ') 3112900Seric *p++; 3122900Seric while (*p == ' ') 3132900Seric *p++; 3142900Seric if (!isupper(*p) || p[3] != ' ' || p[13] != ':' || p[16] != ':') 3152900Seric continue; 3162900Seric 3172900Seric /* we have a possible date */ 3182900Seric for (dt = MonthList; *dt != NULL; dt++) 3192900Seric if (strncmp(*dt, p, 3) == 0) 3202900Seric break; 3212900Seric 3222900Seric if (*dt != NULL) 3232900Seric break; 3242900Seric } 3252900Seric 3262900Seric if (*p != NULL) 3272900Seric { 3282900Seric /* we have found a date */ 3292900Seric Date = xalloc(25); 3302900Seric strncpy(Date, p, 25); 3312900Seric Date[24] = '\0'; 3322900Seric } 3332900Seric } 3342900Seric /* 3352900Seric ** HVALUE -- return value of a header. 3362900Seric ** 3372900Seric ** Parameters: 3382900Seric ** field -- the field name. 3392900Seric ** 3402900Seric ** Returns: 3412900Seric ** pointer to the value part. 3422900Seric ** NULL if not found. 3432900Seric ** 3442900Seric ** Side Effects: 3452900Seric ** sets the H_USED bit in the header if found. 3462900Seric */ 3472900Seric 3482900Seric char * 3492900Seric hvalue(field) 3502900Seric char *field; 3512900Seric { 3522900Seric register HDR *h; 3532900Seric 3542900Seric for (h = Header; h != NULL; h = h->h_link) 3552900Seric { 3562900Seric if (strcmp(h->h_field, field) == 0) 3572900Seric { 3582900Seric h->h_flags |= H_USED; 3592900Seric return (h->h_value); 3602900Seric } 3612900Seric } 3622900Seric return (NULL); 3632900Seric } 3642900Seric /* 3651392Seric ** MAKEMSGID -- Compute a message id for this process. 3661392Seric ** 3671392Seric ** This routine creates a message id for a message if 3681392Seric ** it did not have one already. If the MESSAGEID compile 3691392Seric ** flag is set, the messageid will be added to any message 3701392Seric ** that does not already have one. Currently it is more 3711392Seric ** of an artifact, but I suggest that if you are hacking, 3721392Seric ** you leave it in -- I may want to use it someday if 3731392Seric ** duplicate messages turn out to be a problem. 3741392Seric ** 3751392Seric ** Parameters: 3761392Seric ** none. 3771392Seric ** 3781392Seric ** Returns: 3792900Seric ** a message id. 3801392Seric ** 3811392Seric ** Side Effects: 3822900Seric ** none. 3831392Seric */ 3841392Seric 3852900Seric char * 3861392Seric makemsgid() 3871392Seric { 3881392Seric auto long t; 3891392Seric extern char *MyLocName; 3901392Seric extern char *ArpaHost; 3912900Seric static char buf[50]; 392*2988Seric extern long time(); 393*2988Seric extern char *sprintf(); 3941392Seric 3951392Seric time(&t); 3962900Seric sprintf(buf, "<%ld.%d.%s@%s>", t, getpid(), MyLocName, ArpaHost); 3972900Seric return (buf); 3981392Seric } 3992900Seric /* 4002900Seric ** ISHEADER -- predicate telling if argument is a header. 4012900Seric ** 4022900Seric ** Parameters: 4032900Seric ** s -- string to check for possible headerness. 4042900Seric ** 4052900Seric ** Returns: 4062900Seric ** TRUE if s is a header. 4072900Seric ** FALSE otherwise. 4082900Seric ** 4092900Seric ** Side Effects: 4102900Seric ** none. 4112900Seric */ 4122900Seric 4132900Seric bool 4142900Seric isheader(s) 4152900Seric register char *s; 4162900Seric { 4172900Seric if (!isalnum(*s)) 4182900Seric return (FALSE); 4192900Seric while (!isspace(*s) && *s != ':') 4202900Seric s++; 4212900Seric while (isspace(*s)) 4222900Seric s++; 4232900Seric return (*s == ':'); 4242900Seric } 425