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