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