File Coverage

blib/lib/HTTP/Cookies/Find.pm
Criterion Covered Total %
statement 78 193 40.4
branch 11 82 13.4
condition 2 15 13.3
subroutine 20 22 90.9
pod 2 2 100.0
total 113 314 35.9


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