1 /* $OpenBSD: names.c,v 1.16 2001/11/21 20:41:55 millert Exp $ */ 2 /* $NetBSD: names.c,v 1.5 1996/06/08 19:48:32 christos Exp $ */ 3 4 /* 5 * Copyright (c) 1980, 1993 6 * The Regents of the University of California. All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. All advertising materials mentioning features or use of this software 17 * must display the following acknowledgement: 18 * This product includes software developed by the University of 19 * California, Berkeley and its contributors. 20 * 4. Neither the name of the University nor the names of its contributors 21 * may be used to endorse or promote products derived from this software 22 * without specific prior written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34 * SUCH DAMAGE. 35 */ 36 37 #ifndef lint 38 #if 0 39 static const char sccsid[] = "@(#)names.c 8.1 (Berkeley) 6/6/93"; 40 #else 41 static const char rcsid[] = "$OpenBSD: names.c,v 1.16 2001/11/21 20:41:55 millert Exp $"; 42 #endif 43 #endif /* not lint */ 44 45 /* 46 * Mail -- a mail program 47 * 48 * Handle name lists. 49 */ 50 51 #include "rcv.h" 52 #include <fcntl.h> 53 #include "extern.h" 54 55 /* 56 * Allocate a single element of a name list, 57 * initialize its name field to the passed 58 * name and return it. 59 */ 60 struct name * 61 nalloc(char *str, int ntype) 62 { 63 struct name *np; 64 65 np = (struct name *)salloc(sizeof(*np)); 66 np->n_flink = NULL; 67 np->n_blink = NULL; 68 np->n_type = ntype; 69 np->n_name = savestr(str); 70 return(np); 71 } 72 73 /* 74 * Find the tail of a list and return it. 75 */ 76 struct name * 77 tailof(struct name *name) 78 { 79 struct name *np; 80 81 np = name; 82 if (np == NULL) 83 return(NULL); 84 while (np->n_flink != NULL) 85 np = np->n_flink; 86 return(np); 87 } 88 89 /* 90 * Extract a list of names from a line, 91 * and make a list of names from it. 92 * Return the list or NULL if none found. 93 */ 94 struct name * 95 extract(char *line, int ntype) 96 { 97 char *cp; 98 struct name *top, *np, *t; 99 char *nbuf; 100 101 if (line == NULL || *line == '\0') 102 return(NULL); 103 if ((nbuf = (char *)malloc(strlen(line) + 1)) == NULL) 104 errx(1, "Out of memory"); 105 top = NULL; 106 np = NULL; 107 cp = line; 108 while ((cp = yankword(cp, nbuf)) != NULL) { 109 t = nalloc(nbuf, ntype); 110 if (top == NULL) 111 top = t; 112 else 113 np->n_flink = t; 114 t->n_blink = np; 115 np = t; 116 } 117 (void)free(nbuf); 118 return(top); 119 } 120 121 /* 122 * Turn a list of names into a string of the same names. 123 */ 124 char * 125 detract(struct name *np, int ntype) 126 { 127 int s, comma; 128 char *cp, *top; 129 struct name *p; 130 131 comma = ntype & GCOMMA; 132 if (np == NULL) 133 return(NULL); 134 ntype &= ~GCOMMA; 135 s = 0; 136 if (debug && comma) 137 fputs("detract asked to insert commas\n", stderr); 138 for (p = np; p != NULL; p = p->n_flink) { 139 if (ntype && (p->n_type & GMASK) != ntype) 140 continue; 141 s += strlen(p->n_name) + 1; 142 if (comma) 143 s++; 144 } 145 if (s == 0) 146 return(NULL); 147 s += 2; 148 top = salloc(s); 149 cp = top; 150 for (p = np; p != NULL; p = p->n_flink) { 151 if (ntype && (p->n_type & GMASK) != ntype) 152 continue; 153 cp = copy(p->n_name, cp); 154 if (comma && p->n_flink != NULL) 155 *cp++ = ','; 156 *cp++ = ' '; 157 } 158 *--cp = 0; 159 if (comma && *--cp == ',') 160 *cp = 0; 161 return(top); 162 } 163 164 /* 165 * Grab a single word (liberal word) 166 * Throw away things between ()'s, and take anything between <>. 167 */ 168 char * 169 yankword(char *ap, char *wbuf) 170 { 171 char *cp, *cp2; 172 173 cp = ap; 174 for (;;) { 175 if (*cp == '\0') 176 return(NULL); 177 if (*cp == '(') { 178 int nesting = 0; 179 180 while (*cp != '\0') { 181 switch (*cp++) { 182 case '(': 183 nesting++; 184 break; 185 case ')': 186 --nesting; 187 break; 188 } 189 if (nesting <= 0) 190 break; 191 } 192 } else if (*cp == ' ' || *cp == '\t' || *cp == ',') 193 cp++; 194 else 195 break; 196 } 197 if (*cp == '<') 198 for (cp2 = wbuf; *cp && (*cp2++ = *cp++) != '>';) 199 ; 200 else 201 for (cp2 = wbuf; *cp && !strchr(" \t,(", *cp); *cp2++ = *cp++) 202 ; 203 *cp2 = '\0'; 204 return(cp); 205 } 206 207 /* 208 * For each recipient in the passed name list with a / 209 * in the name, append the message to the end of the named file 210 * and remove him from the recipient list. 211 * 212 * Recipients whose name begins with | are piped through the given 213 * program and removed. 214 */ 215 struct name * 216 outof(struct name *names, FILE *fo, struct header *hp) 217 { 218 int c, ispipe; 219 struct name *np, *top; 220 time_t now; 221 char *date, *fname; 222 FILE *fout, *fin; 223 224 top = names; 225 np = names; 226 (void)time(&now); 227 date = ctime(&now); 228 while (np != NULL) { 229 if (!isfileaddr(np->n_name) && np->n_name[0] != '|') { 230 np = np->n_flink; 231 continue; 232 } 233 ispipe = np->n_name[0] == '|'; 234 if (ispipe) 235 fname = np->n_name+1; 236 else 237 fname = expand(np->n_name); 238 239 /* 240 * See if we have copied the complete message out yet. 241 * If not, do so. 242 */ 243 if (image < 0) { 244 int fd; 245 char tempname[PATHSIZE]; 246 247 (void)snprintf(tempname, sizeof(tempname), 248 "%s/mail.ReXXXXXXXXXX", tmpdir); 249 if ((fd = mkstemp(tempname)) == -1 || 250 (fout = Fdopen(fd, "a")) == NULL) { 251 warn("%s", tempname); 252 senderr++; 253 goto cant; 254 } 255 image = open(tempname, O_RDWR); 256 (void)rm(tempname); 257 if (image < 0) { 258 warn("%s", tempname); 259 senderr++; 260 (void)Fclose(fout); 261 goto cant; 262 } 263 (void)fcntl(image, F_SETFD, 1); 264 fprintf(fout, "From %s %s", myname, date); 265 puthead(hp, fout, GTO|GSUBJECT|GCC|GNL); 266 while ((c = getc(fo)) != EOF) 267 (void)putc(c, fout); 268 rewind(fo); 269 (void)putc('\n', fout); 270 (void)fflush(fout); 271 if (ferror(fout)) 272 warn("%s", tempname); 273 (void)Fclose(fout); 274 } 275 276 /* 277 * Now either copy "image" to the desired file 278 * or give it as the standard input to the desired 279 * program as appropriate. 280 */ 281 if (ispipe) { 282 pid_t pid; 283 char *shell; 284 sigset_t nset; 285 286 /* 287 * XXX 288 * We can't really reuse the same image file, 289 * because multiple piped recipients will 290 * share the same lseek location and trample 291 * on one another. 292 */ 293 shell = value("SHELL"); 294 sigemptyset(&nset); 295 sigaddset(&nset, SIGHUP); 296 sigaddset(&nset, SIGINT); 297 sigaddset(&nset, SIGQUIT); 298 pid = start_command(shell, &nset, 299 image, -1, "-c", fname, NULL); 300 if (pid < 0) { 301 senderr++; 302 goto cant; 303 } 304 free_child(pid); 305 } else { 306 int f; 307 if ((fout = Fopen(fname, "a")) == NULL) { 308 warn("%s", fname); 309 senderr++; 310 goto cant; 311 } 312 if ((f = dup(image)) < 0) { 313 warn("dup"); 314 fin = NULL; 315 } else 316 fin = Fdopen(f, "r"); 317 if (fin == NULL) { 318 fputs("Can't reopen image\n", stderr); 319 (void)Fclose(fout); 320 senderr++; 321 goto cant; 322 } 323 rewind(fin); 324 while ((c = getc(fin)) != EOF) 325 (void)putc(c, fout); 326 if (ferror(fout)) { 327 senderr++; 328 warn("%s", fname); 329 } 330 (void)Fclose(fout); 331 (void)Fclose(fin); 332 } 333 cant: 334 /* 335 * In days of old we removed the entry from the 336 * the list; now for sake of header expansion 337 * we leave it in and mark it as deleted. 338 */ 339 np->n_type |= GDEL; 340 np = np->n_flink; 341 } 342 if (image >= 0) { 343 (void)close(image); 344 image = -1; 345 } 346 return(top); 347 } 348 349 /* 350 * Determine if the passed address is a local "send to file" address. 351 * If any of the network metacharacters precedes any slashes, it can't 352 * be a filename. We cheat with .'s to allow path names like ./... 353 */ 354 int 355 isfileaddr(char *name) 356 { 357 char *cp; 358 359 if (*name == '+') 360 return(1); 361 for (cp = name; *cp; cp++) { 362 if (*cp == '!' || *cp == '%' || *cp == '@') 363 return(0); 364 if (*cp == '/') 365 return(1); 366 } 367 return(0); 368 } 369 370 /* 371 * Map all of the aliased users in the invoker's mailrc 372 * file and insert them into the list. 373 * Changed after all these months of service to recursively 374 * expand names (2/14/80). 375 */ 376 struct name * 377 usermap(struct name *names) 378 { 379 struct name *new, *np, *cp; 380 struct grouphead *gh; 381 int metoo; 382 383 new = NULL; 384 np = names; 385 metoo = (value("metoo") != NULL); 386 while (np != NULL) { 387 if (np->n_name[0] == '\\') { 388 cp = np->n_flink; 389 new = put(new, np); 390 np = cp; 391 continue; 392 } 393 gh = findgroup(np->n_name); 394 cp = np->n_flink; 395 if (gh != NULL) 396 new = gexpand(new, gh, metoo, np->n_type); 397 else 398 new = put(new, np); 399 np = cp; 400 } 401 return(new); 402 } 403 404 /* 405 * Recursively expand a group name. We limit the expansion to some 406 * fixed level to keep things from going haywire. 407 * Direct recursion is not expanded for convenience. 408 */ 409 struct name * 410 gexpand(struct name *nlist, struct grouphead *gh, int metoo, int ntype) 411 { 412 struct group *gp; 413 struct grouphead *ngh; 414 struct name *np; 415 static int depth; 416 char *cp; 417 418 if (depth > MAXEXP) { 419 printf("Expanding alias to depth larger than %d\n", MAXEXP); 420 return(nlist); 421 } 422 depth++; 423 for (gp = gh->g_list; gp != NULL; gp = gp->ge_link) { 424 cp = gp->ge_name; 425 if (*cp == '\\') 426 goto quote; 427 if (strcmp(cp, gh->g_name) == 0) 428 goto quote; 429 if ((ngh = findgroup(cp)) != NULL) { 430 nlist = gexpand(nlist, ngh, metoo, ntype); 431 continue; 432 } 433 quote: 434 np = nalloc(cp, ntype); 435 /* 436 * At this point should allow to expand 437 * to self if only person in group 438 */ 439 if (gp == gh->g_list && gp->ge_link == NULL) 440 goto skip; 441 if (!metoo && strcmp(cp, myname) == 0) 442 np->n_type |= GDEL; 443 skip: 444 nlist = put(nlist, np); 445 } 446 depth--; 447 return(nlist); 448 } 449 450 /* 451 * Concatenate the two passed name lists, return the result. 452 */ 453 struct name * 454 cat(struct name *n1, struct name *n2) 455 { 456 struct name *tail; 457 458 if (n1 == NULL) 459 return(n2); 460 if (n2 == NULL) 461 return(n1); 462 tail = tailof(n1); 463 tail->n_flink = n2; 464 n2->n_blink = tail; 465 return(n1); 466 } 467 468 /* 469 * Unpack the name list onto a vector of strings. 470 * Return an error if the name list won't fit. 471 */ 472 char ** 473 unpack(struct name *sm, struct name *np) 474 { 475 char **ap, **top; 476 int t, extra, metoo, verbose; 477 478 if ((t = count(np)) == 0) 479 errx(1, "No names to unpack"); 480 t += count(sm); 481 482 /* 483 * Compute the number of extra arguments we will need. 484 * We need at least four extra -- one for "send-mail", one for the 485 * "-i" flag, one for the "--" to signal end of command line 486 * arguments, and one for the terminating 0 pointer. 487 */ 488 extra = 4; 489 metoo = value("metoo") != NULL; 490 if (metoo) 491 extra++; 492 verbose = value("verbose") != NULL; 493 if (verbose) 494 extra++; 495 top = (char **)salloc((t + extra) * sizeof(*top)); 496 ap = top; 497 *ap++ = "send-mail"; 498 *ap++ = "-i"; 499 if (metoo) 500 *ap++ = "-m"; 501 if (verbose) 502 *ap++ = "-v"; 503 for (; sm != NULL; sm = sm->n_flink) 504 if ((sm->n_type & GDEL) == 0) 505 *ap++ = sm->n_name; 506 *ap++ = "--"; 507 for (; np != NULL; np = np->n_flink) 508 if ((np->n_type & GDEL) == 0) 509 *ap++ = np->n_name; 510 *ap = NULL; 511 return(top); 512 } 513 514 /* 515 * Remove all of the duplicates from the passed name list by 516 * insertion sorting them, then checking for dups. 517 * Return the head of the new list. 518 */ 519 struct name * 520 elide(struct name *names) 521 { 522 struct name *np, *t, *new; 523 struct name *x; 524 525 if (names == NULL) 526 return(NULL); 527 new = names; 528 np = names; 529 np = np->n_flink; 530 if (np != NULL) 531 np->n_blink = NULL; 532 new->n_flink = NULL; 533 while (np != NULL) { 534 t = new; 535 while (strcasecmp(t->n_name, np->n_name) < 0) { 536 if (t->n_flink == NULL) 537 break; 538 t = t->n_flink; 539 } 540 541 /* 542 * If we ran out of t's, put the new entry after 543 * the current value of t. 544 */ 545 if (strcasecmp(t->n_name, np->n_name) < 0) { 546 t->n_flink = np; 547 np->n_blink = t; 548 t = np; 549 np = np->n_flink; 550 t->n_flink = NULL; 551 continue; 552 } 553 554 /* 555 * Otherwise, put the new entry in front of the 556 * current t. If at the front of the list, 557 * the new guy becomes the new head of the list. 558 */ 559 if (t == new) { 560 t = np; 561 np = np->n_flink; 562 t->n_flink = new; 563 new->n_blink = t; 564 t->n_blink = NULL; 565 new = t; 566 continue; 567 } 568 569 /* 570 * The normal case -- we are inserting into the 571 * middle of the list. 572 */ 573 x = np; 574 np = np->n_flink; 575 x->n_flink = t; 576 x->n_blink = t->n_blink; 577 t->n_blink->n_flink = x; 578 t->n_blink = x; 579 } 580 581 /* 582 * Now the list headed up by new is sorted. 583 * Go through it and remove duplicates. 584 */ 585 np = new; 586 while (np != NULL) { 587 t = np; 588 while (t->n_flink != NULL && 589 strcasecmp(np->n_name, t->n_flink->n_name) == 0) 590 t = t->n_flink; 591 if (t == np || t == NULL) { 592 np = np->n_flink; 593 continue; 594 } 595 596 /* 597 * Now t points to the last entry with the same name 598 * as np. Make np point beyond t. 599 */ 600 np->n_flink = t->n_flink; 601 if (t->n_flink != NULL) 602 t->n_flink->n_blink = np; 603 np = np->n_flink; 604 } 605 return(new); 606 } 607 608 /* 609 * Put another node onto a list of names and return 610 * the list. 611 */ 612 struct name * 613 put(struct name *list, struct name *node) 614 { 615 node->n_flink = list; 616 node->n_blink = NULL; 617 if (list != NULL) 618 list->n_blink = node; 619 return(node); 620 } 621 622 /* 623 * Determine the number of undeleted elements in 624 * a name list and return it. 625 */ 626 int 627 count(struct name *np) 628 { 629 int c; 630 631 for (c = 0; np != NULL; np = np->n_flink) 632 if ((np->n_type & GDEL) == 0) 633 c++; 634 return(c); 635 } 636 637 /* 638 * Delete the given name from a namelist. 639 */ 640 struct name * 641 delname(struct name *np, char *name) 642 { 643 struct name *p; 644 645 for (p = np; p != NULL; p = p->n_flink) 646 if ((strcasecmp(p->n_name, name) == 0) || 647 (value("allnet") && 648 strncasecmp(p->n_name, name, strlen(name)) == 0 && 649 *(p->n_name+strlen(name)) == '@')) { 650 if (p->n_blink == NULL) { 651 if (p->n_flink != NULL) 652 p->n_flink->n_blink = NULL; 653 np = p->n_flink; 654 continue; 655 } 656 if (p->n_flink == NULL) { 657 if (p->n_blink != NULL) 658 p->n_blink->n_flink = NULL; 659 continue; 660 } 661 p->n_blink->n_flink = p->n_flink; 662 p->n_flink->n_blink = p->n_blink; 663 } 664 return(np); 665 } 666 667 /* 668 * Pretty print a name list 669 * Uncomment it if you need it. 670 */ 671 #if 0 672 void 673 prettyprint(struct name *name) 674 { 675 struct name *np; 676 677 np = name; 678 while (np != NULL) { 679 fprintf(stderr, "%s(%d) ", np->n_name, np->n_type); 680 np = np->n_flink; 681 } 682 putc('\n', stderr); 683 } 684 #endif 685