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 2233728Sbostic #ifdef DBM 23*51171Sbostic static char sccsid[] = "@(#)alias.c 5.26 (Berkeley) 09/23/91 (with DBM)"; 2433728Sbostic #else 2550575Seric #ifdef NEWDB 26*51171Sbostic static char sccsid[] = "@(#)alias.c 5.26 (Berkeley) 09/23/91 (with NEWDB)"; 2750575Seric #else 28*51171Sbostic static char sccsid[] = "@(#)alias.c 5.26 (Berkeley) 09/23/91 (without DBM)"; 2933728Sbostic #endif 3050575Seric #endif 3133728Sbostic #endif /* not lint */ 3233728Sbostic 3350575Seric # ifdef DBM 3450575Seric # ifdef NEWDB 3550577Seric ERROR ERROR ERROR: must choose one of DBM or NEWDB compilation flags 3650575Seric # endif 3750575Seric # endif 3850575Seric 39292Seric /* 40292Seric ** ALIAS -- Compute aliases. 41292Seric ** 429368Seric ** Scans the alias file for an alias for the given address. 439368Seric ** If found, it arranges to deliver to the alias list instead. 449368Seric ** Uses libdbm database if -DDBM. 45292Seric ** 46292Seric ** Parameters: 474097Seric ** a -- address to alias. 484999Seric ** sendq -- a pointer to the head of the send queue 494999Seric ** to put the aliases in. 50292Seric ** 51292Seric ** Returns: 52292Seric ** none 53292Seric ** 54292Seric ** Side Effects: 553185Seric ** Aliases found are expanded. 56292Seric ** 57292Seric ** Notes: 58292Seric ** If NoAlias (the "-n" flag) is set, no aliasing is 59292Seric ** done. 60292Seric ** 61292Seric ** Deficiencies: 62292Seric ** It should complain about names that are aliased to 63292Seric ** nothing. 64292Seric */ 65292Seric 66292Seric 671503Smark #ifdef DBM 682966Seric typedef struct 692966Seric { 7050575Seric char *data; 7150575Seric int size; 7250575Seric } DBT; 7350575Seric extern DBT fetch(); 7450575Seric #endif /* DBM */ 75292Seric 7650575Seric #ifdef NEWDB 7750575Seric static DB *AliasDBptr; 7850575Seric #endif 7950575Seric 804999Seric alias(a, sendq) 814097Seric register ADDRESS *a; 824999Seric ADDRESS **sendq; 83292Seric { 844081Seric register char *p; 855701Seric extern char *aliaslookup(); 86292Seric 877671Seric if (tTd(27, 1)) 884098Seric printf("alias(%s)\n", a->q_paddr); 89292Seric 904098Seric /* don't realias already aliased names */ 914098Seric if (bitset(QDONTSEND, a->q_flags)) 924098Seric return; 934098Seric 946898Seric CurEnv->e_to = a->q_paddr; 954098Seric 964314Seric /* 974314Seric ** Look up this name 984314Seric */ 994314Seric 10024944Seric if (NoAlias) 10124944Seric p = NULL; 10224944Seric else 10324944Seric p = aliaslookup(a->q_user); 1044098Seric if (p == NULL) 1054098Seric return; 106292Seric 107292Seric /* 1084098Seric ** Match on Alias. 1094098Seric ** Deliver to the target list. 1101515Seric */ 1111515Seric 1127671Seric if (tTd(27, 1)) 1134098Seric printf("%s (%s, %s) aliased to %s\n", 1144098Seric a->q_paddr, a->q_host, a->q_user, p); 1157051Seric message(Arpa_Info, "aliased to %s", p); 1164098Seric AliasLevel++; 1179614Seric sendtolist(p, a, sendq); 1184098Seric AliasLevel--; 1194098Seric } 1204098Seric /* 1215701Seric ** ALIASLOOKUP -- look up a name in the alias file. 1225701Seric ** 1235701Seric ** Parameters: 1245701Seric ** name -- the name to look up. 1255701Seric ** 1265701Seric ** Returns: 1275701Seric ** the value of name. 1285701Seric ** NULL if unknown. 1295701Seric ** 1305701Seric ** Side Effects: 1315701Seric ** none. 1325701Seric ** 1335701Seric ** Warnings: 1345701Seric ** The return value will be trashed across calls. 1355701Seric */ 1365701Seric 1375701Seric char * 1385701Seric aliaslookup(name) 1395701Seric char *name; 1405701Seric { 14150575Seric # if defined(NEWDB) || defined(DBM) 14250575Seric DBT rhs, lhs; 14350575Seric int s; 1445701Seric 1455701Seric /* create a key for fetch */ 14650575Seric lhs.data = name; 14750575Seric lhs.size = strlen(name) + 1; 14850575Seric # ifdef NEWDB 14950575Seric s = AliasDBptr->get(AliasDBptr, &lhs, &rhs, 0); 15050575Seric if (s != 0) 15150575Seric return (NULL); 15250575Seric # else 1535701Seric rhs = fetch(lhs); 15450575Seric # endif 15550575Seric return (rhs.data); 1565701Seric # else DBM 1575701Seric register STAB *s; 1585701Seric 1595701Seric s = stab(name, ST_ALIAS, ST_FIND); 1605701Seric if (s == NULL) 1615701Seric return (NULL); 1625701Seric return (s->s_alias); 1635701Seric # endif DBM 1645701Seric } 1655701Seric /* 1664098Seric ** INITALIASES -- initialize for aliasing 1674098Seric ** 1684098Seric ** Very different depending on whether we are running DBM or not. 1694098Seric ** 1704098Seric ** Parameters: 1714098Seric ** aliasfile -- location of aliases. 1724157Seric ** init -- if set and if DBM, initialize the DBM files. 1734098Seric ** 1744098Seric ** Returns: 1754098Seric ** none. 1764098Seric ** 1774098Seric ** Side Effects: 1784098Seric ** initializes aliases: 1794098Seric ** if DBM: opens the database. 1804098Seric ** if ~DBM: reads the aliases into the symbol table. 1814098Seric */ 1824098Seric 18340559Sbostic # define DBMMODE 0644 1844157Seric 1854157Seric initaliases(aliasfile, init) 1864098Seric char *aliasfile; 1874157Seric bool init; 1884098Seric { 18950575Seric #if defined(DBM) || defined(NEWDB) 1908437Seric int atcnt; 19125522Seric time_t modtime; 19225522Seric bool automatic = FALSE; 1934322Seric char buf[MAXNAME]; 19450575Seric #endif 1959368Seric struct stat stb; 19627176Seric static bool initialized = FALSE; 19746928Sbostic static int readaliases(); 1984322Seric 19927176Seric if (initialized) 20027176Seric return; 20127176Seric initialized = TRUE; 20227176Seric 20317984Seric if (aliasfile == NULL || stat(aliasfile, &stb) < 0) 2048437Seric { 20525522Seric if (aliasfile != NULL && init) 20625522Seric syserr("Cannot open %s", aliasfile); 2078437Seric NoAlias = TRUE; 20811937Seric errno = 0; 2098437Seric return; 2108437Seric } 2118437Seric 21250575Seric # if defined(DBM) || defined(NEWDB) 2134322Seric /* 2148437Seric ** Check to see that the alias file is complete. 2158437Seric ** If not, we will assume that someone died, and it is up 2168437Seric ** to us to rebuild it. 2178437Seric */ 2188437Seric 21925689Seric if (!init) 22050575Seric { 22150575Seric # ifdef NEWDB 22250575Seric (void) strcpy(buf, aliasfile); 22350575Seric (void) strcat(buf, ".db"); 224*51171Sbostic AliasDBptr = dbopen(buf, O_RDONLY, DBMMODE, DB_HASH, NULL); 22550576Seric if (AliasDBptr == NULL) 22650576Seric { 22750576Seric syserr("initaliases: cannot open %s", buf); 22850576Seric NoAlias = TRUE; 22950576Seric return; 23050576Seric } 23150575Seric # else 23225689Seric dbminit(aliasfile); 23350575Seric # endif 23450575Seric } 23517471Seric atcnt = SafeAlias * 2; 23617471Seric if (atcnt > 0) 23717471Seric { 23817471Seric while (!init && atcnt-- >= 0 && aliaslookup("@") == NULL) 23925689Seric { 24025689Seric /* 24125689Seric ** Reinitialize alias file in case the new 24225689Seric ** one is mv'ed in instead of cp'ed in. 24325689Seric ** 24425689Seric ** Only works with new DBM -- old one will 24525689Seric ** just consume file descriptors forever. 24625689Seric ** If you have a dbmclose() it can be 24725689Seric ** added before the sleep(30). 24825689Seric */ 24925689Seric 25050575Seric # ifdef NEWDB 25150575Seric AliasDBptr->close(AliasDBptr); 25250575Seric # endif 25350575Seric 25417471Seric sleep(30); 25550575Seric # ifdef NEWDB 25650575Seric (void) strcpy(buf, aliasfile); 25750575Seric (void) strcat(buf, ".db"); 258*51171Sbostic AliasDBptr = 259*51171Sbostic dbopen(buf, O_RDONLY, DBMMODE, DB_HASH, NULL); 26050576Seric if (AliasDBptr == NULL) 26150576Seric { 26250576Seric syserr("initaliases: cannot open %s", buf); 26350576Seric NoAlias = TRUE; 26450576Seric return; 26550576Seric } 26650575Seric # else 26725689Seric # ifdef NDBM 26825689Seric dbminit(aliasfile); 26950575Seric # endif 27050575Seric # endif 27125689Seric } 27217471Seric } 27317471Seric else 27417471Seric atcnt = 1; 2758437Seric 2768437Seric /* 2774322Seric ** See if the DBM version of the file is out of date with 2784322Seric ** the text version. If so, go into 'init' mode automatically. 27940559Sbostic ** This only happens if our effective userid owns the DBM. 28040559Sbostic ** Note the unpalatable hack to see if the stat succeeded. 2814322Seric */ 2824322Seric 2834322Seric modtime = stb.st_mtime; 2844322Seric (void) strcpy(buf, aliasfile); 28550575Seric # ifdef NEWDB 28650575Seric (void) strcat(buf, ".db"); 28750575Seric # else 2884322Seric (void) strcat(buf, ".pag"); 28950575Seric # endif 2904322Seric stb.st_ino = 0; 29119039Seric if (!init && (stat(buf, &stb) < 0 || stb.st_mtime < modtime || atcnt < 0)) 2924322Seric { 29311937Seric errno = 0; 29440559Sbostic if (AutoRebuild && stb.st_ino != 0 && stb.st_uid == geteuid()) 2954322Seric { 2964322Seric init = TRUE; 29725522Seric automatic = TRUE; 2987051Seric message(Arpa_Info, "rebuilding alias database"); 29924944Seric #ifdef LOG 30024944Seric if (LogLevel >= 7) 30124944Seric syslog(LOG_INFO, "rebuilding alias database"); 30224944Seric #endif LOG 3034322Seric } 3044322Seric else 3054322Seric { 30619039Seric #ifdef LOG 30724944Seric if (LogLevel >= 7) 30824944Seric syslog(LOG_INFO, "alias database out of date"); 30919039Seric #endif LOG 3104322Seric message(Arpa_Info, "Warning: alias database out of date"); 3114322Seric } 3124322Seric } 3134322Seric 3144322Seric 3154322Seric /* 3168437Seric ** If necessary, load the DBM file. 3174322Seric ** If running without DBM, load the symbol table. 3184322Seric */ 3194322Seric 3204157Seric if (init) 3218437Seric { 32225522Seric #ifdef LOG 32325522Seric if (LogLevel >= 6) 32425522Seric { 32525522Seric extern char *username(); 32625522Seric 32725522Seric syslog(LOG_NOTICE, "alias database %srebuilt by %s", 32825522Seric automatic ? "auto" : "", username()); 32925522Seric } 33025522Seric #endif LOG 3314157Seric readaliases(aliasfile, TRUE); 3328437Seric } 3334098Seric # else DBM 3344157Seric readaliases(aliasfile, init); 3354157Seric # endif DBM 3364157Seric } 3374157Seric /* 3384157Seric ** READALIASES -- read and process the alias file. 3394157Seric ** 3404157Seric ** This routine implements the part of initaliases that occurs 3414157Seric ** when we are not going to use the DBM stuff. 3424157Seric ** 3434157Seric ** Parameters: 3444157Seric ** aliasfile -- the pathname of the alias file master. 3454157Seric ** init -- if set, initialize the DBM stuff. 3464157Seric ** 3474157Seric ** Returns: 3484157Seric ** none. 3494157Seric ** 3504157Seric ** Side Effects: 3514157Seric ** Reads aliasfile into the symbol table. 3524157Seric ** Optionally, builds the .dir & .pag files. 3534157Seric */ 3544157Seric 3554157Seric static 3564157Seric readaliases(aliasfile, init) 3574157Seric char *aliasfile; 3584157Seric bool init; 3594157Seric { 3604098Seric register char *p; 3614098Seric char *rhs; 3624098Seric bool skipping; 3639368Seric int naliases, bytes, longest; 3649368Seric FILE *af; 36540970Sbostic void (*oldsigint)(); 3664098Seric ADDRESS al, bl; 3674106Seric register STAB *s; 36850575Seric # ifdef NEWDB 36950575Seric DB *dbp; 37050575Seric # endif 3719368Seric char line[BUFSIZ]; 3724098Seric 3734098Seric if ((af = fopen(aliasfile, "r")) == NULL) 3741515Seric { 3757671Seric if (tTd(27, 1)) 3764106Seric printf("Can't open %s\n", aliasfile); 3774098Seric errno = 0; 3784098Seric NoAlias++; 3794098Seric return; 3804098Seric } 3814314Seric 38250575Seric # if defined(DBM) || defined(NEWDB) 38319784Seric /* see if someone else is rebuilding the alias file already */ 38419784Seric if (flock(fileno(af), LOCK_EX | LOCK_NB) < 0 && errno == EWOULDBLOCK) 38519784Seric { 38619784Seric /* yes, they are -- wait until done and then return */ 38719784Seric message(Arpa_Info, "Alias file is already being rebuilt"); 38819784Seric if (OpMode != MD_INITALIAS) 38919784Seric { 39019784Seric /* wait for other rebuild to complete */ 39119784Seric (void) flock(fileno(af), LOCK_EX); 39219784Seric } 39323108Seric (void) fclose(af); 39419784Seric errno = 0; 39519784Seric return; 39619784Seric } 39719784Seric # endif DBM 39819784Seric 3994314Seric /* 40019784Seric ** If initializing, create the new DBM files. 40119784Seric */ 40219784Seric 40319784Seric if (init) 40419784Seric { 40519784Seric oldsigint = signal(SIGINT, SIG_IGN); 40650575Seric # ifdef DBM 40719784Seric (void) strcpy(line, aliasfile); 40819784Seric (void) strcat(line, ".dir"); 40919784Seric if (close(creat(line, DBMMODE)) < 0) 41019784Seric { 41119784Seric syserr("cannot make %s", line); 41219784Seric (void) signal(SIGINT, oldsigint); 41319784Seric return; 41419784Seric } 41519784Seric (void) strcpy(line, aliasfile); 41619784Seric (void) strcat(line, ".pag"); 41719784Seric if (close(creat(line, DBMMODE)) < 0) 41819784Seric { 41919784Seric syserr("cannot make %s", line); 42019784Seric (void) signal(SIGINT, oldsigint); 42119784Seric return; 42219784Seric } 42326501Seric dbminit(aliasfile); 42450575Seric # endif 42550575Seric # ifdef NEWDB 42650575Seric (void) strcpy(line, aliasfile); 42750575Seric (void) strcat(line, ".db"); 428*51171Sbostic dbp = dbopen(line, 429*51171Sbostic O_RDWR|O_CREAT|O_TRUNC, DBMMODE, DB_HASH, NULL); 43050576Seric if (dbp == NULL) 43150576Seric { 43250576Seric syserr("readaliases: cannot create %s", line); 43350576Seric (void) signal(SIGINT, oldsigint); 43450576Seric return; 43550576Seric } 43650575Seric # endif 43719784Seric } 43819784Seric 43919784Seric /* 4404314Seric ** Read and interpret lines 4414314Seric */ 4424314Seric 4439368Seric FileName = aliasfile; 4449368Seric LineNumber = 0; 4454322Seric naliases = bytes = longest = 0; 4464098Seric skipping = FALSE; 4474098Seric while (fgets(line, sizeof (line), af) != NULL) 4484098Seric { 4494322Seric int lhssize, rhssize; 4504322Seric 4519368Seric LineNumber++; 45225278Seric p = index(line, '\n'); 45325278Seric if (p != NULL) 45425278Seric *p = '\0'; 4554098Seric switch (line[0]) 4564098Seric { 4574098Seric case '#': 4584098Seric case '\0': 4594098Seric skipping = FALSE; 4604098Seric continue; 4614065Seric 4624098Seric case ' ': 4634098Seric case '\t': 4644098Seric if (!skipping) 4659368Seric syserr("Non-continuation line starts with space"); 4664098Seric skipping = TRUE; 4674097Seric continue; 4684098Seric } 4694098Seric skipping = FALSE; 4701874Seric 4714314Seric /* 4724314Seric ** Process the LHS 4734314Seric ** Find the final colon, and parse the address. 47416898Seric ** It should resolve to a local name -- this will 47516898Seric ** be checked later (we want to optionally do 47616898Seric ** parsing of the RHS first to maximize error 47716898Seric ** detection). 4784314Seric */ 4794314Seric 4804098Seric for (p = line; *p != '\0' && *p != ':' && *p != '\n'; p++) 4814097Seric continue; 48216898Seric if (*p++ != ':') 4834098Seric { 4849368Seric syserr("missing colon"); 4854097Seric continue; 4864098Seric } 48716898Seric if (parseaddr(line, &al, 1, ':') == NULL) 4884098Seric { 48916898Seric syserr("illegal alias name"); 49016898Seric continue; 4914098Seric } 49216898Seric loweraddr(&al); 4934314Seric 4944314Seric /* 4954314Seric ** Process the RHS. 4964314Seric ** 'al' is the internal form of the LHS address. 4974314Seric ** 'p' points to the text of the RHS. 4984314Seric */ 4994314Seric 5004098Seric rhs = p; 5014098Seric for (;;) 5024098Seric { 5034098Seric register char c; 5041515Seric 50525821Seric if (init && CheckAliases) 5064098Seric { 5074157Seric /* do parsing & compression of addresses */ 50825278Seric while (*p != '\0') 5094098Seric { 51025278Seric extern char *DelimChar; 51125278Seric 51225278Seric while (isspace(*p) || *p == ',') 5134157Seric p++; 51425278Seric if (*p == '\0') 51525278Seric break; 51625278Seric if (parseaddr(p, &bl, -1, ',') == NULL) 51725278Seric usrerr("%s... bad address", p); 51825278Seric p = DelimChar; 5194098Seric } 5204098Seric } 5214157Seric else 52215769Seric { 52316898Seric p = &p[strlen(p)]; 52416898Seric if (p[-1] == '\n') 52516898Seric *--p = '\0'; 52615769Seric } 5271515Seric 5284098Seric /* see if there should be a continuation line */ 5294106Seric c = fgetc(af); 5304106Seric if (!feof(af)) 5314314Seric (void) ungetc(c, af); 5324106Seric if (c != ' ' && c != '\t') 5334098Seric break; 5344098Seric 5354098Seric /* read continuation line */ 5364098Seric if (fgets(p, sizeof line - (p - line), af) == NULL) 5374098Seric break; 5389368Seric LineNumber++; 5394098Seric } 54016898Seric if (al.q_mailer != LocalMailer) 54116898Seric { 54216898Seric syserr("cannot alias non-local names"); 54316898Seric continue; 54416898Seric } 5454314Seric 5464314Seric /* 5474314Seric ** Insert alias into symbol table or DBM file 5484314Seric */ 5494314Seric 55016898Seric lhssize = strlen(al.q_user) + 1; 5514322Seric rhssize = strlen(rhs) + 1; 5524322Seric 55350575Seric # if defined(DBM) || defined(NEWDB) 5544157Seric if (init) 5554157Seric { 55650575Seric DBT key, content; 5574157Seric 55850575Seric key.size = lhssize; 55950575Seric key.data = al.q_user; 56050575Seric content.size = rhssize; 56150575Seric content.data = rhs; 56250575Seric # ifdef DBM 5634157Seric store(key, content); 56450575Seric # else 565*51171Sbostic if (dbp->put(dbp, &key, &content, 0) != 0) 56650576Seric syserr("readaliases: db put (%s)", al.q_user); 56750575Seric # endif 5684157Seric } 5694157Seric else 5704157Seric # endif DBM 5714157Seric { 5724157Seric s = stab(al.q_user, ST_ALIAS, ST_ENTER); 5734157Seric s->s_alias = newstr(rhs); 5744157Seric } 5754322Seric 5764322Seric /* statistics */ 5774322Seric naliases++; 5784322Seric bytes += lhssize + rhssize; 5794322Seric if (rhssize > longest) 5804322Seric longest = rhssize; 5811515Seric } 58219784Seric 58350575Seric # if defined(DBM) || defined(NEWDB) 58419784Seric if (init) 58519784Seric { 58619784Seric /* add the distinquished alias "@" */ 58750575Seric DBT key; 58819784Seric 58950575Seric key.size = 2; 59050575Seric key.data = "@"; 59150575Seric # ifdef NEWDB 59250576Seric if (dbp->sync(dbp) != 0 || 593*51171Sbostic dbp->put(dbp, &key, &key, 0) != 0 || 59450576Seric dbp->close(dbp) != 0) 59550576Seric syserr("readaliases: db close failure"); 59650575Seric # else 59719784Seric store(key, key); 59850575Seric # endif 59919784Seric 60019784Seric /* restore the old signal */ 60119784Seric (void) signal(SIGINT, oldsigint); 60219784Seric } 60319784Seric # endif DBM 60419784Seric 60519784Seric /* closing the alias file drops the lock */ 6064098Seric (void) fclose(af); 6076898Seric CurEnv->e_to = NULL; 6089368Seric FileName = NULL; 6097051Seric message(Arpa_Info, "%d aliases, longest %d bytes, %d bytes total", 6104322Seric naliases, longest, bytes); 61124944Seric # ifdef LOG 61224944Seric if (LogLevel >= 8) 61324944Seric syslog(LOG_INFO, "%d aliases, longest %d bytes, %d bytes total", 61424944Seric naliases, longest, bytes); 61524944Seric # endif LOG 616292Seric } 617292Seric /* 618292Seric ** FORWARD -- Try to forward mail 619292Seric ** 620292Seric ** This is similar but not identical to aliasing. 621292Seric ** 622292Seric ** Parameters: 6234314Seric ** user -- the name of the user who's mail we would like 6244314Seric ** to forward to. It must have been verified -- 6254314Seric ** i.e., the q_home field must have been filled 6264314Seric ** in. 6274999Seric ** sendq -- a pointer to the head of the send queue to 6284999Seric ** put this user's aliases in. 629292Seric ** 630292Seric ** Returns: 6314098Seric ** none. 632292Seric ** 633292Seric ** Side Effects: 6343185Seric ** New names are added to send queues. 635292Seric */ 636292Seric 6374999Seric forward(user, sendq) 6382966Seric ADDRESS *user; 6394999Seric ADDRESS **sendq; 640292Seric { 6414078Seric char buf[60]; 6424536Seric extern bool safefile(); 6434069Seric 6447671Seric if (tTd(27, 1)) 6454098Seric printf("forward(%s)\n", user->q_paddr); 6464098Seric 6474594Seric if (user->q_mailer != LocalMailer || bitset(QBADADDR, user->q_flags)) 6484098Seric return; 6494314Seric if (user->q_home == NULL) 6504314Seric syserr("forward: no home"); 6514069Seric 6524069Seric /* good address -- look for .forward file in home */ 6539368Seric define('z', user->q_home, CurEnv); 65416154Seric expand("\001z/.forward", buf, &buf[sizeof buf - 1], CurEnv); 6554536Seric if (!safefile(buf, user->q_uid, S_IREAD)) 6564098Seric return; 6574069Seric 6584069Seric /* we do have an address to forward to -- do it */ 6594999Seric include(buf, "forwarding", user, sendq); 660292Seric } 661