line  
 stmt  
 bran  
 cond  
 sub  
 pod  
 time  
 code  
 
1 
 
  
 
   
 
 
 
 
 
 
 
 
 
 
 
 package Net::OpenID::Common;  
 
2 
 
 
 
 
 
 
 
 
 
 
 
 
 
 $Net::OpenID::Common::VERSION = '1.20';  
 
3 
 
 
 
 
 
 
 
 
 
 
 
 
 
 =head1 NAME  
 
4 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
5 
 
 
 
 
 
 
 
 
 
 
 
 
 
 Net::OpenID::Common - Libraries shared between Net::OpenID::Consumer and Net::OpenID::Server  
 
6 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
7 
 
 
 
 
 
 
 
 
 
 
 
 
 
 =head1 VERSION  
 
8 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
9 
 
 
 
 
 
 
 
 
 
 
 
 
 
 version 1.20  
 
10 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
11 
 
 
 
 
 
 
 
 
 
 
 
 
 
 =head1 DESCRIPTION  
 
12 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
13 
 
 
 
 
 
 
 
 
 
 
 
 
 
 The Consumer and Server implementations share a few libraries which live with this module. This module is here largely to hold the version number and this documentation, though it also incorporates some utility functions inherited from previous versions of L.   
 
14 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
15 
 
 
 
 
 
 
 
 
 
 
 
 
 
 =head1 COPYRIGHT  
 
16 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
17 
 
 
 
 
 
 
 
 
 
 
 
 
 
 This package is Copyright (c) 2005 Brad Fitzpatrick, and (c) 2008 Martin Atkins. All rights reserved.  
 
18 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
19 
 
 
 
 
 
 
 
 
 
 
 
 
 
 You may distribute under the terms of either the GNU General Public License or the Artistic License, as specified in the Perl README file. If you need more liberal licensing terms, please contact the maintainer.  
 
20 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
21 
 
 
 
 
 
 
 
 
 
 
 
 
 
 =head1 AUTHORS  
 
22 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
23 
 
 
 
 
 
 
 
 
 
 
 
 
 
 Brad Fitzpatrick    
 
24 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
25 
 
 
 
 
 
 
 
 
 
 
 
 
 
 Tatsuhiko Miyagawa    
 
26 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
27 
 
 
 
 
 
 
 
 
 
 
 
 
 
 Martin Atkins    
 
28 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
29 
 
 
 
 
 
 
 
 
 
 
 
 
 
 Robert Norris    
 
30 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
31 
 
 
 
 
 
 
 
 
 
 
 
 
 
 Roger Crew    
 
32 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
33 
 
 
 
 
 
 
 
 
 
 
 
 
 
 =head1 MAINTAINER  
 
34 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
35 
 
 
 
 
 
 
 
 
 
 
 
 
 
 Maintained by Roger Crew    
 
36 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
37 
 
 
 
 
 
 
 
 
 
 
 
 
 
 =cut  
 
38 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
39 
 
 
 
 
 
 
 
 
 
 
 
 
 
 # This package should totally be called Net::OpenID::util, but  
 
40 
 
 
 
 
 
 
 
 
 
 
 
 
 
 # it was historically named wrong so we're just leaving it  
 
41 
 
 
 
 
 
 
 
 
 
 
 
 
 
 # like this to avoid confusion.  
 
42 
 
 
 
 
 
 
 
 
 
 
 
 
 
 package OpenID::util;  
 
43 
 
 
 
 
 
 
 
 
 
 
 
 
 
 $OpenID::util::VERSION = '1.20';  
 
44 
 
7
 
 
 
 
 
  
7
   
 
 
 
57262
 
 use Crypt::DH::GMP;  
 
  
 
  
0
   
 
 
 
 
 
 
 
 
 
 
 
    
 
  
 
0
 
 
 
 
 
 
 
 
 
 
 
    
 
45 
 
 
 
 
 
 
 
 
 
 
 
 
 
 use Math::BigInt;  
 
46 
 
 
 
 
 
 
 
 
 
 
 
 
 
 use Time::Local ();  
 
47 
 
 
 
 
 
 
 
 
 
 
 
 
 
 use MIME::Base64 ();  
 
48 
 
 
 
 
 
 
 
 
 
 
 
 
 
 use URI::Escape ();  
 
49 
 
 
 
 
 
 
 
 
 
 
 
 
 
 use HTML::Parser ();  
 
50 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
51 
 
 
 
 
 
 
 
 
 
 
 
 
 
 use constant VERSION_1_NAMESPACE => "http://openid.net/signon/1.1";  
 
52 
 
 
 
 
 
 
 
 
 
 
 
 
 
 use constant VERSION_2_NAMESPACE => "http://specs.openid.net/auth/2.0";  
 
53 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
54 
 
 
 
 
 
 
 
 
 
 
 
 
 
 # I guess this is a bit daft since constants are subs anyway,  
 
55 
 
 
 
 
 
 
 
 
 
 
 
 
 
 # but whatever.  
 
56 
 
 
 
 
 
 
 
 
 
 
 
 
 
 sub version_1_namespace {  
 
57 
 
 
 
 
 
 
 
 
 
 
 
 
 
     return VERSION_1_NAMESPACE;  
 
58 
 
 
 
 
 
 
 
 
 
 
 
 
 
 }  
 
59 
 
 
 
 
 
 
 
 
 
 
 
 
 
 sub version_2_namespace {  
 
60 
 
 
 
 
 
 
 
 
 
 
 
 
 
     return VERSION_2_NAMESPACE;  
 
61 
 
 
 
 
 
 
 
 
 
 
 
 
 
 }  
 
62 
 
 
 
 
 
 
 
 
 
 
 
 
 
 sub version_1_xrds_service_url {  
 
63 
 
 
 
 
 
 
 
 
 
 
 
 
 
     return VERSION_1_NAMESPACE;  
 
64 
 
 
 
 
 
 
 
 
 
 
 
 
 
 }  
 
65 
 
 
 
 
 
 
 
 
 
 
 
 
 
 sub version_2_xrds_service_url {  
 
66 
 
 
 
 
 
 
 
 
 
 
 
 
 
     return "http://specs.openid.net/auth/2.0/signon";  
 
67 
 
 
 
 
 
 
 
 
 
 
 
 
 
 }  
 
68 
 
 
 
 
 
 
 
 
 
 
 
 
 
 sub version_2_xrds_directed_service_url {  
 
69 
 
 
 
 
 
 
 
 
 
 
 
 
 
     return "http://specs.openid.net/auth/2.0/server";  
 
70 
 
 
 
 
 
 
 
 
 
 
 
 
 
 }  
 
71 
 
 
 
 
 
 
 
 
 
 
 
 
 
 sub version_2_identifier_select_url {  
 
72 
 
 
 
 
 
 
 
 
 
 
 
 
 
     return "http://specs.openid.net/auth/2.0/identifier_select";  
 
73 
 
 
 
 
 
 
 
 
 
 
 
 
 
 }  
 
74 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
75 
 
 
 
 
 
 
 
 
 
 
 
 
 
 sub parse_keyvalue {  
 
76 
 
 
 
 
 
 
 
 
 
 
 
 
 
     my $reply = shift;  
 
77 
 
 
 
 
 
 
 
 
 
 
 
 
 
     my %ret;  
 
78 
 
 
 
 
 
 
 
 
 
 
 
 
 
     $reply =~ s/\r//g;  
 
79 
 
 
 
 
 
 
 
 
 
 
 
 
 
     foreach (split /\n/, $reply) {  
 
80 
 
 
 
 
 
 
 
 
 
 
 
 
 
         next unless /^(\S+?):(.*)/;  
 
81 
 
 
 
 
 
 
 
 
 
 
 
 
 
         $ret{$1} = $2;  
 
82 
 
 
 
 
 
 
 
 
 
 
 
 
 
     }  
 
83 
 
 
 
 
 
 
 
 
 
 
 
 
 
     return %ret;  
 
84 
 
 
 
 
 
 
 
 
 
 
 
 
 
 }  
 
85 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
86 
 
 
 
 
 
 
 
 
 
 
 
 
 
 sub eurl  
 
87 
 
 
 
 
 
 
 
 
 
 
 
 
 
 {  
 
88 
 
 
 
 
 
 
 
 
 
 
 
 
 
     my $a = $_[0];  
 
89 
 
 
 
 
 
 
 
 
 
 
 
 
 
     $a =~ s/([^a-zA-Z0-9_\,\-.\/\\\: ])/uc sprintf("%%%02x",ord($1))/eg;  
 
90 
 
 
 
 
 
 
 
 
 
 
 
 
 
     $a =~ tr/ /+/;  
 
91 
 
 
 
 
 
 
 
 
 
 
 
 
 
     return $a;  
 
92 
 
 
 
 
 
 
 
 
 
 
 
 
 
 }  
 
93 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
94 
 
 
 
 
 
 
 
 
 
 
 
 
 
 sub push_url_arg {  
 
95 
 
 
 
 
 
 
 
 
 
 
 
 
 
     my $uref = shift;  
 
96 
 
 
 
 
 
 
 
 
 
 
 
 
 
     $$uref =~ s/[&?]$//;  
 
97 
 
 
 
 
 
 
 
 
 
 
 
 
 
     my $got_qmark = ($$uref =~ /\?/);  
 
98 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
99 
 
 
 
 
 
 
 
 
 
 
 
 
 
     while (@_) {  
 
100 
 
 
 
 
 
 
 
 
 
 
 
 
 
         my $key = shift;  
 
101 
 
 
 
 
 
 
 
 
 
 
 
 
 
         my $value = shift;  
 
102 
 
 
 
 
 
 
 
 
 
 
 
 
 
         $$uref .= $got_qmark ? "&" : ($got_qmark = 1, "?");  
 
103 
 
 
 
 
 
 
 
 
 
 
 
 
 
         $$uref .= URI::Escape::uri_escape($key) . "=" . URI::Escape::uri_escape($value);  
 
104 
 
 
 
 
 
 
 
 
 
 
 
 
 
     }  
 
105 
 
 
 
 
 
 
 
 
 
 
 
 
 
 }  
 
106 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
107 
 
 
 
 
 
 
 
 
 
 
 
 
 
 sub push_openid2_url_arg {  
 
108 
 
 
 
 
 
 
 
 
 
 
 
 
 
     my $uref = shift;  
 
109 
 
 
 
 
 
 
 
 
 
 
 
 
 
     my %args = @_;  
 
110 
 
 
 
 
 
 
 
 
 
 
 
 
 
     push_url_arg($uref,  
 
111 
 
 
 
 
 
 
 
 
 
 
 
 
 
         'openid.ns' => VERSION_2_NAMESPACE,  
 
112 
 
 
 
 
 
 
 
 
 
 
 
 
 
         map {  
 
113 
 
 
 
 
 
 
 
 
 
 
 
 
 
             'openid.'.$_ => $args{$_}  
 
114 
 
 
 
 
 
 
 
 
 
 
 
 
 
         } keys %args,  
 
115 
 
 
 
 
 
 
 
 
 
 
 
 
 
     );  
 
116 
 
 
 
 
 
 
 
 
 
 
 
 
 
 }  
 
117 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
118 
 
 
 
 
 
 
 
 
 
 
 
 
 
 sub time_to_w3c {  
 
119 
 
 
 
 
 
 
 
 
 
 
 
 
 
     my $time = shift || time();  
 
120 
 
 
 
 
 
 
 
 
 
 
 
 
 
     my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = gmtime($time);  
 
121 
 
 
 
 
 
 
 
 
 
 
 
 
 
     $mon++;  
 
122 
 
 
 
 
 
 
 
 
 
 
 
 
 
     $year += 1900;  
 
123 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
124 
 
 
 
 
 
 
 
 
 
 
 
 
 
     return sprintf("%04d-%02d-%02dT%02d:%02d:%02dZ",  
 
125 
 
 
 
 
 
 
 
 
 
 
 
 
 
                    $year, $mon, $mday,  
 
126 
 
 
 
 
 
 
 
 
 
 
 
 
 
                    $hour, $min, $sec);  
 
127 
 
 
 
 
 
 
 
 
 
 
 
 
 
 }  
 
128 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
129 
 
 
 
 
 
 
 
 
 
 
 
 
 
 sub w3c_to_time {  
 
130 
 
 
 
 
 
 
 
 
 
 
 
 
 
     my $hms = shift;  
 
131 
 
 
 
 
 
 
 
 
 
 
 
 
 
     return 0 unless  
 
132 
 
 
 
 
 
 
 
 
 
 
 
 
 
         $hms =~ /^(\d{4,4})-(\d\d)-(\d\d)T(\d\d):(\d\d):(\d\d)Z$/;  
 
133 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
134 
 
 
 
 
 
 
 
 
 
 
 
 
 
     my $time;  
 
135 
 
 
 
 
 
 
 
 
 
 
 
 
 
     eval {  
 
136 
 
 
 
 
 
 
 
 
 
 
 
 
 
         $time = Time::Local::timegm($6, $5, $4, $3, $2 - 1, $1);  
 
137 
 
 
 
 
 
 
 
 
 
 
 
 
 
     };  
 
138 
 
 
 
 
 
 
 
 
 
 
 
 
 
     return 0 if $@;  
 
139 
 
 
 
 
 
 
 
 
 
 
 
 
 
     return $time;  
 
140 
 
 
 
 
 
 
 
 
 
 
 
 
 
 }  
 
141 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
142 
 
 
 
 
 
 
 
 
 
 
 
 
 
 sub int2bytes {  
 
143 
 
 
 
 
 
 
 
 
 
 
 
 
 
     my ($int) = @_;  
 
144 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
145 
 
 
 
 
 
 
 
 
 
 
 
 
 
     my $bigint = Math::BigInt->new($int);  
 
146 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
147 
 
 
 
 
 
 
 
 
 
 
 
 
 
     die "Can't deal with negative numbers" if $bigint->is_negative;  
 
148 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
149 
 
 
 
 
 
 
 
 
 
 
 
 
 
     my $bits = $bigint->as_bin;  
 
150 
 
 
 
 
 
 
 
 
 
 
 
 
 
     die unless $bits =~ s/^0b//;  
 
151 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
152 
 
 
 
 
 
 
 
 
 
 
 
 
 
     # prepend zeros to round to byte boundary, or to unset high bit  
 
153 
 
 
 
 
 
 
 
 
 
 
 
 
 
     my $prepend = (8 - length($bits) % 8) || ($bits =~ /^1/ ? 8 : 0);  
 
154 
 
 
 
 
 
 
 
 
 
 
 
 
 
     $bits = ("0" x $prepend) . $bits if $prepend;  
 
155 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
156 
 
 
 
 
 
 
 
 
 
 
 
 
 
     return pack("B*", $bits);  
 
157 
 
 
 
 
 
 
 
 
 
 
 
 
 
 }  
 
158 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
159 
 
 
 
 
 
 
 
 
 
 
 
 
 
 sub int2arg {  
 
160 
 
 
 
 
 
 
 
 
 
 
 
 
 
     return b64(int2bytes($_[0]));  
 
161 
 
 
 
 
 
 
 
 
 
 
 
 
 
 }  
 
162 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
163 
 
 
 
 
 
 
 
 
 
 
 
 
 
 sub b64 {  
 
164 
 
 
 
 
 
 
 
 
 
 
 
 
 
     my $val = MIME::Base64::encode_base64($_[0]);  
 
165 
 
 
 
 
 
 
 
 
 
 
 
 
 
     $val =~ s/\s+//g;  
 
166 
 
 
 
 
 
 
 
 
 
 
 
 
 
     return $val;  
 
167 
 
 
 
 
 
 
 
 
 
 
 
 
 
 }  
 
168 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
169 
 
 
 
 
 
 
 
 
 
 
 
 
 
 sub d64 {  
 
170 
 
 
 
 
 
 
 
 
 
 
 
 
 
     return MIME::Base64::decode_base64($_[0]);  
 
171 
 
 
 
 
 
 
 
 
 
 
 
 
 
 }  
 
172 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
173 
 
 
 
 
 
 
 
 
 
 
 
 
 
 sub bytes2int {  
 
174 
 
 
 
 
 
 
 
 
 
 
 
 
 
     return Math::BigInt->new("0b" . unpack("B*", $_[0]))->bstr;  
 
175 
 
 
 
 
 
 
 
 
 
 
 
 
 
 }  
 
176 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
177 
 
 
 
 
 
 
 
 
 
 
 
 
 
 sub arg2int {  
 
178 
 
 
 
 
 
 
 
 
 
 
 
 
 
     my ($arg) = @_;  
 
179 
 
 
 
 
 
 
 
 
 
 
 
 
 
     return undef unless defined $arg and $arg ne "";  
 
180 
 
 
 
 
 
 
 
 
 
 
 
 
 
     # don't accept base-64 encoded numbers over 700 bytes.  which means  
 
181 
 
 
 
 
 
 
 
 
 
 
 
 
 
     # those over 4200 bits.  
 
182 
 
 
 
 
 
 
 
 
 
 
 
 
 
     return 0 if length($arg) > 700;  
 
183 
 
 
 
 
 
 
 
 
 
 
 
 
 
     return bytes2int(MIME::Base64::decode_base64($arg));  
 
184 
 
 
 
 
 
 
 
 
 
 
 
 
 
 }  
 
185 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
186 
 
 
 
 
 
 
 
 
 
 
 
 
 
 sub timing_indep_eq {  
 
187 
 
 
 
 
 
 
 
 
 
 
 
 
 
     no warnings 'uninitialized';  
 
188 
 
 
 
 
 
 
 
 
 
 
 
 
 
     my ($x, $y)=@_;  
 
189 
 
 
 
 
 
 
 
 
 
 
 
 
 
     warnings::warn('uninitialized','Use of uninitialized value in timing_indep_eq')  
 
190 
 
 
 
 
 
 
 
 
 
 
 
 
 
 	if (warnings::enabled('uninitialized') && !(defined($x) && defined($y)));  
 
191 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
192 
 
 
 
 
 
 
 
 
 
 
 
 
 
     return '' if length($x)!=length($y);  
 
193 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
194 
 
 
 
 
 
 
 
 
 
 
 
 
 
     my $n=length($x);  
 
195 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
196 
 
 
 
 
 
 
 
 
 
 
 
 
 
     my $result=0;  
 
197 
 
 
 
 
 
 
 
 
 
 
 
 
 
     for (my $i=0; $i<$n; $i++) {  
 
198 
 
 
 
 
 
 
 
 
 
 
 
 
 
         $result |= ord(substr($x, $i, 1)) ^ ord(substr($y, $i, 1));  
 
199 
 
 
 
 
 
 
 
 
 
 
 
 
 
     }  
 
200 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
201 
 
 
 
 
 
 
 
 
 
 
 
 
 
     return !$result;  
 
202 
 
 
 
 
 
 
 
 
 
 
 
 
 
 }  
 
203 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
204 
 
 
 
 
 
 
 
 
 
 
 
 
 
 sub get_dh {  
 
205 
 
 
 
 
 
 
 
 
 
 
 
 
 
     my ($p, $g) = @_;  
 
206 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
207 
 
 
 
 
 
 
 
 
 
 
 
 
 
     $p ||= "155172898181473697471232257763715539915724801966915404479707795314057629378541917580651227423698188993727816152646631438561595825688188889951272158842675419950341258706556549803580104870537681476726513255747040765857479291291572334510643245094715007229621094194349783925984760375594985848253359305585439638443";  
 
208 
 
 
 
 
 
 
 
 
 
 
 
 
 
     $g ||= "2";  
 
209 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
210 
 
 
 
 
 
 
 
 
 
 
 
 
 
     return if $p <= 10 or $g <= 1;  
 
211 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
212 
 
 
 
 
 
 
 
 
 
 
 
 
 
     my $dh = Crypt::DH::GMP->new(p => $p, g => $g);  
 
213 
 
 
 
 
 
 
 
 
 
 
 
 
 
     $dh->generate_keys;  
 
214 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
215 
 
 
 
 
 
 
 
 
 
 
 
 
 
     return $dh;  
 
216 
 
 
 
 
 
 
 
 
 
 
 
 
 
 }  
 
217 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
218 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
219 
 
 
 
 
 
 
 
 
 
 
 
 
 
 ################################################################  
 
220 
 
 
 
 
 
 
 
 
 
 
 
 
 
 # HTML parsing  
 
221 
 
 
 
 
 
 
 
 
 
 
 
 
 
 #  
 
222 
 
 
 
 
 
 
 
 
 
 
 
 
 
 # This is a stripped-down version of HTML::HeadParser  
 
223 
 
 
 
 
 
 
 
 
 
 
 
 
 
 # that only recognizes  
 
224 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
225 
 
 
 
 
 
 
 
 
 
 
 
 
 
 our @_linkmeta_parser_options =  
 
226 
 
 
 
 
 
 
 
 
 
 
 
 
 
   (  
 
227 
 
 
 
 
 
 
 
 
 
 
 
 
 
    api_version => 3,  
 
228 
 
 
 
 
 
 
 
 
 
 
 
 
 
    ignore_elements => [qw(script style base isindex command noscript title object)],  
 
229 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
230 
 
 
 
 
 
 
 
 
 
 
 
 
 
    start_document_h  
 
231 
 
 
 
 
 
 
 
 
 
 
 
 
 
    => [sub {  
 
232 
 
 
 
 
 
 
 
 
 
 
 
 
 
            my($p) = @_;  
 
233 
 
 
 
 
 
 
 
 
 
 
 
 
 
            $p->{first_chunk} = 0;  
 
234 
 
 
 
 
 
 
 
 
 
 
 
 
 
            $p->{found} = {};  
 
235 
 
 
 
 
 
 
 
 
 
 
 
 
 
        },  
 
236 
 
 
 
 
 
 
 
 
 
 
 
 
 
        "self"],  
 
237 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
238 
 
 
 
 
 
 
 
 
 
 
 
 
 
    end_h  
 
239 
 
 
 
 
 
 
 
 
 
 
 
 
 
    => [sub {  
 
240 
 
 
 
 
 
 
 
 
 
 
 
 
 
            my($p,$tag) = @_;  
 
241 
 
 
 
 
 
 
 
 
 
 
 
 
 
            $p->eof if $tag eq 'head'  
 
242 
 
 
 
 
 
 
 
 
 
 
 
 
 
        },  
 
243 
 
 
 
 
 
 
 
 
 
 
 
 
 
        "self,tagname"],  
 
244 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
245 
 
 
 
 
 
 
 
 
 
 
 
 
 
    start_h  
 
246 
 
 
 
 
 
 
 
 
 
 
 
 
 
    => [sub {  
 
247 
 
 
 
 
 
 
 
 
 
 
 
 
 
            my($p, $tag, $attr) = @_;  
 
248 
 
 
 
 
 
 
 
 
 
 
 
 
 
            if ($tag eq 'meta' || $tag eq 'link') {  
 
249 
 
 
 
 
 
 
 
 
 
 
 
 
 
                if ($tag eq 'link' && ($attr->{rel}||'') =~ m/\s/) {  
 
250 
 
 
 
 
 
 
 
 
 
 
 
 
 
                    # split  
 
251 
 
 
 
 
 
 
 
 
 
 
 
 
 
                    # into multiple  
 
252 
 
 
 
 
 
 
 
 
 
 
 
 
 
                    push @{$p->{found}->{$tag}},  
 
253 
 
 
 
 
 
 
 
 
 
 
 
 
 
                      map { +{%{$attr}, rel => $_} }  
 
254 
 
 
 
 
 
 
 
 
 
 
 
 
 
                        split /\s+/,$attr->{rel};  
 
255 
 
 
 
 
 
 
 
 
 
 
 
 
 
                }  
 
256 
 
 
 
 
 
 
 
 
 
 
 
 
 
                else {  
 
257 
 
 
 
 
 
 
 
 
 
 
 
 
 
                    push @{$p->{found}->{$tag}}, $attr;  
 
258 
 
 
 
 
 
 
 
 
 
 
 
 
 
                }  
 
259 
 
 
 
 
 
 
 
 
 
 
 
 
 
            }  
 
260 
 
 
 
 
 
 
 
 
 
 
 
 
 
            elsif ($tag ne 'head' && $tag ne 'html') {  
 
261 
 
 
 
 
 
 
 
 
 
 
 
 
 
                # stop parsing  
 
262 
 
 
 
 
 
 
 
 
 
 
 
 
 
                $p->eof;  
 
263 
 
 
 
 
 
 
 
 
 
 
 
 
 
            }  
 
264 
 
 
 
 
 
 
 
 
 
 
 
 
 
        },  
 
265 
 
 
 
 
 
 
 
 
 
 
 
 
 
        "self,tagname,attr"],  
 
266 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
267 
 
 
 
 
 
 
 
 
 
 
 
 
 
    text_h  
 
268 
 
 
 
 
 
 
 
 
 
 
 
 
 
    => [sub {  
 
269 
 
 
 
 
 
 
 
 
 
 
 
 
 
            my($p, $text) = @_;  
 
270 
 
 
 
 
 
 
 
 
 
 
 
 
 
            unless ($p->{first_chunk}) {  
 
271 
 
 
 
 
 
 
 
 
 
 
 
 
 
                # drop Unicode BOM if found  
 
272 
 
 
 
 
 
 
 
 
 
 
 
 
 
                if ($p->utf8_mode) {  
 
273 
 
 
 
 
 
 
 
 
 
 
 
 
 
                    $text =~ s/^\xEF\xBB\xBF//;  
 
274 
 
 
 
 
 
 
 
 
 
 
 
 
 
                }  
 
275 
 
 
 
 
 
 
 
 
 
 
 
 
 
                else {  
 
276 
 
 
 
 
 
 
 
 
 
 
 
 
 
                    $text =~ s/^\x{FEFF}//;  
 
277 
 
 
 
 
 
 
 
 
 
 
 
 
 
                }  
 
278 
 
 
 
 
 
 
 
 
 
 
 
 
 
                $p->{first_chunk}++;  
 
279 
 
 
 
 
 
 
 
 
 
 
 
 
 
            }  
 
280 
 
 
 
 
 
 
 
 
 
 
 
 
 
            # Normal text outside of an allowed  tag  
 
281 
 
 
 
 
 
 
 
 
 
 
 
 
 
            # means start of body  
 
282 
 
 
 
 
 
 
 
 
 
 
 
 
 
            $p->eof if ($text =~ /\S/);  
 
283 
 
 
 
 
 
 
 
 
 
 
 
 
 
        },  
 
284 
 
 
 
 
 
 
 
 
 
 
 
 
 
        "self,text"],  
 
285 
 
 
 
 
 
 
 
 
 
 
 
 
 
   );  
 
286 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
287 
 
 
 
 
 
 
 
 
 
 
 
 
 
 # XXX this line is also in HTML::HeadParser; do we need it?  
 
288 
 
 
 
 
 
 
 
 
 
 
 
 
 
 # current theory is we don't because we're requiring at  
 
289 
 
 
 
 
 
 
 
 
 
 
 
 
 
 # least version 3.40 which is already pretty ancient.  
 
290 
 
 
 
 
 
 
 
 
 
 
 
 
 
 #   
 
291 
 
 
 
 
 
 
 
 
 
 
 
 
 
 # *utf8_mode = sub { 1 } unless HTML::Entities::UNICODE_SUPPORT;  
 
292 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
293 
 
 
 
 
 
 
 
 
 
 
 
 
 
 our $_linkmeta_parser;  
 
294 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
295 
 
 
 
 
 
 
 
 
 
 
 
 
 
 # return { link => [links...], meta => [metas...] }  
 
296 
 
 
 
 
 
 
 
 
 
 
 
 
 
 # where each link/meta is a hash of the attribute values  
 
297 
 
 
 
 
 
 
 
 
 
 
 
 
 
 sub html_extract_linkmetas {  
 
298 
 
 
 
 
 
 
 
 
 
 
 
 
 
     my $doc = shift;  
 
299 
 
 
 
 
 
 
 
 
 
 
 
 
 
     $_linkmeta_parser ||= HTML::Parser->new(@_linkmeta_parser_options);  
 
300 
 
 
 
 
 
 
 
 
 
 
 
 
 
     $_linkmeta_parser->parse($doc);  
 
301 
 
 
 
 
 
 
 
 
 
 
 
 
 
     $_linkmeta_parser->eof;  
 
302 
 
 
 
 
 
 
 
 
 
 
 
 
 
     return delete $_linkmeta_parser->{found};  
 
303 
 
 
 
 
 
 
 
 
 
 
 
 
 
 }  
 
304 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
305 
 
 
 
 
 
 
 
 
 
 
 
 
 
 ### DEPRECATED, do not use, will be removed Real Soon Now  
 
306 
 
 
 
 
 
 
 
 
 
 
 
 
 
 sub _extract_head_markup_only {  
 
307 
 
 
 
 
 
 
 
 
 
 
 
 
 
     my $htmlref = shift;  
 
308 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
309 
 
 
 
 
 
 
 
 
 
 
 
 
 
     # kill all CDATA sections  
 
310 
 
 
 
 
 
 
 
 
 
 
 
 
 
     $$htmlref =~ s///sg;  
 
311 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
312 
 
 
 
 
 
 
 
 
 
 
 
 
 
     # kill all comments  
 
313 
 
 
 
 
 
 
 
 
 
 
 
 
 
     $$htmlref =~ s///sg;  
 
314 
 
 
 
 
 
 
 
 
 
 
 
 
 
     # ***FIX?*** Strictly speaking, SGML comments must have matched  
 
315 
 
 
 
 
 
 
 
 
 
 
 
 
 
     # pairs of '--'s but almost nobody checks for this or even knows  
 
316 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
317 
 
 
 
 
 
 
 
 
 
 
 
 
 
     # trim everything past the body.  this is in case the user doesn't  
 
318 
 
 
 
 
 
 
 
 
 
 
 
 
 
     # have a head document and somebody was able to inject their own  
 
319 
 
 
 
 
 
 
 
 
 
 
 
 
 
     # head.  -- brad choate  
 
320 
 
 
 
 
 
 
 
 
 
 
 
 
 
     $$htmlref =~ s/
  
321 
 
 
 
 
 
 
 
 
 
 
 
 
 
 }  
 
322 
 
 
 
 
 
 
 
 
 
 
 
 
 
    
 
323 
 
 
 
 
 
 
 
 
 
 
 
 
 
 1;