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