dbox 0.5.3 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,83 @@
1
+ == 0.6.0 / 2011-11-24
2
+ * Major Enhancements
3
+ * Upgrade from Dropbox API v0 to v1.
4
+ * Storage of hashes of local files to alleviate problems with moving/copying directories causing files to be detected as modified.
5
+ * Handling of writes that would overwrite unsaved data (files are renamed).
6
+ * SSL support.
7
+ * Sync API.
8
+
9
+ == 0.5.3 / 2011-11-04
10
+ * Major Enhancements
11
+ * Tracking & returning of files that fail to sync.
12
+ * Better consistency during failure scenarios.
13
+ * Minor Enhancements
14
+ * Returning of exit status of 1 when failures occur.
15
+ * Bug Fixes
16
+ * Fixed incorrect handling of some Dropbox server errors.
17
+
18
+ == 0.5.2 / 2011-10-03
19
+ * Bug Fixes
20
+ * Fixed broken database migration.
21
+
22
+ == 0.5.1 / 2011-10-03
23
+ * Bug Fixes
24
+ * Fixed cascading deletes on directory deletion.
25
+ * Fixed support for moving/renaming local repository.
26
+
27
+ == 0.5.0 / 2011-09-19
28
+ * Major Enhancements
29
+ * Complete rewrite of metadata storage to use SQLite database.
30
+ * Streaming downloads.
31
+ * Parallelized downloads and uploads.
32
+ * Minor Enhancements
33
+ * Atomic saving of downloaded files to eliminate partial files if killed during download.
34
+
35
+ == 0.4.4 / 2011-06-28
36
+ * Minor Enhancements
37
+ * Detection of corrupt database.
38
+ * Added gem dependencies.
39
+ * Bug Fixes
40
+ * Fixed Ruby 1.9 support.
41
+ * Fixed DB corruption issue when hard killing process.
42
+
43
+ == 0.4.3 / 2011-05-18
44
+ * Minor Enhancements
45
+ * More robust DB saving to handle errors after partial pushes or pulls.
46
+ * Bug Fixes
47
+ * Fixed revision stamping.
48
+ * Fixed sorting of filenames in changelist.
49
+
50
+ == 0.4.2 / 2011-05-17
51
+ * Bug Fixes
52
+ * Fixed directories with certain characters: []{}*?.
53
+ * Fixed URL encoding.
54
+
55
+ == 0.4.1 / 2011-05-16
56
+ * Minor Enhancements
57
+ * Added Dbox.exists? to Ruby API.
58
+ * Improved logging.
59
+
60
+ == 0.4.0 / 2011-05-16
61
+ * Major Enhancements
62
+ * Added move API to rename remote folder.
63
+
64
+ == 0.3.0 / 2011-05-14
65
+ * Major Enhancements
66
+ * Added returning of changelist from push & pull operations.
67
+ * Improved error handling.
68
+ * Minor Enhancements
69
+ * Logging cleanup.
70
+ * Bug Fixes
71
+ * Fixed re-download after upload issue.
72
+
73
+ == 0.2.0 / 2011-05-14
74
+ * Major Enhancements
75
+ * Logging.
76
+ * Tests.
77
+
78
+ == 0.1.0 / 2011-05-13
79
+ * Inaugural Release
80
+ * Ruby API.
81
+ * Command-line client.
82
+ * Create, clone, pull, push support.
83
+ * Metadata database to track synchronized content.
data/README.md CHANGED
@@ -1,9 +1,13 @@
1
1
  dbox
2
2
  ====
3
3
 
4
- An easy way to push and pull your Dropbox folders, with fine-grained control over what folder you are syncing, where you are syncing it to, and when you are doing it.
4
+ Dropbox integration made easy.
5
5
 
6
- **IMPORTANT:** This is **not** an automated Dropbox client. It will exit after sucessfully pushing/pulling, so if you want regular updates, you can run it in cron, a loop, etc. If you do want to run it in a loop, take a look at [sample_polling_script.rb](http://github.com/kenpratt/dbox/blob/master/sample_polling_script.rb).
6
+ Push and pull your Dropbox folders, with fine-grained control over what folder you are syncing, where you are syncing it to, and when you are doing it.
7
+
8
+ Take your pick: a command-line client or a Ruby API.
9
+
10
+ **IMPORTANT:** This is **not** an automated Dropbox client. It will exit after sucessfully pushing/pulling, so if you want regular updates, you can run it in cron, a loop, etc. If you do want to run it in a loop, take a look at [sample_polling_script.rb](http://github.com/kenpratt/dbox/blob/master/sample_polling_script.rb). You get deterministic control over what you want Dropbox to do and when you want it to happen.
7
11
 
8
12
 
9
13
  Installation
@@ -17,7 +21,7 @@ $ gem install dbox
17
21
 
18
22
  ### Get developer keys
19
23
 
20
- * Follow the instructions at https://www.dropbox.com/developers/quickstart to create a Dropbox development application, and copy the application keys. Unless you get your app approved for production status, these keys will only work with the account you create them under, so make sure you are logged in with the account you want to access from dbox.
24
+ * Follow the instructions at https://www.dropbox.com/developers/quickstart to create a Dropbox development application, and copy the application keys. Unless you get your app approved for production status, these keys will only work with the account you create them under, so make sure you are logged in with the account you want to access from ```dbox```.
21
25
 
22
26
  * Now either set the keys as environment variables:
23
27
 
@@ -26,7 +30,7 @@ $ export DROPBOX_APP_KEY=cmlrrjd3j0gbend
26
30
  $ export DROPBOX_APP_SECRET=uvuulp75xf9jffl
27
31
  ```
28
32
 
29
- * Or include them in calls to dbox:
33
+ * Or include them in calls to ```dbox```:
30
34
 
31
35
  ```sh
32
36
  $ DROPBOX_APP_KEY=cmlrrjd3j0gbend DROPBOX_APP_SECRET=uvuulp75xf9jffl dbox ...
@@ -53,7 +57,7 @@ $ export DROPBOX_AUTH_KEY=v4d7l1rez1czksn
53
57
  $ export DROPBOX_AUTH_SECRET=pqej9rmnj0i1gcxr4
54
58
  ```
55
59
 
56
- * Or include them in calls to dbox:
60
+ * Or include them in calls to ```dbox```:
57
61
 
58
62
  ```sh
59
63
  $ DROPBOX_AUTH_KEY=v4d7l1rez1czksn DROPBOX_AUTH_SECRET=pqej9rmnj0i1gcxr4 dbox ...
@@ -97,6 +101,12 @@ $ dbox pull [<local_path>]
97
101
  $ dbox push [<local_path>]
98
102
  ```
99
103
 
104
+ #### Sync (pull changes from Dropbox, then push changes to Dropbox)
105
+
106
+ ```sh
107
+ $ dbox sync [<local_path>]
108
+ ```
109
+
100
110
  #### Move (move/rename the Dropbox folder)
101
111
 
102
112
  ```sh
@@ -146,12 +156,20 @@ Oh, Hello
146
156
  Using dbox from Ruby
147
157
  --------------------
148
158
 
149
- The Ruby clone, pull, and push APIs return a hash listing the changes made during that pull/push. If any failures were encountered while uploading or downloading from Dropbox, they will be shown in the ```:failed``` entry in the hash. Often, trying your operation again will resolve the failures as the Dropbox API returns errors for valid operations on occasion.
159
+ The Ruby clone, pull, and push APIs return a hash of the changes made during that operation. If any failures were encountered while uploading or downloading from Dropbox, they will be shown in the ```:failed``` entry in the hash. Often, trying your operation again will resolve the failures as the Dropbox API occasionally returns errors for valid operations.
150
160
 
151
161
  ```ruby
152
- { :created => ["foo.txt"], :deleted => [], :updated => [], :failed => [] }
162
+ { :created => ["foo.txt"], :deleted => [], :updated => [] :failed => [] }
153
163
  ```
154
164
 
165
+ If any conflicts occur where file contents would be lost, the conflicting file is renamed and the resulting hash has a ```:conflicts``` entry. On a push operation, the conflicting file being pushed will be renamed. On a pull, the existing file that would have been overwritten will be renamed and the downloaded file will take the name (as that will keep multiple clients in sync).
166
+
167
+ ```ruby
168
+ { :created => [], :updated => [], :deleted => [], :conflicts => [{ :original => "foo.txt", :renamed => "foo (1).txt" }], :failed => [] }
169
+ ```
170
+
171
+ The sync API returns a hash with two entries: ```:push``` and ```:pull```, which contain the change hashes for the two operations.
172
+
155
173
  ### Usage
156
174
 
157
175
  #### Setup
@@ -186,6 +204,12 @@ Dbox.pull(local_path)
186
204
  Dbox.push(local_path)
187
205
  ```
188
206
 
207
+ #### Sync (pull changes from Dropbox, then push changes to Dropbox)
208
+
209
+ ```ruby
210
+ Dbox.sync(local_path)
211
+ ```
212
+
189
213
  #### Move (move/rename the Dropbox folder)
190
214
 
191
215
  ```ruby
data/TODO.txt CHANGED
@@ -1,6 +1,6 @@
1
+ * Make Dbox case insentive on case sensitive filesystems, as Dropbox is case insensitive.
1
2
  * Refactor threading in change detection in pull to use ParallelTasks?
2
3
  * Saving SQLite db changes dir timestamp -- try to preserve it?
3
- * Add a "sync" command that pushes and pulls in one go
4
4
  * See if prepared statements speed up operations on large repos much
5
5
  * Look down directory tree until you hit a .dbox.sqlite3 file so you can use commands from anywhere inside a tree (like git)
6
6
  * Add support for partial push/pull (subtree push/pull)?
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.5.3
1
+ 0.6.0
data/bin/dbox CHANGED
@@ -14,6 +14,7 @@ Commands:
14
14
  clone <remote_path> [<local_path>] Clone an existing Dropbox folder
15
15
  pull [<local_path>] Pull chonges from Dropbox
16
16
  push [<local_path>] Push changes to Dropbox
17
+ sync [<local_path>] Sync changes to Dropbox
17
18
  move <new_remote_path> [<local_path>] Move the remote Dropbox folder to a new location
18
19
 
19
20
  Environment varables needed for everything:
@@ -53,7 +54,7 @@ when "create", "clone"
53
54
 
54
55
  res = Dbox.send(command, remote_path, local_path)
55
56
  exit 1 if res[:failed].size > 0
56
- when "pull", "push"
57
+ when "pull", "push", "sync"
57
58
  # default to current directory
58
59
  local_path = args[0] || "."
59
60
 
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = "dbox"
8
- s.version = "0.5.3"
8
+ s.version = "0.6.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Ken Pratt"]
12
- s.date = "2011-11-04"
12
+ s.date = "2011-11-24"
13
13
  s.description = "An easy-to-use Dropbox client with fine-grained control over syncs."
14
14
  s.email = "ken@kenpratt.net"
15
15
  s.executables = ["dbox"]
@@ -19,6 +19,7 @@ Gem::Specification.new do |s|
19
19
  ]
20
20
  s.files = [
21
21
  ".document",
22
+ "History.txt",
22
23
  "LICENSE.txt",
23
24
  "README.md",
24
25
  "Rakefile",
@@ -28,23 +29,24 @@ Gem::Specification.new do |s|
28
29
  "dbox.gemspec",
29
30
  "lib/dbox.rb",
30
31
  "lib/dbox/api.rb",
32
+ "lib/dbox/cacert.pem",
31
33
  "lib/dbox/database.rb",
32
34
  "lib/dbox/db.rb",
33
35
  "lib/dbox/loggable.rb",
34
36
  "lib/dbox/parallel_tasks.rb",
35
37
  "lib/dbox/syncer.rb",
38
+ "lib/dbox/utils.rb",
36
39
  "sample_polling_script.rb",
37
40
  "spec/dbox_spec.rb",
38
41
  "spec/spec_helper.rb",
39
- "vendor/dropbox-client-ruby/LICENSE",
40
- "vendor/dropbox-client-ruby/README",
41
- "vendor/dropbox-client-ruby/Rakefile",
42
- "vendor/dropbox-client-ruby/config/testing.json.example",
43
- "vendor/dropbox-client-ruby/lib/dropbox.rb",
44
- "vendor/dropbox-client-ruby/manifest",
45
- "vendor/dropbox-client-ruby/test/authenticator_test.rb",
46
- "vendor/dropbox-client-ruby/test/client_test.rb",
47
- "vendor/dropbox-client-ruby/test/util.rb"
42
+ "vendor/dropbox-ruby-sdk/CHANGELOG",
43
+ "vendor/dropbox-ruby-sdk/LICENSE",
44
+ "vendor/dropbox-ruby-sdk/README",
45
+ "vendor/dropbox-ruby-sdk/cli_example.rb",
46
+ "vendor/dropbox-ruby-sdk/dropbox_controller.rb",
47
+ "vendor/dropbox-ruby-sdk/gemspec.rb",
48
+ "vendor/dropbox-ruby-sdk/lib/dropbox_sdk.rb",
49
+ "vendor/dropbox-ruby-sdk/web_file_browser.rb"
48
50
  ]
49
51
  s.homepage = "http://github.com/kenpratt/dbox"
50
52
  s.licenses = ["MIT"]
@@ -1,8 +1,8 @@
1
1
  ROOT_PATH = File.expand_path(File.join(File.dirname(__FILE__), ".."))
2
2
  $:.unshift File.join(ROOT_PATH, "lib")
3
- $:.unshift File.join(ROOT_PATH, "vendor", "dropbox-client-ruby", "lib")
3
+ $:.unshift File.join(ROOT_PATH, "vendor", "dropbox-ruby-sdk", "lib")
4
4
 
5
- require "dropbox"
5
+ require "dropbox_sdk"
6
6
  require "fileutils"
7
7
  require "time"
8
8
  require "yaml"
@@ -12,6 +12,7 @@ require "sqlite3"
12
12
  require "active_support/core_ext/hash/indifferent_access"
13
13
 
14
14
  require "dbox/loggable"
15
+ require "dbox/utils"
15
16
  require "dbox/api"
16
17
  require "dbox/database"
17
18
  require "dbox/db"
@@ -53,6 +54,14 @@ module Dbox
53
54
  Dbox::Syncer.push(local_path)
54
55
  end
55
56
 
57
+ def self.sync(local_path)
58
+ log.debug "Syncing (local: #{local_path})"
59
+ res = {}
60
+ res[:pull] = pull(local_path)
61
+ res[:push] = push(local_path)
62
+ res
63
+ end
64
+
56
65
  def self.move(new_remote_path, local_path)
57
66
  log.debug "Moving (new remote: #{new_remote_path}, local: #{local_path})"
58
67
  new_remote_path = clean_remote_path(new_remote_path)
@@ -9,19 +9,24 @@ module Dbox
9
9
  include Loggable
10
10
 
11
11
  def self.authorize
12
- auth = Authenticator.new(conf)
13
- authorize_url = auth.get_request_token
14
- 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."
12
+ app_key = ENV["DROPBOX_APP_KEY"]
13
+ app_secret = ENV["DROPBOX_APP_SECRET"]
14
+
15
+ raise(ConfigurationError, "Please set the DROPBOX_APP_KEY environment variable to a Dropbox application key") unless app_key
16
+ raise(ConfigurationError, "Please set the DROPBOX_APP_SECRET environment variable to a Dropbox application secret") unless app_secret
17
+
18
+ auth = DropboxSession.new(app_key, app_secret)
19
+ 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."
15
20
  STDIN.readline
16
21
  res = auth.get_access_token
17
- puts "export DROPBOX_AUTH_KEY=#{res.token}"
22
+ puts "export DROPBOX_AUTH_KEY=#{res.key}"
18
23
  puts "export DROPBOX_AUTH_SECRET=#{res.secret}"
19
24
  puts
20
25
  puts "This auth token will last for 10 years, or when you choose to invalidate it, whichever comes first."
21
26
  puts
22
27
  puts "Now either include these constants in yours calls to dbox, or set them as environment variables."
23
28
  puts "In bash, including them in calls looks like:"
24
- puts "$ DROPBOX_AUTH_KEY=#{res.token} DROPBOX_AUTH_SECRET=#{res.secret} dbox ..."
29
+ puts "$ DROPBOX_AUTH_KEY=#{res.key} DROPBOX_AUTH_SECRET=#{res.secret} dbox ..."
25
30
  end
26
31
 
27
32
  def self.connect
@@ -35,7 +40,6 @@ module Dbox
35
40
  # IMPORTANT: API.new is private. Please use API.authorize or API.connect as the entry point.
36
41
  private_class_method :new
37
42
  def initialize
38
- @conf = self.class.conf
39
43
  end
40
44
 
41
45
  def initialize_copy(other)
@@ -43,45 +47,57 @@ module Dbox
43
47
  end
44
48
 
45
49
  def connect
50
+ app_key = ENV["DROPBOX_APP_KEY"]
51
+ app_secret = ENV["DROPBOX_APP_SECRET"]
46
52
  auth_key = ENV["DROPBOX_AUTH_KEY"]
47
53
  auth_secret = ENV["DROPBOX_AUTH_SECRET"]
48
54
 
55
+ raise(ConfigurationError, "Please set the DROPBOX_APP_KEY environment variable to a Dropbox application key") unless app_key
56
+ raise(ConfigurationError, "Please set the DROPBOX_APP_SECRET environment variable to a Dropbox application secret") unless app_secret
49
57
  raise(ConfigurationError, "Please set the DROPBOX_AUTH_KEY environment variable to an authenticated Dropbox session key") unless auth_key
50
58
  raise(ConfigurationError, "Please set the DROPBOX_AUTH_SECRET environment variable to an authenticated Dropbox session secret") unless auth_secret
51
59
 
52
- @auth = Authenticator.new(@conf, auth_key, auth_secret)
53
- @client = DropboxClient.new(@conf["server"], @conf["content_server"], @conf["port"], @auth)
60
+ @session = DropboxSession.new(app_key, app_secret)
61
+ @session.set_access_token(auth_key, auth_secret)
62
+ @client = DropboxClient.new(@session, 'dropbox')
54
63
  end
55
64
 
56
65
  def run(path)
57
66
  begin
58
67
  res = yield
59
- case res
60
- when Hash
61
- HashWithIndifferentAccess.new(res)
62
- when String
63
- res
64
- when Net::HTTPNotFound
65
- raise RemoteMissing, "#{path} does not exist on Dropbox"
66
- when Net::HTTPForbidden
67
- raise RequestDenied, "Operation on #{path} denied"
68
- when Net::HTTPNotModified
69
- :not_modified
70
- when true
71
- true
72
- else
73
- raise RuntimeError, "Unexpected result: #{res.inspect}"
74
- end
68
+ handle_response(path, res) { raise RuntimeError, "Unexpected result: #{res.inspect}" }
69
+ rescue DropboxNotModified => e
70
+ :not_modified
71
+ rescue DropboxAuthError => e
72
+ raise e
75
73
  rescue DropboxError => e
76
- log.debug e.inspect
77
- raise ServerError, "Server error -- might be a hiccup, please try your request again (#{e.message})"
74
+ handle_response(path, e.http_response) { raise ServerError, "Server error -- might be a hiccup, please try your request again (#{e.message})" }
75
+ end
76
+ end
77
+
78
+ def handle_response(path, res, &else_proc)
79
+ case res
80
+ when Hash
81
+ HashWithIndifferentAccess.new(res)
82
+ when String
83
+ res
84
+ when Net::HTTPNotFound
85
+ raise RemoteMissing, "#{path} does not exist on Dropbox"
86
+ when Net::HTTPForbidden
87
+ raise RequestDenied, "Operation on #{path} denied"
88
+ when Net::HTTPNotModified
89
+ :not_modified
90
+ when true
91
+ true
92
+ else
93
+ else_proc.call()
78
94
  end
79
95
  end
80
96
 
81
- def metadata(path = "/", hash = nil)
97
+ def metadata(path = "/", hash = nil, list=true)
82
98
  log.debug "Fetching metadata for #{path}"
83
99
  run(path) do
84
- res = @client.metadata(@conf["root"], escape_path(path), 10000, hash)
100
+ res = @client.metadata(path, 10000, list, hash)
85
101
  log.debug res.inspect
86
102
  res
87
103
  end
@@ -90,11 +106,14 @@ module Dbox
90
106
  def create_dir(path)
91
107
  log.info "Creating #{path}"
92
108
  run(path) do
93
- case res = @client.file_create_folder(@conf["root"], path)
94
- when Net::HTTPForbidden
95
- raise RemoteAlreadyExists, "Either the directory at #{path} already exists, or it has invalid characters in the name"
96
- else
97
- res
109
+ begin
110
+ @client.file_create_folder(path)
111
+ rescue DropboxError => e
112
+ if e.http_response.kind_of?(Net::HTTPForbidden)
113
+ raise RemoteAlreadyExists, "Either the directory at #{path} already exists, or it has invalid characters in the name"
114
+ else
115
+ raise e
116
+ end
98
117
  end
99
118
  end
100
119
  end
@@ -102,67 +121,112 @@ module Dbox
102
121
  def delete_dir(path)
103
122
  log.info "Deleting #{path}"
104
123
  run(path) do
105
- @client.file_delete(@conf["root"], path)
124
+ @client.file_delete(path)
106
125
  end
107
126
  end
108
127
 
109
- def get_file(path, output_file_obj)
128
+ def get_file(path, file_obj, stream=false)
110
129
  log.info "Downloading #{path}"
111
- run(path) do
112
- @client.get_file(@conf["root"], escape_path(path), output_file_obj)
130
+ unless stream
131
+ # just download directly using the get_file API
132
+ res = run(path) { @client.get_file(path) }
133
+ if res.kind_of?(String)
134
+ file_obj << res
135
+ true
136
+ else
137
+ raise DropboxError.new("Invalid response #{res.inspect}")
138
+ end
139
+ else
140
+ # use the media API to get a URL that we can stream from, and
141
+ # then stream the file to disk
142
+ res = run(path) { @client.media(path) }
143
+ url = res[:url] if res && res.kind_of?(Hash)
144
+ if url
145
+ streaming_download(url, file_obj)
146
+ else
147
+ get_file(path, file_obj, false)
148
+ end
113
149
  end
114
150
  end
115
151
 
116
- def put_file(path, file_obj)
152
+ def put_file(path, file_obj, previous_revision=nil)
117
153
  log.info "Uploading #{path}"
154
+
155
+ # temporary workaround for bug in Dropbox /put_files API where
156
+ # passing a valid older parent_rev causes a 500
157
+ if previous_revision
158
+ m = metadata(path)
159
+ if m && m[:rev] && m[:rev] != previous_revision
160
+ previous_revision = nil
161
+ end
162
+ end
163
+
118
164
  run(path) do
119
- dir = File.dirname(path)
120
- name = File.basename(path)
121
- @client.put_file(@conf["root"], escape_path(dir), name, file_obj)
165
+ @client.put_file(path, file_obj, false, previous_revision)
122
166
  end
123
167
  end
124
168
 
125
169
  def delete_file(path)
126
170
  log.info "Deleting #{path}"
127
171
  run(path) do
128
- @client.file_delete(@conf["root"], path)
172
+ @client.file_delete(path)
129
173
  end
130
174
  end
131
175
 
132
176
  def move(old_path, new_path)
133
177
  log.info "Moving #{old_path} to #{new_path}"
134
178
  run(old_path) do
135
- case res = @client.file_move(@conf["root"], old_path, new_path)
136
- when Net::HTTPBadRequest
137
- raise RemoteAlreadyExists, "Error during move -- there may already be a Dropbox folder at #{new_path}"
138
- else
139
- res
179
+ begin
180
+ @client.file_move(old_path, new_path)
181
+ rescue DropboxError => e
182
+ if e.http_response.kind_of?(Net::HTTPForbidden)
183
+ raise RemoteAlreadyExists, "Error during move -- there may already be a Dropbox folder at #{new_path}"
184
+ else
185
+ raise e
186
+ end
140
187
  end
141
188
  end
142
189
  end
143
190
 
144
- def escape_path(path)
145
- path.split("/").map {|s| CGI.escape(s).gsub("+", "%20") }.join("/")
191
+ def streaming_download(url, io)
192
+ url = URI.parse(url)
193
+ http = Net::HTTP.new(url.host, url.port)
194
+ http.use_ssl = true
195
+
196
+ req = Net::HTTP::Get.new(url.request_uri)
197
+ req["User-Agent"] = "OfficialDropboxRubySDK/#{Dropbox::SDK_VERSION}"
198
+
199
+ http.request(req) do |res|
200
+ if res.kind_of?(Net::HTTPSuccess)
201
+ # stream into given io
202
+ res.read_body {|chunk| io.write(chunk) }
203
+ true
204
+ else
205
+ raise DropboxError.new("Invalid response #{res}\n#{res.body}")
206
+ end
207
+ end
146
208
  end
209
+ end
210
+ end
147
211
 
148
- def self.conf
149
- app_key = ENV["DROPBOX_APP_KEY"]
150
- app_secret = ENV["DROPBOX_APP_SECRET"]
212
+ # monkey-patch DropboxSession to add SSL certificate checking, since the
213
+ # Dropbox Ruby SDK doesn't do it and doesn't have a place to hook into.
214
+ class DropboxSession
215
+ private
216
+ def do_http(uri, auth_token, request) # :nodoc:
217
+ http = Net::HTTP.new(uri.host, uri.port)
218
+ http.use_ssl = true
151
219
 
152
- raise(ConfigurationError, "Please set the DROPBOX_APP_KEY environment variable to a Dropbox application key") unless app_key
153
- raise(ConfigurationError, "Please set the DROPBOX_APP_SECRET environment variable to a Dropbox application secret") unless app_secret
220
+ # IMPORTANT: other than these two extra lines, this should be
221
+ # identical to the definition in dropbox_sdk.rb
222
+ http.verify_mode = OpenSSL::SSL::VERIFY_PEER
223
+ http.ca_file = File.join(File.dirname(__FILE__), "cacert.pem")
154
224
 
155
- {
156
- "server" => "api.dropbox.com",
157
- "content_server" => "api-content.dropbox.com",
158
- "port" => 80,
159
- "request_token_url" => "http://api.dropbox.com/0/oauth/request_token",
160
- "access_token_url" => "http://api.dropbox.com/0/oauth/access_token",
161
- "authorization_url" => "http://www.dropbox.com/0/oauth/authorize",
162
- "root" => "dropbox",
163
- "consumer_key" => app_key,
164
- "consumer_secret" => app_secret
165
- }
166
- end
225
+ request.add_field('Authorization', build_auth_header(auth_token))
226
+
227
+ #We use this to better understand how developers are using our SDKs.
228
+ request['User-Agent'] = "OfficialDropboxRubySDK/#{Dropbox::SDK_VERSION}"
229
+
230
+ http.request(request)
167
231
  end
168
232
  end