#!/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();