Great Circle Associates Majordomo-Users
(June 1994)
 

Indexed By Date: [Previous] [Next] Indexed By Thread: [Previous] [Next]

Subject: Autobounce program
From: Alan Millar <amillar @ bolis . sf-bay . org>
Date: Thu, 30 Jun 1994 00:56:02 -0800 (PDT)
To: majordomo-users @ greatcircle . com
Reply-to: Alan Millar <AMillar @ bolis . sf-bay . org>

Here it is!  My autobounce program for Majordomo 1.62

There are two files, "autobounce" and "autobounce.cf".  Both go
in the majordomo directory.

autobounce:
====================================================================
#!/usr/local/bin/perl

# == Autobounce ==
# (C) copyright 1994 by Alan Millar.  This program may be freely
#  copied, but may not be sold for profit.  No warranty, etc.

# Move problem addresses from mailing list to bounces list.
# Done by interpreting bounce e-mail messages sent in reply
#  to a list posting

# Interpretation is done based on a table of possible incoming message
#  formats.  The table is made of six assoc. arrays, which contain
#  patterns which must be matched to extract a bounced address from
#  the message.  Things to match are the sender, the subject line,
#  a starting and ending line of the bounce section of the message,
#  and right and left patterns surrounding the bounced address.

# When a valid address is found, a message is sent to Majordomo
#  with the appropriate commands.
# 
# 	approve passwd unsubscribe this-list user@fubar.com
# 	approve passwd subscribe bounces user@fubar.com (930401 this-list)
#
# All subscription/unsubscription is done by mail to Majordomo,
#  to keep the interface open-ended.

# Typical use: create alias which pipes into this program, and send
#  out mail from this alias address, so that bounce replies come
#  back here.  Any unrecognized message is quietly ignored.

# This program is actually rather safe.  It will only remove an
#  address (and add to bounces list) if that address is a list
#  member.  So spurious addresses or garbage that accidentally
#  slips through will not be added to the bounces list.
#  In these cases, it does a Majordomo "which" command just to
#  be helpful.

# Parameters:
#  -l listname		list to remove from
#  -f address		approval address; defaults to listname-approval
#  -b bouncelist	list to add to; defaults to "bounces"
#  -n			if defined, don't add to bounces list
#  -d [mailer]		print debug messages, optionally trace specific mailer

# Written by Alan Millar  June 14 1994.  
#  inspired by "bounce" by Brent Chapman

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

# What shall we use for temporary files?
$tmp = "/tmp/majordomo.$$";

# 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) {
    &abort("$cf not readable; stopped");
}
eval(`cat $cf`);

chdir($homedir) || &abort("Can't chdir(\"$homedir\"): $!");
unshift(@INC, $homedir);
require "majordomo.pl";
require "shlock.pl";

require "getopts.pl";

&Getopts("l:f:b:d:n") || &abort("autobounce: Getopts(): $!");


#-----------------------------
# Get name of list we are processing bounces for
# List name should be -l.  Try looking at first arg if no -l spec'd.
if (defined($opt_l)) {
  $list = $opt_l;
} else {
  $list = $ARGV[0];
}

$list = &valid_list($listdir,$list);
if (! $list ) {
    &abort("autobounce: no valid list specified");
}


#-----------------------------
# approval address for requests.  Majordomo will send reply here.
if (defined($opt_f)) {
    $approval = $opt_f;
} else {
    $approval = $list . "-approval@" . $whereami;
}

#-----------------------------
# If option -n is specified, address should not be added to bounces list.
$add_bounces = (! defined($opt_n));

#-----------------------------
if (defined($opt_b)) {
  $bounce_list = $opt_b;
} else {
  $bounce_list = "bounces";
}
$bounce_list = &valid_list($listdir,$bounce_list);
if (! $bounce_list && $add_bounces ) {
    &main'abort("autobounce: no valid bounce list specified:" .
       " $bounce_list $opt_b");
}

#--------------------------------
# pick up table of bounce message profiles.

do 'autobounce.cf';

#-----------------------------------
# grab current time
local($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime;

#--------------------------
# Get password for list to unsubscribe from
open (PASSWD, "<$listdir/$list.passwd")
  || &abort ("Can't read password file $listdir/$list.passwd\n");

$list_passwd = &chop_nl(<PASSWD>);
if (! $list_passwd) {
    &abort("no password for list $list; stopping");
}
close (PASSWD);

#--------------------------
# Get password for bounces list to subscribe to.
if ($add_bounces) {
  open (PASSWD, "<$listdir/$bounce_list.passwd")
    || &abort ("Can't read password file $listdir/$bounce_list.passwd\n");

  $bounce_passwd = &chop_nl(<PASSWD>);
  if (! $bounce_passwd) {
      &abort("no password for list $bounce_list; stopping");
  }
  close (PASSWD);
} # if not add_bounces

#-----------------------------------------
# read headers from incoming message
#-----------------------------------------

&ParseMailHeader(STDIN, *hdrs);

#--------------------------------
# Try to determine sending domain
#  Many bounce messages only give local user name.  Try appending
#  domain of sending mailer as fall-back
($from_address,@junk) = &ParseAddrs($hdrs{"from"});
if ($from_address =~ /@/) {
  # address is @domain
  $sending_domain = $from_address;
  $sending_domain =~ s/^.*@//;
  if ($opt_d) {printf "@domain=%s\n", $sending_domain; }
} elsif ($from_address =~ /!/ ) {
  # try parsing bang path
  #  strip last item, should be user name
  $sending_domain = $from_address;
  $sending_domain =~ s/![^!]+$//;
  #  strip any leading items
  $sending_domain =~ s/^.*!//;
  if ($opt_d) {printf "domain!=%s\n", $sending_domain; }
} else {
  $sending_domain = "";
} # if @ domain

#---
# initialize message section state
foreach $bouncer (keys %bounce_from) {
  $section_ok{$bouncer} = 0;
} # foreach

#-----------------------------------------
# read message body, looking for bounce section
#-----------------------------------------

while ($line = <STDIN> ) {

  $line = &chop_nl($line);

  if ($opt_d) {print "$opt_d=$section_ok{$opt_d} $line\n"; }

  # on each line, check to see if it matches profile of any known
  #  bouncer in table.
  foreach $bouncer (keys %bounce_from) {

    # first check from: and subject: to see if they match
    if ($hdrs{"from"} =~ /$bounce_from{$bouncer}/i &&
        $hdrs{"subject"} =~ /$bounce_subject{$bouncer}/i ) {
      # then check line contents
      if ($line =~ /$bounce_startline{$bouncer}/i) {
	$section_ok{$bouncer} = 1;
      } elsif ($line =~ /$bounce_endline{$bouncer}/i) {
	$section_ok{$bouncer} = 0;
      } elsif ( $section_ok{$bouncer} &&
		$line =~ /$bounce_left{$bouncer}/i &&
		$line =~ /$bounce_right{$bouncer}/i) {
	#Line matches left/right patterns
	$address = $line;
	if ($opt_d) {print "$bouncer line= $address\n"; }
	#strip left part preceding address
	$address =~ s/$bounce_left{$bouncer}//i;
	if ($opt_d) {print "$bouncer L= $address\n"; }
	#strip right part following address
	$address =~ s/$bounce_right{$bouncer}//i;
	if ($opt_d) {print "$bouncer R= $address\n"; }

	# strip any comments or brackets from address
	($newaddr,@junk) = &ParseAddrs($address);

	$rest_of_line = $line;
	$rest_of_line =~ s/$newaddr//i;  # take address out of rest of line
	$rest_of_line =~ s/[^a-zA-z0-9]+/ /g; # strip garbage
	if ($rest_of_line !~ /^ *$/) {$rest_of_line = " - " . $rest_of_line;}
	
	# Add address to list of addresses needing to be bounced.
	$newaddr =~ tr/A-Z/a-z/;
	if ($newaddr !~ /^ *$/) {
	  $bounce_address{$newaddr} = $rest_of_line;
	} # if non-blank

	# remember which profile matched message.
	$mailer_found = $bouncer;

      } # if line matches
    } # if subject and sender
  } # foreach $bouncer
} # while STDIN

#----------------------------
# Done reading message.  

# Only do the rest if we found some addresses.

if (scalar(keys %bounce_address) >= 1) {

  # We have some addresses.  Create message to Majordomo.
  $to = $whoami;
  $sender = $approval;

  if (defined($opt_d)) {
      open(MSG, ">&STDOUT");
  } else {
      eval "\$prog=\"$mailer\"";
      open(MSG, "|$prog") || 
   &abort("open(MSG, \"|$prog\"): $!\nStopped");
  }

  print MSG "To: $to\n";
  print MSG "From: $sender\n";
  print MSG "Subject: autobounce request for list $list\n";
  print MSG "\n\n";

  foreach $address (keys %bounce_address) {

    # Put in comments on what we're doing.
    printf MSG "# Autobounce request\n";
    printf MSG "# Bouncing address : %s\n", $address;
    printf MSG "#  From mailer     : %s\n", $hdrs{"from"};
    printf MSG "#  Mailer type     : %s\n", $mailer_found;
    printf MSG "#  On Date         : %s\n", $hdrs{"date"};
    printf MSG "#  Reason          : %s\n", $bounce_address{$address};

    # If we extracted any addresses, make sure they are on the list.
    #  if not, don't do subscribe/unsub.

    if (&is_list_member($address,$listdir,$list) > 0) {
      $final_address = $address;
    } else {
      # address wasn't on list, try appending sending domain
      $final_address = $address . "@" . $sending_domain;
      if (! &is_list_member($final_address,$listdir,$list) > 0) {
	$final_address = "";
      } # if not list member
    } # if list member

    if ($final_address ne "" ) {
      printf MSG "approve %s unsubscribe %s %s\n", 
	  $list_passwd, $list, $final_address;
      if ($add_bounces) {
        printf MSG "approve %s subscribe %s %s (%02d%02d%02d %s%s)\n",
	  $bounce_passwd, $bounce_list, $final_address, 
	  $year, $mon+1, $mday, $list,
	  $bounce_address{$address};
      } # if add_bounces
    } else {
      print MSG "which $address\n";
    } # if final_address not blank

  } # foreach address

  print MSG "# autobounce by Alan Millar <amillar@bolis.sf-bay.org>";

  close(MSG);

} # if any addresses found

exit (0);

====================================================================


autobounce.cf:
====================================================================
#-------------------------------------
# NOTE: Make sure the bounce_left pattern starts with ^ and
#   bounce_right ends with $ anchors!
#
# Back-quoted metacharacters need a double backslash.
#-------------------------------------
# Most versions of sendmail
$bounce_from{"sendmail"}	= "mail delivery subsystem";
$bounce_subject{"sendmail"}	= "returned mail";
$bounce_startline{"sendmail"}	= "^ *-+ *transcript of session";
$bounce_endline{"sendmail"}	= "^ *-+ *unsent message";
$bounce_left{"sendmail"}	= "^ *5[0-9]+ *";
$bounce_right{"sendmail"}	= "[.][.][.].*$";
#-------------------------------------
# Microsoft Mail for PC networks
$bounce_from{"ms-mail"}		= "admin";
$bounce_subject{"ms-mail"}	= "mail failure";
$bounce_startline{"ms-mail"}	= "following unknown address";
$bounce_endline{"ms-mail"}	= "---+";
$bounce_left{"ms-mail"}		= "^ *[a-z0-9/]+/";
$bounce_right{"ms-mail"}	= " *$";
#-------------------------------------
# SMail 3
$bounce_from{"smail3"}		= "mailer-daemon";
$bounce_subject{"smail3"}	= "mail failed";
$bounce_startline{"smail3"}	= "failed addresses follow";
$bounce_endline{"smail3"}	= "message text follow";
$bounce_left{"smail3"}		= "^ *";
$bounce_right{"smail3"}		= "[.][.][.].*$";
#-------------------------------------
# LMail
$bounce_from{"lmail"}		= "lmail";
$bounce_subject{"lmail"}	= "undeliver";
$bounce_startline{"lmail"}	= "error description";
$bounce_endline{"lmail"}	= "^ *error-end:";
$bounce_left{"lmail"}		= "^ *error-for: *";
$bounce_right{"lmail"}		= " *$";
#-------------------------------------
# PMDF
$bounce_from{"pmdf"}		= "pmdf";
$bounce_subject{"pmdf"}		= "undeliver";
$bounce_startline{"pmdf"}	= "could not be delivered";
$bounce_endline{"pmdf"}		= "^ *reason:";
$bounce_left{"pmdf"}		= "^ *addressee: *";
$bounce_right{"pmdf"}		= " *$";
#-------------------------------------
# IBM VM Batch SMTP mailer 
$bounce_from{"ibm-bsmtp"}	= "network mailer";
$bounce_subject{"ibm-bsmtp"}	= "delivery";
$bounce_startline{"ibm-bsmtp"}	= "transaction log follows";
$bounce_endline{"ibm-bsmtp"}	= "message follows";
$bounce_left{"ibm-bsmtp"}	= "^5[0-9]+.*: *";
$bounce_right{"ibm-bsmtp"}	= " *$";
#-------------------------------------
# America On-Line service
$bounce_from{"aol"}		= "aol.com";
$bounce_subject{"aol"}		= "returned mail";
$bounce_startline{"aol"}	= "could not be delivered";
$bounce_endline{"aol"}		= "text you sent follows";
$bounce_left{"aol"}		= "^.*\\(";
$bounce_right{"aol"}		= "\\).*$";
#-------------------------------------
# MCI Mail service
$bounce_from{"mcimail"}		= "mcimail.com";
$bounce_subject{"mcimail"}	= "rejected";
$bounce_startline{"mcimail"}	= "could not be forwarded";
$bounce_endline{"mcimail"}	= "your message follows";
$bounce_left{"mcimail"}		= "^ *to: *";
$bounce_right{"mcimail"}	= " *$";
#-------------------------------------
# CompuServe users
$bounce_from{"compuserve"}	= "compuserve.com";
$bounce_subject{"compuserve"}	= "undeliverable";
$bounce_startline{"compuserve"}	= "\\*\\*\\*\\*\\*";
$bounce_endline{"compuserve"}	= "could not process";
$bounce_left{"compuserve"}	= "^\\?.*: *";
$bounce_right{"compuserve"}	= " *$";
#-------------------------------------
====================================================================


- Alan

----
Alan Millar          E-Mail: amillar@bolis.SF-Bay.org
System Administrator    Web: ftp://ftp.netcom.com/pub/amillar/welcome.html
I can't give you brains, but I can give you a diploma -Wizard of Oz to Scarecrow


Indexed By Date Previous: Re: Help in change sender address
From: Alan Millar <amillar@bolis.sf-bay.org>
Next: Re: autobounce program
From: Alan Millar <amillar@bolis.sf-bay.org>
Indexed By Thread Previous: Re: Bad Entries in List
From: David Barr <barr@pop.psu.edu>
Next: Re: autobounce program
From: Alan Millar <amillar@bolis.sf-bay.org>

Google
 
Search Internet Search www.greatcircle.com