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