1 /* $NetBSD: names.c,v 1.30 2012/10/21 01:11:23 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.30 2012/10/21 01:11:23 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 */ 55 PUBLIC struct name * 56 nalloc(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 */ 71 static struct name * 72 tailof(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 */ 88 static char * 89 yankword(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 */ 132 PUBLIC struct name * 133 extract(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? */ 157 static 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 */ 165 PUBLIC char * 166 detract(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 */ 221 static int 222 isfileaddr(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 */ 245 PUBLIC struct name * 246 outof(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 begin = names; 258 np = names; 259 (void)time(&now); 260 date = ctime(&now); 261 while (np != NULL) { 262 if (!isfileaddr(np->n_name) && np->n_name[0] != '|') { 263 np = np->n_flink; 264 continue; 265 } 266 ispipe = np->n_name[0] == '|'; 267 if (ispipe) 268 fname = np->n_name+1; 269 else { 270 fname = expand(np->n_name); 271 if (fname == NULL) { 272 warnx("Filename expansion of %s failed", 273 np->n_name); 274 senderr++; 275 goto cant; 276 } 277 } 278 279 280 /* 281 * See if we have copied the complete message out yet. 282 * If not, do so. 283 */ 284 285 if (image < 0) { 286 (void)snprintf(tempname, sizeof(tempname), 287 "%s/mail.ReXXXXXXXXXXXX", tmpdir); 288 if ((fd = mkstemp(tempname)) == -1 || 289 (fout = Fdopen(fd, "ae")) == NULL) { 290 if (fd != -1) 291 (void)close(fd); 292 warn("%s", tempname); 293 senderr++; 294 goto cant; 295 } 296 image = open(tempname, O_RDWR | O_CLOEXEC); 297 (void)unlink(tempname); 298 if (image < 0) { 299 warn("%s", tempname); 300 senderr++; 301 (void)Fclose(fout); 302 goto cant; 303 } 304 (void)fprintf(fout, "From %s %s", myname, date); 305 #ifdef MIME_SUPPORT 306 (void)puthead(hp, fout, GTO|GSUBJECT|GCC|GMISC|GMIME|GNL); 307 #else 308 (void)puthead(hp, fout, GTO|GSUBJECT|GCC|GMISC|GNL); 309 #endif 310 while ((c = getc(fo)) != EOF) 311 (void)putc(c, fout); 312 rewind(fo); 313 (void)putc('\n', fout); 314 (void)fflush(fout); 315 if (ferror(fout)) { 316 warn("%s", tempname); 317 senderr++; 318 (void)Fclose(fout); 319 goto cant; 320 } 321 (void)Fclose(fout); 322 } 323 324 /* 325 * Now either copy "image" to the desired file 326 * or give it as the standard input to the desired 327 * program as appropriate. 328 */ 329 330 if (ispipe) { 331 int pid; 332 const char *shellcmd; 333 sigset_t nset; 334 335 /* 336 * XXX 337 * We can't really reuse the same image file, 338 * because multiple piped recipients will 339 * share the same lseek location and trample 340 * on one another. 341 */ 342 if ((shellcmd = value(ENAME_SHELL)) == NULL) 343 shellcmd = _PATH_CSHELL; 344 (void)sigemptyset(&nset); 345 (void)sigaddset(&nset, SIGHUP); 346 (void)sigaddset(&nset, SIGINT); 347 (void)sigaddset(&nset, SIGQUIT); 348 pid = start_command(shellcmd, &nset, 349 image, -1, "-c", fname, NULL); 350 if (pid < 0) { 351 senderr++; 352 goto cant; 353 } 354 free_child(pid); 355 } else { 356 int f; 357 if ((fout = Fopen(fname, "ae")) == NULL) { 358 warn("%s", fname); 359 senderr++; 360 goto cant; 361 } 362 if ((f = dup(image)) < 0) { 363 warn("dup"); 364 fin = NULL; 365 } else 366 fin = Fdopen(f, "re"); 367 if (fin == NULL) { 368 (void)fprintf(stderr, "Can't reopen image\n"); 369 (void)Fclose(fout); 370 senderr++; 371 goto cant; 372 } 373 rewind(fin); 374 while ((c = getc(fin)) != EOF) 375 (void)putc(c, fout); 376 if (ferror(fout)) { 377 warn("%s", fname); 378 senderr++; 379 (void)Fclose(fout); 380 (void)Fclose(fin); 381 goto cant; 382 } 383 (void)Fclose(fout); 384 (void)Fclose(fin); 385 } 386 cant: 387 /* 388 * In days of old we removed the entry from the 389 * the list; now for sake of header expansion 390 * we leave it in and mark it as deleted. 391 */ 392 np->n_type |= GDEL; 393 np = np->n_flink; 394 } 395 if (image >= 0) { 396 (void)close(image); 397 image = -1; 398 } 399 return begin; 400 } 401 402 /* 403 * Put another node onto a list of names and return 404 * the list. 405 */ 406 static struct name * 407 put(struct name *list, struct name *node) 408 { 409 node->n_flink = list; 410 node->n_blink = NULL; 411 if (list != NULL) 412 list->n_blink = node; 413 return node; 414 } 415 416 /* 417 * Recursively expand a group name. We limit the expansion to some 418 * fixed level to keep things from going haywire. 419 * Direct recursion is not expanded for convenience. 420 */ 421 PUBLIC 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 (void)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 * Map all of the aliased users in the invoker's mailrc 464 * file and insert them into the list. 465 * Changed after all these months of service to recursively 466 * expand names (2/14/80). 467 */ 468 469 PUBLIC struct name * 470 usermap(struct name *names) 471 { 472 struct name *new, *np, *cp; 473 struct grouphead *gh; 474 int metoo; 475 476 new = NULL; 477 np = names; 478 metoo = (value(ENAME_METOO) != NULL); 479 while (np != NULL) { 480 if (np->n_name[0] == '\\') { 481 cp = np->n_flink; 482 new = put(new, np); 483 np = cp; 484 continue; 485 } 486 gh = findgroup(np->n_name); 487 cp = np->n_flink; 488 if (gh != NULL) 489 new = gexpand(new, gh, metoo, np->n_type); 490 else 491 new = put(new, np); 492 np = cp; 493 } 494 return new; 495 } 496 497 /* 498 * Concatenate the two passed name lists, return the result. 499 */ 500 PUBLIC struct name * 501 cat(struct name *n1, struct name *n2) 502 { 503 struct name *tail; 504 505 if (n1 == NULL) 506 return n2; 507 if (n2 == NULL) 508 return n1; 509 tail = tailof(n1); 510 tail->n_flink = n2; 511 n2->n_blink = tail; 512 return n1; 513 } 514 515 /* 516 * Determine the number of undeleted elements in 517 * a name list and return it. 518 */ 519 PUBLIC int 520 count(struct name *np) 521 { 522 int c; 523 524 for (c = 0; np != NULL; np = np->n_flink) 525 if ((np->n_type & GDEL) == 0) 526 c++; 527 return c; 528 } 529 530 /* 531 * Unpack the name list onto a vector of strings. 532 * Return an error if the name list won't fit. 533 */ 534 PUBLIC const char ** 535 unpack(struct name *np) 536 { 537 const char **ap, **begin; 538 struct name *n; 539 int t, extra, metoo, verbose; 540 541 n = np; 542 if ((t = count(n)) == 0) 543 errx(EXIT_FAILURE, "No names to unpack"); 544 /* 545 * Compute the number of extra arguments we will need. 546 * We need at least two extra -- one for "mail" and one for 547 * the terminating 0 pointer. Additional spots may be needed 548 * to pass along -f to the host mailer. 549 */ 550 extra = 2; 551 extra++; 552 metoo = value(ENAME_METOO) != NULL; 553 if (metoo) 554 extra++; 555 verbose = value(ENAME_VERBOSE) != NULL; 556 if (verbose) 557 extra++; 558 begin = salloc((t + extra) * sizeof(*begin)); 559 ap = begin; 560 *ap++ = "sendmail"; 561 *ap++ = "-i"; 562 if (metoo) 563 *ap++ = "-m"; 564 if (verbose) 565 *ap++ = "-v"; 566 for (/*EMPTY*/; n != NULL; n = n->n_flink) 567 if ((n->n_type & GDEL) == 0) 568 *ap++ = n->n_name; 569 *ap = NULL; 570 return begin; 571 } 572 573 /* 574 * Remove all of the duplicates from the passed name list by 575 * insertion sorting them, then checking for dups. 576 * Return the head of the new list. 577 */ 578 PUBLIC struct name * 579 elide(struct name *names) 580 { 581 struct name *np, *t, *new; 582 struct name *x; 583 584 if (names == NULL) 585 return NULL; 586 new = names; 587 np = names; 588 np = np->n_flink; 589 if (np != NULL) 590 np->n_blink = NULL; 591 new->n_flink = NULL; 592 while (np != NULL) { 593 t = new; 594 while (strcasecmp(t->n_name, np->n_name) < 0) { 595 if (t->n_flink == NULL) 596 break; 597 t = t->n_flink; 598 } 599 600 /* 601 * If we ran out of t's, put the new entry after 602 * the current value of t. 603 */ 604 605 if (strcasecmp(t->n_name, np->n_name) < 0) { 606 t->n_flink = np; 607 np->n_blink = t; 608 t = np; 609 np = np->n_flink; 610 t->n_flink = NULL; 611 continue; 612 } 613 614 /* 615 * Otherwise, put the new entry in front of the 616 * current t. If at the front of the list, 617 * the new guy becomes the new head of the list. 618 */ 619 620 if (t == new) { 621 t = np; 622 np = np->n_flink; 623 t->n_flink = new; 624 new->n_blink = t; 625 t->n_blink = NULL; 626 new = t; 627 continue; 628 } 629 630 /* 631 * The normal case -- we are inserting into the 632 * middle of the list. 633 */ 634 635 x = np; 636 np = np->n_flink; 637 x->n_flink = t; 638 x->n_blink = t->n_blink; 639 t->n_blink->n_flink = x; 640 t->n_blink = x; 641 } 642 643 /* 644 * Now the list headed up by new is sorted. 645 * Go through it and remove duplicates. 646 */ 647 648 np = new; 649 while (np != NULL) { 650 t = np; 651 while (t->n_flink != NULL && 652 strcasecmp(np->n_name, t->n_flink->n_name) == 0) 653 t = t->n_flink; 654 if (t == np || t == NULL) { 655 np = np->n_flink; 656 continue; 657 } 658 659 /* 660 * Now t points to the last entry with the same name 661 * as np. Make np point beyond t. 662 */ 663 664 np->n_flink = t->n_flink; 665 if (t->n_flink != NULL) 666 t->n_flink->n_blink = np; 667 np = np->n_flink; 668 } 669 return new; 670 } 671 672 /* 673 * Delete the given name from a namelist. 674 */ 675 PUBLIC struct name * 676 delname(struct name *np, char name[]) 677 { 678 struct name *p; 679 680 for (p = np; p != NULL; p = p->n_flink) 681 if (strcasecmp(p->n_name, name) == 0) { 682 if (p->n_blink == NULL) { 683 if (p->n_flink != NULL) 684 p->n_flink->n_blink = NULL; 685 np = p->n_flink; 686 continue; 687 } 688 if (p->n_flink == NULL) { 689 if (p->n_blink != NULL) 690 p->n_blink->n_flink = NULL; 691 continue; 692 } 693 p->n_blink->n_flink = p->n_flink; 694 p->n_flink->n_blink = p->n_blink; 695 } 696 return np; 697 } 698 699 /* 700 * Pretty print a name list 701 * Uncomment it if you need it. 702 */ 703 #if 0 704 PUBLIC void 705 prettyprint(name) 706 struct name *name; 707 { 708 struct name *np; 709 710 np = name; 711 while (np != NULL) { 712 (void)fprintf(stderr, "%s(%d) ", np->n_name, np->n_type); 713 np = np->n_flink; 714 } 715 (void)fprintf(stderr, "\n"); 716 } 717 #endif 718