1 /* 2 * Copyright (c) 1980, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 4. Neither the name of the University nor the names of its contributors 14 * may be used to endorse or promote products derived from this software 15 * without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 */ 29 30 #ifndef lint 31 #if 0 32 static char sccsid[] = "@(#)names.c 8.1 (Berkeley) 6/6/93"; 33 #endif 34 #endif /* not lint */ 35 #include <sys/cdefs.h> 36 __FBSDID("$FreeBSD$"); 37 38 /* 39 * Mail -- a mail program 40 * 41 * Handle name lists. 42 */ 43 44 #include "rcv.h" 45 #include <fcntl.h> 46 #include "extern.h" 47 48 /* 49 * Allocate a single element of a name list, 50 * initialize its name field to the passed 51 * name and return it. 52 */ 53 struct name * 54 nalloc(str, ntype) 55 char str[]; 56 int ntype; 57 { 58 struct name *np; 59 60 np = (struct name *)salloc(sizeof(*np)); 61 np->n_flink = NULL; 62 np->n_blink = NULL; 63 np->n_type = ntype; 64 np->n_name = savestr(str); 65 return (np); 66 } 67 68 /* 69 * Find the tail of a list and return it. 70 */ 71 struct name * 72 tailof(name) 73 struct name *name; 74 { 75 struct name *np; 76 77 np = name; 78 if (np == NULL) 79 return (NULL); 80 while (np->n_flink != NULL) 81 np = np->n_flink; 82 return (np); 83 } 84 85 /* 86 * Extract a list of names from a line, 87 * and make a list of names from it. 88 * Return the list or NULL if none found. 89 */ 90 struct name * 91 extract(line, ntype) 92 char line[]; 93 int ntype; 94 { 95 char *cp, *nbuf; 96 struct name *top, *np, *t; 97 98 if (line == NULL || *line == '\0') 99 return (NULL); 100 if ((nbuf = malloc(strlen(line) + 1)) == NULL) 101 err(1, "Out of memory"); 102 top = NULL; 103 np = NULL; 104 cp = line; 105 while ((cp = yankword(cp, nbuf)) != NULL) { 106 t = nalloc(nbuf, ntype); 107 if (top == NULL) 108 top = t; 109 else 110 np->n_flink = t; 111 t->n_blink = np; 112 np = t; 113 } 114 (void)free(nbuf); 115 return (top); 116 } 117 118 /* 119 * Turn a list of names into a string of the same names. 120 */ 121 char * 122 detract(np, ntype) 123 struct name *np; 124 int ntype; 125 { 126 int s, comma; 127 char *cp, *top; 128 struct name *p; 129 130 comma = ntype & GCOMMA; 131 if (np == NULL) 132 return (NULL); 133 ntype &= ~GCOMMA; 134 s = 0; 135 if (debug && comma) 136 fprintf(stderr, "detract asked to insert commas\n"); 137 for (p = np; p != NULL; p = p->n_flink) { 138 if (ntype && (p->n_type & GMASK) != ntype) 139 continue; 140 s += strlen(p->n_name) + 1; 141 if (comma) 142 s++; 143 } 144 if (s == 0) 145 return (NULL); 146 s += 2; 147 top = salloc(s); 148 cp = top; 149 for (p = np; p != NULL; p = p->n_flink) { 150 if (ntype && (p->n_type & GMASK) != ntype) 151 continue; 152 cp += strlcpy(cp, p->n_name, strlen(p->n_name) + 1); 153 if (comma && p->n_flink != NULL) 154 *cp++ = ','; 155 *cp++ = ' '; 156 } 157 *--cp = '\0'; 158 if (comma && *--cp == ',') 159 *cp = '\0'; 160 return (top); 161 } 162 163 /* 164 * Grab a single word (liberal word) 165 * Throw away things between ()'s, and take anything between <>. 166 */ 167 char * 168 yankword(ap, wbuf) 169 char *ap, wbuf[]; 170 { 171 char *cp, *cp2; 172 173 cp = ap; 174 for (;;) { 175 if (*cp == '\0') 176 return (NULL); 177 if (*cp == '(') { 178 int nesting = 0; 179 180 while (*cp != '\0') { 181 switch (*cp++) { 182 case '(': 183 nesting++; 184 break; 185 case ')': 186 --nesting; 187 break; 188 } 189 if (nesting <= 0) 190 break; 191 } 192 } else if (*cp == ' ' || *cp == '\t' || *cp == ',') 193 cp++; 194 else 195 break; 196 } 197 if (*cp == '<') 198 for (cp2 = wbuf; *cp && (*cp2++ = *cp++) != '>';) 199 ; 200 else 201 for (cp2 = wbuf; *cp != '\0' && strchr(" \t,(", *cp) == NULL; 202 *cp2++ = *cp++) 203 ; 204 *cp2 = '\0'; 205 return (cp); 206 } 207 208 /* 209 * Grab a single login name (liberal word) 210 * Throw away things between ()'s, take anything between <>, 211 * and look for words before metacharacters %, @, !. 212 */ 213 char * 214 yanklogin(ap, wbuf) 215 char *ap, wbuf[]; 216 { 217 char *cp, *cp2, *cp_temp; 218 int n; 219 220 cp = ap; 221 for (;;) { 222 if (*cp == '\0') 223 return (NULL); 224 if (*cp == '(') { 225 int nesting = 0; 226 227 while (*cp != '\0') { 228 switch (*cp++) { 229 case '(': 230 nesting++; 231 break; 232 case ')': 233 --nesting; 234 break; 235 } 236 if (nesting <= 0) 237 break; 238 } 239 } else if (*cp == ' ' || *cp == '\t' || *cp == ',') 240 cp++; 241 else 242 break; 243 } 244 245 /* 246 * Now, let's go forward till we meet the needed character, 247 * and step one word back. 248 */ 249 250 /* First, remember current point. */ 251 cp_temp = cp; 252 n = 0; 253 254 /* 255 * Note that we look ahead in a cycle. This is safe, since 256 * non-end of string is checked first. 257 */ 258 while(*cp != '\0' && strchr("@%!", *(cp + 1)) == NULL) 259 cp++; 260 261 /* 262 * Now, start stepping back to the first non-word character, 263 * while counting the number of symbols in a word. 264 */ 265 while(cp != cp_temp && strchr(" \t,<>", *(cp - 1)) == NULL) { 266 n++; 267 cp--; 268 } 269 270 /* Finally, grab the word forward. */ 271 cp2 = wbuf; 272 while(n >= 0) { 273 *cp2++=*cp++; 274 n--; 275 } 276 277 *cp2 = '\0'; 278 return (cp); 279 } 280 281 /* 282 * For each recipient in the passed name list with a / 283 * in the name, append the message to the end of the named file 284 * and remove him from the recipient list. 285 * 286 * Recipients whose name begins with | are piped through the given 287 * program and removed. 288 */ 289 struct name * 290 outof(names, fo, hp) 291 struct name *names; 292 FILE *fo; 293 struct header *hp; 294 { 295 int c, ispipe; 296 struct name *np, *top; 297 time_t now; 298 char *date, *fname; 299 FILE *fout, *fin; 300 301 top = names; 302 np = names; 303 (void)time(&now); 304 date = ctime(&now); 305 while (np != NULL) { 306 if (!isfileaddr(np->n_name) && np->n_name[0] != '|') { 307 np = np->n_flink; 308 continue; 309 } 310 ispipe = np->n_name[0] == '|'; 311 if (ispipe) 312 fname = np->n_name+1; 313 else 314 fname = expand(np->n_name); 315 316 /* 317 * See if we have copied the complete message out yet. 318 * If not, do so. 319 */ 320 321 if (image < 0) { 322 int fd; 323 char tempname[PATHSIZE]; 324 325 (void)snprintf(tempname, sizeof(tempname), 326 "%s/mail.ReXXXXXXXXXX", tmpdir); 327 if ((fd = mkstemp(tempname)) == -1 || 328 (fout = Fdopen(fd, "a")) == NULL) { 329 warn("%s", tempname); 330 senderr++; 331 goto cant; 332 } 333 image = open(tempname, O_RDWR); 334 (void)rm(tempname); 335 if (image < 0) { 336 warn("%s", tempname); 337 senderr++; 338 (void)Fclose(fout); 339 goto cant; 340 } 341 (void)fcntl(image, F_SETFD, 1); 342 fprintf(fout, "From %s %s", myname, date); 343 puthead(hp, fout, 344 GTO|GSUBJECT|GCC|GREPLYTO|GINREPLYTO|GNL); 345 while ((c = getc(fo)) != EOF) 346 (void)putc(c, fout); 347 rewind(fo); 348 fprintf(fout, "\n"); 349 (void)fflush(fout); 350 if (ferror(fout)) { 351 warn("%s", tempname); 352 senderr++; 353 (void)Fclose(fout); 354 goto cant; 355 } 356 (void)Fclose(fout); 357 } 358 359 /* 360 * Now either copy "image" to the desired file 361 * or give it as the standard input to the desired 362 * program as appropriate. 363 */ 364 365 if (ispipe) { 366 int pid; 367 char *sh; 368 sigset_t nset; 369 370 /* 371 * XXX 372 * We can't really reuse the same image file, 373 * because multiple piped recipients will 374 * share the same lseek location and trample 375 * on one another. 376 */ 377 if ((sh = value("SHELL")) == NULL) 378 sh = _PATH_CSHELL; 379 (void)sigemptyset(&nset); 380 (void)sigaddset(&nset, SIGHUP); 381 (void)sigaddset(&nset, SIGINT); 382 (void)sigaddset(&nset, SIGQUIT); 383 pid = start_command(sh, &nset, image, -1, "-c", fname, 384 NULL); 385 if (pid < 0) { 386 senderr++; 387 goto cant; 388 } 389 free_child(pid); 390 } else { 391 int f; 392 if ((fout = Fopen(fname, "a")) == NULL) { 393 warn("%s", fname); 394 senderr++; 395 goto cant; 396 } 397 if ((f = dup(image)) < 0) { 398 warn("dup"); 399 fin = NULL; 400 } else 401 fin = Fdopen(f, "r"); 402 if (fin == NULL) { 403 fprintf(stderr, "Can't reopen image\n"); 404 (void)Fclose(fout); 405 senderr++; 406 goto cant; 407 } 408 rewind(fin); 409 while ((c = getc(fin)) != EOF) 410 (void)putc(c, fout); 411 if (ferror(fout)) { 412 warnx("%s", fname); 413 senderr++; 414 (void)Fclose(fout); 415 (void)Fclose(fin); 416 goto cant; 417 } 418 (void)Fclose(fout); 419 (void)Fclose(fin); 420 } 421 cant: 422 /* 423 * In days of old we removed the entry from the 424 * the list; now for sake of header expansion 425 * we leave it in and mark it as deleted. 426 */ 427 np->n_type |= GDEL; 428 np = np->n_flink; 429 } 430 if (image >= 0) { 431 (void)close(image); 432 image = -1; 433 } 434 return (top); 435 } 436 437 /* 438 * Determine if the passed address is a local "send to file" address. 439 * If any of the network metacharacters precedes any slashes, it can't 440 * be a filename. We cheat with .'s to allow path names like ./... 441 */ 442 int 443 isfileaddr(name) 444 char *name; 445 { 446 char *cp; 447 448 if (*name == '+') 449 return (1); 450 for (cp = name; *cp != '\0'; cp++) { 451 if (*cp == '!' || *cp == '%' || *cp == '@') 452 return (0); 453 if (*cp == '/') 454 return (1); 455 } 456 return (0); 457 } 458 459 /* 460 * Map all of the aliased users in the invoker's mailrc 461 * file and insert them into the list. 462 * Changed after all these months of service to recursively 463 * expand names (2/14/80). 464 */ 465 466 struct name * 467 usermap(names) 468 struct name *names; 469 { 470 struct name *new, *np, *cp; 471 struct grouphead *gh; 472 int metoo; 473 474 new = NULL; 475 np = names; 476 metoo = (value("metoo") != NULL); 477 while (np != NULL) { 478 if (np->n_name[0] == '\\') { 479 cp = np->n_flink; 480 new = put(new, np); 481 np = cp; 482 continue; 483 } 484 gh = findgroup(np->n_name); 485 cp = np->n_flink; 486 if (gh != NULL) 487 new = gexpand(new, gh, metoo, np->n_type); 488 else 489 new = put(new, np); 490 np = cp; 491 } 492 return (new); 493 } 494 495 /* 496 * Recursively expand a group name. We limit the expansion to some 497 * fixed level to keep things from going haywire. 498 * Direct recursion is not expanded for convenience. 499 */ 500 501 struct name * 502 gexpand(nlist, gh, metoo, ntype) 503 struct name *nlist; 504 struct grouphead *gh; 505 int metoo, ntype; 506 { 507 struct group *gp; 508 struct grouphead *ngh; 509 struct name *np; 510 static int depth; 511 char *cp; 512 513 if (depth > MAXEXP) { 514 printf("Expanding alias to depth larger than %d\n", MAXEXP); 515 return (nlist); 516 } 517 depth++; 518 for (gp = gh->g_list; gp != NULL; gp = gp->ge_link) { 519 cp = gp->ge_name; 520 if (*cp == '\\') 521 goto quote; 522 if (strcmp(cp, gh->g_name) == 0) 523 goto quote; 524 if ((ngh = findgroup(cp)) != NULL) { 525 nlist = gexpand(nlist, ngh, metoo, ntype); 526 continue; 527 } 528 quote: 529 np = nalloc(cp, ntype); 530 /* 531 * At this point should allow to expand 532 * to self if only person in group 533 */ 534 if (gp == gh->g_list && gp->ge_link == NULL) 535 goto skip; 536 if (!metoo && strcmp(cp, myname) == 0) 537 np->n_type |= GDEL; 538 skip: 539 nlist = put(nlist, np); 540 } 541 depth--; 542 return (nlist); 543 } 544 545 /* 546 * Concatenate the two passed name lists, return the result. 547 */ 548 struct name * 549 cat(n1, n2) 550 struct name *n1, *n2; 551 { 552 struct name *tail; 553 554 if (n1 == NULL) 555 return (n2); 556 if (n2 == NULL) 557 return (n1); 558 tail = tailof(n1); 559 tail->n_flink = n2; 560 n2->n_blink = tail; 561 return (n1); 562 } 563 564 /* 565 * Unpack the name list onto a vector of strings. 566 * Return an error if the name list won't fit. 567 */ 568 char ** 569 unpack(np) 570 struct name *np; 571 { 572 char **ap, **top; 573 struct name *n; 574 int t, extra, metoo, verbose; 575 576 n = np; 577 if ((t = count(n)) == 0) 578 errx(1, "No names to unpack"); 579 /* 580 * Compute the number of extra arguments we will need. 581 * We need at least two extra -- one for "mail" and one for 582 * the terminating 0 pointer. Additional spots may be needed 583 * to pass along -f to the host mailer. 584 */ 585 extra = 2; 586 extra++; 587 metoo = value("metoo") != NULL; 588 if (metoo) 589 extra++; 590 verbose = value("verbose") != NULL; 591 if (verbose) 592 extra++; 593 top = (char **)salloc((t + extra) * sizeof(*top)); 594 ap = top; 595 *ap++ = "send-mail"; 596 *ap++ = "-i"; 597 if (metoo) 598 *ap++ = "-m"; 599 if (verbose) 600 *ap++ = "-v"; 601 for (; n != NULL; n = n->n_flink) 602 if ((n->n_type & GDEL) == 0) 603 *ap++ = n->n_name; 604 *ap = NULL; 605 return (top); 606 } 607 608 /* 609 * Remove all of the duplicates from the passed name list by 610 * insertion sorting them, then checking for dups. 611 * Return the head of the new list. 612 */ 613 struct name * 614 elide(names) 615 struct name *names; 616 { 617 struct name *np, *t, *new; 618 struct name *x; 619 620 if (names == NULL) 621 return (NULL); 622 new = names; 623 np = names; 624 np = np->n_flink; 625 if (np != NULL) 626 np->n_blink = NULL; 627 new->n_flink = NULL; 628 while (np != NULL) { 629 t = new; 630 while (strcasecmp(t->n_name, np->n_name) < 0) { 631 if (t->n_flink == NULL) 632 break; 633 t = t->n_flink; 634 } 635 636 /* 637 * If we ran out of t's, put the new entry after 638 * the current value of t. 639 */ 640 641 if (strcasecmp(t->n_name, np->n_name) < 0) { 642 t->n_flink = np; 643 np->n_blink = t; 644 t = np; 645 np = np->n_flink; 646 t->n_flink = NULL; 647 continue; 648 } 649 650 /* 651 * Otherwise, put the new entry in front of the 652 * current t. If at the front of the list, 653 * the new guy becomes the new head of the list. 654 */ 655 656 if (t == new) { 657 t = np; 658 np = np->n_flink; 659 t->n_flink = new; 660 new->n_blink = t; 661 t->n_blink = NULL; 662 new = t; 663 continue; 664 } 665 666 /* 667 * The normal case -- we are inserting into the 668 * middle of the list. 669 */ 670 671 x = np; 672 np = np->n_flink; 673 x->n_flink = t; 674 x->n_blink = t->n_blink; 675 t->n_blink->n_flink = x; 676 t->n_blink = x; 677 } 678 679 /* 680 * Now the list headed up by new is sorted. 681 * Go through it and remove duplicates. 682 */ 683 684 np = new; 685 while (np != NULL) { 686 t = np; 687 while (t->n_flink != NULL && 688 strcasecmp(np->n_name, t->n_flink->n_name) == 0) 689 t = t->n_flink; 690 if (t == np || t == NULL) { 691 np = np->n_flink; 692 continue; 693 } 694 695 /* 696 * Now t points to the last entry with the same name 697 * as np. Make np point beyond t. 698 */ 699 700 np->n_flink = t->n_flink; 701 if (t->n_flink != NULL) 702 t->n_flink->n_blink = np; 703 np = np->n_flink; 704 } 705 return (new); 706 } 707 708 /* 709 * Put another node onto a list of names and return 710 * the list. 711 */ 712 struct name * 713 put(list, node) 714 struct name *list, *node; 715 { 716 node->n_flink = list; 717 node->n_blink = NULL; 718 if (list != NULL) 719 list->n_blink = node; 720 return (node); 721 } 722 723 /* 724 * Determine the number of undeleted elements in 725 * a name list and return it. 726 */ 727 int 728 count(np) 729 struct name *np; 730 { 731 int c; 732 733 for (c = 0; np != NULL; np = np->n_flink) 734 if ((np->n_type & GDEL) == 0) 735 c++; 736 return (c); 737 } 738 739 /* 740 * Delete the given name from a namelist. 741 */ 742 struct name * 743 delname(np, name) 744 struct name *np; 745 char name[]; 746 { 747 struct name *p; 748 749 for (p = np; p != NULL; p = p->n_flink) 750 if (strcasecmp(p->n_name, name) == 0) { 751 if (p->n_blink == NULL) { 752 if (p->n_flink != NULL) 753 p->n_flink->n_blink = NULL; 754 np = p->n_flink; 755 continue; 756 } 757 if (p->n_flink == NULL) { 758 if (p->n_blink != NULL) 759 p->n_blink->n_flink = NULL; 760 continue; 761 } 762 p->n_blink->n_flink = p->n_flink; 763 p->n_flink->n_blink = p->n_blink; 764 } 765 return (np); 766 } 767 768 /* 769 * Pretty print a name list 770 * Uncomment it if you need it. 771 */ 772 773 /* 774 void 775 prettyprint(name) 776 struct name *name; 777 { 778 struct name *np; 779 780 np = name; 781 while (np != NULL) { 782 fprintf(stderr, "%s(%d) ", np->n_name, np->n_type); 783 np = np->n_flink; 784 } 785 fprintf(stderr, "\n"); 786 } 787 */ 788