dbox 0.7.6 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,332 +0,0 @@
1
- # An example use of the /delta API call. Maintains a local cache of
2
- # the App Folder's contents. Use the 'update' sub-command to update
3
- # the local cache. Use the 'find' sub-command to search the local
4
- # cache.
5
- #
6
- # Example usage:
7
- #
8
- # 1. Link to your Dropbox account
9
- # > ruby search_cache.rb link
10
- #
11
- # 2. Go to Dropbox and make changes to the contents.
12
- #
13
- # 3. Update the local cache to match what's on Dropbox.
14
- # > ruby search_cache.rb update
15
- #
16
- # 4. Search the local cache.
17
- # > ruby search_cache.rb find 'txt'
18
-
19
- # Repeat steps 2-4 any number of times.
20
-
21
-
22
-
23
- require './lib/dropbox_sdk'
24
- require 'json'
25
-
26
- # You must use your Dropbox App key and secret to use the API.
27
- # Find this at https://www.dropbox.com/developers
28
- APP_KEY = ''
29
- APP_SECRET = ''
30
- ACCESS_TYPE = :app_folder #The two valid values here are :app_folder and :dropbox
31
- #The default is :app_folder, but your application might be
32
- #set to have full :dropbox access. Check your app at
33
- #https://www.dropbox.com/developers/apps
34
-
35
-
36
- STATE_FILE = 'search_cache.json'
37
-
38
- def main()
39
- if APP_KEY == '' or APP_SECRET == ''
40
- warn "ERROR: Set your APP_KEY and APP_SECRET at the top of search_cache.rb"
41
- exit
42
- end
43
- prog_name = __FILE__
44
- args = ARGV
45
- if args.size == 0
46
- warn("Usage:\n")
47
- warn(" #{prog_name} link Link to a user's account.")
48
- warn(" #{prog_name} update Update cache to the latest on Dropbox.")
49
- warn(" #{prog_name} update <num> Update cache, limit to <num> pages of /delta.")
50
- warn(" #{prog_name} find <term> Search cache for <term>.")
51
- warn(" #{prog_name} find Display entire cache contents")
52
- warn(" #{prog_name} reset Delete the cache.")
53
- exit
54
- end
55
-
56
- command = args[0]
57
- if command == 'link'
58
- command_link(args)
59
- elsif command == 'update'
60
- command_update(args)
61
- elsif command == 'find'
62
- command_find(args)
63
- elsif command == 'reset'
64
- command_reset(args)
65
- else
66
- warn "ERROR: Unknown command: #{command}"
67
- warn "Run with no arguments for help."
68
- exit(1)
69
- end
70
- end
71
-
72
-
73
-
74
- def command_link(args)
75
- if args.size != 1
76
- warn "ERROR: \"link\" doesn't take any arguments"
77
- exit
78
- end
79
-
80
- sess = DropboxSession.new(APP_KEY, APP_SECRET)
81
- sess.get_request_token
82
-
83
- # Make the user log in and authorize this token
84
- url = sess.get_authorize_url
85
- puts "1. Go to: #{url}"
86
- puts "2. Authorize this app."
87
- puts "After you're done, press ENTER."
88
- STDIN.gets
89
-
90
- # This will fail if the user didn't visit the above URL and hit 'Allow'
91
- sess.get_access_token
92
- access_token = sess.access_token
93
- puts "Link successful."
94
-
95
- save_state({
96
- 'access_token' => [access_token.key, access_token.secret],
97
- 'tree' => {}
98
- })
99
- end
100
-
101
-
102
- def command_update(args)
103
- if args.size == 1
104
- page_limit = nil
105
- elsif args.size == 2
106
- page_limit = Integer(args[1])
107
- else
108
- warn "ERROR: \"update\" takes either zero or one argument."
109
- exit
110
- end
111
-
112
- # Load state
113
- state = load_state()
114
- access_token = state['access_token']
115
- cursor = state['cursor']
116
- tree = state['tree']
117
-
118
- # Connect to Dropbox
119
- sess = DropboxSession.new(APP_KEY, APP_SECRET)
120
- sess.set_access_token(*access_token)
121
- c = DropboxClient.new(sess, ACCESS_TYPE)
122
-
123
- page = 0
124
- changed = false
125
- while (page_limit == nil) or (page < page_limit)
126
- # Get /delta results from Dropbox
127
- result = c.delta(cursor)
128
- page += 1
129
- if result['reset'] == true
130
- puts 'reset'
131
- changed = true
132
- tree = {}
133
- end
134
- cursor = result['cursor']
135
- # Apply the entries one by one to our cached tree.
136
- for delta_entry in result['entries']
137
- changed = true
138
- apply_delta(tree, delta_entry)
139
- end
140
- cursor = result['cursor']
141
- if not result['has_more']
142
- break
143
- end
144
- end
145
-
146
- # Save state
147
- if changed
148
- state['cursor'] = cursor
149
- state['tree'] = tree
150
- save_state(state)
151
- else
152
- puts "No updates."
153
- end
154
-
155
- end
156
-
157
- # We track folder state as a tree of Node objects.
158
- class Node
159
- attr_accessor :path, :content
160
- def initialize(path, content)
161
- # The "original" page (i.e. not the lower-case path)
162
- @path = path
163
- # For files, content is a pair (size, modified)
164
- # For folders, content is a hash of children Nodes, keyed by lower-case file names.
165
- @content = content
166
- end
167
- def folder?()
168
- @content.is_a? Hash
169
- end
170
- def to_json()
171
- [@path, Node.to_json_content(@content)]
172
- end
173
- def self.from_json(jnode)
174
- path, jcontent = jnode
175
- Node.new(path, Node.from_json_content(jcontent))
176
- end
177
- def self.to_json_content(content)
178
- if content.is_a? Hash
179
- map_hash_values(content) { |child| child.to_json }
180
- else
181
- content
182
- end
183
- end
184
- def self.from_json_content(jcontent)
185
- if jcontent.is_a? Hash
186
- map_hash_values(jcontent) { |jchild| Node.from_json jchild }
187
- else
188
- jcontent
189
- end
190
- end
191
- end
192
-
193
- # Run a mapping function over every value in a Hash, returning a new Hash.
194
- def map_hash_values(h)
195
- new = {}
196
- h.each { |k,v| new[k] = yield v }
197
- new
198
- end
199
-
200
-
201
- def apply_delta(root, e)
202
- path, metadata = e
203
- branch, leaf = split_path(path)
204
-
205
- if metadata != nil
206
- puts "+ #{path}"
207
- # Traverse down the tree until we find the parent folder of the entry
208
- # we want to add. Create any missing folders along the way.
209
- children = root
210
- branch.each do |part|
211
- node = get_or_create_child(children, part)
212
- # If there's no folder here, make an empty one.
213
- if not node.folder?
214
- node.content = {}
215
- end
216
- children = node.content
217
- end
218
-
219
- # Create the file/folder.
220
- node = get_or_create_child(children, leaf)
221
- node.path = metadata['path'] # Save the un-lower-cased path.
222
- if metadata['is_dir']
223
- # Only create a folder if there isn't one there already.
224
- node.content = {} if not node.folder?
225
- else
226
- node.content = metadata['size'], metadata['modified']
227
- end
228
- else
229
- puts "- #{path}"
230
- # Traverse down the tree until we find the parent of the entry we
231
- # want to delete.
232
- children = root
233
- missing_parent = false
234
- branch.each do |part|
235
- node = children[part]
236
- # If one of the parent folders is missing, then we're done.
237
- if node == nil or not node.folder?
238
- missing_parent = true
239
- break
240
- end
241
- children = node.content
242
- end
243
- # If we made it all the way, delete the file/folder.
244
- if not missing_parent
245
- children.delete(leaf)
246
- end
247
- end
248
- end
249
-
250
- def get_or_create_child(children, name)
251
- child = children[name]
252
- if child == nil
253
- children[name] = child = Node.new(nil, nil)
254
- end
255
- child
256
- end
257
-
258
- def split_path(path)
259
- bad, *parts = path.split '/'
260
- [parts, parts.pop]
261
- end
262
-
263
-
264
- def command_find(args)
265
- if args.size == 1
266
- term = ''
267
- elsif args.size == 2
268
- term = args[1]
269
- else
270
- warn("ERROR: \"find\" takes either zero or one arguments.")
271
- exit
272
- end
273
-
274
- state = load_state()
275
- results = []
276
- search_tree(results, state['tree'], term)
277
- for r in results
278
- puts("#{r}")
279
- end
280
- puts("[Matches: #{results.size}]")
281
- end
282
-
283
-
284
- def command_reset(args)
285
- if args.size != 1
286
- warn("ERROR: \"reset\" takes no arguments.")
287
- exit
288
- end
289
-
290
- # Delete cursor, empty tree.
291
- state = load_state()
292
- if state.has_key?('cursor')
293
- state.delete('cursor')
294
- end
295
- state['tree'] = {}
296
- save_state(state)
297
- end
298
-
299
-
300
- # Recursively search 'tree' for files that contain the string in 'term'.
301
- # Print out any matches.
302
- def search_tree(results, tree, term)
303
- tree.each do |name_lc, node|
304
- path = node.path
305
- if (path != nil) and path.include?(term)
306
- if node.folder?
307
- results.push("#{path}")
308
- else
309
- size, modified = node.content
310
- results.push("#{path} (#{size}, #{modified})")
311
- end
312
- end
313
- if node.folder?
314
- search_tree(results, node.content, term)
315
- end
316
- end
317
- end
318
-
319
- def save_state(state)
320
- state['tree'] = Node.to_json_content(state['tree'])
321
- File.open(STATE_FILE,"w") do |f|
322
- f.write(JSON.pretty_generate(state, :max_nesting => false))
323
- end
324
- end
325
-
326
- def load_state()
327
- state = JSON.parse(File.read(STATE_FILE), :max_nesting => false)
328
- state['tree'] = Node.from_json_content(state['tree'])
329
- state
330
- end
331
-
332
- main()
@@ -1,184 +0,0 @@
1
- # -------------------------------------------------------------------
2
- # An example webapp that lets you browse and upload files to Dropbox.
3
- # Demonstrates:
4
- # - The webapp OAuth process.
5
- # - The metadata() and put_file() calls.
6
- #
7
- # To run:
8
- # 1. Install Sinatra $ gem install sinatra
9
- # 2. Launch server $ ruby web_file_browser.rb
10
- # 3. Browse to http://localhost:4567/
11
- # -------------------------------------------------------------------
12
-
13
- require 'rubygems'
14
- require 'sinatra'
15
- require 'pp'
16
- require './lib/dropbox_sdk'
17
-
18
- # Get your app's key and secret from https://www.dropbox.com/developers/
19
- APP_KEY = ''
20
- APP_SECRET = ''
21
- ACCESS_TYPE = :app_folder #The two valid values here are :app_folder and :dropbox
22
- #The default is :app_folder, but your application might be
23
- #set to have full :dropbox access. Check your app at
24
- #https://www.dropbox.com/developers/apps
25
-
26
- # -------------------------------------------------------------------
27
- # OAuth stuff
28
-
29
- get '/oauth-start' do
30
- # OAuth Step 1: Get a request token from Dropbox.
31
- db_session = DropboxSession.new(APP_KEY, APP_SECRET)
32
- begin
33
- db_session.get_request_token
34
- rescue DropboxError => e
35
- return html_page "Exception in OAuth step 1", "<p>#{h e}</p>"
36
- end
37
-
38
- session[:request_db_session] = db_session.serialize
39
-
40
- # OAuth Step 2: Send the user to the Dropbox website so they can authorize
41
- # our app. After the user authorizes our app, Dropbox will redirect them
42
- # to our '/oauth-callback' endpoint.
43
- auth_url = db_session.get_authorize_url url('/oauth-callback')
44
- redirect auth_url
45
- end
46
-
47
- get '/oauth-callback' do
48
- # Finish OAuth Step 2
49
- ser = session[:request_db_session]
50
- unless ser
51
- return html_page "Error in OAuth step 2", "<p>Couldn't find OAuth state in session.</p>"
52
- end
53
- db_session = DropboxSession.deserialize(ser)
54
-
55
- # OAuth Step 3: Get an access token from Dropbox.
56
- begin
57
- db_session.get_access_token
58
- rescue DropboxError => e
59
- return html_page "Exception in OAuth step 3", "<p>#{h e}</p>"
60
- end
61
- session.delete(:request_db_session)
62
- session[:authorized_db_session] = db_session.serialize
63
- redirect url('/')
64
- # In this simple example, we store the authorized DropboxSession in the web
65
- # session hash. A "real" webapp might store it somewhere more persistent.
66
- end
67
-
68
- # If we already have an authorized DropboxSession, returns a DropboxClient.
69
- def get_db_client
70
- if session[:authorized_db_session]
71
- db_session = DropboxSession.deserialize(session[:authorized_db_session])
72
- begin
73
- return DropboxClient.new(db_session, ACCESS_TYPE)
74
- rescue DropboxAuthError => e
75
- # The stored session didn't work. Fall through and start OAuth.
76
- session[:authorized_db_session].delete
77
- end
78
- end
79
- end
80
-
81
- # -------------------------------------------------------------------
82
- # File/folder display stuff
83
-
84
- get '/' do
85
- # Get the DropboxClient object. Redirect to OAuth flow if necessary.
86
- db_client = get_db_client
87
- unless db_client
88
- redirect url("/oauth-start")
89
- end
90
-
91
- # Call DropboxClient.metadata
92
- path = params[:path] || '/'
93
- begin
94
- entry = db_client.metadata(path)
95
- rescue DropboxAuthError => e
96
- session.delete(:authorized_db_session) # An auth error means the db_session is probably bad
97
- return html_page "Dropbox auth error", "<p>#{h e}</p>"
98
- rescue DropboxError => e
99
- if e.http_response.code == '404'
100
- return html_page "Path not found: #{h path}", ""
101
- else
102
- return html_page "Dropbox API error", "<pre>#{h e.http_response}</pre>"
103
- end
104
- end
105
-
106
- if entry['is_dir']
107
- render_folder(db_client, entry)
108
- else
109
- render_file(db_client, entry)
110
- end
111
- end
112
-
113
- def render_folder(db_client, entry)
114
- # Provide an upload form (so the user can add files to this folder)
115
- out = "<form action='/upload' method='post' enctype='multipart/form-data'>"
116
- out += "<label for='file'>Upload file:</label> <input name='file' type='file'/>"
117
- out += "<input type='submit' value='Upload'/>"
118
- out += "<input name='folder' type='hidden' value='#{h entry['path']}'/>"
119
- out += "</form>" # TODO: Add a token to counter CSRF attacks.
120
- # List of folder contents
121
- entry['contents'].each do |child|
122
- cp = child['path'] # child path
123
- cn = File.basename(cp) # child name
124
- if (child['is_dir']) then cn += '/' end
125
- out += "<div><a style='text-decoration: none' href='/?path=#{h cp}'>#{h cn}</a></div>"
126
- end
127
-
128
- html_page "Folder: #{entry['path']}", out
129
- end
130
-
131
- def render_file(db_client, entry)
132
- # Just dump out metadata hash
133
- html_page "File: #{entry['path']}", "<pre>#{h entry.pretty_inspect}</pre>"
134
- end
135
-
136
- # -------------------------------------------------------------------
137
- # File upload handler
138
-
139
- post '/upload' do
140
- # Check POST parameter.
141
- file = params[:file]
142
- unless file && (temp_file = file[:tempfile]) && (name = file[:filename])
143
- return html_page "Upload error", "<p>No file selected.</p>"
144
- end
145
-
146
- # Get the DropboxClient object.
147
- db_client = get_db_client
148
- unless db_client
149
- return html_page "Upload error", "<p>Not linked with a Dropbox account.</p>"
150
- end
151
-
152
- # Call DropboxClient.put_file
153
- begin
154
- entry = db_client.put_file("#{params[:folder]}/#{name}", temp_file.read)
155
- rescue DropboxAuthError => e
156
- session.delete(:authorized_db_session) # An auth error means the db_session is probably bad
157
- return html_page "Dropbox auth error", "<p>#{h e}</p>"
158
- rescue DropboxError => e
159
- return html_page "Dropbox API error", "<p>#{h e}</p>"
160
- end
161
-
162
- html_page "Upload complete", "<pre>#{h entry.pretty_inspect}</pre>"
163
- end
164
-
165
- # -------------------------------------------------------------------
166
-
167
- def html_page(title, body)
168
- "<html>" +
169
- "<head><title>#{h title}</title></head>" +
170
- "<body><h1>#{h title}</h1>#{body}</body>" +
171
- "</html>"
172
- end
173
-
174
- enable :sessions
175
-
176
- helpers do
177
- include Rack::Utils
178
- alias_method :h, :escape_html
179
- end
180
-
181
- if APP_KEY == '' or APP_SECRET == ''
182
- puts "You must set APP_KEY and APP_SECRET at the top of \"#{__FILE__}\"!"
183
- exit 1
184
- end