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 2133728Sbostic #ifndef lint 22*51756Seric #ifdef NEWDB 23*51756Seric static char sccsid[] = "@(#)alias.c 5.27 (Berkeley) 11/19/91 (with NEWDB)"; 24*51756Seric #else 2533728Sbostic #ifdef DBM 26*51756Seric static char sccsid[] = "@(#)alias.c 5.27 (Berkeley) 11/19/91 (with DBM)"; 2733728Sbostic #else 28*51756Seric static char sccsid[] = "@(#)alias.c 5.27 (Berkeley) 11/19/91 (without DBM)"; 2933728Sbostic #endif 3050575Seric #endif 3133728Sbostic #endif /* not lint */ 32*51756Seric /* 33292Seric ** ALIAS -- Compute aliases. 34292Seric ** 359368Seric ** Scans the alias file for an alias for the given address. 369368Seric ** If found, it arranges to deliver to the alias list instead. 379368Seric ** Uses libdbm database if -DDBM. 38292Seric ** 39292Seric ** Parameters: 404097Seric ** a -- address to alias. 414999Seric ** sendq -- a pointer to the head of the send queue 424999Seric ** to put the aliases in. 43292Seric ** 44292Seric ** Returns: 45292Seric ** none 46292Seric ** 47292Seric ** Side Effects: 483185Seric ** Aliases found are expanded. 49292Seric ** 50292Seric ** Notes: 51292Seric ** If NoAlias (the "-n" flag) is set, no aliasing is 52292Seric ** done. 53292Seric ** 54292Seric ** Deficiencies: 55292Seric ** It should complain about names that are aliased to 56292Seric ** nothing. 57292Seric */ 58292Seric 59292Seric 601503Smark #ifdef DBM 61*51756Seric #ifndef NEWDB 622966Seric typedef struct 632966Seric { 6450575Seric char *data; 6550575Seric int size; 6650575Seric } DBT; 67*51756Seric #endif 6850575Seric extern DBT fetch(); 6950575Seric #endif /* DBM */ 70292Seric 7150575Seric #ifdef NEWDB 7250575Seric static DB *AliasDBptr; 7350575Seric #endif 7450575Seric 754999Seric alias(a, sendq) 764097Seric register ADDRESS *a; 774999Seric ADDRESS **sendq; 78292Seric { 794081Seric register char *p; 805701Seric extern char *aliaslookup(); 81292Seric 827671Seric if (tTd(27, 1)) 834098Seric printf("alias(%s)\n", a->q_paddr); 84292Seric 854098Seric /* don't realias already aliased names */ 864098Seric if (bitset(QDONTSEND, a->q_flags)) 874098Seric return; 884098Seric 896898Seric CurEnv->e_to = a->q_paddr; 904098Seric 914314Seric /* 924314Seric ** Look up this name 934314Seric */ 944314Seric 9524944Seric if (NoAlias) 9624944Seric p = NULL; 9724944Seric else 9824944Seric p = aliaslookup(a->q_user); 994098Seric if (p == NULL) 1004098Seric return; 101292Seric 102292Seric /* 1034098Seric ** Match on Alias. 1044098Seric ** Deliver to the target list. 1051515Seric */ 1061515Seric 1077671Seric if (tTd(27, 1)) 1084098Seric printf("%s (%s, %s) aliased to %s\n", 1094098Seric a->q_paddr, a->q_host, a->q_user, p); 1107051Seric message(Arpa_Info, "aliased to %s", p); 1114098Seric AliasLevel++; 1129614Seric sendtolist(p, a, sendq); 1134098Seric AliasLevel--; 1144098Seric } 1154098Seric /* 1165701Seric ** ALIASLOOKUP -- look up a name in the alias file. 1175701Seric ** 1185701Seric ** Parameters: 1195701Seric ** name -- the name to look up. 1205701Seric ** 1215701Seric ** Returns: 1225701Seric ** the value of name. 1235701Seric ** NULL if unknown. 1245701Seric ** 1255701Seric ** Side Effects: 1265701Seric ** none. 1275701Seric ** 1285701Seric ** Warnings: 1295701Seric ** The return value will be trashed across calls. 1305701Seric */ 1315701Seric 1325701Seric char * 1335701Seric aliaslookup(name) 1345701Seric char *name; 1355701Seric { 13650575Seric # if defined(NEWDB) || defined(DBM) 13750575Seric DBT rhs, lhs; 13850575Seric int s; 1395701Seric 1405701Seric /* create a key for fetch */ 14150575Seric lhs.data = name; 14250575Seric lhs.size = strlen(name) + 1; 14350575Seric # ifdef NEWDB 144*51756Seric if (AliasDBptr != NULL) 145*51756Seric { 146*51756Seric s = AliasDBptr->get(AliasDBptr, &lhs, &rhs, 0); 147*51756Seric if (s == 0) 148*51756Seric return (rhs.data); 149*51756Seric } 150*51756Seric # ifdef DBM 151*51756Seric else 152*51756Seric { 153*51756Seric rhs = fetch(lhs); 154*51756Seric return (rhs.data); 155*51756Seric } 156*51756Seric # endif 15750575Seric # else 1585701Seric rhs = fetch(lhs); 159*51756Seric return (rhs.data); 16050575Seric # endif 161*51756Seric # else /* neither NEWDB nor DBM */ 1625701Seric register STAB *s; 1635701Seric 1645701Seric s = stab(name, ST_ALIAS, ST_FIND); 165*51756Seric if (s != NULL) 166*51756Seric return (s->s_alias); 167*51756Seric # endif 168*51756Seric return (NULL); 1695701Seric } 1705701Seric /* 1714098Seric ** INITALIASES -- initialize for aliasing 1724098Seric ** 1734098Seric ** Very different depending on whether we are running DBM or not. 1744098Seric ** 1754098Seric ** Parameters: 1764098Seric ** aliasfile -- location of aliases. 1774157Seric ** init -- if set and if DBM, initialize the DBM files. 1784098Seric ** 1794098Seric ** Returns: 1804098Seric ** none. 1814098Seric ** 1824098Seric ** Side Effects: 1834098Seric ** initializes aliases: 1844098Seric ** if DBM: opens the database. 1854098Seric ** if ~DBM: reads the aliases into the symbol table. 1864098Seric */ 1874098Seric 18840559Sbostic # define DBMMODE 0644 1894157Seric 1904157Seric initaliases(aliasfile, init) 1914098Seric char *aliasfile; 1924157Seric bool init; 1934098Seric { 19450575Seric #if defined(DBM) || defined(NEWDB) 1958437Seric int atcnt; 19625522Seric time_t modtime; 19725522Seric bool automatic = FALSE; 1984322Seric char buf[MAXNAME]; 19950575Seric #endif 2009368Seric struct stat stb; 20127176Seric static bool initialized = FALSE; 20246928Sbostic static int readaliases(); 2034322Seric 20427176Seric if (initialized) 20527176Seric return; 20627176Seric initialized = TRUE; 20727176Seric 20817984Seric if (aliasfile == NULL || stat(aliasfile, &stb) < 0) 2098437Seric { 21025522Seric if (aliasfile != NULL && init) 21125522Seric syserr("Cannot open %s", aliasfile); 2128437Seric NoAlias = TRUE; 21311937Seric errno = 0; 2148437Seric return; 2158437Seric } 2168437Seric 21750575Seric # if defined(DBM) || defined(NEWDB) 2184322Seric /* 2198437Seric ** Check to see that the alias file is complete. 2208437Seric ** If not, we will assume that someone died, and it is up 2218437Seric ** to us to rebuild it. 2228437Seric */ 2238437Seric 22425689Seric if (!init) 22550575Seric { 22650575Seric # ifdef NEWDB 22750575Seric (void) strcpy(buf, aliasfile); 22850575Seric (void) strcat(buf, ".db"); 22951171Sbostic AliasDBptr = dbopen(buf, O_RDONLY, DBMMODE, DB_HASH, NULL); 23050576Seric if (AliasDBptr == NULL) 23150576Seric { 232*51756Seric # ifdef DBM 233*51756Seric dbminit(aliasfile); 234*51756Seric # else 23550576Seric syserr("initaliases: cannot open %s", buf); 23650576Seric NoAlias = TRUE; 23750576Seric return; 238*51756Seric # endif 23950576Seric } 24050575Seric # else 24125689Seric dbminit(aliasfile); 24250575Seric # endif 24350575Seric } 24417471Seric atcnt = SafeAlias * 2; 24517471Seric if (atcnt > 0) 24617471Seric { 24717471Seric while (!init && atcnt-- >= 0 && aliaslookup("@") == NULL) 24825689Seric { 24925689Seric /* 25025689Seric ** Reinitialize alias file in case the new 25125689Seric ** one is mv'ed in instead of cp'ed in. 25225689Seric ** 25325689Seric ** Only works with new DBM -- old one will 25425689Seric ** just consume file descriptors forever. 25525689Seric ** If you have a dbmclose() it can be 25625689Seric ** added before the sleep(30). 25725689Seric */ 25825689Seric 25950575Seric # ifdef NEWDB 260*51756Seric if (AliasDBptr != NULL) 261*51756Seric AliasDBptr->close(AliasDBptr); 26250575Seric # endif 26350575Seric 26417471Seric sleep(30); 26550575Seric # ifdef NEWDB 26650575Seric (void) strcpy(buf, aliasfile); 26750575Seric (void) strcat(buf, ".db"); 26851171Sbostic AliasDBptr = 26951171Sbostic dbopen(buf, O_RDONLY, DBMMODE, DB_HASH, NULL); 27050576Seric if (AliasDBptr == NULL) 27150576Seric { 272*51756Seric # ifdef NDBM 273*51756Seric dbminit(aliasfile); 274*51756Seric # else 27550576Seric syserr("initaliases: cannot open %s", buf); 27650576Seric NoAlias = TRUE; 27750576Seric return; 278*51756Seric # endif 27950576Seric } 28050575Seric # else 28125689Seric # ifdef NDBM 28225689Seric dbminit(aliasfile); 28350575Seric # endif 28450575Seric # endif 28525689Seric } 28617471Seric } 28717471Seric else 28817471Seric atcnt = 1; 2898437Seric 2908437Seric /* 2914322Seric ** See if the DBM version of the file is out of date with 2924322Seric ** the text version. If so, go into 'init' mode automatically. 29340559Sbostic ** This only happens if our effective userid owns the DBM. 29440559Sbostic ** Note the unpalatable hack to see if the stat succeeded. 2954322Seric */ 2964322Seric 2974322Seric modtime = stb.st_mtime; 2984322Seric (void) strcpy(buf, aliasfile); 29950575Seric # ifdef NEWDB 30050575Seric (void) strcat(buf, ".db"); 30150575Seric # else 3024322Seric (void) strcat(buf, ".pag"); 30350575Seric # endif 3044322Seric stb.st_ino = 0; 30519039Seric if (!init && (stat(buf, &stb) < 0 || stb.st_mtime < modtime || atcnt < 0)) 3064322Seric { 30711937Seric errno = 0; 30840559Sbostic if (AutoRebuild && stb.st_ino != 0 && stb.st_uid == geteuid()) 3094322Seric { 3104322Seric init = TRUE; 31125522Seric automatic = TRUE; 3127051Seric message(Arpa_Info, "rebuilding alias database"); 31324944Seric #ifdef LOG 31424944Seric if (LogLevel >= 7) 31524944Seric syslog(LOG_INFO, "rebuilding alias database"); 31624944Seric #endif LOG 3174322Seric } 3184322Seric else 3194322Seric { 32019039Seric #ifdef LOG 32124944Seric if (LogLevel >= 7) 32224944Seric syslog(LOG_INFO, "alias database out of date"); 32319039Seric #endif LOG 3244322Seric message(Arpa_Info, "Warning: alias database out of date"); 3254322Seric } 3264322Seric } 3274322Seric 3284322Seric 3294322Seric /* 3308437Seric ** If necessary, load the DBM file. 3314322Seric ** If running without DBM, load the symbol table. 3324322Seric */ 3334322Seric 3344157Seric if (init) 3358437Seric { 33625522Seric #ifdef LOG 33725522Seric if (LogLevel >= 6) 33825522Seric { 33925522Seric extern char *username(); 34025522Seric 34125522Seric syslog(LOG_NOTICE, "alias database %srebuilt by %s", 34225522Seric automatic ? "auto" : "", username()); 34325522Seric } 34425522Seric #endif LOG 3454157Seric readaliases(aliasfile, TRUE); 3468437Seric } 3474098Seric # else DBM 3484157Seric readaliases(aliasfile, init); 3494157Seric # endif DBM 3504157Seric } 3514157Seric /* 3524157Seric ** READALIASES -- read and process the alias file. 3534157Seric ** 3544157Seric ** This routine implements the part of initaliases that occurs 3554157Seric ** when we are not going to use the DBM stuff. 3564157Seric ** 3574157Seric ** Parameters: 3584157Seric ** aliasfile -- the pathname of the alias file master. 3594157Seric ** init -- if set, initialize the DBM stuff. 3604157Seric ** 3614157Seric ** Returns: 3624157Seric ** none. 3634157Seric ** 3644157Seric ** Side Effects: 3654157Seric ** Reads aliasfile into the symbol table. 3664157Seric ** Optionally, builds the .dir & .pag files. 3674157Seric */ 3684157Seric 3694157Seric static 3704157Seric readaliases(aliasfile, init) 3714157Seric char *aliasfile; 3724157Seric bool init; 3734157Seric { 3744098Seric register char *p; 3754098Seric char *rhs; 3764098Seric bool skipping; 3779368Seric int naliases, bytes, longest; 3789368Seric FILE *af; 37940970Sbostic void (*oldsigint)(); 3804098Seric ADDRESS al, bl; 3814106Seric register STAB *s; 38250575Seric # ifdef NEWDB 38350575Seric DB *dbp; 38450575Seric # endif 3859368Seric char line[BUFSIZ]; 3864098Seric 3874098Seric if ((af = fopen(aliasfile, "r")) == NULL) 3881515Seric { 3897671Seric if (tTd(27, 1)) 3904106Seric printf("Can't open %s\n", aliasfile); 3914098Seric errno = 0; 3924098Seric NoAlias++; 3934098Seric return; 3944098Seric } 3954314Seric 39650575Seric # if defined(DBM) || defined(NEWDB) 39719784Seric /* see if someone else is rebuilding the alias file already */ 39819784Seric if (flock(fileno(af), LOCK_EX | LOCK_NB) < 0 && errno == EWOULDBLOCK) 39919784Seric { 40019784Seric /* yes, they are -- wait until done and then return */ 40119784Seric message(Arpa_Info, "Alias file is already being rebuilt"); 40219784Seric if (OpMode != MD_INITALIAS) 40319784Seric { 40419784Seric /* wait for other rebuild to complete */ 40519784Seric (void) flock(fileno(af), LOCK_EX); 40619784Seric } 40723108Seric (void) fclose(af); 40819784Seric errno = 0; 40919784Seric return; 41019784Seric } 41119784Seric # endif DBM 41219784Seric 4134314Seric /* 41419784Seric ** If initializing, create the new DBM files. 41519784Seric */ 41619784Seric 41719784Seric if (init) 41819784Seric { 41919784Seric oldsigint = signal(SIGINT, SIG_IGN); 420*51756Seric # ifdef NEWDB 421*51756Seric (void) strcpy(line, aliasfile); 422*51756Seric (void) strcat(line, ".db"); 423*51756Seric dbp = dbopen(line, 424*51756Seric O_RDWR|O_CREAT|O_TRUNC, DBMMODE, DB_HASH, NULL); 425*51756Seric if (dbp == NULL) 426*51756Seric { 427*51756Seric syserr("readaliases: cannot create %s", line); 428*51756Seric (void) signal(SIGINT, oldsigint); 429*51756Seric return; 430*51756Seric } 431*51756Seric # else 43250575Seric # ifdef DBM 43319784Seric (void) strcpy(line, aliasfile); 43419784Seric (void) strcat(line, ".dir"); 43519784Seric if (close(creat(line, DBMMODE)) < 0) 43619784Seric { 43719784Seric syserr("cannot make %s", line); 43819784Seric (void) signal(SIGINT, oldsigint); 43919784Seric return; 44019784Seric } 44119784Seric (void) strcpy(line, aliasfile); 44219784Seric (void) strcat(line, ".pag"); 44319784Seric if (close(creat(line, DBMMODE)) < 0) 44419784Seric { 44519784Seric syserr("cannot make %s", line); 44619784Seric (void) signal(SIGINT, oldsigint); 44719784Seric return; 44819784Seric } 44926501Seric dbminit(aliasfile); 45050575Seric # endif 45150575Seric # endif 45219784Seric } 45319784Seric 45419784Seric /* 4554314Seric ** Read and interpret lines 4564314Seric */ 4574314Seric 4589368Seric FileName = aliasfile; 4599368Seric LineNumber = 0; 4604322Seric naliases = bytes = longest = 0; 4614098Seric skipping = FALSE; 4624098Seric while (fgets(line, sizeof (line), af) != NULL) 4634098Seric { 4644322Seric int lhssize, rhssize; 4654322Seric 4669368Seric LineNumber++; 46725278Seric p = index(line, '\n'); 46825278Seric if (p != NULL) 46925278Seric *p = '\0'; 4704098Seric switch (line[0]) 4714098Seric { 4724098Seric case '#': 4734098Seric case '\0': 4744098Seric skipping = FALSE; 4754098Seric continue; 4764065Seric 4774098Seric case ' ': 4784098Seric case '\t': 4794098Seric if (!skipping) 4809368Seric syserr("Non-continuation line starts with space"); 4814098Seric skipping = TRUE; 4824097Seric continue; 4834098Seric } 4844098Seric skipping = FALSE; 4851874Seric 4864314Seric /* 4874314Seric ** Process the LHS 4884314Seric ** Find the final colon, and parse the address. 48916898Seric ** It should resolve to a local name -- this will 49016898Seric ** be checked later (we want to optionally do 49116898Seric ** parsing of the RHS first to maximize error 49216898Seric ** detection). 4934314Seric */ 4944314Seric 4954098Seric for (p = line; *p != '\0' && *p != ':' && *p != '\n'; p++) 4964097Seric continue; 49716898Seric if (*p++ != ':') 4984098Seric { 4999368Seric syserr("missing colon"); 5004097Seric continue; 5014098Seric } 50216898Seric if (parseaddr(line, &al, 1, ':') == NULL) 5034098Seric { 50416898Seric syserr("illegal alias name"); 50516898Seric continue; 5064098Seric } 50716898Seric loweraddr(&al); 5084314Seric 5094314Seric /* 5104314Seric ** Process the RHS. 5114314Seric ** 'al' is the internal form of the LHS address. 5124314Seric ** 'p' points to the text of the RHS. 5134314Seric */ 5144314Seric 5154098Seric rhs = p; 5164098Seric for (;;) 5174098Seric { 5184098Seric register char c; 5191515Seric 52025821Seric if (init && CheckAliases) 5214098Seric { 5224157Seric /* do parsing & compression of addresses */ 52325278Seric while (*p != '\0') 5244098Seric { 52525278Seric extern char *DelimChar; 52625278Seric 52725278Seric while (isspace(*p) || *p == ',') 5284157Seric p++; 52925278Seric if (*p == '\0') 53025278Seric break; 53125278Seric if (parseaddr(p, &bl, -1, ',') == NULL) 53225278Seric usrerr("%s... bad address", p); 53325278Seric p = DelimChar; 5344098Seric } 5354098Seric } 5364157Seric else 53715769Seric { 53816898Seric p = &p[strlen(p)]; 53916898Seric if (p[-1] == '\n') 54016898Seric *--p = '\0'; 54115769Seric } 5421515Seric 5434098Seric /* see if there should be a continuation line */ 5444106Seric c = fgetc(af); 5454106Seric if (!feof(af)) 5464314Seric (void) ungetc(c, af); 5474106Seric if (c != ' ' && c != '\t') 5484098Seric break; 5494098Seric 5504098Seric /* read continuation line */ 5514098Seric if (fgets(p, sizeof line - (p - line), af) == NULL) 5524098Seric break; 5539368Seric LineNumber++; 5544098Seric } 55516898Seric if (al.q_mailer != LocalMailer) 55616898Seric { 55716898Seric syserr("cannot alias non-local names"); 55816898Seric continue; 55916898Seric } 5604314Seric 5614314Seric /* 5624314Seric ** Insert alias into symbol table or DBM file 5634314Seric */ 5644314Seric 56516898Seric lhssize = strlen(al.q_user) + 1; 5664322Seric rhssize = strlen(rhs) + 1; 5674322Seric 56850575Seric # if defined(DBM) || defined(NEWDB) 5694157Seric if (init) 5704157Seric { 57150575Seric DBT key, content; 5724157Seric 57350575Seric key.size = lhssize; 57450575Seric key.data = al.q_user; 57550575Seric content.size = rhssize; 57650575Seric content.data = rhs; 577*51756Seric # ifdef NEWDB 57851171Sbostic if (dbp->put(dbp, &key, &content, 0) != 0) 57950576Seric syserr("readaliases: db put (%s)", al.q_user); 580*51756Seric # else 581*51756Seric store(key, content); 58250575Seric # endif 5834157Seric } 5844157Seric else 5854157Seric # endif DBM 5864157Seric { 5874157Seric s = stab(al.q_user, ST_ALIAS, ST_ENTER); 5884157Seric s->s_alias = newstr(rhs); 5894157Seric } 5904322Seric 5914322Seric /* statistics */ 5924322Seric naliases++; 5934322Seric bytes += lhssize + rhssize; 5944322Seric if (rhssize > longest) 5954322Seric longest = rhssize; 5961515Seric } 59719784Seric 59850575Seric # if defined(DBM) || defined(NEWDB) 59919784Seric if (init) 60019784Seric { 60119784Seric /* add the distinquished alias "@" */ 60250575Seric DBT key; 60319784Seric 60450575Seric key.size = 2; 60550575Seric key.data = "@"; 60650575Seric # ifdef NEWDB 60750576Seric if (dbp->sync(dbp) != 0 || 60851171Sbostic dbp->put(dbp, &key, &key, 0) != 0 || 60950576Seric dbp->close(dbp) != 0) 61050576Seric syserr("readaliases: db close failure"); 61150575Seric # else 61219784Seric store(key, key); 61350575Seric # endif 61419784Seric 61519784Seric /* restore the old signal */ 61619784Seric (void) signal(SIGINT, oldsigint); 61719784Seric } 61819784Seric # endif DBM 61919784Seric 62019784Seric /* closing the alias file drops the lock */ 6214098Seric (void) fclose(af); 6226898Seric CurEnv->e_to = NULL; 6239368Seric FileName = NULL; 6247051Seric message(Arpa_Info, "%d aliases, longest %d bytes, %d bytes total", 6254322Seric naliases, longest, bytes); 62624944Seric # ifdef LOG 62724944Seric if (LogLevel >= 8) 62824944Seric syslog(LOG_INFO, "%d aliases, longest %d bytes, %d bytes total", 62924944Seric naliases, longest, bytes); 63024944Seric # endif LOG 631292Seric } 632292Seric /* 633292Seric ** FORWARD -- Try to forward mail 634292Seric ** 635292Seric ** This is similar but not identical to aliasing. 636292Seric ** 637292Seric ** Parameters: 6384314Seric ** user -- the name of the user who's mail we would like 6394314Seric ** to forward to. It must have been verified -- 6404314Seric ** i.e., the q_home field must have been filled 6414314Seric ** in. 6424999Seric ** sendq -- a pointer to the head of the send queue to 6434999Seric ** put this user's aliases in. 644292Seric ** 645292Seric ** Returns: 6464098Seric ** none. 647292Seric ** 648292Seric ** Side Effects: 6493185Seric ** New names are added to send queues. 650292Seric */ 651292Seric 6524999Seric forward(user, sendq) 6532966Seric ADDRESS *user; 6544999Seric ADDRESS **sendq; 655292Seric { 6564078Seric char buf[60]; 6574536Seric extern bool safefile(); 6584069Seric 6597671Seric if (tTd(27, 1)) 6604098Seric printf("forward(%s)\n", user->q_paddr); 6614098Seric 6624594Seric if (user->q_mailer != LocalMailer || bitset(QBADADDR, user->q_flags)) 6634098Seric return; 6644314Seric if (user->q_home == NULL) 6654314Seric syserr("forward: no home"); 6664069Seric 6674069Seric /* good address -- look for .forward file in home */ 6689368Seric define('z', user->q_home, CurEnv); 66916154Seric expand("\001z/.forward", buf, &buf[sizeof buf - 1], CurEnv); 6704536Seric if (!safefile(buf, user->q_uid, S_IREAD)) 6714098Seric return; 6724069Seric 6734069Seric /* we do have an address to forward to -- do it */ 6744999Seric include(buf, "forwarding", user, sendq); 675292Seric } 676