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