File Coverage

blib/lib/Net/OAuth/Simple.pm
Criterion Covered Total %
statement 50 267 18.7
branch 3 90 3.3
condition 3 27 11.1
subroutine 15 56 26.7
pod 29 29 100.0
total 100 469 21.3


line stmt bran cond sub pod time code
1             package Net::OAuth::Simple;
2              
3 2     2   92485 use warnings;
  2         4  
  2         65  
4 2     2   10 use strict;
  2         4  
  2         100  
5             our $VERSION = "1.7";
6              
7 2     2   860 use URI;
  2         7517  
  2         60  
8 2     2   814 use LWP;
  2         42276  
  2         62  
9 2     2   2695 use CGI;
  2         67140  
  2         14  
10 2     2   2019 use HTTP::Request::Common ();
  2         4884  
  2         41  
11 2     2   11 use Carp;
  2         2  
  2         152  
12 2     2   1600 use Net::OAuth;
  2         1297  
  2         65  
13 2     2   12 use Scalar::Util qw(blessed);
  2         3  
  2         158  
14 2     2   1736 use Digest::SHA;
  2         7782  
  2         99  
15 2     2   13 use File::Basename;
  2         4  
  2         326  
16             require Net::OAuth::Request;
17             require Net::OAuth::RequestTokenRequest;
18             require Net::OAuth::AccessTokenRequest;
19             require Net::OAuth::ProtectedResourceRequest;
20             require Net::OAuth::XauthAccessTokenRequest;
21             require Net::OAuth::UserAuthRequest;
22              
23             BEGIN {
24 2     2   4 eval { require Math::Random::MT };
  2         796  
25 2 50       4573 unless ($@) {
26 0         0 Math::Random::MT->import(qw(srand rand));
27             }
28             }
29              
30             our @required_constructor_params = qw(consumer_key consumer_secret);
31             our @access_token_params = qw(access_token access_token_secret);
32             our @general_token_params = qw(general_token general_token_secret);
33              
34             =head1 NAME
35              
36             Net::OAuth::Simple - a simple wrapper round the OAuth protocol
37              
38             =head1 SYNOPSIS
39              
40             First create a sub class of C that will do you requests
41             for you.
42              
43             package Net::AppThatUsesOAuth;
44              
45             use strict;
46             use base qw(Net::OAuth::Simple);
47              
48              
49             sub new {
50             my $class = shift;
51             my %tokens = @_;
52             return $class->SUPER::new( tokens => \%tokens,
53             protocol_version => '1.0a',
54             urls => {
55             authorization_url => ...,
56             request_token_url => ...,
57             access_token_url => ...,
58             });
59             }
60              
61             sub view_restricted_resource {
62             my $self = shift;
63             my $url = shift;
64             return $self->make_restricted_request($url, 'GET');
65             }
66              
67             sub update_restricted_resource {
68             my $self = shift;
69             my $url = shift;
70             my %extra_params = @_;
71             return $self->make_restricted_request($url, 'POST', %extra_params);
72             }
73             1;
74              
75              
76             Then in your main app you need to do
77              
78             # Get the tokens from the command line, a config file or wherever
79             my %tokens = get_tokens();
80             my $app = Net::AppThatUsesOAuth->new(%tokens);
81              
82             # Check to see we have a consumer key and secret
83             unless ($app->consumer_key && $app->consumer_secret) {
84             die "You must go get a consumer key and secret from App\n";
85             }
86              
87             # If the app is authorized (i.e has an access token and secret)
88             # Then look at a restricted resourse
89             if ($app->authorized) {
90             my $response = $app->view_restricted_resource;
91             print $response->content."\n";
92             exit;
93             }
94              
95              
96             # Otherwise the user needs to go get an access token and secret
97             print "Go to ".$app->get_authorization_url."\n";
98             print "Then hit return after\n";
99             ;
100              
101             my ($access_token, $access_token_secret) = $app->request_access_token;
102              
103             # Now save those values
104              
105              
106             Note the flow will be somewhat different for web apps since the request token
107             and secret will need to be saved whilst the user visits the authorization url.
108              
109             For examples go look at the C module and the C command
110             line script that ships with it. Also in the same distribution in the C
111             directory is a sample web app.
112              
113             =head1 METHODS
114              
115             =cut
116              
117             =head2 new [params]
118              
119             Create a new OAuth enabled app - takes a hash of params.
120              
121             One of the keys of the hash must be C, the value of which
122             must be a hash ref with the keys:
123              
124             =over 4
125              
126             =item consumer_key
127              
128             =item consumer_secret
129              
130             =back
131              
132             Then, when you have your per-use access token and secret you
133             can supply
134              
135             =over 4
136              
137             =item access_token
138              
139             =item access_secret
140              
141             =back
142              
143             Another key of the hash must be C, the value of which must
144             be a hash ref with the keys
145              
146             =over 4
147              
148             =item authorization_url
149              
150             =item request_token_url
151              
152             =item access_token_url
153              
154             =back
155              
156             If you pass in a key C with a value equal to B<1.0a> then
157             the newest version of the OAuth protocol will be used. A value equal to B<1.0> will
158             mean the old version will be used. Defaults to B<1.0a>
159              
160             You can pass in your own User Agent by using the key C.
161              
162             If you pass in C then instead of C-ing on error
163             methods will return undef instead and the error can be retrieved using the
164             C method. See the section on B.
165              
166             =cut
167              
168             sub new {
169 1     1 1 3172 my $class = shift;
170 1         6 my %params = @_;
171 1   50     9 $params{protocol_version} ||= '1.0a';
172 1         3 my $client = bless \%params, $class;
173              
174             # Set up LibWWWPerl for HTTP requests
175 1   33     10 $client->{browser} ||= LWP::UserAgent->new;
176              
177             # Verify arguments
178 1         5 $client->_check;
179              
180             # Client Object
181 1         3 return $client;
182             }
183              
184             # Validate required constructor params
185             sub _check {
186 1     1   3 my $self = shift;
187              
188 1         4 foreach my $param ( @required_constructor_params ) {
189 2 50       32 unless ( defined $self->{tokens}->{$param} ) {
190 0         0 return $self->_error("Missing required parameter '$param'");
191             }
192             }
193              
194             return $self->_error("browser must be a LWP::UserAgent")
195 1 50 33     26 unless blessed $self->{browser} && $self->{browser}->isa('LWP::UserAgent');
196             }
197              
198             =head2 oauth_1_0a
199              
200             Whether or not we're using 1.0a version of OAuth (necessary for,
201             amongst others, FireEagle)
202              
203             =cut
204             sub oauth_1_0a {
205 0     0 1   my $self = shift;
206 0           return $self->{protocol_version } eq '1.0a';
207             }
208              
209             =head2 authorized
210              
211             Whether the client has the necessary credentials to be authorized.
212              
213             Note that the credentials may be wrong and so the request may still fail.
214              
215             =cut
216              
217             sub authorized {
218 0     0 1   my $self = shift;
219 0           foreach my $param ( @access_token_params ) {
220 0 0 0       return 0 unless defined $self->{tokens}->{$param} && length $self->{tokens}->{$param};
221             }
222 0           return 1;
223             }
224              
225             =head2 signature_method [method]
226              
227             The signature method to use.
228              
229             Defaults to HMAC-SHA1
230              
231             =cut
232             sub signature_method {
233 0     0 1   my $self = shift;
234 0 0         $self->{signature_method} = shift if @_;
235 0   0       return $self->{signature_method} || 'HMAC-SHA1';
236             }
237              
238             =head2 tokens
239              
240             Get all the tokens.
241              
242             =cut
243             sub tokens {
244 0     0 1   my $self = shift;
245 0 0         if (@_) {
246 0           my %tokens = @_;
247 0           $self->{tokens} = \%tokens;
248             }
249 0 0         return %{$self->{tokens}||{}};
  0            
250             }
251              
252             =head2 consumer_key [consumer key]
253              
254             Returns the current consumer key.
255              
256             Can optionally set the consumer key.
257              
258             =cut
259              
260             sub consumer_key {
261 0     0 1   my $self = shift;
262 0           $self->_token('consumer_key', @_);
263             }
264              
265             =head2 consumer_secret [consumer secret]
266              
267             Returns the current consumer secret.
268              
269             Can optionally set the consumer secret.
270              
271             =cut
272              
273             sub consumer_secret {
274 0     0 1   my $self = shift;
275 0           $self->_token('consumer_secret', @_);
276             }
277              
278              
279             =head2 access_token [access_token]
280              
281             Returns the current access token.
282              
283             Can optionally set a new token.
284              
285             =cut
286              
287             sub access_token {
288 0     0 1   my $self = shift;
289 0           $self->_token('access_token', @_);
290             }
291              
292             =head2 access_token_secret [access_token_secret]
293              
294             Returns the current access token secret.
295              
296             Can optionally set a new secret.
297              
298             =cut
299              
300             sub access_token_secret {
301 0     0 1   my $self = shift;
302 0           return $self->_token('access_token_secret', @_);
303             }
304              
305             =head2 general_token [token]
306              
307             Get or set the general token.
308              
309             See documentation in C
310              
311             =cut
312              
313             sub general_token {
314 0     0 1   my $self = shift;
315 0           $self->_token('general_token', @_);
316             }
317              
318             =head2 general_token_secret [secret]
319              
320             Get or set the general token secret.
321              
322             See documentation in C
323              
324             =cut
325              
326             sub general_token_secret {
327 0     0 1   my $self = shift;
328 0           $self->_token('general_token_secret', @_);
329             }
330              
331             =head2 authorized_general_token
332              
333             Is the app currently authorized for general token requests.
334              
335             See documentation in C
336              
337             =cut
338              
339             sub authorized_general_token {
340 0     0 1   my $self = shift;
341 0           foreach my $param ( @general_token_params ) {
342 0 0         return 0 unless defined $self->$param();
343             }
344 0           return 1;
345             }
346              
347              
348             =head2 request_token [request_token]
349              
350             Returns the current request token.
351              
352             Can optionally set a new token.
353              
354             =cut
355              
356             sub request_token {
357 0     0 1   my $self = shift;
358 0           $self->_token('request_token', @_);
359             }
360              
361              
362             =head2 request_token_secret [request_token_secret]
363              
364             Returns the current request token secret.
365              
366             Can optionally set a new secret.
367              
368             =cut
369              
370             sub request_token_secret {
371 0     0 1   my $self = shift;
372 0           return $self->_token('request_token_secret', @_);
373             }
374              
375             =head2 verifier [verifier]
376              
377             Returns the current oauth_verifier.
378              
379             Can optionally set a new verifier.
380              
381             =cut
382              
383             sub verifier {
384 0     0 1   my $self = shift;
385 0           return $self->_param('verifier', @_);
386             }
387              
388             =head2 callback [callback]
389              
390             Returns the oauth callback.
391              
392             Can optionally set the oauth callback.
393              
394             =cut
395              
396             sub callback {
397 0     0 1   my $self = shift;
398 0           $self->_param('callback', @_);
399             }
400              
401             =head2 callback_confirmed [callback_confirmed]
402              
403             Returns the oauth callback confirmed.
404              
405             Can optionally set the oauth callback confirmed.
406              
407             =cut
408              
409             sub callback_confirmed {
410 0     0 1   my $self = shift;
411 0           $self->_param('callback_confirmed', @_);
412             }
413              
414              
415             sub _token {
416 0     0     my $self = shift;
417 0           $self->_store('tokens', @_);
418             }
419              
420             sub _param {
421 0     0     my $self = shift;
422 0           $self->_store('params', @_);
423             }
424              
425             sub _store {
426 0     0     my $self = shift;
427 0           my $ns = shift;
428 0           my $key = shift;
429 0 0         $self->{$ns}->{$key} = shift if @_;
430 0           return $self->{$ns}->{$key};
431             }
432              
433             =head2 authorization_url
434              
435             Get the url the user needs to visit to authorize as a URI object.
436              
437             Note: this is the base url - not the full url with the necessary OAuth params.
438              
439             =cut
440             sub authorization_url {
441 0     0 1   my $self = shift;
442 0           return $self->_url('authorization_url', @_);
443             }
444              
445              
446             =head2 request_token_url
447              
448             Get the url to obtain a request token as a URI object.
449              
450             =cut
451             sub request_token_url {
452 0     0 1   my $self = shift;
453 0           return $self->_url('request_token_url', @_);
454             }
455              
456             =head2 access_token_url
457              
458             Get the url to obtain an access token as a URI object.
459              
460             =cut
461             sub access_token_url {
462 0     0 1   my $self = shift;
463 0           return $self->_url('access_token_url', @_);
464             }
465              
466             sub _url {
467 0     0     my $self = shift;
468 0           my $key = shift;
469 0 0         $self->{urls}->{$key} = shift if @_;
470 0   0       my $url = $self->{urls}->{$key} || return;;
471 0           return URI->new($url);
472             }
473              
474             # generate a random number
475             sub _nonce {
476 0     0     return int( rand( 2**32 ) );
477             }
478              
479             =head2 request_access_token [param[s]]
480              
481             Request the access token and access token secret for this user.
482              
483             The user must have authorized this app at the url given by
484             C first.
485              
486             Returns the access token and access token secret but also sets
487             them internally so that after calling this method you can
488             immediately call a restricted method.
489              
490             If you pass in a hash of params then they will added as parameters to the URL.
491              
492             =cut
493              
494             sub request_access_token {
495 0     0 1   my $self = shift;
496 0           my %params = @_;
497 0           my $url = $self->access_token_url;
498              
499 0 0         $params{token} = $self->request_token unless defined $params{token};
500 0 0         $params{token_secret} = $self->request_token_secret unless defined $params{token_secret};
501              
502 0 0         if ($self->oauth_1_0a) {
503 0 0         $params{verifier} = $self->verifier unless defined $params{verifier};
504 0 0         return $self->_error("You must pass a verified parameter when using OAuth v1.0a") unless defined $params{verifier};
505              
506             }
507              
508              
509 0           my $access_token_response = $self->_make_request(
510             'Net::OAuth::AccessTokenRequest',
511             $url, 'POST',
512             %params,
513             );
514              
515 0           return $self->_decode_tokens($url, $access_token_response);
516             }
517              
518             sub _decode_tokens {
519 0     0     my $self = shift;
520 0           my $url = shift;
521 0           my $access_token_response = shift;
522              
523             # Cast response into CGI query for EZ parameter decoding
524 0           my $access_token_response_query =
525             new CGI( $access_token_response->content );
526              
527             # Split out token and secret parameters from the access token response
528 0           $self->access_token($access_token_response_query->param('oauth_token'));
529 0           $self->access_token_secret($access_token_response_query->param('oauth_token_secret'));
530              
531 0           delete $self->{tokens}->{$_} for qw(request_token request_token_secret verifier);
532              
533 0 0 0       return $self->_error("$url did not reply with an access token")
534             unless ( $self->access_token && $self->access_token_secret );
535              
536 0           return ( $self->access_token, $self->access_token_secret );
537              
538             }
539              
540             =head2 xauth_request_access_token [param[s]]
541              
542             The same as C but for xAuth.
543              
544             For more information on xAuth see
545              
546             http://apiwiki.twitter.com/Twitter-REST-API-Method%3A-oauth-access_token-for-xAuth
547              
548             You must pass in the parameters
549              
550             x_auth_username
551             x_auth_password
552             x_auth_mode
553              
554             You must have HTTPS enabled for LWP::UserAgent.
555              
556             See C for a sample implementation.
557              
558             =cut
559             sub xauth_request_access_token {
560 0     0 1   my $self = shift;
561 0           my %params = @_;
562 0           my $url = $self->access_token_url;
563 0           $url =~ s !^http:!https:!; # force https
564              
565 0           my %xauth_params = map { $_ => $params{$_} }
566 0           grep {/^x_auth_/}
567 0           @{Net::OAuth::XauthAccessTokenRequest->required_message_params};
  0            
568              
569 0           my $access_token_response = $self->_make_request(
570             'Net::OAuth::XauthAccessTokenRequest',
571             $url, 'POST',
572             %xauth_params,
573             );
574              
575 0           return $self->_decode_tokens($url, $access_token_response);
576             }
577              
578             =head2 request_request_token [param[s]]
579              
580             Request the request token and request token secret for this user.
581              
582             This is called automatically by C if necessary.
583              
584             If you pass in a hash of params then they will added as parameters to the URL.
585              
586             =cut
587              
588              
589             sub request_request_token {
590 0     0 1   my $self = shift;
591 0           my %params = @_;
592 0           my $url = $self->request_token_url;
593              
594 0 0         if ($self->oauth_1_0a) {
595 0 0         $params{callback} = $self->callback unless defined $params{callback};
596 0 0         return $self->_error("You must pass a callback parameter when using OAuth v1.0a") unless defined $params{callback};
597             }
598              
599 0           my $request_token_response = $self->_make_request(
600             'Net::OAuth::RequestTokenRequest',
601             $url, 'GET',
602             %params);
603              
604 0 0         return $self->_error("GET for $url failed: ".$request_token_response->status_line)
605             unless ( $request_token_response->is_success );
606              
607             # Cast response into CGI query for EZ parameter decoding
608 0           my $request_token_response_query =
609             new CGI( $request_token_response->content );
610              
611             # Split out token and secret parameters from the request token response
612 0           $self->request_token($request_token_response_query->param('oauth_token'));
613 0           $self->request_token_secret($request_token_response_query->param('oauth_token_secret'));
614 0           $self->callback_confirmed($request_token_response_query->param('oauth_callback_confirmed'));
615              
616             # Hack to deal with bug in older versions of oauth-php (See https://code.google.com/p/oauth-php/issues/detail?id=60)
617 0 0         $self->callback_confirmed($request_token_response_query->param('oauth_callback_accepted'))
618             unless $self->callback_confirmed;
619              
620 0 0 0       return $self->_error("Response does not confirm to OAuth1.0a. oauth_callback_confirmed not received")
621             if $self->oauth_1_0a && !$self->callback_confirmed;
622              
623             }
624              
625             =head2 get_authorization_url [param[s]]
626              
627             Get the URL to authorize a user as a URI object.
628              
629             If you pass in a hash of params then they will added as parameters to the URL.
630              
631             =cut
632              
633             sub get_authorization_url {
634 0     0 1   my $self = shift;
635 0           my %params = @_;
636 0           my $url = $self->authorization_url;
637 0 0         if (!defined $self->request_token) {
638 0           $self->request_request_token(%params);
639             }
640             #$params{oauth_token} = $self->request_token;
641 0           $url->query_form(%params);
642 0           my $req = $self->_build_request('Net::OAuth::UserAuthRequest', $url, "GET");
643 0           return $req->normalized_request_url;
644             }
645              
646             =head2 make_restricted_request [extra[s]]
647              
648             Make a request to C using the given HTTP method.
649              
650             Any extra parameters can be passed in as a hash.
651              
652             =cut
653             sub make_restricted_request {
654 0     0 1   my $self = shift;
655              
656 0 0         return $self->_error("This restricted request is not authorized") unless $self->authorized;
657              
658 0           return $self->_restricted_request( $self->access_token, $self->access_token_secret, @_ );
659             }
660              
661             =head2 make_general_request [extra[s]]
662              
663             Make a request to C using the given HTTP method using
664             the general purpose tokens.
665              
666             Any extra parameters can be passed in as a hash.
667              
668             =cut
669             sub make_general_request {
670 0     0 1   my $self = shift;
671              
672 0 0         return $self->_error("This general request is not authorized") unless $self->authorized_general_token;
673              
674 0           return $self->_restricted_request( $self->general_token, $self->general_token_secret, @_ );
675             }
676              
677             sub _restricted_request {
678 0     0     my $self = shift;
679 0           my $token = shift;
680 0           my $secret = shift;
681 0           my $url = shift;
682 0           my $method = shift;
683 0           my %extras = @_;
684 0           my $response = $self->_make_request(
685             'Net::OAuth::ProtectedResourceRequest',
686             $url, $method,
687             token => $token,
688             token_secret => $secret,
689             extra_params => \%extras
690             );
691 0           return $response;
692             }
693              
694             sub _make_request {
695 0     0     my $self = shift;
696 0           my $class = shift;
697 0           my $url = shift;
698 0           my $method = uc(shift);
699 0           my @extra = @_;
700              
701 0           my $request = $self->_build_request($class, $url, $method, @extra);
702 0           my $response = $self->{browser}->request($request);
703 0 0         return $self->_error("$method on ".$request->normalized_request_url." failed: ".$response->status_line." - ".$response->content)
704             unless ( $response->is_success );
705              
706 0           return $response;
707             }
708              
709 2     2   2068 use Data::Dumper;
  2         14879  
  2         2279  
710             sub _build_request {
711 0     0     my $self = shift;
712 0           my $class = shift;
713 0           my $url = shift;
714 0           my $method = uc(shift);
715 0           my @extra = @_;
716              
717 0           my $uri = URI->new($url);
718 0           my %query = $uri->query_form;
719 0           $uri->query_form({});
720              
721              
722 0           my $content;
723             my $filename;
724 0 0         if ('PUT' eq $method) {
725             # Get the content (goes in the body), and hash the content for inclusion in the message
726 0           my %params = @extra;
727 0           $filename = delete $params{extra_params}->{filename};
728              
729 0 0         return $self->_error('Missing required parameter $filename') unless $filename;
730              
731             # Slurp the file from above
732 0           my $content = "";
733 0     0     $self->_read_file( $filename, sub { $content .= shift } ) ;
  0            
734 0           ($filename) = fileparse($filename);
735              
736              
737             # Net::OAuth doesn't seem to handle body hash, so do it ourselves
738              
739             # Per http://oauth.googlecode.com/svn/spec/ext/body_hash/1.0/oauth-bodyhash.html#parameter
740             # If the OAuth signature method is HMAC-SHA1 or RSA-SHA1, SHA1 MUST be used as the body hash algorithm
741             # No discussion of other signature methods, but the draft spec for OAuth2 predictably says that SHA256
742             # should be used for HMAC-SHA256
743              
744 0 0 0       if ($self->signature_method eq 'HMAC-SHA1' || $self->signature_method eq 'RSA-SHA1') {
    0          
745 0           $params{body_hash} = Digest::SHA::sha1_hex($content);
746             } elsif ($self->signature_method eq 'HMAC-SHA256') {
747 0           $params{body_hash} = Digest::SHA::sha256_hex($content);
748             } else {
749 0           return $self->_error("Unknown signature method: ".$self->signature_method);
750             }
751              
752 0           @extra = %params;
753             }
754              
755              
756              
757 0 0         my $request = $class->new(
758             consumer_key => $self->consumer_key,
759             consumer_secret => $self->consumer_secret,
760             request_url => $uri,
761             request_method => $method,
762             signature_method => $self->signature_method,
763             protocol_version => $self->oauth_1_0a ? Net::OAuth::PROTOCOL_VERSION_1_0A : Net::OAuth::PROTOCOL_VERSION_1_0,
764             timestamp => time,
765             nonce => $self->_nonce,
766             extra_params => \%query,
767             @extra,
768             );
769 0 0         $request->add_optional_message_params('body_hash') if 'PUT' eq $method;
770 0           $request->sign;
771 0 0         return $self->_error("Couldn't verify request! Check OAuth parameters.")
772             unless $request->verify;
773              
774 0 0 0       my $req_url = ('GET' eq $method || 'DELETE' eq $method) ? $request->to_url() : $url;
775              
776 0           my $req = HTTP::Request->new( $method => $req_url);
777              
778 0 0         if ('PUT' eq $method) {
779 0           $req->header('Authorization' => $request->to_authorization_header(""));
780 0           $req->header('Content-disposition' => qq!attachment; filename="$filename"!);
781 0           $req->content($content);
782             }
783              
784 0 0         if ('POST' eq $method) {
785             # "@extra" params are the ones that don't start with oath_ in the hash
786             # User passed them, they must want us to actually send them, huh?
787 0           $request->add_optional_message_params($_) for grep { ! /^oauth_/ } keys %{$request->to_hash};
  0            
  0            
788 0           $req->content_type('application/x-www-form-urlencoded');
789 0           $req->content($request->to_post_body);
790             }
791              
792 0           return $req;
793             }
794              
795              
796             sub _error {
797 0     0     my $self = shift;
798 0           my $mess = shift;
799 0 0         if ($self->{return_undef_on_error}) {
800 0           $self->{_last_error} = $mess;
801             } else {
802 0           croak $mess;
803             }
804 0           return undef;
805             }
806              
807             =head2 last_error
808              
809             Get the last error message.
810              
811             Only works if C was passed in to the constructor.
812              
813             See the section on B.
814              
815             =cut
816             sub last_error {
817 0     0 1   my $self = shift;
818 0           return $self->{_last_error};
819             }
820              
821             =head2 load_tokens
822              
823             A convenience method for loading tokens from a config file.
824              
825             Returns a hash with the token names suitable for passing to
826             C.
827              
828             Returns an empty hash if the file doesn't exist.
829              
830             =cut
831             sub load_tokens {
832 0     0 1   my $class = shift;
833 0           my $file = shift;
834 0           my %tokens = ();
835 0 0         return %tokens unless -f $file;
836              
837             $class->_read_file($file, sub {
838 0     0     $_ = shift;
839 0           chomp;
840 0 0         next if /^#/;
841 0 0         next if /^\s*$/;
842 0 0         next unless /=/;
843 0           s/(^\s*|\s*$)//g;
844 0           my ($key, $val) = split /\s*=\s*/, $_, 2;
845 0           $tokens{$key} = $val;
846 0           });
847 0           return %tokens;
848             }
849              
850             =head2 save_tokens [token[s]]
851              
852             A convenience method to save a hash of tokens out to the given file.
853              
854             =cut
855             sub save_tokens {
856 0     0 1   my $class = shift;
857 0           my $file = shift;
858 0           my %tokens = @_;
859              
860 0           my $max = 0;
861 0           foreach my $key (keys %tokens) {
862 0 0         $max = length($key) if length($key)>$max;
863             }
864              
865 0 0         open(my $fh, ">$file") || die "Couldn't open $file for writing: $!\n";
866 0           foreach my $key (sort keys %tokens) {
867 0           my $pad = " "x($max-length($key));
868 0           print $fh "$key ${pad}= ".$tokens{$key}."\n";
869             }
870 0           close($fh);
871             }
872              
873             sub _read_file {
874 0     0     my $self = shift;
875 0           my $file = shift;
876 0           my $sub = shift;
877              
878 0 0         open(my $fh, $file) || die "Couldn't open $file: $!\n";
879 0           while (<$fh>) {
880 0 0         $sub->($_) if $sub;
881             }
882 0           close($fh);
883             }
884              
885             =head1 ERROR HANDLING
886              
887             Originally this module would die upon encountering an error (inheriting behaviour
888             from the original Yahoo! code).
889              
890             This is still the default behaviour however if you now pass
891              
892             return_undef_on_error => 1
893              
894             into the constructor then all methods will return undef on error instead.
895              
896             The error message is accessible via the C method.
897              
898             =head1 GOOGLE'S SCOPE PARAMETER
899              
900             Google's OAuth API requires the non-standard C parameter to be set
901             in C, and you also explicitly need to pass an C
902             to C method, so that you can direct the user to your site
903             if you're authenticating users in Web Application mode. Otherwise Google will let
904             user grant acesss as a desktop app mode and doesn't redirect users back.
905              
906             Here's an example class that uses Google's Portable Contacts API via OAuth:
907              
908             package Net::AppUsingGoogleOAuth;
909             use strict;
910             use base qw(Net::OAuth::Simple);
911              
912             sub new {
913             my $class = shift;
914             my %tokens = @_;
915             return $class->SUPER::new(
916             tokens => \%tokens,
917             urls => {
918             request_token_url => "https://www.google.com/accounts/OAuthGetRequestToken?scope=http://www-opensocial.googleusercontent.com/api/people",
919             authorization_url => "https://www.google.com/accounts/OAuthAuthorizeToken",
920             access_token_url => "https://www.google.com/accounts/OAuthGetAccessToken",
921             },
922             );
923             }
924              
925             package main;
926             my $oauth = Net::AppUsingGoogleOAuth->new(%tokens);
927              
928             # Web application
929             $app->redirect( $oauth->get_authorization_url( callback => "http://you.example.com/oauth/callback") );
930              
931             # Desktop application
932             print "Open the URL and come back once you're authenticated!\n",
933             $oauth->get_authorization_url;
934              
935             See L and other
936             services API documentation for the possible list of I parameter value.
937              
938             =head1 RANDOMNESS
939              
940             If C is installed then any nonces generated will use a
941             Mersenne Twiser instead of Perl's built in randomness function.
942              
943             =head1 EXAMPLES
944              
945             There are example Twitter and Twitter xAuth 'desktop' apps and a FireEagle OAuth 1.0a web app
946             in the examples directory of the distribution.
947              
948             =head1 BUGS
949              
950             Non known
951              
952             =head1 DEVELOPERS
953              
954             The latest code for this module can be found at
955              
956             https://svn.unixbeard.net/simon/Net-OAuth-Simple
957              
958             =head1 AUTHOR
959              
960             Simon Wistow, C<>
961              
962             =head1 BUGS
963              
964             Please report any bugs or feature requests to C, or through
965             the web interface at L. I will be notified, and then you'll
966             automatically be notified of progress on your bug as I make changes.
967              
968             =head1 SUPPORT
969              
970             You can find documentation for this module with the perldoc command.
971              
972             perldoc Net::OAuth::Simple
973              
974              
975             You can also look for information at:
976              
977             =over 4
978              
979             =item * RT: CPAN's request tracker
980              
981             L
982              
983             =item * AnnoCPAN: Annotated CPAN documentation
984              
985             L
986              
987             =item * CPAN Ratings
988              
989             L
990              
991             =item * Search CPAN
992              
993             L
994              
995             =back
996              
997             =head1 COPYRIGHT & LICENSE
998              
999             Copyright 2009 Simon Wistow, all rights reserved.
1000              
1001             This program is free software; you can redistribute it and/or modify it
1002             under the same terms as Perl itself.
1003              
1004              
1005             =cut
1006              
1007             1; # End of Net::OAuth::Simple