dropbox-sdk 1.5.1 → 1.6
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +4 -0
- data/README +52 -7
- data/cli_example.rb +14 -28
- data/dropbox_controller.rb +91 -41
- data/lib/dropbox_sdk.rb +468 -135
- data/web_file_browser.rb +65 -61
- metadata +50 -36
data/CHANGELOG
CHANGED
data/README
CHANGED
@@ -1,7 +1,52 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
1
|
+
Dropbox Core SDK for Ruby
|
2
|
+
|
3
|
+
A Ruby library that for Dropbox's HTTP-based Core API.
|
4
|
+
|
5
|
+
https://www.dropbox.com/developers/core/docs
|
6
|
+
|
7
|
+
----------------------------------
|
8
|
+
Setup
|
9
|
+
|
10
|
+
You can install this package using 'pip':
|
11
|
+
|
12
|
+
# pip install dropbox
|
13
|
+
|
14
|
+
----------------------------------
|
15
|
+
Getting a Dropbox API key
|
16
|
+
|
17
|
+
You need a Dropbox API key to make API requests.
|
18
|
+
- Go to: https://dropbox.com/developers/apps
|
19
|
+
- If you've already registered an app, click on the "Options" link to see the
|
20
|
+
app's API key and secret.
|
21
|
+
- Otherwise, click "Create an app" to register an app. Choose "Full Dropbox" or
|
22
|
+
"App Folder" depending on your needs.
|
23
|
+
See: https://www.dropbox.com/developers/reference#permissions
|
24
|
+
|
25
|
+
----------------------------------
|
26
|
+
Using the Dropbox API
|
27
|
+
|
28
|
+
Full documentation: https://www.dropbox.com/developers/core/
|
29
|
+
|
30
|
+
Before your app can access a Dropbox user's files, the user must authorize your
|
31
|
+
application using OAuth 2. Successfully completing this authorization flow
|
32
|
+
gives you an "access token" for the user's Dropbox account, which grants you the
|
33
|
+
ability to make Dropbox API calls to access their files.
|
34
|
+
|
35
|
+
- Authorization example for a web app: web_file_browser.rb
|
36
|
+
- Authorization example for a command-line tool:
|
37
|
+
https://www.dropbox.com/developers/core/start/ruby
|
38
|
+
|
39
|
+
Once you have an access token, create a DropboxClient instance and start making
|
40
|
+
API calls.
|
41
|
+
|
42
|
+
You only need to perform the authorization process once per user. Once you have
|
43
|
+
an access token for a user, save it somewhere persistent, like in a database.
|
44
|
+
The next time that user visits your app, you can skip the authorization process
|
45
|
+
and go straight to making API calls.
|
46
|
+
|
47
|
+
----------------------------------
|
48
|
+
Running the Examples
|
49
|
+
|
50
|
+
There are example programs included in the tarball. Before you can run an
|
51
|
+
example, you need to edit the ".rb" file and put your Dropbox API app key and
|
52
|
+
secret in the "APP_KEY" and "APP_SECRET" constants.
|
data/cli_example.rb
CHANGED
@@ -12,10 +12,6 @@ require 'pp'
|
|
12
12
|
# Find this at https://www.dropbox.com/developers
|
13
13
|
APP_KEY = ''
|
14
14
|
APP_SECRET = ''
|
15
|
-
ACCESS_TYPE = :app_folder #The two valid values here are :app_folder and :dropbox
|
16
|
-
#The default is :app_folder, but your application might be
|
17
|
-
#set to have full :dropbox access. Check your app at
|
18
|
-
#https://www.dropbox.com/developers/apps
|
19
15
|
|
20
16
|
class DropboxCLI
|
21
17
|
LOGIN_REQUIRED = %w{put get cp mv rm ls mkdir info logout search thumbnail}
|
@@ -27,37 +23,28 @@ class DropboxCLI
|
|
27
23
|
exit
|
28
24
|
end
|
29
25
|
|
30
|
-
@session = DropboxSession.new(APP_KEY, APP_SECRET)
|
31
26
|
@client = nil
|
32
27
|
end
|
33
28
|
|
34
29
|
def login
|
35
|
-
|
36
|
-
|
37
|
-
# from a previous session
|
38
|
-
########
|
39
|
-
# @session.set_access_token('key', 'secret')
|
40
|
-
|
41
|
-
if @session.authorized?
|
42
|
-
puts "already logged in!"
|
30
|
+
if not @client.nil?
|
31
|
+
puts "already logged in!"
|
43
32
|
else
|
33
|
+
web_auth = DropboxOAuth2FlowNoRedirect.new(APP_KEY, APP_SECRET)
|
34
|
+
authorize_url = web_auth.start()
|
35
|
+
puts "1. Go to: #{authorize_url}"
|
36
|
+
puts "2. Click \"Allow\" (you might have to log in first)."
|
37
|
+
puts "3. Copy the authorization code."
|
44
38
|
|
45
|
-
|
46
|
-
|
39
|
+
print "Enter the authorization code here: "
|
40
|
+
STDOUT.flush
|
41
|
+
auth_code = STDIN.gets.strip
|
47
42
|
|
48
|
-
|
49
|
-
puts "Got a request token. Your request token key is #{@session.request_token.key} and your token secret is #{@session.request_token.secret}"
|
50
|
-
|
51
|
-
# make the user log in and authorize this token
|
52
|
-
puts "AUTHORIZING", authorize_url, "Please visit that web page and hit 'Allow', then hit Enter here."
|
53
|
-
gets
|
54
|
-
|
55
|
-
# get the access token from the server. Its then stored in the session.
|
56
|
-
@session.get_access_token
|
43
|
+
access_token, user_id = web_auth.finish(auth_code)
|
57
44
|
|
45
|
+
@client = DropboxClient.new(access_token)
|
46
|
+
puts "You are logged in. Your access token is #{access_token}."
|
58
47
|
end
|
59
|
-
puts "You are logged in. Your access token key is #{@session.access_token.key} your secret is #{@session.access_token.secret}"
|
60
|
-
@client = DropboxClient.new(@session, ACCESS_TYPE)
|
61
48
|
end
|
62
49
|
|
63
50
|
def command_loop
|
@@ -96,9 +83,8 @@ class DropboxCLI
|
|
96
83
|
end
|
97
84
|
|
98
85
|
def logout(command)
|
99
|
-
@session.clear_access_token
|
100
|
-
puts "You are logged out."
|
101
86
|
@client = nil
|
87
|
+
puts "You are logged out."
|
102
88
|
end
|
103
89
|
|
104
90
|
def put(command)
|
data/dropbox_controller.rb
CHANGED
@@ -1,57 +1,107 @@
|
|
1
|
-
|
1
|
+
# ---------------------------------------------------------------------------------------
|
2
|
+
# A Rails 3 controller that:
|
3
|
+
# - Runs the through Dropbox's OAuth 2 flow, yielding a Dropbox API access token.
|
4
|
+
# - Makes a Dropbox API call to upload a file.
|
5
|
+
#
|
6
|
+
# To run:
|
7
|
+
# 1. You need a Rails 3 project (to create one, run: rails new <folder-name>)
|
8
|
+
# 2. Copy this file into <folder-name>/app/controllers/
|
9
|
+
# 3. Add the following lines to <folder-name>/config/routes.rb
|
10
|
+
# get "dropbox/main"
|
11
|
+
# post "dropbox/upload"
|
12
|
+
# get "dropbox/auth_start"
|
13
|
+
# get "dropbox/auth_finish"
|
14
|
+
# 4. Run: rails server
|
15
|
+
# 5. Point your browser at: https://localhost:3000/dropbox/main
|
2
16
|
|
3
|
-
|
4
|
-
|
17
|
+
require '../lib/dropbox_sdk'
|
18
|
+
require 'securerandom'
|
5
19
|
|
6
|
-
# You must set these
|
7
20
|
APP_KEY = ""
|
8
21
|
APP_SECRET = ""
|
9
|
-
ACCESS_TYPE = :app_folder #The two valid values here are :app_folder and :dropbox
|
10
|
-
#The default is :app_folder, but your application might be
|
11
|
-
#set to have full :dropbox access. Check your app at
|
12
|
-
#https://www.dropbox.com/developers/apps
|
13
22
|
|
23
|
+
class DropboxController < ApplicationController
|
14
24
|
|
15
|
-
|
16
|
-
|
17
|
-
|
25
|
+
def main
|
26
|
+
client = get_dropbox_client
|
27
|
+
unless client
|
28
|
+
redirect_to(:action => 'auth_start') and return
|
29
|
+
end
|
18
30
|
|
19
|
-
|
20
|
-
def authorize
|
21
|
-
if not params[:oauth_token] then
|
22
|
-
dbsession = DropboxSession.new(APP_KEY, APP_SECRET)
|
31
|
+
account_info = client.account_info
|
23
32
|
|
24
|
-
|
33
|
+
# Show a file upload page
|
34
|
+
render :inline =>
|
35
|
+
"#{account_info['email']} <br/><%= form_tag({:action => :upload}, :multipart => true) do %><%= file_field_tag 'file' %><%= submit_tag 'Upload' %><% end %>"
|
36
|
+
end
|
25
37
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
dbsession.get_access_token #we've been authorized, so now request an access_token
|
32
|
-
session[:dropbox_session] = dbsession.serialize
|
38
|
+
def upload
|
39
|
+
client = get_dropbox_client
|
40
|
+
unless client
|
41
|
+
redirect_to(:action => 'auth_start') and return
|
42
|
+
end
|
33
43
|
|
34
|
-
|
44
|
+
begin
|
45
|
+
# Upload the POST'd file to Dropbox, keeping the same name
|
46
|
+
resp = client.put_file(params[:file].original_filename, params[:file].read)
|
47
|
+
render :text => "Upload successful. File now at #{resp['path']}"
|
48
|
+
rescue DropboxAuthError => e
|
49
|
+
session.delete(:access_token) # An auth error means the access token is probably bad
|
50
|
+
logger.info "Dropbox auth error: #{e}"
|
51
|
+
render :text => "Dropbox auth error"
|
52
|
+
rescue DropboxError => e
|
53
|
+
logger.info "Dropbox API error: #{e}"
|
54
|
+
render :text => "Dropbox API error"
|
35
55
|
end
|
36
56
|
end
|
37
57
|
|
38
|
-
def
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
58
|
+
def get_dropbox_client
|
59
|
+
if session[:access_token]
|
60
|
+
begin
|
61
|
+
access_token = session[:access_token]
|
62
|
+
DropboxClient.new(access_token)
|
63
|
+
rescue
|
64
|
+
# Maybe something's wrong with the access token?
|
65
|
+
session.delete(:access_token)
|
66
|
+
raise
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def get_web_auth()
|
72
|
+
redirect_uri = url_for(:action => 'auth_finish')
|
73
|
+
DropboxOAuth2Flow.new(APP_KEY, APP_SECRET, redirect_uri, session, :dropbox_auth_csrf_token)
|
74
|
+
end
|
75
|
+
|
76
|
+
def auth_start
|
77
|
+
authorize_url = get_web_auth().start()
|
78
|
+
|
79
|
+
# Send the user to the Dropbox website so they can authorize our app. After the user
|
80
|
+
# authorizes our app, Dropbox will redirect them here with a 'code' parameter.
|
81
|
+
redirect_to authorize_url
|
82
|
+
end
|
83
|
+
|
84
|
+
def auth_finish
|
85
|
+
begin
|
86
|
+
access_token, user_id, url_state = get_web_auth.finish(params)
|
87
|
+
session[:access_token] = access_token
|
88
|
+
redirect_to :action => 'main'
|
89
|
+
rescue DropboxOAuth2Flow::BadRequestError => e
|
90
|
+
render :text => "Error in OAuth 2 flow: Bad request: #{e}"
|
91
|
+
rescue DropboxOAuth2Flow::BadStateError => e
|
92
|
+
logger.info("Error in OAuth 2 flow: No CSRF token in session: #{e}")
|
93
|
+
redirect_to(:action => 'auth_start')
|
94
|
+
rescue DropboxOAuth2Flow::CsrfError => e
|
95
|
+
logger.info("Error in OAuth 2 flow: CSRF mismatch: #{e}")
|
96
|
+
render :text => "CSRF error"
|
97
|
+
rescue DropboxOAuth2Flow::NotApprovedError => e
|
98
|
+
render :text => "Not approved? Why not, bro?"
|
99
|
+
rescue DropboxOAuth2Flow::ProviderError => e
|
100
|
+
logger.info "Error in OAuth 2 flow: Error redirect from Dropbox: #{e}"
|
101
|
+
render :text => "Strange error."
|
102
|
+
rescue DropboxError => e
|
103
|
+
logger.info "Error getting OAuth 2 access token: #{e}"
|
104
|
+
render :text => "Error communicating with Dropbox servers."
|
55
105
|
end
|
56
106
|
end
|
57
107
|
end
|
data/lib/dropbox_sdk.rb
CHANGED
@@ -4,6 +4,9 @@ require 'net/https'
|
|
4
4
|
require 'cgi'
|
5
5
|
require 'json'
|
6
6
|
require 'yaml'
|
7
|
+
require 'base64'
|
8
|
+
require 'securerandom'
|
9
|
+
require 'pp'
|
7
10
|
|
8
11
|
module Dropbox # :nodoc:
|
9
12
|
API_SERVER = "api.dropbox.com"
|
@@ -11,36 +14,17 @@ module Dropbox # :nodoc:
|
|
11
14
|
WEB_SERVER = "www.dropbox.com"
|
12
15
|
|
13
16
|
API_VERSION = 1
|
14
|
-
SDK_VERSION = "1.
|
17
|
+
SDK_VERSION = "1.6"
|
15
18
|
|
16
19
|
TRUSTED_CERT_FILE = File.join(File.dirname(__FILE__), 'trusted-certs.crt')
|
17
|
-
end
|
18
|
-
|
19
|
-
# DropboxSession is responsible for holding OAuth information. It knows how to take your consumer key and secret
|
20
|
-
# and request an access token, an authorize url, and get an access token. You just need to pass it to
|
21
|
-
# DropboxClient after its been authorized.
|
22
|
-
class DropboxSession
|
23
|
-
|
24
|
-
# * consumer_key - Your Dropbox application's "app key".
|
25
|
-
# * consumer_secret - Your Dropbox application's "app secret".
|
26
|
-
def initialize(consumer_key, consumer_secret)
|
27
|
-
@consumer_key = consumer_key
|
28
|
-
@consumer_secret = consumer_secret
|
29
|
-
@request_token = nil
|
30
|
-
@access_token = nil
|
31
|
-
end
|
32
|
-
|
33
|
-
private
|
34
20
|
|
35
|
-
def do_http(uri,
|
21
|
+
def self.do_http(uri, request) # :nodoc:
|
36
22
|
http = Net::HTTP.new(uri.host, uri.port)
|
37
23
|
|
38
24
|
http.use_ssl = true
|
39
|
-
|
25
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
40
26
|
http.ca_file = Dropbox::TRUSTED_CERT_FILE
|
41
27
|
|
42
|
-
request.add_field('Authorization', build_auth_header(auth_token))
|
43
|
-
|
44
28
|
#We use this to better understand how developers are using our SDKs.
|
45
29
|
request['User-Agent'] = "OfficialDropboxRubySDK/#{Dropbox::SDK_VERSION}"
|
46
30
|
|
@@ -48,38 +32,69 @@ class DropboxSession
|
|
48
32
|
http.request(request)
|
49
33
|
rescue OpenSSL::SSL::SSLError => e
|
50
34
|
raise DropboxError.new("SSL error connecting to Dropbox. " +
|
51
|
-
"There may be a problem with the set of certificates in \"#{Dropbox::TRUSTED_CERT_FILE}\". "
|
52
|
-
e)
|
35
|
+
"There may be a problem with the set of certificates in \"#{Dropbox::TRUSTED_CERT_FILE}\". #{e}")
|
53
36
|
end
|
54
37
|
end
|
55
38
|
|
56
|
-
|
57
|
-
|
39
|
+
# Parse response. You probably shouldn't be calling this directly. This takes responses from the server
|
40
|
+
# and parses them. It also checks for errors and raises exceptions with the appropriate messages.
|
41
|
+
def self.parse_response(response, raw=false) # :nodoc:
|
42
|
+
if response.kind_of?(Net::HTTPServerError)
|
43
|
+
raise DropboxError.new("Dropbox Server Error: #{response} - #{response.body}", response)
|
44
|
+
elsif response.kind_of?(Net::HTTPUnauthorized)
|
45
|
+
raise DropboxAuthError.new("User is not authenticated.", response)
|
46
|
+
elsif not response.kind_of?(Net::HTTPSuccess)
|
47
|
+
begin
|
48
|
+
d = JSON.parse(response.body)
|
49
|
+
rescue
|
50
|
+
raise DropboxError.new("Dropbox Server Error: body=#{response.body}", response)
|
51
|
+
end
|
52
|
+
if d['user_error'] and d['error']
|
53
|
+
raise DropboxError.new(d['error'], response, d['user_error']) #user_error is translated
|
54
|
+
elsif d['error']
|
55
|
+
raise DropboxError.new(d['error'], response)
|
56
|
+
else
|
57
|
+
raise DropboxError.new(response.body, response)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
return response.body if raw
|
62
|
+
|
63
|
+
begin
|
64
|
+
return JSON.parse(response.body)
|
65
|
+
rescue JSON::ParserError
|
66
|
+
raise DropboxError.new("Unable to parse JSON response: #{response.body}", response)
|
67
|
+
end
|
58
68
|
end
|
59
69
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
header += "oauth_token=\"#{key}\", oauth_signature=\"#{URI.escape(@consumer_secret)}&#{secret}\""
|
70
|
+
# A string comparison function that is resistant to timing attacks. If you're comparing a
|
71
|
+
# string you got from the outside world with a string that is supposed to be a secret, use
|
72
|
+
# this function to check equality.
|
73
|
+
def self.safe_string_equals(a, b)
|
74
|
+
if a.length != b.length
|
75
|
+
false
|
67
76
|
else
|
68
|
-
|
77
|
+
a.chars.zip(b.chars).map {|ac,bc| ac == bc}.all?
|
69
78
|
end
|
70
|
-
header
|
71
79
|
end
|
80
|
+
end
|
72
81
|
|
73
|
-
|
74
|
-
|
75
|
-
|
82
|
+
class DropboxSessionBase # :nodoc:
|
83
|
+
|
84
|
+
protected
|
85
|
+
|
86
|
+
def do_http(uri, request) # :nodoc:
|
87
|
+
sign_request(request)
|
88
|
+
Dropbox::do_http(uri, request)
|
76
89
|
end
|
77
90
|
|
78
91
|
public
|
79
92
|
|
80
93
|
def do_get(url, headers=nil) # :nodoc:
|
81
94
|
assert_authorized
|
82
|
-
|
95
|
+
uri = URI.parse(url)
|
96
|
+
request = Net::HTTP::Get.new(uri.request_uri)
|
97
|
+
do_http(uri, request)
|
83
98
|
end
|
84
99
|
|
85
100
|
def do_http_with_body(uri, request, body)
|
@@ -103,7 +118,7 @@ class DropboxSession
|
|
103
118
|
request.body = s
|
104
119
|
end
|
105
120
|
end
|
106
|
-
do_http(uri,
|
121
|
+
do_http(uri, request)
|
107
122
|
end
|
108
123
|
|
109
124
|
def do_post(url, headers=nil, body=nil) # :nodoc:
|
@@ -117,7 +132,51 @@ class DropboxSession
|
|
117
132
|
uri = URI.parse(url)
|
118
133
|
do_http_with_body(uri, Net::HTTP::Put.new(uri.request_uri, headers), body)
|
119
134
|
end
|
135
|
+
end
|
120
136
|
|
137
|
+
# DropboxSession is responsible for holding OAuth 1 information. It knows how to take your consumer key and secret
|
138
|
+
# and request an access token, an authorize url, and get an access token. You just need to pass it to
|
139
|
+
# DropboxClient after its been authorized.
|
140
|
+
class DropboxSession < DropboxSessionBase # :nodoc:
|
141
|
+
|
142
|
+
# * consumer_key - Your Dropbox application's "app key".
|
143
|
+
# * consumer_secret - Your Dropbox application's "app secret".
|
144
|
+
def initialize(consumer_key, consumer_secret)
|
145
|
+
@consumer_key = consumer_key
|
146
|
+
@consumer_secret = consumer_secret
|
147
|
+
@request_token = nil
|
148
|
+
@access_token = nil
|
149
|
+
end
|
150
|
+
|
151
|
+
private
|
152
|
+
|
153
|
+
def build_auth_header(token) # :nodoc:
|
154
|
+
header = "OAuth oauth_version=\"1.0\", oauth_signature_method=\"PLAINTEXT\", " +
|
155
|
+
"oauth_consumer_key=\"#{URI.escape(@consumer_key)}\", "
|
156
|
+
if token
|
157
|
+
key = URI.escape(token.key)
|
158
|
+
secret = URI.escape(token.secret)
|
159
|
+
header += "oauth_token=\"#{key}\", oauth_signature=\"#{URI.escape(@consumer_secret)}&#{secret}\""
|
160
|
+
else
|
161
|
+
header += "oauth_signature=\"#{URI.escape(@consumer_secret)}&\""
|
162
|
+
end
|
163
|
+
header
|
164
|
+
end
|
165
|
+
|
166
|
+
def do_get_with_token(url, token, headers=nil) # :nodoc:
|
167
|
+
uri = URI.parse(url)
|
168
|
+
request = Net::HTTP::Get.new(uri.request_uri)
|
169
|
+
request.add_field('Authorization', build_auth_header(token))
|
170
|
+
Dropbox::do_http(uri, request)
|
171
|
+
end
|
172
|
+
|
173
|
+
protected
|
174
|
+
|
175
|
+
def sign_request(request) # :nodoc:
|
176
|
+
request.add_field('Authorization', build_auth_header(@access_token))
|
177
|
+
end
|
178
|
+
|
179
|
+
public
|
121
180
|
|
122
181
|
def get_token(url_end, input_token, error_message_prefix) #: nodoc:
|
123
182
|
response = do_get_with_token("https://#{Dropbox::API_SERVER}:443/#{Dropbox::API_VERSION}/oauth#{url_end}", input_token)
|
@@ -193,7 +252,7 @@ class DropboxSession
|
|
193
252
|
return @access_token if authorized?
|
194
253
|
|
195
254
|
if @request_token.nil?
|
196
|
-
raise
|
255
|
+
raise RuntimeError.new("No request token. You must set this or get an authorize url first.")
|
197
256
|
end
|
198
257
|
|
199
258
|
@access_token = get_token("/access_token", @request_token, "Couldn't get access token.")
|
@@ -244,6 +303,302 @@ class DropboxSession
|
|
244
303
|
end
|
245
304
|
|
246
305
|
|
306
|
+
class DropboxOAuth2Session < DropboxSessionBase # :nodoc:
|
307
|
+
def initialize(oauth2_access_token)
|
308
|
+
if not oauth2_access_token.is_a?(String)
|
309
|
+
raise "bad type for oauth2_access_token (expecting String)"
|
310
|
+
end
|
311
|
+
@access_token = oauth2_access_token
|
312
|
+
end
|
313
|
+
|
314
|
+
def assert_authorized
|
315
|
+
true
|
316
|
+
end
|
317
|
+
|
318
|
+
protected
|
319
|
+
|
320
|
+
def sign_request(request) # :nodoc:
|
321
|
+
request.add_field('Authorization', 'Bearer ' + @access_token)
|
322
|
+
end
|
323
|
+
end
|
324
|
+
|
325
|
+
# Base class for the two OAuth 2 authorization helpers.
|
326
|
+
class DropboxOAuth2FlowBase # :nodoc:
|
327
|
+
def initialize(consumer_key, consumer_secret, locale=nil)
|
328
|
+
if not consumer_key.is_a?(String)
|
329
|
+
raise ArgumentError, "consumer_key must be a String, got #{consumer_key.inspect}"
|
330
|
+
end
|
331
|
+
if not consumer_secret.is_a?(String)
|
332
|
+
raise ArgumentError, "consumer_secret must be a String, got #{consumer_secret.inspect}"
|
333
|
+
end
|
334
|
+
if not (locale.nil? or locale.is_a?(String))
|
335
|
+
raise ArgumentError, "locale must be a String or nil, got #{locale.inspect}"
|
336
|
+
end
|
337
|
+
@consumer_key = consumer_key
|
338
|
+
@consumer_secret = consumer_secret
|
339
|
+
@locale = locale
|
340
|
+
end
|
341
|
+
|
342
|
+
def _get_authorize_url(redirect_uri, state)
|
343
|
+
params = {"client_id" => @consumer_key, "response_type" => "code"}
|
344
|
+
if not redirect_uri.nil?
|
345
|
+
params["redirect_uri"] = redirect_uri
|
346
|
+
end
|
347
|
+
if not state.nil?
|
348
|
+
params["state"] = state
|
349
|
+
end
|
350
|
+
if not @locale.nil?
|
351
|
+
params['locale'] = @locale
|
352
|
+
end
|
353
|
+
|
354
|
+
host = Dropbox::WEB_SERVER
|
355
|
+
path = "/#{Dropbox::API_VERSION}/oauth2/authorize"
|
356
|
+
|
357
|
+
target = URI::Generic.new("https", nil, host, nil, nil, path, nil, nil, nil)
|
358
|
+
|
359
|
+
target.query = params.collect {|k,v|
|
360
|
+
CGI.escape(k) + "=" + CGI.escape(v)
|
361
|
+
}.join("&")
|
362
|
+
|
363
|
+
target.to_s
|
364
|
+
end
|
365
|
+
|
366
|
+
# Finish the OAuth 2 authorization process. If you used a redirect_uri, pass that in.
|
367
|
+
# Will return an access token string that you can use with DropboxClient.
|
368
|
+
def _finish(code, original_redirect_uri)
|
369
|
+
if not code.is_a?(String)
|
370
|
+
raise ArgumentError, "code must be a String"
|
371
|
+
end
|
372
|
+
|
373
|
+
uri = URI.parse("https://#{Dropbox::API_SERVER}/1/oauth2/token")
|
374
|
+
request = Net::HTTP::Post.new(uri.request_uri)
|
375
|
+
client_credentials = @consumer_key + ':' + @consumer_secret
|
376
|
+
request.add_field('Authorization', 'Basic ' + Base64.encode64(client_credentials).chomp("\n"))
|
377
|
+
|
378
|
+
params = {
|
379
|
+
"grant_type" => "authorization_code",
|
380
|
+
"code" => code,
|
381
|
+
}
|
382
|
+
|
383
|
+
if not @locale.nil?
|
384
|
+
params['locale'] = @locale
|
385
|
+
end
|
386
|
+
if not original_redirect_uri.nil?
|
387
|
+
params['redirect_uri'] = original_redirect_uri
|
388
|
+
end
|
389
|
+
|
390
|
+
form_data = {}
|
391
|
+
params.each {|k,v| form_data[k.to_s] = v if !v.nil?}
|
392
|
+
request.set_form_data(form_data)
|
393
|
+
|
394
|
+
response = Dropbox::do_http(uri, request)
|
395
|
+
|
396
|
+
j = Dropbox::parse_response(response)
|
397
|
+
["token_type", "access_token", "uid"].each { |k|
|
398
|
+
if not j.has_key?(k)
|
399
|
+
raise DropboxError.new("Bad response from /token: missing \"#{k}\".")
|
400
|
+
end
|
401
|
+
if not j[k].is_a?(String)
|
402
|
+
raise DropboxError.new("Bad response from /token: field \"#{k}\" is not a string.")
|
403
|
+
end
|
404
|
+
}
|
405
|
+
if j["token_type"] != "bearer" and j["token_type"] != "Bearer":
|
406
|
+
raise DropboxError.new("Bad response from /token: \"token_type\" is \"#{token_type}\".")
|
407
|
+
end
|
408
|
+
|
409
|
+
return j['access_token'], j['uid']
|
410
|
+
end
|
411
|
+
end
|
412
|
+
|
413
|
+
# OAuth 2 authorization helper for apps that can't provide a redirect URI
|
414
|
+
# (such as the command line example apps).
|
415
|
+
class DropboxOAuth2FlowNoRedirect < DropboxOAuth2FlowBase
|
416
|
+
|
417
|
+
# * consumer_key: Your Dropbox API app's "app key"
|
418
|
+
# * consumer_secret: Your Dropbox API app's "app secret"
|
419
|
+
# * locale: The locale of the user currently using your app.
|
420
|
+
def initialize(consumer_key, consumer_secret, locale=nil)
|
421
|
+
super(consumer_key, consumer_secret, locale)
|
422
|
+
end
|
423
|
+
|
424
|
+
# Returns a authorization_url, which is a page on Dropbox's website. Have the user
|
425
|
+
# visit this URL and approve your app.
|
426
|
+
def start()
|
427
|
+
_get_authorize_url(nil, nil)
|
428
|
+
end
|
429
|
+
|
430
|
+
# If the user approves your app, they will be presented with an "authorization code".
|
431
|
+
# Have the user copy/paste that authorization code into your app and then call this
|
432
|
+
# method to get an access token.
|
433
|
+
#
|
434
|
+
# Returns a two-entry list (access_token, user_id)
|
435
|
+
# * access_token is an access token string that can be passed to DropboxClient.
|
436
|
+
# * user_id is the Dropbox user ID of the user that just approved your app.
|
437
|
+
def finish(code)
|
438
|
+
_finish(code, nil)
|
439
|
+
end
|
440
|
+
end
|
441
|
+
|
442
|
+
# The standard OAuth 2 authorization helper. Use this if you're writing a web app.
|
443
|
+
class DropboxOAuth2Flow < DropboxOAuth2FlowBase
|
444
|
+
|
445
|
+
# * consumer_key: Your Dropbox API app's "app key"
|
446
|
+
# * consumer_secret: Your Dropbox API app's "app secret"
|
447
|
+
# * redirect_uri: The URI that the Dropbox server will redirect the user to after the user
|
448
|
+
# finishes authorizing your app. This URI must be HTTPs-based and pre-registered with
|
449
|
+
# the Dropbox servers, though localhost URIs are allowed without pre-registration and can
|
450
|
+
# be either HTTP or HTTPS.
|
451
|
+
# * session: A hash that represents the current web app session (will be used to save the CSRF
|
452
|
+
# token)
|
453
|
+
# * csrf_token_key: The key to use when storing the CSRF token in the session (for example,
|
454
|
+
# :dropbox_auth_csrf_token)
|
455
|
+
# * locale: The locale of the user currently using your app (ex: "en" or "en_US").
|
456
|
+
def initialize(consumer_key, consumer_secret, redirect_uri, session, csrf_token_session_key, locale=nil)
|
457
|
+
super(consumer_key, consumer_secret, locale)
|
458
|
+
if not redirect_uri.is_a?(String)
|
459
|
+
raise ArgumentError, "redirect_uri must be a String, got #{consumer_secret.inspect}"
|
460
|
+
end
|
461
|
+
@redirect_uri = redirect_uri
|
462
|
+
@session = session
|
463
|
+
@csrf_token_session_key = csrf_token_session_key
|
464
|
+
end
|
465
|
+
|
466
|
+
# Starts the OAuth 2 authorizaton process, which involves redirecting the user to
|
467
|
+
# the returned "authorization URL" (a URL on the Dropbox website). When the user then
|
468
|
+
# either approves or denies your app access, Dropbox will redirect them to the
|
469
|
+
# redirect_uri you provided to the constructor, at which point you should call finish()
|
470
|
+
# to complete the process.
|
471
|
+
#
|
472
|
+
# This function will also save a CSRF token to the session and csrf_token_session_key
|
473
|
+
# you provided to the constructor. This CSRF token will be checked on finish() to prevent
|
474
|
+
# request forgery.
|
475
|
+
#
|
476
|
+
# * url_state: Any data you would like to keep in the URL through the authorization
|
477
|
+
# process. This exact value will be returned to you by finish().
|
478
|
+
#
|
479
|
+
# Returns the URL to redirect the user to.
|
480
|
+
def start(url_state=nil)
|
481
|
+
unless url_state.nil? or url_state.is_a?(String)
|
482
|
+
raise ArgumentError, "url_state must be a String"
|
483
|
+
end
|
484
|
+
|
485
|
+
csrf_token = SecureRandom.base64(16)
|
486
|
+
state = csrf_token
|
487
|
+
unless url_state.nil?
|
488
|
+
state += "|" + url_state
|
489
|
+
end
|
490
|
+
@session[@csrf_token_session_key] = csrf_token
|
491
|
+
|
492
|
+
return _get_authorize_url(@redirect_uri, state)
|
493
|
+
end
|
494
|
+
|
495
|
+
# Call this after the user has visited the authorize URL (see: start()), approved your app,
|
496
|
+
# and was redirected to your redirect URI.
|
497
|
+
#
|
498
|
+
# * query_params: The query params on the GET request to your redirect URI.
|
499
|
+
#
|
500
|
+
# Returns a tuple of (access_token, user_id, url_state). access_token can be used to
|
501
|
+
# construct a DropboxClient. user_id is the Dropbox user ID of the user that jsut approved
|
502
|
+
# your app. url_state is the value you originally passed in to start().
|
503
|
+
#
|
504
|
+
# Can throw BadRequestError, BadStateError, CsrfError, NotApprovedError,
|
505
|
+
# ProviderError, and the standard DropboxError.
|
506
|
+
def finish(query_params)
|
507
|
+
csrf_token_from_session = @session[@csrf_token_session_key]
|
508
|
+
|
509
|
+
# Check well-formedness of request.
|
510
|
+
|
511
|
+
state = query_params['state']
|
512
|
+
if state.nil?
|
513
|
+
raise BadRequestError.new("Missing query parameter 'state'.")
|
514
|
+
end
|
515
|
+
|
516
|
+
error = query_params['error']
|
517
|
+
error_description = query_params['error_description']
|
518
|
+
code = query_params['code']
|
519
|
+
|
520
|
+
if not error.nil? and not code.nil?
|
521
|
+
raise BadRequestError.new("Query parameters 'code' and 'error' are both set;" +
|
522
|
+
" only one must be set.")
|
523
|
+
end
|
524
|
+
if error.nil? and code.nil?
|
525
|
+
raise BadRequestError.new("Neither query parameter 'code' or 'error' is set.")
|
526
|
+
end
|
527
|
+
|
528
|
+
# Check CSRF token
|
529
|
+
|
530
|
+
if csrf_token_from_session.nil?
|
531
|
+
raise BadStateError.new("Missing CSRF token in session.");
|
532
|
+
end
|
533
|
+
unless csrf_token_from_session.length > 20
|
534
|
+
raise RuntimeError.new("CSRF token unexpectedly short: #{csrf_token_from_session.inspect}")
|
535
|
+
end
|
536
|
+
|
537
|
+
split_pos = state.index('|')
|
538
|
+
if split_pos.nil?
|
539
|
+
given_csrf_token = state
|
540
|
+
url_state = nil
|
541
|
+
else
|
542
|
+
given_csrf_token, url_state = state.split('|', 2)
|
543
|
+
end
|
544
|
+
if not Dropbox::safe_string_equals(csrf_token_from_session, given_csrf_token)
|
545
|
+
raise CsrfError.new("Expected #{csrf_token_from_session.inspect}, " +
|
546
|
+
"got #{given_csrf_token.inspect}.")
|
547
|
+
end
|
548
|
+
@session.delete(@csrf_token_session_key)
|
549
|
+
|
550
|
+
# Check for error identifier
|
551
|
+
|
552
|
+
if not error.nil?
|
553
|
+
if error == 'access_denied'
|
554
|
+
# The user clicked "Deny"
|
555
|
+
if error_description.nil?
|
556
|
+
raise NotApprovedError.new("No additional description from Dropbox.")
|
557
|
+
else
|
558
|
+
raise NotApprovedError.new("Additional description from Dropbox: #{error_description}")
|
559
|
+
end
|
560
|
+
else
|
561
|
+
# All other errors.
|
562
|
+
full_message = error
|
563
|
+
if not error_description.nil?
|
564
|
+
full_message += ": " + error_description
|
565
|
+
end
|
566
|
+
raise ProviderError.new(full_message)
|
567
|
+
end
|
568
|
+
end
|
569
|
+
|
570
|
+
# If everything went ok, make the network call to get an access token.
|
571
|
+
|
572
|
+
access_token, user_id = _finish(code, @redirect_uri)
|
573
|
+
return access_token, user_id, url_state
|
574
|
+
end
|
575
|
+
|
576
|
+
# Thrown if the redirect URL was missing parameters or if the given parameters were not valid.
|
577
|
+
#
|
578
|
+
# The recommended action is to show an HTTP 400 error page.
|
579
|
+
class BadRequestError < Exception; end
|
580
|
+
|
581
|
+
# Thrown if all the parameters are correct, but there's no CSRF token in the session. This
|
582
|
+
# probably means that the session expired.
|
583
|
+
#
|
584
|
+
# The recommended action is to redirect the user's browser to try the approval process again.
|
585
|
+
class BadStateError < Exception; end
|
586
|
+
|
587
|
+
# Thrown if the given 'state' parameter doesn't contain the CSRF token from the user's session.
|
588
|
+
# This is blocked to prevent CSRF attacks.
|
589
|
+
#
|
590
|
+
# The recommended action is to respond with an HTTP 403 error page.
|
591
|
+
class CsrfError < Exception; end
|
592
|
+
|
593
|
+
# The user chose not to approve your app.
|
594
|
+
class NotApprovedError < Exception; end
|
595
|
+
|
596
|
+
# Dropbox redirected to your redirect URI with some unexpected error identifier and error
|
597
|
+
# message.
|
598
|
+
class ProviderError < Exception; end
|
599
|
+
end
|
600
|
+
|
601
|
+
|
247
602
|
# A class that represents either an OAuth request token or an OAuth access token.
|
248
603
|
class OAuthToken # :nodoc:
|
249
604
|
def initialize(key, secret)
|
@@ -289,19 +644,27 @@ end
|
|
289
644
|
class DropboxNotModified < DropboxError
|
290
645
|
end
|
291
646
|
|
292
|
-
#
|
293
|
-
#
|
647
|
+
# Use this class to make Dropbox API calls. You'll need to obtain an OAuth 2 access token
|
648
|
+
# first; you can get one using either DropboxOAuth2Flow or DropboxOAuth2FlowNoRedirect.
|
294
649
|
class DropboxClient
|
295
650
|
|
296
|
-
#
|
297
|
-
#
|
298
|
-
|
299
|
-
|
651
|
+
# Args:
|
652
|
+
# * +oauth2_access_token+: Obtained via DropboxOAuth2Flow or DropboxOAuth2FlowNoRedirect.
|
653
|
+
# * +locale+: The user's current locale (used to localize error messages).
|
654
|
+
def initialize(oauth2_access_token, root="auto", locale=nil)
|
655
|
+
if oauth2_access_token.is_a?(String)
|
656
|
+
@session = DropboxOAuth2Session.new(oauth2_access_token)
|
657
|
+
elsif oauth2_access_token.is_a?(DropboxSession)
|
658
|
+
@session = oauth2_access_token
|
659
|
+
@session.get_access_token
|
660
|
+
else
|
661
|
+
raise ArgumentError.new("oauth2_access_token doesn't have a valid type")
|
662
|
+
end
|
300
663
|
|
301
664
|
@root = root.to_s # If they passed in a symbol, make it a string
|
302
665
|
|
303
|
-
if not ["dropbox","app_folder"].include?(@root)
|
304
|
-
raise
|
666
|
+
if not ["dropbox","app_folder","auto"].include?(@root)
|
667
|
+
raise ArgumentError.new("root must be :dropbox, :app_folder, or :auto")
|
305
668
|
end
|
306
669
|
if @root == "app_folder"
|
307
670
|
#App Folder is the name of the access type, but for historical reasons
|
@@ -310,63 +673,32 @@ class DropboxClient
|
|
310
673
|
end
|
311
674
|
|
312
675
|
@locale = locale
|
313
|
-
@session = session
|
314
|
-
end
|
315
|
-
|
316
|
-
# Parse response. You probably shouldn't be calling this directly. This takes responses from the server
|
317
|
-
# and parses them. It also checks for errors and raises exceptions with the appropriate messages.
|
318
|
-
def parse_response(response, raw=false) # :nodoc:
|
319
|
-
if response.kind_of?(Net::HTTPServerError)
|
320
|
-
raise DropboxError.new("Dropbox Server Error: #{response} - #{response.body}", response)
|
321
|
-
elsif response.kind_of?(Net::HTTPUnauthorized)
|
322
|
-
raise DropboxAuthError.new(response, "User is not authenticated.")
|
323
|
-
elsif not response.kind_of?(Net::HTTPSuccess)
|
324
|
-
begin
|
325
|
-
d = JSON.parse(response.body)
|
326
|
-
rescue
|
327
|
-
raise DropboxError.new("Dropbox Server Error: body=#{response.body}", response)
|
328
|
-
end
|
329
|
-
if d['user_error'] and d['error']
|
330
|
-
raise DropboxError.new(d['error'], response, d['user_error']) #user_error is translated
|
331
|
-
elsif d['error']
|
332
|
-
raise DropboxError.new(d['error'], response)
|
333
|
-
else
|
334
|
-
raise DropboxError.new(response.body, response)
|
335
|
-
end
|
336
|
-
end
|
337
|
-
|
338
|
-
return response.body if raw
|
339
|
-
|
340
|
-
begin
|
341
|
-
return JSON.parse(response.body)
|
342
|
-
rescue JSON::ParserError
|
343
|
-
raise DropboxError.new("Unable to parse JSON response: #{response.body}", response)
|
344
|
-
end
|
345
676
|
end
|
346
677
|
|
347
|
-
# Returns
|
678
|
+
# Returns some information about the current user's Dropbox account (the "current user"
|
679
|
+
# is the user associated with the access token you're using).
|
348
680
|
#
|
349
681
|
# For a detailed description of what this call returns, visit:
|
350
682
|
# https://www.dropbox.com/developers/reference/api#account-info
|
351
683
|
def account_info()
|
352
684
|
response = @session.do_get build_url("/account/info")
|
353
|
-
parse_response(response)
|
685
|
+
Dropbox::parse_response(response)
|
354
686
|
end
|
355
687
|
|
356
688
|
# Uploads a file to a server. This uses the HTTP PUT upload method for simplicity
|
357
689
|
#
|
358
|
-
#
|
359
|
-
# * to_path
|
690
|
+
# Args:
|
691
|
+
# * +to_path+: The directory path to upload the file to. If the destination
|
360
692
|
# directory does not yet exist, it will be created.
|
361
|
-
# * file_obj
|
693
|
+
# * +file_obj+: A file-like object to upload. If you would like, you can
|
362
694
|
# pass a string as file_obj.
|
363
|
-
# * overwrite
|
695
|
+
# * +overwrite+: Whether to overwrite an existing file at the given path. [default is False]
|
364
696
|
# If overwrite is False and a file already exists there, Dropbox
|
365
697
|
# will rename the upload to make sure it doesn't overwrite anything.
|
366
698
|
# You must check the returned metadata to know what this new name is.
|
367
699
|
# This field should only be True if your intent is to potentially
|
368
700
|
# clobber changes to a file that you don't know about.
|
369
|
-
# * parent_rev
|
701
|
+
# * +parent_rev+: The rev field from the 'parent' of this upload. [optional]
|
370
702
|
# If your intent is to update the file at the given path, you should
|
371
703
|
# pass the parent_rev parameter set to the rev value from the most recent
|
372
704
|
# metadata you have of the existing file at that path. If the server
|
@@ -376,10 +708,11 @@ class DropboxClient
|
|
376
708
|
# The file will always be overwritten if you send the most-recent parent_rev,
|
377
709
|
# and it will never be overwritten you send a less-recent one.
|
378
710
|
# Returns:
|
379
|
-
# * a Hash containing the metadata of the newly uploaded file. The file may have a different
|
711
|
+
# * a Hash containing the metadata of the newly uploaded file. The file may have a different
|
712
|
+
# name if it conflicted.
|
380
713
|
#
|
381
714
|
# Simple Example
|
382
|
-
# client = DropboxClient(
|
715
|
+
# client = DropboxClient(oauth2_access_token)
|
383
716
|
# #session is a DropboxSession I've already authorized
|
384
717
|
# client.put_file('/test_file_on_dropbox', open('/tmp/test_file'))
|
385
718
|
# This will upload the "/tmp/test_file" from my computer into the root of my App's app folder
|
@@ -398,14 +731,14 @@ class DropboxClient
|
|
398
731
|
{"Content-Type" => "application/octet-stream"},
|
399
732
|
file_obj)
|
400
733
|
|
401
|
-
parse_response(response)
|
734
|
+
Dropbox::parse_response(response)
|
402
735
|
end
|
403
736
|
|
404
737
|
# Returns a ChunkedUploader object.
|
405
738
|
#
|
406
739
|
# Args:
|
407
|
-
# * file_obj
|
408
|
-
# * total_size
|
740
|
+
# * +file_obj+: The file-like object to be uploaded. Must support .read()
|
741
|
+
# * +total_size+: The total size of file_obj
|
409
742
|
def get_chunked_uploader(file_obj, total_size)
|
410
743
|
ChunkedUploader.new(self, file_obj, total_size)
|
411
744
|
end
|
@@ -428,7 +761,7 @@ class DropboxClient
|
|
428
761
|
# be called again to resume the upload.
|
429
762
|
#
|
430
763
|
# Args:
|
431
|
-
# * chunk_size
|
764
|
+
# * +chunk_size+: The chunk size for each individual upload. Defaults to 4MB.
|
432
765
|
def upload(chunk_size=4*1024*1024)
|
433
766
|
last_chunk = nil
|
434
767
|
|
@@ -439,7 +772,7 @@ class DropboxClient
|
|
439
772
|
|
440
773
|
resp = {}
|
441
774
|
begin
|
442
|
-
resp =
|
775
|
+
resp = Dropbox::parse_response(@client.partial_chunked_upload(last_chunk, @upload_id, @offset))
|
443
776
|
last_chunk = nil
|
444
777
|
rescue DropboxError => e
|
445
778
|
resp = JSON.parse(e.http_response.body)
|
@@ -457,9 +790,9 @@ class DropboxClient
|
|
457
790
|
# Completes a file upload
|
458
791
|
#
|
459
792
|
# Args:
|
460
|
-
# * to_path
|
793
|
+
# * +to_path+: The directory path to upload the file to. If the destination
|
461
794
|
# directory does not yet exist, it will be created.
|
462
|
-
# * overwrite
|
795
|
+
# * +overwrite+: Whether to overwrite an existing file at the given path. [default is False]
|
463
796
|
# If overwrite is False and a file already exists there, Dropbox
|
464
797
|
# will rename the upload to make sure it doesn't overwrite anything.
|
465
798
|
# You must check the returned metadata to know what this new name is.
|
@@ -481,7 +814,7 @@ class DropboxClient
|
|
481
814
|
# https://www.dropbox.com/developers/reference/api#metadata
|
482
815
|
def finish(to_path, overwrite=false, parent_rev=nil)
|
483
816
|
response = @client.commit_chunked_upload(to_path, @upload_id, overwrite, parent_rev)
|
484
|
-
|
817
|
+
Dropbox::parse_response(response)
|
485
818
|
end
|
486
819
|
end
|
487
820
|
|
@@ -505,28 +838,28 @@ class DropboxClient
|
|
505
838
|
# Download a file
|
506
839
|
#
|
507
840
|
# Args:
|
508
|
-
# * from_path
|
509
|
-
# * rev
|
841
|
+
# * +from_path+: The path to the file to be downloaded
|
842
|
+
# * +rev+: A previous revision value of the file to be downloaded
|
510
843
|
#
|
511
844
|
# Returns:
|
512
845
|
# * The file contents.
|
513
846
|
def get_file(from_path, rev=nil)
|
514
847
|
response = get_file_impl(from_path, rev)
|
515
|
-
parse_response(response, raw=true)
|
848
|
+
Dropbox::parse_response(response, raw=true)
|
516
849
|
end
|
517
850
|
|
518
851
|
# Download a file and get its metadata.
|
519
852
|
#
|
520
853
|
# Args:
|
521
|
-
# * from_path
|
522
|
-
# * rev
|
854
|
+
# * +from_path+: The path to the file to be downloaded
|
855
|
+
# * +rev+: A previous revision value of the file to be downloaded
|
523
856
|
#
|
524
857
|
# Returns:
|
525
858
|
# * The file contents.
|
526
859
|
# * The file metadata as a hash.
|
527
860
|
def get_file_and_metadata(from_path, rev=nil)
|
528
861
|
response = get_file_impl(from_path, rev)
|
529
|
-
parsed_response = parse_response(response, raw=true)
|
862
|
+
parsed_response = Dropbox::parse_response(response, raw=true)
|
530
863
|
metadata = parse_metadata(response)
|
531
864
|
return parsed_response, metadata
|
532
865
|
end
|
@@ -534,8 +867,8 @@ class DropboxClient
|
|
534
867
|
# Download a file (helper method - don't call this directly).
|
535
868
|
#
|
536
869
|
# Args:
|
537
|
-
# * from_path
|
538
|
-
# * rev
|
870
|
+
# * +from_path+: The path to the file to be downloaded
|
871
|
+
# * +rev+: A previous revision value of the file to be downloaded
|
539
872
|
#
|
540
873
|
# Returns:
|
541
874
|
# * The HTTPResponse for the file download request.
|
@@ -551,7 +884,7 @@ class DropboxClient
|
|
551
884
|
# Parses out file metadata from a raw dropbox HTTP response.
|
552
885
|
#
|
553
886
|
# Args:
|
554
|
-
# * dropbox_raw_response
|
887
|
+
# * +dropbox_raw_response+: The raw, unparsed HTTPResponse from Dropbox.
|
555
888
|
#
|
556
889
|
# Returns:
|
557
890
|
# * The metadata of the file as a hash.
|
@@ -569,9 +902,9 @@ class DropboxClient
|
|
569
902
|
|
570
903
|
# Copy a file or folder to a new location.
|
571
904
|
#
|
572
|
-
#
|
573
|
-
# * from_path
|
574
|
-
# * to_path
|
905
|
+
# Args:
|
906
|
+
# * +from_path+: The path to the file or folder to be copied.
|
907
|
+
# * +to_path+: The destination path of the file or folder to be copied.
|
575
908
|
# This parameter should include the destination filename (e.g.
|
576
909
|
# from_path: '/test.txt', to_path: '/dir/test.txt'). If there's
|
577
910
|
# already a file at the to_path, this copy will be renamed to
|
@@ -588,13 +921,13 @@ class DropboxClient
|
|
588
921
|
"to_path" => format_path(to_path, false),
|
589
922
|
}
|
590
923
|
response = @session.do_post build_url("/fileops/copy", params)
|
591
|
-
parse_response(response)
|
924
|
+
Dropbox::parse_response(response)
|
592
925
|
end
|
593
926
|
|
594
927
|
# Create a folder.
|
595
928
|
#
|
596
929
|
# Arguments:
|
597
|
-
# * path
|
930
|
+
# * +path+: The path of the new folder.
|
598
931
|
#
|
599
932
|
# Returns:
|
600
933
|
# * A hash with the metadata of the newly created folder.
|
@@ -607,13 +940,13 @@ class DropboxClient
|
|
607
940
|
}
|
608
941
|
response = @session.do_post build_url("/fileops/create_folder", params)
|
609
942
|
|
610
|
-
parse_response(response)
|
943
|
+
Dropbox::parse_response(response)
|
611
944
|
end
|
612
945
|
|
613
946
|
# Deletes a file
|
614
947
|
#
|
615
948
|
# Arguments:
|
616
|
-
# * path
|
949
|
+
# * +path+: The path of the file to delete
|
617
950
|
#
|
618
951
|
# Returns:
|
619
952
|
# * A Hash with the metadata of file just deleted.
|
@@ -625,14 +958,14 @@ class DropboxClient
|
|
625
958
|
"path" => format_path(path, false),
|
626
959
|
}
|
627
960
|
response = @session.do_post build_url("/fileops/delete", params)
|
628
|
-
parse_response(response)
|
961
|
+
Dropbox::parse_response(response)
|
629
962
|
end
|
630
963
|
|
631
964
|
# Moves a file
|
632
965
|
#
|
633
966
|
# Arguments:
|
634
|
-
# * from_path
|
635
|
-
# * to_path
|
967
|
+
# * +from_path+: The path of the file to be moved
|
968
|
+
# * +to_path+: The destination path of the file or folder to be moved
|
636
969
|
# If the file or folder already exists, it will be renamed to be unique.
|
637
970
|
#
|
638
971
|
# Returns:
|
@@ -646,7 +979,7 @@ class DropboxClient
|
|
646
979
|
"to_path" => format_path(to_path, false),
|
647
980
|
}
|
648
981
|
response = @session.do_post build_url("/fileops/move", params)
|
649
|
-
parse_response(response)
|
982
|
+
Dropbox::parse_response(response)
|
650
983
|
end
|
651
984
|
|
652
985
|
# Retrives metadata for a file or folder
|
@@ -686,7 +1019,7 @@ class DropboxClient
|
|
686
1019
|
if response.kind_of? Net::HTTPRedirection
|
687
1020
|
raise DropboxNotModified.new("metadata not modified")
|
688
1021
|
end
|
689
|
-
parse_response(response)
|
1022
|
+
Dropbox::parse_response(response)
|
690
1023
|
end
|
691
1024
|
|
692
1025
|
# Search directory for filenames matching query
|
@@ -711,7 +1044,7 @@ class DropboxClient
|
|
711
1044
|
}
|
712
1045
|
|
713
1046
|
response = @session.do_get build_url("/search/#{@root}#{format_path(path)}", params)
|
714
|
-
parse_response(response)
|
1047
|
+
Dropbox::parse_response(response)
|
715
1048
|
end
|
716
1049
|
|
717
1050
|
# Retrive revisions of a file
|
@@ -734,7 +1067,7 @@ class DropboxClient
|
|
734
1067
|
}
|
735
1068
|
|
736
1069
|
response = @session.do_get build_url("/revisions/#{@root}#{format_path(path)}", params)
|
737
|
-
parse_response(response)
|
1070
|
+
Dropbox::parse_response(response)
|
738
1071
|
|
739
1072
|
end
|
740
1073
|
|
@@ -754,7 +1087,7 @@ class DropboxClient
|
|
754
1087
|
}
|
755
1088
|
|
756
1089
|
response = @session.do_post build_url("/restore/#{@root}#{format_path(path)}", params)
|
757
|
-
parse_response(response)
|
1090
|
+
Dropbox::parse_response(response)
|
758
1091
|
end
|
759
1092
|
|
760
1093
|
# Returns a direct link to a media file
|
@@ -771,7 +1104,7 @@ class DropboxClient
|
|
771
1104
|
# {'url': 'https://dl.dropbox.com/0/view/wvxv1fw6on24qw7/file.mov', 'expires': 'Thu, 16 Sep 2011 01:01:25 +0000'}
|
772
1105
|
def media(path)
|
773
1106
|
response = @session.do_get build_url("/media/#{@root}#{format_path(path)}")
|
774
|
-
parse_response(response)
|
1107
|
+
Dropbox::parse_response(response)
|
775
1108
|
end
|
776
1109
|
|
777
1110
|
# Get a URL to share a media file
|
@@ -790,7 +1123,7 @@ class DropboxClient
|
|
790
1123
|
# https://www.dropbox.com/developers/reference/api#shares
|
791
1124
|
def shares(path)
|
792
1125
|
response = @session.do_get build_url("/shares/#{@root}#{format_path(path)}")
|
793
|
-
parse_response(response)
|
1126
|
+
Dropbox::parse_response(response)
|
794
1127
|
end
|
795
1128
|
|
796
1129
|
# Download a thumbnail for an image.
|
@@ -806,7 +1139,7 @@ class DropboxClient
|
|
806
1139
|
# * The thumbnail data
|
807
1140
|
def thumbnail(from_path, size='large')
|
808
1141
|
response = thumbnail_impl(from_path, size)
|
809
|
-
parse_response(response, raw=true)
|
1142
|
+
Dropbox::parse_response(response, raw=true)
|
810
1143
|
end
|
811
1144
|
|
812
1145
|
# Download a thumbnail for an image along with the image's metadata.
|
@@ -820,7 +1153,7 @@ class DropboxClient
|
|
820
1153
|
# * The metadata for the image as a hash
|
821
1154
|
def thumbnail_and_metadata(from_path, size='large')
|
822
1155
|
response = thumbnail_impl(from_path, size)
|
823
|
-
parsed_response = parse_response(response, raw=true)
|
1156
|
+
parsed_response = Dropbox::parse_response(response, raw=true)
|
824
1157
|
metadata = parse_metadata(response)
|
825
1158
|
return parsed_response, metadata
|
826
1159
|
end
|
@@ -876,14 +1209,14 @@ class DropboxClient
|
|
876
1209
|
end
|
877
1210
|
|
878
1211
|
response = @session.do_post build_url("/delta", params)
|
879
|
-
parse_response(response)
|
1212
|
+
Dropbox::parse_response(response)
|
880
1213
|
end
|
881
1214
|
|
882
1215
|
# Download a thumbnail (helper method - don't call this directly).
|
883
1216
|
#
|
884
1217
|
# Args:
|
885
|
-
# * from_path
|
886
|
-
# * size
|
1218
|
+
# * +from_path+: The path to the file to be thumbnailed.
|
1219
|
+
# * +size+: A string describing the desired thumbnail size. See thumbnail()
|
887
1220
|
# for details.
|
888
1221
|
#
|
889
1222
|
# Returns:
|
@@ -906,7 +1239,7 @@ class DropboxClient
|
|
906
1239
|
# used to instantly copy that file to the Dropbox of another account.
|
907
1240
|
#
|
908
1241
|
# Args:
|
909
|
-
# * path
|
1242
|
+
# * +path+: The path to the file for a copy ref to be created on.
|
910
1243
|
#
|
911
1244
|
# Returns:
|
912
1245
|
# * A Hash object that looks like the following example:
|
@@ -916,15 +1249,15 @@ class DropboxClient
|
|
916
1249
|
|
917
1250
|
response = @session.do_get(build_url(path, {}))
|
918
1251
|
|
919
|
-
parse_response(response)
|
1252
|
+
Dropbox::parse_response(response)
|
920
1253
|
end
|
921
1254
|
|
922
1255
|
# Adds the file referenced by the copy ref to the specified path
|
923
1256
|
#
|
924
1257
|
# Args:
|
925
|
-
# * copy_ref
|
1258
|
+
# * +copy_ref+: A copy ref string that was returned from a create_copy_ref call.
|
926
1259
|
# The copy_ref can be created from any other Dropbox account, or from the same account.
|
927
|
-
# * to_path
|
1260
|
+
# * +to_path+: The path to where the file will be created.
|
928
1261
|
#
|
929
1262
|
# Returns:
|
930
1263
|
# * A hash with the metadata of the new file.
|
@@ -937,7 +1270,7 @@ class DropboxClient
|
|
937
1270
|
|
938
1271
|
response = @session.do_post(build_url(path, params))
|
939
1272
|
|
940
|
-
parse_response(response)
|
1273
|
+
Dropbox::parse_response(response)
|
941
1274
|
end
|
942
1275
|
|
943
1276
|
def build_url(url, params=nil, content_server=false) # :nodoc:
|
@@ -963,7 +1296,7 @@ class DropboxClient
|
|
963
1296
|
end
|
964
1297
|
|
965
1298
|
#From the oauth spec plus "/". Slash should not be ecsaped
|
966
|
-
RESERVED_CHARACTERS = /[^a-zA-Z0-9\-\.\_\~\/]/
|
1299
|
+
RESERVED_CHARACTERS = /[^a-zA-Z0-9\-\.\_\~\/]/ # :nodoc:
|
967
1300
|
|
968
1301
|
def format_path(path, escape=true) # :nodoc:
|
969
1302
|
path = path.gsub(/\/+/,"/")
|