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*68692Seric static char sccsid[] = "@(#)collect.c 8.32 (Berkeley) 03/31/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 4267599Seric static jmp_buf CtxCollectTimeout; 4368433Seric static void collecttimeout(); 4467599Seric static bool CollectProgress; 4567599Seric static EVENT *CollectTimeout; 4667599Seric 4767599Seric /* values for input state machine */ 4867599Seric #define IS_NORM 0 /* middle of line */ 4967599Seric #define IS_BOL 1 /* beginning of line */ 5067599Seric #define IS_DOT 2 /* read a dot at beginning of line */ 5167599Seric #define IS_DOTCR 3 /* read ".\r" at beginning of line */ 5267599Seric #define IS_CR 4 /* read a carriage return */ 5367599Seric 5467599Seric /* values for message state machine */ 5567599Seric #define MS_UFROM 0 /* reading Unix from line */ 5667599Seric #define MS_HEADER 1 /* reading message header */ 5767599Seric #define MS_BODY 2 /* reading message body */ 5867599Seric 5968433Seric void 6067546Seric collect(fp, smtpmode, requeueflag, hdrp, e) 6167546Seric FILE *fp; 6252105Seric bool smtpmode; 6358929Seric bool requeueflag; 6467546Seric HDR **hdrp; 6555012Seric register ENVELOPE *e; 661392Seric { 671392Seric register FILE *tf; 6852105Seric bool ignrdot = smtpmode ? FALSE : IgnrDot; 6967268Seric time_t dbto = smtpmode ? TimeOuts.to_datablock : 0; 7067599Seric register char *bp; 7168433Seric int c = '\0'; 7264718Seric bool inputerr = FALSE; 7367546Seric bool headeronly = FALSE; 7467599Seric char *buf; 7567599Seric int buflen; 7667599Seric int istate; 7767599Seric int mstate; 7867599Seric char *pbp; 7967599Seric char peekbuf[8]; 8068564Seric char dfname[20]; 8167599Seric char bufbuf[MAXLINE]; 8267599Seric extern bool isheader(); 8368433Seric extern void eatheader(); 8468433Seric extern void tferror(); 851392Seric 8667546Seric if (hdrp == NULL) 8767546Seric hdrp = &e->e_header; 8867546Seric else 8967546Seric headeronly = TRUE; 9066796Seric 911392Seric /* 921392Seric ** Create the temp file name and create the file. 931392Seric */ 941392Seric 9567546Seric if (!headeronly) 961392Seric { 9767617Seric struct stat stbuf; 9867617Seric 9968564Seric strcpy(dfname, queuename(e, 'd')); 10068564Seric if ((tf = dfopen(dfname, O_WRONLY|O_CREAT|O_TRUNC, FileMode)) == NULL) 10167546Seric { 10268564Seric syserr("Cannot create %s", dfname); 10368559Seric e->e_flags |= EF_NO_BODY_RETN; 10467546Seric finis(); 10567546Seric } 10667617Seric if (fstat(fileno(tf), &stbuf) < 0) 10767617Seric e->e_dfino = -1; 10867617Seric else 10967688Seric { 11067688Seric e->e_dfdev = stbuf.st_dev; 11167617Seric e->e_dfino = stbuf.st_ino; 11267688Seric } 11367546Seric HasEightBits = FALSE; 11468560Seric e->e_msgsize = 0; 11568564Seric e->e_flags |= EF_HAS_DF; 1161392Seric } 1171392Seric 1184316Seric /* 1194322Seric ** Tell ARPANET to go ahead. 1204322Seric */ 1214322Seric 12252105Seric if (smtpmode) 12358151Seric message("354 Enter mail, end with \".\" on a line by itself"); 1244322Seric 1254322Seric /* 12667599Seric ** Read the message. 12767599Seric ** 12867599Seric ** This is done using two interleaved state machines. 12967599Seric ** The input state machine is looking for things like 13067599Seric ** hidden dots; the message state machine is handling 13167599Seric ** the larger picture (e.g., header versus body). 1324316Seric */ 1334316Seric 13467599Seric buf = bp = bufbuf; 13567599Seric buflen = sizeof bufbuf; 13667599Seric pbp = peekbuf; 13767599Seric istate = IS_BOL; 13867599Seric mstate = SaveFrom ? MS_HEADER : MS_UFROM; 13967599Seric CollectProgress = FALSE; 14067599Seric 14167599Seric /* if transmitting binary, don't map NL to EOL */ 14267599Seric if (e->e_bodytype != NULL && strcasecmp(e->e_bodytype, "8BITMIME") == 0) 14367599Seric e->e_flags |= EF_NL_NOT_EOL; 14467599Seric 14567599Seric if (dbto != 0) 1462900Seric { 14767599Seric /* handle possible input timeout */ 14867599Seric if (setjmp(CtxCollectTimeout) != 0) 14967599Seric { 15067599Seric #ifdef LOG 15167599Seric syslog(LOG_NOTICE, 15267599Seric "timeout waiting for input from %s during message collect", 15367599Seric CurHostName ? CurHostName : "<local machine>"); 15467599Seric #endif 15567599Seric errno = 0; 15667599Seric usrerr("451 timeout waiting for input during message collect"); 15740965Sbostic goto readerr; 15867599Seric } 15967599Seric CollectTimeout = setevent(dbto, collecttimeout, dbto); 1602900Seric } 1612900Seric 16240965Sbostic for (;;) 1631392Seric { 16467599Seric if (tTd(30, 35)) 16567599Seric printf("top, istate=%d, mstate=%d\n", istate, mstate); 16640965Sbostic for (;;) 1671392Seric { 16867599Seric if (pbp > peekbuf) 16967599Seric c = *--pbp; 17067599Seric else 17164916Seric { 17267599Seric while (!feof(InChannel) && !ferror(InChannel)) 17367599Seric { 17467599Seric errno = 0; 17567599Seric c = fgetc(InChannel); 17667599Seric if (errno != EINTR) 17767599Seric break; 17867599Seric clearerr(InChannel); 17967599Seric } 18067599Seric CollectProgress = TRUE; 18167599Seric if (TrafficLogFile != NULL) 18267599Seric { 18367599Seric if (istate == IS_BOL) 18467599Seric fprintf(TrafficLogFile, "%05d <<< ", 18567599Seric getpid()); 18667599Seric if (c == EOF) 18767599Seric fprintf(TrafficLogFile, "[EOF]\n"); 18867599Seric else 18967599Seric fputc(c, TrafficLogFile); 19067599Seric } 19167599Seric if (c == EOF) 19267599Seric goto readerr; 19367599Seric if (SevenBitInput) 19467599Seric c &= 0x7f; 19567599Seric else 19667599Seric HasEightBits |= bitset(0x80, c); 19768560Seric if (!headeronly) 19868560Seric e->e_msgsize++; 19967599Seric } 20067599Seric if (tTd(30, 94)) 20167599Seric printf("istate=%d, c=%c (0x%x)\n", 20267599Seric istate, c, c); 20367599Seric switch (istate) 20467599Seric { 20567599Seric case IS_BOL: 20667599Seric if (c == '.') 20767599Seric { 20867599Seric istate = IS_DOT; 20967599Seric continue; 21067599Seric } 21164916Seric break; 21240965Sbostic 21367599Seric case IS_DOT: 21467599Seric if (c == '\n' && !ignrdot && 21567599Seric !bitset(EF_NL_NOT_EOL, e->e_flags)) 21667599Seric goto readerr; 21767599Seric else if (c == '\r' && 21867599Seric !bitset(EF_CRLF_NOT_EOL, e->e_flags)) 21967599Seric { 22067599Seric istate = IS_DOTCR; 22167599Seric continue; 22267599Seric } 22367599Seric else if (c != '.' || 22467599Seric (OpMode != MD_SMTP && 22567599Seric OpMode != MD_DAEMON && 22667599Seric OpMode != MD_ARPAFTP)) 22767599Seric { 22867599Seric *pbp++ = c; 22967599Seric c = '.'; 23067599Seric } 2312900Seric break; 23240965Sbostic 23367599Seric case IS_DOTCR: 23467599Seric if (c == '\n') 23567599Seric goto readerr; 23667599Seric else 23767599Seric { 23867599Seric /* push back the ".\rx" */ 23967599Seric *pbp++ = c; 24067599Seric *pbp++ = '\r'; 24167599Seric c = '.'; 24267599Seric } 24367599Seric break; 24440965Sbostic 24567599Seric case IS_CR: 24667689Seric if (c == '\n') 24767689Seric istate = IS_BOL; 24867689Seric else 24967599Seric { 25067599Seric ungetc(c, InChannel); 25167599Seric c = '\r'; 25267689Seric istate = IS_NORM; 25367599Seric } 25467689Seric goto bufferchar; 25567599Seric } 25657135Seric 25767689Seric if (c == '\r' && !bitset(EF_CRLF_NOT_EOL, e->e_flags)) 25840965Sbostic { 25967599Seric istate = IS_CR; 26067599Seric continue; 26167599Seric } 26267599Seric else if (c == '\n' && !bitset(EF_NL_NOT_EOL, e->e_flags)) 26367599Seric istate = IS_BOL; 26467599Seric else 26567599Seric istate = IS_NORM; 26640965Sbostic 26767689Seric bufferchar: 26867599Seric if (mstate == MS_BODY) 26967599Seric { 27067599Seric /* just put the character out */ 27167831Seric if (MaxMessageSize <= 0 || 27267831Seric e->e_msgsize <= MaxMessageSize) 27367831Seric fputc(c, tf); 27467599Seric continue; 27540965Sbostic } 2761392Seric 27767599Seric /* header -- buffer up */ 27867599Seric if (bp >= &buf[buflen - 2]) 27967599Seric { 28067599Seric char *obuf; 2811392Seric 28267599Seric if (mstate != MS_HEADER) 28367599Seric break; 28440965Sbostic 28567599Seric /* out of space for header */ 28667599Seric obuf = buf; 28767599Seric if (buflen < MEMCHUNKSIZE) 28867599Seric buflen *= 2; 28967599Seric else 29067599Seric buflen += MEMCHUNKSIZE; 29167599Seric buf = xalloc(buflen); 29267599Seric bcopy(obuf, buf, bp - obuf); 29367599Seric bp = &buf[bp - obuf]; 29467599Seric if (obuf != bufbuf) 29567599Seric free(obuf); 29667599Seric } 29767599Seric *bp++ = c; 29867599Seric if (istate == IS_BOL) 29967599Seric break; 30040965Sbostic } 30167599Seric *bp = '\0'; 30240965Sbostic 30367599Seric nextstate: 30467599Seric if (tTd(30, 35)) 30567599Seric printf("nextstate, istate=%d, mstate=%d, line = \"%s\"\n", 30667599Seric istate, mstate, buf); 30767599Seric switch (mstate) 30867599Seric { 30968433Seric extern int chompheader(); 31068433Seric 31167599Seric case MS_UFROM: 31267599Seric mstate = MS_HEADER; 31367599Seric if (strncmp(buf, "From ", 5) == 0) 31467599Seric { 31568433Seric extern void eatfrom(); 31668433Seric 31767704Seric bp = buf; 31867599Seric eatfrom(buf, e); 31967599Seric continue; 32067599Seric } 32167599Seric /* fall through */ 3222900Seric 32367599Seric case MS_HEADER: 32467599Seric if (!isheader(buf)) 32567599Seric { 32667599Seric mstate = MS_BODY; 32767599Seric goto nextstate; 32867599Seric } 32957135Seric 33067599Seric /* check for possible continuation line */ 33167599Seric do 33267599Seric { 33367599Seric clearerr(InChannel); 33467599Seric errno = 0; 33567599Seric c = fgetc(InChannel); 33667599Seric } while (errno == EINTR); 33767599Seric if (c != EOF) 33867599Seric ungetc(c, InChannel); 33967599Seric if (c == ' ' || c == '\t') 34067599Seric { 34167599Seric /* yep -- defer this */ 34267599Seric continue; 34367599Seric } 34457135Seric 34567599Seric /* trim off trailing CRLF or NL */ 34667599Seric if (*--bp != '\n' || *--bp != '\r') 34767599Seric bp++; 34867599Seric *bp = '\0'; 34967599Seric if (bitset(H_EOH, chompheader(buf, FALSE, e))) 35067599Seric mstate = MS_BODY; 35167599Seric break; 3521392Seric 35367599Seric case MS_BODY: 35467599Seric if (tTd(30, 1)) 35567599Seric printf("EOH\n"); 35667599Seric if (headeronly) 35767599Seric goto readerr; 35867599Seric bp = buf; 3592900Seric 36067599Seric /* toss blank line */ 36167599Seric if ((!bitset(EF_CRLF_NOT_EOL, e->e_flags) && 36267599Seric bp[0] == '\r' && bp[1] == '\n') || 36367599Seric (!bitset(EF_NL_NOT_EOL, e->e_flags) && 36467599Seric bp[0] == '\n')) 36567599Seric { 36667599Seric break; 36767599Seric } 36867546Seric 36967599Seric /* if not a blank separator, write it out */ 37067831Seric if (MaxMessageSize <= 0 || 37167831Seric e->e_msgsize <= MaxMessageSize) 37267831Seric { 37367831Seric while (*bp != '\0') 37467831Seric fputc(*bp++, tf); 37567831Seric } 3762900Seric break; 37767599Seric } 37867599Seric bp = buf; 37964718Seric } 38040965Sbostic 38167546Seric readerr: 38267546Seric if ((feof(fp) && smtpmode) || ferror(fp)) 38364718Seric { 38464916Seric if (tTd(30, 1)) 38564916Seric printf("collect: read error\n"); 38664718Seric inputerr = TRUE; 38764718Seric } 38864718Seric 38966765Seric /* reset global timer */ 39067599Seric clrevent(CollectTimeout); 39166765Seric 39267546Seric if (headeronly) 39367546Seric return; 39467546Seric 39567546Seric if (tf != NULL) 39664762Seric { 39767546Seric if (fflush(tf) != 0) 39867546Seric tferror(tf, e); 39967546Seric if (fsync(fileno(tf)) < 0 || fclose(tf) < 0) 40067546Seric { 40167546Seric tferror(tf, e); 40267546Seric finis(); 40367546Seric } 40464762Seric } 4052900Seric 406*68692Seric /* An EOF when running SMTP is an error */ 407*68692Seric if (inputerr && (OpMode == MD_SMTP || OpMode == MD_DAEMON)) 40816136Seric { 40958308Seric char *host; 41064718Seric char *problem; 41158082Seric 41258308Seric host = RealHostName; 41358308Seric if (host == NULL) 41458308Seric host = "localhost"; 41558308Seric 41667546Seric if (feof(fp)) 41764718Seric problem = "unexpected close"; 41867546Seric else if (ferror(fp)) 41964718Seric problem = "I/O error"; 42064718Seric else 42164718Seric problem = "read timeout"; 42236233Skarels # ifdef LOG 42367546Seric if (LogLevel > 0 && feof(fp)) 42436230Skarels syslog(LOG_NOTICE, 42566864Seric "collect: %s on connection from %s, sender=%s: %s\n", 42666864Seric problem, host, e->e_from.q_paddr, errstring(errno)); 42736233Skarels # endif 42867546Seric if (feof(fp)) 42965951Seric usrerr("451 collect: %s on connection from %s, from=%s", 43064718Seric problem, host, e->e_from.q_paddr); 43165951Seric else 43265951Seric syserr("451 collect: %s on connection from %s, from=%s", 43365951Seric problem, host, e->e_from.q_paddr); 43411145Seric 43516136Seric /* don't return an error indication */ 43655012Seric e->e_to = NULL; 43755012Seric e->e_flags &= ~EF_FATALERRS; 43864124Seric e->e_flags |= EF_CLRQUEUE; 43916136Seric 44016136Seric /* and don't try to deliver the partial message either */ 44164718Seric if (InChild) 44264718Seric ExitStat = EX_QUIT; 44316136Seric finis(); 44416136Seric } 44516136Seric 4462900Seric /* 4472900Seric ** Find out some information from the headers. 4483386Seric ** Examples are who is the from person & the date. 4492900Seric */ 4502900Seric 45158929Seric eatheader(e, !requeueflag); 4527673Seric 45364068Seric /* collect statistics */ 45464068Seric if (OpMode != MD_VERIFY) 45568433Seric { 45668433Seric extern void markstats(); 45768433Seric 45864068Seric markstats(e, (ADDRESS *) NULL); 45968433Seric } 46064068Seric 4617782Seric /* 4627782Seric ** Add an Apparently-To: line if we have no recipient lines. 4637782Seric */ 4644622Seric 46567546Seric if (hvalue("to", e->e_header) == NULL && 46667546Seric hvalue("cc", e->e_header) == NULL && 46767546Seric hvalue("bcc", e->e_header) == NULL && 46867546Seric hvalue("apparently-to", e->e_header) == NULL) 4697367Seric { 4707367Seric register ADDRESS *q; 47168449Seric char *hdr = NULL; 47268449Seric extern void addheader(); 4737367Seric 4747367Seric /* create an Apparently-To: field */ 4757367Seric /* that or reject the message.... */ 47668449Seric switch (NoRecipientAction) 4777367Seric { 47868449Seric case NRA_ADD_APPARENTLY_TO: 47968449Seric hdr = "Apparently-To"; 48068449Seric break; 48168433Seric 48268449Seric case NRA_ADD_TO: 48368449Seric hdr = "To"; 48468449Seric break; 48568449Seric 48668449Seric case NRA_ADD_BCC: 48768449Seric addheader("Bcc", "", &e->e_header); 48868449Seric break; 48968449Seric 49068449Seric case NRA_ADD_TO_UNDISCLOSED: 49168449Seric addheader("To", "undisclosed-recipients:;", &e->e_header); 49268449Seric break; 4937367Seric } 49468449Seric 49568449Seric if (hdr != NULL) 49668449Seric { 49768449Seric for (q = e->e_sendqueue; q != NULL; q = q->q_next) 49868449Seric { 49968449Seric if (q->q_alias != NULL) 50068449Seric continue; 50168449Seric if (tTd(30, 3)) 50268449Seric printf("Adding %s: %s\n", 50368449Seric hdr, q->q_paddr); 50468449Seric addheader(hdr, q->q_paddr, &e->e_header); 50568449Seric } 50668449Seric } 5077367Seric } 5087367Seric 50959320Seric /* check for message too large */ 51059320Seric if (MaxMessageSize > 0 && e->e_msgsize > MaxMessageSize) 51159320Seric { 51259320Seric usrerr("552 Message exceeds maximum fixed size (%ld)", 51359320Seric MaxMessageSize); 51459320Seric } 51559320Seric 51667547Seric /* check for illegal 8-bit data */ 51767547Seric if (HasEightBits) 51867547Seric { 51967547Seric e->e_flags |= EF_HAS8BIT; 52067887Seric if (!bitset(MM_PASS8BIT|MM_MIME8BIT, MimeMode)) 52167547Seric usrerr("554 Eight bit data not allowed"); 52267547Seric } 52367547Seric 52468564Seric if ((e->e_dfp = fopen(dfname, "r")) == NULL) 52558690Seric { 52658690Seric /* we haven't acked receipt yet, so just chuck this */ 52768564Seric syserr("Cannot reopen %s", dfname); 52858690Seric finis(); 52958690Seric } 5301392Seric } 53140965Sbostic 53267599Seric 53368433Seric static void 53467599Seric collecttimeout(timeout) 53567599Seric time_t timeout; 53640965Sbostic { 53767599Seric /* if no progress was made, die now */ 53867599Seric if (!CollectProgress) 53967599Seric longjmp(CtxCollectTimeout, 1); 54040965Sbostic 54167599Seric /* otherwise reset the timeout */ 54267599Seric CollectTimeout = setevent(timeout, collecttimeout, timeout); 54367599Seric CollectProgress = FALSE; 54440965Sbostic } 54540965Sbostic /* 54611544Seric ** TFERROR -- signal error on writing the temporary file. 54711544Seric ** 54811544Seric ** Parameters: 54911544Seric ** tf -- the file pointer for the temporary file. 550*68692Seric ** e -- the current envelope. 55111544Seric ** 55211544Seric ** Returns: 55311544Seric ** none. 55411544Seric ** 55511544Seric ** Side Effects: 55611544Seric ** Gives an error message. 55711544Seric ** Arranges for following output to go elsewhere. 55811544Seric */ 55911544Seric 56068433Seric void 56155012Seric tferror(tf, e) 56211544Seric FILE *tf; 56355012Seric register ENVELOPE *e; 56411544Seric { 56511544Seric if (errno == ENOSPC) 56611544Seric { 56766782Seric struct stat st; 56866782Seric long avail; 56966782Seric long bsize; 57066782Seric 57168559Seric e->e_flags |= EF_NO_BODY_RETN; 57266782Seric if (fstat(fileno(tf), &st) < 0) 57366782Seric st.st_size = 0; 57468564Seric (void) freopen(queuename(e, 'd'), "w", tf); 57566782Seric if (st.st_size <= 0) 57666782Seric fprintf(tf, "\n*** Mail could not be accepted"); 57766782Seric else if (sizeof st.st_size > sizeof (long)) 57866782Seric fprintf(tf, "\n*** Mail of at least %qd bytes could not be accepted\n", 57966782Seric st.st_size); 58066782Seric else 58166782Seric fprintf(tf, "\n*** Mail of at least %ld bytes could not be accepted\n", 58266782Seric st.st_size); 58366782Seric fprintf(tf, "*** at %s due to lack of disk space for temp file.\n", 58466782Seric MyHostName); 58566782Seric avail = freespace(QueueDir, &bsize); 58666782Seric if (avail > 0) 58766782Seric { 58866782Seric if (bsize > 1024) 58966782Seric avail *= bsize / 1024; 59066782Seric else if (bsize < 1024) 59166782Seric avail /= 1024 / bsize; 59266782Seric fprintf(tf, "*** Currently, %ld kilobytes are available for mail temp files.\n", 59366782Seric avail); 59466782Seric } 595*68692Seric usrerr("452 Out of disk space for temp file"); 59611544Seric } 59711544Seric else 598*68692Seric syserr("collect: Cannot write tf%s", e->e_id); 59911544Seric (void) freopen("/dev/null", "w", tf); 60011544Seric } 60111544Seric /* 6022900Seric ** EATFROM -- chew up a UNIX style from line and process 6032900Seric ** 6042900Seric ** This does indeed make some assumptions about the format 6052900Seric ** of UNIX messages. 6062900Seric ** 6072900Seric ** Parameters: 6082900Seric ** fm -- the from line. 6092900Seric ** 6102900Seric ** Returns: 6112900Seric ** none. 6122900Seric ** 6132900Seric ** Side Effects: 6142900Seric ** extracts what information it can from the header, 6153386Seric ** such as the date. 6162900Seric */ 6172900Seric 6184321Seric # ifndef NOTUNIX 6194321Seric 6204203Seric char *DowList[] = 6214203Seric { 6224203Seric "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", NULL 6234203Seric }; 6244203Seric 6252900Seric char *MonthList[] = 6262900Seric { 6272900Seric "Jan", "Feb", "Mar", "Apr", "May", "Jun", 6282900Seric "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", 6292900Seric NULL 6302900Seric }; 6312900Seric 63268433Seric void 63355012Seric eatfrom(fm, e) 6342900Seric char *fm; 63555012Seric register ENVELOPE *e; 6362900Seric { 6372900Seric register char *p; 6382900Seric register char **dt; 6392900Seric 6407673Seric if (tTd(30, 2)) 6414203Seric printf("eatfrom(%s)\n", fm); 6424203Seric 6432900Seric /* find the date part */ 6442900Seric p = fm; 6452900Seric while (*p != '\0') 6462900Seric { 6472900Seric /* skip a word */ 6482900Seric while (*p != '\0' && *p != ' ') 64916896Seric p++; 6502900Seric while (*p == ' ') 65116896Seric p++; 65258050Seric if (!(isascii(*p) && isupper(*p)) || 65358050Seric p[3] != ' ' || p[13] != ':' || p[16] != ':') 6542900Seric continue; 6552900Seric 6562900Seric /* we have a possible date */ 6574203Seric for (dt = DowList; *dt != NULL; dt++) 6582900Seric if (strncmp(*dt, p, 3) == 0) 6592900Seric break; 6604203Seric if (*dt == NULL) 6614203Seric continue; 6622900Seric 6634203Seric for (dt = MonthList; *dt != NULL; dt++) 6644203Seric if (strncmp(*dt, &p[4], 3) == 0) 6654203Seric break; 6662900Seric if (*dt != NULL) 6672900Seric break; 6682900Seric } 6692900Seric 67060502Seric if (*p != '\0') 6712900Seric { 6723386Seric char *q; 6735366Seric extern char *arpadate(); 6743386Seric 6752900Seric /* we have found a date */ 6763386Seric q = xalloc(25); 67723103Seric (void) strncpy(q, p, 25); 6783386Seric q[24] = '\0'; 6795366Seric q = arpadate(q); 68055012Seric define('a', newstr(q), e); 6812900Seric } 6822900Seric } 6834321Seric 68456795Seric # endif /* NOTUNIX */ 685