line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
2
|
|
|
2
|
|
148226
|
use strict; |
|
2
|
|
|
|
|
5
|
|
|
2
|
|
|
|
|
65
|
|
2
|
2
|
|
|
2
|
|
11
|
use warnings; |
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
124
|
|
3
|
|
|
|
|
|
|
|
4
|
|
|
|
|
|
|
package Email::MIME::XPath; |
5
|
|
|
|
|
|
|
|
6
|
|
|
|
|
|
|
our $VERSION = '0.005'; |
7
|
2
|
|
|
2
|
|
1821
|
use Tree::XPathEngine; |
|
2
|
|
|
|
|
59390
|
|
|
2
|
|
|
|
|
67
|
|
8
|
2
|
|
|
2
|
|
27
|
use Scalar::Util (); |
|
2
|
|
|
|
|
3
|
|
|
2
|
|
|
|
|
32
|
|
9
|
2
|
|
|
2
|
|
10
|
use Carp (); |
|
2
|
|
|
|
|
3
|
|
|
2
|
|
|
|
|
130
|
|
10
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
my (@EXTERNAL_AUTO, @EXTERNAL, @INTERNAL, @SPECIAL); |
12
|
|
|
|
|
|
|
BEGIN { |
13
|
2
|
|
|
2
|
|
7
|
@EXTERNAL_AUTO = qw(findnodes findnodes_as_string findvalue exists find); |
14
|
2
|
|
|
|
|
4
|
@EXTERNAL = qw(findnode matches); |
15
|
2
|
|
|
|
|
6
|
@INTERNAL = qw(get_name get_next_sibling get_previous_sibling get_root_node |
16
|
|
|
|
|
|
|
get_parent_node get_child_nodes |
17
|
|
|
|
|
|
|
is_element_node |
18
|
|
|
|
|
|
|
is_document_node |
19
|
|
|
|
|
|
|
is_attribute_node |
20
|
|
|
|
|
|
|
is_text_node |
21
|
|
|
|
|
|
|
cmp address |
22
|
|
|
|
|
|
|
get_attributes |
23
|
|
|
|
|
|
|
to_literal); |
24
|
2
|
|
|
|
|
176
|
@SPECIAL = qw(__xpath_engine __xpath_engine_options __build_parents |
25
|
|
|
|
|
|
|
__xpath_parent); |
26
|
|
|
|
|
|
|
} |
27
|
|
|
|
|
|
|
|
28
|
2
|
|
|
|
|
75
|
use Sub::Exporter -setup => { |
29
|
|
|
|
|
|
|
into => 'Email::MIME', |
30
|
|
|
|
|
|
|
exports => [ @EXTERNAL, @SPECIAL, @INTERNAL ], |
31
|
|
|
|
|
|
|
groups => { |
32
|
|
|
|
|
|
|
external_auto => \&_build_external, |
33
|
|
|
|
|
|
|
external => [ @EXTERNAL ], |
34
|
|
|
|
|
|
|
internal => [ @INTERNAL ], |
35
|
|
|
|
|
|
|
special => [ @SPECIAL ], |
36
|
|
|
|
|
|
|
default => [ |
37
|
|
|
|
|
|
|
-external_auto => { -prefix => 'xpath_' }, |
38
|
|
|
|
|
|
|
-external => { -prefix => 'xpath_' }, |
39
|
|
|
|
|
|
|
-internal => { -prefix => 'xpath_' }, |
40
|
|
|
|
|
|
|
-internal => { -prefix => '__xpath_' }, |
41
|
|
|
|
|
|
|
-special, |
42
|
|
|
|
|
|
|
], |
43
|
|
|
|
|
|
|
}, |
44
|
2
|
|
|
2
|
|
982
|
}; |
|
2
|
|
|
|
|
10766
|
|
45
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
sub _build_external { |
47
|
2
|
|
|
2
|
|
2818
|
my ($class, $group, $arg) = @_; |
48
|
|
|
|
|
|
|
return { |
49
|
10
|
|
|
|
|
23
|
map { |
50
|
2
|
|
|
|
|
13
|
my $method = $_; |
51
|
|
|
|
|
|
|
$method => sub { |
52
|
2
|
|
|
2
|
|
1892
|
my $self = shift; |
53
|
2
|
|
|
|
|
9
|
$self->__build_parents; |
54
|
2
|
|
|
|
|
6
|
return $self->__xpath_engine->$method(@_, $self); |
55
|
|
|
|
|
|
|
} |
56
|
10
|
|
|
|
|
55
|
} @EXTERNAL_AUTO |
57
|
|
|
|
|
|
|
}; |
58
|
|
|
|
|
|
|
} |
59
|
|
|
|
|
|
|
|
60
|
|
|
|
|
|
|
sub matches { |
61
|
0
|
|
|
0
|
0
|
0
|
my $self = shift; |
62
|
0
|
|
|
|
|
0
|
$self->__build_parents; |
63
|
0
|
|
|
|
|
0
|
my ($path, $context) = @_; |
64
|
0
|
|
0
|
|
|
0
|
$context ||= $self; |
65
|
0
|
|
|
|
|
0
|
return $self->__xpath_engine->matches($self, $path, $context); |
66
|
|
|
|
|
|
|
}; |
67
|
|
|
|
|
|
|
|
68
|
|
|
|
|
|
|
sub findnode { |
69
|
4
|
|
|
4
|
0
|
1884
|
my $self = shift; |
70
|
4
|
|
|
|
|
17
|
$self->__build_parents; |
71
|
4
|
|
|
|
|
9
|
my (@nodes) = $self->__xpath_engine->findnodes(@_, $self); |
72
|
4
|
50
|
|
|
|
363
|
Carp::croak "findnode found more than one node" if @nodes > 1; |
73
|
4
|
|
|
|
|
29
|
return $nodes[0]; |
74
|
|
|
|
|
|
|
} |
75
|
|
|
|
|
|
|
|
76
|
2
|
|
|
2
|
|
25
|
sub __xpath_engine_options { () } |
77
|
|
|
|
|
|
|
|
78
|
|
|
|
|
|
|
sub __xpath_engine { |
79
|
104
|
|
66
|
104
|
|
8532
|
return $_[0]->{__xpath_engine} ||= Tree::XPathEngine->new( |
80
|
|
|
|
|
|
|
$_[0]->__xpath_engine_options |
81
|
|
|
|
|
|
|
); |
82
|
|
|
|
|
|
|
} |
83
|
|
|
|
|
|
|
|
84
|
|
|
|
|
|
|
# this is a terrible, terrible hack. something like this should be in |
85
|
|
|
|
|
|
|
# Email::MIME instead. try to future-proof it somewhat. -- hdp, 2007-04-20 |
86
|
|
|
|
|
|
|
sub __is_multipart { |
87
|
6
|
|
|
6
|
|
24
|
return grep { $_ != $_[0] } $_[0]->parts; |
|
8
|
|
|
|
|
92
|
|
88
|
|
|
|
|
|
|
} |
89
|
|
|
|
|
|
|
|
90
|
|
|
|
|
|
|
# XXX a lot of trickery here is necessary because Email::MIME objects can be |
91
|
|
|
|
|
|
|
# shared among multiple trees at once. We keep track of parent/address |
92
|
|
|
|
|
|
|
# information inside the XPathEngine object, which is (originally) only inside |
93
|
|
|
|
|
|
|
# the top-level part. |
94
|
|
|
|
|
|
|
sub __build_parents { |
95
|
6
|
|
|
6
|
|
12
|
my $self = shift; |
96
|
6
|
100
|
|
|
|
19
|
return if $self->__xpath_engine->{__parent}; |
97
|
2
|
|
|
|
|
58
|
my $parent = $self->__xpath_engine->{__parent} = {}; |
98
|
2
|
|
|
|
|
8
|
my $address = $self->__xpath_engine->{__address} = {}; |
99
|
2
|
|
|
|
|
9
|
$self->__xpath_engine->{__root} = $self; |
100
|
2
|
|
|
|
|
5
|
Scalar::Util::weaken($self->__xpath_engine->{__root}); |
101
|
2
|
|
|
|
|
4
|
my $id = 0; |
102
|
2
|
|
|
|
|
19
|
$address->{$self} = sprintf("%03d", $id++); |
103
|
2
|
100
|
|
|
|
7
|
if (__is_multipart($self)) { |
104
|
1
|
|
|
|
|
3
|
my @q = $self; |
105
|
1
|
|
|
|
|
4
|
while (@q) { |
106
|
2
|
|
|
|
|
5
|
my $part = shift @q; |
107
|
2
|
|
|
|
|
7
|
my @subparts = $part->parts; |
108
|
2
|
|
|
|
|
23
|
for (@subparts) { |
109
|
4
|
|
|
|
|
14
|
$parent->{$_} = $part; |
110
|
4
|
|
|
|
|
14
|
Scalar::Util::weaken $parent->{$_}; |
111
|
4
|
|
|
|
|
17
|
$address->{$_} = sprintf("%03d", $id++); |
112
|
|
|
|
|
|
|
# XXX this will cause collisions if more than one Email::MIME::XPath |
113
|
|
|
|
|
|
|
# shares parts |
114
|
4
|
|
|
|
|
17
|
$_->{__xpath_engine} = $self->__xpath_engine; |
115
|
4
|
|
|
|
|
20
|
Scalar::Util::weaken $_->{__xpath_engine}; |
116
|
|
|
|
|
|
|
} |
117
|
2
|
|
|
|
|
5
|
push @q, grep { __is_multipart($_) } @subparts; |
|
4
|
|
|
|
|
9
|
|
118
|
|
|
|
|
|
|
} |
119
|
|
|
|
|
|
|
} |
120
|
|
|
|
|
|
|
} |
121
|
|
|
|
|
|
|
|
122
|
|
|
|
|
|
|
sub __xpath_parent { |
123
|
6
|
|
|
6
|
|
17
|
$_[0]->__xpath_engine->{__parent}->{$_[0]} |
124
|
|
|
|
|
|
|
} |
125
|
|
|
|
|
|
|
|
126
|
|
|
|
|
|
|
sub address { |
127
|
67
|
|
|
67
|
1
|
1248
|
$_[0]->__xpath_engine->{__address}->{$_[0]} |
128
|
|
|
|
|
|
|
} |
129
|
|
|
|
|
|
|
|
130
|
|
|
|
|
|
|
sub get_name { |
131
|
|
|
|
|
|
|
#my $subname = (caller(0))[3]; warn "$subname from " . $_[0]->__xpath_address; |
132
|
24
|
|
50
|
24
|
0
|
818
|
my $name = (split /;/, $_[0]->content_type || 'text/plain')[0]; |
133
|
24
|
|
|
|
|
1181
|
$name =~ tr{/+}{._}; |
134
|
24
|
|
|
|
|
65
|
$name = (split /\./, $name)[1]; |
135
|
|
|
|
|
|
|
#my $name = __is_multipart($_[0]) ? 'multi' : 'part'; |
136
|
|
|
|
|
|
|
#warn "name = $name"; |
137
|
24
|
|
|
|
|
72
|
return $name; |
138
|
|
|
|
|
|
|
} |
139
|
|
|
|
|
|
|
sub get_next_sibling { |
140
|
|
|
|
|
|
|
#my $subname = (caller(0))[3]; warn "$subname from " . $_[0]->__xpath_address; |
141
|
0
|
|
|
0
|
0
|
0
|
return; |
142
|
|
|
|
|
|
|
} |
143
|
|
|
|
|
|
|
sub get_previous_sibling { |
144
|
|
|
|
|
|
|
#my $subname = (caller(0))[3]; warn "$subname from " . $_[0]->__xpath_address; |
145
|
0
|
|
|
0
|
0
|
0
|
return; |
146
|
|
|
|
|
|
|
} |
147
|
|
|
|
|
|
|
sub get_root_node { |
148
|
|
|
|
|
|
|
#my $subname = (caller(0))[3]; warn "$subname from " . $_[0]->__xpath_address; |
149
|
6
|
|
|
6
|
0
|
8310
|
$_[0]->__xpath_engine->{__root}->__xpath_get_parent_node; |
150
|
|
|
|
|
|
|
} |
151
|
|
|
|
|
|
|
sub get_parent_node { |
152
|
|
|
|
|
|
|
#my $subname = (caller(0))[3]; warn "$subname from " . $_[0]->__xpath_address; |
153
|
6
|
|
|
6
|
0
|
31
|
my $node = shift; |
154
|
6
|
|
50
|
|
|
21
|
return $node->__xpath_parent || bless { root => $node }, 'Email::MIME::XPath::Root'; |
155
|
|
|
|
|
|
|
} |
156
|
|
|
|
|
|
|
sub get_child_nodes { |
157
|
|
|
|
|
|
|
#my $subname = (caller(0))[3]; warn "$subname from " . $_[0]->__xpath_address; |
158
|
41
|
|
|
41
|
0
|
2072
|
my @kids = grep { $_ != $_[0] } $_[0]->parts; |
|
57
|
|
|
|
|
549
|
|
159
|
41
|
|
|
|
|
116
|
return @kids; |
160
|
|
|
|
|
|
|
} |
161
|
22
|
|
|
22
|
0
|
978
|
sub is_element_node { 1 } |
162
|
0
|
|
|
0
|
0
|
0
|
sub is_document_node { 0 } |
163
|
0
|
|
|
0
|
0
|
0
|
sub is_attribute_node { 0 } |
164
|
0
|
|
|
0
|
0
|
0
|
sub is_text_node { } |
165
|
|
|
|
|
|
|
|
166
|
|
|
|
|
|
|
sub get_attributes { |
167
|
|
|
|
|
|
|
#my $subname = (caller(0))[3]; warn "$subname from " . $_[0]->__xpath_address; |
168
|
16
|
|
|
16
|
0
|
2311
|
my $node = shift; |
169
|
64
|
|
|
|
|
1314
|
my %attr = ( |
170
|
|
|
|
|
|
|
content_type => (split /;/, $node->content_type || 'text/plain')[0], |
171
|
|
|
|
|
|
|
address => $node->__xpath_address, |
172
|
|
|
|
|
|
|
$node->header('Content-Disposition') ? (filename => $node->filename) : (), |
173
|
|
|
|
|
|
|
map { |
174
|
16
|
100
|
50
|
|
|
61
|
my $val = $node->header($_); |
175
|
64
|
100
|
|
|
|
1940
|
defined $val ? (lc($_) => $val) : () |
176
|
|
|
|
|
|
|
} qw(from to cc subject), |
177
|
|
|
|
|
|
|
); |
178
|
|
|
|
|
|
|
#use Data::Dumper; warn Dumper(\%attr); |
179
|
45
|
|
|
|
|
320
|
return map { |
180
|
16
|
|
|
|
|
61
|
bless { |
181
|
|
|
|
|
|
|
name => $_, |
182
|
|
|
|
|
|
|
value => $attr{$_}, |
183
|
|
|
|
|
|
|
node => $node, |
184
|
|
|
|
|
|
|
} => 'Email::MIME::XPath::Attribute' |
185
|
|
|
|
|
|
|
} keys %attr; |
186
|
|
|
|
|
|
|
} |
187
|
|
|
|
|
|
|
sub cmp { |
188
|
29
|
|
|
29
|
0
|
357
|
return $_[0]->__xpath_address <=> $_[1]->__xpath_address |
189
|
|
|
|
|
|
|
} |
190
|
0
|
|
|
0
|
0
|
0
|
sub to_literal { } |
191
|
|
|
|
|
|
|
|
192
|
|
|
|
|
|
|
package Email::MIME::XPath::Root; |
193
|
|
|
|
|
|
|
|
194
|
8
|
|
|
8
|
|
23
|
sub __xpath_address { -1 } # root is always first |
195
|
10
|
|
|
10
|
|
610
|
sub xpath_get_child_nodes { $_[0]->{root} } |
196
|
0
|
|
|
0
|
|
0
|
sub xpath_get_attributes { () } |
197
|
0
|
|
|
0
|
|
0
|
sub xpath_is_document_node { 1 } |
198
|
0
|
|
|
0
|
|
0
|
sub xpath_is_element_node { 0 } |
199
|
0
|
|
|
0
|
|
0
|
sub xpath_is_attribute_node { 0 } |
200
|
|
|
|
|
|
|
|
201
|
|
|
|
|
|
|
# my testing doesn't seem to use this, but I've gotten test failures saying |
202
|
|
|
|
|
|
|
# that it's necessary. I'm tempted to simply @ISA = Email::MIME::XPath, but |
203
|
|
|
|
|
|
|
# that might have other undesirable ramifications. |
204
|
|
|
|
|
|
|
|
205
|
0
|
|
|
0
|
|
0
|
sub xpath_cmp { $_[0]->__xpath_address <=> $_[1]->__xpath_address } |
206
|
|
|
|
|
|
|
|
207
|
|
|
|
|
|
|
package Email::MIME::XPath::Attribute; |
208
|
|
|
|
|
|
|
|
209
|
0
|
|
|
0
|
|
0
|
sub xpath_get_value { return $_[0]->{value} } |
210
|
45
|
|
|
45
|
|
735
|
sub xpath_get_name { return $_[0]->{name} } |
211
|
12
|
|
|
12
|
|
1241
|
sub xpath_string_value { return $_[0]->{value} } |
212
|
0
|
|
|
0
|
|
|
sub xpath_is_document_node { 0 } |
213
|
0
|
|
|
0
|
|
|
sub xpath_is_element_node { 0 } |
214
|
0
|
|
|
0
|
|
|
sub xpath_is_attribute_node { 1 } |
215
|
0
|
|
|
0
|
|
|
sub to_string { return sprintf('%s="%s"', $_[0]->{name}, $_[0]->{value}) } |
216
|
0
|
|
0
|
0
|
|
|
sub address { return join(":", $_[0]->{node}, $_[0]->{rank} || 0) } |
217
|
0
|
|
|
0
|
|
|
sub xpath_cmp { $_[0]->address cmp $_[1]->address } |
218
|
|
|
|
|
|
|
|
219
|
|
|
|
|
|
|
1; |
220
|
|
|
|
|
|
|
|
221
|
|
|
|
|
|
|
__END__ |