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*66765Seric static char sccsid[] = "@(#)collect.c 8.10 (Berkeley) 04/12/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 73*66765Seric /* set global timer to monitor progress */ 74*66765Seric sfgetset(TimeOuts.to_datablock); 75*66765Seric 764322Seric /* 774316Seric ** Try to read a UNIX-style From line 784316Seric */ 794316Seric 8061093Seric if (sfgets(buf, MAXLINE, InChannel, TimeOuts.to_datablock, 8161093Seric "initial message read") == NULL) 8240965Sbostic goto readerr; 834557Seric fixcrlf(buf, FALSE); 844321Seric # ifndef NOTUNIX 854322Seric if (!SaveFrom && strncmp(buf, "From ", 5) == 0) 862900Seric { 8740965Sbostic if (!flusheol(buf, InChannel)) 8840965Sbostic goto readerr; 8955012Seric eatfrom(buf, e); 9061093Seric if (sfgets(buf, MAXLINE, InChannel, TimeOuts.to_datablock, 9161093Seric "message header read") == NULL) 9240965Sbostic goto readerr; 934557Seric fixcrlf(buf, FALSE); 942900Seric } 9556795Seric # endif /* NOTUNIX */ 962900Seric 971392Seric /* 985975Seric ** Copy InChannel to temp file & do message editing. 991392Seric ** To keep certain mailers from getting confused, 1001392Seric ** and to keep the output clean, lines that look 10113932Seric ** like UNIX "From" lines are deleted in the header. 1021392Seric */ 1031392Seric 10440965Sbostic workbuf = buf; /* `workbuf' contains a header field */ 10540965Sbostic freebuf = buf2; /* `freebuf' can be used for read-ahead */ 10640965Sbostic for (;;) 1071392Seric { 10857135Seric char *curbuf; 10957135Seric int curbuffree; 11057135Seric register int curbuflen; 11157135Seric char *p; 11257135Seric 11340965Sbostic /* first, see if the header is over */ 11440965Sbostic if (!isheader(workbuf)) 11540965Sbostic { 11640965Sbostic fixcrlf(workbuf, TRUE); 11719036Seric break; 11840965Sbostic } 11919036Seric 1207681Seric /* if the line is too long, throw the rest away */ 12140965Sbostic if (!flusheol(workbuf, InChannel)) 12240965Sbostic goto readerr; 1237681Seric 12440965Sbostic /* it's okay to toss '\n' now (flusheol() needed it) */ 12540965Sbostic fixcrlf(workbuf, TRUE); 1264557Seric 12757135Seric curbuf = workbuf; 12857135Seric curbuflen = strlen(curbuf); 12957135Seric curbuffree = MAXLINE - curbuflen; 13057135Seric p = curbuf + curbuflen; 1312900Seric 1322900Seric /* get the rest of this field */ 13340965Sbostic for (;;) 1341392Seric { 13557135Seric int clen; 13657135Seric 13761093Seric if (sfgets(freebuf, MAXLINE, InChannel, 13861093Seric TimeOuts.to_datablock, 13961093Seric "message header read") == NULL) 14064916Seric { 14164916Seric freebuf[0] = '\0'; 14264916Seric break; 14364916Seric } 14440965Sbostic 14540965Sbostic /* is this a continuation line? */ 14640965Sbostic if (*freebuf != ' ' && *freebuf != '\t') 1472900Seric break; 14840965Sbostic 14940965Sbostic if (!flusheol(freebuf, InChannel)) 15040965Sbostic goto readerr; 15140965Sbostic 15257135Seric fixcrlf(freebuf, TRUE); 15357135Seric clen = strlen(freebuf) + 1; 15457135Seric 15557135Seric /* if insufficient room, dynamically allocate buffer */ 15657135Seric if (clen >= curbuffree) 15740965Sbostic { 15857135Seric /* reallocate buffer */ 15957135Seric int nbuflen = ((p - curbuf) + clen) * 2; 16057135Seric char *nbuf = xalloc(nbuflen); 16140965Sbostic 16257135Seric p = nbuf + curbuflen; 16357135Seric curbuffree = nbuflen - curbuflen; 16457135Seric bcopy(curbuf, nbuf, curbuflen); 16557135Seric if (curbuf != buf && curbuf != buf2) 16657135Seric free(curbuf); 16757135Seric curbuf = nbuf; 16840965Sbostic } 16957135Seric *p++ = '\n'; 17057135Seric bcopy(freebuf, p, clen - 1); 17157135Seric p += clen - 1; 17257135Seric curbuffree -= clen; 17357135Seric curbuflen += clen; 1741392Seric } 17557135Seric *p++ = '\0'; 1761392Seric 17757135Seric e->e_msgsize += curbuflen; 1781392Seric 1792900Seric /* 18040965Sbostic ** The working buffer now becomes the free buffer, since 18140965Sbostic ** the free buffer contains a new header field. 18240965Sbostic ** 18340965Sbostic ** This is premature, since we still havent called 18440965Sbostic ** chompheader() to process the field we just created 18540965Sbostic ** (so the call to chompheader() will use `freebuf'). 18640965Sbostic ** This convolution is necessary so that if we break out 18740965Sbostic ** of the loop due to H_EOH, `workbuf' will always be 18840965Sbostic ** the next unprocessed buffer. 18940965Sbostic */ 19040965Sbostic 19140965Sbostic { 19240965Sbostic register char *tmp = workbuf; 19340965Sbostic workbuf = freebuf; 19440965Sbostic freebuf = tmp; 19540965Sbostic } 19640965Sbostic 19740965Sbostic /* 1982900Seric ** Snarf header away. 1992900Seric */ 2002900Seric 20157135Seric if (bitset(H_EOH, chompheader(curbuf, FALSE, e))) 2023058Seric break; 20357135Seric 20457135Seric /* 20557135Seric ** If the buffer was dynamically allocated, free it. 20657135Seric */ 20757135Seric 20857135Seric if (curbuf != buf && curbuf != buf2) 20957135Seric free(curbuf); 21040965Sbostic } 2111392Seric 2127673Seric if (tTd(30, 1)) 2132900Seric printf("EOH\n"); 2142900Seric 21540965Sbostic if (*workbuf == '\0') 21640965Sbostic { 21740965Sbostic /* throw away a blank line */ 21861093Seric if (sfgets(buf, MAXLINE, InChannel, TimeOuts.to_datablock, 21961093Seric "message separator read") == NULL) 22040965Sbostic goto readerr; 22140965Sbostic } 22240965Sbostic else if (workbuf == buf2) /* guarantee `buf' contains data */ 22340965Sbostic (void) strcpy(buf, buf2); 2242900Seric 2252900Seric /* 2262900Seric ** Collect the body of the message. 2272900Seric */ 2282900Seric 22964718Seric for (;;) 2302900Seric { 2314551Seric register char *bp = buf; 2324156Seric 2337852Seric fixcrlf(buf, TRUE); 2344557Seric 2352900Seric /* check for end-of-message */ 23652105Seric if (!ignrdot && buf[0] == '.' && (buf[1] == '\n' || buf[1] == '\0')) 2372900Seric break; 2382900Seric 2394551Seric /* check for transparent dot */ 24065580Seric if ((OpMode == MD_SMTP || OpMode == MD_DAEMON) && 24165580Seric bp[0] == '.' && bp[1] == '.') 2424551Seric bp++; 2434551Seric 2444156Seric /* 2454156Seric ** Figure message length, output the line to the temp 2464156Seric ** file, and insert a newline if missing. 2474156Seric */ 2484156Seric 24955012Seric e->e_msgsize += strlen(bp) + 1; 2504551Seric fputs(bp, tf); 2517852Seric fputs("\n", tf); 2521392Seric if (ferror(tf)) 25355012Seric tferror(tf, e); 25464718Seric if (sfgets(buf, MAXLINE, InChannel, TimeOuts.to_datablock, 25564718Seric "message body read") == NULL) 25664718Seric goto readerr; 25764718Seric } 25840965Sbostic 25964718Seric if (feof(InChannel) || ferror(InChannel)) 26064718Seric { 26140965Sbostic readerr: 26264916Seric if (tTd(30, 1)) 26364916Seric printf("collect: read error\n"); 26464718Seric inputerr = TRUE; 26564718Seric } 26664718Seric 267*66765Seric /* reset global timer */ 268*66765Seric sfgetset((time_t) 0); 269*66765Seric 27011544Seric if (fflush(tf) != 0) 27155012Seric tferror(tf, e); 27264762Seric if (fsync(fileno(tf)) < 0 || fclose(tf) < 0) 27364762Seric { 27464762Seric syserr("cannot sync message data to disk (%s)", e->e_df); 27564762Seric finis(); 27664762Seric } 2772900Seric 27811145Seric /* An EOF when running SMTP is an error */ 27965580Seric if (inputerr && (OpMode == MD_SMTP || OpMode == MD_DAEMON)) 28016136Seric { 28158308Seric char *host; 28264718Seric char *problem; 28358082Seric 28458308Seric host = RealHostName; 28558308Seric if (host == NULL) 28658308Seric host = "localhost"; 28758308Seric 28864718Seric if (feof(InChannel)) 28964718Seric problem = "unexpected close"; 29064718Seric else if (ferror(InChannel)) 29164718Seric problem = "I/O error"; 29264718Seric else 29364718Seric problem = "read timeout"; 29436233Skarels # ifdef LOG 29558308Seric if (LogLevel > 0 && feof(InChannel)) 29636230Skarels syslog(LOG_NOTICE, 29764718Seric "collect: %s on connection from %s, sender=%s: %m\n", 29864718Seric problem, host, e->e_from.q_paddr); 29936233Skarels # endif 30065951Seric if (feof(InChannel)) 30165951Seric usrerr("451 collect: %s on connection from %s, from=%s", 30264718Seric problem, host, e->e_from.q_paddr); 30365951Seric else 30465951Seric syserr("451 collect: %s on connection from %s, from=%s", 30565951Seric problem, host, e->e_from.q_paddr); 30611145Seric 30716136Seric /* don't return an error indication */ 30855012Seric e->e_to = NULL; 30955012Seric e->e_flags &= ~EF_FATALERRS; 31064124Seric e->e_flags |= EF_CLRQUEUE; 31116136Seric 31216136Seric /* and don't try to deliver the partial message either */ 31364718Seric if (InChild) 31464718Seric ExitStat = EX_QUIT; 31516136Seric finis(); 31616136Seric } 31716136Seric 3182900Seric /* 3192900Seric ** Find out some information from the headers. 3203386Seric ** Examples are who is the from person & the date. 3212900Seric */ 3222900Seric 32358929Seric eatheader(e, !requeueflag); 3247673Seric 32564068Seric /* collect statistics */ 32664068Seric if (OpMode != MD_VERIFY) 32764068Seric markstats(e, (ADDRESS *) NULL); 32864068Seric 3297782Seric /* 3307782Seric ** Add an Apparently-To: line if we have no recipient lines. 3317782Seric */ 3324622Seric 33355012Seric if (hvalue("to", e) == NULL && hvalue("cc", e) == NULL && 33455012Seric hvalue("bcc", e) == NULL && hvalue("apparently-to", e) == NULL) 3357367Seric { 3367367Seric register ADDRESS *q; 3377367Seric 3387367Seric /* create an Apparently-To: field */ 3397367Seric /* that or reject the message.... */ 34055012Seric for (q = e->e_sendqueue; q != NULL; q = q->q_next) 3417367Seric { 3427389Seric if (q->q_alias != NULL) 3437389Seric continue; 3447673Seric if (tTd(30, 3)) 3457367Seric printf("Adding Apparently-To: %s\n", q->q_paddr); 34659579Seric addheader("Apparently-To", q->q_paddr, e); 3477367Seric } 3487367Seric } 3497367Seric 35059320Seric /* check for message too large */ 35159320Seric if (MaxMessageSize > 0 && e->e_msgsize > MaxMessageSize) 35259320Seric { 35359320Seric usrerr("552 Message exceeds maximum fixed size (%ld)", 35459320Seric MaxMessageSize); 35559320Seric } 35659320Seric 35755012Seric if ((e->e_dfp = fopen(e->e_df, "r")) == NULL) 35858690Seric { 35958690Seric /* we haven't acked receipt yet, so just chuck this */ 36055012Seric syserr("Cannot reopen %s", e->e_df); 36158690Seric finis(); 36258690Seric } 3631392Seric } 3641392Seric /* 36540965Sbostic ** FLUSHEOL -- if not at EOL, throw away rest of input line. 36640965Sbostic ** 36740965Sbostic ** Parameters: 36840965Sbostic ** buf -- last line read in (checked for '\n'), 36940965Sbostic ** fp -- file to be read from. 37040965Sbostic ** 37140965Sbostic ** Returns: 37240965Sbostic ** FALSE on error from sfgets(), TRUE otherwise. 37340965Sbostic ** 37440965Sbostic ** Side Effects: 37540965Sbostic ** none. 37640965Sbostic */ 37740965Sbostic 37840965Sbostic bool 37940965Sbostic flusheol(buf, fp) 38040965Sbostic char *buf; 38140965Sbostic FILE *fp; 38240965Sbostic { 38340965Sbostic register char *p = buf; 38457134Seric bool printmsg = TRUE; 38557134Seric char junkbuf[MAXLINE]; 38640965Sbostic 38757134Seric while (strchr(p, '\n') == NULL) 38857134Seric { 38957134Seric if (printmsg) 39058151Seric usrerr("553 header line too long"); 39157134Seric printmsg = FALSE; 39261093Seric if (sfgets(junkbuf, MAXLINE, fp, TimeOuts.to_datablock, 39361093Seric "long line flush") == NULL) 39457134Seric return (FALSE); 39540965Sbostic p = junkbuf; 39640965Sbostic } 39740965Sbostic 39857134Seric return (TRUE); 39940965Sbostic } 40040965Sbostic /* 40111544Seric ** TFERROR -- signal error on writing the temporary file. 40211544Seric ** 40311544Seric ** Parameters: 40411544Seric ** tf -- the file pointer for the temporary file. 40511544Seric ** 40611544Seric ** Returns: 40711544Seric ** none. 40811544Seric ** 40911544Seric ** Side Effects: 41011544Seric ** Gives an error message. 41111544Seric ** Arranges for following output to go elsewhere. 41211544Seric */ 41311544Seric 41455012Seric tferror(tf, e) 41511544Seric FILE *tf; 41655012Seric register ENVELOPE *e; 41711544Seric { 41811544Seric if (errno == ENOSPC) 41911544Seric { 42055012Seric (void) freopen(e->e_df, "w", tf); 42111544Seric fputs("\nMAIL DELETED BECAUSE OF LACK OF DISK SPACE\n\n", tf); 42211544Seric usrerr("452 Out of disk space for temp file"); 42311544Seric } 42411544Seric else 42555012Seric syserr("collect: Cannot write %s", e->e_df); 42611544Seric (void) freopen("/dev/null", "w", tf); 42711544Seric } 42811544Seric /* 4292900Seric ** EATFROM -- chew up a UNIX style from line and process 4302900Seric ** 4312900Seric ** This does indeed make some assumptions about the format 4322900Seric ** of UNIX messages. 4332900Seric ** 4342900Seric ** Parameters: 4352900Seric ** fm -- the from line. 4362900Seric ** 4372900Seric ** Returns: 4382900Seric ** none. 4392900Seric ** 4402900Seric ** Side Effects: 4412900Seric ** extracts what information it can from the header, 4423386Seric ** such as the date. 4432900Seric */ 4442900Seric 4454321Seric # ifndef NOTUNIX 4464321Seric 4474203Seric char *DowList[] = 4484203Seric { 4494203Seric "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", NULL 4504203Seric }; 4514203Seric 4522900Seric char *MonthList[] = 4532900Seric { 4542900Seric "Jan", "Feb", "Mar", "Apr", "May", "Jun", 4552900Seric "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", 4562900Seric NULL 4572900Seric }; 4582900Seric 45955012Seric eatfrom(fm, e) 4602900Seric char *fm; 46155012Seric register ENVELOPE *e; 4622900Seric { 4632900Seric register char *p; 4642900Seric register char **dt; 4652900Seric 4667673Seric if (tTd(30, 2)) 4674203Seric printf("eatfrom(%s)\n", fm); 4684203Seric 4692900Seric /* find the date part */ 4702900Seric p = fm; 4712900Seric while (*p != '\0') 4722900Seric { 4732900Seric /* skip a word */ 4742900Seric while (*p != '\0' && *p != ' ') 47516896Seric p++; 4762900Seric while (*p == ' ') 47716896Seric p++; 47858050Seric if (!(isascii(*p) && isupper(*p)) || 47958050Seric p[3] != ' ' || p[13] != ':' || p[16] != ':') 4802900Seric continue; 4812900Seric 4822900Seric /* we have a possible date */ 4834203Seric for (dt = DowList; *dt != NULL; dt++) 4842900Seric if (strncmp(*dt, p, 3) == 0) 4852900Seric break; 4864203Seric if (*dt == NULL) 4874203Seric continue; 4882900Seric 4894203Seric for (dt = MonthList; *dt != NULL; dt++) 4904203Seric if (strncmp(*dt, &p[4], 3) == 0) 4914203Seric break; 4922900Seric if (*dt != NULL) 4932900Seric break; 4942900Seric } 4952900Seric 49660502Seric if (*p != '\0') 4972900Seric { 4983386Seric char *q; 4995366Seric extern char *arpadate(); 5003386Seric 5012900Seric /* we have found a date */ 5023386Seric q = xalloc(25); 50323103Seric (void) strncpy(q, p, 25); 5043386Seric q[24] = '\0'; 5055366Seric q = arpadate(q); 50655012Seric define('a', newstr(q), e); 5072900Seric } 5082900Seric } 5094321Seric 51056795Seric # endif /* NOTUNIX */ 511