File Coverage

blib/lib/Email/Filter.pm
Criterion Covered Total %
statement 63 88 71.5
branch 9 24 37.5
condition 2 11 18.1
subroutine 16 24 66.6
pod 9 13 69.2
total 99 160 61.8


line stmt bran cond sub pod time code
1 1     1   915 use strict;
  1         2  
  1         42  
2             package Email::Filter 1.035;
3             # ABSTRACT: Library for creating easy email filters
4              
5 1     1   370 use Email::LocalDelivery;
  1         42357  
  1         30  
6 1     1   703 use Email::Simple;
  1         4863  
  1         31  
7 1     1   631 use Class::Trigger;
  1         1115  
  1         6  
8 1     1   827 use IPC::Run qw(run);
  1         28669  
  1         62  
9              
10 1     1   11 use constant DELIVERED => 0;
  1         2  
  1         74  
11 1     1   6 use constant TEMPFAIL => 75;
  1         2  
  1         36  
12 1     1   5 use constant REJECTED => 100;
  1         2  
  1         407  
13              
14             #pod =head1 SYNOPSIS
15             #pod
16             #pod use Email::Filter;
17             #pod my $mail = Email::Filter->new(emergency => "~/emergency_mbox");
18             #pod $mail->pipe("listgate", "p5p") if $mail->from =~ /perl5-porters/;
19             #pod $mail->accept("perl") if $mail->from =~ /perl/;
20             #pod $mail->reject("We do not accept spam") if $mail->subject =~ /enlarge/;
21             #pod $mail->ignore if $mail->subject =~ /boring/i;
22             #pod ...
23             #pod $mail->exit(0);
24             #pod $mail->accept("~/Mail/Archive/backup");
25             #pod $mail->exit(1);
26             #pod $mail->accept()
27             #pod
28             #pod =head1 DESCRIPTION
29             #pod
30             #pod This module replaces C or C, and allows you to write
31             #pod programs describing how your mail should be filtered.
32             #pod
33             #pod =head1 TRIGGERS
34             #pod
35             #pod Users of C will note that this class is much leaner than
36             #pod the one it replaces. For instance, it has no logging; the concept of
37             #pod "local options" has gone away, and so on. This is a deliberate design
38             #pod decision to make the class as simple and maintainable as possible.
39             #pod
40             #pod To make up for this, however, C contains a trigger
41             #pod mechanism provided by L, to allow you to add your own
42             #pod functionality. You do this by calling the C method:
43             #pod
44             #pod Email::Filter->add_trigger( after_accept => \&log_accept );
45             #pod
46             #pod Hopefully this will also help subclassers.
47             #pod
48             #pod The methods below will list which triggers they provide.
49             #pod
50             #pod =head1 ERROR RECOVERY
51             #pod
52             #pod If something bad happens during the C or C method, or
53             #pod the C object gets destroyed without being properly
54             #pod handled, then a fail-safe error recovery process is called. This first
55             #pod checks for the existence of the C setting, and tries to
56             #pod deliver to that mailbox. If there is no emergency mailbox or that
57             #pod delivery failed, then the program will either exit with a temporary
58             #pod failure error code, queuing the mail for redelivery later, or produce a
59             #pod warning to standard error, depending on the status of the C
60             #pod setting.
61             #pod
62             #pod =cut
63              
64             sub done_ok {
65 2     2 0 9 my $self = shift;
66 2         6 $self->{delivered} = 1;
67 2 50       13 exit DELIVERED unless $self->{noexit};
68             }
69              
70             sub fail_badly {
71 0     0 0 0 my $self = shift;
72 0         0 $self->{giveup} = 1; # Don't get caught by DESTROY
73 0 0       0 exit TEMPFAIL unless $self->{noexit};
74 0         0 warn "Message ".$self->simple->header("Message-ID").
75             "was never handled properly\n";
76             }
77              
78             sub fail_gracefully {
79 0     0 0 0 my $self = shift;
80 0         0 our $FAILING_GRACEFULLY;
81 0 0 0     0 if ($self->{emergency} and ! $FAILING_GRACEFULLY) {
82 0         0 local $FAILING_GRACEFULLY = 1;
83 0 0       0 $self->done_ok if $self->accept($self->{emergency});
84             }
85 0         0 $self->fail_badly;
86             }
87              
88             sub DESTROY {
89 1     1   537 my $self = shift;
90             return if $self->{delivered} # All OK.
91             or $self->{giveup} # Tried emergency, didn't work.
92 1 0 33     216 or !$self->{emergency}; # Not much we can do.
      0        
93 0         0 $self->fail_gracefully();
94             }
95              
96             #pod =method new
97             #pod
98             #pod Email::Filter->new(); # Read from STDIN
99             #pod Email::Filter->new(data => $string); # Read from string
100             #pod
101             #pod Email::Filter->new(emergency => "~simon/urgh");
102             #pod # Deliver here in case of error
103             #pod
104             #pod This takes an email either from standard input, the usual case when
105             #pod called as a mail filter, or from a string.
106             #pod
107             #pod You may also provide an "emergency" option, which is a filename to
108             #pod deliver the mail to if it couldn't, for some reason, be handled
109             #pod properly.
110             #pod
111             #pod =over 3
112             #pod
113             #pod =item Hint
114             #pod
115             #pod If you put your constructor in a C block, like so:
116             #pod
117             #pod use Email::Filter;
118             #pod BEGIN { $item = Email::Filter->new(emergency => "~simon/urgh"); }
119             #pod
120             #pod right at the top of your mail filter script, you'll even be protected
121             #pod from losing mail even in the case of syntax errors in your script. How
122             #pod neat is that?
123             #pod
124             #pod =back
125             #pod
126             #pod This method provides the C trigger, called once an object is
127             #pod instantiated.
128             #pod
129             #pod =cut
130              
131             sub new {
132 1     1 1 745 my $class = shift;
133 1         5 my %stuff = @_;
134 1         2 my $data;
135              
136             {
137 1         1 local $/;
  1         14  
138 1 50       10 $data = exists $stuff{data} ? $stuff{data} : scalar ;
139             # shave any leading From_ line
140 1         3 $data =~ s/^From .*?[\x0a\x0d]//
141             }
142              
143             my $obj = bless {
144             mail => Email::Simple->new($data),
145             emergency => $stuff{emergency},
146 1   50     8 noexit => ($stuff{noexit} || 0)
147             }, $class;
148 1         388 $obj->call_trigger("new");
149 1         94 return $obj;
150             }
151              
152             #pod =method exit
153             #pod
154             #pod $mail->exit(1|0);
155             #pod
156             #pod Sets or clears the 'exit' flag which determines whether or not the
157             #pod following methods exit after successful completion.
158             #pod
159             #pod The sense-inverted 'noexit' method is also provided for backwards
160             #pod compatibility with C, but setting "noexit" to "yes" got a
161             #pod bit mind-bending after a while.
162             #pod
163             #pod =cut
164              
165 1     1 1 1060 sub exit { $_[0]->{noexit} = !$_[1]; }
166 0     0 0 0 sub noexit { $_[0]->{noexit} = $_[1]; }
167              
168             #pod =method simple
169             #pod
170             #pod $mail->simple();
171             #pod
172             #pod Gets and sets the underlying C object for this filter;
173             #pod see L for more details.
174             #pod
175             #pod =cut
176              
177             sub simple {
178 3     3 1 9 my ($filter, $mail) = @_;
179 3 50       11 if ($mail) { $filter->{mail} = $mail; }
  0         0  
180 3         20 return $filter->{mail};
181             }
182              
183             #pod =method header
184             #pod
185             #pod $mail->header("X-Something")
186             #pod
187             #pod Returns the specified mail headers. In scalar context, returns the
188             #pod first such header; in list context, returns them all.
189             #pod
190             #pod =cut
191              
192 0     0 1 0 sub header { my ($mail, $head) = @_; $mail->simple->header($head); }
  0         0  
193              
194             #pod =method body
195             #pod
196             #pod $mail->body()
197             #pod
198             #pod Returns the body text of the email
199             #pod
200             #pod =cut
201              
202 0     0 1 0 sub body { $_[0]->simple->body }
203              
204             #pod =method from
205             #pod
206             #pod =method to
207             #pod
208             #pod =method cc
209             #pod
210             #pod =method bcc
211             #pod
212             #pod =method subject
213             #pod
214             #pod =method received
215             #pod
216             #pod $mail->
()
217             #pod
218             #pod Convenience accessors for C
219             #pod
220             #pod =cut
221              
222 1     1   7 { no strict 'refs';
  1         2  
  1         403  
223             for my $head (qw(From To CC Bcc Subject Received)) {
224 0     0   0 *{lc $head} = sub { $_[0]->header($head) }
225             }
226             }
227              
228             #pod =method ignore
229             #pod
230             #pod Ignores this mail, exiting unconditionally unless C has been set
231             #pod to false.
232             #pod
233             #pod This method provides the "ignore" trigger.
234             #pod
235             #pod =cut
236              
237             sub ignore {
238 0     0 1 0 $_[0]->call_trigger("ignore");
239 0         0 $_[0]->done_ok;
240             }
241              
242             #pod =method accept
243             #pod
244             #pod $mail->accept();
245             #pod $mail->accept(@where);
246             #pod
247             #pod Accepts the mail into a given mailbox or mailboxes.
248             #pod Unix C<~/> and C<~user/> prefices are resolved. If no mailbox is given,
249             #pod the default is determined according to L:
250             #pod C<$ENV{MAIL}>, F, F, or
251             #pod F<~you/Maildir/>.
252             #pod
253             #pod This provides the C and C triggers, and
254             #pod exits unless C has been set to false. They are passed a reference to the
255             #pod C<@where> array.
256             #pod
257             #pod =cut
258              
259             sub accept {
260 1     1 1 1093 my ($self, @where) = @_;
261 1         7 $self->call_trigger("before_accept", \@where);
262             # Unparsing and reparsing is so fast we prefer to do that in order
263             # to keep to LocalDelivery's clean interface.
264 1 50       76 if (Email::LocalDelivery->deliver($self->simple->as_string, @where)) {
265 1         8057 $self->call_trigger("after_accept", \@where);
266 1         176 $self->done_ok;
267             } else {
268 0         0 $self->fail_gracefully();
269             }
270             }
271              
272             #pod =method reject
273             #pod
274             #pod $mail->reject("Go away!");
275             #pod
276             #pod This rejects the email; if called in a pipe from a mail transport agent, (such
277             #pod as in a F<~/.forward> file) the mail will be bounced back to the sender as
278             #pod undeliverable. If a reason is given, this will be included in the bounce.
279             #pod
280             #pod This calls the C trigger. C has no effect here.
281             #pod
282             #pod =cut
283              
284             sub reject {
285 0     0 1 0 my $self = shift;
286 0         0 $self->call_trigger("reject");
287 0         0 $self->{delivered} = 1;
288 0         0 $! = REJECTED; die @_,"\n";
  0         0  
289             }
290              
291             #pod =method pipe
292             #pod
293             #pod $mail->pipe(qw[sendmail foo\@bar.com]);
294             #pod
295             #pod Pipes the mail to an external program, returning the standard output
296             #pod from that program if C has been set to false. The program and each
297             #pod of its arguments must be supplied in a list. This allows you to do
298             #pod things like:
299             #pod
300             #pod $mail->exit(0);
301             #pod $mail->simple(Email::Simple->new($mail->pipe("spamassassin")));
302             #pod $mail->exit(1);
303             #pod
304             #pod in the absence of decent C support.
305             #pod
306             #pod If the program returns a non-zero exit code, the behaviour is dependent
307             #pod on the status of the C flag. If this flag is set to true (the
308             #pod default), then C tries to recover. (See L)
309             #pod If not, nothing is returned.
310             #pod
311             #pod If the last argument to C is a reference to a hash, it is taken to
312             #pod contain parameters to modify how C itself behaves. The only useful
313             #pod parameter at this time is:
314             #pod
315             #pod header_only - only pipe the header, not the body
316             #pod
317             #pod =cut
318              
319             sub pipe {
320 2     2 1 1950 my ($self, @program) = @_;
321 2         4 my $arg;
322 2 50       9 $arg = (ref $program[-1] eq 'HASH') ? (pop @program) : {};
323              
324 2         4 my $stdout;
325              
326             my $string = $arg->{header_only}
327 2 50       11 ? $self->simple->header_obj->as_string
328             : $self->simple->as_string;
329              
330 2         358 $self->call_trigger("pipe", \@program, $arg);
331 2 100       105 if (eval {run(\@program, \$string, \$stdout)} ) {
  2         11  
332 1         8346 $self->done_ok;
333 1         14 return $stdout;
334             }
335 1 50       1675 $self->fail_gracefully() unless $self->{noexit};
336 1         10 return;
337             }
338              
339             1;
340              
341             __END__