flickr 1.0.2 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +20 -0
- data/README.rdoc +185 -0
- data/examples/auth.rb +22 -0
- data/examples/interestingness.rb +7 -0
- data/examples/search.rb +20 -0
- data/examples/sinatra.rb +31 -0
- data/examples/upload.rb +16 -0
- data/examples/web_oauth.rb +47 -0
- data/flickr_rdoc.rb +128 -0
- data/lib/flickr.rb +281 -0
- data/lib/flickr/errors.rb +15 -0
- data/lib/flickr/oauth_client.rb +175 -0
- data/lib/flickr/request.rb +7 -0
- data/lib/flickr/response.rb +38 -0
- data/lib/flickr/response_list.rb +29 -0
- data/lib/flickr/util.rb +13 -0
- data/lib/flickr/version.rb +3 -0
- data/rakefile +22 -0
- data/test/test.rb +471 -0
- data/test/test_cache.rb +45 -0
- data/test/test_request.rb +36 -0
- data/test/test_response.rb +30 -0
- data/test/test_upload.rb +41 -0
- metadata +103 -53
- data/Rakefile +0 -36
- data/flickr.rb +0 -501
- data/index.html +0 -129
- data/test_flickr.rb +0 -173
data/lib/flickr.rb
ADDED
@@ -0,0 +1,281 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'yaml'
|
3
|
+
require 'flickr/version'
|
4
|
+
require 'flickr/util'
|
5
|
+
require 'flickr/errors'
|
6
|
+
require 'flickr/oauth_client'
|
7
|
+
require 'flickr/request'
|
8
|
+
require 'flickr/response'
|
9
|
+
require 'flickr/response_list'
|
10
|
+
|
11
|
+
class Flickr
|
12
|
+
|
13
|
+
USER_AGENT = "Flickr/#{VERSION} (+https://github.com/hanklords/flickraw)".freeze
|
14
|
+
END_POINT = 'https://api.flickr.com/services'.freeze
|
15
|
+
FLICKR_OAUTH_REQUEST_TOKEN = (END_POINT + '/oauth/request_token').freeze
|
16
|
+
FLICKR_OAUTH_AUTHORIZE = (END_POINT + '/oauth/authorize').freeze
|
17
|
+
FLICKR_OAUTH_ACCESS_TOKEN = (END_POINT + '/oauth/access_token').freeze
|
18
|
+
REST_PATH = (END_POINT + '/rest/').freeze
|
19
|
+
UPLOAD_PATH = (END_POINT + '/upload/').freeze
|
20
|
+
REPLACE_PATH = (END_POINT + '/replace/').freeze
|
21
|
+
PHOTO_SOURCE_URL = 'https://farm%s.staticflickr.com/%s/%s_%s%s.%s'.freeze
|
22
|
+
URL_PROFILE = 'https://www.flickr.com/people/'.freeze
|
23
|
+
URL_PHOTOSTREAM = 'https://www.flickr.com/photos/'.freeze
|
24
|
+
URL_SHORT = 'https://flic.kr/p/'.freeze
|
25
|
+
|
26
|
+
# Authenticated access token
|
27
|
+
attr_accessor :access_token
|
28
|
+
|
29
|
+
# Authenticated access token secret
|
30
|
+
attr_accessor :access_secret
|
31
|
+
|
32
|
+
attr_reader :client
|
33
|
+
|
34
|
+
@@initialized = false
|
35
|
+
@@mutex = Mutex.new
|
36
|
+
|
37
|
+
def initialize(api_key = ENV['FLICKR_API_KEY'], shared_secret = ENV['FLICKR_SHARED_SECRET'])
|
38
|
+
|
39
|
+
raise FlickrAppNotConfigured.new("No API key defined!") if api_key.nil?
|
40
|
+
raise FlickrAppNotConfigured.new("No shared secret defined!") if shared_secret.nil?
|
41
|
+
|
42
|
+
@access_token = @access_secret = nil
|
43
|
+
@oauth_consumer = oauth_consumer api_key, shared_secret
|
44
|
+
|
45
|
+
@@mutex.synchronize do
|
46
|
+
unless @@initialized
|
47
|
+
build_classes retrieve_endpoints
|
48
|
+
@@initialized = true
|
49
|
+
end
|
50
|
+
end
|
51
|
+
@client = self # used for propagating the client to sub-classes
|
52
|
+
end
|
53
|
+
|
54
|
+
# This is the central method. It does the actual request to the Flickr server.
|
55
|
+
#
|
56
|
+
# Raises FailedResponse if the response status is _failed_.
|
57
|
+
def call(req, args={}, &block)
|
58
|
+
oauth_args = args.delete(:oauth) || {}
|
59
|
+
http_response = @oauth_consumer.post_form(REST_PATH, @access_secret, {:oauth_token => @access_token}.merge(oauth_args), build_args(args, req))
|
60
|
+
process_response(req, http_response.body)
|
61
|
+
end
|
62
|
+
|
63
|
+
# Get an oauth request token.
|
64
|
+
#
|
65
|
+
# token = flickr.get_request_token(:oauth_callback => "https://example.com")
|
66
|
+
def get_request_token(args = {})
|
67
|
+
@oauth_consumer.request_token(FLICKR_OAUTH_REQUEST_TOKEN, args)
|
68
|
+
end
|
69
|
+
|
70
|
+
# Get the oauth authorize url.
|
71
|
+
#
|
72
|
+
# auth_url = flickr.get_authorize_url(token['oauth_token'], :perms => 'delete')
|
73
|
+
def get_authorize_url(token, args = {})
|
74
|
+
@oauth_consumer.authorize_url(FLICKR_OAUTH_AUTHORIZE, args.merge(:oauth_token => token))
|
75
|
+
end
|
76
|
+
|
77
|
+
# Get an oauth access token.
|
78
|
+
#
|
79
|
+
# flickr.get_access_token(token['oauth_token'], token['oauth_token_secret'], oauth_verifier)
|
80
|
+
def get_access_token(token, secret, verify)
|
81
|
+
access_token = @oauth_consumer.access_token(FLICKR_OAUTH_ACCESS_TOKEN, secret, :oauth_token => token, :oauth_verifier => verify)
|
82
|
+
@access_token, @access_secret = access_token['oauth_token'], access_token['oauth_token_secret']
|
83
|
+
access_token
|
84
|
+
end
|
85
|
+
|
86
|
+
# Use this to upload the photo in _file_.
|
87
|
+
#
|
88
|
+
# flickr.upload_photo '/path/to/the/photo', :title => 'Title', :description => 'This is the description'
|
89
|
+
#
|
90
|
+
# See https://www.flickr.com/services/api/upload.api.html for more information on the arguments.
|
91
|
+
def upload_photo(file, args={})
|
92
|
+
upload_flickr(UPLOAD_PATH, file, args)
|
93
|
+
end
|
94
|
+
|
95
|
+
# Use this to replace the photo with :photo_id with the photo in _file_.
|
96
|
+
#
|
97
|
+
# flickr.replace_photo '/path/to/the/photo', :photo_id => id
|
98
|
+
#
|
99
|
+
# See https://www.flickr.com/services/api/replace.api.html for more information on the arguments.
|
100
|
+
def replace_photo(file, args={})
|
101
|
+
upload_flickr(REPLACE_PATH, file, args)
|
102
|
+
end
|
103
|
+
|
104
|
+
private
|
105
|
+
|
106
|
+
def retrieve_endpoints
|
107
|
+
if Flickr.cache and File.exist?(Flickr.cache)
|
108
|
+
YAML.load_file Flickr.cache
|
109
|
+
else
|
110
|
+
endpoints = call('flickr.reflection.getMethods').to_a
|
111
|
+
File.open(Flickr.cache, 'w') do |file|
|
112
|
+
file.write(YAML.dump endpoints)
|
113
|
+
end if Flickr.cache
|
114
|
+
endpoints
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def oauth_consumer(api_key, shared_secret)
|
119
|
+
client = OAuthClient.new api_key, shared_secret
|
120
|
+
client.proxy = Flickr.proxy
|
121
|
+
client.check_certificate = Flickr.check_certificate
|
122
|
+
client.ca_file = Flickr.ca_file
|
123
|
+
client.ca_path = Flickr.ca_path
|
124
|
+
client.user_agent = USER_AGENT
|
125
|
+
client
|
126
|
+
end
|
127
|
+
|
128
|
+
def build_classes(endpoints)
|
129
|
+
|
130
|
+
endpoints.sort.each do |endpoint|
|
131
|
+
|
132
|
+
*breadcrumbs, tail = endpoint.split '.'
|
133
|
+
|
134
|
+
raise "Invalid namespace" unless 'flickr' == breadcrumbs.shift
|
135
|
+
|
136
|
+
base_class = breadcrumbs.reduce(::Flickr) do |memo, klass|
|
137
|
+
|
138
|
+
cklass = klass.capitalize
|
139
|
+
|
140
|
+
if memo.const_defined? cklass, false
|
141
|
+
memo.const_get cklass
|
142
|
+
else
|
143
|
+
new_class = Class.new { include ::Flickr::Request }
|
144
|
+
memo.const_set cklass, new_class
|
145
|
+
memo.send(:define_method, klass) do
|
146
|
+
new_class.new @client
|
147
|
+
end
|
148
|
+
new_class
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
base_class.send(:define_method, tail) do |*args, &block|
|
153
|
+
@client.call(endpoint, *args, &block)
|
154
|
+
end unless base_class.method_defined? tail
|
155
|
+
|
156
|
+
end
|
157
|
+
|
158
|
+
end
|
159
|
+
|
160
|
+
def build_args(args={}, method_name=nil)
|
161
|
+
args['method'] = method_name if method_name
|
162
|
+
args.merge('format' => 'json', 'nojsoncallback' => '1')
|
163
|
+
end
|
164
|
+
|
165
|
+
def process_response(req, response)
|
166
|
+
puts response.inspect if ENV['FLICKR_DEBUG']
|
167
|
+
|
168
|
+
if /\A<\?xml / === response # upload_photo returns xml data whatever we ask
|
169
|
+
if response[/stat="(\w+)"/, 1] == 'fail'
|
170
|
+
msg = response[/msg="([^"]+)"/, 1]
|
171
|
+
code = response[/code="([^"]+)"/, 1]
|
172
|
+
raise FailedResponse.new(msg, code, req)
|
173
|
+
end
|
174
|
+
|
175
|
+
type = response[/<(\w+)/, 1]
|
176
|
+
h = {
|
177
|
+
'secret' => response[/secret="([^"]+)"/, 1],
|
178
|
+
'originalsecret' => response[/originalsecret="([^"]+)"/, 1],
|
179
|
+
'_content' => response[/>([^<]+)<\//, 1]
|
180
|
+
}.delete_if { |_, v| v.nil? }
|
181
|
+
|
182
|
+
Response.build h, type
|
183
|
+
else
|
184
|
+
json = JSON.load(response.empty? ? '{}' : response)
|
185
|
+
raise FailedResponse.new(json['message'], json['code'], req) if json.delete('stat') == 'fail'
|
186
|
+
type, json = json.to_a.first if json.size == 1 and json.values.all? { |x| Hash === x }
|
187
|
+
|
188
|
+
Response.build json, type
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
def upload_flickr(method, file, args={})
|
193
|
+
oauth_args = args.delete(:oauth) || {}
|
194
|
+
args = build_args(args)
|
195
|
+
if file.respond_to? :read
|
196
|
+
args['photo'] = file
|
197
|
+
else
|
198
|
+
args['photo'] = open(file, 'rb')
|
199
|
+
close_after = true
|
200
|
+
end
|
201
|
+
|
202
|
+
http_response = @oauth_consumer.post_multipart(method, @access_secret, {:oauth_token => @access_token}.merge(oauth_args), args)
|
203
|
+
args['photo'].close if close_after
|
204
|
+
process_response(method, http_response.body)
|
205
|
+
end
|
206
|
+
|
207
|
+
class << self
|
208
|
+
# Your flickr API key, see https://www.flickr.com/services/api/keys for more information
|
209
|
+
attr_accessor :api_key
|
210
|
+
|
211
|
+
# The shared secret of _api_key_, see https://www.flickr.com/services/api/keys for more information
|
212
|
+
attr_accessor :shared_secret
|
213
|
+
|
214
|
+
# Use a proxy
|
215
|
+
attr_accessor :proxy
|
216
|
+
|
217
|
+
# Use ssl connection
|
218
|
+
attr_accessor :secure
|
219
|
+
|
220
|
+
# Check the server certificate (ssl connection only)
|
221
|
+
attr_accessor :check_certificate
|
222
|
+
|
223
|
+
# Set path of a CA certificate file in PEM format (ssl connection only)
|
224
|
+
attr_accessor :ca_file
|
225
|
+
|
226
|
+
# Set path to a directory of CA certificate files in PEM format (ssl connection only)
|
227
|
+
attr_accessor :ca_path
|
228
|
+
|
229
|
+
# Set path to a file that can be used to store endpoints
|
230
|
+
attr_accessor :cache
|
231
|
+
|
232
|
+
BASE58_ALPHABET = '123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ'.freeze
|
233
|
+
|
234
|
+
def base58(id)
|
235
|
+
id = id.to_i
|
236
|
+
alphabet = BASE58_ALPHABET.split(//)
|
237
|
+
base = alphabet.length
|
238
|
+
begin
|
239
|
+
id, m = id.divmod(base)
|
240
|
+
r = alphabet[m] + (r || '')
|
241
|
+
end while id > 0
|
242
|
+
r
|
243
|
+
end
|
244
|
+
|
245
|
+
def url(r); PHOTO_SOURCE_URL % [r.farm, r.server, r.id, r.secret, '', 'jpg'] end
|
246
|
+
def url_m(r); PHOTO_SOURCE_URL % [r.farm, r.server, r.id, r.secret, '_m', 'jpg'] end
|
247
|
+
def url_s(r); PHOTO_SOURCE_URL % [r.farm, r.server, r.id, r.secret, '_s', 'jpg'] end
|
248
|
+
def url_t(r); PHOTO_SOURCE_URL % [r.farm, r.server, r.id, r.secret, '_t', 'jpg'] end
|
249
|
+
def url_b(r); PHOTO_SOURCE_URL % [r.farm, r.server, r.id, r.secret, '_b', 'jpg'] end
|
250
|
+
def url_z(r); PHOTO_SOURCE_URL % [r.farm, r.server, r.id, r.secret, '_z', 'jpg'] end
|
251
|
+
def url_q(r); PHOTO_SOURCE_URL % [r.farm, r.server, r.id, r.secret, '_q', 'jpg'] end
|
252
|
+
def url_n(r); PHOTO_SOURCE_URL % [r.farm, r.server, r.id, r.secret, '_n', 'jpg'] end
|
253
|
+
def url_c(r); PHOTO_SOURCE_URL % [r.farm, r.server, r.id, r.secret, '_c', 'jpg'] end
|
254
|
+
def url_h(r); PHOTO_SOURCE_URL % [r.farm, r.server, r.id, r.secret, '_h', 'jpg'] end
|
255
|
+
def url_k(r); PHOTO_SOURCE_URL % [r.farm, r.server, r.id, r.secret, '_k', 'jpg'] end
|
256
|
+
def url_o(r); PHOTO_SOURCE_URL % [r.farm, r.server, r.id, r.originalsecret, '_o', r.originalformat] end
|
257
|
+
def url_profile(r); URL_PROFILE + (r.owner.respond_to?(:nsid) ? r.owner.nsid : r.owner) + '/' end
|
258
|
+
def url_photopage(r); url_photostream(r) + r.id end
|
259
|
+
def url_photosets(r); url_photostream(r) + 'sets/' end
|
260
|
+
def url_photoset(r); url_photosets(r) + r.id end
|
261
|
+
def url_short(r); URL_SHORT + base58(r.id) end
|
262
|
+
def url_short_m(r); URL_SHORT + 'img/' + base58(r.id) + '_m.jpg' end
|
263
|
+
def url_short_s(r); URL_SHORT + 'img/' + base58(r.id) + '.jpg' end
|
264
|
+
def url_short_t(r); URL_SHORT + 'img/' + base58(r.id) + '_t.jpg' end
|
265
|
+
def url_short_q(r); URL_SHORT + 'img/' + base58(r.id) + '_q.jpg' end
|
266
|
+
def url_short_n(r); URL_SHORT + 'img/' + base58(r.id) + '_n.jpg' end
|
267
|
+
def url_photostream(r)
|
268
|
+
URL_PHOTOSTREAM +
|
269
|
+
if r.respond_to?(:pathalias) && r.pathalias
|
270
|
+
r.pathalias
|
271
|
+
elsif r.owner.respond_to?(:nsid)
|
272
|
+
r.owner.nsid
|
273
|
+
else
|
274
|
+
r.owner
|
275
|
+
end + '/'
|
276
|
+
end
|
277
|
+
end
|
278
|
+
|
279
|
+
self.check_certificate = true
|
280
|
+
|
281
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
class Flickr
|
2
|
+
class Error < StandardError; end
|
3
|
+
|
4
|
+
class FlickrAppNotConfigured < Error; end
|
5
|
+
|
6
|
+
class FailedResponse < Error
|
7
|
+
attr_reader :code
|
8
|
+
alias :msg :message
|
9
|
+
def initialize(msg, code, req)
|
10
|
+
@code = code
|
11
|
+
super("'#{req}' - #{msg}")
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
@@ -0,0 +1,175 @@
|
|
1
|
+
require 'openssl'
|
2
|
+
require 'net/https'
|
3
|
+
|
4
|
+
class Flickr
|
5
|
+
class OAuthClient
|
6
|
+
|
7
|
+
class UnknownSignatureMethod < Error; end
|
8
|
+
|
9
|
+
class FailedResponse < Error
|
10
|
+
def initialize(str)
|
11
|
+
@response = OAuthClient.parse_response(str)
|
12
|
+
super(@response['oauth_problem'])
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class << self
|
17
|
+
def encode_value(v)
|
18
|
+
v = v.to_s.encode('utf-8').force_encoding('ascii-8bit') if RUBY_VERSION >= '1.9'
|
19
|
+
v.to_s
|
20
|
+
end
|
21
|
+
|
22
|
+
def escape(s)
|
23
|
+
encode_value(s).gsub(/[^a-zA-Z0-9\-\.\_\~]/) do |special|
|
24
|
+
special.unpack("C*").map { |i| sprintf("%%%02X", i) }.join
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def parse_response(text); Hash[text.split('&').map { |s| s.split('=') }] end
|
29
|
+
|
30
|
+
def signature_base_string(method, url, params)
|
31
|
+
params_norm = params.map { |k, v| "#{escape(k)}=#{escape(v)}" }.sort.join('&')
|
32
|
+
"#{method.to_s.upcase}&#{escape(url)}&#{escape(params_norm)}"
|
33
|
+
end
|
34
|
+
|
35
|
+
def sign_plaintext(method, url, params, token_secret, consumer_secret)
|
36
|
+
"#{escape(consumer_secret)}&#{escape(token_secret)}"
|
37
|
+
end
|
38
|
+
|
39
|
+
def sign_rsa_sha1(method, url, params, token_secret, consumer_secret)
|
40
|
+
text = signature_base_string(method, url, params)
|
41
|
+
key = OpenSSL::PKey::RSA.new(consumer_secret)
|
42
|
+
digest = OpenSSL::Digest::SHA1.new
|
43
|
+
[key.sign(digest, text)].pack('m0').gsub(/\n$/,'')
|
44
|
+
end
|
45
|
+
|
46
|
+
def sign_hmac_sha1(method, url, params, token_secret, consumer_secret)
|
47
|
+
text = signature_base_string(method, url, params)
|
48
|
+
key = "#{escape(consumer_secret)}&#{escape(token_secret)}"
|
49
|
+
digest = OpenSSL::Digest::SHA1.new
|
50
|
+
[OpenSSL::HMAC.digest(digest, key, text)].pack('m0').gsub(/\n$/,'')
|
51
|
+
end
|
52
|
+
|
53
|
+
def gen_timestamp; Time.now.to_i end
|
54
|
+
|
55
|
+
def gen_nonce; [OpenSSL::Random.random_bytes(32)].pack('m0').gsub(/\n$/,'') end
|
56
|
+
|
57
|
+
def gen_default_params
|
58
|
+
{
|
59
|
+
:oauth_version => "1.0",
|
60
|
+
:oauth_signature_method => 'HMAC-SHA1',
|
61
|
+
:oauth_nonce => gen_nonce,
|
62
|
+
:oauth_timestamp => gen_timestamp,
|
63
|
+
}
|
64
|
+
end
|
65
|
+
|
66
|
+
def authorization_header(url, params)
|
67
|
+
params_norm = params.map { |k, v| %(#{escape(k)}="#{escape(v)}") }.sort.join(', ')
|
68
|
+
%(OAuth realm="#{url.to_s}", #{params_norm})
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
attr_accessor :user_agent
|
73
|
+
attr_reader :proxy
|
74
|
+
attr_accessor :check_certificate
|
75
|
+
attr_accessor :ca_file
|
76
|
+
attr_accessor :ca_path
|
77
|
+
def proxy=(url); @proxy = URI.parse(url || '') end
|
78
|
+
|
79
|
+
def initialize(consumer_key, consumer_secret)
|
80
|
+
@consumer_key, @consumer_secret = consumer_key, consumer_secret
|
81
|
+
self.proxy = nil
|
82
|
+
end
|
83
|
+
|
84
|
+
def request_token(url, oauth_params = {})
|
85
|
+
r = post_form(url, nil, {:oauth_callback => 'oob'}.merge(oauth_params))
|
86
|
+
OAuthClient.parse_response(r.body)
|
87
|
+
end
|
88
|
+
|
89
|
+
def authorize_url(url, oauth_params = {})
|
90
|
+
params_norm = oauth_params.map { |k, v| "#{OAuthClient.escape(k)}=#{OAuthClient.escape(v)}" }.sort.join('&')
|
91
|
+
url = URI.parse(url)
|
92
|
+
url.query = url.query ? "#{url.query}&#{params_norm}" : params_norm
|
93
|
+
url.to_s
|
94
|
+
end
|
95
|
+
|
96
|
+
def access_token(url, token_secret, oauth_params = {})
|
97
|
+
r = post_form(url, token_secret, oauth_params)
|
98
|
+
OAuthClient.parse_response(r.body)
|
99
|
+
end
|
100
|
+
|
101
|
+
def post_form(url, token_secret, oauth_params = {}, params = {})
|
102
|
+
encoded_params = Hash[*params.map { |k, v| [OAuthClient.encode_value(k), OAuthClient.encode_value(v)]}.flatten]
|
103
|
+
post(url, token_secret, oauth_params, params) { |request| request.form_data = encoded_params }
|
104
|
+
end
|
105
|
+
|
106
|
+
def post_multipart(url, token_secret, oauth_params = {}, params = {})
|
107
|
+
post(url, token_secret, oauth_params, params) do |request|
|
108
|
+
boundary = "Flickr#{OAuthClient.gen_nonce}"
|
109
|
+
request['Content-type'] = "multipart/form-data, boundary=#{boundary}"
|
110
|
+
|
111
|
+
request.body = ''
|
112
|
+
params.each do |k, v|
|
113
|
+
if v.respond_to? :read
|
114
|
+
basename = File.basename(v.path.to_s) if v.respond_to? :path
|
115
|
+
basename ||= File.basename(v.base_uri.to_s) if v.respond_to? :base_uri
|
116
|
+
basename ||= "unknown"
|
117
|
+
request.body << "--#{boundary}\r\n" <<
|
118
|
+
"Content-Disposition: form-data; name=\"#{OAuthClient.encode_value(k)}\"; filename=\"#{OAuthClient.encode_value(basename)}\"\r\n" <<
|
119
|
+
"Content-Transfer-Encoding: binary\r\n" <<
|
120
|
+
"Content-Type: image/jpeg\r\n\r\n" <<
|
121
|
+
v.read << "\r\n"
|
122
|
+
else
|
123
|
+
request.body << "--#{boundary}\r\n" <<
|
124
|
+
"Content-Disposition: form-data; name=\"#{OAuthClient.encode_value(k)}\"\r\n\r\n" <<
|
125
|
+
"#{OAuthClient.encode_value(v)}\r\n"
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
request.body << "--#{boundary}--"
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
private
|
134
|
+
|
135
|
+
def sign(method, url, params, token_secret = nil)
|
136
|
+
case params[:oauth_signature_method]
|
137
|
+
when 'HMAC-SHA1'
|
138
|
+
OAuthClient.sign_hmac_sha1(method, url, params, token_secret, @consumer_secret)
|
139
|
+
when 'RSA-SHA1'
|
140
|
+
OAuthClient.sign_rsa_sha1(method, url, params, token_secret, @consumer_secret)
|
141
|
+
when 'PLAINTEXT'
|
142
|
+
OAuthClient.sign_plaintext(method, url, params, token_secret, @consumer_secret)
|
143
|
+
else
|
144
|
+
raise UnknownSignatureMethod, params[:oauth_signature_method]
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
def post(url, token_secret, oauth_params, params)
|
149
|
+
url = URI.parse(url)
|
150
|
+
default_oauth_params = OAuthClient.gen_default_params
|
151
|
+
default_oauth_params[:oauth_consumer_key] = @consumer_key
|
152
|
+
default_oauth_params[:oauth_signature_method] = 'PLAINTEXT' if url.scheme == 'https'
|
153
|
+
oauth_params = default_oauth_params.merge(oauth_params)
|
154
|
+
params_signed = params.reject { |_, v| v.respond_to? :read }.merge(oauth_params)
|
155
|
+
oauth_params[:oauth_signature] = sign(:post, url, params_signed, token_secret)
|
156
|
+
|
157
|
+
http = Net::HTTP.new(url.host, url.port, @proxy.host, @proxy.port, @proxy.user, @proxy.password)
|
158
|
+
http.use_ssl = (url.scheme == 'https')
|
159
|
+
http.verify_mode = (@check_certificate ? OpenSSL::SSL::VERIFY_PEER : OpenSSL::SSL::VERIFY_NONE)
|
160
|
+
http.ca_file = @ca_file
|
161
|
+
http.ca_path = @ca_path
|
162
|
+
r = http.start do |agent|
|
163
|
+
request = Net::HTTP::Post.new(url.path)
|
164
|
+
request['User-Agent'] = @user_agent if @user_agent
|
165
|
+
request['Authorization'] = OAuthClient.authorization_header(url, oauth_params)
|
166
|
+
|
167
|
+
yield request
|
168
|
+
agent.request(request)
|
169
|
+
end
|
170
|
+
|
171
|
+
raise FailedResponse.new(r.body) if r.is_a? Net::HTTPClientError
|
172
|
+
r
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|