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*66796Seric static char sccsid[] = "@(#)collect.c 8.12 (Berkeley) 04/15/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 40*66796Seric char *CollectErrorMessage; 41*66796Seric bool CollectErrno; 42*66796Seric 4358929Seric collect(smtpmode, requeueflag, e) 4452105Seric bool smtpmode; 4558929Seric bool requeueflag; 4655012Seric register ENVELOPE *e; 471392Seric { 481392Seric register FILE *tf; 4952105Seric bool ignrdot = smtpmode ? FALSE : IgnrDot; 5057135Seric char buf[MAXLINE], buf2[MAXLINE]; 5140965Sbostic register char *workbuf, *freebuf; 5264718Seric bool inputerr = FALSE; 532900Seric extern char *hvalue(); 5440965Sbostic extern bool isheader(), flusheol(); 551392Seric 56*66796Seric CollectErrorMessage = NULL; 57*66796Seric CollectErrno = 0; 58*66796Seric 591392Seric /* 601392Seric ** Create the temp file name and create the file. 611392Seric */ 621392Seric 6364086Seric e->e_df = queuename(e, 'd'); 6464086Seric e->e_df = newstr(e->e_df); 6559745Seric if ((tf = dfopen(e->e_df, O_WRONLY|O_CREAT, FileMode)) == NULL) 661392Seric { 6755012Seric syserr("Cannot create %s", e->e_df); 685366Seric NoReturn = TRUE; 695366Seric finis(); 701392Seric } 711392Seric 724316Seric /* 734322Seric ** Tell ARPANET to go ahead. 744322Seric */ 754322Seric 7652105Seric if (smtpmode) 7758151Seric message("354 Enter mail, end with \".\" on a line by itself"); 784322Seric 7966765Seric /* set global timer to monitor progress */ 8066765Seric sfgetset(TimeOuts.to_datablock); 8166765Seric 824322Seric /* 834316Seric ** Try to read a UNIX-style From line 844316Seric */ 854316Seric 8661093Seric if (sfgets(buf, MAXLINE, InChannel, TimeOuts.to_datablock, 8761093Seric "initial message read") == NULL) 8840965Sbostic goto readerr; 894557Seric fixcrlf(buf, FALSE); 904321Seric # ifndef NOTUNIX 914322Seric if (!SaveFrom && strncmp(buf, "From ", 5) == 0) 922900Seric { 9340965Sbostic if (!flusheol(buf, InChannel)) 9440965Sbostic goto readerr; 9555012Seric eatfrom(buf, e); 9661093Seric if (sfgets(buf, MAXLINE, InChannel, TimeOuts.to_datablock, 9761093Seric "message header read") == NULL) 9840965Sbostic goto readerr; 994557Seric fixcrlf(buf, FALSE); 1002900Seric } 10156795Seric # endif /* NOTUNIX */ 1022900Seric 1031392Seric /* 1045975Seric ** Copy InChannel to temp file & do message editing. 1051392Seric ** To keep certain mailers from getting confused, 1061392Seric ** and to keep the output clean, lines that look 10713932Seric ** like UNIX "From" lines are deleted in the header. 1081392Seric */ 1091392Seric 11040965Sbostic workbuf = buf; /* `workbuf' contains a header field */ 11140965Sbostic freebuf = buf2; /* `freebuf' can be used for read-ahead */ 11240965Sbostic for (;;) 1131392Seric { 11457135Seric char *curbuf; 11557135Seric int curbuffree; 11657135Seric register int curbuflen; 11757135Seric char *p; 11857135Seric 11940965Sbostic /* first, see if the header is over */ 12040965Sbostic if (!isheader(workbuf)) 12140965Sbostic { 12240965Sbostic fixcrlf(workbuf, TRUE); 12319036Seric break; 12440965Sbostic } 12519036Seric 1267681Seric /* if the line is too long, throw the rest away */ 12740965Sbostic if (!flusheol(workbuf, InChannel)) 12840965Sbostic goto readerr; 1297681Seric 13040965Sbostic /* it's okay to toss '\n' now (flusheol() needed it) */ 13140965Sbostic fixcrlf(workbuf, TRUE); 1324557Seric 13357135Seric curbuf = workbuf; 13457135Seric curbuflen = strlen(curbuf); 13557135Seric curbuffree = MAXLINE - curbuflen; 13657135Seric p = curbuf + curbuflen; 1372900Seric 1382900Seric /* get the rest of this field */ 13940965Sbostic for (;;) 1401392Seric { 14157135Seric int clen; 14257135Seric 14361093Seric if (sfgets(freebuf, MAXLINE, InChannel, 14461093Seric TimeOuts.to_datablock, 14561093Seric "message header read") == NULL) 14664916Seric { 14764916Seric freebuf[0] = '\0'; 14864916Seric break; 14964916Seric } 15040965Sbostic 15140965Sbostic /* is this a continuation line? */ 15240965Sbostic if (*freebuf != ' ' && *freebuf != '\t') 1532900Seric break; 15440965Sbostic 15540965Sbostic if (!flusheol(freebuf, InChannel)) 15640965Sbostic goto readerr; 15740965Sbostic 15857135Seric fixcrlf(freebuf, TRUE); 15957135Seric clen = strlen(freebuf) + 1; 16057135Seric 16157135Seric /* if insufficient room, dynamically allocate buffer */ 16257135Seric if (clen >= curbuffree) 16340965Sbostic { 16457135Seric /* reallocate buffer */ 16557135Seric int nbuflen = ((p - curbuf) + clen) * 2; 16657135Seric char *nbuf = xalloc(nbuflen); 16740965Sbostic 16857135Seric p = nbuf + curbuflen; 16957135Seric curbuffree = nbuflen - curbuflen; 17057135Seric bcopy(curbuf, nbuf, curbuflen); 17157135Seric if (curbuf != buf && curbuf != buf2) 17257135Seric free(curbuf); 17357135Seric curbuf = nbuf; 17440965Sbostic } 17557135Seric *p++ = '\n'; 17657135Seric bcopy(freebuf, p, clen - 1); 17757135Seric p += clen - 1; 17857135Seric curbuffree -= clen; 17957135Seric curbuflen += clen; 1801392Seric } 18157135Seric *p++ = '\0'; 1821392Seric 18357135Seric e->e_msgsize += curbuflen; 1841392Seric 1852900Seric /* 18640965Sbostic ** The working buffer now becomes the free buffer, since 18740965Sbostic ** the free buffer contains a new header field. 18840965Sbostic ** 18940965Sbostic ** This is premature, since we still havent called 19040965Sbostic ** chompheader() to process the field we just created 19140965Sbostic ** (so the call to chompheader() will use `freebuf'). 19240965Sbostic ** This convolution is necessary so that if we break out 19340965Sbostic ** of the loop due to H_EOH, `workbuf' will always be 19440965Sbostic ** the next unprocessed buffer. 19540965Sbostic */ 19640965Sbostic 19740965Sbostic { 19840965Sbostic register char *tmp = workbuf; 19940965Sbostic workbuf = freebuf; 20040965Sbostic freebuf = tmp; 20140965Sbostic } 20240965Sbostic 20340965Sbostic /* 2042900Seric ** Snarf header away. 2052900Seric */ 2062900Seric 20757135Seric if (bitset(H_EOH, chompheader(curbuf, FALSE, e))) 2083058Seric break; 20957135Seric 21057135Seric /* 21157135Seric ** If the buffer was dynamically allocated, free it. 21257135Seric */ 21357135Seric 21457135Seric if (curbuf != buf && curbuf != buf2) 21557135Seric free(curbuf); 21640965Sbostic } 2171392Seric 2187673Seric if (tTd(30, 1)) 2192900Seric printf("EOH\n"); 2202900Seric 22140965Sbostic if (*workbuf == '\0') 22240965Sbostic { 22340965Sbostic /* throw away a blank line */ 22461093Seric if (sfgets(buf, MAXLINE, InChannel, TimeOuts.to_datablock, 22561093Seric "message separator read") == NULL) 22640965Sbostic goto readerr; 22740965Sbostic } 22840965Sbostic else if (workbuf == buf2) /* guarantee `buf' contains data */ 22940965Sbostic (void) strcpy(buf, buf2); 2302900Seric 2312900Seric /* 2322900Seric ** Collect the body of the message. 2332900Seric */ 2342900Seric 23564718Seric for (;;) 2362900Seric { 2374551Seric register char *bp = buf; 2384156Seric 2397852Seric fixcrlf(buf, TRUE); 2404557Seric 2412900Seric /* check for end-of-message */ 24252105Seric if (!ignrdot && buf[0] == '.' && (buf[1] == '\n' || buf[1] == '\0')) 2432900Seric break; 2442900Seric 2454551Seric /* check for transparent dot */ 24665580Seric if ((OpMode == MD_SMTP || OpMode == MD_DAEMON) && 24765580Seric bp[0] == '.' && bp[1] == '.') 2484551Seric bp++; 2494551Seric 2504156Seric /* 2514156Seric ** Figure message length, output the line to the temp 2524156Seric ** file, and insert a newline if missing. 2534156Seric */ 2544156Seric 25555012Seric e->e_msgsize += strlen(bp) + 1; 2564551Seric fputs(bp, tf); 2577852Seric fputs("\n", tf); 2581392Seric if (ferror(tf)) 25955012Seric tferror(tf, e); 26064718Seric if (sfgets(buf, MAXLINE, InChannel, TimeOuts.to_datablock, 26164718Seric "message body read") == NULL) 26264718Seric goto readerr; 26364718Seric } 26440965Sbostic 26564718Seric if (feof(InChannel) || ferror(InChannel)) 26664718Seric { 26740965Sbostic readerr: 26864916Seric if (tTd(30, 1)) 26964916Seric printf("collect: read error\n"); 27064718Seric inputerr = TRUE; 27164718Seric } 27264718Seric 27366765Seric /* reset global timer */ 27466765Seric sfgetset((time_t) 0); 27566765Seric 27611544Seric if (fflush(tf) != 0) 27755012Seric tferror(tf, e); 27864762Seric if (fsync(fileno(tf)) < 0 || fclose(tf) < 0) 27964762Seric { 280*66796Seric tferror(tf, e); 28164762Seric finis(); 28264762Seric } 2832900Seric 284*66796Seric if (CollectErrorMessage != NULL && Errors <= 0) 28516136Seric { 286*66796Seric if (CollectErrno != 0) 287*66796Seric { 288*66796Seric errno = CollectErrno; 289*66796Seric syserr(CollectErrorMessage, e->e_df); 290*66796Seric finis(); 291*66796Seric } 292*66796Seric usrerr(CollectErrorMessage); 293*66796Seric } 294*66796Seric else if (inputerr && (OpMode == MD_SMTP || OpMode == MD_DAEMON)) 295*66796Seric { 296*66796Seric /* An EOF when running SMTP is an error */ 29758308Seric char *host; 29864718Seric char *problem; 29958082Seric 30058308Seric host = RealHostName; 30158308Seric if (host == NULL) 30258308Seric host = "localhost"; 30358308Seric 30464718Seric if (feof(InChannel)) 30564718Seric problem = "unexpected close"; 30664718Seric else if (ferror(InChannel)) 30764718Seric problem = "I/O error"; 30864718Seric else 30964718Seric problem = "read timeout"; 31036233Skarels # ifdef LOG 31158308Seric if (LogLevel > 0 && feof(InChannel)) 31236230Skarels syslog(LOG_NOTICE, 31364718Seric "collect: %s on connection from %s, sender=%s: %m\n", 31464718Seric problem, host, e->e_from.q_paddr); 31536233Skarels # endif 31665951Seric if (feof(InChannel)) 31765951Seric usrerr("451 collect: %s on connection from %s, from=%s", 31864718Seric problem, host, e->e_from.q_paddr); 31965951Seric else 32065951Seric syserr("451 collect: %s on connection from %s, from=%s", 32165951Seric problem, host, e->e_from.q_paddr); 32211145Seric 32316136Seric /* don't return an error indication */ 32455012Seric e->e_to = NULL; 32555012Seric e->e_flags &= ~EF_FATALERRS; 32664124Seric e->e_flags |= EF_CLRQUEUE; 32716136Seric 32816136Seric /* and don't try to deliver the partial message either */ 32964718Seric if (InChild) 33064718Seric ExitStat = EX_QUIT; 33116136Seric finis(); 33216136Seric } 33316136Seric 3342900Seric /* 3352900Seric ** Find out some information from the headers. 3363386Seric ** Examples are who is the from person & the date. 3372900Seric */ 3382900Seric 33958929Seric eatheader(e, !requeueflag); 3407673Seric 34164068Seric /* collect statistics */ 34264068Seric if (OpMode != MD_VERIFY) 34364068Seric markstats(e, (ADDRESS *) NULL); 34464068Seric 3457782Seric /* 3467782Seric ** Add an Apparently-To: line if we have no recipient lines. 3477782Seric */ 3484622Seric 34955012Seric if (hvalue("to", e) == NULL && hvalue("cc", e) == NULL && 35055012Seric hvalue("bcc", e) == NULL && hvalue("apparently-to", e) == NULL) 3517367Seric { 3527367Seric register ADDRESS *q; 3537367Seric 3547367Seric /* create an Apparently-To: field */ 3557367Seric /* that or reject the message.... */ 35655012Seric for (q = e->e_sendqueue; q != NULL; q = q->q_next) 3577367Seric { 3587389Seric if (q->q_alias != NULL) 3597389Seric continue; 3607673Seric if (tTd(30, 3)) 3617367Seric printf("Adding Apparently-To: %s\n", q->q_paddr); 36259579Seric addheader("Apparently-To", q->q_paddr, e); 3637367Seric } 3647367Seric } 3657367Seric 36659320Seric /* check for message too large */ 36759320Seric if (MaxMessageSize > 0 && e->e_msgsize > MaxMessageSize) 36859320Seric { 36959320Seric usrerr("552 Message exceeds maximum fixed size (%ld)", 37059320Seric MaxMessageSize); 37159320Seric } 37259320Seric 37355012Seric if ((e->e_dfp = fopen(e->e_df, "r")) == NULL) 37458690Seric { 37558690Seric /* we haven't acked receipt yet, so just chuck this */ 37655012Seric syserr("Cannot reopen %s", e->e_df); 37758690Seric finis(); 37858690Seric } 3791392Seric } 3801392Seric /* 38140965Sbostic ** FLUSHEOL -- if not at EOL, throw away rest of input line. 38240965Sbostic ** 38340965Sbostic ** Parameters: 38440965Sbostic ** buf -- last line read in (checked for '\n'), 38540965Sbostic ** fp -- file to be read from. 38640965Sbostic ** 38740965Sbostic ** Returns: 38840965Sbostic ** FALSE on error from sfgets(), TRUE otherwise. 38940965Sbostic ** 39040965Sbostic ** Side Effects: 39140965Sbostic ** none. 39240965Sbostic */ 39340965Sbostic 39440965Sbostic bool 39540965Sbostic flusheol(buf, fp) 39640965Sbostic char *buf; 39740965Sbostic FILE *fp; 39840965Sbostic { 39940965Sbostic register char *p = buf; 40057134Seric char junkbuf[MAXLINE]; 40140965Sbostic 40257134Seric while (strchr(p, '\n') == NULL) 40357134Seric { 404*66796Seric CollectErrorMessage = "553 header line too long"; 405*66796Seric CollectErrno = 0; 40661093Seric if (sfgets(junkbuf, MAXLINE, fp, TimeOuts.to_datablock, 40761093Seric "long line flush") == NULL) 40857134Seric return (FALSE); 40940965Sbostic p = junkbuf; 41040965Sbostic } 41140965Sbostic 41257134Seric return (TRUE); 41340965Sbostic } 41440965Sbostic /* 41511544Seric ** TFERROR -- signal error on writing the temporary file. 41611544Seric ** 41711544Seric ** Parameters: 41811544Seric ** tf -- the file pointer for the temporary file. 41911544Seric ** 42011544Seric ** Returns: 42111544Seric ** none. 42211544Seric ** 42311544Seric ** Side Effects: 42411544Seric ** Gives an error message. 42511544Seric ** Arranges for following output to go elsewhere. 42611544Seric */ 42711544Seric 42855012Seric tferror(tf, e) 42911544Seric FILE *tf; 43055012Seric register ENVELOPE *e; 43111544Seric { 432*66796Seric CollectErrno = errno; 43311544Seric if (errno == ENOSPC) 43411544Seric { 43566782Seric struct stat st; 43666782Seric long avail; 43766782Seric long bsize; 43866782Seric 43966782Seric NoReturn = TRUE; 44066782Seric if (fstat(fileno(tf), &st) < 0) 44166782Seric st.st_size = 0; 44255012Seric (void) freopen(e->e_df, "w", tf); 44366782Seric if (st.st_size <= 0) 44466782Seric fprintf(tf, "\n*** Mail could not be accepted"); 44566782Seric else if (sizeof st.st_size > sizeof (long)) 44666782Seric fprintf(tf, "\n*** Mail of at least %qd bytes could not be accepted\n", 44766782Seric st.st_size); 44866782Seric else 44966782Seric fprintf(tf, "\n*** Mail of at least %ld bytes could not be accepted\n", 45066782Seric st.st_size); 45166782Seric fprintf(tf, "*** at %s due to lack of disk space for temp file.\n", 45266782Seric MyHostName); 45366782Seric avail = freespace(QueueDir, &bsize); 45466782Seric if (avail > 0) 45566782Seric { 45666782Seric if (bsize > 1024) 45766782Seric avail *= bsize / 1024; 45866782Seric else if (bsize < 1024) 45966782Seric avail /= 1024 / bsize; 46066782Seric fprintf(tf, "*** Currently, %ld kilobytes are available for mail temp files.\n", 46166782Seric avail); 46266782Seric } 463*66796Seric CollectErrorMessage = "452 Out of disk space for temp file"; 46411544Seric } 46511544Seric else 466*66796Seric { 467*66796Seric CollectErrorMessage = "cannot write message body to disk (%s)"; 468*66796Seric } 46911544Seric (void) freopen("/dev/null", "w", tf); 47011544Seric } 47111544Seric /* 4722900Seric ** EATFROM -- chew up a UNIX style from line and process 4732900Seric ** 4742900Seric ** This does indeed make some assumptions about the format 4752900Seric ** of UNIX messages. 4762900Seric ** 4772900Seric ** Parameters: 4782900Seric ** fm -- the from line. 4792900Seric ** 4802900Seric ** Returns: 4812900Seric ** none. 4822900Seric ** 4832900Seric ** Side Effects: 4842900Seric ** extracts what information it can from the header, 4853386Seric ** such as the date. 4862900Seric */ 4872900Seric 4884321Seric # ifndef NOTUNIX 4894321Seric 4904203Seric char *DowList[] = 4914203Seric { 4924203Seric "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", NULL 4934203Seric }; 4944203Seric 4952900Seric char *MonthList[] = 4962900Seric { 4972900Seric "Jan", "Feb", "Mar", "Apr", "May", "Jun", 4982900Seric "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", 4992900Seric NULL 5002900Seric }; 5012900Seric 50255012Seric eatfrom(fm, e) 5032900Seric char *fm; 50455012Seric register ENVELOPE *e; 5052900Seric { 5062900Seric register char *p; 5072900Seric register char **dt; 5082900Seric 5097673Seric if (tTd(30, 2)) 5104203Seric printf("eatfrom(%s)\n", fm); 5114203Seric 5122900Seric /* find the date part */ 5132900Seric p = fm; 5142900Seric while (*p != '\0') 5152900Seric { 5162900Seric /* skip a word */ 5172900Seric while (*p != '\0' && *p != ' ') 51816896Seric p++; 5192900Seric while (*p == ' ') 52016896Seric p++; 52158050Seric if (!(isascii(*p) && isupper(*p)) || 52258050Seric p[3] != ' ' || p[13] != ':' || p[16] != ':') 5232900Seric continue; 5242900Seric 5252900Seric /* we have a possible date */ 5264203Seric for (dt = DowList; *dt != NULL; dt++) 5272900Seric if (strncmp(*dt, p, 3) == 0) 5282900Seric break; 5294203Seric if (*dt == NULL) 5304203Seric continue; 5312900Seric 5324203Seric for (dt = MonthList; *dt != NULL; dt++) 5334203Seric if (strncmp(*dt, &p[4], 3) == 0) 5344203Seric break; 5352900Seric if (*dt != NULL) 5362900Seric break; 5372900Seric } 5382900Seric 53960502Seric if (*p != '\0') 5402900Seric { 5413386Seric char *q; 5425366Seric extern char *arpadate(); 5433386Seric 5442900Seric /* we have found a date */ 5453386Seric q = xalloc(25); 54623103Seric (void) strncpy(q, p, 25); 5473386Seric q[24] = '\0'; 5485366Seric q = arpadate(q); 54955012Seric define('a', newstr(q), e); 5502900Seric } 5512900Seric } 5524321Seric 55356795Seric # endif /* NOTUNIX */ 554