PluginProbe
UpdraftPlus: WP Backup & Migration Plugin / 1.8.13
UpdraftPlus: WP Backup & Migration Plugin v1.8.13
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.8.13, at includes/Dropbox/API.php

594 lines 22.2 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, $file);
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 * @param boolean $allow_resume - append to the file if it already exists
206 * @return array
207 */
208 public function getFile($file, $outFile = false, $revision = null, $allow_resume = false)
209 {
210 // Only allow php response format for this call
211 if ($this->responseFormat !== 'php') {
212 throw new Exception('This method only supports the `php` response format');
213 }
214
215 $params = array('rev' => $revision);
216
217 $handle = null;
218 if ($outFile !== false) {
219 // Create a file handle if $outFile is specified
220 if ($allow_resume && file_exists($outFile)) {
221 if (!$handle = fopen($outFile, 'a')) {
222 throw new Exception("Unable to open file handle for $outFile");
223 } else {
224 $this->OAuth->setOutFile($handle);
225 $params['headers'] = array('Range: bytes='.filesize($outFile).'-');
226 }
227 }
228 elseif (!$handle = fopen($outFile, 'w')) {
229 throw new Exception("Unable to open file handle for $outFile");
230 } else {
231 $this->OAuth->setOutFile($handle);
232 }
233 }
234
235 $file = $this->encodePath($file);
236 $call = 'files/' . $this->root . '/' . $file;
237 $response = $this->fetch('GET', self::CONTENT_URL, $call, $params);
238
239 // Close the file handle if one was opened
240 if ($handle) fclose($handle);
241
242 return array(
243 'name' => ($outFile) ? $outFile : basename($file),
244 'mime' => $this->getMimeType(($outFile) ? $outFile : $response['body'], $outFile),
245 'meta' => json_decode($response['headers']['x-dropbox-metadata']),
246 'data' => $response['body'],
247 );
248 }
249
250 /**
251 * Retrieves file and folder metadata
252 * @param string $path The path to the file/folder, relative to root
253 * @param string $rev Return metadata for a specific revision (Default: latest rev)
254 * @param int $limit Maximum number of listings to return
255 * @param string $hash Metadata hash to compare against
256 * @param bool $list Return contents field with response
257 * @param bool $deleted Include files/folders that have been deleted
258 * @return object stdClass
259 */
260 public function metaData($path = null, $rev = null, $limit = 10000, $hash = false, $list = true, $deleted = false)
261 {
262 $call = 'metadata/' . $this->root . '/' . $this->encodePath($path);
263 $params = array(
264 'file_limit' => ($limit < 1) ? 1 : (($limit > 10000) ? 10000 : (int) $limit),
265 'hash' => (is_string($hash)) ? $hash : 0,
266 'list' => (int) $list,
267 'include_deleted' => (int) $deleted,
268 'rev' => (is_string($rev)) ? $rev : null,
269 );
270 $response = $this->fetch('POST', self::API_URL, $call, $params);
271 return $response;
272 }
273
274 /**
275 * Return "delta entries", intructing you how to update
276 * your application state to match the server's state
277 * Important: This method does not make changes to the application state
278 * @param null|string $cursor Used to keep track of your current state
279 * @return array Array of delta entries
280 */
281 public function delta($cursor = null)
282 {
283 $call = 'delta';
284 $params = array('cursor' => $cursor);
285 $response = $this->fetch('POST', self::API_URL, $call, $params);
286 return $response;
287 }
288
289 /**
290 * Obtains metadata for the previous revisions of a file
291 * @param string Path to the file, relative to root
292 * @param integer Number of revisions to return (1-1000)
293 * @return array
294 */
295 public function revisions($file, $limit = 10)
296 {
297 $call = 'revisions/' . $this->root . '/' . $this->encodePath($file);
298 $params = array(
299 'rev_limit' => ($limit < 1) ? 1 : (($limit > 1000) ? 1000 : (int) $limit),
300 );
301 $response = $this->fetch('GET', self::API_URL, $call, $params);
302 return $response;
303 }
304
305 /**
306 * Restores a file path to a previous revision
307 * @param string $file Path to the file, relative to root
308 * @param string $revision The revision of the file to restore
309 * @return object stdClass
310 */
311 public function restore($file, $revision)
312 {
313 $call = 'restore/' . $this->root . '/' . $this->encodePath($file);
314 $params = array('rev' => $revision);
315 $response = $this->fetch('POST', self::API_URL, $call, $params);
316 return $response;
317 }
318
319 /**
320 * Returns metadata for all files and folders that match the search query
321 * @param mixed $query The search string. Must be at least 3 characters long
322 * @param string $path The path to the folder you want to search in
323 * @param integer $limit Maximum number of results to return (1-1000)
324 * @param boolean $deleted Include deleted files/folders in the search
325 * @return array
326 */
327 public function search($query, $path = '', $limit = 1000, $deleted = false)
328 {
329 $call = 'search/' . $this->root . '/' . $this->encodePath($path);
330 $params = array(
331 'query' => $query,
332 'file_limit' => ($limit < 1) ? 1 : (($limit > 1000) ? 1000 : (int) $limit),
333 'include_deleted' => (int) $deleted,
334 );
335 $response = $this->fetch('GET', self::API_URL, $call, $params);
336 return $response;
337 }
338
339 /**
340 * Creates and returns a shareable link to files or folders
341 * The link returned is for a preview page from which the user an choose to
342 * download the file if they wish. For direct download links, see media().
343 * @param string $path The path to the file/folder you want a sharable link to
344 * @return object stdClass
345 */
346 public function shares($path, $shortUrl = true)
347 {
348 $call = 'shares/' . $this->root . '/' .$this->encodePath($path);
349 $params = array('short_url' => ($shortUrl) ? 1 : 0);
350 $response = $this->fetch('POST', self::API_URL, $call, $params);
351 return $response;
352 }
353
354 /**
355 * Returns a link directly to a file
356 * @param string $path The path to the media file you want a direct link to
357 * @return object stdClass
358 */
359 public function media($path)
360 {
361 $call = 'media/' . $this->root . '/' . $this->encodePath($path);
362 $response = $this->fetch('POST', self::API_URL, $call);
363 return $response;
364 }
365
366 /**
367 * Gets a thumbnail for an image
368 * @param string $file The path to the image you wish to thumbnail
369 * @param string $format The thumbnail format, either JPEG or PNG
370 * @param string $size The size of the thumbnail
371 * @return array
372 */
373 public function thumbnails($file, $format = 'JPEG', $size = 'small')
374 {
375 // Only allow php response format for this call
376 if ($this->responseFormat !== 'php') {
377 throw new Exception('This method only supports the `php` response format');
378 }
379
380 $format = strtoupper($format);
381 // If $format is not 'PNG', default to 'JPEG'
382 if ($format != 'PNG') $format = 'JPEG';
383
384 $size = strtolower($size);
385 $sizes = array('s', 'm', 'l', 'xl', 'small', 'medium', 'large');
386 // If $size is not valid, default to 'small'
387 if (!in_array($size, $sizes)) $size = 'small';
388
389 $call = 'thumbnails/' . $this->root . '/' . $this->encodePath($file);
390 $params = array('format' => $format, 'size' => $size);
391 $response = $this->fetch('GET', self::CONTENT_URL, $call, $params);
392
393 return array(
394 'name' => basename($file),
395 'mime' => $this->getMimeType($response['body']),
396 'meta' => json_decode($response['headers']['x-dropbox-metadata']),
397 'data' => $response['body'],
398 );
399 }
400
401 /**
402 * Creates and returns a copy_ref to a file
403 * This reference string can be used to copy that file to another user's
404 * Dropbox by passing it in as the from_copy_ref parameter on /fileops/copy
405 * @param $path File for which ref should be created, relative to root
406 * @return array
407 */
408 public function copyRef($path)
409 {
410 $call = 'copy_ref/' . $this->root . '/' . $this->encodePath($path);
411 $response = $this->fetch('GET', self::API_URL, $call);
412 return $response;
413 }
414
415 /**
416 * Copies a file or folder to a new location
417 * @param string $from File or folder to be copied, relative to root
418 * @param string $to Destination path, relative to root
419 * @param null|string $fromCopyRef Must be used instead of the from_path
420 * @return object stdClass
421 */
422 public function copy($from, $to, $fromCopyRef = null)
423 {
424 $call = 'fileops/copy';
425 $params = array(
426 'root' => $this->root,
427 'from_path' => $this->normalisePath($from),
428 'to_path' => $this->normalisePath($to),
429 );
430
431 if ($fromCopyRef) {
432 $params['from_path'] = null;
433 $params['from_copy_ref'] = $fromCopyRef;
434 }
435
436 $response = $this->fetch('POST', self::API_URL, $call, $params);
437 return $response;
438 }
439
440 /**
441 * Creates a folder
442 * @param string New folder to create relative to root
443 * @return object stdClass
444 */
445 public function create($path)
446 {
447 $call = 'fileops/create_folder';
448 $params = array('root' => $this->root, 'path' => $this->normalisePath($path));
449 $response = $this->fetch('POST', self::API_URL, $call, $params);
450 return $response;
451 }
452
453 /**
454 * Deletes a file or folder
455 * @param string $path The path to the file or folder to be deleted
456 * @return object stdClass
457 */
458 public function delete($path)
459 {
460 $call = 'fileops/delete';
461 $params = array('root' => $this->root, 'path' => $this->normalisePath($path));
462 $response = $this->fetch('POST', self::API_URL, $call, $params);
463 return $response;
464 }
465
466 /**
467 * Moves a file or folder to a new location
468 * @param string $from File or folder to be moved, relative to root
469 * @param string $to Destination path, relative to root
470 * @return object stdClass
471 */
472 public function move($from, $to)
473 {
474 $call = 'fileops/move';
475 $params = array(
476 'root' => $this->root,
477 'from_path' => $this->normalisePath($from),
478 'to_path' => $this->normalisePath($to),
479 );
480 $response = $this->fetch('POST', self::API_URL, $call, $params);
481 return $response;
482 }
483
484 /**
485 * Intermediate fetch function
486 * @param string $method The HTTP method
487 * @param string $url The API endpoint
488 * @param string $call The API method to call
489 * @param array $params Additional parameters
490 * @return mixed
491 */
492 private function fetch($method, $url, $call, array $params = array())
493 {
494 // Make the API call via the consumer
495 $response = $this->OAuth->fetch($method, $url, $call, $params);
496
497 // Format the response and return
498 switch ($this->responseFormat) {
499 case 'json':
500 return json_encode($response);
501 case 'jsonp':
502 $response = json_encode($response);
503 return $this->callback . '(' . $response . ')';
504 default:
505 return $response;
506 }
507 }
508
509 /**
510 * Set the API response format
511 * @param string $format One of php, json or jsonp
512 * @return void
513 */
514 public function setResponseFormat($format)
515 {
516 $format = strtolower($format);
517 if (!in_array($format, array('php', 'json', 'jsonp'))) {
518 throw new Exception("Expected a format of php, json or jsonp, got '$format'");
519 } else {
520 $this->responseFormat = $format;
521 }
522 }
523
524 /**
525 * Set the chunk size for chunked uploads
526 * If $chunkSize is empty, set to 4194304 bytes (4 MB)
527 * @see \Dropbox\API\chunkedUpload()
528 */
529 public function setChunkSize($chunkSize = 4194304)
530 {
531 if (!is_int($chunkSize)) {
532 throw new Exception('Expecting chunk size to be an integer, got ' . gettype($chunkSize));
533 } elseif ($chunkSize > 157286400) {
534 throw new Exception('Chunk size must not exceed 157286400 bytes, got ' . $chunkSize);
535 } else {
536 $this->chunkSize = $chunkSize;
537 }
538 }
539
540 /**
541 * Set the JSONP callback function
542 * @param string $function
543 * @return void
544 */
545 public function setCallback($function)
546 {
547 $this->callback = $function;
548 }
549
550 /**
551 * Get the mime type of downloaded file
552 * If the Fileinfo extension is not loaded, return false
553 * @param string $data File contents as a string or filename
554 * @param string $isFilename Is $data a filename?
555 * @return boolean|string Mime type and encoding of the file
556 */
557 private function getMimeType($data, $isFilename = false)
558 {
559 if (extension_loaded('fileinfo')) {
560 $finfo = new finfo(FILEINFO_MIME);
561 if ($isFilename !== false) {
562 return $finfo->file($data);
563 }
564 return $finfo->buffer($data);
565 }
566 return false;
567 }
568
569 /**
570 * Trim the path of forward slashes and replace
571 * consecutive forward slashes with a single slash
572 * @param string $path The path to normalise
573 * @return string
574 */
575 private function normalisePath($path)
576 {
577 $path = preg_replace('#/+#', '/', trim($path, '/'));
578 return $path;
579 }
580
581 /**
582 * Encode the path, then replace encoded slashes
583 * with literal forward slash characters
584 * @param string $path The path to encode
585 * @return string
586 */
587 private function encodePath($path)
588 {
589 $path = $this->normalisePath($path);
590 $path = str_replace('%2F', '/', rawurlencode($path));
591 return $path;
592 }
593 }
594