#!/usr/local/bin/perl
#

$valid_users = "kroot,root,daemon";
$valid_group = "misnsm";
$localdocs = "http://infobahn/bsd/depts/ia/docs/messaging/mjdadmin.htm";

require "getopts.pl";

# WARNING: when called from wrapper, a quoted argument is not
# 	preserved: -d 'a test list' becomes '-d' 'a' 'test' 'list'.
#	Therefore, the -description argument is pretty useless
#	for wrapper purposes, but perfectly fine if exec'd via
#	MajorCool (which is already wrapped).
#
die("Usage: $0 -d description -o owner -p passwd list...")
	unless &Getopts("C:d:o:p:");
&squawk("no list specified") unless @ARGV;

$cf = $ENV{"MAJORDOMO_CF"} || "/etc/majordomo.cf"; 
$cf = $opt_C if $opt_C;

$passwd = "$list.admin";
$passwd = $opt_p if $opt_p;

# Read and execute the .cf file
die("$cf not readable; stopped") if ! -r $cf;
die("require of majordomo.cf failed $@") unless require "$cf";

# Go to the home directory specified by the .cf file
chdir("$homedir");

# All these should be in the standard PERL library
unshift(@INC, $homedir);
require "ctime.pl";		# To get MoY definitions for month abbrevs
require "majordomo_version.pl";	# What version of Majordomo is this?
require "majordomo.pl";		# all sorts of general-purpose Majordomo subs
require "shlock.pl";		# NNTP-style file locking
require "config_parse.pl";	# functions to parse the config files

# Here's where the fun begins...

# check to see if the cf file is valid
die("listdir not defined. Is majordomo.cf being included correctly?")
	if !defined($listdir);

# where do we look for files, by default?
$filedir = $listdir if !defined($filedir);
$filedir_suffix = ".archive" if !defined($filedir_suffix);

#----------------------------------------------------------------------------

# 1.94 has this set in $CF already
$sendmail_command = "/usr/lib/sendmail" unless $sendmail_command;

# don't allow just anyone to run this command...
print "Validating permissions...\n";
&squawk("access denied") unless &is_user($valid_users,$valid_group);

# unbuffered stdout/stderr
select((select(STDOUT), $|=1)[$[]);
select((select(STDERR), $|=1)[$[]);

foreach $list (@ARGV) {
	print "Verifying list name...\n";
	&squawk("list '$list' must be lowercase only") if $list =~ /[A-Z]+/;
	&squawk("invalid characters in list '$list'") unless $list =~ /^[-\w]+$/;
	&squawk("list '$list' already exists") if -f "$listdir/$list";
	print "Checking for existing aliases...\n";
	&squawk("address '$list' already exists")
		unless system("$sendmail_command -bv $list >/dev/null");
	#
	# NOTE: depending on sendmail version, the above might
	# need to be changed to an "if".

	# create empty list; make config file
	#
	print "Adding list '$list'...\n";
	open(LIST, ">$listdir/$list"); close(LIST);
	print "Creating default config file...\n";
	&get_config($listdir, $list) if !&cf_ck_bool($list, '', 1);

	# tweak config based on command line settings
	#
	if ($opt_d || $opt_o || $opt_p) {
		print "Setting specified config options...\n";
		open(OLDCF, "<$listdir/$list.config");
		open(NEWCF, ">$listdir/$list.config.new");
		while (<OLDCF>) {
			s/^description\s+=.*$/description = $opt_d/g if $opt_d;
			s/^owner\s+=.*$/owner = $opt_o/g if $opt_o;
			s/^admin_passwd\s+=.*$/admin_passwd = $opt_p/g if $opt_p;
			print NEWCF $_;
		}
		close(OLDCF); close(NEWCF);
		rename("$listdir/$list.config.new", "$listdir/$list.config");
	}

	# send instructions
	#
	if ($opt_o) {
		print "Sending instructions to '$opt_o'...\n";
		&send_docs;
	}

	# link to MASTER.PASSWD if it exists
	link("$listdir/MASTER.PASSWD", "$listdir/$list.passwd")
		if (-f "$listdir/MASTER.PASSWD");

	if (-x "$homedir/mj_build_aliases") {
		print "Starting alias rebuild...\n";
		system("nohup $homedir/mj_build_aliases @ >/dev/null &");
	}
	print "Done.\n";
}

sub squawk {
	local(@argv) = split('/',$0);
	printf STDERR "%s: @_\n", $argv[$#argv];
	exit(1);
}

### IS_USER(users, group)
#   boolean: match current user against other users/group
#   <users> can be single token or colon-separated list
#   <group> can be name or gid
#
sub is_user {
	local($users, $group) = @_[0,1];
	local($valid);
	$current_user = getlogin || (getpwuid($<))[0] || die "Unknown user";
	#print "$current_user: ";
	if ($users) {
		foreach (split(',', $users)) {
			#print "$_...";
			if ($current_user eq $_) {
				$valid = 1; last;
			}
		}
	}
	if ($group && ! $valid) {
		#print "$group()...";
		foreach (&group_members($group)) {
			#print "$_...";
			if ($_ eq $current_user) {
				$valid = 1; last;
			}
		}
	}
	return $valid;
}

### GROUP_MEMBERS(group)
#   list: return members of Unix group
#		checks both primary (/etc/passwd) and supplementary (/etc/group)
#   <group> can be name or gid
#
sub group_members {
	local($target) = @_;
	return unless $target;
	local(@users);
	if ($target =~ /[^\d]*/) {
		# supplementary group, by name
		local($name,$passwd,$gid,$members) = getgrnam($target);
		$target = $gid;
		@users = split(' ',$members);
	}
	else {
		# supplementary group, by gid
		local($name,$passwd,$gid,$members) = getgrgid($target);
		@users = split(' ',$members);
	}
	# primary group, by gid
	while (($user,$pass,$uid,$gid) = getpwent) {
		push(@users, $user) if ($gid == $target);
	}
	return @users;
}

sub send_docs {
	&set_mailer($bounce_mailer ? $bounce_mailer : $mailer);
	&set_mail_sender($whoami_owner);
	&set_mail_from($whoami_owner);
	&sendmail(MAIL, $opt_o, "$list: Majordomo list created");
	print MAIL <<"EOF";
A Majordomo list called "$list" has been created in your name. If
this new list is unexpected or otherwise incorrect, please contact:
	$whoami_owner
as soon as possible.

Majordomo address: $whoami
Majordomo owner: $whoami_owner
List name: $list
List posting address: $list@$whereami
List request address: $list-request@$whereami
List password: $passwd

Your mailing list has been established.  It is being served by an
automated mailing list manager called 'Majordomo'.  This message has
all the details of how to manage your list remotely using Majordomo.
There's a lot of info here, so please read this completely and
carefully, and save it for future reference.

Your list-owner password is shown above.  Keep track of this; you'll
need it later.  Instructions for changing your password are below.

As soon as possible, please establish an "info" file for your
list (see below) to create the file that someone will receive when
they join or ask about your list.

See $localdocs for
configuration instructions and other details. List configuration may be
done via mail to Majordomo, or through the MajorCool Web interface at:
	$whereami_url

If you have any questions about all of this, send them to the 
Majordomo-Owner address shown at the top of this file.

================
The Gory Details
================

Your mailing list is managed by an automated mailing list management
program called Majordomo. Majordomo should free you from dealing with
most of the administrivia usually associated with running mailing lists
(adding users, dropping users, etc.).

To submit something to your list, you (or anybody else) should simply
mail it to the list posting address shown at the top of this file.

If somebody sends something to list request address shown above (which
is the traditional Internet way of being added to a mailing list),
they'll get back a message telling them how to use Majordomo for
routine requests, and how to contact you if they need to speak to a
human being.

To be added to your list, a user simply sends a message of the form

    subscribe your-list-name

by email to either the Majordomo address or the list request address
shown above.

Majordomo understands several commands, and is not limited to a single
command per message (it will process commands until reaching
end-of-message or the command "end").  The command "help" will tell you
about all the other commands.

Actually, it won't tell you about _all_ the other commands that
Majordomo understands.  There are several commands there for use by
list owners such as yourself, which are not advertised to the public.
All of these commands are password-protected on a list-by-list basis,
but anyone with a valid list/password combination can invoke these
commands.  This is not exactly high-tech security, but it's more
intended to keep annoyance to a minimum than to be foolproof.

The "documented" commands which Majordomo understands and which are
for everyone to use are:

    subscribe <list> [<address>]
    unsubscribe <list> [<address>]
    which [<address>]
    who <list>
    info <list>
    index <list>
    get <list>
    lists
    help
    end

You can get detailed explanations of all of these by asking for "help"
from Majordomo.

The "undocumented" commands for use by list owners are:

    approve <passwd> {subscribe|unsubscribe} <list> [<address>]
	This is so that you can approve subscription or unsubscription
	actions that need approval by the list owner.  Note that this
	is just a standard "subscribe" or "unsubscribe" command prefixed
	with "approve <password>" (where you substitute the password for
	your list, which is listed above, for "<password>").

    newinfo <list> <password>
	This is so that you can replace the information file that people
	get when they do "info <list>" or "subscribe <list>".  It reads
	everything after the "newinfo" command to end-of-message or the
	word "EOF" on a line by itself as the new info for the list.

    config <list> <password>
	Retrieves a self-documenting configuration file for the list <list>.
	The <password> is the password contained in the admin_passwd 
	setting of the configuration file.

    newconfig <list> <password>
   	Validates and installs a new configuration file.  It reads
	everything after the "newconfig" command to end-of-message or
	the word "EOF" on a line by itself as the new info for the
	list.  The config file is expected to be a complete config
	file as returned by "config".  Incremental changing of the
	config file is not yet supported.  As soon as the config file
	is validated and installed its settings are available for
	use. This is useful to remember if you have multiple commands
	in your mail message since they will be subject to the
	settings of the new config file.  If there is an error in the
	config file (incorrect value...), the config file will not be
	accepted and the error message identifying the problem line(s)
	will be returned to the sender.  Note that only the error
	messages are returned to the sender not the entire config
	file, so it would be a good idea to keep a copy of your
	outgoing email message.

    writeconfig <list> <password> 
	Write a new config file in standard form. Writeconfig forces a
	rewrite of the config file with all comments and default
	values in place. It is useful to use after an upgrade of
	Majordomo since it will add the new keywords for people to
	change. It also updates the documentation in the file if that
	has changed.

	   
Configuring Your List
=====================

You should retrieve the configuration file for your list. To do this,
send an email message to the Majordomo address listed at the top of
this form. The contents of this message should be:

	config <List name> <List password>

Where <List name> <List password> are given at the top of this mail. You
will receive a config file that can be used to change the operation of
your list.

The configuration file is meant to be self documenting. Once you have
completed all of the changes to the config file, You should use the
newconfig command (described above) to put a new configuration file in
place.


Approval
========

When Majordomo requests your approval for something, it sends you a
message that includes a template of the approval message; if you
concur, you simply need to replace "PASSWORD" in the template with your
list password, and send the template line back to Majordomo.

The requests for approval that Majordomo generates all start with
"APPROVE" in the "Subject:" line.  

You aren't limited to approving only things to Majordomo requests
approval for.  You can approve any "subscribe" or "unsubscribe"
request, regardless of whether Majordomo has requested this approval,
with an "approve" command.  Thus, you can subscribe or unsubscribe
people from your list without them having to send anything to
Majordomo; just send an appropriate "approve PASSWORD subscribe LIST
ADDRESS" or "approve PASSWORD unsubscribe LIST ADDRESS" command off to
Majordomo.


Bounced Messages
================

Majordomo may bounce certain messages that people attempt to post to
your mailing list.  These messages may be bounced because they appear
to be administrative requests (i.e., someone mailed a request to
subscribe or unsubscribe to the posting address rather than to
Majordomo or to the -request address), because they are too long, or
for any of a number of other reasons.

Majordomo will forward these messages to you in another message whose
subject line begins with the word "BOUNCE"; the subject line will also
indicate the name of the list the message was bounced from (in case you
manage more than one list) and the reason the message was bounced.

If you decide that the message is OK and should not have been bounced,
then you can cause Majordomo to post it anyway by sending the message
back to the posting address (NOT to the Majordomo address) with a
special "Approved: password" header.  To do this, follow these
instructions:

	1)  Save the original message (the body of the message you received
	from Majordomo) in a file.

	2)  Edit the file to insert a line that says "Approved: password" (where
	"password" is the password for your list) at the top, before the
	headers and body of the original message.

	3)  Send this edited file back to the posting address for your list (NOT
	to Majordomo).

This time around, Majordomo will notice the "Approved:" line and check
it against your list password.  If it matches, Majordomo will strip off
the header of your message and the "Approved:" line (leaving just the
original message), and send the original message on through.


Moderation
==========

If your list is moderated, (the 'moderate' parameter in the config file
is 'yes') then messages without an "Approved:" line are bounced, just
as described above.  To cause them to be posted to the list, you add a
valid "Approved:" line and send them back, just as described above.


MajorCool
=========

MajorCool is an alternative to using mail-based commands sent to the
Majordomo list manager address. MajorCool provides a Web interface
for many (but not all) of the most common config file settings. In 
addition, you can manage user subscriptions or edit the "info" file
using this interface.  The MajorCool Web interface is available as:
	$whereami_url

EOF
	close(MAIL);
}