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