File Coverage

blib/lib/HTML/FromMail/Message.pm
Criterion Covered Total %
statement 27 166 16.2
branch 0 68 0.0
condition 0 39 0.0
subroutine 9 31 29.0
pod 21 22 95.4
total 57 326 17.4


line stmt bran cond sub pod time code
1             # This code is part of Perl distribution HTML-FromMail version 4.00.
2             # The POD got stripped from this file by OODoc version 3.05.
3             # For contributors see file ChangeLog.
4              
5             # This software is copyright (c) 2003-2025 by Mark Overmeer.
6              
7             # This is free software; you can redistribute it and/or modify it under
8             # the same terms as the Perl 5 programming language system itself.
9             # SPDX-License-Identifier: Artistic-1.0-Perl OR GPL-1.0-or-later
10              
11              
12             package HTML::FromMail::Message;{
13             our $VERSION = '4.00';
14             }
15              
16 1     1   4021 use base 'HTML::FromMail::Page';
  1         4  
  1         200  
17              
18 1     1   8 use strict;
  1         3  
  1         35  
19 1     1   5 use warnings;
  1         2  
  1         75  
20              
21 1     1   8 use Log::Report 'html-frommail';
  1         2  
  1         9  
22              
23 1     1   474 use HTML::FromMail::Head ();
  1         3  
  1         26  
24 1     1   18 use HTML::FromMail::Field ();
  1         3  
  1         47  
25 1     1   6 use HTML::FromMail::Default::Previewers ();
  1         2  
  1         25  
26 1     1   5 use HTML::FromMail::Default::HTMLifiers ();
  1         2  
  1         46  
27              
28 1     1   9 use File::Basename qw/basename/;
  1         2  
  1         3617  
29              
30             #--------------------
31              
32             sub init($)
33 0     0 0   { my ($self, $args) = @_;
34 0   0       $args->{topic} ||= 'message';
35 0           $self->SUPER::init($args);
36              
37 0           $self->{HFM_dispose} = $args->{disposition};
38 0           my $settings = $self->settings;
39              
40             # Collect previewers
41 0           my @prevs = @HTML::FromMail::Default::Previewers::previewers;
42 0 0         if(my $prevs = $settings->{previewers})
43 0           { unshift @prevs, @$prevs;
44             }
45 0           $self->{HFM_previewers} = \@prevs;
46              
47             # Collect htmlifiers
48 0           my @html = @HTML::FromMail::Default::HTMLifiers::htmlifiers;
49 0 0         if(my $html = $settings->{htmlifiers})
50 0           { unshift @html, @$html;
51             }
52 0           $self->{HFM_htmlifiers} = \@html;
53              
54             # We will use header and field formatters
55 0           $self->{HFM_field} = HTML::FromMail::Field->new(settings => $settings);
56 0           $self->{HFM_head} = HTML::FromMail::Head ->new(settings => $settings);
57              
58 0           $self;
59             }
60              
61             #-----------
62              
63 0     0 1   sub fields() { $_[0]->{HFM_field} }
64              
65              
66 0     0 1   sub header() { $_[0]->{HFM_head} }
67              
68             #-----------
69              
70              
71             my $attach_id = 0;
72              
73             sub createAttachment($$$)
74 0     0 1   { my ($self, $message, $part, $args) = @_;
75 0 0         my $outdir = $args->{outdir} or panic;
76 0           my $decoded = $part->decoded;
77              
78 0           my $filename = $part->label('filename');
79 0 0         unless(defined $filename)
80 0           { $filename = $decoded->dispositionFilename($outdir);
81 0           $part->label(filename => $filename);
82             }
83              
84 0           $decoded->write(filename => $filename);
85              
86 0           +( url => basename($filename),
87             size => (-s $filename),
88             type => $decoded->type->body,
89              
90             filename => $filename, # absolute
91             decoded => $decoded,
92             );
93             }
94              
95              
96             sub htmlField($$)
97 0     0 1   { my ($self, $message, $args) = @_;
98              
99 0           my $name = $args->{name};
100 0 0         unless(defined $name)
101 0           { warning __x"no field name specified in {template}.", template => $args->{input};
102 0           $name = "NONE";
103             }
104              
105 0           my $current = $self->lookup('part_object', $args);
106              
107 0           my $head;
108 0   0       for($args->{from} || 'PART')
109 0   0       { my $source = ($_ eq 'PART' ? $current : $_ eq 'PARENT' ? $current->container : undef) || $message;
110 0           $head = $source->head;
111             }
112              
113 0           my @fields = $self->fields->fromHead($head, $name, $args);
114              
115 0 0         $args->{formatter}->onFinalToken($args)
116             or return [ map +{ field_object => $_ }, @fields ];
117              
118 0           my $f = $self->fields;
119 0           join "
\n", map $f->htmlBody($_, $args), @fields;
120             }
121              
122              
123             sub htmlSubject($$)
124 0     0 1   { my ($self, $message, $args) = @_;
125 0           my %args = (%$args, name => 'subject', from => 'NESSAGE');
126 0           $self->htmlField($message, \%args);
127             }
128              
129              
130             sub htmlName($$)
131 0     0 1   { my ($self, $message, $args) = @_;
132              
133 0 0         my $field = $self->lookup('field_object', $args)
134             or error __x"use of 'name' outside field container."; #XXX better message?
135              
136 0           $self->fields->htmlName($field, $args);
137             }
138              
139              
140             sub htmlBody($$)
141 0     0 1   { my ($self, $message, $args) = @_;
142              
143 0 0         my $field = $self->lookup('field_object', $args)
144             or error __x"use of 'body' outside field container";
145              
146 0           $self->fields->htmlBody($field, $args);
147             }
148              
149              
150             sub htmlAddresses($$)
151 0     0 1   { my ($self, $message, $args) = @_;
152              
153 0 0         my $field = $self->lookup('field_object', $args)
154             or error __x"use of 'addresses' outside field container";
155              
156 0           $self->fields->htmlAddresses($field, $args);
157             }
158              
159              
160             sub htmlHead($$)
161 0     0 1   { my ($self, $message, $args) = @_;
162              
163 0   0       my $current = $self->lookup('part_object', $args) || $message;
164 0 0         my $head = $current->head or return;
165 0           my @fields = $self->header->fields($head, $args);
166              
167 0 0         $args->{formatter}->onFinalToken($args)
168             or return [ map +{ field_object => $_ }, @fields ];
169              
170 0           local $" = '';
171 0           "
@{ [ map $_->string, @fields ] }
\n";
  0            
172             }
173              
174              
175             sub htmlMessage($$)
176 0     0 1   { my ($self, $message, $args) = @_;
177 0           +{ message_text => $args->{formatter}->containerText($args) };
178             }
179              
180              
181             sub htmlMultipart($$)
182 0     0 1   { my ($self, $message, $args) = @_;
183 0   0       my $current = $self->lookup('part_object', $args) || $message;
184 0 0         $current->isMultipart or return '';
185              
186 0           my $body = $current->body; # un-decoded info is more useful
187 0           +{ type => $body->mimeType->type, size => $body->size };
188             }
189              
190              
191             sub htmlNested($$)
192 0     0 1   { my ($self, $message, $args) = @_;
193 0   0       my $current = $self->lookup('part_object', $args) || $message;
194 0 0         $current->isNested or return '';
195              
196 0           my $partnr = $self->lookup('part_number', $args);
197 0 0         $partnr .= '.' if length $partnr;
198              
199 0           [ +{ part_number => $partnr . '1', part_object => $current->body->nested } ];
200             }
201              
202              
203             sub htmlifier($)
204 0     0 1   { my ($self, $type) = @_;
205 0           my $pairs = $self->{HFM_htmlifiers};
206 0           for(my $i=0; $i < @$pairs; $i+=2)
207 0 0         { return $pairs->[$i+1] if $type eq $pairs->[0];
208             }
209 0           undef;
210             }
211              
212              
213             sub previewer($)
214 0     0 1   { my ($self, $type) = @_;
215 0           my $pairs = $self->{HFM_previewers};
216 0           for(my $i=0; $i < @$pairs; $i+=2)
217 0 0 0       { return $pairs->[$i+1] if $type eq $pairs->[$i] || $type->mediaType eq $pairs->[$i];
218             }
219 0           undef;
220             }
221              
222              
223             sub disposition($$$)
224 0     0 1   { my ($self, $message, $part, $args) = @_;
225 0 0 0       return '' if $part->isMultipart || $part->isNested;
226              
227 0           my $cd = $part->head->get('Content-Disposition');
228              
229 0 0         my $sugg = defined $cd ? lc($cd->body) : '';
230 0 0         $sugg = 'attach' if $sugg =~ m/^\s*attach/;
231              
232 0           my $body = $part->body;
233 0           my $type = $body->mimeType;
234              
235 0 0         if($sugg eq 'inline')
    0          
    0          
    0          
236 0 0         { $sugg = $self->htmlifier($type) ? 'inline' : $self->previewer($type) ? 'preview' : 'attach';
    0          
237             }
238             elsif($sugg eq 'attach')
239 0 0         { $sugg = 'preview' if $self->previewer($type);
240             }
241 0           elsif($self->htmlifier($type)) { $sugg = 'inline' }
242 0           elsif($self->previewer($type)) { $sugg = 'preview' }
243 0           else { $sugg = 'attach' }
244              
245             # User may have a different opinion.
246 0 0         my $disp = $self->settings->{disposition} or return $sugg;
247 0           $disp->($message, $part, $sugg, $args)
248             }
249              
250              
251             sub htmlInline($$)
252 0     0 1   { my ($self, $message, $args) = @_;
253              
254 0   0       my $current = $self->lookup('part_object', $args) || $message;
255 0           my $dispose = $self->disposition($message, $current, $args);
256 0 0         $dispose eq 'inline' or return '';
257              
258 0           my @attach = $self->createAttachment($message, $current, $args);
259 0           my $inliner = $self->htmlifier($current->body->mimeType);
260 0           my $inline = $inliner->($self, $message, $current, $args);
261              
262 0           +{ %$inline, @attach };
263             }
264              
265              
266             sub htmlAttach($$)
267 0     0 1   { my ($self, $message, $args) = @_;
268              
269 0   0       my $current = $self->lookup('part_object', $args) || $message;
270 0           my $dispose = $self->disposition($message, $current, $args);
271 0 0         $dispose eq 'attach' or return '';
272              
273 0           my %attach = $self->createAttachment($message, $current, $args);
274 0           \%attach;
275             }
276              
277              
278             sub htmlPreview($$)
279 0     0 1   { my ($self, $message, $args) = @_;
280              
281 0   0       my $current = $self->lookup('part_object', $args) || $message;
282 0           my $dispose = $self->disposition($message, $current, $args);
283 0 0         $dispose eq 'preview' or return '';
284              
285 0           my %attach = $self->createAttachment($message, $current, $args);
286 0           my $previewer = $self->previewer($current->body->mimeType);
287 0           $previewer->($self, $message, $current, \%attach, $args);
288             }
289              
290              
291             sub htmlForeachPart($$)
292 0     0 1   { my ($self, $message, $args) = @_;
293 0   0       my $part = $self->lookup('part_object', $args) || $message;
294              
295 0 0         $part or error __x"foreachPart not used within part.";
296 0 0         $part->isMultipart or error __x"foreachPart outside multipart.";
297              
298 0   0       my $parentnr = $self->lookup('part_number',$args) || '';
299 0 0         $parentnr .= '.' if length $parentnr;
300              
301 0           my @parts = $part->parts;
302 0           my @part_data;
303              
304 0           for(my $partnr = 0; $partnr < @parts; $partnr++)
305 0           { push @part_data, +{
306             part_number => $parentnr . ($partnr+1),
307             part_object => $parts[$partnr],
308             };
309             }
310              
311 0           \@part_data;
312             }
313              
314              
315             sub htmlRawText($$)
316 0     0 1   { my ($self, $message, $args) = @_;
317 0   0       my $part = $self->lookup('part_object', $args) || $message;
318 0           $self->plain2html($part->decoded->string);
319             }
320              
321              
322             sub htmlPart($$)
323 0     0 1   { my ($self, $message, $args) = @_;
324 0           my $format = $args->{formatter};
325 0           my $msg = $format->lookup('message_text', $args);
326              
327 0 0         defined $msg or error __x"part outside a 'message' block.";
328 0           $format->processText($msg, $args);
329             }
330              
331             #--------------------
332              
333             1;