1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* @author Jörn Friedrich Dreyer <[email protected]> |
4
|
|
|
* @author Michael Gapczynski <[email protected]> |
5
|
|
|
* @author Morris Jobke <[email protected]> |
6
|
|
|
* @author Philipp Kapfer <[email protected]> |
7
|
|
|
* @author Robin Appelman <[email protected]> |
8
|
|
|
* @author Robin McCorkell <[email protected]> |
9
|
|
|
* @author Sascha Schmidt <[email protected]> |
10
|
|
|
* @author Thomas Müller <[email protected]> |
11
|
|
|
* @author Vincent Petry <[email protected]> |
12
|
|
|
* |
13
|
|
|
* @copyright Copyright (c) 2016, ownCloud, Inc. |
14
|
|
|
* @license AGPL-3.0 |
15
|
|
|
* |
16
|
|
|
* This code is free software: you can redistribute it and/or modify |
17
|
|
|
* it under the terms of the GNU Affero General Public License, version 3, |
18
|
|
|
* as published by the Free Software Foundation. |
19
|
|
|
* |
20
|
|
|
* This program is distributed in the hope that it will be useful, |
21
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
22
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
23
|
|
|
* GNU Affero General Public License for more details. |
24
|
|
|
* |
25
|
|
|
* You should have received a copy of the GNU Affero General Public License, version 3, |
26
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/> |
27
|
|
|
* |
28
|
|
|
*/ |
29
|
|
|
|
30
|
|
|
namespace OCA\Files_External\Lib\Storage; |
31
|
|
|
|
32
|
|
|
use GuzzleHttp\Exception\RequestException; |
33
|
|
|
use Icewind\Streams\IteratorDirectory; |
34
|
|
|
use Icewind\Streams\RetryWrapper; |
35
|
|
|
|
36
|
|
|
require_once __DIR__ . '/../3rdparty/Dropbox/autoload.php'; |
37
|
|
|
|
38
|
|
|
class Dropbox extends \OC\Files\Storage\Common { |
39
|
|
|
|
40
|
|
|
private $dropbox; |
41
|
|
|
private $root; |
42
|
|
|
private $id; |
43
|
|
|
private $metaData = array(); |
44
|
|
|
private $oauth; |
45
|
|
|
|
46
|
|
|
private static $tempFiles = array(); |
47
|
|
|
|
48
|
|
|
public function __construct($params) { |
49
|
|
|
if (isset($params['configured']) && $params['configured'] == 'true' |
50
|
|
|
&& isset($params['app_key']) |
51
|
|
|
&& isset($params['app_secret']) |
52
|
|
|
&& isset($params['token']) |
53
|
|
|
&& isset($params['token_secret']) |
54
|
|
|
) { |
55
|
|
|
$this->root = isset($params['root']) ? $params['root'] : ''; |
56
|
|
|
$this->id = 'dropbox::'.$params['app_key'] . $params['token']. '/' . $this->root; |
57
|
|
|
$this->oauth = new \Dropbox_OAuth_Curl($params['app_key'], $params['app_secret']); |
58
|
|
|
$this->oauth->setToken($params['token'], $params['token_secret']); |
59
|
|
|
// note: Dropbox_API connection is lazy |
60
|
|
|
$this->dropbox = new \Dropbox_API($this->oauth, 'auto'); |
61
|
|
|
} else { |
62
|
|
|
throw new \Exception('Creating Dropbox storage failed'); |
63
|
|
|
} |
64
|
|
|
} |
65
|
|
|
|
66
|
|
|
/** |
67
|
|
|
* @param string $path |
68
|
|
|
*/ |
69
|
|
|
private function deleteMetaData($path) { |
70
|
|
|
$path = ltrim($this->root.$path, '/'); |
71
|
|
|
if (isset($this->metaData[$path])) { |
72
|
|
|
unset($this->metaData[$path]); |
73
|
|
|
return true; |
74
|
|
|
} |
75
|
|
|
return false; |
76
|
|
|
} |
77
|
|
|
|
78
|
|
|
private function setMetaData($path, $metaData) { |
79
|
|
|
$this->metaData[ltrim($path, '/')] = $metaData; |
80
|
|
|
} |
81
|
|
|
|
82
|
|
|
/** |
83
|
|
|
* Returns the path's metadata |
84
|
|
|
* @param string $path path for which to return the metadata |
85
|
|
|
* @param bool $list if true, also return the directory's contents |
86
|
|
|
* @return mixed directory contents if $list is true, file metadata if $list is |
87
|
|
|
* false, null if the file doesn't exist or "false" if the operation failed |
88
|
|
|
*/ |
89
|
|
|
private function getDropBoxMetaData($path, $list = false) { |
90
|
|
|
$path = ltrim($this->root.$path, '/'); |
91
|
|
|
if ( ! $list && isset($this->metaData[$path])) { |
92
|
|
|
return $this->metaData[$path]; |
93
|
|
|
} else { |
94
|
|
|
if ($list) { |
95
|
|
|
try { |
96
|
|
|
$response = $this->dropbox->getMetaData($path); |
97
|
|
|
} catch (\Exception $exception) { |
98
|
|
|
\OCP\Util::writeLog('files_external', $exception->getMessage(), \OCP\Util::ERROR); |
99
|
|
|
return false; |
100
|
|
|
} |
101
|
|
|
$contents = array(); |
102
|
|
|
if ($response && isset($response['contents'])) { |
103
|
|
|
// Cache folder's contents |
104
|
|
|
foreach ($response['contents'] as $file) { |
105
|
|
|
if (!isset($file['is_deleted']) || !$file['is_deleted']) { |
106
|
|
|
$this->setMetaData($path.'/'.basename($file['path']), $file); |
107
|
|
|
$contents[] = $file; |
108
|
|
|
} |
109
|
|
|
} |
110
|
|
|
unset($response['contents']); |
111
|
|
|
} |
112
|
|
|
if (!isset($response['is_deleted']) || !$response['is_deleted']) { |
113
|
|
|
$this->setMetaData($path, $response); |
114
|
|
|
} |
115
|
|
|
// Return contents of folder only |
116
|
|
|
return $contents; |
117
|
|
|
} else { |
118
|
|
|
try { |
119
|
|
|
$requestPath = $path; |
120
|
|
|
if ($path === '.') { |
121
|
|
|
$requestPath = ''; |
122
|
|
|
} |
123
|
|
|
|
124
|
|
|
$response = $this->dropbox->getMetaData($requestPath, 'false'); |
125
|
|
|
if (!isset($response['is_deleted']) || !$response['is_deleted']) { |
126
|
|
|
$this->setMetaData($path, $response); |
127
|
|
|
return $response; |
128
|
|
|
} |
129
|
|
|
return null; |
130
|
|
|
} catch (\Exception $exception) { |
131
|
|
|
if ($exception instanceof \Dropbox_Exception_NotFound) { |
|
|
|
|
132
|
|
|
// don't log, might be a file_exist check |
133
|
|
|
return false; |
134
|
|
|
} |
135
|
|
|
\OCP\Util::writeLog('files_external', $exception->getMessage(), \OCP\Util::ERROR); |
136
|
|
|
return false; |
137
|
|
|
} |
138
|
|
|
} |
139
|
|
|
} |
140
|
|
|
} |
141
|
|
|
|
142
|
|
|
public function getId(){ |
143
|
|
|
return $this->id; |
144
|
|
|
} |
145
|
|
|
|
146
|
|
View Code Duplication |
public function mkdir($path) { |
147
|
|
|
$path = $this->root.$path; |
148
|
|
|
try { |
149
|
|
|
$this->dropbox->createFolder($path); |
150
|
|
|
return true; |
151
|
|
|
} catch (\Exception $exception) { |
152
|
|
|
\OCP\Util::writeLog('files_external', $exception->getMessage(), \OCP\Util::ERROR); |
153
|
|
|
return false; |
154
|
|
|
} |
155
|
|
|
} |
156
|
|
|
|
157
|
|
|
public function rmdir($path) { |
158
|
|
|
return $this->unlink($path); |
159
|
|
|
} |
160
|
|
|
|
161
|
|
|
public function opendir($path) { |
162
|
|
|
$contents = $this->getDropBoxMetaData($path, true); |
163
|
|
|
if ($contents !== false) { |
164
|
|
|
$files = array(); |
165
|
|
|
foreach ($contents as $file) { |
166
|
|
|
$files[] = basename($file['path']); |
167
|
|
|
} |
168
|
|
|
return IteratorDirectory::wrap($files); |
169
|
|
|
} |
170
|
|
|
return false; |
171
|
|
|
} |
172
|
|
|
|
173
|
|
|
public function stat($path) { |
174
|
|
|
$metaData = $this->getDropBoxMetaData($path); |
175
|
|
|
if ($metaData) { |
176
|
|
|
$stat['size'] = $metaData['bytes']; |
|
|
|
|
177
|
|
|
$stat['atime'] = time(); |
178
|
|
|
$stat['mtime'] = (isset($metaData['modified'])) ? strtotime($metaData['modified']) : time(); |
179
|
|
|
return $stat; |
180
|
|
|
} |
181
|
|
|
return false; |
182
|
|
|
} |
183
|
|
|
|
184
|
|
|
public function filetype($path) { |
185
|
|
|
if ($path == '' || $path == '/') { |
186
|
|
|
return 'dir'; |
187
|
|
|
} else { |
188
|
|
|
$metaData = $this->getDropBoxMetaData($path); |
189
|
|
|
if ($metaData) { |
190
|
|
|
if ($metaData['is_dir'] == 'true') { |
191
|
|
|
return 'dir'; |
192
|
|
|
} else { |
193
|
|
|
return 'file'; |
194
|
|
|
} |
195
|
|
|
} |
196
|
|
|
} |
197
|
|
|
return false; |
198
|
|
|
} |
199
|
|
|
|
200
|
|
|
public function file_exists($path) { |
201
|
|
|
if ($path == '' || $path == '/') { |
202
|
|
|
return true; |
203
|
|
|
} |
204
|
|
|
if ($this->getDropBoxMetaData($path)) { |
205
|
|
|
return true; |
206
|
|
|
} |
207
|
|
|
return false; |
208
|
|
|
} |
209
|
|
|
|
210
|
|
View Code Duplication |
public function unlink($path) { |
211
|
|
|
try { |
212
|
|
|
$this->dropbox->delete($this->root.$path); |
213
|
|
|
$this->deleteMetaData($path); |
214
|
|
|
return true; |
215
|
|
|
} catch (\Exception $exception) { |
216
|
|
|
\OCP\Util::writeLog('files_external', $exception->getMessage(), \OCP\Util::ERROR); |
217
|
|
|
return false; |
218
|
|
|
} |
219
|
|
|
} |
220
|
|
|
|
221
|
|
|
public function rename($path1, $path2) { |
222
|
|
|
try { |
223
|
|
|
// overwrite if target file exists and is not a directory |
224
|
|
|
$destMetaData = $this->getDropBoxMetaData($path2); |
225
|
|
|
if (isset($destMetaData) && $destMetaData !== false && !$destMetaData['is_dir']) { |
226
|
|
|
$this->unlink($path2); |
227
|
|
|
} |
228
|
|
|
$this->dropbox->move($this->root.$path1, $this->root.$path2); |
229
|
|
|
$this->deleteMetaData($path1); |
230
|
|
|
return true; |
231
|
|
|
} catch (\Exception $exception) { |
232
|
|
|
\OCP\Util::writeLog('files_external', $exception->getMessage(), \OCP\Util::ERROR); |
233
|
|
|
return false; |
234
|
|
|
} |
235
|
|
|
} |
236
|
|
|
|
237
|
|
|
public function copy($path1, $path2) { |
238
|
|
|
$path1 = $this->root.$path1; |
239
|
|
|
$path2 = $this->root.$path2; |
240
|
|
|
try { |
241
|
|
|
$this->dropbox->copy($path1, $path2); |
242
|
|
|
return true; |
243
|
|
|
} catch (\Exception $exception) { |
244
|
|
|
\OCP\Util::writeLog('files_external', $exception->getMessage(), \OCP\Util::ERROR); |
245
|
|
|
return false; |
246
|
|
|
} |
247
|
|
|
} |
248
|
|
|
|
249
|
|
|
public function fopen($path, $mode) { |
250
|
|
|
$path = $this->root.$path; |
251
|
|
|
switch ($mode) { |
252
|
|
|
case 'r': |
253
|
|
|
case 'rb': |
254
|
|
|
try { |
255
|
|
|
// slashes need to stay |
256
|
|
|
$encodedPath = str_replace('%2F', '/', rawurlencode(trim($path, '/'))); |
257
|
|
|
$downloadUrl = 'https://api-content.dropbox.com/1/files/auto/' . $encodedPath; |
258
|
|
|
$headers = $this->oauth->getOAuthHeader($downloadUrl, [], 'GET'); |
259
|
|
|
|
260
|
|
|
$client = \OC::$server->getHTTPClientService()->newClient(); |
261
|
|
|
try { |
262
|
|
|
$response = $client->get($downloadUrl, [ |
263
|
|
|
'headers' => $headers, |
264
|
|
|
'stream' => true, |
265
|
|
|
]); |
266
|
|
|
} catch (RequestException $e) { |
|
|
|
|
267
|
|
View Code Duplication |
if (!is_null($e->getResponse())) { |
|
|
|
|
268
|
|
|
if ($e->getResponse()->getStatusCode() === 404) { |
269
|
|
|
return false; |
270
|
|
|
} else { |
271
|
|
|
throw $e; |
272
|
|
|
} |
273
|
|
|
} else { |
274
|
|
|
throw $e; |
275
|
|
|
} |
276
|
|
|
} |
277
|
|
|
|
278
|
|
|
$handle = $response->getBody(); |
279
|
|
|
return RetryWrapper::wrap($handle); |
280
|
|
|
} catch (\Exception $exception) { |
281
|
|
|
\OCP\Util::writeLog('files_external', $exception->getMessage(), \OCP\Util::ERROR); |
282
|
|
|
return false; |
283
|
|
|
} |
284
|
|
|
case 'w': |
285
|
|
|
case 'wb': |
286
|
|
|
case 'a': |
287
|
|
|
case 'ab': |
288
|
|
|
case 'r+': |
289
|
|
|
case 'w+': |
290
|
|
|
case 'wb+': |
291
|
|
|
case 'a+': |
292
|
|
|
case 'x': |
293
|
|
|
case 'x+': |
294
|
|
|
case 'c': |
295
|
|
View Code Duplication |
case 'c+': |
|
|
|
|
296
|
|
|
if (strrpos($path, '.') !== false) { |
297
|
|
|
$ext = substr($path, strrpos($path, '.')); |
298
|
|
|
} else { |
299
|
|
|
$ext = ''; |
300
|
|
|
} |
301
|
|
|
$tmpFile = \OCP\Files::tmpFile($ext); |
302
|
|
|
\OC\Files\Stream\Close::registerCallback($tmpFile, array($this, 'writeBack')); |
303
|
|
|
if ($this->file_exists($path)) { |
304
|
|
|
$source = $this->fopen($path, 'r'); |
305
|
|
|
file_put_contents($tmpFile, $source); |
306
|
|
|
} |
307
|
|
|
self::$tempFiles[$tmpFile] = $path; |
308
|
|
|
return fopen('close://'.$tmpFile, $mode); |
309
|
|
|
} |
310
|
|
|
return false; |
311
|
|
|
} |
312
|
|
|
|
313
|
|
|
public function writeBack($tmpFile) { |
314
|
|
|
if (isset(self::$tempFiles[$tmpFile])) { |
315
|
|
|
$handle = fopen($tmpFile, 'r'); |
316
|
|
|
try { |
317
|
|
|
$this->dropbox->putFile(self::$tempFiles[$tmpFile], $handle); |
318
|
|
|
unlink($tmpFile); |
319
|
|
|
$this->deleteMetaData(self::$tempFiles[$tmpFile]); |
320
|
|
|
} catch (\Exception $exception) { |
321
|
|
|
\OCP\Util::writeLog('files_external', $exception->getMessage(), \OCP\Util::ERROR); |
322
|
|
|
} |
323
|
|
|
} |
324
|
|
|
} |
325
|
|
|
|
326
|
|
|
public function free_space($path) { |
327
|
|
|
try { |
328
|
|
|
$info = $this->dropbox->getAccountInfo(); |
329
|
|
|
return $info['quota_info']['quota'] - $info['quota_info']['normal']; |
|
|
|
|
330
|
|
|
} catch (\Exception $exception) { |
331
|
|
|
\OCP\Util::writeLog('files_external', $exception->getMessage(), \OCP\Util::ERROR); |
332
|
|
|
return false; |
333
|
|
|
} |
334
|
|
|
} |
335
|
|
|
|
336
|
|
View Code Duplication |
public function touch($path, $mtime = null) { |
337
|
|
|
if ($this->file_exists($path)) { |
338
|
|
|
return false; |
339
|
|
|
} else { |
340
|
|
|
$this->file_put_contents($path, ''); |
341
|
|
|
} |
342
|
|
|
return true; |
343
|
|
|
} |
344
|
|
|
|
345
|
|
|
/** |
346
|
|
|
* check if curl is installed |
347
|
|
|
*/ |
348
|
|
|
public static function checkDependencies() { |
349
|
|
|
return true; |
350
|
|
|
} |
351
|
|
|
|
352
|
|
|
} |
353
|
|
|
|
This error could be the result of:
1. Missing dependencies
PHP Analyzer uses your
composer.json
file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects thecomposer.json
to be in the root folder of your repository.Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the
require
orrequire-dev
section?2. Missing use statement
PHP does not complain about undefined classes in
ìnstanceof
checks. For example, the following PHP code will work perfectly fine:If you have not tested against this specific condition, such errors might go unnoticed.