1 package HTTP::Cookies::Microsoft;
5 use vars qw(@ISA $VERSION);
10 @ISA=qw(HTTP::Cookies);
12 sub load_cookies_from_file
16 my ($key, $value, $domain_path, $flags, $lo_expire, $hi_expire);
17 my ($lo_create, $hi_create, $sep);
19 open(COOKIES, $file) || return;
21 while ($key = <COOKIES>)
24 chomp($value = <COOKIES>);
25 chomp($domain_path= <COOKIES>);
26 chomp($flags = <COOKIES>); # 0x0001 bit is for secure
27 chomp($lo_expire = <COOKIES>);
28 chomp($hi_expire = <COOKIES>);
29 chomp($lo_create = <COOKIES>);
30 chomp($hi_create = <COOKIES>);
31 chomp($sep = <COOKIES>);
33 if (!defined($key) || !defined($value) || !defined($domain_path) ||
34 !defined($flags) || !defined($hi_expire) || !defined($lo_expire) ||
35 !defined($hi_create) || !defined($lo_create) || !defined($sep) ||
41 if ($domain_path =~ /^([^\/]+)(\/.*)$/)
46 push(@cookies, {KEY => $key, VALUE => $value, DOMAIN => $domain,
47 PATH => $path, FLAGS =>$flags, HIXP =>$hi_expire,
48 LOXP => $lo_expire, HICREATE => $hi_create,
49 LOCREATE => $lo_create});
60 my $user = lc(Win32::LoginName());
65 # MSIE stores create and expire times as Win32 FILETIME,
66 # which is 64 bits of 100 nanosecond intervals since Jan 01 1601
68 # But Cookies code expects time in 32-bit value expressed
69 # in seconds since Jan 01 1970
71 sub epoch_time_offset_from_win32_filetime
73 my ($high, $low) = @_;
75 #--------------------------------------------------------
77 #--------------------------------------------------------
78 # 0x019db1de 0xd53e8000 is 1970 Jan 01 00:00:00 in Win32 FILETIME
80 # 100 nanosecond intervals == 0.1 microsecond intervals
82 my $filetime_low32_1970 = 0xd53e8000;
83 my $filetime_high32_1970 = 0x019db1de;
85 #------------------------------------
87 #------------------------------------
88 # To go from 100 nanosecond intervals to seconds since 00:00 Jan 01 1970:
90 # 1. Adjust 100 nanosecond intervals to Jan 01 1970 base
91 # 2. Divide by 10 to get to microseconds (1/millionth second)
92 # 3. Divide by 1000000 (10 ^ 6) to get to seconds
94 # We can combine Step 2 & 3 into one divide.
96 # After much trial and error, I came up with the following code which
97 # avoids using Math::BigInt or floating pt, but still gives correct answers
99 # If the filetime is before the epoch, return 0
100 if (($high < $filetime_high32_1970) ||
101 (($high == $filetime_high32_1970) && ($low < $filetime_low32_1970)))
106 # Can't multiply by 0x100000000, (1 << 32),
107 # without Perl issuing an integer overflow warning
109 # So use two multiplies by 0x10000 instead of one multiply by 0x100000000
111 # The result is the same.
113 my $date1970 = (($filetime_high32_1970 * 0x10000) * 0x10000) + $filetime_low32_1970;
114 my $time = (($high * 0x10000) * 0x10000) + $low;
124 my($self, $file) = @_;
125 my $now = time() - $HTTP::Cookies::EPOCH_OFFSET;
130 # open the cookie file and get the data
131 $cookie_data = load_cookies_from_file($file);
133 foreach my $cookie (@{$cookie_data})
135 my $secure = ($cookie->{FLAGS} & 1) != 0;
136 my $expires = epoch_time_offset_from_win32_filetime($cookie->{HIXP}, $cookie->{LOXP});
138 $self->set_cookie(undef, $cookie->{KEY}, $cookie->{VALUE},
139 $cookie->{PATH}, $cookie->{DOMAIN}, undef,
140 0, $secure, $expires-$now, 0);
147 my($self, $cookie_index) = @_;
148 my $now = time() - $HTTP::Cookies::EPOCH_OFFSET;
150 my $delay_load = (defined($self->{'delayload'}) && $self->{'delayload'});
151 my $user_name = get_user_name();
154 $cookie_index ||= $self->{'file'} || return;
155 if ($cookie_index =~ /[\\\/][^\\\/]+$/)
157 $cookie_dir = $` . "\\";
162 open(INDEX, $cookie_index) || return;
164 if (256 != read(INDEX, $data, 256))
166 warn "$cookie_index file is not large enough";
171 # Cookies' index.dat file starts with 32 bytes of signature
172 # followed by an offset to the first record, stored as a little-endian DWORD
173 my ($sig, $size) = unpack('a32 V', $data);
175 if (($sig !~ /^Client UrlCache MMF Ver 5\.2/) || # check that sig is valid (only tested in IE6.0)
178 warn "$cookie_index ['$sig' $size] does not seem to contain cookies";
183 if (0 == seek(INDEX, $size, 0)) # move the file ptr to start of the first record
189 # Cookies are usually stored in 'URL ' records in two contiguous 0x80 byte sectors (256 bytes)
190 # so read in two 0x80 byte sectors and adjust if not a Cookie.
191 while (256 == read(INDEX, $data, 256))
193 # each record starts with a 4-byte signature
194 # and a count (little-endian DWORD) of 0x80 byte sectors for the record
195 ($sig, $size) = unpack('a4 V', $data);
197 # Cookies are found in 'URL ' records
200 # skip over uninteresting record: I've seen 'HASH' and 'LEAK' records
201 if (($sig eq 'HASH') || ($sig eq 'LEAK'))
203 # '-2' takes into account the two 0x80 byte sectors we've just read in
204 if (($size > 0) && ($size != 2))
206 if (0 == seek(INDEX, ($size-2)*0x80, 1))
208 # Seek failed. Something's wrong. Gonna stop.
216 #$REMOVE Need to check if URL records in Cookies' index.dat will
217 # ever use more than two 0x80 byte sectors
220 my $more_data = ($size-2)*0x80;
222 if ($more_data != read(INDEX, $data, $more_data, 256))
228 (my $user_name2 = $user_name) =~ s/ /_/g;
229 if ($data =~ /Cookie\:\Q$user_name\E\@([\x21-\xFF]+).*?((?:\Q$user_name\E|\Q$user_name2\E)\@[\x21-\xFF]+\.txt)/)
231 my $cookie_file = $cookie_dir . $2; # form full pathname
235 $self->load_cookie($cookie_file);
241 # grab only the domain name, drop everything from the first dir sep on
242 if ($domain =~ m{[\\/]})
247 # set the delayload cookie for this domain with
248 # the cookie_file as cookie for later-loading info
249 $self->set_cookie(undef, 'cookie', $cookie_file,
250 '//+delayload', $domain, undef,
251 0, 0, $now+86400, 0);
267 HTTP::Cookies::Microsoft - access to Microsoft cookies files
272 use HTTP::Cookies::Microsoft;
273 use Win32::TieRegistry(Delimiter => "/");
274 my $cookies_dir = $Registry->
275 {"CUser/Software/Microsoft/Windows/CurrentVersion/Explorer/Shell Folders/Cookies"};
277 $cookie_jar = HTTP::Cookies::Microsoft->new(
278 file => "$cookies_dir\\index.dat",
281 my $browser = LWP::UserAgent->new;
282 $browser->cookie_jar( $cookie_jar );
286 This is a subclass of C<HTTP::Cookies> which
287 loads Microsoft Internet Explorer 5.x and 6.x for Windows (MSIE)
290 See the documentation for L<HTTP::Cookies>.
294 The following methods are provided:
298 =item $cookie_jar = HTTP::Cookies::Microsoft->new;
300 The constructor takes hash style parameters. In addition
301 to the regular HTTP::Cookies parameters, HTTP::Cookies::Microsoft
302 recognizes the following:
304 delayload: delay loading of cookie data until a request
305 is actually made. This results in faster
306 runtime unless you use most of the cookies
307 since only the domain's cookie data
314 Please note that the code DOESN'T support saving to the MSIE
319 Johnny Lee <typo_pl@hotmail.com>
323 Copyright 2002 Johnny Lee
325 This library is free software; you can redistribute it and/or
326 modify it under the same terms as Perl itself.