date and time created 83/02/11 15:45:06 by rrh
[unix-history] / usr / src / usr.bin / mail / names.c
CommitLineData
65d3742f
KS
1#
2
3/*
4 * Mail -- a mail program
5 *
6 * Handle name lists.
7 */
8
9#include "rcv.h"
10
03c98b7d 11static char *SccsId = "@(#)names.c 2.8 %G%";
65d3742f
KS
12
13/*
14 * Allocate a single element of a name list,
15 * initialize its name field to the passed
16 * name and return it.
17 */
18
19struct name *
20nalloc(str)
21 char str[];
22{
23 register struct name *np;
24
25 np = (struct name *) salloc(sizeof *np);
26 np->n_flink = NIL;
27 np->n_blink = NIL;
28 np->n_type = -1;
29 np->n_name = savestr(str);
30 return(np);
31}
32
33/*
34 * Find the tail of a list and return it.
35 */
36
37struct name *
38tailof(name)
39 struct name *name;
40{
41 register struct name *np;
42
43 np = name;
44 if (np == NIL)
45 return(NIL);
46 while (np->n_flink != NIL)
47 np = np->n_flink;
48 return(np);
49}
50
51/*
52 * Extract a list of names from a line,
53 * and make a list of names from it.
54 * Return the list or NIL if none found.
55 */
56
57struct name *
58extract(line, ntype)
59 char line[];
60{
61 register char *cp;
62 register struct name *top, *np, *t;
63 char nbuf[BUFSIZ], abuf[BUFSIZ];
64
65 if (line == NOSTR || strlen(line) == 0)
66 return(NIL);
67 top = NIL;
68 np = NIL;
69 cp = line;
70 while ((cp = yankword(cp, nbuf)) != NOSTR) {
71 if (np != NIL && equal(nbuf, "at")) {
72 strcpy(abuf, nbuf);
73 if ((cp = yankword(cp, nbuf)) == NOSTR) {
74 strcpy(nbuf, abuf);
75 goto normal;
76 }
77 strcpy(abuf, np->n_name);
78 stradd(abuf, '@');
79 strcat(abuf, nbuf);
80 np->n_name = savestr(abuf);
81 continue;
82 }
83normal:
84 t = nalloc(nbuf);
85 t->n_type = 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
100char *
101detract(np, ntype)
102 register struct name *np;
103{
104 register int s;
105 register char *cp, *top;
106 register struct name *p;
107 register int comma;
108
109 comma = ntype & GCOMMA;
110 if (np == NIL)
111 return(NOSTR);
112 ntype &= ~GCOMMA;
113 s = 0;
114 if (debug && comma)
115 fprintf(stderr, "detract asked to insert commas\n");
116 for (p = np; p != NIL; p = p->n_flink) {
117 if (ntype && (p->n_type & GMASK) != ntype)
118 continue;
119 s += strlen(p->n_name) + 1;
120 if (comma)
121 s++;
122 }
123 if (s == 0)
124 return(NOSTR);
125 s += 2;
126 top = salloc(s);
127 cp = top;
128 for (p = np; p != NIL; p = p->n_flink) {
129 if (ntype && (p->n_type & GMASK) != ntype)
130 continue;
131 cp = copy(p->n_name, cp);
132 if (comma && p->n_flink != NIL)
133 *cp++ = ',';
134 *cp++ = ' ';
135 }
136 *--cp = 0;
137 if (comma && *--cp == ',')
138 *cp = 0;
139 return(top);
140}
141
142/*
143 * Grab a single word (liberal word)
144 * Throw away things between ()'s.
145 */
146
147char *
148yankword(ap, wbuf)
149 char *ap, wbuf[];
150{
151 register char *cp, *cp2;
152
153 do {
154 for (cp = ap; *cp && any(*cp, " \t,"); cp++)
155 ;
156 if (*cp == '(') {
157 while (*cp && *cp != ')')
158 cp++;
159 if (*cp)
160 cp++;
161 }
162 if (*cp == '\0')
163 return(NOSTR);
164 } while (any(*cp, " \t,("));
165 for (cp2 = wbuf; *cp && !any(*cp, " \t,("); *cp2++ = *cp++)
166 ;
167 *cp2 = '\0';
168 return(cp);
169}
170
171/*
172 * Verify that all the users in the list of names are
173 * legitimate. Bitch about and delink those who aren't.
174 */
175
176struct name *
177verify(names)
178 struct name *names;
179{
180 register struct name *np, *top, *t, *x;
181 register char *cp;
182
ac57be53 183#ifdef SENDMAIL
65d3742f
KS
184 return(names);
185#else
186 top = names;
187 np = names;
188 while (np != NIL) {
189 if (np->n_type & GDEL) {
190 np = np->n_flink;
191 continue;
192 }
193 for (cp = "!:@^"; *cp; cp++)
194 if (any(*cp, np->n_name))
195 break;
196 if (*cp != 0) {
197 np = np->n_flink;
198 continue;
199 }
200 cp = np->n_name;
201 while (*cp == '\\')
202 cp++;
203 if (equal(cp, "msgs") ||
204 getuserid(cp) != -1) {
205 np = np->n_flink;
206 continue;
207 }
208 fprintf(stderr, "Can't send to %s\n", np->n_name);
209 senderr++;
210 if (np == top) {
211 top = np->n_flink;
212 if (top != NIL)
213 top->n_blink = NIL;
214 np = top;
215 continue;
216 }
217 x = np->n_blink;
218 t = np->n_flink;
219 x->n_flink = t;
220 if (t != NIL)
221 t->n_blink = x;
222 np = t;
223 }
224 return(top);
225#endif
226}
227
228/*
229 * For each recipient in the passed name list with a /
230 * in the name, append the message to the end of the named file
231 * and remove him from the recipient list.
232 *
233 * Recipients whose name begins with | are piped through the given
234 * program and removed.
235 */
236
237struct name *
238outof(names, fo, hp)
239 struct name *names;
240 FILE *fo;
241 struct header *hp;
242{
243 register int c;
244 register struct name *np, *top, *t, *x;
245 long now;
246 char *date, *fname, *shell, *ctime();
247 FILE *fout, *fin;
248 int ispipe, s, pid;
249 extern char tempEdit[];
250
251 top = names;
252 np = names;
253 time(&now);
254 date = ctime(&now);
255 while (np != NIL) {
5cf80950 256 if (!isfileaddr(np->n_name) && np->n_name[0] != '|') {
65d3742f
KS
257 np = np->n_flink;
258 continue;
259 }
260 ispipe = np->n_name[0] == '|';
261 if (ispipe)
262 fname = np->n_name+1;
263 else
264 fname = expand(np->n_name);
265
266 /*
267 * See if we have copied the complete message out yet.
268 * If not, do so.
269 */
270
271 if (image < 0) {
272 if ((fout = fopen(tempEdit, "a")) == NULL) {
273 perror(tempEdit);
274 senderr++;
275 goto cant;
276 }
277 image = open(tempEdit, 2);
278 unlink(tempEdit);
279 if (image < 0) {
280 perror(tempEdit);
281 senderr++;
282 goto cant;
283 }
284 else {
285 rewind(fo);
286 fprintf(fout, "From %s %s", myname, date);
287 puthead(hp, fout, GTO|GSUBJECT|GCC|GNL);
288 while ((c = getc(fo)) != EOF)
289 putc(c, fout);
290 rewind(fo);
291 putc('\n', fout);
292 fflush(fout);
293 if (ferror(fout))
294 perror(tempEdit);
295 fclose(fout);
296 }
297 }
298
299 /*
300 * Now either copy "image" to the desired file
301 * or give it as the standard input to the desired
302 * program as appropriate.
303 */
304
305 if (ispipe) {
306 wait(&s);
307 switch (pid = fork()) {
308 case 0:
cb9e16fc 309 sigchild();
c3ca8d0d
KS
310 sigsys(SIGHUP, SIG_IGN);
311 sigsys(SIGINT, SIG_IGN);
312 sigsys(SIGQUIT, SIG_IGN);
65d3742f
KS
313 close(0);
314 dup(image);
315 close(image);
316 if ((shell = value("SHELL")) == NOSTR)
317 shell = SHELL;
318 execl(shell, shell, "-c", fname, 0);
319 perror(shell);
320 exit(1);
321 break;
322
323 case -1:
324 perror("fork");
325 senderr++;
326 goto cant;
327 }
328 }
329 else {
330 if ((fout = fopen(fname, "a")) == NULL) {
331 perror(fname);
332 senderr++;
333 goto cant;
334 }
335 fin = Fdopen(image, "r");
336 if (fin == NULL) {
337 fprintf(stderr, "Can't reopen image\n");
338 fclose(fout);
339 senderr++;
340 goto cant;
341 }
342 rewind(fin);
343 while ((c = getc(fin)) != EOF)
344 putc(c, fout);
345 if (ferror(fout))
346 senderr++, perror(fname);
347 fclose(fout);
348 fclose(fin);
349 }
350
351cant:
352
353 /*
354 * In days of old we removed the entry from the
355 * the list; now for sake of header expansion
356 * we leave it in and mark it as deleted.
357 */
358
359#ifdef CRAZYWOW
360 if (np == top) {
361 top = np->n_flink;
362 if (top != NIL)
363 top->n_blink = NIL;
364 np = top;
365 continue;
366 }
367 x = np->n_blink;
368 t = np->n_flink;
369 x->n_flink = t;
370 if (t != NIL)
371 t->n_blink = x;
372 np = t;
373#endif
374
375 np->n_type |= GDEL;
376 np = np->n_flink;
377 }
378 if (image >= 0) {
379 close(image);
380 image = -1;
381 }
382 return(top);
383}
384
5cf80950
KS
385/*
386 * Determine if the passed address is a local "send to file" address.
387 * If any of the network metacharacters precedes any slashes, it can't
388 * be a filename. We cheat with .'s to allow path names like ./...
389 */
390isfileaddr(name)
391 char *name;
392{
393 register char *cp;
394 extern char *metanet;
395
396 if (any('@', name))
397 return(0);
1c617162
KS
398 if (*name == '+')
399 return(1);
5cf80950
KS
400 for (cp = name; *cp; cp++) {
401 if (*cp == '.')
402 continue;
403 if (any(*cp, metanet))
404 return(0);
405 if (*cp == '/')
406 return(1);
407 }
408 return(0);
409}
410
65d3742f
KS
411/*
412 * Map all of the aliased users in the invoker's mailrc
413 * file and insert them into the list.
414 * Changed after all these months of service to recursively
415 * expand names (2/14/80).
416 */
417
418struct name *
419usermap(names)
420 struct name *names;
421{
422 register struct name *new, *np, *cp;
423 struct name *getto;
424 struct grouphead *gh;
425 register int metoo;
426
427 new = NIL;
428 np = names;
429 getto = NIL;
430 metoo = (value("metoo") != NOSTR);
431 while (np != NIL) {
432 if (np->n_name[0] == '\\') {
433 cp = np->n_flink;
434 new = put(new, np);
435 np = cp;
436 continue;
437 }
438 gh = findgroup(np->n_name);
439 cp = np->n_flink;
440 if (gh != NOGRP)
441 new = gexpand(new, gh, metoo, np->n_type);
442 else
443 new = put(new, np);
444 np = cp;
445 }
446 return(new);
447}
448
449/*
450 * Recursively expand a group name. We limit the expansion to some
451 * fixed level to keep things from going haywire.
452 * Direct recursion is not expanded for convenience.
453 */
454
455struct name *
456gexpand(nlist, gh, metoo, ntype)
457 struct name *nlist;
458 struct grouphead *gh;
459{
460 struct group *gp;
461 struct grouphead *ngh;
462 struct name *np;
463 static int depth;
464 char *cp;
465
466 if (depth > MAXEXP) {
467 printf("Expanding alias to depth larger than %d\n", MAXEXP);
468 return(nlist);
469 }
470 depth++;
471 for (gp = gh->g_list; gp != NOGE; gp = gp->ge_link) {
472 cp = gp->ge_name;
473 if (*cp == '\\')
474 goto quote;
475 if (strcmp(cp, gh->g_name) == 0)
476 goto quote;
477 if ((ngh = findgroup(cp)) != NOGRP) {
478 nlist = gexpand(nlist, ngh, metoo, ntype);
479 continue;
480 }
481quote:
482 np = nalloc(cp);
483 np->n_type = ntype;
484 /*
485 * At this point should allow to expand
486 * to self if only person in group
487 */
488 if (gp == gh->g_list && gp->ge_link == NOGE)
489 goto skip;
490 if (!metoo && strcmp(cp, myname) == 0)
491 np->n_type |= GDEL;
492skip:
493 nlist = put(nlist, np);
494 }
495 depth--;
496 return(nlist);
497}
498
499
500
501/*
502 * Compute the length of the passed name list and
503 * return it.
504 */
505
506lengthof(name)
507 struct name *name;
508{
509 register struct name *np;
510 register int c;
511
512 for (c = 0, np = name; np != NIL; c++, np = np->n_flink)
513 ;
514 return(c);
515}
516
517/*
518 * Concatenate the two passed name lists, return the result.
519 */
520
521struct name *
522cat(n1, n2)
523 struct name *n1, *n2;
524{
525 register struct name *tail;
526
527 if (n1 == NIL)
528 return(n2);
529 if (n2 == NIL)
530 return(n1);
531 tail = tailof(n1);
532 tail->n_flink = n2;
533 n2->n_blink = tail;
534 return(n1);
535}
536
537/*
538 * Unpack the name list onto a vector of strings.
539 * Return an error if the name list won't fit.
540 */
541
542char **
543unpack(np)
544 struct name *np;
545{
546 register char **ap, **top;
547 register struct name *n;
548 char *cp;
549 char hbuf[10];
03c98b7d 550 int t, extra, metoo, verbose;
65d3742f
KS
551
552 n = np;
553 if ((t = lengthof(n)) == 0)
554 panic("No names to unpack");
555
556 /*
557 * Compute the number of extra arguments we will need.
558 * We need at least two extra -- one for "mail" and one for
559 * the terminating 0 pointer. Additional spots may be needed
560 * to pass along -r and -f to the host mailer.
561 */
562
563 extra = 2;
564 if (rflag != NOSTR)
565 extra += 2;
ac57be53 566#ifdef SENDMAIL
65d3742f
KS
567 extra++;
568 metoo = value("metoo") != NOSTR;
569 if (metoo)
570 extra++;
03c98b7d
CL
571 verbose = value("verbose") != NOSTR;
572 if (verbose)
573 extra++;
ac57be53 574#endif SENDMAIL
65d3742f
KS
575 if (hflag)
576 extra += 2;
577 top = (char **) salloc((t + extra) * sizeof cp);
578 ap = top;
579 *ap++ = "send-mail";
580 if (rflag != NOSTR) {
581 *ap++ = "-r";
582 *ap++ = rflag;
583 }
ac57be53 584#ifdef SENDMAIL
65d3742f
KS
585 *ap++ = "-i";
586 if (metoo)
587 *ap++ = "-m";
03c98b7d
CL
588 if (verbose)
589 *ap++ = "-v";
ac57be53 590#endif SENDMAIL
65d3742f
KS
591 if (hflag) {
592 *ap++ = "-h";
593 sprintf(hbuf, "%d", hflag);
594 *ap++ = savestr(hbuf);
595 }
596 while (n != NIL) {
597 if (n->n_type & GDEL) {
598 n = n->n_flink;
599 continue;
600 }
6f63024f 601 *ap++ = n->n_name;
65d3742f
KS
602 n = n->n_flink;
603 }
604 *ap = NOSTR;
605 return(top);
606}
607
608/*
609 * See if the user named himself as a destination
610 * for outgoing mail. If so, set the global flag
611 * selfsent so that we avoid removing his mailbox.
612 */
613
614mechk(names)
615 struct name *names;
616{
617 register struct name *np;
618
619 for (np = names; np != NIL; np = np->n_flink)
20b5c5b1 620 if ((np->n_type & GDEL) == 0 && equal(np->n_name, myname)) {
65d3742f
KS
621 selfsent++;
622 return;
623 }
624}
625
626/*
627 * Remove all of the duplicates from the passed name list by
628 * insertion sorting them, then checking for dups.
629 * Return the head of the new list.
630 */
631
632struct name *
633elide(names)
634 struct name *names;
635{
636 register struct name *np, *t, *new;
637 struct name *x;
638
639 if (names == NIL)
640 return(NIL);
641 new = names;
642 np = names;
643 np = np->n_flink;
644 if (np != NIL)
645 np->n_blink = NIL;
646 new->n_flink = NIL;
647 while (np != NIL) {
648 t = new;
649 while (nstrcmp(t->n_name, np->n_name) < 0) {
650 if (t->n_flink == NIL)
651 break;
652 t = t->n_flink;
653 }
654
655 /*
656 * If we ran out of t's, put the new entry after
657 * the current value of t.
658 */
659
660 if (nstrcmp(t->n_name, np->n_name) < 0) {
661 t->n_flink = np;
662 np->n_blink = t;
663 t = np;
664 np = np->n_flink;
665 t->n_flink = NIL;
666 continue;
667 }
668
669 /*
670 * Otherwise, put the new entry in front of the
671 * current t. If at the front of the list,
672 * the new guy becomes the new head of the list.
673 */
674
675 if (t == new) {
676 t = np;
677 np = np->n_flink;
678 t->n_flink = new;
679 new->n_blink = t;
680 t->n_blink = NIL;
681 new = t;
682 continue;
683 }
684
685 /*
686 * The normal case -- we are inserting into the
687 * middle of the list.
688 */
689
690 x = np;
691 np = np->n_flink;
692 x->n_flink = t;
693 x->n_blink = t->n_blink;
694 t->n_blink->n_flink = x;
695 t->n_blink = x;
696 }
697
698 /*
699 * Now the list headed up by new is sorted.
700 * Go through it and remove duplicates.
701 */
702
703 np = new;
704 while (np != NIL) {
705 t = np;
706 while (t->n_flink!=NIL &&
707 icequal(np->n_name,t->n_flink->n_name))
708 t = t->n_flink;
709 if (t == np || t == NIL) {
710 np = np->n_flink;
711 continue;
712 }
713
714 /*
715 * Now t points to the last entry with the same name
716 * as np. Make np point beyond t.
717 */
718
719 np->n_flink = t->n_flink;
720 if (t->n_flink != NIL)
721 t->n_flink->n_blink = np;
722 np = np->n_flink;
723 }
724 return(new);
725}
726
727/*
728 * Version of strcmp which ignores case differences.
729 */
730
731nstrcmp(s1, s2)
732 register char *s1, *s2;
733{
734 register int c1, c2;
735
736 do {
737 c1 = *s1++;
738 c2 = *s2++;
739 } while (c1 && c1 == c2);
740 return(c1 - c2);
741}
742
743/*
744 * Put another node onto a list of names and return
745 * the list.
746 */
747
748struct name *
749put(list, node)
750 struct name *list, *node;
751{
752 node->n_flink = list;
753 node->n_blink = NIL;
754 if (list != NIL)
755 list->n_blink = node;
756 return(node);
757}
758
759/*
760 * Determine the number of elements in
761 * a name list and return it.
762 */
763
764count(np)
765 register struct name *np;
766{
767 register int c = 0;
768
769 while (np != NIL) {
770 c++;
771 np = np->n_flink;
772 }
773 return(c);
774}
775
03c98b7d
CL
776cmpdomain(name, dname)
777 register char *name, *dname;
778{
779 char buf[BUFSIZ];
780
781 strcpy(buf, dname);
782 buf[strlen(name)] = '\0';
783 return(icequal(name, buf));
784}
785
65d3742f 786/*
03c98b7d
CL
787 * Delete the given name from a namelist, using the passed
788 * function to compare the names.
65d3742f 789 */
65d3742f 790struct name *
03c98b7d 791delname(np, name, cmpfun)
65d3742f
KS
792 register struct name *np;
793 char name[];
03c98b7d 794 int (* cmpfun)();
65d3742f
KS
795{
796 register struct name *p;
797
798 for (p = np; p != NIL; p = p->n_flink)
03c98b7d 799 if ((* cmpfun)(p->n_name, name)) {
65d3742f
KS
800 if (p->n_blink == NIL) {
801 if (p->n_flink != NIL)
802 p->n_flink->n_blink = NIL;
803 np = p->n_flink;
804 continue;
805 }
806 if (p->n_flink == NIL) {
807 if (p->n_blink != NIL)
808 p->n_blink->n_flink = NIL;
809 continue;
810 }
811 p->n_blink->n_flink = p->n_flink;
812 p->n_flink->n_blink = p->n_blink;
813 }
814 return(np);
815}
816
817/*
818 * Call the given routine on each element of the name
819 * list, replacing said value if need be.
820 */
821
822mapf(np, from)
823 register struct name *np;
824 char *from;
825{
826 register struct name *p;
827
828 for (p = np; p != NIL; p = p->n_flink)
829 p->n_name = netmap(p->n_name, from);
830}
831
832/*
833 * Pretty print a name list
834 * Uncomment it if you need it.
835 */
836
837prettyprint(name)
838 struct name *name;
839{
840 register struct name *np;
841
842 np = name;
843 while (np != NIL) {
844 fprintf(stderr, "%s(%d) ", np->n_name, np->n_type);
845 np = np->n_flink;
846 }
847 fprintf(stderr, "\n");
848}