File Coverage

blib/lib/Email/ARF/Report.pm
Criterion Covered Total %
statement 75 75 100.0
branch 20 26 76.9
condition 8 15 53.3
subroutine 20 20 100.0
pod 10 10 100.0
total 133 146 91.1


line stmt bran cond sub pod time code
1 3     3   63769 use strict;
  3         7  
  3         100  
2 3     3   15 use warnings;
  3         5  
  3         153  
3             package Email::ARF::Report;
4             {
5             $Email::ARF::Report::VERSION = '0.010';
6             }
7             # ABSTRACT: interpret Abuse Reporting Format (ARF) messages
8              
9 3     3   16 use Carp ();
  3         6  
  3         64  
10 3     3   2763 use Email::MIME 1.900 (); # ->subtypes
  3         243544  
  3         82  
11 3     3   34 use Email::MIME::ContentType 1.016 (); # type/subtype
  3         50  
  3         63  
12 3     3   17 use Scalar::Util ();
  3         7  
  3         52  
13 3     3   2618 use Params::Util qw(_INSTANCE);
  3         17054  
  3         3072  
14              
15              
16             sub new {
17 16     16 1 23091 my ($class, $source) = @_;
18              
19 16 100       235 Carp::croak "no report source provided" unless $source;
20              
21 15 100       100 my $mime = Scalar::Util::blessed $source
22             ? $source
23             : Email::MIME->new($source);
24              
25             Carp::croak "ARF report source could not be interpreted as MIME message"
26 15 100       7385 unless eval { $mime->isa('Email::MIME') };
  15         218  
27              
28 14         47 my $ct_header = $mime->content_type;
29 14         504 my $ct = Email::MIME::ContentType::parse_content_type($ct_header);
30              
31 14 50 66     1407 Carp::croak "non-ARF content type '$ct_header' on ARF report source"
      66        
32             unless $ct->{type} eq 'multipart'
33             and $ct->{subtype} eq 'report'
34             and $ct->{attributes}{'report-type'} eq 'feedback-report';
35              
36 13 100       51 Carp::croak "too few subparts for ARF report" unless $mime->subparts >= 3;
37              
38 12         131 my ($description_part, $report_part, $original_part) = $mime->subparts;
39              
40 12         107 my $report_header = $report_part->content_type;
41 12         397 my $report_ct = Email::MIME::ContentType::parse_content_type($report_header);
42 12 50 33     355 Carp::croak "bad content type '$report_header' for machine-readable section"
43             unless $report_ct->{type} eq 'message'
44             and $report_ct->{subtype} eq 'feedback-report';
45              
46 12         92 my $self = bless {
47             mime => $mime,
48             description_part => $description_part,
49             original_part => $original_part,
50             } => $class;
51              
52 12         41 $self->{fields} = $self->_email_from_body($report_part, 1)->header_obj;
53 12         4265 $self->{original_email} = $self->_email_from_body($original_part);
54              
55 12         4075 return $self;
56             }
57              
58             sub _email_from_body {
59 24     24   36 my ($self, $src_email, $append_nl) = @_;
60              
61 24         68 my $src_email_body = $src_email->body;
62              
63 24         949 $src_email_body =~ s/\A(\x0d|\x0a)+//g;
64              
65 24 100       110 my $email = Email::MIME->new(
66             $append_nl ? "$src_email_body\n" : $src_email_body
67             );
68             }
69              
70              
71             sub create {
72 3     3 1 5854 my ($class, %arg) = @_;
73              
74 3         44 require Email::MIME::Creator;
75              
76 3         26 my $description_part = Email::MIME->create(
77             attributes => { content_type => 'text/plain' },
78             body => $arg{description},
79             );
80              
81 1         2 my $original_body = ref $arg{original_email}
82             ? Scalar::Util::blessed $arg{original_email}
83             ? $arg{original_email}->as_string
84 3 100       2645 : ${ $arg{original_email} }
    100          
85             : $arg{original_email};
86              
87 3         92 $description_part->header_set('Date');
88              
89 3         119 my $original_part = Email::MIME->create(
90             attributes => { content_type => 'message/rfc822' },
91             body => $original_body,
92             );
93              
94 3         2041 $original_part->header_set('Date');
95              
96 3         12 my $field_pairs = ref $arg{fields} eq 'HASH'
97 3 50       113 ? [ %{ $arg{fields} } ]
98             : $arg{fields};
99              
100 3         17 my $fields = Email::Simple->create(header => $field_pairs);
101              
102 3         958 $fields->header_set('Date');
103              
104 3 50       98 unless (defined $fields->header('user-agent')) {
105 3   50     114 $fields->header_set(
106             'User-Agent',
107             "$class/" . ($class->VERSION || '(dev)')
108             );
109             }
110              
111 3 50       99 unless (defined $fields->header('version')) {
112 3         64 $fields->header_set('Version', "1");
113             }
114              
115 3 50       101 unless (defined $fields->header('Feedback-Type')) {
116 3         60 $fields->header_set('Feedback-Type', "other");
117             }
118              
119 3         104 my $report_part = Email::MIME->create(
120             attributes => { content_type => 'message/feedback-report' },
121             body => $fields->header_obj->as_string,
122             );
123              
124 3         1962 $report_part->header_set('Date');
125              
126 3   50     125 my $report = Email::MIME->create(
      50        
127             attributes => {
128             # It is so asinine that I need to do this! Only certain blessed
129             # attributes are heeded, here. The rest are dropped. -- rjbs, 2007-03-21
130             content_type => 'multipart/report; report-type="feedback-report"',
131             },
132             parts => [ $description_part, $report_part, $original_part ],
133              
134             header => $arg{header} || [],
135             header_str => $arg{header_str} || [],
136             );
137              
138 3         10190 $class->new($report);
139             }
140              
141              
142             sub as_email {
143 9     9 1 4087 return Email::MIME->new($_[0]->as_string)
144             }
145              
146              
147 18     18 1 89 sub as_string { $_[0]->{mime}->as_string }
148              
149              
150             sub original_email {
151 9     9 1 4403 $_[0]->{original_email}
152             }
153              
154              
155 9     9   39 sub _description_part { $_[0]->{description_part} }
156              
157             sub description {
158 9     9 1 4503 $_[0]->_description_part->body;
159             }
160              
161 42     42   167 sub _fields { $_[0]->{fields} }
162              
163              
164             sub field {
165 42     42 1 7841 my ($self, $field) = @_;
166              
167 42         95 return $self->_fields->header($field);
168             }
169              
170              
171 9     9 1 18070 sub feedback_type { $_[0]->field('Feedback-Type'); }
172 9     9 1 4533 sub user_agent { $_[0]->field('User-Agent'); }
173 9     9 1 4221 sub arf_version { $_[0]->field('Version'); }
174              
175              
176             1;
177              
178             __END__