1 /* 2 * Copyright (c) 1983 Eric P. Allman 3 * Copyright (c) 1988 Regents of the University of California. 4 * All rights reserved. 5 * 6 * %sccs.include.redist.c% 7 */ 8 9 # include <sys/types.h> 10 # include <sys/stat.h> 11 # include <sys/file.h> 12 # include <signal.h> 13 # include "sendmail.h" 14 # include <fcntl.h> 15 # include <pwd.h> 16 17 # ifdef DBM 18 ERROR: DBM is no longer supported -- use NDBM instead. 19 # endif 20 21 # ifdef NEWDB 22 # include <db.h> 23 # endif 24 25 # ifdef NDBM 26 # include <ndbm.h> 27 # endif 28 29 #ifndef lint 30 #ifdef NEWDB 31 #ifdef NDBM 32 static char sccsid[] = "@(#)alias.c 6.15 (Berkeley) 02/20/93 (with NEWDB and NDBM)"; 33 #else 34 static char sccsid[] = "@(#)alias.c 6.15 (Berkeley) 02/20/93 (with NEWDB)"; 35 #endif 36 #else 37 #ifdef NDBM 38 static char sccsid[] = "@(#)alias.c 6.15 (Berkeley) 02/20/93 (with NDBM)"; 39 #else 40 static char sccsid[] = "@(#)alias.c 6.15 (Berkeley) 02/20/93 (without NEWDB or NDBM)"; 41 #endif 42 #endif 43 #endif /* not lint */ 44 /* 45 ** ALIAS -- Compute aliases. 46 ** 47 ** Scans the alias file for an alias for the given address. 48 ** If found, it arranges to deliver to the alias list instead. 49 ** Uses libdbm database if -DDBM. 50 ** 51 ** Parameters: 52 ** a -- address to alias. 53 ** sendq -- a pointer to the head of the send queue 54 ** to put the aliases in. 55 ** e -- the current envelope. 56 ** 57 ** Returns: 58 ** none 59 ** 60 ** Side Effects: 61 ** Aliases found are expanded. 62 ** 63 ** Notes: 64 ** If NoAlias (the "-n" flag) is set, no aliasing is 65 ** done. 66 ** 67 ** Deficiencies: 68 ** It should complain about names that are aliased to 69 ** nothing. 70 */ 71 72 73 /* 74 ** Sun YP servers read the dbm files directly, so we have to build them 75 ** even if NEWDB 76 */ 77 78 #ifdef NDBM 79 # ifndef NEWDB 80 # define IF_MAKEDBMFILES 81 # else 82 # ifdef YPCOMPAT 83 # define IF_MAKEDBMFILES if (makedbmfiles) 84 # endif 85 # endif 86 #endif 87 88 typedef union 89 { 90 #ifdef NDBM 91 datum dbm; 92 #endif 93 #ifdef NEWDB 94 DBT dbt; 95 #endif 96 struct 97 { 98 char *data; 99 int size; 100 } xx; 101 } DBdatum; 102 103 #ifdef NEWDB 104 static DB *AliasDBptr; 105 #endif 106 #ifdef NDBM 107 static DBM *AliasDBMptr; 108 #endif 109 110 alias(a, sendq, e) 111 register ADDRESS *a; 112 ADDRESS **sendq; 113 register ENVELOPE *e; 114 { 115 register char *p; 116 int naliases; 117 extern char *aliaslookup(); 118 119 if (tTd(27, 1)) 120 printf("alias(%s)\n", a->q_paddr); 121 122 /* don't realias already aliased names */ 123 if (bitset(QDONTSEND, a->q_flags)) 124 return; 125 126 e->e_to = a->q_paddr; 127 128 /* 129 ** Look up this name 130 */ 131 132 if (NoAlias) 133 p = NULL; 134 else 135 p = aliaslookup(a->q_user); 136 if (p == NULL) 137 return; 138 139 /* 140 ** Match on Alias. 141 ** Deliver to the target list. 142 */ 143 144 if (tTd(27, 1)) 145 printf("%s (%s, %s) aliased to %s\n", 146 a->q_paddr, a->q_host, a->q_user, p); 147 message(Arpa_Info, "aliased to %s", p); 148 if (bitset(EF_VRFYONLY, e->e_flags)) 149 return; 150 #ifdef LOG 151 if (LogLevel > 9) 152 syslog(LOG_INFO, "%s: alias %s => %s", e->e_id, a->q_paddr, p); 153 #endif 154 a->q_flags &= ~QSELFREF; 155 AliasLevel++; 156 naliases = sendtolist(p, a, sendq, e); 157 AliasLevel--; 158 if (naliases > 0 && !bitset(QSELFREF, a->q_flags)) 159 { 160 if (tTd(27, 5)) 161 { 162 printf("alias: QDONTSEND "); 163 printaddr(a, FALSE); 164 } 165 a->q_flags |= QDONTSEND; 166 } 167 } 168 /* 169 ** ALIASLOOKUP -- look up a name in the alias file. 170 ** 171 ** Parameters: 172 ** name -- the name to look up. 173 ** 174 ** Returns: 175 ** the value of name. 176 ** NULL if unknown. 177 ** 178 ** Side Effects: 179 ** none. 180 ** 181 ** Warnings: 182 ** The return value will be trashed across calls. 183 */ 184 185 char * 186 aliaslookup(name) 187 char *name; 188 { 189 int i; 190 char keybuf[MAXNAME + 1]; 191 # if defined(NEWDB) || defined(NDBM) 192 DBdatum rhs, lhs; 193 int s; 194 # else /* neither NEWDB nor NDBM */ 195 register STAB *s; 196 # endif 197 198 /* create a key for fetch */ 199 i = strlen(name) + 1; 200 if (i > sizeof keybuf) 201 i = sizeof keybuf; 202 bcopy(name, keybuf, i); 203 if (!bitnset(M_USR_UPPER, LocalMailer->m_flags)) 204 makelower(keybuf); 205 206 # if defined(NEWDB) || defined(NDBM) 207 lhs.xx.size = i; 208 lhs.xx.data = keybuf; 209 # ifdef NEWDB 210 if (AliasDBptr != NULL) 211 { 212 i = AliasDBptr->get(AliasDBptr, &lhs.dbt, &rhs.dbt, 0); 213 if (i == 0) 214 return (rhs.dbt.data); 215 } 216 # ifdef NDBM 217 else if (AliasDBMptr != NULL) 218 { 219 rhs.dbm = dbm_fetch(AliasDBMptr, lhs.dbm); 220 return (rhs.dbm.dptr); 221 } 222 # endif /* NDBM */ 223 return (NULL); 224 # else /* not NEWDB */ 225 rhs.dbm = dbm_fetch(AliasDBMptr, lhs.dbm); 226 return (rhs.dbm.dptr); 227 # endif /* NEWDB */ 228 # else /* neither NEWDB nor NDBM */ 229 s = stab(keybuf, ST_ALIAS, ST_FIND); 230 if (s != NULL) 231 return (s->s_alias); 232 return (NULL); 233 # endif 234 } 235 /* 236 ** INITALIASES -- initialize for aliasing 237 ** 238 ** Very different depending on whether we are running NDBM or not. 239 ** 240 ** Parameters: 241 ** aliasfile -- location of aliases. 242 ** init -- if set and if NDBM, initialize the NDBM files. 243 ** 244 ** Returns: 245 ** none. 246 ** 247 ** Side Effects: 248 ** initializes aliases: 249 ** if NDBM: opens the database. 250 ** if ~NDBM: reads the aliases into the symbol table. 251 */ 252 253 # define DBMMODE 0644 254 255 initaliases(aliasfile, init, e) 256 char *aliasfile; 257 bool init; 258 register ENVELOPE *e; 259 { 260 #if defined(NDBM) || defined(NEWDB) 261 int atcnt; 262 time_t modtime; 263 bool automatic = FALSE; 264 char buf[MAXNAME]; 265 #endif 266 struct stat stb; 267 static bool initialized = FALSE; 268 static int readaliases(); 269 270 if (initialized) 271 return; 272 initialized = TRUE; 273 274 if (aliasfile == NULL || stat(aliasfile, &stb) < 0) 275 { 276 if (aliasfile != NULL && init) 277 syserr("Cannot open %s", aliasfile); 278 NoAlias = TRUE; 279 errno = 0; 280 return; 281 } 282 283 # if defined(NDBM) || defined(NEWDB) 284 /* 285 ** Check to see that the alias file is complete. 286 ** If not, we will assume that someone died, and it is up 287 ** to us to rebuild it. 288 */ 289 290 if (!init) 291 { 292 # ifdef NEWDB 293 (void) strcpy(buf, aliasfile); 294 (void) strcat(buf, ".db"); 295 AliasDBptr = dbopen(buf, O_RDONLY, DBMMODE, DB_HASH, NULL); 296 if (AliasDBptr == NULL) 297 { 298 # ifdef NDBM 299 AliasDBMptr = dbm_open(aliasfile, O_RDONLY, DBMMODE); 300 if (AliasDBMptr == NULL) 301 { 302 syserr("initaliases: cannot open %s", buf); 303 NoAlias = TRUE; 304 return; 305 } 306 # else 307 syserr("initaliases: cannot open %s", buf); 308 NoAlias = TRUE; 309 return; 310 # endif 311 } 312 # else 313 AliasDBMptr = dbm_open(aliasfile, O_RDONLY, DBMMODE); 314 if (AliasDBMptr == NULL) 315 { 316 syserr("initaliases: cannot open DBM database %s.{pag,dir}", 317 aliasfile); 318 NoAlias = TRUE; 319 return; 320 } 321 # endif 322 } 323 atcnt = SafeAlias * 2; 324 if (atcnt > 0) 325 { 326 while (!init && atcnt-- >= 0 && aliaslookup("@") == NULL) 327 { 328 /* 329 ** Reinitialize alias file in case the new 330 ** one is mv'ed in instead of cp'ed in. 331 ** 332 ** Only works with new DBM -- old one will 333 ** just consume file descriptors forever. 334 ** If you have a dbmclose() it can be 335 ** added before the sleep(30). 336 */ 337 338 # ifdef NEWDB 339 if (AliasDBptr != NULL) 340 AliasDBptr->close(AliasDBptr); 341 # endif 342 # ifdef NDBM 343 if (AliasDBMptr != NULL) 344 dbm_close(AliasDBMptr); 345 # endif 346 347 sleep(30); 348 # ifdef NEWDB 349 (void) strcpy(buf, aliasfile); 350 (void) strcat(buf, ".db"); 351 AliasDBptr = 352 dbopen(buf, O_RDONLY, DBMMODE, DB_HASH, NULL); 353 if (AliasDBptr == NULL) 354 { 355 # ifdef NDBM 356 AliasDBMptr = dbm_open(aliasfile, O_RDONLY, DBMMODE); 357 # else 358 syserr("initaliases: cannot open %s", buf); 359 NoAlias = TRUE; 360 return; 361 # endif 362 } 363 # else 364 # ifdef NDBM 365 AliasDBMptr = dbm_open(aliasfile, O_RDONLY, DBMMODE); 366 if (AliasDBMptr == NULL) 367 { 368 syserr("initaliases: cannot open DBM database %s.{pag,dir}", 369 aliasfile); 370 NoAlias = TRUE; 371 return; 372 } 373 # endif 374 # endif 375 } 376 } 377 else 378 atcnt = 1; 379 380 /* 381 ** See if the NDBM version of the file is out of date with 382 ** the text version. If so, go into 'init' mode automatically. 383 ** This only happens if our effective userid owns the DBM. 384 ** Note the unpalatable hack to see if the stat succeeded. 385 */ 386 387 modtime = stb.st_mtime; 388 (void) strcpy(buf, aliasfile); 389 # ifdef NEWDB 390 (void) strcat(buf, ".db"); 391 # else 392 (void) strcat(buf, ".pag"); 393 # endif 394 stb.st_ino = 0; 395 if (!init && (stat(buf, &stb) < 0 || stb.st_mtime < modtime || atcnt < 0)) 396 { 397 errno = 0; 398 if (AutoRebuild && stb.st_ino != 0 && stb.st_uid == geteuid()) 399 { 400 init = TRUE; 401 automatic = TRUE; 402 message(Arpa_Info, "rebuilding alias database"); 403 #ifdef LOG 404 if (LogLevel > 14) 405 syslog(LOG_INFO, "rebuilding alias database"); 406 #endif /* LOG */ 407 } 408 else 409 { 410 #ifdef LOG 411 if (LogLevel > 3) 412 syslog(LOG_INFO, "alias database out of date"); 413 #endif /* LOG */ 414 message(Arpa_Info, "Warning: alias database out of date"); 415 } 416 } 417 418 419 /* 420 ** If necessary, load the NDBM file. 421 ** If running without NDBM, load the symbol table. 422 */ 423 424 if (init) 425 { 426 #ifdef LOG 427 if (LogLevel > 7) 428 { 429 extern char *username(); 430 431 syslog(LOG_NOTICE, "alias database %srebuilt by %s", 432 automatic ? "auto" : "", username()); 433 } 434 #endif /* LOG */ 435 readaliases(aliasfile, TRUE, e); 436 } 437 # else /* NDBM */ 438 readaliases(aliasfile, init, e); 439 # endif /* NDBM */ 440 } 441 /* 442 ** READALIASES -- read and process the alias file. 443 ** 444 ** This routine implements the part of initaliases that occurs 445 ** when we are not going to use the DBM stuff. 446 ** 447 ** Parameters: 448 ** aliasfile -- the pathname of the alias file master. 449 ** init -- if set, initialize the NDBM stuff. 450 ** 451 ** Returns: 452 ** none. 453 ** 454 ** Side Effects: 455 ** Reads aliasfile into the symbol table. 456 ** Optionally, builds the .dir & .pag files. 457 */ 458 459 static 460 readaliases(aliasfile, init, e) 461 char *aliasfile; 462 bool init; 463 register ENVELOPE *e; 464 { 465 register char *p; 466 char *rhs; 467 bool skipping; 468 int naliases, bytes, longest; 469 FILE *af; 470 bool makedbmfiles; 471 void (*oldsigint)(); 472 ADDRESS al, bl; 473 register STAB *s; 474 # ifdef NEWDB 475 DB *dbp; 476 # endif 477 # ifdef NDBM 478 DBM *dbmp; 479 # endif 480 # ifdef LOCKF 481 struct flock fld; 482 # endif 483 char line[BUFSIZ]; 484 485 if ((af = fopen(aliasfile, "r+")) == NULL) 486 { 487 if (init) 488 syserr("Can't open %s", aliasfile); 489 else if (tTd(27, 1)) 490 printf("Can't open %s\n", aliasfile); 491 errno = 0; 492 NoAlias++; 493 return; 494 } 495 496 # if defined(NDBM) || defined(NEWDB) 497 /* see if someone else is rebuilding the alias file already */ 498 # ifdef LOCKF 499 fld.l_type = F_WRLCK; 500 fld.l_whence = fld.l_start = fld.l_len = 0; 501 if (fcntl(fileno(af), F_SETLK, &fld) < 0) 502 # else 503 if (flock(fileno(af), LOCK_EX | LOCK_NB) < 0 && errno == EWOULDBLOCK) 504 # endif 505 { 506 /* yes, they are -- wait until done and then return */ 507 message(Arpa_Info, "Alias file is already being rebuilt"); 508 if (OpMode != MD_INITALIAS) 509 { 510 /* wait for other rebuild to complete */ 511 # ifdef LOCKF 512 (void) fcntl(fileno(af), F_SETLKW, &fld); 513 # else 514 (void) flock(fileno(af), LOCK_EX); 515 # endif 516 } 517 (void) fclose(af); 518 errno = 0; 519 return; 520 } 521 # endif /* NDBM */ 522 523 /* 524 ** If initializing, create the new DBM files. 525 */ 526 527 if (init) 528 { 529 oldsigint = signal(SIGINT, SIG_IGN); 530 # ifdef NEWDB 531 (void) strcpy(line, aliasfile); 532 (void) strcat(line, ".db"); 533 dbp = dbopen(line, 534 O_RDWR|O_CREAT|O_TRUNC, DBMMODE, DB_HASH, NULL); 535 if (dbp == NULL) 536 { 537 syserr("readaliases: cannot create %s", line); 538 (void) signal(SIGINT, oldsigint); 539 return; 540 } 541 # endif 542 # ifdef IF_MAKEDBMFILES 543 # ifdef NEWDB 544 makedbmfiles = access("/var/yp/Makefile", R_OK) == 0; 545 # endif 546 IF_MAKEDBMFILES 547 { 548 dbmp = dbm_open(aliasfile, 549 O_RDWR|O_CREAT|O_TRUNC, DBMMODE); 550 if (dbmp == NULL) 551 { 552 syserr("readaliases: cannot create %s.{dir,pag}", 553 aliasfile); 554 (void) signal(SIGINT, oldsigint); 555 return; 556 } 557 } 558 # endif 559 } 560 561 /* 562 ** Read and interpret lines 563 */ 564 565 FileName = aliasfile; 566 LineNumber = 0; 567 naliases = bytes = longest = 0; 568 skipping = FALSE; 569 while (fgets(line, sizeof (line), af) != NULL) 570 { 571 int lhssize, rhssize; 572 573 LineNumber++; 574 p = strchr(line, '\n'); 575 if (p != NULL) 576 *p = '\0'; 577 switch (line[0]) 578 { 579 case '#': 580 case '\0': 581 skipping = FALSE; 582 continue; 583 584 case ' ': 585 case '\t': 586 if (!skipping) 587 syserr("Non-continuation line starts with space"); 588 skipping = TRUE; 589 continue; 590 } 591 skipping = FALSE; 592 593 /* 594 ** Process the LHS 595 ** Find the colon separator, and parse the address. 596 ** It should resolve to a local name -- this will 597 ** be checked later (we want to optionally do 598 ** parsing of the RHS first to maximize error 599 ** detection). 600 */ 601 602 for (p = line; *p != '\0' && *p != ':' && *p != '\n'; p++) 603 continue; 604 if (*p++ != ':') 605 { 606 syserr("missing colon"); 607 continue; 608 } 609 if (parseaddr(line, &al, 1, ':', e) == NULL) 610 { 611 syserr("illegal alias name"); 612 continue; 613 } 614 loweraddr(&al); 615 616 /* 617 ** Process the RHS. 618 ** 'al' is the internal form of the LHS address. 619 ** 'p' points to the text of the RHS. 620 */ 621 622 rhs = p; 623 for (;;) 624 { 625 register char c; 626 627 if (init && CheckAliases) 628 { 629 /* do parsing & compression of addresses */ 630 while (*p != '\0') 631 { 632 extern char *DelimChar; 633 634 while ((isascii(*p) && isspace(*p)) || 635 *p == ',') 636 p++; 637 if (*p == '\0') 638 break; 639 if (parseaddr(p, &bl, -1, ',', e) == NULL) 640 usrerr("%s... bad address", p); 641 p = DelimChar; 642 } 643 } 644 else 645 { 646 p = &p[strlen(p)]; 647 if (p[-1] == '\n') 648 *--p = '\0'; 649 } 650 651 /* see if there should be a continuation line */ 652 c = fgetc(af); 653 if (!feof(af)) 654 (void) ungetc(c, af); 655 if (c != ' ' && c != '\t') 656 break; 657 658 /* read continuation line */ 659 if (fgets(p, sizeof line - (p - line), af) == NULL) 660 break; 661 LineNumber++; 662 663 /* check for line overflow */ 664 if (strchr(p, '\n') == NULL) 665 { 666 usrerr("alias too long"); 667 break; 668 } 669 } 670 if (al.q_mailer != LocalMailer) 671 { 672 syserr("cannot alias non-local names"); 673 continue; 674 } 675 676 /* 677 ** Insert alias into symbol table or DBM file 678 */ 679 680 lhssize = strlen(al.q_user) + 1; 681 if (!bitnset(M_USR_UPPER, al.q_mailer->m_flags)) 682 makelower(al.q_user); 683 rhssize = strlen(rhs) + 1; 684 685 # if defined(NDBM) || defined(NEWDB) 686 if (init) 687 { 688 DBdatum key, content; 689 int putstat; 690 691 key.xx.size = lhssize; 692 key.xx.data = al.q_user; 693 content.xx.size = rhssize; 694 content.xx.data = rhs; 695 # ifdef NEWDB 696 putstat = dbp->put(dbp, &key.dbt, &content.dbt, 697 R_NOOVERWRITE); 698 if (putstat > 0) 699 { 700 usrerr("050 Warning: duplicate alias name %s", 701 al.q_user); 702 putstat = dbp->put(dbp, &key.dbt, 703 &content.dbt, 0); 704 } 705 if (putstat != 0) 706 syserr("readaliases: db put (%s)", al.q_user); 707 # endif 708 # ifdef IF_MAKEDBMFILES 709 IF_MAKEDBMFILES 710 { 711 putstat = dbm_store(dbmp, key.dbm, content.dbm, 712 DBM_INSERT); 713 if (putstat > 0) 714 { 715 usrerr("050 Warning: duplicate alias name %s", 716 al.q_user); 717 putstat = dbm_store(dbmp, key.dbm, 718 content.dbm, DBM_REPLACE); 719 } 720 if (putstat != 0) 721 syserr("readaliases: dbm store (%s)", 722 al.q_user); 723 } 724 # endif 725 if (al.q_paddr != NULL) 726 free(al.q_paddr); 727 if (al.q_host != NULL) 728 free(al.q_host); 729 if (al.q_user != NULL) 730 free(al.q_user); 731 } 732 else 733 # endif /* NDBM */ 734 { 735 s = stab(al.q_user, ST_ALIAS, ST_ENTER); 736 s->s_alias = newstr(rhs); 737 } 738 739 /* statistics */ 740 naliases++; 741 bytes += lhssize + rhssize; 742 if (rhssize > longest) 743 longest = rhssize; 744 } 745 746 # if defined(NDBM) || defined(NEWDB) 747 if (init) 748 { 749 /* add the distinquished alias "@" */ 750 DBdatum key; 751 752 key.xx.size = 2; 753 key.xx.data = "@"; 754 # ifdef NEWDB 755 if (dbp->sync(dbp) != 0 || 756 dbp->put(dbp, &key.dbt, &key.dbt, 0) != 0 || 757 dbp->close(dbp) != 0) 758 syserr("readaliases: db close failure"); 759 # endif 760 # ifdef IF_MAKEDBMFILES 761 IF_MAKEDBMFILES 762 { 763 #ifdef YPCOMPAT 764 nis_magic(dbmp); 765 #endif 766 if (dbm_store(dbmp, key.dbm, key.dbm, DBM_REPLACE) != 0 || 767 dbm_error(dbmp)) 768 syserr("readaliases: dbm close failure"); 769 dbm_close(dbmp); 770 } 771 # endif 772 773 /* restore the old signal */ 774 (void) signal(SIGINT, oldsigint); 775 } 776 # endif /* NDBM */ 777 778 /* closing the alias file drops the lock */ 779 (void) fclose(af); 780 e->e_to = NULL; 781 FileName = NULL; 782 message(Arpa_Info, "%d aliases, longest %d bytes, %d bytes total", 783 naliases, longest, bytes); 784 # ifdef LOG 785 if (LogLevel > 7) 786 syslog(LOG_INFO, "%d aliases, longest %d bytes, %d bytes total", 787 naliases, longest, bytes); 788 # endif /* LOG */ 789 } 790 /* 791 ** NIS_MAGIC -- Add NIS magic dbm data 792 ** 793 ** This adds the magic entries needed by SunOS to make this a valid 794 ** NIS map. 795 ** 796 ** Parameters: 797 ** dbmp -- a pointer to the DBM structure. 798 ** 799 ** Returns: 800 ** none. 801 */ 802 803 # ifdef YPCOMPAT 804 805 static void 806 nis_magic(dbmp) 807 DBM *dbmp; 808 { 809 int i; 810 static datum key[2] = 811 { 812 { "YP_LAST_MODIFIED", sizeof "YP_LAST_MODIFIED" - 1 }, 813 { "YP_MASTER_NAME", sizeof "YP_MASTER_NAME" - 1 }, 814 }; 815 datum contents[2]; 816 char tbuf[12]; 817 char hbuf[MAXHOSTNAMELEN]; 818 819 (void) sprintf(tbuf, "%010ld", curtime()); 820 contents[0].dptr = tbuf; 821 contents[0].dsize = strlen(tbuf); 822 823 (void) myhostname(hbuf, sizeof hbuf); 824 contents[1].dptr = hbuf; 825 contents[1].dptr = strlen(hbuf); 826 827 for (i = 0; i < sizeof key / sizeof *key; i++) 828 { 829 if (dbm_store(dbmp, key[i], contents[i], DBM_REPLACE) != 0 || 830 dbm_error(dbmp)) 831 syserr("nis_magic: dbm_store failure"); 832 } 833 } 834 835 # endif 836 /* 837 ** FORWARD -- Try to forward mail 838 ** 839 ** This is similar but not identical to aliasing. 840 ** 841 ** Parameters: 842 ** user -- the name of the user who's mail we would like 843 ** to forward to. It must have been verified -- 844 ** i.e., the q_home field must have been filled 845 ** in. 846 ** sendq -- a pointer to the head of the send queue to 847 ** put this user's aliases in. 848 ** 849 ** Returns: 850 ** none. 851 ** 852 ** Side Effects: 853 ** New names are added to send queues. 854 */ 855 856 forward(user, sendq, e) 857 ADDRESS *user; 858 ADDRESS **sendq; 859 register ENVELOPE *e; 860 { 861 char *pp; 862 char *ep; 863 extern bool safefile(); 864 865 if (tTd(27, 1)) 866 printf("forward(%s)\n", user->q_paddr); 867 868 if (user->q_mailer != LocalMailer || bitset(QBADADDR, user->q_flags)) 869 return; 870 if (user->q_home == NULL) 871 { 872 syserr("forward: no home"); 873 user->q_home = "/nosuchdirectory"; 874 } 875 876 /* good address -- look for .forward file in home */ 877 define('z', user->q_home, e); 878 define('u', user->q_user, e); 879 define('h', user->q_host, e); 880 if (ForwardPath == NULL) 881 ForwardPath = newstr("\201z/.forward"); 882 883 for (pp = ForwardPath; pp != NULL; pp = ep) 884 { 885 char buf[MAXPATHLEN+1]; 886 887 ep = strchr(pp, ':'); 888 if (ep != NULL) 889 *ep = '\0'; 890 expand(pp, buf, &buf[sizeof buf - 1], e); 891 if (ep != NULL) 892 *ep++ = ':'; 893 if (tTd(27, 3)) 894 printf("forward: trying %s\n", buf); 895 if (include(buf, TRUE, user, sendq, e) == 0) 896 break; 897 } 898 } 899