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