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*67599Seric static char sccsid[] = "@(#)collect.c 8.19 (Berkeley) 08/07/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 45*67599Seric static jmp_buf CtxCollectTimeout; 46*67599Seric static int collecttimeout(); 47*67599Seric static bool CollectProgress; 48*67599Seric static EVENT *CollectTimeout; 49*67599Seric 50*67599Seric /* values for input state machine */ 51*67599Seric #define IS_NORM 0 /* middle of line */ 52*67599Seric #define IS_BOL 1 /* beginning of line */ 53*67599Seric #define IS_DOT 2 /* read a dot at beginning of line */ 54*67599Seric #define IS_DOTCR 3 /* read ".\r" at beginning of line */ 55*67599Seric #define IS_CR 4 /* read a carriage return */ 56*67599Seric 57*67599Seric /* values for message state machine */ 58*67599Seric #define MS_UFROM 0 /* reading Unix from line */ 59*67599Seric #define MS_HEADER 1 /* reading message header */ 60*67599Seric #define MS_BODY 2 /* reading message body */ 61*67599Seric 62*67599Seric 6367546Seric collect(fp, smtpmode, requeueflag, hdrp, e) 6467546Seric FILE *fp; 6552105Seric bool smtpmode; 6658929Seric bool requeueflag; 6767546Seric HDR **hdrp; 6855012Seric register ENVELOPE *e; 691392Seric { 701392Seric register FILE *tf; 7152105Seric bool ignrdot = smtpmode ? FALSE : IgnrDot; 7267268Seric time_t dbto = smtpmode ? TimeOuts.to_datablock : 0; 73*67599Seric register char *bp; 74*67599Seric register int c; 7564718Seric bool inputerr = FALSE; 7667546Seric bool headeronly = FALSE; 77*67599Seric char *buf; 78*67599Seric int buflen; 79*67599Seric int istate; 80*67599Seric int mstate; 81*67599Seric char *pbp; 82*67599Seric char peekbuf[8]; 83*67599Seric char bufbuf[MAXLINE]; 842900Seric extern char *hvalue(); 85*67599Seric extern bool isheader(); 861392Seric 8766796Seric CollectErrorMessage = NULL; 8866796Seric CollectErrno = 0; 8967546Seric if (hdrp == NULL) 9067546Seric hdrp = &e->e_header; 9167546Seric else 9267546Seric headeronly = TRUE; 9366796Seric 941392Seric /* 951392Seric ** Create the temp file name and create the file. 961392Seric */ 971392Seric 9867546Seric if (!headeronly) 991392Seric { 10067546Seric e->e_df = queuename(e, 'd'); 10167546Seric e->e_df = newstr(e->e_df); 10267546Seric if ((tf = dfopen(e->e_df, O_WRONLY|O_CREAT|O_TRUNC, FileMode)) == NULL) 10367546Seric { 10467546Seric syserr("Cannot create %s", e->e_df); 10567546Seric e->e_flags |= EF_NORETURN; 10667546Seric finis(); 10767546Seric } 10867546Seric HasEightBits = FALSE; 1091392Seric } 1101392Seric 1114316Seric /* 1124322Seric ** Tell ARPANET to go ahead. 1134322Seric */ 1144322Seric 11552105Seric if (smtpmode) 11658151Seric message("354 Enter mail, end with \".\" on a line by itself"); 1174322Seric 1184322Seric /* 119*67599Seric ** Read the message. 120*67599Seric ** 121*67599Seric ** This is done using two interleaved state machines. 122*67599Seric ** The input state machine is looking for things like 123*67599Seric ** hidden dots; the message state machine is handling 124*67599Seric ** the larger picture (e.g., header versus body). 1254316Seric */ 1264316Seric 127*67599Seric buf = bp = bufbuf; 128*67599Seric buflen = sizeof bufbuf; 129*67599Seric pbp = peekbuf; 130*67599Seric istate = IS_BOL; 131*67599Seric mstate = SaveFrom ? MS_HEADER : MS_UFROM; 132*67599Seric CollectProgress = FALSE; 133*67599Seric 134*67599Seric /* if transmitting binary, don't map NL to EOL */ 135*67599Seric if (e->e_bodytype != NULL && strcasecmp(e->e_bodytype, "8BITMIME") == 0) 136*67599Seric e->e_flags |= EF_NL_NOT_EOL; 137*67599Seric 138*67599Seric if (dbto != 0) 1392900Seric { 140*67599Seric /* handle possible input timeout */ 141*67599Seric if (setjmp(CtxCollectTimeout) != 0) 142*67599Seric { 143*67599Seric #ifdef LOG 144*67599Seric syslog(LOG_NOTICE, 145*67599Seric "timeout waiting for input from %s during message collect", 146*67599Seric CurHostName ? CurHostName : "<local machine>"); 147*67599Seric #endif 148*67599Seric errno = 0; 149*67599Seric usrerr("451 timeout waiting for input during message collect"); 15040965Sbostic goto readerr; 151*67599Seric } 152*67599Seric CollectTimeout = setevent(dbto, collecttimeout, dbto); 1532900Seric } 1542900Seric 15540965Sbostic for (;;) 1561392Seric { 157*67599Seric if (tTd(30, 35)) 158*67599Seric printf("top, istate=%d, mstate=%d\n", istate, mstate); 15940965Sbostic for (;;) 1601392Seric { 161*67599Seric if (pbp > peekbuf) 162*67599Seric c = *--pbp; 163*67599Seric else 16464916Seric { 165*67599Seric while (!feof(InChannel) && !ferror(InChannel)) 166*67599Seric { 167*67599Seric errno = 0; 168*67599Seric c = fgetc(InChannel); 169*67599Seric if (errno != EINTR) 170*67599Seric break; 171*67599Seric clearerr(InChannel); 172*67599Seric } 173*67599Seric CollectProgress = TRUE; 174*67599Seric if (TrafficLogFile != NULL) 175*67599Seric { 176*67599Seric if (istate == IS_BOL) 177*67599Seric fprintf(TrafficLogFile, "%05d <<< ", 178*67599Seric getpid()); 179*67599Seric if (c == EOF) 180*67599Seric fprintf(TrafficLogFile, "[EOF]\n"); 181*67599Seric else 182*67599Seric fputc(c, TrafficLogFile); 183*67599Seric } 184*67599Seric if (c == EOF) 185*67599Seric goto readerr; 186*67599Seric if (SevenBitInput) 187*67599Seric c &= 0x7f; 188*67599Seric else 189*67599Seric HasEightBits |= bitset(0x80, c); 190*67599Seric e->e_msgsize++; 191*67599Seric } 192*67599Seric if (tTd(30, 94)) 193*67599Seric printf("istate=%d, c=%c (0x%x)\n", 194*67599Seric istate, c, c); 195*67599Seric switch (istate) 196*67599Seric { 197*67599Seric case IS_BOL: 198*67599Seric if (c == '.') 199*67599Seric { 200*67599Seric istate = IS_DOT; 201*67599Seric continue; 202*67599Seric } 20364916Seric break; 20440965Sbostic 205*67599Seric case IS_DOT: 206*67599Seric if (c == '\n' && !ignrdot && 207*67599Seric !bitset(EF_NL_NOT_EOL, e->e_flags)) 208*67599Seric goto readerr; 209*67599Seric else if (c == '\r' && 210*67599Seric !bitset(EF_CRLF_NOT_EOL, e->e_flags)) 211*67599Seric { 212*67599Seric istate = IS_DOTCR; 213*67599Seric continue; 214*67599Seric } 215*67599Seric else if (c != '.' || 216*67599Seric (OpMode != MD_SMTP && 217*67599Seric OpMode != MD_DAEMON && 218*67599Seric OpMode != MD_ARPAFTP)) 219*67599Seric { 220*67599Seric *pbp++ = c; 221*67599Seric c = '.'; 222*67599Seric } 2232900Seric break; 22440965Sbostic 225*67599Seric case IS_DOTCR: 226*67599Seric if (c == '\n') 227*67599Seric goto readerr; 228*67599Seric else 229*67599Seric { 230*67599Seric /* push back the ".\rx" */ 231*67599Seric *pbp++ = c; 232*67599Seric *pbp++ = '\r'; 233*67599Seric c = '.'; 234*67599Seric } 235*67599Seric break; 23640965Sbostic 237*67599Seric case IS_CR: 238*67599Seric if (c != '\n') 239*67599Seric { 240*67599Seric ungetc(c, InChannel); 241*67599Seric c = '\r'; 242*67599Seric } 243*67599Seric else if (!bitset(EF_CRLF_NOT_EOL, e->e_flags)) 244*67599Seric istate = IS_BOL; 245*67599Seric break; 246*67599Seric } 24757135Seric 248*67599Seric if (c == '\r') 24940965Sbostic { 250*67599Seric istate = IS_CR; 251*67599Seric continue; 252*67599Seric } 253*67599Seric else if (c == '\n' && !bitset(EF_NL_NOT_EOL, e->e_flags)) 254*67599Seric istate = IS_BOL; 255*67599Seric else 256*67599Seric istate = IS_NORM; 25740965Sbostic 258*67599Seric if (mstate == MS_BODY) 259*67599Seric { 260*67599Seric /* just put the character out */ 261*67599Seric fputc(c, tf); 262*67599Seric continue; 26340965Sbostic } 2641392Seric 265*67599Seric /* header -- buffer up */ 266*67599Seric if (bp >= &buf[buflen - 2]) 267*67599Seric { 268*67599Seric char *obuf; 2691392Seric 270*67599Seric if (mstate != MS_HEADER) 271*67599Seric break; 27240965Sbostic 273*67599Seric /* out of space for header */ 274*67599Seric obuf = buf; 275*67599Seric if (buflen < MEMCHUNKSIZE) 276*67599Seric buflen *= 2; 277*67599Seric else 278*67599Seric buflen += MEMCHUNKSIZE; 279*67599Seric buf = xalloc(buflen); 280*67599Seric bcopy(obuf, buf, bp - obuf); 281*67599Seric bp = &buf[bp - obuf]; 282*67599Seric if (obuf != bufbuf) 283*67599Seric free(obuf); 284*67599Seric } 285*67599Seric *bp++ = c; 286*67599Seric if (istate == IS_BOL) 287*67599Seric break; 28840965Sbostic } 289*67599Seric *bp = '\0'; 29040965Sbostic 291*67599Seric nextstate: 292*67599Seric if (tTd(30, 35)) 293*67599Seric printf("nextstate, istate=%d, mstate=%d, line = \"%s\"\n", 294*67599Seric istate, mstate, buf); 295*67599Seric switch (mstate) 296*67599Seric { 297*67599Seric case MS_UFROM: 298*67599Seric mstate = MS_HEADER; 299*67599Seric if (strncmp(buf, "From ", 5) == 0) 300*67599Seric { 301*67599Seric eatfrom(buf, e); 302*67599Seric continue; 303*67599Seric } 304*67599Seric /* fall through */ 3052900Seric 306*67599Seric case MS_HEADER: 307*67599Seric if (!isheader(buf)) 308*67599Seric { 309*67599Seric mstate = MS_BODY; 310*67599Seric goto nextstate; 311*67599Seric } 31257135Seric 313*67599Seric /* check for possible continuation line */ 314*67599Seric do 315*67599Seric { 316*67599Seric clearerr(InChannel); 317*67599Seric errno = 0; 318*67599Seric c = fgetc(InChannel); 319*67599Seric } while (errno == EINTR); 320*67599Seric if (c != EOF) 321*67599Seric ungetc(c, InChannel); 322*67599Seric if (c == ' ' || c == '\t') 323*67599Seric { 324*67599Seric /* yep -- defer this */ 325*67599Seric continue; 326*67599Seric } 32757135Seric 328*67599Seric /* trim off trailing CRLF or NL */ 329*67599Seric if (*--bp != '\n' || *--bp != '\r') 330*67599Seric bp++; 331*67599Seric *bp = '\0'; 332*67599Seric if (bitset(H_EOH, chompheader(buf, FALSE, e))) 333*67599Seric mstate = MS_BODY; 334*67599Seric break; 3351392Seric 336*67599Seric case MS_BODY: 337*67599Seric if (tTd(30, 1)) 338*67599Seric printf("EOH\n"); 339*67599Seric if (headeronly) 340*67599Seric goto readerr; 341*67599Seric bp = buf; 3422900Seric 343*67599Seric /* toss blank line */ 344*67599Seric if ((!bitset(EF_CRLF_NOT_EOL, e->e_flags) && 345*67599Seric bp[0] == '\r' && bp[1] == '\n') || 346*67599Seric (!bitset(EF_NL_NOT_EOL, e->e_flags) && 347*67599Seric bp[0] == '\n')) 348*67599Seric { 349*67599Seric break; 350*67599Seric } 35167546Seric 352*67599Seric /* if not a blank separator, write it out */ 353*67599Seric while (*bp != '\0') 354*67599Seric fputc(*bp++, tf); 3552900Seric break; 356*67599Seric } 357*67599Seric bp = buf; 35864718Seric } 35940965Sbostic 36067546Seric readerr: 36167546Seric if ((feof(fp) && smtpmode) || ferror(fp)) 36264718Seric { 36364916Seric if (tTd(30, 1)) 36464916Seric printf("collect: read error\n"); 36564718Seric inputerr = TRUE; 36664718Seric } 36764718Seric 36866765Seric /* reset global timer */ 369*67599Seric clrevent(CollectTimeout); 37066765Seric 37167546Seric if (headeronly) 37267546Seric return; 37367546Seric 37467546Seric if (tf != NULL) 37564762Seric { 37667546Seric if (fflush(tf) != 0) 37767546Seric tferror(tf, e); 37867546Seric if (fsync(fileno(tf)) < 0 || fclose(tf) < 0) 37967546Seric { 38067546Seric tferror(tf, e); 38167546Seric finis(); 38267546Seric } 38364762Seric } 3842900Seric 38566796Seric if (CollectErrorMessage != NULL && Errors <= 0) 38616136Seric { 38766796Seric if (CollectErrno != 0) 38866796Seric { 38966796Seric errno = CollectErrno; 39066796Seric syserr(CollectErrorMessage, e->e_df); 39166796Seric finis(); 39266796Seric } 39366796Seric usrerr(CollectErrorMessage); 39466796Seric } 39566796Seric else if (inputerr && (OpMode == MD_SMTP || OpMode == MD_DAEMON)) 39666796Seric { 39766796Seric /* An EOF when running SMTP is an error */ 39858308Seric char *host; 39964718Seric char *problem; 40058082Seric 40158308Seric host = RealHostName; 40258308Seric if (host == NULL) 40358308Seric host = "localhost"; 40458308Seric 40567546Seric if (feof(fp)) 40664718Seric problem = "unexpected close"; 40767546Seric else if (ferror(fp)) 40864718Seric problem = "I/O error"; 40964718Seric else 41064718Seric problem = "read timeout"; 41136233Skarels # ifdef LOG 41267546Seric if (LogLevel > 0 && feof(fp)) 41336230Skarels syslog(LOG_NOTICE, 41466864Seric "collect: %s on connection from %s, sender=%s: %s\n", 41566864Seric problem, host, e->e_from.q_paddr, errstring(errno)); 41636233Skarels # endif 41767546Seric if (feof(fp)) 41865951Seric usrerr("451 collect: %s on connection from %s, from=%s", 41964718Seric problem, host, e->e_from.q_paddr); 42065951Seric else 42165951Seric syserr("451 collect: %s on connection from %s, from=%s", 42265951Seric problem, host, e->e_from.q_paddr); 42311145Seric 42416136Seric /* don't return an error indication */ 42555012Seric e->e_to = NULL; 42655012Seric e->e_flags &= ~EF_FATALERRS; 42764124Seric e->e_flags |= EF_CLRQUEUE; 42816136Seric 42916136Seric /* and don't try to deliver the partial message either */ 43064718Seric if (InChild) 43164718Seric ExitStat = EX_QUIT; 43216136Seric finis(); 43316136Seric } 43416136Seric 4352900Seric /* 4362900Seric ** Find out some information from the headers. 4373386Seric ** Examples are who is the from person & the date. 4382900Seric */ 4392900Seric 44058929Seric eatheader(e, !requeueflag); 4417673Seric 44264068Seric /* collect statistics */ 44364068Seric if (OpMode != MD_VERIFY) 44464068Seric markstats(e, (ADDRESS *) NULL); 44564068Seric 4467782Seric /* 4477782Seric ** Add an Apparently-To: line if we have no recipient lines. 4487782Seric */ 4494622Seric 45067546Seric if (hvalue("to", e->e_header) == NULL && 45167546Seric hvalue("cc", e->e_header) == NULL && 45267546Seric hvalue("bcc", e->e_header) == NULL && 45367546Seric hvalue("apparently-to", e->e_header) == NULL) 4547367Seric { 4557367Seric register ADDRESS *q; 4567367Seric 4577367Seric /* create an Apparently-To: field */ 4587367Seric /* that or reject the message.... */ 45955012Seric for (q = e->e_sendqueue; q != NULL; q = q->q_next) 4607367Seric { 4617389Seric if (q->q_alias != NULL) 4627389Seric continue; 4637673Seric if (tTd(30, 3)) 4647367Seric printf("Adding Apparently-To: %s\n", q->q_paddr); 46567546Seric addheader("Apparently-To", q->q_paddr, &e->e_header); 4667367Seric } 4677367Seric } 4687367Seric 46959320Seric /* check for message too large */ 47059320Seric if (MaxMessageSize > 0 && e->e_msgsize > MaxMessageSize) 47159320Seric { 47259320Seric usrerr("552 Message exceeds maximum fixed size (%ld)", 47359320Seric MaxMessageSize); 47459320Seric } 47559320Seric 47667547Seric /* check for illegal 8-bit data */ 47767547Seric if (HasEightBits) 47867547Seric { 47967547Seric e->e_flags |= EF_HAS8BIT; 48067547Seric if (bitset(MM_MIME8BIT, MimeMode)) 48167547Seric { 48267547Seric /* convert it to MIME */ 48367547Seric if (hvalue("MIME-Version", e->e_header) == NULL) 48467547Seric { 48567547Seric char mimebuf[20]; 48667547Seric 48767547Seric strcpy(mimebuf, "MIME-Version: 1.0"); 48867547Seric chompheader(mimebuf, FALSE, e); 48967547Seric } 49067547Seric if (e->e_bodytype == NULL) 49167547Seric e->e_bodytype = "8BITMIME"; 49267547Seric } 49367547Seric else if (!bitset(MM_PASS8BIT, MimeMode)) 49467547Seric usrerr("554 Eight bit data not allowed"); 49567547Seric } 49667547Seric 49755012Seric if ((e->e_dfp = fopen(e->e_df, "r")) == NULL) 49858690Seric { 49958690Seric /* we haven't acked receipt yet, so just chuck this */ 50055012Seric syserr("Cannot reopen %s", e->e_df); 50158690Seric finis(); 50258690Seric } 5031392Seric } 50440965Sbostic 505*67599Seric 506*67599Seric static 507*67599Seric collecttimeout(timeout) 508*67599Seric time_t timeout; 50940965Sbostic { 510*67599Seric /* if no progress was made, die now */ 511*67599Seric if (!CollectProgress) 512*67599Seric longjmp(CtxCollectTimeout, 1); 51340965Sbostic 514*67599Seric /* otherwise reset the timeout */ 515*67599Seric CollectTimeout = setevent(timeout, collecttimeout, timeout); 516*67599Seric CollectProgress = FALSE; 51740965Sbostic } 51840965Sbostic /* 51911544Seric ** TFERROR -- signal error on writing the temporary file. 52011544Seric ** 52111544Seric ** Parameters: 52211544Seric ** tf -- the file pointer for the temporary file. 52311544Seric ** 52411544Seric ** Returns: 52511544Seric ** none. 52611544Seric ** 52711544Seric ** Side Effects: 52811544Seric ** Gives an error message. 52911544Seric ** Arranges for following output to go elsewhere. 53011544Seric */ 53111544Seric 53255012Seric tferror(tf, e) 53311544Seric FILE *tf; 53455012Seric register ENVELOPE *e; 53511544Seric { 53666796Seric CollectErrno = errno; 53711544Seric if (errno == ENOSPC) 53811544Seric { 53966782Seric struct stat st; 54066782Seric long avail; 54166782Seric long bsize; 54266782Seric 54367473Seric e->e_flags |= EF_NORETURN; 54466782Seric if (fstat(fileno(tf), &st) < 0) 54566782Seric st.st_size = 0; 54655012Seric (void) freopen(e->e_df, "w", tf); 54766782Seric if (st.st_size <= 0) 54866782Seric fprintf(tf, "\n*** Mail could not be accepted"); 54966782Seric else if (sizeof st.st_size > sizeof (long)) 55066782Seric fprintf(tf, "\n*** Mail of at least %qd bytes could not be accepted\n", 55166782Seric st.st_size); 55266782Seric else 55366782Seric fprintf(tf, "\n*** Mail of at least %ld bytes could not be accepted\n", 55466782Seric st.st_size); 55566782Seric fprintf(tf, "*** at %s due to lack of disk space for temp file.\n", 55666782Seric MyHostName); 55766782Seric avail = freespace(QueueDir, &bsize); 55866782Seric if (avail > 0) 55966782Seric { 56066782Seric if (bsize > 1024) 56166782Seric avail *= bsize / 1024; 56266782Seric else if (bsize < 1024) 56366782Seric avail /= 1024 / bsize; 56466782Seric fprintf(tf, "*** Currently, %ld kilobytes are available for mail temp files.\n", 56566782Seric avail); 56666782Seric } 56766796Seric CollectErrorMessage = "452 Out of disk space for temp file"; 56811544Seric } 56911544Seric else 57066796Seric { 57166796Seric CollectErrorMessage = "cannot write message body to disk (%s)"; 57266796Seric } 57311544Seric (void) freopen("/dev/null", "w", tf); 57411544Seric } 57511544Seric /* 5762900Seric ** EATFROM -- chew up a UNIX style from line and process 5772900Seric ** 5782900Seric ** This does indeed make some assumptions about the format 5792900Seric ** of UNIX messages. 5802900Seric ** 5812900Seric ** Parameters: 5822900Seric ** fm -- the from line. 5832900Seric ** 5842900Seric ** Returns: 5852900Seric ** none. 5862900Seric ** 5872900Seric ** Side Effects: 5882900Seric ** extracts what information it can from the header, 5893386Seric ** such as the date. 5902900Seric */ 5912900Seric 5924321Seric # ifndef NOTUNIX 5934321Seric 5944203Seric char *DowList[] = 5954203Seric { 5964203Seric "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", NULL 5974203Seric }; 5984203Seric 5992900Seric char *MonthList[] = 6002900Seric { 6012900Seric "Jan", "Feb", "Mar", "Apr", "May", "Jun", 6022900Seric "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", 6032900Seric NULL 6042900Seric }; 6052900Seric 60655012Seric eatfrom(fm, e) 6072900Seric char *fm; 60855012Seric register ENVELOPE *e; 6092900Seric { 6102900Seric register char *p; 6112900Seric register char **dt; 6122900Seric 6137673Seric if (tTd(30, 2)) 6144203Seric printf("eatfrom(%s)\n", fm); 6154203Seric 6162900Seric /* find the date part */ 6172900Seric p = fm; 6182900Seric while (*p != '\0') 6192900Seric { 6202900Seric /* skip a word */ 6212900Seric while (*p != '\0' && *p != ' ') 62216896Seric p++; 6232900Seric while (*p == ' ') 62416896Seric p++; 62558050Seric if (!(isascii(*p) && isupper(*p)) || 62658050Seric p[3] != ' ' || p[13] != ':' || p[16] != ':') 6272900Seric continue; 6282900Seric 6292900Seric /* we have a possible date */ 6304203Seric for (dt = DowList; *dt != NULL; dt++) 6312900Seric if (strncmp(*dt, p, 3) == 0) 6322900Seric break; 6334203Seric if (*dt == NULL) 6344203Seric continue; 6352900Seric 6364203Seric for (dt = MonthList; *dt != NULL; dt++) 6374203Seric if (strncmp(*dt, &p[4], 3) == 0) 6384203Seric break; 6392900Seric if (*dt != NULL) 6402900Seric break; 6412900Seric } 6422900Seric 64360502Seric if (*p != '\0') 6442900Seric { 6453386Seric char *q; 6465366Seric extern char *arpadate(); 6473386Seric 6482900Seric /* we have found a date */ 6493386Seric q = xalloc(25); 65023103Seric (void) strncpy(q, p, 25); 6513386Seric q[24] = '\0'; 6525366Seric q = arpadate(q); 65355012Seric define('a', newstr(q), e); 6542900Seric } 6552900Seric } 6564321Seric 65756795Seric # endif /* NOTUNIX */ 658