122694Sdist /* 235073Sbostic * Copyright (c) 1983 Eric P. Allman 333728Sbostic * Copyright (c) 1988 Regents of the University of California. 433728Sbostic * All rights reserved. 533728Sbostic * 642824Sbostic * %sccs.include.redist.c% 733728Sbostic */ 822694Sdist 950577Seric # include <sys/types.h> 1050577Seric # include <sys/stat.h> 1150577Seric # include <signal.h> 1250577Seric # include <errno.h> 1350577Seric # include "sendmail.h" 1450577Seric # include <sys/file.h> 1550577Seric # include <pwd.h> 1650577Seric 1750577Seric # ifdef NEWDB 1850577Seric # include <db.h> 1950577Seric # endif 2050577Seric 21*51835Seric # ifdef LOCKF 22*51835Seric # include <unistd.h> 23*51835Seric # endif 24*51835Seric 2533728Sbostic #ifndef lint 2651756Seric #ifdef NEWDB 27*51835Seric static char sccsid[] = "@(#)alias.c 5.28 (Berkeley) 12/05/91 (with NEWDB)"; 2851756Seric #else 2933728Sbostic #ifdef DBM 30*51835Seric static char sccsid[] = "@(#)alias.c 5.28 (Berkeley) 12/05/91 (with DBM)"; 3133728Sbostic #else 32*51835Seric static char sccsid[] = "@(#)alias.c 5.28 (Berkeley) 12/05/91 (without DBM)"; 3333728Sbostic #endif 3450575Seric #endif 3533728Sbostic #endif /* not lint */ 3651756Seric /* 37292Seric ** ALIAS -- Compute aliases. 38292Seric ** 399368Seric ** Scans the alias file for an alias for the given address. 409368Seric ** If found, it arranges to deliver to the alias list instead. 419368Seric ** Uses libdbm database if -DDBM. 42292Seric ** 43292Seric ** Parameters: 444097Seric ** a -- address to alias. 454999Seric ** sendq -- a pointer to the head of the send queue 464999Seric ** to put the aliases in. 47292Seric ** 48292Seric ** Returns: 49292Seric ** none 50292Seric ** 51292Seric ** Side Effects: 523185Seric ** Aliases found are expanded. 53292Seric ** 54292Seric ** Notes: 55292Seric ** If NoAlias (the "-n" flag) is set, no aliasing is 56292Seric ** done. 57292Seric ** 58292Seric ** Deficiencies: 59292Seric ** It should complain about names that are aliased to 60292Seric ** nothing. 61292Seric */ 62292Seric 63292Seric 641503Smark #ifdef DBM 6551756Seric #ifndef NEWDB 662966Seric typedef struct 672966Seric { 6850575Seric char *data; 6950575Seric int size; 7050575Seric } DBT; 7151756Seric #endif 7250575Seric extern DBT fetch(); 7350575Seric #endif /* DBM */ 74292Seric 7550575Seric #ifdef NEWDB 7650575Seric static DB *AliasDBptr; 7750575Seric #endif 7850575Seric 794999Seric alias(a, sendq) 804097Seric register ADDRESS *a; 814999Seric ADDRESS **sendq; 82292Seric { 834081Seric register char *p; 845701Seric extern char *aliaslookup(); 85292Seric 867671Seric if (tTd(27, 1)) 874098Seric printf("alias(%s)\n", a->q_paddr); 88292Seric 894098Seric /* don't realias already aliased names */ 904098Seric if (bitset(QDONTSEND, a->q_flags)) 914098Seric return; 924098Seric 936898Seric CurEnv->e_to = a->q_paddr; 944098Seric 954314Seric /* 964314Seric ** Look up this name 974314Seric */ 984314Seric 9924944Seric if (NoAlias) 10024944Seric p = NULL; 10124944Seric else 10224944Seric p = aliaslookup(a->q_user); 1034098Seric if (p == NULL) 1044098Seric return; 105292Seric 106292Seric /* 1074098Seric ** Match on Alias. 1084098Seric ** Deliver to the target list. 1091515Seric */ 1101515Seric 1117671Seric if (tTd(27, 1)) 1124098Seric printf("%s (%s, %s) aliased to %s\n", 1134098Seric a->q_paddr, a->q_host, a->q_user, p); 1147051Seric message(Arpa_Info, "aliased to %s", p); 1154098Seric AliasLevel++; 1169614Seric sendtolist(p, a, sendq); 1174098Seric AliasLevel--; 1184098Seric } 1194098Seric /* 1205701Seric ** ALIASLOOKUP -- look up a name in the alias file. 1215701Seric ** 1225701Seric ** Parameters: 1235701Seric ** name -- the name to look up. 1245701Seric ** 1255701Seric ** Returns: 1265701Seric ** the value of name. 1275701Seric ** NULL if unknown. 1285701Seric ** 1295701Seric ** Side Effects: 1305701Seric ** none. 1315701Seric ** 1325701Seric ** Warnings: 1335701Seric ** The return value will be trashed across calls. 1345701Seric */ 1355701Seric 1365701Seric char * 1375701Seric aliaslookup(name) 1385701Seric char *name; 1395701Seric { 14050575Seric # if defined(NEWDB) || defined(DBM) 14150575Seric DBT rhs, lhs; 14250575Seric int s; 1435701Seric 1445701Seric /* create a key for fetch */ 14550575Seric lhs.data = name; 14650575Seric lhs.size = strlen(name) + 1; 14750575Seric # ifdef NEWDB 14851756Seric if (AliasDBptr != NULL) 14951756Seric { 15051756Seric s = AliasDBptr->get(AliasDBptr, &lhs, &rhs, 0); 15151756Seric if (s == 0) 15251756Seric return (rhs.data); 15351756Seric } 15451756Seric # ifdef DBM 15551756Seric else 15651756Seric { 15751756Seric rhs = fetch(lhs); 15851756Seric return (rhs.data); 15951756Seric } 16051756Seric # endif 16150575Seric # else 1625701Seric rhs = fetch(lhs); 16351756Seric return (rhs.data); 16450575Seric # endif 16551756Seric # else /* neither NEWDB nor DBM */ 1665701Seric register STAB *s; 1675701Seric 1685701Seric s = stab(name, ST_ALIAS, ST_FIND); 16951756Seric if (s != NULL) 17051756Seric return (s->s_alias); 17151756Seric # endif 17251756Seric return (NULL); 1735701Seric } 1745701Seric /* 1754098Seric ** INITALIASES -- initialize for aliasing 1764098Seric ** 1774098Seric ** Very different depending on whether we are running DBM or not. 1784098Seric ** 1794098Seric ** Parameters: 1804098Seric ** aliasfile -- location of aliases. 1814157Seric ** init -- if set and if DBM, initialize the DBM files. 1824098Seric ** 1834098Seric ** Returns: 1844098Seric ** none. 1854098Seric ** 1864098Seric ** Side Effects: 1874098Seric ** initializes aliases: 1884098Seric ** if DBM: opens the database. 1894098Seric ** if ~DBM: reads the aliases into the symbol table. 1904098Seric */ 1914098Seric 19240559Sbostic # define DBMMODE 0644 1934157Seric 1944157Seric initaliases(aliasfile, init) 1954098Seric char *aliasfile; 1964157Seric bool init; 1974098Seric { 19850575Seric #if defined(DBM) || defined(NEWDB) 1998437Seric int atcnt; 20025522Seric time_t modtime; 20125522Seric bool automatic = FALSE; 2024322Seric char buf[MAXNAME]; 20350575Seric #endif 2049368Seric struct stat stb; 20527176Seric static bool initialized = FALSE; 20646928Sbostic static int readaliases(); 2074322Seric 20827176Seric if (initialized) 20927176Seric return; 21027176Seric initialized = TRUE; 21127176Seric 21217984Seric if (aliasfile == NULL || stat(aliasfile, &stb) < 0) 2138437Seric { 21425522Seric if (aliasfile != NULL && init) 21525522Seric syserr("Cannot open %s", aliasfile); 2168437Seric NoAlias = TRUE; 21711937Seric errno = 0; 2188437Seric return; 2198437Seric } 2208437Seric 22150575Seric # if defined(DBM) || defined(NEWDB) 2224322Seric /* 2238437Seric ** Check to see that the alias file is complete. 2248437Seric ** If not, we will assume that someone died, and it is up 2258437Seric ** to us to rebuild it. 2268437Seric */ 2278437Seric 22825689Seric if (!init) 22950575Seric { 23050575Seric # ifdef NEWDB 23150575Seric (void) strcpy(buf, aliasfile); 23250575Seric (void) strcat(buf, ".db"); 23351171Sbostic AliasDBptr = dbopen(buf, O_RDONLY, DBMMODE, DB_HASH, NULL); 23450576Seric if (AliasDBptr == NULL) 23550576Seric { 23651756Seric # ifdef DBM 23751756Seric dbminit(aliasfile); 23851756Seric # else 23950576Seric syserr("initaliases: cannot open %s", buf); 24050576Seric NoAlias = TRUE; 24150576Seric return; 24251756Seric # endif 24350576Seric } 24450575Seric # else 24525689Seric dbminit(aliasfile); 24650575Seric # endif 24750575Seric } 24817471Seric atcnt = SafeAlias * 2; 24917471Seric if (atcnt > 0) 25017471Seric { 25117471Seric while (!init && atcnt-- >= 0 && aliaslookup("@") == NULL) 25225689Seric { 25325689Seric /* 25425689Seric ** Reinitialize alias file in case the new 25525689Seric ** one is mv'ed in instead of cp'ed in. 25625689Seric ** 25725689Seric ** Only works with new DBM -- old one will 25825689Seric ** just consume file descriptors forever. 25925689Seric ** If you have a dbmclose() it can be 26025689Seric ** added before the sleep(30). 26125689Seric */ 26225689Seric 26350575Seric # ifdef NEWDB 26451756Seric if (AliasDBptr != NULL) 26551756Seric AliasDBptr->close(AliasDBptr); 26650575Seric # endif 26750575Seric 26817471Seric sleep(30); 26950575Seric # ifdef NEWDB 27050575Seric (void) strcpy(buf, aliasfile); 27150575Seric (void) strcat(buf, ".db"); 27251171Sbostic AliasDBptr = 27351171Sbostic dbopen(buf, O_RDONLY, DBMMODE, DB_HASH, NULL); 27450576Seric if (AliasDBptr == NULL) 27550576Seric { 27651756Seric # ifdef NDBM 27751756Seric dbminit(aliasfile); 27851756Seric # else 27950576Seric syserr("initaliases: cannot open %s", buf); 28050576Seric NoAlias = TRUE; 28150576Seric return; 28251756Seric # endif 28350576Seric } 28450575Seric # else 28525689Seric # ifdef NDBM 28625689Seric dbminit(aliasfile); 28750575Seric # endif 28850575Seric # endif 28925689Seric } 29017471Seric } 29117471Seric else 29217471Seric atcnt = 1; 2938437Seric 2948437Seric /* 2954322Seric ** See if the DBM version of the file is out of date with 2964322Seric ** the text version. If so, go into 'init' mode automatically. 29740559Sbostic ** This only happens if our effective userid owns the DBM. 29840559Sbostic ** Note the unpalatable hack to see if the stat succeeded. 2994322Seric */ 3004322Seric 3014322Seric modtime = stb.st_mtime; 3024322Seric (void) strcpy(buf, aliasfile); 30350575Seric # ifdef NEWDB 30450575Seric (void) strcat(buf, ".db"); 30550575Seric # else 3064322Seric (void) strcat(buf, ".pag"); 30750575Seric # endif 3084322Seric stb.st_ino = 0; 30919039Seric if (!init && (stat(buf, &stb) < 0 || stb.st_mtime < modtime || atcnt < 0)) 3104322Seric { 31111937Seric errno = 0; 31240559Sbostic if (AutoRebuild && stb.st_ino != 0 && stb.st_uid == geteuid()) 3134322Seric { 3144322Seric init = TRUE; 31525522Seric automatic = TRUE; 3167051Seric message(Arpa_Info, "rebuilding alias database"); 31724944Seric #ifdef LOG 31824944Seric if (LogLevel >= 7) 31924944Seric syslog(LOG_INFO, "rebuilding alias database"); 32024944Seric #endif LOG 3214322Seric } 3224322Seric else 3234322Seric { 32419039Seric #ifdef LOG 32524944Seric if (LogLevel >= 7) 32624944Seric syslog(LOG_INFO, "alias database out of date"); 32719039Seric #endif LOG 3284322Seric message(Arpa_Info, "Warning: alias database out of date"); 3294322Seric } 3304322Seric } 3314322Seric 3324322Seric 3334322Seric /* 3348437Seric ** If necessary, load the DBM file. 3354322Seric ** If running without DBM, load the symbol table. 3364322Seric */ 3374322Seric 3384157Seric if (init) 3398437Seric { 34025522Seric #ifdef LOG 34125522Seric if (LogLevel >= 6) 34225522Seric { 34325522Seric extern char *username(); 34425522Seric 34525522Seric syslog(LOG_NOTICE, "alias database %srebuilt by %s", 34625522Seric automatic ? "auto" : "", username()); 34725522Seric } 34825522Seric #endif LOG 3494157Seric readaliases(aliasfile, TRUE); 3508437Seric } 3514098Seric # else DBM 3524157Seric readaliases(aliasfile, init); 3534157Seric # endif DBM 3544157Seric } 3554157Seric /* 3564157Seric ** READALIASES -- read and process the alias file. 3574157Seric ** 3584157Seric ** This routine implements the part of initaliases that occurs 3594157Seric ** when we are not going to use the DBM stuff. 3604157Seric ** 3614157Seric ** Parameters: 3624157Seric ** aliasfile -- the pathname of the alias file master. 3634157Seric ** init -- if set, initialize the DBM stuff. 3644157Seric ** 3654157Seric ** Returns: 3664157Seric ** none. 3674157Seric ** 3684157Seric ** Side Effects: 3694157Seric ** Reads aliasfile into the symbol table. 3704157Seric ** Optionally, builds the .dir & .pag files. 3714157Seric */ 3724157Seric 3734157Seric static 3744157Seric readaliases(aliasfile, init) 3754157Seric char *aliasfile; 3764157Seric bool init; 3774157Seric { 3784098Seric register char *p; 3794098Seric char *rhs; 3804098Seric bool skipping; 3819368Seric int naliases, bytes, longest; 3829368Seric FILE *af; 38340970Sbostic void (*oldsigint)(); 3844098Seric ADDRESS al, bl; 3854106Seric register STAB *s; 38650575Seric # ifdef NEWDB 38750575Seric DB *dbp; 38850575Seric # endif 3899368Seric char line[BUFSIZ]; 3904098Seric 3914098Seric if ((af = fopen(aliasfile, "r")) == NULL) 3921515Seric { 3937671Seric if (tTd(27, 1)) 3944106Seric printf("Can't open %s\n", aliasfile); 3954098Seric errno = 0; 3964098Seric NoAlias++; 3974098Seric return; 3984098Seric } 3994314Seric 40050575Seric # if defined(DBM) || defined(NEWDB) 40119784Seric /* see if someone else is rebuilding the alias file already */ 402*51835Seric # ifdef LOCKF 403*51835Seric if (lockf(fileno(af), F_TEST, 0) < 0 && errno == EACCES) 404*51835Seric # else 40519784Seric if (flock(fileno(af), LOCK_EX | LOCK_NB) < 0 && errno == EWOULDBLOCK) 406*51835Seric # endif 40719784Seric { 40819784Seric /* yes, they are -- wait until done and then return */ 40919784Seric message(Arpa_Info, "Alias file is already being rebuilt"); 41019784Seric if (OpMode != MD_INITALIAS) 41119784Seric { 41219784Seric /* wait for other rebuild to complete */ 413*51835Seric # ifdef LOCKF 414*51835Seric (void) lockf(fileno(af), F_LOCK, 0); 415*51835Seric # else 41619784Seric (void) flock(fileno(af), LOCK_EX); 417*51835Seric # endif 41819784Seric } 41923108Seric (void) fclose(af); 42019784Seric errno = 0; 42119784Seric return; 42219784Seric } 42319784Seric # endif DBM 42419784Seric 4254314Seric /* 42619784Seric ** If initializing, create the new DBM files. 42719784Seric */ 42819784Seric 42919784Seric if (init) 43019784Seric { 43119784Seric oldsigint = signal(SIGINT, SIG_IGN); 43251756Seric # ifdef NEWDB 43351756Seric (void) strcpy(line, aliasfile); 43451756Seric (void) strcat(line, ".db"); 43551756Seric dbp = dbopen(line, 43651756Seric O_RDWR|O_CREAT|O_TRUNC, DBMMODE, DB_HASH, NULL); 43751756Seric if (dbp == NULL) 43851756Seric { 43951756Seric syserr("readaliases: cannot create %s", line); 44051756Seric (void) signal(SIGINT, oldsigint); 44151756Seric return; 44251756Seric } 44351756Seric # else 44450575Seric # ifdef DBM 44519784Seric (void) strcpy(line, aliasfile); 44619784Seric (void) strcat(line, ".dir"); 44719784Seric if (close(creat(line, DBMMODE)) < 0) 44819784Seric { 44919784Seric syserr("cannot make %s", line); 45019784Seric (void) signal(SIGINT, oldsigint); 45119784Seric return; 45219784Seric } 45319784Seric (void) strcpy(line, aliasfile); 45419784Seric (void) strcat(line, ".pag"); 45519784Seric if (close(creat(line, DBMMODE)) < 0) 45619784Seric { 45719784Seric syserr("cannot make %s", line); 45819784Seric (void) signal(SIGINT, oldsigint); 45919784Seric return; 46019784Seric } 46126501Seric dbminit(aliasfile); 46250575Seric # endif 46350575Seric # endif 46419784Seric } 46519784Seric 46619784Seric /* 4674314Seric ** Read and interpret lines 4684314Seric */ 4694314Seric 4709368Seric FileName = aliasfile; 4719368Seric LineNumber = 0; 4724322Seric naliases = bytes = longest = 0; 4734098Seric skipping = FALSE; 4744098Seric while (fgets(line, sizeof (line), af) != NULL) 4754098Seric { 4764322Seric int lhssize, rhssize; 4774322Seric 4789368Seric LineNumber++; 47925278Seric p = index(line, '\n'); 48025278Seric if (p != NULL) 48125278Seric *p = '\0'; 4824098Seric switch (line[0]) 4834098Seric { 4844098Seric case '#': 4854098Seric case '\0': 4864098Seric skipping = FALSE; 4874098Seric continue; 4884065Seric 4894098Seric case ' ': 4904098Seric case '\t': 4914098Seric if (!skipping) 4929368Seric syserr("Non-continuation line starts with space"); 4934098Seric skipping = TRUE; 4944097Seric continue; 4954098Seric } 4964098Seric skipping = FALSE; 4971874Seric 4984314Seric /* 4994314Seric ** Process the LHS 5004314Seric ** Find the final colon, and parse the address. 50116898Seric ** It should resolve to a local name -- this will 50216898Seric ** be checked later (we want to optionally do 50316898Seric ** parsing of the RHS first to maximize error 50416898Seric ** detection). 5054314Seric */ 5064314Seric 5074098Seric for (p = line; *p != '\0' && *p != ':' && *p != '\n'; p++) 5084097Seric continue; 50916898Seric if (*p++ != ':') 5104098Seric { 5119368Seric syserr("missing colon"); 5124097Seric continue; 5134098Seric } 51416898Seric if (parseaddr(line, &al, 1, ':') == NULL) 5154098Seric { 51616898Seric syserr("illegal alias name"); 51716898Seric continue; 5184098Seric } 51916898Seric loweraddr(&al); 5204314Seric 5214314Seric /* 5224314Seric ** Process the RHS. 5234314Seric ** 'al' is the internal form of the LHS address. 5244314Seric ** 'p' points to the text of the RHS. 5254314Seric */ 5264314Seric 5274098Seric rhs = p; 5284098Seric for (;;) 5294098Seric { 5304098Seric register char c; 5311515Seric 53225821Seric if (init && CheckAliases) 5334098Seric { 5344157Seric /* do parsing & compression of addresses */ 53525278Seric while (*p != '\0') 5364098Seric { 53725278Seric extern char *DelimChar; 53825278Seric 53925278Seric while (isspace(*p) || *p == ',') 5404157Seric p++; 54125278Seric if (*p == '\0') 54225278Seric break; 54325278Seric if (parseaddr(p, &bl, -1, ',') == NULL) 54425278Seric usrerr("%s... bad address", p); 54525278Seric p = DelimChar; 5464098Seric } 5474098Seric } 5484157Seric else 54915769Seric { 55016898Seric p = &p[strlen(p)]; 55116898Seric if (p[-1] == '\n') 55216898Seric *--p = '\0'; 55315769Seric } 5541515Seric 5554098Seric /* see if there should be a continuation line */ 5564106Seric c = fgetc(af); 5574106Seric if (!feof(af)) 5584314Seric (void) ungetc(c, af); 5594106Seric if (c != ' ' && c != '\t') 5604098Seric break; 5614098Seric 5624098Seric /* read continuation line */ 5634098Seric if (fgets(p, sizeof line - (p - line), af) == NULL) 5644098Seric break; 5659368Seric LineNumber++; 5664098Seric } 56716898Seric if (al.q_mailer != LocalMailer) 56816898Seric { 56916898Seric syserr("cannot alias non-local names"); 57016898Seric continue; 57116898Seric } 5724314Seric 5734314Seric /* 5744314Seric ** Insert alias into symbol table or DBM file 5754314Seric */ 5764314Seric 57716898Seric lhssize = strlen(al.q_user) + 1; 5784322Seric rhssize = strlen(rhs) + 1; 5794322Seric 58050575Seric # if defined(DBM) || defined(NEWDB) 5814157Seric if (init) 5824157Seric { 58350575Seric DBT key, content; 5844157Seric 58550575Seric key.size = lhssize; 58650575Seric key.data = al.q_user; 58750575Seric content.size = rhssize; 58850575Seric content.data = rhs; 58951756Seric # ifdef NEWDB 59051171Sbostic if (dbp->put(dbp, &key, &content, 0) != 0) 59150576Seric syserr("readaliases: db put (%s)", al.q_user); 59251756Seric # else 59351756Seric store(key, content); 59450575Seric # endif 5954157Seric } 5964157Seric else 5974157Seric # endif DBM 5984157Seric { 5994157Seric s = stab(al.q_user, ST_ALIAS, ST_ENTER); 6004157Seric s->s_alias = newstr(rhs); 6014157Seric } 6024322Seric 6034322Seric /* statistics */ 6044322Seric naliases++; 6054322Seric bytes += lhssize + rhssize; 6064322Seric if (rhssize > longest) 6074322Seric longest = rhssize; 6081515Seric } 60919784Seric 61050575Seric # if defined(DBM) || defined(NEWDB) 61119784Seric if (init) 61219784Seric { 61319784Seric /* add the distinquished alias "@" */ 61450575Seric DBT key; 61519784Seric 61650575Seric key.size = 2; 61750575Seric key.data = "@"; 61850575Seric # ifdef NEWDB 61950576Seric if (dbp->sync(dbp) != 0 || 62051171Sbostic dbp->put(dbp, &key, &key, 0) != 0 || 62150576Seric dbp->close(dbp) != 0) 62250576Seric syserr("readaliases: db close failure"); 62350575Seric # else 62419784Seric store(key, key); 62550575Seric # endif 62619784Seric 62719784Seric /* restore the old signal */ 62819784Seric (void) signal(SIGINT, oldsigint); 62919784Seric } 63019784Seric # endif DBM 63119784Seric 63219784Seric /* closing the alias file drops the lock */ 6334098Seric (void) fclose(af); 6346898Seric CurEnv->e_to = NULL; 6359368Seric FileName = NULL; 6367051Seric message(Arpa_Info, "%d aliases, longest %d bytes, %d bytes total", 6374322Seric naliases, longest, bytes); 63824944Seric # ifdef LOG 63924944Seric if (LogLevel >= 8) 64024944Seric syslog(LOG_INFO, "%d aliases, longest %d bytes, %d bytes total", 64124944Seric naliases, longest, bytes); 64224944Seric # endif LOG 643292Seric } 644292Seric /* 645292Seric ** FORWARD -- Try to forward mail 646292Seric ** 647292Seric ** This is similar but not identical to aliasing. 648292Seric ** 649292Seric ** Parameters: 6504314Seric ** user -- the name of the user who's mail we would like 6514314Seric ** to forward to. It must have been verified -- 6524314Seric ** i.e., the q_home field must have been filled 6534314Seric ** in. 6544999Seric ** sendq -- a pointer to the head of the send queue to 6554999Seric ** put this user's aliases in. 656292Seric ** 657292Seric ** Returns: 6584098Seric ** none. 659292Seric ** 660292Seric ** Side Effects: 6613185Seric ** New names are added to send queues. 662292Seric */ 663292Seric 6644999Seric forward(user, sendq) 6652966Seric ADDRESS *user; 6664999Seric ADDRESS **sendq; 667292Seric { 6684078Seric char buf[60]; 6694536Seric extern bool safefile(); 6704069Seric 6717671Seric if (tTd(27, 1)) 6724098Seric printf("forward(%s)\n", user->q_paddr); 6734098Seric 6744594Seric if (user->q_mailer != LocalMailer || bitset(QBADADDR, user->q_flags)) 6754098Seric return; 6764314Seric if (user->q_home == NULL) 6774314Seric syserr("forward: no home"); 6784069Seric 6794069Seric /* good address -- look for .forward file in home */ 6809368Seric define('z', user->q_home, CurEnv); 68116154Seric expand("\001z/.forward", buf, &buf[sizeof buf - 1], CurEnv); 6824536Seric if (!safefile(buf, user->q_uid, S_IREAD)) 6834098Seric return; 6844069Seric 6854069Seric /* we do have an address to forward to -- do it */ 6864999Seric include(buf, "forwarding", user, sendq); 687292Seric } 688