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*36478Sedward static char sccsid[] = "@(#)aux.c 5.16 (Berkeley) 12/23/88"; 2034905Sbostic #endif /* not lint */ 211219Skas 221219Skas #include "rcv.h" 231219Skas #include <sys/stat.h> 241219Skas 251219Skas /* 261219Skas * Mail -- a mail program 271219Skas * 281219Skas * Auxiliary functions. 291219Skas */ 301219Skas 311219Skas /* 321219Skas * Return a pointer to a dynamic copy of the argument. 331219Skas */ 341219Skas char * 351219Skas savestr(str) 361219Skas char *str; 371219Skas { 3834987Sedward char *new; 3934987Sedward int size = strlen(str) + 1; 401219Skas 4134987Sedward if ((new = salloc(size)) != NOSTR) 4234987Sedward bcopy(str, new, size); 4334987Sedward return new; 441219Skas } 451219Skas 461219Skas /* 471219Skas * Announce a fatal error and die. 481219Skas */ 491219Skas 5031142Sedward /*VARARGS1*/ 5131142Sedward panic(fmt, a, b) 5231142Sedward char *fmt; 531219Skas { 5431142Sedward fprintf(stderr, "panic: "); 5531142Sedward fprintf(stderr, fmt, a, b); 5631142Sedward putc('\n', stderr); 571219Skas exit(1); 581219Skas } 591219Skas 601219Skas /* 611219Skas * Touch the named message by setting its MTOUCH flag. 621219Skas * Touched messages have the effect of not being sent 631219Skas * back to the system mailbox on exit. 641219Skas */ 6534987Sedward touch(mp) 6634987Sedward register struct message *mp; 671219Skas { 681479Skas 691479Skas mp->m_flag |= MTOUCH; 701479Skas if ((mp->m_flag & MREAD) == 0) 711479Skas mp->m_flag |= MREAD|MSTATUS; 721219Skas } 731219Skas 741219Skas /* 751219Skas * Test to see if the passed file name is a directory. 761219Skas * Return true if it is. 771219Skas */ 781219Skas isdir(name) 791219Skas char name[]; 801219Skas { 811219Skas struct stat sbuf; 821219Skas 831219Skas if (stat(name, &sbuf) < 0) 841219Skas return(0); 851219Skas return((sbuf.st_mode & S_IFMT) == S_IFDIR); 861219Skas } 871219Skas 881219Skas /* 891219Skas * Count the number of arguments in the given string raw list. 901219Skas */ 911219Skas argcount(argv) 921219Skas char **argv; 931219Skas { 941219Skas register char **ap; 951219Skas 9631142Sedward for (ap = argv; *ap++ != NOSTR;) 971219Skas ; 9831142Sedward return ap - argv - 1; 991219Skas } 1001219Skas 1011219Skas /* 1021219Skas * Return the desired header line from the passed message 1031219Skas * pointer (or NOSTR if the desired header field is not available). 1041219Skas */ 1051219Skas char * 1061219Skas hfield(field, mp) 1071219Skas char field[]; 1081219Skas struct message *mp; 1091219Skas { 1101219Skas register FILE *ibuf; 1111219Skas char linebuf[LINESIZE]; 1121219Skas register int lc; 11331142Sedward register char *hfield; 11431142Sedward char *colon; 1151219Skas 1161219Skas ibuf = setinput(mp); 11731142Sedward if ((lc = mp->m_lines - 1) < 0) 11831142Sedward return NOSTR; 119*36478Sedward if (readline(ibuf, linebuf, LINESIZE) < 0) 12031142Sedward return NOSTR; 12131142Sedward while (lc > 0) { 12231142Sedward if ((lc = gethfield(ibuf, linebuf, lc, &colon)) < 0) 12331142Sedward return NOSTR; 12431142Sedward if (hfield = ishfield(linebuf, colon, field)) 12531142Sedward return savestr(hfield); 12631142Sedward } 12731142Sedward return NOSTR; 1281219Skas } 1291219Skas 1301219Skas /* 1311219Skas * Return the next header field found in the given message. 13231142Sedward * Return >= 0 if something found, < 0 elsewise. 13331142Sedward * "colon" is set to point to the colon in the header. 1341219Skas * Must deal with \ continuations & other such fraud. 1351219Skas */ 13631142Sedward gethfield(f, linebuf, rem, colon) 1371219Skas register FILE *f; 1381219Skas char linebuf[]; 1391219Skas register int rem; 14031142Sedward char **colon; 1411219Skas { 1421219Skas char line2[LINESIZE]; 1431219Skas register char *cp, *cp2; 1441219Skas register int c; 1451219Skas 1461219Skas for (;;) { 14731142Sedward if (--rem < 0) 14831142Sedward return -1; 149*36478Sedward if ((c = readline(f, linebuf, LINESIZE)) <= 0) 15031142Sedward return -1; 15131142Sedward for (cp = linebuf; isprint(*cp) && *cp != ' ' && *cp != ':'; 15231142Sedward cp++) 15331142Sedward ; 15431142Sedward if (*cp != ':' || cp == linebuf) 1551219Skas continue; 1561219Skas /* 1571219Skas * I guess we got a headline. 1581219Skas * Handle wraparounding 1591219Skas */ 16031142Sedward *colon = cp; 16131142Sedward cp = linebuf + c; 1621219Skas for (;;) { 16331142Sedward while (--cp >= linebuf && (*cp == ' ' || *cp == '\t')) 16431142Sedward ; 16531142Sedward cp++; 1661219Skas if (rem <= 0) 1671219Skas break; 16831142Sedward ungetc(c = getc(f), f); 16931142Sedward if (c != ' ' && c != '\t') 1701219Skas break; 171*36478Sedward if ((c = readline(f, line2, LINESIZE)) < 0) 1721219Skas break; 1731219Skas rem--; 17431142Sedward for (cp2 = line2; *cp2 == ' ' || *cp2 == '\t'; cp2++) 1751219Skas ; 17631142Sedward c -= cp2 - line2; 17731142Sedward if (cp + c >= linebuf + LINESIZE - 2) 1781219Skas break; 1791219Skas *cp++ = ' '; 18031142Sedward bcopy(cp2, cp, c); 18131142Sedward cp += c; 1821219Skas } 18331142Sedward *cp = 0; 18431142Sedward return rem; 1851219Skas } 1861219Skas /* NOTREACHED */ 1871219Skas } 1881219Skas 1891219Skas /* 1901219Skas * Check whether the passed line is a header line of 19131142Sedward * the desired breed. Return the field body, or 0. 1921219Skas */ 1931219Skas 19431142Sedward char* 19531142Sedward ishfield(linebuf, colon, field) 1961219Skas char linebuf[], field[]; 19731142Sedward char *colon; 1981219Skas { 19931142Sedward register char *cp = colon; 2001219Skas 2011219Skas *cp = 0; 20234987Sedward if (strcasecmp(linebuf, field) != 0) { 20331142Sedward *cp = ':'; 20431142Sedward return 0; 2051219Skas } 20631142Sedward *cp = ':'; 20731142Sedward for (cp++; *cp == ' ' || *cp == '\t'; cp++) 20831142Sedward ; 20931142Sedward return cp; 2101219Skas } 2111219Skas 2121219Skas /* 2137571Skurt * Copy a string, lowercasing it as we go. 2147571Skurt */ 2157571Skurt istrcpy(dest, src) 21631142Sedward register char *dest, *src; 2177571Skurt { 2187571Skurt 2197571Skurt do { 22031142Sedward if (isupper(*src)) 22131142Sedward *dest++ = tolower(*src); 22231142Sedward else 22331142Sedward *dest++ = *src; 22431142Sedward } while (*src++ != 0); 2257571Skurt } 2267571Skurt 2277571Skurt /* 2281219Skas * The following code deals with input stacking to do source 2291219Skas * commands. All but the current file pointer are saved on 2301219Skas * the stack. 2311219Skas */ 2321219Skas 23334965Sedward static int ssp; /* Top of file stack */ 2341519Skas struct sstack { 2351519Skas FILE *s_file; /* File we were in. */ 2361519Skas int s_cond; /* Saved state of conditionals */ 2375785Skurt int s_loading; /* Loading .mailrc, etc. */ 23818661Sserge } sstack[NOFILE]; 2391219Skas 2401219Skas /* 2411219Skas * Pushdown current input file and switch to a new one. 2421219Skas * Set the global flag "sourcing" so that others will realize 2431219Skas * that they are no longer reading from a tty (in all probability). 2441219Skas */ 24534966Sedward source(arglist) 24634966Sedward char **arglist; 2471219Skas { 24834966Sedward FILE *fi; 24934966Sedward char *cp; 2501219Skas 25134966Sedward if ((cp = expand(*arglist)) == NOSTR) 2521219Skas return(1); 2533914Skurt if ((fi = fopen(cp, "r")) == NULL) { 2543914Skurt perror(cp); 2553914Skurt return(1); 2561219Skas } 25734965Sedward if (ssp >= NOFILE - 1) { 2581219Skas printf("Too much \"sourcing\" going on.\n"); 2591219Skas fclose(fi); 2601219Skas return(1); 2611219Skas } 26234965Sedward sstack[ssp].s_file = input; 2631519Skas sstack[ssp].s_cond = cond; 2645785Skurt sstack[ssp].s_loading = loading; 26534965Sedward ssp++; 2665785Skurt loading = 0; 2671519Skas cond = CANY; 2681219Skas input = fi; 2691219Skas sourcing++; 2701219Skas return(0); 2711219Skas } 2721219Skas 2731219Skas /* 2741219Skas * Pop the current input back to the previous level. 2751219Skas * Update the "sourcing" flag as appropriate. 2761219Skas */ 2771219Skas unstack() 2781219Skas { 27934965Sedward if (ssp <= 0) { 2801219Skas printf("\"Source\" stack over-pop.\n"); 2811219Skas sourcing = 0; 2821219Skas return(1); 2831219Skas } 2841219Skas fclose(input); 2851519Skas if (cond != CANY) 2861519Skas printf("Unmatched \"if\"\n"); 28734965Sedward ssp--; 2881519Skas cond = sstack[ssp].s_cond; 2895785Skurt loading = sstack[ssp].s_loading; 29034965Sedward input = sstack[ssp].s_file; 29134965Sedward if (ssp == 0) 2925785Skurt sourcing = loading; 2931219Skas return(0); 2941219Skas } 2951219Skas 2961219Skas /* 2971219Skas * Touch the indicated file. 2981219Skas * This is nifty for the shell. 2991219Skas */ 3001219Skas alter(name) 3011219Skas char name[]; 3021219Skas { 3031219Skas struct stat statb; 3041219Skas long time(); 3051219Skas time_t time_p[2]; 3061219Skas 3071219Skas if (stat(name, &statb) < 0) 3081219Skas return; 3091219Skas time_p[0] = time((long *) 0) + 1; 3101219Skas time_p[1] = statb.st_mtime; 3111219Skas utime(name, time_p); 3121219Skas } 3131219Skas 3141219Skas /* 3151219Skas * Examine the passed line buffer and 3161219Skas * return true if it is all blanks and tabs. 3171219Skas */ 3181219Skas blankline(linebuf) 3191219Skas char linebuf[]; 3201219Skas { 3211219Skas register char *cp; 3221219Skas 3231219Skas for (cp = linebuf; *cp; cp++) 32418661Sserge if (*cp != ' ' && *cp != '\t') 3251219Skas return(0); 3261219Skas return(1); 3271219Skas } 3281219Skas 3291219Skas /* 3303195Skas * Get sender's name from this message. If the message has 3313195Skas * a bunch of arpanet stuff in it, we may have to skin the name 3323195Skas * before returning it. 3333195Skas */ 3343195Skas char * 3353195Skas nameof(mp, reptype) 3363195Skas register struct message *mp; 3373195Skas { 3385237Skurt register char *cp, *cp2; 3393195Skas 3405237Skurt cp = skin(name1(mp, reptype)); 3415237Skurt if (reptype != 0 || charcount(cp, '!') < 2) 3425237Skurt return(cp); 3435237Skurt cp2 = rindex(cp, '!'); 3445237Skurt cp2--; 3455237Skurt while (cp2 > cp && *cp2 != '!') 3465237Skurt cp2--; 3475237Skurt if (*cp2 == '!') 3485237Skurt return(cp2 + 1); 3495237Skurt return(cp); 3503195Skas } 3513195Skas 3523195Skas /* 35325912Smckusick * Skin an arpa net address according to the RFC 822 interpretation 3543195Skas * of "host-phrase." 3553195Skas */ 3563195Skas char * 3573195Skas skin(name) 3583195Skas char *name; 3593195Skas { 3603195Skas register int c; 3613195Skas register char *cp, *cp2; 36225912Smckusick char *bufend; 3633195Skas int gotlt, lastsp; 3643195Skas char nbuf[BUFSIZ]; 36518661Sserge int nesting; 3663195Skas 3673195Skas if (name == NOSTR) 3683195Skas return(NOSTR); 36912819Sleres if (index(name, '(') == NOSTR && index(name, '<') == NOSTR 37031142Sedward && index(name, ' ') == NOSTR) 3713195Skas return(name); 3723195Skas gotlt = 0; 3733195Skas lastsp = 0; 37425912Smckusick bufend = nbuf; 37525912Smckusick for (cp = name, cp2 = bufend; c = *cp++; ) { 3763195Skas switch (c) { 3773195Skas case '(': 37825912Smckusick /* 37925912Smckusick * Start of a "comment". 38025912Smckusick * Ignore it. 38125912Smckusick */ 38218661Sserge nesting = 1; 38325912Smckusick while ((c = *cp) != 0) { 38425912Smckusick cp++; 38525912Smckusick switch (c) { 38625912Smckusick case '\\': 38725912Smckusick if (*cp == 0) 38825912Smckusick goto outcm; 38925912Smckusick cp++; 39025912Smckusick break; 39118661Sserge case '(': 39218661Sserge nesting++; 39318661Sserge break; 39418661Sserge 39518661Sserge case ')': 39618661Sserge --nesting; 39718661Sserge break; 39818661Sserge } 39918661Sserge 40018661Sserge if (nesting <= 0) 40118661Sserge break; 40218661Sserge } 40325912Smckusick outcm: 40412819Sleres lastsp = 0; 4053195Skas break; 4063195Skas 40725912Smckusick case '"': 40825912Smckusick /* 40925912Smckusick * Start of a "quoted-string". 41025912Smckusick * Copy it in its entirety. 41125912Smckusick */ 41225912Smckusick while ((c = *cp) != 0) { 41325912Smckusick cp++; 41425912Smckusick switch (c) { 41525912Smckusick case '\\': 41625912Smckusick if ((c = *cp) == 0) 41725912Smckusick goto outqs; 41825912Smckusick cp++; 41925912Smckusick break; 42025912Smckusick case '"': 42125912Smckusick goto outqs; 42225912Smckusick } 42325912Smckusick *cp2++ = c; 42425912Smckusick } 42525912Smckusick outqs: 42625912Smckusick lastsp = 0; 42725912Smckusick break; 42825912Smckusick 4293195Skas case ' ': 43012819Sleres if (cp[0] == 'a' && cp[1] == 't' && cp[2] == ' ') 43112819Sleres cp += 3, *cp2++ = '@'; 43212819Sleres else 43312819Sleres if (cp[0] == '@' && cp[1] == ' ') 43412819Sleres cp += 2, *cp2++ = '@'; 43512819Sleres else 43612819Sleres lastsp = 1; 4373195Skas break; 4383195Skas 4393195Skas case '<': 44025912Smckusick cp2 = bufend; 4413195Skas gotlt++; 4423195Skas lastsp = 0; 4433195Skas break; 4443195Skas 4453195Skas case '>': 44625912Smckusick if (gotlt) { 44725912Smckusick gotlt = 0; 44825912Smckusick while (*cp != ',' && *cp != 0) 44925912Smckusick cp++; 45025912Smckusick if (*cp == 0 ) 45125912Smckusick goto done; 45225912Smckusick *cp2++ = ','; 45325912Smckusick *cp2++ = ' '; 45425912Smckusick bufend = cp2; 45525912Smckusick break; 45625912Smckusick } 4573195Skas 4583195Skas /* Fall into . . . */ 4593195Skas 4603195Skas default: 4613195Skas if (lastsp) { 4623195Skas lastsp = 0; 4633195Skas *cp2++ = ' '; 4643195Skas } 4653195Skas *cp2++ = c; 4663195Skas break; 4673195Skas } 4683195Skas } 4693195Skas done: 4703195Skas *cp2 = 0; 4713195Skas 4723195Skas return(savestr(nbuf)); 4733195Skas } 4743195Skas 4753195Skas /* 4761219Skas * Fetch the sender's name from the passed message. 4773195Skas * Reptype can be 4783195Skas * 0 -- get sender's name for display purposes 4793195Skas * 1 -- get sender's name for reply 4803195Skas * 2 -- get sender's name for Reply 4811219Skas */ 4821219Skas char * 4833195Skas name1(mp, reptype) 4841219Skas register struct message *mp; 4851219Skas { 4861219Skas char namebuf[LINESIZE]; 4871219Skas char linebuf[LINESIZE]; 4881219Skas register char *cp, *cp2; 4891219Skas register FILE *ibuf; 4901219Skas int first = 1; 4911219Skas 4923195Skas if ((cp = hfield("from", mp)) != NOSTR) 49331142Sedward return cp; 4943195Skas if (reptype == 0 && (cp = hfield("sender", mp)) != NOSTR) 49531142Sedward return cp; 4961219Skas ibuf = setinput(mp); 49731142Sedward namebuf[0] = 0; 498*36478Sedward if (readline(ibuf, linebuf, LINESIZE) < 0) 4991219Skas return(savestr(namebuf)); 5001219Skas newname: 50131142Sedward for (cp = linebuf; *cp && *cp != ' '; cp++) 5021219Skas ; 50331142Sedward for (; *cp == ' ' || *cp == '\t'; cp++) 5041219Skas ; 50531142Sedward for (cp2 = &namebuf[strlen(namebuf)]; 50631142Sedward *cp && *cp != ' ' && *cp != '\t' && cp2 < namebuf + LINESIZE - 1;) 50731142Sedward *cp2++ = *cp++; 5081219Skas *cp2 = '\0'; 509*36478Sedward if (readline(ibuf, linebuf, LINESIZE) < 0) 5101219Skas return(savestr(namebuf)); 5111219Skas if ((cp = index(linebuf, 'F')) == NULL) 5121219Skas return(savestr(namebuf)); 5131219Skas if (strncmp(cp, "From", 4) != 0) 5141219Skas return(savestr(namebuf)); 5151219Skas while ((cp = index(cp, 'r')) != NULL) { 5161219Skas if (strncmp(cp, "remote", 6) == 0) { 5171219Skas if ((cp = index(cp, 'f')) == NULL) 5181219Skas break; 5191219Skas if (strncmp(cp, "from", 4) != 0) 5201219Skas break; 5211219Skas if ((cp = index(cp, ' ')) == NULL) 5221219Skas break; 5231219Skas cp++; 5241219Skas if (first) { 52531142Sedward strcpy(namebuf, cp); 5261219Skas first = 0; 5271219Skas } else 5281219Skas strcpy(rindex(namebuf, '!')+1, cp); 5291219Skas strcat(namebuf, "!"); 5301219Skas goto newname; 5311219Skas } 5321219Skas cp++; 5331219Skas } 5341219Skas return(savestr(namebuf)); 5351219Skas } 5361219Skas 5371219Skas /* 5385237Skurt * Count the occurances of c in str 5395237Skurt */ 5405237Skurt charcount(str, c) 5415237Skurt char *str; 5425237Skurt { 5435237Skurt register char *cp; 5445237Skurt register int i; 5455237Skurt 5465237Skurt for (i = 0, cp = str; *cp; cp++) 5475237Skurt if (*cp == c) 5485237Skurt i++; 5495237Skurt return(i); 5505237Skurt } 5515237Skurt 5525237Skurt /* 55331142Sedward * Are any of the characters in the two strings the same? 5541219Skas */ 55531142Sedward anyof(s1, s2) 55631142Sedward register char *s1, *s2; 5571219Skas { 5581219Skas 55931142Sedward while (*s1) 56031142Sedward if (index(s2, *s1++)) 56131142Sedward return 1; 56231142Sedward return 0; 5631219Skas } 5641219Skas 5651219Skas /* 56631142Sedward * Convert c to upper case 5671219Skas */ 56831142Sedward raise(c) 56931142Sedward register c; 57031142Sedward { 57131142Sedward 57231142Sedward if (islower(c)) 57331142Sedward return toupper(c); 57431142Sedward return c; 57531142Sedward } 57631142Sedward 57731142Sedward /* 57831142Sedward * Copy s1 to s2, return pointer to null in s2. 57931142Sedward */ 58031142Sedward char * 58131142Sedward copy(s1, s2) 5821219Skas register char *s1, *s2; 5831219Skas { 5841219Skas 58531142Sedward while (*s2++ = *s1++) 58631142Sedward ; 58731142Sedward return s2 - 1; 5881219Skas } 5891219Skas 5901219Skas /* 5917571Skurt * See if the given header field is supposed to be ignored. 5927571Skurt */ 59334692Sedward isign(field, ignore) 5947571Skurt char *field; 59534692Sedward struct ignoretab ignore[2]; 5967571Skurt { 5977571Skurt char realfld[BUFSIZ]; 5987571Skurt 59934969Sedward if (ignore == ignoreall) 60034969Sedward return 1; 60118661Sserge /* 60218661Sserge * Lower-case the string, so that "Status" and "status" 60318661Sserge * will hash to the same place. 60418661Sserge */ 6057571Skurt istrcpy(realfld, field); 60634692Sedward if (ignore[1].i_count > 0) 60734692Sedward return (!member(realfld, ignore + 1)); 60818661Sserge else 60918661Sserge return (member(realfld, ignore)); 6107571Skurt } 61118661Sserge 61218661Sserge member(realfield, table) 61318661Sserge register char *realfield; 61434692Sedward struct ignoretab *table; 61518661Sserge { 61618661Sserge register struct ignore *igp; 61718661Sserge 61834692Sedward for (igp = table->i_head[hash(realfield)]; igp != 0; igp = igp->i_link) 61931142Sedward if (*igp->i_field == *realfield && 62031142Sedward equal(igp->i_field, realfield)) 62118661Sserge return (1); 62218661Sserge return (0); 62318661Sserge } 624