#! /bin/sh # This is a shell archive. Remove anything before this line, then unpack # it by saving it into a file and typing "sh file". To overwrite existing # files, type "sh file -c". You can also feed this as standard input via # unshar, or by typing "sh 'resend.README' <<'END_OF_FILE' The .cf file is a config file for my listserv-like package called Majordomo. The only one of those variables that "resend" actually uses is "homedir", which tells it where to look for the "majordomo.pl" library. X Sendmail (and probably other packages) have a 256 byte limit on the length of an alias. It's very easy to exceed that limit with all the flags that you might use for a typical "resend" invocation. To get around that limit, "resend" supports an "@filename" mechanism for retrieving all the flags from "filename". You can put all the flags in a file (for instance, you might name it /mail/lists/listname.resend), and reference the file with an "@filename" argument (for instance, X"resend @/mail/lists/listname.resend"). If you do this, you should put ALL the flags in the file, and the "@filename" argument should be the only argument to "resend". X Note that the "-C " flag, if used, MUST BE THE FIRST FLAG, regardless of whether it's on the command line or in the file of flags referenced by an "@filename" argument. X Here's the rundown of the arguments to "resend": X X -C specify alternate config file (must be first!) X -l REQUIRED: specify list name X -h REQUIRED: specify host name X -f specify "sender" (default -request) X -m specify special sendmail flags X -M specify max message length to forward X -p add "Precedence: " header X -r add "Reply-To: " header X -I Bounce messages from users not listed in file X in colon-separated X -a approval password X -A moderate list (require "Approved:" for posting) X -R delete "Received:" lines X -s enable "administrivia" checks X -d debug; say it, but don't do it X Any message that "resend" doesn't like is sent to the list owner (the X"-f" address, or "-request" if -f isn't used) along with a comment indicating what "resend" didn't like about it. To go ahead and send the message, just feed it to resend without the flag that caused it to reject it (in other words, if it rejected it because it was too long, omit the "-M <>" flag; if it rejected it because it was administrivia, omit the "-s" flag). X If you specify "-a " flag, this "approval" password can be used in an "Approved: " line to override most of the other checks (those enabled by "-s", "-M", and so forth). The "Approved: X" line can either be one of the mail headers, or the first line of the body of the message. If it is in the headers, the rest of the headers are resent as part of the approved message. If it is in the body, the current headers are discarded in favor of the headers from the original message which should follow the "Approved:" line in the body. X The owner of a mailing list can thus post messages that were initially bounced by adding an "Approved: " line and resubmitting the message. Any "Approved: " line is stripped before the message is sent to the mailing list, so that list members won't learn the password. If the argument to the "-a" flag begins with a "/", it is assumed to be a file name from which the actual password is read. X You can make a list "moderated" by specifying the "-A" flag. If the X"-A" flag is set, then any messages not containing a valid "Approved:" line are sent to the list owner, rather than the whole list.; the list owner can then review the message, add an appropriate "Approved:" line, and resubmit them (these last two steps can be done easily with the "approve" command that comes with Majordomo). If you specify the "-A" flag, you must also specify the "-a " flag, so that resend knows what approval password to use. X If you only want to accept messages from members of a list, you can use the "-I " flag to do this. "" should be a colon-separated list of files in the $listdir directory (specified in the config file) that "resend" will check the address in "From:" line of a message against. If the address doesn't show up in one of those files, and the message doesn't have a valid "approved" header on it, it will be bounced to the list owner. X X$Header: /mycroft/brent/majordomo/RCS/resend.README,v 1.5 1993/11/11 02:42:58 brent Exp $ END_OF_FILE if test 4500 -ne `wc -c <'resend.README'`; then echo shar: \"'resend.README'\" unpacked with wrong size! fi # end of 'resend.README' fi if test -f 'resend' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'resend'\" else echo shar: Extracting \"'resend'\" \(8759 characters\) sed "s/^X//" >'resend' <<'END_OF_FILE' X#!/usr/local/bin/perl -U X X# Copyright 1992, D. Brent Chapman. All Rights Reserved. For use by X# permission only. X# X# $Source: /mycroft/brent/majordomo/RCS/resend,v $ X# $Revision: 1.19 $ X# $Date: 1993/11/11 02:23:37 $ X# $Author: brent $ X# $State: Exp $ X# X# $Locker: $ X# X X# set our path explicitly X$ENV{'PATH'} = "/bin:/usr/bin:/usr/ucb"; X X# What shall we use for temporary files? X$tmp = "/tmp/majordomo.$$"; X X# If the first argument is "@filename", read the real arguments X# from "filename", and shove them onto the ARGV for later processing X# by &Getopts() X if ($ARGV[0] =~ /^@/) { X $fn = shift(@ARGV); X $fn =~ s/^@//; X open(AV, $fn) || die("open(AV, \"$fn\"): $!\nStopped"); X undef($/); # set input field separator X $av = ; # read whole file into string X close(AV); X @av = split(/\s+/, $av); X unshift(@ARGV, @av); X $/ = "\n"; X} X X# Read and execute the .cf file X$cf = $ENV{"MAJORDOMO_CF"} || "/etc/majordomo.cf"; if ($ARGV[0] eq "-C") { X $cf = $ARGV[1]; X shift(@ARGV); X shift(@ARGV); X} if (! -r $cf) { X die("$cf not readable; stopped"); X} eval(`cat $cf`); X chdir($homedir) || die("Can't chdir(\"$homedir\"): $!"); unshift(@INC, $homedir); require "majordomo.pl"; require "getopts.pl"; X X&Getopts("Aa:df:h:I:l:m:M:p:Rr:s") || die("resend: Getopts(): $!"); X if (! defined($opt_l) || ! defined($opt_h)) { X die("resend: must specify both '-l list' and '-h host' arguments"); X} X if (defined($opt_f)) { X $sendmail_sender = $opt_f; X} else { X $sendmail_sender = "$opt_l-request"; X} X if (defined($opt_a)) { X if ($opt_a =~ /^\//) { X open(PWD, $opt_a) || die("resend: open(PWD, \"$opt_a\"): $!"); X $opt_a = &chop_nl(); X } X} X if (defined($opt_A) && ! defined($opt_a)) { X die("resend: must also specify '-a passwd' if using '-A' flag"); X} X X$sender = "$sendmail_sender@$opt_h"; X open(OUT, ">/tmp/resend.$$.out") || X die("resend: Can't open /tmp/resend.$$.out: $!"); X open(IN, ">/tmp/resend.$$.in") || X die("resend: Can't open /tmp/resend.$$.in: $!"); X while () { X print IN $_; X} X close(IN); X open(IN, "/tmp/resend.$$.in") || X die("resend: Can't open /tmp/resend.$$.tmp: $!"); X do { X $restart = 0; X $pre_hdr = 1; X while () { X if ($pre_hdr) { X if (/^\s*$/) { X # skip leading blank lines; usually only there if this is a X # restart after an in-body "Approved:" line X next; X } else { X $pre_hdr = 0; X $in_hdr = 1; X $kept_last = 0; X } X } X if ($in_hdr) { X if (/^\s*$/) { X # end of header; add new header fields X print OUT "Sender: $sender\n"; X if (defined($opt_p)) { X print OUT "Precedence: $opt_p\n"; X } X if (defined($opt_r)) { X print OUT "Reply-To: $opt_r@$opt_h\n"; X } X $in_hdr = 0; X print OUT $_; X } elsif (/^approved:\s*(.*)/i && defined($opt_a)) { X $approved = &chop_nl($1); X if ($approved ne $opt_a) { X &bounce("Invalid 'Approved:' header"); X } X } elsif (/^from /i # skip all these headers X || /^sender:/i X || /^return-receipt-to:/i X || /^errors-to:/i X || /^return-path:/i X || (/^reply-to:/i && defined($opt_r)) # skip only if "-r" set X || (/^precedence:/i && defined($opt_p)) # skip only if "-p" set X || (/^received:/i && defined($opt_R)) # skip only if "-R" set X || (/^\s/ && ! $kept_last) # skip if skipped last X ) { X # reset $kept_last in case next line is continuation X $kept_last = 0; X } else { X # check for administrivia requests X if (defined($opt_s) && ! defined($approved) X && (/^subject:\s*subscribe\b/i || X /^subject:\s*unsubscribe\b/i)) { X &bounce("Admin request"); X } X if (defined($opt_I)) X { X if ( /^from:\s*(.+)/i ) X { X $from = $1; X $from_last = 1; X } X elsif ( defined($from_last) ) X { X if ( /^\s+(.+)/ ) X { X $from .= " $1"; X } X else X { X undef($from_last); X } X } X } X &check_hdr_line($_); # check for length & balance X $kept_last = 1; X print OUT $_; X } X } else { X # this isn't a header line, so print it (maybe) X # first, though, is the first line of the body an "Approved:" line? X if (($body_len == 0) && /^approved:\s*(.*)/i && defined($opt_a)) { X # OK, is it a valid "Approved:" line? X $approved = &chop_nl($1); X if ($approved ne $opt_a) { X &bounce("Invalid 'Approved:' header"); X } else { X # Yes, it's a valid "Approved:" line... X # So, we start over X $restart = 1; X close(OUT); X unlink("/tmp/resend.$$.out"); X open(OUT, ">/tmp/resend.$$.out") || X die("resend: Can't open /tmp/resend.$$.out: $!"); X last; X } X } X # make sure it doesn't make the message too long X if (defined($opt_M) && ! defined($approved) X && ($body_len += length($_)) > $opt_M) { X &bounce("Message too long (>$opt_M)"); X } X # add admin-request recognition heuristics here... (body) X if (defined($opt_s) && ! defined($approved) && ($body_line++ < 5) && ( X /\badd me\b/i X || /\bdelete me\b/i X || /\bsubscribe\b/i || /^sub\b/i X || /\bunsubscribe\b/i || /^unsub\b/i X )) { X &bounce("Admin request"); X } X print OUT $_; X } X } X} while ($restart); X if (defined($opt_A) && ! defined($approved)) { X &bounce("Approval required"); X} X close(OUT); X if ( defined($opt_I) && defined($from) && ! defined($approved) ) { X local($infile) = 0; X X @files = split (/[:\t\n]+/, $opt_I); X X foreach $file (@files) { X if ( open (LISTFD, "<${listdir}/${file}") != 0 ) { X @output = grep (&addr_match($from, $_), ); X close (LISTFD); X X if ( $#output != -1 ) { X $infile = 1; X last; X } X } X } X X if ( $infile == 0 ) { X &bounce ("Non-member submission from [$from]"); X } X} X X$sendmail_cmd = "/usr/lib/sendmail $opt_m -f$sendmail_sender " . X join(" ", @ARGV); X if (defined($opt_d)) { X $| = 1; X print "Command: $sendmail_cmd\n"; X $status = (system("cat /tmp/resend.$$.out") >> 8); X unlink(); X exit($status); X} else { X system("$sendmail_cmd ); X exit(0); X} X sub check_balance { X # set a temporary variable X local($t) = shift; X # strip out all nested parentheses X 1 while $t =~ s/\([^\(\)]*\)//g; X # strip out all nested angle brackets X 1 while $t =~ s/\<[^\<\>]*\>//g; X # if any parentheses or angle brackets remain, were imbalanced X if ($t =~ /[\(\)\<\>]/ && ! defined($approved)) { X &bounce("Imbalanced parentheses or angle brackets"); X return(undef); X } X return(1); X} X sub check_hdr_line { X X local($_) = shift; X X if (! /^\s/) { # is this a continuation line? X # Not a continuation line. X # If $balanced_fld is defined, it means the last field was one X # that needed to have balanced "()" and "<>" (i.e., "To:", "From:", X # and "Cc:", so check it. We do it here in case the last field was X # multi-line. X X if (defined($balanced_fld)) { X &check_balance($balanced_fld); X } X X # we undefine $balanced_fld and reset $field_len; these may be set below X X undef($balanced_fld); X $field_len = 0; X } X X # is this a field that must be checked for balanced "()" and "<>"? X if (defined($balanced_fld) || /^from:/i || /^cc:/i || /^to:/i) { X # yes it is, but we can't check it yet because there might be X # continuation lines. Buffer it to be checked at the beginning X # of the next non-continuation line. X X # is this line too long? X if ((length($_) > 128) && ! defined($approved)) { X &bounce("Header line too long (>128)"); X return(undef); X } X X # is this field too long? X if ((($field_len += length($_)) > 1024) && ! defined($approved)) { X &bounce("Header field too long (>1024)"); X return(undef); X } X X $balanced_fld .= $_; X chop($balanced_fld); X } X X # if we get here, everything was OK. X return(1); X} X sub bounce { X local($reason) = shift; X local($_); X X &resend_sendmail(BOUNCE, $sender, "BOUNCE $opt_l@$opt_h: $reason"); X X seek(IN, 0, 0); X while () { X print BOUNCE $_; X } X close(BOUNCE); X unlink(); X exit(0); X} X sub resend_sendmail { X local(*MAIL) = shift; X local($to) = shift; X local($subject) = shift; X X # clean up the addresses, for use on the sendmail command line X local(@to) = &ParseAddrs($to); X for (@to) { X $_ = join(", ", &ParseAddrs($_)); X } X $to = join(", ", @to); X X # open the process X if (defined($opt_d)) { X # debugging, so just say it, don't do it X open(MAIL, ">-"); X print MAIL ">>> /usr/lib/sendmail -f$sendmail_sender $to\n"; X } else { X open(MAIL, "|/usr/lib/sendmail -f$sendmail_sender $to") || X &abort("Can't connect to sendmail: $!"); X } X X # generate the header X print MAIL <<"EOM"; To: $to XFrom: $sender Subject: $subject X XEOM X X return; X} END_OF_FILE if test 8759 -ne `wc -c <'resend'`; then echo shar: \"'resend'\" unpacked with wrong size! fi chmod +x 'resend' # end of 'resend' fi echo shar: End of shell archive. exit 0 .