File Coverage

blib/lib/MIME/Lite/Generator.pm
Criterion Covered Total %
statement 120 132 90.9
branch 66 82 80.4
condition 6 9 66.6
subroutine 15 17 88.2
pod 2 12 16.6
total 209 252 82.9


line stmt bran cond sub pod time code
1             package MIME::Lite::Generator;
2              
3 2     2   46230 use strict;
  2         3  
  2         60  
4 2     2   9 use warnings;
  2         3  
  2         57  
5 2     2   16 use Carp;
  2         2  
  2         109  
6 2     2   11 use FileHandle;
  2         3  
  2         14  
7 2     2   640 use MIME::Lite;
  2         2  
  2         2371  
8              
9             our $VERSION = 0.02;
10              
11             sub new {
12 46     46 1 76517 my ( $class, $msg, $is_smtp ) = @_;
13            
14 46         86 my $encoding = uc( $msg->{Attrs}{'content-transfer-encoding'} );
15 46         43 my $chunk_getter;
16 46 100 66     251 if (defined( $msg->{Path} ) || defined( $msg->{FH} ) || defined ( $msg->{Data} )) {
      100        
17 36 100       105 if ($encoding eq 'BINARY') {
    100          
    100          
    100          
    50          
18 4 100       12 $chunk_getter = defined ( $msg->{Data} )
19             ? 'get_encoded_chunk_data_other'
20             : 'get_encoded_chunk_fh_binary';
21             }
22             elsif ($encoding eq '8BIT') {
23 19 100       41 $chunk_getter = defined ( $msg->{Data} )
24             ? 'get_encoded_chunk_data_other'
25             : 'get_encoded_chunk_fh_8bit';
26             }
27             elsif ($encoding eq '7BIT') {
28 4 100       12 $chunk_getter = defined ( $msg->{Data} )
29             ? 'get_encoded_chunk_data_other'
30             : 'get_encoded_chunk_fh_7bit';
31             }
32             elsif ($encoding eq 'QUOTED-PRINTABLE') {
33 4 100       11 $chunk_getter = defined ( $msg->{Data} )
34             ? 'get_encoded_chunk_data_qp'
35             : 'get_encoded_chunk_fh_qp';
36             }
37             elsif ($encoding eq 'BASE64') {
38 5 100       18 $chunk_getter = defined ( $msg->{Data} )
39             ? 'get_encoded_chunk_data_other'
40             : 'get_encoded_chunk_fh_base64';
41             }
42             else {
43 0         0 $chunk_getter = 'get_encoded_chunk_unknown';
44             }
45             }
46             else {
47 10         14 $chunk_getter = 'get_encoded_chunk_nodata';
48             }
49            
50 46 100       338 bless {
51             msg => $msg,
52             is_smtp => $is_smtp,
53             generators => [],
54             has_chunk => 0,
55             state => 'init',
56             encoding => $encoding,
57             chunk_getter => $chunk_getter,
58             last => '',
59             }, ref($class) ? ref($class) : $class;
60             }
61              
62             sub get {
63 1616     1616 1 2874 my $self = shift;
64            
65             ### Do we have generators for embedded/main part(s)
66 1616         1077 while (@{ $self->{generators} }) {
  1616         2475  
67 783         1032 my $str_ref = $self->{generators}[0]->get();
68 783 100       2719 return $str_ref if $str_ref;
69            
70 25         18 shift @{ $self->{generators} };
  25         33  
71            
72 25 50       183 if ($self->{boundary}) {
73 25 100       21 if (@{ $self->{generators} }) {
  25         48  
74 15         41 return \"\n--$self->{boundary}\n";
75             }
76            
77             ### Boundary at the end
78 10         26 return \"\n--$self->{boundary}--\n\n";
79             }
80             }
81            
82             ### What we should to generate
83 833 100       1176 if ($self->{state} eq 'init') {
84 45         51 my $attrs = $self->{msg}{Attrs};
85 45         53 my $sub_attrs = $self->{msg}{SubAttrs};
86 45         30 my $rv;
87 45         48 $self->{state} = 'first';
88            
89             ### Output either the body or the parts.
90             ### Notice that we key off of the content-type! We expect fewer
91             ### accidents that way, since the syntax will always match the MIME type.
92 45         41 my $type = $attrs->{'content-type'};
93 45 100       135 if ( $type =~ m{^multipart/}i ) {
    50          
94 10         21 $self->{boundary} = $sub_attrs->{'content-type'}{'boundary'};
95              
96             ### Preamble:
97 10 50       21 $rv = \($self->{msg}->header_as_string . "\n" . (
98             defined( $self->{msg}{Preamble} )
99             ? $self->{msg}{Preamble}
100             : "This is a multi-part message in MIME format.\n"
101             ) .
102             "\n--$self->{boundary}\n");
103              
104             ### Parts:
105 10         1215 my $part;
106 10         11 foreach $part ( @{ $self->{msg}{Parts} } ) {
  10         16  
107 25         23 push @{ $self->{generators} }, $self->new($part, $self->{out}, $self->{is_smtp});
  25         68  
108             }
109             }
110             elsif ( $type =~ m{^message/} ) {
111 0         0 my @parts = @{ $self->{msg}{Parts} };
  0         0  
112              
113             ### It's a toss-up; try both data and parts:
114 0 0       0 if ( @parts == 0 ) {
    0          
115 0         0 $self->{has_chunk} = 1;
116 0         0 $rv = $self->get_encoded_chunk()
117             }
118             elsif ( @parts == 1 ) {
119 0         0 $self->{generators}[0] = $self->new($parts[0], $self->{out}, $self->{is_smtp});
120 0         0 $rv = $self->{generators}[0]->get();
121             }
122             else {
123 0         0 Carp::croak "can't handle message with >1 part\n";
124             }
125             }
126             else {
127 35         34 $self->{has_chunk} = 1;
128 35         56 $rv = $self->get_encoded_chunk();
129             }
130            
131 45         3310 return $rv;
132             }
133            
134 788 100       1137 return $self->{has_chunk} ? $self->get_encoded_chunk() : undef;
135             }
136              
137             sub get_encoded_chunk {
138 788     788 0 588 my $self = shift;
139            
140 788 100       954 if ($self->{state} eq 'first') {
141 35         39 $self->{state} = '';
142             ### Open file if necessary:
143 35 100       72 unless (defined $self->{msg}{Data}) {
144 10 50       19 if ( defined( $self->{msg}{Path} ) ) {
145 10   33     53 $self->{fh} = new FileHandle || Carp::croak "can't get new filehandle\n";
146 10 50       298 $self->{fh}->open($self->{msg}{Path})
147             or Carp::croak "open $self->{msg}{Path}: $!\n";
148             }
149             else {
150 0         0 $self->{fh} = $self->{msg}{FH};
151             }
152 10 50       397 CORE::binmode($self->{fh}) if $self->{msg}->binmode;
153             }
154            
155             ### Headers first
156 35         158 return \($self->{msg}->header_as_string . "\n");
157             }
158            
159 753         602 my $chunk_getter = $self->{chunk_getter};
160 753         952 $self->$chunk_getter();
161             }
162              
163             sub get_encoded_chunk_data_qp {
164 4     4 0 3 my $self = shift;
165            
166             ### Encode it line by line:
167 4 100       28 if ($self->{msg}{Data} =~ m{^(.*[\r\n]*)}smg) {
168 2         3 my $line = $1; # copy to avoid weird bug; rt 39334
169 2         40 return \MIME::Lite::encode_qp($line);
170             }
171            
172 2         3 $self->{has_chunk} = 0;
173 2         9 return;
174             }
175              
176             sub get_encoded_chunk_data_other {
177 23     23 0 22 my $self = shift;
178 23         26 $self->{has_chunk} = 0;
179            
180 23 100       48 if ($self->{encoding} eq 'BINARY') {
181 2 50       5 $self->{is_smtp} and $self->{msg}{Data} =~ s/(?!\r)\n\z/\r/;
182 2         4 return \$self->{msg}{Data};
183             }
184            
185 21 100       54 if ($self->{encoding} eq '8BIT') {
186 17         61 return \MIME::Lite::encode_8bit( $self->{msg}{Data} );
187             }
188            
189 4 100       8 if ($self->{encoding} eq '7BIT') {
190 2         6 return \MIME::Lite::encode_7bit( $self->{msg}{Data} );
191             }
192            
193 2 50       6 if ($self->{encoding} eq 'BASE64') {
194 2         10 return \MIME::Lite::encode_base64( $self->{msg}{Data} );
195             }
196             }
197              
198             sub get_encoded_chunk_fh_binary {
199 6     6 0 7 my $self = shift;
200 6         5 my $rv;
201            
202 6 100       47 if ( read( $self->{fh}, $_, 2048 ) ) {
203 4         5 $rv = $self->{last};
204 4         9 $self->{last} = $_;
205             }
206             else {
207 2         7 seek $self->{fh}, 0, 0;
208 2         3 $self->{has_chunk} = 0;
209 2 50       9 if ( length $self->{last} ) {
210 2 50       5 $self->{is_smtp} and $self->{last} =~ s/(?!\r)\n\z/\r/;
211 2         3 $rv = $self->{last};
212             }
213             }
214            
215 6 50       15 return defined($rv) ? \$rv : undef;
216             }
217              
218             sub get_encoded_chunk_fh_8bit {
219 198     198 0 147 my $self = shift;
220            
221 198 100       360 if ( defined( $_ = readline( $self->{fh} ) ) ) {
222 196         265 return \MIME::Lite::encode_8bit($_);
223             }
224            
225 2         4 $self->{has_chunk} = 0;
226 2         6 seek $self->{fh}, 0, 0;
227 2         4 return;
228             }
229              
230             sub get_encoded_chunk_fh_7bit {
231 198     198 0 146 my $self = shift;
232            
233 198 100       352 if ( defined( $_ = readline( $self->{fh} ) ) ) {
234 196         305 return \MIME::Lite::encode_7bit($_);
235             }
236            
237 2         4 $self->{has_chunk} = 0;
238 2         7 seek $self->{fh}, 0, 0;
239 2         5 return;
240             }
241              
242             sub get_encoded_chunk_fh_qp {
243 198     198 0 156 my $self = shift;
244            
245 198 100       363 if ( defined( $_ = readline( $self->{fh} ) ) ) {
246 196         536 return \MIME::Lite::encode_qp($_);
247             }
248            
249 2         5 $self->{has_chunk} = 0;
250 2         9 seek $self->{fh}, 0, 0;
251 2         4 return;
252             }
253              
254             sub get_encoded_chunk_fh_base64 {
255 126     126 0 94 my $self = shift;
256            
257 126 100       265 if ( read( $self->{fh}, $_, 45 ) ) {
258 124         237 return \MIME::Lite::encode_base64($_);
259             }
260            
261 2         4 $self->{has_chunk} = 0;
262 2         6 seek $self->{fh}, 0, 0;
263 2         4 return;
264             }
265              
266             sub get_encoded_chunk_unknown {
267 0     0 0   croak "unsupported encoding: `$_[0]->{encoding}'\n";
268             }
269              
270             sub get_encoded_chunk_nodata {
271 0     0 0   croak "no data in this part\n";
272             }
273              
274             1;
275              
276             __END__