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