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*68564Seric static char sccsid[] = "@(#)collect.c 8.31 (Berkeley) 03/21/95"; 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 4567599Seric static jmp_buf CtxCollectTimeout; 4668433Seric static void collecttimeout(); 4767599Seric static bool CollectProgress; 4867599Seric static EVENT *CollectTimeout; 4967599Seric 5067599Seric /* values for input state machine */ 5167599Seric #define IS_NORM 0 /* middle of line */ 5267599Seric #define IS_BOL 1 /* beginning of line */ 5367599Seric #define IS_DOT 2 /* read a dot at beginning of line */ 5467599Seric #define IS_DOTCR 3 /* read ".\r" at beginning of line */ 5567599Seric #define IS_CR 4 /* read a carriage return */ 5667599Seric 5767599Seric /* values for message state machine */ 5867599Seric #define MS_UFROM 0 /* reading Unix from line */ 5967599Seric #define MS_HEADER 1 /* reading message header */ 6067599Seric #define MS_BODY 2 /* reading message body */ 6167599Seric 6267599Seric 6368433Seric void 6467546Seric collect(fp, smtpmode, requeueflag, hdrp, e) 6567546Seric FILE *fp; 6652105Seric bool smtpmode; 6758929Seric bool requeueflag; 6867546Seric HDR **hdrp; 6955012Seric register ENVELOPE *e; 701392Seric { 711392Seric register FILE *tf; 7252105Seric bool ignrdot = smtpmode ? FALSE : IgnrDot; 7367268Seric time_t dbto = smtpmode ? TimeOuts.to_datablock : 0; 7467599Seric register char *bp; 7568433Seric int c = '\0'; 7664718Seric bool inputerr = FALSE; 7767546Seric bool headeronly = FALSE; 7867599Seric char *buf; 7967599Seric int buflen; 8067599Seric int istate; 8167599Seric int mstate; 8267599Seric char *pbp; 8367599Seric char peekbuf[8]; 84*68564Seric char dfname[20]; 8567599Seric char bufbuf[MAXLINE]; 8667599Seric extern bool isheader(); 8768433Seric extern void eatheader(); 8868433Seric extern void tferror(); 891392Seric 9066796Seric CollectErrorMessage = NULL; 9166796Seric CollectErrno = 0; 9267546Seric if (hdrp == NULL) 9367546Seric hdrp = &e->e_header; 9467546Seric else 9567546Seric headeronly = TRUE; 9666796Seric 971392Seric /* 981392Seric ** Create the temp file name and create the file. 991392Seric */ 1001392Seric 10167546Seric if (!headeronly) 1021392Seric { 10367617Seric struct stat stbuf; 10467617Seric 105*68564Seric strcpy(dfname, queuename(e, 'd')); 106*68564Seric if ((tf = dfopen(dfname, O_WRONLY|O_CREAT|O_TRUNC, FileMode)) == NULL) 10767546Seric { 108*68564Seric syserr("Cannot create %s", dfname); 10968559Seric e->e_flags |= EF_NO_BODY_RETN; 11067546Seric finis(); 11167546Seric } 11267617Seric if (fstat(fileno(tf), &stbuf) < 0) 11367617Seric e->e_dfino = -1; 11467617Seric else 11567688Seric { 11667688Seric e->e_dfdev = stbuf.st_dev; 11767617Seric e->e_dfino = stbuf.st_ino; 11867688Seric } 11967546Seric HasEightBits = FALSE; 12068560Seric e->e_msgsize = 0; 121*68564Seric e->e_flags |= EF_HAS_DF; 1221392Seric } 1231392Seric 1244316Seric /* 1254322Seric ** Tell ARPANET to go ahead. 1264322Seric */ 1274322Seric 12852105Seric if (smtpmode) 12958151Seric message("354 Enter mail, end with \".\" on a line by itself"); 1304322Seric 1314322Seric /* 13267599Seric ** Read the message. 13367599Seric ** 13467599Seric ** This is done using two interleaved state machines. 13567599Seric ** The input state machine is looking for things like 13667599Seric ** hidden dots; the message state machine is handling 13767599Seric ** the larger picture (e.g., header versus body). 1384316Seric */ 1394316Seric 14067599Seric buf = bp = bufbuf; 14167599Seric buflen = sizeof bufbuf; 14267599Seric pbp = peekbuf; 14367599Seric istate = IS_BOL; 14467599Seric mstate = SaveFrom ? MS_HEADER : MS_UFROM; 14567599Seric CollectProgress = FALSE; 14667599Seric 14767599Seric /* if transmitting binary, don't map NL to EOL */ 14867599Seric if (e->e_bodytype != NULL && strcasecmp(e->e_bodytype, "8BITMIME") == 0) 14967599Seric e->e_flags |= EF_NL_NOT_EOL; 15067599Seric 15167599Seric if (dbto != 0) 1522900Seric { 15367599Seric /* handle possible input timeout */ 15467599Seric if (setjmp(CtxCollectTimeout) != 0) 15567599Seric { 15667599Seric #ifdef LOG 15767599Seric syslog(LOG_NOTICE, 15867599Seric "timeout waiting for input from %s during message collect", 15967599Seric CurHostName ? CurHostName : "<local machine>"); 16067599Seric #endif 16167599Seric errno = 0; 16267599Seric usrerr("451 timeout waiting for input during message collect"); 16340965Sbostic goto readerr; 16467599Seric } 16567599Seric CollectTimeout = setevent(dbto, collecttimeout, dbto); 1662900Seric } 1672900Seric 16840965Sbostic for (;;) 1691392Seric { 17067599Seric if (tTd(30, 35)) 17167599Seric printf("top, istate=%d, mstate=%d\n", istate, mstate); 17240965Sbostic for (;;) 1731392Seric { 17467599Seric if (pbp > peekbuf) 17567599Seric c = *--pbp; 17667599Seric else 17764916Seric { 17867599Seric while (!feof(InChannel) && !ferror(InChannel)) 17967599Seric { 18067599Seric errno = 0; 18167599Seric c = fgetc(InChannel); 18267599Seric if (errno != EINTR) 18367599Seric break; 18467599Seric clearerr(InChannel); 18567599Seric } 18667599Seric CollectProgress = TRUE; 18767599Seric if (TrafficLogFile != NULL) 18867599Seric { 18967599Seric if (istate == IS_BOL) 19067599Seric fprintf(TrafficLogFile, "%05d <<< ", 19167599Seric getpid()); 19267599Seric if (c == EOF) 19367599Seric fprintf(TrafficLogFile, "[EOF]\n"); 19467599Seric else 19567599Seric fputc(c, TrafficLogFile); 19667599Seric } 19767599Seric if (c == EOF) 19867599Seric goto readerr; 19967599Seric if (SevenBitInput) 20067599Seric c &= 0x7f; 20167599Seric else 20267599Seric HasEightBits |= bitset(0x80, c); 20368560Seric if (!headeronly) 20468560Seric e->e_msgsize++; 20567599Seric } 20667599Seric if (tTd(30, 94)) 20767599Seric printf("istate=%d, c=%c (0x%x)\n", 20867599Seric istate, c, c); 20967599Seric switch (istate) 21067599Seric { 21167599Seric case IS_BOL: 21267599Seric if (c == '.') 21367599Seric { 21467599Seric istate = IS_DOT; 21567599Seric continue; 21667599Seric } 21764916Seric break; 21840965Sbostic 21967599Seric case IS_DOT: 22067599Seric if (c == '\n' && !ignrdot && 22167599Seric !bitset(EF_NL_NOT_EOL, e->e_flags)) 22267599Seric goto readerr; 22367599Seric else if (c == '\r' && 22467599Seric !bitset(EF_CRLF_NOT_EOL, e->e_flags)) 22567599Seric { 22667599Seric istate = IS_DOTCR; 22767599Seric continue; 22867599Seric } 22967599Seric else if (c != '.' || 23067599Seric (OpMode != MD_SMTP && 23167599Seric OpMode != MD_DAEMON && 23267599Seric OpMode != MD_ARPAFTP)) 23367599Seric { 23467599Seric *pbp++ = c; 23567599Seric c = '.'; 23667599Seric } 2372900Seric break; 23840965Sbostic 23967599Seric case IS_DOTCR: 24067599Seric if (c == '\n') 24167599Seric goto readerr; 24267599Seric else 24367599Seric { 24467599Seric /* push back the ".\rx" */ 24567599Seric *pbp++ = c; 24667599Seric *pbp++ = '\r'; 24767599Seric c = '.'; 24867599Seric } 24967599Seric break; 25040965Sbostic 25167599Seric case IS_CR: 25267689Seric if (c == '\n') 25367689Seric istate = IS_BOL; 25467689Seric else 25567599Seric { 25667599Seric ungetc(c, InChannel); 25767599Seric c = '\r'; 25867689Seric istate = IS_NORM; 25967599Seric } 26067689Seric goto bufferchar; 26167599Seric } 26257135Seric 26367689Seric if (c == '\r' && !bitset(EF_CRLF_NOT_EOL, e->e_flags)) 26440965Sbostic { 26567599Seric istate = IS_CR; 26667599Seric continue; 26767599Seric } 26867599Seric else if (c == '\n' && !bitset(EF_NL_NOT_EOL, e->e_flags)) 26967599Seric istate = IS_BOL; 27067599Seric else 27167599Seric istate = IS_NORM; 27240965Sbostic 27367689Seric bufferchar: 27467599Seric if (mstate == MS_BODY) 27567599Seric { 27667599Seric /* just put the character out */ 27767831Seric if (MaxMessageSize <= 0 || 27867831Seric e->e_msgsize <= MaxMessageSize) 27967831Seric fputc(c, tf); 28067599Seric continue; 28140965Sbostic } 2821392Seric 28367599Seric /* header -- buffer up */ 28467599Seric if (bp >= &buf[buflen - 2]) 28567599Seric { 28667599Seric char *obuf; 2871392Seric 28867599Seric if (mstate != MS_HEADER) 28967599Seric break; 29040965Sbostic 29167599Seric /* out of space for header */ 29267599Seric obuf = buf; 29367599Seric if (buflen < MEMCHUNKSIZE) 29467599Seric buflen *= 2; 29567599Seric else 29667599Seric buflen += MEMCHUNKSIZE; 29767599Seric buf = xalloc(buflen); 29867599Seric bcopy(obuf, buf, bp - obuf); 29967599Seric bp = &buf[bp - obuf]; 30067599Seric if (obuf != bufbuf) 30167599Seric free(obuf); 30267599Seric } 30367599Seric *bp++ = c; 30467599Seric if (istate == IS_BOL) 30567599Seric break; 30640965Sbostic } 30767599Seric *bp = '\0'; 30840965Sbostic 30967599Seric nextstate: 31067599Seric if (tTd(30, 35)) 31167599Seric printf("nextstate, istate=%d, mstate=%d, line = \"%s\"\n", 31267599Seric istate, mstate, buf); 31367599Seric switch (mstate) 31467599Seric { 31568433Seric extern int chompheader(); 31668433Seric 31767599Seric case MS_UFROM: 31867599Seric mstate = MS_HEADER; 31967599Seric if (strncmp(buf, "From ", 5) == 0) 32067599Seric { 32168433Seric extern void eatfrom(); 32268433Seric 32367704Seric bp = buf; 32467599Seric eatfrom(buf, e); 32567599Seric continue; 32667599Seric } 32767599Seric /* fall through */ 3282900Seric 32967599Seric case MS_HEADER: 33067599Seric if (!isheader(buf)) 33167599Seric { 33267599Seric mstate = MS_BODY; 33367599Seric goto nextstate; 33467599Seric } 33557135Seric 33667599Seric /* check for possible continuation line */ 33767599Seric do 33867599Seric { 33967599Seric clearerr(InChannel); 34067599Seric errno = 0; 34167599Seric c = fgetc(InChannel); 34267599Seric } while (errno == EINTR); 34367599Seric if (c != EOF) 34467599Seric ungetc(c, InChannel); 34567599Seric if (c == ' ' || c == '\t') 34667599Seric { 34767599Seric /* yep -- defer this */ 34867599Seric continue; 34967599Seric } 35057135Seric 35167599Seric /* trim off trailing CRLF or NL */ 35267599Seric if (*--bp != '\n' || *--bp != '\r') 35367599Seric bp++; 35467599Seric *bp = '\0'; 35567599Seric if (bitset(H_EOH, chompheader(buf, FALSE, e))) 35667599Seric mstate = MS_BODY; 35767599Seric break; 3581392Seric 35967599Seric case MS_BODY: 36067599Seric if (tTd(30, 1)) 36167599Seric printf("EOH\n"); 36267599Seric if (headeronly) 36367599Seric goto readerr; 36467599Seric bp = buf; 3652900Seric 36667599Seric /* toss blank line */ 36767599Seric if ((!bitset(EF_CRLF_NOT_EOL, e->e_flags) && 36867599Seric bp[0] == '\r' && bp[1] == '\n') || 36967599Seric (!bitset(EF_NL_NOT_EOL, e->e_flags) && 37067599Seric bp[0] == '\n')) 37167599Seric { 37267599Seric break; 37367599Seric } 37467546Seric 37567599Seric /* if not a blank separator, write it out */ 37667831Seric if (MaxMessageSize <= 0 || 37767831Seric e->e_msgsize <= MaxMessageSize) 37867831Seric { 37967831Seric while (*bp != '\0') 38067831Seric fputc(*bp++, tf); 38167831Seric } 3822900Seric break; 38367599Seric } 38467599Seric bp = buf; 38564718Seric } 38640965Sbostic 38767546Seric readerr: 38867546Seric if ((feof(fp) && smtpmode) || ferror(fp)) 38964718Seric { 39064916Seric if (tTd(30, 1)) 39164916Seric printf("collect: read error\n"); 39264718Seric inputerr = TRUE; 39364718Seric } 39464718Seric 39566765Seric /* reset global timer */ 39667599Seric clrevent(CollectTimeout); 39766765Seric 39867546Seric if (headeronly) 39967546Seric return; 40067546Seric 40167546Seric if (tf != NULL) 40264762Seric { 40367546Seric if (fflush(tf) != 0) 40467546Seric tferror(tf, e); 40567546Seric if (fsync(fileno(tf)) < 0 || fclose(tf) < 0) 40667546Seric { 40767546Seric tferror(tf, e); 40867546Seric finis(); 40967546Seric } 41064762Seric } 4112900Seric 41266796Seric if (CollectErrorMessage != NULL && Errors <= 0) 41316136Seric { 41466796Seric if (CollectErrno != 0) 41566796Seric { 41666796Seric errno = CollectErrno; 417*68564Seric syserr(CollectErrorMessage, dfname); 41866796Seric finis(); 41966796Seric } 42066796Seric usrerr(CollectErrorMessage); 42166796Seric } 42266796Seric else if (inputerr && (OpMode == MD_SMTP || OpMode == MD_DAEMON)) 42366796Seric { 42466796Seric /* An EOF when running SMTP is an error */ 42558308Seric char *host; 42664718Seric char *problem; 42758082Seric 42858308Seric host = RealHostName; 42958308Seric if (host == NULL) 43058308Seric host = "localhost"; 43158308Seric 43267546Seric if (feof(fp)) 43364718Seric problem = "unexpected close"; 43467546Seric else if (ferror(fp)) 43564718Seric problem = "I/O error"; 43664718Seric else 43764718Seric problem = "read timeout"; 43836233Skarels # ifdef LOG 43967546Seric if (LogLevel > 0 && feof(fp)) 44036230Skarels syslog(LOG_NOTICE, 44166864Seric "collect: %s on connection from %s, sender=%s: %s\n", 44266864Seric problem, host, e->e_from.q_paddr, errstring(errno)); 44336233Skarels # endif 44467546Seric if (feof(fp)) 44565951Seric usrerr("451 collect: %s on connection from %s, from=%s", 44664718Seric problem, host, e->e_from.q_paddr); 44765951Seric else 44865951Seric syserr("451 collect: %s on connection from %s, from=%s", 44965951Seric problem, host, e->e_from.q_paddr); 45011145Seric 45116136Seric /* don't return an error indication */ 45255012Seric e->e_to = NULL; 45355012Seric e->e_flags &= ~EF_FATALERRS; 45464124Seric e->e_flags |= EF_CLRQUEUE; 45516136Seric 45616136Seric /* and don't try to deliver the partial message either */ 45764718Seric if (InChild) 45864718Seric ExitStat = EX_QUIT; 45916136Seric finis(); 46016136Seric } 46116136Seric 4622900Seric /* 4632900Seric ** Find out some information from the headers. 4643386Seric ** Examples are who is the from person & the date. 4652900Seric */ 4662900Seric 46758929Seric eatheader(e, !requeueflag); 4687673Seric 46964068Seric /* collect statistics */ 47064068Seric if (OpMode != MD_VERIFY) 47168433Seric { 47268433Seric extern void markstats(); 47368433Seric 47464068Seric markstats(e, (ADDRESS *) NULL); 47568433Seric } 47664068Seric 4777782Seric /* 4787782Seric ** Add an Apparently-To: line if we have no recipient lines. 4797782Seric */ 4804622Seric 48167546Seric if (hvalue("to", e->e_header) == NULL && 48267546Seric hvalue("cc", e->e_header) == NULL && 48367546Seric hvalue("bcc", e->e_header) == NULL && 48467546Seric hvalue("apparently-to", e->e_header) == NULL) 4857367Seric { 4867367Seric register ADDRESS *q; 48768449Seric char *hdr = NULL; 48868449Seric extern void addheader(); 4897367Seric 4907367Seric /* create an Apparently-To: field */ 4917367Seric /* that or reject the message.... */ 49268449Seric switch (NoRecipientAction) 4937367Seric { 49468449Seric case NRA_ADD_APPARENTLY_TO: 49568449Seric hdr = "Apparently-To"; 49668449Seric break; 49768433Seric 49868449Seric case NRA_ADD_TO: 49968449Seric hdr = "To"; 50068449Seric break; 50168449Seric 50268449Seric case NRA_ADD_BCC: 50368449Seric addheader("Bcc", "", &e->e_header); 50468449Seric break; 50568449Seric 50668449Seric case NRA_ADD_TO_UNDISCLOSED: 50768449Seric addheader("To", "undisclosed-recipients:;", &e->e_header); 50868449Seric break; 5097367Seric } 51068449Seric 51168449Seric if (hdr != NULL) 51268449Seric { 51368449Seric for (q = e->e_sendqueue; q != NULL; q = q->q_next) 51468449Seric { 51568449Seric if (q->q_alias != NULL) 51668449Seric continue; 51768449Seric if (tTd(30, 3)) 51868449Seric printf("Adding %s: %s\n", 51968449Seric hdr, q->q_paddr); 52068449Seric addheader(hdr, q->q_paddr, &e->e_header); 52168449Seric } 52268449Seric } 5237367Seric } 5247367Seric 52559320Seric /* check for message too large */ 52659320Seric if (MaxMessageSize > 0 && e->e_msgsize > MaxMessageSize) 52759320Seric { 52859320Seric usrerr("552 Message exceeds maximum fixed size (%ld)", 52959320Seric MaxMessageSize); 53059320Seric } 53159320Seric 53267547Seric /* check for illegal 8-bit data */ 53367547Seric if (HasEightBits) 53467547Seric { 53567547Seric e->e_flags |= EF_HAS8BIT; 53667887Seric if (!bitset(MM_PASS8BIT|MM_MIME8BIT, MimeMode)) 53767547Seric usrerr("554 Eight bit data not allowed"); 53867547Seric } 53967547Seric 540*68564Seric if ((e->e_dfp = fopen(dfname, "r")) == NULL) 54158690Seric { 54258690Seric /* we haven't acked receipt yet, so just chuck this */ 543*68564Seric syserr("Cannot reopen %s", dfname); 54458690Seric finis(); 54558690Seric } 5461392Seric } 54740965Sbostic 54867599Seric 54968433Seric static void 55067599Seric collecttimeout(timeout) 55167599Seric time_t timeout; 55240965Sbostic { 55367599Seric /* if no progress was made, die now */ 55467599Seric if (!CollectProgress) 55567599Seric longjmp(CtxCollectTimeout, 1); 55640965Sbostic 55767599Seric /* otherwise reset the timeout */ 55867599Seric CollectTimeout = setevent(timeout, collecttimeout, timeout); 55967599Seric CollectProgress = FALSE; 56040965Sbostic } 56140965Sbostic /* 56211544Seric ** TFERROR -- signal error on writing the temporary file. 56311544Seric ** 56411544Seric ** Parameters: 56511544Seric ** tf -- the file pointer for the temporary file. 56611544Seric ** 56711544Seric ** Returns: 56811544Seric ** none. 56911544Seric ** 57011544Seric ** Side Effects: 57111544Seric ** Gives an error message. 57211544Seric ** Arranges for following output to go elsewhere. 57311544Seric */ 57411544Seric 57568433Seric void 57655012Seric tferror(tf, e) 57711544Seric FILE *tf; 57855012Seric register ENVELOPE *e; 57911544Seric { 58066796Seric CollectErrno = errno; 58111544Seric if (errno == ENOSPC) 58211544Seric { 58366782Seric struct stat st; 58466782Seric long avail; 58566782Seric long bsize; 58666782Seric 58768559Seric e->e_flags |= EF_NO_BODY_RETN; 58866782Seric if (fstat(fileno(tf), &st) < 0) 58966782Seric st.st_size = 0; 590*68564Seric (void) freopen(queuename(e, 'd'), "w", tf); 59166782Seric if (st.st_size <= 0) 59266782Seric fprintf(tf, "\n*** Mail could not be accepted"); 59366782Seric else if (sizeof st.st_size > sizeof (long)) 59466782Seric fprintf(tf, "\n*** Mail of at least %qd bytes could not be accepted\n", 59566782Seric st.st_size); 59666782Seric else 59766782Seric fprintf(tf, "\n*** Mail of at least %ld bytes could not be accepted\n", 59866782Seric st.st_size); 59966782Seric fprintf(tf, "*** at %s due to lack of disk space for temp file.\n", 60066782Seric MyHostName); 60166782Seric avail = freespace(QueueDir, &bsize); 60266782Seric if (avail > 0) 60366782Seric { 60466782Seric if (bsize > 1024) 60566782Seric avail *= bsize / 1024; 60666782Seric else if (bsize < 1024) 60766782Seric avail /= 1024 / bsize; 60866782Seric fprintf(tf, "*** Currently, %ld kilobytes are available for mail temp files.\n", 60966782Seric avail); 61066782Seric } 61166796Seric CollectErrorMessage = "452 Out of disk space for temp file"; 61211544Seric } 61311544Seric else 61466796Seric { 61566796Seric CollectErrorMessage = "cannot write message body to disk (%s)"; 61666796Seric } 61711544Seric (void) freopen("/dev/null", "w", tf); 61811544Seric } 61911544Seric /* 6202900Seric ** EATFROM -- chew up a UNIX style from line and process 6212900Seric ** 6222900Seric ** This does indeed make some assumptions about the format 6232900Seric ** of UNIX messages. 6242900Seric ** 6252900Seric ** Parameters: 6262900Seric ** fm -- the from line. 6272900Seric ** 6282900Seric ** Returns: 6292900Seric ** none. 6302900Seric ** 6312900Seric ** Side Effects: 6322900Seric ** extracts what information it can from the header, 6333386Seric ** such as the date. 6342900Seric */ 6352900Seric 6364321Seric # ifndef NOTUNIX 6374321Seric 6384203Seric char *DowList[] = 6394203Seric { 6404203Seric "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", NULL 6414203Seric }; 6424203Seric 6432900Seric char *MonthList[] = 6442900Seric { 6452900Seric "Jan", "Feb", "Mar", "Apr", "May", "Jun", 6462900Seric "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", 6472900Seric NULL 6482900Seric }; 6492900Seric 65068433Seric void 65155012Seric eatfrom(fm, e) 6522900Seric char *fm; 65355012Seric register ENVELOPE *e; 6542900Seric { 6552900Seric register char *p; 6562900Seric register char **dt; 6572900Seric 6587673Seric if (tTd(30, 2)) 6594203Seric printf("eatfrom(%s)\n", fm); 6604203Seric 6612900Seric /* find the date part */ 6622900Seric p = fm; 6632900Seric while (*p != '\0') 6642900Seric { 6652900Seric /* skip a word */ 6662900Seric while (*p != '\0' && *p != ' ') 66716896Seric p++; 6682900Seric while (*p == ' ') 66916896Seric p++; 67058050Seric if (!(isascii(*p) && isupper(*p)) || 67158050Seric p[3] != ' ' || p[13] != ':' || p[16] != ':') 6722900Seric continue; 6732900Seric 6742900Seric /* we have a possible date */ 6754203Seric for (dt = DowList; *dt != NULL; dt++) 6762900Seric if (strncmp(*dt, p, 3) == 0) 6772900Seric break; 6784203Seric if (*dt == NULL) 6794203Seric continue; 6802900Seric 6814203Seric for (dt = MonthList; *dt != NULL; dt++) 6824203Seric if (strncmp(*dt, &p[4], 3) == 0) 6834203Seric break; 6842900Seric if (*dt != NULL) 6852900Seric break; 6862900Seric } 6872900Seric 68860502Seric if (*p != '\0') 6892900Seric { 6903386Seric char *q; 6915366Seric extern char *arpadate(); 6923386Seric 6932900Seric /* we have found a date */ 6943386Seric q = xalloc(25); 69523103Seric (void) strncpy(q, p, 25); 6963386Seric q[24] = '\0'; 6975366Seric q = arpadate(q); 69855012Seric define('a', newstr(q), e); 6992900Seric } 7002900Seric } 7014321Seric 70256795Seric # endif /* NOTUNIX */ 703