smfst2@unix.cis.pitt.edu (Seth M Fuller) (06/11/91)
Below is a shell archive of a global mail script I adapted from
Larry Wall's gsh. I haven't seen anything like it and I thought it
might be useful to others.
Seth M. Fuller
#!/bin/sh
# to extract, remove the header and type "sh filename"
if `test ! -s ./README.gmail`
then
echo "writing ./README.gmail"
cat > ./README.gmail << '\End\Of\Shar\'
Gmail --- Mail Globally
This is gmail. Gmail is a perl script that I adapted from
Larry Wall's gsh. It allows you to mail globally to users via an alias
file called gusers. I realize there are other ways of doing this with
many mail programs, but I wanted a neat, simple solution to use at our
offices that wouldn't require a lot of maintenance. Gmail lets you
compose your mail message in your favorite editor (or stdin), then
mails it to the list of users (or aliases) you specify on the command
line.
Gmail uses an alias file much like ghosts. The main difference
being that the first field is a host name and the second field is a
user name. All other fields are aliases.
Gmail is also smart enough to know whether your mail program
is BSD mail or ATT mail and from that whether or not to prompt for a
subject. Gmail tests to see if the file /usr/lib/sendmail exists, if
it does it assumes this is a BSD mail program, if not an ATT mail
program. If anybody knows of a better way of doing this let me know.
If anybody has any comments I can be reached at:
Seth M. Fuller
Giant Eagle, Inc.
MIS Dept.
101 Kappa Drive
Pittsburgh, PA 15238
(412)963-6200
e-mail: unix.cis.pitt.edu!smfst2
\End\Of\Shar\
else
echo "will not over write ./README.gmail"
fi
if `test ! -s ./gmail`
then
echo "writing ./gmail"
cat > ./gmail << '\End\Of\Shar\'
#! /acct/bin/perl
# gmail --- Mail globally via the gusers file
# Author: Seth M. Fuller
# I adapted this program from gsh, written by Larry Wall.
# This program accesses a file called gusers, similar to ghosts, for its
# aliases. The main difference is that the first two fields are the host
# name for the user and the user's login name. The remaining fields are
# any mailing groups that the user belongs to.
# Mail globally
$SIG{'QUIT'} = 'quit'; # install signal handler for SIGQUIT
sub getswitches {
while ($ARGV[0] =~ /^-/) { # parse switches
$ARGV[0] =~ /^-b/ && ($bsdmail++,shift(@ARGV),next);
$ARGV[0] =~ /^-s/ && ($silent++,shift(@ARGV),next);
$ARGV[0] =~ /^-d/ && ($from_stdin++,shift(@ARGV),next);
$ARGV[0] =~ /^-e/ && ($got_editor++,$editor=$ARGV[1],
shift(@ARGV),shift(@ARGV),next);
last;
}
}
do getswitches(); # get any switches before class
$systype = shift; # get name representing set of users
$cmd = join(' ',@ARGV); # remaining args constitute the command
$cmd =~ s/'/'"'"'/g; # quote any embedded single quotes
$one_of_these = ":$systype:"; # prepare to expand "macros"
$one_of_these =~ s/\+/:/g; # we hope to end up with list of
$one_of_these =~ s/-/:-/g; # colon separated attributes
if (! $got_editor)
{
$editor = $ENV{'GMEDIT'}; # Get or set editor
if (! $editor)
{
$editor = $ENV{'EDITOR'}; # Get or set editor
if (! $editor)
{
$editor = "vi";
}
}
}
else
{
$from_stdin = 0;
}
$remainder = '';
$bsdmail++ if -f "/usr/lib/sendmail";
if ($bsdmail)
{
$|=1; # Turn off buffering for reading from keyboard
print "Subject: ";
@subject = &readline();
$subject = join('', @subject);
}
if ($from_stdin) { # Get mail from stdin
print "Enter body of mail message then Ctrl-D (on a line by itself)\n";
`cat >/tmp/gmail$$`; # get input into a handy place
}
else
{
system("$editor /tmp/gmail$$");
if ( -f "/tmp/gmail$$" == 0)
{
unlink "/tmp/gmail$$";
exit 0;
}
}
@ARGV = ();
push(@ARGV,'.gurem') if -f '.gurem';
push(@ARGV,'.gusers') if -f '.gusers';
push(@ARGV,'/etc/gusers') if -f '/etc/gusers';
push(@ARGV,'gusers') if -f 'gusers';
line: while (<>) { # for each line of gusers
s/[ \t]*\n//; # trim trailing whitespace
if (!$_ || /^#/) { # skip blank line or comment
next line;
}
if (/^(\w+)=(.+)/) { # a macro line?
$name = $1; $repl = $2;
$repl =~ s/\+/:/g;
$repl =~ s/-/:-/g;
$one_of_these =~ s/:$name:/:$repl:/; # do expansion in "wanted" list
$repl =~ s/:/:-/g;
$one_of_these =~ s/:-$name:/:-$repl:/;
next line;
}
# we have a normal line
@attr = split(' '); # a list of attributes to match against
# which we put into an array
$host = $attr[0]; # the first attribute is the host name
if ($host eq "local")
{
$host = "";
}
else
{
$host = $host . "!";
}
$user = $attr[1]; # the second attribute is the user name
$wanted = 0;
foreach $attr (@attr) { # iterate over attribute array
$wanted++ if index($one_of_these,":$attr:") >= 0;
$wanted = -9999 if index($one_of_these,":-$attr:") >= 0;
}
if ($wanted > 0) {
if ($bsdmail)
{
print "mail -s \"$subject\" $host$user\n" unless $silent;
}
else
{
print "mail $host$user\n" unless $silent;
}
$SIG{'INT'} = 'DEFAULT';
if ($bsdmail)
{
if (open(PIPE, # mail the msg.
"mail -s \"$subject\" $host$user < /tmp/gmail$$ 2>&1|")) {
$SIG{'INT'} = 'cont';
close(PIPE);
}
else {
print "(Can't execute mail: $!)\n";
$SIG{'INT'} = 'cont';
}
}
else
{
if (open(PIPE,
"mail $host$user < /tmp/gmail$$ 2>&1|")) { # mail the msg.
$SIG{'INT'} = 'cont';
close(PIPE);
}
else {
print "(Can't execute mail: $!)\n";
$SIG{'INT'} = 'cont';
}
}
}
}
unlink "/tmp/gmail$$";
if ($remainder) {
chop($remainder);
open(gurem,">.gurem") || (printf stderr "Can't make a .gurem file: $!\n");
print gurem 'urem=', $remainder, "\n";
close(gurem);
print 'urem=', $remainder, "\n";
}
# here are a couple of subroutines that serve as signal handlers
sub cont {
print "\rContinuing...\n";
$remainder .= "$host+";
}
sub quit {
$| = 1;
print "\r";
$SIG{'INT'} = '';
kill 2, $$;
}
# This subroutine reads a line from the standard input and returns an
# array with all of characters typed by the user up to the newline.
sub readline
{
$more_line = 1;
while ($more_line)
{
$ch = getc;
if ($ch lt ' ')
{
$more_line = 0;
}
else
{
push(@line, $ch);
}
}
return @line;
}
\End\Of\Shar\
else
echo "will not over write ./gmail"
fi
if `test ! -s ./gmail.man`
then
echo "writing ./gmail.man"
cat > ./gmail.man << '\End\Of\Shar\'
.TH GMAIL L "10 Jun 1991"
.SH NAME
gmail \- globally mail to aliases
.SH SYNOPSIS
.B gmail
[options]
.I user-alias
.SH DESCRIPTION
.I Gmail
works similarly to mail(1) except that you may specify a set of users mail to.
The host/user sets are defined in the file /etc/gusers. Each line of gusers
file is of the form of:
host user [alias] [alias] [alias] ...
If the host is the word "local" no host is specified in the mail command and
the message goes to the user on the local system.
(An individual user name can be used as a set containing one member.)
You can give a command like
gmail mis
to mail to all people in the MIS Department.
.P
Gmail starts up an editor to allow you to type in your message. Once you
save the message and exit the editor, gmail starts sending it out to all
users in the list. Gmail will first check to see if the environment variable
GMEDIT is set. If set gmail will use the value of GMEDIT as the editor. If
there is no value for GMEDIT, gmail will check to see if the environment
variable EDITOR is set and will use that editor, otherwise, gmail defaults to
vi as the editor. The -e option allows you to specify the editor to use on the
command line (full path not needed). The -e option overrides the envronment
variables.
You may instead use the -d option to accept the mail message from standard
input until end of file.
.P
You may specify the union of two or more sets by using + as follows:
gmail pos+store
which will mail to all members of the POS and store groups.
.P
Commonly used sets should be defined in /etc/gusers.
For example, you could add a line that says
mis=admin+pos+store
Another way to do that would be to add the word "mis" after each of the
host/user entries:
calvin chas admin mis
.br
mickey seth retail store mis
.br
pluto johnc retail pos
Users and sets of user can also be excluded:
retail=mis-admin
Any user so excluded will never be included, even if a subsequent set on the
line includes it:
foo=abc+def
bar=xyz-abc+foo
comes out to xyz+def.
You can define private user sets by creating .gusers in your current directory
with entries just like /etc/gusers.
Also, if there is a file .gurem, it defines "urem" to be the remaining users
from the last gmail that didn't succeed everywhere.
Options are
.IP "\-b" 8
Requests a subject for BSD style mail programs. Gmail will try to determine
if your mail program is BSD or ATT and request or not request a subject
accordingly (in other words you probably won't need to use this option if
gmail can figure out whether or not you use BSD mail by whether or not your
system has /usr/lib/sendmail).
.IP "\-d" 8
Causes gmail to collect input till end of file, and then distribute that input
to each invokation of mail.
.IP "\-e" 8
Specify the editor to use for the mail message. Incompatible with the -d option.
.IP "\-s" 8
Do work silently.
.PP
Interrupting with a SIGINT will cause the mail to the current user to be
skipped and execution resumed with the next user.
To stop completely, send a SIGQUIT.
.SH RESTRICTIONS
Gmail only sends mail. It is not for receiving mail.
No provision is made carbon copying, but that should not be needed with
well defined sets of users.
.SH SEE ALSO
mail(1)
\End\Of\Shar\
else
echo "will not over write ./gmail.man"
fi
if `test ! -s ./gusers`
then
echo "writing ./gusers"
cat > ./gusers << '\End\Of\Shar\'
# This first section gives alternate sets defined in terms of the sets given
# by the second section. The order is important--all references must be
# forward references.
mis=store+pos+admin
# This second section defines the basic sets. Each host/user combination
# should have a line that specifies which sets it is a member of.
# Extra sets should be separated by white space. (The first section isn't
# strictly necessary, since all sets could be defined in the second section,
# but then it wouldn't be so readable.)
# Each line must be of the form of:
# host user [alias] [alias] [alias] ...
local seth retail store
calvin chas admin
pluto Jon retail pos
pluto anne retail store
pluto floyd retail store
cricket fredb retail store
pluto joe retail
pluto johnc retail pos
pluto maryann retail pos
\End\Of\Shar\
else
echo "will not over write ./gusers"
fi
echo "Finished archive 1 of 1"
exit