dbox 0.7.6 → 0.8.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.
@@ -1,25 +0,0 @@
1
- # Build with: gem build gemspec.rb
2
- Gem::Specification.new do |s|
3
- s.name = "dropbox-sdk"
4
-
5
- s.version = "1.3.1"
6
- s.license = 'MIT'
7
-
8
- s.authors = ["Dropbox, Inc."]
9
- s.email = ["support-api@dropbox.com"]
10
-
11
- s.add_dependency "json"
12
-
13
- s.homepage = "http://www.dropbox.com/developers/"
14
- s.summary = "Dropbox REST API Client."
15
- s.description = <<-EOF
16
- A library that provides a plain function-call interface to the
17
- Dropbox API web endpoints.
18
- EOF
19
-
20
- s.files = [
21
- "CHANGELOG", "LICENSE", "README",
22
- "cli_example.rb", "dropbox_controller.rb", "web_file_browser.rb",
23
- "lib/dropbox_sdk.rb", "lib/trusted-certs.crt",
24
- ]
25
- end
@@ -1,883 +0,0 @@
1
- require 'rubygems'
2
- require 'uri'
3
- require 'net/https'
4
- require 'cgi'
5
- require 'json'
6
- require 'yaml'
7
-
8
- module Dropbox # :nodoc:
9
- API_SERVER = "api.dropbox.com"
10
- API_CONTENT_SERVER = "api-content.dropbox.com"
11
- WEB_SERVER = "www.dropbox.com"
12
-
13
- API_VERSION = 1
14
- SDK_VERSION = "1.3.1"
15
-
16
- 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
-
35
- def do_http(uri, auth_token, request) # :nodoc:
36
- http = Net::HTTP.new(uri.host, uri.port)
37
-
38
- http.use_ssl = true
39
- enable_cert_checking(http)
40
- http.ca_file = Dropbox::TRUSTED_CERT_FILE
41
-
42
- request.add_field('Authorization', build_auth_header(auth_token))
43
-
44
- #We use this to better understand how developers are using our SDKs.
45
- request['User-Agent'] = "OfficialDropboxRubySDK/#{Dropbox::SDK_VERSION}"
46
-
47
- begin
48
- http.request(request)
49
- rescue OpenSSL::SSL::SSLError => e
50
- 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)
53
- end
54
- end
55
-
56
- def enable_cert_checking(http)
57
- http.verify_mode = OpenSSL::SSL::VERIFY_PEER
58
- end
59
-
60
- def build_auth_header(token) # :nodoc:
61
- header = "OAuth oauth_version=\"1.0\", oauth_signature_method=\"PLAINTEXT\", " +
62
- "oauth_consumer_key=\"#{URI.escape(@consumer_key)}\", "
63
- if token
64
- key = URI.escape(token.key)
65
- secret = URI.escape(token.secret)
66
- header += "oauth_token=\"#{key}\", oauth_signature=\"#{URI.escape(@consumer_secret)}&#{secret}\""
67
- else
68
- header += "oauth_signature=\"#{URI.escape(@consumer_secret)}&\""
69
- end
70
- header
71
- end
72
-
73
- def do_get_with_token(url, token, headers=nil) # :nodoc:
74
- uri = URI.parse(url)
75
- do_http(uri, token, Net::HTTP::Get.new(uri.request_uri))
76
- end
77
-
78
- public
79
-
80
- def do_get(url, headers=nil) # :nodoc:
81
- assert_authorized
82
- do_get_with_token(url, @access_token)
83
- end
84
-
85
- def do_http_with_body(uri, request, body)
86
- if body != nil
87
- if body.is_a?(Hash)
88
- form_data = {}
89
- body.each {|k,v| form_data[k.to_s] = v if !v.nil?}
90
- request.set_form_data(form_data)
91
- elsif body.respond_to?(:read)
92
- if body.respond_to?(:length)
93
- request["Content-Length"] = body.length.to_s
94
- elsif body.respond_to?(:stat) && body.stat.respond_to?(:size)
95
- request["Content-Length"] = body.stat.size.to_s
96
- else
97
- raise ArgumentError, "Don't know how to handle 'body' (responds to 'read' but not to 'length' or 'stat.size')."
98
- end
99
- request.body_stream = body
100
- else
101
- s = body.to_s
102
- request["Content-Length"] = s.length
103
- request.body = s
104
- end
105
- end
106
- do_http(uri, @access_token, request)
107
- end
108
-
109
- def do_post(url, headers=nil, body=nil) # :nodoc:
110
- assert_authorized
111
- uri = URI.parse(url)
112
- do_http_with_body(uri, Net::HTTP::Post.new(uri.request_uri, headers), body)
113
- end
114
-
115
- def do_put(url, headers=nil, body=nil) # :nodoc:
116
- assert_authorized
117
- uri = URI.parse(url)
118
- do_http_with_body(uri, Net::HTTP::Put.new(uri.request_uri, headers), body)
119
- end
120
-
121
-
122
- def get_token(url_end, input_token, error_message_prefix) #: nodoc:
123
- response = do_get_with_token("https://#{Dropbox::API_SERVER}:443/#{Dropbox::API_VERSION}/oauth#{url_end}", input_token)
124
- if not response.kind_of?(Net::HTTPSuccess) # it must be a 200
125
- raise DropboxAuthError.new("#{error_message_prefix} Server returned #{response.code}: #{response.message}.", response)
126
- end
127
- parts = CGI.parse(response.body)
128
-
129
- if !parts.has_key? "oauth_token" and parts["oauth_token"].length != 1
130
- raise DropboxAuthError.new("Invalid response from #{url_end}: missing \"oauth_token\" parameter: #{response.body}", response)
131
- end
132
- if !parts.has_key? "oauth_token_secret" and parts["oauth_token_secret"].length != 1
133
- raise DropboxAuthError.new("Invalid response from #{url_end}: missing \"oauth_token\" parameter: #{response.body}", response)
134
- end
135
-
136
- OAuthToken.new(parts["oauth_token"][0], parts["oauth_token_secret"][0])
137
- end
138
-
139
- # This returns a request token. Requests one from the dropbox server using the provided application key and secret if nessecary.
140
- def get_request_token()
141
- @request_token ||= get_token("/request_token", nil, "Error getting request token. Is your app key and secret correctly set?")
142
- end
143
-
144
- # This returns a URL that your user must visit to grant
145
- # permissions to this application.
146
- def get_authorize_url(callback=nil)
147
- get_request_token()
148
-
149
- url = "/#{Dropbox::API_VERSION}/oauth/authorize?oauth_token=#{URI.escape(@request_token.key)}"
150
- if callback
151
- url += "&oauth_callback=#{URI.escape(callback)}"
152
- end
153
- if @locale
154
- url += "&locale=#{URI.escape(@locale)}"
155
- end
156
-
157
- "https://#{Dropbox::WEB_SERVER}#{url}"
158
- end
159
-
160
- # Clears the access_token
161
- def clear_access_token
162
- @access_token = nil
163
- end
164
-
165
- # Returns the request token, or nil if one hasn't been acquired yet.
166
- def request_token
167
- @request_token
168
- end
169
-
170
- # Returns the access token, or nil if one hasn't been acquired yet.
171
- def access_token
172
- @access_token
173
- end
174
-
175
- # Given a saved request token and secret, set this location's token and secret
176
- # * token - this is the request token
177
- # * secret - this is the request token secret
178
- def set_request_token(key, secret)
179
- @request_token = OAuthToken.new(key, secret)
180
- end
181
-
182
- # Given a saved access token and secret, you set this Session to use that token and secret
183
- # * token - this is the access token
184
- # * secret - this is the access token secret
185
- def set_access_token(key, secret)
186
- @access_token = OAuthToken.new(key, secret)
187
- end
188
-
189
- # Returns the access token. If this DropboxSession doesn't yet have an access_token, it requests one
190
- # using the request_token generate from your app's token and secret. This request will fail unless
191
- # your user has gone to the authorize_url and approved your request
192
- def get_access_token
193
- return @access_token if authorized?
194
-
195
- if @request_token.nil?
196
- raise DropboxAuthError.new("No request token. You must set this or get an authorize url first.")
197
- end
198
-
199
- @access_token = get_token("/access_token", @request_token, "Couldn't get access token.")
200
- end
201
-
202
- # If we have an access token, then do nothing. If not, throw a RuntimeError.
203
- def assert_authorized
204
- unless authorized?
205
- raise RuntimeError.new('Session does not yet have a request token')
206
- end
207
- end
208
-
209
- # Returns true if this Session has been authorized and has an access_token.
210
- def authorized?
211
- !!@access_token
212
- end
213
-
214
- # serialize the DropboxSession.
215
- # At DropboxSession's state is capture in three key/secret pairs. Consumer, request, and access.
216
- # Serialize returns these in a YAML string, generated from a converted array of the form:
217
- # [consumer_key, consumer_secret, request_token.token, request_token.secret, access_token.token, access_token.secret]
218
- # access_token is only included if it already exists in the DropboxSesssion
219
- def serialize
220
- toreturn = []
221
- if @access_token
222
- toreturn.push @access_token.secret, @access_token.key
223
- end
224
-
225
- get_request_token
226
-
227
- toreturn.push @request_token.secret, @request_token.key
228
- toreturn.push @consumer_secret, @consumer_key
229
-
230
- toreturn.to_yaml
231
- end
232
-
233
- # Takes a serialized DropboxSession YAML String and returns a new DropboxSession object
234
- def self.deserialize(ser)
235
- ser = YAML::load(ser)
236
- session = DropboxSession.new(ser.pop, ser.pop)
237
- session.set_request_token(ser.pop, ser.pop)
238
-
239
- if ser.length > 0
240
- session.set_access_token(ser.pop, ser.pop)
241
- end
242
- session
243
- end
244
- end
245
-
246
-
247
- # A class that represents either an OAuth request token or an OAuth access token.
248
- class OAuthToken # :nodoc:
249
- def initialize(key, secret)
250
- @key = key
251
- @secret = secret
252
- end
253
-
254
- def key
255
- @key
256
- end
257
-
258
- def secret
259
- @secret
260
- end
261
- end
262
-
263
-
264
- # This is the usual error raised on any Dropbox related Errors
265
- class DropboxError < RuntimeError
266
- attr_accessor :http_response, :error, :user_error
267
- def initialize(error, http_response=nil, user_error=nil)
268
- @error = error
269
- @http_response = http_response
270
- @user_error = user_error
271
- end
272
-
273
- def to_s
274
- return "#{user_error} (#{error})" if user_error
275
- "#{error}"
276
- end
277
- end
278
-
279
- # This is the error raised on Authentication failures. Usually this means
280
- # one of three things
281
- # * Your user failed to go to the authorize url and approve your application
282
- # * You set an invalid or expired token and secret on your Session
283
- # * Your user deauthorized the application after you stored a valid token and secret
284
- class DropboxAuthError < DropboxError
285
- end
286
-
287
- # This is raised when you call metadata with a hash and that hash matches
288
- # See documentation in metadata function
289
- class DropboxNotModified < DropboxError
290
- end
291
-
292
- # This is the Dropbox Client API you'll be working with most often. You need to give it
293
- # a DropboxSession which has already been authorized, or which it can authorize.
294
- class DropboxClient
295
-
296
- # Initialize a new DropboxClient. You need to give it a session which has been authorized. See
297
- # documentation on DropboxSession for how to authorize it.
298
- def initialize(session, root="app_folder", locale=nil)
299
- session.get_access_token
300
-
301
- @root = root.to_s # If they passed in a symbol, make it a string
302
-
303
- if not ["dropbox","app_folder"].include?(@root)
304
- raise DropboxError.new("root must be :dropbox or :app_folder")
305
- end
306
- if @root == "app_folder"
307
- #App Folder is the name of the access type, but for historical reasons
308
- #sandbox is the URL root component that indicates this
309
- @root = "sandbox"
310
- end
311
-
312
- @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
- end
346
-
347
- # Returns account info in a Hash object
348
- #
349
- # For a detailed description of what this call returns, visit:
350
- # https://www.dropbox.com/developers/reference/api#account-info
351
- def account_info()
352
- response = @session.do_get build_url("/account/info")
353
- parse_response(response)
354
- end
355
-
356
- # Uploads a file to a server. This uses the HTTP PUT upload method for simplicity
357
- #
358
- # Arguments:
359
- # * to_path: The directory path to upload the file to. If the destination
360
- # directory does not yet exist, it will be created.
361
- # * file_obj: A file-like object to upload. If you would like, you can
362
- # pass a string as file_obj.
363
- # * overwrite: Whether to overwrite an existing file at the given path. [default is False]
364
- # If overwrite is False and a file already exists there, Dropbox
365
- # will rename the upload to make sure it doesn't overwrite anything.
366
- # You must check the returned metadata to know what this new name is.
367
- # This field should only be True if your intent is to potentially
368
- # clobber changes to a file that you don't know about.
369
- # * parent_rev: The rev field from the 'parent' of this upload. [optional]
370
- # If your intent is to update the file at the given path, you should
371
- # pass the parent_rev parameter set to the rev value from the most recent
372
- # metadata you have of the existing file at that path. If the server
373
- # has a more recent version of the file at the specified path, it will
374
- # automatically rename your uploaded file, spinning off a conflict.
375
- # Using this parameter effectively causes the overwrite parameter to be ignored.
376
- # The file will always be overwritten if you send the most-recent parent_rev,
377
- # and it will never be overwritten you send a less-recent one.
378
- # Returns:
379
- # * a Hash containing the metadata of the newly uploaded file. The file may have a different name if it conflicted.
380
- #
381
- # Simple Example
382
- # client = DropboxClient(session, :app_folder)
383
- # #session is a DropboxSession I've already authorized
384
- # client.put_file('/test_file_on_dropbox', open('/tmp/test_file'))
385
- # This will upload the "/tmp/test_file" from my computer into the root of my App's app folder
386
- # and call it "test_file_on_dropbox".
387
- # The file will not overwrite any pre-existing file.
388
- def put_file(to_path, file_obj, overwrite=false, parent_rev=nil)
389
- path = "/files_put/#{@root}#{format_path(to_path)}"
390
-
391
- params = {
392
- 'overwrite' => overwrite.to_s
393
- }
394
-
395
- params['parent_rev'] = parent_rev unless parent_rev.nil?
396
-
397
- response = @session.do_put(build_url(path, params, content_server=true),
398
- {"Content-Type" => "application/octet-stream"},
399
- file_obj)
400
-
401
- parse_response(response)
402
- end
403
-
404
- # Download a file
405
- #
406
- # Args:
407
- # * from_path: The path to the file to be downloaded
408
- # * rev: A previous revision value of the file to be downloaded
409
- #
410
- # Returns:
411
- # * The file contents.
412
- def get_file(from_path, rev=nil)
413
- response = get_file_impl(from_path, rev)
414
- parse_response(response, raw=true)
415
- end
416
-
417
- # Download a file and get its metadata.
418
- #
419
- # Args:
420
- # * from_path: The path to the file to be downloaded
421
- # * rev: A previous revision value of the file to be downloaded
422
- #
423
- # Returns:
424
- # * The file contents.
425
- # * The file metadata as a hash.
426
- def get_file_and_metadata(from_path, rev=nil)
427
- response = get_file_impl(from_path, rev)
428
- parsed_response = parse_response(response, raw=true)
429
- metadata = parse_metadata(response)
430
- return parsed_response, metadata
431
- end
432
-
433
- # Download a file (helper method - don't call this directly).
434
- #
435
- # Args:
436
- # * from_path: The path to the file to be downloaded
437
- # * rev: A previous revision value of the file to be downloaded
438
- #
439
- # Returns:
440
- # * The HTTPResponse for the file download request.
441
- def get_file_impl(from_path, rev=nil) # :nodoc:
442
- params = {}
443
- params['rev'] = rev.to_s if rev
444
-
445
- path = "/files/#{@root}#{format_path(from_path)}"
446
- @session.do_get build_url(path, params, content_server=true)
447
- end
448
- private :get_file_impl
449
-
450
- # Parses out file metadata from a raw dropbox HTTP response.
451
- #
452
- # Args:
453
- # * dropbox_raw_response: The raw, unparsed HTTPResponse from Dropbox.
454
- #
455
- # Returns:
456
- # * The metadata of the file as a hash.
457
- def parse_metadata(dropbox_raw_response) # :nodoc:
458
- begin
459
- raw_metadata = dropbox_raw_response['x-dropbox-metadata']
460
- metadata = JSON.parse(raw_metadata)
461
- rescue
462
- raise DropboxError.new("Dropbox Server Error: x-dropbox-metadata=#{raw_metadata}",
463
- dropbox_raw_response)
464
- end
465
- return metadata
466
- end
467
- private :parse_metadata
468
-
469
- # Copy a file or folder to a new location.
470
- #
471
- # Arguments:
472
- # * from_path: The path to the file or folder to be copied.
473
- # * to_path: The destination path of the file or folder to be copied.
474
- # This parameter should include the destination filename (e.g.
475
- # from_path: '/test.txt', to_path: '/dir/test.txt'). If there's
476
- # already a file at the to_path, this copy will be renamed to
477
- # be unique.
478
- #
479
- # Returns:
480
- # * A hash with the metadata of the new copy of the file or folder.
481
- # For a detailed description of what this call returns, visit:
482
- # https://www.dropbox.com/developers/reference/api#fileops-copy
483
- def file_copy(from_path, to_path)
484
- params = {
485
- "root" => @root,
486
- "from_path" => format_path(from_path, false),
487
- "to_path" => format_path(to_path, false),
488
- }
489
- response = @session.do_post build_url("/fileops/copy", params)
490
- parse_response(response)
491
- end
492
-
493
- # Create a folder.
494
- #
495
- # Arguments:
496
- # * path: The path of the new folder.
497
- #
498
- # Returns:
499
- # * A hash with the metadata of the newly created folder.
500
- # For a detailed description of what this call returns, visit:
501
- # https://www.dropbox.com/developers/reference/api#fileops-create-folder
502
- def file_create_folder(path)
503
- params = {
504
- "root" => @root,
505
- "path" => format_path(path, false),
506
- }
507
- response = @session.do_post build_url("/fileops/create_folder", params)
508
-
509
- parse_response(response)
510
- end
511
-
512
- # Deletes a file
513
- #
514
- # Arguments:
515
- # * path: The path of the file to delete
516
- #
517
- # Returns:
518
- # * A Hash with the metadata of file just deleted.
519
- # For a detailed description of what this call returns, visit:
520
- # https://www.dropbox.com/developers/reference/api#fileops-delete
521
- def file_delete(path)
522
- params = {
523
- "root" => @root,
524
- "path" => format_path(path, false),
525
- }
526
- response = @session.do_post build_url("/fileops/delete", params)
527
- parse_response(response)
528
- end
529
-
530
- # Moves a file
531
- #
532
- # Arguments:
533
- # * from_path: The path of the file to be moved
534
- # * to_path: The destination path of the file or folder to be moved
535
- # If the file or folder already exists, it will be renamed to be unique.
536
- #
537
- # Returns:
538
- # * A Hash with the metadata of file or folder just moved.
539
- # For a detailed description of what this call returns, visit:
540
- # https://www.dropbox.com/developers/reference/api#fileops-delete
541
- def file_move(from_path, to_path)
542
- params = {
543
- "root" => @root,
544
- "from_path" => format_path(from_path, false),
545
- "to_path" => format_path(to_path, false),
546
- }
547
- response = @session.do_post build_url("/fileops/move", params)
548
- parse_response(response)
549
- end
550
-
551
- # Retrives metadata for a file or folder
552
- #
553
- # Arguments:
554
- # * path: The path to the file or folder.
555
- # * list: Whether to list all contained files (only applies when
556
- # path refers to a folder).
557
- # * file_limit: The maximum number of file entries to return within
558
- # a folder. If the number of files in the directory exceeds this
559
- # limit, an exception is raised. The server will return at max
560
- # 25,000 files within a folder.
561
- # * hash: Every directory listing has a hash parameter attached that
562
- # can then be passed back into this function later to save on
563
- # bandwidth. Rather than returning an unchanged folder's contents, if
564
- # the hash matches a DropboxNotModified exception is raised.
565
- # * rev: Optional. The revision of the file to retrieve the metadata for.
566
- # This parameter only applies for files. If omitted, you'll receive
567
- # the most recent revision metadata.
568
- # * include_deleted: Specifies whether to include deleted files in metadata results.
569
- #
570
- # Returns:
571
- # * A Hash object with the metadata of the file or folder (and contained files if
572
- # appropriate). For a detailed description of what this call returns, visit:
573
- # https://www.dropbox.com/developers/reference/api#metadata
574
- def metadata(path, file_limit=25000, list=true, hash=nil, rev=nil, include_deleted=false)
575
- params = {
576
- "file_limit" => file_limit.to_s,
577
- "list" => list.to_s,
578
- "include_deleted" => include_deleted.to_s
579
- }
580
-
581
- params["hash"] = hash if hash
582
- params["rev"] = rev if rev
583
-
584
- response = @session.do_get build_url("/metadata/#{@root}#{format_path(path)}", params=params)
585
- if response.kind_of? Net::HTTPRedirection
586
- raise DropboxNotModified.new("metadata not modified")
587
- end
588
- parse_response(response)
589
- end
590
-
591
- # Search directory for filenames matching query
592
- #
593
- # Arguments:
594
- # * path: The directory to search within
595
- # * query: The query to search on (3 character minimum)
596
- # * file_limit: The maximum number of file entries to return/
597
- # If the number of files exceeds this
598
- # limit, an exception is raised. The server will return at max 1,000
599
- # * include_deleted: Whether to include deleted files in search results
600
- #
601
- # Returns:
602
- # * A Hash object with a list the metadata of the file or folders matching query
603
- # inside path. For a detailed description of what this call returns, visit:
604
- # https://www.dropbox.com/developers/reference/api#search
605
- def search(path, query, file_limit=1000, include_deleted=false)
606
- params = {
607
- 'query' => query,
608
- 'file_limit' => file_limit.to_s,
609
- 'include_deleted' => include_deleted.to_s
610
- }
611
-
612
- response = @session.do_get build_url("/search/#{@root}#{format_path(path)}", params)
613
- parse_response(response)
614
- end
615
-
616
- # Retrive revisions of a file
617
- #
618
- # Arguments:
619
- # * path: The file to fetch revisions for. Note that revisions
620
- # are not available for folders.
621
- # * rev_limit: The maximum number of file entries to return within
622
- # a folder. The server will return at max 1,000 revisions.
623
- #
624
- # Returns:
625
- # * A Hash object with a list of the metadata of the all the revisions of
626
- # all matches files (up to rev_limit entries)
627
- # For a detailed description of what this call returns, visit:
628
- # https://www.dropbox.com/developers/reference/api#revisions
629
- def revisions(path, rev_limit=1000)
630
-
631
- params = {
632
- 'rev_limit' => rev_limit.to_s
633
- }
634
-
635
- response = @session.do_get build_url("/revisions/#{@root}#{format_path(path)}", params)
636
- parse_response(response)
637
-
638
- end
639
-
640
- # Restore a file to a previous revision.
641
- #
642
- # Arguments:
643
- # * path: The file to restore. Note that folders can't be restored.
644
- # * rev: A previous rev value of the file to be restored to.
645
- #
646
- # Returns:
647
- # * A Hash object with a list the metadata of the file or folders restored
648
- # For a detailed description of what this call returns, visit:
649
- # https://www.dropbox.com/developers/reference/api#search
650
- def restore(path, rev)
651
- params = {
652
- 'rev' => rev.to_s
653
- }
654
-
655
- response = @session.do_post build_url("/restore/#{@root}#{format_path(path)}", params)
656
- parse_response(response)
657
- end
658
-
659
- # Returns a direct link to a media file
660
- # All of Dropbox's API methods require OAuth, which may cause problems in
661
- # situations where an application expects to be able to hit a URL multiple times
662
- # (for example, a media player seeking around a video file). This method
663
- # creates a time-limited URL that can be accessed without any authentication.
664
- #
665
- # Arguments:
666
- # * path: The file to stream.
667
- #
668
- # Returns:
669
- # * A Hash object that looks like the following:
670
- # {'url': 'https://dl.dropbox.com/0/view/wvxv1fw6on24qw7/file.mov', 'expires': 'Thu, 16 Sep 2011 01:01:25 +0000'}
671
- def media(path)
672
- response = @session.do_get build_url("/media/#{@root}#{format_path(path)}")
673
- parse_response(response)
674
- end
675
-
676
- # Get a URL to share a media file
677
- # Shareable links created on Dropbox are time-limited, but don't require any
678
- # authentication, so they can be given out freely. The time limit should allow
679
- # at least a day of shareability, though users have the ability to disable
680
- # a link from their account if they like.
681
- #
682
- # Arguments:
683
- # * path: The file to share.
684
- #
685
- # Returns:
686
- # * A Hash object that looks like the following example:
687
- # {'url': 'http://www.dropbox.com/s/m/a2mbDa2', 'expires': 'Thu, 16 Sep 2011 01:01:25 +0000'}
688
- # For a detailed description of what this call returns, visit:
689
- # https://www.dropbox.com/developers/reference/api#shares
690
- def shares(path)
691
- response = @session.do_get build_url("/shares/#{@root}#{format_path(path)}")
692
- parse_response(response)
693
- end
694
-
695
- # Download a thumbnail for an image.
696
- #
697
- # Arguments:
698
- # * from_path: The path to the file to be thumbnailed.
699
- # * size: A string describing the desired thumbnail size. At this time,
700
- # 'small', 'medium', and 'large' are officially supported sizes
701
- # (32x32, 64x64, and 128x128 respectively), though others may
702
- # be available. Check https://www.dropbox.com/developers/reference/api#thumbnails
703
- # for more details. [defaults to large]
704
- # Returns:
705
- # * The thumbnail data
706
- def thumbnail(from_path, size='large')
707
- response = thumbnail_impl(from_path, size)
708
- parse_response(response, raw=true)
709
- end
710
-
711
- # Download a thumbnail for an image alongwith the image's metadata.
712
- #
713
- # Arguments:
714
- # * from_path: The path to the file to be thumbnailed.
715
- # * size: A string describing the desired thumbnail size. See thumbnail()
716
- # for details.
717
- # Returns:
718
- # * The thumbnail data
719
- # * The metadata for the image as a hash
720
- def thumbnail_and_metadata(from_path, size='large')
721
- response = thumbnail_impl(from_path, size)
722
- parsed_response = parse_response(response, raw=true)
723
- metadata = parse_metadata(response)
724
- return parsed_response, metadata
725
- end
726
-
727
- # A way of letting you keep a local representation of the Dropbox folder
728
- # heirarchy. You can periodically call delta() to get a list of "delta
729
- # entries", which are instructions on how to update your local state to
730
- # match the server's state.
731
- #
732
- # Arguments:
733
- # * +cursor+: On the first call, omit this argument (or pass in +nil+). On
734
- # subsequent calls, pass in the +cursor+ string returned by the previous
735
- # call.
736
- #
737
- # Returns: A hash with three fields.
738
- # * +entries+: A list of "delta entries" (described below)
739
- # * +reset+: If +true+, you should reset local state to be an empty folder
740
- # before processing the list of delta entries. This is only +true+ only
741
- # in rare situations.
742
- # * +cursor+: A string that is used to keep track of your current state.
743
- # On the next call to delta(), pass in this value to return entries
744
- # that were recorded since the cursor was returned.
745
- # * +has_more+: If +true+, then there are more entries available; you can
746
- # call delta() again immediately to retrieve those entries. If +false+,
747
- # then wait at least 5 minutes (preferably longer) before checking again.
748
- #
749
- # Delta Entries: Each entry is a 2-item list of one of following forms:
750
- # * [_path_, _metadata_]: Indicates that there is a file/folder at the given
751
- # path. You should add the entry to your local state. (The _metadata_
752
- # value is the same as what would be returned by the #metadata() call.)
753
- # * If the path refers to parent folders that don't yet exist in your
754
- # local state, create those parent folders in your local state. You
755
- # will eventually get entries for those parent folders.
756
- # * If the new entry is a file, replace whatever your local state has at
757
- # _path_ with the new entry.
758
- # * If the new entry is a folder, check what your local state has at
759
- # _path_. If it's a file, replace it with the new entry. If it's a
760
- # folder, apply the new _metadata_ to the folder, but do not modify
761
- # the folder's children.
762
- # * [path, +nil+]: Indicates that there is no file/folder at the _path_ on
763
- # Dropbox. To update your local state to match, delete whatever is at
764
- # _path_, including any children (you will sometimes also get separate
765
- # delta entries for each child, but this is not guaranteed). If your
766
- # local state doesn't have anything at _path_, ignore this entry.
767
- #
768
- # Remember: Dropbox treats file names in a case-insensitive but case-preserving
769
- # way. To facilitate this, the _path_ strings above are lower-cased versions of
770
- # the actual path. The _metadata_ dicts have the original, case-preserved path.
771
- def delta(cursor=nil)
772
- params = {}
773
- if cursor
774
- params['cursor'] = cursor
775
- end
776
-
777
- response = @session.do_post build_url("/delta", params)
778
- parse_response(response)
779
- end
780
-
781
- # Download a thumbnail (helper method - don't call this directly).
782
- #
783
- # Args:
784
- # * from_path: The path to the file to be thumbnailed.
785
- # * size: A string describing the desired thumbnail size. See thumbnail()
786
- # for details.
787
- #
788
- # Returns:
789
- # * The HTTPResponse for the thumbnail request.
790
- def thumbnail_impl(from_path, size='large') # :nodoc:
791
- from_path = format_path(from_path, false)
792
-
793
- raise DropboxError.new("size must be small medium or large. (not '#{size})") unless ['small','medium','large'].include?(size)
794
-
795
- params = {
796
- "size" => size
797
- }
798
-
799
- url = build_url("/thumbnails/#{@root}#{from_path}", params, content_server=true)
800
-
801
- @session.do_get url
802
- end
803
- private :thumbnail_impl
804
-
805
-
806
- # Creates and returns a copy ref for a specific file. The copy ref can be
807
- # used to instantly copy that file to the Dropbox of another account.
808
- #
809
- # Args:
810
- # * path: The path to the file for a copy ref to be created on.
811
- #
812
- # Returns:
813
- # * A Hash object that looks like the following example:
814
- # {"expires"=>"Fri, 31 Jan 2042 21:01:05 +0000", "copy_ref"=>"z1X6ATl6aWtzOGq0c3g5Ng"}
815
- def create_copy_ref(path)
816
- path = "/copy_ref/#{@root}#{format_path(path)}"
817
-
818
- response = @session.do_get(build_url(path, {}))
819
-
820
- parse_response(response)
821
- end
822
-
823
- # Adds the file referenced by the copy ref to the specified path
824
- #
825
- # Args:
826
- # * copy_ref: A copy ref string that was returned from a create_copy_ref call.
827
- # The copy_ref can be created from any other Dropbox account, or from the same account.
828
- # * to_path: The path to where the file will be created.
829
- #
830
- # Returns:
831
- # * A hash with the metadata of the new file.
832
- def add_copy_ref(to_path, copy_ref)
833
- path = "/fileops/copy"
834
-
835
- params = {'from_copy_ref' => copy_ref,
836
- 'to_path' => "#{format_path(to_path)}",
837
- 'root' => @root}
838
-
839
- response = @session.do_post(build_url(path, params))
840
-
841
- parse_response(response)
842
- end
843
-
844
- def build_url(url, params=nil, content_server=false) # :nodoc:
845
- port = 443
846
- host = content_server ? Dropbox::API_CONTENT_SERVER : Dropbox::API_SERVER
847
- versioned_url = "/#{Dropbox::API_VERSION}#{url}"
848
-
849
- target = URI::Generic.new("https", nil, host, port, nil, versioned_url, nil, nil, nil)
850
-
851
- #add a locale param if we have one
852
- #initialize a params object is we don't have one
853
- if @locale
854
- (params ||= {})['locale']=@locale
855
- end
856
-
857
- if params
858
- target.query = params.collect {|k,v|
859
- CGI.escape(k) + "=" + CGI.escape(v)
860
- }.join("&")
861
- end
862
-
863
- target.to_s
864
- end
865
-
866
- #From the oauth spec plus "/". Slash should not be ecsaped
867
- RESERVED_CHARACTERS = /[^a-zA-Z0-9\-\.\_\~\/]/
868
-
869
- def format_path(path, escape=true) # :nodoc:
870
- path = path.gsub(/\/+/,"/")
871
- # replace multiple slashes with a single one
872
-
873
- path = path.gsub(/^\/?/,"/")
874
- # ensure the path starts with a slash
875
-
876
- path.gsub(/\/?$/,"")
877
- # ensure the path doesn't end with a slash
878
-
879
- return URI.escape(path, RESERVED_CHARACTERS) if escape
880
- path
881
- end
882
-
883
- end