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> 16*51937Seric # ifdef LOCKF 17*51937Seric # include <fcntl.h> 18*51937Seric # endif 1950577Seric 2050577Seric # ifdef NEWDB 2150577Seric # include <db.h> 2250577Seric # endif 2350577Seric 2433728Sbostic #ifndef lint 2551756Seric #ifdef NEWDB 26*51937Seric static char sccsid[] = "@(#)alias.c 5.31 (Berkeley) 12/14/91 (with NEWDB)"; 2751756Seric #else 2833728Sbostic #ifdef DBM 29*51937Seric static char sccsid[] = "@(#)alias.c 5.31 (Berkeley) 12/14/91 (with DBM)"; 3033728Sbostic #else 31*51937Seric static char sccsid[] = "@(#)alias.c 5.31 (Berkeley) 12/14/91 (without DBM)"; 3233728Sbostic #endif 3350575Seric #endif 3433728Sbostic #endif /* not lint */ 3551756Seric /* 36292Seric ** ALIAS -- Compute aliases. 37292Seric ** 389368Seric ** Scans the alias file for an alias for the given address. 399368Seric ** If found, it arranges to deliver to the alias list instead. 409368Seric ** Uses libdbm database if -DDBM. 41292Seric ** 42292Seric ** Parameters: 434097Seric ** a -- address to alias. 444999Seric ** sendq -- a pointer to the head of the send queue 454999Seric ** to put the aliases in. 46292Seric ** 47292Seric ** Returns: 48292Seric ** none 49292Seric ** 50292Seric ** Side Effects: 513185Seric ** Aliases found are expanded. 52292Seric ** 53292Seric ** Notes: 54292Seric ** If NoAlias (the "-n" flag) is set, no aliasing is 55292Seric ** done. 56292Seric ** 57292Seric ** Deficiencies: 58292Seric ** It should complain about names that are aliased to 59292Seric ** nothing. 60292Seric */ 61292Seric 62292Seric 631503Smark #ifdef DBM 6451756Seric #ifndef NEWDB 652966Seric typedef struct 662966Seric { 6750575Seric char *data; 6850575Seric int size; 6950575Seric } DBT; 7051756Seric #endif 7150575Seric extern DBT fetch(); 7250575Seric #endif /* DBM */ 73292Seric 7450575Seric #ifdef NEWDB 7550575Seric static DB *AliasDBptr; 7650575Seric #endif 7750575Seric 784999Seric alias(a, sendq) 794097Seric register ADDRESS *a; 804999Seric ADDRESS **sendq; 81292Seric { 824081Seric register char *p; 835701Seric extern char *aliaslookup(); 84292Seric 857671Seric if (tTd(27, 1)) 864098Seric printf("alias(%s)\n", a->q_paddr); 87292Seric 884098Seric /* don't realias already aliased names */ 894098Seric if (bitset(QDONTSEND, a->q_flags)) 904098Seric return; 914098Seric 926898Seric CurEnv->e_to = a->q_paddr; 934098Seric 944314Seric /* 954314Seric ** Look up this name 964314Seric */ 974314Seric 9824944Seric if (NoAlias) 9924944Seric p = NULL; 10024944Seric else 10124944Seric p = aliaslookup(a->q_user); 1024098Seric if (p == NULL) 1034098Seric return; 104292Seric 105292Seric /* 1064098Seric ** Match on Alias. 1074098Seric ** Deliver to the target list. 1081515Seric */ 1091515Seric 1107671Seric if (tTd(27, 1)) 1114098Seric printf("%s (%s, %s) aliased to %s\n", 1124098Seric a->q_paddr, a->q_host, a->q_user, p); 1137051Seric message(Arpa_Info, "aliased to %s", p); 1144098Seric AliasLevel++; 1159614Seric sendtolist(p, a, sendq); 1164098Seric AliasLevel--; 1174098Seric } 1184098Seric /* 1195701Seric ** ALIASLOOKUP -- look up a name in the alias file. 1205701Seric ** 1215701Seric ** Parameters: 1225701Seric ** name -- the name to look up. 1235701Seric ** 1245701Seric ** Returns: 1255701Seric ** the value of name. 1265701Seric ** NULL if unknown. 1275701Seric ** 1285701Seric ** Side Effects: 1295701Seric ** none. 1305701Seric ** 1315701Seric ** Warnings: 1325701Seric ** The return value will be trashed across calls. 1335701Seric */ 1345701Seric 1355701Seric char * 1365701Seric aliaslookup(name) 1375701Seric char *name; 1385701Seric { 13950575Seric # if defined(NEWDB) || defined(DBM) 14050575Seric DBT rhs, lhs; 14150575Seric int s; 1425701Seric 1435701Seric /* create a key for fetch */ 14450575Seric lhs.data = name; 14550575Seric lhs.size = strlen(name) + 1; 14650575Seric # ifdef NEWDB 14751756Seric if (AliasDBptr != NULL) 14851756Seric { 14951756Seric s = AliasDBptr->get(AliasDBptr, &lhs, &rhs, 0); 15051756Seric if (s == 0) 15151756Seric return (rhs.data); 15251756Seric } 15351756Seric # ifdef DBM 15451756Seric else 15551756Seric { 15651756Seric rhs = fetch(lhs); 15751756Seric return (rhs.data); 15851756Seric } 15951756Seric # endif 16050575Seric # else 1615701Seric rhs = fetch(lhs); 16251756Seric return (rhs.data); 16350575Seric # endif 16451756Seric # else /* neither NEWDB nor DBM */ 1655701Seric register STAB *s; 1665701Seric 1675701Seric s = stab(name, ST_ALIAS, ST_FIND); 16851756Seric if (s != NULL) 16951756Seric return (s->s_alias); 17051756Seric # endif 17151756Seric return (NULL); 1725701Seric } 1735701Seric /* 1744098Seric ** INITALIASES -- initialize for aliasing 1754098Seric ** 1764098Seric ** Very different depending on whether we are running DBM or not. 1774098Seric ** 1784098Seric ** Parameters: 1794098Seric ** aliasfile -- location of aliases. 1804157Seric ** init -- if set and if DBM, initialize the DBM files. 1814098Seric ** 1824098Seric ** Returns: 1834098Seric ** none. 1844098Seric ** 1854098Seric ** Side Effects: 1864098Seric ** initializes aliases: 1874098Seric ** if DBM: opens the database. 1884098Seric ** if ~DBM: reads the aliases into the symbol table. 1894098Seric */ 1904098Seric 19140559Sbostic # define DBMMODE 0644 1924157Seric 1934157Seric initaliases(aliasfile, init) 1944098Seric char *aliasfile; 1954157Seric bool init; 1964098Seric { 19750575Seric #if defined(DBM) || defined(NEWDB) 1988437Seric int atcnt; 19925522Seric time_t modtime; 20025522Seric bool automatic = FALSE; 2014322Seric char buf[MAXNAME]; 20250575Seric #endif 2039368Seric struct stat stb; 20427176Seric static bool initialized = FALSE; 20546928Sbostic static int readaliases(); 2064322Seric 20727176Seric if (initialized) 20827176Seric return; 20927176Seric initialized = TRUE; 21027176Seric 21117984Seric if (aliasfile == NULL || stat(aliasfile, &stb) < 0) 2128437Seric { 21325522Seric if (aliasfile != NULL && init) 21425522Seric syserr("Cannot open %s", aliasfile); 2158437Seric NoAlias = TRUE; 21611937Seric errno = 0; 2178437Seric return; 2188437Seric } 2198437Seric 22050575Seric # if defined(DBM) || defined(NEWDB) 2214322Seric /* 2228437Seric ** Check to see that the alias file is complete. 2238437Seric ** If not, we will assume that someone died, and it is up 2248437Seric ** to us to rebuild it. 2258437Seric */ 2268437Seric 22725689Seric if (!init) 22850575Seric { 22950575Seric # ifdef NEWDB 23050575Seric (void) strcpy(buf, aliasfile); 23150575Seric (void) strcat(buf, ".db"); 23251171Sbostic AliasDBptr = dbopen(buf, O_RDONLY, DBMMODE, DB_HASH, NULL); 23350576Seric if (AliasDBptr == NULL) 23450576Seric { 23551756Seric # ifdef DBM 23651756Seric dbminit(aliasfile); 23751756Seric # else 23850576Seric syserr("initaliases: cannot open %s", buf); 23950576Seric NoAlias = TRUE; 24050576Seric return; 24151756Seric # endif 24250576Seric } 24350575Seric # else 24425689Seric dbminit(aliasfile); 24550575Seric # endif 24650575Seric } 24717471Seric atcnt = SafeAlias * 2; 24817471Seric if (atcnt > 0) 24917471Seric { 25017471Seric while (!init && atcnt-- >= 0 && aliaslookup("@") == NULL) 25125689Seric { 25225689Seric /* 25325689Seric ** Reinitialize alias file in case the new 25425689Seric ** one is mv'ed in instead of cp'ed in. 25525689Seric ** 25625689Seric ** Only works with new DBM -- old one will 25725689Seric ** just consume file descriptors forever. 25825689Seric ** If you have a dbmclose() it can be 25925689Seric ** added before the sleep(30). 26025689Seric */ 26125689Seric 26250575Seric # ifdef NEWDB 26351756Seric if (AliasDBptr != NULL) 26451756Seric AliasDBptr->close(AliasDBptr); 26550575Seric # endif 26650575Seric 26717471Seric sleep(30); 26850575Seric # ifdef NEWDB 26950575Seric (void) strcpy(buf, aliasfile); 27050575Seric (void) strcat(buf, ".db"); 27151171Sbostic AliasDBptr = 27251171Sbostic dbopen(buf, O_RDONLY, DBMMODE, DB_HASH, NULL); 27350576Seric if (AliasDBptr == NULL) 27450576Seric { 27551756Seric # ifdef NDBM 27651756Seric dbminit(aliasfile); 27751756Seric # else 27850576Seric syserr("initaliases: cannot open %s", buf); 27950576Seric NoAlias = TRUE; 28050576Seric return; 28151756Seric # endif 28250576Seric } 28350575Seric # else 28425689Seric # ifdef NDBM 28525689Seric dbminit(aliasfile); 28650575Seric # endif 28750575Seric # endif 28825689Seric } 28917471Seric } 29017471Seric else 29117471Seric atcnt = 1; 2928437Seric 2938437Seric /* 2944322Seric ** See if the DBM version of the file is out of date with 2954322Seric ** the text version. If so, go into 'init' mode automatically. 29640559Sbostic ** This only happens if our effective userid owns the DBM. 29740559Sbostic ** Note the unpalatable hack to see if the stat succeeded. 2984322Seric */ 2994322Seric 3004322Seric modtime = stb.st_mtime; 3014322Seric (void) strcpy(buf, aliasfile); 30250575Seric # ifdef NEWDB 30350575Seric (void) strcat(buf, ".db"); 30450575Seric # else 3054322Seric (void) strcat(buf, ".pag"); 30650575Seric # endif 3074322Seric stb.st_ino = 0; 30819039Seric if (!init && (stat(buf, &stb) < 0 || stb.st_mtime < modtime || atcnt < 0)) 3094322Seric { 31011937Seric errno = 0; 31140559Sbostic if (AutoRebuild && stb.st_ino != 0 && stb.st_uid == geteuid()) 3124322Seric { 3134322Seric init = TRUE; 31425522Seric automatic = TRUE; 3157051Seric message(Arpa_Info, "rebuilding alias database"); 31624944Seric #ifdef LOG 31724944Seric if (LogLevel >= 7) 31824944Seric syslog(LOG_INFO, "rebuilding alias database"); 31924944Seric #endif LOG 3204322Seric } 3214322Seric else 3224322Seric { 32319039Seric #ifdef LOG 32424944Seric if (LogLevel >= 7) 32524944Seric syslog(LOG_INFO, "alias database out of date"); 32619039Seric #endif LOG 3274322Seric message(Arpa_Info, "Warning: alias database out of date"); 3284322Seric } 3294322Seric } 3304322Seric 3314322Seric 3324322Seric /* 3338437Seric ** If necessary, load the DBM file. 3344322Seric ** If running without DBM, load the symbol table. 3354322Seric */ 3364322Seric 3374157Seric if (init) 3388437Seric { 33925522Seric #ifdef LOG 34025522Seric if (LogLevel >= 6) 34125522Seric { 34225522Seric extern char *username(); 34325522Seric 34425522Seric syslog(LOG_NOTICE, "alias database %srebuilt by %s", 34525522Seric automatic ? "auto" : "", username()); 34625522Seric } 34725522Seric #endif LOG 3484157Seric readaliases(aliasfile, TRUE); 3498437Seric } 3504098Seric # else DBM 3514157Seric readaliases(aliasfile, init); 3524157Seric # endif DBM 3534157Seric } 3544157Seric /* 3554157Seric ** READALIASES -- read and process the alias file. 3564157Seric ** 3574157Seric ** This routine implements the part of initaliases that occurs 3584157Seric ** when we are not going to use the DBM stuff. 3594157Seric ** 3604157Seric ** Parameters: 3614157Seric ** aliasfile -- the pathname of the alias file master. 3624157Seric ** init -- if set, initialize the DBM stuff. 3634157Seric ** 3644157Seric ** Returns: 3654157Seric ** none. 3664157Seric ** 3674157Seric ** Side Effects: 3684157Seric ** Reads aliasfile into the symbol table. 3694157Seric ** Optionally, builds the .dir & .pag files. 3704157Seric */ 3714157Seric 3724157Seric static 3734157Seric readaliases(aliasfile, init) 3744157Seric char *aliasfile; 3754157Seric bool init; 3764157Seric { 3774098Seric register char *p; 3784098Seric char *rhs; 3794098Seric bool skipping; 3809368Seric int naliases, bytes, longest; 3819368Seric FILE *af; 38240970Sbostic void (*oldsigint)(); 3834098Seric ADDRESS al, bl; 3844106Seric register STAB *s; 38550575Seric # ifdef NEWDB 38650575Seric DB *dbp; 38750575Seric # endif 388*51937Seric # ifdef LOCKF 389*51937Seric struct flock fld; 390*51937Seric # endif 3919368Seric char line[BUFSIZ]; 3924098Seric 393*51937Seric if ((af = fopen(aliasfile, "r+")) == NULL) 3941515Seric { 3957671Seric if (tTd(27, 1)) 3964106Seric printf("Can't open %s\n", aliasfile); 3974098Seric errno = 0; 3984098Seric NoAlias++; 3994098Seric return; 4004098Seric } 4014314Seric 40250575Seric # if defined(DBM) || defined(NEWDB) 40319784Seric /* see if someone else is rebuilding the alias file already */ 40451835Seric # ifdef LOCKF 405*51937Seric fld.l_type = F_WRLCK; 406*51937Seric fld.l_whence = fld.l_start = fld.l_len = 0; 407*51937Seric if (fcntl(fileno(af), F_SETLK, &fld) < 0) 40851835Seric # else 40919784Seric if (flock(fileno(af), LOCK_EX | LOCK_NB) < 0 && errno == EWOULDBLOCK) 41051835Seric # endif 41119784Seric { 41219784Seric /* yes, they are -- wait until done and then return */ 41319784Seric message(Arpa_Info, "Alias file is already being rebuilt"); 41419784Seric if (OpMode != MD_INITALIAS) 41519784Seric { 41619784Seric /* wait for other rebuild to complete */ 41751835Seric # ifdef LOCKF 418*51937Seric (void) fcntl(fileno(af), F_SETLKW, &fld); 41951835Seric # else 42019784Seric (void) flock(fileno(af), LOCK_EX); 42151835Seric # endif 42219784Seric } 42323108Seric (void) fclose(af); 42419784Seric errno = 0; 42519784Seric return; 42619784Seric } 42719784Seric # endif DBM 42819784Seric 4294314Seric /* 43019784Seric ** If initializing, create the new DBM files. 43119784Seric */ 43219784Seric 43319784Seric if (init) 43419784Seric { 43519784Seric oldsigint = signal(SIGINT, SIG_IGN); 43651756Seric # ifdef NEWDB 43751756Seric (void) strcpy(line, aliasfile); 43851756Seric (void) strcat(line, ".db"); 43951756Seric dbp = dbopen(line, 44051756Seric O_RDWR|O_CREAT|O_TRUNC, DBMMODE, DB_HASH, NULL); 44151756Seric if (dbp == NULL) 44251756Seric { 44351756Seric syserr("readaliases: cannot create %s", line); 44451756Seric (void) signal(SIGINT, oldsigint); 44551756Seric return; 44651756Seric } 44751756Seric # else 44850575Seric # ifdef DBM 44919784Seric (void) strcpy(line, aliasfile); 45019784Seric (void) strcat(line, ".dir"); 45119784Seric if (close(creat(line, DBMMODE)) < 0) 45219784Seric { 45319784Seric syserr("cannot make %s", line); 45419784Seric (void) signal(SIGINT, oldsigint); 45519784Seric return; 45619784Seric } 45719784Seric (void) strcpy(line, aliasfile); 45819784Seric (void) strcat(line, ".pag"); 45919784Seric if (close(creat(line, DBMMODE)) < 0) 46019784Seric { 46119784Seric syserr("cannot make %s", line); 46219784Seric (void) signal(SIGINT, oldsigint); 46319784Seric return; 46419784Seric } 46526501Seric dbminit(aliasfile); 46650575Seric # endif 46750575Seric # endif 46819784Seric } 46919784Seric 47019784Seric /* 4714314Seric ** Read and interpret lines 4724314Seric */ 4734314Seric 4749368Seric FileName = aliasfile; 4759368Seric LineNumber = 0; 4764322Seric naliases = bytes = longest = 0; 4774098Seric skipping = FALSE; 4784098Seric while (fgets(line, sizeof (line), af) != NULL) 4794098Seric { 4804322Seric int lhssize, rhssize; 4814322Seric 4829368Seric LineNumber++; 48325278Seric p = index(line, '\n'); 48425278Seric if (p != NULL) 48525278Seric *p = '\0'; 4864098Seric switch (line[0]) 4874098Seric { 4884098Seric case '#': 4894098Seric case '\0': 4904098Seric skipping = FALSE; 4914098Seric continue; 4924065Seric 4934098Seric case ' ': 4944098Seric case '\t': 4954098Seric if (!skipping) 4969368Seric syserr("Non-continuation line starts with space"); 4974098Seric skipping = TRUE; 4984097Seric continue; 4994098Seric } 5004098Seric skipping = FALSE; 5011874Seric 5024314Seric /* 5034314Seric ** Process the LHS 5044314Seric ** Find the final colon, and parse the address. 50516898Seric ** It should resolve to a local name -- this will 50616898Seric ** be checked later (we want to optionally do 50716898Seric ** parsing of the RHS first to maximize error 50816898Seric ** detection). 5094314Seric */ 5104314Seric 5114098Seric for (p = line; *p != '\0' && *p != ':' && *p != '\n'; p++) 5124097Seric continue; 51316898Seric if (*p++ != ':') 5144098Seric { 5159368Seric syserr("missing colon"); 5164097Seric continue; 5174098Seric } 51816898Seric if (parseaddr(line, &al, 1, ':') == NULL) 5194098Seric { 52016898Seric syserr("illegal alias name"); 52116898Seric continue; 5224098Seric } 52316898Seric loweraddr(&al); 5244314Seric 5254314Seric /* 5264314Seric ** Process the RHS. 5274314Seric ** 'al' is the internal form of the LHS address. 5284314Seric ** 'p' points to the text of the RHS. 5294314Seric */ 5304314Seric 5314098Seric rhs = p; 5324098Seric for (;;) 5334098Seric { 5344098Seric register char c; 5351515Seric 53625821Seric if (init && CheckAliases) 5374098Seric { 5384157Seric /* do parsing & compression of addresses */ 53925278Seric while (*p != '\0') 5404098Seric { 54125278Seric extern char *DelimChar; 54225278Seric 54325278Seric while (isspace(*p) || *p == ',') 5444157Seric p++; 54525278Seric if (*p == '\0') 54625278Seric break; 54725278Seric if (parseaddr(p, &bl, -1, ',') == NULL) 54825278Seric usrerr("%s... bad address", p); 54925278Seric p = DelimChar; 5504098Seric } 5514098Seric } 5524157Seric else 55315769Seric { 55416898Seric p = &p[strlen(p)]; 55516898Seric if (p[-1] == '\n') 55616898Seric *--p = '\0'; 55715769Seric } 5581515Seric 5594098Seric /* see if there should be a continuation line */ 5604106Seric c = fgetc(af); 5614106Seric if (!feof(af)) 5624314Seric (void) ungetc(c, af); 5634106Seric if (c != ' ' && c != '\t') 5644098Seric break; 5654098Seric 5664098Seric /* read continuation line */ 5674098Seric if (fgets(p, sizeof line - (p - line), af) == NULL) 5684098Seric break; 5699368Seric LineNumber++; 5704098Seric } 57116898Seric if (al.q_mailer != LocalMailer) 57216898Seric { 57316898Seric syserr("cannot alias non-local names"); 57416898Seric continue; 57516898Seric } 5764314Seric 5774314Seric /* 5784314Seric ** Insert alias into symbol table or DBM file 5794314Seric */ 5804314Seric 58116898Seric lhssize = strlen(al.q_user) + 1; 5824322Seric rhssize = strlen(rhs) + 1; 5834322Seric 58450575Seric # if defined(DBM) || defined(NEWDB) 5854157Seric if (init) 5864157Seric { 58750575Seric DBT key, content; 5884157Seric 58950575Seric key.size = lhssize; 59050575Seric key.data = al.q_user; 59150575Seric content.size = rhssize; 59250575Seric content.data = rhs; 59351756Seric # ifdef NEWDB 59451171Sbostic if (dbp->put(dbp, &key, &content, 0) != 0) 59550576Seric syserr("readaliases: db put (%s)", al.q_user); 59651756Seric # else 59751756Seric store(key, content); 59850575Seric # endif 5994157Seric } 6004157Seric else 6014157Seric # endif DBM 6024157Seric { 6034157Seric s = stab(al.q_user, ST_ALIAS, ST_ENTER); 6044157Seric s->s_alias = newstr(rhs); 6054157Seric } 6064322Seric 6074322Seric /* statistics */ 6084322Seric naliases++; 6094322Seric bytes += lhssize + rhssize; 6104322Seric if (rhssize > longest) 6114322Seric longest = rhssize; 6121515Seric } 61319784Seric 61450575Seric # if defined(DBM) || defined(NEWDB) 61519784Seric if (init) 61619784Seric { 61719784Seric /* add the distinquished alias "@" */ 61850575Seric DBT key; 61919784Seric 62050575Seric key.size = 2; 62150575Seric key.data = "@"; 62250575Seric # ifdef NEWDB 62350576Seric if (dbp->sync(dbp) != 0 || 62451171Sbostic dbp->put(dbp, &key, &key, 0) != 0 || 62550576Seric dbp->close(dbp) != 0) 62650576Seric syserr("readaliases: db close failure"); 62750575Seric # else 62819784Seric store(key, key); 62950575Seric # endif 63019784Seric 63119784Seric /* restore the old signal */ 63219784Seric (void) signal(SIGINT, oldsigint); 63319784Seric } 63419784Seric # endif DBM 63519784Seric 63619784Seric /* closing the alias file drops the lock */ 6374098Seric (void) fclose(af); 6386898Seric CurEnv->e_to = NULL; 6399368Seric FileName = NULL; 6407051Seric message(Arpa_Info, "%d aliases, longest %d bytes, %d bytes total", 6414322Seric naliases, longest, bytes); 64224944Seric # ifdef LOG 64324944Seric if (LogLevel >= 8) 64424944Seric syslog(LOG_INFO, "%d aliases, longest %d bytes, %d bytes total", 64524944Seric naliases, longest, bytes); 64624944Seric # endif LOG 647292Seric } 648292Seric /* 649292Seric ** FORWARD -- Try to forward mail 650292Seric ** 651292Seric ** This is similar but not identical to aliasing. 652292Seric ** 653292Seric ** Parameters: 6544314Seric ** user -- the name of the user who's mail we would like 6554314Seric ** to forward to. It must have been verified -- 6564314Seric ** i.e., the q_home field must have been filled 6574314Seric ** in. 6584999Seric ** sendq -- a pointer to the head of the send queue to 6594999Seric ** put this user's aliases in. 660292Seric ** 661292Seric ** Returns: 6624098Seric ** none. 663292Seric ** 664292Seric ** Side Effects: 6653185Seric ** New names are added to send queues. 666292Seric */ 667292Seric 6684999Seric forward(user, sendq) 6692966Seric ADDRESS *user; 6704999Seric ADDRESS **sendq; 671292Seric { 6724078Seric char buf[60]; 6734536Seric extern bool safefile(); 6744069Seric 6757671Seric if (tTd(27, 1)) 6764098Seric printf("forward(%s)\n", user->q_paddr); 6774098Seric 6784594Seric if (user->q_mailer != LocalMailer || bitset(QBADADDR, user->q_flags)) 6794098Seric return; 6804314Seric if (user->q_home == NULL) 6814314Seric syserr("forward: no home"); 6824069Seric 6834069Seric /* good address -- look for .forward file in home */ 6849368Seric define('z', user->q_home, CurEnv); 68516154Seric expand("\001z/.forward", buf, &buf[sizeof buf - 1], CurEnv); 6864536Seric if (!safefile(buf, user->q_uid, S_IREAD)) 6874098Seric return; 6884069Seric 6894069Seric /* we do have an address to forward to -- do it */ 6904999Seric include(buf, "forwarding", user, sendq); 691292Seric } 692