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