122694Sdist /* 235073Sbostic * Copyright (c) 1983 Eric P. Allman 362522Sbostic * Copyright (c) 1988, 1993 462522Sbostic * The Regents of the University of California. All rights reserved. 533728Sbostic * 642824Sbostic * %sccs.include.redist.c% 733728Sbostic */ 822694Sdist 958332Seric # include "sendmail.h" 1050577Seric # include <pwd.h> 1156766Seric 1233728Sbostic #ifndef lint 13*64718Seric static char sccsid[] = "@(#)alias.c 8.17 (Berkeley) 10/15/93"; 1433728Sbostic #endif /* not lint */ 1559673Seric 1659673Seric 1760537Seric MAP *AliasDB[MAXALIASDB + 1]; /* actual database list */ 1859673Seric int NAliasDBs; /* number of alias databases */ 1959673Seric /* 20292Seric ** ALIAS -- Compute aliases. 21292Seric ** 229368Seric ** Scans the alias file for an alias for the given address. 239368Seric ** If found, it arranges to deliver to the alias list instead. 249368Seric ** Uses libdbm database if -DDBM. 25292Seric ** 26292Seric ** Parameters: 274097Seric ** a -- address to alias. 284999Seric ** sendq -- a pointer to the head of the send queue 294999Seric ** to put the aliases in. 3058092Seric ** e -- the current envelope. 31292Seric ** 32292Seric ** Returns: 33292Seric ** none 34292Seric ** 35292Seric ** Side Effects: 363185Seric ** Aliases found are expanded. 37292Seric ** 38292Seric ** Deficiencies: 39292Seric ** It should complain about names that are aliased to 40292Seric ** nothing. 41292Seric */ 42292Seric 4355012Seric alias(a, sendq, e) 444097Seric register ADDRESS *a; 454999Seric ADDRESS **sendq; 4655012Seric register ENVELOPE *e; 47292Seric { 484081Seric register char *p; 4958082Seric int naliases; 5058170Seric char *owner; 5158170Seric char obuf[MAXNAME + 6]; 525701Seric extern char *aliaslookup(); 53292Seric 547671Seric if (tTd(27, 1)) 554098Seric printf("alias(%s)\n", a->q_paddr); 56292Seric 574098Seric /* don't realias already aliased names */ 5858680Seric if (bitset(QDONTSEND|QBADADDR|QVERIFIED, a->q_flags)) 594098Seric return; 604098Seric 6159673Seric if (NoAlias) 6259673Seric return; 6359673Seric 6455012Seric e->e_to = a->q_paddr; 654098Seric 664314Seric /* 674314Seric ** Look up this name 684314Seric */ 694314Seric 7059673Seric p = aliaslookup(a->q_user, e); 714098Seric if (p == NULL) 724098Seric return; 73292Seric 74292Seric /* 754098Seric ** Match on Alias. 764098Seric ** Deliver to the target list. 771515Seric */ 781515Seric 797671Seric if (tTd(27, 1)) 804098Seric printf("%s (%s, %s) aliased to %s\n", 814098Seric a->q_paddr, a->q_host, a->q_user, p); 8258092Seric if (bitset(EF_VRFYONLY, e->e_flags)) 8358154Seric { 8458154Seric a->q_flags |= QVERIFIED; 8558884Seric e->e_nrcpts++; 8658092Seric return; 8758154Seric } 8858154Seric message("aliased to %s", p); 8957977Seric #ifdef LOG 9058020Seric if (LogLevel > 9) 9157977Seric syslog(LOG_INFO, "%s: alias %s => %s", e->e_id, a->q_paddr, p); 9257977Seric #endif 9358082Seric a->q_flags &= ~QSELFREF; 944098Seric AliasLevel++; 9558082Seric naliases = sendtolist(p, a, sendq, e); 964098Seric AliasLevel--; 9764301Seric if (!bitset(QSELFREF, a->q_flags)) 9858065Seric { 9958065Seric if (tTd(27, 5)) 10058065Seric { 10158065Seric printf("alias: QDONTSEND "); 10258065Seric printaddr(a, FALSE); 10358065Seric } 10458065Seric a->q_flags |= QDONTSEND; 10558065Seric } 10658170Seric 10758170Seric /* 10858170Seric ** Look for owner of alias 10958170Seric */ 11058170Seric 11158170Seric (void) strcpy(obuf, "owner-"); 11258170Seric if (strncmp(a->q_user, "owner-", 6) == 0) 11358170Seric (void) strcat(obuf, "owner"); 11458170Seric else 11558170Seric (void) strcat(obuf, a->q_user); 11658170Seric if (!bitnset(M_USR_UPPER, a->q_mailer->m_flags)) 11758170Seric makelower(obuf); 11859673Seric owner = aliaslookup(obuf, e); 11958170Seric if (owner != NULL) 12058170Seric { 12158170Seric if (strchr(owner, ',') != NULL) 12258170Seric owner = obuf; 12358170Seric a->q_owner = newstr(owner); 12458170Seric } 1254098Seric } 1264098Seric /* 1275701Seric ** ALIASLOOKUP -- look up a name in the alias file. 1285701Seric ** 1295701Seric ** Parameters: 1305701Seric ** name -- the name to look up. 1315701Seric ** 1325701Seric ** Returns: 1335701Seric ** the value of name. 1345701Seric ** NULL if unknown. 1355701Seric ** 1365701Seric ** Side Effects: 1375701Seric ** none. 1385701Seric ** 1395701Seric ** Warnings: 1405701Seric ** The return value will be trashed across calls. 1415701Seric */ 1425701Seric 1435701Seric char * 14459673Seric aliaslookup(name, e) 1455701Seric char *name; 14659673Seric ENVELOPE *e; 1475701Seric { 14859673Seric register int dbno; 14960089Seric register MAP *map; 15059673Seric register char *p; 1515701Seric 15259673Seric for (dbno = 0; dbno < NAliasDBs; dbno++) 15359673Seric { 15460089Seric auto int stat; 15560089Seric 15660537Seric map = AliasDB[dbno]; 15760207Seric if (!bitset(MF_OPEN, map->map_mflags)) 15859673Seric continue; 15960207Seric p = (*map->map_class->map_lookup)(map, name, NULL, &stat); 16059673Seric if (p != NULL) 16159673Seric return p; 16259673Seric } 16359673Seric return NULL; 16459673Seric } 16559673Seric /* 16659673Seric ** SETALIAS -- set up an alias map 16759673Seric ** 16859673Seric ** Called when reading configuration file. 16959673Seric ** 17059673Seric ** Parameters: 17159673Seric ** spec -- the alias specification 17259673Seric ** 17359673Seric ** Returns: 17459673Seric ** none. 17559673Seric */ 17657381Seric 17759673Seric setalias(spec) 17859673Seric char *spec; 17959673Seric { 18059673Seric register char *p; 18160089Seric register MAP *map; 18259673Seric char *class; 18359673Seric STAB *s; 18459673Seric 18559697Seric if (tTd(27, 8)) 18659697Seric printf("setalias(%s)\n", spec); 18759697Seric 18859758Seric for (p = spec; p != NULL; ) 18951756Seric { 19060537Seric char aname[50]; 19160537Seric 19259758Seric while (isspace(*p)) 19359758Seric p++; 19460502Seric if (*p == '\0') 19559673Seric break; 19659673Seric spec = p; 19759673Seric 19859758Seric if (NAliasDBs >= MAXALIASDB) 19959758Seric { 20059758Seric syserr("Too many alias databases defined, %d max", MAXALIASDB); 20159758Seric return; 20259758Seric } 20360537Seric (void) sprintf(aname, "Alias%d", NAliasDBs); 20460537Seric s = stab(aname, ST_MAP, ST_ENTER); 20560537Seric map = &s->s_map; 20660537Seric AliasDB[NAliasDBs] = map; 20760089Seric bzero(map, sizeof *map); 20859758Seric 20959758Seric p = strpbrk(p, " ,/:"); 21059758Seric if (p != NULL && *p == ':') 21159758Seric { 21260089Seric /* map name */ 21359758Seric *p++ = '\0'; 21459758Seric class = spec; 21559758Seric spec = p; 21659758Seric } 21759758Seric else 21859758Seric { 21959758Seric class = "implicit"; 22060228Seric map->map_mflags = MF_OPTIONAL|MF_INCLNULL; 22159758Seric } 22259758Seric 22359758Seric /* find end of spec */ 22459758Seric if (p != NULL) 22559758Seric p = strchr(p, ','); 22659758Seric if (p != NULL) 22759758Seric *p++ = '\0'; 22859758Seric 22959758Seric /* look up class */ 23060089Seric s = stab(class, ST_MAPCLASS, ST_FIND); 23159758Seric if (s == NULL) 23259758Seric { 23359758Seric if (tTd(27, 1)) 23459758Seric printf("Unknown alias class %s\n", class); 23559758Seric } 23660207Seric else if (!bitset(MCF_ALIASOK, s->s_mapclass.map_cflags)) 23760207Seric { 23860207Seric syserr("setalias: map class %s can't handle aliases", 23960207Seric class); 24060207Seric } 24159758Seric else 24259758Seric { 24360207Seric map->map_class = &s->s_mapclass; 24460089Seric if (map->map_class->map_parse(map, spec)) 24560089Seric { 24660207Seric map->map_mflags |= MF_VALID|MF_ALIAS; 24760089Seric NAliasDBs++; 24860089Seric } 24959758Seric } 25059756Seric } 2515701Seric } 2525701Seric /* 25359673Seric ** ALIASWAIT -- wait for distinguished @:@ token to appear. 25459673Seric ** 25559673Seric ** This can decide to reopen or rebuild the alias file 256*64718Seric ** 257*64718Seric ** Parameters: 258*64718Seric ** map -- a pointer to the map descriptor for this alias file. 259*64718Seric ** ext -- the filename extension (e.g., ".db") for the 260*64718Seric ** database file. 261*64718Seric ** isopen -- if set, the database is already open, and we 262*64718Seric ** should check for validity; otherwise, we are 263*64718Seric ** just checking to see if it should be created. 264*64718Seric ** 265*64718Seric ** Returns: 266*64718Seric ** TRUE -- if the database is open when we return. 267*64718Seric ** FALSE -- if the database is closed when we return. 26859673Seric */ 26959673Seric 270*64718Seric bool 271*64718Seric aliaswait(map, ext, isopen) 27260089Seric MAP *map; 27360207Seric char *ext; 274*64718Seric int isopen; 27559673Seric { 27659673Seric int atcnt; 27759673Seric time_t mtime; 27859673Seric struct stat stb; 27959673Seric char buf[MAXNAME]; 28059673Seric 28159697Seric if (tTd(27, 3)) 28260207Seric printf("aliaswait(%s:%s)\n", 28360207Seric map->map_class->map_cname, map->map_file); 28464648Seric if (bitset(MF_ALIASWAIT, map->map_mflags)) 28564648Seric return; 28664648Seric map->map_mflags |= MF_ALIASWAIT; 28759697Seric 28817471Seric atcnt = SafeAlias * 2; 28917471Seric if (atcnt > 0) 29017471Seric { 29160089Seric auto int st; 29260089Seric 293*64718Seric while (isopen && atcnt-- >= 0 && 29460089Seric map->map_class->map_lookup(map, "@", NULL, &st) == NULL) 29525689Seric { 29625689Seric /* 29759673Seric ** Close and re-open the alias database in case 29859673Seric ** the one is mv'ed instead of cp'ed in. 29925689Seric */ 30025689Seric 30159697Seric if (tTd(27, 2)) 30259697Seric printf("aliaswait: sleeping\n"); 30359697Seric 30460089Seric map->map_class->map_close(map); 30517471Seric sleep(30); 306*64718Seric isopen = map->map_class->map_open(map, O_RDONLY); 30725689Seric } 30817471Seric } 3098437Seric 31059673Seric /* see if we need to go into auto-rebuild mode */ 31160207Seric if (!bitset(MCF_REBUILDABLE, map->map_class->map_cflags)) 31260207Seric { 31360207Seric if (tTd(27, 3)) 31460207Seric printf("aliaswait: not rebuildable\n"); 31564648Seric map->map_mflags &= ~MF_ALIASWAIT; 316*64718Seric return isopen; 31760207Seric } 31860207Seric if (stat(map->map_file, &stb) < 0) 31960207Seric { 32060207Seric if (tTd(27, 3)) 32160207Seric printf("aliaswait: no source file\n"); 32264648Seric map->map_mflags &= ~MF_ALIASWAIT; 323*64718Seric return isopen; 32460207Seric } 32559673Seric mtime = stb.st_mtime; 32660089Seric (void) strcpy(buf, map->map_file); 32760207Seric if (ext != NULL) 32860207Seric (void) strcat(buf, ext); 32959673Seric if (stat(buf, &stb) < 0 || stb.st_mtime < mtime || atcnt < 0) 3304322Seric { 33159673Seric /* database is out of date */ 33240559Sbostic if (AutoRebuild && stb.st_ino != 0 && stb.st_uid == geteuid()) 3334322Seric { 33460089Seric message("auto-rebuilding alias database %s", buf); 335*64718Seric if (isopen) 336*64718Seric map->map_class->map_close(map); 33760207Seric rebuildaliases(map, TRUE); 338*64718Seric isopen = map->map_class->map_open(map, O_RDONLY); 3394322Seric } 3404322Seric else 3414322Seric { 34219039Seric #ifdef LOG 34358020Seric if (LogLevel > 3) 34459673Seric syslog(LOG_INFO, "alias database %s out of date", 34560089Seric buf); 34656795Seric #endif /* LOG */ 34760089Seric message("Warning: alias database %s out of date", buf); 3484322Seric } 3494322Seric } 35064648Seric map->map_mflags &= ~MF_ALIASWAIT; 351*64718Seric return isopen; 35259673Seric } 35359673Seric /* 35459673Seric ** REBUILDALIASES -- rebuild the alias database. 35559673Seric ** 35659673Seric ** Parameters: 35760089Seric ** map -- the database to rebuild. 35859673Seric ** automatic -- set if this was automatically generated. 35959673Seric ** 36059673Seric ** Returns: 36159673Seric ** none. 36259673Seric ** 36359673Seric ** Side Effects: 36459673Seric ** Reads the text version of the database, builds the 36559673Seric ** DBM or DB version. 36659673Seric */ 3674322Seric 36860207Seric rebuildaliases(map, automatic) 36960089Seric register MAP *map; 37059673Seric bool automatic; 37159673Seric { 37259673Seric FILE *af; 37364388Seric bool nolock = FALSE; 37464564Seric sigfunc_t oldsigint; 3754322Seric 37660207Seric if (!bitset(MCF_REBUILDABLE, map->map_class->map_cflags)) 37759673Seric return; 3784322Seric 37959673Seric /* try to lock the source file */ 38060089Seric if ((af = fopen(map->map_file, "r+")) == NULL) 38159673Seric { 38264388Seric if (errno != EACCES || automatic || 38364388Seric (af = fopen(map->map_file, "r")) == NULL) 38464388Seric { 38564388Seric int saveerr = errno; 38664382Seric 38764388Seric if (tTd(27, 1)) 38864388Seric printf("Can't open %s: %s\n", 38964388Seric map->map_file, errstring(saveerr)); 39064388Seric if (!automatic) 39164388Seric message("newaliases: cannot open %s: %s", 39264388Seric map->map_file, errstring(saveerr)); 39364388Seric errno = 0; 39464388Seric return; 39564388Seric } 39664388Seric nolock = TRUE; 39764388Seric message("warning: cannot lock %s: %s", 39864388Seric map->map_file, errstring(errno)); 3998437Seric } 40059673Seric 40159673Seric /* see if someone else is rebuilding the alias file */ 40264388Seric if (!nolock && 40364388Seric !lockfile(fileno(af), map->map_file, NULL, LOCK_EX|LOCK_NB)) 40459673Seric { 40559673Seric /* yes, they are -- wait until done */ 40659673Seric message("Alias file %s is already being rebuilt", 40760089Seric map->map_file); 40859673Seric if (OpMode != MD_INITALIAS) 40959673Seric { 41059673Seric /* wait for other rebuild to complete */ 41164335Seric (void) lockfile(fileno(af), map->map_file, NULL, 41259673Seric LOCK_EX); 41359673Seric } 41459673Seric (void) fclose(af); 41559673Seric errno = 0; 41659673Seric return; 41759673Seric } 41859673Seric 41964035Seric oldsigint = setsignal(SIGINT, SIG_IGN); 42059673Seric 42160207Seric if (map->map_class->map_open(map, O_RDWR)) 42260089Seric { 42364382Seric #ifdef LOG 42464382Seric if (LogLevel > 7) 42564382Seric { 42664382Seric syslog(LOG_NOTICE, "alias database %s %srebuilt by %s", 42764382Seric map->map_file, automatic ? "auto" : "", 42864382Seric username()); 42964382Seric } 43064382Seric #endif /* LOG */ 43160207Seric map->map_mflags |= MF_OPEN|MF_WRITABLE; 43260207Seric readaliases(map, af, automatic); 43360089Seric } 43460207Seric else 43560207Seric { 43660207Seric if (tTd(27, 1)) 43760207Seric printf("Can't create database for %s: %s\n", 43860207Seric map->map_file, errstring(errno)); 43960207Seric if (!automatic) 44060207Seric syserr("Cannot create database for alias file %s", 44160207Seric map->map_file); 44260207Seric } 44359673Seric 44459673Seric /* close the file, thus releasing locks */ 44559673Seric fclose(af); 44659673Seric 44759673Seric /* add distinguished entries and close the database */ 44860207Seric if (bitset(MF_OPEN, map->map_mflags)) 44960089Seric map->map_class->map_close(map); 45059673Seric 45159673Seric /* restore the old signal */ 45264035Seric (void) setsignal(SIGINT, oldsigint); 4534157Seric } 4544157Seric /* 4554157Seric ** READALIASES -- read and process the alias file. 4564157Seric ** 4574157Seric ** This routine implements the part of initaliases that occurs 4584157Seric ** when we are not going to use the DBM stuff. 4594157Seric ** 4604157Seric ** Parameters: 46160089Seric ** map -- the alias database descriptor. 46259673Seric ** af -- file to read the aliases from. 46359733Seric ** automatic -- set if this was an automatic rebuild. 4644157Seric ** 4654157Seric ** Returns: 4664157Seric ** none. 4674157Seric ** 4684157Seric ** Side Effects: 4694157Seric ** Reads aliasfile into the symbol table. 4704157Seric ** Optionally, builds the .dir & .pag files. 4714157Seric */ 4724157Seric 47360207Seric readaliases(map, af, automatic) 47460089Seric register MAP *map; 47559673Seric FILE *af; 47659733Seric int automatic; 4774157Seric { 4784098Seric register char *p; 4794098Seric char *rhs; 4804098Seric bool skipping; 48159673Seric long naliases, bytes, longest; 4824098Seric ADDRESS al, bl; 4839368Seric char line[BUFSIZ]; 4844098Seric 4854314Seric /* 4864314Seric ** Read and interpret lines 4874314Seric */ 4884314Seric 48960089Seric FileName = map->map_file; 4909368Seric LineNumber = 0; 4914322Seric naliases = bytes = longest = 0; 4924098Seric skipping = FALSE; 4934098Seric while (fgets(line, sizeof (line), af) != NULL) 4944098Seric { 4954322Seric int lhssize, rhssize; 4964322Seric 4979368Seric LineNumber++; 49856795Seric p = strchr(line, '\n'); 49925278Seric if (p != NULL) 50025278Seric *p = '\0'; 5014098Seric switch (line[0]) 5024098Seric { 5034098Seric case '#': 5044098Seric case '\0': 5054098Seric skipping = FALSE; 5064098Seric continue; 5074065Seric 5084098Seric case ' ': 5094098Seric case '\t': 5104098Seric if (!skipping) 51158151Seric syserr("554 Non-continuation line starts with space"); 5124098Seric skipping = TRUE; 5134097Seric continue; 5144098Seric } 5154098Seric skipping = FALSE; 5161874Seric 5174314Seric /* 5184314Seric ** Process the LHS 51957736Seric ** Find the colon separator, and parse the address. 52016898Seric ** It should resolve to a local name -- this will 52116898Seric ** be checked later (we want to optionally do 52216898Seric ** parsing of the RHS first to maximize error 52316898Seric ** detection). 5244314Seric */ 5254314Seric 5264098Seric for (p = line; *p != '\0' && *p != ':' && *p != '\n'; p++) 5274097Seric continue; 52816898Seric if (*p++ != ':') 5294098Seric { 53058151Seric syserr("554 missing colon"); 5314097Seric continue; 5324098Seric } 53364284Seric if (parseaddr(line, &al, RF_COPYALL, ':', NULL, CurEnv) == NULL) 5344098Seric { 53564278Seric syserr("554 %s... illegal alias name", al.q_paddr); 53616898Seric continue; 5374098Seric } 5384314Seric 5394314Seric /* 5404314Seric ** Process the RHS. 5414314Seric ** 'al' is the internal form of the LHS address. 5424314Seric ** 'p' points to the text of the RHS. 5434314Seric */ 5444314Seric 54558914Seric while (isascii(*p) && isspace(*p)) 54658914Seric p++; 5474098Seric rhs = p; 5484098Seric for (;;) 5494098Seric { 5504098Seric register char c; 55158662Seric register char *nlp; 5521515Seric 55358662Seric nlp = &p[strlen(p)]; 55458662Seric if (nlp[-1] == '\n') 55558662Seric *--nlp = '\0'; 55658662Seric 55759673Seric if (CheckAliases) 5584098Seric { 5594157Seric /* do parsing & compression of addresses */ 56025278Seric while (*p != '\0') 5614098Seric { 56258333Seric auto char *delimptr; 56325278Seric 56458050Seric while ((isascii(*p) && isspace(*p)) || 56558050Seric *p == ',') 5664157Seric p++; 56725278Seric if (*p == '\0') 56825278Seric break; 56964284Seric if (parseaddr(p, &bl, RF_COPYNONE, ',', 57064284Seric &delimptr, CurEnv) == NULL) 57158151Seric usrerr("553 %s... bad address", p); 57258333Seric p = delimptr; 5734098Seric } 5744098Seric } 5754157Seric else 57615769Seric { 57758662Seric p = nlp; 57815769Seric } 5791515Seric 5804098Seric /* see if there should be a continuation line */ 5814106Seric c = fgetc(af); 5824106Seric if (!feof(af)) 5834314Seric (void) ungetc(c, af); 5844106Seric if (c != ' ' && c != '\t') 5854098Seric break; 5864098Seric 5874098Seric /* read continuation line */ 5884098Seric if (fgets(p, sizeof line - (p - line), af) == NULL) 5894098Seric break; 5909368Seric LineNumber++; 59157135Seric 59257135Seric /* check for line overflow */ 59357135Seric if (strchr(p, '\n') == NULL) 59457135Seric { 59558151Seric usrerr("554 alias too long"); 59657135Seric break; 59757135Seric } 5984098Seric } 59916898Seric if (al.q_mailer != LocalMailer) 60016898Seric { 60164278Seric syserr("554 %s... cannot alias non-local names", 60264278Seric al.q_paddr); 60316898Seric continue; 60416898Seric } 6054314Seric 6064314Seric /* 6074314Seric ** Insert alias into symbol table or DBM file 6084314Seric */ 6094314Seric 61057381Seric if (!bitnset(M_USR_UPPER, al.q_mailer->m_flags)) 61157381Seric makelower(al.q_user); 6124322Seric 61359673Seric lhssize = strlen(al.q_user); 61459673Seric rhssize = strlen(rhs); 61560089Seric map->map_class->map_store(map, al.q_user, rhs); 6164157Seric 61759673Seric if (al.q_paddr != NULL) 61859673Seric free(al.q_paddr); 61959673Seric if (al.q_host != NULL) 62059673Seric free(al.q_host); 62159673Seric if (al.q_user != NULL) 62259673Seric free(al.q_user); 6234322Seric 6244322Seric /* statistics */ 6254322Seric naliases++; 6264322Seric bytes += lhssize + rhssize; 6274322Seric if (rhssize > longest) 6284322Seric longest = rhssize; 6291515Seric } 63019784Seric 63160207Seric CurEnv->e_to = NULL; 63259673Seric FileName = NULL; 63359733Seric if (Verbose || !automatic) 63459733Seric message("%s: %d aliases, longest %d bytes, %d bytes total", 63560089Seric map->map_file, naliases, longest, bytes); 63659673Seric # ifdef LOG 63759673Seric if (LogLevel > 7) 63859673Seric syslog(LOG_INFO, "%s: %d aliases, longest %d bytes, %d bytes total", 63960089Seric map->map_file, naliases, longest, bytes); 64059673Seric # endif /* LOG */ 64159673Seric } 64259673Seric /* 643292Seric ** FORWARD -- Try to forward mail 644292Seric ** 645292Seric ** This is similar but not identical to aliasing. 646292Seric ** 647292Seric ** Parameters: 6484314Seric ** user -- the name of the user who's mail we would like 6494314Seric ** to forward to. It must have been verified -- 6504314Seric ** i.e., the q_home field must have been filled 6514314Seric ** in. 6524999Seric ** sendq -- a pointer to the head of the send queue to 6534999Seric ** put this user's aliases in. 654292Seric ** 655292Seric ** Returns: 6564098Seric ** none. 657292Seric ** 658292Seric ** Side Effects: 6593185Seric ** New names are added to send queues. 660292Seric */ 661292Seric 66255012Seric forward(user, sendq, e) 6632966Seric ADDRESS *user; 6644999Seric ADDRESS **sendq; 66555012Seric register ENVELOPE *e; 666292Seric { 66757136Seric char *pp; 66857136Seric char *ep; 6694069Seric 6707671Seric if (tTd(27, 1)) 6714098Seric printf("forward(%s)\n", user->q_paddr); 6724098Seric 6734594Seric if (user->q_mailer != LocalMailer || bitset(QBADADDR, user->q_flags)) 6744098Seric return; 6754314Seric if (user->q_home == NULL) 67658059Seric { 67758151Seric syserr("554 forward: no home"); 67858059Seric user->q_home = "/nosuchdirectory"; 67958059Seric } 6804069Seric 6814069Seric /* good address -- look for .forward file in home */ 68255012Seric define('z', user->q_home, e); 68357136Seric define('u', user->q_user, e); 68457136Seric define('h', user->q_host, e); 68557136Seric if (ForwardPath == NULL) 68658050Seric ForwardPath = newstr("\201z/.forward"); 68757136Seric 68857136Seric for (pp = ForwardPath; pp != NULL; pp = ep) 68957136Seric { 69058247Seric int err; 69157232Seric char buf[MAXPATHLEN+1]; 69257136Seric 69357136Seric ep = strchr(pp, ':'); 69457136Seric if (ep != NULL) 69557136Seric *ep = '\0'; 69657136Seric expand(pp, buf, &buf[sizeof buf - 1], e); 69757136Seric if (ep != NULL) 69857136Seric *ep++ = ':'; 69957136Seric if (tTd(27, 3)) 70057136Seric printf("forward: trying %s\n", buf); 70163753Seric 70258247Seric err = include(buf, TRUE, user, sendq, e); 70358247Seric if (err == 0) 70457136Seric break; 70564325Seric else if (transienterror(err)) 70658247Seric { 70758247Seric /* we have to suspend this message */ 70859563Seric if (tTd(27, 2)) 70959563Seric printf("forward: transient error on %s\n", buf); 71059563Seric #ifdef LOG 71159563Seric if (LogLevel > 2) 71259624Seric syslog(LOG_ERR, "%s: forward %s: transient error: %s", 71359624Seric e->e_id, buf, errstring(err)); 71459563Seric #endif 71559611Seric message("%s: %s: message queued", buf, errstring(err)); 71663853Seric user->q_flags |= QQUEUEUP; 71758247Seric return; 71858247Seric } 71957136Seric } 720292Seric } 721