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