122697Sdist /* 234920Sbostic * Copyright (c) 1983 Eric P. Allman 362522Sbostic * Copyright (c) 1988, 1993 462522Sbostic * The Regents of the University of California. All rights reserved. 533728Sbostic * 642824Sbostic * %sccs.include.redist.c% 733728Sbostic */ 822697Sdist 922697Sdist #ifndef lint 10*65580Seric static char sccsid[] = "@(#)collect.c 8.8 (Berkeley) 01/08/94"; 1133728Sbostic #endif /* not lint */ 1222697Sdist 131439Seric # include <errno.h> 143309Seric # include "sendmail.h" 151392Seric 161392Seric /* 172969Seric ** COLLECT -- read & parse message header & make temp file. 181392Seric ** 191392Seric ** Creates a temporary file name and copies the standard 209371Seric ** input to that file. Leading UNIX-style "From" lines are 219371Seric ** stripped off (after important information is extracted). 221392Seric ** 231392Seric ** Parameters: 2452106Seric ** smtpmode -- if set, we are running SMTP: give an RFC821 2552105Seric ** style message to say we are ready to collect 2652105Seric ** input, and never ignore a single dot to mean 2752105Seric ** end of message. 2858929Seric ** requeueflag -- this message will be requeued later, so 2958929Seric ** don't do final processing on it. 3058929Seric ** e -- the current envelope. 311392Seric ** 321392Seric ** Returns: 334162Seric ** none. 341392Seric ** 351392Seric ** Side Effects: 361392Seric ** Temp file is created and filled. 374162Seric ** The from person may be set. 381392Seric */ 391392Seric 4058929Seric collect(smtpmode, requeueflag, e) 4152105Seric bool smtpmode; 4258929Seric bool requeueflag; 4355012Seric register ENVELOPE *e; 441392Seric { 451392Seric register FILE *tf; 4652105Seric bool ignrdot = smtpmode ? FALSE : IgnrDot; 4757135Seric char buf[MAXLINE], buf2[MAXLINE]; 4840965Sbostic register char *workbuf, *freebuf; 4964718Seric bool inputerr = FALSE; 502900Seric extern char *hvalue(); 5140965Sbostic extern bool isheader(), flusheol(); 521392Seric 531392Seric /* 541392Seric ** Create the temp file name and create the file. 551392Seric */ 561392Seric 5764086Seric e->e_df = queuename(e, 'd'); 5864086Seric e->e_df = newstr(e->e_df); 5959745Seric if ((tf = dfopen(e->e_df, O_WRONLY|O_CREAT, FileMode)) == NULL) 601392Seric { 6155012Seric syserr("Cannot create %s", e->e_df); 625366Seric NoReturn = TRUE; 635366Seric finis(); 641392Seric } 651392Seric 664316Seric /* 674322Seric ** Tell ARPANET to go ahead. 684322Seric */ 694322Seric 7052105Seric if (smtpmode) 7158151Seric message("354 Enter mail, end with \".\" on a line by itself"); 724322Seric 734322Seric /* 744316Seric ** Try to read a UNIX-style From line 754316Seric */ 764316Seric 7761093Seric if (sfgets(buf, MAXLINE, InChannel, TimeOuts.to_datablock, 7861093Seric "initial message read") == NULL) 7940965Sbostic goto readerr; 804557Seric fixcrlf(buf, FALSE); 814321Seric # ifndef NOTUNIX 824322Seric if (!SaveFrom && strncmp(buf, "From ", 5) == 0) 832900Seric { 8440965Sbostic if (!flusheol(buf, InChannel)) 8540965Sbostic goto readerr; 8655012Seric eatfrom(buf, e); 8761093Seric if (sfgets(buf, MAXLINE, InChannel, TimeOuts.to_datablock, 8861093Seric "message header read") == NULL) 8940965Sbostic goto readerr; 904557Seric fixcrlf(buf, FALSE); 912900Seric } 9256795Seric # endif /* NOTUNIX */ 932900Seric 941392Seric /* 955975Seric ** Copy InChannel to temp file & do message editing. 961392Seric ** To keep certain mailers from getting confused, 971392Seric ** and to keep the output clean, lines that look 9813932Seric ** like UNIX "From" lines are deleted in the header. 991392Seric */ 1001392Seric 10140965Sbostic workbuf = buf; /* `workbuf' contains a header field */ 10240965Sbostic freebuf = buf2; /* `freebuf' can be used for read-ahead */ 10340965Sbostic for (;;) 1041392Seric { 10557135Seric char *curbuf; 10657135Seric int curbuffree; 10757135Seric register int curbuflen; 10857135Seric char *p; 10957135Seric 11040965Sbostic /* first, see if the header is over */ 11140965Sbostic if (!isheader(workbuf)) 11240965Sbostic { 11340965Sbostic fixcrlf(workbuf, TRUE); 11419036Seric break; 11540965Sbostic } 11619036Seric 1177681Seric /* if the line is too long, throw the rest away */ 11840965Sbostic if (!flusheol(workbuf, InChannel)) 11940965Sbostic goto readerr; 1207681Seric 12140965Sbostic /* it's okay to toss '\n' now (flusheol() needed it) */ 12240965Sbostic fixcrlf(workbuf, TRUE); 1234557Seric 12457135Seric curbuf = workbuf; 12557135Seric curbuflen = strlen(curbuf); 12657135Seric curbuffree = MAXLINE - curbuflen; 12757135Seric p = curbuf + curbuflen; 1282900Seric 1292900Seric /* get the rest of this field */ 13040965Sbostic for (;;) 1311392Seric { 13257135Seric int clen; 13357135Seric 13461093Seric if (sfgets(freebuf, MAXLINE, InChannel, 13561093Seric TimeOuts.to_datablock, 13661093Seric "message header read") == NULL) 13764916Seric { 13864916Seric freebuf[0] = '\0'; 13964916Seric break; 14064916Seric } 14140965Sbostic 14240965Sbostic /* is this a continuation line? */ 14340965Sbostic if (*freebuf != ' ' && *freebuf != '\t') 1442900Seric break; 14540965Sbostic 14640965Sbostic if (!flusheol(freebuf, InChannel)) 14740965Sbostic goto readerr; 14840965Sbostic 14957135Seric fixcrlf(freebuf, TRUE); 15057135Seric clen = strlen(freebuf) + 1; 15157135Seric 15257135Seric /* if insufficient room, dynamically allocate buffer */ 15357135Seric if (clen >= curbuffree) 15440965Sbostic { 15557135Seric /* reallocate buffer */ 15657135Seric int nbuflen = ((p - curbuf) + clen) * 2; 15757135Seric char *nbuf = xalloc(nbuflen); 15840965Sbostic 15957135Seric p = nbuf + curbuflen; 16057135Seric curbuffree = nbuflen - curbuflen; 16157135Seric bcopy(curbuf, nbuf, curbuflen); 16257135Seric if (curbuf != buf && curbuf != buf2) 16357135Seric free(curbuf); 16457135Seric curbuf = nbuf; 16540965Sbostic } 16657135Seric *p++ = '\n'; 16757135Seric bcopy(freebuf, p, clen - 1); 16857135Seric p += clen - 1; 16957135Seric curbuffree -= clen; 17057135Seric curbuflen += clen; 1711392Seric } 17257135Seric *p++ = '\0'; 1731392Seric 17457135Seric e->e_msgsize += curbuflen; 1751392Seric 1762900Seric /* 17740965Sbostic ** The working buffer now becomes the free buffer, since 17840965Sbostic ** the free buffer contains a new header field. 17940965Sbostic ** 18040965Sbostic ** This is premature, since we still havent called 18140965Sbostic ** chompheader() to process the field we just created 18240965Sbostic ** (so the call to chompheader() will use `freebuf'). 18340965Sbostic ** This convolution is necessary so that if we break out 18440965Sbostic ** of the loop due to H_EOH, `workbuf' will always be 18540965Sbostic ** the next unprocessed buffer. 18640965Sbostic */ 18740965Sbostic 18840965Sbostic { 18940965Sbostic register char *tmp = workbuf; 19040965Sbostic workbuf = freebuf; 19140965Sbostic freebuf = tmp; 19240965Sbostic } 19340965Sbostic 19440965Sbostic /* 1952900Seric ** Snarf header away. 1962900Seric */ 1972900Seric 19857135Seric if (bitset(H_EOH, chompheader(curbuf, FALSE, e))) 1993058Seric break; 20057135Seric 20157135Seric /* 20257135Seric ** If the buffer was dynamically allocated, free it. 20357135Seric */ 20457135Seric 20557135Seric if (curbuf != buf && curbuf != buf2) 20657135Seric free(curbuf); 20740965Sbostic } 2081392Seric 2097673Seric if (tTd(30, 1)) 2102900Seric printf("EOH\n"); 2112900Seric 21240965Sbostic if (*workbuf == '\0') 21340965Sbostic { 21440965Sbostic /* throw away a blank line */ 21561093Seric if (sfgets(buf, MAXLINE, InChannel, TimeOuts.to_datablock, 21661093Seric "message separator read") == NULL) 21740965Sbostic goto readerr; 21840965Sbostic } 21940965Sbostic else if (workbuf == buf2) /* guarantee `buf' contains data */ 22040965Sbostic (void) strcpy(buf, buf2); 2212900Seric 2222900Seric /* 2232900Seric ** Collect the body of the message. 2242900Seric */ 2252900Seric 22664718Seric for (;;) 2272900Seric { 2284551Seric register char *bp = buf; 2294156Seric 2307852Seric fixcrlf(buf, TRUE); 2314557Seric 2322900Seric /* check for end-of-message */ 23352105Seric if (!ignrdot && buf[0] == '.' && (buf[1] == '\n' || buf[1] == '\0')) 2342900Seric break; 2352900Seric 2364551Seric /* check for transparent dot */ 237*65580Seric if ((OpMode == MD_SMTP || OpMode == MD_DAEMON) && 238*65580Seric bp[0] == '.' && bp[1] == '.') 2394551Seric bp++; 2404551Seric 2414156Seric /* 2424156Seric ** Figure message length, output the line to the temp 2434156Seric ** file, and insert a newline if missing. 2444156Seric */ 2454156Seric 24655012Seric e->e_msgsize += strlen(bp) + 1; 2474551Seric fputs(bp, tf); 2487852Seric fputs("\n", tf); 2491392Seric if (ferror(tf)) 25055012Seric tferror(tf, e); 25164718Seric if (sfgets(buf, MAXLINE, InChannel, TimeOuts.to_datablock, 25264718Seric "message body read") == NULL) 25364718Seric goto readerr; 25464718Seric } 25540965Sbostic 25664718Seric if (feof(InChannel) || ferror(InChannel)) 25764718Seric { 25840965Sbostic readerr: 25964916Seric if (tTd(30, 1)) 26064916Seric printf("collect: read error\n"); 26164718Seric inputerr = TRUE; 26264718Seric } 26364718Seric 26411544Seric if (fflush(tf) != 0) 26555012Seric tferror(tf, e); 26664762Seric if (fsync(fileno(tf)) < 0 || fclose(tf) < 0) 26764762Seric { 26864762Seric syserr("cannot sync message data to disk (%s)", e->e_df); 26964762Seric finis(); 27064762Seric } 2712900Seric 27211145Seric /* An EOF when running SMTP is an error */ 273*65580Seric if (inputerr && (OpMode == MD_SMTP || OpMode == MD_DAEMON)) 27416136Seric { 27558308Seric char *host; 27664718Seric char *problem; 27758082Seric 27858308Seric host = RealHostName; 27958308Seric if (host == NULL) 28058308Seric host = "localhost"; 28158308Seric 28264718Seric if (feof(InChannel)) 28364718Seric problem = "unexpected close"; 28464718Seric else if (ferror(InChannel)) 28564718Seric problem = "I/O error"; 28664718Seric else 28764718Seric problem = "read timeout"; 28836233Skarels # ifdef LOG 28958308Seric if (LogLevel > 0 && feof(InChannel)) 29036230Skarels syslog(LOG_NOTICE, 29164718Seric "collect: %s on connection from %s, sender=%s: %m\n", 29264718Seric problem, host, e->e_from.q_paddr); 29336233Skarels # endif 29458082Seric (feof(InChannel) ? usrerr : syserr) 29564718Seric ("451 collect: %s on connection from %s, from=%s", 29664718Seric problem, host, e->e_from.q_paddr); 29711145Seric 29816136Seric /* don't return an error indication */ 29955012Seric e->e_to = NULL; 30055012Seric e->e_flags &= ~EF_FATALERRS; 30164124Seric e->e_flags |= EF_CLRQUEUE; 30216136Seric 30316136Seric /* and don't try to deliver the partial message either */ 30464718Seric if (InChild) 30564718Seric ExitStat = EX_QUIT; 30616136Seric finis(); 30716136Seric } 30816136Seric 3092900Seric /* 3102900Seric ** Find out some information from the headers. 3113386Seric ** Examples are who is the from person & the date. 3122900Seric */ 3132900Seric 31458929Seric eatheader(e, !requeueflag); 3157673Seric 31664068Seric /* collect statistics */ 31764068Seric if (OpMode != MD_VERIFY) 31864068Seric markstats(e, (ADDRESS *) NULL); 31964068Seric 3207782Seric /* 3217782Seric ** Add an Apparently-To: line if we have no recipient lines. 3227782Seric */ 3234622Seric 32455012Seric if (hvalue("to", e) == NULL && hvalue("cc", e) == NULL && 32555012Seric hvalue("bcc", e) == NULL && hvalue("apparently-to", e) == NULL) 3267367Seric { 3277367Seric register ADDRESS *q; 3287367Seric 3297367Seric /* create an Apparently-To: field */ 3307367Seric /* that or reject the message.... */ 33155012Seric for (q = e->e_sendqueue; q != NULL; q = q->q_next) 3327367Seric { 3337389Seric if (q->q_alias != NULL) 3347389Seric continue; 3357673Seric if (tTd(30, 3)) 3367367Seric printf("Adding Apparently-To: %s\n", q->q_paddr); 33759579Seric addheader("Apparently-To", q->q_paddr, e); 3387367Seric } 3397367Seric } 3407367Seric 34159320Seric /* check for message too large */ 34259320Seric if (MaxMessageSize > 0 && e->e_msgsize > MaxMessageSize) 34359320Seric { 34459320Seric usrerr("552 Message exceeds maximum fixed size (%ld)", 34559320Seric MaxMessageSize); 34659320Seric } 34759320Seric 34855012Seric if ((e->e_dfp = fopen(e->e_df, "r")) == NULL) 34958690Seric { 35058690Seric /* we haven't acked receipt yet, so just chuck this */ 35155012Seric syserr("Cannot reopen %s", e->e_df); 35258690Seric finis(); 35358690Seric } 3541392Seric } 3551392Seric /* 35640965Sbostic ** FLUSHEOL -- if not at EOL, throw away rest of input line. 35740965Sbostic ** 35840965Sbostic ** Parameters: 35940965Sbostic ** buf -- last line read in (checked for '\n'), 36040965Sbostic ** fp -- file to be read from. 36140965Sbostic ** 36240965Sbostic ** Returns: 36340965Sbostic ** FALSE on error from sfgets(), TRUE otherwise. 36440965Sbostic ** 36540965Sbostic ** Side Effects: 36640965Sbostic ** none. 36740965Sbostic */ 36840965Sbostic 36940965Sbostic bool 37040965Sbostic flusheol(buf, fp) 37140965Sbostic char *buf; 37240965Sbostic FILE *fp; 37340965Sbostic { 37440965Sbostic register char *p = buf; 37557134Seric bool printmsg = TRUE; 37657134Seric char junkbuf[MAXLINE]; 37740965Sbostic 37857134Seric while (strchr(p, '\n') == NULL) 37957134Seric { 38057134Seric if (printmsg) 38158151Seric usrerr("553 header line too long"); 38257134Seric printmsg = FALSE; 38361093Seric if (sfgets(junkbuf, MAXLINE, fp, TimeOuts.to_datablock, 38461093Seric "long line flush") == NULL) 38557134Seric return (FALSE); 38640965Sbostic p = junkbuf; 38740965Sbostic } 38840965Sbostic 38957134Seric return (TRUE); 39040965Sbostic } 39140965Sbostic /* 39211544Seric ** TFERROR -- signal error on writing the temporary file. 39311544Seric ** 39411544Seric ** Parameters: 39511544Seric ** tf -- the file pointer for the temporary file. 39611544Seric ** 39711544Seric ** Returns: 39811544Seric ** none. 39911544Seric ** 40011544Seric ** Side Effects: 40111544Seric ** Gives an error message. 40211544Seric ** Arranges for following output to go elsewhere. 40311544Seric */ 40411544Seric 40555012Seric tferror(tf, e) 40611544Seric FILE *tf; 40755012Seric register ENVELOPE *e; 40811544Seric { 40911544Seric if (errno == ENOSPC) 41011544Seric { 41155012Seric (void) freopen(e->e_df, "w", tf); 41211544Seric fputs("\nMAIL DELETED BECAUSE OF LACK OF DISK SPACE\n\n", tf); 41311544Seric usrerr("452 Out of disk space for temp file"); 41411544Seric } 41511544Seric else 41655012Seric syserr("collect: Cannot write %s", e->e_df); 41711544Seric (void) freopen("/dev/null", "w", tf); 41811544Seric } 41911544Seric /* 4202900Seric ** EATFROM -- chew up a UNIX style from line and process 4212900Seric ** 4222900Seric ** This does indeed make some assumptions about the format 4232900Seric ** of UNIX messages. 4242900Seric ** 4252900Seric ** Parameters: 4262900Seric ** fm -- the from line. 4272900Seric ** 4282900Seric ** Returns: 4292900Seric ** none. 4302900Seric ** 4312900Seric ** Side Effects: 4322900Seric ** extracts what information it can from the header, 4333386Seric ** such as the date. 4342900Seric */ 4352900Seric 4364321Seric # ifndef NOTUNIX 4374321Seric 4384203Seric char *DowList[] = 4394203Seric { 4404203Seric "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", NULL 4414203Seric }; 4424203Seric 4432900Seric char *MonthList[] = 4442900Seric { 4452900Seric "Jan", "Feb", "Mar", "Apr", "May", "Jun", 4462900Seric "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", 4472900Seric NULL 4482900Seric }; 4492900Seric 45055012Seric eatfrom(fm, e) 4512900Seric char *fm; 45255012Seric register ENVELOPE *e; 4532900Seric { 4542900Seric register char *p; 4552900Seric register char **dt; 4562900Seric 4577673Seric if (tTd(30, 2)) 4584203Seric printf("eatfrom(%s)\n", fm); 4594203Seric 4602900Seric /* find the date part */ 4612900Seric p = fm; 4622900Seric while (*p != '\0') 4632900Seric { 4642900Seric /* skip a word */ 4652900Seric while (*p != '\0' && *p != ' ') 46616896Seric p++; 4672900Seric while (*p == ' ') 46816896Seric p++; 46958050Seric if (!(isascii(*p) && isupper(*p)) || 47058050Seric p[3] != ' ' || p[13] != ':' || p[16] != ':') 4712900Seric continue; 4722900Seric 4732900Seric /* we have a possible date */ 4744203Seric for (dt = DowList; *dt != NULL; dt++) 4752900Seric if (strncmp(*dt, p, 3) == 0) 4762900Seric break; 4774203Seric if (*dt == NULL) 4784203Seric continue; 4792900Seric 4804203Seric for (dt = MonthList; *dt != NULL; dt++) 4814203Seric if (strncmp(*dt, &p[4], 3) == 0) 4824203Seric break; 4832900Seric if (*dt != NULL) 4842900Seric break; 4852900Seric } 4862900Seric 48760502Seric if (*p != '\0') 4882900Seric { 4893386Seric char *q; 4905366Seric extern char *arpadate(); 4913386Seric 4922900Seric /* we have found a date */ 4933386Seric q = xalloc(25); 49423103Seric (void) strncpy(q, p, 25); 4953386Seric q[24] = '\0'; 4965366Seric q = arpadate(q); 49755012Seric define('a', newstr(q), e); 4982900Seric } 4992900Seric } 5004321Seric 50156795Seric # endif /* NOTUNIX */ 502