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*67268Seric static char sccsid[] = "@(#)collect.c 8.15 (Berkeley) 05/29/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 4066796Seric char *CollectErrorMessage; 4166796Seric bool CollectErrno; 4266796Seric 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; 50*67268Seric time_t dbto = smtpmode ? TimeOuts.to_datablock : 0; 5140965Sbostic register char *workbuf, *freebuf; 5264718Seric bool inputerr = FALSE; 53*67268Seric char buf[MAXLINE], buf2[MAXLINE]; 542900Seric extern char *hvalue(); 5540965Sbostic extern bool isheader(), flusheol(); 561392Seric 5766796Seric CollectErrorMessage = NULL; 5866796Seric CollectErrno = 0; 5966796Seric 601392Seric /* 611392Seric ** Create the temp file name and create the file. 621392Seric */ 631392Seric 6464086Seric e->e_df = queuename(e, 'd'); 6564086Seric e->e_df = newstr(e->e_df); 6666891Seric if ((tf = dfopen(e->e_df, O_WRONLY|O_CREAT|O_TRUNC, FileMode)) == NULL) 671392Seric { 6855012Seric syserr("Cannot create %s", e->e_df); 695366Seric NoReturn = TRUE; 705366Seric finis(); 711392Seric } 721392Seric 734316Seric /* 744322Seric ** Tell ARPANET to go ahead. 754322Seric */ 764322Seric 7752105Seric if (smtpmode) 7858151Seric message("354 Enter mail, end with \".\" on a line by itself"); 794322Seric 8066765Seric /* set global timer to monitor progress */ 81*67268Seric sfgetset(dbto); 8266765Seric 834322Seric /* 844316Seric ** Try to read a UNIX-style From line 854316Seric */ 864316Seric 87*67268Seric if (sfgets(buf, MAXLINE, InChannel, dbto, 8861093Seric "initial message read") == NULL) 8940965Sbostic goto readerr; 904557Seric fixcrlf(buf, FALSE); 914321Seric # ifndef NOTUNIX 924322Seric if (!SaveFrom && strncmp(buf, "From ", 5) == 0) 932900Seric { 94*67268Seric if (!flusheol(buf, InChannel, dbto)) 9540965Sbostic goto readerr; 9655012Seric eatfrom(buf, e); 97*67268Seric if (sfgets(buf, MAXLINE, InChannel, dbto, 9861093Seric "message header read") == NULL) 9940965Sbostic goto readerr; 1004557Seric fixcrlf(buf, FALSE); 1012900Seric } 10256795Seric # endif /* NOTUNIX */ 1032900Seric 1041392Seric /* 1055975Seric ** Copy InChannel to temp file & do message editing. 1061392Seric ** To keep certain mailers from getting confused, 1071392Seric ** and to keep the output clean, lines that look 10813932Seric ** like UNIX "From" lines are deleted in the header. 1091392Seric */ 1101392Seric 11140965Sbostic workbuf = buf; /* `workbuf' contains a header field */ 11240965Sbostic freebuf = buf2; /* `freebuf' can be used for read-ahead */ 11340965Sbostic for (;;) 1141392Seric { 11557135Seric char *curbuf; 11657135Seric int curbuffree; 11757135Seric register int curbuflen; 11857135Seric char *p; 11957135Seric 12040965Sbostic /* first, see if the header is over */ 12140965Sbostic if (!isheader(workbuf)) 12240965Sbostic { 12340965Sbostic fixcrlf(workbuf, TRUE); 12419036Seric break; 12540965Sbostic } 12619036Seric 1277681Seric /* if the line is too long, throw the rest away */ 128*67268Seric if (!flusheol(workbuf, InChannel, dbto)) 12940965Sbostic goto readerr; 1307681Seric 13140965Sbostic /* it's okay to toss '\n' now (flusheol() needed it) */ 13240965Sbostic fixcrlf(workbuf, TRUE); 1334557Seric 13457135Seric curbuf = workbuf; 13557135Seric curbuflen = strlen(curbuf); 13657135Seric curbuffree = MAXLINE - curbuflen; 13757135Seric p = curbuf + curbuflen; 1382900Seric 1392900Seric /* get the rest of this field */ 14040965Sbostic for (;;) 1411392Seric { 14257135Seric int clen; 14357135Seric 14461093Seric if (sfgets(freebuf, MAXLINE, InChannel, 145*67268Seric dbto, 14661093Seric "message header read") == NULL) 14764916Seric { 14864916Seric freebuf[0] = '\0'; 14964916Seric break; 15064916Seric } 15140965Sbostic 15240965Sbostic /* is this a continuation line? */ 15340965Sbostic if (*freebuf != ' ' && *freebuf != '\t') 1542900Seric break; 15540965Sbostic 156*67268Seric if (!flusheol(freebuf, InChannel, dbto)) 15740965Sbostic goto readerr; 15840965Sbostic 15957135Seric fixcrlf(freebuf, TRUE); 16057135Seric clen = strlen(freebuf) + 1; 16157135Seric 16257135Seric /* if insufficient room, dynamically allocate buffer */ 16357135Seric if (clen >= curbuffree) 16440965Sbostic { 16557135Seric /* reallocate buffer */ 16657135Seric int nbuflen = ((p - curbuf) + clen) * 2; 16757135Seric char *nbuf = xalloc(nbuflen); 16840965Sbostic 16957135Seric p = nbuf + curbuflen; 17057135Seric curbuffree = nbuflen - curbuflen; 17157135Seric bcopy(curbuf, nbuf, curbuflen); 17257135Seric if (curbuf != buf && curbuf != buf2) 17357135Seric free(curbuf); 17457135Seric curbuf = nbuf; 17540965Sbostic } 17657135Seric *p++ = '\n'; 17757135Seric bcopy(freebuf, p, clen - 1); 17857135Seric p += clen - 1; 17957135Seric curbuffree -= clen; 18057135Seric curbuflen += clen; 1811392Seric } 18257135Seric *p++ = '\0'; 1831392Seric 18457135Seric e->e_msgsize += curbuflen; 1851392Seric 1862900Seric /* 18740965Sbostic ** The working buffer now becomes the free buffer, since 18840965Sbostic ** the free buffer contains a new header field. 18940965Sbostic ** 19040965Sbostic ** This is premature, since we still havent called 19140965Sbostic ** chompheader() to process the field we just created 19240965Sbostic ** (so the call to chompheader() will use `freebuf'). 19340965Sbostic ** This convolution is necessary so that if we break out 19440965Sbostic ** of the loop due to H_EOH, `workbuf' will always be 19540965Sbostic ** the next unprocessed buffer. 19640965Sbostic */ 19740965Sbostic 19840965Sbostic { 19940965Sbostic register char *tmp = workbuf; 20040965Sbostic workbuf = freebuf; 20140965Sbostic freebuf = tmp; 20240965Sbostic } 20340965Sbostic 20440965Sbostic /* 2052900Seric ** Snarf header away. 2062900Seric */ 2072900Seric 20857135Seric if (bitset(H_EOH, chompheader(curbuf, FALSE, e))) 2093058Seric break; 21057135Seric 21157135Seric /* 21257135Seric ** If the buffer was dynamically allocated, free it. 21357135Seric */ 21457135Seric 21557135Seric if (curbuf != buf && curbuf != buf2) 21657135Seric free(curbuf); 21740965Sbostic } 2181392Seric 2197673Seric if (tTd(30, 1)) 2202900Seric printf("EOH\n"); 2212900Seric 22240965Sbostic if (*workbuf == '\0') 22340965Sbostic { 22440965Sbostic /* throw away a blank line */ 225*67268Seric if (sfgets(buf, MAXLINE, InChannel, dbto, 22661093Seric "message separator read") == NULL) 22740965Sbostic goto readerr; 22840965Sbostic } 22940965Sbostic else if (workbuf == buf2) /* guarantee `buf' contains data */ 23040965Sbostic (void) strcpy(buf, buf2); 2312900Seric 2322900Seric /* 2332900Seric ** Collect the body of the message. 2342900Seric */ 2352900Seric 23664718Seric for (;;) 2372900Seric { 2384551Seric register char *bp = buf; 2394156Seric 2407852Seric fixcrlf(buf, TRUE); 2414557Seric 2422900Seric /* check for end-of-message */ 24352105Seric if (!ignrdot && buf[0] == '.' && (buf[1] == '\n' || buf[1] == '\0')) 2442900Seric break; 2452900Seric 2464551Seric /* check for transparent dot */ 24765580Seric if ((OpMode == MD_SMTP || OpMode == MD_DAEMON) && 24865580Seric bp[0] == '.' && bp[1] == '.') 2494551Seric bp++; 2504551Seric 2514156Seric /* 2524156Seric ** Figure message length, output the line to the temp 2534156Seric ** file, and insert a newline if missing. 2544156Seric */ 2554156Seric 25655012Seric e->e_msgsize += strlen(bp) + 1; 2574551Seric fputs(bp, tf); 2587852Seric fputs("\n", tf); 2591392Seric if (ferror(tf)) 26055012Seric tferror(tf, e); 261*67268Seric if (sfgets(buf, MAXLINE, InChannel, dbto, 26264718Seric "message body read") == NULL) 26364718Seric goto readerr; 26464718Seric } 26540965Sbostic 26664718Seric if (feof(InChannel) || ferror(InChannel)) 26764718Seric { 26840965Sbostic readerr: 26964916Seric if (tTd(30, 1)) 27064916Seric printf("collect: read error\n"); 27164718Seric inputerr = TRUE; 27264718Seric } 27364718Seric 27466765Seric /* reset global timer */ 27566765Seric sfgetset((time_t) 0); 27666765Seric 27711544Seric if (fflush(tf) != 0) 27855012Seric tferror(tf, e); 27964762Seric if (fsync(fileno(tf)) < 0 || fclose(tf) < 0) 28064762Seric { 28166796Seric tferror(tf, e); 28264762Seric finis(); 28364762Seric } 2842900Seric 28566796Seric if (CollectErrorMessage != NULL && Errors <= 0) 28616136Seric { 28766796Seric if (CollectErrno != 0) 28866796Seric { 28966796Seric errno = CollectErrno; 29066796Seric syserr(CollectErrorMessage, e->e_df); 29166796Seric finis(); 29266796Seric } 29366796Seric usrerr(CollectErrorMessage); 29466796Seric } 29566796Seric else if (inputerr && (OpMode == MD_SMTP || OpMode == MD_DAEMON)) 29666796Seric { 29766796Seric /* An EOF when running SMTP is an error */ 29858308Seric char *host; 29964718Seric char *problem; 30058082Seric 30158308Seric host = RealHostName; 30258308Seric if (host == NULL) 30358308Seric host = "localhost"; 30458308Seric 30564718Seric if (feof(InChannel)) 30664718Seric problem = "unexpected close"; 30764718Seric else if (ferror(InChannel)) 30864718Seric problem = "I/O error"; 30964718Seric else 31064718Seric problem = "read timeout"; 31136233Skarels # ifdef LOG 31258308Seric if (LogLevel > 0 && feof(InChannel)) 31336230Skarels syslog(LOG_NOTICE, 31466864Seric "collect: %s on connection from %s, sender=%s: %s\n", 31566864Seric problem, host, e->e_from.q_paddr, errstring(errno)); 31636233Skarels # endif 31765951Seric if (feof(InChannel)) 31865951Seric usrerr("451 collect: %s on connection from %s, from=%s", 31964718Seric problem, host, e->e_from.q_paddr); 32065951Seric else 32165951Seric syserr("451 collect: %s on connection from %s, from=%s", 32265951Seric problem, host, e->e_from.q_paddr); 32311145Seric 32416136Seric /* don't return an error indication */ 32555012Seric e->e_to = NULL; 32655012Seric e->e_flags &= ~EF_FATALERRS; 32764124Seric e->e_flags |= EF_CLRQUEUE; 32816136Seric 32916136Seric /* and don't try to deliver the partial message either */ 33064718Seric if (InChild) 33164718Seric ExitStat = EX_QUIT; 33216136Seric finis(); 33316136Seric } 33416136Seric 3352900Seric /* 3362900Seric ** Find out some information from the headers. 3373386Seric ** Examples are who is the from person & the date. 3382900Seric */ 3392900Seric 34058929Seric eatheader(e, !requeueflag); 3417673Seric 34264068Seric /* collect statistics */ 34364068Seric if (OpMode != MD_VERIFY) 34464068Seric markstats(e, (ADDRESS *) NULL); 34564068Seric 3467782Seric /* 3477782Seric ** Add an Apparently-To: line if we have no recipient lines. 3487782Seric */ 3494622Seric 35055012Seric if (hvalue("to", e) == NULL && hvalue("cc", e) == NULL && 35155012Seric hvalue("bcc", e) == NULL && hvalue("apparently-to", e) == NULL) 3527367Seric { 3537367Seric register ADDRESS *q; 3547367Seric 3557367Seric /* create an Apparently-To: field */ 3567367Seric /* that or reject the message.... */ 35755012Seric for (q = e->e_sendqueue; q != NULL; q = q->q_next) 3587367Seric { 3597389Seric if (q->q_alias != NULL) 3607389Seric continue; 3617673Seric if (tTd(30, 3)) 3627367Seric printf("Adding Apparently-To: %s\n", q->q_paddr); 36359579Seric addheader("Apparently-To", q->q_paddr, e); 3647367Seric } 3657367Seric } 3667367Seric 36759320Seric /* check for message too large */ 36859320Seric if (MaxMessageSize > 0 && e->e_msgsize > MaxMessageSize) 36959320Seric { 37059320Seric usrerr("552 Message exceeds maximum fixed size (%ld)", 37159320Seric MaxMessageSize); 37259320Seric } 37359320Seric 37455012Seric if ((e->e_dfp = fopen(e->e_df, "r")) == NULL) 37558690Seric { 37658690Seric /* we haven't acked receipt yet, so just chuck this */ 37755012Seric syserr("Cannot reopen %s", e->e_df); 37858690Seric finis(); 37958690Seric } 3801392Seric } 3811392Seric /* 38240965Sbostic ** FLUSHEOL -- if not at EOL, throw away rest of input line. 38340965Sbostic ** 38440965Sbostic ** Parameters: 38540965Sbostic ** buf -- last line read in (checked for '\n'), 38640965Sbostic ** fp -- file to be read from. 38740965Sbostic ** 38840965Sbostic ** Returns: 38940965Sbostic ** FALSE on error from sfgets(), TRUE otherwise. 39040965Sbostic ** 39140965Sbostic ** Side Effects: 39240965Sbostic ** none. 39340965Sbostic */ 39440965Sbostic 39540965Sbostic bool 396*67268Seric flusheol(buf, fp, dbto) 39740965Sbostic char *buf; 39840965Sbostic FILE *fp; 399*67268Seric time_t dbto; 40040965Sbostic { 40140965Sbostic register char *p = buf; 40257134Seric char junkbuf[MAXLINE]; 40340965Sbostic 40457134Seric while (strchr(p, '\n') == NULL) 40557134Seric { 40666796Seric CollectErrorMessage = "553 header line too long"; 40766796Seric CollectErrno = 0; 408*67268Seric if (sfgets(junkbuf, MAXLINE, fp, dbto, 40961093Seric "long line flush") == NULL) 41057134Seric return (FALSE); 41140965Sbostic p = junkbuf; 41240965Sbostic } 41340965Sbostic 41457134Seric return (TRUE); 41540965Sbostic } 41640965Sbostic /* 41711544Seric ** TFERROR -- signal error on writing the temporary file. 41811544Seric ** 41911544Seric ** Parameters: 42011544Seric ** tf -- the file pointer for the temporary file. 42111544Seric ** 42211544Seric ** Returns: 42311544Seric ** none. 42411544Seric ** 42511544Seric ** Side Effects: 42611544Seric ** Gives an error message. 42711544Seric ** Arranges for following output to go elsewhere. 42811544Seric */ 42911544Seric 43055012Seric tferror(tf, e) 43111544Seric FILE *tf; 43255012Seric register ENVELOPE *e; 43311544Seric { 43466796Seric CollectErrno = errno; 43511544Seric if (errno == ENOSPC) 43611544Seric { 43766782Seric struct stat st; 43866782Seric long avail; 43966782Seric long bsize; 44066782Seric 44166782Seric NoReturn = TRUE; 44266782Seric if (fstat(fileno(tf), &st) < 0) 44366782Seric st.st_size = 0; 44455012Seric (void) freopen(e->e_df, "w", tf); 44566782Seric if (st.st_size <= 0) 44666782Seric fprintf(tf, "\n*** Mail could not be accepted"); 44766782Seric else if (sizeof st.st_size > sizeof (long)) 44866782Seric fprintf(tf, "\n*** Mail of at least %qd bytes could not be accepted\n", 44966782Seric st.st_size); 45066782Seric else 45166782Seric fprintf(tf, "\n*** Mail of at least %ld bytes could not be accepted\n", 45266782Seric st.st_size); 45366782Seric fprintf(tf, "*** at %s due to lack of disk space for temp file.\n", 45466782Seric MyHostName); 45566782Seric avail = freespace(QueueDir, &bsize); 45666782Seric if (avail > 0) 45766782Seric { 45866782Seric if (bsize > 1024) 45966782Seric avail *= bsize / 1024; 46066782Seric else if (bsize < 1024) 46166782Seric avail /= 1024 / bsize; 46266782Seric fprintf(tf, "*** Currently, %ld kilobytes are available for mail temp files.\n", 46366782Seric avail); 46466782Seric } 46566796Seric CollectErrorMessage = "452 Out of disk space for temp file"; 46611544Seric } 46711544Seric else 46866796Seric { 46966796Seric CollectErrorMessage = "cannot write message body to disk (%s)"; 47066796Seric } 47111544Seric (void) freopen("/dev/null", "w", tf); 47211544Seric } 47311544Seric /* 4742900Seric ** EATFROM -- chew up a UNIX style from line and process 4752900Seric ** 4762900Seric ** This does indeed make some assumptions about the format 4772900Seric ** of UNIX messages. 4782900Seric ** 4792900Seric ** Parameters: 4802900Seric ** fm -- the from line. 4812900Seric ** 4822900Seric ** Returns: 4832900Seric ** none. 4842900Seric ** 4852900Seric ** Side Effects: 4862900Seric ** extracts what information it can from the header, 4873386Seric ** such as the date. 4882900Seric */ 4892900Seric 4904321Seric # ifndef NOTUNIX 4914321Seric 4924203Seric char *DowList[] = 4934203Seric { 4944203Seric "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", NULL 4954203Seric }; 4964203Seric 4972900Seric char *MonthList[] = 4982900Seric { 4992900Seric "Jan", "Feb", "Mar", "Apr", "May", "Jun", 5002900Seric "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", 5012900Seric NULL 5022900Seric }; 5032900Seric 50455012Seric eatfrom(fm, e) 5052900Seric char *fm; 50655012Seric register ENVELOPE *e; 5072900Seric { 5082900Seric register char *p; 5092900Seric register char **dt; 5102900Seric 5117673Seric if (tTd(30, 2)) 5124203Seric printf("eatfrom(%s)\n", fm); 5134203Seric 5142900Seric /* find the date part */ 5152900Seric p = fm; 5162900Seric while (*p != '\0') 5172900Seric { 5182900Seric /* skip a word */ 5192900Seric while (*p != '\0' && *p != ' ') 52016896Seric p++; 5212900Seric while (*p == ' ') 52216896Seric p++; 52358050Seric if (!(isascii(*p) && isupper(*p)) || 52458050Seric p[3] != ' ' || p[13] != ':' || p[16] != ':') 5252900Seric continue; 5262900Seric 5272900Seric /* we have a possible date */ 5284203Seric for (dt = DowList; *dt != NULL; dt++) 5292900Seric if (strncmp(*dt, p, 3) == 0) 5302900Seric break; 5314203Seric if (*dt == NULL) 5324203Seric continue; 5332900Seric 5344203Seric for (dt = MonthList; *dt != NULL; dt++) 5354203Seric if (strncmp(*dt, &p[4], 3) == 0) 5364203Seric break; 5372900Seric if (*dt != NULL) 5382900Seric break; 5392900Seric } 5402900Seric 54160502Seric if (*p != '\0') 5422900Seric { 5433386Seric char *q; 5445366Seric extern char *arpadate(); 5453386Seric 5462900Seric /* we have found a date */ 5473386Seric q = xalloc(25); 54823103Seric (void) strncpy(q, p, 25); 5493386Seric q[24] = '\0'; 5505366Seric q = arpadate(q); 55155012Seric define('a', newstr(q), e); 5522900Seric } 5532900Seric } 5544321Seric 55556795Seric # endif /* NOTUNIX */ 556