File Coverage

blib/lib/Mail/Message/Body.pm
Criterion Covered Total %
statement 160 217 73.7
branch 94 148 63.5
condition 35 59 59.3
subroutine 35 55 63.6
pod 32 34 94.1
total 356 513 69.4


line stmt bran cond sub pod time code
1             # Copyrights 2001-2021 by [Mark Overmeer ].
2             # For other contributors see ChangeLog.
3             # See the manual pages for details on the licensing terms.
4             # Pod stripped from pm file by OODoc 2.02.
5             # This code is part of distribution Mail-Message. Meta-POD processed with
6             # OODoc into POD and HTML manual-pages. See README.md
7             # Copyright Mark Overmeer. Licensed under the same terms as Perl itself.
8              
9             package Mail::Message::Body;
10 31     31   1726 use vars '$VERSION';
  31         71  
  31         1711  
11             $VERSION = '3.011';
12              
13 31     31   195 use base 'Mail::Reporter';
  31         63  
  31         9041  
14              
15 31     31   209 use strict;
  31         61  
  31         645  
16 31     31   159 use warnings;
  31         70  
  31         890  
17              
18 31     31   8083 use Mail::Message::Field;
  31         84  
  31         1132  
19 31     31   4428 use Mail::Message::Body::Lines;
  31         70  
  31         893  
20 31     31   15390 use Mail::Message::Body::File;
  31         106  
  31         1453  
21              
22 31     31   245 use Carp;
  31         76  
  31         2159  
23 31     31   216 use Scalar::Util qw/weaken refaddr/;
  31         104  
  31         1603  
24 31     31   197 use File::Basename qw/basename/;
  31         107  
  31         2931  
25              
26 31     31   17296 use MIME::Types;
  31         135053  
  31         5247  
27             my $mime_types = MIME::Types->new;
28             my $mime_plain = $mime_types->type('text/plain');
29              
30              
31             use overload
32 421     421   2427 bool => sub {1} # $body->print if $body
33             , '""' => 'string_unless_carp'
34             , '@{}' => 'lines'
35 11 50   11   141 , '==' => sub {ref $_[1] && refaddr $_[0] == refaddr $_[1]}
36 31 50   31   362 , '!=' => sub {ref $_[1] && refaddr $_[0] != refaddr $_[1]};
  31     41   177  
  31         393  
  41         495  
37              
38             #------------------------------------------
39              
40              
41             my $body_count = 0; # to be able to compare bodies for equivalence.
42              
43             sub new(@)
44 214     214 1 22651 { my $class = shift;
45              
46 214 100       1223 return $class->SUPER::new(@_)
47             unless $class eq __PACKAGE__;
48              
49 16         66 my %args = @_;
50              
51             exists $args{file}
52 16 50       118 ? Mail::Message::Body::File->new(@_)
53             : Mail::Message::Body::Lines->new(@_);
54             }
55              
56             # All body implementations shall implement all of the following!!
57              
58 0     0   0 sub _data_from_filename(@) {shift->notImplemented}
59 0     0   0 sub _data_from_filehandle(@) {shift->notImplemented}
60 0     0   0 sub _data_from_glob(@) {shift->notImplemented}
61 0     0   0 sub _data_from_lines(@) {shift->notImplemented}
62              
63             sub init($)
64 198     198 0 505 { my ($self, $args) = @_;
65              
66 198         707 $self->SUPER::init($args);
67              
68 198   50     848 $self->{MMB_modified} = $args->{modified} || 0;
69              
70 198         382 my $filename = $args->{filename};
71 198         372 my $mime = $args->{mime_type};
72              
73 198 100 100     1030 if(defined(my $file = $args->{file}))
    100          
    100          
74             {
75 3 50       41 if(!ref $file)
    50          
    50          
76 0 0       0 { $self->_data_from_filename($file) or return;
77 0   0     0 $filename ||= $file;
78 0   0     0 $mime ||= $mime_types->mimeTypeOf($filename)
      0        
79             || (-T $file ? 'text/plain' : 'application/octet-stream');
80             }
81             elsif(ref $file eq 'GLOB')
82 0 0       0 { $self->_data_from_glob($file) or return }
83             elsif($file->isa('IO::Handle'))
84 3 50       17 { $self->_data_from_filehandle($file) or return }
85             else
86 0         0 { croak "message body: illegal datatype `".ref($file)."' for file option" }
87             }
88             elsif(defined(my $data = $args->{data}))
89             {
90 138 100       447 if(!ref $data)
    50          
91 80         392 { my @lines = split /^/, $data;
92 80         385 $self->_data_from_lines(\@lines)
93             }
94             elsif(ref $data eq 'ARRAY')
95 58 50       215 { $self->_data_from_lines($data) or return;
96             }
97             else
98 0         0 { croak "message body: illegal datatype `".ref($data)."' for data option" }
99             }
100             elsif(! $self->isMultipart && ! $self->isNested)
101             { # Neither 'file' nor 'data', so empty body.
102 13 50       57 $self->_data_from_lines( [] ) or return;
103             }
104              
105             # Set the content info
106              
107             my ($transfer, $disp, $charset, $descr, $cid) = @$args{
108 198         721 qw/transfer_encoding disposition charset description content_id/ };
109              
110 198 50       503 if(defined $filename)
111 0 0 0     0 { $disp //= Mail::Message::Field->new
112             ( 'Content-Disposition' => (-T $filename ? 'inline' : 'attachment')
113             , filename => basename($filename)
114             );
115 0   0     0 $mime //= $mime_types->mimeTypeOf($filename);
116             }
117              
118 198 50 66     986 if(ref $mime && $mime->isa('MIME::Type'))
119 0         0 { $mime = $mime->type;
120             }
121              
122 198 100       614 if(defined(my $based = $args->{based_on}))
123 119 100       417 { $mime = $based->type unless defined $mime;
124 119 100       433 $transfer = $based->transferEncoding unless defined $transfer;
125 119 50       502 $disp = $based->disposition unless defined $disp;
126 119 50       539 $descr = $based->description unless defined $descr;
127 119 50       506 $cid = $based->contentId unless defined $cid;
128              
129             $self->{MMB_checked}
130 119 100       526 = defined $args->{checked} ? $args->{checked} : $based->checked;
131             }
132             else
133 79         166 { $transfer = $args->{transfer_encoding};
134 79   100     369 $self->{MMB_checked} = $args->{checked} || 0;
135             }
136              
137 198   100     670 $mime ||= 'text/plain';
138 198         585 $mime = $self->type($mime);
139 198 100 100     1356 $mime->attribute(charset => ($charset || 'PERL'))
      100        
140             if $mime =~ m!^text/!i && !$mime->attribute('charset');
141              
142 198 100       810 $self->transferEncoding($transfer) if defined $transfer;
143 198 100       766 $self->disposition($disp) if defined $disp;
144 198 100       619 $self->description($descr) if defined $descr;
145 198 100       640 $self->contentId($cid) if defined $cid;
146 198         589 $self->type($mime);
147              
148 198   50     1018 $self->{MMB_eol} = $args->{eol} || 'NATIVE';
149              
150             # Set message where the body belongs to.
151              
152             $self->message($args->{message})
153 198 100       600 if defined $args->{message};
154              
155 198         588 $self->{MMB_seqnr} = $body_count++;
156 198         927 $self;
157             }
158              
159              
160              
161 0     0 1 0 sub clone() {shift->notImplemented}
162              
163             #------------------------------------------
164              
165              
166             sub decoded(@)
167 24     24 1 61 { my $self = shift;
168 24         103 $self->encode(charset => 'PERL', transfer_encoding => 'none', @_);
169             }
170              
171              
172             sub eol(;$)
173 0     0 1 0 { my $self = shift;
174 0 0       0 return $self->{MMB_eol} unless @_;
175              
176 0         0 my $eol = shift;
177 0 0       0 if($eol eq 'NATIVE')
178 0 0       0 { $eol = $^O =~ m/^win/i ? 'CRLF'
    0          
179             : $^O =~ m/^mac/i ? 'CR'
180             : 'LF';
181             }
182              
183 0 0 0     0 return $self if $eol eq $self->{MMB_eol} && $self->checked;
184 0         0 my $lines = $self->lines;
185 0 0       0 if(@$lines)
186             { # sometimes texts lack \n on last line
187 0         0 $lines->[-1] .= "\n";
188            
189              
190 0 0       0 if($eol eq 'CR') {s/[\015\012]+$/\015/ for @$lines}
  0 0       0  
    0          
191 0         0 elsif($eol eq 'LF') {s/[\015\012]+$/\012/ for @$lines}
192 0         0 elsif($eol eq 'CRLF') {s/[\015\012]+$/\015\012/ for @$lines}
193             else
194 0         0 { $self->log(WARNING => "Unknown line terminator $eol ignored");
195 0         0 return $self->eol('NATIVE');
196             }
197             }
198              
199 0         0 (ref $self)->new(based_on => $self, eol => $eol, data => $lines);
200             }
201              
202             #------------------------------------------
203              
204              
205             sub message(;$)
206 125     125 1 207 { my $self = shift;
207 125 100       312 if(@_)
208 118 50       441 { if($self->{MMB_message} = shift)
209 118         406 { weaken $self->{MMB_message};
210             }
211             }
212 125         286 $self->{MMB_message};
213             }
214              
215              
216             sub isDelayed() {0}
217              
218              
219             sub isMultipart() {0}
220              
221              
222             sub isNested() {0}
223              
224              
225             sub partNumberOf($)
226 0     0 1 0 { shift->log(ERROR => 'part number needs multi-part or nested');
227 0         0 'ERROR';
228             }
229              
230             #------------------------------------------
231              
232              
233             sub type(;$)
234 883     883 1 1410 { my $self = shift;
235 883 100 66     4827 return $self->{MMB_type} if !@_ && defined $self->{MMB_type};
236              
237 413         719 delete $self->{MMB_mime};
238 413 100       913 my $type = defined $_[0] ? shift : 'text/plain';
239              
240 413 100       1562 $self->{MMB_type} = ref $type ? $type->clone
241             : Mail::Message::Field->new('Content-Type' => $type);
242             }
243              
244              
245             sub mimeType()
246 44     44 1 327 { my $self = shift;
247 44 100       191 return $self->{MMB_mime} if exists $self->{MMB_mime};
248              
249 26         68 my $field = $self->{MMB_type};
250 26 50       133 my $body = defined $field ? $field->body : '';
251              
252 26 50       81 return $self->{MMB_mime} = $mime_plain
253             unless length $body;
254              
255             $self->{MMB_mime}
256 26   33     124 = $mime_types->type($body) || MIME::Type->new(type => $body);
257             }
258              
259              
260 59     59 1 4810 sub charset() { shift->type->attribute('charset') }
261              
262              
263             sub transferEncoding(;$)
264 489     489 1 4832 { my $self = shift;
265 489 100 100     2243 return $self->{MMB_transfer} if !@_ && defined $self->{MMB_transfer};
266              
267 224 100       526 my $set = defined $_[0] ? shift : 'none';
268 224 100       948 $self->{MMB_transfer} = ref $set ? $set->clone
269             : Mail::Message::Field->new('Content-Transfer-Encoding' => $set);
270             }
271              
272              
273             sub description(;$)
274 338     338 1 516 { my $self = shift;
275 338 100 100     1096 return $self->{MMB_description} if !@_ && $self->{MMB_description};
276              
277 185 100       442 my $disp = defined $_[0] ? shift : 'none';
278 185 100       575 $self->{MMB_description} = ref $disp ? $disp->clone
279             : Mail::Message::Field->new('Content-Description' => $disp);
280             }
281              
282              
283             sub disposition(;$)
284 340     340 1 535 { my $self = shift;
285 340 100 100     1223 return $self->{MMB_disposition} if !@_ && $self->{MMB_disposition};
286              
287 185 100       460 my $disp = defined $_[0] ? shift : 'none';
288              
289 185 100       637 $self->{MMB_disposition} = ref $disp ? $disp->clone
290             : Mail::Message::Field->new('Content-Disposition' => $disp);
291             }
292              
293              
294             sub contentId(;$)
295 338     338 1 533 { my $self = shift;
296 338 100 100     1101 return $self->{MMB_id} if !@_ && $self->{MMB_id};
297              
298 185 100       422 my $cid = defined $_[0] ? shift : 'none';
299 185 100       588 $self->{MMB_id} = ref $cid ? $cid->clone
300             : Mail::Message::Field->new('Content-ID' => $cid);
301             }
302              
303              
304             sub checked(;$)
305 98     98 1 2348 { my $self = shift;
306 98 50       401 @_ ? ($self->{MMB_checked} = shift) : $self->{MMB_checked};
307             }
308              
309              
310 0     0 1 0 sub nrLines(@) {shift->notImplemented}
311              
312              
313 0     0 1 0 sub size(@) {shift->notImplemented}
314              
315             #------------------------------------------
316              
317              
318 0     0 1 0 sub string() {shift->notImplemented}
319              
320             sub string_unless_carp()
321 21     21 0 9995 { my $self = shift;
322 21 50       146 return $self->string unless (caller)[0] eq 'Carp';
323              
324 0         0 (my $class = ref $self) =~ s/^Mail::Message/MM/;
325 0         0 "$class object";
326             }
327              
328              
329 0     0 1 0 sub lines() {shift->notImplemented}
330              
331              
332 0     0 1 0 sub file(;$) {shift->notImplemented}
333              
334              
335 0     0 1 0 sub print(;$) {shift->notImplemented}
336              
337              
338 0     0 1 0 sub printEscapedFrom($) {shift->notImplemented}
339              
340              
341             sub write(@)
342 0     0 1 0 { my ($self, %args) = @_;
343 0         0 my $filename = $args{filename};
344 0 0       0 die "No filename for write() body" unless defined $filename;
345              
346 0 0       0 open OUT, '>', $filename or return;
347 0         0 $self->print(\*OUT);
348 0 0       0 close OUT or return undef;
349 0         0 $self;
350             }
351              
352              
353 0     0 1 0 sub endsOnNewline() {shift->notImplemented}
354              
355              
356 0     0 1 0 sub stripTrailingNewline() {shift->notImplemented}
357              
358             #------------------------------------------
359              
360              
361 0     0 1 0 sub read(@) {shift->notImplemented}
362              
363              
364             sub contentInfoTo($)
365 83     83 1 196 { my ($self, $head) = @_;
366 83 50       225 return unless defined $head;
367              
368 83         300 my $lines = $self->nrLines;
369 83         281 my $size = $self->size;
370 83 50       242 $size += $lines if $Mail::Message::crlf_platform;
371              
372 83         217 $head->set($self->type);
373 83         269 $head->set($self->transferEncoding);
374 83         280 $head->set($self->disposition);
375 83         270 $head->set($self->description);
376 83         257 $head->set($self->contentId);
377 83         214 $self;
378             }
379              
380              
381             sub contentInfoFrom($)
382 17     17 1 41 { my ($self, $head) = @_;
383              
384 17         47 $self->type($head->get('Content-Type', 0));
385              
386             my ($te, $disp, $desc, $cid)
387 17   100     37 = map { my $x = $head->get("Content-$_") || '';
  68         151  
388 68         152 s/^\s+//,s/\s+$// for $x;
389 68 100       142 length $x ? $x : undef
390             } qw/Transfer-Encoding Disposition Description ID/;
391              
392 17         68 $self->transferEncoding($te);
393 17         60 $self->disposition($disp);
394 17         57 $self->description($desc);
395 17         52 $self->contentId($cid);
396              
397 17         30 delete $self->{MMB_mime};
398 17         45 $self;
399              
400             }
401              
402              
403             sub modified(;$)
404 10     10 1 21 { my $self = shift;
405 10 50       31 return $self->isModified unless @_; # compat 2.036
406 10         28 $self->{MMB_modified} = shift;
407             }
408              
409              
410 0     0 1 0 sub isModified() { shift->{MMB_modified} }
411              
412              
413             sub fileLocation(;@)
414 17     17 1 26 { my $self = shift;
415 17 100       37 return @$self{ qw/MMB_begin MMB_end/ } unless @_;
416 15         64 @$self{ qw/MMB_begin MMB_end/ } = @_;
417             }
418              
419              
420             sub moveLocation($)
421 0     0 1 0 { my ($self, $dist) = @_;
422 0         0 $self->{MMB_begin} -= $dist;
423 0         0 $self->{MMB_end} -= $dist;
424 0         0 $self;
425             }
426              
427              
428 2     2 1 7 sub load() {shift}
429              
430             #------------------------------------------
431              
432              
433             my @in_encode = qw/check encode encoded eol isBinary isText unify
434             dispositionFilename/;
435             my %in_module = map { ($_ => 'encode') } @in_encode;
436              
437             sub AUTOLOAD(@)
438 12     12   615 { my $self = shift;
439 12         27 our $AUTOLOAD;
440 12         143 (my $call = $AUTOLOAD) =~ s/.*\:\://g;
441              
442 12   100     76 my $mod = $in_module{$call} || 'construct';
443 12 100       64 if($mod eq 'encode'){ require Mail::Message::Body::Encode }
  9         5695  
444 3         2101 else { require Mail::Message::Body::Construct }
445              
446 31     31   97346 no strict 'refs';
  31         114  
  31         3833  
447 12 50       347 return $self->$call(@_) if $self->can($call); # now loaded
448              
449             # AUTOLOAD inheritance is a pain
450 0           confess "Method $call() is not defined for a ", ref $self;
451             }
452              
453             #------------------------------------------
454              
455              
456             1;