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
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.
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]
24 * Copyright 2001 Sun Microsystems, Inc. All rights reserved.
25 * Use is subject to license terms.
26 * Copyright (c) 2016 by Delphix. All rights reserved.
29 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
30 /* All Rights Reserved */
33 * University Copyright- Copyright (c) 1982, 1986, 1988
34 * The Regents of the University of California
37 * University Acknowledgment- Portions of this document are derived from
38 * software developed by the University of California, Berkeley, and its
43 * mailx -- a modified version of a University of California at Berkeley
52 static struct name
*nalloc(char str
[]);
53 static int isfileaddr(char *name
);
54 static int lengthof(struct name
*name
);
55 static struct name
*gexpand(struct name
*nlist
, struct grouphead
*gh
,
56 int metoo
, int arg_ntype
);
57 static char *norm(register char *user
, register char *ubuf
,
59 static struct name
*put(struct name
*list
, struct name
*node
);
62 * Allocate a single element of a name list,
63 * initialize its name field to the passed
70 register struct name
*np
;
72 np
= (struct name
*)salloc(sizeof (*np
));
76 np
->n_full
= savestr(str
);
77 np
->n_name
= skin(np
->n_full
);
82 * Find the tail of a list and return it.
86 tailof(struct name
*name
)
88 register struct name
*np
;
93 while (np
->n_flink
!= NIL
)
99 * Extract a list of names from a line,
100 * and make a list of names from it.
101 * Return the list or NIL if none found.
105 extract(char line
[], int arg_ntype
)
107 short ntype
= (short)arg_ntype
;
109 register struct name
*top
, *np
, *t
;
110 char nbuf
[BUFSIZ
], abuf
[BUFSIZ
];
113 if (line
== NOSTR
|| strlen(line
) == 0)
115 comma
= docomma(line
);
119 while ((cp
= yankword(cp
, nbuf
, sizeof (nbuf
), comma
)) != NOSTR
) {
120 if (np
!= NIL
&& equal(nbuf
, "at")) {
121 nstrcpy(abuf
, sizeof (abuf
), nbuf
);
122 if ((cp
= yankword(cp
, nbuf
, sizeof (nbuf
),
124 nstrcpy(nbuf
, sizeof (nbuf
), abuf
);
127 snprintf(abuf
, sizeof (abuf
), "%s@%s", np
->n_name
,
129 np
->n_name
= savestr(abuf
);
146 * Turn a list of names into a string of the same names.
150 detract(register struct name
*np
, int ntype
)
153 register char *cp
, *top
;
154 register struct name
*p
;
159 for (p
= np
; p
!= NIL
; p
= p
->n_flink
) {
160 if ((ntype
&& (p
->n_type
& GMASK
) != ntype
) ||
163 s
+= strlen(p
->n_full
) + 2;
167 top
= (char *)salloc((unsigned)(++s
));
169 for (p
= np
; p
!= NIL
; p
= p
->n_flink
) {
170 if ((ntype
&& (p
->n_type
& GMASK
) != ntype
) ||
173 cp
= copy(p
->n_full
, cp
);
182 outpre(struct name
*to
)
184 register struct name
*np
;
186 for (np
= to
; np
; np
= np
->n_flink
)
187 if (isfileaddr(np
->n_name
))
193 * For each recipient in the passed name list with a /
194 * in the name, append the message to the end of the named file
195 * and remove them from the recipient list.
197 * Recipients whose name begins with | are piped through the given
198 * program and removed.
202 outof(struct name
*names
, FILE *fo
)
205 register struct name
*np
;
207 char *date
, *fname
, *shell
;
216 if (value("expandaddr") == NOSTR
)
219 for (np
= names
; np
!= NIL
; np
= np
->n_flink
) {
220 if (!isfileaddr(np
->n_name
) && np
->n_name
[0] != '|')
223 ispipe
= np
->n_name
[0] == '|';
225 fname
= np
->n_name
+1;
227 fname
= safeexpand(np
->n_name
);
230 * See if we have copied the complete message out yet.
235 fd
= open(tempEdit
, O_CREAT
|O_EXCL
|O_APPEND
|O_WRONLY
,
237 if ((fd
< 0) && (errno
== EEXIST
)) {
238 if ((fd
= open(tempEdit
, O_APPEND
|O_WRONLY
,
245 if ((fout
= fdopen(fd
, "a")) == NULL
) {
250 image
= open(tempEdit
, O_RDWR
);
260 fprintf(fout
, "From %s %s", myname
, date
);
261 while ((c
= getc(fo
)) != EOF
)
272 * Now either copy "image" to the desired file
273 * or give it as the standard input to the desired
274 * program as appropriate.
282 sigset(SIGHUP
, SIG_IGN
);
283 sigset(SIGINT
, SIG_IGN
);
284 sigset(SIGQUIT
, SIG_IGN
);
289 if ((shell
= value("SHELL")) == NOSTR
||
292 (void) execlp(shell
, shell
, "-c", fname
,
304 if ((fout
= fopen(fname
, "a")) == NULL
) {
309 fin
= Fdopen(image
, "r");
312 gettext("Can't reopen image\n"));
319 putc(getc(fin
), fout
);
320 while (fgets(line
, sizeof (line
), fin
)) {
321 if (strncmp(line
, "From ", 5) == 0)
326 while ((c
= getc(fin
)) != EOF
)
332 senderr
++, perror(fname
);
338 * In days of old we removed the entry from the
339 * the list; now for sake of header expansion
340 * we leave it in and mark it as deleted.
345 register struct name
*t
, *x
;
373 * Determine if the passed address is a local "send to file" address.
374 * If any of the network metacharacters precedes any slashes, it can't
375 * be a filename. We cheat with .'s to allow path names like ./...
376 * If "fcc" has been unset, then short-circuit those tests, but not
380 isfileaddr(char *name
)
383 char *fcc
= value("fcc");
391 for (cp
= name
; *cp
; cp
++) {
394 if (any(*cp
, metanet
))
403 * Map all of the aliased users in the invoker's mailrc
404 * file and insert them into the list.
405 * Changed after all these months of service to recursively
406 * expand names (2/14/80).
410 usermap(struct name
*names
)
412 register struct name
*newnames
, *np
, *cp
;
413 struct grouphead
*gh
;
418 metoo
= (value("metoo") != NOSTR
);
420 if (np
->n_name
[0] == '\\') {
422 newnames
= put(newnames
, np
);
426 gh
= findgroup(np
->n_name
);
429 newnames
= gexpand(newnames
, gh
, metoo
, np
->n_type
);
431 newnames
= put(newnames
, np
);
438 * Recursively expand a group name. We limit the expansion to some
439 * fixed level to keep things from going haywire.
440 * Direct recursion is not expanded for convenience.
444 gexpand(struct name
*nlist
, struct grouphead
*gh
, int metoo
, int arg_ntype
)
446 short ntype
= (short)arg_ntype
;
448 struct grouphead
*ngh
;
453 if (depth
> MAXEXP
) {
454 printf(gettext("Expanding alias to depth larger than %d\n"),
459 for (gp
= gh
->g_list
; gp
!= NOGE
; gp
= gp
->ge_link
) {
463 if (strcmp(cp
, gh
->g_name
) == 0)
465 if ((ngh
= findgroup(cp
)) != NOGRP
) {
466 nlist
= gexpand(nlist
, ngh
, metoo
, ntype
);
473 * At this point should allow to expand
474 * to self if only person in group
476 if (gp
== gh
->g_list
&& gp
->ge_link
== NOGE
)
478 if (!metoo
&& samebody(myname
, gp
->ge_name
, FALSE
))
481 nlist
= put(nlist
, np
);
488 * Normalize a network name for comparison purposes.
491 norm(register char *user
, register char *ubuf
, int nbangs
)
496 while (*user
++ == '!')
499 if (!strchr(user
, '!')) {
500 snprintf(ubuf
, BUFSIZ
, "%s!%s", host
, user
);
505 cp
= user
+ strlen(user
);
507 while (cp
> user
&& *--cp
!= '!')
509 user
= (cp
> user
) ? ++cp
: cp
;
511 * Now strip off all Internet-type
514 if ((cp
= strchr(user
, '%')) == NOSTR
)
515 cp
= strchr(user
, '@');
518 strncpy(ubuf
, user
, cp
- user
);
519 ubuf
[cp
- user
] = '\0';
529 * Implement allnet options.
532 samebody(register char *user
, register char *addr
, int fuzzy
)
534 char ubuf
[BUFSIZ
], abuf
[BUFSIZ
];
535 char *allnet
= value("allnet");
536 int nbangs
= allnet
? (strcmp(allnet
, "uucp") == 0) ? 2 : 1 : 0;
538 if (fuzzy
&& value("fuzzymatch")) {
541 (void) strlcpy(ubuf
, user
, BUFSIZ
);
542 for (i
= 0; ubuf
[i
]; i
++)
543 ubuf
[i
] = tolower(ubuf
[i
]);
544 (void) strlcpy(abuf
, addr
, BUFSIZ
);
545 for (i
= 0; abuf
[i
]; i
++)
546 abuf
[i
] = tolower(abuf
[i
]);
547 return (strstr(abuf
, ubuf
) != NOSTR
);
549 user
= norm(user
, ubuf
, nbangs
);
550 addr
= norm(addr
, abuf
, nbangs
);
551 return (strcmp(user
, addr
) == 0);
555 * Compute the length of the passed name list and
559 lengthof(struct name
*name
)
561 register struct name
*np
;
564 for (c
= 0, np
= name
; np
!= NIL
; c
++, np
= np
->n_flink
)
570 * Concatenate the two passed name lists, return the result.
574 cat(struct name
*n1
, struct name
*n2
)
576 register struct name
*tail
;
589 * Unpack the name list onto a vector of strings.
590 * Return an error if the name list won't fit.
594 unpack(struct name
*np
)
596 register char **ap
, **top
;
597 register struct name
*n
;
599 int t
, extra
, metoo
, verbose
;
602 if ((t
= lengthof(n
)) == 0)
603 panic("No names to unpack");
606 * Compute the number of extra arguments we will need. We need at least
607 * 3 extra -- one for "mail", one for a terminating -- to stop sendmail
608 * option processing, and one for the terminating 0 pointer.
610 * Additional spots may be needed to pass along -r and -f to the host
619 metoo
= value("metoo") != NOSTR
;
622 verbose
= value("verbose") != NOSTR
;
627 top
= (char **)salloc((t
+ extra
) * sizeof (char *));
630 if (rflag
!= NOSTR
) {
641 snprintf(hbuf
, sizeof (hbuf
), "%d", hflag
);
642 *ap
++ = savestr(hbuf
);
646 if (n
->n_type
& GDEL
) {
658 * See if the user named themself as a destination
659 * for outgoing mail. If so, set the global flag
660 * selfsent so that we avoid removing their mailbox.
664 mechk(struct name
*names
)
666 register struct name
*np
;
668 for (np
= names
; np
!= NIL
; np
= np
->n_flink
)
669 if ((np
->n_type
& GDEL
) == 0 &&
670 samebody(np
->n_name
, myname
, FALSE
)) {
677 * Remove all of the duplicates from the passed name list by
678 * insertion sorting them, then checking for dups.
679 * Return the head of the new list.
683 elide(struct name
*names
)
685 register struct name
*np
, *t
, *newnames
;
695 newnames
->n_flink
= NIL
;
698 while (strcmp(t
->n_name
, np
->n_name
) < 0) {
699 if (t
->n_flink
== NIL
)
705 * If we ran out of t's, put the new entry after
706 * the current value of t.
709 if (strcmp(t
->n_name
, np
->n_name
) < 0) {
719 * Otherwise, put the new entry in front of the
720 * current t. If at the front of the list,
721 * the new guy becomes the new head of the list.
727 t
->n_flink
= newnames
;
728 newnames
->n_blink
= t
;
735 * The normal case -- we are inserting into the
736 * middle of the list.
742 x
->n_blink
= t
->n_blink
;
743 t
->n_blink
->n_flink
= x
;
748 * Now the list headed up by new is sorted.
749 * Go through it and remove duplicates.
750 * Remember the best "type" among all the
751 * duplicates of a name.
760 while (t
->n_flink
!= NIL
&&
761 strcmp(np
->n_name
, t
->n_flink
->n_name
) == 0) {
763 /* "To" before "Cc" before "Bcc" */
764 if (t
->n_type
< type
)
767 if (t
== np
|| t
== NIL
) {
773 * Now t points to the last entry with the same name
774 * as np. Make np point beyond t.
777 np
->n_flink
= t
->n_flink
;
778 if (t
->n_flink
!= NIL
)
779 t
->n_flink
->n_blink
= np
;
787 * Put another node onto a list of names and return
792 put(struct name
*list
, struct name
*node
)
794 node
->n_flink
= list
;
797 list
->n_blink
= node
;
803 * Delete the given name from a namelist.
806 delname(register struct name
*np
, char name
[])
808 register struct name
*p
;
810 for (p
= np
; p
!= NIL
; p
= p
->n_flink
)
811 if (samebody(name
, p
->n_name
, FALSE
)) {
812 if (p
->n_blink
== NIL
) {
813 if (p
->n_flink
!= NIL
)
814 p
->n_flink
->n_blink
= NIL
;
818 if (p
->n_flink
== NIL
) {
819 if (p
->n_blink
!= NIL
)
820 p
->n_blink
->n_flink
= NIL
;
823 p
->n_blink
->n_flink
= p
->n_flink
;
824 p
->n_flink
->n_blink
= p
->n_blink
;
830 * Call the given routine on each element of the name
831 * list, replacing said value if need be.
835 mapf(register struct name
*np
, char *from
)
837 register struct name
*p
;
839 if (debug
) fprintf(stderr
, "mapf %lx, %s\n", (long)np
, from
);
840 for (p
= np
; p
!= NIL
; p
= p
->n_flink
)
841 if ((p
->n_type
& GDEL
) == 0) {
842 p
->n_name
= netmap(p
->n_name
, from
);
843 p
->n_full
= splice(p
->n_name
, p
->n_full
);
845 if (debug
) fprintf(stderr
, "mapf %s done\n", from
);