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