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