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