lib/dbox/api.rb in dbox-0.5.3 vs lib/dbox/api.rb in dbox-0.6.0
- old
+ new
@@ -7,23 +7,28 @@
class API
include Loggable
def self.authorize
- auth = Authenticator.new(conf)
- authorize_url = auth.get_request_token
- puts "Please visit the following URL in your browser, log into Dropbox, and authorize the app you created.\n\n#{authorize_url}\n\nWhen you have done so, press [ENTER] to continue."
+ app_key = ENV["DROPBOX_APP_KEY"]
+ app_secret = ENV["DROPBOX_APP_SECRET"]
+
+ raise(ConfigurationError, "Please set the DROPBOX_APP_KEY environment variable to a Dropbox application key") unless app_key
+ raise(ConfigurationError, "Please set the DROPBOX_APP_SECRET environment variable to a Dropbox application secret") unless app_secret
+
+ auth = DropboxSession.new(app_key, app_secret)
+ puts "Please visit the following URL in your browser, log into Dropbox, and authorize the app you created.\n\n#{auth.get_authorize_url}\n\nWhen you have done so, press [ENTER] to continue."
STDIN.readline
res = auth.get_access_token
- puts "export DROPBOX_AUTH_KEY=#{res.token}"
+ puts "export DROPBOX_AUTH_KEY=#{res.key}"
puts "export DROPBOX_AUTH_SECRET=#{res.secret}"
puts
puts "This auth token will last for 10 years, or when you choose to invalidate it, whichever comes first."
puts
puts "Now either include these constants in yours calls to dbox, or set them as environment variables."
puts "In bash, including them in calls looks like:"
- puts "$ DROPBOX_AUTH_KEY=#{res.token} DROPBOX_AUTH_SECRET=#{res.secret} dbox ..."
+ puts "$ DROPBOX_AUTH_KEY=#{res.key} DROPBOX_AUTH_SECRET=#{res.secret} dbox ..."
end
def self.connect
api = new()
api.connect
@@ -33,136 +38,195 @@
attr_reader :client
# IMPORTANT: API.new is private. Please use API.authorize or API.connect as the entry point.
private_class_method :new
def initialize
- @conf = self.class.conf
end
def initialize_copy(other)
@client = other.client.clone()
end
def connect
+ app_key = ENV["DROPBOX_APP_KEY"]
+ app_secret = ENV["DROPBOX_APP_SECRET"]
auth_key = ENV["DROPBOX_AUTH_KEY"]
auth_secret = ENV["DROPBOX_AUTH_SECRET"]
+ raise(ConfigurationError, "Please set the DROPBOX_APP_KEY environment variable to a Dropbox application key") unless app_key
+ raise(ConfigurationError, "Please set the DROPBOX_APP_SECRET environment variable to a Dropbox application secret") unless app_secret
raise(ConfigurationError, "Please set the DROPBOX_AUTH_KEY environment variable to an authenticated Dropbox session key") unless auth_key
raise(ConfigurationError, "Please set the DROPBOX_AUTH_SECRET environment variable to an authenticated Dropbox session secret") unless auth_secret
- @auth = Authenticator.new(@conf, auth_key, auth_secret)
- @client = DropboxClient.new(@conf["server"], @conf["content_server"], @conf["port"], @auth)
+ @session = DropboxSession.new(app_key, app_secret)
+ @session.set_access_token(auth_key, auth_secret)
+ @client = DropboxClient.new(@session, 'dropbox')
end
def run(path)
begin
res = yield
- case res
- when Hash
- HashWithIndifferentAccess.new(res)
- when String
- res
- when Net::HTTPNotFound
- raise RemoteMissing, "#{path} does not exist on Dropbox"
- when Net::HTTPForbidden
- raise RequestDenied, "Operation on #{path} denied"
- when Net::HTTPNotModified
- :not_modified
- when true
- true
- else
- raise RuntimeError, "Unexpected result: #{res.inspect}"
- end
+ handle_response(path, res) { raise RuntimeError, "Unexpected result: #{res.inspect}" }
+ rescue DropboxNotModified => e
+ :not_modified
+ rescue DropboxAuthError => e
+ raise e
rescue DropboxError => e
- log.debug e.inspect
- raise ServerError, "Server error -- might be a hiccup, please try your request again (#{e.message})"
+ handle_response(path, e.http_response) { raise ServerError, "Server error -- might be a hiccup, please try your request again (#{e.message})" }
end
end
- def metadata(path = "/", hash = nil)
+ def handle_response(path, res, &else_proc)
+ case res
+ when Hash
+ HashWithIndifferentAccess.new(res)
+ when String
+ res
+ when Net::HTTPNotFound
+ raise RemoteMissing, "#{path} does not exist on Dropbox"
+ when Net::HTTPForbidden
+ raise RequestDenied, "Operation on #{path} denied"
+ when Net::HTTPNotModified
+ :not_modified
+ when true
+ true
+ else
+ else_proc.call()
+ end
+ end
+
+ def metadata(path = "/", hash = nil, list=true)
log.debug "Fetching metadata for #{path}"
run(path) do
- res = @client.metadata(@conf["root"], escape_path(path), 10000, hash)
+ res = @client.metadata(path, 10000, list, hash)
log.debug res.inspect
res
end
end
def create_dir(path)
log.info "Creating #{path}"
run(path) do
- case res = @client.file_create_folder(@conf["root"], path)
- when Net::HTTPForbidden
- raise RemoteAlreadyExists, "Either the directory at #{path} already exists, or it has invalid characters in the name"
- else
- res
+ begin
+ @client.file_create_folder(path)
+ rescue DropboxError => e
+ if e.http_response.kind_of?(Net::HTTPForbidden)
+ raise RemoteAlreadyExists, "Either the directory at #{path} already exists, or it has invalid characters in the name"
+ else
+ raise e
+ end
end
end
end
def delete_dir(path)
log.info "Deleting #{path}"
run(path) do
- @client.file_delete(@conf["root"], path)
+ @client.file_delete(path)
end
end
- def get_file(path, output_file_obj)
+ def get_file(path, file_obj, stream=false)
log.info "Downloading #{path}"
- run(path) do
- @client.get_file(@conf["root"], escape_path(path), output_file_obj)
+ unless stream
+ # just download directly using the get_file API
+ res = run(path) { @client.get_file(path) }
+ if res.kind_of?(String)
+ file_obj << res
+ true
+ else
+ raise DropboxError.new("Invalid response #{res.inspect}")
+ end
+ else
+ # use the media API to get a URL that we can stream from, and
+ # then stream the file to disk
+ res = run(path) { @client.media(path) }
+ url = res[:url] if res && res.kind_of?(Hash)
+ if url
+ streaming_download(url, file_obj)
+ else
+ get_file(path, file_obj, false)
+ end
end
end
- def put_file(path, file_obj)
+ def put_file(path, file_obj, previous_revision=nil)
log.info "Uploading #{path}"
+
+ # temporary workaround for bug in Dropbox /put_files API where
+ # passing a valid older parent_rev causes a 500
+ if previous_revision
+ m = metadata(path)
+ if m && m[:rev] && m[:rev] != previous_revision
+ previous_revision = nil
+ end
+ end
+
run(path) do
- dir = File.dirname(path)
- name = File.basename(path)
- @client.put_file(@conf["root"], escape_path(dir), name, file_obj)
+ @client.put_file(path, file_obj, false, previous_revision)
end
end
def delete_file(path)
log.info "Deleting #{path}"
run(path) do
- @client.file_delete(@conf["root"], path)
+ @client.file_delete(path)
end
end
def move(old_path, new_path)
log.info "Moving #{old_path} to #{new_path}"
run(old_path) do
- case res = @client.file_move(@conf["root"], old_path, new_path)
- when Net::HTTPBadRequest
- raise RemoteAlreadyExists, "Error during move -- there may already be a Dropbox folder at #{new_path}"
- else
- res
+ begin
+ @client.file_move(old_path, new_path)
+ rescue DropboxError => e
+ if e.http_response.kind_of?(Net::HTTPForbidden)
+ raise RemoteAlreadyExists, "Error during move -- there may already be a Dropbox folder at #{new_path}"
+ else
+ raise e
+ end
end
end
end
- def escape_path(path)
- path.split("/").map {|s| CGI.escape(s).gsub("+", "%20") }.join("/")
+ def streaming_download(url, io)
+ url = URI.parse(url)
+ http = Net::HTTP.new(url.host, url.port)
+ http.use_ssl = true
+
+ req = Net::HTTP::Get.new(url.request_uri)
+ req["User-Agent"] = "OfficialDropboxRubySDK/#{Dropbox::SDK_VERSION}"
+
+ http.request(req) do |res|
+ if res.kind_of?(Net::HTTPSuccess)
+ # stream into given io
+ res.read_body {|chunk| io.write(chunk) }
+ true
+ else
+ raise DropboxError.new("Invalid response #{res}\n#{res.body}")
+ end
+ end
end
+ end
+end
- def self.conf
- app_key = ENV["DROPBOX_APP_KEY"]
- app_secret = ENV["DROPBOX_APP_SECRET"]
+# monkey-patch DropboxSession to add SSL certificate checking, since the
+# Dropbox Ruby SDK doesn't do it and doesn't have a place to hook into.
+class DropboxSession
+ private
+ def do_http(uri, auth_token, request) # :nodoc:
+ http = Net::HTTP.new(uri.host, uri.port)
+ http.use_ssl = true
- raise(ConfigurationError, "Please set the DROPBOX_APP_KEY environment variable to a Dropbox application key") unless app_key
- raise(ConfigurationError, "Please set the DROPBOX_APP_SECRET environment variable to a Dropbox application secret") unless app_secret
+ # IMPORTANT: other than these two extra lines, this should be
+ # identical to the definition in dropbox_sdk.rb
+ http.verify_mode = OpenSSL::SSL::VERIFY_PEER
+ http.ca_file = File.join(File.dirname(__FILE__), "cacert.pem")
- {
- "server" => "api.dropbox.com",
- "content_server" => "api-content.dropbox.com",
- "port" => 80,
- "request_token_url" => "http://api.dropbox.com/0/oauth/request_token",
- "access_token_url" => "http://api.dropbox.com/0/oauth/access_token",
- "authorization_url" => "http://www.dropbox.com/0/oauth/authorize",
- "root" => "dropbox",
- "consumer_key" => app_key,
- "consumer_secret" => app_secret
- }
- end
+ request.add_field('Authorization', build_auth_header(auth_token))
+
+ #We use this to better understand how developers are using our SDKs.
+ request['User-Agent'] = "OfficialDropboxRubySDK/#{Dropbox::SDK_VERSION}"
+
+ http.request(request)
end
end