From cdb3b5771de51aa0334043ef3b14fb06ad1a89b4 Mon Sep 17 00:00:00 2001 From: lars Date: Mon, 23 Jan 2006 21:40:12 +0000 Subject: [PATCH] new release: v0.07.1 --- Ezmlm/tags/Ezmlm-0.07.1/Changes | 46 + Ezmlm/tags/Ezmlm-0.07.1/Ezmlm.pm | 1214 +++++++++++++++++ Ezmlm/tags/Ezmlm-0.07.1/MANIFEST | 7 + Ezmlm/tags/Ezmlm-0.07.1/META.yml | 10 + Ezmlm/tags/Ezmlm-0.07.1/Makefile.PL | 131 ++ Ezmlm/tags/Ezmlm-0.07.1/README | 21 + Ezmlm/tags/Ezmlm-0.07.1/test.pl | 234 ++++ Ezmlm/tags/packages/Ezmlm-0.07.1.tar.gz | Bin 0 -> 15417 bytes .../libemail-ezmlm-perl_0.07.1-1_all.deb | Bin 0 -> 22594 bytes 9 files changed, 1663 insertions(+) create mode 100644 Ezmlm/tags/Ezmlm-0.07.1/Changes create mode 100644 Ezmlm/tags/Ezmlm-0.07.1/Ezmlm.pm create mode 100644 Ezmlm/tags/Ezmlm-0.07.1/MANIFEST create mode 100644 Ezmlm/tags/Ezmlm-0.07.1/META.yml create mode 100644 Ezmlm/tags/Ezmlm-0.07.1/Makefile.PL create mode 100644 Ezmlm/tags/Ezmlm-0.07.1/README create mode 100644 Ezmlm/tags/Ezmlm-0.07.1/test.pl create mode 100644 Ezmlm/tags/packages/Ezmlm-0.07.1.tar.gz create mode 100644 Ezmlm/tags/packages/libemail-ezmlm-perl_0.07.1-1_all.deb diff --git a/Ezmlm/tags/Ezmlm-0.07.1/Changes b/Ezmlm/tags/Ezmlm-0.07.1/Changes new file mode 100644 index 0000000..be2fcde --- /dev/null +++ b/Ezmlm/tags/Ezmlm-0.07.1/Changes @@ -0,0 +1,46 @@ +Revision history for Perl extension Mail::Ezmlm. + +0.01 Sun Oct 31 12:58:16 1999 + - original version; created by h2xs 1.1.1.1.2.1 + +0.02 Wed Jan 26 07:59:10 SAST 2000 + - Added functions to check various options + (ismodsub, ismodpost, isremote, isdeny, isallow, isdigest) + - Allowed sub, unsub, list, subscribers, issub to work with list subparts + (ie, the allow, deny, mod, digest sub directories) + - Changed system() calls to safer ones (ie command, switches) + - Made error handling better (errmsg() and errno()) + - Added support for creating MySQL tables via ezmlm-mktab + +0.03 Mon Sep 25 11:49:26 SAST 2000 + - fixed the issub() function + - fixed the problem with dashes in hostnames. + - hopefully got rid of some of the warnings from sub() and unsub() + +0.04 Mon May 26 18:15:38 SAST 2003 + - fixed return value of Makefile.PL (Andrew Pam ) + - fixed issub() (again) to handle parts properly (bug 602; moguo@servism.com) + - converted module global variables to instance variables + +0.05 Sat Mar 5 12:47:10 SAST 2005 + - fixed forced scalar return in subscribers() (Jon Coulter ) + - fixed handling of dashes in hostnames (bug 5571; Lars Braeuer ) + - fixed some tainting problems (Scott Beck and Matt Simerson ) + - fixed order of control/defaulthost and control/me (bug 1515) + - fixed a bug in Makefile.PL (bug 11771). does not affect most users, so released as 0.05.1 + +0.06 Mon Dec 26 18:55:12 CET 2005 + - support for ezmlm-idx-5.0.0 added + - fixed version check + +0.07 Mon Jan 2 22:12:32 CET 2006 + - new functions for text management (idx >= 5.0) + - new functions for language setting (idx >= 5.0) + - new functions for charset setting (idx >= 5.0) + - new functions for config directory setting (idx >= 5.0) + - look for ezmlm-make at run-time + - requires Text::ParseWords + +0.07.1 Mon Jan 23 22:30:14 CET 2006 + - fix misinterpretation of empty settings + diff --git a/Ezmlm/tags/Ezmlm-0.07.1/Ezmlm.pm b/Ezmlm/tags/Ezmlm-0.07.1/Ezmlm.pm new file mode 100644 index 0000000..c3baf51 --- /dev/null +++ b/Ezmlm/tags/Ezmlm-0.07.1/Ezmlm.pm @@ -0,0 +1,1214 @@ +# =========================================================================== +# Ezmlm.pm - version 0.04 - 26/05/2003 +# $Id: Ezmlm.pm,v 1.10 2005/03/05 14:11:11 guy Exp $ +# +# Object methods for ezmlm mailing lists +# +# Copyright (C) 1999-2005, Guy Antony Halse, All Rights Reserved. +# Please send bug reports and comments to guy@rucus.ru.ac.za +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# +# Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# Neither name Guy Antony Halse nor the names of any contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS +# IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# +# ========================================================================== +# POD is at the end of this file. Search for '=head' to find it +package Mail::Ezmlm; + +use strict; +use vars qw($QMAIL_BASE $EZMLM_BASE $MYSQL_BASE $VERSION @ISA @EXPORT @EXPORT_OK); +use Carp; +use Text::ParseWords; + +require Exporter; + +@ISA = qw(Exporter); +# Items to export into callers namespace by default. Note: do not export +# names by default without a very good reason. Use EXPORT_OK instead. +# Do not simply export all your public functions/methods/constants. +@EXPORT = qw( + +); +$VERSION = '0.07'; + +require 5.005; + +# == Begin site dependant variables == +$EZMLM_BASE = '/usr/local/bin'; #Autoinserted by Makefile.PL +$QMAIL_BASE = '/var/qmail'; #Autoinserted by Makefile.PL +$MYSQL_BASE = ''; #Autoinserted by Makefile.PL +# == End site dependant variables == + +# == check the ezmlm-make path == +$EZMLM_BASE = '/usr/local/bin/ezmlm' unless (-e "$EZMLM_BASE/ezmlm-make"); +$EZMLM_BASE = '/usr/local/bin/ezmlm-idx' unless (-e "$EZMLM_BASE/ezmlm-make"); +$EZMLM_BASE = '/usr/local/bin' unless (-e "$EZMLM_BASE/ezmlm-make"); +$EZMLM_BASE = '/usr/bin/ezmlm' unless (-e "$EZMLM_BASE/ezmlm-make"); +$EZMLM_BASE = '/usr/bin/ezmlm-idx' unless (-e "$EZMLM_BASE/ezmlm-make"); +$EZMLM_BASE = '/usr/bin' unless (-e "$EZMLM_BASE/ezmlm-make"); + +# == clean up the path for taint checking == +local $ENV{'PATH'} = $EZMLM_BASE; + +# == Initialiser - Returns a reference to the object == +sub new { + my($class, $list) = @_; + my $self = {}; + bless $self, ref $class || $class || 'Mail::Ezmlm'; + $self->setlist($list) if(defined($list) && $list); + return $self; +} + +# == Make a new mailing list and set it to current == +sub make { + my($self, %list) = @_; + my($VHOST, $comandline, $hostname); + + # Do we want to use command line switches + my $commandline = ''; + $commandline = '-' . $list{'-switches'} if(defined($list{'-switches'})); + my @commandline; + # UGLY! + foreach (split(/["'](.+?)["']|(\s-\w+)/, $commandline)) { + next if (!defined($_) or !$_ or $_ eq ' '); + push @commandline, $_; + } + + # These three variables are essential + ($self->_seterror(-1, 'must define -dir in a make()') && return 0) unless(defined($list{'-dir'})); + ($self->_seterror(-1, 'must define -qmail in a make()') && return 0) unless(defined($list{'-qmail'})); + ($self->_seterror(-1, 'must define -name in a make()') && return 0) unless(defined($list{'-name'})); + + # Determine hostname if it is not supplied + $hostname = $self->_getdefaultdomain; + if(defined($list{'-host'})) { + $VHOST = 1 unless ($list{'-host'} eq $hostname); + } else { + $list{'-host'} = $hostname; + } + + # Attempt to make the list if we can. + unless(-e $list{'-dir'}) { + system("$EZMLM_BASE/ezmlm-make", @commandline, $list{'-dir'}, $list{'-qmail'}, $list{'-name'}, $list{'-host'}) == 0 + || ($self->_seterror($?) && return undef); + } else { + ($self->_seterror(-1, '-dir must be defined in make()') && return 0); + } + + # Sort out the DIR/inlocal problem if necessary + if(defined($VHOST)) { + unless(defined($list{'-user'})) { + ($self->_seterror(-1, '-user must match virtual host user in make()') && return 0) unless($list{'-user'} = $self->_getvhostuser($list{'-host'})); + } + + open(INLOCAL, ">$list{'-dir'}/inlocal") || ($self->_seterror(-1, 'unable to read inlocal in make()') && return 0); + print INLOCAL $list{'-user'} . '-' . $list{'-name'} . "\n"; + close INLOCAL; + } + + $self->_seterror(undef); + return $self->setlist($list{'-dir'}); +} + +# == Update the current list == +sub update { + my($self, $switches) = @_; + my($outhost, $inlocal); + + # Do we have the command line switches + ($self->_seterror(-1, 'nothing to update()') && return 0) unless(defined($switches)); + $switches = '-e' . $switches; + my @switch_list; + + # UGLY! + #foreach (split(/["'](.+?)["']|(-\w+)/, $switches)) { + # next if (!defined($_)); + # # untaint input + # $_ =~ m/^([\w _\/,\.\@:'"-]*)$/; + # push @switches, $1; + #} + foreach ("ewords('\s+', 1, $switches)) { + next if (!defined($_)); + # untaint input + $_ =~ s/['"]//g; + $_ =~ m/^([\w _\/,\.\@:'"-]*)$/; + if ($_ eq '') { + push @switch_list, ""; + } else { + push @switch_list, $1; + } + } + + # can we actually alter this list; + ($self->_seterror(-1, 'must setlist() before you update()') && return 0) unless(defined($self->{'LIST_NAME'})); + # check for important files: 'config' (idx < v5.0) or 'flags' (idx >= 5.0) + ($self->_seterror(-1, "$self->{'LIST_NAME'} does not appear to be a valid list in update()") && return 0) unless((-e "$self->{'LIST_NAME'}/config") || (-e "$self->{'LIST_NAME'}/flags")); + + # Work out if this is a vhost. + open(OUTHOST, "<$self->{'LIST_NAME'}/outhost") || ($self->_seterror(-1, 'unable to read outhost in update()') && return 0); + chomp($outhost = ); + close(OUTHOST); + + # Save the contents of inlocal if it is a vhost + unless($outhost eq $self->_getdefaultdomain) { + open(INLOCAL, "<$self->{'LIST_NAME'}/inlocal") || ($self->_seterror(-1, 'unable to read inlocal in update()') && return 0); + chomp($inlocal = ); + close(INLOCAL); + } + + # Attempt to update the list if we can. + system("$EZMLM_BASE/ezmlm-make", @switch_list, $self->{'LIST_NAME'}) == 0 + || ($self->_seterror($?) && return undef); + + # Sort out the DIR/inlocal problem if necessary + if(defined($inlocal)) { + open(INLOCAL, ">$self->{'LIST_NAME'}/inlocal") || ($self->_seterror(-1, 'unable to write inlocal in update()') && return 0); + print INLOCAL "$inlocal\n"; + close INLOCAL; + } + + $self->_seterror(undef); + return $self->{'LIST_NAME'}; +} + +# == Get a list of options for the current list == +sub getconfig { + my($self) = @_; + my($options); + + # Read the config file + if(-e "$self->{'LIST_NAME'}/flags") { + # this file exists since ezmlm-idx-5.0.0 + # 'config' is not authorative anymore since that version + $options = $self->_getconfig_idx5(); + } elsif(open(CONFIG, "<$self->{'LIST_NAME'}/config")) { + # 'config' contains the authorative information + while() { + if (/^F:-(\w+)/) { + $options = $1; + } elsif (/^(\d):(.+)$/) { + my $opt_num = $1; + my $value = $2; + $options .= " -$opt_num '$value'" if ($value =~ /\S/); + } + } + close CONFIG; + } else { + # Try manually - this will ignore all string settings, that can only be found + # in the config file + $options = $self->_getconfigmanual(); + } + + ($self->_seterror(-1, 'unable to read configuration in getconfig()') && return undef) unless (defined($options)); + + $self->_seterror(undef); + return $options; +} + +# == Return the name of the current list == +sub thislist { + my($self) = shift; + $self->_seterror(undef); + return $self->{'LIST_NAME'}; +} + +# == Set the current mailing list == +sub setlist { + my($self, $list) = @_; + if ($list =~ m/^([\w\d\_\-\.\/]+)$/) { + $list = $1; + if (-e "$list/lock") { + $self->_seterror(undef); + return $self->{'LIST_NAME'} = $list; + } else { + $self->_seterror(-1, "$list does not appear to be a valid list in setlist()"); + return undef; + } + } else { + $self->_seterror(-1, "$list contains tainted data in setlist()"); + return undef; + } +} + +# == Output the subscribers to $stream == +sub list { + my($self, $stream, $part) = @_; + $stream = *STDOUT unless (defined($stream)); + if(defined($part)) { + print $stream $self->subscribers($part); + } else { + print $stream $self->subscribers; + } +} + +# == Return an array of subscribers == +sub subscribers { + my($self, $part) = @_; + my(@subscribers); + ($self->_seterror(-1, 'must setlist() before returning subscribers()') && return undef) unless(defined($self->{'LIST_NAME'})); + if(defined($part) && $part) { + ($self->_seterror(-1, "$part part of $self->{'LIST_NAME'} does not appear to exist in subscribers()") && return undef) unless(-e "$self->{'LIST_NAME'}/$part"); + @subscribers = map { s/[\r\n]// && $_ } sort `$EZMLM_BASE/ezmlm-list $self->{'LIST_NAME'}/$part`; + } else { + @subscribers = map { s/[\r\n]// && $_ } sort `$EZMLM_BASE/ezmlm-list $self->{'LIST_NAME'}`; + } + + if($?) { + $self->_seterror($?, 'error during ezmlm-list in subscribers()'); + return (scalar @subscribers ? @subscribers : undef); + } else { + $self->_seterror(undef); + return @subscribers; + } +} + +# == Subscribe users to the current list == +sub sub { + my($self, @addresses) = @_; + ($self->_seterror(-1, 'sub() must be called with at least one address') && return 0) unless @addresses; + my($part) = pop @addresses unless ($#addresses < 1 or $addresses[$#addresses] =~ /\@/); + my($address); + ($self->_seterror(-1, 'must setlist() before sub()') && return 0) unless(defined($self->{'LIST_NAME'})); + + if(defined($part) && $part) { + ($self->_seterror(-1, "$part of $self->{'LIST_NAME'} does not appear to exist in sub()") && return 0) unless(-e "$self->{'LIST_NAME'}/$part"); + foreach $address (@addresses) { + next unless $self->_checkaddress($address); + system("$EZMLM_BASE/ezmlm-sub", "$self->{'LIST_NAME'}/$part", $address) == 0 || + ($self->_seterror($?) && return undef); + } + } else { + foreach $address (@addresses) { + next unless $self->_checkaddress($address); + system("$EZMLM_BASE/ezmlm-sub", $self->{'LIST_NAME'}, $address) == 0 || + ($self->_seterror($?) && return undef); + } + } + $self->_seterror(undef); + return 1; +} + +# == Unsubscribe users from a list == +sub unsub { + my($self, @addresses) = @_; + ($self->_seterror(-1, 'unsub() must be called with at least one address') && return 0) unless @addresses; + my($part) = pop @addresses unless ($#addresses < 1 or $addresses[$#addresses] =~ /\@/); + my($address); + ($self->_seterror(-1, 'must setlist() before unsub()') && return 0) unless(defined($self->{'LIST_NAME'})); + + if(defined($part) && $part) { + ($self->_seterror(-1, "$part of $self->{'LIST_NAME'} does not appear to exist in unsub()") && return 0) unless(-e "$self->{'LIST_NAME'}/$part"); + foreach $address (@addresses) { + next unless $self->_checkaddress($address); + system("$EZMLM_BASE/ezmlm-unsub", "$self->{'LIST_NAME'}/$part", $address) == 0 || + ($self->_seterror($?) && return undef); + } + } else { + foreach $address (@addresses) { + next unless $self->_checkaddress($address); + system("$EZMLM_BASE/ezmlm-unsub", $self->{'LIST_NAME'}, $address) == 0 || + ($self->_seterror($?) && return undef); + } + } + $self->_seterror(undef); + return 1; +} + +# == Test whether people are subscribed to the list == +sub issub { + my($self, @addresses) = @_; + my($part) = pop @addresses unless ($#addresses < 1 or $addresses[$#addresses] =~ /\@/); + my($address, $issub); $issub = 1; + ($self->_seterror(-1, 'must setlist() before issub()') && return 0) unless(defined($self->{'LIST_NAME'})); + + local $ENV{'SENDER'}; + + if(defined($part) && $part) { + ($self->_seterror(-1, "$part of $self->{'LIST_NAME'} does not appear to exist in issub()") && return 0) unless(-e "$self->{'LIST_NAME'}/$part"); + foreach $address (@addresses) { + $ENV{'SENDER'} = $address; + undef($issub) if ((system("$EZMLM_BASE/ezmlm-issubn", "$self->{'LIST_NAME'}/$part") / 256) != 0) + } + } else { + foreach $address (@addresses) { + $ENV{'SENDER'} = $address; + undef($issub) if ((system("$EZMLM_BASE/ezmlm-issubn", $self->{'LIST_NAME'}) / 256) != 0) + } + } + + $self->_seterror(undef); + return $issub; +} + +# == Is the list posting moderated == +# DEPRECATED: useless - you should better check the appropriate config flag +sub ismodpost { + my($self) = @_; + ($self->_seterror(-1, 'must setlist() before ismodpost()') && return 0) unless(defined($self->{'LIST_NAME'})); + $self->_seterror(undef); + return -e "$self->{'LIST_NAME'}/modpost"; +} + +# == Is the list subscriber moderated == +# DEPRECATED: useless - you should better check the appropriate config flag +sub ismodsub { + my($self) = @_; + ($self->_seterror(-1, 'must setlist() before ismodsub()') && return 0) unless(defined($self->{'LIST_NAME'})); + $self->_seterror(undef); + return -e "$self->{'LIST_NAME'}/modsub"; +} + +# == Is the list remote adminable == +# DEPRECATED: useless - you should better check the appropriate config flag +sub isremote { + my($self) = @_; + ($self->_seterror(-1, 'must setlist() before isremote()') && return 0) unless(defined($self->{'LIST_NAME'})); + $self->_seterror(undef); + return -e "$self->{'LIST_NAME'}/remote"; +} + +# == Does the list have a kill list == +# DEPRECATED: useless - you should better check the appropriate config flag +sub isdeny { + my($self) = @_; + ($self->_seterror(-1, 'must setlist() before isdeny()') && return 0) unless(defined($self->{'LIST_NAME'})); + $self->_seterror(undef); + return -e "$self->{'LIST_NAME'}/deny"; +} + +# == Does the list have an allow list == +# DEPRECATED: useless - the allow list is always created automatically +sub isallow { + my($self) = @_; + ($self->_seterror(-1, 'must setlist() before isallow()') && return 0) unless(defined($self->{'LIST_NAME'})); + $self->_seterror(undef); + return -e "$self->{'LIST_NAME'}/allow"; +} + +# == Is this a digested list == +# DEPRECATED: useless - you should better check the appropriate config flag +sub isdigest { + my($self) = @_; + ($self->_seterror(-1, 'must setlist() before isdigest()') && return 0) unless(defined($self->{'LIST_NAME'})); + $self->_seterror(undef); + return -e "$self->{'LIST_NAME'}/digest"; +} + +# == retrieve file contents == +sub getpart { + my($self, $part) = @_; + my(@contents, $content); + # check for the file in the list directory first + my $filename = $self->{'LIST_NAME'} . "/$part"; + # check for default file in config directory, if necessary + # BEWARE: get_config_dir and get_lang may _not_ cause an eternal loop :) + $filename = $self->get_config_dir() . '/' . $self->get_lang() . "/$part" + if (!(-e "$filename") && (get_version() >= 5) && + ($part ne 'conf-etc') && ($part ne 'conf-lang')); + if (open(PART, "<$filename")) { + while() { + unless ( /^#/ ) { + chomp($contents[$#contents++] = $_); + $content .= $_; + } + } + close PART; + if(wantarray) { + return @contents; + } else { + return $content; + } + } ($self->_seterror($?) && return undef); +} + +# == set files contents == +sub setpart { + my($self, $part, @content) = @_; + my($line); + if(open(PART, ">$self->{'LIST_NAME'}/$part")) { + foreach $line (@content) { + $line =~ s/[\r]//g; $line =~ s/\n$//; + print PART "$line\n"; + } + close PART; + return 1; + } ($self->_seterror($?) && return undef); +} + +# == get the configuration directory for this list (idx >= 5.0) == +# return '/etc/ezmlm' for idx < 5.0 +sub get_config_dir { + my $self = shift; + my $conf_dir; + if ((get_version() >= 5) && (ref $self) && (-e "$self->{'LIST_NAME'}/conf-etc")) { + chomp($conf_dir = $self->getpart('conf-etc')); + } else { + $conf_dir = '/etc/ezmlm'; + } + return $conf_dir; +} + +# == set the configuration directory for this list (idx >= 5.0) == +# return without error for idx < 5.0 +sub set_config_dir { + my ($self, $conf_dir) = @_; + return (0==0) if (get_version() < 5); + $self->setpart('conf-etc', "$conf_dir"); +} + + +# == get list of available languages (for idx >= 5.0) == +# return empty list for idx < 5.0 +sub get_available_languages { + my $self = shift; + my @langs = (); + return @langs if (get_version() < 5); + + $self->_seterror(undef) if (ref $self); + + # check for language directories + my $conf_dir; + if (ref $self) { + ($self->_seterror(-1, 'could not retrieve configuration directory') && return 0) + unless ($conf_dir = $self->get_config_dir()); + } else { + $conf_dir = get_config_dir(); + } + if (opendir DIR, "$conf_dir") { + my @dirs; + @dirs = grep !/^\./, readdir DIR; + closedir DIR; + my $item; + foreach $item (@dirs) { + push (@langs, $item) if (-e "$conf_dir/$item/text"); + } + return @langs; + } else { + $self->_seterror(-1, 'could not access configuration directory') if (ref $self); + return undef; + } +} + + +# == get the selected language of the list (idx >= 5.0) == +# return empty string for idx < 5.0 +sub get_lang { + my ($self) = shift; + my $lang; + return '' if (get_version() < 5); + if (-e "$self->{'LIST_NAME'}/conf-lang") { + chomp($lang = $self->getpart('conf-lang')); + } else { + $lang = 'default'; + } + return $lang; +} + + +# == set the selected language of the list (idx >= 5.0) == +# return without error for idx < 5.0 +sub set_lang { + my ($self, $lang) = @_; + return (0==0) if (get_version() < 5); + if (($lang eq 'default') || ($lang eq '')) { + return 1 if (unlink "$self->{'LIST_NAME'}/conf-lang"); + } else { + return 1 if ($self->setpart('conf-lang', "$lang")); + } + return 0; +} + + +# == get the selected charset of the list (idx >= 5.0) == +# return empty string for idx < 5.0 +sub get_charset { + my ($self) = shift; + my $charset; + return '' if (get_version() < 5); + chomp($charset = $self->getpart('charset')); + # default if no 'charset' file exists + $charset = 'us-ascii' if ($charset eq ''); + return $charset; +} + + +# == set the selected charset of the list (idx >= 5.0) == +# return without error for idx < 5.0 +# remove list' specific charset file, if the default charset of the current language +# was chosen +sub set_charset { + my ($self, $charset) = @_; + return (0==0) if (get_version() < 5); + # first: remove current charset + unlink "$self->{'LIST_NAME'}/charset"; + # second: get default value of the current language + my $default_charset = $self->getpart('charset'); + # last: create new charset file only if the selected charset is not the default anyway + if (($charset eq $default_charset) || ($charset !~ /\S/)) { + # do not write the specific charset, as the default charset of the language is + # sufficient + return 1; + } else { + return 1 if ($self->setpart('charset', "$charset")); + } + return 0; +} + + +# == get list of available text files == +sub get_available_text_files { + my ($self) = shift; + my @files; + my $item; + my %seen = (); + + # customized text files of this list (idx >= 5.0) + # OR text files of this list (idx < 5.0) + if (opendir DIR, "$self->{'LIST_NAME'}/text") { + my @local_files = grep !/^\./, readdir DIR; + closedir DIR; + foreach $item (@local_files) { + unless ($seen{$item}) { + push (@files, $item); + $seen{$item} = 1; + } + } + } + + # default text files (only idx >= 5.0) + if (get_version() >= 5) { + my $dirname = $self->get_config_dir . '/' . $self->get_lang() . '/text'; + $dirname = $self->get_config_dir . '/default/text' unless (-e $dirname); + if (opendir GLOBDIR, $dirname) { + my @global_files = grep !/^\./, readdir GLOBDIR; + closedir GLOBDIR; + foreach $item (@global_files) { + unless ($seen{$item}) { + push (@files, $item); + $seen{$item} = 1; + } + } + } + } + + if ($#files > 0) { + return @files; + } else { + $self->_seterror(-1, 'no textfiles found'); + return undef; + } +} + +# == get text file content == +sub get_text_content { + my ($self, $textfile) = @_; + + if (-e "$self->{'LIST_NAME'}/text/$textfile") { + return $self->getpart("text/$textfile"); + } elsif (get_version() >= 5) { + my $filename = $self->get_config_dir() . '/' . $self->get_lang() . "/text/$textfile"; + $filename = "/etc/ezmlm/default/$textfile" unless (-e "$filename"); + my @contents; + my $content; + if (open(PART, "<$filename")) { + while() { + chomp($contents[$#contents++] = $_); + $content .= $_; + } + close PART; + if(wantarray) { + return @contents; + } else { + return $content; + } + } else { + $self->_seterror($?, "could not open $filename"); + return undef; + } + } else { + $self->_seterror(-1, "could not get the text file ($textfile)"); + return undef; + } +} + + +# == set text file content == +sub set_text_content { + my ($self, $textfile, @content) = @_; + mkdir "$self->{'LIST_NAME'}/text" unless (-e "$self->{'LIST_NAME'}/text"); + return 1 if ($self->setpart("text/$textfile", @content)); + return 0; +} + + +# == check if specified text file is customized or default (for idx >= 5.0) == +# return whether the text file exists in the list's directory (false) or not (true) +# empty filename returns false +sub is_text_default { + my ($self, $textfile) = @_; + return (0==1) if ($textfile eq ''); + if (-e "$self->{'LIST_NAME'}/text/$textfile") { + return (1==0); + } else { + return (0==0); + } +} + + +# == remove non-default text file (for idx >= 5.0) == +# return without error for idx < 5 +# otherwise: remove customized text file from the list's directory +sub reset_text { + my ($self, $textfile) = @_; + return if (get_version() < 5); + return if ($textfile eq ''); + return if ($textfile =~ /[^\w_\.-]/); + return if ($self->is_text_default($textfile)); + ($self->_seterror(-1, "could not remove customized text file ($textfile)") && return 0) + unless unlink("$self->{'LIST_NAME'}/text/$textfile"); + return 1; +} + + +# == return an error message if appropriate == +sub errmsg { + my($self) = @_; + return $self->{'ERRMSG'}; +} + +sub errno { + my($self) = @_; + return $self->{'ERRNO'}; +} + +# == Test the compatiblity of the module == +sub check_version { + my $self = shift; + my $version = `$EZMLM_BASE/ezmlm-make -V 2>&1`; + $self->_seterror(undef) if (ref $self); + + # ezmlm-idx is necessary + if (get_version() >= 4) { + return 0; + } else { + return $version; + } +} + +# == get the major ezmlm version == +# return values: +# 0 => unknown version +# 3 => ezmlm v0.53 +# 4 => ezmlm-idx v0.4* +# 5 => ezmlm-idx v5.* +sub get_version { + my ($ezmlm, $idx); + my $version = `$EZMLM_BASE/ezmlm-make -V 2>&1`; + + $version = $1 if ($version =~ m/^[^:]*:\s+(.*)$/); + $ezmlm = $1 if ($version =~ m/ezmlm-([\d\.]+)$/); + $idx = $1 if ($version =~ m/ezmlm-idx-([\d\.]+)$/); + + if(defined($ezmlm)) { + return 3; + } elsif (defined($idx)) { + if (($idx =~ m/^(\d)/) && ($1 >= 5)) { + # version 5.0 or higher + return 5; + } elsif (($idx =~ m/^0\.(\d)/) && ($1 >= 0)) { + # version 0.4 or higher + return 4; + } else { + return 0; + } + } else { + return 0; + } +} + +# == Create SQL Database tables if defined for a list == +sub createsql { + my($self) = @_; + + ($self->_seterror(-1, 'MySQL must be compiled into Ezmlm for createsql() to work') && return 0) unless(defined($MYSQL_BASE) && $MYSQL_BASE); + ($self->_seterror(-1, 'must setlist() before isdigest()') && return 0) unless(defined($self->{'LIST_NAME'})); + my($config) = $self->getconfig(); + + if($config =~ m/-6\s+'(.+?)'\s*/){ + my($sqlsettings) = $1; + my($host, $port, $user, $password, $database, $table) = split(':', $sqlsettings, 6); + + ($self->_seterror(-1, 'error in list configuration while trying createsql()') && return 0) + unless (defined($host) && defined($port) && defined($user) + && defined($password) && defined($database) && defined($table)); + + system("$EZMLM_BASE/ezmlm-mktab -d $table | $MYSQL_BASE/mysql -h$host -P$port -u$user -p$password -f $database") == 0 || + ($self->_seterror($?) && return undef); + + } else { + $self->_seterror(-1, 'config for thislist() must include SQL options'); + return 0; + } + + ($self->_seterror(undef) && return 1); + +} + + +# == Internal function to set the error to return == +sub _seterror { + my($self, $no, $mesg) = @_; + + if(defined($no) && $no) { + if($no < 0) { + $self->{'ERRNO'} = -1; + $self->{'ERRMSG'} = $mesg || 'An undefined error occoured'; + } else { + $self->{'ERRNO'} = $no / 256; + $self->{'ERRMSG'} = $! || $mesg || 'An undefined error occoured in a system() call'; + } + } else { + $self->{'ERRNO'} = 0; + $self->{'ERRMSG'} = undef; + } + return 1; +} + +# == Internal function to test for valid email addresses == +sub _checkaddress { + my($self, $address) = @_; + return 1 unless defined($address); + return 0 unless ($address =~ m/^(\S+\@\S+\.\S+)$/); + $_[1] = $1; + return 1; +} + +# == Internal function to work out a list configuration (idx >= v5.0) == +sub _getconfig_idx5 { + my($self) = @_; + my ($options, %optionfiles); + my ($file, $opt_num, $temp); + + # read flag file (available since ezmlm-idx 5.0.0) + chomp($options = $self->getpart('flags')); + # remove prefixed '-' + $options =~ s/^-//; + + # since ezmlm-idx v5, we have to read the config + # values from different files + # first: preset a array with "filename" and "option_number" + %optionfiles = ( + 'sublist', '0', + 'fromheader', '3', + 'tstdigopts', '4', + 'owner', '5', + 'sql', '6', + 'modpost', '7', + 'modsub', '8', + 'remote', '9'); + while (($file, $opt_num) = each(%optionfiles)) { + if (-e "$self->{'LIST_NAME'}/$file") { + chomp($temp = $self->getpart($file)); + $temp =~ m/^(.*)$/m; # take only the first line + $temp = $1; + $options .= " -$opt_num '$temp'" if ($temp =~ /\S/); + } + } + + return $options; +} + +# == Internal function to work out a list configuration manually (idx < v5.0.0 ) == +sub _getconfigmanual { + # use this function for strange lists without + # 'config' (idx < v5.0) and 'flags' (idx >= v5.0) + my($self) = @_; + my ($savedollarslash, $options, $manager, $editor, $i); + + # Read the whole of DIR/editor and DIR/manager in + $savedollarslash = $/; + undef $/; + # $/ = \0777; + + open (EDITOR, "<$self->{'LIST_NAME'}/editor") || ($self->_seterror($?) && return undef); + open (MANAGER, "<$self->{'LIST_NAME'}/manager") || ($self->_seterror($?) && return undef); + $editor = ; $manager = ; + close(EDITOR), close(MANAGER); + + $/ = $savedollarslash; + + $options = ''; + $options .= 'a' if (-e "$self->{'LIST_NAME'}/archived"); + $options .= 'd' if (-e "$self->{'LIST_NAME'}/digest"); + $options .= 'f' if (-e "$self->{'LIST_NAME'}/prefix"); + $options .= 'g' if ($manager =~ /ezmlm-get -\w*s/ ); + $options .= 'i' if (-e "$self->{'LIST_NAME'}/indexed"); + $options .= 'k' if (-e "$self->{'LIST_NAME'}/blacklist" || -e "$self->{'LIST_NAME'}/deny"); + $options .= 'l' if ($manager =~ /ezmlm-manage -\w*l/ ); + $options .= 'm' if (-e "$self->{'LIST_NAME'}/modpost"); + $options .= 'n' if ($manager =~ /ezmlm-manage -\w*e/ ); + $options .= 'p' if (-e "$self->{'LIST_NAME'}/public"); + $options .= 'q' if ($manager =~ /ezmlm-request/ ); + $options .= 'r' if (-e "$self->{'LIST_NAME'}/remote"); + $options .= 's' if (-e "$self->{'LIST_NAME'}/modsub"); + $options .= 't' if (-e "$self->{'LIST_NAME'}/text/trailer"); + $options .= 'u' if (($options !~ /m/ && $editor =~ /ezmlm-issubn \'/ ) + || $editor =~ /ezmlm-gate/ ); + $options .= 'x' if (-e "$self->{'LIST_NAME'}/extra" || -e "$self->{'LIST_NAME'}/allow"); + + # Add the unselected options too + # but we will skip invalid options (any of 'cevz') + foreach $i ('a' .. 'z') { + $options .= uc($i) unless (('cevz' =~ /$i/) || ($options =~ /$i/i)) + } + + # there is no way to get the other string settings, that are only + # defined in 'config' - sorry ... + + return $options; +} + +# == Internal Function to try to determine the vhost user == +sub _getvhostuser { + my($self, $hostname) = @_; + my($username); + + open(VD, "<$QMAIL_BASE/control/virtualdomains") || ($self->_seterror($?) && return undef); + while() { + last if(($username) = /^\s*$hostname:(\w+)$/); + } + close VD; + + return $username; +} + +# == Internal function to work out default host name == +sub _getdefaultdomain { + my($self) = @_; + my($hostname); + + open (GETHOST, "<$QMAIL_BASE/control/defaultdomain") + || open (GETHOST, "<$QMAIL_BASE/control/me") + || ($self->_seterror($?) && return undef); + chomp($hostname = ); + close GETHOST; + + return $hostname; +} + +1; +__END__ + +=head1 NAME + +Ezmlm - Object Methods for Ezmlm Mailing Lists + +=head1 SYNOPSIS + + use Mail::Ezmlm; + $list = new Mail::Ezmlm; + +The rest is a bit complicated for a Synopsis, see the description. + +=head1 ABSTRACT + +Ezmlm is a Perl module that is designed to provide an object interface to +the ezmlm mailing list manager software. See the ezmlm web page +(http://www.ezmlm.org/) for a complete description of the software. + +This version of the module is designed to work with ezmlm version 0.53. +It is fully compatible with ezmlm's IDX extensions (version 0.4xx and 5.0 ). Both +of these can be obtained via anon ftp from ftp://ftp.ezmlm.org/pub/patches/ + +=head1 DESCRIPTION + +=head2 Setting up a new Ezmlm object: + + use Mail::Ezmlm; + $list = new Mail::Ezmlm; + $list = new Mail::Ezmlm('/home/user/lists/moolist'); + +=head2 Changing which list the Ezmlm object points at: + + + $list->setlist('/home/user/lists/moolist'); + +=head2 Getting a list of current subscribers: + +=item Two methods of listing subscribers is provided. The first prints a list +of subscribers, one per line, to the supplied FILEHANDLE. If no filehandle is +given, this defaults to STDOUT. An optional second argument specifies the +part of the list to display (mod, digest, allow, deny). If the part is +specified, then the FILEHANDLE must be specified. + + $list->list; + $list->list(\*STDERR); + $list->list(\*STDERR, 'deny'); + +=item The second method returns an array containing the subscribers. The +optional argument specifies which part of the list to display (mod, digest, +allow, deny). + + @subscribers = $list->subscribers; + @subscribers = $list->subscribers('allow'); + +=head2 Testing for subscription: + + $list->issub('nobody@on.web.za'); + $list->issub(@addresses); + $list->issub(@addresses, 'mod'); + +issub() returns 1 if all the addresses supplied are found as subscribers +of the current mailing list, otherwise it returns undefined. The optional +argument specifies which part of the list to check (mod, digest, allow, +deny). + +=head2 Subscribing to a list: + + $list->sub('nobody@on.web.za'); + $list->sub(@addresses); + $list->sub(@addresses, 'digest'); + +sub() takes a LIST of addresses and subscribes them to the current mailing list. +The optional argument specifies which part of the list to subscribe to (mod, +digest, allow, deny). + + +=head2 Unsubscribing from a list: + + $list->unsub('nobody@on.web.za'); + $list->unsub(@addresses); + $list->unsub(@addresses, 'mod'); + +unsub() takes a LIST of addresses and unsubscribes them (if they exist) from the +current mailing list. The optional argument specifies which part of the list +to unsubscribe from (mod, digest, allow, deny). + + +=head2 Creating a new list: + + $list->make(-dir=>'/home/user/list/moo', + -qmail=>'/home/user/.qmail-moo', + -name=>'user-moo', + -host=>'on.web.za', + -user=>'onwebza', + -switches=>'mPz'); + +make() creates the list as defined and sets it to the current list. There are +three variables which must be defined in order for this to occur; -dir, -qmail and -name. + +=over 6 + +=item -dir is the full path of the directory in which the mailing list is to +be created. + +=item -qmail is the full path and name of the .qmail file to create. + +=item -name is the local part of the mailing list address (eg if your list +was user-moo@on.web.za, -name is 'user-moo'). + +=item -host is the name of the host that this list is being created on. If +this item is omitted, make() will try to determine your hostname. If -host is +not the same as your hostname, then make() will attempt to fix DIR/inlocal for +a virtual host. + +=item -user is the name of the user who owns this list. This item only needs to +be defined for virtual domains. If it exists, it is prepended to -name in DIR/inlocal. +If it is not defined, the make() will attempt to work out what it should be from +the qmail control files. + +=item -switches is a list of command line switches to pass to ezmlm-make(1). +Note that the leading dash ('-') should be ommitted from the string. + +=back + +make() returns the value of thislist() for success, undefined if there was a +problem with the ezmlm-make system call and 0 if there was some other problem. + +See the ezmlm-make(1) man page for more details + +=head2 Determining which list we are currently altering: + + $whichlist = $list->thislist; + print $list->thislist; + +=head2 Getting the current configuration of the current list: + + $list->getconfig; + +getconfig() returns a string that contains the command line switches that +would be necessary to re-create the current list. It does this by reading the +DIR/config file (idx < v5.0) or DIR/flags (idx >= v5.0) if one of them exists. +If it can't find these files it attempts to work things out for itself (with +varying degrees of success). If both these methods fail, then getconfig() +returns undefined. + + $list->ismodpost; + $list->ismodsub; + $list->isremote; + $list->isdeny; + $list->isallow; + +The above five functions test various features of the list, and return a 1 +if the list has that feature, or a 0 if it doesn't. These functions are +considered DEPRECATED as their result is not reliable. Use "getconfig" instead. + +=head2 Updating the configuration of the current list: + + $list->update('msPd'); + +update() can be used to rebuild the current mailing list with new command line +options. These options can be supplied as a string argument to the procedure. +Note that you do not need to supply the '-' or the 'e' command line switch. + + @part = $list->getpart('headeradd'); + $part = $list->getpart('headeradd'); + $list->setpart('headerremove', @part); + +getpart() and setpart() can be used to retrieve and set the contents of +various text files such as headeradd, headerremove, mimeremove, etc. + +=head2 Manage language dependent text files + + $list->get_available_text_files; + $list->get_text_content('sub-ok'); + $list->set_text_content('sub-ok', @content); + +These functions allow you to manipulate the text files, that are used for +automatic replies by ezmlm. + + $list->is_text_default('sub-ok'); + $list->reset_text('sub-ok'); + +These two functions are available if you are using ezmlm-idx v5.0 or higher. +is_text_default() checks, if there is a customized text file defined for this list. +reset_text() removes the customized text file from this list. Ezmlm-idx will use +system-wide default text file, if there is no customized text file for this list. + +=head2 Change the list's settings (for ezmlm-idx >= 5.0) + + Mail::Ezmlm->get_config_dir; + $list->get_config_dir; + $list->set_config_dir('/etc/ezmlm-local'); + +These function access the file 'conf-etc' in the mailing list's directory. The +static function always returns the default configuration directory of ezmlm-idx +(/etc/ezmlm). + + $list->get_available_languages; + $list->get_lang; + $list->set_lang('de'); + $list->get_charset; + $list->set_charset('iso-8859-1:Q'); + +These functions allow you to change the language of the text files, that are used +for automatic replies of ezmlm-idx (since v5.0 the configured language is stored +in 'conf-lang' within the mailing list's directory). Customized files (in the 'text' +directory of a mailing list directory) override the default language files. +Empty strings for set_lang() and set_charset() reset the setting to its default value. + +=head2 Get the installed version of ezmlm + + Mail::Ezmlm->get_version; + +The result is one of the following: + 0 - unknown + 3 - ezmlm 0.53 + 4 - ezmlm-idx 0.4xx + 5 - ezmlm-idx 5.x + +=head2 Creating MySQL tables: + + $list->createsql(); + +Currently only works for MySQL. + +createsql() will attempt to create the table specified in the SQL connect +options of the current mailing list. It will return an error if the current +mailing list was not configured to use SQL, or is Ezmlm was not compiled +with MySQL support. See the MySQL info pages for more information. + +=head2 Checking the Mail::Ezmlm and ezmlm version numbers + +The version number of the Mail::Ezmlm module is stored in the variable +$Mail::Ezmlm::VERSION. The compatibility of this version of Mail::Ezmlm +with your system installed version of ezmlm can be checked with + + $list->check_version(); + +This returns 0 for compatible, or the version string of ezmlm-make(2) if +the module is incompatible with your set up. + +=head1 RETURN VALUES + +All of the routines described above have return values. 0 or undefined are +used to indicate that an error of some form has occoured, while anything +>0 (including strings, etc) are used to indicate success. + +If an error is encountered, the functions + + $list->errno(); + $list->errmsg(); + +can be used to determine what the error was. + +errno() returns; 0 or undef if there was no error. + -1 for an error relating to this module. + >0 exit value of the last system() call. + +errmsg() returns a string containing a description of the error ($! if it +was from a system() call). If there is no error, it returns undef. + +For those who are interested, in those sub routines that have to make system +calls to perform their function, an undefined value indicates that the +system call failed, while 0 indicates some other error. Things that you would +expect to return a string (such as thislist()) return undefined to indicate +that they haven't a clue ... as opposed to the empty string which would mean +that they know about nothing :) + +=head1 AUTHOR + + Guy Antony Halse + Lars Kruse + +=head1 BUGS + + There are no known bugs. + + Please report bugs to the author or use the bug tracking system at + https://systemausfall.org/trac/ezmlm-web. + +=head1 SEE ALSO + + ezmlm(5), ezmlm-make(2), ezmlm-sub(1), + ezmlm-unsub(1), ezmlm-list(1), ezmlm-issub(1) + + http://rucus.ru.ac.za/~guy/ezmlm/ + https://systemausfall.org/toolforge/ezmlm-web + http://www.ezmlm.org/ + http://www.qmail.org/ + +=cut diff --git a/Ezmlm/tags/Ezmlm-0.07.1/MANIFEST b/Ezmlm/tags/Ezmlm-0.07.1/MANIFEST new file mode 100644 index 0000000..8bcd77d --- /dev/null +++ b/Ezmlm/tags/Ezmlm-0.07.1/MANIFEST @@ -0,0 +1,7 @@ +Changes +Ezmlm.pm +MANIFEST +README +Makefile.PL +test.pl +META.yml diff --git a/Ezmlm/tags/Ezmlm-0.07.1/META.yml b/Ezmlm/tags/Ezmlm-0.07.1/META.yml new file mode 100644 index 0000000..54e8589 --- /dev/null +++ b/Ezmlm/tags/Ezmlm-0.07.1/META.yml @@ -0,0 +1,10 @@ +# http://module-build.sourceforge.net/META-spec.html +#XXXXXXX This is a prototype!!! It will change in the future!!! XXXXX# +name: Ezmlm +version: 0.07.1 +version_from: Ezmlm.pm +installdirs: site +requires: + +distribution_type: module +generated_by: ExtUtils::MakeMaker version 6.17 diff --git a/Ezmlm/tags/Ezmlm-0.07.1/Makefile.PL b/Ezmlm/tags/Ezmlm-0.07.1/Makefile.PL new file mode 100644 index 0000000..d28f315 --- /dev/null +++ b/Ezmlm/tags/Ezmlm-0.07.1/Makefile.PL @@ -0,0 +1,131 @@ +# $Id: Makefile.PL,v 1.3 2005/03/05 14:15:20 guy Exp $ + +use ExtUtils::MakeMaker; +# See lib/ExtUtils/MakeMaker.pm for details of how to influence +# the contents of the Makefile that is written. +WriteMakefile( + 'CONFIGURE' => \&set_paths, + 'NAME' => 'Mail::Ezmlm', + 'VERSION_FROM' => 'Ezmlm.pm', # finds $VERSION + 'DISTNAME' => 'Ezmlm', + 'dist' => { COMPRESS => 'gzip', SUFFIX => 'gz' }, + 'clean' => { FILES => 'ezmlmtmp' } +); + +sub set_paths { + my($qmail_path, $ezmlm_path); + + # special case to handle the FreeBSD ports system + if ($ENV{BSD_BATCH_INSTALL}) { + print STDERR "\$BSD_BATCH_INSTALL is set in your environment, assuming port defaults\n"; + return {}; + } + + print << 'EOM'; + +We now need to know where some things live on your system. I'll try and make +some intelligent guesses - if I get it right, please just press enter at the +prompt. If I get them wrong, please type in the correct path for me and then +press enter. + +First I need to know where the Ezmlm binaries live (ie where I can find +ezmlm-make, ezmlm-sub, etc). + +EOM + + *prompt = \&ExtUtils::MakeMaker::prompt; + + # guess default + $ezmlm_path = '/usr/local/bin/ezmlm'; + $ezmlm_path = '/usr/local/bin/ezmlm-idx' unless (-e "$ezmlm_path/ezmlm-make"); + $ezmlm_path = '/usr/local/bin' unless (-e "$ezmlm_path/ezmlm-make"); + $ezmlm_path = '/usr/bin/ezmlm' unless (-e "$ezmlm_path/ezmlm-make"); + $ezmlm_path = '/usr/bin/ezmlm-idx' unless (-e "$ezmlm_path/ezmlm-make"); + $ezmlm_path = '/usr/bin' unless (-e "$ezmlm_path/ezmlm-make"); + # return to default, if nothing can be found + $ezmlm_path = '/usr/local/bin/ezmlm' unless (-e "$ezmlm_path/ezmlm-make"); + + foreach (1..10) { + $ezmlm_path = prompt('Ezmlm binary directory?', "$ezmlm_path"); + last if (-e "$ezmlm_path/ezmlm-make"); + print "I can't find $ezmlm_path/ezmlm-make. Please try again\n"; + } + unless (-e "$ezmlm_path/ezmlm-make") { + print STDERR "Warning: No correct input after $_ attempts. Continuing with warnings ...\n"; + } + + print << 'EOM'; + +Now I need to know where Qmail resides on your system. The Qmail base +directory is the one in which the Qmail bin, control, etc directories +live in. + +EOM + + foreach (1..10) { + $qmail_path = prompt('Qmail base directory?', '/var/qmail'); + last if (-e "$qmail_path/control"); + print "I can't find $qmail_path/control. Please try again\n"; + } + if (! -e "$qmail_path/control") { + print STDERR "Warning: No correct input after $_ attempts. Continuing with warnings ...\n"; + } + + if(`strings $ezmlm_path/ezmlm-sub | grep -i 'MySQL'`) { + + print << 'EOM'; + +It appears you have compiled MySQL support into your version of Ezmlm. If +this is correct, I now need to know where the MySQL client (mysql) lives on +your machine. + +Please leave this blank if you do not want to enable MySQL support in the +Mail::Ezmlm module. + +EOM + + $mysql_path = '/usr/bin'; + $mysql_path = '/usr/local/bin' unless (-e "$mysql_path/mysql"); + # return to default - if nothing works + $mysql_path = '/usr/bin' unless (-e "$mysql_path/mysql"); + + foreach (1..10) { + $mysql_path = prompt('MySQL binary directory?', "$mysql_path"); + last if (-e "$mysql_path/mysql" || $mysql_path eq ''); + print "I can't find $mysql_path/mysql. Please enter the full path\n"; + print "or leave this option blank if you don't want to use MySQL\n"; + } + unless ((-e "$mysql_path/mysql") || ($mysql_path eq '')) { + print STDERR "Warning: No correct input after $_ attempts. Continuing with warnings ...\n"; + } + + } + + print << 'EOM'; + +Thank you. I will use this information to configure Mail::Ezmlm for you + +EOM + + # Back up file + open(EZMLM, 'Ezmlm.pm.tmp.$$") or die "Unable to create temp file: $!"; + while() { print TMP; } + close TMP; close EZMLM; + + # Do variable substitution + open(EZMLM, '>Ezmlm.pm') or die "Unable to open Ezmlm.pm for write: $!"; + open(TMP, ") { + s{^\$EZMLM_BASE\s*=\s*['"].+?['"]\s*;\s*(#.*|)$}{\$EZMLM_BASE = '$ezmlm_path'; #Autoinserted by Makefile.PL}; + s{^\$QMAIL_BASE\s*=\s*['"].+?['"]\s*;\s*(#.*|)$}{\$QMAIL_BASE = '$qmail_path'; #Autoinserted by Makefile.PL}; + s{^\$MYSQL_BASE\s*=\s*['"].*?['"]\s*;\s*(#.*|)$}{\$MYSQL_BASE = '$mysql_path'; #Autoinserted by Makefile.PL}; + print EZMLM; + } + close TMP; close EZMLM; + + unlink "Ezmlm.pm.tmp.$$"; + + return {}; + +} diff --git a/Ezmlm/tags/Ezmlm-0.07.1/README b/Ezmlm/tags/Ezmlm-0.07.1/README new file mode 100644 index 0000000..82630f1 --- /dev/null +++ b/Ezmlm/tags/Ezmlm-0.07.1/README @@ -0,0 +1,21 @@ +Ezmlm.pm + +Object methods for ezmlm mailing lists. + +Install by doing the following ... +# perl Makefile.PL +# make test +# make install + +One thing. For some reason MakeMaker doesn't like symlinks. Please make sure +you use the full cantonical path for the qmail and ezmlm binaries. + +Documentation is in pod format. Please run perldoc Mail::Ezmlm after you have +installed it. + +Much as I'd like to, I don't have the time to regularly maintain this. New +releases are infrequent at best. Check http://guy.rucus.net/ezmlm/contrib/ +for patches, etc that may be useful. + +- Guy Antony Halse +- Lars Kruse diff --git a/Ezmlm/tags/Ezmlm-0.07.1/test.pl b/Ezmlm/tags/Ezmlm-0.07.1/test.pl new file mode 100644 index 0000000..4e0d4ee --- /dev/null +++ b/Ezmlm/tags/Ezmlm-0.07.1/test.pl @@ -0,0 +1,234 @@ +# =========================================================================== +# test.pl - version 0.02 - 25/09/2000 +# $Id: test.pl,v 1.5 2005/03/05 14:08:30 guy Exp $ +# Test suite for Mail::Ezmlm +# +# Copyright (C) 1999, Guy Antony Halse, All Rights Reserved. +# Please send bug reports and comments to guy-ezmlm@rucus.ru.ac.za +# +# This program is subject to the restrictions set out in the copyright +# agreement that can be found in the Ezmlm.pm file in this distribution +# +# ========================================================================== +# Before `make install' is performed this script should be runnable with +# `make test'. After `make install' it should work as `perl test.pl' + +######################### We start with some black magic to print on failure. + +$failed = 0; + +BEGIN { $| = 1; print "1..9\n"; } +END {($failed++ && print "not ok 1\n") unless $loaded;} +use Mail::Ezmlm; +$loaded = 1; +print "Loading: ok 1\n"; + +######################### End of black magic. + +# Insert your test code below (better if it prints "ok 13" +# (correspondingly "not ok 13") depending on the success of chunk 13 +# of the test code): + +use Cwd; +use File::Find; +$list = new Mail::Ezmlm; + +# create a temp directory if necessary +$TMP = cwd() . '/ezmlmtmp'; +mkdir $TMP, 0755 unless (-d $TMP); + +print 'Checking list creation: '; +$test1 = $list->make(-name=>"ezmlm-test1-$$", + -qmail=>"$TMP/.qmail-ezmlm-test1-$$", + -dir=>"$TMP/ezmlm-test1-$$"); +if($test1 eq "$TMP/ezmlm-test1-$$") { + print "ok 2\n"; +} else { + print 'not ok 2 [', $list->errmsg(), "]\n"; + $failed++; +} + +print 'Checking vhost list creation: '; +$test2 = $list->make(-name=>"ezmlm-test2-$$", + -qmail=>"$TMP/.qmail-ezmlm-test2-$$", + -dir=>"$TMP/ezmlm-test2-$$", + -host=>'on.web.za', + -user=>'onwebza'); +if($test2 eq "$TMP/ezmlm-test2-$$") { + open(INLOCAL, "<$TMP/ezmlm-test2-$$/inlocal"); + chomp($test2 = ); + close INLOCAL; + if($test2 eq "onwebza-ezmlm-test2-$$") { + print "ok 3\n"; + } else { + print 'not ok 3 [', $list->errmsg(), "]\n"; + $failed++; + } +} else { + print 'not ok 3 [', $list->errmsg(), "]\n"; + $failed++; +} + +print 'Testing list update: '; +if($list->update('ms')) { + print "ok 4\n"; +} else { + print 'not ok 4 [', $list->errmsg(), "]\n"; + $failed++; +} + +print 'Testing setlist() and thislist(): '; +$list->setlist("$TMP/ezmlm-test1-$$"); +if($list->thislist eq "$TMP/ezmlm-test1-$$") { + print "ok 5\n"; +} else { + print 'not ok 5 [', $list->errmsg(), "]\n"; + $failed++; +} + +print 'Testing list subscription and subscription listing: '; +$list->sub('nobody@on.web.za'); +$list->sub('anonymous@on.web.za', 'test@on.web.za'); +@subscribers = $list->subscribers; +if($subscribers[1] =~ /nobody\@on.web.za/) { + print "ok 6\n"; +} else { + print 'not ok 6 [', $list->errmsg(), "]\n"; + $failed++; +} + +print 'Testing issub(): '; +if(defined($list->issub('nobody@on.web.za'))) { + if(defined($list->issub('some@non.existant.address'))) { + print 'not ok 7 [', $list->errmsg(), "]\n"; + $failed++; + } else { + print "ok 7\n"; + } +} else { + print 'not ok 7 [', $list->errmsg(), "]\n"; + $failed++; +} + +print 'Testing list unsubscription: '; +$list->unsub('nobody@on.web.za'); +$list->unsub('anonymous@on.web.za', 'test@on.web.za'); +@subscribers = $list->subscribers; +unless(@subscribers) { + print "ok 8\n"; +} else { + print 'not ok 8 [', $list->errmsg(), "]\n"; + $failed++; +} + +print 'Testing installed version of ezmlm: '; +my($version) = $list->check_version(); +if ($version) { + $version =~ s/\n//; + print 'not ok 9 [Warning: Ezmlm.pm is designed to work with ezmlm-idx > 0.40. Your version reports as: ', $version, "]\n"; +} else { + print "ok 9\n"; +} + +print 'Testing retrieving of text files: '; +if ($list->get_text_content('sub-ok') ne '') { + print "ok 10\n"; +} else { + print 'not ok 10 [', $list->errmsg(), "]\n"; + $failed++; +} + +print 'Testing changing of text files: '; +$list->set_text_content('sub-ok', "testing message\n"); +if ($list->get_text_content('sub-ok') eq "testing message\n") { + print "ok 11\n"; +} else { + print 'not ok 11 [', $list->errmsg(), "]\n"; + $failed++; +} + +print 'Testing if text file is marked as customized (only idx >= 5.0): '; +if ($list->get_version() >= 5) { + if ($list->is_text_default('sub-ok')) { + print 'not ok 12 [', $list->errmsg(), "]\n"; + $failed++; + } else { + print "ok 12\n"; + } +} else { + print "ok 12 [skipped]\n"; +} + +print 'Testing resetting text files (only idx >= 5.0): '; +if ($list->get_version() >= 5) { + $list->reset_text('sub-ok'); + if ($list->is_text_default('sub-ok')) { + print "ok 13\n"; + } else { + print 'not ok 13 [', $list->errmsg(), "]\n"; + $failed++; + } +} else { + print "ok 13 [skipped]\n"; +} + +print 'Testing retrieving available languages (only idx >= 5.0): '; +if ($list->get_version() >= 5) { + my @avail_langs = $list->get_available_languages(); + if ($#avail_langs > 0) { + print "ok 14\n"; + } else { + print 'not ok 14 [', $list->errmsg(), "]\n"; + $failed++; + } +} else { + print "ok 14 [skipped]\n"; +} + +print 'Testing changing the configured language (only idx >= 5.0): '; +if ($list->get_version() >= 5) { + my @avail_langs = $list->get_available_languages(); + $list->set_lang($avail_langs[$#avail_langs-1]); + if ($list->get_lang() eq $avail_langs[$#avail_langs-1]) { + print "ok 15\n"; + } else { + print 'not ok 15 [', $list->errmsg(), "]\n"; + $failed++; + } +} else { + print "ok 15 [skipped]\n"; +} + +print 'Testing getting the configuration directory (only idx >= 5.0): '; +if ($list->get_version() >= 5) { + if ($list->get_config_dir() ne '') { + print "ok 16\n"; + } else { + print 'not ok 16 [', $list->errmsg(), "]\n"; + $failed++; + } +} else { + print "ok 16 [skipped]\n"; +} + +print 'Testing changing the configuration directory (only idx >= 5.0): '; +if ($list->get_version() >= 5) { + $list->set_config_dir('/etc/ezmlm-local'); + if ($list->get_config_dir() eq '/etc/ezmlm-local') { + print "ok 17\n"; + } else { + print 'not ok 17 [', $list->errmsg(), "]\n"; + $failed++; + } +} else { + print "ok 17 [skipped]\n"; +} + +if($failed > 0) { + print "\n$failed tests were failed\n"; + exit $failed; +} else { + print "\nSuccessful :-)\n"; + finddepth(sub { (-d $File::Find::name) ? rmdir ($File::Find::name) : unlink ($File::Find::name) }, cwd() . "/ezmlmtmp"); + exit; +} diff --git a/Ezmlm/tags/packages/Ezmlm-0.07.1.tar.gz b/Ezmlm/tags/packages/Ezmlm-0.07.1.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..a89f018e7fbc30a6a21b2f8c2e98ea29c7a134b1 GIT binary patch literal 15417 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