This commit is contained in:
tuend-work
2025-11-13 08:41:45 +07:00
parent 1b646f6a89
commit 18736081c6
166 changed files with 72044 additions and 2 deletions

View File

@@ -0,0 +1,109 @@
#!/bin/perl
#(Message inbox:15)
#Return-Path: Majordomo-Users-Owner@greatcircle.com
#Message-Id: <m0oXmfl-0002wDC@hock.bolis.sf-bay.org>
#From: Alan Millar <amillar@bolis.sf-bay.org>
#Subject: Perl prog to create list archives
#To: majordomo-users@greatcircle.com
#Date: Wed, 1 Sep 1993 00:32:03 -0800 (PDT)
#Cc: brent@greatcircle.com
#Reply-To: Alan Millar <AMillar@bolis.sf-bay.org>
#
#
#Hi-
#
#Here is a perl program I wrote to keep mailing list archives.
#It is designed to produce list archive files similar to Revised
#Listserv. Each message is separated by a line of "==="s and
#most of the header "noise" is gone. Instead of being stored
#in one big file, they are split into one file per month with
#the name logYYMM where YY and MM are the numeric year and
#month.
#
#I call it from /usr/lib/aliases using:
#
# listname-archive: "|/usr/local/mail/majordomo/wrapper archive.pl \
# /usr/local/mail/lists/listname.archive"
#
#Where the last parameter is the directory name to put the
#log files into.
#
#Give it a try and let me know what you think.
#
#- Alan
#
#---- ,,,,
#Alan Millar amillar@bolis.SF-Bay.org __oo \
#System Administrator =___/
#The skill of accurate perception is called cynicism by those who don't
#possess it.
#----
# archive.pl
# Mailing list archiver. Specify the directory (not the file)
# on the command line. Messages are written to a file
# called 'logYYMM' in that directory, where YY is the two digit
# year and MM is the two-digit month.
# Written by Alan Millar August 25 1993.
# All these should be in the standard PERL library
unshift(@INC, $homedir);
require "majordomo.pl"; # all sorts of general-purpose Majordomo subs
require "shlock.pl"; # NNTP-style file locking
# The headers we want to keep, in order:
@keepHeaders =
( "To", "cc"
, "from", "reply-to", "organization"
, "date", "subject"
, "summary", "keywords"
, "Content-Type"
);
#-----------------------------------
# Set up output file. See if directory is specified on command line.
$outputDir = $ARGV[0];
if (! -d $outputDir) {
$outputDir = "/tmp";
}
$outputDir =~ s/\/$//; # drop trailing slash
#------------------------------------
# Get date for log file name
($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
# log file name is form "logYYMM"
$logFile = sprintf("$outputDir/log%2.2d%2.2d",$year,$mon + 1);
# open output file
&lopen(OUTPUT,">>",$logFile);
# Parse the mail header of the message, so we can figure out who to reply to
&ParseMailHeader(STDIN, *hdrs);
# Print the headers we want
print OUTPUT "========================================";
print OUTPUT "======================================\n";
foreach $key (@keepHeaders) {
$key =~ tr[A-Z][a-z];
if (defined($hdrs{$key})) {
$newKey = $key; substr($newKey,0,1) =~ tr/a-z/A-Z/;
printf OUTPUT "%-15s%s\n", "$newKey: ", $hdrs{$key};
} # if non-blank
} # foreach
print OUTPUT "\n";
# copy the rest of the message
while (<STDIN>) {
print OUTPUT $_;
}
print OUTPUT "\n";
&lclose(OUTPUT);

View File

@@ -0,0 +1,34 @@
#!/bin/perl
# archive: A hack to use mh to handle the archives
#
# You may redistribute this file, or inlcude it into the offical majordomo
# package
#
# $Source: /sources/cvsrepos/majordomo/contrib/archive_mh.pl,v $
# $Revision: 1.4 $
# $Date: 1997/03/10 15:40:41 $
# $Author: cwilson $
# $State: Exp $
#
# $Locker: $
# set our path explicitly
$ENV{'PATH'} = "/bin:/usr/bin:/usr/ucb";
# Read and execute the .cf file
$cf = $ENV{"MAJORDOMO_CF"} || "/tools/majordomo-1.56/majordomo.cf";
if ($ARGV[0] eq "-C") {
$cf = $ARGV[1];
shift(@ARGV);
shift(@ARGV);
}
if (! -r $cf) {
die("$cf not readable; stopped");
}
require "$cf";
# Go to the home directory specified by the .cf file
chdir("$homedir");
exec("/tools/mh-6.8/lib/mh/rcvstore +$filedir/$ARGV[0] -nocreate\n");

View File

@@ -0,0 +1,348 @@
From: pdc@lunch.asd.sgi.com (Paul Close)
Subject: Digest code diffs for 1.90
Date: Thu, 21 Apr 1994 17:56:22 -0700 (PDT)
Here are my changes to digest which support config file settings for
specifying the digest size in lines and/or the maximum age of the oldest
article, in days. Also support a new flag, -p (for "push"), intended for
use by cron jobs. It checks to see if a digest should be sent, and sends
it if it should (pretty well because an article is too old, but you could
use this to send all the time).
A few comments on the code. In &should_be_sent, I calculate how big the
article would be if the headers were stripped, both in bytes and in lines.
I add in a bit of a fudge factor for mail headers, just so we don't get too
close to maxlength bytes before sending. Typically, the line count will
cause a digest to be sent before the byte count would (I see the maxlength
count as more of a mailer issue than a digest issue).
The old digest code had a strange construct: s/\n+$/\n/; I assumed that
this was to trim newlines off the end of the string, but multi-line regexps
don't work that way. The only way I could get this to work is:
$len = length($body) - 1;
$len-- while (substr($body,$len,1) eq "\n");
substr($body,$len+1) = "";
Any clever hacks appreciated. In the same area, I changed the code that
reads the body of the message to read the whole thing at once (undef $/)
rather than do multiple string concatenations. Seems more efficient. I
also added a ^From escaper, using enough of a real "From " line pattern,
that it shouldn't match just any line beginning with From.
Under the heading of random perl lore, to count the number of newlines in a
multi-line string, I used:
$lines += ($body =~ s/\n/\n/g);
seems pretty straightforward, but I have the nagging suspicion there's an
easier way.
Finally, I made digest safe past the year 2000, by printing $year+1900
rather than 19$year. Whoopee!
Comments welcome! The code is based on 1.90b2, which is the latest I have.
Index: digest/digest
*** digest/digest.old Sun Mar 6 14:47:06 1994
--- digest/digest Thu Apr 21 17:35:33 1994
***************
*** 63,72 ****
if (defined($opt_r)) {
&receive_message;
} elsif (defined($opt_m)) {
&make_digest;
} else {
! &abort("Usage: digest {-r|-m} [-c config|(-C -l list)]\nStopped");
}
&free_lock;
--- 63,79 ----
if (defined($opt_r)) {
&receive_message;
+ if (&should_be_sent()) {
+ &make_digest;
+ }
} elsif (defined($opt_m)) {
&make_digest;
+ } elsif (defined($opt_p)) {
+ if (&should_be_sent()) {
+ &make_digest;
+ }
} else {
! &abort("Usage: digest {-r|-m|-p} [-c config|(-C -l list)]\nStopped");
}
&free_lock;
***************
*** 73,97 ****
exit(0);
sub receive_message {
- $sum = 0;
$i = 0;
do {
! $i++;
! $file = sprintf("%s/%03d", $V{'INCOMING'}, $i);
! $sum += (-s $file);
} until (! -e $file);
print STDERR "Receiving $i\n";
open(MSG, ">$file") || &abort("open(MSG, \">$file\"): $!");
while (<STDIN>) {
print MSG $_;
}
close(MSG);
- $sum += (-s $file);
- if ($sum > $V{'DIGEST_SIZE'}) {
- &make_digest;
- }
- return(1);
}
--- 80,146 ----
exit(0);
+ sub should_be_sent {
+ # fudge factors for headers and footers
+ $sum = 600 + length($HEADER) + length($HEADERS) + length($TRAILER);
+ $lines = 25;
+ $i = 0;
+ while (1) {
+ $file = sprintf("%s/%03d", $V{'INCOMING'}, ++$i);
+ last unless (-e $file);
+ open(COUNT, "<$file") || &abort("open(COUNT, \"<$file\"): $!");
+
+ $/ = ''; # grab the header
+ $head = <COUNT>;
+
+ # only count From/Date/Subject header fields to get a
+ # more accurate size and line count.
+ $head =~ s/\n\s+/ /g;
+ $head =~ /^(From:\s+.*)/i && ($sum += length($1)+1, $lines++);
+ $head =~ /^(Subject:\s+.*)/i && ($sum += length($1)+1, $lines++);
+ $head =~ /^(Date:\s+.*)/i && ($sum += length($1)+1, $lines++);
+ $sum++, $lines++;
+
+ # count the body of the message
+ undef $/;
+ $body = <COUNT>;
+ $sum += length($body);
+ $lines += ($body =~ s/\n/\n/g); # count newlines
+
+ $/ = "\n";
+ close(COUNT);
+ $sum += length($EB) + 2, $lines += 2; # account for message delimiter
+
+ if ($V{'DIGEST_SIZE'} && $sum > $V{'DIGEST_SIZE'}) {
+ return(1);
+ }
+ if ($V{'DIGEST_LINES'} && $lines > $V{'DIGEST_LINES'}) {
+ return(1);
+ }
+ if ($V{'MAX_AGE'} && (-M $file) > $V{'MAX_AGE'}) {
+ return(1);
+ }
+ }
+ print "don't send. sum = $sum, lines = $lines\n";
+
+ return(0);
+ }
+
sub receive_message {
$i = 0;
do {
! $file = sprintf("%s/%03d", $V{'INCOMING'}, ++$i);
} until (! -e $file);
+
print STDERR "Receiving $i\n";
open(MSG, ">$file") || &abort("open(MSG, \">$file\"): $!");
+
+ # copy the message
while (<STDIN>) {
print MSG $_;
}
+
close(MSG);
}
***************
*** 111,129 ****
$head = <message>;
$head =~ s/\n\s+/ /g;
$body = "";
! ($subj) = ($head =~ /^subject:\s+(.*)/i);
! $subj = "[none]" unless $subj;
! ($from) = ($head =~ /^from:\s+(.*)/i);
! ($date) = ($head =~ /^date:\s+(.*)/i);
! $/ = "\n";
! while (<message>) {
! s/^-/- -/; #escape encapsulation boundaries in message
! $body .= $_;
! }
close(message);
! $body =~ s/\n+$/\n/;
push(@subj,$subj);
print TEMP <<EOF;
From: $from
--- 160,184 ----
$head = <message>;
$head =~ s/\n\s+/ /g;
$body = "";
! $subj = ($head =~ /^Subject:\s+(.*)/i)? $1: "[none]";
! ($from) = $head =~ /^From:\s+(.*)/i;
! ($date) = $head =~ /^Date:\s+(.*)/i;
! undef $/;
! $body = <message>;
close(message);
!
! # escape ^From <user> <weekday> <month> <day> <hr:min:sec> ...
! $body =~
! s/^From (\S+\s+\w{3}\s+\w{3}\s+\d+\s+\d+:\d+:\d+)/>From $1/g;
! $body =~ s/^-/- -/g; # escape encapsulation boundaries in message
! # trim trailing \n's
! $len = length($body) - 1;
! $len-- while (substr($body,$len,1) eq "\n");
! substr($body,$len+1) = "";
+ $/ = "\n";
+
push(@subj,$subj);
print TEMP <<EOF;
From: $from
***************
*** 131,136 ****
--- 186,192 ----
Subject: $subj
$body
+
$EB
EOF
***************
*** 204,211 ****
$* = 1;
$HOME = $ENV{"HOME"} || (getpwuid($>))[7];
chdir($HOME);
! &getopt("rmc:Cl:") ||
! &abort("Usage: digest {-r|-m} [-c config|(-C -l list)]\nStopped");
$config = $opt_c || "$HOME/.digestrc";
$TEMP = "/tmp/digest.$$";
$SIG{'INT'} = 'cleanup';
--- 260,267 ----
$* = 1;
$HOME = $ENV{"HOME"} || (getpwuid($>))[7];
chdir($HOME);
! &getopt("rmpc:Cl:") ||
! &abort("Usage: digest {-r|-m|-p} [-c config|(-C -l list)]\nStopped");
$config = $opt_c || "$HOME/.digestrc";
$TEMP = "/tmp/digest.$$";
$SIG{'INT'} = 'cleanup';
***************
*** 245,252 ****
$NUMBER = $config_opts{$opt_l,"digest_issue"};
$Precedence = $config_opts{$opt_l,"precedence"};
$Precedence = "bulk" if ($Precedence eq "");
! $V{'ARCHIVE'} = "$filedir/$opt_l$filedirsuffix";
$V{'DIGEST_SIZE'} = $config_opts{$opt_l, "maxlength"};
$V{'ERRORS-TO'} = $config_opts{$opt_l,"sender"};
$V{'FROM'} = $config_opts{$opt_l, "sender"};
$V{'INCOMING'} = "$digest_work_dir/$opt_l";
--- 301,310 ----
$NUMBER = $config_opts{$opt_l,"digest_issue"};
$Precedence = $config_opts{$opt_l,"precedence"};
$Precedence = "bulk" if ($Precedence eq "");
! $V{'ARCHIVE'} = "$filedir/$opt_l$filedir_suffix";
$V{'DIGEST_SIZE'} = $config_opts{$opt_l, "maxlength"};
+ $V{'DIGEST_LINES'} = $config_opts{$opt_l, "digest_maxlines"};
+ $V{'MAX_AGE'} = $config_opts{$opt_l, "digest_maxdays"};
$V{'ERRORS-TO'} = $config_opts{$opt_l,"sender"};
$V{'FROM'} = $config_opts{$opt_l, "sender"};
$V{'INCOMING'} = "$digest_work_dir/$opt_l";
***************
*** 327,333 ****
sub getdate {
local($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
! return($DAYS[$wday] . ", $mday " . $MONTHS[$mon] . " 19$year");
}
sub set_lock {
--- 385,392 ----
sub getdate {
local($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
! $year += 1900;
! return("$DAYS[$wday], $mday $MONTHS[$mon] $year");
}
sub set_lock {
Index: config_parse.pl
*** config_parse.pl.old Thu Apr 21 07:32:50 1994
--- config_parse.pl Thu Apr 21 07:41:33 1994
***************
*** 128,133 ****
--- 128,135 ----
'digest_archive', '',
'digest_rm_footer', '',
'digest_rm_fronter', '',
+ 'digest_maxlines', '',
+ 'digest_maxdays', '',
# general stuff below
'comments', '', # comments about config file
);
***************
*** 331,336 ****
--- 333,346 ----
Just like digest_rm_footer, it is also non-operative.',
);
+ 'digest_maxlines',
+ "automatically generate a new digest when the size of the digest exceeds
+ this number of lines.",
+
+ 'digest_maxdays',
+ "automatically generate a new digest when the age of the oldest article in
+ the queue exceeds this number of days.",
+
# match commands to their subsystem, by default only 4 subsystems
# exist, majordomo, resend, digest and config.
%subsystem = (
***************
*** 372,377 ****
--- 382,389 ----
'digest_archive', 'digest',
'digest_rm_footer', 'digest',
'digest_rm_fronter', 'digest',
+ 'digest_maxlines', 'digest',
+ 'digest_maxdays', 'digest',
# general stuff here
'comments', 'config',
);
***************
*** 418,423 ****
--- 430,437 ----
'digest_archive', 'grab_absolute_dir',
'digest_rm_footer', 'grab_word',
'digest_rm_fronter', 'grab_word',
+ 'digest_maxlines', 'grab_integer',
+ 'digest_maxdays', 'grab_integer',
# general stuff below
'comments', 'grab_string_array',
);
--
Paul Close pdc@sgi.com ...!{ames, decwrl, uunet}!sgi!pdc
No fate but what we make

View File

@@ -0,0 +1,45 @@
#!/usr/local/bin/perl
# Program name digest.num -- Digest numbering.
#
# Lindsay Haisley, FMP Computer Serivces (fmouse@fmp.com)
#
# Usage: digest.num -l list_name [-i issue_num] [-v volume_num]
#
# Sets number for next digest issue and volume number in the config file
# for list list_name. If issue_num and volume_num are not supplied, they
# are set to 0.
$cf = $ENV{"MAJORDOMO_CF"} || "/etc/majordomo.cf";
require "$cf";
# chdir("$homedir");
require "shlock.pl";
require "config_parse.pl";
require "getopt.pl";
&Getopt('liv');
die "No list config specified\n" if !defined($opt_l);
die "List config file $opt_l.config does not exist\n" unless -e "$listdir/$opt_l.config";
&get_config($listdir, $opt_l);
if (defined($opt_v)) {
$volume = $opt_v;
} else {
$volume = 0;
}
if (defined($opt_i)) {
$issue = $opt_i;
} else {
$issue = 0;
}
$config_opts{$opt_l, "digest_volume"} = $volume;
$config_opts{$opt_l, "digest_issue"} = $issue;
&set_lock("$listdir/$opt_l.config.LOCK");
&config'writeconfig($listdir, $opt_l);
&free_lock("$listdir/$opt_l.config.LOCK");
print STDERR "Config for list $opt_l set to volume $volume, issue $issue\n";

View File

@@ -0,0 +1,28 @@
#! /bin/sh
# This script was contributed by "Paul Pomes" <P-Pomes@uiuc.edu>
#
# It only works with versions of "digest" that have been modified
# to work with the config file moddifications in majordomo 1.90 and above.
# This script can be called from cron to automatically generate
# digests for all of the lists in DIGESTDIR. E.G.
#
# daily
# 0 2 * * * /path/to/digest.send
#
# weekly (on monday)
# 0 2 * * 1 /path/to/digest.send
#
# monthly (first of the month)
# 0 2 1 * * /path/to/digest.send
#
DIGESTDIR=/usr/spool/digests
cd $DIGESTDIR
for i in *
do
if [ -f $i/001 ];
then
/path/to/majordomo/wrapper digest -m -C -l $i ${i}-outgoing
fi
done

View File

@@ -0,0 +1,156 @@
#!/bin/perl
#
# Print various statistics about the Log file
#
# Todo: summarize admin commands
#
# Paul Close, April 1994
#
while (<>) {
if (($mon,$day,$time,$who,$cmd) =
/([A-Za-z]+) (\d+) ([\d:]+)\s+.*majordomo\[\d+\]\s+{(.*)} (.*)/)
{
@f = split(' ',$cmd);
$cmd = $f[0];
$f[1] =~ s/[<>]//g;
$f[2] =~ s/[<>]//g;
$count{$cmd}++;
# help
# lists
# which [address]
# approve PASSWD ...
if ($cmd eq "approve" ||
$cmd eq "help" ||
$cmd eq "lists" ||
$cmd eq "which")
{
${$cmd}++;
}
# index list
# info list
# who list
elsif ($cmd eq "index" ||
$cmd eq "info" ||
$cmd eq "who")
{
if ($#f == 1) {
$lists{$f[1]}++;
$f[1] =~ s/-//g;
${$f[1]}{$cmd}++;
} else {
$bad{$cmd}++;
}
}
# get list file
# newinfo list passwd
elsif ($cmd eq "get" ||
$cmd eq "newinfo")
{
if ($#f == 2) {
$lists{$f[1]}++;
$f[1] =~ s/-//g;
${$f[1]}{$cmd}++;
if ($cmd eq "get") {
$req = &ParseAddrs($who);
$long{$req} = $who;
$getcount{$req}++;
}
} else {
$bad{$cmd}++;
}
}
# subscribe list [address]
# unsubscribe list [address]
elsif ($cmd eq "subscribe" ||
$cmd eq "unsubscribe")
{
if ($#f >= 1) {
$lists{$f[1]}++;
$f[1] =~ s/-//g;
${$f[1]}{$cmd}++;
} else {
$bad{$cmd}++;
}
}
# request cmd list subscribe (for approval)
elsif ($cmd eq "request") {
if ($#f >= 2) {
$lists{$f[2]}++;
$f[2] =~ s/-//g;
${$f[2]}{$cmd}++;
} else {
$bad{$cmd}++;
}
}
else {
$unrecognized{$cmd}++;
}
} else {
warn "line $. didn't match!\n" if !/^$/;
}
}
#print "Command summary:\n";
#foreach $cmd (sort keys %count) {
# printf " %-20s %4d\n", $cmd, $count{$cmd};
#}
print "Global commands:\n";
printf(" %-15s %4d\n", "help", $help) if defined($help);
printf(" %-15s %4d\n", "lists", $lists) if defined($lists);
printf(" %-15s %4d\n", "which", $which) if defined($which);
print "\n";
#print "Unrecognized commands:\n";
#foreach $cmd (sort keys %unrecognized) {
# printf " %-15s %4d\n", $cmd, $unrecognized{$cmd};
#}
#print "\n";
if (defined(%bad)) {
print "Incomplete commands:\n";
foreach $cmd (sort keys %bad) {
printf " %-15s %4d\n", $cmd, $bad{$cmd};
}
print "\n";
}
# skip request and newinfo
print "List subscr unsub index get info who config approve\n";
foreach $list (sort keys %lists) {
printf "%-20s", substr($list,0,20);
$list =~ s/-//g;
%l = %{$list};
printf " %6d %6d %6d %6d %6d %6d %6d %6d\n", $l{subscribe}, $l{unsubscribe},
$l{index}, $l{get}, $l{info}, $l{who}, $l{config}, $l{approve};
}
print "\n";
@reqs = sort {$getcount{$b}<=>$getcount{$a};} keys %getcount;
if ($#reqs >= 0) {
print "Top requestors (get command):\n";
for ($i=0; $i < 5; $i++) {
printf " %5d %s\n", $getcount{$reqs[$i]}, $long{$reqs[$i]};
last if ($i == $#reqs);
}
}
# from majordomo.pl, modified to work on a single address
# $addrs = &ParseAddrs($addr_list)
sub ParseAddrs {
local($_) = shift;
1 while s/\([^\(\)]*\)//g; # strip comments
1 while s/"[^"]*"//g; # strip comments
1 while s/.*<(.*)>.*/\1/;
s/^\s+//;
s/\s+$//;
$_;
}

View File

@@ -0,0 +1,98 @@
#!/bin/perl
#
# Given an archive directory, create a table of contents file and a topics
# file. The table of contents file simply lists each subject that appears
# in each archive file, while the topics file is a list of each unique
# subject and the files that subject appears in.
#
# I run this from cron every night....
#
# Paul Close, April 1994
#
if ($#ARGV != -1) {
$dir = $ARGV[0];
shift;
}
else {
die "usage: $0 archive_directory\n";
}
opendir(FILES, $dir) || die "Can't open directory $dir: $!\n";
@files = readdir(FILES); # get all files in archive directory
closedir(FILES);
open(INDEX,">$dir/CONTENTS") || die "Can't open $dir/CONTENTS: $!\n";
open(TOPICS,">$dir/TOPICS") || die "Can't open $dir/TOPICS: $!\n";
foreach $basename (@files) {
next if $basename eq '.';
next if $basename eq '..';
next if $basename eq "CONTENTS";
next if $basename eq "TOPICS";
print INDEX "\n$basename:\n";
open(FILE, "$dir/$basename") || next;
while (<FILE>) {
if (/^Subject:\s+(.*)/i) {
($subj = $1) =~ s/\s*$//;
next if $subj eq "";
#
# for index file, just print the subject
#
print INDEX " $subj\n";
#
# for topics file, strip Re:'s, remove digest postings,
# and trim the length to 40 chars for pretty-printing.
#
1 while ($subj =~ s/^Re(\[\d+\]|2?):\s*//i); # trim all Re:'s
next if $subj eq "";
next if $subj =~ /[A-Za-z]+ Digest, Volume \d+,/i;
next if $subj =~ /[A-Za-z]+ Digest V\d+ #\d+/i;
if (length($subj) > 40) {
$subj = substr($subj, 0, 37) . "...";
}
#
# Make a key that's all lower case, and no whitespace to
# reduce duplicate topics that differ only by those. This
# also results in a list of topics sorted case-independent.
#
($key = $subj) =~ tr/A-Z/a-z/;
$key =~ s/\s+//g;
$subjlist{$key} .= "$basename,";
if (!defined($realsubj{$key})) {
$realsubj{$key} = $subj;
}
}
}
close(FILE);
}
close(INDEX);
foreach $subj (sort keys %subjlist) {
#
# for each subject, record each file it was found in
#
undef %found;
undef @names;
for (split(",", $subjlist{$subj})) {
$found{$_} = 1;
}
#
# make list of 'found' names and wrap at 80 columns
#
$names = join(", ", sort keys %found);
undef @namelist;
while (length($names) > 40) {
$index = 40;
$index-- until (substr($names, $index, 1) eq " " || $index < 0);
push(@namelist,substr($names,0,$index));
$names = substr($names,$index+1);
}
push(@namelist,$names);
printf TOPICS "%-40s %s\n", $realsubj{$subj}, $namelist[0];
for ($i=1; $i <= $#namelist; $i++) {
print TOPICS " " x 41, $namelist[$i], "\n";
}
}
close(TOPICS);

View File

@@ -0,0 +1,103 @@
#!/bin/perl
# $Source: /sources/cvsrepos/majordomo/contrib/new-list,v $
# $Revision: 1.14 $
# $Date: 1996/12/09 16:50:45 $
# $Author: cwilson $
# $State: Exp $
#
# $Locker: $
# set our path explicitly
$ENV{'PATH'} = "/bin:/usr/bin:/usr/ucb";
# Read and execute the .cf file
$cf = $ENV{"MAJORDOMO_CF"} || "/etc/majordomo.cf";
if ($ARGV[0] eq "-C") {
$cf = $ARGV[1];
shift(@ARGV);
shift(@ARGV);
}
if (! -r $cf) {
die("$cf not readable; stopped");
}
require "$cf";
chdir($homedir) || die("Can't chdir(\"$homedir\"): $!");
unshift(@INC, $homedir);
require "majordomo.pl";
require "shlock.pl";
&ParseMailHeader(STDIN, *hdrs);
$reply_to = &RetMailAddr(*hdrs);
$reply_to = join(", ", &ParseAddrs($reply_to));
die("new-list: $reply_to is not a valid return address.\n")
if (! &valid_addr($reply_to));
$in_reply_to = $hdrs{"message-id"} . ", from " . $hdrs{"from"};
$list = $ARGV[0];
# 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;
$sender = "$list-approval";
$mailcmd = eval qq/"$bounce_mailer"/;
if (defined($isParent = open(MAIL, "|-"))) {
&do_exec_sendmail(split(' ', $mailcmd))
unless $isParent;
} else {
&abort("Failed to fork prior to mailer exec");
}
print MAIL <<"EOM";
To: $reply_to
Cc: $list-approval
From: $list-approval
Subject: Your mail to $list\@$whereami
In-Reply-To: $in_reply_to
Reply-To: $list-approval\@$whereami
This pre-recorded message is being sent in response to your recent
email to $list\@$whereami.
If you were trying to subscribe to the list, please send your request
to $whoami, not to $list\@$whereami.
This is a new list. Your message is being returned unsent, but please
hold on to it. After a few days, when the flood of subscription
requests has died down somewhat, the owner of the list will announce
that the list is "open for business"; you should resubmit your posting
then. This way, everybody who joins the list within the first few days
of its existence starts out on an even footing, and we don't end up
with every other message asking "what did I miss?".
Here's your original, unsent message:
EOM
;
foreach ("From", "To", "Cc", "Subject", "Date", "Message-ID") {
($hdr = $_) =~ tr/A-Z/a-z/;
if (defined($hdrs{$hdr})) {
print MAIL $_, ": ", $hdrs{$hdr}, "\n";
}
}
print MAIL "\n";
while (<STDIN>) {
print MAIL $_;
}
close(MAIL);
exit 0;

View File

@@ -0,0 +1,559 @@
#!/usr/bin/perl -U
# Copyright 1996 MACS, Inc.
# Copyright 1992, D. Brent Chapman. See the Majordomo license agreement
# for usage rights.
#
# $Source: /sources/cvsrepos/majordomo/contrib/sequencer,v $
# $Revision: 1.2 $
# $Date: 1996/12/09 16:50:48 $
# $Author: cwilson $
# $State: Exp $
#
# $Locker: $
#
# sequence - a program for sequencing and archiving e-mail messages
# from majordomo
#
# Based heavily upon the resend script included in the majordomo distribution
# set our path explicitly
$ENV{'PATH'} = "/bin:/usr/bin:/usr/sbin:/sbin";
# What shall we use for temporary files?
$tmp = "/tmp/majordomo.$$";
# Before doing anything else tell the world I am sequencer
# The mj_ prefix is reserved for tools that are part of majordomo proper.
$main'program_name = 'sequencer';
# If the first argument is "@filename", read the real arguments
# from "filename", and shove them onto the ARGV for later processing
# by &Getopts()
if ($ARGV[0] =~ /^@/) {
$fn = shift(@ARGV);
$fn =~ s/^@//;
open(AV, $fn) || die("open(AV, \"$fn\"): $!\nStopped");
undef($/); # set input field separator
$av = <AV>; # read whole file into string
close(AV);
@av = split(/\s+/, $av);
unshift(@ARGV, @av);
$/ = "\n";
}
# Read and execute the .cf file
$cf = $ENV{"MAJORDOMO_CF"} || "/etc/majordomo.cf";
if ($ARGV[0] eq "-C") {
$cf = $ARGV[1];
shift(@ARGV);
shift(@ARGV);
}
if (! -r $cf) {
die("$cf not readable; stopped");
}
require "$cf";
chdir($homedir) || die("Can't chdir(\"$homedir\"): $!");
unshift(@INC, $homedir);
use Getopt::Std;
require "majordomo.pl";
require "majordomo_version.pl";
require "config_parse.pl";
require "shlock.pl";
getopts("Aa:df:h:I:l:m:M:nNp:Rr:s") || die("sequencer: Getopts() failed: $!");
if (! defined($opt_l) || ! defined($opt_h)) {
die("sequencer: must specify both '-l list' and '-h host' arguments");
}
# smash case for the list name
$opt_l =~ tr/A-Z/a-z/;
if ( ! @ARGV) {
die("sequencer: must specify outgoing list as last arg(s)");
}
$opt_r = "$opt_r@$opt_h" if ( defined($opt_r) );
&get_config($listdir, $opt_l);
$opt_A = &cf_ck_bool($opt_l,"moderate") if &cf_ck_bool($opt_l,"moderate");
$opt_h = $config_opts{$opt_l,"resend_host"}
if($config_opts{$opt_l,"resend_host"} ne '');
$opt_a = $config_opts{$opt_l,"approve_passwd"}
if ($config_opts{$opt_l,"approve_passwd"} ne '');
$opt_M = $config_opts{$opt_l,"maxlength"}
if ($config_opts{$opt_l,"maxlength"} ne '');
$opt_f = $config_opts{$opt_l,"sender"}
if ($config_opts{$opt_l,"sender"} ne '');
$opt_p = $config_opts{$opt_l,"precedence"}
if ($config_opts{$opt_l,"precedence"} ne '');
$opt_r = $config_opts{$opt_l,"reply_to"}
if ($config_opts{$opt_l,"reply_to"} ne '');
$opt_I = $config_opts{$opt_l,"restrict_post"}
if ($config_opts{$opt_l,"restrict_post"} ne '');
$opt_R = &cf_ck_bool($opt_l,"purge_received")
if &cf_ck_bool($opt_l,"purge_received");
$opt_s = &cf_ck_bool($opt_l,"administrivia")
if &cf_ck_bool($opt_l,"administrivia");
$opt_d = &cf_ck_bool($opt_l,"debug")
if &cf_ck_bool($opt_l,"debug");
if (defined($opt_f)) {
$sendmail_sender = $opt_f;
} else {
$sendmail_sender = "$opt_l-request";
}
if (defined($opt_a)) {
if ($opt_a =~ /^\//) {
open(PWD, $opt_a) || die("sequencer: open(PWD, \"$opt_a\"): $!");
$opt_a = &chop_nl(<PWD>);
}
}
if (defined($opt_A) && ! defined($opt_a)) {
die("sequencer: must also specify '-a passwd' if using '-A' flag");
}
# code added for getting new sequence number
if (defined($opt_N)) {
$opt_n = $opt_N;
}
if (defined($opt_n)) {
$seqfile = "$listdir/$opt_l.seq";
if (! -r $seqfile) { # if there is no sequence file, make one
open(SEQ, ">$seqfile") || die("sequencer: open of $seqfile failed: $!");
print SEQ "1\n";
close SEQ;
}
&main'lopen(SEQ, "<", "$seqfile") || die("sequencer: locked open of $seqfile failed: $!");
chop($seqnum = <SEQ>);
# note that the sequence file is opened and locked from here until
# the message is sent
}
$sender = "$sendmail_sender@$opt_h";
&open_temp(OUT, "/tmp/sequencer.$$.out") ||
&abort("sequencer:1 Can't open /tmp/sequencer.$$.out: $!");
&open_temp(IN, "/tmp/sequencer.$$.in") ||
&abort("sequencer: Can't open /tmp/sequencer.$$.in: $!");
while (<STDIN>) {
print IN $_;
}
close(IN);
open(IN, "/tmp/sequencer.$$.in") ||
die("sequencer: Can't open /tmp/sequencer.$$.tmp: $!");
do {
$restart = 0;
$pre_hdr = 1;
while (<IN>) {
if ($pre_hdr) {
if (/^\s*$/) {
# skip leading blank lines; usually only there if this is a
# restart after an in-body "Approved:" line
next;
} else {
$pre_hdr = 0;
$in_hdr = 1;
$kept_last = 0;
}
}
if ($in_hdr) {
if (/^\s*$/) {
# end of header; add new header fields
# if there is no subject, create one
if (!defined($subject)) {
local($foo);
if ($config_opts{$opt_l,"subject_prefix"} ne '') {
$foo = &config'substitute_values(
$config_opts{$opt_l,"subject_prefix"}, $opt_l);
# for sequencing we add a special keyword!
if (defined($opt_n)) {
$foo =~ s/\$SEQNUM/$seqnum/;
}
local($foo_pat) = $foo;
$foo_pat =~ s/(\W)/\\$1/g;
if (!/$foo_pat/) {
$foo = $foo . " ";
}
}
$subject = $foo . "Message for " . $opt_l;
print OUT $subject, "\n";
}
print OUT "Sender: $sender\n";
if (defined($opt_p)) {
print OUT "Precedence: $opt_p\n";
}
if (defined($opt_r)) {
print OUT "Reply-To: ", &config'substitute_values($opt_r),
"\n";
}
# print out additonal headers
if ( $config_opts{$opt_l,"message_headers"} ne '' ) {
local($headers) = &config'substitute_values (
$config_opts{$opt_l,"message_headers"}, $opt_l);
$headers =~ s/\001/\n/g;
print OUT $headers;
}
$in_hdr = 0;
print OUT $_;
# print out front matter
if ( $config_opts{$opt_l,"message_fronter"} ne '' ) {
local($fronter) = &config'substitute_values (
$config_opts{$opt_l,"message_fronter"}, $opt_l);
$fronter =~ s/\001|$/\n/g;
print OUT $fronter;
}
} elsif (/^approved:\s*(.*)/i && defined($opt_a)) {
$approved = &chop_nl($1);
if ($approved ne $opt_a &&
!(&main'valid_passwd($listdir, $opt_l, $approved))) {
&bounce("Invalid 'Approved:' header");
}
} elsif (/^from /i # skip all these headers
|| /^sender:/i
|| /^return-receipt-to:/i
|| /^errors-to:/i
|| /^return-path:/i
|| (/^reply-to:/i && defined($opt_r)) # skip only if "-r" set
|| (/^precedence:/i && defined($opt_p)) # skip only if "-p" set
|| (/^received:/i && defined($opt_R)) # skip only if "-R" set
|| (/^\s/ && ! $kept_last) # skip if skipped last
) {
# reset $kept_last in case next line is continuation
$kept_last = 0;
} else {
# check for administrivia requests
if (defined($opt_s) && ! defined($approved)
&& (/^subject:\s*subscribe\b/i ||
/^subject:\s*unsubscribe\b/i ||
/^subject:\s*help\b/i ||
/^subject:\s*RCPT:\b/ ||
/^subject:\s*Delivery Confirmation\b/ ||
/^subject:\s*NON-DELIVERY of:/ ||
/^subject:\s*Undeliverable Message\b/ ||
/^subject:\s*Receipt Confirmation\b/ ||
/^subject:\s*Failed mail\b/ ||
/^subject:\s.*\bchange\b.*\baddress\b/ ||
/^subject:\s*request\b.*\baddition\b/i)) {
&bounce("Admin request");
}
# prepend subject prefix
if ( (/^subject:\s*/i) &&
($config_opts{$opt_l,"subject_prefix"} ne '')
) {
local($foo) = &config'substitute_values(
$config_opts{$opt_l,"subject_prefix"}, $opt_l);
# for sequencing we add a special keyword!
if (defined($opt_n)) {
$foo =~ s/\$SEQNUM/$seqnum/;
}
$subject = $_;
$subject =~ s/^subject:\s*(.*)/$1/i;
$subject = &chop_nl($foo . " " . $subject);
local($foo_pat) = $foo;
$foo_pat =~ s/(\W)/\\$1/g;
s/^subject:\s*/Subject: $foo /i if !/$foo_pat/;
}
if ( /^from:\s*(.+)/i )
{
$from = $1;
$from_last = 1;
}
elsif ( defined($from_last) )
{
if ( /^\s+(.+)/ )
{
$from .= " $1";
}
else
{
undef($from_last);
}
}
&check_hdr_line($_); # check for length & balance
$kept_last = 1;
print OUT $_;
}
} else {
# this isn't a header line, so print it (maybe)
# first, though, is the first line of the body an "Approved:" line?
if (($body_len == 0) && /^approved:\s*(.*)/i && defined($opt_a)) {
# OK, is it a valid "Approved:" line?
$approved = &chop_nl($1);
if ($approved ne $opt_a &&
!(&main'valid_passwd($listdir, $opt_l, $approved))) {
&bounce("Invalid 'Approved:' header");
} else {
# Yes, it's a valid "Approved:" line...
# So, we start over
$restart = 1;
close(OUT);
unlink("/tmp/sequencer.$$.out");
&open_temp(OUT, "/tmp/sequencer.$$.out") ||
&abort("sequencer:2 Can't open /tmp/sequencer.$$.out: $!");
last;
}
}
$body_len += length($_);
# make sure it doesn't make the message too long
if (defined($opt_M) && ! defined($approved)
&& ($body_len > $opt_M)) {
&bounce("Message too long (>$opt_M)");
}
# add admin-request recognition heuristics here... (body)
if (defined($opt_s) && ! defined($approved) && ($body_line++ < 5) && (
/\badd me\b/i
|| /\bdelete me\b/i || /\bremove\s+me\b/i
|| /\bchange\b.*\baddress\b/
|| /\bsubscribe\b/i || /^sub\b/i
|| /\bunsubscribe\b/i || /^unsub\b/i
|| /^\s*help\s*$/i # help
|| /^\s*info\s*$/i # info
|| /^\s*info\s+\S+\s*$/i # info list
|| /^\s*lists\s*$/i # lists
|| /^\s*which\s*$/i # which
|| /^\s*which\s+\S+\s*$/i # which address
|| /^\s*index\s*$/i # index
|| /^\s*index\s+\S+\s*$/i # index list
|| /^\s*who\s*$/i # who
|| /^\s*who\s+\S+\s*$/i # who list
|| /^\s*get\s+\S+\s*$/i # get file
|| /^\s*get\s+\S+\s+\S+\s*$/i # get list file
|| /^\s*approve\b/i
|| /^\s*passwd\b/i
|| /^\s*newinfo\b/i
|| /^\s*config\b/i
|| /^\s*newconfig\b/i
|| /^\s*writeconfig\b/i
|| /^\s*mkdigest\b/i
)) {
&bounce("Admin request");
}
print OUT $_;
}
}
} while ($restart);
if ( $config_opts{$opt_l,"message_footer"} ne '' ) {
local($footer) = &config'substitute_values(
$config_opts{$opt_l,"message_footer"}, $opt_l);
$footer =~ s/\001/\n/g;
print OUT $footer;
}
close(OUT);
if ( defined($opt_I) && defined($from) && ! defined($approved) ) {
local($infile) = 0;
@files = split (/[:\t\n]+/, $opt_I);
foreach $file (@files) {
if ($file !~ /^\//) {
$file = "$listdir/$file";
}
if ( open (LISTFD, "<${file}") != 0 ) {
@output = grep (&addr_match($from, $_), <LISTFD>);
close (LISTFD);
if ( $#output != -1 ) {
$infile = 1;
last;
}
} else {
die("sequencer:Can't open $file: $!");
}
}
if ( $infile == 0 ) {
&bounce ("Non-member submission from [$from]");
}
}
if (defined($opt_A) && ! defined($approved)) {
&bounce("Approval required");
}
$sendmail_cmd = "/usr/lib/sendmail $opt_m -f$sendmail_sender " .
join(" ", @ARGV);
if (defined($opt_d)) {
$| = 1;
print "Command: $sendmail_cmd\n";
$status = (system("cat /tmp/sequencer.$$.out") >> 8);
unlink(</tmp/sequencer.$$.*>);
#remember to unlock the sequence file here!
if (defined($opt_n)) {
&main'lclose(SEQ);
}
exit($status);
} else {
local(*MAILOUT, *MAILIN, @mailer);
@mailer = split(' ', "$sendmail_cmd");
open(MAILOUT, "|-") || &do_exec_sendmail(@mailer);
# create archival copy
if (defined($opt_N)) {
if (open (INDEX, ">>$filedir/$opt_l$filedir_suffix/INDEX")) {
$timenow = localtime(time);
printf(INDEX "%s\n\tFrom %s on %s\n", $subject, $from, $timenow);
close (INDEX);
}
open (ARCHIVE, ">$filedir/$opt_l$filedir_suffix/$seqnum");
}
open(MAILIN, "/tmp/sequencer.$$.out");
while (<MAILIN>) {
print MAILOUT $_;
if (defined($opt_N)) {
print ARCHIVE $_;
}
}
close(MAILOUT);
if (defined($opt_N)) {
close(ARCHIVE);
}
if (defined($opt_n)) {
$seqnum++;
&main'lreopen(SEQ, ">", "$seqfile");
print SEQ $seqnum, "\n";
&main'lclose(SEQ);
}
close(MAILIN);
unlink(</tmp/sequencer.$$.*>);
exit(0);
}
sub check_balance {
# set a temporary variable
local($t) = shift;
# strip out all nested parentheses
1 while $t =~ s/\([^\(\)]*\)//g;
# strip out all nested angle brackets
1 while $t =~ s/\<[^\<\>]*\>//g;
# if any parentheses or angle brackets remain, were imbalanced
if ($t =~ /[\(\)\<\>]/ && ! defined($approved)) {
&bounce("Imbalanced parentheses or angle brackets");
return(undef);
}
return(1);
}
sub check_hdr_line {
local($_) = shift;
if (! /^\s/) { # is this a continuation line?
# Not a continuation line.
# If $balanced_fld is defined, it means the last field was one
# that needed to have balanced "()" and "<>" (i.e., "To:", "From:",
# and "Cc:", so check it. We do it here in case the last field was
# multi-line.
if (defined($balanced_fld)) {
&check_balance($balanced_fld);
}
# we undefine $balanced_fld and reset $field_len; these may be set below
undef($balanced_fld);
$field_len = 0;
}
# is this a field that must be checked for balanced "()" and "<>"?
if (defined($balanced_fld) || /^from:/i || /^cc:/i || /^to:/i) {
# yes it is, but we can't check it yet because there might be
# continuation lines. Buffer it to be checked at the beginning
# of the next non-continuation line.
# is this line too long?
if ((length($_) > 128) && ! defined($approved)) {
&bounce("Header line too long (>128)");
return(undef);
}
# is this field too long?
if ((($field_len += length($_)) > 1024) && ! defined($approved)) {
&bounce("Header field too long (>1024)");
return(undef);
}
$balanced_fld .= $_;
chop($balanced_fld);
}
# if we get here, everything was OK.
return(1);
}
sub bounce {
local($reason) = shift;
local($_);
&resend_sendmail(BOUNCE, $sender, "BOUNCE $opt_l@$opt_h: $reason");
seek(IN, 0, 0);
while (<IN>) {
print BOUNCE $_;
}
close(BOUNCE);
unlink(</tmp/sequencer.$$.*>);
exit(0);
}
sub resend_sendmail {
local(*MAIL) = shift;
local($to) = shift;
local($subject) = shift;
# clean up the addresses, for use on the sendmail command line
local(@to) = &ParseAddrs($to);
for (@to) {
$_ = join(", ", &ParseAddrs($_));
}
$to = join(", ", @to);
# open the process
if (defined($opt_d)) {
# debugging, so just say it, don't do it
open(MAIL, ">-");
print MAIL ">>> /usr/lib/sendmail -f$sendmail_sender -t\n";
} else {
local(@mailer) = split(' ',"/usr/lib/sendmail -f$sendmail_sender -t");
open(MAIL, "|-") || &do_exec_sendmail(@mailer);
}
# generate the header
print MAIL <<"EOM";
To: $to
From: $sender
Subject: $subject
EOM
return;
}