File Coverage

blib/lib/HTTP/Cookies/Find.pm
Criterion Covered Total %
statement 75 190 39.4
branch 11 82 13.4
condition 2 15 13.3
subroutine 19 21 90.4
pod 2 2 100.0
total 109 310 35.1


line stmt bran cond sub pod time code
1              
2             # $Id: Find.pm,v 1.415 2010-05-08 12:42:51 Martin Exp $
3              
4             package HTTP::Cookies::Find;
5              
6 3     3   125931 use strict;
  3         7  
  3         130  
7              
8 3     3   17 use base 'HTTP::Cookies';
  3         5  
  3         2663  
9              
10 3     3   51424 use Carp;
  3         12  
  3         287  
11 3     3   3645 use Config::IniFiles;
  3         122745  
  3         118  
12 3     3   33 use Data::Dumper; # for debugging only
  3         6  
  3         156  
13 3     3   3415 use File::HomeDir;
  3         24426  
  3         250  
14 3     3   2984 use File::Spec::Functions;
  3         2815  
  3         443  
15 3     3   2986 use File::Slurp;
  3         56401  
  3         294  
16 3     3   3802 use HTTP::Cookies::Mozilla;
  3         6930  
  3         97  
17 3     3   48 use HTTP::Cookies::Netscape;
  3         7  
  3         82  
18 3     3   2359 use User;
  3         582  
  3         243  
19              
20             our
21             $VERSION = do { my @r = (q$Revision: 1.415 $ =~ /\d+/g); sprintf "%d."."%03d" x $#r, @r };
22              
23             =head1 NAME
24              
25             HTTP::Cookies::Find - Locate cookies for the current user on the local machine.
26              
27             =head1 SYNOPSIS
28              
29             use HTTP::Cookies::Find;
30             my $oCookies = HTTP::Cookies::Find->new('domain.com');
31             my @asMsg = HTTP::Cookies::Find::errors;
32             # Now $oCookies is a subclass of HTTP::Cookies
33             # and @asMsg is an array of error messages
34              
35             # Call in array context to find cookies from multiple
36             # browsers/versions:
37             my @aoCookies = HTTP::Cookies::Find->new('domain.com');
38             # Now @aoCookies is an array of HTTP::Cookies objects
39              
40             =head1 DESCRIPTION
41              
42             Looks in various normal places for HTTP cookie files.
43              
44             =head1 METHODS
45              
46             =over
47              
48             =item new
49              
50             Returns a list of cookie jars of type HTTP::Cookies::[vendor],
51             for all vendor browsers found on the system.
52             If called in scalar context, returns one cookie jar for the "first" vendor browser found on the system.
53             The returned cookie objects are not tied to the cookie files on disk;
54             the returned cookie objects are read-only copies of the found cookies.
55             If no argument is given, the returned cookie objects contain read-only copies of ALL cookies.
56             If an argument is given, the returned cookie objects contain read-only copies of only those cookies whose hostname "matches" the argument.
57             Here "matches" means case-insensitive pattern match;
58             you can pass a qr{} regexp as well as a plain string for matching.
59              
60             =cut
61              
62             ############################################# main pod documentation end ##
63              
64 3     3   18 use constant DEBUG_NEW => 0;
  3         6  
  3         159  
65 3     3   15 use constant DEBUG_GET => 0;
  3         4  
  3         129  
66              
67             # We use global variables so that the callback function can see them:
68 3     3   23 use vars qw( $sUser $sHostGlobal $oReal );
  3         5  
  3         6825  
69              
70             my @asError;
71              
72             sub _add_error
73             {
74 2     2   5 push @asError, shift;
75             } # _add_error
76              
77             sub new
78             {
79 1     1 1 22 my $class = shift;
80 1   50     5 $sHostGlobal = shift || '';
81 1         1 my @aoRet;
82 1 50 33     14 if ($^O =~ m!win32!i)
    50          
83             {
84 0         0 WIN32_MSIE:
85             {
86             # Massage the hostname in an attempt to make it match MS' highlevel
87             # naming scheme:
88 0         0 my $sHost = $sHostGlobal;
89 0         0 $sHost =~ s!\.(com|edu|gov|net|org)\Z!!; # delete USA domain
90 0         0 $sHost =~ s!\.[a-z][a-z]\.[a-z][a-z]\Z!!; # delete intl domain
91             # We only look at cookies for the logged-in user:
92 0         0 $sUser = lc User->Login;
93 0         0 print STDERR " + Finding cookies for user $sUser with host matching ($sHost)...\n" if DEBUG_NEW;
94 0         0 my ($sDir, %hsRegistry);
95 0         0 eval q{require HTTP::Cookies::Microsoft};
96 0 0       0 if ($@)
97             {
98 0         0 _add_error qq{ EEE can not load module HTTP::Cookies::Microsoft: $@\n};
99 0         0 last WIN32_MSIE;
100             } # if
101 0         0 eval q{use Win32::TieRegistry(
102             Delimiter => '/',
103             TiedHash => \%hsRegistry,
104             )};
105 0 0       0 if ($@)
106             {
107 0         0 _add_error qq{ EEE can not load module Win32::TieRegistry: $@\n};
108 0         0 last WIN32_MSIE;
109             } # if
110 0   0     0 $sDir = $hsRegistry{"CUser/Software/Microsoft/Windows/CurrentVersion/Explorer/Shell Folders/Cookies"} || '';
111 0 0       0 if ($sDir eq '')
112             {
113 0         0 _add_error qq{ EEE can not find registry entry for MSIE cookies\n};
114 0         0 last WIN32_MSIE;
115             } # if
116 0 0       0 unless (-d $sDir)
117             {
118 0         0 ; _add_error qq{ EEE registry entry for MSIE cookies is $sDir but that directory does not exist.\n}
119             ; last WIN32_MSIE
120 0         0 } # unless
121             # index.dat is for XP; Low/index.dat is for Vista:
122 0         0 foreach my $sFnameBase (qw( index.dat Low/index.dat ))
123             {
124 0         0 my $sFnameCookies = "$sDir\\$sFnameBase";
125 0 0       0 if (-f $sFnameCookies)
126             {
127 0         0 _get_cookies($sFnameCookies, 'HTTP::Cookies::Microsoft');
128 0         0 last WIN32_MSIE;
129             } # if
130             } # foreach
131             } # end of WIN32_MSIE block
132             # At this point, $oReal contains MSIE cookies (or undef).
133 0 0       0 if (ref($oReal))
134             {
135 0 0       0 return $oReal if ! wantarray;
136 0         0 push @aoRet, $oReal;
137             } # if found MSIE cookies
138             # If wantarray, or the MSIE cookie search failed, go on and look
139             # for Netscape cookies:
140             WIN32_NETSCAPE:
141             {
142 0         0 $oReal = undef;
  0         0  
143 0         0 my $sDirWin = $ENV{WINDIR};
144 0         0 my $sFnameWinIni = catfile($sDirWin, 'win.ini');
145 0 0       0 if (! -f $sFnameWinIni)
146             {
147 0         0 _add_error qq{ EEE Windows ini file $sFnameWinIni does not exist\n};
148 0         0 last WIN32_NETSCAPE;
149             } # if
150 0         0 my $oIniWin = new Config::IniFiles(
151             -file => $sFnameWinIni,
152             );
153 0 0       0 if (! ref($oIniWin))
154             {
155 0         0 _add_error qq{ EEE can not parse $sFnameWinIni\n};
156 0         0 last WIN32_NETSCAPE;
157             } # if
158 0         0 my $sFnameNSIni = $oIniWin->val('Netscape', 'ini');
159 0 0       0 if (! defined $sFnameNSIni)
160             {
161 0         0 _add_error qq{ EEE Netscape / Mozilla is not installed\n};
162 0         0 last WIN32_NETSCAPE;
163             } # if
164 0 0       0 if (! -f $sFnameNSIni)
165             {
166 0         0 _add_error qq{ EEE Netscape ini file $sFnameNSIni does not exist\n};
167 0         0 last WIN32_NETSCAPE;
168             } # if
169 0         0 my $oIniNS = Config::IniFiles->new(
170             -file => $sFnameNSIni,
171             );
172 0 0       0 if (! ref($oIniNS))
173             {
174 0         0 _add_error qq{ EEE can not parse $sFnameNSIni\n};
175 0         0 last WIN32_NETSCAPE;
176             } # if
177 0         0 my $sFnameCookies = $oIniNS->val('Cookies', 'Cookie File');
178 0         0 _get_cookies($sFnameCookies, 'HTTP::Cookies::Netscape');
179             } # end of WIN32_NETSCAPE block
180             # At this point, $oReal contains Netscape cookies (or undef).
181 0 0       0 if (ref($oReal))
182             {
183 0 0       0 return $oReal if ! wantarray;
184 0         0 push @aoRet, $oReal;
185             } # if found Netscape cookies
186             # If wantarray, or the previous cookie searches failed, go on and
187             # look for FireFox cookies:
188             WIN32_FIREFOX:
189             {
190 0         0 $oReal = undef;
  0         0  
191 0         0 my $sProfileDir = "$ENV{APPDATA}/Mozilla/Firefox/Profiles";
192 0 0       0 if (! opendir (DIR, $sProfileDir))
193             {
194 0         0 _add_error qq{ EEE Can't open Mozilla profile directory ( $sProfileDir ): $! };
195 0         0 last WIN32_FIREFOX;
196             } # if
197 0         0 my $bMozFound;
198 0         0 while ( my $test = readdir( DIR ) )
199             {
200 0 0 0     0 if ( -d "$sProfileDir/$test" && -f "$sProfileDir/$test/cookies.txt" )
201             {
202 0         0 $bMozFound = 1;
203 0         0 my $sFnameCookies = "$sProfileDir/$test/cookies.txt";
204 0         0 _get_cookies($sFnameCookies, 'HTTP::Cookies::Mozilla');
205             } # if
206             } # while
207 0 0       0 closedir DIR or warn;
208 0 0       0 if ( ! $bMozFound )
209             {
210 0         0 _add_error qq{ EEE No Mozilla cookie files found under $sProfileDir\\* }
211             } # if
212             } # end of WIN32_FIREFOX block
213             # At this point, $oReal contains Netscape cookies (or undef):
214 0 0       0 if (ref($oReal))
215             {
216 0 0       0 return $oReal if ! wantarray;
217 0         0 push @aoRet, $oReal;
218             } # if found Mozilla cookies
219             # No more places to look, fall through and return what we've
220             # found.
221             } # if MSWin32
222             elsif (
223             ($^O =~ m!solaris!i)
224             ||
225             ($^O =~ m!linux!i)
226             )
227             {
228             # Unix-like operating systems.
229 1         2 $oReal = undef;
230             UNIX_NETSCAPE4:
231             {
232 1         2 ; my $sFname = catfile(home(), '.netscape', 'cookies')
  1         7  
233 1         68 ; print STDERR " + try $sFname...\n" if DEBUG_NEW
234 1         4 ; _get_cookies($sFname, 'HTTP::Cookies::Netscape')
235 1 50       4 ; last UNIX_NETSCAPE4 unless ref($oReal)
236 0         0 ; push @aoRet, $oReal
237             } # end of UNIX_NETSCAPE4 block
238             # At this point, $oReal contains Netscape 7 cookies (or undef).
239 1 50       3 ; if (ref($oReal))
240             {
241 0 0       0 ; return $oReal if ! wantarray
242 0         0 ; push @aoRet, $oReal
243             } # if found any cookies
244             UNIX_NETSCAPE7:
245             {
246 1         2 ;
247             } # end of UNIX_NETSCAPE7 block
248             # At this point, $oReal contains Netscape 7 cookies (or undef).
249 1 50       4 ; if (ref($oReal))
250             {
251 0 0       0 ; return $oReal if ! wantarray
252 0         0 ; push @aoRet, $oReal
253             } # if found any cookies
254             UNIX_MOZILLA:
255             {
256 1     1   32 ; eval q{use HTTP::Cookies::Mozilla}
  1         2  
  1         21  
  1         2  
  1         115  
257 1         4 ; my $sAppregFname = catfile(home(), '.mozilla', 'appreg')
258             # ; print STDERR " + try to read appreg ==$sAppregFname==\n"
259 1 50       46 ; if (! -f $sAppregFname)
260             {
261 1         5 ; _add_error qq{ EEE Mozilla file $sAppregFname does not exist\n};
262             ; last UNIX_MOZILLA
263 1         2 } # if
264 0         0 ; my $sAppreg
265 0         0 ; eval { $sAppreg = read_file($sAppregFname, binmode => ':raw') }
  0         0  
266 0   0     0 ; $sAppreg ||= '';
267 0         0 ; my ($sDir) = ($sAppreg =~ m!(.mozilla/.+?\.slt)\b!)
268             # ; print STDERR " + found slt ==$sDir==\n"
269 0         0 ; my $sFname = catfile(home(), $sDir, 'cookies.txt')
270             # ; print STDERR " + try to read cookies ==$sFname==\n"
271 0         0 ; _get_cookies($sFname, 'HTTP::Cookies::Mozilla')
272             } # end of UNIX_MOZILLA block
273             # At this point, $oReal contains Mozilla cookies (or undef).
274             # ; print STDERR " + After mozilla cookie check, oReal is ==$oReal==\n"
275 1 50       5 ; if (ref($oReal))
276             {
277 0 0       0 ; return $oReal if ! wantarray
278             # ; print STDERR " + wantarray, keep looking\n"
279 0         0 ; push @aoRet, $oReal
280             } # if found Mozilla cookies
281             } # if Unix
282             else
283             {
284             # Future expansion: implement Netscape / other OS combinations
285             }
286 1 50       6 return wantarray ? @aoRet : $oReal;
287             } # new
288              
289             =item errors
290              
291             If anything went wrong while finding cookies,
292             errors() will return a list of string(s) describing the error(s).
293              
294             =cut
295              
296             sub errors
297             {
298 1     1 1 8 return @asError;
299             } # errors
300              
301             sub _get_cookies
302             {
303             # Required arg1 = cookies filename:
304 1     1   2 my $sFnameCookies = shift;
305             # Required arg2 = cookies object type:
306 1         2 my $sClass = shift;
307 1 0       9 my $rcCallback = ($sClass =~ m!Microsoft!) ? \&_callback_msie
    50          
    50          
308             : ($sClass =~ m!Netscape!) ? \&_callback_mozilla
309             : ($sClass =~ m!Mozilla!) ? \&_callback_mozilla
310             : \&_callback_mozilla;
311             # Our return value is an object of type HTTP::Cookies.
312 1         2 print STDERR " + _get_cookies($sFnameCookies,$sClass)\n" if DEBUG_GET;
313 1 50       19 if (! -f $sFnameCookies)
314             {
315 1         6 _add_error qq{ EEE cookies file $sFnameCookies does not exist\n};
316 1         2 return undef;
317             } # if
318             # Because $oReal is a global variable, force creation of a new
319             # object into a new variable:
320 0           my $oRealNS = $sClass->new;
321 0 0         unless (ref $oRealNS)
322             {
323 0           _add_error qq{ EEE can not create an empty $sClass object.\n};
324 0           return undef;
325             } # unless
326 0           print STDERR " + created oRealNS ==$oRealNS==...\n" if DEBUG_GET;
327 0           $oReal = $oRealNS;
328             # This is a dummy object that we use to find the appropriate
329             # cookies:
330 0           my $oDummy = $sClass->new(
331             File => $sFnameCookies,
332             'delayload' => 1,
333             );
334 0 0         unless (ref $oDummy)
335             {
336 0           _add_error qq{ EEE can not create an empty $sClass object.\n};
337 0           return undef;
338             } # unless
339 0           print STDERR " + created oDummy ==$oDummy==...\n" if DEBUG_GET;
340 0 0         $oDummy->scan($rcCallback) if ref($oDummy);
341 0           print STDERR " + return oReal ==$oReal==...\n" if DEBUG_GET;
342 0           return $oReal;
343             } # _get_cookies
344              
345              
346             sub _callback_msie
347             {
348 0     0     my ($version,
349             $key, $val,
350             $path, $domain, $port, $path_spec,
351             $secure, $expires, $discard, $hash) = @_;
352             # All we care about at this level is the filename, which is in the
353             # $val slot:
354 0           print STDERR " + consider cookie, val==$val==\n" if (DEBUG_NEW);
355 0 0         return unless ($val =~ m!\@.*$sHostGlobal!i);
356 0           print STDERR " + matches host ($sHostGlobal)\n" if (1 < DEBUG_NEW);
357 0 0         return unless ($val =~ m!\\$sUser\@!);
358 0           print STDERR " + matches user ($sUser)\n" if (1 < DEBUG_NEW);
359             # This cookie file matches the user and host. Add it to the cookies
360             # we'll keep:
361 0           $oReal->load_cookie($val);
362             } # _callback_msie
363              
364             sub _callback_mozilla
365             {
366             # print STDERR " + _callback got a cookie: ", Dumper(\@_);
367             # return;
368             # my ($version,
369             # $key, $val,
370             # $path, $domain, $port, $path_spec,
371             # $secure, $expires, $discard, $hash) = @_;
372 0     0     my $sDomain = $_[4];
373 0           print STDERR " + consider cookie from domain ($sDomain), want host ($sHostGlobal)...\n" if DEBUG_NEW;
374 0 0 0       return if (($sHostGlobal ne '') && ($sDomain !~ m!$sHostGlobal!i));
375 0           print STDERR " + domain ($sDomain) matches host ($sHostGlobal)\n" if DEBUG_NEW;
376 0           $oReal->set_cookie(@_);
377             } # _callback_mozilla
378              
379             1;
380              
381             __END__