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 *
nalloc(str,ntype)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 *
tailof(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 *
extract(line,ntype)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 *
detract(np,ntype)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 *
yankword(ap,wbuf)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 *
outof(names,fo,hp)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
isfileaddr(name)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 *
usermap(names)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 *
gexpand(nlist,gh,metoo,ntype)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 *
cat(n1,n2)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 **
unpack(np)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 *
elide(names)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 *
put(list,node)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
count(np)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 *
delname(np,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