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*67546Seric static char sccsid[] = "@(#)collect.c 8.17 (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: 24*67546Seric ** 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. 31*67546Seric ** 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 45*67546Seric collect(fp, smtpmode, requeueflag, hdrp, e) 46*67546Seric FILE *fp; 4752105Seric bool smtpmode; 4858929Seric bool requeueflag; 49*67546Seric 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; 57*67546Seric 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; 64*67546Seric if (hdrp == NULL) 65*67546Seric hdrp = &e->e_header; 66*67546Seric else 67*67546Seric headeronly = TRUE; 6866796Seric 691392Seric /* 701392Seric ** Create the temp file name and create the file. 711392Seric */ 721392Seric 73*67546Seric if (!headeronly) 741392Seric { 75*67546Seric e->e_df = queuename(e, 'd'); 76*67546Seric e->e_df = newstr(e->e_df); 77*67546Seric if ((tf = dfopen(e->e_df, O_WRONLY|O_CREAT|O_TRUNC, FileMode)) == NULL) 78*67546Seric { 79*67546Seric syserr("Cannot create %s", e->e_df); 80*67546Seric e->e_flags |= EF_NORETURN; 81*67546Seric finis(); 82*67546Seric } 83*67546Seric 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 100*67546Seric if (sfgets(buf, MAXLINE, fp, dbto, "initial message read") == NULL) 10140965Sbostic goto readerr; 1024557Seric fixcrlf(buf, FALSE); 1034321Seric # ifndef NOTUNIX 104*67546Seric if (!headeronly && !SaveFrom && strncmp(buf, "From ", 5) == 0) 1052900Seric { 106*67546Seric if (!flusheol(buf, fp, dbto)) 10740965Sbostic goto readerr; 10855012Seric eatfrom(buf, e); 109*67546Seric if (sfgets(buf, MAXLINE, fp, dbto, 11061093Seric "message header read") == NULL) 11140965Sbostic goto readerr; 1124557Seric fixcrlf(buf, FALSE); 1132900Seric } 11456795Seric # endif /* NOTUNIX */ 1152900Seric 1161392Seric /* 117*67546Seric ** 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 */ 140*67546Seric 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 156*67546Seric 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 167*67546Seric 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 233*67546Seric if (headeronly) 234*67546Seric { 235*67546Seric if (*workbuf != '\0') 236*67546Seric syserr("collect: lost first line of message"); 237*67546Seric goto readerr; 238*67546Seric } 239*67546Seric 24040965Sbostic if (*workbuf == '\0') 24140965Sbostic { 24240965Sbostic /* throw away a blank line */ 243*67546Seric 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); 279*67546Seric if (sfgets(buf, MAXLINE, fp, dbto, "message body read") == NULL) 28064718Seric goto readerr; 28164718Seric } 28240965Sbostic 283*67546Seric readerr: 284*67546Seric 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 294*67546Seric if (headeronly) 295*67546Seric return; 296*67546Seric 297*67546Seric if (tf != NULL) 29864762Seric { 299*67546Seric if (fflush(tf) != 0) 300*67546Seric tferror(tf, e); 301*67546Seric if (fsync(fileno(tf)) < 0 || fclose(tf) < 0) 302*67546Seric { 303*67546Seric tferror(tf, e); 304*67546Seric finis(); 305*67546Seric } 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 328*67546Seric if (feof(fp)) 32964718Seric problem = "unexpected close"; 330*67546Seric else if (ferror(fp)) 33164718Seric problem = "I/O error"; 33264718Seric else 33364718Seric problem = "read timeout"; 33436233Skarels # ifdef LOG 335*67546Seric 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 340*67546Seric 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 373*67546Seric if (hvalue("to", e->e_header) == NULL && 374*67546Seric hvalue("cc", e->e_header) == NULL && 375*67546Seric hvalue("bcc", e->e_header) == NULL && 376*67546Seric 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); 388*67546Seric 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 39955012Seric if ((e->e_dfp = fopen(e->e_df, "r")) == NULL) 40058690Seric { 40158690Seric /* we haven't acked receipt yet, so just chuck this */ 40255012Seric syserr("Cannot reopen %s", e->e_df); 40358690Seric finis(); 40458690Seric } 4051392Seric } 4061392Seric /* 40740965Sbostic ** FLUSHEOL -- if not at EOL, throw away rest of input line. 40840965Sbostic ** 40940965Sbostic ** Parameters: 41040965Sbostic ** buf -- last line read in (checked for '\n'), 41140965Sbostic ** fp -- file to be read from. 41240965Sbostic ** 41340965Sbostic ** Returns: 41440965Sbostic ** FALSE on error from sfgets(), TRUE otherwise. 41540965Sbostic ** 41640965Sbostic ** Side Effects: 41740965Sbostic ** none. 41840965Sbostic */ 41940965Sbostic 42040965Sbostic bool 42167268Seric flusheol(buf, fp, dbto) 42240965Sbostic char *buf; 42340965Sbostic FILE *fp; 42467268Seric time_t dbto; 42540965Sbostic { 42640965Sbostic register char *p = buf; 42757134Seric char junkbuf[MAXLINE]; 42840965Sbostic 42957134Seric while (strchr(p, '\n') == NULL) 43057134Seric { 43166796Seric CollectErrorMessage = "553 header line too long"; 43266796Seric CollectErrno = 0; 43367268Seric if (sfgets(junkbuf, MAXLINE, fp, dbto, 43461093Seric "long line flush") == NULL) 43557134Seric return (FALSE); 43640965Sbostic p = junkbuf; 43740965Sbostic } 43840965Sbostic 43957134Seric return (TRUE); 44040965Sbostic } 44140965Sbostic /* 44211544Seric ** TFERROR -- signal error on writing the temporary file. 44311544Seric ** 44411544Seric ** Parameters: 44511544Seric ** tf -- the file pointer for the temporary file. 44611544Seric ** 44711544Seric ** Returns: 44811544Seric ** none. 44911544Seric ** 45011544Seric ** Side Effects: 45111544Seric ** Gives an error message. 45211544Seric ** Arranges for following output to go elsewhere. 45311544Seric */ 45411544Seric 45555012Seric tferror(tf, e) 45611544Seric FILE *tf; 45755012Seric register ENVELOPE *e; 45811544Seric { 45966796Seric CollectErrno = errno; 46011544Seric if (errno == ENOSPC) 46111544Seric { 46266782Seric struct stat st; 46366782Seric long avail; 46466782Seric long bsize; 46566782Seric 46667473Seric e->e_flags |= EF_NORETURN; 46766782Seric if (fstat(fileno(tf), &st) < 0) 46866782Seric st.st_size = 0; 46955012Seric (void) freopen(e->e_df, "w", tf); 47066782Seric if (st.st_size <= 0) 47166782Seric fprintf(tf, "\n*** Mail could not be accepted"); 47266782Seric else if (sizeof st.st_size > sizeof (long)) 47366782Seric fprintf(tf, "\n*** Mail of at least %qd bytes could not be accepted\n", 47466782Seric st.st_size); 47566782Seric else 47666782Seric fprintf(tf, "\n*** Mail of at least %ld bytes could not be accepted\n", 47766782Seric st.st_size); 47866782Seric fprintf(tf, "*** at %s due to lack of disk space for temp file.\n", 47966782Seric MyHostName); 48066782Seric avail = freespace(QueueDir, &bsize); 48166782Seric if (avail > 0) 48266782Seric { 48366782Seric if (bsize > 1024) 48466782Seric avail *= bsize / 1024; 48566782Seric else if (bsize < 1024) 48666782Seric avail /= 1024 / bsize; 48766782Seric fprintf(tf, "*** Currently, %ld kilobytes are available for mail temp files.\n", 48866782Seric avail); 48966782Seric } 49066796Seric CollectErrorMessage = "452 Out of disk space for temp file"; 49111544Seric } 49211544Seric else 49366796Seric { 49466796Seric CollectErrorMessage = "cannot write message body to disk (%s)"; 49566796Seric } 49611544Seric (void) freopen("/dev/null", "w", tf); 49711544Seric } 49811544Seric /* 4992900Seric ** EATFROM -- chew up a UNIX style from line and process 5002900Seric ** 5012900Seric ** This does indeed make some assumptions about the format 5022900Seric ** of UNIX messages. 5032900Seric ** 5042900Seric ** Parameters: 5052900Seric ** fm -- the from line. 5062900Seric ** 5072900Seric ** Returns: 5082900Seric ** none. 5092900Seric ** 5102900Seric ** Side Effects: 5112900Seric ** extracts what information it can from the header, 5123386Seric ** such as the date. 5132900Seric */ 5142900Seric 5154321Seric # ifndef NOTUNIX 5164321Seric 5174203Seric char *DowList[] = 5184203Seric { 5194203Seric "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", NULL 5204203Seric }; 5214203Seric 5222900Seric char *MonthList[] = 5232900Seric { 5242900Seric "Jan", "Feb", "Mar", "Apr", "May", "Jun", 5252900Seric "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", 5262900Seric NULL 5272900Seric }; 5282900Seric 52955012Seric eatfrom(fm, e) 5302900Seric char *fm; 53155012Seric register ENVELOPE *e; 5322900Seric { 5332900Seric register char *p; 5342900Seric register char **dt; 5352900Seric 5367673Seric if (tTd(30, 2)) 5374203Seric printf("eatfrom(%s)\n", fm); 5384203Seric 5392900Seric /* find the date part */ 5402900Seric p = fm; 5412900Seric while (*p != '\0') 5422900Seric { 5432900Seric /* skip a word */ 5442900Seric while (*p != '\0' && *p != ' ') 54516896Seric p++; 5462900Seric while (*p == ' ') 54716896Seric p++; 54858050Seric if (!(isascii(*p) && isupper(*p)) || 54958050Seric p[3] != ' ' || p[13] != ':' || p[16] != ':') 5502900Seric continue; 5512900Seric 5522900Seric /* we have a possible date */ 5534203Seric for (dt = DowList; *dt != NULL; dt++) 5542900Seric if (strncmp(*dt, p, 3) == 0) 5552900Seric break; 5564203Seric if (*dt == NULL) 5574203Seric continue; 5582900Seric 5594203Seric for (dt = MonthList; *dt != NULL; dt++) 5604203Seric if (strncmp(*dt, &p[4], 3) == 0) 5614203Seric break; 5622900Seric if (*dt != NULL) 5632900Seric break; 5642900Seric } 5652900Seric 56660502Seric if (*p != '\0') 5672900Seric { 5683386Seric char *q; 5695366Seric extern char *arpadate(); 5703386Seric 5712900Seric /* we have found a date */ 5723386Seric q = xalloc(25); 57323103Seric (void) strncpy(q, p, 25); 5743386Seric q[24] = '\0'; 5755366Seric q = arpadate(q); 57655012Seric define('a', newstr(q), e); 5772900Seric } 5782900Seric } 5794321Seric 58056795Seric # endif /* NOTUNIX */ 581