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