#!/bin/perl
# $Modified: Thu Jan 13 18:29:15 2000 by cwilson $
# majordomo: a person who speaks, makes arrangements, or takes charge
# for another.
#
# Copyright 1992, D. Brent Chapman. See the Majordomo license agreement for
# usage rights.
#
# $Source: /sources/cvsrepos/majordomo/majordomo,v $
# $Revision: 1.95 $
# $Date: 2000/01/13 17:29:31 $
# $Author: cwilson $
# $State: Exp $
#
# $Locker: $
# set our path explicitly
# PATH it is set in the wrapper, so there is no need to set it here.
# until we run suid...
#$ENV{'PATH'} = "/bin:/usr/bin:/usr/ucb";
# Before doing anything else tell the world I am majordomo
# The mj_ prefix is reserved for tools that are part of majordomo proper.
$main'program_name = 'mj_majordomo';#';
# Read and execute the .cf file
$cf = $ENV{"MAJORDOMO_CF"} || "/etc/majordomo.cf";
while ($ARGV[0]) { # parse for config file or default list
if ($ARGV[0] =~ /^-C$/i) { # sendmail v8 clobbers case
$cf = $ARGV[1];
shift(@ARGV);
shift(@ARGV);
} elsif ($ARGV[0] eq "-l") {
$deflist = $ARGV[1];
shift(@ARGV);
shift(@ARGV);
} else {
die "Unknown argument $ARGV[0]\n";
}
}
if (! -r $cf) {
die("$cf not readable; stopped");
}
require "$cf";
# Go to the home directory specified by the .cf file
chdir("$homedir") || die "chdir to $homedir failed, $!\n";
# If standard error is not attached to a terminal, redirect it to a file.
if (! -t STDERR) {
close STDERR;
open (STDERR, ">>$TMPDIR/majordomo.debug");
}
print STDERR "$0: starting\n" if $DEBUG;
# 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
print STDERR "$0: requires succeeded. Setting defaults.\n" if $DEBUG;
# 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);
# Define all of the mailer properties:
# It is possible that one or both of $sendmail_command and $bounce_mailer
# are not defined, so we provide reasonable defaults.
$sendmail_command = "/usr/lib/sendmail"
unless defined $sendmail_command;
$bounce_mailer = "$sendmail_command -f\$sender -t"
unless defined $bounce_mailer;
&set_abort_addr($whoami_owner);
&set_mail_from($whoami);
&set_mail_sender($whoami_owner);
&set_mailer($bounce_mailer);
$majordomo_dont_reply = $majordomo_dont_reply
|| '(mailer-daemon|uucp|listserv|majordomo)\@';
# where do we look for files, by default?
if (!defined($filedir)) {
$filedir = $listdir;
}
if (!defined($filedir_suffix)) {
$filedir_suffix = ".archive";
}
# what command do we use to generate an index, by default?
if (!defined($index_command)) {
$index_command = "/bin/ls -lRL";
}
# where are we for FTP, by default? (note: only set this if $ftpmail is set)
if (defined($ftpmail_address)) {
if (!defined($ftpmail_location)) {
$ftpmail_location = $whereami;
}
}
print STDERR "$0: done with defaults, parsing mail header.\n" if $DEBUG;
# Parse the mail header of the message, so we can figure out who to reply to
&ParseMailHeader(STDIN, *hdrs);
# Now we try to figure out who to send the replies to.
# $reply_to also becomes the default target for subscribe/unsubscribe
$reply_to = &RetMailAddr(*hdrs);
print STDERR "$0: setting log file.\n" if $DEBUG;
# Set up the log file
&set_log($log, $whereami, "majordomo", $reply_to);
# if somebody has set $reply_to to be our own input address, there's a problem.
if (&addr_match($reply_to, $whoami)) {
&abort( "$whoami punting to avoid mail loop.\n");
exit 0;
}
if (! &valid_addr($reply_to)) {
&abort( "$whoami: $reply_to is not a valid return address.\n");
exit 2;
}
# robots should not reply to other robots...
if ($reply_to =~ m/$majordomo_dont_reply/i) {
&abort( "$whoami: not replying to $1 to avoid mail loop.\n");
exit 0;
}
if ($return_subject && defined $hdrs{'subject'}) {
$sub_addin = ": " . substr($hdrs{'subject'}, 0, 40);
} else {
$sub_addin = '';
}
print STDERR "$0: some quick sanity checks on permissions.\n" if $DEBUG;
# do some sanity checking on permissions
# This bails out via abort if needed.
#
&check_permissions;
print STDERR "$0: opening sendmail process.\n" if $DEBUG;
# Open the sendmail process to send the results back to the requestor
&sendmail(REPLY, $reply_to, "Majordomo results$sub_addin");
select((select(REPLY), $| = 1)[0]);
print STDERR "$0: processing commands in message body.\n" if $DEBUG;
# Process the rest of the message as commands
while (<>) {
$approved = 0; # all requests start as un-approved
$quietnonmember = 0; # show non-member on unsubscribe
while ( /\\\s*$/ ) { # if the last non-whitespace
&chop_nl($_); # character is '\', chop the nl
s/\\\s*$/ /; # replace \ with space char
$_ .= scalar(<>); # append the next line
}
print REPLY ">>>> $_"; # echo the line we are processing
$_ = &chop_nl($_); # strip any trailing newline
s/^\s*#.*//; # strip comments
s/^\s+//; # strip leading whitespace
s/\s+$//; # strip trailing whitespace
s/\\ /\001/g; # protected escaped whitepace
if (/^begin\s+\d+\s+\S+$/) { # bail on MSMail uuencode attachments
print REPLY "ATTACHMENT DETECTED; COMMAND PROCESSING TERMINATED.\n";
last;
}
@parts = split(" "); # split into component parts
grep(s/\001/ /, @parts); # replace protected whitespace with
# whitespace
$cmd = shift(@parts); # isolate the command
$cmd =~ tr/A-Z/a-z/; # downcase the command
if ($cmd eq "") { next; } # skip blank lines
# figure out what to do and do it
# the "do_*" routines implement specific Majordomo commands.
# they are all passed the same arguments: @parts.
$count++; # assume it's a valid command, so count it.
if ($cmd eq "end") { print REPLY "END OF COMMANDS\n"; last; }
elsif ($cmd =~ /^-/ &&
(!defined($hdrs{'content-type'}) ||
$hdrs{'content-type'} !~ /multipart/i))
{
# treat lines beginning with "-" as END only if this is NOT a MIME
# multipart msg. MIME messages should have "Content-Type:"
# headers, and multipart messages should have the string
# "multipart" somewhere in that header. If we just look for
# Content-Type: we trap messages with Content-Type: text/plain,
# which is pretty common these days.
print REPLY "END OF COMMANDS\n";
last;
}
elsif ($cmd eq "subscribe") { &do_subscribe(@parts); }
elsif ($cmd eq "unsubscribe") { &do_unsubscribe(@parts); }
elsif ($cmd eq "signoff") { &do_unsubscribe(@parts); }
elsif ($cmd eq "cancel") { &do_unsubscribe(@parts); }
elsif ($cmd eq "approve") { &do_approve(@parts); }
elsif ($cmd eq "passwd") { &do_passwd(@parts); }
elsif ($cmd eq "which") { &do_which(@parts); }
elsif ($cmd eq "who") { &do_who(@parts); }
elsif ($cmd eq "info") { &do_info(@parts); }
elsif ($cmd eq "newinfo") { &do_newinfo(@parts); }
elsif ($cmd eq "intro") { &do_intro(@parts); }
elsif ($cmd eq "newintro") { &do_newintro(@parts); }
elsif ($cmd eq "config") { &do_config(@parts); }
elsif ($cmd eq "newconfig") { &do_newconfig(@parts); }
elsif ($cmd eq "writeconfig") { &do_writeconfig(@parts); }
elsif ($cmd eq "mkdigest") { &do_mkdigest(@parts); }
elsif ($cmd eq "lists") { &do_lists(@parts); }
elsif ($cmd eq "help") { &do_help(@parts); }
elsif ($cmd eq "get") { &do_get(@parts); }
elsif ($cmd eq "index") { &do_index(@parts); }
elsif ($cmd eq "auth") { &do_auth(@parts); }
else {
&squawk("Command '$cmd' not recognized.");
$count--; # if we get to here, it wasn't really a command
}
}
# we've processed all the commands; let's clean up and go home
&done();