1 /* 2 * Copyright (c) 1980 Regents of the University of California. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms are permitted 6 * provided that the above copyright notice and this paragraph are 7 * duplicated in all such forms and that any documentation, 8 * advertising materials, and other materials related to such 9 * distribution and use acknowledge that the software was developed 10 * by the University of California, Berkeley. The name of the 11 * University may not be used to endorse or promote products derived 12 * from this software without specific prior written permission. 13 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 14 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 15 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 16 */ 17 18 #ifndef lint 19 static char sccsid[] = "@(#)names.c 5.13 (Berkeley) 07/08/88"; 20 #endif /* not lint */ 21 22 /* 23 * Mail -- a mail program 24 * 25 * Handle name lists. 26 */ 27 28 #include "rcv.h" 29 30 /* 31 * Allocate a single element of a name list, 32 * initialize its name field to the passed 33 * name and return it. 34 */ 35 struct name * 36 nalloc(str, ntype) 37 char str[]; 38 { 39 register struct name *np; 40 41 np = (struct name *) salloc(sizeof *np); 42 np->n_flink = NIL; 43 np->n_blink = NIL; 44 np->n_type = ntype; 45 np->n_name = savestr(str); 46 return(np); 47 } 48 49 /* 50 * Find the tail of a list and return it. 51 */ 52 struct name * 53 tailof(name) 54 struct name *name; 55 { 56 register struct name *np; 57 58 np = name; 59 if (np == NIL) 60 return(NIL); 61 while (np->n_flink != NIL) 62 np = np->n_flink; 63 return(np); 64 } 65 66 /* 67 * Extract a list of names from a line, 68 * and make a list of names from it. 69 * Return the list or NIL if none found. 70 */ 71 struct name * 72 extract(line, ntype) 73 char line[]; 74 { 75 register char *cp; 76 register struct name *top, *np, *t; 77 char nbuf[BUFSIZ]; 78 79 if (line == NOSTR || *line == '\0') 80 return NIL; 81 top = NIL; 82 np = NIL; 83 cp = line; 84 while ((cp = yankword(cp, nbuf)) != NOSTR) { 85 t = nalloc(nbuf, ntype); 86 if (top == NIL) 87 top = t; 88 else 89 np->n_flink = t; 90 t->n_blink = np; 91 np = t; 92 } 93 return top; 94 } 95 96 /* 97 * Turn a list of names into a string of the same names. 98 */ 99 char * 100 detract(np, ntype) 101 register struct name *np; 102 { 103 register int s; 104 register char *cp, *top; 105 register struct name *p; 106 register int comma; 107 108 comma = ntype & GCOMMA; 109 if (np == NIL) 110 return(NOSTR); 111 ntype &= ~GCOMMA; 112 s = 0; 113 if (debug && comma) 114 fprintf(stderr, "detract asked to insert commas\n"); 115 for (p = np; p != NIL; p = p->n_flink) { 116 if (ntype && (p->n_type & GMASK) != ntype) 117 continue; 118 s += strlen(p->n_name) + 1; 119 if (comma) 120 s++; 121 } 122 if (s == 0) 123 return(NOSTR); 124 s += 2; 125 top = salloc(s); 126 cp = top; 127 for (p = np; p != NIL; p = p->n_flink) { 128 if (ntype && (p->n_type & GMASK) != ntype) 129 continue; 130 cp = copy(p->n_name, cp); 131 if (comma && p->n_flink != NIL) 132 *cp++ = ','; 133 *cp++ = ' '; 134 } 135 *--cp = 0; 136 if (comma && *--cp == ',') 137 *cp = 0; 138 return(top); 139 } 140 141 /* 142 * Grab a single word (liberal word) 143 * Throw away things between ()'s, and take anything between <>. 144 */ 145 char * 146 yankword(ap, wbuf) 147 char *ap, wbuf[]; 148 { 149 register char *cp, *cp2; 150 151 cp = ap; 152 for (;;) { 153 if (*cp == '\0') 154 return NOSTR; 155 if (*cp == '(') { 156 register int nesting = 0; 157 158 while (*cp != '\0') { 159 switch (*cp++) { 160 case '(': 161 nesting++; 162 break; 163 case ')': 164 --nesting; 165 break; 166 } 167 if (nesting <= 0) 168 break; 169 } 170 } else if (*cp == ' ' || *cp == '\t' || *cp == ',') 171 cp++; 172 else 173 break; 174 } 175 if (*cp == '<') 176 for (cp2 = wbuf; *cp && (*cp2++ = *cp++) != '>';) 177 ; 178 else 179 for (cp2 = wbuf; *cp && !index(" \t,(", *cp); *cp2++ = *cp++) 180 ; 181 *cp2 = '\0'; 182 return cp; 183 } 184 185 /* 186 * For each recipient in the passed name list with a / 187 * in the name, append the message to the end of the named file 188 * and remove him from the recipient list. 189 * 190 * Recipients whose name begins with | are piped through the given 191 * program and removed. 192 */ 193 struct name * 194 outof(names, fo, hp) 195 struct name *names; 196 FILE *fo; 197 struct header *hp; 198 { 199 register int c; 200 register struct name *np, *top; 201 time_t now, time(); 202 char *date, *fname, *ctime(); 203 FILE *fout, *fin; 204 int ispipe; 205 extern char tempEdit[]; 206 207 top = names; 208 np = names; 209 (void) time(&now); 210 date = ctime(&now); 211 while (np != NIL) { 212 if (!isfileaddr(np->n_name) && np->n_name[0] != '|') { 213 np = np->n_flink; 214 continue; 215 } 216 ispipe = np->n_name[0] == '|'; 217 if (ispipe) 218 fname = np->n_name+1; 219 else 220 fname = expand(np->n_name); 221 222 /* 223 * See if we have copied the complete message out yet. 224 * If not, do so. 225 */ 226 227 if (image < 0) { 228 if ((fout = fopen(tempEdit, "a")) == NULL) { 229 perror(tempEdit); 230 senderr++; 231 goto cant; 232 } 233 image = open(tempEdit, 2); 234 (void) unlink(tempEdit); 235 if (image < 0) { 236 perror(tempEdit); 237 senderr++; 238 goto cant; 239 } 240 else { 241 fprintf(fout, "From %s %s", myname, date); 242 puthead(hp, fout, GTO|GSUBJECT|GCC|GNL); 243 while ((c = getc(fo)) != EOF) 244 (void) putc(c, fout); 245 rewind(fo); 246 (void) putc('\n', fout); 247 (void) fflush(fout); 248 if (ferror(fout)) 249 perror(tempEdit); 250 (void) fclose(fout); 251 } 252 } 253 254 /* 255 * Now either copy "image" to the desired file 256 * or give it as the standard input to the desired 257 * program as appropriate. 258 */ 259 260 if (ispipe) { 261 int pid; 262 char *shell; 263 264 /* XXX, can't really reuse the same image file */ 265 if ((shell = value("SHELL")) == NOSTR) 266 shell = SHELL; 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 if ((fout = fopen(fname, "a")) == NULL) { 277 perror(fname); 278 senderr++; 279 goto cant; 280 } 281 fin = Fdopen(image, "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