File Coverage

blib/lib/Net/SinaWeibo/OAuth.pm
Criterion Covered Total %
statement 52 153 33.9
branch 7 58 12.0
condition 2 34 5.8
subroutine 14 21 66.6
pod 3 7 42.8
total 78 273 28.5


line stmt bran cond sub pod time code
1             package Net::SinaWeibo::OAuth;
2             BEGIN {
3 5     5   65078 $Net::SinaWeibo::OAuth::VERSION = '0.003';
4             }
5             # ABSTRACT: Internal OAuth wrapper round OAuth::Lite::Consumer
6 5     5   38 use strict;
  5         10  
  5         160  
7 5     5   24 use warnings;
  5         10  
  5         132  
8 5     5   29 use Carp;
  5         7  
  5         473  
9 5     5   3628 use Data::Dumper;
  5         35100  
  5         316  
10 5     5   44 use base 'OAuth::Lite::Consumer';
  5         13  
  5         5690  
11 5     5   556830 use OAuth::Lite::AuthMethod qw(:all);
  5         43  
  5         648  
12 5     5   26 use List::MoreUtils qw(any);
  5         10  
  5         231  
13 5     5   6031 use HTTP::Request::Common;
  5         14466  
  5         497  
14 5     5   6252 use JSON;
  5         87528  
  5         48  
15 5     5   951 use OAuth::Lite::Util qw(normalize_params);
  5         15  
  5         705  
16             use constant {
17 5         10024 SINA_SITE => 'http://api.t.sina.com.cn',
18             SINA_REQUEST_TOKEN_PATH => '/oauth/request_token',
19             SINA_AUTHORIZATION_PATH => '/oauth/authorize',
20             SINA_ACCESS_TOKEN_PATH => '/oauth/access_token',
21             SINA_FORMAT => 'json',
22 5     5   29 };
  5         10  
23             __PACKAGE__->mk_accessors(qw(
24             last_api
25             last_api_error
26             last_api_error_code
27             last_api_error_subcode
28             ));
29             sub new {
30 1     1 1 13 my ($class,%args) = @_;
31 1         3 my $tokens = delete $args{tokens};
32 1         17 my $self = $class->SUPER::new(
33             site => SINA_SITE,
34             request_token_path => SINA_REQUEST_TOKEN_PATH,
35             access_token_path => SINA_ACCESS_TOKEN_PATH,
36             authorize_path => SINA_AUTHORIZATION_PATH,
37             %args
38             );
39 1 50 33     16467 if ($tokens->{request_token} && $tokens->{request_token_secret}) {
40 0         0 $self->request_token(OAuth::Lite::Token->new(
41             token => $tokens->{request_token},
42             secret => $tokens->{request_token_secret},
43             ));
44             }
45 1 50 33     10 if ($tokens->{access_token} && $tokens->{access_token_secret}) {
46 0         0 $self->access_token(OAuth::Lite::Token->new(
47             token => $tokens->{access_token},
48             secret => $tokens->{access_token_secret},
49             ));
50             }
51 1 50       4 if ($tokens->{verifier}) {
52 0         0 $self->verifier($tokens->{verifier});
53             }
54 1         7 $self;
55             }
56              
57             sub make_restricted_request {
58 0     0 0 0 my ($self,$url,$method,%params) = @_;
59 0         0 my %multi_parts = ();
60 0 0       0 if ($method eq 'POST') {
61 0         0 foreach my $param (keys %params) {
62 0 0       0 next unless substr($param,0,1) eq '@';
63 0         0 $multi_parts{substr($param,1) } = [delete $params{$param}];
64             }
65             }
66 0         0 my $res = $self->request(
67             method => $method,
68             url => SINA_SITE.'/'.$url.'.'.SINA_FORMAT,
69             token => $self->access_token,
70             params => \%params,
71             multi_parts => { %multi_parts }
72             );
73 0   0     0 my $content = $res->decoded_content || $res->content;
74 0 0       0 unless ($res->is_success) {
75 0         0 $self->_api_error($content,$res->code);
76 0         0 croak $content;
77             }
78 0         0 decode_json($content);
79             }
80             sub _api_error {
81 2     2   1381 my ($self,$error,$http_code) = @_;
82 2         3 eval {
83 2         35 my $error = decode_json($error);
84 1         8 $self->last_api_error($error);
85 1 50       14 $self->last_api_error_code($error->{error_code}) if $error->{error_code};
86 1 50       13 if ($error->{error} =~ /^(\d+):.*/) {
87 1         5 $self->last_api_error_subcode($1);
88             }
89             else {
90 0         0 $self->last_api_error_subcode(0);
91             }
92             };
93 2 100       16 if ($@) {
94 1         6 $self->last_api_error($error);
95 1         12 $self->last_api_error_code($http_code);
96 1         8 $self->last_api_error_subcode(0);
97             }
98             }
99              
100             sub load_tokens {
101 0     0 0   my $class = shift;
102 0           my $file = shift;
103 0           my %tokens = ();
104 0 0         return %tokens unless -f $file;
105              
106 0 0         open(my $fh, $file) || die "Couldn't open $file: $!\n";
107 0           while (<$fh>) {
108 0           chomp;
109 0 0         next if /^#/;
110 0 0         next if /^\s*$/;
111 0 0         next unless /=/;
112 0           s/(^\s*|\s*$)//g;
113 0           my ($key, $val) = split /\s*=\s*/, $_, 2;
114 0           $tokens{$key} = $val;
115             }
116 0           close($fh);
117 0           return %tokens;
118             }
119              
120             sub save_tokens {
121 0     0 0   my $class = shift;
122 0           my $file = shift;
123 0           my %tokens = @_;
124              
125 0           my $max = 0;
126 0           foreach my $key (keys %tokens) {
127 0 0         $max = length($key) if length($key)>$max;
128             }
129              
130 0 0         open(my $fh, ">$file") || die "Couldn't open $file for writing: $!\n";
131 0           foreach my $key (sort keys %tokens) {
132 0           my $pad = " "x($max-length($key));
133 0           print $fh "$key ${pad}= ".$tokens{$key}."\n";
134             }
135 0           close($fh);
136             }
137             sub get_request_token {
138 0     0 1   my $self = shift;
139 0           my $res = $self->_get_request_token(@_);
140 0 0         unless ($res->is_success) {
141 0   0       return $self->error($res->status_line.',res:'.($res->decoded_content||$res->content));
142             }
143 0   0       my $token = OAuth::Lite::Token->from_encoded($res->decoded_content||$res->content);
144             # workaround for SinaWeibo BUG!!
145             # return $self->error(qq/oauth_callback_confirmed is not true/)
146             # unless $token && $token->callback_confirmed;
147 0           $self->request_token($token);
148 0           $token;
149             }
150              
151             sub get_authorize_url {
152 0     0 0   my ($self,%args) = @_;
153 0   0       my $token = $args{token} || $self->request_token;
154 0 0         unless ($token) {
155 0           $token = $self->get_request_token(callback_url => $args{callback_url});
156 0 0         Carp::croak "Can't find request token,err:".$self->errstr unless $token;
157             }
158 0   0       my $url = $args{url} || $self->authorization_url;
159 0           my %params = ();
160 0 0         $params{oauth_token} = ( eval { $token->isa('OAuth::Lite::Token') } )
  0            
161             ? $token->token
162             : $token;
163 0 0         $params{oauth_callback} = $args{callback_url} if exists $args{callback_url};
164 0           $url = URI->new($url);
165 0           $url->query_form(%params);
166 0           $url->as_string;
167             }
168             # override method to support multipart-form
169             sub gen_oauth_request {
170              
171 0     0 1   my ($self, %args) = @_;
172              
173 0   0       my $method = $args{method} || $self->{http_method};
174 0           my $url = $args{url};
175 0           my $content = $args{content};
176 0           my $token = $args{token};
177 0   0       my $extra = $args{params} || {};
178 0   0       my $realm = $args{realm}
179             || $self->{realm}
180             || $self->find_realm_from_last_response
181             || '';
182 0   0       my $multi_parts = $args{multi_parts} || {};
183              
184 0 0         if (ref $extra eq 'ARRAY') {
185 0           my %hash;
186 0           for (0...scalar(@$extra)/2-1) {
187 0           my $key = $extra->[$_ * 2];
188 0           my $value = $extra->[$_ * 2 + 1];
189 0   0       $hash{$key} ||= [];
190 0           push @{ $hash{$key} }, $value;
  0            
191             }
192 0           $extra = \%hash;
193             }
194 0   0       my $headers = $args{headers} || {};
195              
196 0 0         croak 'headers is not valid HASH REF.' unless ref $headers eq 'HASH';
197              
198 0           my @send_data_methods = qw/POST PUT/;
199 0           my @non_send_data_methods = qw/GET HEAD DELETE/;
200              
201 0     0     my $is_send_data_method = any { $method eq $_ } @send_data_methods;
  0            
202              
203 0           my $origin_url = $url;
204 0           my $copied_params = {};
205 0           for my $param_key ( keys %$extra ) {
206 0 0         next if $param_key =~ /^x?oauth_/;
207 0           $copied_params->{$param_key} = $extra->{$param_key};
208             }
209 0 0         if ( keys %$copied_params > 0 ) {
210 0           my $data = normalize_params($copied_params);
211 0 0         $url = sprintf q{%s?%s}, $url, $data unless $is_send_data_method;
212             }
213              
214 0           my $header = $self->gen_auth_header($method, $origin_url,
215             { realm => $realm, token => $token, extra => $extra });
216              
217 0           $headers->{Authorization} = $header;
218 0 0         if ($method eq 'GET') {
    0          
219 0           GET $url,%$headers;
220             }
221             elsif ($method eq 'POST') {
222 0 0         if ( keys %$multi_parts) {
223 0           POST $url,{ %$copied_params, %$multi_parts },'Content-Type' => 'form-data',%$headers;
224             }
225             else {
226 0           POST $url,$copied_params,%$headers;
227             }
228             }
229             else {
230 0           Carp::croak 'unsupported http_method:'.$method;
231             }
232             }
233             1;
234              
235              
236             =pod
237              
238             =head1 NAME
239              
240             Net::SinaWeibo::OAuth - Internal OAuth wrapper round OAuth::Lite::Consumer
241              
242             =head1 VERSION
243              
244             version 0.003
245              
246             =head1 SYNOPSIS
247              
248             =head1 DESCRIPTION
249              
250             =head1 AUTHOR
251              
252             Pan Fan(nightsailer)
253              
254             =head1 COPYRIGHT AND LICENSE
255              
256             This software is copyright (c) 2010 by Pan Fan(nightsailer).
257              
258             This is free software; you can redistribute it and/or modify it under
259             the same terms as the Perl 5 programming language system itself.
260              
261             =cut
262              
263              
264             __END__