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