1 /* $NetBSD: names.c,v 1.16 2002/03/06 13:45:51 wiz Exp $ */ 2 3 /* 4 * Copyright (c) 1980, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. All advertising materials mentioning features or use of this software 16 * must display the following acknowledgement: 17 * This product includes software developed by the University of 18 * California, Berkeley and its contributors. 19 * 4. Neither the name of the University nor the names of its contributors 20 * may be used to endorse or promote products derived from this software 21 * without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 */ 35 36 #include <sys/cdefs.h> 37 #ifndef lint 38 #if 0 39 static char sccsid[] = "@(#)names.c 8.1 (Berkeley) 6/6/93"; 40 #else 41 __RCSID("$NetBSD: names.c,v 1.16 2002/03/06 13:45:51 wiz Exp $"); 42 #endif 43 #endif /* not lint */ 44 45 /* 46 * Mail -- a mail program 47 * 48 * Handle name lists. 49 */ 50 51 #include "rcv.h" 52 #include "extern.h" 53 54 extern char *tmpdir; 55 56 /* 57 * Allocate a single element of a name list, 58 * initialize its name field to the passed 59 * name and return it. 60 */ 61 struct name * 62 nalloc(char str[], int ntype) 63 { 64 struct name *np; 65 66 np = (struct name *) salloc(sizeof *np); 67 np->n_flink = NULL; 68 np->n_blink = NULL; 69 np->n_type = ntype; 70 np->n_name = savestr(str); 71 return(np); 72 } 73 74 /* 75 * Find the tail of a list and return it. 76 */ 77 struct name * 78 tailof(struct name *name) 79 { 80 struct name *np; 81 82 np = name; 83 if (np == NULL) 84 return(NULL); 85 while (np->n_flink != NULL) 86 np = np->n_flink; 87 return(np); 88 } 89 90 /* 91 * Extract a list of names from a line, 92 * and make a list of names from it. 93 * Return the list or NULL if none found. 94 */ 95 struct name * 96 extract(char line[], int ntype) 97 { 98 char *cp; 99 struct name *begin, *np, *t; 100 char nbuf[BUFSIZ]; 101 102 if (line == NULL || *line == '\0') 103 return NULL; 104 begin = NULL; 105 np = NULL; 106 cp = line; 107 while ((cp = yankword(cp, nbuf)) != NULL) { 108 t = nalloc(nbuf, ntype); 109 if (begin == NULL) 110 begin = t; 111 else 112 np->n_flink = t; 113 t->n_blink = np; 114 np = t; 115 } 116 return begin; 117 } 118 119 /* 120 * Turn a list of names into a string of the same names. 121 */ 122 char * 123 detract(struct name *np, int ntype) 124 { 125 int s; 126 char *cp, *begin; 127 struct name *p; 128 int comma; 129 130 comma = ntype & GCOMMA; 131 if (np == NULL) 132 return(NULL); 133 ntype &= ~GCOMMA; 134 s = 0; 135 if (debug && comma) 136 fprintf(stderr, "detract asked to insert commas\n"); 137 for (p = np; p != NULL; 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(NULL); 146 s += 2; 147 begin = salloc(s); 148 cp = begin; 149 for (p = np; p != NULL; 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 != NULL) 154 *cp++ = ','; 155 *cp++ = ' '; 156 } 157 *--cp = 0; 158 if (comma && *--cp == ',') 159 *cp = 0; 160 return(begin); 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(char *ap, char wbuf[]) 169 { 170 char *cp, *cp2; 171 172 cp = ap; 173 for (;;) { 174 if (*cp == '\0') 175 return NULL; 176 if (*cp == '(') { 177 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 && !strchr(" \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(struct name *names, FILE *fo, struct header *hp) 216 { 217 int c, fd; 218 struct name *np, *begin; 219 time_t now; 220 char *date, *fname; 221 FILE *fout, *fin; 222 int ispipe; 223 char tempname[PATHSIZE]; 224 225 begin = names; 226 np = names; 227 (void)time(&now); 228 date = ctime(&now); 229 while (np != NULL) { 230 if (!isfileaddr(np->n_name) && np->n_name[0] != '|') { 231 np = np->n_flink; 232 continue; 233 } 234 ispipe = np->n_name[0] == '|'; 235 if (ispipe) 236 fname = np->n_name+1; 237 else 238 fname = expand(np->n_name); 239 240 /* 241 * See if we have copied the complete message out yet. 242 * If not, do so. 243 */ 244 245 if (image < 0) { 246 (void)snprintf(tempname, sizeof(tempname), 247 "%s/mail.ReXXXXXXXXXXXX", tmpdir); 248 if ((fd = mkstemp(tempname)) == -1 || 249 (fout = Fdopen(fd, "a")) == NULL) { 250 if (fd != -1) 251 close(fd); 252 warn("%s", tempname); 253 senderr++; 254 goto cant; 255 } 256 image = open(tempname, 2); 257 (void)unlink(tempname); 258 if (image < 0) { 259 warn("%s", tempname); 260 senderr++; 261 (void)Fclose(fout); 262 goto cant; 263 } 264 (void)fcntl(image, F_SETFD, 1); 265 fprintf(fout, "From %s %s", myname, date); 266 puthead(hp, fout, GTO|GSUBJECT|GCC|GNL); 267 while ((c = getc(fo)) != EOF) 268 (void)putc(c, fout); 269 rewind(fo); 270 (void)putc('\n', fout); 271 (void)fflush(fout); 272 if (ferror(fout)) { 273 warn("%s", tempname); 274 senderr++; 275 (void)Fclose(fout); 276 goto cant; 277 } 278 (void)Fclose(fout); 279 } 280 281 /* 282 * Now either copy "image" to the desired file 283 * or give it as the standard input to the desired 284 * program as appropriate. 285 */ 286 287 if (ispipe) { 288 int pid; 289 char *shellcmd; 290 sigset_t nset; 291 292 /* 293 * XXX 294 * We can't really reuse the same image file, 295 * because multiple piped recipients will 296 * share the same lseek location and trample 297 * on one another. 298 */ 299 if ((shellcmd = value("SHELL")) == NULL) 300 shellcmd = _PATH_CSHELL; 301 sigemptyset(&nset); 302 sigaddset(&nset, SIGHUP); 303 sigaddset(&nset, SIGINT); 304 sigaddset(&nset, SIGQUIT); 305 pid = start_command(shellcmd, &nset, 306 image, -1, "-c", fname, NULL); 307 if (pid < 0) { 308 senderr++; 309 goto cant; 310 } 311 free_child(pid); 312 } else { 313 int f; 314 if ((fout = Fopen(fname, "a")) == NULL) { 315 warn("%s", fname); 316 senderr++; 317 goto cant; 318 } 319 if ((f = dup(image)) < 0) { 320 warn("dup"); 321 fin = NULL; 322 } else 323 fin = Fdopen(f, "r"); 324 if (fin == NULL) { 325 fprintf(stderr, "Can't reopen image\n"); 326 (void)Fclose(fout); 327 senderr++; 328 goto cant; 329 } 330 rewind(fin); 331 while ((c = getc(fin)) != EOF) 332 (void)putc(c, fout); 333 if (ferror(fout)) { 334 warn("%s", fname); 335 senderr++; 336 (void)Fclose(fout); 337 (void)Fclose(fin); 338 goto cant; 339 } 340 (void)Fclose(fout); 341 (void)Fclose(fin); 342 } 343 cant: 344 /* 345 * In days of old we removed the entry from the 346 * the list; now for sake of header expansion 347 * we leave it in and mark it as deleted. 348 */ 349 np->n_type |= GDEL; 350 np = np->n_flink; 351 } 352 if (image >= 0) { 353 (void)close(image); 354 image = -1; 355 } 356 return(begin); 357 } 358 359 /* 360 * Determine if the passed address is a local "send to file" address. 361 * If any of the network metacharacters precedes any slashes, it can't 362 * be a filename. We cheat with .'s to allow path names like ./... 363 */ 364 int 365 isfileaddr(char *name) 366 { 367 char *cp; 368 369 if (*name == '+') 370 return 1; 371 for (cp = name; *cp; cp++) { 372 if (*cp == '!' || *cp == '%' || *cp == '@') 373 return 0; 374 if (*cp == '/') 375 return 1; 376 } 377 return 0; 378 } 379 380 /* 381 * Map all of the aliased users in the invoker's mailrc 382 * file and insert them into the list. 383 * Changed after all these months of service to recursively 384 * expand names (2/14/80). 385 */ 386 387 struct name * 388 usermap(struct name *names) 389 { 390 struct name *new, *np, *cp; 391 struct grouphead *gh; 392 int metoo; 393 394 new = NULL; 395 np = names; 396 metoo = (value("metoo") != NULL); 397 while (np != NULL) { 398 if (np->n_name[0] == '\\') { 399 cp = np->n_flink; 400 new = put(new, np); 401 np = cp; 402 continue; 403 } 404 gh = findgroup(np->n_name); 405 cp = np->n_flink; 406 if (gh != NULL) 407 new = gexpand(new, gh, metoo, np->n_type); 408 else 409 new = put(new, np); 410 np = cp; 411 } 412 return(new); 413 } 414 415 /* 416 * Recursively expand a group name. We limit the expansion to some 417 * fixed level to keep things from going haywire. 418 * Direct recursion is not expanded for convenience. 419 */ 420 421 struct name * 422 gexpand(struct name *nlist, struct grouphead *gh, int metoo, int ntype) 423 { 424 struct group *gp; 425 struct grouphead *ngh; 426 struct name *np; 427 static int depth; 428 char *cp; 429 430 if (depth > MAXEXP) { 431 printf("Expanding alias to depth larger than %d\n", MAXEXP); 432 return(nlist); 433 } 434 depth++; 435 for (gp = gh->g_list; gp != NULL; gp = gp->ge_link) { 436 cp = gp->ge_name; 437 if (*cp == '\\') 438 goto quote; 439 if (strcmp(cp, gh->g_name) == 0) 440 goto quote; 441 if ((ngh = findgroup(cp)) != NULL) { 442 nlist = gexpand(nlist, ngh, metoo, ntype); 443 continue; 444 } 445 quote: 446 np = nalloc(cp, ntype); 447 /* 448 * At this point should allow to expand 449 * to self if only person in group 450 */ 451 if (gp == gh->g_list && gp->ge_link == NULL) 452 goto skip; 453 if (!metoo && strcmp(cp, myname) == 0) 454 np->n_type |= GDEL; 455 skip: 456 nlist = put(nlist, np); 457 } 458 depth--; 459 return(nlist); 460 } 461 462 /* 463 * Concatenate the two passed name lists, return the result. 464 */ 465 struct name * 466 cat(struct name *n1, struct name *n2) 467 { 468 struct name *tail; 469 470 if (n1 == NULL) 471 return(n2); 472 if (n2 == NULL) 473 return(n1); 474 tail = tailof(n1); 475 tail->n_flink = n2; 476 n2->n_blink = tail; 477 return(n1); 478 } 479 480 /* 481 * Unpack the name list onto a vector of strings. 482 * Return an error if the name list won't fit. 483 */ 484 char ** 485 unpack(struct name *np) 486 { 487 char **ap, **begin; 488 struct name *n; 489 int t, extra, metoo, verbose; 490 491 n = np; 492 if ((t = count(n)) == 0) 493 errx(1, "No names to unpack"); 494 /* 495 * Compute the number of extra arguments we will need. 496 * We need at least two extra -- one for "mail" and one for 497 * the terminating 0 pointer. Additional spots may be needed 498 * to pass along -f to the host mailer. 499 */ 500 extra = 2; 501 extra++; 502 metoo = value("metoo") != NULL; 503 if (metoo) 504 extra++; 505 verbose = value("verbose") != NULL; 506 if (verbose) 507 extra++; 508 begin = (char **) salloc((t + extra) * sizeof *begin); 509 ap = begin; 510 *ap++ = "send-mail"; 511 *ap++ = "-i"; 512 if (metoo) 513 *ap++ = "-m"; 514 if (verbose) 515 *ap++ = "-v"; 516 for (; n != NULL; n = n->n_flink) 517 if ((n->n_type & GDEL) == 0) 518 *ap++ = n->n_name; 519 *ap = NULL; 520 return(begin); 521 } 522 523 /* 524 * Remove all of the duplicates from the passed name list by 525 * insertion sorting them, then checking for dups. 526 * Return the head of the new list. 527 */ 528 struct name * 529 elide(struct name *names) 530 { 531 struct name *np, *t, *new; 532 struct name *x; 533 534 if (names == NULL) 535 return(NULL); 536 new = names; 537 np = names; 538 np = np->n_flink; 539 if (np != NULL) 540 np->n_blink = NULL; 541 new->n_flink = NULL; 542 while (np != NULL) { 543 t = new; 544 while (strcasecmp(t->n_name, np->n_name) < 0) { 545 if (t->n_flink == NULL) 546 break; 547 t = t->n_flink; 548 } 549 550 /* 551 * If we ran out of t's, put the new entry after 552 * the current value of t. 553 */ 554 555 if (strcasecmp(t->n_name, np->n_name) < 0) { 556 t->n_flink = np; 557 np->n_blink = t; 558 t = np; 559 np = np->n_flink; 560 t->n_flink = NULL; 561 continue; 562 } 563 564 /* 565 * Otherwise, put the new entry in front of the 566 * current t. If at the front of the list, 567 * the new guy becomes the new head of the list. 568 */ 569 570 if (t == new) { 571 t = np; 572 np = np->n_flink; 573 t->n_flink = new; 574 new->n_blink = t; 575 t->n_blink = NULL; 576 new = t; 577 continue; 578 } 579 580 /* 581 * The normal case -- we are inserting into the 582 * middle of the list. 583 */ 584 585 x = np; 586 np = np->n_flink; 587 x->n_flink = t; 588 x->n_blink = t->n_blink; 589 t->n_blink->n_flink = x; 590 t->n_blink = x; 591 } 592 593 /* 594 * Now the list headed up by new is sorted. 595 * Go through it and remove duplicates. 596 */ 597 598 np = new; 599 while (np != NULL) { 600 t = np; 601 while (t->n_flink != NULL && 602 strcasecmp(np->n_name, t->n_flink->n_name) == 0) 603 t = t->n_flink; 604 if (t == np || t == NULL) { 605 np = np->n_flink; 606 continue; 607 } 608 609 /* 610 * Now t points to the last entry with the same name 611 * as np. Make np point beyond t. 612 */ 613 614 np->n_flink = t->n_flink; 615 if (t->n_flink != NULL) 616 t->n_flink->n_blink = np; 617 np = np->n_flink; 618 } 619 return(new); 620 } 621 622 /* 623 * Put another node onto a list of names and return 624 * the list. 625 */ 626 struct name * 627 put(struct name *list, struct name *node) 628 { 629 node->n_flink = list; 630 node->n_blink = NULL; 631 if (list != NULL) 632 list->n_blink = node; 633 return(node); 634 } 635 636 /* 637 * Determine the number of undeleted elements in 638 * a name list and return it. 639 */ 640 int 641 count(struct name *np) 642 { 643 int c; 644 645 for (c = 0; np != NULL; np = np->n_flink) 646 if ((np->n_type & GDEL) == 0) 647 c++; 648 return c; 649 } 650 651 /* 652 * Delete the given name from a namelist. 653 */ 654 struct name * 655 delname(struct name *np, char name[]) 656 { 657 struct name *p; 658 659 for (p = np; p != NULL; p = p->n_flink) 660 if (strcasecmp(p->n_name, name) == 0) { 661 if (p->n_blink == NULL) { 662 if (p->n_flink != NULL) 663 p->n_flink->n_blink = NULL; 664 np = p->n_flink; 665 continue; 666 } 667 if (p->n_flink == NULL) { 668 if (p->n_blink != NULL) 669 p->n_blink->n_flink = NULL; 670 continue; 671 } 672 p->n_blink->n_flink = p->n_flink; 673 p->n_flink->n_blink = p->n_blink; 674 } 675 return np; 676 } 677 678 /* 679 * Pretty print a name list 680 * Uncomment it if you need it. 681 */ 682 683 /* 684 void 685 prettyprint(name) 686 struct name *name; 687 { 688 struct name *np; 689 690 np = name; 691 while (np != NULL) { 692 fprintf(stderr, "%s(%d) ", np->n_name, np->n_type); 693 np = np->n_flink; 694 } 695 fprintf(stderr, "\n"); 696 } 697 */ 698