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