File Coverage

blib/lib/HTTP/Cookies/ChromeMacOS.pm
Criterion Covered Total %
statement 44 103 42.7
branch 0 18 0.0
condition 0 20 0.0
subroutine 15 25 60.0
pod 2 2 100.0
total 61 168 36.3


line stmt bran cond sub pod time code
1             package HTTP::Cookies::ChromeMacOS;
2              
3 1     1   35406 use 5.010;
  1         7  
4 1     1   11 use strict;
  1         3  
  1         36  
5 1     1   8 use warnings;
  1         18  
  1         44  
6              
7 1     1   3639 use DBI;
  1         20434  
  1         90  
8 1     1   1233 use utf8;
  1         14  
  1         6  
9 1     1   1080 use POSIX;
  1         7632  
  1         7  
10 1     1   19005 use PBKDF2::Tiny qw/derive/;
  1         11849  
  1         149  
11 1     1   1076 use Crypt::CBC;
  1         7558  
  1         77  
12              
13             =head1 NAME
14              
15             HTTP::Cookies::ChromeMacOS - MacOS系统读取Chrome Cookies
16              
17             =head1 VERSION
18              
19             Version 0.01
20              
21             =cut
22              
23             our $VERSION = '0.02';
24              
25              
26             =head1 SYNOPSIS
27              
28             Quick summary of what the module does.
29              
30             Perhaps a little code snippet.
31              
32             use HTTP::Cookies::ChromeMacOS;
33              
34             my $cookie = HTTP::Cookies::ChromeMacOS->new();
35             $cookie->load( "/path/to/Cookies", 'Want to load domain' );
36              
37             # /path/to/Cookies Usually is: ~/Library/Application Support/Google/Chrome/Default/Cookies
38             # Want to load domain can be: google, yahoo, facebook etc or null will load all cookies
39              
40             my $ua = LWP::UserAgent->new(
41             cookie_jar => $cookie,
42             agent => 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.134 Safari/537.36',
43             );
44              
45             ...
46              
47             =head1 SUBROUTINES/METHODS
48              
49             =head2 The Chrome cookies table
50              
51             creation_utc INTEGER NOT NULL UNIQUE PRIMARY KEY
52             host_key TEXT NOT NULL
53             name TEXT NOT NULL
54             value TEXT NOT NULL
55             path TEXT NOT NULL
56             expires_utc INTEGER NOT NULL
57             secure INTEGER NOT NULL
58             httponly INTEGER NOT NULL
59             last_access_utc INTEGER NOT NULL
60              
61             =cut
62              
63              
64 1     1   8 use base qw( HTTP::Cookies );
  1         3  
  1         5804  
65              
66 1     1   28047 use constant TRUE => 1;
  1         4  
  1         84  
67 1     1   7 use constant FALSE => 0;
  1         2  
  1         942  
68              
69             my ( $dbh, $pass );
70              
71             sub _get_dbh {
72 0     0     my ( $self, $file ) = @_;
73 0 0 0       return $dbh if $dbh && $dbh->ping;
74 0           $dbh = DBI->connect( "dbi:SQLite:dbname=$file", '', '',
75             {
76             sqlite_see_if_its_a_number => 1,
77             }
78             );
79              
80 0           return $dbh;
81             }
82              
83             sub _get_rows {
84 0     0     my( $self, $file, $domain ) = @_;
85 0   0       $domain ||= '';
86              
87 0           my $dbh = $self->_get_dbh( $file );
88              
89 0           my @cols = qw/
90             creation_utc
91             host_key
92             name
93             value
94             encrypted_value
95             path
96             expires_utc
97             secure
98             httponly
99             last_access_utc
100             /;
101              
102 0           my $sql = 'SELECT ' . join( ', ', @cols ) . ' FROM cookies WHERE host_key like "%' . $domain . '%"';
103 0           my $sth = $dbh->prepare( $sql );
104 0           $sth->execute;
105              
106 0           my @rows = map { bless $_, 'HTTP::Cookies::Chrome::Record' } @{ $sth->fetchall_arrayref };
  0            
  0            
107 0           $dbh->disconnect;
108              
109 0           return \@rows;
110             }
111              
112             sub load {
113 0     0 1   my( $self, $file, $domain ) = @_;
114              
115 0   0       $file ||= $self->{'file'} || return;
      0        
116              
117              
118 0           my $salt = 'saltysalt';
119 0           my $iv = ' ' x 16;
120 0           my $salt_len = 16;
121 0           my $pass = _get_pass();
122 0           my $iterations = 1003;
123              
124 0           my $key = derive( 'SHA-1', $pass, $salt, $iterations, $salt_len );
125              
126              
127 0           my $cipher = Crypt::CBC->new(
128             -cipher => 'Crypt::OpenSSL::AES',
129             -key => $key,
130             -keysize => 16,
131             -iv => $iv,
132             -header => 'none',
133             -literal_key => 1,
134             );
135              
136 0           foreach my $row ( @{ $self->_get_rows( $file, $domain ) } ) {
  0            
137 0   0       my $value = $row->value || $row->encrypted_value || '';
138 0 0         if ( $value =~ /^v10/ ) {
139 0           $value =~ s/^v10//;
140 0           $value = $cipher->decrypt( $value );
141             }
142              
143             $self->set_cookie(
144             undef,
145 0           $row->name,
146             $value,
147             $row->path,
148             $row->host_key,
149             undef,
150             undef,
151             $row->secure,
152             time() + 86400, # never expires for readonly
153             0,
154             {}
155             );
156             }
157              
158 0           return 1;
159             }
160              
161             sub _get_pass {
162             # On Mac, replace password from keychain
163             # On Linux, replace password with 'peanuts'
164 0 0   0     return $pass if $pass;
165 0           $pass = `security find-generic-password -w -s "Chrome Safe Storage"`;
166 0           chomp( $pass );
167 0           return $pass;
168             }
169              
170             sub save {
171 0     0 1   my( $self, $new_file ) = @_;
172              
173             # never save, This is a ReadOnly Version
174 0           return;
175             }
176              
177             sub _filter_cookies {
178 0     0     my( $self ) = @_;
179              
180             $self->scan(
181             sub {
182 0     0     my( $version, $key, $val, $path, $domain, $port,
183             $path_spec, $secure, $expires, $discard, $rest ) = @_;
184              
185 0 0 0       return if $discard && not $self->{ignore_discard};
186              
187 0 0 0       return if defined $expires && time > $expires;
188              
189 0           $expires = do {
190 0 0         unless( $expires ) { 0 }
  0            
191             else {
192 0           $expires * 1_000_000
193             }
194             };
195              
196 0 0         $secure = $secure ? TRUE : FALSE;
197              
198 0 0         my $bool = $domain =~ /^\./ ? TRUE : FALSE;
199              
200 0           $self->_insert(
201             $domain,
202             $key,
203             $val,
204             $path,
205             $expires,
206             $secure,
207             );
208             }
209 0           );
210              
211             }
212              
213              
214             sub _get_utc_microseconds {
215 1     1   14 no warnings 'uninitialized';
  1         2  
  1         62  
216 1     1   19860 use bignum;
  1         29767  
  1         7  
217 0   0 0     POSIX::strftime( '%s', gmtime() ) * 1_000_000 + ($_[1]//0);
218             }
219              
220             # This code from: https://github.com/briandfoy/HTTP-Cookies-Chrome
221             # I did small change
222 0           BEGIN {
223             package HTTP::Cookies::Chrome::Record;
224 1     1   115424 use vars qw($AUTOLOAD);
  1         2  
  1         168  
225              
226 1     1   3 my %columns = map { state $n = 0; $_, $n++ } qw(
  10         11  
  10         71  
227             creation_utc
228             host_key
229             name
230             value
231             encrypted_value
232             path
233             expires_utc
234             secure
235             httponly
236             last_access_utc
237             );
238              
239              
240             sub AUTOLOAD {
241 0     0     my( $self ) = @_;
242 0           my $method = $AUTOLOAD;
243 0           $method =~ s/.*:://;
244              
245 0 0         die "" unless exists $columns{$method};
246              
247 0           $self->[ $columns{$method} ];
248             }
249              
250 0     0     sub DESTROY { return 1 }
251             }
252              
253              
254              
255             =head1 AUTHOR
256              
257             MC Cheung, C<< >>
258              
259             =head1 BUGS
260              
261             Please report any bugs or feature requests to C, or through
262             the web interface at L. I will be notified, and then you'll
263             automatically be notified of progress on your bug as I make changes.
264              
265              
266              
267              
268             =head1 SUPPORT
269              
270             You can find documentation for this module with the perldoc command.
271              
272             perldoc HTTP::Cookies::ChromeMacOS
273              
274              
275             You can also look for information at:
276              
277             =over 4
278              
279             =item * RT: CPAN's request tracker (report bugs here)
280              
281             L
282              
283             =item * AnnoCPAN: Annotated CPAN documentation
284              
285             L
286              
287             =item * CPAN Ratings
288              
289             L
290              
291             =item * Search CPAN
292              
293             L
294              
295             =back
296              
297              
298             =head1 ACKNOWLEDGEMENTS
299              
300              
301             =head1 LICENSE AND COPYRIGHT
302              
303             Copyright 2015 MC Cheung.
304              
305             This program is free software; you can redistribute it and/or modify it
306             under the terms of the the Artistic License (2.0). You may obtain a
307             copy of the full license at:
308              
309             L
310              
311             Any use, modification, and distribution of the Standard or Modified
312             Versions is governed by this Artistic License. By using, modifying or
313             distributing the Package, you accept this license. Do not use, modify,
314             or distribute the Package, if you do not accept this license.
315              
316             If your Modified Version has been derived from a Modified Version made
317             by someone other than you, you are nevertheless required to ensure that
318             your Modified Version complies with the requirements of this license.
319              
320             This license does not grant you the right to use any trademark, service
321             mark, tradename, or logo of the Copyright Holder.
322              
323             This license includes the non-exclusive, worldwide, free-of-charge
324             patent license to make, have made, use, offer to sell, sell, import and
325             otherwise transfer the Package with respect to any patent claims
326             licensable by the Copyright Holder that are necessarily infringed by the
327             Package. If you institute patent litigation (including a cross-claim or
328             counterclaim) against any party alleging that the Package constitutes
329             direct or contributory patent infringement, then this Artistic License
330             to you shall terminate on the date that such litigation is filed.
331              
332             Disclaimer of Warranty: THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER
333             AND CONTRIBUTORS "AS IS' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES.
334             THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
335             PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY
336             YOUR LOCAL LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR
337             CONTRIBUTOR WILL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR
338             CONSEQUENTIAL DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE,
339             EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
340              
341              
342             =cut
343              
344             1; # End of HTTP::Cookies::ChromeMacOS