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-2022 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.03.
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   1195 use vars '$VERSION';
  31         57  
  31         1355  
11             $VERSION = '3.012';
12              
13 31     31   152 use base 'Mail::Reporter';
  31         67  
  31         8014  
14              
15 31     31   182 use strict;
  31         47  
  31         600  
16 31     31   135 use warnings;
  31         47  
  31         763  
17              
18 31     31   6838 use Mail::Message::Field;
  31         74  
  31         1083  
19 31     31   3810 use Mail::Message::Body::Lines;
  31         61  
  31         780  
20 31     31   12861 use Mail::Message::Body::File;
  31         103  
  31         1141  
21              
22 31     31   207 use Carp;
  31         65  
  31         1739  
23 31     31   177 use Scalar::Util qw/weaken refaddr/;
  31         124  
  31         1414  
24 31     31   180 use File::Basename qw/basename/;
  31         93  
  31         2552  
25              
26 31     31   14200 use MIME::Types;
  31         113616  
  31         4341  
27             my $mime_types = MIME::Types->new;
28             my $mime_plain = $mime_types->type('text/plain');
29              
30              
31             use overload
32 421     421   2178 bool => sub {1} # $body->print if $body
33             , '""' => 'string_unless_carp'
34             , '@{}' => 'lines'
35 11 50   11   143 , '==' => sub {ref $_[1] && refaddr $_[0] == refaddr $_[1]}
36 31 50   31   346 , '!=' => sub {ref $_[1] && refaddr $_[0] != refaddr $_[1]};
  31     41   142  
  31         407  
  41         434  
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 21657 { my $class = shift;
45              
46 214 100       1147 return $class->SUPER::new(@_)
47             unless $class eq __PACKAGE__;
48              
49 16         63 my %args = @_;
50              
51             exists $args{file}
52 16 50       113 ? 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 433 { my ($self, $args) = @_;
65              
66 198         658 $self->SUPER::init($args);
67              
68 198   50     782 $self->{MMB_modified} = $args->{modified} || 0;
69              
70 198         343 my $filename = $args->{filename};
71 198         329 my $mime = $args->{mime_type};
72              
73 198 100 100     960 if(defined(my $file = $args->{file}))
    100          
    100          
74             {
75 3 50       39 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       423 if(!ref $data)
    50          
91 80         387 { my @lines = split /^/, $data;
92 80         381 $self->_data_from_lines(\@lines)
93             }
94             elsif(ref $data eq 'ARRAY')
95 58 50       182 { $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       45 $self->_data_from_lines( [] ) or return;
103             }
104              
105             # Set the content info
106              
107             my ($transfer, $disp, $charset, $descr, $cid) = @$args{
108 198         758 qw/transfer_encoding disposition charset description content_id/ };
109              
110 198 50       496 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       544 if(defined(my $based = $args->{based_on}))
123 119 100       417 { $mime = $based->type unless defined $mime;
124 119 100       389 $transfer = $based->transferEncoding unless defined $transfer;
125 119 50       513 $disp = $based->disposition unless defined $disp;
126 119 50       503 $descr = $based->description unless defined $descr;
127 119 50       482 $cid = $based->contentId unless defined $cid;
128              
129             $self->{MMB_checked}
130 119 100       531 = defined $args->{checked} ? $args->{checked} : $based->checked;
131             }
132             else
133 79         127 { $transfer = $args->{transfer_encoding};
134 79   100     300 $self->{MMB_checked} = $args->{checked} || 0;
135             }
136              
137 198   100     581 $mime ||= 'text/plain';
138 198         594 $mime = $self->type($mime);
139 198 100 100     1164 $mime->attribute(charset => ($charset || 'PERL'))
      100        
140             if $mime =~ m!^text/!i && !$mime->attribute('charset');
141              
142 198 100       726 $self->transferEncoding($transfer) if defined $transfer;
143 198 100       625 $self->disposition($disp) if defined $disp;
144 198 100       605 $self->description($descr) if defined $descr;
145 198 100       590 $self->contentId($cid) if defined $cid;
146 198         542 $self->type($mime);
147              
148 198   50     956 $self->{MMB_eol} = $args->{eol} || 'NATIVE';
149              
150             # Set message where the body belongs to.
151              
152             $self->message($args->{message})
153 198 100       539 if defined $args->{message};
154              
155 198         463 $self->{MMB_seqnr} = $body_count++;
156 198         834 $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 69 { my $self = shift;
168 24         117 $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 227 { my $self = shift;
207 125 100       308 if(@_)
208 118 50       394 { if($self->{MMB_message} = shift)
209 118         382 { weaken $self->{MMB_message};
210             }
211             }
212 125         263 $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 1181 { my $self = shift;
235 883 100 66     4505 return $self->{MMB_type} if !@_ && defined $self->{MMB_type};
236              
237 413         724 delete $self->{MMB_mime};
238 413 100       828 my $type = defined $_[0] ? shift : 'text/plain';
239              
240 413 100       1462 $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 383 { my $self = shift;
247 44 100       178 return $self->{MMB_mime} if exists $self->{MMB_mime};
248              
249 26         52 my $field = $self->{MMB_type};
250 26 50       128 my $body = defined $field ? $field->body : '';
251              
252 26 50       105 return $self->{MMB_mime} = $mime_plain
253             unless length $body;
254              
255             $self->{MMB_mime}
256 26   33     135 = $mime_types->type($body) || MIME::Type->new(type => $body);
257             }
258              
259              
260 59     59 1 4769 sub charset() { shift->type->attribute('charset') }
261              
262              
263             sub transferEncoding(;$)
264 489     489 1 4269 { my $self = shift;
265 489 100 100     2003 return $self->{MMB_transfer} if !@_ && defined $self->{MMB_transfer};
266              
267 224 100       510 my $set = defined $_[0] ? shift : 'none';
268 224 100       848 $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 476 { my $self = shift;
275 338 100 100     989 return $self->{MMB_description} if !@_ && $self->{MMB_description};
276              
277 185 100       397 my $disp = defined $_[0] ? shift : 'none';
278 185 100       551 $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 485 { my $self = shift;
285 340 100 100     1134 return $self->{MMB_disposition} if !@_ && $self->{MMB_disposition};
286              
287 185 100       446 my $disp = defined $_[0] ? shift : 'none';
288              
289 185 100       532 $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 514 { my $self = shift;
296 338 100 100     1031 return $self->{MMB_id} if !@_ && $self->{MMB_id};
297              
298 185 100       399 my $cid = defined $_[0] ? shift : 'none';
299 185 100       489 $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 1448 { my $self = shift;
306 98 50       380 @_ ? ($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 9449 { my $self = shift;
322 21 50       166 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 176 { my ($self, $head) = @_;
366 83 50       279 return unless defined $head;
367              
368 83         293 my $lines = $self->nrLines;
369 83         287 my $size = $self->size;
370 83 50       268 $size += $lines if $Mail::Message::crlf_platform;
371              
372 83         193 $head->set($self->type);
373 83         237 $head->set($self->transferEncoding);
374 83         296 $head->set($self->disposition);
375 83         266 $head->set($self->description);
376 83         259 $head->set($self->contentId);
377 83         187 $self;
378             }
379              
380              
381             sub contentInfoFrom($)
382 17     17 1 31 { my ($self, $head) = @_;
383              
384 17         41 $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         159  
388 68         139 s/^\s+//,s/\s+$// for $x;
389 68 100       143 length $x ? $x : undef
390             } qw/Transfer-Encoding Disposition Description ID/;
391              
392 17         66 $self->transferEncoding($te);
393 17         54 $self->disposition($disp);
394 17         53 $self->description($desc);
395 17         65 $self->contentId($cid);
396              
397 17         36 delete $self->{MMB_mime};
398 17         51 $self;
399              
400             }
401              
402              
403             sub modified(;$)
404 10     10 1 19 { my $self = shift;
405 10 50       22 return $self->isModified unless @_; # compat 2.036
406 10         24 $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 25 { my $self = shift;
415 17 100       34 return @$self{ qw/MMB_begin MMB_end/ } unless @_;
416 15         58 @$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 6 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   457 { my $self = shift;
439 12         20 our $AUTOLOAD;
440 12         85 (my $call = $AUTOLOAD) =~ s/.*\:\://g;
441              
442 12   100     74 my $mod = $in_module{$call} || 'construct';
443 12 100       46 if($mod eq 'encode'){ require Mail::Message::Body::Encode }
  9         4898  
444 3         2124 else { require Mail::Message::Body::Construct }
445              
446 31     31   81891 no strict 'refs';
  31         73  
  31         3391  
447 12 50       249 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;