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