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