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__ |