File Coverage

blib/lib/Postfix/ContentFilter.pm
Criterion Covered Total %
statement 66 77 85.7
branch 20 36 55.5
condition 10 24 41.6
subroutine 13 16 81.2
pod 3 3 100.0
total 112 156 71.7


line stmt bran cond sub pod time code
1 3     3   160454 use strict;
  3         9  
  3         107  
2 3     3   13 use warnings;
  3         3  
  3         105  
3             package Postfix::ContentFilter;
4             # ABSTRACT: a perl content_filter for postfix
5              
6 3     3   13 use Carp;
  3         8  
  3         199  
7 3     3   515 use Try::Tiny 0.11;
  3         1179  
  3         183  
8 3     3   3225 use IPC::Run 0.92 qw(start pump finish timeout);
  3         134295  
  3         230  
9 3     3   25 use Scalar::Util qw(blessed);
  3         5  
  3         158  
10 3     3   1547 use Class::Load qw(load_first_existing_class);
  3         73427  
  3         2894  
11              
12             our $VERSION = '1.12'; # VERSION
13              
14              
15             our $parser;
16             our $sendmail = [qw[ /usr/sbin/sendmail -G -i ]];
17             our $output;
18             our $error;
19              
20              
21             sub new($%) {
22 2     2 1 194224 my ($class, $options) = @_;
23 2         7 my $self = bless {}, $class;
24 2 50 33     11 if ($options && $options->{parser}) {
25 0         0 parser($self, $options->{parser});
26             }
27 2         5 return $self;
28             }
29              
30              
31             sub parser {
32 4     4 1 14 my ($self, $ptype) = @_;
33 4         14 my $parsers = {
34             # Key is parser, value is returned entity
35             'MIME::Parser' => 'MIME::Entity',
36             'Mail::Message' => 'Mail::Message',
37             };
38            
39 4 100 66     40 return $self->{parser} if defined $self->{parser} and not defined $ptype;
40              
41 2   33     8 $ptype = load_first_existing_class(map { $_ => {} } ($ptype || qw(MIME::Parser Mail::Message)));
  2         17  
42            
43 2 50       286 if (my $ent = $parsers->{$ptype}) {
44 2         8 $self->{parser} = $ptype;
45 2         5 $self->{entity} = $ent;
46             } else {
47 0         0 croak "Unknown parser $ptype";
48             }
49            
50 2         15 return $self->{parser};
51             }
52              
53             sub _parse {
54 0     0   0 my ($self, $handle) = @_;
55             }
56              
57              
58             sub process($&;*) {
59 2     2 1 4 my ($class, $coderef, $handle) = @_;
60            
61 2 50       14 my $self = blessed $class
62             ? $class
63             : bless {}, $class
64             ; # For backwards compatibility, to enable calling directly
65              
66 2 50       8 confess "please call as ".__PACKAGE__."->process(sub{ ... })" unless ref $coderef eq 'CODE';
67            
68 2 50       6 $handle = \*STDIN unless ref $handle eq 'GLOB';
69              
70 2         3 my $entity;
71 2         6 my $parser = $self->parser;
72 2   33     10 my $module = ref $parser || $parser;
73            
74 2 100       7 if ($module eq 'Mail::Message') {
    50          
75 1 50       14 $entity = $parser->read($handle) or confess "failed to parse with Mail::Message";
76             } elsif ($module eq 'MIME::Parser') {
77 1         6 $parser = $parser->new;
78 1 50       125 $entity = $parser->parse($handle) or confess "failed to parse wth MIME::Parser";
79             } else {
80 0         0 confess "Unkown parser $parser";
81             }
82            
83             try {
84 2     2   115 $entity = $coderef->($entity);
85             } catch {
86 0   0 0   0 $module = ref $parser || $parser;
87 0 0       0 if ($module eq 'Mail::Message') {
    0          
88 0         0 $entity->DESTROY;
89             } elsif ($module eq 'MIME::Parser') {
90 0         0 $parser->filer->purge;
91             }
92 0         0 confess $_;
93 2         19878 };
94            
95 2 50 33     3797 confess "subref should return instance of $self->{entity}"
96             unless blessed($entity) and $entity->isa($self->{entity});
97              
98 2         6 my $ret;
99            
100 2 50       13 delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV', 'PATH'} if ${^TAINT};
101            
102 2         5 $output = undef;
103 2         21 $error = undef;
104              
105 2         25 my $in;
106            
107             try {
108            
109 2     2   112 my $h = start [ @$sendmail, @ARGV ], \$in, \$output, \$error, timeout(60);
110            
111 2   66     14119 my $module = ref $parser || $parser;
112 2 100       20 if ($module eq 'Mail::Message') {
    50          
113 1         31 $in = $entity->string;
114             } elsif ($module eq 'MIME::Parser') {
115 1         11 $in = $entity->as_string;
116             }
117            
118 2         2607 pump $h;
119            
120 2         1410 $ret = finish $h;
121            
122             } catch {
123              
124 0     0   0 local $, = ' ';
125 0         0 confess "error: $_ with @$sendmail @ARGV";
126              
127             } finally {
128              
129 2   66 2   4092 my $module = ref $parser || $parser;
130 2 100       16 if ($module eq 'Mail::Message') {
    50          
131 1         14 $entity->DESTROY;
132             } elsif ($module eq 'MIME::Parser') {
133 1         13 $parser->filer->purge;
134             }
135              
136 2         28 };
137            
138 2         602 return $ret;
139             }
140              
141              
142             1;
143              
144             __END__