PluginProbe
UpdraftPlus: WP Backup & Migration Plugin / 1.16.5
UpdraftPlus: WP Backup & Migration Plugin v1.16.5
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 / Dropbox2 / API.php

API.php in UpdraftPlus: WP Backup & Migration Plugin 1.16.5, at includes/Dropbox2/API.php

388 lines 13.9 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 UpdraftPlus_Dropbox_API {
12 // API Endpoints
13 const API_URL_V2 = 'https://api.dropboxapi.com/';
14 const CONTENT_URL_V2 = 'https://content.dropboxapi.com/2/';
15
16 /**
17 * OAuth consumer object
18 * @var null|OAuth\Consumer
19 */
20 private $OAuth;
21
22 /**
23 * The root level for file paths
24 * Either `dropbox` or `sandbox` (preferred)
25 * @var null|string
26 */
27 private $root;
28
29 /**
30 * Format of the API response
31 * @var string
32 */
33 private $responseFormat = 'php';
34
35 /**
36 * JSONP callback
37 * @var string
38 */
39 private $callback = 'dropboxCallback';
40
41 /**
42 * Chunk size used for chunked uploads
43 * @see \Dropbox\API::chunkedUpload()
44 */
45 private $chunkSize = 4194304;
46
47 /**
48 * Set the OAuth consumer object
49 * See 'General Notes' at the link below for information on access type
50 * @link https://www.dropbox.com/developers/reference/api
51 * @param OAuth\Consumer\ConsumerAbstract $OAuth
52 * @param string $root Dropbox app access type
53 */
54 public function __construct(Dropbox_ConsumerAbstract $OAuth, $root = 'sandbox') {
55 $this->OAuth = $OAuth;
56 $this->setRoot($root);
57 }
58
59 /**
60 * Set the root level
61 * @param mixed $root
62 * @throws Exception
63 * @return void
64 */
65 public function setRoot($root) {
66 if ($root !== 'sandbox' && $root !== 'dropbox') {
67 throw new Exception("Expected a root of either 'dropbox' or 'sandbox', got '$root'");
68 } else {
69 $this->root = $root;
70 }
71 }
72
73 /**
74 * Retrieves information about the user's account
75 * @return object stdClass
76 */
77 public function accountInfo() {
78 $call = '2/users/get_current_account';
79 $params = array('api_v2' => true);
80 $response = $this->fetch('POST', self::API_URL_V2, $call, $params);
81 return $response;
82 }
83
84 /**
85 * Retrieves information about the user's quota
86 * @return object stdClass
87 */
88 public function quotaInfo() {
89 $call = '2/users/get_space_usage';
90 $params = array('api_v2' => true);
91 $response = $this->fetch('POST', self::API_URL_V2, $call, $params);
92 return $response;
93 }
94
95 /**
96 * Uploads large files to Dropbox in mulitple chunks
97 * @param string $file Absolute path to the file to be uploaded
98 * @param string|bool $filename The destination filename of the uploaded file
99 * @param string $path Path to upload the file to, relative to root
100 * @param boolean $overwrite Should the file be overwritten? (Default: true)
101 * @param integer $offset position to seek to when opening the file
102 * @param string $uploadID existing upload_id to resume an upload
103 * @param string|array function to call back to upon each chunk
104 * @return stdClass
105 */
106 public function chunkedUpload($file, $filename = false, $path = '', $overwrite = true, $offset = 0, $uploadID = null, $callback = null) {
107
108 if (file_exists($file)) {
109 if ($handle = @fopen($file, 'r')) {
110 // Set initial upload ID and offset
111 if ($offset > 0) {
112 fseek($handle, $offset);
113 }
114
115 /*
116 Set firstCommit to true so that the upload session start endpoint is called.
117 */
118 $firstCommit = (0 == $offset);
119
120 // Read from the file handle until EOF, uploading each chunk
121 while ($data = fread($handle, $this->chunkSize)) {
122
123 // Set the file, request parameters and send the request
124 $this->OAuth->setInFile($data);
125
126 if ($firstCommit) {
127 $params = array(
128 'close' => false,
129 'api_v2' => true,
130 'content_upload' => true
131 );
132 $response = $this->fetch('POST', self::CONTENT_URL_V2, 'files/upload_session/start', $params);
133 $firstCommit = false;
134 } else {
135 $params = array(
136 'cursor' => array(
137 'session_id' => $uploadID,
138 // If you send it as a string, Dropbox will be unhappy
139 'offset' => (int)$offset
140 ),
141 'api_v2' => true,
142 'content_upload' => true
143 );
144 $response = $this->append_upload($params, false);
145 }
146
147 // On subsequent chunks, use the upload ID returned by the previous request
148 if (isset($response['body']->session_id)) {
149 $uploadID = $response['body']->session_id;
150 }
151
152 /*
153 API v2 no longer returns the offset, we need to manually work this out. So check that there are no errors and update the offset as well as calling the callback method.
154 */
155 if (!isset($response['body']->error)) {
156 $offset = ftell($handle);
157 if ($callback) {
158 call_user_func($callback, $offset, $uploadID, $file);
159 }
160 $this->OAuth->setInFile(null);
161 }
162 }
163
164 // Complete the chunked upload
165 $filename = (is_string($filename)) ? $filename : basename($file);
166 $params = array(
167 'cursor' => array(
168 'session_id' => $uploadID,
169 'offset' => $offset
170 ),
171 'commit' => array(
172 'path' => '/' . $this->encodePath($path . $filename),
173 'mode' => 'add'
174 ),
175 'api_v2' => true,
176 'content_upload' => true
177 );
178 $response = $this->append_upload($params, true);
179 return $response;
180 } else {
181 throw new Exception('Could not open ' . $file . ' for reading');
182 }
183 }
184
185 // Throw an Exception if the file does not exist
186 throw new Exception('Local file ' . $file . ' does not exist');
187 }
188
189 private function append_upload($params, $last_call) {
190 try {
191 if ($last_call){
192 $response = $this->fetch('POST', self::CONTENT_URL_V2, 'files/upload_session/finish', $params);
193 } else {
194 $response = $this->fetch('POST', self::CONTENT_URL_V2, 'files/upload_session/append_v2', $params);
195 }
196 } catch (Exception $e) {
197 $responseCheck = json_decode($e->getMessage());
198 if (isset($responseCheck) && strpos($responseCheck[0] , 'incorrect_offset') !== false) {
199 $expected_offset = $responseCheck[1];
200 throw new Exception('Submitted input out of alignment: got ['.$params['cursor']['offset'].'] expected ['.$expected_offset.']');
201
202 // $params['cursor']['offset'] = $responseCheck[1];
203 // $response = $this->append_upload($params, $last_call);
204 } else {
205 throw $e;
206 }
207 }
208 return $response;
209 }
210
211 /**
212 * Chunked downloads a file from Dropbox, it will return false if a file handle is not passed and will return true if the call was successful.
213 *
214 * @param string $file Path - to file, relative to root, including path
215 * @param resource $outFile - the local file handle
216 * @param array $options - any extra options to be passed e.g headers
217 * @return boolean - a boolean to indicate success or failure
218 */
219 public function download($file, $outFile = null, $options = array()) {
220 // Only allow php response format for this call
221 if ($this->responseFormat !== 'php') {
222 throw new Exception('This method only supports the `php` response format');
223 }
224
225 if ($outFile) {
226 $this->OAuth->setOutFile($outFile);
227
228 $params = array('path' => '/' . $file, 'api_v2' => true, 'content_download' => true);
229
230 if (isset($options['headers'])) {
231 foreach ($options['headers'] as $key => $header) {
232 $headers[] = $key . ': ' . $header;
233 }
234 $params['headers'] = $headers;
235 }
236
237 $file = $this->encodePath($file);
238 $call = 'files/download';
239
240 $response = $this->fetch('GET', self::CONTENT_URL_V2, $call, $params);
241
242 fclose($outFile);
243
244 return true;
245 } else {
246 return false;
247 }
248 }
249
250 /**
251 * Returns metadata for all files and folders that match the search query
252 * @param mixed $query The search string. Must be at least 3 characters long
253 * @param string [$path=''] The path to the folder you want to search in
254 * @param integer [$limit=1000] Maximum number of results to return (1-1000)
255 * @param integer [$start=0] Result number to start from
256 * @return array
257 */
258 public function search($query, $path = '', $limit = 1000, $start = 0) {
259 $call = '2/files/search';
260 $path = $this->encodePath($path);
261 // APIv2 requires that the path match this regex: String(pattern="(/(.|[\r\n])*)?|(ns:[0-9]+(/.*)?)")
262 if ($path && '/' != substr($path, 0, 1)) $path = "/$path";
263 $params = array(
264 'path' => $path,
265 'query' => $query,
266 'start' => $start,
267 'max_results' => ($limit < 1) ? 1 : (($limit > 1000) ? 1000 : (int) $limit),
268 'api_v2' => true,
269 );
270 $response = $this->fetch('POST', self::API_URL_V2, $call, $params);
271 return $response;
272 }
273
274 /**
275 * Deletes a file or folder
276 * @param string $path The path to the file or folder to be deleted
277 * @return object stdClass
278 */
279 public function delete($path) {
280 $call = '2/files/delete';
281 $params = array('path' => '/' . $this->normalisePath($path), 'api_v2' => true);
282 $response = $this->fetch('POST', self::API_URL_V2, $call, $params);
283 return $response;
284 }
285
286 /**
287 * Intermediate fetch function
288 * @param string $method The HTTP method
289 * @param string $url The API endpoint
290 * @param string $call The API method to call
291 * @param array $params Additional parameters
292 * @return mixed
293 */
294 private function fetch($method, $url, $call, array $params = array()) {
295 // Make the API call via the consumer
296 $response = $this->OAuth->fetch($method, $url, $call, $params);
297
298 // Format the response and return
299 switch ($this->responseFormat) {
300 case 'json':
301 return json_encode($response);
302 case 'jsonp':
303 $response = json_encode($response);
304 return $this->callback . '(' . $response . ')';
305 default:
306 return $response;
307 }
308 }
309
310 /**
311 * Set the API response format
312 * @param string $format One of php, json or jsonp
313 * @return void
314 */
315 public function setResponseFormat($format) {
316 $format = strtolower($format);
317 if (!in_array($format, array('php', 'json', 'jsonp'))) {
318 throw new Exception("Expected a format of php, json or jsonp, got '$format'");
319 } else {
320 $this->responseFormat = $format;
321 }
322 }
323
324 /**
325 * Set the chunk size for chunked uploads
326 * If $chunkSize is empty, set to 4194304 bytes (4 MB)
327 * @see \Dropbox\API\chunkedUpload()
328 */
329 public function setChunkSize($chunkSize = 4194304) {
330 if (!is_int($chunkSize)) {
331 throw new Exception('Expecting chunk size to be an integer, got ' . gettype($chunkSize));
332 } elseif ($chunkSize > 157286400) {
333 throw new Exception('Chunk size must not exceed 157286400 bytes, got ' . $chunkSize);
334 } else {
335 $this->chunkSize = $chunkSize;
336 }
337 }
338
339 /**
340 * Set the JSONP callback function
341 * @param string $function
342 * @return void
343 */
344 public function setCallback($function) {
345 $this->callback = $function;
346 }
347
348 /**
349 * Get the mime type of downloaded file
350 * If the Fileinfo extension is not loaded, return false
351 * @param string $data File contents as a string or filename
352 * @param string $isFilename Is $data a filename?
353 * @return boolean|string Mime type and encoding of the file
354 */
355 private function getMimeType($data, $isFilename = false) {
356 if (extension_loaded('fileinfo')) {
357 $finfo = new finfo(FILEINFO_MIME);
358 if ($isFilename !== false) {
359 return $finfo->file($data);
360 }
361 return $finfo->buffer($data);
362 }
363 return false;
364 }
365
366 /**
367 * Trim the path of forward slashes and replace
368 * consecutive forward slashes with a single slash
369 * @param string $path The path to normalise
370 * @return string
371 */
372 private function normalisePath($path) {
373 $path = preg_replace('#/+#', '/', trim($path, '/'));
374 return $path;
375 }
376
377 /**
378 * Encode the path, then replace encoded slashes
379 * with literal forward slash characters
380 * @param string $path The path to encode
381 * @return string
382 */
383 private function encodePath($path) {
384 // in APIv1, encoding was needed because parameters were passed as part of the URL; this is no longer done in our APIv2 SDK; hence, all that we now do here is normalise.
385 return $this->normalisePath($path);
386 }
387 }
388