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