Mail-Procmail

 view release on metacpan or  search on metacpan

lib/Mail/Procmail.pm  view on Meta::CPAN

my $RCS_Id = '$Id: Procmail.pm,v 1.24 2004-09-19 12:34:56+02 jv Exp jv $ ';

# Author          : Johan Vromans
# Created On      : Tue Aug  8 13:53:22 2000
# Last Modified By: Johan Vromans
# Last Modified On:
# Update Count    : 254
# Status          : Unknown, Use with caution!

=head1 NAME

Mail::Procmail - Procmail-like facility for creating easy mail filters.

=head1 SYNOPSIS

    use Mail::Procmail;

    # Set up. Log everything up to log level 3.
    my $m_obj = pm_init ( loglevel  => 3 );

    # Pre-fetch some interesting headers.
    my $m_from		    = pm_gethdr("from");
    my $m_to		    = pm_gethdr("to");
    my $m_subject	    = pm_gethdr("subject");

    # Default mailbox.
    my $default = "/var/spool/mail/".getpwuid($>);

    pm_log(1, "Mail from $m_from");

    pm_ignore("Non-ASCII in subject")
      if $m_subject =~ /[\232-\355]{3}/;

    pm_resend("jojan")
      if $m_to =~ /jjk@/i;

    # Make sure I see these.
    pm_deliver($default, continue => 1)
      if $m_subject =~ /getopt(ions|(-|::)?long)/i;

    # And so on ...

    # Final delivery.
    pm_deliver($default);

=head1 DESCRIPTION

F<procmail> is a great mail filter program, but it has weird recipe
format. It's pattern matching capabilities are basic and often
insufficient. I wanted something flexible whereby I could filter my
mail using the power of Perl.

I've been considering to write a procmail replacement in Perl for a
while, but it was Simon Cozen's C<Mail::Audit> module, and his article
in The Perl Journal #18, that set it off.

I first started using Simon's great module, and then decided to write
my own since I liked certain things to be done differently. And I
couldn't wait for his updates.

C<Mail::Procmail> allows a piece of email to be logged, examined,
delivered into a mailbox, filtered, resent elsewhere, rejected, and so
on. It is designed to allow you to easily create filter programs to
stick in a F<.forward> or F<.procmailrc> file, or similar.

=head1 DIFFERENCES WITH MAIL::AUDIT

Note that several changes are due to personal preferences and do not
necessarily imply deficiencies in C<Mail::Audit>.

=over

=item General

Not object oriented. Procmail functionality typically involves one
single message. All (relevant) functions are exported.

=item Delivery

Each of the delivery methods is able to continue (except
I<pm_reject> and I<pm_ignore>).

Each of the delivery methods is able to pretend they did it
(for testing a new filter).

No default file argument for mailbox delivery, since this is system
dependent.

lib/Mail/Procmail.pm  view on Meta::CPAN

	    s/\s+/ /g;
	    s/[\r\n]+$//;
	}
	if ( $debug ) {
	    $hdr =~ s/-(.)/"-".ucfirst($1)/ge;
	    warn (ucfirst($hdr), ": ", $val, "\n");
	}
	return $val unless wantarray;
	push (@ret, $val);
    }
    wantarray ? @ret : '';
}

=head2 pm_gethdr_raw

Like pm_gethdr, but without whitespace cleanup.

=cut

sub pm_gethdr_raw {
    my ($hdr, $ix) = @_;
    my @ret;
    foreach my $val ( $m_head->get($hdr, $ix) ) {
	last unless defined $val;
	if ( $debug ) {
	    $hdr =~ s/-(.)/"-".ucfirst($1)/ge;
	    warn (ucfirst($hdr), ": ", $val, "\n");
	}
	return $val unless wantarray;
	push (@ret, $val);
    }
    wantarray ? @ret : '';
}

=head2 pm_body

This routine fetches the body of a message, as a reference to an array
of lines.

Example:

    $body = pm_body();			# ref of lines
    $body = join("", @{pm_body()});	# as one string

=cut

sub pm_body {
    $m_obj->body;
}

=head2 pm_deliver

This routine performs delivery to a Unix style mbox file, or maildir.

In case of an mbox file, the file is locked first by acquiring
exclusive access. Note that older style locking, with a lockfile with
C<.lock> extension, is I<not> supported.

Example:

    pm_deliver("/var/spool/mail/".getpwuid($>));

Attributes:

=over

=item *

continue

If true, processing will continue after delivery. Otherwise the
program will exit with a DELIVERED status.

=back

=cut

sub _pm_msg_size {
    length($m_obj->head->as_string || '') + length(join("", @{$m_obj->body}));
}

sub pm_deliver {
    my ($target, %atts) = @_;
    my $line = (caller(0))[2];
    pm_log(2, "deliver[$line]: $target "._pm_msg_size());

    # Is it a Maildir?
    if ( -d "$target/tmp" && -d "$target/new" ) {
	my $msg_file = "/${\time}.$$.$pm_hostname";
	my $tmp_path = "$target/tmp/$msg_file";
	my $new_path = "$target/new/$msg_file";
	pm_log(3,"Looks like maildir, writing to $new_path");

	# since mutt won't add a lines tag to maildir messages,
	# we'll add it here
	unless ( pm_gethdr("lines") ) {
	    my $body = $m_obj->body;
	    my $num_lines = @$body;
	    $m_head->add("Lines", $num_lines);
	    pm_log(4,"Adding Lines: $num_lines header");
	}
	my $tmp = _new_fh();
	unless (open ($tmp, ">$tmp_path") ) {
	    pm_log(0,"Couldn't open $tmp_path! $!");
	    exit TEMPFAIL;
	}
	print $tmp ($m_obj->as_mbox_string);
	close($tmp);

	unless ( $test ) {
	    unless (link($tmp_path, $new_path) ) {
		pm_log(0,"Couldn't link $tmp_path to $new_path : $!");
		exit TEMPFAIL;
	    }
	}
	unlink($tmp_path) or pm_log(1,"Couldn't unlink $tmp_path: $!");
    }
    else {
	# It's an mbox, I hope.
	my $fh = _new_fh();
	unless (open($fh, ">>$target")) {



( run in 0.508 second using v1.01-cache-2.11-cpan-39bf76dae61 )