flickr 1.0.2 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|