File Coverage

blib/lib/Net/OpenID/Common.pm
Criterion Covered Total %
statement 1 3 33.3
branch n/a
condition n/a
subroutine 1 1 100.0
pod n/a
total 2 4 50.0


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 and tags
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 s
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;