From 007dba196e3efeb6da094d3356acee3611e5b576 Mon Sep 17 00:00:00 2001 From: lars Date: Tue, 20 Jun 2006 02:03:55 +0000 Subject: [PATCH] new release: 0.07.2 fixes 'get_charset' and 'set_charset' for idx < 5.0 --- Ezmlm/tags/Ezmlm-0.07.2/Changes | 49 + Ezmlm/tags/Ezmlm-0.07.2/Ezmlm.pm | 1211 +++++++++++++++++ Ezmlm/tags/Ezmlm-0.07.2/MANIFEST | 7 + Ezmlm/tags/Ezmlm-0.07.2/META.yml | 10 + Ezmlm/tags/Ezmlm-0.07.2/Makefile.PL | 131 ++ Ezmlm/tags/Ezmlm-0.07.2/README | 21 + Ezmlm/tags/Ezmlm-0.07.2/test.pl | 234 ++++ Ezmlm/tags/packages/Ezmlm-0.07.2.tar.gz | Bin 0 -> 15494 bytes .../libemail-ezmlm-perl_0.07.2-1_all.deb | Bin 0 -> 22734 bytes Ezmlm/trunk/Changes | 3 + Ezmlm/trunk/Ezmlm.pm | 13 +- 11 files changed, 1671 insertions(+), 8 deletions(-) create mode 100644 Ezmlm/tags/Ezmlm-0.07.2/Changes create mode 100644 Ezmlm/tags/Ezmlm-0.07.2/Ezmlm.pm create mode 100644 Ezmlm/tags/Ezmlm-0.07.2/MANIFEST create mode 100644 Ezmlm/tags/Ezmlm-0.07.2/META.yml create mode 100644 Ezmlm/tags/Ezmlm-0.07.2/Makefile.PL create mode 100644 Ezmlm/tags/Ezmlm-0.07.2/README create mode 100644 Ezmlm/tags/Ezmlm-0.07.2/test.pl create mode 100644 Ezmlm/tags/packages/Ezmlm-0.07.2.tar.gz create mode 100644 Ezmlm/tags/packages/libemail-ezmlm-perl_0.07.2-1_all.deb diff --git a/Ezmlm/tags/Ezmlm-0.07.2/Changes b/Ezmlm/tags/Ezmlm-0.07.2/Changes new file mode 100644 index 0000000..e55c456 --- /dev/null +++ b/Ezmlm/tags/Ezmlm-0.07.2/Changes @@ -0,0 +1,49 @@ +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 + +0.07.2 Tue Jun 20 01:05:56 UTC 2006 + - fixed 'get_charset' and 'set_charset' for idx < 5.0 + diff --git a/Ezmlm/tags/Ezmlm-0.07.2/Ezmlm.pm b/Ezmlm/tags/Ezmlm-0.07.2/Ezmlm.pm new file mode 100644 index 0000000..6d23cf1 --- /dev/null +++ b/Ezmlm/tags/Ezmlm-0.07.2/Ezmlm.pm @@ -0,0 +1,1211 @@ +# =========================================================================== +# 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 == +# return default value (us-ascii) if no charset is specified +sub get_charset { + my ($self) = shift; + my $charset; + 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) == +# remove list' specific charset file, if the default charset of the current language +# was chosen +sub set_charset { + my ($self, $charset) = @_; + # 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 functions access the file 'conf-etc' in the mailing list's directory. The +static function (first example) 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.2/MANIFEST b/Ezmlm/tags/Ezmlm-0.07.2/MANIFEST new file mode 100644 index 0000000..8bcd77d --- /dev/null +++ b/Ezmlm/tags/Ezmlm-0.07.2/MANIFEST @@ -0,0 +1,7 @@ +Changes +Ezmlm.pm +MANIFEST +README +Makefile.PL +test.pl +META.yml diff --git a/Ezmlm/tags/Ezmlm-0.07.2/META.yml b/Ezmlm/tags/Ezmlm-0.07.2/META.yml new file mode 100644 index 0000000..ce29801 --- /dev/null +++ b/Ezmlm/tags/Ezmlm-0.07.2/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.2 +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.2/Makefile.PL b/Ezmlm/tags/Ezmlm-0.07.2/Makefile.PL new file mode 100644 index 0000000..d28f315 --- /dev/null +++ b/Ezmlm/tags/Ezmlm-0.07.2/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.2/README b/Ezmlm/tags/Ezmlm-0.07.2/README new file mode 100644 index 0000000..82630f1 --- /dev/null +++ b/Ezmlm/tags/Ezmlm-0.07.2/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.2/test.pl b/Ezmlm/tags/Ezmlm-0.07.2/test.pl new file mode 100644 index 0000000..4e0d4ee --- /dev/null +++ b/Ezmlm/tags/Ezmlm-0.07.2/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.2.tar.gz b/Ezmlm/tags/packages/Ezmlm-0.07.2.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..60b58ac1c2ede34e582533d092bf2938f2a44f2a GIT binary patch literal 15494 zcmV;1JbA+(iwFRSQI|vj1MFOTSKG$6@4xs{OpH%$Cw@Q@(p3l~227%L!KSuJnp;vw zSOe(9mK;fjx+&+g-{0OddijB!K$_Kcu2+-DnwkBY{ho2_e-8SC@>*r>r^@5%58vjq zhR;tM8~iUmlmGJb`r6Z{8_zb@o~`5k`udY+Pks;^--w>nXXFQ_C&Uk)>jt;YTQvR; z{nV5De`J1?=XPIKjt}n>2W{}Pr%&hX|2lNzDfa)#vyI13pFRBv^VXj{*?9JYSi4Um z=6(Ll_kUI73~N&)cD{5a)+e`d)+J;zvy1Blw`FKrSjK-q=$57-lnUOE? z7@P!l-{0J%<@Cqf%B+gEl%j8+Rm0(GG+G%B#JTGUO9mzegnKS7-D?rJ!gkL4BkA-c z)(4kT^js&9PQX*>dq^L?n1Qf;aqZawW>hk7@gT$DLPp?|-)lAwj8`YEdY-;+?}*bM zeHnCzW^n13)Rac;NR0|TC4U5xHaFQG@_N#ndaG?T8{LCe^C-`gco=NtOJY@=+m7Xn zoSvnY>>KS)3_Ds9LbPl@NTK~K_L@h>t$Mr7lP~_Xhk)HaIXEzW)o=3RMz8MmrRn5T zs}GFBx>_m;PcRr_dZxIQ$@rr)5pgX(6NJIIkozm9>;aqja^^b*vufFZ9657`<~lpw-W6d{8EPflIlcP?T-tP2=`2hxrdgVv5FuCK;e-IFzR6ld2%4xS|L)7$Re{ z1g{tVXvm=*D_x~eSJLxsC>Jt7m31LFX5eUi(j(1mi4=5pMCYuLLHGJLh3CRx?0@PP zIl__FW*Ex=Dr7qndZl*=e_wGb;ES`q>3pP}A)+mp{2YV^P{1S|^QOqQyD4FGe1faIsZT#){RkGhY?EX78gI4F#2P^u`@Zr%?ajqr)RO>E}`1 zRTNY|B0tTZC<_D*nkvE>3>(UcqBi$HNt{7-j*g*gJK?knDy2#;mz@Q#hvfmDAT1^* zhJ=`fBJ(+uwS#@ok^$^3<;Ht`7lPpL>XE00wbi~W5N&M6125_ab}%AAP4T<)h5S-A zEN3$Od>MvGT(__bNIS8K`=5V5%`u^<-`e$4|KT?N{FcwYuRMA||KQse{uEX#5C1CW zZaybxlfy|wl>C-ht&IZLc6{jt@Zo3UWGmukD~a=$qndHJG|qT7OX3wE){td7WAWPT`?4fzh+i$5>Wh|yeO^hc0th-W@Fntwv(W{N zNj1Kx&w}iA!2L;hPmuW18}&wh#T!-3Ugb}d31~@b_IdUhyLetjK@vHlkHoX5`ZYQs zqLAsAR4gWUj(^=z03b*o?Q^@w;j|=7gwdh&2GshrD7Csm1!k$9C}%EizwcgiDegL! zO^bY5M1W+lDQ3{c9ac?H&$Xl&P>0H+2CeEenP)C%>!CD}OxitJ;y$hIv%m>N5~1ot zC8z-n@p=9s!C&=E@&7#7}3QU}VR zT_hw*AOSVYJQOjOILCkzfbX6M*J`J=@~B2GbcbS!P7wm+4Xi)D9}(VpZL~$ZdC+-V zYt;pwk6X<*#(sTY?7qWbUFvg zXy@}hSZll!^L|cX+aIG+vbifQUx3BMyxtql0Ok zW{K&l&sZp$2jZyS+Ix+!wO!-T=)7Z$2S%qs*bacMCXQ>Zjy5EjZ*PgiW}Ag|(yo_)qf;Z~1O$@8FrIf$ z+6KvtHm=b@5pi%iT`vMst!1=nilI=ytpV=|vCi8B z%G6^*c}u*R!Z;AM{Wk`Yt|pTXKpKWt7rbnr?7h~4S4MEJA@I1lPb~`VlPP-eAr@Y7 zwkO)s^m?jmpWnVj>YXQh=knAJGDFls=0&Pp+ET}D z8Xi!ImqxoLUcxYG7xcf~=KmBG$UV~=s;7?pBiP(L1`7Gs^(-IwJ^5*bqMbr9Vj!LK zWfRJ`78kB-QRU*plZg|`ibIBg(+?n53gY_;s&5a5{c%VWs8Kzp;pnUn4|zUvdK@*X zIPKyCbxyU2*_lTI z@q&p?{1TvMoREASxVr$k7sl?5r7vJcZzr8OQ)7m9G&*ji^e|9cO{o=jmSrXQ7&w1i z>%7k20A~Vt2+wekpqWV6kSvvv2~vRyX9-I_mmb{!Am72q1S;1DXlgh+@>+ZrnH4(1 zMi%EITh39|R|J-q-7OjrIbZhA@tw-ul{03MUzG^BP|LV$7(Jy9{iz2_)5|-)3*g~zXm?aYeZ$x~uLgD1pSLegz9Y>x=9cs-g z8_Z|Lsv`ei6Py-uZ(cXs2(mfUA#^?u+VJJl^#k&nw0A4)`mSZF4@p%Ep{Du5hd;#z z`bu#~>K3SjIHe9wBK%!$Z#~F$=^y+}9aBMF;gyx) z$iGaH0hnEN`UP@2pq5GvPs)T-qk@YL^&L{#%t}G)M;Ci1JC*E^Et1x< zf;UvA@#6H%?Ra9OD>c!vdCs>qsi#Q@ z@mIZ_@e3vQ_`8-0d7?EsGT`%s$z9LANN(!LfoLa0GDlNb)mdH6q}CXVlx3!>Fhn>? zl3$y0Fv{dea44Oswxgm43S5}ofX}RC58^YuaeBLXXZ1GEQ9d|2FH(iMw9_m_JnDS- zpDWu7Mj(MiCU|fjC!x}(@ufBPionpol!EG`m6a9l(Ho7!<{s6E*`3rLhY|_TfM)VA zO84(WA>OhB`pigU6%!m(CS1JICcMb%BO%p%)~n+UouV$%p=3~;h^mo2#KH1pXz{TN zU2oD6k*!LH#gQ6L1;$($Xi~uuszu_)u$GBBx)gCxyi^Bxb0gv$iH8TN6i%6%#U3#t zu*m6!{>I@{varKfI!vmsU1D5$PaP*$7X->EOvVUFRI9UtBp(+8_{dR-9G?`@D}>DL z|A;~L_rhIz>>?CN7veQKH@o}~=iQ|Fzrzne9QnS|x) z5I|R*phsqpoCXR@v#(B%buV&391ks_2+v7E<74BcHHyK0&L0}>PM5EmDz~z#D=$jV zQSHLVQszemoWCjZ>KryN3aB5%b8&?tl~bksdEdP7_4}P|Dqf58h|kW#jk;Z`IIgaE zka3(*(tCv@-_mi>iMY$o=1!F&Gr(6B;o1?-oyA;a!;Ist>wV+^W9!BXwPOT3WkhE7 zyv>u2N};pQX9CndcizAdrI2rQLr0f)COKi$nz=ta_pm>FU0}%#N~a9jkhuFv2~teNnZPSaUB2*8N8Pbe zr+xF11E{ug6v_M`ofIf2+NfH5<3Sk}t>RlJv0?uha3SZZ+)_d70>?%nO4LC)>xug1 z!(1~CooU2IL>oC5xCC`WGf83MJ`Yyt8zBnKj*h7mW~;v+Y?ce0gy^>^@;Q2jq|)rd zsa4!W{(?vy&Zjm6mUNxbAYQ_65lTi}6F%NbL#}L#tSCpT@@i&2tA3K8m;FanPuta^ zqCf?4t+$Gebg{E4I^Gx=w4(xhS?x66ak4KQ+AHcFtJ{$j$OF1JRZ?3=S%K^H$MG#_ zf)`!cPiWe$d#jKaxJoB~x17HXZ(6CVS3nxUo3LwTi&1GDxt-8LxT&?25$hGP;kNVz zM19m5{(5JY#gbV0`-H*zm-czEb!QjSR==T$P^a2&ngpGOO_UU=UK{I?S`+1(r`Bor zw2Wl4`aV`)JxQx=bZe2`QXBbWHg!i*%;#?9HvR~K&M#95>zs6rX<9Bni&D~TiU=kv z%zP6~l3Vb_o`f=ZX$gcDa|VhD3^&o%HAlfv$4$B<*YoT%Iy(a0IhdU^2jTwB*gQ1> z4@1+7cQINg9=1FCbc{Hq{Ava#(#cTFV3K$6Lr`H&8xyD}v(d(qTC^&~xi%ko5!3U` zF<;F{NFY?(#H%#-DW>uMWn${0-r`hEqV|WqT!P74T*=}RWlFN#NLNqu8%&b@$Y@T0Is9PJW7C&ij)y$@}=;3GrCE~Myxk^Z_lv(OcatT!C{s=R(eN5^111vCnkhV)906;pfx+EpQ1@B zMRceNlwB~O?^z6R+Y+{${J2B+g zI1N9xyrJt;M~;3tAw84Y4}2$gzkb}R@6|f>{Y{GbY*l4GcJnXYQQv}w2XulLUtWaP zcrL1UI$Q`_cYX6h+cm%wrkUO5FEl9)>gx~w7Rqa`K5NujID%QSj4Q3bL7Wqw@($9x zi;udSFe!e`k)Cx+ zUy_z|#`hsdg7ZD3NO%{?(4nIY_j;ieSz7T-I&JP>n`2-9P}-vNBA1ScsLUB_0jdS} zAVCK7Jw(Wer|g-}+AaG638A#Ur2;aFdr?7#^j(yYF(*Zc6`n1je0+2o-DrqUaXF=1 z(A5r?@ugsXoV@?SKl~#M`s_^|ghc-%nsfSJVyS!2G%J1MG@V6OL{_I)6DY!8rG#ko z4n{~y69@dOVz*AeQraXgyZR89Zcx(oGkVoGDQz*wqKl-pD|#l~*oKXg)E%VFeHXRC zCQ_vtG^asg^D81>RoBa+ae~hyAw&9{>Ve94L!c@rF3<{n5{*T4F^Jx)8P*;ZWjd3%1itTG!}tb@7$TW`_-am z-E>jirs+`mZiH;>GNF1mpbG#QXq9$CzHlcr&9FQMlFZ^+#o|J`+FC^Tg@(@*kPT$r zw$A(jTmS!))edBPr<`{u=~kOe?L^Uq-I5|OjZGYpBET(o{ShF$(0W}@+wE-a%rV3B ztlIdPmS$^hKwIsWLl$#n76;|@LF8`HUp)5`t)XVOiS!(c2$C<@HmXhzD13RYBcvQwk0@TEXsJUuO2M(_~@072Gj-u9)8gwPk&%r7~Jg3_KzoF zyGhnHQhi?^X3lSfSu%^qo>T~5z*&mTxrWDeUd<*KXxQjhdQ5Hl(4NKbMb7lAqxqz| zEA*v>)riiJ09mpYMJ%bd%ka-$q;AiM`qfAzc0S8b1l}xSIaR$~hz4?xf=1!I#R7J3 z)M|@Q0t4EG$1JwB;T^No{emObQZt^j5Z9;)HCQ-Yd9((8`KQ5eofEYLz$TC4L(fsV z%y@QaJ+G%H=B4Jn4o0T0bZStCBw7V|ACz7bmB%=(0GSz4^@a!Q&i)cSJ-b9-_L&D} z7qXg`XM@4|Ja0w$S&}q3c%G?_aW*)pMm)4+f_z+twNhr^n1Aa^6}ZM)%4hTXP7TKIG(-t^^g{0XBE_{kaLWoSwx5vc^LfOWae(5$E6#Wa%3OB1UtDD^LlxV;be$kD`3c? zfzx73(Kl|X?3pqn^s=6cr3qPPKL&Q4(z?kCpx_0FIOXWY>BFJ9_ z@d$Ht54GG8pw-VYCLnp=;wsi>(|L>$&zL_pn{g_kaj3K?g?Lf)&{iB*F0a>RUeA=V z48cbqY4t9$kd+i1bC*882|b=>X{&UluK@BQz88v6sK%=#j~#-)Omyi$iRHy0xK3k@ z@9TneA>ZtLuYh&sS2WoA{>PK6Hz#51hjy_TuS#L%oo&9@tz*NL$t?zrcVrYrbFx*L zXR5D&%8c`BU(TX;)ZwdF&yJsn>zC98GGWcC z&tJGsV`SwgVId~bEIAz|vun-Ja3p^{LOM+J`Y%xs=o0s;{4*$eOzS(L^zwLjR zDA#zJ5*O$?5?4?VJKoOiRBl>5)H3~qlaKyxV(pqKIeV1&;(QQn-s)g7s(Ff;7*gZQ zx~Xn$<9lhK6ZSC(@U0#5jwS-{cfP{+{lfcx_?02pe2~{Um^hN8!}pDkfH?r&7T=Pa z#=Ie)zwdt>{P5M_B>%b|VqghUGw?(f)ZmWl-=7RmLUs|vDmas+70_$gujE@sQYl~H zw(Zi5)_)v8!Nph#Ks~ej;>i&Gb4gj-WztfppiQ%^aQ@=ebeUYh(drN>{r$l4;B{N= zoP?!@JLQI9bt@a**$*n!tDQ1M3r4B~6XCrPdDgis za>;)hRUH-!tt>c^g)!{cSxhL*rA=;N+J^my4)@vgZPEOqWPf2JGP3=v^LybT|Nmz6 zL2>qot!Q|>hW02HK`sT zaefiix}>qI)=L_(YRze!sx>hKQmu2l2piT_rwYo|di%AsRc!ktdS$_6LhK|x8_kEj zx5e4qWBB~=I5L+~!2pLMDC(vwB zxCtw3F9bwU6cD9hBOn#yUl{cWN2C%3n6NGrAb*qhfM>NLlrBYfj8!igsmA6&)mIaXc6-r^CVMZcH>75lt_QX7Ul+ z(8P^#;;aEZU9ls%B!X(wCeJb*;W3uarcrvqGK<{g1aX7*c){Ed1&h(#-m!M1dei5y zCx+Q*6ixF{lwUTLJqEU5bikb>H5Lz(S%!Zk#n4Gtm)VGPp%~qYi_swPr__gQ0l8Yg zBV0GM8Ke`Q|J;Os;PpvoZ*LEKAz^9#@bS^>7q1ouGvY26g}hqe{>LppJ9vKZ(;2TAm3Gl8RZQ3EF|O!wvTC^~ zCqlWypB8qAi4OoXD*BqP=u3QkO7-&9P(fd-QoY%VdL*laV-+r+X}R{CYg^g`jUvVz zW<_spGimm2R3|lvxYuzbYA4>&_5hTUz?9z8SX}?qX)+_Z z=LqxC8OiGLb(&4`1h!%x$6^?U-oq2lk*0@m@bH)tMCmzDi!bBpNb6Tjc49`v`)x0jVW3M`cu)4ebMBAboubYDGfKl}(HQG7&}% z6+7i?xjK6M7X=qWoaTH`I}-Ny?}-LSA$}uN58<>7crZBy#YLHQc8ZbtU@Y$vU}sb0 zHf9s%f6oaH@LvZGKo7Jh5e4pUn`L}_c>L(q5hVAG*3<8s<^{alPp2Es|W_D z0@I}pR=-wHPh{pcX00|2b%K=v1n!af)$6OwWUz#ySfvnU4>3R|9SkuwiLk%qW0gxc zfgc^PN%4dxfbX0$Lc)>g|Jb3>2=^-Xk$BPwN@Jq}wK{lEfz!aoSW<%wy zqyTChFfmjIsSpNmsYiTn+VWzN z-7QU**UZ>um?(`B@RAY(_%SPK?E(X~3V1PUt_`ukgBVS?5F=2duNz@DDI4^;63C27e0y6^{gQhy#+j0ob(;^J(Y}bFy z24f9lr4&>`Rec;(1B{XHM7*O;mK?(tvZPn%YYp$;5=<$}3}$h;cfWW`;w4#YTDF)| z75U{tdewqhhvT97G)0Sk(G!+0qwawBEaVLY`iw9ssJ2954h-ms?5?1eoOAn<(1fVxw= z`aa_PVmx3Z5;-NGthco>Oi~#G8}B$&G4&dt=>iAu2u;h9Rjk-H4#o}U&FjYM9z5cx z8QTiy_Y#P5!UG2*{VaGo3RQf82RmitB$P$>AB_NZvo>wx7CjoaXHLG6m!gl;d&!Zl zohjho)7WbJhYFL39-RlAEd-l`e=| zvc_#YvS}^OllNZM2tb)2^0HLfiF3ND^2F)=)g|EARhrw$BjV~=l71zP#)}|yLnM^4eD6=*uVVpB`U}@ZJZ_+_i(4lhjMpAG^%;3z1P@&_C7(T{A zVbNJ#XCY$pEG4;1lvgEtBK{K7L1KJ3VV% zC?xj>71aUr`7+TUpet8`rWd~y*)8`(_SJ9|c?Z0!%V^=CQOBss()L=w$Y~wi+!zax z2+9Rl8XJr`CaauV;@nrhL5?ImKkd!aYf?|k%mUm(PR@u@<|h13>b`>RA66SVK?cEC zcB|fmXHi<4ajt2W39xb+zR32=&HkfQXE|*{vbmm-b!#0_E`XbkY^8?r1zdbN2dXEe zWj^jSW4we8G^cwGh$Bay1M#3tZu{JY=s)(0FDU?^`;Ju!{yU^-{5M4E><2`lMyDAC zBEtXZ1kh|mf){x95f0_S4UJt9(rGhc5Qh;G>2*M>mG+&*Tze9$(L zv_Q3S{!%Di$rGeSNpQmq8OKlO$!NHcCE$aKoLimkBb;%r;h|Lm(zs>ooDFH{UZKd~ z3_FX5a}>eZ;~JxGi32O%ZOm9qCSpo}Sq>#eR4uMmZD`@FiEz2+oEdDN!p0gvylC!W z-Eu~<}?5N)ukMdAKuaamZJD` zhzXRYHBBb-kv@d&kR8Pl!Ij7?9nB6vj0i|4Gs{E$v&r|#s2sd?EpflgU}smEe+D?7 z%1jT-z-;mbF^6|7w8Nk{kOqq#@}|A`i43ZA|8VI&OCbi%xEo@FCD-2)_ZmZA9-6Td zWj9CvC8A1NS186W`TzJM#fS@)z4$})#Jn~ETrD?RL3S&$fYaW>xIT_Fx#&Y#MA7Om ze*B`k6Je=EWN)n!DLEXA2~TI^6KQWT_tbP9l~|OR&j>G#R;|u*;rM-oI*Nvl%jgoB z#y!6}1Pn^XEQIQI7L9Q6b~dq$ilE|vCq9tK`Ls5S{f*&-&L}~ZU)O6%p0#e@?%!#3 z2mf0QPIr%=Ie1WrtFmyv2c+UD?ROt0Y*I+O&b>n2_$?S21fU5GYIQS5E1`RD#mWN3 z^vI&Sc(_ZQ8u^e1maj*tl7F+>b`(kqrnte}bvI)a7U|H5!owQF+B1Y)a=2RNV5KemF=)?%9)!WZFA8BQy5IdHXg|Xst6Bhv#RZBY=1!^09 zuqurli`0((P3uQWu|V~`m;La4SKAsIulb2EZWTA!eFDDiUhGT<~bdy%b&S60cI=(_<0Skq$DWoG_>EiE0Jd zv!cuvWmlJgXgP)zQdQY>W;Lt)8gm8`=a^g8(a9s|p51U!Y1-7m2|`3)JIYqIIpc@_ zQiI2Bz^Yz(V`}JX8C|gDlpYMeJA8F~^x`=SS+&|VvF?AqZr7>G&>+E%Fy4zXUsG^& zJxe+e56xVk!a5&#i9O9b>{(=V!c9%Z>VAYoHm4Wy@jc`S178~r2UasRsjM*<*+~>rAjS|+rQHmg;;Iv+*TkL%_d9r2qLT=zQ2A6N(zRiUI=3TXQ(=<8 zrrNENs~BK1N2>xYn=;poyF}z%TK8@;_En|jDp2TFMtG%#NZcK4sZa%hG^Q7LUje8L ztbC~r&_q-p8!C`$HcLu57|l0?`Dj9e;?0PT7hJlW#I>~5@gq5R zYg8o^(|!Psq%6SBSqvCY3FDaI*6WW9zk|LEDveLMkq-v0+zo{t7F#<`aYy{d3yQ*6 zyon9tN>ZdCem_C;1nY!k;9b{DoRu0jJoBR@E#Q2h$9PTiM+p#c12~N^46&sQPVwBP zF!2KQu~?5qTjMxNoyPF`#eF}Y;RS)y!1%<*(g(9xzk<#4WPYs<(pi>Xt3P5a19kUe ze%+!Y>cMmlq<)yjv-^R13N(lMpDA8KcZa~(jvnOm@#K7phiN#B@0*q%e)|MXn2h=b zafSVjPv;l7wbe_^u!IK@Ikw?jJvUBmOS>%RD+|7=*)-yNNER`g1qzLV@>fq_|3m5=>t@j&E|0vc!QD4}FUs8}$-_RSIW6$}?dhjlC zE@G<0aGTD<=q&s>YX1`;Dp6aO4m`_7fZ-Rh9ldGMH-7S7lc=3v2lvkAv*3%H=lAk3 z3El}idttBr?BMy)H;2csKXV@)cy@Po=wEn>|8_ciyI*uW+q?bV&hE}0lnkx0hG|Oho%B}kTZ~CzGBxh^Pa54@w4g{|b4<0`|44%Qsg{))v@@as= z@o+K zCZR=ooG=G&>ntWx0f{q0j222N!pj+K`^~@bBZdUX3{0lkESp_V;xE7aQW=qi$mE3g zKA)pb3=2^0&4Bz13^&63v@o;snh{gKe1m+$K$){jSZ$254dwG>76<5yluSu}Gfa?# zJ)M(NE)H!VoKSEPr<~2|&FM8X_kQ;6EE(m40iJdE|EXcAcf;=9@A{PU_P<;PpSzD0 z`+uhkfAjv|+1c&w(f;4vx!M1}#q%nDmk_4IPB!Ts$REQv7`WmtV0Hjkx=J0-Q}qHE z)a|b7_6GgigYK^C-nnxJg$ylVuab)d%@nm**VtK|0uS1IpQ~=j|LcWaYO<%)pW*YL zfY3(UojebtBgyi7N}F8ZNL! z#A~A(6(!~5M&d-Bq2=6G&j4|ba4 zzltfiqBf+YMbnhE9w>g|9*t#gy+H%tp@Bb(t`QBp zfY$xN_H8rpZFk_Vn3JW#hEi7bgLF8Juhh$EtnR*#(lmK+F~Xoa*nbEM6-M*>4YxH7 zf_iijB`HOX66XxUAq2t%w&k^|qrhUf)7uBsxtM1U@^}hcc#MrogJ)R^`wn-Th2 zfajxZjuv3*?kFDqJxd?(KR9F`2nIa?oD~brbMN=}y8A3GdN_^ZIkkJre?1sa;`by^ z^4cXVKq)0*uiz6jcYK!3X6hlHAb0aq_~*ezmZP=iw1p>4BCG>tAp9y;R`-5&zJvww(*WK|pG93r9i118s zN2&=WI)J?a7va@AA49<)E$HeKv;U9<$Ur#LD0QANuhdt~0@1x8;`$EY^7YI2=Q zI!yfs)hXn5TT*$HWp5qGk(?Pe)O4Pw{-JJpm(Li_%S2xPNC$Ra9MVCl##RfuJ?5WpqNc{;e?OsQ9x`R%C(BD{Wn2t-hIUY5L6KR$dPfaA~)hjHOt5-1+PH1nu zsntb!45uAlvaC1C<*pu&$)XZtP!#l5TK_^LAXx~I78lcqyhnfx`n#y`X5v3TP2^Ic zJSp>;7L1(f!9z0eJSK~`92sRfz zz(XqB&th;)WFN)#0n*|kIpbIY=v#nJ?|?yMHymuSx|X961`iLP96eXRsI7lcHVdh+ z(G9~pn9@@H8XP`L-4l#3A@NVPMj7y5!~I`_lJ~oO;;b3c z#M6vi00z39{bkcVOo_>HrWj%bj-wQCd&Wi`WYB(=0S9;*qqSUJI6t;X7mxA{>}-1j zs@Bi4DWK{kOK}vyZkRc3!<5+Smk4x5MKPx!V)t^MVlj-$uFdYG>&76U$v?Up?$aON z0OSUPZ<2I~Kv>}a4-JX6RHP_&o8#W*rq8VEj-XPyCarE~S34UNpG0J;Eash|_$l$=deR_ZUsrw`Xap zjUT8Un!0FQr?4bNfN=<%1z_u~1Y3__@F7sEs!<8Fs)Ea$NLAU76fi_XyjKPBo&#}; z-E;K(>5E4PPbrv8xv2Jv+h7|a6C{xVbyq6im#-8hM&8jIe<(7>B4lJV9tv#hrF5~N zgb3TK5W&Xjcuw28rJ3w~_)e}m7!YWC=IB@gxt0B`vDZ@<(_9Q&61~AM@j0rVdqdXB6ibF$OPQ%6PUwY;wSrKzoWkYGYibE#&ty~EB3NM zi~CNf)L)VO)IAxNDu=N*%?J!E$}mLu-r;X0$cXQoNQ$j{hipAncf3rhJ*XjSa+g$w^URw%)GmN=G{-2 zd2;UO*&0XFw=9@;+`LLT9jlhIA}gL#VxdDG2RkgxXgSS$%bk)N!rlQHQ`NVAEkSKV z+Q+DxlX&>ULL@dWHA`|^t_g>w{)Qh6CFTcZe#OZf5H6?h3Lvb8()JoqGF#nxM;ZsT zkA@efE2Q?w^(ptIRIPZZ(t2(wWoFD!45tLDz!Y&PuqM^JbCH+HVTEqCeo4 z)57;2NL$?>3K;8Tu*5vZOKTR_qQ7b_mJ`fttwn$3T3omRE2{l4KgR)BN(E}~ICs3e zt6;L63SJ8)yB~+2p9?4-;G??~fS9?jT&W1qy;T5RP8_cV(7iH1kqr{br5r<+b~;J* zC%pP|b%m}H{6>?WLi1Mh_Xrli6h*?NxqZS(=sT4F()Y z_`lV3jKa5i#kYZ^6s-LCYtzW`HjFG!mp$!v> z9f7}o9$y7w=97yLl z>8`Hl{gLsTxSClDBA8p4+L$;pn%X#;xcZQilCrRH0J%81f!v&=q`?2y|KG?01ah*o zlal^d|69fgtV}=zGbcxPS0{TWcN148E8qY3eomhM4*p*BndQQ&}KJRTmBJd}rA~QdmvND@#&JYVj+q_D&P?k^Yj&ps+eF$d>0< z?A7Jx=3atx$zt<58Ri)L1)!A{voLhRpTd@A_yT)pKZ^#DM*=@*uC<=O)y`M;#5YjR zLN1tjzEwWq(Y(%;;Qo4tI9Gj#4gd2S-!PHM8xtOEuXA+6ssxj&eTKVI(=V$_Y{Vyf zGWDqM3H0z}dk_;qPIpMRuQOftE-7=?22#K~>X8Lp)b(5moo@55iJ0~Z+BUjWa&e_= zB#e)^n$;dGA4SKT?hUpA>EkwenbtIR@o<}G&@I#d5^kJ-u1}u5>Z~M>u}<|;)a}c& zUF%=unT;GoZb)uRov&!#a3)t@RwGht%2L#k%koyFVede$sPg!JH=e5j^L8}S2rOLn zGf<`67RyJc>|!Fu4Ka>yO00*yiF#%|tAA-OPcjt$LuM;}T*kBHnU%1R<@_+TjnaQ_ z%)SuDq*CM*$F<^T(`G|RWGl=Fd)WEP-(xw^@ltt_dhbW-p^f~yc?@C*|GoK*cUboQ z4ZmaKSH#sQxw}5GLLFTN=;0XRaJ22FT!C#|GWmPE;(qK*rG8_IW6EXFfTpjYqD8&n z*h8CpZKk;zV3aom$CtTk-7b}6#4lBx!C{F0TI$<7{o~Znc^YL~p?$t0C|xOfI}LV( z!?(sv)agJF-1m47@W6ckJ#~6`smAA>GMbvO?Tsj}2cRu7!_MmwbyMA@I>{^#XX>%> zi{4ie9rT|gD+VujaIO+1=?Gp7BozCVjB=L|UdeK&~#UaI2;NUxgWpMqf-vi^4Ivm)cG!`t`wG!#{*Pq{~ zQVdNh*popRp8;|*7OFxdH|THQZVwgB9N4P=c141e6{+r@@92xZg%pC=Y!=>Sz6g%{ zJNkoSKE5+gN$M8Petffj27LI-9e?-B1W7Q5?T~U3d~v?LzrS^t_sbtsAl>eRb_qZv zUyKsca;M)U-`=PWUwtU^VaB*ox8$zUfHF2b_$uvlK%notTxlS>Q`Iso=DoTt4 zp(s^`4E*h!68R(eC|tHvp*+l&lKJ{Sc!~V%+asS~+orSaNkn=R(zsBpyDlS4!rvq>4bc8iE#Y} zYV}4MiAw3%)}v%y7Hcv^C0m-Gh0-M%(j69P*o*ef!L0bVe8c9zWXaoM+fY&%J$pP( zS^K6K4!i?He5l~qWe8)2ZueX)Q}EU1kYjhrP}X$@JWAi6^=!5bug;_}vf@YQwd+v0 z7$0`cIwBM>shXKp)1Uz=aj2}){|Y8=Mb;L+Wi+j1DU^BDJ^|}PEY$MBSB+^zEON4P zWl$Eg7K(tpE+ib#Ni&o-36^HE_%j?_9uBBp0|SPzxm=p-xM4+d#Hxr@Vr}vGkUGJmV4c|ND18#Re}c8Zw6~%sMMV&7 z{>8YX|BJ?U#oFV&{S|%*_QsV}OBsR&k);R#gOryw55>U+PorJRiKA6uLlMcIM4aF& z%t8K_2JM=Fc8?;_6g)0Pw-V0F$uB5VuZp_o_lu5G1+N^ALiFdL95fm}L1wM0r1%&{ zN|TBSLV8Z}cz8$!%N?DCw5UIWH>fUGA1Oz*`qlqIl{i(NhIh0I)0P9RhCFS-MMV}w zdMffGDMpKKnOqbP13w+LJU#{$tq_fziZef2Rg(Ttr0GQ)EmUs5m>i8jQJh}AC}ppL zUmUM2UP)3QlK#-l*lE9zj|YufYm{CLqxgu9I{`!8rO6f=JPM~=JxX#ieIXXe5)Aer zOM{NZau?34N{kiJtxiim014GTj4Fm%SVdFDPbnM12QG%FR*hM7=gjcrR0QS$)=Gm` zl&BU?w~r%14UHS6pUwbLCpw8qP@?HB4>merM=npym;?rifti3NodJSMC&v#ZAVAJh zp$@M+OuvJ_Lm(&TC*oRC$f7cWfm=D8%~gaASthTV|C+x{5A zqtuuGEm|fU9j7%m1_z#<3Z^;m^0&|xSJcTK5g#b5TuX>@WP4Ro?5C-A71#QOSURfBCYd+lIN5Q z9GNHuUO2MfHA^czT83sA-bcF_Jv0#@Mhv#5ym$#gT#X@0VKZ_lbKAib;?iUSmG!e# zKC%Wj>()BM^8BGROF0!^PLfmi_lrZbOhN|AiC;M~=CY2DrGg}i0=A4W2Llk5F-3%* zSf8hGVzLpY=!OR$An`DRwcp56ts?&;jx-0tBdNyzlnPJ)p^Ar0Pu5A?t zVvrvS3EZ&xCrm5D&r@2|wmrW{$p+XBu#6z%aLSZ?lv3^TvGR`C7+lgVh!9|;YF<4I z{!sIxVIr12ex=S4h7MIKb}c$Sf;1~RcYf0RuD!Z!~$X) zaGKB*hs%c6@+U&w)m=o)0%u34s#q$$7`z1=cAkXM6?^O4fdW#2+Z97qaea^ui6O7T z1DO^fG6RFttrbwv@6_}$lcu2?;X!?+)ao&@brU8S%1xaI*5U{Q6%q-snNt%~Y`%N& zU{ZS)V6&)}NZLX|E)(IiaQLNgAviLfZgkR@S6yPFn7e72in-4n?9W>GuX->~9Y|*= zh>=v(pooO<*?5}R3Aj-lb@aNdW#vJLG>J}#pEOiWTtbS(B$GFQwD1OnZm1sbA;}}X#x&2vq%FGX641O-Lz5$F=S4>d0K+!cd zR4-Ho^dL?+wZu8w;LT3J;Eg-G_=7YQ0ktznu@cD~m!Q;YXv}3~tY2H1G`+o^@N?lb zCYVeq+fUtNnHEd;8xg3t+bsrb($0QTLnGftXM~U@k-aWY1XUFgYT@(Y1vXeKIJ z`d0V}OjgWtFo%k8_9JhN@ZxCRt_G>&f+Af116rxFA#E39IC=_XxG333QZ8DpW}%lT zDEvxD0dD1(D&OW7{%t6%sNn#=v79<)rEc%nM2xgXPKr)y+ z*uuvVB1dQu>`FRr)Ev!wQXGmzhY=TQNUlaqG7=_5h4hHRvZzapuC&4sJUm2~ywH}oY~tV_!G5Y6|LFk%Uh@spBVW-Y^@KxeH=(biEkWGh*N~MVF}Yp-4=Yf2H^k) z)6K9bTLZ2;aKTFTi=`9EwKw6I`$!zL&>Qvo~A6$)_Y zs^6woScVe1w;Mc0EBQSCs#ClT58O6SSrx8=R8#8mr{W@ENyN;-PYvZbVP(;esv;_w zN1kYIV__M0$=|+UY&i0KkRv52GhnAn+W&|q6KLd8yHJlIL+6H?MT;7TRdKzIuOMGj zOIoxYz@Va8A$l3?DG`_Wo=zs3cpC9uiowD}WuUqyW}(z+j8P;;BSqyFdg~w)@ZviG z@R{slW{r47$)Wmi;xr@tlBQ^w&TM*n%?i)5+zG;$lm{$aA+Xo9l3v<;Vonf*B7fR3 zTEW~IP+V{%Dm2pbP@YCv2WwZf@9a%A#o234Efyk;_!#2zXG9Mhxgl0VPhlEtehF)X z=w=yW*K-KFDgMx#lHtNG(h=0}JM&0@FqzSGz#^o33xkUOrio8j5-&_ser3bJCYDk6 zD*9r!C^t`+_KHQ*q5O%Xgc^fncVsh_P_$y-ti46kX5DVpLl9{GY&L}}Hi&g;z z`)|9qn-j-ZR+-~>GV5nBlf|uz(VXs1Z3PmpXBQ|G^H!}A+aHaL9!z&X84D^tGVZ7Q zevenWbaUvMltjom3_(RYm>?rc=KuvmSXWe5F2#*LmS`9hTc#mIaD0@1>141dWL74= zz#hhXqB+Y2HycK{u&ZdPNb1894+6(#E=igCc*W>M30AB2T_GcLWjdSrgtu9@b|XOj~Yj)e{LyQiiv)IL+yBBYtIye(cLkg?Al-w%eR1|JuTmoCtK22AG zmB*jOdNq`tQ)Oga@bHO6;Tp42&;y>7Y0*Smu%pQdm3KP z4$KW$p(zZ*YOv&)bJ0?ZQ_25|zJ9<-C$wB~l__Wsf(+P;0eg*sa#hFM!rS{IP9HEq z^;94Y4fhL}SxB6WpZ8!rE5X#70ylPGpy_-IDNzm9E22(EaHSEbGG?TqLPLv91zKYa z%?|g~$R+#S9;FnzJKuzEm)t>wsxwGP=&Z|a^5Ju{G2+)S!RA996YN0*+m#&hzujWi zJI_oIxyxhZMT=IjP{^;FG&YLtLx@8}%x`g{p2y#sBNwE0 zb^WyrBLnCjI&Li5(dk?MsM-%PgeVOMDG?1w)lfsyF)&ImAV7wEL1@~R^KV6hX)MNb zhV>%&B4nTwG{g6X5$a)gkZr*;w_}rJ9`#VPg;uR5C6`wI5Jdzx9I@#e9d>5-BxV#D<<{y|5%EYhUPV2@yy!syTgGz!U>pU| zMpd@2Jh*aK?FWFQKL9FB;UU2ox@)Lu@vV)02XgIAe}FjqyFIiQv*&a7%>&#%Ft zAmSGMd2%Tw9;`AcmBXmpptN<_nYx|NiGJilEX+GJ#F-RbeCeI;1+!Uy^wXQ&Rr( zGdAGR?Bc@EurHfDUx@BqE85}urJKi`q1beDA1Z7OAY|gvUzA6}v8zOL(U?4Zd#ws*v?X7$}xBMldx&l_WKlZF{h8tXs0+;i; z7_9EU2L;cDd^WM@`3KR-wt=7JYXaX85MT-SO4pJfqg`Lr6+T6qfCARG>}`4&lknha zbn9#hK23HB+}nk6fI)JYz4mYCdyIi|COULVGyrV7aFN0I0!g$Imtedz2upx zcaVvDK_TN5_xp_Gl#C*&6E6}43Eb~(`wsv)gvOT;8Rcop*G)7${@yp5NJ!x)tOT^7 z@;8yqVEiM--%%sNLy}ayJIcNMEqipIw;Y*NJlq|8z_{=N>lBREBuJ%zT{M8~l;PMOPULjZL6P%?WtOk9h z1&>gGUzyf~%R@>@8QxLQpKJ|56@9BTcl-?ns()TFfP9yESFE&nKT=nH%C_QEah5`k zR7%E1+6olT?=p$yPA69rOmVJ1=nm{_dWTwh8A9osv4CF?5iusUW`mq%7Zg5U!}?9{G-!mD40<3%_7E~XolV3tCp+IGA6seueWpw<6JVqL$hUz*S|dkb3l6YqSj42X2pL z^3iaWD)(@2o0k;MdPAXfH00p5j2|bgj0C5lp-v`x`*>q#_eu~6K`u3|De$^{mvPqF4 znBz3nS{a@fX7FUK1btyb`e4|Mc`+x1XZjWTGFj>79pl9tKk}q#*v8LE`=)e0ec$g` zJ-aCYPNq64+!#5=CI~GOI{0eX-(jJcUR*jSS&*Wfn(sOOGqO=*LY5(?L$_a_L#LT9F{ ze%OY~$~KqV(Hq&&L0f)l_TP_ShwMheBbKPG17RLXRF?{Cr>I-><8b^a1za{0w46ym zHfJ~(bfGaBauD{}6^s?87$ILzi6fyUKV@++8*E;ZDtCJ4u!NM5K6W|mfW=fl_^|v8 zv7iWDwCNO9td649fT_Q@=-8CYj)UE#d+Z-58jOFWk3~9NcCV1W;>OF?~hv6Yf{nLN(wrlIi zJp${Q%rJx*jl6!8$&hkx+#S){!HA^?1KZf15zOI1zgki2OTA`~kpX&W{MY)J_dG2R z1FR;p=@bNooTLYX%#=q9IBOXZ>=+7^RkWrta6W%PD>|86b57B0dFkxjmk5Kf!Wl!y zor&WR@(O57!8YOM;E<_My~)~>{JsTM%qK?}D@b%ZXklVV3NY%zv4s(AnVnD5&S|~M zJ+s0a%){cX1xgF(;7ZsVZc~r$BD|AoD?LAjXGrOW7CXtFOVF6(=qwAs;B6Pp_3`}OR-0+PwmE+y=<6<2ZwPotW*9H0V_6B2PIOQJ65?ck3$h?E^8LUyRDy5BiD_5z*I!(=F;RQ4b>=h9ktq8&lwe}B9 z4flmrcNrYp1p2@+U#Obn!h0@X{~-BE7fpL^MxN}8d{jWMN>^X}g5rf>C3nd?NRzjM zo%iR_+8Ot9mF3KfU6QPt=LmOc8htRg8iS$%z8zum{`1>iyI@b zEh0V+C^i^fxrQn((GDd^SgMn;lV(V`aQX)4!|=BgC2w-vA&pE`1&Sdwv|c9Kdg2FV z&LXilro;Xn|nj5}HhZAO_zdiF+7WYVBlvJ(=^1DSV z?c`+=RzLKqEy^vk(=(5QK-b2lTi51EwsX2E^}I z@Ep^0Z)Au8x@nzyp7(c&>u(^jNTPZn#Spg@ihpcN2-QS*Hn_?IV`=e#ZBq*H2CqG7 zebTx}?XYn1p$L@<0aBOcAlB|;vaxPFqlo}2NVgQ;92D+eHE43Nn6hgfuw~8ai>197 zC*o@2$xIW9&Ut)J^Rwg@N4-1CJq9`^ZKr3TZMp+wQX^+8>!=&M zR#P+5ggZe0E>fVy~$a+wsz)Ojk9uN=dR-XZ2 z5xSorw)hV=jYYZMj3=+81d~pql{Xamv-kpnV;`etY{WHeEIE@T4|}M#>3s2#*G|w3 zge$L`n;(G+j}iGLL5K?cvC$bjFT8H{8y)_peb#(C^lMF%iNGedR@jlW34k>8$K+Vn zKv5TFIrI&K@OL?1u1h8nx>X{1PZq+FPp3=_rvv!#12D%m!JeT8cK;Z%UZ{AUiB5!V+Yl3faSn zB44BnHv(*e;ULFl#XJA|d z7S>Nk1^{>r)3KMMI}vC8S<~$Bs(8hd53y@1@XBx7kG}SRmCBovcRE@V{&u^|0IaCp z$ZBJrTcG8k@R<_5hZKU&RCXw8;49iB1)3bfUST1x6*7qDu5qA0m@o10n!f2zo`r56 zLjGNnwTPY?+X2x=+1(Z`fE3=Z!}`oLv`9h`woGWIHPG&hYV+c3=!n4Nlv~qZ6u}y0SnDHl29@Vk=zwh99$5Tx=HlVG%{#1i&SGS1g@DM zuI2~H4^VsR9G=fs05W!%Gh*eh?TB@A7heb|DtC8nuS(&hwI*~n@9_#;6z@ujqmEPX zDKtY}d?#@dCO#R&*W`=dX0*$2EyB>O+h#IucxE;L>VCQnb!d-(`Z!jGLBVFwq7Bwl zK=FLKjiO|H9h>E;gD!u^nXwyem=V6Zb~t~p>jwBw5o2u+ahuX!+bpO?=L}Z9y(|r7 zeSvD(X+XORbpv7v{0HPiHp8`GNS;tU6@T8o5ku-eTOm+30y%@CCMj6+_brnw_zze` z;x&vZ1KNtF_z?n_nKbY;K|s}C9CXsTD@ceC|3<(~HT+b@D=pChWeB?vg351?Xc0+xCuG3H>gyB`fpAe*k69TkG zPz3{tCQQzY5F~GVQJPZ-n$F4vp(p`&VudP)qJb}5s0X*1LSGVm_ruiSmlz--e-OIR zK>fi`#u z=UG^s==s*fMNJARyEI-kL>WWNPlCuWUK(c7ZPGFb(p!qKWP8O}N}eSco@{}6u)$$N z&lKl*Q+71`r5t?*7t1nbyMWtrn-4v@gE*wHahN=V)LiJs*+J;fd*HjMW~^wf;M+SU zdd8vN9^DuRt^`KZI9J3eY3reh@louin3tC8uvpTfoVAUBBPKjh;2zM(AY8NBL?O*} z$`(z@|8)-j6+BM67#s|?2H0T0`ma}a*HkT(IuuxQpVh2NBSbM z&mF^(H;oi=A{a467cBL2n5Oa_E(<4z_%7WwYhZ%UVNc8|DI=8fJP@BHd^)jv5l?Z$ z+z|$AKoue2K;~#0T`ukI0xVB6loMxcLtj`d2qG{<4lIv^5?u3ommP6dP>P4>cANSx zf!iK+;TtBI(v@Fu;0f&MyG4mJ^EMU>AHIo>hZYp>pBf;JDQ)hk}=Cfoi!#?1_$)sxQWC$dP%Ukm)B7kIlg@XAo*Wqr%8w9oDhO@`MedayDJq z0Gr&DM|_j+mK^y;abE3V<3Mj*DTzl=c1Z15ESs7lSOXozL0*B#@xuYY=a-tfDA?b4 zZTVZjg&2*luqF$*0oy^k5r{3Yp^yqi(P{NW1lWS}aM4cuC0)X;73wBGKnh@)(fbd! z!>@h%9Q_lcFtl}Lv%*i9tnlSa-!o{ozuN!6_e}G}R=RrR=MSs9xV!E0< z24@QK%*`l|hL@8&yaNS@utLmv5TiJ;`?7Q#NVqwU4mT8^xqGQhh$UCmDkjDxsL`+R z6RYwNG2xSEA$o$KXYY`h1qq0y7K?(!yXRt`1Rifac|EN(@|@VSX^FC8F=ZALO@O{@ z@lLX4!pTvU3mhslxXN)Zr4sYM8VX}-!`eB^rdbbuBRp`}-#$e5VL@4Dw3yq19LV^e z>hZ=If@VZVw~}FQjL5HWmcaHi6|;&q^GP>tIbdZeySlneA7t&D&B1vhU?U@Cb&1A? zf5A5?+=C^8fo3#24pUN=tU>(3Fr|k+2>e)R_i5G-*__tmjpNFc75oL{X(oAajikwv z2msqTWIsdV#ld~5K4tlEFq_HY0c9FmrmYsvvf&wjuna;Xpj85BqcNxHGdJcI?Cb9y z_$efM3(Sw}j7D=rwMsD!S$7BA&l!Speet4aPPTx*@a4dAZBi83tx$a_%t5b+K8)KR z#=a@&G~SdG{1Esd@arqeq@deyOUQe82&fTa?KWk#WIFvuxE2}M;d*v3;m~nU8zs$e z8373pMaN04g}xZi5(hnMh8g7gAFv06qj%yu zxCBi>GTtp;A6uxR)R2C?NH~27X3D>;Mw*B$WpXD|>rksv?p$`k#LscAE%95*caiM7e>JndFauuycst1_W&)&)MoJ~!7S{?6@(#(@2+g} zfwHuPpuzGkeI%vfY-OZwN_-hooCWSwgk5V{E}Q$XkWyGxjV-xhuK^aOkx35KWjXIk z3(>#;eqIGYoU6Mh!qQFgt&OKu=3ke}Z?XpA)7%*%mibAu+#bc~OGQdxChrnX&r$+4 zshIc<2|2kZkL&0Ie`I+4ZD~A<=m$9E<%p1FzQj zG`@ZiU=MSf71GJYW@*(YJv@%m$eb(gPD3K`@5pI(rkhOVM`&oHbcmNS56sQw(P3XW z2T1OA?hMQA4$j*l`Xa(QvSGpm+3$+K(nQH8OKyYaErat!zBrl`I}IlO#NP>*Dk82Y zMIM{crYQ;(YN>!%xIpUeloj2z4tR=31^*icZ|xN>)Iz-fJ_;OAmKo9dxA4&Zj(Z-@ zj=1I%Rf1Jw=mYWqr@ldLVO;8n<>^2Q*{8Z6a~~cTP#!6=ysX=M@3oFU0HAOao_Pa^ zQzK$$LPLqRr(?9SBfo)&#s#(Heuthe-XS9T5Y^14bBqj(Xy|72zGS2=)-9jd(3cHY|vg{8Zdgp`c(E9?Y^8e8)}v_crY8 zZvH4e3b5&0cH|EAb=bs)8ai(02YxPsTkZOkW)xypCN^33K2u5=#$hadFT8Rt-xlx$ z1*@`cJ&A`=3QIsYB4#ee(VCj1E{mwVdggdqsqNblP9xqxQc05vBw?mPL;(GiqgL<=JhXRo`O$pH@+-~{mN0|q2@{!p%Eh+% z;%NqkqHi_Ck4utF9%|~&70=Olo8>O01&3%$%Nbf_@$7YBOMYHzeCc7r+Zn;-I-bVa z4Z}Yv@%`7_7kFFIk7yO+LMwVUcs6#)KCE5tglDMReRgj#C85~7pwntL1O>4pHwTHG zI*18Z{>DWzBU;96Xly|Q>S`(ZxT{WgjAJ*(DU{Z50Qc(C>|kk zc(AVdTb0m5w#o%=L6K@jR1}-a9Cr*^aj0*T`j9b2N@p^zb+DOWdrkmGwAA7;M5*B6 zocx4$x4fev;|#)o$yG>ua_NJBLh>rux9MBEieH0rR4|SbWEctW(wdvhf=Gt(EUa;% zuyO3u=_-W=2OcL+S$LTa`*s7)Lr4xg&d{;CDfL5C{0|(<2(0U z#RkTYF(k@LWoLS;H&6!4=w28PdLcnauE#I<$@AV;Nf>kO{{p5#M(&=@NS%&TFwM?( zli?xzas&81#whnh8o@~~jv||Bd|D<4ZdSlV$Tk?$8fSM;$>{wLn zQ!APG3YRM+TOMWYbmsZZsipNj^o|v>;?xi0<;0O1901 zc+~c=86Iv?ZwXuM)LG<0TvpPbGu}eWDx5rH=ABSG#JWQPN|1K596VECilwZpQ8ojY znarT;TS62EmU%o>vK`Mt@uvuqDiNk(q0YB_1F<8QJ?uq6?T3F_;@(I#Ms(WRDVA1Y zcXHD7cT*9YFVF`;gk%wTIQmZqaK*!%%##V%CW_E5+9^aX@~yy96f3TEp2)>)34?x@ z86(O)bM1ekw#>Uf)DGMk?0tFt0?Da#HJj3oiQ@J=1A zG?y9CGwT>j1va_yhYiCj`e-2J-SE4Qs2TV19YFP97VC@~h<3f|8C#+TD}YGdw&GjT z3q2Zeryvq&m*=kC!}$!Nl1wLjJbQ?*>3HgEpFH6@)r>FvO;a z$>f=TFu;d%b0*FSir8&aRRVY|M^N27+|==_$zB#A=dy7{t1r&RVMth_5?Y(hv@F1D zleZ>f!SK^@Fw93N{0C6{(;^|_;EayBib;4e577dVQ9eg}pr5;(tznUgTtH#mWZ+-} z%B!^^t8D5SEaWNy!3d7GQ7typEYOWseFC`Yv=Zqa#j zMdkBn4nzgT}YhGA>v!UuFrfs_) zZBb05ZyOhxx~m~8kxIJr6O9_SVIHH5MtAvWk8Bc{IE@5#1 z*CbOWpm~!gGzEEK{dhaxu%(T zOfkyNRRbnJhMTIR^%e2E1CoH5>*7P(A+$fJy!pFXA_qjEgt6l2#|;PyF^Z}WLhCIJ zSt0!BfMMY6MUg4X(QYjJ2I|mjVegQNHVMgE$zn87xP!5hI?i$7ZXZ=HeC~hdGKpa_O?@O;Gnu6%KBNyL ze!-yEhl8#%h)tmUI4Zfgks&1xo)>m%91^E&#|X54988-vo@*60p4!^Vf-vFaRq25Yx&TZ_=mE$7*LU#^YgI!-L4N_mz-C99`tcP# z5{<;mCk7mtWagMCY8eAnYFnl2Xz%xT_}88g$z;Fme_-|T;njta=yy=U7vZ$zr5R^* zi|?n!{{*M8MT4@F)Ts_ZyRe|$-SPPfOftrz@1Tz*Z}-=K6)Q_ag7k;t531wc4|8=c z#s@c_n{=!4KYiul3V1C)xjSIY5W^DRw>yFIex`~y0x1>LJCF6EoYi@Kee(?@81@Z% zeukIF-@3c5d(Agm_4==;_CxEOyY^4GUTXoDQqSv7ypEOEY=?U)F&S&S#V1?M!%=dA zk!9*#&7aTX^nss_J482nyEFYu^rsf8vR8pCyObDhR=}G_D>Joc0CBsJlkHt;ln@9t2f?yQ<(4_ z`XhdQqxybz?UTYK@%+2S1C{e4)6i;gVU&+LDRurQ;%nb`(~1~xra+L6XNy)#EtOCH zg$2%eBJvqE>R(S{7kb8*Q**B80j#N**<1aJ1nSBdwEk2YXzWc=Oh zDUkK|MtxNK#iQsLK~5_MBYp3_Rlr%BLMr6R(XyIStwI1_pH@UgTWFH~XZykFjqcae z%vHN=Ppt@DdB`JJzzwGjU#iHY)f<$n0cLv7>_5KP+s>$jX0GV`*^VnCRGdaF{8E zPgsk-c)8gfiocmw1@@ZKvDvV5)#o=*w|wCvuu z{Wfi7h8$Pj-@n##owkF;6=n{x{`3%)RgbCM=*5UysRu5cjkR*gu$^z!X7iR^8yk#F zs@ZrM_MWx447Bo5Q#daB+%r60XUQG|B30bl6qYE)ql_y3rTCsVebSX^FOc$=ZP#l# zC`P=-mS0Q;!|ZPs%T}p_2vl4Sleh?F^P+l=#+3~t|v<{|H?t6)8B7N_FFjpl}uU>ZqNw=jhe&tQS zbxxK@t{^_#>81BIZN@-TC_H*WOVicsd@;Dlb>(ZlM>p7`1l`?V>!g$HI6RwOl&Wz# zc{H@XX^sxNIc?XXuk^e1gQQGnZCD_E}%t z`$cULg9~^)+)=N#d>8(;I^@^Er&?7sjHyueVf6ICL$I;vw%=(RSM4`DP0;7_c(3ZW>7VCm>QLem z>*ZwE@e(L}eQOBo=G&R6yIgmD5yCg^$KaiU#o?=Sbtl(nzi^#dRYu38n-}yZlJ0vH zQ$uu~-&xNm;G8o4V2C2K-k>pb(f?~*>_dmH?4p0pX>d01ZKvX7moMj`j=|iCV0b^v zQ0VH@WsNNG_})^3jWSJ%}0 z4wBD7^H7LYSi5(Jzaf*m_~CUTgM%dysWrn=7QpM%-pKv)#OKN6|Lf47q{ zBz)8Jkuqvf5`IM_m#_~ zPq%!kUg%<>R0{#}33k|P4ut1z%3y^G@nAp^O(@=$#rAaP5CyrFuaW`c zxJJd5+x^Ce<~Wz_=21W$y+VGs|5fWhr3jTPQ7a|wF8__22I4+nzmvZ6n&&VWC;2q> zUGDF>6}|1_9fz%n_uOc<2Bd)7tZkFkWGi+n@qU?t$D}-W6(xYM z>0YPi9;Nx4jRIBnZocBhgU!J{j2&OiN;dc9KC~ztO9i=V3>VT#(1*(T7-yB3<+46q zgVwCyPHS_|tXif5)9!tFU47SQ>#;*rcZ5F1j!wmC;J@vWws%V^r#Rf74|=1-UL^LX z5A{1$5k$pW0U5s9*sNw_k?XHS66w1^zs-DZ{fBC29zV$g^MBIg1=M@C5i~>pPVgew zLMh;-5B7eY<@rWZS>b$Gu8&PqnCg0Kntgq1X4Pnj?2vX?6Hrf8pLBOpF3@{+qGpuk zd5Pyb^t?CH(zLtZ&kTtS{L`>%o3+gAu9o3k{`bX?49mm#GzO2^;dy#Fpvl~50)%$n z^EZestBq*9mzq_mX1(LAPNpJh3qRoB)%tsbrejWE*9^Pux++?O?+=XvA2m~yG9q6z zuNhpD<#yTh@;?HRtJkiaf2XS7bH`zhA4s%ZOloJ?nO}VR*X0baeR2%UzM5dD1#O4t zy|;U+K96VCB(!imF!ORy(O19!Ema)K8aSR3ymo8V{&*edK%ejUFl(|gw7@7=a`f>G z<*(Q33P=!j$UWG(@+0eOVm~l^>%FE(iVR`~2r}mQ1PHfKpX6|Cv7QdI?3O_K2DN>d znF9KIRvS1w`*Dr8+U{#Kd!+PfJdWbh@cWiXcK!Xn@1_H&wG?$5@&#MNK&*FcxhM7G zgO54OvIU?<>dr$n|1%4q5sdffBXaZrax-Ok;*9MxDuD&M0cAj_U9h5#UjN5Tb#?T*T!MV zme`BG-{{lhPq|%no!?WB`S)wipRpZ$kFKD$k^2@V!hVl4|7|@$kdxb4Q<+ELVPWBw z-n9|@Z}sVZGgVj4bzi5G03&vXU1KBv@dhD^9eIB{kLTb2@g7A1E40&8Qtd!|4nVG2!1+%;cspz#zkAzP1bG2TKl=uAjqk>u z5cjQL5*_}0Z7L&3vpdgsUw@^xadxxb6$OpE|Fm{IKh4kgq%=2etp+nb7gKoGI7-On zOw%(H0IQ4U-_xDAb{@Qc)3<$g-)~-9*DdkQRk>VV#Quq7?{_Y>4e$4hdTi@A;A}C% z>DLgMp8jIL1HG@5unz?YArmgx%Q6ur~yn0Hhe;gxhir;An{Cje2YWQy3X=zp?7SQdjzi)giUKG$g&*^bc6J)lt^{~*B z+Bx1Hdjb@Dimx0C%r4jA2leI(-Q1-F5akNI)mlw#Z~jiK!zf^~t9)DY`x{ME;p?fX zFMD=rfoE`CyR}y9H0(u0{fx&yz-_GjguL`NiPDh7XJqnxXq9THkadv%@;OCdMbO|w z^kA-67M0<1!W_hTGP^Nf28o1p!Z=IvQrj}#^~w8uhv$>#w9&ZUpW*8C{+{t_(eau` zaJ0(yAPfiy3p^-kFVDn@?5$OKS5c(y;B~NjtkIYUGJLI+VLWeZsT-YP8@PC_yq@2} zwhYSBfw4o7~oeLVB} zd`=F1=xoZrB3U-7CTM!ga%5X5Tp5b5I!I&VQ`*j5ak$$_upmyT-sO5w29+@HzR~b8 ze)9SX|MU3Vij~qb=rXwBSaIpx%|_!XPSNP)oxU*V(q{dwk)!q4_%@97FR?fA(%;SN zeQ*Q@3pvVss`zNdRIipu(`#CT({zYk;5Y9qZ3)iqjQ7tDJ8k1KPYS9$_13s* zyp&{rq1)5SbHg64=%^zQ8}EEJ-Uai*y5{Jz{jTDhF0Ymglw3$$`RWP3h*+B)>qgw~jpWt1BHKP@(z zcmZWzJwMhQYu5yP`Y(MK!$&B|`#u`fhq&0r9MHOCrf#Urg|sH|QNR5}PbT6WkHJx% zSM0g^H#(^sh{q9;4cqAXMS2f(6IQVDTa|5h6?Q#MhiXRes7dUfx)~ib+_cW$6Qg*8 zR1E);A*U${5>5nlIf!JI8Hi%u8_qG1wAi^_9jW*b$Hd*#D-(V9oo^n0m9t5lxcpaaElkfF0kd*U$31f{io1I zP{#RRpIf`X{OT})$qgrd=Z}5!ZgtBP>yn~ zBZ!sA?Y;NA-*@lpeLOGc>Aalv-^}y11f{; z{`+Wt50qbbsn`u~o+`+CDs#T%v}0@l%IUNg=9Hu-J*?i{5_t&^uw>tGBaUUfS$F=ORCalifsLKh|0@YhKD4$U*b1}~(& zOn)ts$hv&s!$k#2?HsPajU4D#@YBMQZ1aJaT2v zRjoWSei?zrswxJK6i>(IjT|j@-OS(}8?RXN&xI`xQD(Ior-ekkjqd&db2_uh6B#Qm zcAVE>+vYRme1kcAHPn1)In2_8GKxfVUiXi`OS35Grcuf9I^?oW2^VcWzgip9Jq}E6 zZ^lOJ8u%Yb-zwBD?}1o9$ha{2V*9rx^u|M0t-%=N3N|wPO*U&u!~oDp2LOk*6nB!01=SRn#P2$!S?pgsU^HS0&pYe z3onGG(K=`s+_ps&d;$Guy`IQzqWKAbj=5){gXStOyUaaA(pNICy-nWjrR;vwlvW5lsarW$nZBCfGqp&P`S7-* z=Yo_E0e@p8{qNO}DylEVw5qznAk=yzU;-8qnc8(8J(RV&pXb7UIce@ST|vce9XR!; z=a{0e=t(noO)(%}p;mBz(c|juJez^lS>Y5EdR3FJ(Bf8hDFQiny{Q^er^cJb%FL~x zH|ESwWR9~TL+8Rjq#e~G-|Mp*m1C{sm|d=+N)76$wa%A7W2e7R0AE6A7Yw`9 zwMBQzrttQIfAdmf>d58$YjXt7m1t|f!^IgdVessJC)(rJDpi9&_Ss5f^Zr7*+;m3S zZAkI*(bgPsBli7?3_Ze}FLTmI&t_GaFPppktjjrGQfSeX$2IBVD9chuY zCdHfRx#~OeQh{7KYiIML%LrxGuJ5BOE`kZajYwFztI&^`5^+H1N_a0wx}Z_`d>U-rkR zW8X|~Z4V>+M3Amd#9zum#4{9)n~BIwbXu@kGcD_7ad z1a83^tZac(cb9>~=rgHZ_PWlK*_p-47&myz62WITtTgo}zvcH>QfO4!W!i5eTwD7%yGooGE!=w*! zbfRV#JsI#Pt{kcVCb|~FB%Wnt149AkZ z&<21H%H10AbsvmDT$QrVHV)1Kw{5w1}0*=Jb)D6)&@nQtDS`NP;3V7?m2$Z^CKu!cKjhFC|EPwO-;qC1E~{Xwrh2KIpLlOo;C*0W|NnZ?atUxJ(YFlYKvA=bpAeC)~ z1YjU#`Fz$v8jO@Oo(W~N^%~jI^ljY25hshr4d>rbwZAVzbp$euN%VDFDXZZF z_Ivc$({4o4+Mih%E#Zvtzc&~zSUy-;dsAC@-wIBB6ThVxhv1H{VE)5bi0zt-()L$O7t_~_#y_@NwsOzQfd&$J#MIAlN0d)D{ zu6klb@-+=LV<%A*{_)GkZcCoaT>>?gf^wdTLyFo68r`YJow8)u&xr5e)FWx*sNyL* z%6_yGg2^HS3wS77TIi`gE<^tdEw>dn3 z1xN>W@Vnm(MCbNqWZ@h!KaY?n7=b9~)8g|Xo5fFjeEHpphaNt(Bq=2r=^rY4v>7AK zK~Q2>8OJfBa`yA%)(=gMp-v*41<44O>`k>9)FvRD!M1+4-dFTF4c+8Vr`LqB8qNM} zFNXRrA!~L_uD`Q6l6&?s%v^s_23kaSa29t*N+K<4F5YXd|3l=YW{ajk$ z(`P-c=Q7mLDRDwZ{%Q8NrPJDiZ;OfD)k!T#z4&7iW8DF*qu~4`pSZ_59K&s%BuUfJ zpGo41dB-AK*f~?~UN@5yJNOmxc8wDLB>OF}#yEmrY4v19=k)Xoa$6*3Jp`mO%4B$m zr^wnnJ^J;Pz=!c)vH0HCsU5KEKZzGTCcYTj-lV-p^QY_Hv2)!n;XhFTin|kPQ6F~m zo^Xdm_HTfw*)k`wck!I+Hmw&k(Gc1y>M|B0_B!W*~N}OSZO6mi0SubzwcHHLWC_qnse2jy;pH`H^2szS`aqn7jr5g#8oiBr8u zmTin}B|z$b4t4?G8LO0dX(mW>heylpDJzz2T--?1Amt|o@9A%68tBrKXJ6*Rid-|9 z2$7}Ga5m=$&CFu}ulCvudrw(7gO1c@q8}qqq3VXOm~QrOv0A!dAc$fTk| zh(m>!IEZzGk%&uNDl1FN^Bt{W-%5v}hk6XBPB!7NPV5^<|C+4>$=Dhg94_wpPat`TXWOs$`id0N(s2b|d z_r{5z*vy27dr=p0_ecehlYDj*B8P`^kcXCpiv#%{{+O{Lsq^joEF+N_;{J>Q0E!(4 ztF-Lu^%HT?$NA_ukLYA8moALR)FhNBDOb zNWZN^yS_d4<{VP_YdL=kPZVM!=UJIB)1r>Y^C_uJYx6Ba$;9|@!`r;0CzT}9*-e6?@%u_G9fsW-L_BE7DXwu#six4ow{{>0|_-qoH$;maUM zFxcou3GhoA*1$(1nK-BNgNY-@hej>g_Kum7j`aPe=YT1)@xfkw8)wm7aV?sOJR*i{ z0e1sg9&Xb6lU7mIL}P4Ty=%`!a(X4;5k@W^Oq&Qwf)x0R(be3YaAsP@(S&*dg6cTp z?V2^P&qqc1rwqQL>5RtFRRs=BcQK7$&o~L26q=wOidrp+HFcQNSWIpBl4|@g-Y%jNYp#fghg}9iAu|KaU(3;HgffNjgz?aDSDlTFSdAX8XtGWv32PGBrB| zo9{la`Dv=F5<5J3kee<>-|Oio3%&lTdyc+uQ)wkhm&}Ll*VD+GJ{t~1YVbq~*gsJx zcK-PJ%U8al)G3|JG;t-f{)p8_H5&a?poasMqxF|&-D{9ibVu=;<)QCj~6M@9dGqZp+LMT!3hj-oeU zqJtS$(oPBss;Vq-hdmGl$yG2%B{H)_M3Ga|RqI53Rej8n6Z^SD>uvxgKTDrlX0qDD z3c>-^jkcdE5Q6ZN95Gt4n9uw?Dn1o=y1ssbatq9oa~jkdSJ@Ks97=0bUcU@e(~%a(2%2CRH}aHk1v~C1wI?Vl zN9{2`=082_X@^so=dfFVK1I7Me(F;m(_&4o<2LV@?Q|P!2#ZHt3WFcMw+iiTR>oWG z#Gdn9X`@;Yy2q^9z{-`F%0c^Tg;0|Q@2o_rV0|*ok%GD4lxU$*MmCmcqC*+8xQ30^ zaa;46(M<+-U4)Eq~;sJhbVlrBL5wu?3gEfpqoYHqSGO7R$<}HGLm7 zat5bq$LH*{n0w&|E8*{uh_TM)2J=$Vcg=jHNc}?$^Ge05PoMaFV2o9~rcCvwm{PHo z3&Z#DToZ5Oqv(T7nS%&}9rBHh7pxZ(dpB0AW6XFjKTR?K4V}Waw6zgHD&(fN#r)Cu zn3WyU;Xb3_wbfE}T1BsqRg>35p9N0o0Gu*k^ek@&NL&4AfS1iZxKH)~+0NJUvs{1d zrqUhSDFGg}hKwQF+J8W$Lm}P`E9fIhKl_OhgaTWT;-*;OMj>9@V;WdfJ$AvH6UU|q z>-$z<5hbxRTZS`%y@Z->Y`a=16D~^D&UhvKLHQ`8>+Q;Z-+R&WTz$FU{{)%c?w6}z zknnoD!~L?3ou2F)h^U<^K)7}{i~jLnF-bLQnsY6mv&%moIKIOapl;p>gEeZ|IdpPb z=5(GrGozf-YxnY18xnWtl;uu*~?$~)=m0EgW61Y;f1?I(}F1-nKtzo`1q+;W@h}A>K%;pcMyXhX4ou0kjl2 zWw;|@JuN-~;*NvOPTAt#Nne|@E7LS+T6{kcf$yN=j}vn0ca3I)j1a;2bngv{qmC&P zCr`#%`{9NR&o0(C5zgz2wt40otY1BV8qjmbm4MCuAcUD-+k1g?Y?Cl3$=`7R?RU=` zyUZ6z!@dLKZ`#Zd+v2^iAgaZiair8*<2CT$4a&A}buhCVk~T{Bwzhk_x?Ffk3Ao)P?>w^D#bB;v4K4~y06nmW zL)day7IIwuTY(eR7jX%z%9J5{YisC|@xto-mTJ4*)taMsA}nWl-7UVuIU-l7Av#Jt f`N;;u<(npGf&W)Hfxq3s6Z%usW)UzF5|IA~*PoY= literal 0 HcmV?d00001 diff --git a/Ezmlm/trunk/Changes b/Ezmlm/trunk/Changes index e020587..5f0653a 100644 --- a/Ezmlm/trunk/Changes +++ b/Ezmlm/trunk/Changes @@ -47,3 +47,6 @@ Revision history for Perl extension Mail::Ezmlm. 0.07.2 Sun May 6 06:20:13 CEST 2006 - fix parsing of ezmlm-make options +0.07.2 Tue Jun 20 01:05:56 UTC 2006 + - fixed 'get_charset' and 'set_charset' for idx < 5.0 + diff --git a/Ezmlm/trunk/Ezmlm.pm b/Ezmlm/trunk/Ezmlm.pm index 4da764f..a3d7bbe 100644 --- a/Ezmlm/trunk/Ezmlm.pm +++ b/Ezmlm/trunk/Ezmlm.pm @@ -546,12 +546,11 @@ sub set_lang { } -# == get the selected charset of the list (idx >= 5.0) == -# return empty string for idx < 5.0 +# == get the selected charset of the list == +# return default value (us-ascii) if no charset is specified 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 ''); @@ -560,12 +559,10 @@ sub get_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 @@ -1118,9 +1115,9 @@ system-wide default text file, if there is no customized text file for this list $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). +These functions access the file 'conf-etc' in the mailing list's directory. The +static function (first example) always returns the default configuration directory +of ezmlm-idx (/etc/ezmlm). $list->get_available_languages; $list->get_lang;