1 /* $NetBSD: names.c,v 1.5 1996/06/08 19:48:32 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 #ifndef lint 37 #if 0 38 static char sccsid[] = "@(#)names.c 8.1 (Berkeley) 6/6/93"; 39 #else 40 static char rcsid[] = "$NetBSD: names.c,v 1.5 1996/06/08 19:48:32 christos Exp $"; 41 #endif 42 #endif /* not lint */ 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 */ 59 struct name * 60 nalloc(str, ntype) 61 char str[]; 62 int ntype; 63 { 64 register 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 register 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 register char *cp; 102 register 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 register struct name *np; 128 int ntype; 129 { 130 register int s; 131 register char *cp, *top; 132 register struct name *p; 133 register 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 register char *cp, *cp2; 177 178 cp = ap; 179 for (;;) { 180 if (*cp == '\0') 181 return NOSTR; 182 if (*cp == '(') { 183 register 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 register int c; 227 register 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 (void) Fclose(fout); 279 } 280 281 /* 282 * Now either copy "image" to the desired file 283 * or give it as the standard input to the desired 284 * program as appropriate. 285 */ 286 287 if (ispipe) { 288 int pid; 289 char *shell; 290 sigset_t nset; 291 292 /* 293 * XXX 294 * We can't really reuse the same image file, 295 * because multiple piped recipients will 296 * share the same lseek location and trample 297 * on one another. 298 */ 299 if ((shell = value("SHELL")) == NOSTR) 300 shell = _PATH_CSHELL; 301 sigemptyset(&nset); 302 sigaddset(&nset, SIGHUP); 303 sigaddset(&nset, SIGINT); 304 sigaddset(&nset, SIGQUIT); 305 pid = start_command(shell, &nset, 306 image, -1, "-c", fname, NOSTR); 307 if (pid < 0) { 308 senderr++; 309 goto cant; 310 } 311 free_child(pid); 312 } else { 313 int f; 314 if ((fout = Fopen(fname, "a")) == NULL) { 315 perror(fname); 316 senderr++; 317 goto cant; 318 } 319 if ((f = dup(image)) < 0) { 320 perror("dup"); 321 fin = NULL; 322 } else 323 fin = Fdopen(f, "r"); 324 if (fin == NULL) { 325 fprintf(stderr, "Can't reopen image\n"); 326 (void) Fclose(fout); 327 senderr++; 328 goto cant; 329 } 330 rewind(fin); 331 while ((c = getc(fin)) != EOF) 332 (void) putc(c, fout); 333 if (ferror(fout)) 334 senderr++, perror(fname); 335 (void) Fclose(fout); 336 (void) Fclose(fin); 337 } 338 cant: 339 /* 340 * In days of old we removed the entry from the 341 * the list; now for sake of header expansion 342 * we leave it in and mark it as deleted. 343 */ 344 np->n_type |= GDEL; 345 np = np->n_flink; 346 } 347 if (image >= 0) { 348 (void) close(image); 349 image = -1; 350 } 351 return(top); 352 } 353 354 /* 355 * Determine if the passed address is a local "send to file" address. 356 * If any of the network metacharacters precedes any slashes, it can't 357 * be a filename. We cheat with .'s to allow path names like ./... 358 */ 359 int 360 isfileaddr(name) 361 char *name; 362 { 363 register char *cp; 364 365 if (*name == '+') 366 return 1; 367 for (cp = name; *cp; cp++) { 368 if (*cp == '!' || *cp == '%' || *cp == '@') 369 return 0; 370 if (*cp == '/') 371 return 1; 372 } 373 return 0; 374 } 375 376 /* 377 * Map all of the aliased users in the invoker's mailrc 378 * file and insert them into the list. 379 * Changed after all these months of service to recursively 380 * expand names (2/14/80). 381 */ 382 383 struct name * 384 usermap(names) 385 struct name *names; 386 { 387 register struct name *new, *np, *cp; 388 struct grouphead *gh; 389 register int metoo; 390 391 new = NIL; 392 np = names; 393 metoo = (value("metoo") != NOSTR); 394 while (np != NIL) { 395 if (np->n_name[0] == '\\') { 396 cp = np->n_flink; 397 new = put(new, np); 398 np = cp; 399 continue; 400 } 401 gh = findgroup(np->n_name); 402 cp = np->n_flink; 403 if (gh != NOGRP) 404 new = gexpand(new, gh, metoo, np->n_type); 405 else 406 new = put(new, np); 407 np = cp; 408 } 409 return(new); 410 } 411 412 /* 413 * Recursively expand a group name. We limit the expansion to some 414 * fixed level to keep things from going haywire. 415 * Direct recursion is not expanded for convenience. 416 */ 417 418 struct name * 419 gexpand(nlist, gh, metoo, ntype) 420 struct name *nlist; 421 struct grouphead *gh; 422 int metoo, ntype; 423 { 424 struct group *gp; 425 struct grouphead *ngh; 426 struct name *np; 427 static int depth; 428 char *cp; 429 430 if (depth > MAXEXP) { 431 printf("Expanding alias to depth larger than %d\n", MAXEXP); 432 return(nlist); 433 } 434 depth++; 435 for (gp = gh->g_list; gp != NOGE; gp = gp->ge_link) { 436 cp = gp->ge_name; 437 if (*cp == '\\') 438 goto quote; 439 if (strcmp(cp, gh->g_name) == 0) 440 goto quote; 441 if ((ngh = findgroup(cp)) != NOGRP) { 442 nlist = gexpand(nlist, ngh, metoo, ntype); 443 continue; 444 } 445 quote: 446 np = nalloc(cp, ntype); 447 /* 448 * At this point should allow to expand 449 * to self if only person in group 450 */ 451 if (gp == gh->g_list && gp->ge_link == NOGE) 452 goto skip; 453 if (!metoo && strcmp(cp, myname) == 0) 454 np->n_type |= GDEL; 455 skip: 456 nlist = put(nlist, np); 457 } 458 depth--; 459 return(nlist); 460 } 461 462 /* 463 * Concatenate the two passed name lists, return the result. 464 */ 465 struct name * 466 cat(n1, n2) 467 struct name *n1, *n2; 468 { 469 register struct name *tail; 470 471 if (n1 == NIL) 472 return(n2); 473 if (n2 == NIL) 474 return(n1); 475 tail = tailof(n1); 476 tail->n_flink = n2; 477 n2->n_blink = tail; 478 return(n1); 479 } 480 481 /* 482 * Unpack the name list onto a vector of strings. 483 * Return an error if the name list won't fit. 484 */ 485 char ** 486 unpack(np) 487 struct name *np; 488 { 489 register char **ap, **top; 490 register struct name *n; 491 int t, extra, metoo, verbose; 492 493 n = np; 494 if ((t = count(n)) == 0) 495 panic("No names to unpack"); 496 /* 497 * Compute the number of extra arguments we will need. 498 * We need at least two extra -- one for "mail" and one for 499 * the terminating 0 pointer. Additional spots may be needed 500 * to pass along -f to the host mailer. 501 */ 502 extra = 2; 503 extra++; 504 metoo = value("metoo") != NOSTR; 505 if (metoo) 506 extra++; 507 verbose = value("verbose") != NOSTR; 508 if (verbose) 509 extra++; 510 top = (char **) salloc((t + extra) * sizeof *top); 511 ap = top; 512 *ap++ = "send-mail"; 513 *ap++ = "-i"; 514 if (metoo) 515 *ap++ = "-m"; 516 if (verbose) 517 *ap++ = "-v"; 518 for (; n != NIL; n = n->n_flink) 519 if ((n->n_type & GDEL) == 0) 520 *ap++ = n->n_name; 521 *ap = NOSTR; 522 return(top); 523 } 524 525 /* 526 * Remove all of the duplicates from the passed name list by 527 * insertion sorting them, then checking for dups. 528 * Return the head of the new list. 529 */ 530 struct name * 531 elide(names) 532 struct name *names; 533 { 534 register struct name *np, *t, *new; 535 struct name *x; 536 537 if (names == NIL) 538 return(NIL); 539 new = names; 540 np = names; 541 np = np->n_flink; 542 if (np != NIL) 543 np->n_blink = NIL; 544 new->n_flink = NIL; 545 while (np != NIL) { 546 t = new; 547 while (strcasecmp(t->n_name, np->n_name) < 0) { 548 if (t->n_flink == NIL) 549 break; 550 t = t->n_flink; 551 } 552 553 /* 554 * If we ran out of t's, put the new entry after 555 * the current value of t. 556 */ 557 558 if (strcasecmp(t->n_name, np->n_name) < 0) { 559 t->n_flink = np; 560 np->n_blink = t; 561 t = np; 562 np = np->n_flink; 563 t->n_flink = NIL; 564 continue; 565 } 566 567 /* 568 * Otherwise, put the new entry in front of the 569 * current t. If at the front of the list, 570 * the new guy becomes the new head of the list. 571 */ 572 573 if (t == new) { 574 t = np; 575 np = np->n_flink; 576 t->n_flink = new; 577 new->n_blink = t; 578 t->n_blink = NIL; 579 new = t; 580 continue; 581 } 582 583 /* 584 * The normal case -- we are inserting into the 585 * middle of the list. 586 */ 587 588 x = np; 589 np = np->n_flink; 590 x->n_flink = t; 591 x->n_blink = t->n_blink; 592 t->n_blink->n_flink = x; 593 t->n_blink = x; 594 } 595 596 /* 597 * Now the list headed up by new is sorted. 598 * Go through it and remove duplicates. 599 */ 600 601 np = new; 602 while (np != NIL) { 603 t = np; 604 while (t->n_flink != NIL && 605 strcasecmp(np->n_name, t->n_flink->n_name) == 0) 606 t = t->n_flink; 607 if (t == np || t == NIL) { 608 np = np->n_flink; 609 continue; 610 } 611 612 /* 613 * Now t points to the last entry with the same name 614 * as np. Make np point beyond t. 615 */ 616 617 np->n_flink = t->n_flink; 618 if (t->n_flink != NIL) 619 t->n_flink->n_blink = np; 620 np = np->n_flink; 621 } 622 return(new); 623 } 624 625 /* 626 * Put another node onto a list of names and return 627 * the list. 628 */ 629 struct name * 630 put(list, node) 631 struct name *list, *node; 632 { 633 node->n_flink = list; 634 node->n_blink = NIL; 635 if (list != NIL) 636 list->n_blink = node; 637 return(node); 638 } 639 640 /* 641 * Determine the number of undeleted elements in 642 * a name list and return it. 643 */ 644 int 645 count(np) 646 register struct name *np; 647 { 648 register int c; 649 650 for (c = 0; np != NIL; np = np->n_flink) 651 if ((np->n_type & GDEL) == 0) 652 c++; 653 return c; 654 } 655 656 /* 657 * Delete the given name from a namelist. 658 */ 659 struct name * 660 delname(np, name) 661 register struct name *np; 662 char name[]; 663 { 664 register struct name *p; 665 666 for (p = np; p != NIL; p = p->n_flink) 667 if (strcasecmp(p->n_name, name) == 0) { 668 if (p->n_blink == NIL) { 669 if (p->n_flink != NIL) 670 p->n_flink->n_blink = NIL; 671 np = p->n_flink; 672 continue; 673 } 674 if (p->n_flink == NIL) { 675 if (p->n_blink != NIL) 676 p->n_blink->n_flink = NIL; 677 continue; 678 } 679 p->n_blink->n_flink = p->n_flink; 680 p->n_flink->n_blink = p->n_blink; 681 } 682 return np; 683 } 684 685 /* 686 * Pretty print a name list 687 * Uncomment it if you need it. 688 */ 689 690 /* 691 void 692 prettyprint(name) 693 struct name *name; 694 { 695 register struct name *np; 696 697 np = name; 698 while (np != NIL) { 699 fprintf(stderr, "%s(%d) ", np->n_name, np->n_type); 700 np = np->n_flink; 701 } 702 fprintf(stderr, "\n"); 703 } 704 */ 705