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