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