1 /* 2 * Copyright (c) 1980 Regents of the University of California. 3 * All rights reserved. 4 * 5 * %sccs.include.redist.c% 6 */ 7 8 #ifndef lint 9 static char sccsid[] = "@(#)names.c 5.17 (Berkeley) 06/26/92"; 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 fprintf(fout, "From %s %s", myname, date); 237 puthead(hp, fout, GTO|GSUBJECT|GCC|GNL); 238 while ((c = getc(fo)) != EOF) 239 (void) putc(c, fout); 240 rewind(fo); 241 (void) putc('\n', fout); 242 (void) fflush(fout); 243 if (ferror(fout)) 244 perror(tempEdit); 245 (void) Fclose(fout); 246 } 247 248 /* 249 * Now either copy "image" to the desired file 250 * or give it as the standard input to the desired 251 * program as appropriate. 252 */ 253 254 if (ispipe) { 255 int pid; 256 char *shell; 257 258 /* 259 * XXX 260 * We can't really reuse the same image file, 261 * because multiple piped recipients will 262 * share the same lseek location and trample 263 * on one another. 264 */ 265 if ((shell = value("SHELL")) == NOSTR) 266 shell = _PATH_CSHELL; 267 pid = start_command(shell, sigmask(SIGHUP)| 268 sigmask(SIGINT)|sigmask(SIGQUIT), 269 image, -1, "-c", fname, NOSTR); 270 if (pid < 0) { 271 senderr++; 272 goto cant; 273 } 274 free_child(pid); 275 } else { 276 int f; 277 if ((fout = Fopen(fname, "a")) == NULL) { 278 perror(fname); 279 senderr++; 280 goto cant; 281 } 282 if ((f = dup(image)) < 0) { 283 perror("dup"); 284 fin = NULL; 285 } else 286 fin = Fdopen(f, "r"); 287 if (fin == NULL) { 288 fprintf(stderr, "Can't reopen image\n"); 289 (void) Fclose(fout); 290 senderr++; 291 goto cant; 292 } 293 rewind(fin); 294 while ((c = getc(fin)) != EOF) 295 (void) putc(c, fout); 296 if (ferror(fout)) 297 senderr++, perror(fname); 298 (void) Fclose(fout); 299 (void) Fclose(fin); 300 } 301 cant: 302 /* 303 * In days of old we removed the entry from the 304 * the list; now for sake of header expansion 305 * we leave it in and mark it as deleted. 306 */ 307 np->n_type |= GDEL; 308 np = np->n_flink; 309 } 310 if (image >= 0) { 311 (void) close(image); 312 image = -1; 313 } 314 return(top); 315 } 316 317 /* 318 * Determine if the passed address is a local "send to file" address. 319 * If any of the network metacharacters precedes any slashes, it can't 320 * be a filename. We cheat with .'s to allow path names like ./... 321 */ 322 int 323 isfileaddr(name) 324 char *name; 325 { 326 register char *cp; 327 328 if (*name == '+') 329 return 1; 330 for (cp = name; *cp; cp++) { 331 if (*cp == '!' || *cp == '%' || *cp == '@') 332 return 0; 333 if (*cp == '/') 334 return 1; 335 } 336 return 0; 337 } 338 339 /* 340 * Map all of the aliased users in the invoker's mailrc 341 * file and insert them into the list. 342 * Changed after all these months of service to recursively 343 * expand names (2/14/80). 344 */ 345 346 struct name * 347 usermap(names) 348 struct name *names; 349 { 350 register struct name *new, *np, *cp; 351 struct grouphead *gh; 352 register int metoo; 353 354 new = NIL; 355 np = names; 356 metoo = (value("metoo") != NOSTR); 357 while (np != NIL) { 358 if (np->n_name[0] == '\\') { 359 cp = np->n_flink; 360 new = put(new, np); 361 np = cp; 362 continue; 363 } 364 gh = findgroup(np->n_name); 365 cp = np->n_flink; 366 if (gh != NOGRP) 367 new = gexpand(new, gh, metoo, np->n_type); 368 else 369 new = put(new, np); 370 np = cp; 371 } 372 return(new); 373 } 374 375 /* 376 * Recursively expand a group name. We limit the expansion to some 377 * fixed level to keep things from going haywire. 378 * Direct recursion is not expanded for convenience. 379 */ 380 381 struct name * 382 gexpand(nlist, gh, metoo, ntype) 383 struct name *nlist; 384 struct grouphead *gh; 385 int metoo, ntype; 386 { 387 struct group *gp; 388 struct grouphead *ngh; 389 struct name *np; 390 static int depth; 391 char *cp; 392 393 if (depth > MAXEXP) { 394 printf("Expanding alias to depth larger than %d\n", MAXEXP); 395 return(nlist); 396 } 397 depth++; 398 for (gp = gh->g_list; gp != NOGE; gp = gp->ge_link) { 399 cp = gp->ge_name; 400 if (*cp == '\\') 401 goto quote; 402 if (strcmp(cp, gh->g_name) == 0) 403 goto quote; 404 if ((ngh = findgroup(cp)) != NOGRP) { 405 nlist = gexpand(nlist, ngh, metoo, ntype); 406 continue; 407 } 408 quote: 409 np = nalloc(cp, ntype); 410 /* 411 * At this point should allow to expand 412 * to self if only person in group 413 */ 414 if (gp == gh->g_list && gp->ge_link == NOGE) 415 goto skip; 416 if (!metoo && strcmp(cp, myname) == 0) 417 np->n_type |= GDEL; 418 skip: 419 nlist = put(nlist, np); 420 } 421 depth--; 422 return(nlist); 423 } 424 425 /* 426 * Concatenate the two passed name lists, return the result. 427 */ 428 struct name * 429 cat(n1, n2) 430 struct name *n1, *n2; 431 { 432 register struct name *tail; 433 434 if (n1 == NIL) 435 return(n2); 436 if (n2 == NIL) 437 return(n1); 438 tail = tailof(n1); 439 tail->n_flink = n2; 440 n2->n_blink = tail; 441 return(n1); 442 } 443 444 /* 445 * Unpack the name list onto a vector of strings. 446 * Return an error if the name list won't fit. 447 */ 448 char ** 449 unpack(np) 450 struct name *np; 451 { 452 register char **ap, **top; 453 register struct name *n; 454 int t, extra, metoo, verbose; 455 456 n = np; 457 if ((t = count(n)) == 0) 458 panic("No names to unpack"); 459 /* 460 * Compute the number of extra arguments we will need. 461 * We need at least two extra -- one for "mail" and one for 462 * the terminating 0 pointer. Additional spots may be needed 463 * to pass along -f to the host mailer. 464 */ 465 extra = 2; 466 extra++; 467 metoo = value("metoo") != NOSTR; 468 if (metoo) 469 extra++; 470 verbose = value("verbose") != NOSTR; 471 if (verbose) 472 extra++; 473 top = (char **) salloc((t + extra) * sizeof *top); 474 ap = top; 475 *ap++ = "send-mail"; 476 *ap++ = "-i"; 477 if (metoo) 478 *ap++ = "-m"; 479 if (verbose) 480 *ap++ = "-v"; 481 for (; n != NIL; n = n->n_flink) 482 if ((n->n_type & GDEL) == 0) 483 *ap++ = n->n_name; 484 *ap = NOSTR; 485 return(top); 486 } 487 488 /* 489 * Remove all of the duplicates from the passed name list by 490 * insertion sorting them, then checking for dups. 491 * Return the head of the new list. 492 */ 493 struct name * 494 elide(names) 495 struct name *names; 496 { 497 register struct name *np, *t, *new; 498 struct name *x; 499 500 if (names == NIL) 501 return(NIL); 502 new = names; 503 np = names; 504 np = np->n_flink; 505 if (np != NIL) 506 np->n_blink = NIL; 507 new->n_flink = NIL; 508 while (np != NIL) { 509 t = new; 510 while (strcasecmp(t->n_name, np->n_name) < 0) { 511 if (t->n_flink == NIL) 512 break; 513 t = t->n_flink; 514 } 515 516 /* 517 * If we ran out of t's, put the new entry after 518 * the current value of t. 519 */ 520 521 if (strcasecmp(t->n_name, np->n_name) < 0) { 522 t->n_flink = np; 523 np->n_blink = t; 524 t = np; 525 np = np->n_flink; 526 t->n_flink = NIL; 527 continue; 528 } 529 530 /* 531 * Otherwise, put the new entry in front of the 532 * current t. If at the front of the list, 533 * the new guy becomes the new head of the list. 534 */ 535 536 if (t == new) { 537 t = np; 538 np = np->n_flink; 539 t->n_flink = new; 540 new->n_blink = t; 541 t->n_blink = NIL; 542 new = t; 543 continue; 544 } 545 546 /* 547 * The normal case -- we are inserting into the 548 * middle of the list. 549 */ 550 551 x = np; 552 np = np->n_flink; 553 x->n_flink = t; 554 x->n_blink = t->n_blink; 555 t->n_blink->n_flink = x; 556 t->n_blink = x; 557 } 558 559 /* 560 * Now the list headed up by new is sorted. 561 * Go through it and remove duplicates. 562 */ 563 564 np = new; 565 while (np != NIL) { 566 t = np; 567 while (t->n_flink != NIL && 568 strcasecmp(np->n_name, t->n_flink->n_name) == 0) 569 t = t->n_flink; 570 if (t == np || t == NIL) { 571 np = np->n_flink; 572 continue; 573 } 574 575 /* 576 * Now t points to the last entry with the same name 577 * as np. Make np point beyond t. 578 */ 579 580 np->n_flink = t->n_flink; 581 if (t->n_flink != NIL) 582 t->n_flink->n_blink = np; 583 np = np->n_flink; 584 } 585 return(new); 586 } 587 588 /* 589 * Put another node onto a list of names and return 590 * the list. 591 */ 592 struct name * 593 put(list, node) 594 struct name *list, *node; 595 { 596 node->n_flink = list; 597 node->n_blink = NIL; 598 if (list != NIL) 599 list->n_blink = node; 600 return(node); 601 } 602 603 /* 604 * Determine the number of undeleted elements in 605 * a name list and return it. 606 */ 607 int 608 count(np) 609 register struct name *np; 610 { 611 register int c; 612 613 for (c = 0; np != NIL; np = np->n_flink) 614 if ((np->n_type & GDEL) == 0) 615 c++; 616 return c; 617 } 618 619 /* 620 * Delete the given name from a namelist. 621 */ 622 struct name * 623 delname(np, name) 624 register struct name *np; 625 char name[]; 626 { 627 register struct name *p; 628 629 for (p = np; p != NIL; p = p->n_flink) 630 if (strcasecmp(p->n_name, name) == 0) { 631 if (p->n_blink == NIL) { 632 if (p->n_flink != NIL) 633 p->n_flink->n_blink = NIL; 634 np = p->n_flink; 635 continue; 636 } 637 if (p->n_flink == NIL) { 638 if (p->n_blink != NIL) 639 p->n_blink->n_flink = NIL; 640 continue; 641 } 642 p->n_blink->n_flink = p->n_flink; 643 p->n_flink->n_blink = p->n_blink; 644 } 645 return np; 646 } 647 648 /* 649 * Pretty print a name list 650 * Uncomment it if you need it. 651 */ 652 653 /* 654 void 655 prettyprint(name) 656 struct name *name; 657 { 658 register struct name *np; 659 660 np = name; 661 while (np != NIL) { 662 fprintf(stderr, "%s(%d) ", np->n_name, np->n_type); 663 np = np->n_flink; 664 } 665 fprintf(stderr, "\n"); 666 } 667 */ 668