File Coverage

blib/lib/HTTP/Cookies/Find.pm
Criterion Covered Total %
statement 79 198 39.9
branch 11 82 13.4
condition 3 20 15.0
subroutine 20 22 90.9
pod 2 2 100.0
total 115 324 35.4


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