122444Sdist /* 222444Sdist * Copyright (c) 1980 Regents of the University of California. 333499Sbostic * All rights reserved. 433499Sbostic * 533499Sbostic * Redistribution and use in source and binary forms are permitted 634905Sbostic * provided that the above copyright notice and this paragraph are 734905Sbostic * duplicated in all such forms and that any documentation, 834905Sbostic * advertising materials, and other materials related to such 934905Sbostic * distribution and use acknowledge that the software was developed 1034905Sbostic * by the University of California, Berkeley. The name of the 1134905Sbostic * University may not be used to endorse or promote products derived 1234905Sbostic * from this software without specific prior written permission. 1334905Sbostic * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 1434905Sbostic * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 1534905Sbostic * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 1622444Sdist */ 1722444Sdist 1834905Sbostic #ifndef lint 19*38078Sbostic static char sccsid[] = "@(#)aux.c 5.17 (Berkeley) 05/19/89"; 2034905Sbostic #endif /* not lint */ 211219Skas 221219Skas #include "rcv.h" 231219Skas #include <sys/stat.h> 24*38078Sbostic #include <sys/time.h> 251219Skas 261219Skas /* 271219Skas * Mail -- a mail program 281219Skas * 291219Skas * Auxiliary functions. 301219Skas */ 311219Skas 321219Skas /* 331219Skas * Return a pointer to a dynamic copy of the argument. 341219Skas */ 351219Skas char * 361219Skas savestr(str) 371219Skas char *str; 381219Skas { 3934987Sedward char *new; 4034987Sedward int size = strlen(str) + 1; 411219Skas 4234987Sedward if ((new = salloc(size)) != NOSTR) 4334987Sedward bcopy(str, new, size); 4434987Sedward return new; 451219Skas } 461219Skas 471219Skas /* 481219Skas * Announce a fatal error and die. 491219Skas */ 501219Skas 5131142Sedward /*VARARGS1*/ 5231142Sedward panic(fmt, a, b) 5331142Sedward char *fmt; 541219Skas { 5531142Sedward fprintf(stderr, "panic: "); 5631142Sedward fprintf(stderr, fmt, a, b); 5731142Sedward putc('\n', stderr); 581219Skas exit(1); 591219Skas } 601219Skas 611219Skas /* 621219Skas * Touch the named message by setting its MTOUCH flag. 631219Skas * Touched messages have the effect of not being sent 641219Skas * back to the system mailbox on exit. 651219Skas */ 6634987Sedward touch(mp) 6734987Sedward register struct message *mp; 681219Skas { 691479Skas 701479Skas mp->m_flag |= MTOUCH; 711479Skas if ((mp->m_flag & MREAD) == 0) 721479Skas mp->m_flag |= MREAD|MSTATUS; 731219Skas } 741219Skas 751219Skas /* 761219Skas * Test to see if the passed file name is a directory. 771219Skas * Return true if it is. 781219Skas */ 791219Skas isdir(name) 801219Skas char name[]; 811219Skas { 821219Skas struct stat sbuf; 831219Skas 841219Skas if (stat(name, &sbuf) < 0) 851219Skas return(0); 861219Skas return((sbuf.st_mode & S_IFMT) == S_IFDIR); 871219Skas } 881219Skas 891219Skas /* 901219Skas * Count the number of arguments in the given string raw list. 911219Skas */ 921219Skas argcount(argv) 931219Skas char **argv; 941219Skas { 951219Skas register char **ap; 961219Skas 9731142Sedward for (ap = argv; *ap++ != NOSTR;) 981219Skas ; 9931142Sedward return ap - argv - 1; 1001219Skas } 1011219Skas 1021219Skas /* 1031219Skas * Return the desired header line from the passed message 1041219Skas * pointer (or NOSTR if the desired header field is not available). 1051219Skas */ 1061219Skas char * 1071219Skas hfield(field, mp) 1081219Skas char field[]; 1091219Skas struct message *mp; 1101219Skas { 1111219Skas register FILE *ibuf; 1121219Skas char linebuf[LINESIZE]; 1131219Skas register int lc; 11431142Sedward register char *hfield; 11531142Sedward char *colon; 1161219Skas 1171219Skas ibuf = setinput(mp); 11831142Sedward if ((lc = mp->m_lines - 1) < 0) 11931142Sedward return NOSTR; 12036478Sedward if (readline(ibuf, linebuf, LINESIZE) < 0) 12131142Sedward return NOSTR; 12231142Sedward while (lc > 0) { 12331142Sedward if ((lc = gethfield(ibuf, linebuf, lc, &colon)) < 0) 12431142Sedward return NOSTR; 12531142Sedward if (hfield = ishfield(linebuf, colon, field)) 12631142Sedward return savestr(hfield); 12731142Sedward } 12831142Sedward return NOSTR; 1291219Skas } 1301219Skas 1311219Skas /* 1321219Skas * Return the next header field found in the given message. 13331142Sedward * Return >= 0 if something found, < 0 elsewise. 13431142Sedward * "colon" is set to point to the colon in the header. 1351219Skas * Must deal with \ continuations & other such fraud. 1361219Skas */ 13731142Sedward gethfield(f, linebuf, rem, colon) 1381219Skas register FILE *f; 1391219Skas char linebuf[]; 1401219Skas register int rem; 14131142Sedward char **colon; 1421219Skas { 1431219Skas char line2[LINESIZE]; 1441219Skas register char *cp, *cp2; 1451219Skas register int c; 1461219Skas 1471219Skas for (;;) { 14831142Sedward if (--rem < 0) 14931142Sedward return -1; 15036478Sedward if ((c = readline(f, linebuf, LINESIZE)) <= 0) 15131142Sedward return -1; 15231142Sedward for (cp = linebuf; isprint(*cp) && *cp != ' ' && *cp != ':'; 15331142Sedward cp++) 15431142Sedward ; 15531142Sedward if (*cp != ':' || cp == linebuf) 1561219Skas continue; 1571219Skas /* 1581219Skas * I guess we got a headline. 1591219Skas * Handle wraparounding 1601219Skas */ 16131142Sedward *colon = cp; 16231142Sedward cp = linebuf + c; 1631219Skas for (;;) { 16431142Sedward while (--cp >= linebuf && (*cp == ' ' || *cp == '\t')) 16531142Sedward ; 16631142Sedward cp++; 1671219Skas if (rem <= 0) 1681219Skas break; 16931142Sedward ungetc(c = getc(f), f); 17031142Sedward if (c != ' ' && c != '\t') 1711219Skas break; 17236478Sedward if ((c = readline(f, line2, LINESIZE)) < 0) 1731219Skas break; 1741219Skas rem--; 17531142Sedward for (cp2 = line2; *cp2 == ' ' || *cp2 == '\t'; cp2++) 1761219Skas ; 17731142Sedward c -= cp2 - line2; 17831142Sedward if (cp + c >= linebuf + LINESIZE - 2) 1791219Skas break; 1801219Skas *cp++ = ' '; 18131142Sedward bcopy(cp2, cp, c); 18231142Sedward cp += c; 1831219Skas } 18431142Sedward *cp = 0; 18531142Sedward return rem; 1861219Skas } 1871219Skas /* NOTREACHED */ 1881219Skas } 1891219Skas 1901219Skas /* 1911219Skas * Check whether the passed line is a header line of 19231142Sedward * the desired breed. Return the field body, or 0. 1931219Skas */ 1941219Skas 19531142Sedward char* 19631142Sedward ishfield(linebuf, colon, field) 1971219Skas char linebuf[], field[]; 19831142Sedward char *colon; 1991219Skas { 20031142Sedward register char *cp = colon; 2011219Skas 2021219Skas *cp = 0; 20334987Sedward if (strcasecmp(linebuf, field) != 0) { 20431142Sedward *cp = ':'; 20531142Sedward return 0; 2061219Skas } 20731142Sedward *cp = ':'; 20831142Sedward for (cp++; *cp == ' ' || *cp == '\t'; cp++) 20931142Sedward ; 21031142Sedward return cp; 2111219Skas } 2121219Skas 2131219Skas /* 2147571Skurt * Copy a string, lowercasing it as we go. 2157571Skurt */ 2167571Skurt istrcpy(dest, src) 21731142Sedward register char *dest, *src; 2187571Skurt { 2197571Skurt 2207571Skurt do { 22131142Sedward if (isupper(*src)) 22231142Sedward *dest++ = tolower(*src); 22331142Sedward else 22431142Sedward *dest++ = *src; 22531142Sedward } while (*src++ != 0); 2267571Skurt } 2277571Skurt 2287571Skurt /* 2291219Skas * The following code deals with input stacking to do source 2301219Skas * commands. All but the current file pointer are saved on 2311219Skas * the stack. 2321219Skas */ 2331219Skas 23434965Sedward static int ssp; /* Top of file stack */ 2351519Skas struct sstack { 2361519Skas FILE *s_file; /* File we were in. */ 2371519Skas int s_cond; /* Saved state of conditionals */ 2385785Skurt int s_loading; /* Loading .mailrc, etc. */ 23918661Sserge } sstack[NOFILE]; 2401219Skas 2411219Skas /* 2421219Skas * Pushdown current input file and switch to a new one. 2431219Skas * Set the global flag "sourcing" so that others will realize 2441219Skas * that they are no longer reading from a tty (in all probability). 2451219Skas */ 24634966Sedward source(arglist) 24734966Sedward char **arglist; 2481219Skas { 24934966Sedward FILE *fi; 25034966Sedward char *cp; 2511219Skas 25234966Sedward if ((cp = expand(*arglist)) == NOSTR) 2531219Skas return(1); 2543914Skurt if ((fi = fopen(cp, "r")) == NULL) { 2553914Skurt perror(cp); 2563914Skurt return(1); 2571219Skas } 25834965Sedward if (ssp >= NOFILE - 1) { 2591219Skas printf("Too much \"sourcing\" going on.\n"); 2601219Skas fclose(fi); 2611219Skas return(1); 2621219Skas } 26334965Sedward sstack[ssp].s_file = input; 2641519Skas sstack[ssp].s_cond = cond; 2655785Skurt sstack[ssp].s_loading = loading; 26634965Sedward ssp++; 2675785Skurt loading = 0; 2681519Skas cond = CANY; 2691219Skas input = fi; 2701219Skas sourcing++; 2711219Skas return(0); 2721219Skas } 2731219Skas 2741219Skas /* 2751219Skas * Pop the current input back to the previous level. 2761219Skas * Update the "sourcing" flag as appropriate. 2771219Skas */ 2781219Skas unstack() 2791219Skas { 28034965Sedward if (ssp <= 0) { 2811219Skas printf("\"Source\" stack over-pop.\n"); 2821219Skas sourcing = 0; 2831219Skas return(1); 2841219Skas } 2851219Skas fclose(input); 2861519Skas if (cond != CANY) 2871519Skas printf("Unmatched \"if\"\n"); 28834965Sedward ssp--; 2891519Skas cond = sstack[ssp].s_cond; 2905785Skurt loading = sstack[ssp].s_loading; 29134965Sedward input = sstack[ssp].s_file; 29234965Sedward if (ssp == 0) 2935785Skurt sourcing = loading; 2941219Skas return(0); 2951219Skas } 2961219Skas 2971219Skas /* 2981219Skas * Touch the indicated file. 2991219Skas * This is nifty for the shell. 3001219Skas */ 3011219Skas alter(name) 302*38078Sbostic char *name; 3031219Skas { 304*38078Sbostic struct stat sb; 305*38078Sbostic struct timeval tv[2]; 306*38078Sbostic time_t time(); 3071219Skas 308*38078Sbostic if (stat(name, &sb)) 3091219Skas return; 310*38078Sbostic tv[0].tv_sec = time((time_t *)0) + 1; 311*38078Sbostic tv[1].tv_sec = sb.st_mtime; 312*38078Sbostic tv[0].tv_usec = tv[1].tv_usec = 0; 313*38078Sbostic (void)utimes(name, tv); 3141219Skas } 3151219Skas 3161219Skas /* 3171219Skas * Examine the passed line buffer and 3181219Skas * return true if it is all blanks and tabs. 3191219Skas */ 3201219Skas blankline(linebuf) 3211219Skas char linebuf[]; 3221219Skas { 3231219Skas register char *cp; 3241219Skas 3251219Skas for (cp = linebuf; *cp; cp++) 32618661Sserge if (*cp != ' ' && *cp != '\t') 3271219Skas return(0); 3281219Skas return(1); 3291219Skas } 3301219Skas 3311219Skas /* 3323195Skas * Get sender's name from this message. If the message has 3333195Skas * a bunch of arpanet stuff in it, we may have to skin the name 3343195Skas * before returning it. 3353195Skas */ 3363195Skas char * 3373195Skas nameof(mp, reptype) 3383195Skas register struct message *mp; 3393195Skas { 3405237Skurt register char *cp, *cp2; 3413195Skas 3425237Skurt cp = skin(name1(mp, reptype)); 3435237Skurt if (reptype != 0 || charcount(cp, '!') < 2) 3445237Skurt return(cp); 3455237Skurt cp2 = rindex(cp, '!'); 3465237Skurt cp2--; 3475237Skurt while (cp2 > cp && *cp2 != '!') 3485237Skurt cp2--; 3495237Skurt if (*cp2 == '!') 3505237Skurt return(cp2 + 1); 3515237Skurt return(cp); 3523195Skas } 3533195Skas 3543195Skas /* 35525912Smckusick * Skin an arpa net address according to the RFC 822 interpretation 3563195Skas * of "host-phrase." 3573195Skas */ 3583195Skas char * 3593195Skas skin(name) 3603195Skas char *name; 3613195Skas { 3623195Skas register int c; 3633195Skas register char *cp, *cp2; 36425912Smckusick char *bufend; 3653195Skas int gotlt, lastsp; 3663195Skas char nbuf[BUFSIZ]; 36718661Sserge int nesting; 3683195Skas 3693195Skas if (name == NOSTR) 3703195Skas return(NOSTR); 37112819Sleres if (index(name, '(') == NOSTR && index(name, '<') == NOSTR 37231142Sedward && index(name, ' ') == NOSTR) 3733195Skas return(name); 3743195Skas gotlt = 0; 3753195Skas lastsp = 0; 37625912Smckusick bufend = nbuf; 37725912Smckusick for (cp = name, cp2 = bufend; c = *cp++; ) { 3783195Skas switch (c) { 3793195Skas case '(': 38025912Smckusick /* 38125912Smckusick * Start of a "comment". 38225912Smckusick * Ignore it. 38325912Smckusick */ 38418661Sserge nesting = 1; 38525912Smckusick while ((c = *cp) != 0) { 38625912Smckusick cp++; 38725912Smckusick switch (c) { 38825912Smckusick case '\\': 38925912Smckusick if (*cp == 0) 39025912Smckusick goto outcm; 39125912Smckusick cp++; 39225912Smckusick break; 39318661Sserge case '(': 39418661Sserge nesting++; 39518661Sserge break; 39618661Sserge 39718661Sserge case ')': 39818661Sserge --nesting; 39918661Sserge break; 40018661Sserge } 40118661Sserge 40218661Sserge if (nesting <= 0) 40318661Sserge break; 40418661Sserge } 40525912Smckusick outcm: 40612819Sleres lastsp = 0; 4073195Skas break; 4083195Skas 40925912Smckusick case '"': 41025912Smckusick /* 41125912Smckusick * Start of a "quoted-string". 41225912Smckusick * Copy it in its entirety. 41325912Smckusick */ 41425912Smckusick while ((c = *cp) != 0) { 41525912Smckusick cp++; 41625912Smckusick switch (c) { 41725912Smckusick case '\\': 41825912Smckusick if ((c = *cp) == 0) 41925912Smckusick goto outqs; 42025912Smckusick cp++; 42125912Smckusick break; 42225912Smckusick case '"': 42325912Smckusick goto outqs; 42425912Smckusick } 42525912Smckusick *cp2++ = c; 42625912Smckusick } 42725912Smckusick outqs: 42825912Smckusick lastsp = 0; 42925912Smckusick break; 43025912Smckusick 4313195Skas case ' ': 43212819Sleres if (cp[0] == 'a' && cp[1] == 't' && cp[2] == ' ') 43312819Sleres cp += 3, *cp2++ = '@'; 43412819Sleres else 43512819Sleres if (cp[0] == '@' && cp[1] == ' ') 43612819Sleres cp += 2, *cp2++ = '@'; 43712819Sleres else 43812819Sleres lastsp = 1; 4393195Skas break; 4403195Skas 4413195Skas case '<': 44225912Smckusick cp2 = bufend; 4433195Skas gotlt++; 4443195Skas lastsp = 0; 4453195Skas break; 4463195Skas 4473195Skas case '>': 44825912Smckusick if (gotlt) { 44925912Smckusick gotlt = 0; 45025912Smckusick while (*cp != ',' && *cp != 0) 45125912Smckusick cp++; 45225912Smckusick if (*cp == 0 ) 45325912Smckusick goto done; 45425912Smckusick *cp2++ = ','; 45525912Smckusick *cp2++ = ' '; 45625912Smckusick bufend = cp2; 45725912Smckusick break; 45825912Smckusick } 4593195Skas 4603195Skas /* Fall into . . . */ 4613195Skas 4623195Skas default: 4633195Skas if (lastsp) { 4643195Skas lastsp = 0; 4653195Skas *cp2++ = ' '; 4663195Skas } 4673195Skas *cp2++ = c; 4683195Skas break; 4693195Skas } 4703195Skas } 4713195Skas done: 4723195Skas *cp2 = 0; 4733195Skas 4743195Skas return(savestr(nbuf)); 4753195Skas } 4763195Skas 4773195Skas /* 4781219Skas * Fetch the sender's name from the passed message. 4793195Skas * Reptype can be 4803195Skas * 0 -- get sender's name for display purposes 4813195Skas * 1 -- get sender's name for reply 4823195Skas * 2 -- get sender's name for Reply 4831219Skas */ 4841219Skas char * 4853195Skas name1(mp, reptype) 4861219Skas register struct message *mp; 4871219Skas { 4881219Skas char namebuf[LINESIZE]; 4891219Skas char linebuf[LINESIZE]; 4901219Skas register char *cp, *cp2; 4911219Skas register FILE *ibuf; 4921219Skas int first = 1; 4931219Skas 4943195Skas if ((cp = hfield("from", mp)) != NOSTR) 49531142Sedward return cp; 4963195Skas if (reptype == 0 && (cp = hfield("sender", mp)) != NOSTR) 49731142Sedward return cp; 4981219Skas ibuf = setinput(mp); 49931142Sedward namebuf[0] = 0; 50036478Sedward if (readline(ibuf, linebuf, LINESIZE) < 0) 5011219Skas return(savestr(namebuf)); 5021219Skas newname: 50331142Sedward for (cp = linebuf; *cp && *cp != ' '; cp++) 5041219Skas ; 50531142Sedward for (; *cp == ' ' || *cp == '\t'; cp++) 5061219Skas ; 50731142Sedward for (cp2 = &namebuf[strlen(namebuf)]; 50831142Sedward *cp && *cp != ' ' && *cp != '\t' && cp2 < namebuf + LINESIZE - 1;) 50931142Sedward *cp2++ = *cp++; 5101219Skas *cp2 = '\0'; 51136478Sedward if (readline(ibuf, linebuf, LINESIZE) < 0) 5121219Skas return(savestr(namebuf)); 5131219Skas if ((cp = index(linebuf, 'F')) == NULL) 5141219Skas return(savestr(namebuf)); 5151219Skas if (strncmp(cp, "From", 4) != 0) 5161219Skas return(savestr(namebuf)); 5171219Skas while ((cp = index(cp, 'r')) != NULL) { 5181219Skas if (strncmp(cp, "remote", 6) == 0) { 5191219Skas if ((cp = index(cp, 'f')) == NULL) 5201219Skas break; 5211219Skas if (strncmp(cp, "from", 4) != 0) 5221219Skas break; 5231219Skas if ((cp = index(cp, ' ')) == NULL) 5241219Skas break; 5251219Skas cp++; 5261219Skas if (first) { 52731142Sedward strcpy(namebuf, cp); 5281219Skas first = 0; 5291219Skas } else 5301219Skas strcpy(rindex(namebuf, '!')+1, cp); 5311219Skas strcat(namebuf, "!"); 5321219Skas goto newname; 5331219Skas } 5341219Skas cp++; 5351219Skas } 5361219Skas return(savestr(namebuf)); 5371219Skas } 5381219Skas 5391219Skas /* 5405237Skurt * Count the occurances of c in str 5415237Skurt */ 5425237Skurt charcount(str, c) 5435237Skurt char *str; 5445237Skurt { 5455237Skurt register char *cp; 5465237Skurt register int i; 5475237Skurt 5485237Skurt for (i = 0, cp = str; *cp; cp++) 5495237Skurt if (*cp == c) 5505237Skurt i++; 5515237Skurt return(i); 5525237Skurt } 5535237Skurt 5545237Skurt /* 55531142Sedward * Are any of the characters in the two strings the same? 5561219Skas */ 55731142Sedward anyof(s1, s2) 55831142Sedward register char *s1, *s2; 5591219Skas { 5601219Skas 56131142Sedward while (*s1) 56231142Sedward if (index(s2, *s1++)) 56331142Sedward return 1; 56431142Sedward return 0; 5651219Skas } 5661219Skas 5671219Skas /* 56831142Sedward * Convert c to upper case 5691219Skas */ 57031142Sedward raise(c) 57131142Sedward register c; 57231142Sedward { 57331142Sedward 57431142Sedward if (islower(c)) 57531142Sedward return toupper(c); 57631142Sedward return c; 57731142Sedward } 57831142Sedward 57931142Sedward /* 58031142Sedward * Copy s1 to s2, return pointer to null in s2. 58131142Sedward */ 58231142Sedward char * 58331142Sedward copy(s1, s2) 5841219Skas register char *s1, *s2; 5851219Skas { 5861219Skas 58731142Sedward while (*s2++ = *s1++) 58831142Sedward ; 58931142Sedward return s2 - 1; 5901219Skas } 5911219Skas 5921219Skas /* 5937571Skurt * See if the given header field is supposed to be ignored. 5947571Skurt */ 59534692Sedward isign(field, ignore) 5967571Skurt char *field; 59734692Sedward struct ignoretab ignore[2]; 5987571Skurt { 5997571Skurt char realfld[BUFSIZ]; 6007571Skurt 60134969Sedward if (ignore == ignoreall) 60234969Sedward return 1; 60318661Sserge /* 60418661Sserge * Lower-case the string, so that "Status" and "status" 60518661Sserge * will hash to the same place. 60618661Sserge */ 6077571Skurt istrcpy(realfld, field); 60834692Sedward if (ignore[1].i_count > 0) 60934692Sedward return (!member(realfld, ignore + 1)); 61018661Sserge else 61118661Sserge return (member(realfld, ignore)); 6127571Skurt } 61318661Sserge 61418661Sserge member(realfield, table) 61518661Sserge register char *realfield; 61634692Sedward struct ignoretab *table; 61718661Sserge { 61818661Sserge register struct ignore *igp; 61918661Sserge 62034692Sedward for (igp = table->i_head[hash(realfield)]; igp != 0; igp = igp->i_link) 62131142Sedward if (*igp->i_field == *realfield && 62231142Sedward equal(igp->i_field, realfield)) 62318661Sserge return (1); 62418661Sserge return (0); 62518661Sserge } 626