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