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