| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package HTTP::Cookies::Opera; |
|
2
|
|
|
|
|
|
|
|
|
3
|
3
|
|
|
3
|
|
182592
|
use strict; |
|
|
3
|
|
|
|
|
10
|
|
|
|
3
|
|
|
|
|
213
|
|
|
4
|
3
|
|
|
3
|
|
18
|
use warnings; |
|
|
3
|
|
|
|
|
5
|
|
|
|
3
|
|
|
|
|
1087
|
|
|
5
|
|
|
|
|
|
|
|
|
6
|
3
|
|
|
3
|
|
8373
|
use parent qw(HTTP::Cookies); |
|
|
3
|
|
|
|
|
2073
|
|
|
|
3
|
|
|
|
|
18
|
|
|
7
|
3
|
|
|
3
|
|
105060
|
use Carp qw(croak); |
|
|
3
|
|
|
|
|
10
|
|
|
|
3
|
|
|
|
|
409
|
|
|
8
|
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
our $VERSION = '0.08'; |
|
10
|
|
|
|
|
|
|
$VERSION = eval $VERSION; |
|
11
|
|
|
|
|
|
|
|
|
12
|
3
|
|
|
3
|
|
17
|
use constant DEBUG => !! $ENV{HTTP_COOKIES_OPERA_DEBUG}; |
|
|
3
|
|
|
|
|
7
|
|
|
|
3
|
|
|
|
|
412
|
|
|
13
|
3
|
|
|
3
|
|
18
|
use constant FILE_VER => 1; |
|
|
3
|
|
|
|
|
6
|
|
|
|
3
|
|
|
|
|
148
|
|
|
14
|
3
|
|
|
3
|
|
18
|
use constant APP_VER => 2; |
|
|
3
|
|
|
|
|
6
|
|
|
|
3
|
|
|
|
|
215
|
|
|
15
|
3
|
|
|
3
|
|
20
|
use constant TAG_LEN => 1; |
|
|
3
|
|
|
|
|
6
|
|
|
|
3
|
|
|
|
|
152
|
|
|
16
|
3
|
|
|
3
|
|
16
|
use constant LEN_LEN => 2; |
|
|
3
|
|
|
|
|
4
|
|
|
|
3
|
|
|
|
|
8015
|
|
|
17
|
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
sub load { |
|
19
|
3
|
|
|
3
|
1
|
129
|
my ($self, $file) = @_; |
|
20
|
3
|
50
|
33
|
|
|
33
|
$file ||= $self->{file} or return; |
|
21
|
|
|
|
|
|
|
|
|
22
|
3
|
50
|
|
|
|
541
|
open my $fh, '<', $file or die "$file: $!"; |
|
23
|
3
|
|
|
|
|
13
|
binmode $fh; |
|
24
|
3
|
50
|
|
|
|
197
|
12 == read($fh, my $header, 12) or croak 'bad file header'; |
|
25
|
3
|
|
|
|
|
24
|
my ($file_ver, $app_ver, $tag_len, $len_len) = unpack 'NNnn', $header; |
|
26
|
|
|
|
|
|
|
|
|
27
|
3
|
50
|
33
|
|
|
125
|
croak 'unexpected file format' |
|
|
|
|
33
|
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
28
|
|
|
|
|
|
|
unless FILE_VER == $file_ver >> 12 and APP_VER == $app_ver >> 12 |
|
29
|
|
|
|
|
|
|
and TAG_LEN == $tag_len and LEN_LEN == $len_len; |
|
30
|
|
|
|
|
|
|
|
|
31
|
3
|
|
|
|
|
7
|
my (@domain_parts, @path_parts, %cookie); |
|
32
|
|
|
|
|
|
|
|
|
33
|
3
|
|
|
|
|
14
|
while (TAG_LEN == read $fh, my $tag, TAG_LEN) { |
|
34
|
329
|
|
|
|
|
680
|
$tag = unpack 'C', $tag; |
|
35
|
329
|
|
|
|
|
687
|
DEBUG and printf "tag: %#x\n", $tag; |
|
36
|
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
# End of domain component. |
|
38
|
329
|
100
|
|
|
|
2263
|
if (0x84 == $tag) { |
|
|
|
100
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
39
|
21
|
|
|
|
|
273
|
pop @domain_parts; |
|
40
|
|
|
|
|
|
|
} |
|
41
|
|
|
|
|
|
|
# End of path component. |
|
42
|
|
|
|
|
|
|
elsif (0x85 == $tag) { |
|
43
|
27
|
|
|
|
|
34
|
pop @path_parts; |
|
44
|
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
# Add last constructed cookie as this path will have no more. |
|
46
|
27
|
|
|
|
|
74
|
$self->_add_cookie(\%cookie); |
|
47
|
|
|
|
|
|
|
} |
|
48
|
3
|
|
|
|
|
9
|
elsif (0x99 == $tag) { $cookie{secure} = 1 } |
|
49
|
|
|
|
|
|
|
elsif (0x3 == $tag) { |
|
50
|
|
|
|
|
|
|
# Add previous cookie now that it is fully constructed. |
|
51
|
35
|
|
|
|
|
134
|
$self->_add_cookie(\%cookie); |
|
52
|
|
|
|
|
|
|
|
|
53
|
|
|
|
|
|
|
# Reset cookie for new record. |
|
54
|
35
|
|
|
|
|
2223
|
%cookie = ( |
|
55
|
|
|
|
|
|
|
domain => join('.', reverse @domain_parts), |
|
56
|
|
|
|
|
|
|
path => '/' . join('/', @path_parts), |
|
57
|
|
|
|
|
|
|
); |
|
58
|
|
|
|
|
|
|
} |
|
59
|
|
|
|
|
|
|
|
|
60
|
|
|
|
|
|
|
# Record is a flag and contains no payload. |
|
61
|
329
|
100
|
|
|
|
3479
|
next if 0x80 & $tag; |
|
62
|
|
|
|
|
|
|
|
|
63
|
242
|
50
|
|
|
|
4205
|
LEN_LEN == read $fh, my $len, LEN_LEN or croak 'bad file'; |
|
64
|
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
# Tags have unique ids among top-level domain/path/cookie records as |
|
66
|
|
|
|
|
|
|
# well as the payload records, so simplify parsing by treating the |
|
67
|
|
|
|
|
|
|
# payload records as top-level records during the next iteration. |
|
68
|
242
|
100
|
|
|
|
919
|
next if 0x3 >= $tag; |
|
69
|
|
|
|
|
|
|
|
|
70
|
180
|
|
|
|
|
708
|
$len = unpack 'n', $len; |
|
71
|
180
|
|
|
|
|
173
|
DEBUG and printf " len: %d\n", $len; |
|
72
|
180
|
50
|
|
|
|
507
|
$len == read $fh, my $payload, $len or croak 'bad file'; |
|
73
|
|
|
|
|
|
|
|
|
74
|
180
|
100
|
|
|
|
727
|
if (0x1e == $tag) { push @domain_parts, $payload } |
|
|
18
|
100
|
|
|
|
37
|
|
|
|
|
100
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
75
|
9
|
|
|
|
|
56
|
elsif (0x1d == $tag) { push @path_parts, $payload } |
|
76
|
35
|
|
|
|
|
164
|
elsif (0x10 == $tag) { $cookie{key} = $payload } |
|
77
|
35
|
|
|
|
|
68
|
elsif (0x11 == $tag) { $cookie{val} = $payload } |
|
78
|
|
|
|
|
|
|
elsif (0x12 == $tag) { |
|
79
|
|
|
|
|
|
|
# Time is stored in 8 bytes for Opera >=10, 4 bytes for <10. |
|
80
|
35
|
50
|
|
|
|
86
|
$payload = unpack 8 == $len ? 'x4N' : 'N', $payload; |
|
81
|
35
|
|
|
|
|
157
|
$cookie{maxage} = $payload - time; |
|
82
|
35
|
|
|
|
|
224
|
DEBUG and $payload = scalar localtime $payload; |
|
83
|
|
|
|
|
|
|
} |
|
84
|
|
|
|
|
|
|
elsif (0x1a == $tag) { |
|
85
|
|
|
|
|
|
|
# Version- not yet seen. |
|
86
|
|
|
|
|
|
|
} |
|
87
|
|
|
|
|
|
|
|
|
88
|
180
|
|
|
|
|
633
|
DEBUG and printf " payload: %s\n", $payload; |
|
89
|
|
|
|
|
|
|
} |
|
90
|
|
|
|
|
|
|
|
|
91
|
3
|
|
|
|
|
53
|
close $fh; |
|
92
|
|
|
|
|
|
|
|
|
93
|
3
|
|
|
|
|
27
|
return 1; |
|
94
|
|
|
|
|
|
|
} |
|
95
|
|
|
|
|
|
|
|
|
96
|
|
|
|
|
|
|
sub _add_cookie { |
|
97
|
62
|
|
|
62
|
|
4325
|
my ($self, $cookie) = @_; |
|
98
|
|
|
|
|
|
|
|
|
99
|
62
|
100
|
|
|
|
201
|
return unless exists $cookie->{key}; |
|
100
|
|
|
|
|
|
|
|
|
101
|
56
|
|
|
|
|
276
|
$self->set_cookie( |
|
102
|
|
|
|
|
|
|
undef, @$cookie{qw(key val path domain)}, undef, undef, |
|
103
|
|
|
|
|
|
|
@$cookie{qw(secure maxage)}, undef, undef |
|
104
|
|
|
|
|
|
|
); |
|
105
|
|
|
|
|
|
|
} |
|
106
|
|
|
|
|
|
|
|
|
107
|
|
|
|
|
|
|
sub save { |
|
108
|
1
|
|
|
1
|
1
|
12
|
my ($self, $file) = @_; |
|
109
|
1
|
50
|
33
|
|
|
7
|
$file ||= $self->{file} or return; |
|
110
|
|
|
|
|
|
|
|
|
111
|
1
|
50
|
|
|
|
176
|
open my $fh, '>', $file or die "$file: $!"; |
|
112
|
1
|
|
|
|
|
4
|
binmode $fh; |
|
113
|
|
|
|
|
|
|
|
|
114
|
1
|
|
|
|
|
16
|
print $fh pack 'NNnn', FILE_VER << 12, APP_VER << 12, TAG_LEN, LEN_LEN; |
|
115
|
|
|
|
|
|
|
|
|
116
|
|
|
|
|
|
|
# Cannot call scan() as it iterates over the domains in lexical order, |
|
117
|
|
|
|
|
|
|
# but Opera requires the cookies to be stored in a hierarchy of domain |
|
118
|
|
|
|
|
|
|
# components (i.e. com -> opera -> www). |
|
119
|
9
|
50
|
|
|
|
17
|
my @domains = sort { $a->[0] cmp $b->[0] } map { |
|
|
5
|
|
|
|
|
22
|
|
|
120
|
|
|
|
|
|
|
# Do not split IP addresses into components. |
|
121
|
1
|
|
|
|
|
5
|
my @parts = /^(?:\d+\.){3}\d+$/ ? ($_) : reverse split '\.'; |
|
122
|
5
|
|
|
|
|
21
|
[ join('.', @parts), $_, \@parts ] |
|
123
|
1
|
|
|
|
|
3
|
} keys %{$self->{COOKIES}}; |
|
124
|
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
# Add an empty domain field to close the last open domain record. |
|
126
|
1
|
|
|
|
|
3
|
push @domains, []; |
|
127
|
|
|
|
|
|
|
|
|
128
|
1
|
|
|
|
|
2
|
my @prev_domain; |
|
129
|
1
|
|
|
|
|
4
|
for my $aref (@domains) { |
|
130
|
6
|
|
|
|
|
10
|
my ($sort_key, $domain, $parts) = @$aref; |
|
131
|
|
|
|
|
|
|
|
|
132
|
|
|
|
|
|
|
# Opera does not support cross-subdomain cookies. |
|
133
|
|
|
|
|
|
|
# |
|
134
|
|
|
|
|
|
|
# TODO: if a domain cookie and a cross-subdomain cookie both exist |
|
135
|
|
|
|
|
|
|
# for the same key, which should take precedence? |
|
136
|
6
|
100
|
66
|
|
|
35
|
my $is_cross = $parts && length $parts->[-1] ? 0 : pop @$parts || 1; |
|
|
|
|
50
|
|
|
|
|
|
137
|
|
|
|
|
|
|
|
|
138
|
|
|
|
|
|
|
# Close domain component records for previous domain. |
|
139
|
6
|
|
|
|
|
16
|
for (my $i = @prev_domain - 1; 0 <= $i; $i--) { |
|
140
|
11
|
|
|
|
|
15
|
my $prev = $prev_domain[$i]; |
|
141
|
11
|
100
|
100
|
|
|
69
|
if (length $prev and $prev ne ($parts->[$i] || '')) { |
|
|
|
|
66
|
|
|
|
|
|
142
|
6
|
|
|
|
|
2
|
DEBUG and print " closing: $prev\n"; |
|
143
|
6
|
|
|
|
|
6
|
pop @prev_domain; |
|
144
|
6
|
|
|
|
|
18
|
print $fh pack 'C', 0x84; |
|
145
|
|
|
|
|
|
|
} |
|
146
|
|
|
|
|
|
|
} |
|
147
|
|
|
|
|
|
|
|
|
148
|
6
|
100
|
|
|
|
13
|
last unless $domain; |
|
149
|
5
|
|
|
|
|
6
|
DEBUG and print "domain: $domain\n"; |
|
150
|
|
|
|
|
|
|
|
|
151
|
|
|
|
|
|
|
# Open domain component records for next domain. |
|
152
|
5
|
|
|
|
|
13
|
for (my $i = @prev_domain; $i < @$parts; $i++) { |
|
153
|
6
|
|
|
|
|
9
|
my $curr = $parts->[$i]; |
|
154
|
6
|
50
|
50
|
|
|
115
|
if (length $curr and $curr ne ($prev_domain[$i] || '')) { |
|
|
|
|
33
|
|
|
|
|
|
155
|
6
|
|
|
|
|
5
|
DEBUG and print " opening: $curr\n"; |
|
156
|
6
|
|
|
|
|
10
|
push @prev_domain, $curr; |
|
157
|
6
|
|
|
|
|
16
|
print $fh pack 'Cn', 0x1, 3 + length($curr); |
|
158
|
6
|
|
|
|
|
9
|
print $fh pack 'Cn', 0x1e, length($curr); |
|
159
|
6
|
|
|
|
|
6
|
print $fh $curr; |
|
160
|
6
|
100
|
|
|
|
23
|
print $fh pack 'C', 0x85 if $i < @$parts - 1; |
|
161
|
|
|
|
|
|
|
} |
|
162
|
|
|
|
|
|
|
} |
|
163
|
|
|
|
|
|
|
|
|
164
|
5
|
|
|
|
|
7
|
my @paths = sort keys %{$self->{COOKIES}{$domain}}; |
|
|
5
|
|
|
|
|
18
|
|
|
165
|
|
|
|
|
|
|
|
|
166
|
|
|
|
|
|
|
# Add an empty path field to close the last open path record. |
|
167
|
5
|
|
|
|
|
7
|
push @paths, ''; |
|
168
|
|
|
|
|
|
|
|
|
169
|
5
|
|
|
|
|
6
|
my @prev_path; |
|
170
|
5
|
|
|
|
|
6
|
for my $path (@paths) { |
|
171
|
12
|
|
|
|
|
26
|
my @parts = split '/', $path; |
|
172
|
12
|
|
|
|
|
14
|
shift @parts; |
|
173
|
|
|
|
|
|
|
|
|
174
|
|
|
|
|
|
|
# Close path component records for previous path. |
|
175
|
12
|
|
|
|
|
31
|
for (my $i = @prev_path - 1; 0 <= $i; $i--) { |
|
176
|
3
|
|
|
|
|
4
|
my $prev = $prev_path[$i]; |
|
177
|
3
|
50
|
50
|
|
|
155
|
if (length $prev and $prev ne ($parts[$i] || '')) { |
|
|
|
|
33
|
|
|
|
|
|
178
|
3
|
|
|
|
|
3
|
DEBUG and print " closing: $prev\n"; |
|
179
|
3
|
|
|
|
|
4
|
print $fh pack 'C', 0x85; |
|
180
|
3
|
|
|
|
|
8
|
pop @prev_path; |
|
181
|
|
|
|
|
|
|
} |
|
182
|
|
|
|
|
|
|
} |
|
183
|
|
|
|
|
|
|
|
|
184
|
12
|
100
|
|
|
|
24
|
last unless $path; |
|
185
|
7
|
|
|
|
|
5
|
DEBUG and print " path: $path\n"; |
|
186
|
|
|
|
|
|
|
|
|
187
|
|
|
|
|
|
|
# Open path component records for next path. |
|
188
|
7
|
|
|
|
|
19
|
for (my $i = @prev_path; $i < @parts; $i++) { |
|
189
|
3
|
|
|
|
|
4
|
my $curr = $parts[$i]; |
|
190
|
3
|
50
|
50
|
|
|
23
|
if (length $curr and $curr ne ($prev_path[$i] || '')) { |
|
|
|
|
33
|
|
|
|
|
|
191
|
3
|
|
|
|
|
42
|
DEBUG and print " opening: $curr\n"; |
|
192
|
3
|
|
|
|
|
8
|
print $fh pack 'Cn', 0x2, 3 + length($curr); |
|
193
|
3
|
|
|
|
|
6
|
print $fh pack 'Cn', 0x1d, length($curr); |
|
194
|
3
|
|
|
|
|
2
|
print $fh $curr; |
|
195
|
3
|
|
|
|
|
9
|
push @prev_path, $curr; |
|
196
|
|
|
|
|
|
|
} |
|
197
|
|
|
|
|
|
|
} |
|
198
|
|
|
|
|
|
|
|
|
199
|
7
|
|
|
|
|
13
|
my $href = $self->{COOKIES}{$domain}{$path}; |
|
200
|
7
|
|
|
|
|
25
|
while (my ($key, $aref) = each %$href) { |
|
201
|
|
|
|
|
|
|
my ( |
|
202
|
11
|
|
|
|
|
91
|
$version, $val, $port, $path_spec, $secure, $expires, |
|
203
|
|
|
|
|
|
|
$discard, $rest |
|
204
|
|
|
|
|
|
|
) = @$aref; |
|
205
|
|
|
|
|
|
|
|
|
206
|
11
|
50
|
33
|
|
|
25
|
next if $discard and not $self->{ignore_discard}; |
|
207
|
11
|
50
|
33
|
|
|
109
|
if (defined $expires and time > $expires) { |
|
208
|
0
|
|
|
|
|
0
|
DEBUG and print " expired cookie: $key\n"; |
|
209
|
0
|
|
|
|
|
0
|
next; |
|
210
|
|
|
|
|
|
|
} |
|
211
|
|
|
|
|
|
|
|
|
212
|
11
|
|
|
|
|
121
|
DEBUG and print " cookie: $key -> $val\n"; |
|
213
|
11
|
|
|
|
|
24
|
print $fh pack 'Cn', 0x3, |
|
214
|
|
|
|
|
|
|
17 + length($key) + length($val) + !! $secure; |
|
215
|
11
|
|
|
|
|
22
|
print $fh pack('Cn', 0x10, length($key)), $key; |
|
216
|
11
|
|
|
|
|
14
|
print $fh pack('Cn', 0x11, length($val)), $val; |
|
217
|
11
|
|
|
|
|
23
|
print $fh pack 'Cnx4N', 0x12, 8, $expires; |
|
218
|
11
|
100
|
|
|
|
60
|
print $fh pack 'C', 0x99 if $secure; |
|
219
|
|
|
|
|
|
|
} |
|
220
|
|
|
|
|
|
|
} |
|
221
|
|
|
|
|
|
|
|
|
222
|
5
|
|
|
|
|
12
|
print $fh pack 'C', 0x85; |
|
223
|
|
|
|
|
|
|
} |
|
224
|
|
|
|
|
|
|
|
|
225
|
1
|
|
|
|
|
2
|
print $fh pack 'C', 0x84; |
|
226
|
1
|
|
|
|
|
78
|
close $fh; |
|
227
|
|
|
|
|
|
|
|
|
228
|
1
|
|
|
|
|
74
|
return 1; |
|
229
|
|
|
|
|
|
|
} |
|
230
|
|
|
|
|
|
|
|
|
231
|
|
|
|
|
|
|
|
|
232
|
|
|
|
|
|
|
1; |
|
233
|
|
|
|
|
|
|
|
|
234
|
|
|
|
|
|
|
__END__ |