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*67547Seric static char sccsid[] = "@(#)collect.c 8.18 (Berkeley) 07/23/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: 2467546Seric ** fp -- file to read. 2552106Seric ** smtpmode -- if set, we are running SMTP: give an RFC821 2652105Seric ** style message to say we are ready to collect 2752105Seric ** input, and never ignore a single dot to mean 2852105Seric ** end of message. 2958929Seric ** requeueflag -- this message will be requeued later, so 3058929Seric ** don't do final processing on it. 3167546Seric ** hdrp -- the location to stash the header. 3258929Seric ** e -- the current envelope. 331392Seric ** 341392Seric ** Returns: 354162Seric ** none. 361392Seric ** 371392Seric ** Side Effects: 381392Seric ** Temp file is created and filled. 394162Seric ** The from person may be set. 401392Seric */ 411392Seric 4266796Seric char *CollectErrorMessage; 4366796Seric bool CollectErrno; 4466796Seric 4567546Seric collect(fp, smtpmode, requeueflag, hdrp, e) 4667546Seric FILE *fp; 4752105Seric bool smtpmode; 4858929Seric bool requeueflag; 4967546Seric HDR **hdrp; 5055012Seric register ENVELOPE *e; 511392Seric { 521392Seric register FILE *tf; 5352105Seric bool ignrdot = smtpmode ? FALSE : IgnrDot; 5467268Seric time_t dbto = smtpmode ? TimeOuts.to_datablock : 0; 5540965Sbostic register char *workbuf, *freebuf; 5664718Seric bool inputerr = FALSE; 5767546Seric bool headeronly = FALSE; 5867268Seric char buf[MAXLINE], buf2[MAXLINE]; 592900Seric extern char *hvalue(); 6040965Sbostic extern bool isheader(), flusheol(); 611392Seric 6266796Seric CollectErrorMessage = NULL; 6366796Seric CollectErrno = 0; 6467546Seric if (hdrp == NULL) 6567546Seric hdrp = &e->e_header; 6667546Seric else 6767546Seric headeronly = TRUE; 6866796Seric 691392Seric /* 701392Seric ** Create the temp file name and create the file. 711392Seric */ 721392Seric 7367546Seric if (!headeronly) 741392Seric { 7567546Seric e->e_df = queuename(e, 'd'); 7667546Seric e->e_df = newstr(e->e_df); 7767546Seric if ((tf = dfopen(e->e_df, O_WRONLY|O_CREAT|O_TRUNC, FileMode)) == NULL) 7867546Seric { 7967546Seric syserr("Cannot create %s", e->e_df); 8067546Seric e->e_flags |= EF_NORETURN; 8167546Seric finis(); 8267546Seric } 8367546Seric HasEightBits = FALSE; 841392Seric } 851392Seric 864316Seric /* 874322Seric ** Tell ARPANET to go ahead. 884322Seric */ 894322Seric 9052105Seric if (smtpmode) 9158151Seric message("354 Enter mail, end with \".\" on a line by itself"); 924322Seric 9366765Seric /* set global timer to monitor progress */ 9467268Seric sfgetset(dbto); 9566765Seric 964322Seric /* 974316Seric ** Try to read a UNIX-style From line 984316Seric */ 994316Seric 10067546Seric if (sfgets(buf, MAXLINE, fp, dbto, "initial message read") == NULL) 10140965Sbostic goto readerr; 1024557Seric fixcrlf(buf, FALSE); 1034321Seric # ifndef NOTUNIX 10467546Seric if (!headeronly && !SaveFrom && strncmp(buf, "From ", 5) == 0) 1052900Seric { 10667546Seric if (!flusheol(buf, fp, dbto)) 10740965Sbostic goto readerr; 10855012Seric eatfrom(buf, e); 10967546Seric if (sfgets(buf, MAXLINE, fp, dbto, 11061093Seric "message header read") == NULL) 11140965Sbostic goto readerr; 1124557Seric fixcrlf(buf, FALSE); 1132900Seric } 11456795Seric # endif /* NOTUNIX */ 1152900Seric 1161392Seric /* 11767546Seric ** Copy fp to temp file & do message editing. 1181392Seric ** To keep certain mailers from getting confused, 1191392Seric ** and to keep the output clean, lines that look 12013932Seric ** like UNIX "From" lines are deleted in the header. 1211392Seric */ 1221392Seric 12340965Sbostic workbuf = buf; /* `workbuf' contains a header field */ 12440965Sbostic freebuf = buf2; /* `freebuf' can be used for read-ahead */ 12540965Sbostic for (;;) 1261392Seric { 12757135Seric char *curbuf; 12857135Seric int curbuffree; 12957135Seric register int curbuflen; 13057135Seric char *p; 13157135Seric 13240965Sbostic /* first, see if the header is over */ 13340965Sbostic if (!isheader(workbuf)) 13440965Sbostic { 13540965Sbostic fixcrlf(workbuf, TRUE); 13619036Seric break; 13740965Sbostic } 13819036Seric 1397681Seric /* if the line is too long, throw the rest away */ 14067546Seric if (!flusheol(workbuf, fp, dbto)) 14140965Sbostic goto readerr; 1427681Seric 14340965Sbostic /* it's okay to toss '\n' now (flusheol() needed it) */ 14440965Sbostic fixcrlf(workbuf, TRUE); 1454557Seric 14657135Seric curbuf = workbuf; 14757135Seric curbuflen = strlen(curbuf); 14857135Seric curbuffree = MAXLINE - curbuflen; 14957135Seric p = curbuf + curbuflen; 1502900Seric 1512900Seric /* get the rest of this field */ 15240965Sbostic for (;;) 1531392Seric { 15457135Seric int clen; 15557135Seric 15667546Seric if (sfgets(freebuf, MAXLINE, fp, dbto, 15761093Seric "message header read") == NULL) 15864916Seric { 15964916Seric freebuf[0] = '\0'; 16064916Seric break; 16164916Seric } 16240965Sbostic 16340965Sbostic /* is this a continuation line? */ 16440965Sbostic if (*freebuf != ' ' && *freebuf != '\t') 1652900Seric break; 16640965Sbostic 16767546Seric if (!flusheol(freebuf, fp, dbto)) 16840965Sbostic goto readerr; 16940965Sbostic 17057135Seric fixcrlf(freebuf, TRUE); 17157135Seric clen = strlen(freebuf) + 1; 17257135Seric 17357135Seric /* if insufficient room, dynamically allocate buffer */ 17457135Seric if (clen >= curbuffree) 17540965Sbostic { 17657135Seric /* reallocate buffer */ 17757135Seric int nbuflen = ((p - curbuf) + clen) * 2; 17857135Seric char *nbuf = xalloc(nbuflen); 17940965Sbostic 18057135Seric p = nbuf + curbuflen; 18157135Seric curbuffree = nbuflen - curbuflen; 18257135Seric bcopy(curbuf, nbuf, curbuflen); 18357135Seric if (curbuf != buf && curbuf != buf2) 18457135Seric free(curbuf); 18557135Seric curbuf = nbuf; 18640965Sbostic } 18757135Seric *p++ = '\n'; 18857135Seric bcopy(freebuf, p, clen - 1); 18957135Seric p += clen - 1; 19057135Seric curbuffree -= clen; 19157135Seric curbuflen += clen; 1921392Seric } 19357135Seric *p++ = '\0'; 1941392Seric 19557135Seric e->e_msgsize += curbuflen; 1961392Seric 1972900Seric /* 19840965Sbostic ** The working buffer now becomes the free buffer, since 19940965Sbostic ** the free buffer contains a new header field. 20040965Sbostic ** 20140965Sbostic ** This is premature, since we still havent called 20240965Sbostic ** chompheader() to process the field we just created 20340965Sbostic ** (so the call to chompheader() will use `freebuf'). 20440965Sbostic ** This convolution is necessary so that if we break out 20540965Sbostic ** of the loop due to H_EOH, `workbuf' will always be 20640965Sbostic ** the next unprocessed buffer. 20740965Sbostic */ 20840965Sbostic 20940965Sbostic { 21040965Sbostic register char *tmp = workbuf; 21140965Sbostic workbuf = freebuf; 21240965Sbostic freebuf = tmp; 21340965Sbostic } 21440965Sbostic 21540965Sbostic /* 2162900Seric ** Snarf header away. 2172900Seric */ 2182900Seric 21957135Seric if (bitset(H_EOH, chompheader(curbuf, FALSE, e))) 2203058Seric break; 22157135Seric 22257135Seric /* 22357135Seric ** If the buffer was dynamically allocated, free it. 22457135Seric */ 22557135Seric 22657135Seric if (curbuf != buf && curbuf != buf2) 22757135Seric free(curbuf); 22840965Sbostic } 2291392Seric 2307673Seric if (tTd(30, 1)) 2312900Seric printf("EOH\n"); 2322900Seric 23367546Seric if (headeronly) 23467546Seric { 23567546Seric if (*workbuf != '\0') 23667546Seric syserr("collect: lost first line of message"); 23767546Seric goto readerr; 23867546Seric } 23967546Seric 24040965Sbostic if (*workbuf == '\0') 24140965Sbostic { 24240965Sbostic /* throw away a blank line */ 24367546Seric if (sfgets(buf, MAXLINE, fp, dbto, 24461093Seric "message separator read") == NULL) 24540965Sbostic goto readerr; 24640965Sbostic } 24740965Sbostic else if (workbuf == buf2) /* guarantee `buf' contains data */ 24840965Sbostic (void) strcpy(buf, buf2); 2492900Seric 2502900Seric /* 2512900Seric ** Collect the body of the message. 2522900Seric */ 2532900Seric 25464718Seric for (;;) 2552900Seric { 2564551Seric register char *bp = buf; 2574156Seric 2587852Seric fixcrlf(buf, TRUE); 2594557Seric 2602900Seric /* check for end-of-message */ 26152105Seric if (!ignrdot && buf[0] == '.' && (buf[1] == '\n' || buf[1] == '\0')) 2622900Seric break; 2632900Seric 2644551Seric /* check for transparent dot */ 26565580Seric if ((OpMode == MD_SMTP || OpMode == MD_DAEMON) && 26665580Seric bp[0] == '.' && bp[1] == '.') 2674551Seric bp++; 2684551Seric 2694156Seric /* 2704156Seric ** Figure message length, output the line to the temp 2714156Seric ** file, and insert a newline if missing. 2724156Seric */ 2734156Seric 27455012Seric e->e_msgsize += strlen(bp) + 1; 2754551Seric fputs(bp, tf); 2767852Seric fputs("\n", tf); 2771392Seric if (ferror(tf)) 27855012Seric tferror(tf, e); 27967546Seric if (sfgets(buf, MAXLINE, fp, dbto, "message body read") == NULL) 28064718Seric goto readerr; 28164718Seric } 28240965Sbostic 28367546Seric readerr: 28467546Seric if ((feof(fp) && smtpmode) || ferror(fp)) 28564718Seric { 28664916Seric if (tTd(30, 1)) 28764916Seric printf("collect: read error\n"); 28864718Seric inputerr = TRUE; 28964718Seric } 29064718Seric 29166765Seric /* reset global timer */ 29266765Seric sfgetset((time_t) 0); 29366765Seric 29467546Seric if (headeronly) 29567546Seric return; 29667546Seric 29767546Seric if (tf != NULL) 29864762Seric { 29967546Seric if (fflush(tf) != 0) 30067546Seric tferror(tf, e); 30167546Seric if (fsync(fileno(tf)) < 0 || fclose(tf) < 0) 30267546Seric { 30367546Seric tferror(tf, e); 30467546Seric finis(); 30567546Seric } 30664762Seric } 3072900Seric 30866796Seric if (CollectErrorMessage != NULL && Errors <= 0) 30916136Seric { 31066796Seric if (CollectErrno != 0) 31166796Seric { 31266796Seric errno = CollectErrno; 31366796Seric syserr(CollectErrorMessage, e->e_df); 31466796Seric finis(); 31566796Seric } 31666796Seric usrerr(CollectErrorMessage); 31766796Seric } 31866796Seric else if (inputerr && (OpMode == MD_SMTP || OpMode == MD_DAEMON)) 31966796Seric { 32066796Seric /* An EOF when running SMTP is an error */ 32158308Seric char *host; 32264718Seric char *problem; 32358082Seric 32458308Seric host = RealHostName; 32558308Seric if (host == NULL) 32658308Seric host = "localhost"; 32758308Seric 32867546Seric if (feof(fp)) 32964718Seric problem = "unexpected close"; 33067546Seric else if (ferror(fp)) 33164718Seric problem = "I/O error"; 33264718Seric else 33364718Seric problem = "read timeout"; 33436233Skarels # ifdef LOG 33567546Seric if (LogLevel > 0 && feof(fp)) 33636230Skarels syslog(LOG_NOTICE, 33766864Seric "collect: %s on connection from %s, sender=%s: %s\n", 33866864Seric problem, host, e->e_from.q_paddr, errstring(errno)); 33936233Skarels # endif 34067546Seric if (feof(fp)) 34165951Seric usrerr("451 collect: %s on connection from %s, from=%s", 34264718Seric problem, host, e->e_from.q_paddr); 34365951Seric else 34465951Seric syserr("451 collect: %s on connection from %s, from=%s", 34565951Seric problem, host, e->e_from.q_paddr); 34611145Seric 34716136Seric /* don't return an error indication */ 34855012Seric e->e_to = NULL; 34955012Seric e->e_flags &= ~EF_FATALERRS; 35064124Seric e->e_flags |= EF_CLRQUEUE; 35116136Seric 35216136Seric /* and don't try to deliver the partial message either */ 35364718Seric if (InChild) 35464718Seric ExitStat = EX_QUIT; 35516136Seric finis(); 35616136Seric } 35716136Seric 3582900Seric /* 3592900Seric ** Find out some information from the headers. 3603386Seric ** Examples are who is the from person & the date. 3612900Seric */ 3622900Seric 36358929Seric eatheader(e, !requeueflag); 3647673Seric 36564068Seric /* collect statistics */ 36664068Seric if (OpMode != MD_VERIFY) 36764068Seric markstats(e, (ADDRESS *) NULL); 36864068Seric 3697782Seric /* 3707782Seric ** Add an Apparently-To: line if we have no recipient lines. 3717782Seric */ 3724622Seric 37367546Seric if (hvalue("to", e->e_header) == NULL && 37467546Seric hvalue("cc", e->e_header) == NULL && 37567546Seric hvalue("bcc", e->e_header) == NULL && 37667546Seric hvalue("apparently-to", e->e_header) == NULL) 3777367Seric { 3787367Seric register ADDRESS *q; 3797367Seric 3807367Seric /* create an Apparently-To: field */ 3817367Seric /* that or reject the message.... */ 38255012Seric for (q = e->e_sendqueue; q != NULL; q = q->q_next) 3837367Seric { 3847389Seric if (q->q_alias != NULL) 3857389Seric continue; 3867673Seric if (tTd(30, 3)) 3877367Seric printf("Adding Apparently-To: %s\n", q->q_paddr); 38867546Seric addheader("Apparently-To", q->q_paddr, &e->e_header); 3897367Seric } 3907367Seric } 3917367Seric 39259320Seric /* check for message too large */ 39359320Seric if (MaxMessageSize > 0 && e->e_msgsize > MaxMessageSize) 39459320Seric { 39559320Seric usrerr("552 Message exceeds maximum fixed size (%ld)", 39659320Seric MaxMessageSize); 39759320Seric } 39859320Seric 399*67547Seric /* check for illegal 8-bit data */ 400*67547Seric if (HasEightBits) 401*67547Seric { 402*67547Seric e->e_flags |= EF_HAS8BIT; 403*67547Seric if (bitset(MM_MIME8BIT, MimeMode)) 404*67547Seric { 405*67547Seric /* convert it to MIME */ 406*67547Seric if (hvalue("MIME-Version", e->e_header) == NULL) 407*67547Seric { 408*67547Seric char mimebuf[20]; 409*67547Seric 410*67547Seric strcpy(mimebuf, "MIME-Version: 1.0"); 411*67547Seric chompheader(mimebuf, FALSE, e); 412*67547Seric } 413*67547Seric if (e->e_bodytype == NULL) 414*67547Seric e->e_bodytype = "8BITMIME"; 415*67547Seric } 416*67547Seric else if (!bitset(MM_PASS8BIT, MimeMode)) 417*67547Seric usrerr("554 Eight bit data not allowed"); 418*67547Seric } 419*67547Seric 42055012Seric if ((e->e_dfp = fopen(e->e_df, "r")) == NULL) 42158690Seric { 42258690Seric /* we haven't acked receipt yet, so just chuck this */ 42355012Seric syserr("Cannot reopen %s", e->e_df); 42458690Seric finis(); 42558690Seric } 4261392Seric } 4271392Seric /* 42840965Sbostic ** FLUSHEOL -- if not at EOL, throw away rest of input line. 42940965Sbostic ** 43040965Sbostic ** Parameters: 43140965Sbostic ** buf -- last line read in (checked for '\n'), 43240965Sbostic ** fp -- file to be read from. 43340965Sbostic ** 43440965Sbostic ** Returns: 43540965Sbostic ** FALSE on error from sfgets(), TRUE otherwise. 43640965Sbostic ** 43740965Sbostic ** Side Effects: 43840965Sbostic ** none. 43940965Sbostic */ 44040965Sbostic 44140965Sbostic bool 44267268Seric flusheol(buf, fp, dbto) 44340965Sbostic char *buf; 44440965Sbostic FILE *fp; 44567268Seric time_t dbto; 44640965Sbostic { 44740965Sbostic register char *p = buf; 44857134Seric char junkbuf[MAXLINE]; 44940965Sbostic 45057134Seric while (strchr(p, '\n') == NULL) 45157134Seric { 45266796Seric CollectErrorMessage = "553 header line too long"; 45366796Seric CollectErrno = 0; 45467268Seric if (sfgets(junkbuf, MAXLINE, fp, dbto, 45561093Seric "long line flush") == NULL) 45657134Seric return (FALSE); 45740965Sbostic p = junkbuf; 45840965Sbostic } 45940965Sbostic 46057134Seric return (TRUE); 46140965Sbostic } 46240965Sbostic /* 46311544Seric ** TFERROR -- signal error on writing the temporary file. 46411544Seric ** 46511544Seric ** Parameters: 46611544Seric ** tf -- the file pointer for the temporary file. 46711544Seric ** 46811544Seric ** Returns: 46911544Seric ** none. 47011544Seric ** 47111544Seric ** Side Effects: 47211544Seric ** Gives an error message. 47311544Seric ** Arranges for following output to go elsewhere. 47411544Seric */ 47511544Seric 47655012Seric tferror(tf, e) 47711544Seric FILE *tf; 47855012Seric register ENVELOPE *e; 47911544Seric { 48066796Seric CollectErrno = errno; 48111544Seric if (errno == ENOSPC) 48211544Seric { 48366782Seric struct stat st; 48466782Seric long avail; 48566782Seric long bsize; 48666782Seric 48767473Seric e->e_flags |= EF_NORETURN; 48866782Seric if (fstat(fileno(tf), &st) < 0) 48966782Seric st.st_size = 0; 49055012Seric (void) freopen(e->e_df, "w", tf); 49166782Seric if (st.st_size <= 0) 49266782Seric fprintf(tf, "\n*** Mail could not be accepted"); 49366782Seric else if (sizeof st.st_size > sizeof (long)) 49466782Seric fprintf(tf, "\n*** Mail of at least %qd bytes could not be accepted\n", 49566782Seric st.st_size); 49666782Seric else 49766782Seric fprintf(tf, "\n*** Mail of at least %ld bytes could not be accepted\n", 49866782Seric st.st_size); 49966782Seric fprintf(tf, "*** at %s due to lack of disk space for temp file.\n", 50066782Seric MyHostName); 50166782Seric avail = freespace(QueueDir, &bsize); 50266782Seric if (avail > 0) 50366782Seric { 50466782Seric if (bsize > 1024) 50566782Seric avail *= bsize / 1024; 50666782Seric else if (bsize < 1024) 50766782Seric avail /= 1024 / bsize; 50866782Seric fprintf(tf, "*** Currently, %ld kilobytes are available for mail temp files.\n", 50966782Seric avail); 51066782Seric } 51166796Seric CollectErrorMessage = "452 Out of disk space for temp file"; 51211544Seric } 51311544Seric else 51466796Seric { 51566796Seric CollectErrorMessage = "cannot write message body to disk (%s)"; 51666796Seric } 51711544Seric (void) freopen("/dev/null", "w", tf); 51811544Seric } 51911544Seric /* 5202900Seric ** EATFROM -- chew up a UNIX style from line and process 5212900Seric ** 5222900Seric ** This does indeed make some assumptions about the format 5232900Seric ** of UNIX messages. 5242900Seric ** 5252900Seric ** Parameters: 5262900Seric ** fm -- the from line. 5272900Seric ** 5282900Seric ** Returns: 5292900Seric ** none. 5302900Seric ** 5312900Seric ** Side Effects: 5322900Seric ** extracts what information it can from the header, 5333386Seric ** such as the date. 5342900Seric */ 5352900Seric 5364321Seric # ifndef NOTUNIX 5374321Seric 5384203Seric char *DowList[] = 5394203Seric { 5404203Seric "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", NULL 5414203Seric }; 5424203Seric 5432900Seric char *MonthList[] = 5442900Seric { 5452900Seric "Jan", "Feb", "Mar", "Apr", "May", "Jun", 5462900Seric "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", 5472900Seric NULL 5482900Seric }; 5492900Seric 55055012Seric eatfrom(fm, e) 5512900Seric char *fm; 55255012Seric register ENVELOPE *e; 5532900Seric { 5542900Seric register char *p; 5552900Seric register char **dt; 5562900Seric 5577673Seric if (tTd(30, 2)) 5584203Seric printf("eatfrom(%s)\n", fm); 5594203Seric 5602900Seric /* find the date part */ 5612900Seric p = fm; 5622900Seric while (*p != '\0') 5632900Seric { 5642900Seric /* skip a word */ 5652900Seric while (*p != '\0' && *p != ' ') 56616896Seric p++; 5672900Seric while (*p == ' ') 56816896Seric p++; 56958050Seric if (!(isascii(*p) && isupper(*p)) || 57058050Seric p[3] != ' ' || p[13] != ':' || p[16] != ':') 5712900Seric continue; 5722900Seric 5732900Seric /* we have a possible date */ 5744203Seric for (dt = DowList; *dt != NULL; dt++) 5752900Seric if (strncmp(*dt, p, 3) == 0) 5762900Seric break; 5774203Seric if (*dt == NULL) 5784203Seric continue; 5792900Seric 5804203Seric for (dt = MonthList; *dt != NULL; dt++) 5814203Seric if (strncmp(*dt, &p[4], 3) == 0) 5824203Seric break; 5832900Seric if (*dt != NULL) 5842900Seric break; 5852900Seric } 5862900Seric 58760502Seric if (*p != '\0') 5882900Seric { 5893386Seric char *q; 5905366Seric extern char *arpadate(); 5913386Seric 5922900Seric /* we have found a date */ 5933386Seric q = xalloc(25); 59423103Seric (void) strncpy(q, p, 25); 5953386Seric q[24] = '\0'; 5965366Seric q = arpadate(q); 59755012Seric define('a', newstr(q), e); 5982900Seric } 5992900Seric } 6004321Seric 60156795Seric # endif /* NOTUNIX */ 602