jystewart.net : reading, writing, web development

services | portfolio | code | blog | about | contact

Code

Mailman Interface Module

Language: Perl
Date: July 2004

In the past couple of years the python-based mailing list manager mailman has become the list manager of choice. Its simple interface allows for easy administration of large lists and its increasing familiarity only helps it build on its success.

A number of websites I run utilise mailman but in several cases the web hosting provider does not permit access to the utilities that can normally be used to interface a program with mailman. As a result I was having to have subscriptions emailed to me and then manually enter them into the mailman databases. The time came for some automation.

The two modules below allow for that automation. When invoked in a perl script the main module initiates a simple web browsing robot that takes a list of email addresses and attempts to subscribe them. The accompanying module is then invoked to interpret the HTML results page. The process is relatively simple, but the modules have saved a lot of time!

The Code:

MailmanAction.pm

package MailmanAction;
use LWP::UserAgent;
use MailmanResultParser;
use Emai::Valud;

my $ua = LWP::UserAgent->new;
$ua->agent("MailmanManager/0.1");
my ($uri,$res,$password);

sub new {
	# Initiate the class. Takes a base URI for the mailman install and a password as input
	$class = shift;
	$uri = shift;
	$password = shift;
	my $self = bless {}, $class;
	return $self;
}

sub add_members {
	# Takes an array of classes and an output mode. We support $output = 'html' or
	# $output !='html
	$self = shift;
	@addresses = shift;
	$output = shift;

	my $req = HTTP::Request->new(GET => $uri."/members/add?adminpw=$password&subscribe_or_invite=0&send_welcome_msg_to_this_batch=1&send_notifications_to_list_owner=0&subscribees=".join("\n",@addresses));
	$res = $ua->request($req);
	if ($res->is_success) {
		my $parser = new MailmanResultParser;
		$parser->parse($res->content);

		if ($output eq 'html') {
			return $self->addition_html($parser);
		} else {
			return 1;
		}
	} else {
		return 0;
	}
}

sub addition_html {
	# If the user has asked for html output, this produces it.
	$self = shift;
	my $parser = shift;

	if ($parser->return_successes()) {
		$result .= "<p>The following were successfully subscribed:</p>\n\n<ul>\n<li>\n";
		$result .= join("</li>\n<li>",split(",",$parser->return_successes()))."</li>\n</ul>";
	}
	if ($parser->return_failures()) {
		$result .= "\n\n<p>The following returned errors:</p>\n\n<ul>\n<li>\n";
		$result .= join("</li>\n<li>",split(",",$parser->return_failures()))."</li>\n</ul>";
	}
	return $result;
}
1;

MailmanResultParser.pm:

package MailmanResultParser;
use base 'HTML::Parser';

my $ready = 0;
my @success;
my @failures;

sub return_failures() {
	# returns a string containing all email addresses that didn't work
	return join("\n",@failures);
}

sub return_successes() {
	# returns a string containing all email addresses that worked
	return join(", ",@success);
}

sub start () {
	# This searches through for the appropriate starting point in the file
	
	my($self,$tag,$attr,$attrseq,$orig) = @_;

	if ($tag eq 'h5') {
		$ready = 1;
	}
	
	if ($tag eq 'ul' && $ready > 1) {
		$ready++;
	}
}

sub text () {
	# This is the core. It extracts what we need.
	
	my ($self,$text) = @_;
	
	if ($ready == 3) {
		chomp $text;
		unless ($text =~ /^\s*$/) { push @failures, $text; }
	}
	
	if ($ready == 5) {
		if ($text =~ /@/) { push @success, $text; }
	}
	
	if ($ready == 1 && $text =~ 'Successfully subscribed:') {
		$ready = 4;
	}
	
	if ($ready == 1 && $text =~ 'Error subscribing:') {
		$ready++;
	}

}

sub end () {
	# Once we're past all results, we want to stop parsing so set $ready back to 0
	my  ($self,$tag) = @_;
	if ($ready == 3 && $tag eq 'ul') {
		$ready = 0;
	}
}

1;