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