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