File Coverage

blib/lib/Email/Reply.pm
Criterion Covered Total %
statement 88 88 100.0
branch 30 38 78.9
condition 15 28 53.5
subroutine 16 16 100.0
pod 1 1 100.0
total 150 171 87.7


line stmt bran cond sub pod time code
1 1     1   26595 use strict;
  1         3  
  1         35  
2 1     1   6 use warnings;
  1         1  
  1         66  
3             package Email::Reply;
4             {
5             $Email::Reply::VERSION = '1.203';
6             }
7             # ABSTRACT: reply to an email message
8              
9 1     1   807 use Email::Abstract 2.01;
  1         36435  
  1         37  
10 1     1   823 use Email::Address 1.80;
  1         39441  
  1         79  
11 1     1   1106 use Email::MIME 1.82;
  1         32513  
  1         35  
12 1     1   13 use Exporter 5.57 'import';
  1         14  
  1         1294  
13              
14             my $CLASS = __PACKAGE__;
15             our @EXPORT = qw[reply];
16             my $CRLF = "\x0d\x0a";
17              
18             # Want to subclass and still use the functional interface?
19             # That's cool, just add these lines to your package:
20             # use base qw[Exporter];
21             # use vars qw[@EXPORT $CLASS];
22             # @EXPORT = qw[reply];
23             # $CLASS = __PACKAGE__;
24             # *reply = \&Email::Reply::reply;
25              
26             sub reply {
27 3     3 1 21646 my $reply = $CLASS->_new(@_);
28 3         16 $reply->_make_headers;
29 3 100       15 $reply->_encapsulate_message if $reply->{attach};
30 3 50 33     1046 $reply->_quote_body($reply->{original})
31             if $reply->{quote} || $reply->{top_post};
32 3 50       18 $reply->_post_reply if $reply->{body};
33 3 100       18 return $reply->{message} ? $reply->_mime : $reply->_simple;
34             }
35              
36             sub _new {
37 3     3   22 my ($class, %args) = @_;
38 3         8 my $self = {};
39 3         29 $self->{original} = Email::MIME->new(Email::Abstract->as_string($args{to}));
40              
41 3   66     6963 ($self->{from}) =
42             Email::Address->parse($args{from} || $self->{original}->header('To'));
43              
44 3   33     1845 ($self->{to}) = Email::Address->parse(
45             $self->{original}->header('Reply-To')
46             || $self->{original}->header('From')
47             || $self->{original}->header('Return-Path')
48             );
49              
50 3   66     387 $self->{attrib} = $args{attrib}
51             || (($self->{to}->name || join($self->{to}->address, '<', '>')) . ' wrote:');
52              
53 3   100     352 $self->{prefix} = $args{prefix} || '> ';
54 3         10 $self->{top_post} = $args{top_post};
55 3 50       16 $self->{quote} = exists $args{quote} ? $args{quote} : 1;
56 3         11 $self->{all} = $args{all};
57 3         10 $self->{quoted} = '';
58 3         8 $self->{body} = $args{body};
59 3         7 $self->{attach} = $args{attach};
60 3         10 $self->{keep_sig} = $args{keep_sig};
61              
62 3         17 return bless $self, $class;
63             }
64              
65             sub _make_headers {
66 3     3   6 my $self = shift;
67              
68 3         19 my @header = (From => $self->{from},);
69              
70 3 50       17 $self->{to}
71             ->name((Email::Address->parse($self->{original}->header('From')))[0]->name)
72             unless $self->{to}->name;
73 3         43 push @header, To => $self->{to};
74              
75 3   50     12 my $subject = $self->{original}->header('Subject') || '';
76 3 100       140 $subject = "Re: $subject" unless $subject =~ /\bRe:/i;
77 3         10 push @header, Subject => $subject;
78              
79 3         15 my ($msg_id) = Email::Address->parse($self->{original}->header('Message-ID'));
80 3         296 push @header, 'In-Reply-To' => $msg_id;
81              
82 3         13 my @refs = Email::Address->parse($self->{original}->header('References'));
83 3 100       132 @refs = Email::Address->parse($self->{original}->header('In-Reply-To'))
84             unless @refs;
85 3 100       72 push @refs, $msg_id if $msg_id;
86 3 100       89 push @header, References => join ' ', @refs if @refs;
87              
88 3 100       49 if ($self->{all}) {
89 1         6 my @addrs = (
90             Email::Address->parse($self->{original}->header('To')),
91             Email::Address->parse($self->{original}->header('Cc')),
92             );
93 1 50       247 unless ($self->{self}) {
94 1         3 @addrs = grep { $_->address ne $self->{from}->address } @addrs;
  3         26  
95             }
96 1 50       15 push @header, Cc => join ', ', @addrs if @addrs;
97             }
98              
99 3         41 $self->{header} = \@header;
100             }
101              
102             sub _encapsulate_message {
103 1     1   2 my $self = shift;
104 1         9 $self->{message} = Email::MIME->create(
105             attributes => { content_type => 'message/rfc822', },
106             body => $self->{original}->as_string,
107             );
108             }
109              
110             my $crlf = qr/\x0a\x0d|\x0d\x0a|\x0a|\x0d/;
111              
112             sub _quote_body {
113 5     5   35 my ($self, $part) = @_;
114 5 100       17 return if length $self->{quoted};
115 4 100       17 return map $self->_quote_body($_), $part->parts if $part->parts > 1;
116 3 50 33     43 return if $part->content_type && $part->content_type !~ m[\btext/plain\b];
117              
118 3         141 my $body = $part->body;
119              
120 3 100 33     212 $body = ($self->_strip_sig($body) || $body)
      100        
121             if !$self->{keep_sig} && $body =~ /$crlf--\s*$crlf/o;
122              
123 3         53 my ($end) = $body =~ /($crlf)/;
124 3   33     12 $end ||= $CRLF;
125 3         29 $body =~ s/[\r\n\s]+$//;
126 3         13 $body = $self->_quote_orig_body($body);
127 3         11 $body = "$self->{attrib}$end$body$end";
128              
129 3         9 $self->{crlf} = $end;
130 3         12 $self->{quoted} = $body;
131             }
132              
133             # Yes, you are witnessing elitism.
134 1     1   40 sub _strip_sig { reverse +(split /$crlf\s*--$crlf/o, reverse(pop), 2)[1] }
135              
136             sub _quote_orig_body {
137 3     3   8 my ($self, $body) = @_;
138 3         84 $body =~ s/($crlf)/$1$self->{prefix}/g;
139 3         16 "$self->{prefix}$body";
140             }
141              
142             sub _post_reply {
143 3     3   6 my $self = shift;
144 3 50       13 return $self->{reply_body} = $self->{body}
145             unless length $self->{quoted};
146 3         13 my @parts = (@{$self}{qw[quoted body]});
  3         12  
147 3 100       11 @parts = reverse @parts if $self->{top_post};
148 3         16 $self->{reply_body} = join $self->{crlf}, @parts;
149             }
150              
151             sub _mime {
152 1     1   3 my $self = shift;
153 1         7 Email::MIME->create(
154             header => $self->{header},
155             parts =>
156             [ Email::MIME->create(body => $self->{reply_body}), $self->{message}, ],
157             );
158             }
159              
160             sub _simple {
161 2     2   4 my $self = shift;
162 2         13 Email::Simple->create(
163             header => $self->{header},
164             body => $self->{reply_body},
165             );
166             }
167              
168             1;
169              
170             __END__