File Coverage

blib/lib/Mail/Message/Construct/Reply.pm
Criterion Covered Total %
statement 91 99 91.9
branch 52 84 61.9
condition 27 58 46.5
subroutine 10 10 100.0
pod 3 3 100.0
total 183 254 72.0


line stmt bran cond sub pod time code
1             # This code is part of Perl distribution Mail-Message version 4.04.
2             # The POD got stripped from this file by OODoc version 3.06.
3             # For contributors see file ChangeLog.
4              
5             # This software is copyright (c) 2001-2026 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 Mail::Message;{
13             our $VERSION = '4.04';
14             }
15              
16              
17 3     3   3140 use strict;
  3         8  
  3         124  
18 3     3   15 use warnings;
  3         6  
  3         288  
19              
20 3     3   22 use Log::Report 'mail-message', import => [ qw/__x error info trace/ ];
  3         9  
  3         35  
21              
22 3     3   1183 use Mail::Message::Body::Multipart ();
  3         10  
  3         96  
23 3     3   18 use Mail::Address ();
  3         8  
  3         67  
24              
25 3     3   13 use Scalar::Util qw/blessed/;
  3         8  
  3         5552  
26              
27             #--------------------
28              
29             # tests in t/55reply1r.t, demo in the examples/ directory
30              
31             sub reply(@)
32 3     3 1 617 { my ($self, %args) = @_;
33              
34 3         8 my $body = $args{body};
35 3   66     17 my $strip = !exists $args{strip_signature} || $args{strip_signature};
36 3   50     13 my $include = $args{include} || 'INLINE';
37              
38 3 50 33     17 if($include eq 'NO')
    50          
39             { # Throw away real body.
40 0   0     0 $body //= Mail::Message::Body->new(data => ["\n[The original message is not included]\n\n"]);
41             }
42             elsif($include eq 'INLINE' || $include eq 'ATTACH')
43             {
44 3 50       10 unless(defined $body)
45             { # text attachment
46 3         13 $body = $self->body;
47 3 50 33     23 $body = $body->part(0)->body if $body->isMultipart && $body->parts==1;
48 3 50       13 $body = $body->nested->body if $body->isNested;
49              
50             $body
51             = $strip && ! $body->isMultipart && !$body->isBinary
52             ? $body->decoded->stripSignature(pattern => $args{strip_signature}, max_lines => $args{max_signature})
53 3 100 66     35 : $body->decoded;
54             }
55              
56 3 50 33     25 if($include eq 'INLINE' && $body->isMultipart) { $include = 'ATTACH' }
  0 50 33     0  
57             elsif($include eq 'INLINE' && $body->isBinary)
58 0         0 { $include = 'ATTACH';
59 0         0 $body = Mail::Message::Body::Multipart->new(parts => [$body]);
60             }
61              
62 3 50       34 if($include eq 'INLINE')
63 3 50       16 { my $quote = defined $args{quote} ? $args{quote} : exists $args{quote} ? undef : '> ';
    100          
64 3 100       8 if(defined $quote)
65 2 100   2   12 { my $quoting = ref $quote ? $quote : sub { $quote . $_ };
  2         7  
66 2         13 $body = $body->foreachLine($quoting);
67             }
68             }
69             }
70             else
71 0         0 { error __x"cannot include reply source as {kind}.", kind => $include;
72             }
73              
74             #
75             # Collect header info
76             #
77              
78 3         15 my $mainhead = $self->toplevel->head;
79              
80             # Where it comes from
81 3         10 my $from = delete $args{From};
82 3 50       10 unless(defined $from)
83 3         11 { my @from = $self->to;
84 3 50       888 $from = \@from if @from;
85             }
86              
87             # To whom to send
88 3   33     20 my $to = delete $args{To} || $mainhead->get('reply-to') || $mainhead->get('from');
89 3 50       11 defined $to or return;
90              
91             # Add Cc
92 3         7 my $cc = delete $args{Cc};
93 3 100 66     16 if(!defined $cc && $args{group_reply})
94 1         7 { my @cc = $self->cc;
95 1 50       244 $cc = [ $self->cc ] if @cc;
96             }
97              
98             # Create a subject
99 3         275 my $srcsub = delete $args{Subject};
100 3 0       17 my $subject
    50          
101             = ! defined $srcsub ? $self->replySubject($self->subject)
102             : ref $srcsub ? $srcsub->($self->subject)
103             : $srcsub;
104              
105             # Create a nice message-id
106 3         9 my $msgid = delete $args{'Message-ID'};
107 3 50 33     10 $msgid = "<$msgid>" if $msgid && $msgid !~ /^\s*\<.*\>\s*$/;
108              
109             # Thread information
110 3         13 my $origid = '<'.$self->messageId.'>';
111 3         9 my $refs = $mainhead->get('references');
112              
113             # Prelude
114             my $prelude
115             = defined $args{prelude} ? $args{prelude}
116             : exists $args{prelude} ? undef
117 3 100       23 : [ $self->replyPrelude($to) ];
    50          
118              
119 3 100 66     22 $prelude = Mail::Message::Body->new(data => $prelude)
120             if defined $prelude && ! blessed $prelude;
121              
122 3         8 my $postlude = $args{postlude};
123 3 50 66     15 $postlude = Mail::Message::Body->new(data => $postlude)
124             if defined $postlude && ! blessed $postlude;
125              
126             #
127             # Create the message.
128             #
129              
130 3         5 my $total;
131 3 50       14 if($include eq 'NO') {$total = $body}
  0 50       0  
    0          
132             elsif($include eq 'INLINE')
133 3         6 { my $signature = $args{signature};
134 3 50 33     11 $signature = $signature->body
135             if defined $signature && $signature->isa('Mail::Message');
136              
137 3 50       25 $total = $body->concatenate($prelude, $body, $postlude, (defined $signature ? "-- \n$signature" : undef));
138             }
139             elsif($include eq 'ATTACH')
140 0         0 { my $intro = $prelude->concatenate($prelude, ["\n", "[Your message is attached]\n"], $postlude);
141 0         0 $total = Mail::Message::Body::Multipart->new(parts => [ $intro, $body, $args{signature} ]);
142             }
143              
144 3   50     21 my $msgtype = $args{message_type} || 'Mail::Message';
145 3 50 50     32 my $reply = $msgtype->buildFromBody(
146             $total,
147             From => $from || 'Undisclosed senders:;',
148             To => $to,
149             Subject => $subject,
150             'In-Reply-To' => $origid,
151             References => ($refs ? "$refs $origid" : $origid),
152             );
153              
154 3         12 my $newhead = $reply->head;
155 3 100       13 $newhead->set(Cc => $cc) if $cc;
156 3 100       13 $newhead->set(Bcc => delete $args{Bcc}) if $args{Bcc};
157             $newhead->add($_ => $args{$_})
158 3         31 for sort grep /^[A-Z]/, keys %args;
159              
160             # Ready
161 3         21 trace "Reply created from $origid";
162 3         134 $self->label(replied => 1);
163 3         16 $reply;
164             }
165              
166              
167             # tests in t/35reply1rs.t
168              
169             sub replySubject($)
170 24     24 1 191498 { my ($thing, $subject) = @_;
171 24 100 100     128 $subject = 'your mail' unless defined $subject && length $subject;
172 24         72 my @subject = split /\:/, $subject;
173 24         39 my $re_count = 1;
174              
175             # Strip multiple Re's from the start.
176              
177 24         43 while(@subject)
178 38 100       133 { last if $subject[0] =~ /[A-QS-Za-qs-z][A-DF-Za-df-z]/;
179              
180 14         30 for(shift @subject)
181 14         97 { while( /\bRe(?:\[\s*(\d+)\s*\]|\b)/g )
182 17   50     105 { $re_count += ($1 // 1);
183             }
184             }
185             }
186              
187             # Strip multiple Re's from the end.
188              
189 24 50       42 if(@subject)
190 24         44 { for($subject[-1])
191 24         119 { $re_count++ while s/\s*\(\s*(re|forw)\W*\)\s*$//i;
192             }
193             }
194              
195             # Create the new subject string.
196              
197 24   50     72 my $text = (join ':', @subject) || 'your mail';
198 24         108 s/^\s+//, s/\s+$// for $text;
199              
200 24 100       204 $re_count==1 ? "Re: $text" : "Re[$re_count]: $text";
201             }
202              
203              
204              
205             sub replyPrelude($)
206 2     2 1 7 { my ($self, $who) = @_;
207 2 50       9 $who = $who->[0] if ref $who eq 'ARRAY';
208              
209 2 50       21 my $user
    50          
    50          
210             = ! defined $who ? undef
211             : ! blessed $who ? (Mail::Address->parse($who))[0]
212             : $who->isa('Mail::Message::Field') ? ($who->addresses)[0]
213             : $who;
214              
215 2 50 33     596 my $from = blessed $user && $user->isa('Mail::Address') ? ($user->name || $user->address || $user->format) : 'someone';
      33        
216 2         201 my $time = gmtime $self->timestamp;
217 2         14 "On $time, $from wrote:\n";
218             }
219              
220             1;