File Coverage

blib/lib/HTTP/Cookies/PhantomJS.pm
Criterion Covered Total %
statement 120 123 97.5
branch 31 40 77.5
condition 12 19 63.1
subroutine 13 13 100.0
pod 2 2 100.0
total 178 197 90.3


line stmt bran cond sub pod time code
1             package HTTP::Cookies::PhantomJS;
2              
3 1     1   766 use strict;
  1         2  
  1         25  
4 1     1   11213 use HTTP::Cookies;
  1         24858  
  1         29  
5 1     1   1538 use HTTP::Response;
  1         82512  
  1         34  
6 1     1   780 use HTTP::Request;
  1         959  
  1         29  
7 1     1   6 use HTTP::Headers::Util qw/split_header_words join_header_words/;
  1         2  
  1         74  
8 1     1   5 use HTTP::Date qw/time2str/;
  1         2  
  1         70  
9              
10             our @ISA = 'HTTP::Cookies';
11             our $VERSION = '0.01';
12              
13 1     1   7 use constant MAGIC => 'cookies="@Variant(\0\0\0\x7f\0\0\0\x16QList\0\0\0\0\x1';
  1         2  
  1         1393  
14             my %ESCAPES = (
15             'b' => "\b",
16             'f' => "\f",
17             'n' => "\n",
18             'r' => "\r",
19             't' => "\t",
20             '\\' => '\\',
21             );
22              
23             sub _read_length_block {
24 298     298   408 my $str_ref = shift;
25            
26 298         327 my $bytes;
27 298         638 for (1..4) {
28 1192         1914 my $c = substr($$str_ref, 0, 1, '');
29 1192 100       2320 if ($c ne '\\') {
30 107         187 $bytes .= sprintf '%x', ord($c);
31 107         202 next;
32             }
33            
34 1085         1587 $c = substr($$str_ref, 0, 1, '');
35 1085 100       2121 if ($c ne 'x') {
36 892 100       1707 if (exists $ESCAPES{$c}) {
37 4         10 $bytes .= sprintf '%x', ord($ESCAPES{$c});
38             }
39             else {
40 888         1870 $bytes .= sprintf '%x', int $c;
41             }
42 892         1385 next;
43             }
44            
45 193         305 $c = substr($$str_ref, 0, 1, '');
46 193 100       737 if (substr($$str_ref, 0, 1) =~ /[a-f0-9]/) {
47 185         314 $c .= substr($$str_ref, 0, 1, '');
48             }
49 193 50 66     559 if (length($c) == 1 && $bytes && substr($bytes, -2) ne '\0') {
      66        
50             # \0\0\x1\x4 -> 00104
51 8         17 $c = '0'.$c;
52             }
53 193         407 $bytes .= $c;
54             }
55            
56 298         615 hex($bytes);
57             }
58              
59             sub load {
60 3     3 1 1752 my $self = shift;
61 3   100     19 my $file = shift || $self->{'file'} || return;
62            
63 2 50       88 open my $fh, '<', $file or return;
64 2         55 <$fh>; # omit header
65 2         137 my $data = <$fh>;
66 2         104 $data =~ s/\\"/"/g;
67 2         15 close $fh;
68 2 50       13 unless (substr($data, 0, length(MAGIC), '') eq MAGIC) {
69 0         0 warn "$file does not seem to contain cookies";
70 0         0 return;
71             }
72            
73 2         7 my $cnt = _read_length_block(\$data);
74 2         3 my ($len, $cookie, $cookie_str);
75 2         7 for (my $i=0; $i<$cnt; $i++) {
76 296         118491 $len = _read_length_block(\$data);
77 296         619 $cookie_str = substr($data, 0, $len, '');
78            
79             # beginning may be in hex notation
80 296         340 my $additional = 0;
81 296         998 while ((my $c = substr($cookie_str, $additional, 4)) =~ /\\x[a-f0-9]{2}/) {
82 17         49 substr($cookie_str, $additional, 4) = chr hex substr $c, 2;
83 17         69 $additional++;
84             }
85 296         502 $cookie_str .= substr($data, 0, $additional*3, '');
86            
87 296 50       760 if ($additional = $cookie_str =~ s/\\\\/\\/g) {
88 0         0 $cookie_str .= substr($data, 0, $additional, '');
89             }
90             #print $cookie_str, "\n";
91            
92             # properly process quoted values
93             # however anyway it is broken in HTTP::Cookies 6.01 - rt70721
94 296         723 my ($key_val) = split_header_words($cookie_str);
95 296         39399 $key_val = join_header_words($key_val->[0], $key_val->[1]);
96 296         7750 my $tmp = $cookie_str;
97             # value inside key_val may be quoted, but original may be not, so check it
98 296 100       936 substr($tmp, 0, substr($tmp, length($key_val), 1) eq ';' ? length($key_val)+1 : length($key_val)-1) = '';
99 296         875 my @cookie_parts = split ';', $tmp;
100            
101 296         364 my ($domain, $path);
102 296         996 for (my $i=0; $i<@cookie_parts; $i++) {
103 861 50 33     1904 last if $path && $domain;
104 861 100 100     4493 if (!$domain and ($domain) = $cookie_parts[$i] =~ /domain=(.+)/) {
105 296         853 next;
106             }
107 565 50       1106 if (!$path) {
108 565         2433 ($path) = $cookie_parts[$i] =~ /path=(.+)/
109             }
110             }
111            
112             # generate fake request, so we can reuse extract_cookies() method
113 296 100       1634 my $req = HTTP::Request->new(GET => "http://".(substr($domain, 0, 1) eq '.' ? 'www' : '')."$domain$path");
114 296         48814 my $resp = HTTP::Response->new(200, 'OK', ['Set-Cookie', $cookie_str]);
115 296         24321 $resp->request($req);
116            
117 296         2715 $self->extract_cookies($resp);
118             }
119            
120 2         815 1;
121             }
122              
123             sub _generate_length_block {
124 149     149   173 my $length = shift;
125            
126             my $normalize = sub {
127 152     152   208 my $str = shift;
128 152 100       329 return $str if length($str) != 2;
129 149         192 $str =~ s/^0//;
130 149         394 $str;
131 149         383 };
132            
133 149         185 my $bytes;
134 149         275 my $hex = sprintf '%x', $length;
135 149         144 my $part;
136 149         249 for (1..4) {
137 596 100       1485 $bytes = (length($hex) ? '\x'.$normalize->(substr($hex, -2, 2, '')) : '\0'). $bytes;
138             }
139            
140 149         411 $bytes;
141             }
142              
143             sub save {
144 1     1 1 899 my $self = shift;
145 1   0     6 my $file = shift || $self->{'file'} || return;
146 1 50       54 open my $fh, '>', $file or die "Can't open $file: $!";
147            
148 1         3 my $res = MAGIC;
149 1         2 my @cookies;
150            
151             $self->scan(sub {
152 148     148   1691 my ($version,$key,$val,$path,$domain,$port,
153             $path_spec,$secure,$expires,$discard,$rest) = @_;
154            
155 148 50 66     396 return if $discard && !$self->{ignore_discard};
156 148         166 my @cookie_parts;
157            
158 148 100       459 push @cookie_parts, $val =~ /^"/ ? "$key=$val" : join_header_words($key, $val);
159 148 50       3378 push @cookie_parts, 'secure' if $secure;
160 148         258 push @cookie_parts, keys %$rest;
161 148 100       442 push @cookie_parts, 'expires='.time2str($expires) if $expires;
162 148         1408 push @cookie_parts, 'domain='.$domain;
163 148         229 push @cookie_parts, 'path='.$path;
164            
165 148         807 push @cookies, join '; ', @cookie_parts;
166 1         11 });
167            
168 1         17 $res .= _generate_length_block(scalar @cookies);
169 1         3 for my $cookie (@cookies) {
170 148         263 $res .= _generate_length_block(length $cookie);
171 148         237 $cookie =~ s/\\/\\\\/g;
172 148         281 $cookie =~ s/"/\\"/g;
173             # any valid hex symbol at the beginning should be replaced with \x notation
174 148         184 my $i = 0;
175 148         457 while ((my $c = substr($cookie, $i, 1)) =~ /[a-f0-9]/) {
176 15         38 substr($cookie, $i, 1) = sprintf '\x%x', ord($c);
177 15         55 $i += 4;
178             }
179 148         272 $res .= $cookie;
180             }
181 1         3 $res .= ')"';
182            
183 1         15 print $fh "[General]\n";
184 1         131 print $fh $res, "\n";
185 1         27 close $fh;
186            
187 1         21 1;
188             }
189              
190             1;
191              
192             __END__