1 /* 2 * Copyright (c) 1983 Eric P. Allman 3 * Copyright (c) 1988, 1993 4 * The Regents of the University of California. All rights reserved. 5 * 6 * %sccs.include.redist.c% 7 */ 8 9 #ifndef lint 10 static char sccsid[] = "@(#)recipient.c 8.21 (Berkeley) 10/29/93"; 11 #endif /* not lint */ 12 13 # include "sendmail.h" 14 # include <pwd.h> 15 16 /* 17 ** SENDTOLIST -- Designate a send list. 18 ** 19 ** The parameter is a comma-separated list of people to send to. 20 ** This routine arranges to send to all of them. 21 ** 22 ** Parameters: 23 ** list -- the send list. 24 ** ctladdr -- the address template for the person to 25 ** send to -- effective uid/gid are important. 26 ** This is typically the alias that caused this 27 ** expansion. 28 ** sendq -- a pointer to the head of a queue to put 29 ** these people into. 30 ** e -- the envelope in which to add these recipients. 31 ** 32 ** Returns: 33 ** The number of addresses actually on the list. 34 ** 35 ** Side Effects: 36 ** none. 37 */ 38 39 # define MAXRCRSN 10 40 41 sendtolist(list, ctladdr, sendq, e) 42 char *list; 43 ADDRESS *ctladdr; 44 ADDRESS **sendq; 45 register ENVELOPE *e; 46 { 47 register char *p; 48 register ADDRESS *al; /* list of addresses to send to */ 49 bool firstone; /* set on first address sent */ 50 char delimiter; /* the address delimiter */ 51 int naddrs; 52 char *oldto = e->e_to; 53 54 if (list == NULL) 55 { 56 syserr("sendtolist: null list"); 57 return 0; 58 } 59 60 if (tTd(25, 1)) 61 { 62 printf("sendto: %s\n ctladdr=", list); 63 printaddr(ctladdr, FALSE); 64 } 65 66 /* heuristic to determine old versus new style addresses */ 67 if (ctladdr == NULL && 68 (strchr(list, ',') != NULL || strchr(list, ';') != NULL || 69 strchr(list, '<') != NULL || strchr(list, '(') != NULL)) 70 e->e_flags &= ~EF_OLDSTYLE; 71 delimiter = ' '; 72 if (!bitset(EF_OLDSTYLE, e->e_flags) || ctladdr != NULL) 73 delimiter = ','; 74 75 firstone = TRUE; 76 al = NULL; 77 naddrs = 0; 78 79 for (p = list; *p != '\0'; ) 80 { 81 auto char *delimptr; 82 register ADDRESS *a; 83 84 /* parse the address */ 85 while ((isascii(*p) && isspace(*p)) || *p == ',') 86 p++; 87 a = parseaddr(p, NULLADDR, RF_COPYALL, delimiter, &delimptr, e); 88 p = delimptr; 89 if (a == NULL) 90 continue; 91 a->q_next = al; 92 a->q_alias = ctladdr; 93 94 /* see if this should be marked as a primary address */ 95 if (ctladdr == NULL || 96 (firstone && *p == '\0' && bitset(QPRIMARY, ctladdr->q_flags))) 97 a->q_flags |= QPRIMARY; 98 99 if (ctladdr != NULL && sameaddr(ctladdr, a)) 100 ctladdr->q_flags |= QSELFREF; 101 al = a; 102 firstone = FALSE; 103 } 104 105 /* arrange to send to everyone on the local send list */ 106 while (al != NULL) 107 { 108 register ADDRESS *a = al; 109 110 al = a->q_next; 111 a = recipient(a, sendq, e); 112 113 /* arrange to inherit full name */ 114 if (a->q_fullname == NULL && ctladdr != NULL) 115 a->q_fullname = ctladdr->q_fullname; 116 naddrs++; 117 } 118 119 e->e_to = oldto; 120 return (naddrs); 121 } 122 /* 123 ** RECIPIENT -- Designate a message recipient 124 ** 125 ** Saves the named person for future mailing. 126 ** 127 ** Parameters: 128 ** a -- the (preparsed) address header for the recipient. 129 ** sendq -- a pointer to the head of a queue to put the 130 ** recipient in. Duplicate supression is done 131 ** in this queue. 132 ** e -- the current envelope. 133 ** 134 ** Returns: 135 ** The actual address in the queue. This will be "a" if 136 ** the address is not a duplicate, else the original address. 137 ** 138 ** Side Effects: 139 ** none. 140 */ 141 142 ADDRESS * 143 recipient(a, sendq, e) 144 register ADDRESS *a; 145 register ADDRESS **sendq; 146 register ENVELOPE *e; 147 { 148 register ADDRESS *q; 149 ADDRESS **pq; 150 register struct mailer *m; 151 register char *p; 152 bool quoted = FALSE; /* set if the addr has a quote bit */ 153 int findusercount = 0; 154 char buf[MAXNAME]; /* unquoted image of the user name */ 155 extern int safefile(); 156 157 e->e_to = a->q_paddr; 158 m = a->q_mailer; 159 errno = 0; 160 if (tTd(26, 1)) 161 { 162 printf("\nrecipient: "); 163 printaddr(a, FALSE); 164 } 165 166 /* if this is primary, add it to the original recipient list */ 167 if (a->q_alias == NULL) 168 { 169 if (e->e_origrcpt == NULL) 170 e->e_origrcpt = a->q_paddr; 171 else if (e->e_origrcpt != a->q_paddr) 172 e->e_origrcpt = ""; 173 } 174 175 /* break aliasing loops */ 176 if (AliasLevel > MAXRCRSN) 177 { 178 usrerr("554 aliasing/forwarding loop broken"); 179 return (a); 180 } 181 182 /* 183 ** Finish setting up address structure. 184 */ 185 186 /* set the queue timeout */ 187 a->q_timeout = TimeOuts.to_q_return; 188 189 /* get unquoted user for file, program or user.name check */ 190 (void) strcpy(buf, a->q_user); 191 for (p = buf; *p != '\0' && !quoted; p++) 192 { 193 if (*p == '\\') 194 quoted = TRUE; 195 } 196 stripquotes(buf); 197 198 /* check for direct mailing to restricted mailers */ 199 if (a->q_alias == NULL && m == ProgMailer) 200 { 201 a->q_flags |= QBADADDR; 202 usrerr("550 Cannot mail directly to programs"); 203 } 204 205 /* 206 ** Look up this person in the recipient list. 207 ** If they are there already, return, otherwise continue. 208 ** If the list is empty, just add it. Notice the cute 209 ** hack to make from addresses suppress things correctly: 210 ** the QDONTSEND bit will be set in the send list. 211 ** [Please note: the emphasis is on "hack."] 212 */ 213 214 for (pq = sendq; (q = *pq) != NULL; pq = &q->q_next) 215 { 216 if (sameaddr(q, a)) 217 { 218 if (tTd(26, 1)) 219 { 220 printf("%s in sendq: ", a->q_paddr); 221 printaddr(q, FALSE); 222 } 223 if (!bitset(QPRIMARY, q->q_flags)) 224 { 225 if (!bitset(QDONTSEND, a->q_flags)) 226 message("duplicate suppressed"); 227 q->q_flags |= a->q_flags; 228 } 229 a = q; 230 goto testselfdestruct; 231 } 232 } 233 234 /* add address on list */ 235 *pq = a; 236 a->q_next = NULL; 237 238 /* 239 ** Alias the name and handle special mailer types. 240 */ 241 242 trylocaluser: 243 if (tTd(29, 7)) 244 printf("at trylocaluser %s\n", a->q_user); 245 246 if (bitset(QDONTSEND|QBADADDR|QVERIFIED, a->q_flags)) 247 goto testselfdestruct; 248 249 if (m == InclMailer) 250 { 251 a->q_flags |= QDONTSEND; 252 if (a->q_alias == NULL) 253 { 254 a->q_flags |= QBADADDR; 255 usrerr("550 Cannot mail directly to :include:s"); 256 } 257 else 258 { 259 int ret; 260 261 message("including file %s", a->q_user); 262 ret = include(a->q_user, FALSE, a, sendq, e); 263 if (transienterror(ret)) 264 { 265 #ifdef LOG 266 if (LogLevel > 2) 267 syslog(LOG_ERR, "%s: include %s: transient error: %e", 268 e->e_id, a->q_user, errstring(ret)); 269 #endif 270 a->q_flags |= QQUEUEUP; 271 usrerr("451 Cannot open %s: %s", 272 a->q_user, errstring(ret)); 273 } 274 else if (ret != 0) 275 { 276 a->q_flags |= QBADADDR; 277 usrerr("550 Cannot open %s: %s", 278 a->q_user, errstring(ret)); 279 } 280 } 281 } 282 else if (m == FileMailer) 283 { 284 struct stat stb; 285 extern bool writable(); 286 287 p = strrchr(buf, '/'); 288 /* check if writable or creatable */ 289 if (a->q_alias == NULL) 290 { 291 a->q_flags |= QBADADDR; 292 usrerr("550 Cannot mail directly to files"); 293 } 294 else if ((stat(buf, &stb) >= 0) ? (!writable(&stb)) : 295 (*p = '\0', safefile(buf, RealUid, RealGid, NULL, TRUE, S_IWRITE|S_IEXEC) != 0)) 296 { 297 a->q_flags |= QBADADDR; 298 giveresponse(EX_CANTCREAT, m, NULL, a->q_alias, e); 299 } 300 } 301 302 if (m != LocalMailer) 303 { 304 if (!bitset(QDONTSEND, a->q_flags)) 305 e->e_nrcpts++; 306 goto testselfdestruct; 307 } 308 309 /* try aliasing */ 310 alias(a, sendq, e); 311 312 # ifdef USERDB 313 /* if not aliased, look it up in the user database */ 314 if (!bitset(QDONTSEND|QNOTREMOTE|QVERIFIED, a->q_flags)) 315 { 316 extern int udbexpand(); 317 extern int errno; 318 319 if (udbexpand(a, sendq, e) == EX_TEMPFAIL) 320 { 321 a->q_flags |= QQUEUEUP; 322 if (e->e_message == NULL) 323 e->e_message = newstr("Deferred: user database error"); 324 # ifdef LOG 325 if (LogLevel > 8) 326 syslog(LOG_INFO, "%s: deferred: udbexpand: %s", 327 e->e_id, errstring(errno)); 328 # endif 329 message("queued (user database error): %s", 330 errstring(errno)); 331 e->e_nrcpts++; 332 goto testselfdestruct; 333 } 334 } 335 # endif 336 337 /* if it was an alias or a UDB expansion, just return now */ 338 if (bitset(QDONTSEND|QQUEUEUP|QVERIFIED, a->q_flags)) 339 goto testselfdestruct; 340 341 /* 342 ** If we have a level two config file, then pass the name through 343 ** Ruleset 5 before sending it off. Ruleset 5 has the right 344 ** to send rewrite it to another mailer. This gives us a hook 345 ** after local aliasing has been done. 346 */ 347 348 if (tTd(29, 5)) 349 { 350 printf("recipient: testing local? cl=%d, rr5=%x\n\t", 351 ConfigLevel, RewriteRules[5]); 352 printaddr(a, FALSE); 353 } 354 if (!bitset(QNOTREMOTE, a->q_flags) && ConfigLevel >= 2 && 355 RewriteRules[5] != NULL) 356 { 357 maplocaluser(a, sendq, e); 358 } 359 360 /* 361 ** If it didn't get rewritten to another mailer, go ahead 362 ** and deliver it. 363 */ 364 365 if (!bitset(QDONTSEND|QQUEUEUP, a->q_flags)) 366 { 367 auto bool fuzzy; 368 register struct passwd *pw; 369 extern struct passwd *finduser(); 370 371 /* warning -- finduser may trash buf */ 372 pw = finduser(buf, &fuzzy); 373 if (pw == NULL) 374 { 375 a->q_flags |= QBADADDR; 376 giveresponse(EX_NOUSER, m, NULL, a->q_alias, e); 377 } 378 else 379 { 380 char nbuf[MAXNAME]; 381 382 if (fuzzy) 383 { 384 /* name was a fuzzy match */ 385 a->q_user = newstr(pw->pw_name); 386 if (findusercount++ > 3) 387 { 388 a->q_flags |= QBADADDR; 389 usrerr("554 aliasing/forwarding loop for %s broken", 390 pw->pw_name); 391 return (a); 392 } 393 394 /* see if it aliases */ 395 (void) strcpy(buf, pw->pw_name); 396 goto trylocaluser; 397 } 398 a->q_home = newstr(pw->pw_dir); 399 a->q_uid = pw->pw_uid; 400 a->q_gid = pw->pw_gid; 401 a->q_ruser = newstr(pw->pw_name); 402 a->q_flags |= QGOODUID; 403 buildfname(pw->pw_gecos, pw->pw_name, nbuf); 404 if (nbuf[0] != '\0') 405 a->q_fullname = newstr(nbuf); 406 if (!quoted) 407 forward(a, sendq, e); 408 } 409 } 410 if (!bitset(QDONTSEND, a->q_flags)) 411 e->e_nrcpts++; 412 413 testselfdestruct: 414 if (tTd(26, 8)) 415 { 416 printf("testselfdestruct: "); 417 printaddr(a, TRUE); 418 } 419 if (a->q_alias == NULL && a != &e->e_from && 420 bitset(QDONTSEND, a->q_flags)) 421 { 422 q = *sendq; 423 while (q != NULL && bitset(QDONTSEND, q->q_flags)) 424 q = q->q_next; 425 if (q == NULL) 426 { 427 a->q_flags |= QBADADDR; 428 usrerr("554 aliasing/forwarding loop broken"); 429 } 430 } 431 return (a); 432 } 433 /* 434 ** FINDUSER -- find the password entry for a user. 435 ** 436 ** This looks a lot like getpwnam, except that it may want to 437 ** do some fancier pattern matching in /etc/passwd. 438 ** 439 ** This routine contains most of the time of many sendmail runs. 440 ** It deserves to be optimized. 441 ** 442 ** Parameters: 443 ** name -- the name to match against. 444 ** fuzzyp -- an outarg that is set to TRUE if this entry 445 ** was found using the fuzzy matching algorithm; 446 ** set to FALSE otherwise. 447 ** 448 ** Returns: 449 ** A pointer to a pw struct. 450 ** NULL if name is unknown or ambiguous. 451 ** 452 ** Side Effects: 453 ** may modify name. 454 */ 455 456 struct passwd * 457 finduser(name, fuzzyp) 458 char *name; 459 bool *fuzzyp; 460 { 461 register struct passwd *pw; 462 register char *p; 463 extern struct passwd *getpwent(); 464 extern struct passwd *getpwnam(); 465 466 if (tTd(29, 4)) 467 printf("finduser(%s): ", name); 468 469 *fuzzyp = FALSE; 470 471 /* DEC Hesiod getpwnam accepts numeric strings -- short circuit it */ 472 for (p = name; *p != '\0'; p++) 473 if (!isascii(*p) || !isdigit(*p)) 474 break; 475 if (*p == '\0') 476 { 477 if (tTd(29, 4)) 478 printf("failed (numeric input)\n"); 479 return NULL; 480 } 481 482 /* look up this login name using fast path */ 483 if ((pw = getpwnam(name)) != NULL) 484 { 485 if (tTd(29, 4)) 486 printf("found (non-fuzzy)\n"); 487 return (pw); 488 } 489 490 #ifdef MATCHGECOS 491 /* see if fuzzy matching allowed */ 492 if (!MatchGecos) 493 { 494 if (tTd(29, 4)) 495 printf("not found (fuzzy disabled)\n"); 496 return NULL; 497 } 498 499 /* search for a matching full name instead */ 500 for (p = name; *p != '\0'; p++) 501 { 502 if (*p == (SpaceSub & 0177) || *p == '_') 503 *p = ' '; 504 } 505 (void) setpwent(); 506 while ((pw = getpwent()) != NULL) 507 { 508 char buf[MAXNAME]; 509 510 buildfname(pw->pw_gecos, pw->pw_name, buf); 511 if (strchr(buf, ' ') != NULL && !strcasecmp(buf, name)) 512 { 513 if (tTd(29, 4)) 514 printf("fuzzy matches %s\n", pw->pw_name); 515 message("sending to login name %s", pw->pw_name); 516 *fuzzyp = TRUE; 517 return (pw); 518 } 519 } 520 if (tTd(29, 4)) 521 printf("no fuzzy match found\n"); 522 #else 523 if (tTd(29, 4)) 524 printf("not found (fuzzy disabled)\n"); 525 #endif 526 return (NULL); 527 } 528 /* 529 ** WRITABLE -- predicate returning if the file is writable. 530 ** 531 ** This routine must duplicate the algorithm in sys/fio.c. 532 ** Unfortunately, we cannot use the access call since we 533 ** won't necessarily be the real uid when we try to 534 ** actually open the file. 535 ** 536 ** Notice that ANY file with ANY execute bit is automatically 537 ** not writable. This is also enforced by mailfile. 538 ** 539 ** Parameters: 540 ** s -- pointer to a stat struct for the file. 541 ** 542 ** Returns: 543 ** TRUE -- if we will be able to write this file. 544 ** FALSE -- if we cannot write this file. 545 ** 546 ** Side Effects: 547 ** none. 548 */ 549 550 bool 551 writable(s) 552 register struct stat *s; 553 { 554 uid_t euid; 555 gid_t egid; 556 int bits; 557 558 if (bitset(0111, s->st_mode)) 559 return (FALSE); 560 euid = RealUid; 561 egid = RealGid; 562 if (geteuid() == 0) 563 { 564 if (bitset(S_ISUID, s->st_mode)) 565 euid = s->st_uid; 566 if (bitset(S_ISGID, s->st_mode)) 567 egid = s->st_gid; 568 } 569 570 if (euid == 0) 571 return (TRUE); 572 bits = S_IWRITE; 573 if (euid != s->st_uid) 574 { 575 bits >>= 3; 576 if (egid != s->st_gid) 577 bits >>= 3; 578 } 579 return ((s->st_mode & bits) != 0); 580 } 581 /* 582 ** INCLUDE -- handle :include: specification. 583 ** 584 ** Parameters: 585 ** fname -- filename to include. 586 ** forwarding -- if TRUE, we are reading a .forward file. 587 ** if FALSE, it's a :include: file. 588 ** ctladdr -- address template to use to fill in these 589 ** addresses -- effective user/group id are 590 ** the important things. 591 ** sendq -- a pointer to the head of the send queue 592 ** to put these addresses in. 593 ** 594 ** Returns: 595 ** open error status 596 ** 597 ** Side Effects: 598 ** reads the :include: file and sends to everyone 599 ** listed in that file. 600 */ 601 602 static jmp_buf CtxIncludeTimeout; 603 static int includetimeout(); 604 605 int 606 include(fname, forwarding, ctladdr, sendq, e) 607 char *fname; 608 bool forwarding; 609 ADDRESS *ctladdr; 610 ADDRESS **sendq; 611 ENVELOPE *e; 612 { 613 register FILE *fp = NULL; 614 char *oldto = e->e_to; 615 char *oldfilename = FileName; 616 int oldlinenumber = LineNumber; 617 register EVENT *ev = NULL; 618 int nincludes; 619 register ADDRESS *ca; 620 uid_t saveduid, uid; 621 gid_t savedgid, gid; 622 char *uname; 623 int rval = 0; 624 char buf[MAXLINE]; 625 626 if (tTd(27, 2)) 627 printf("include(%s)\n", fname); 628 if (tTd(27, 4)) 629 printf(" ruid=%d euid=%d\n", getuid(), geteuid()); 630 if (tTd(27, 14)) 631 { 632 printf("ctladdr "); 633 printaddr(ctladdr, FALSE); 634 } 635 636 if (tTd(27, 9)) 637 printf("include: old uid = %d/%d\n", getuid(), geteuid()); 638 639 ca = getctladdr(ctladdr); 640 if (ca == NULL) 641 { 642 uid = 0; 643 gid = 0; 644 uname = NULL; 645 saveduid = -1; 646 } 647 else 648 { 649 uid = ca->q_uid; 650 gid = ca->q_gid; 651 uname = ca->q_user; 652 #ifdef HASSETREUID 653 saveduid = geteuid(); 654 savedgid = getegid(); 655 if (saveduid == 0) 656 { 657 initgroups(uname, gid); 658 if (uid != 0) 659 (void) setreuid(0, uid); 660 } 661 #endif 662 } 663 664 if (tTd(27, 9)) 665 printf("include: new uid = %d/%d\n", getuid(), geteuid()); 666 667 /* 668 ** If home directory is remote mounted but server is down, 669 ** this can hang or give errors; use a timeout to avoid this 670 */ 671 672 if (setjmp(CtxIncludeTimeout) != 0) 673 { 674 ctladdr->q_flags |= QQUEUEUP; 675 errno = 0; 676 usrerr("451 open timeout on %s", fname); 677 678 /* return pseudo-error code */ 679 rval = EOPENTIMEOUT; 680 goto resetuid; 681 } 682 ev = setevent((time_t) 60, includetimeout, 0); 683 684 /* the input file must be marked safe */ 685 rval = safefile(fname, uid, gid, uname, forwarding, S_IREAD); 686 if (rval != 0) 687 { 688 /* don't use this :include: file */ 689 clrevent(ev); 690 if (tTd(27, 4)) 691 printf("include: not safe (uid=%d): %s\n", 692 uid, errstring(rval)); 693 goto resetuid; 694 } 695 696 fp = fopen(fname, "r"); 697 if (fp == NULL) 698 { 699 rval = errno; 700 if (tTd(27, 4)) 701 printf("include: open: %s\n", errstring(rval)); 702 } 703 else if (ca == NULL) 704 { 705 struct stat st; 706 707 if (fstat(fileno(fp), &st) < 0) 708 { 709 rval = errno; 710 syserr("Cannot fstat %s!", fname); 711 } 712 else 713 { 714 ctladdr->q_uid = st.st_uid; 715 ctladdr->q_gid = st.st_gid; 716 ctladdr->q_flags |= QGOODUID; 717 } 718 } 719 720 clrevent(ev); 721 722 resetuid: 723 724 #ifdef HASSETREUID 725 if (saveduid == 0) 726 { 727 if (uid != 0) 728 if (setreuid(-1, 0) < 0 || setreuid(RealUid, 0) < 0) 729 syserr("setreuid(%d, 0) failure (real=%d, eff=%d)", 730 RealUid, getuid(), geteuid()); 731 setgid(savedgid); 732 } 733 #endif 734 735 if (tTd(27, 9)) 736 printf("include: reset uid = %d/%d\n", getuid(), geteuid()); 737 738 if (fp == NULL) 739 return rval; 740 741 if (bitset(EF_VRFYONLY, e->e_flags)) 742 { 743 /* don't do any more now */ 744 ctladdr->q_flags |= QVERIFIED; 745 e->e_nrcpts++; 746 xfclose(fp, "include", fname); 747 return rval; 748 } 749 750 /* read the file -- each line is a comma-separated list. */ 751 FileName = fname; 752 LineNumber = 0; 753 ctladdr->q_flags &= ~QSELFREF; 754 nincludes = 0; 755 while (fgets(buf, sizeof buf, fp) != NULL) 756 { 757 register char *p = strchr(buf, '\n'); 758 759 LineNumber++; 760 if (p != NULL) 761 *p = '\0'; 762 if (buf[0] == '#' || buf[0] == '\0') 763 continue; 764 e->e_to = NULL; 765 message("%s to %s", 766 forwarding ? "forwarding" : "sending", buf); 767 #ifdef LOG 768 if (forwarding && LogLevel > 9) 769 syslog(LOG_INFO, "%s: forward %s => %s", 770 e->e_id, oldto, buf); 771 #endif 772 773 AliasLevel++; 774 nincludes += sendtolist(buf, ctladdr, sendq, e); 775 AliasLevel--; 776 } 777 778 if (ferror(fp) && tTd(27, 3)) 779 printf("include: read error: %s\n", errstring(errno)); 780 if (nincludes > 0 && !bitset(QSELFREF, ctladdr->q_flags)) 781 { 782 if (tTd(27, 5)) 783 { 784 printf("include: QDONTSEND "); 785 printaddr(ctladdr, FALSE); 786 } 787 ctladdr->q_flags |= QDONTSEND; 788 } 789 790 (void) xfclose(fp, "include", fname); 791 FileName = oldfilename; 792 LineNumber = oldlinenumber; 793 e->e_to = oldto; 794 return rval; 795 } 796 797 static 798 includetimeout() 799 { 800 longjmp(CtxIncludeTimeout, 1); 801 } 802 /* 803 ** SENDTOARGV -- send to an argument vector. 804 ** 805 ** Parameters: 806 ** argv -- argument vector to send to. 807 ** e -- the current envelope. 808 ** 809 ** Returns: 810 ** none. 811 ** 812 ** Side Effects: 813 ** puts all addresses on the argument vector onto the 814 ** send queue. 815 */ 816 817 sendtoargv(argv, e) 818 register char **argv; 819 register ENVELOPE *e; 820 { 821 register char *p; 822 823 while ((p = *argv++) != NULL) 824 { 825 (void) sendtolist(p, NULLADDR, &e->e_sendqueue, e); 826 } 827 } 828 /* 829 ** GETCTLADDR -- get controlling address from an address header. 830 ** 831 ** If none, get one corresponding to the effective userid. 832 ** 833 ** Parameters: 834 ** a -- the address to find the controller of. 835 ** 836 ** Returns: 837 ** the controlling address. 838 ** 839 ** Side Effects: 840 ** none. 841 */ 842 843 ADDRESS * 844 getctladdr(a) 845 register ADDRESS *a; 846 { 847 while (a != NULL && !bitset(QGOODUID, a->q_flags)) 848 a = a->q_alias; 849 return (a); 850 } 851