1 /* $NetBSD: names.c,v 1.23 2006/10/31 20:07:32 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.23 2006/10/31 20:07:32 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, FD_CLOEXEC); 277 (void)fprintf(fout, "From %s %s", myname, date); 278 #ifdef MIME_SUPPORT 279 (void)puthead(hp, fout, GTO|GSUBJECT|GCC|GMIME|GNL); 280 #else 281 (void)puthead(hp, fout, GTO|GSUBJECT|GCC|GNL); 282 #endif 283 while ((c = getc(fo)) != EOF) 284 (void)putc(c, fout); 285 rewind(fo); 286 (void)putc('\n', fout); 287 (void)fflush(fout); 288 if (ferror(fout)) { 289 warn("%s", tempname); 290 senderr++; 291 (void)Fclose(fout); 292 goto cant; 293 } 294 (void)Fclose(fout); 295 } 296 297 /* 298 * Now either copy "image" to the desired file 299 * or give it as the standard input to the desired 300 * program as appropriate. 301 */ 302 303 if (ispipe) { 304 int pid; 305 const char *shellcmd; 306 sigset_t nset; 307 308 /* 309 * XXX 310 * We can't really reuse the same image file, 311 * because multiple piped recipients will 312 * share the same lseek location and trample 313 * on one another. 314 */ 315 if ((shellcmd = value("SHELL")) == NULL) 316 shellcmd = _PATH_CSHELL; 317 (void)sigemptyset(&nset); 318 (void)sigaddset(&nset, SIGHUP); 319 (void)sigaddset(&nset, SIGINT); 320 (void)sigaddset(&nset, SIGQUIT); 321 pid = start_command(shellcmd, &nset, 322 image, -1, "-c", fname, NULL); 323 if (pid < 0) { 324 senderr++; 325 goto cant; 326 } 327 free_child(pid); 328 } else { 329 int f; 330 if ((fout = Fopen(fname, "a")) == NULL) { 331 warn("%s", fname); 332 senderr++; 333 goto cant; 334 } 335 if ((f = dup(image)) < 0) { 336 warn("dup"); 337 fin = NULL; 338 } else 339 fin = Fdopen(f, "r"); 340 if (fin == NULL) { 341 (void)fprintf(stderr, "Can't reopen image\n"); 342 (void)Fclose(fout); 343 senderr++; 344 goto cant; 345 } 346 rewind(fin); 347 while ((c = getc(fin)) != EOF) 348 (void)putc(c, fout); 349 if (ferror(fout)) { 350 warn("%s", fname); 351 senderr++; 352 (void)Fclose(fout); 353 (void)Fclose(fin); 354 goto cant; 355 } 356 (void)Fclose(fout); 357 (void)Fclose(fin); 358 } 359 cant: 360 /* 361 * In days of old we removed the entry from the 362 * the list; now for sake of header expansion 363 * we leave it in and mark it as deleted. 364 */ 365 np->n_type |= GDEL; 366 np = np->n_flink; 367 } 368 if (image >= 0) { 369 (void)close(image); 370 image = -1; 371 } 372 return(begin); 373 } 374 375 /* 376 * Determine if the passed address is a local "send to file" address. 377 * If any of the network metacharacters precedes any slashes, it can't 378 * be a filename. We cheat with .'s to allow path names like ./... 379 */ 380 int 381 isfileaddr(char *name) 382 { 383 char *cp; 384 385 if (*name == '+') 386 return 1; 387 for (cp = name; *cp; cp++) { 388 if (*cp == '!' || *cp == '%' || *cp == '@') 389 return 0; 390 if (*cp == '/') 391 return 1; 392 } 393 return 0; 394 } 395 396 /* 397 * Map all of the aliased users in the invoker's mailrc 398 * file and insert them into the list. 399 * Changed after all these months of service to recursively 400 * expand names (2/14/80). 401 */ 402 403 struct name * 404 usermap(struct name *names) 405 { 406 struct name *new, *np, *cp; 407 struct grouphead *gh; 408 int metoo; 409 410 new = NULL; 411 np = names; 412 metoo = (value("metoo") != NULL); 413 while (np != NULL) { 414 if (np->n_name[0] == '\\') { 415 cp = np->n_flink; 416 new = put(new, np); 417 np = cp; 418 continue; 419 } 420 gh = findgroup(np->n_name); 421 cp = np->n_flink; 422 if (gh != NULL) 423 new = gexpand(new, gh, metoo, np->n_type); 424 else 425 new = put(new, np); 426 np = cp; 427 } 428 return(new); 429 } 430 431 /* 432 * Recursively expand a group name. We limit the expansion to some 433 * fixed level to keep things from going haywire. 434 * Direct recursion is not expanded for convenience. 435 */ 436 437 struct name * 438 gexpand(struct name *nlist, struct grouphead *gh, int metoo, int ntype) 439 { 440 struct group *gp; 441 struct grouphead *ngh; 442 struct name *np; 443 static int depth; 444 char *cp; 445 446 if (depth > MAXEXP) { 447 (void)printf("Expanding alias to depth larger than %d\n", MAXEXP); 448 return(nlist); 449 } 450 depth++; 451 for (gp = gh->g_list; gp != NULL; gp = gp->ge_link) { 452 cp = gp->ge_name; 453 if (*cp == '\\') 454 goto quote; 455 if (strcmp(cp, gh->g_name) == 0) 456 goto quote; 457 if ((ngh = findgroup(cp)) != NULL) { 458 nlist = gexpand(nlist, ngh, metoo, ntype); 459 continue; 460 } 461 quote: 462 np = nalloc(cp, ntype); 463 /* 464 * At this point should allow to expand 465 * to self if only person in group 466 */ 467 if (gp == gh->g_list && gp->ge_link == NULL) 468 goto skip; 469 if (!metoo && strcmp(cp, myname) == 0) 470 np->n_type |= GDEL; 471 skip: 472 nlist = put(nlist, np); 473 } 474 depth--; 475 return(nlist); 476 } 477 478 /* 479 * Concatenate the two passed name lists, return the result. 480 */ 481 struct name * 482 cat(struct name *n1, struct name *n2) 483 { 484 struct name *tail; 485 486 if (n1 == NULL) 487 return(n2); 488 if (n2 == NULL) 489 return(n1); 490 tail = tailof(n1); 491 tail->n_flink = n2; 492 n2->n_blink = tail; 493 return(n1); 494 } 495 496 /* 497 * Unpack the name list onto a vector of strings. 498 * Return an error if the name list won't fit. 499 */ 500 const char ** 501 unpack(struct name *np) 502 { 503 const char **ap, **begin; 504 struct name *n; 505 int t, extra, metoo, verbose; 506 507 n = np; 508 if ((t = count(n)) == 0) 509 errx(1, "No names to unpack"); 510 /* 511 * Compute the number of extra arguments we will need. 512 * We need at least two extra -- one for "mail" and one for 513 * the terminating 0 pointer. Additional spots may be needed 514 * to pass along -f to the host mailer. 515 */ 516 extra = 2; 517 extra++; 518 metoo = value("metoo") != NULL; 519 if (metoo) 520 extra++; 521 verbose = value("verbose") != NULL; 522 if (verbose) 523 extra++; 524 begin = salloc((t + extra) * sizeof *begin); 525 ap = begin; 526 *ap++ = "sendmail"; 527 *ap++ = "-i"; 528 if (metoo) 529 *ap++ = "-m"; 530 if (verbose) 531 *ap++ = "-v"; 532 for (; n != NULL; n = n->n_flink) 533 if ((n->n_type & GDEL) == 0) 534 *ap++ = n->n_name; 535 *ap = NULL; 536 return(begin); 537 } 538 539 /* 540 * Remove all of the duplicates from the passed name list by 541 * insertion sorting them, then checking for dups. 542 * Return the head of the new list. 543 */ 544 struct name * 545 elide(struct name *names) 546 { 547 struct name *np, *t, *new; 548 struct name *x; 549 550 if (names == NULL) 551 return(NULL); 552 new = names; 553 np = names; 554 np = np->n_flink; 555 if (np != NULL) 556 np->n_blink = NULL; 557 new->n_flink = NULL; 558 while (np != NULL) { 559 t = new; 560 while (strcasecmp(t->n_name, np->n_name) < 0) { 561 if (t->n_flink == NULL) 562 break; 563 t = t->n_flink; 564 } 565 566 /* 567 * If we ran out of t's, put the new entry after 568 * the current value of t. 569 */ 570 571 if (strcasecmp(t->n_name, np->n_name) < 0) { 572 t->n_flink = np; 573 np->n_blink = t; 574 t = np; 575 np = np->n_flink; 576 t->n_flink = NULL; 577 continue; 578 } 579 580 /* 581 * Otherwise, put the new entry in front of the 582 * current t. If at the front of the list, 583 * the new guy becomes the new head of the list. 584 */ 585 586 if (t == new) { 587 t = np; 588 np = np->n_flink; 589 t->n_flink = new; 590 new->n_blink = t; 591 t->n_blink = NULL; 592 new = t; 593 continue; 594 } 595 596 /* 597 * The normal case -- we are inserting into the 598 * middle of the list. 599 */ 600 601 x = np; 602 np = np->n_flink; 603 x->n_flink = t; 604 x->n_blink = t->n_blink; 605 t->n_blink->n_flink = x; 606 t->n_blink = x; 607 } 608 609 /* 610 * Now the list headed up by new is sorted. 611 * Go through it and remove duplicates. 612 */ 613 614 np = new; 615 while (np != NULL) { 616 t = np; 617 while (t->n_flink != NULL && 618 strcasecmp(np->n_name, t->n_flink->n_name) == 0) 619 t = t->n_flink; 620 if (t == np || t == NULL) { 621 np = np->n_flink; 622 continue; 623 } 624 625 /* 626 * Now t points to the last entry with the same name 627 * as np. Make np point beyond t. 628 */ 629 630 np->n_flink = t->n_flink; 631 if (t->n_flink != NULL) 632 t->n_flink->n_blink = np; 633 np = np->n_flink; 634 } 635 return(new); 636 } 637 638 /* 639 * Put another node onto a list of names and return 640 * the list. 641 */ 642 struct name * 643 put(struct name *list, struct name *node) 644 { 645 node->n_flink = list; 646 node->n_blink = NULL; 647 if (list != NULL) 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 */ 656 int 657 count(struct name *np) 658 { 659 int c; 660 661 for (c = 0; np != NULL; np = np->n_flink) 662 if ((np->n_type & GDEL) == 0) 663 c++; 664 return c; 665 } 666 667 /* 668 * Delete the given name from a namelist. 669 */ 670 struct name * 671 delname(struct name *np, char name[]) 672 { 673 struct name *p; 674 675 for (p = np; p != NULL; p = p->n_flink) 676 if (strcasecmp(p->n_name, name) == 0) { 677 if (p->n_blink == NULL) { 678 if (p->n_flink != NULL) 679 p->n_flink->n_blink = NULL; 680 np = p->n_flink; 681 continue; 682 } 683 if (p->n_flink == NULL) { 684 if (p->n_blink != NULL) 685 p->n_blink->n_flink = NULL; 686 continue; 687 } 688 p->n_blink->n_flink = p->n_flink; 689 p->n_flink->n_blink = p->n_blink; 690 } 691 return np; 692 } 693 694 /* 695 * Pretty print a name list 696 * Uncomment it if you need it. 697 */ 698 699 /* 700 void 701 prettyprint(name) 702 struct name *name; 703 { 704 struct name *np; 705 706 np = name; 707 while (np != NULL) { 708 (void)fprintf(stderr, "%s(%d) ", np->n_name, np->n_type); 709 np = np->n_flink; 710 } 711 (void)fprintf(stderr, "\n"); 712 } 713 */ 714