Mail-ExpandAliases

 view release on metacpan or  search on metacpan

ExpandAliases.pm  view on Meta::CPAN

#
#   - Parse aliases file
#
#       o Read file, normalize
#
#           + Skip malformed lines
#
#           + Join multi-line entries
#
#           + Discard comments
#
#       o Create internal structure
#
#   - On call to expand
#
#       o Start with first alias, and expand
#
#       o Expand each alias, unless:
#
#           + It is non-local
#
#           + It has already been seen
#
#   - Return list of responses
# -------------------------------------------------------------------

use strict;
use vars qw($VERSION $DEBUG @POSSIBLE_ALIAS_FILES);

$VERSION = 0.49;
$DEBUG = 0 unless defined $DEBUG;
@POSSIBLE_ALIAS_FILES = qw(/etc/aliases
                           /etc/mail/aliases
                           /etc/postfix/aliases
                           /etc/exim/aliases);

use constant PARSED  => 0;  # Parsed aliases
use constant CACHED  => 1;  # Caches lookups
use constant FILE    => 2;  # "Main" aliases file

# ----------------------------------------------------------------------
# import(@files)
#
# Allow for compile-time additions to @POSSIBLE_ALIAS_FILES
# ----------------------------------------------------------------------
sub import {
    my $class = shift;

    for my $x (@_) {
        if ($x =~ /^debug$/i) {
            $DEBUG = 1;
        }
        elsif (-f "$x") {
            unshift @POSSIBLE_ALIAS_FILES, $x;
        }
    }
}

sub new {
    my ($class, $file) = @_;
    my $self = bless [ { }, { }, "" ] => $class;

    $self->[ FILE ] = (grep { -e $_ && -r _ }
                       ($file, @POSSIBLE_ALIAS_FILES))[0];
    $self->debug("Using alias file " . $self->[ FILE ]);
    $self->init();

    return $self;
}

sub debug {
    my $class = shift;
    $class = ref $class || $class;
    if ($DEBUG) {
        warn "[ $class ] $_\n"
            for (@_);
    }
}

# ----------------------------------------------------------------------
# init($file)
#
# Parse file, extracting aliases.  Note that this is a (more or less)
# literal representation of the file; expansion of aliases happens at
# run time, as aliases are requested.
# # ----------------------------------------------------------------------
sub init {
    my $self = shift;
    my $file = shift || $self->[ FILE ];
    return $self unless defined $file;

    # Chapter 24 of the sendmail book
    # (www.oreilly.com/catalog/sendmail/) describes the process of
    # looking for aliases thusly:
    #
    # "The aliases(5) file is composed of lines of text.  Any line that
    # begins with a # is a comment and is ignored.  Empty lines (those
    # that contain only a newline character) are also ignored.  Any
    # lines that begins with a space or tab is joined (appended) to the
    # line above it.  All other lines are text are viewed as alias
    # lines.  The format for an alias line is:
    #
    #   local: alias
    #
    # "The local must begin a line. It is an address in the form of a
    # local recipient address...  The colon follows the local on
    # the same line and may be preceded with spaces or tabs.  If the
    # colon is missing, sendmail prints and syslog(3)'s the following
    # error message and skips that alias line:
    #
    #   missing colon
    #
    # "The alias (to the right of the colon) is one or more addresses on
    # the same line.  Indented continuation lines are permitted.  Each
    # address should be separated from the next by a comma and optional
    # space characters. A typical alias looks like this:
    #
    #   root: jim, sysadmin@server, gunther ^ | indenting whitespace
    #
    # "Here, root is hte local address to be aliases.  When mail is to
    # be locally delivered to root, it is looked up in the aliases(5)

ExpandAliases.pm  view on Meta::CPAN

    $self = shift;
    @answers = sort keys %{ $self->[ PARSED ] };
    return wantarray ? @answers : \@answers;
}

# ----------------------------------------------------------------------
# exists($alias)
#
# Determine if an alias exists not not
# ----------------------------------------------------------------------
sub exists {
    my ($self, $alias) = @_;
    return CORE::exists($self->[ PARSED ]->{ $alias });
}

# ----------------------------------------------------------------------
# check($alias)
#
# Returns the unexpanded form an an alias.  I.e., exactly what is in the
# file, without expansion.
#
# Unlike expand, if $alias does not exist in the file, check() returns
# the empty array.  Otherwise, $alias returns an array (in list context)
# or a reference to an array (in scalar context) to the items in the
# aliases file.
#
# You can emulate expand() by calling check recusrively.
# ----------------------------------------------------------------------
sub check {
    my $self = shift;
    my $ret;

    if (my $name = shift) {
        $ret = $self->[ PARSED ]->{ $name }
    }

    $ret ||= [];

    return wantarray ? @$ret : [ @$ret ];
}

package File::Aliases;
use constant FH     => 0;
use constant BUFFER => 1;

use IO::File;

# This package ensures that each read (i.e., calls to next() --
# I'm too lazy to implement this as a tied file handle so it can
# be used in <>) returns a single alias entry, which may span
# multiple lines.
#
# XXX I suppose I could simply subclass IO::File, and rename next
# to readline.

sub new {
    my $class = shift;
    my $file = shift;
    my $fh = IO::File->new($file);

    my $self = bless [ $fh, '' ] => $class;
    $self->[ BUFFER ] = <$fh>
        if $fh;

    return $self;
}

sub next {
    my $self = shift;
    my $buffer = $self->[ BUFFER ];
    my $fh = $self->[ FH ];

    return ""
        unless defined $fh;

    $self->[ BUFFER ] = "";
    while (<$fh>) {
        if (/^\S/) {
            $self->[ BUFFER ] = $_;
            last;
        } else {
            $buffer .= $_;
        }
    }

    return $buffer;
}

1;

__END__

=head1 NAME

Mail::ExpandAliases - Expand aliases from /etc/aliases files

=head1 SYNOPSIS

  use Mail::ExpandAliases;

  my $ma = Mail::ExpandAliases->new("/etc/aliases");
  my @list = $ma->expand("listname");

=head1 DESCRIPTION

I've looked for software to expand aliases from an alias file for a
while, but have never found anything adequate.  In this day and age,
few public SMTP servers support EXPN, which makes alias expansion
problematic.  This module, and the accompanying C<expand-alias>
script, attempts to address that deficiency.

=head1 USAGE

Mail::ExpandAliases is an object oriented module, with a constructor
named C<new>:

  my $ma = Mail::ExpandAliases->new("/etc/mail/aliases");

C<new> takes the filename of an aliases file; if not supplied, or if
the file specified does not exist or is not readable,
Mail::ExpandAliases will look in a predetermined set of default



( run in 4.441 seconds using v1.01-cache-2.11-cpan-b16cb0d3907 )