| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package Net::IMAP::SimpleX::NIL; |
|
2
|
|
|
|
|
|
|
|
|
3
|
1
|
|
|
1
|
|
4277
|
use strict; |
|
|
1
|
|
|
|
|
6
|
|
|
|
1
|
|
|
|
|
26
|
|
|
4
|
1
|
|
|
1
|
|
4
|
use warnings; |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
43
|
|
|
5
|
1
|
|
|
1
|
|
963
|
use overload fallback=>1, '""' => sub { "" }; |
|
|
1
|
|
|
0
|
|
835
|
|
|
|
1
|
|
|
|
|
7
|
|
|
|
0
|
|
|
|
|
0
|
|
|
6
|
0
|
|
|
0
|
|
|
sub new { return bless {}, "Net::IMAP::SimpleX::NIL" } |
|
7
|
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
package Net::IMAP::SimpleX::Body; |
|
9
|
|
|
|
|
|
|
|
|
10
|
1
|
|
|
1
|
|
85
|
use strict; |
|
|
1
|
|
|
|
|
8
|
|
|
|
1
|
|
|
|
|
22
|
|
|
11
|
1
|
|
|
1
|
|
4
|
use warnings; |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
25
|
|
|
12
|
1
|
|
|
1
|
|
4
|
no warnings 'once'; ## no critic |
|
|
1
|
|
|
|
|
1
|
|
|
|
1
|
|
|
|
|
72
|
|
|
13
|
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
our $uidm; |
|
15
|
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
BEGIN { |
|
17
|
1
|
|
|
1
|
|
4
|
our @fields = qw/content_description encoded_size charset content_type format part_number id name encoding/; |
|
18
|
1
|
|
|
|
|
3
|
for my $attr (@fields) { |
|
19
|
1
|
|
|
1
|
|
5
|
no strict; |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
54
|
|
|
20
|
9
|
|
|
0
|
|
26
|
*{"Net::IMAP::SimpleX::Body::$attr"} = sub { shift->{$attr}; }; |
|
|
9
|
|
|
|
|
122
|
|
|
|
0
|
|
|
|
|
0
|
|
|
21
|
|
|
|
|
|
|
} |
|
22
|
|
|
|
|
|
|
} |
|
23
|
|
|
|
|
|
|
|
|
24
|
0
|
|
|
0
|
|
|
sub hasparts { return 0; } *has_parts = \&hasparts; |
|
25
|
0
|
|
|
0
|
|
|
sub parts { return } |
|
26
|
0
|
|
|
0
|
|
|
sub type { return } |
|
27
|
0
|
|
|
0
|
|
|
sub body { return shift; } |
|
28
|
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
package Net::IMAP::SimpleX::BodySummary; |
|
30
|
|
|
|
|
|
|
|
|
31
|
1
|
|
|
1
|
|
6
|
use strict; |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
23
|
|
|
32
|
1
|
|
|
1
|
|
4
|
use warnings; |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
20
|
|
|
33
|
1
|
|
|
1
|
|
4
|
no warnings 'once'; ## no critic |
|
|
1
|
|
|
|
|
1
|
|
|
|
1
|
|
|
|
|
255
|
|
|
34
|
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
sub new { |
|
36
|
0
|
|
|
0
|
|
|
my ($class, $data) = @_; |
|
37
|
0
|
|
|
|
|
|
my $self; |
|
38
|
|
|
|
|
|
|
|
|
39
|
0
|
|
|
|
|
|
Net::IMAP::SimpleX::_id_parts($data); |
|
40
|
|
|
|
|
|
|
|
|
41
|
0
|
0
|
|
|
|
|
if ($data->{parts}) { |
|
42
|
0
|
|
|
|
|
|
$self = $data; |
|
43
|
|
|
|
|
|
|
} else { |
|
44
|
0
|
|
|
|
|
|
$self = { body => $data }; |
|
45
|
|
|
|
|
|
|
} |
|
46
|
|
|
|
|
|
|
|
|
47
|
0
|
|
|
|
|
|
return bless $self, $class; |
|
48
|
|
|
|
|
|
|
} |
|
49
|
|
|
|
|
|
|
|
|
50
|
0
|
0
|
|
0
|
|
|
sub hasparts { return shift->{parts} ? 1 : 0; } *has_parts = \&hasparts; |
|
51
|
0
|
0
|
|
0
|
|
|
sub parts { my $self = shift; return wantarray ? @{$self->{parts}} : $self->{parts}; } |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
52
|
0
|
|
0
|
0
|
|
|
sub type { return shift->{type} || undef; } |
|
53
|
0
|
|
|
0
|
|
|
sub body { return shift->{body}; } |
|
54
|
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
|
|
56
|
|
|
|
|
|
|
package Net::IMAP::SimpleX; |
|
57
|
|
|
|
|
|
|
|
|
58
|
1
|
|
|
1
|
|
6
|
use strict; |
|
|
1
|
|
|
|
|
1
|
|
|
|
1
|
|
|
|
|
17
|
|
|
59
|
1
|
|
|
1
|
|
4
|
use warnings; |
|
|
1
|
|
|
|
|
1
|
|
|
|
1
|
|
|
|
|
25
|
|
|
60
|
1
|
|
|
1
|
|
5
|
use Carp; |
|
|
1
|
|
|
|
|
1
|
|
|
|
1
|
|
|
|
|
55
|
|
|
61
|
1
|
|
|
1
|
|
993
|
use Parse::RecDescent; |
|
|
1
|
|
|
|
|
41069
|
|
|
|
1
|
|
|
|
|
10
|
|
|
62
|
1
|
|
|
1
|
|
72
|
use base 'Net::IMAP::Simple'; |
|
|
1
|
|
|
|
|
3
|
|
|
|
1
|
|
|
|
|
718
|
|
|
63
|
|
|
|
|
|
|
|
|
64
|
|
|
|
|
|
|
our $VERSION = "1.1000"; |
|
65
|
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
# directly from http://tools.ietf.org/html/rfc3501#section-9 |
|
67
|
|
|
|
|
|
|
# try and flatten, format as best we can |
|
68
|
|
|
|
|
|
|
our $body_grammar = q { |
|
69
|
|
|
|
|
|
|
body: body_type_mpart | body_type_1part |
|
70
|
|
|
|
|
|
|
{ $return = bless $item[1], 'Net::IMAP::SimpleX::Body'; } |
|
71
|
|
|
|
|
|
|
body_type_mpart: '('body(s) subtype')' |
|
72
|
|
|
|
|
|
|
{ $return = bless { |
|
73
|
|
|
|
|
|
|
parts => $item[2], |
|
74
|
|
|
|
|
|
|
type => $item{subtype} |
|
75
|
|
|
|
|
|
|
}, 'Net::IMAP::SimpleX::BodySummary'; |
|
76
|
|
|
|
|
|
|
} |
|
77
|
|
|
|
|
|
|
body_type_1part: body_type_basic | body_type_text |
|
78
|
|
|
|
|
|
|
{ $return = bless $item[1], 'Net::IMAP::SimpleX::BodySummary'; } |
|
79
|
|
|
|
|
|
|
body_type_basic: '('media_type body_fields')' |
|
80
|
|
|
|
|
|
|
{ $return = { |
|
81
|
|
|
|
|
|
|
content_type => $item{media_type}, |
|
82
|
|
|
|
|
|
|
%{$item{body_fields}} |
|
83
|
|
|
|
|
|
|
}; |
|
84
|
|
|
|
|
|
|
} |
|
85
|
|
|
|
|
|
|
body_type_text: '('media_type body_fields number')' |
|
86
|
|
|
|
|
|
|
{ $return = { |
|
87
|
|
|
|
|
|
|
content_type => $item{media_type}, |
|
88
|
|
|
|
|
|
|
%{$item{body_fields}}, |
|
89
|
|
|
|
|
|
|
}} |
|
90
|
|
|
|
|
|
|
body_fields: body_field_param body_field_id body_field_desc body_field_enc body_field_octets |
|
91
|
|
|
|
|
|
|
{ $return = { |
|
92
|
|
|
|
|
|
|
id => $item{body_field_id}, |
|
93
|
|
|
|
|
|
|
content_description => $item{body_field_desc}, |
|
94
|
|
|
|
|
|
|
encoding => $item{body_field_enc}, |
|
95
|
|
|
|
|
|
|
encoded_size => $item{body_field_octets}, |
|
96
|
|
|
|
|
|
|
$item{body_field_param} ? %{$item{body_field_param}} : () |
|
97
|
|
|
|
|
|
|
}; |
|
98
|
|
|
|
|
|
|
} |
|
99
|
|
|
|
|
|
|
body_field_id: nil | word |
|
100
|
|
|
|
|
|
|
body_field_desc: nil | word |
|
101
|
|
|
|
|
|
|
body_field_enc: word |
|
102
|
|
|
|
|
|
|
body_field_octets: number |
|
103
|
|
|
|
|
|
|
body_field_param: body_field_param_simple | body_field_param_ext | nil |
|
104
|
|
|
|
|
|
|
body_field_param_ext: '('word word word word')' |
|
105
|
|
|
|
|
|
|
{ $return = { $item[2] => $item[3], $item[4] => $item[5] }; } |
|
106
|
|
|
|
|
|
|
body_field_param_simple: '('word word')' |
|
107
|
|
|
|
|
|
|
{ $return = { $item[2] => $item[3] }; } |
|
108
|
|
|
|
|
|
|
body_field_param: nil |
|
109
|
|
|
|
|
|
|
media_type: type subtype |
|
110
|
|
|
|
|
|
|
{ $return = "$item{type}/$item{subtype}"; } |
|
111
|
|
|
|
|
|
|
type: word |
|
112
|
|
|
|
|
|
|
subtype: word |
|
113
|
|
|
|
|
|
|
nil: 'NIL' |
|
114
|
|
|
|
|
|
|
{$return = '';} |
|
115
|
|
|
|
|
|
|
number: /\d+/ |
|
116
|
|
|
|
|
|
|
key: word |
|
117
|
|
|
|
|
|
|
value: word |
|
118
|
|
|
|
|
|
|
word: /[^\s\)\(]+/ |
|
119
|
|
|
|
|
|
|
{ $item[1] =~ s/\"//g; $return = $item[1];} |
|
120
|
|
|
|
|
|
|
}; |
|
121
|
|
|
|
|
|
|
|
|
122
|
|
|
|
|
|
|
our $fetch_grammar = q& |
|
123
|
|
|
|
|
|
|
fetch: fetch_item(s) {$return={ map {(@$_)} reverse @{$item[1]} }} |
|
124
|
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
fetch_item: cmd_start 'FETCH' '(' value_pair(s?) ')' {$return=[$item[1], {map {(@$_)} @{$item[4]}}]} |
|
126
|
|
|
|
|
|
|
|
|
127
|
|
|
|
|
|
|
cmd_start: '*' /\d+/ {$return=$item[2]} |
|
128
|
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
value_pair: tag value {$return=[$item[1], $item[2]]} |
|
130
|
|
|
|
|
|
|
|
|
131
|
|
|
|
|
|
|
tag: /BODY\b(?:\.PEEK)?(?:\[[^\]]*\])?(?:<[\d\.]*>)?/i | atom |
|
132
|
|
|
|
|
|
|
|
|
133
|
|
|
|
|
|
|
value: atom | string | parenthized_list |
|
134
|
|
|
|
|
|
|
|
|
135
|
|
|
|
|
|
|
atom: /[^"()\s{}[\]]+/ { |
|
136
|
|
|
|
|
|
|
# strictly speaking, the NIL atom should be undef, but P::RD isn't going to allow that. |
|
137
|
|
|
|
|
|
|
# returning a null character instead |
|
138
|
|
|
|
|
|
|
$return=($item[1] eq "NIL" ? Net::IMAP::SimpleX::NIL->new : $item[1]) |
|
139
|
|
|
|
|
|
|
} |
|
140
|
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
string: '"' /[^\x0d\x0a"]*/ '"' {$return=$item[2]} | '{' /\d+/ "}\x0d\x0a" { |
|
142
|
|
|
|
|
|
|
$return = length($text) >= $item[2] |
|
143
|
|
|
|
|
|
|
? substr($text,0,$item[2],"") # if the production is accepted, we alter the input stream |
|
144
|
|
|
|
|
|
|
: undef; |
|
145
|
|
|
|
|
|
|
} |
|
146
|
|
|
|
|
|
|
|
|
147
|
|
|
|
|
|
|
parenthized_list: '(' value(s?) ')' {$return=$item[2]} |
|
148
|
|
|
|
|
|
|
&; |
|
149
|
|
|
|
|
|
|
|
|
150
|
|
|
|
|
|
|
sub new { |
|
151
|
0
|
|
|
0
|
1
|
|
my $class = shift; |
|
152
|
0
|
0
|
|
|
|
|
if (my $self = $class->SUPER::new(@_)) { |
|
153
|
|
|
|
|
|
|
|
|
154
|
0
|
|
|
|
|
|
$self->{parser}{body_summary} = Parse::RecDescent->new($body_grammar); |
|
155
|
0
|
|
|
|
|
|
$self->{parser}{fetch} = Parse::RecDescent->new($fetch_grammar); |
|
156
|
|
|
|
|
|
|
|
|
157
|
0
|
|
|
|
|
|
return $self; |
|
158
|
|
|
|
|
|
|
} |
|
159
|
|
|
|
|
|
|
} |
|
160
|
|
|
|
|
|
|
|
|
161
|
|
|
|
|
|
|
sub _id_parts { |
|
162
|
0
|
|
|
0
|
|
|
my $data = shift; |
|
163
|
0
|
|
|
|
|
|
my $pre = shift; |
|
164
|
0
|
0
|
|
|
|
|
$pre = $pre ? "$pre." : ''; |
|
165
|
|
|
|
|
|
|
|
|
166
|
0
|
|
|
|
|
|
my $id = 1; |
|
167
|
0
|
0
|
|
|
|
|
if (my $parts = $data->{parts}) { |
|
168
|
0
|
|
|
|
|
|
for my $sub (@$parts){ |
|
169
|
0
|
0
|
|
|
|
|
_id_parts($sub,"$pre$id") if $sub->{parts}; |
|
170
|
0
|
|
|
|
|
|
$sub->{part_number} = "$pre$id"; |
|
171
|
0
|
|
|
|
|
|
$id++; |
|
172
|
|
|
|
|
|
|
} |
|
173
|
|
|
|
|
|
|
|
|
174
|
|
|
|
|
|
|
} else { |
|
175
|
0
|
|
|
|
|
|
$data->{part_number} = $id; |
|
176
|
|
|
|
|
|
|
} |
|
177
|
|
|
|
|
|
|
|
|
178
|
0
|
|
|
|
|
|
return; |
|
179
|
|
|
|
|
|
|
} |
|
180
|
|
|
|
|
|
|
|
|
181
|
|
|
|
|
|
|
sub body_summary { |
|
182
|
0
|
|
|
0
|
1
|
|
my ($self, $number) = @_; |
|
183
|
|
|
|
|
|
|
|
|
184
|
0
|
|
|
|
|
|
my $bodysummary; |
|
185
|
|
|
|
|
|
|
|
|
186
|
|
|
|
|
|
|
return $self->_process_cmd( |
|
187
|
|
|
|
|
|
|
cmd => [ 'FETCH' => qq[$number BODY] ], |
|
188
|
|
|
|
|
|
|
|
|
189
|
0
|
|
|
0
|
|
|
final => sub { return $bodysummary; }, |
|
190
|
|
|
|
|
|
|
|
|
191
|
|
|
|
|
|
|
process => sub { |
|
192
|
0
|
0
|
|
0
|
|
|
if ($_[0] =~ m/\(BODY\s+(.*?)\)\s*$/i) { |
|
193
|
0
|
|
|
|
|
|
my $body_parts = $self->{parser}{body_summary}->body($1); |
|
194
|
0
|
|
|
|
|
|
$bodysummary = Net::IMAP::SimpleX::BodySummary->new($body_parts); |
|
195
|
|
|
|
|
|
|
} |
|
196
|
|
|
|
|
|
|
}, |
|
197
|
|
|
|
|
|
|
|
|
198
|
0
|
|
|
|
|
|
); |
|
199
|
|
|
|
|
|
|
} |
|
200
|
|
|
|
|
|
|
|
|
201
|
|
|
|
|
|
|
sub uidfetch { |
|
202
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
|
203
|
|
|
|
|
|
|
|
|
204
|
0
|
|
|
|
|
|
local $uidm = 1; # auto-pop this after the fetch |
|
205
|
|
|
|
|
|
|
|
|
206
|
0
|
|
|
|
|
|
return $self->fetch(@_); |
|
207
|
|
|
|
|
|
|
} |
|
208
|
|
|
|
|
|
|
|
|
209
|
|
|
|
|
|
|
sub fetch { |
|
210
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
|
211
|
0
|
0
|
|
|
|
|
my $msg = shift; $msg =~ s/[^\*\d:,-]//g; croak "which message?" unless $msg; |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
212
|
0
|
|
0
|
|
|
|
my $spec = "@_" || 'FULL'; |
|
213
|
|
|
|
|
|
|
|
|
214
|
0
|
0
|
|
|
|
|
$spec = "BODY[$spec]" if $spec =~ m/^[\d\.]+\z/; |
|
215
|
|
|
|
|
|
|
|
|
216
|
0
|
|
|
|
|
|
$self->_be_on_a_box; |
|
217
|
|
|
|
|
|
|
|
|
218
|
|
|
|
|
|
|
# cut and pasted from ::Server |
|
219
|
0
|
0
|
|
|
|
|
$spec = [qw/FLAGS INTERNALDATE RFC822.SIZE ENVELOPE/] if uc $spec eq "ALL"; |
|
220
|
0
|
0
|
|
|
|
|
$spec = [qw/FLAGS INTERNALDATE RFC822.SIZE/] if uc $spec eq "FAST"; |
|
221
|
0
|
0
|
|
|
|
|
$spec = [qw/FLAGS INTERNALDATE RFC822.SIZE ENVELOPE BODY/] if uc $spec eq "FULL"; |
|
222
|
0
|
0
|
|
|
|
|
$spec = [ $spec ] unless ref $spec; |
|
223
|
|
|
|
|
|
|
|
|
224
|
0
|
|
|
|
|
|
my $stxt = join(" ", map {s/[^()[\]\s<>\da-zA-Z.-]//g; uc($_)} @$spec); ## no critic: really? don't modify $_? pfft |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
225
|
|
|
|
|
|
|
|
|
226
|
0
|
0
|
|
|
|
|
$self->_debug( caller, __LINE__, parsed_fetch=> "$msg ($stxt)" ) if $self->{debug}; |
|
227
|
|
|
|
|
|
|
|
|
228
|
0
|
|
|
|
|
|
my $entire_response = ""; |
|
229
|
|
|
|
|
|
|
|
|
230
|
|
|
|
|
|
|
return $self->_process_cmd( |
|
231
|
|
|
|
|
|
|
cmd => [ ($uidm ? "UID FETCH" : "FETCH")=> qq[$msg ($stxt)] ], |
|
232
|
|
|
|
|
|
|
|
|
233
|
|
|
|
|
|
|
final => sub { |
|
234
|
|
|
|
|
|
|
#open my $fh, ">", "entire_response.dat"; |
|
235
|
|
|
|
|
|
|
#print $fh $entire_response; |
|
236
|
|
|
|
|
|
|
|
|
237
|
0
|
0
|
|
0
|
|
|
if( my $res = $self->{parser}{fetch}->fetch($entire_response) ) { |
|
238
|
0
|
0
|
|
|
|
|
$self->_debug( caller, __LINE__, parsed_fetch=> "PARSED") if $self->{debug}; |
|
239
|
0
|
0
|
|
|
|
|
return wantarray ? %$res : $res; |
|
240
|
|
|
|
|
|
|
} |
|
241
|
|
|
|
|
|
|
|
|
242
|
0
|
0
|
|
|
|
|
$self->_debug( caller, __LINE__, parsed_fetch=> "PARSE FAIL") if $self->{debug}; |
|
243
|
0
|
|
|
|
|
|
return; |
|
244
|
|
|
|
|
|
|
}, |
|
245
|
|
|
|
|
|
|
|
|
246
|
|
|
|
|
|
|
process => sub { |
|
247
|
0
|
|
|
0
|
|
|
$entire_response .= $_[0]; |
|
248
|
0
|
|
|
|
|
|
return 1; |
|
249
|
|
|
|
|
|
|
}, |
|
250
|
|
|
|
|
|
|
|
|
251
|
0
|
0
|
|
|
|
|
); |
|
252
|
|
|
|
|
|
|
} |
|
253
|
|
|
|
|
|
|
|
|
254
|
|
|
|
|
|
|
1; |