line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Net::IMAP::Client::MsgSummary; |
2
|
|
|
|
|
|
|
|
3
|
1
|
|
|
1
|
|
558
|
use Encode (); |
|
1
|
|
|
|
|
9673
|
|
|
1
|
|
|
|
|
27
|
|
4
|
1
|
|
|
1
|
|
408
|
use Net::IMAP::Client::MsgAddress (); |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
2132
|
|
5
|
|
|
|
|
|
|
|
6
|
|
|
|
|
|
|
sub new { |
7
|
0
|
|
|
0
|
1
|
|
my ($class, $data, $part_id, $has_headers) = @_; |
8
|
|
|
|
|
|
|
|
9
|
0
|
|
|
|
|
|
bless my $self = {}, $class; |
10
|
|
|
|
|
|
|
|
11
|
0
|
0
|
|
|
|
|
if ($part_id) { |
12
|
0
|
|
|
|
|
|
$self->{part_id} = $part_id; |
13
|
|
|
|
|
|
|
} |
14
|
|
|
|
|
|
|
|
15
|
0
|
|
|
|
|
|
my $tmp = $data->{BODY}; |
16
|
0
|
0
|
|
|
|
|
if ($tmp) { |
17
|
0
|
|
|
|
|
|
$self->_parse_body($tmp); |
18
|
|
|
|
|
|
|
} |
19
|
|
|
|
|
|
|
|
20
|
0
|
|
|
|
|
|
$tmp = $data->{BODYSTRUCTURE}; |
21
|
0
|
0
|
|
|
|
|
if ($tmp) { |
22
|
0
|
|
|
|
|
|
$self->_parse_bodystructure($tmp); |
23
|
|
|
|
|
|
|
} |
24
|
|
|
|
|
|
|
|
25
|
0
|
|
|
|
|
|
$tmp = $data->{ENVELOPE}; |
26
|
0
|
0
|
|
|
|
|
if ($tmp) { |
27
|
0
|
|
|
|
|
|
$self->_parse_envelope($tmp); |
28
|
|
|
|
|
|
|
} |
29
|
|
|
|
|
|
|
|
30
|
0
|
|
|
|
|
|
$self->{flags} = $data->{FLAGS}; |
31
|
0
|
|
|
|
|
|
$self->{internaldate} = $data->{INTERNALDATE}; |
32
|
0
|
|
|
|
|
|
$self->{rfc822_size} = $data->{'RFC822.SIZE'}; |
33
|
0
|
|
|
|
|
|
$self->{uid} = $data->{UID}; |
34
|
|
|
|
|
|
|
|
35
|
0
|
0
|
|
|
|
|
if ($has_headers) { |
36
|
0
|
|
|
|
|
|
while (my ($key, $val) = each %$data) { |
37
|
0
|
0
|
|
|
|
|
if ($key =~ /^body(?:\.peek)?\s*\[\s*header\.fields/i) { |
38
|
0
|
|
|
|
|
|
$self->{headers} = $val; |
39
|
0
|
|
|
|
|
|
last; |
40
|
|
|
|
|
|
|
} |
41
|
|
|
|
|
|
|
} |
42
|
|
|
|
|
|
|
} |
43
|
|
|
|
|
|
|
|
44
|
0
|
|
|
|
|
|
return $self; |
45
|
|
|
|
|
|
|
} |
46
|
|
|
|
|
|
|
|
47
|
|
|
|
|
|
|
sub _decode { |
48
|
0
|
|
|
0
|
|
|
my ($str) = @_; |
49
|
0
|
0
|
|
|
|
|
if (defined($str)) { |
50
|
0
|
|
|
|
|
|
eval { $str = Encode::decode('MIME-Header', $str); }; |
|
0
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
} |
52
|
0
|
|
|
|
|
|
return $str; |
53
|
|
|
|
|
|
|
} |
54
|
|
|
|
|
|
|
|
55
|
0
|
|
|
0
|
1
|
|
sub type { $_[0]->{type} } |
56
|
|
|
|
|
|
|
|
57
|
0
|
|
|
0
|
1
|
|
sub subtype { $_[0]->{subtype} } |
58
|
|
|
|
|
|
|
|
59
|
0
|
|
|
0
|
1
|
|
sub parameters { $_[0]->{parameters} } |
60
|
|
|
|
|
|
|
|
61
|
0
|
|
|
0
|
1
|
|
sub cid { $_[0]->{cid} } |
62
|
|
|
|
|
|
|
|
63
|
0
|
|
|
0
|
1
|
|
sub description { _decode($_[0]->{description}) } |
64
|
|
|
|
|
|
|
|
65
|
0
|
|
|
0
|
1
|
|
sub transfer_encoding { $_[0]->{transfer_encoding} } |
66
|
|
|
|
|
|
|
|
67
|
0
|
|
|
0
|
1
|
|
sub encoded_size { $_[0]->{encoded_size} } |
68
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
sub content_type { |
70
|
0
|
|
|
0
|
1
|
|
my ($self) = @_; |
71
|
0
|
0
|
|
|
|
|
if ($self->type) { |
72
|
0
|
|
|
|
|
|
return $self->type . '/' . $self->subtype; |
73
|
|
|
|
|
|
|
} |
74
|
0
|
0
|
|
|
|
|
if ($self->multipart) { |
75
|
0
|
|
|
|
|
|
return 'multipart/' . $self->multipart; |
76
|
|
|
|
|
|
|
} |
77
|
0
|
|
|
|
|
|
return undef; |
78
|
|
|
|
|
|
|
} |
79
|
|
|
|
|
|
|
|
80
|
0
|
|
|
0
|
1
|
|
sub charset { $_[0]->{parameters}->{charset} } |
81
|
|
|
|
|
|
|
|
82
|
|
|
|
|
|
|
sub filename { |
83
|
0
|
|
|
0
|
1
|
|
my ($self) = @_; |
84
|
0
|
|
|
|
|
|
my $disp = $self->{disposition}; |
85
|
0
|
|
|
|
|
|
my $filename; |
86
|
0
|
0
|
|
|
|
|
if ($disp) { |
87
|
0
|
|
|
|
|
|
while (my ($key, $val) = each %$disp) { |
88
|
0
|
0
|
|
|
|
|
if (ref($val) eq 'HASH') { |
89
|
0
|
|
|
|
|
|
$filename = $val->{filename}; |
90
|
0
|
0
|
|
|
|
|
last if $filename; |
91
|
|
|
|
|
|
|
} |
92
|
|
|
|
|
|
|
} |
93
|
|
|
|
|
|
|
} |
94
|
0
|
0
|
|
|
|
|
unless ($filename) { |
95
|
0
|
|
|
|
|
|
$filename = $_[0]->{parameters}->{name}; |
96
|
|
|
|
|
|
|
} |
97
|
0
|
|
|
|
|
|
return _decode($filename); |
98
|
|
|
|
|
|
|
} |
99
|
|
|
|
|
|
|
|
100
|
0
|
|
|
0
|
1
|
|
sub name { _decode($_[0]->{parameters}->{name}) } |
101
|
|
|
|
|
|
|
|
102
|
0
|
|
|
0
|
1
|
|
sub multipart { $_[0]->{multipart_type} } |
103
|
|
|
|
|
|
|
|
104
|
0
|
|
|
0
|
1
|
|
sub parts { $_[0]->{parts} } |
105
|
|
|
|
|
|
|
|
106
|
0
|
|
|
0
|
1
|
|
sub rfc822_size { $_[0]->{rfc822_size} } |
107
|
|
|
|
|
|
|
|
108
|
0
|
|
|
0
|
1
|
|
sub internaldate { $_[0]->{internaldate} } |
109
|
|
|
|
|
|
|
|
110
|
0
|
|
|
0
|
1
|
|
sub flags { $_[0]->{flags} } |
111
|
|
|
|
|
|
|
|
112
|
0
|
|
|
0
|
1
|
|
sub uid { $_[0]->{uid} } |
113
|
|
|
|
|
|
|
|
114
|
0
|
|
|
0
|
1
|
|
sub part_id { $_[0]->{part_id } } |
115
|
|
|
|
|
|
|
|
116
|
0
|
|
|
0
|
1
|
|
sub md5 { $_[0]->{md5} } |
117
|
|
|
|
|
|
|
|
118
|
0
|
|
|
0
|
1
|
|
sub disposition { $_[0]->{disposition} } |
119
|
|
|
|
|
|
|
|
120
|
0
|
|
|
0
|
1
|
|
sub language { $_[0]->{language} } |
121
|
|
|
|
|
|
|
|
122
|
|
|
|
|
|
|
# envelope |
123
|
|
|
|
|
|
|
|
124
|
0
|
|
|
0
|
1
|
|
sub date { $_[0]->{date} } |
125
|
|
|
|
|
|
|
|
126
|
0
|
|
|
0
|
1
|
|
sub subject { _decode($_[0]->{subject}) } |
127
|
|
|
|
|
|
|
|
128
|
0
|
|
|
0
|
1
|
|
sub from { $_[0]->{from} } |
129
|
|
|
|
|
|
|
|
130
|
0
|
|
|
0
|
1
|
|
sub sender { $_[0]->{sender} } |
131
|
|
|
|
|
|
|
|
132
|
0
|
|
|
0
|
1
|
|
sub reply_to { $_[0]->{reply_to} } |
133
|
|
|
|
|
|
|
|
134
|
0
|
|
|
0
|
1
|
|
sub to { $_[0]->{to} } |
135
|
|
|
|
|
|
|
|
136
|
0
|
|
|
0
|
1
|
|
sub cc { $_[0]->{cc} } |
137
|
|
|
|
|
|
|
|
138
|
0
|
|
|
0
|
1
|
|
sub bcc { $_[0]->{bcc} } |
139
|
|
|
|
|
|
|
|
140
|
0
|
|
|
0
|
1
|
|
sub in_reply_to { $_[0]->{in_reply_to} } |
141
|
|
|
|
|
|
|
|
142
|
0
|
|
|
0
|
1
|
|
sub message_id { $_[0]->{message_id} } |
143
|
|
|
|
|
|
|
|
144
|
0
|
|
|
0
|
1
|
|
sub seq_id { $_[0]->{seq_id} } |
145
|
|
|
|
|
|
|
|
146
|
0
|
|
|
0
|
1
|
|
sub headers { $_[0]->{headers} } |
147
|
|
|
|
|
|
|
|
148
|
|
|
|
|
|
|
# utils |
149
|
|
|
|
|
|
|
|
150
|
|
|
|
|
|
|
sub get_subpart { |
151
|
0
|
|
|
0
|
1
|
|
my ($self, $part) = @_; |
152
|
0
|
|
|
|
|
|
foreach my $index (split(/\./, $part)) { |
153
|
0
|
|
|
|
|
|
$self = $self->parts->[$index - 1]; |
154
|
|
|
|
|
|
|
} |
155
|
0
|
|
|
|
|
|
return $self; |
156
|
|
|
|
|
|
|
} |
157
|
|
|
|
|
|
|
|
158
|
|
|
|
|
|
|
my %MT_HAS_ATTACHMENT = ( mixed => 1 ); |
159
|
|
|
|
|
|
|
|
160
|
|
|
|
|
|
|
sub has_attachments { |
161
|
0
|
|
|
0
|
1
|
|
my ($self) = @_; |
162
|
0
|
|
|
|
|
|
my $mt = $self->multipart; |
163
|
0
|
0
|
0
|
|
|
|
return $mt && $MT_HAS_ATTACHMENT{$mt} ? 1 : 0; |
164
|
|
|
|
|
|
|
} |
165
|
|
|
|
|
|
|
|
166
|
0
|
|
|
0
|
1
|
|
sub is_message { $_[0]->content_type eq 'message/rfc822' } |
167
|
|
|
|
|
|
|
|
168
|
0
|
|
|
0
|
1
|
|
sub message { $_[0]->{message} } |
169
|
|
|
|
|
|
|
|
170
|
|
|
|
|
|
|
sub _parse_body { |
171
|
0
|
|
|
0
|
|
|
my ($self, $struct) = @_; |
172
|
|
|
|
|
|
|
|
173
|
0
|
0
|
|
|
|
|
if (ref($struct->[0]) eq 'ARRAY') { |
174
|
0
|
|
|
|
|
|
my @tmp = @$struct; |
175
|
0
|
|
|
|
|
|
my $multipart = pop @tmp; |
176
|
0
|
|
0
|
|
|
|
my $part_id = $self->{part_id} || ''; |
177
|
0
|
0
|
|
|
|
|
$part_id .= '.' |
178
|
|
|
|
|
|
|
if $part_id; |
179
|
0
|
|
|
|
|
|
my $i = 0; |
180
|
0
|
|
|
|
|
|
@tmp = map { __PACKAGE__->new({ BODY => $_}, $part_id . ++$i) } @tmp; |
|
0
|
|
|
|
|
|
|
181
|
0
|
|
|
|
|
|
$self->{multipart_type} = lc $multipart; |
182
|
0
|
|
|
|
|
|
$self->{parts} = \@tmp; |
183
|
|
|
|
|
|
|
} else { |
184
|
0
|
|
|
|
|
|
$self->{type} = lc $struct->[0]; |
185
|
0
|
|
|
|
|
|
$self->{subtype} = lc $struct->[1]; |
186
|
0
|
0
|
|
|
|
|
if ($struct->[2]) { |
187
|
0
|
|
|
|
|
|
my %tmp = @{$struct->[2]}; |
|
0
|
|
|
|
|
|
|
188
|
0
|
|
|
|
|
|
$self->{parameters} = \%tmp; |
189
|
|
|
|
|
|
|
} |
190
|
0
|
|
|
|
|
|
$self->{cid} = $struct->[3]; |
191
|
0
|
|
|
|
|
|
$self->{description} = $struct->[4]; |
192
|
0
|
|
|
|
|
|
$self->{transfer_encoding} = $struct->[5]; |
193
|
0
|
|
|
|
|
|
$self->{encoded_size} = $struct->[6]; |
194
|
|
|
|
|
|
|
|
195
|
0
|
0
|
0
|
|
|
|
if ($self->is_message && $struct->[7] && $struct->[8]) { |
|
|
|
0
|
|
|
|
|
196
|
|
|
|
|
|
|
# continue parsing attached message |
197
|
0
|
|
|
|
|
|
$self->{message} = __PACKAGE__->new({ |
198
|
|
|
|
|
|
|
ENVELOPE => $struct->[7], |
199
|
|
|
|
|
|
|
BODY => $struct->[8], |
200
|
|
|
|
|
|
|
}); |
201
|
|
|
|
|
|
|
} |
202
|
|
|
|
|
|
|
} |
203
|
|
|
|
|
|
|
} |
204
|
|
|
|
|
|
|
|
205
|
|
|
|
|
|
|
sub _parse_bodystructure { |
206
|
0
|
|
|
0
|
|
|
my ($self, $struct) = @_; |
207
|
|
|
|
|
|
|
|
208
|
0
|
0
|
|
|
|
|
if (ref($struct->[0]) eq 'ARRAY') { |
209
|
0
|
|
|
|
|
|
my $multipart; |
210
|
|
|
|
|
|
|
my @tmp; |
211
|
0
|
|
|
|
|
|
foreach (@$struct) { |
212
|
0
|
0
|
|
|
|
|
if (ref($_) eq 'ARRAY') { |
213
|
0
|
|
|
|
|
|
push @tmp, $_; |
214
|
|
|
|
|
|
|
} else { |
215
|
0
|
|
|
|
|
|
$multipart = $_; |
216
|
0
|
|
|
|
|
|
last; # XXX: ignoring the rest (extension data) for now. |
217
|
|
|
|
|
|
|
} |
218
|
|
|
|
|
|
|
} |
219
|
0
|
|
0
|
|
|
|
my $part_id = $self->{part_id} || ''; |
220
|
0
|
0
|
|
|
|
|
$part_id .= '.' |
221
|
|
|
|
|
|
|
if $part_id; |
222
|
0
|
|
|
|
|
|
my $i = 0; |
223
|
0
|
|
|
|
|
|
@tmp = map { __PACKAGE__->new({ BODYSTRUCTURE => $_}, $part_id . ++$i) } @tmp; |
|
0
|
|
|
|
|
|
|
224
|
0
|
|
|
|
|
|
$self->{multipart_type} = lc $multipart; |
225
|
0
|
|
|
|
|
|
$self->{parts} = \@tmp; |
226
|
|
|
|
|
|
|
} else { |
227
|
0
|
|
|
|
|
|
$self->{type} = lc $struct->[0]; |
228
|
0
|
|
|
|
|
|
$self->{subtype} = lc $struct->[1]; |
229
|
0
|
|
|
|
|
|
my $a = $struct->[2]; |
230
|
0
|
0
|
|
|
|
|
if ($a) { |
231
|
0
|
|
|
|
|
|
__lc_key_in_array($a); |
232
|
0
|
|
|
|
|
|
my %tmp = @$a; |
233
|
0
|
|
|
|
|
|
$self->{parameters} = \%tmp; |
234
|
|
|
|
|
|
|
} |
235
|
0
|
|
|
|
|
|
$self->{cid} = $struct->[3]; |
236
|
0
|
|
|
|
|
|
$self->{description} = $struct->[4]; |
237
|
0
|
|
|
|
|
|
$self->{transfer_encoding} = $struct->[5]; |
238
|
0
|
|
|
|
|
|
$self->{encoded_size} = $struct->[6]; |
239
|
|
|
|
|
|
|
|
240
|
0
|
0
|
0
|
|
|
|
if ($self->is_message && $struct->[7] && $struct->[8]) { |
|
|
0
|
0
|
|
|
|
|
241
|
|
|
|
|
|
|
# continue parsing attached message |
242
|
0
|
|
|
|
|
|
$self->{message} = __PACKAGE__->new({ |
243
|
|
|
|
|
|
|
ENVELOPE => $struct->[7], |
244
|
|
|
|
|
|
|
BODYSTRUCTURE => $struct->[8], |
245
|
|
|
|
|
|
|
}); |
246
|
|
|
|
|
|
|
} elsif ($self->type ne 'text') { |
247
|
0
|
|
|
|
|
|
$self->{md5} = $struct->[7]; |
248
|
0
|
|
|
|
|
|
my $a = $struct->[8]; |
249
|
0
|
0
|
|
|
|
|
if ($a) { |
250
|
0
|
|
|
|
|
|
for (my $i = 0; $i < @$a; ++$i) { |
251
|
0
|
|
|
|
|
|
$a->[$i] = lc $a->[$i]; |
252
|
0
|
|
|
|
|
|
++$i; |
253
|
0
|
0
|
|
|
|
|
if (ref($a->[$i]) eq 'ARRAY') { |
254
|
0
|
|
|
|
|
|
__lc_key_in_array($a->[$i]); |
255
|
0
|
|
|
|
|
|
my %foo = @{ $a->[$i] }; |
|
0
|
|
|
|
|
|
|
256
|
0
|
|
|
|
|
|
$a->[$i] = \%foo; |
257
|
|
|
|
|
|
|
} |
258
|
|
|
|
|
|
|
} |
259
|
0
|
|
|
|
|
|
my %tmp = @$a; |
260
|
0
|
|
|
|
|
|
$self->{disposition} = \%tmp; |
261
|
|
|
|
|
|
|
} |
262
|
0
|
|
|
|
|
|
$self->{language} = $struct->[9]; |
263
|
|
|
|
|
|
|
} |
264
|
|
|
|
|
|
|
} |
265
|
|
|
|
|
|
|
} |
266
|
|
|
|
|
|
|
|
267
|
|
|
|
|
|
|
sub __lc_key_in_array { |
268
|
0
|
|
|
0
|
|
|
my ($a) = @_; |
269
|
0
|
|
|
|
|
|
for (my $i = 0; $i < @$a; $i += 2) { |
270
|
0
|
|
|
|
|
|
$a->[$i] = lc $a->[$i]; |
271
|
|
|
|
|
|
|
} |
272
|
|
|
|
|
|
|
} |
273
|
|
|
|
|
|
|
|
274
|
|
|
|
|
|
|
sub _parse_envelope { |
275
|
0
|
|
|
0
|
|
|
my ($self, $struct) = @_; |
276
|
0
|
|
|
|
|
|
$self->{date} = $struct->[0]; |
277
|
0
|
|
|
|
|
|
$self->{subject} = $struct->[1]; |
278
|
0
|
|
|
|
|
|
$self->{from} = _parse_address($struct->[2]); |
279
|
0
|
|
|
|
|
|
$self->{sender} = _parse_address($struct->[3]); |
280
|
0
|
|
|
|
|
|
$self->{reply_to} = _parse_address($struct->[4]); |
281
|
0
|
|
|
|
|
|
$self->{to} = _parse_address($struct->[5]); |
282
|
0
|
|
|
|
|
|
$self->{cc} = _parse_address($struct->[6]); |
283
|
0
|
|
|
|
|
|
$self->{bcc} = _parse_address($struct->[7]); |
284
|
0
|
|
|
|
|
|
$self->{in_reply_to} = $struct->[8]; |
285
|
0
|
|
|
|
|
|
$self->{message_id} = $struct->[9]; |
286
|
|
|
|
|
|
|
} |
287
|
|
|
|
|
|
|
|
288
|
|
|
|
|
|
|
sub _parse_address { |
289
|
0
|
|
|
0
|
|
|
my ($adr) = @_; |
290
|
0
|
0
|
|
|
|
|
if ($adr) { |
291
|
0
|
|
|
|
|
|
$adr = [ map { Net::IMAP::Client::MsgAddress->new($_) } @$adr ]; |
|
0
|
|
|
|
|
|
|
292
|
|
|
|
|
|
|
} |
293
|
0
|
|
|
|
|
|
return $adr; |
294
|
|
|
|
|
|
|
} |
295
|
|
|
|
|
|
|
|
296
|
|
|
|
|
|
|
1; |
297
|
|
|
|
|
|
|
|
298
|
|
|
|
|
|
|
__END__ |