File Coverage

blib/lib/Net/OAuth/Message.pm
Criterion Covered Total %
statement 200 220 90.9
branch 48 62 77.4
condition 38 59 64.4
subroutine 37 39 94.8
pod 1 27 3.7
total 324 407 79.6


line stmt bran cond sub pod time code
1             package Net::OAuth::Message;
2 12     12   139418 use warnings;
  12         22  
  12         2490  
3 12     12   74 use strict;
  12         20  
  12         406  
4 12     12   59 use base qw/Class::Data::Inheritable Class::Accessor/;
  12         19  
  12         6093  
5 12     12   40282 use URI::Escape;
  12         24747  
  12         1107  
6 12     12   2654 use Net::OAuth;
  12         30  
  12         436  
7 12     12   6970 use URI;
  12         72982  
  12         553  
8 12     12   10300 use URI::QueryParam;
  12         1712  
  12         410  
9 12     12   74 use Carp;
  12         24  
  12         1000  
10              
11 12     12   73 use constant OAUTH_PREFIX => 'oauth_';
  12         2152  
  12         51154  
12              
13             our $OAUTH_PREFIX_RE = do {my $p = OAUTH_PREFIX; qr/^$p/};
14              
15             __PACKAGE__->mk_classdata(extension_param_patterns => []);
16              
17             sub add_required_message_params {
18 17     17 0 71 my $class = shift;
19 17         33 $class->required_message_params([@{$class->required_message_params}, @_]);
  17         123  
20 17         993 $class->all_message_params([@{$class->all_message_params}, @_]);
  17         82  
21 17         656 $class->all_params([@{$class->all_params}, @_]);
  17         119  
22 17         747 $class->mk_accessors(@_);
23             }
24              
25             sub add_optional_message_params {
26 0     0 0 0 my $class = shift;
27 0         0 $class->optional_message_params([@{$class->optional_message_params}, @_]);
  0         0  
28 0         0 $class->all_message_params([@{$class->all_message_params}, @_]);
  0         0  
29 0         0 $class->all_params([@{$class->all_params}, @_]);
  0         0  
30 0         0 $class->mk_accessors(@_);
31             }
32              
33             sub add_required_api_params {
34 9     9 0 23 my $class = shift;
35 9         18 $class->required_api_params([@{$class->required_api_params}, @_]);
  9         51  
36 9         356 $class->all_api_params([@{$class->all_api_params}, @_]);
  9         46  
37 9         374 $class->all_params([@{$class->all_params}, @_]);
  9         46  
38 9         184 $class->mk_accessors(@_);
39             }
40              
41             sub add_extension_param_pattern {
42 1     1 0 2 my $class = shift;
43 1         2 $class->extension_param_patterns([@{$class->extension_param_patterns}, @_]);
  1         5  
44             }
45              
46             sub add_to_signature {
47 0     0 0 0 my $class = shift;
48 0         0 $class->signature_elements([@{$class->signature_elements}, @_]);
  0         0  
49             }
50              
51             sub new {
52 37     37 1 1225308 my $proto = shift;
53 37   33     184 my $class = ref $proto || $proto;
54 37         287 my %params = @_;
55 37         140 $class = get_versioned_class($class, \%params);
56 37         105 my $self = bless \%params, $class;
57 37         212 $self->set_defaults;
58 37         170 $self->check;
59 34         354 return $self;
60             }
61              
62             sub get_versioned_class {
63 46     46 0 98 my $class = shift;
64 46         71 my $params = shift;
65 46   66     193 my $protocol_version = $params->{protocol_version} || $Net::OAuth::PROTOCOL_VERSION;
66 46 100 66     329 if (defined $protocol_version and $protocol_version == Net::OAuth::PROTOCOL_VERSION_1_0A and $class !~ /\::V1_0A\::/) {
      100        
67 9         140 (my $versioned_class = $class) =~ s/::(\w+)$/::V1_0A::$1/;
68 9 100       40 return $versioned_class if Net::OAuth::smart_require($versioned_class);
69             }
70 39         127 return $class;
71             }
72              
73             sub set_defaults {
74 37     37 0 55 my $self = shift;
75 37   100     290 $self->{extra_params} ||= {};
76 37 100 50     210 $self->{version} ||= Net::OAuth::OAUTH_VERSION unless $self->{from_hash};
77             }
78              
79             sub is_extension_param {
80 346     346 0 450 my $self = shift;
81 346         468 my $param = shift;
82 346         515 return grep ($param =~ $_, @{$self->extension_param_patterns});
  346         660  
83             }
84              
85             sub check {
86 37     37 0 72 my $self = shift;
87 37         87 foreach my $k (@{$self->required_message_params}, @{$self->required_api_params}) {
  37         147  
  37         448  
88 221 100       699 if (not defined $self->{$k}) {
89 3         1400 croak "Missing required parameter '$k'";
90             }
91             }
92 34 100 66     271 if ($self->{extra_params} and $self->allow_extra_params) {
93 30         48 foreach my $k (keys %{$self->{extra_params}}) {
  30         110  
94 22 50       221 if ($k =~ $OAUTH_PREFIX_RE) {
95 0         0 croak "Parameter '$k' not allowed in arbitrary params"
96             }
97             }
98             }
99             }
100              
101             sub encode {
102 933     933 0 152760 my $str = shift;
103 933 100       1834 $str = "" unless defined $str;
104 933 50       1593 unless($Net::OAuth::SKIP_UTF8_DOUBLE_ENCODE_CHECK) {
105 933 100 100     2150 if ($str =~ /[\x80-\xFF]/ and !utf8::is_utf8($str)) {
106 1         11 warn "Net::OAuth warning: your OAuth message appears to contain some multi-byte characters that need to be decoded via Encode.pm or a PerlIO layer first. This may result in an incorrect signature.";
107             }
108             }
109 933         1839 return URI::Escape::uri_escape_utf8($str);
110             }
111              
112             sub decode {
113 32     32 0 171 my $str = shift;
114 32         70 return uri_unescape($str);
115             }
116              
117 34     34 0 131 sub allow_extra_params {1}
118              
119 10     10 0 48 sub sign_message {0}
120              
121             sub gather_message_parameters {
122 60     60 0 100 my $self = shift;
123 60         195 my %opts = @_;
124 60 100       217 $opts{quote} = "" unless defined $opts{quote};
125 60   50     357 $opts{params} ||= [];
126 60         102 my %params;
127 60         83 foreach my $k (@{$self->required_message_params}, @{$self->optional_message_params}, @{$opts{add}}) {
  60         273  
  60         586  
  60         497  
128 386 100 100     1006 next if $k eq 'signature' and (!$self->sign_message or !grep ($_ eq 'signature', @{$opts{add}}));
      100        
129 344 100       615 my $message_key = $self->is_extension_param($k) ? $k : OAUTH_PREFIX . $k;
130 344         3179 my $v = $self->$k;
131 344 100       3733 $params{$message_key} = $v if defined $v;
132             }
133 60 100 66     348 if ($self->{extra_params} and !$opts{no_extra} and $self->allow_extra_params) {
      100        
134 50         72 foreach my $k (keys %{$self->{extra_params}}) {
  50         148  
135 39         122 $params{$k} = $self->{extra_params}{$k};
136             }
137 50 100       281 if ($self->can('request_url')) {
138 40         97 my $url = $self->request_url;
139 40         411 _ensure_uri_object($url);
140 40         38310 foreach my $k ($url->query_param) {
141 12         1030 $params{$k} = $url->query_param($k);
142             }
143             }
144             }
145 60 100       1720 if ($opts{hash}) {
146 8         34 return \%params;
147             }
148 52         81 my @pairs;
149 52         224 while (my ($k,$v) = each %params) {
150 327         5670 push @pairs, join('=', encode($k), $opts{quote} . encode($v) . $opts{quote});
151             }
152 52         2078 return sort(@pairs);
153             }
154              
155             sub normalized_message_parameters {
156 32     32 0 1586 my $self = shift;
157 32         99 return join('&', $self->gather_message_parameters);
158             }
159              
160             sub signature_base_string {
161 32     32 0 3865 my $self = shift;
162 32         58 return join('&', map(encode($self->$_), @{$self->signature_elements}));
  32         115  
163             }
164              
165             sub sign {
166 17     17 0 113 my $self = shift;
167 17         81 my $class = $self->_signature_method_class;
168 17         113 $self->signature($class->sign($self, @_));
169             }
170              
171             sub verify {
172 19     19 0 2528 my $self = shift;
173 19         59 my $class = $self->_signature_method_class;
174 19         135 return $class->verify($self, @_);
175             }
176              
177             sub _signature_method_class {
178 36     36   59 my $self = shift;
179 36         159 (my $signature_method = $self->signature_method) =~ s/\W+/_/g;
180 36         609 my $sm_class = 'Net::OAuth::SignatureMethod::' . $signature_method;
181 36 50       115 croak "Unable to load $signature_method plugin" unless Net::OAuth::smart_require($sm_class);
182 36         100 return $sm_class;
183             }
184              
185             sub to_authorization_header {
186 4     4 0 5529 my $self = shift;
187 4         11 my $realm = shift;
188 4   50     24 my $sep = shift || ",";
189 4 50       62 if (defined $realm) {
190 4         18 $realm = "realm=\"$realm\"$sep";
191             }
192             else {
193 0         0 $realm = "";
194             }
195 4         1378 return "OAuth $realm" .
196             join($sep, $self->gather_message_parameters(quote => '"', add => [qw/signature/], no_extra => 1));
197             }
198              
199             sub from_authorization_header {
200 1     1 0 1160 my $proto = shift;
201 1         3 my $header = shift;
202 1   33     6 my $class = ref $proto || $proto;
203 1 50       11 croak "Header must start with \"OAuth \"" unless $header =~ s/OAuth //;
204 1         23 my @header = split /[\s]*,[\s]*/, $header;
205 1 50       7 shift @header if $header[0] =~ /^realm=/i;
206 1         13 return $class->_from_pairs(\@header, @_)
207             }
208              
209             sub _from_pairs() {
210 4     4   9 my $class = shift;
211 4         8 my $pairs = shift;
212 4 50       17 if (ref $pairs ne 'ARRAY') {
213 0         0 croak 'Expected an array!';
214             }
215 4         8 my %params;
216 4         10 foreach my $pair (@$pairs) {
217 16         53 my ($k,$v) = split /=/, $pair;
218 16 50 33     69 if (defined $k and defined $v) {
219 16         84 $v =~ s/(^"|"$)//g;
220 16         42 ($k,$v) = map decode($_), $k, $v;
221 16         224 $params{$k} = $v;
222             }
223             }
224 4         59 return $class->from_hash(\%params, @_);
225             }
226              
227             sub from_hash {
228 9     9 0 477 my $proto = shift;
229 9   33     71 my $class = ref $proto || $proto;
230 9         17 my $hash = shift;
231 9 50       45 if (ref $hash ne 'HASH') {
232 0         0 croak 'Expected a hash!';
233             }
234 9         43 my %api_params = @_;
235             # need to do this earlier than Message->new because
236             # the below validation step needs the correct class.
237             # https://rt.cpan.org/Public/Bug/Display.html?id=47293
238 9         32 $class = get_versioned_class($class, \%api_params);
239 9         24 my %msg_params;
240 9         39 foreach my $k (keys %$hash) {
241 48 100       260 if ($k =~ s/$OAUTH_PREFIX_RE//) {
    50          
242 46 100       62 if (!grep ($_ eq $k, @{$class->all_message_params})) {
  46         119  
243 1         503 croak "Parameter ". OAUTH_PREFIX ."$k not valid for a message of type $class";
244             }
245             else {
246 45         502 $msg_params{$k} = $hash->{OAUTH_PREFIX . $k};
247             }
248             }
249             elsif ($class->is_extension_param($k)) {
250 0 0       0 if (!grep ($_ eq $k, @{$class->all_message_params})) {
  0         0  
251 0         0 croak "Parameter $k not valid for a message of type $class";
252             }
253             else {
254 0         0 $msg_params{$k} = $hash->{$k};
255             }
256             }
257             else {
258 2         27 $msg_params{extra_params}->{$k} = $hash->{$k};
259             }
260             }
261 8         26 $api_params{from_hash} = 1;
262 8         59 return $class->new(%msg_params, %api_params);
263             }
264              
265             sub _ensure_uri_object {
266 82 100   82   673 $_[0] = UNIVERSAL::isa($_[0], 'URI') ? $_[0] : URI->new($_[0]);
267             }
268              
269             sub from_url {
270 2     2 0 6 my $proto = shift;
271 2   33     8 my $class = ref $proto || $proto;
272 2         6 my $url = shift;
273 2         5 _ensure_uri_object($url);
274 2         497 return $class->from_hash($url->query_form_hash, @_);
275             }
276              
277             sub to_post_body {
278 16     16 0 1383 my $self = shift;
279 16         116 return join('&', $self->gather_message_parameters(add => [qw/signature/]));
280             }
281              
282             sub from_post_body {
283 3     3 0 7 my $proto = shift;
284 3   33     15 my $class = ref $proto || $proto;
285 3         14 my @pairs = split '&', shift;
286 3         15 return $class->_from_pairs(\@pairs, @_);
287             }
288              
289             sub to_hash {
290 8     8 0 16 my $self = shift;
291 8         35 return $self->gather_message_parameters(hash => 1, add => [qw/signature/]);
292             }
293              
294             sub to_url {
295 8     8 0 11591 my $self = shift;
296 8         14 my $url = shift;
297 8 50 66     68 if (!defined $url and $self->can('request_url') and defined $self->request_url) {
      66        
298 5         65 $url = $self->request_url;
299             }
300 8 50       54 if (defined $url) {
301 8         28 _ensure_uri_object($url);
302 8         1312 $url = $url->clone; # don't modify the URL that was passed in
303 8         284 $url->query(undef); # remove any existing query params, as these may cause the signature to break
304 8         184 my $params = $self->to_hash;
305 8         20 my $sep = '?';
306 8         64 foreach my $k (sort keys %$params) {
307 49         82 $url .= $sep . encode($k) . '=' . encode( $params->{$k} );
308 49 100       1026 $sep = '&' if $sep eq '?';
309             }
310 8         110 return $url;
311             }
312             else {
313 0           return $self->to_post_body;
314             }
315             }
316              
317             =head1 NAME
318              
319             Net::OAuth::Message - base class for OAuth messages
320              
321             =head1 SEE ALSO
322              
323             L<Net::OAuth>, L<http://oauth.net>
324              
325             =head1 AUTHOR
326              
327             Originally by Keith Grennan <kgrennan@cpan.org>
328              
329             Currently maintained by Robert Rothenberg <rrwo@cpan.org>
330              
331             =head1 COPYRIGHT & LICENSE
332              
333             Copyright 2007-2012, 2024-2025 Keith Grennan
334              
335             This program is free software; you can redistribute it and/or modify it
336             under the same terms as Perl itself.
337              
338             =cut
339              
340             1;