PluginProbe
UpdraftPlus: WP Backup & Migration Plugin / 1.3.8
UpdraftPlus: WP Backup & Migration Plugin v1.3.8
1.26.7 1.26.6 1.26.5 1.26.4 1.26.3 1.9.19 1.9.25 1.9.26 1.9.30 1.9.31 1.9.32 1.9.4 1.9.40 1.9.41 1.9.42 1.9.43 1.9.44 1.9.45 1.9.46 1.9.5 1.9.50 1.9.51 1.9.60 1.9.62 1.9.63 All 371 releases
updraftplus / includes / Dropbox / API.php

API.php in UpdraftPlus: WP Backup & Migration Plugin 1.3.8, at includes/Dropbox/API.php

584 lines 21.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * Dropbox API base class
5 * @author Ben Tadiar <ben@handcraftedbyben.co.uk>
6 * @link https://github.com/benthedesigner/dropbox
7 * @link https://www.dropbox.com/developers
8 * @link https://status.dropbox.com Dropbox status
9 * @package Dropbox
10 */
11 class Dropbox_API
12 {
13 // API Endpoints
14 const API_URL = 'https://api.dropbox.com/1/';
15 const CONTENT_URL = 'https://api-content.dropbox.com/1/';
16
17 /**
18 * OAuth consumer object
19 * @var null|OAuth\Consumer
20 */
21 private $OAuth;
22
23 /**
24 * The root level for file paths
25 * Either `dropbox` or `sandbox` (preferred)
26 * @var null|string
27 */
28 private $root;
29
30 /**
31 * Format of the API response
32 * @var string
33 */
34 private $responseFormat = 'php';
35
36 /**
37 * JSONP callback
38 * @var string
39 */
40 private $callback = 'dropboxCallback';
41
42 /**
43 * Chunk size used for chunked uploads
44 * @see \Dropbox\API::chunkedUpload()
45 */
46 private $chunkSize = 4194304;
47
48 /**
49 * Set the OAuth consumer object
50 * See 'General Notes' at the link below for information on access type
51 * @link https://www.dropbox.com/developers/reference/api
52 * @param OAuth\Consumer\ConsumerAbstract $OAuth
53 * @param string $root Dropbox app access type
54 */
55 public function __construct(Dropbox_ConsumerAbstract $OAuth, $root = 'sandbox')
56 {
57 $this->OAuth = $OAuth;
58 $this->setRoot($root);
59 }
60
61 /**
62 * Set the root level
63 * @param mixed $root
64 * @throws Exception
65 * @return void
66 */
67 public function setRoot($root)
68 {
69 if ($root !== 'sandbox' && $root !== 'dropbox') {
70 throw new Exception("Expected a root of either 'dropbox' or 'sandbox', got '$root'");
71 } else {
72 $this->root = $root;
73 }
74 }
75
76 /**
77 * Retrieves information about the user's account
78 * @return object stdClass
79 */
80 public function accountInfo()
81 {
82 $response = $this->fetch('POST', self::API_URL, 'account/info');
83 return $response;
84 }
85
86 /**
87 * Uploads a physical file from disk
88 * Dropbox impose a 150MB limit to files uploaded via the API. If the file
89 * exceeds this limit or does not exist, an Exception will be thrown
90 * @param string $file Absolute path to the file to be uploaded
91 * @param string|bool $filename The destination filename of the uploaded file
92 * @param string $path Path to upload the file to, relative to root
93 * @param boolean $overwrite Should the file be overwritten? (Default: true)
94 * @return object stdClass
95 */
96 public function putFile($file, $filename = false, $path = '', $overwrite = true)
97 {
98 if (file_exists($file)) {
99 if (filesize($file) <= 157286400) {
100 $call = 'files/' . $this->root . '/' . $this->encodePath($path);
101 // If no filename is provided we'll use the original filename
102 $filename = (is_string($filename)) ? $filename : basename($file);
103 $params = array(
104 'filename' => $filename,
105 'file' => '@' . str_replace('\\', '/', $file) . ';filename=' . $filename,
106 'overwrite' => (int) $overwrite,
107 );
108 $response = $this->fetch('POST', self::CONTENT_URL, $call, $params);
109 return $response;
110 }
111 throw new Exception('File exceeds 150MB upload limit');
112 }
113
114 // Throw an Exception if the file does not exist
115 throw new Exception('Local file ' . $file . ' does not exist');
116 }
117
118 /**
119 * Uploads file data from a stream
120 * Note: This function is experimental and requires further testing
121 * @todo Add filesize check
122 * @param resource $stream A readable stream created using fopen()
123 * @param string $filename The destination filename, including path
124 * @param boolean $overwrite Should the file be overwritten? (Default: true)
125 * @return array
126 */
127 public function putStream($stream, $filename, $overwrite = true)
128 {
129 $this->OAuth->setInFile($stream);
130 $path = $this->encodePath($filename);
131 $call = 'files_put/' . $this->root . '/' . $path;
132 $params = array('overwrite' => (int) $overwrite);
133 $response = $this->fetch('PUT', self::CONTENT_URL, $call, $params);
134 return $response;
135 }
136
137 /**
138 * Uploads large files to Dropbox in mulitple chunks
139 * @param string $file Absolute path to the file to be uploaded
140 * @param string|bool $filename The destination filename of the uploaded file
141 * @param string $path Path to upload the file to, relative to root
142 * @param boolean $overwrite Should the file be overwritten? (Default: true)
143 * @param integer $offset position to seek to when opening the file
144 * @param string $uploadID existing upload_id to resume an upload
145 * @param string|array function to call back to upon each chunk
146 * @return stdClass
147 */
148 public function chunkedUpload($file, $filename = false, $path = '', $overwrite = true, $offset = 0, $uploadID = null, $callback = null)
149 {
150 if (file_exists($file)) {
151 if ($handle = @fopen($file, 'r')) {
152 // Set initial upload ID and offset
153 if ($offset > 0) {
154 fseek($handle, $offset);
155 }
156
157 // Read from the file handle until EOF, uploading each chunk
158 while ($data = fread($handle, $this->chunkSize)) {
159 // Open a temporary file handle and write a chunk of data to it
160 $chunkHandle = fopen('php://temp', 'rw');
161 fwrite($chunkHandle, $data);
162
163 // Set the file, request parameters and send the request
164 $this->OAuth->setInFile($chunkHandle);
165 $params = array('upload_id' => $uploadID, 'offset' => $offset);
166 $response = $this->fetch('PUT', self::CONTENT_URL, 'chunked_upload', $params);
167
168 // On subsequent chunks, use the upload ID returned by the previous request
169 if (isset($response['body']->upload_id)) {
170 $uploadID = $response['body']->upload_id;
171 }
172
173 if (isset($response['body']->offset)) {
174 $offset = $response['body']->offset;
175 if ($callback) {
176 call_user_func($callback, $offset, $uploadID);
177 }
178 }
179
180 // Close the file handle for this chunk
181 fclose($chunkHandle);
182 }
183
184 // Complete the chunked upload
185 $filename = (is_string($filename)) ? $filename : basename($file);
186 $call = 'commit_chunked_upload/' . $this->root . '/' . $this->encodePath($path . $filename);
187 $params = array('overwrite' => (int) $overwrite, 'upload_id' => $uploadID);
188 $response = $this->fetch('POST', self::CONTENT_URL, $call, $params);
189 return $response;
190 } else {
191 throw new Exception('Could not open ' . $file . ' for reading');
192 }
193 }
194
195 // Throw an Exception if the file does not exist
196 throw new Exception('Local file ' . $file . ' does not exist');
197 }
198
199 /**
200 * Downloads a file
201 * Returns the base filename, raw file data and mime type returned by Fileinfo
202 * @param string $file Path to file, relative to root, including path
203 * @param string $outFile Filename to write the downloaded file to
204 * @param string $revision The revision of the file to retrieve
205 * @return array
206 */
207 public function getFile($file, $outFile = false, $revision = null)
208 {
209 // Only allow php response format for this call
210 if ($this->responseFormat !== 'php') {
211 throw new Exception('This method only supports the `php` response format');
212 }
213
214 $handle = null;
215 if ($outFile !== false) {
216 // Create a file handle if $outFile is specified
217 if (!$handle = fopen($outFile, 'w')) {
218 throw new Exception("Unable to open file handle for $outFile");
219 } else {
220 $this->OAuth->setOutFile($handle);
221 }
222 }
223
224 $file = $this->encodePath($file);
225 $call = 'files/' . $this->root . '/' . $file;
226 $params = array('rev' => $revision);
227 $response = $this->fetch('GET', self::CONTENT_URL, $call, $params);
228
229 // Close the file handle if one was opened
230 if ($handle) fclose($handle);
231
232 return array(
233 'name' => ($outFile) ? $outFile : basename($file),
234 'mime' => $this->getMimeType(($outFile) ? $outFile : $response['body'], $outFile),
235 'meta' => json_decode($response['headers']['x-dropbox-metadata']),
236 'data' => $response['body'],
237 );
238 }
239
240 /**
241 * Retrieves file and folder metadata
242 * @param string $path The path to the file/folder, relative to root
243 * @param string $rev Return metadata for a specific revision (Default: latest rev)
244 * @param int $limit Maximum number of listings to return
245 * @param string $hash Metadata hash to compare against
246 * @param bool $list Return contents field with response
247 * @param bool $deleted Include files/folders that have been deleted
248 * @return object stdClass
249 */
250 public function metaData($path = null, $rev = null, $limit = 10000, $hash = false, $list = true, $deleted = false)
251 {
252 $call = 'metadata/' . $this->root . '/' . $this->encodePath($path);
253 $params = array(
254 'file_limit' => ($limit < 1) ? 1 : (($limit > 10000) ? 10000 : (int) $limit),
255 'hash' => (is_string($hash)) ? $hash : 0,
256 'list' => (int) $list,
257 'include_deleted' => (int) $deleted,
258 'rev' => (is_string($rev)) ? $rev : null,
259 );
260 $response = $this->fetch('POST', self::API_URL, $call, $params);
261 return $response;
262 }
263
264 /**
265 * Return "delta entries", intructing you how to update
266 * your application state to match the server's state
267 * Important: This method does not make changes to the application state
268 * @param null|string $cursor Used to keep track of your current state
269 * @return array Array of delta entries
270 */
271 public function delta($cursor = null)
272 {
273 $call = 'delta';
274 $params = array('cursor' => $cursor);
275 $response = $this->fetch('POST', self::API_URL, $call, $params);
276 return $response;
277 }
278
279 /**
280 * Obtains metadata for the previous revisions of a file
281 * @param string Path to the file, relative to root
282 * @param integer Number of revisions to return (1-1000)
283 * @return array
284 */
285 public function revisions($file, $limit = 10)
286 {
287 $call = 'revisions/' . $this->root . '/' . $this->encodePath($file);
288 $params = array(
289 'rev_limit' => ($limit < 1) ? 1 : (($limit > 1000) ? 1000 : (int) $limit),
290 );
291 $response = $this->fetch('GET', self::API_URL, $call, $params);
292 return $response;
293 }
294
295 /**
296 * Restores a file path to a previous revision
297 * @param string $file Path to the file, relative to root
298 * @param string $revision The revision of the file to restore
299 * @return object stdClass
300 */
301 public function restore($file, $revision)
302 {
303 $call = 'restore/' . $this->root . '/' . $this->encodePath($file);
304 $params = array('rev' => $revision);
305 $response = $this->fetch('POST', self::API_URL, $call, $params);
306 return $response;
307 }
308
309 /**
310 * Returns metadata for all files and folders that match the search query
311 * @param mixed $query The search string. Must be at least 3 characters long
312 * @param string $path The path to the folder you want to search in
313 * @param integer $limit Maximum number of results to return (1-1000)
314 * @param boolean $deleted Include deleted files/folders in the search
315 * @return array
316 */
317 public function search($query, $path = '', $limit = 1000, $deleted = false)
318 {
319 $call = 'search/' . $this->root . '/' . $this->encodePath($path);
320 $params = array(
321 'query' => $query,
322 'file_limit' => ($limit < 1) ? 1 : (($limit > 1000) ? 1000 : (int) $limit),
323 'include_deleted' => (int) $deleted,
324 );
325 $response = $this->fetch('GET', self::API_URL, $call, $params);
326 return $response;
327 }
328
329 /**
330 * Creates and returns a shareable link to files or folders
331 * The link returned is for a preview page from which the user an choose to
332 * download the file if they wish. For direct download links, see media().
333 * @param string $path The path to the file/folder you want a sharable link to
334 * @return object stdClass
335 */
336 public function shares($path, $shortUrl = true)
337 {
338 $call = 'shares/' . $this->root . '/' .$this->encodePath($path);
339 $params = array('short_url' => ($shortUrl) ? 1 : 0);
340 $response = $this->fetch('POST', self::API_URL, $call, $params);
341 return $response;
342 }
343
344 /**
345 * Returns a link directly to a file
346 * @param string $path The path to the media file you want a direct link to
347 * @return object stdClass
348 */
349 public function media($path)
350 {
351 $call = 'media/' . $this->root . '/' . $this->encodePath($path);
352 $response = $this->fetch('POST', self::API_URL, $call);
353 return $response;
354 }
355
356 /**
357 * Gets a thumbnail for an image
358 * @param string $file The path to the image you wish to thumbnail
359 * @param string $format The thumbnail format, either JPEG or PNG
360 * @param string $size The size of the thumbnail
361 * @return array
362 */
363 public function thumbnails($file, $format = 'JPEG', $size = 'small')
364 {
365 // Only allow php response format for this call
366 if ($this->responseFormat !== 'php') {
367 throw new Exception('This method only supports the `php` response format');
368 }
369
370 $format = strtoupper($format);
371 // If $format is not 'PNG', default to 'JPEG'
372 if ($format != 'PNG') $format = 'JPEG';
373
374 $size = strtolower($size);
375 $sizes = array('s', 'm', 'l', 'xl', 'small', 'medium', 'large');
376 // If $size is not valid, default to 'small'
377 if (!in_array($size, $sizes)) $size = 'small';
378
379 $call = 'thumbnails/' . $this->root . '/' . $this->encodePath($file);
380 $params = array('format' => $format, 'size' => $size);
381 $response = $this->fetch('GET', self::CONTENT_URL, $call, $params);
382
383 return array(
384 'name' => basename($file),
385 'mime' => $this->getMimeType($response['body']),
386 'meta' => json_decode($response['headers']['x-dropbox-metadata']),
387 'data' => $response['body'],
388 );
389 }
390
391 /**
392 * Creates and returns a copy_ref to a file
393 * This reference string can be used to copy that file to another user's
394 * Dropbox by passing it in as the from_copy_ref parameter on /fileops/copy
395 * @param $path File for which ref should be created, relative to root
396 * @return array
397 */
398 public function copyRef($path)
399 {
400 $call = 'copy_ref/' . $this->root . '/' . $this->encodePath($path);
401 $response = $this->fetch('GET', self::API_URL, $call);
402 return $response;
403 }
404
405 /**
406 * Copies a file or folder to a new location
407 * @param string $from File or folder to be copied, relative to root
408 * @param string $to Destination path, relative to root
409 * @param null|string $fromCopyRef Must be used instead of the from_path
410 * @return object stdClass
411 */
412 public function copy($from, $to, $fromCopyRef = null)
413 {
414 $call = 'fileops/copy';
415 $params = array(
416 'root' => $this->root,
417 'from_path' => $this->normalisePath($from),
418 'to_path' => $this->normalisePath($to),
419 );
420
421 if ($fromCopyRef) {
422 $params['from_path'] = null;
423 $params['from_copy_ref'] = $fromCopyRef;
424 }
425
426 $response = $this->fetch('POST', self::API_URL, $call, $params);
427 return $response;
428 }
429
430 /**
431 * Creates a folder
432 * @param string New folder to create relative to root
433 * @return object stdClass
434 */
435 public function create($path)
436 {
437 $call = 'fileops/create_folder';
438 $params = array('root' => $this->root, 'path' => $this->normalisePath($path));
439 $response = $this->fetch('POST', self::API_URL, $call, $params);
440 return $response;
441 }
442
443 /**
444 * Deletes a file or folder
445 * @param string $path The path to the file or folder to be deleted
446 * @return object stdClass
447 */
448 public function delete($path)
449 {
450 $call = 'fileops/delete';
451 $params = array('root' => $this->root, 'path' => $this->normalisePath($path));
452 $response = $this->fetch('POST', self::API_URL, $call, $params);
453 return $response;
454 }
455
456 /**
457 * Moves a file or folder to a new location
458 * @param string $from File or folder to be moved, relative to root
459 * @param string $to Destination path, relative to root
460 * @return object stdClass
461 */
462 public function move($from, $to)
463 {
464 $call = 'fileops/move';
465 $params = array(
466 'root' => $this->root,
467 'from_path' => $this->normalisePath($from),
468 'to_path' => $this->normalisePath($to),
469 );
470 $response = $this->fetch('POST', self::API_URL, $call, $params);
471 return $response;
472 }
473
474 /**
475 * Intermediate fetch function
476 * @param string $method The HTTP method
477 * @param string $url The API endpoint
478 * @param string $call The API method to call
479 * @param array $params Additional parameters
480 * @return mixed
481 */
482 private function fetch($method, $url, $call, array $params = array())
483 {
484 // Make the API call via the consumer
485 $response = $this->OAuth->fetch($method, $url, $call, $params);
486
487 // Format the response and return
488 switch ($this->responseFormat) {
489 case 'json':
490 return json_encode($response);
491 case 'jsonp':
492 $response = json_encode($response);
493 return $this->callback . '(' . $response . ')';
494 default:
495 return $response;
496 }
497 }
498
499 /**
500 * Set the API response format
501 * @param string $format One of php, json or jsonp
502 * @return void
503 */
504 public function setResponseFormat($format)
505 {
506 $format = strtolower($format);
507 if (!in_array($format, array('php', 'json', 'jsonp'))) {
508 throw new Exception("Expected a format of php, json or jsonp, got '$format'");
509 } else {
510 $this->responseFormat = $format;
511 }
512 }
513
514 /**
515 * Set the chunk size for chunked uploads
516 * If $chunkSize is empty, set to 4194304 bytes (4 MB)
517 * @see \Dropbox\API\chunkedUpload()
518 */
519 public function setChunkSize($chunkSize = 4194304)
520 {
521 if (!is_int($chunkSize)) {
522 throw new Exception('Expecting chunk size to be an integer, got ' . gettype($chunkSize));
523 } elseif ($chunkSize > 157286400) {
524 throw new Exception('Chunk size must not exceed 157286400 bytes, got ' . $chunkSize);
525 } else {
526 $this->chunkSize = $chunkSize;
527 }
528 }
529
530 /**
531 * Set the JSONP callback function
532 * @param string $function
533 * @return void
534 */
535 public function setCallback($function)
536 {
537 $this->callback = $function;
538 }
539
540 /**
541 * Get the mime type of downloaded file
542 * If the Fileinfo extension is not loaded, return false
543 * @param string $data File contents as a string or filename
544 * @param string $isFilename Is $data a filename?
545 * @return boolean|string Mime type and encoding of the file
546 */
547 private function getMimeType($data, $isFilename = false)
548 {
549 if (extension_loaded('fileinfo')) {
550 $finfo = new \finfo(FILEINFO_MIME);
551 if ($isFilename !== false) {
552 return $finfo->file($data);
553 }
554 return $finfo->buffer($data);
555 }
556 return false;
557 }
558
559 /**
560 * Trim the path of forward slashes and replace
561 * consecutive forward slashes with a single slash
562 * @param string $path The path to normalise
563 * @return string
564 */
565 private function normalisePath($path)
566 {
567 $path = preg_replace('#/+#', '/', trim($path, '/'));
568 return $path;
569 }
570
571 /**
572 * Encode the path, then replace encoded slashes
573 * with literal forward slash characters
574 * @param string $path The path to encode
575 * @return string
576 */
577 private function encodePath($path)
578 {
579 $path = $this->normalisePath($path);
580 $path = str_replace('%2F', '/', rawurlencode($path));
581 return $path;
582 }
583 }
584