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