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