PluginProbe ʕ •ᴥ•ʔ
File Manager Pro – Filester / 2.1.3
File Manager Pro – Filester v2.1.3
2.1.3 2.1.2 2.1.1 trunk 1.6.1 1.7.6 1.8 1.8.1 1.8.2 1.8.3 1.8.4 1.8.5 1.8.6 1.8.7 1.8.8 1.8.9 1.9 2.0 2.0.1 2.0.2 2.1.0
filester / includes / File_manager / lib / php / elFinderVolumeOneDrive.class.php
filester / includes / File_manager / lib / php Last commit date
.tmp 21 hours ago editors 21 hours ago libs 21 hours ago plugins 21 hours ago resources 21 hours ago MySQLStorage.sql 21 hours ago autoload.php 21 hours ago elFinder.class.php 21 hours ago elFinderConnector.class.php 21 hours ago elFinderFlysystemGoogleDriveNetmount.php 21 hours ago elFinderPlugin.php 21 hours ago elFinderSession.php 21 hours ago elFinderSessionInterface.php 21 hours ago elFinderVolumeBox.class.php 21 hours ago elFinderVolumeDriver.class.php 21 hours ago elFinderVolumeDropbox.class.php 21 hours ago elFinderVolumeDropbox2.class.php 21 hours ago elFinderVolumeFTP.class.php 21 hours ago elFinderVolumeGoogleDrive.class.php 21 hours ago elFinderVolumeGroup.class.php 21 hours ago elFinderVolumeLocalFileSystem.class.php 21 hours ago elFinderVolumeMySQL.class.php 21 hours ago elFinderVolumeOneDrive.class.php 21 hours ago elFinderVolumeSFTPphpseclib.class.php 21 hours ago elFinderVolumeTrash.class.php 21 hours ago elFinderVolumeTrashMySQL.class.php 21 hours ago mime.types 21 hours ago
elFinderVolumeOneDrive.class.php
2190 lines
1 <?php
2
3 /**
4 * Simple elFinder driver for OneDrive
5 * onedrive api v5.0.
6 *
7 * @author Dmitry (dio) Levashov
8 * @author Cem (discofever)
9 **/
10 class elFinderVolumeOneDrive extends elFinderVolumeDriver
11 {
12 /**
13 * Driver id
14 * Must be started from letter and contains [a-z0-9]
15 * Used as part of volume id.
16 *
17 * @var string
18 **/
19 protected $driverId = 'od';
20
21 /**
22 * @var string The base URL for API requests
23 **/
24 const API_URL = 'https://graph.microsoft.com/v1.0/me/drive/items/';
25
26 /**
27 * @var string The base URL for authorization requests
28 */
29 const AUTH_URL = 'https://login.microsoftonline.com/common/oauth2/v2.0/authorize';
30
31 /**
32 * @var string The base URL for token requests
33 */
34 const TOKEN_URL = 'https://login.microsoftonline.com/common/oauth2/v2.0/token';
35
36 /**
37 * OneDrive token object.
38 *
39 * @var object
40 **/
41 protected $token = null;
42
43 /**
44 * Directory for tmp files
45 * If not set driver will try to use tmbDir as tmpDir.
46 *
47 * @var string
48 **/
49 protected $tmp = '';
50
51 /**
52 * Net mount key.
53 *
54 * @var string
55 **/
56 public $netMountKey = '';
57
58 /**
59 * Thumbnail prefix.
60 *
61 * @var string
62 **/
63 protected $tmbPrefix = '';
64
65 /**
66 * hasCache by folders.
67 *
68 * @var array
69 **/
70 protected $HasdirsCache = array();
71
72 /**
73 * Query options of API call.
74 *
75 * @var array
76 */
77 protected $queryOptions = array();
78
79 /**
80 * Current token expires
81 *
82 * @var integer
83 **/
84 private $expires;
85
86 /**
87 * Path to access token file for permanent mount
88 *
89 * @var string
90 */
91 private $aTokenFile = '';
92
93 /**
94 * Constructor
95 * Extend options with required fields.
96 *
97 * @author Dmitry (dio) Levashov
98 * @author Cem (DiscoFever)
99 **/
100 public function __construct()
101 {
102 $opts = array(
103 'client_id' => '',
104 'client_secret' => '',
105 'accessToken' => '',
106 'root' => 'OneDrive.com',
107 'OneDriveApiClient' => '',
108 'path' => '/',
109 'separator' => '/',
110 'tmbPath' => '',
111 'tmbURL' => '',
112 'tmpPath' => '',
113 'acceptedName' => '#^[^/\\?*:|"<>]*[^./\\?*:|"<>]$#',
114 'rootCssClass' => 'elfinder-navbar-root-onedrive',
115 'useApiThumbnail' => true,
116 );
117 $this->options = array_merge($this->options, $opts);
118 $this->options['mimeDetect'] = 'internal';
119 }
120
121 /*********************************************************************/
122 /* ORIGINAL FUNCTIONS */
123 /*********************************************************************/
124
125 /**
126 * Obtains a new access token from OAuth. This token is valid for one hour.
127 *
128 * @param $client_id
129 * @param $client_secret
130 * @param string $code The code returned by OneDrive after
131 * successful log in
132 *
133 * @return object|string
134 * @throws Exception Thrown if the redirect URI of this Client instance's
135 * state is not set
136 */
137 protected function _od_obtainAccessToken($client_id, $client_secret, $code, $nodeid)
138 {
139 if (null === $client_id) {
140 return 'The client ID must be set to call obtainAccessToken()';
141 }
142
143 if (null === $client_secret) {
144 return 'The client Secret must be set to call obtainAccessToken()';
145 }
146
147 $redirect = elFinder::getConnectorUrl();
148 if (strpos($redirect, '/netmount/onedrive/') === false) {
149 $redirect .= '/netmount/onedrive/' . ($nodeid === 'elfinder'? '1' : $nodeid);
150 }
151
152 $url = self::TOKEN_URL;
153
154 $curl = curl_init();
155
156 $fields = http_build_query(
157 array(
158 'client_id' => $client_id,
159 'redirect_uri' => $redirect,
160 'client_secret' => $client_secret,
161 'code' => $code,
162 'grant_type' => 'authorization_code',
163 )
164 );
165
166 curl_setopt_array($curl, array(
167 // General options.
168 CURLOPT_RETURNTRANSFER => true,
169 CURLOPT_POST => true,
170 CURLOPT_POSTFIELDS => $fields,
171
172 CURLOPT_HTTPHEADER => array(
173 'Content-Length: ' . strlen($fields),
174 ),
175
176 CURLOPT_URL => $url,
177 ));
178
179 $result = elFinder::curlExec($curl);
180
181 $decoded = json_decode($result);
182
183 if (null === $decoded) {
184 throw new \Exception('json_decode() failed');
185 }
186
187 if (!empty($decoded->error)) {
188 $error = $decoded->error;
189 if (!empty($decoded->error_description)) {
190 $error .= ': ' . $decoded->error_description;
191 }
192 throw new \Exception($error);
193 }
194
195 $res = (object)array(
196 'expires' => time() + $decoded->expires_in - 30,
197 'initialToken' => '',
198 'data' => $decoded
199 );
200 if (!empty($decoded->refresh_token)) {
201 $res->initialToken = md5($client_id . $decoded->refresh_token);
202 }
203 return $res;
204 }
205
206 /**
207 * Get token and auto refresh.
208 *
209 * @return true
210 * @throws Exception
211 */
212 protected function _od_refreshToken()
213 {
214 if (!property_exists($this->token, 'expires') || $this->token->expires < time()) {
215 if (!$this->options['client_id']) {
216 $this->options['client_id'] = ELFINDER_ONEDRIVE_CLIENTID;
217 }
218
219 if (!$this->options['client_secret']) {
220 $this->options['client_secret'] = ELFINDER_ONEDRIVE_CLIENTSECRET;
221 }
222
223 if (empty($this->token->data->refresh_token)) {
224 throw new \Exception(elFinder::ERROR_REAUTH_REQUIRE);
225 } else {
226 $refresh_token = $this->token->data->refresh_token;
227 $initialToken = $this->_od_getInitialToken();
228 }
229
230 $url = self::TOKEN_URL;
231
232 $curl = curl_init();
233
234 curl_setopt_array($curl, array(
235 // General options.
236 CURLOPT_RETURNTRANSFER => true,
237 CURLOPT_POST => true, // i am sending post data
238 CURLOPT_POSTFIELDS => 'client_id=' . urlencode($this->options['client_id'])
239 . '&client_secret=' . urlencode($this->options['client_secret'])
240 . '&grant_type=refresh_token'
241 . '&refresh_token=' . urlencode($this->token->data->refresh_token),
242
243 CURLOPT_URL => $url,
244 ));
245
246 $result = elFinder::curlExec($curl);
247
248 $decoded = json_decode($result);
249
250 if (!$decoded) {
251 throw new \Exception('json_decode() failed');
252 }
253
254 if (empty($decoded->access_token)) {
255 if ($this->aTokenFile) {
256 if (is_file($this->aTokenFile)) {
257 unlink($this->aTokenFile);
258 }
259 }
260 $err = property_exists($decoded, 'error')? ' ' . $decoded->error : '';
261 $err .= property_exists($decoded, 'error_description')? ' ' . $decoded->error_description : '';
262 throw new \Exception($err? $err : elFinder::ERROR_REAUTH_REQUIRE);
263 }
264
265 $token = (object)array(
266 'expires' => time() + $decoded->expires_in - 30,
267 'initialToken' => $initialToken,
268 'data' => $decoded,
269 );
270
271 $this->token = $token;
272 $json = json_encode($token);
273
274 if (!empty($decoded->refresh_token)) {
275 if (empty($this->options['netkey']) && $this->aTokenFile) {
276 file_put_contents($this->aTokenFile, json_encode($token));
277 $this->options['accessToken'] = $json;
278 } else if (!empty($this->options['netkey'])) {
279 // OAuth2 refresh token can be used only once,
280 // so update it if it is the same as the token file
281 $aTokenFile = $this->_od_getATokenFile();
282 if ($aTokenFile && is_file($aTokenFile)) {
283 if ($_token = json_decode(file_get_contents($aTokenFile))) {
284 if ($_token->data->refresh_token === $refresh_token) {
285 file_put_contents($aTokenFile, $json);
286 }
287 }
288 }
289 $this->options['accessToken'] = $json;
290 // update session value
291 elFinder::$instance->updateNetVolumeOption($this->options['netkey'], 'accessToken', $this->options['accessToken']);
292 $this->session->set('OneDriveTokens', $token);
293 } else {
294 throw new \Exception(elFinder::ERROR_CREATING_TEMP_DIR);
295 }
296 }
297 }
298
299 return true;
300 }
301
302 /**
303 * Get Parent ID, Item ID, Parent Path as an array from path.
304 *
305 * @param string $path
306 *
307 * @return array
308 */
309 protected function _od_splitPath($path)
310 {
311 $path = trim($path, '/');
312 $pid = '';
313 if ($path === '') {
314 $id = 'root';
315 $parent = '';
316 } else {
317 $paths = explode('/', trim($path, '/'));
318 $id = array_pop($paths);
319 if ($paths) {
320 $parent = '/' . implode('/', $paths);
321 $pid = array_pop($paths);
322 } else {
323 $pid = 'root';
324 $parent = '/';
325 }
326 }
327
328 return array($pid, $id, $parent);
329 }
330
331 /**
332 * Creates a base cURL object which is compatible with the OneDrive API.
333 *
334 * @return resource A compatible cURL object
335 */
336 protected function _od_prepareCurl($url = null)
337 {
338 $curl = curl_init($url);
339
340 $defaultOptions = array(
341 // General options.
342 CURLOPT_RETURNTRANSFER => true,
343 CURLOPT_HTTPHEADER => array(
344 'Content-Type: application/json',
345 'Authorization: Bearer ' . $this->token->data->access_token,
346 ),
347 );
348
349 curl_setopt_array($curl, $defaultOptions);
350
351 return $curl;
352 }
353
354 /**
355 * Creates a base cURL object which is compatible with the OneDrive API.
356 *
357 * @param string $path The path of the API call (eg. me/skydrive)
358 * @param bool $contents
359 *
360 * @return resource A compatible cURL object
361 * @throws elFinderAbortException
362 */
363 protected function _od_createCurl($path, $contents = false)
364 {
365 elFinder::checkAborted();
366 $curl = $this->_od_prepareCurl($path);
367
368 if ($contents) {
369 $res = elFinder::curlExec($curl);
370 } else {
371 $result = json_decode(elFinder::curlExec($curl));
372 if (isset($result->value)) {
373 $res = $result->value;
374 unset($result->value);
375 $result = (array)$result;
376 if (!empty($result['@odata.nextLink'])) {
377 $nextRes = $this->_od_createCurl($result['@odata.nextLink'], false);
378 if (is_array($nextRes)) {
379 $res = array_merge($res, $nextRes);
380 }
381 }
382 } else {
383 $res = $result;
384 }
385 }
386
387 return $res;
388 }
389
390 /**
391 * Get preauthenticated download URL of a file.
392 *
393 * @param string $itemId
394 *
395 * @return string
396 */
397 protected function _od_getDownloadUrl($path)
398 {
399 $dlurl = '';
400 $stat = array();
401 list(, $itemId) = $this->_od_splitPath($path);
402 if (isset($this->cache[$path])) {
403 $stat = $this->cache[$path];
404 if (isset($this->cache[$path]['url']) && $this->cache[$path]['url'] !== '1') {
405 return $this->cache[$path]['url'];
406 }
407 }
408 try {
409 $res = $this->_od_query($itemId, true, false, array(
410 'query' => array(
411 'select' => 'id,@microsoft.graph.downloadUrl',
412 ),
413 ));
414 if (is_object($res) && property_exists($res, '@microsoft.graph.downloadUrl')) {
415 $dlurl = (string)$res->{'@microsoft.graph.downloadUrl'};
416 }
417 } catch (Exception $e) {
418 }
419
420 if (!$dlurl) {
421 try {
422 $url = self::API_URL . $itemId . '/content';
423 $curl = $this->_od_prepareCurl($url);
424 curl_setopt_array($curl, array(
425 CURLOPT_HEADER => true,
426 CURLOPT_NOBODY => true,
427 CURLOPT_FOLLOWLOCATION => false,
428 ));
429 $result = elFinder::curlExec($curl);
430 if (preg_match('/^Location:\s*(.+)\r?$/mi', $result, $m)) {
431 $dlurl = trim($m[1]);
432 }
433 } catch (Exception $e) {
434 }
435 }
436
437 if ($dlurl && $stat) {
438 $stat['url'] = $dlurl;
439 $this->updateCache($path, $stat);
440 }
441 return $dlurl;
442 }
443
444
445 /**
446 * Drive query and fetchAll.
447 *
448 * @param $itemId
449 * @param bool $fetch_self
450 * @param bool $recursive
451 * @param array $options
452 *
453 * @return object|array
454 * @throws elFinderAbortException
455 */
456 protected function _od_query($itemId, $fetch_self = false, $recursive = false, $options = array())
457 {
458 $result = array();
459
460 if (null === $itemId) {
461 $itemId = 'root';
462 }
463
464 if ($fetch_self == true) {
465 $path = $itemId;
466 } else {
467 $path = $itemId . '/children';
468 }
469
470 if (isset($options['query'])) {
471 $path .= '?' . http_build_query($options['query']);
472 }
473
474 $url = self::API_URL . $path;
475
476 $res = $this->_od_createCurl($url);
477 if (!$fetch_self && $recursive && is_array($res)) {
478 foreach ($res as $file) {
479 $result[] = $file;
480 if (!empty($file->folder)) {
481 $result = array_merge($result, $this->_od_query($file->id, false, true, $options));
482 }
483 }
484 } else {
485 $result = $res;
486 }
487
488 return isset($result->error) ? array() : $result;
489 }
490
491 /**
492 * Parse line from onedrive metadata output and return file stat (array).
493 *
494 * @param object $raw line from ftp_rawlist() output
495 *
496 * @return array
497 * @author Dmitry Levashov
498 **/
499 protected function _od_parseRaw($raw)
500 {
501 $stat = array();
502
503 $folder = isset($raw->folder) ? $raw->folder : null;
504
505 $stat['rev'] = isset($raw->id) ? $raw->id : 'root';
506 $stat['name'] = $raw->name;
507 if (isset($raw->lastModifiedDateTime)) {
508 $stat['ts'] = strtotime($raw->lastModifiedDateTime);
509 }
510
511 if ($folder) {
512 $stat['mime'] = 'directory';
513 $stat['size'] = 0;
514 if (empty($folder->childCount)) {
515 $stat['dirs'] = 0;
516 } else {
517 $stat['dirs'] = -1;
518 }
519 } else {
520 if (isset($raw->file->mimeType)) {
521 $stat['mime'] = $raw->file->mimeType;
522 }
523 $stat['size'] = (int)$raw->size;
524 if (!$this->disabledGetUrl) {
525 $stat['url'] = '1';
526 }
527 if (isset($raw->image) && $img = $raw->image) {
528 isset($img->width) ? $stat['width'] = $img->width : $stat['width'] = 0;
529 isset($img->height) ? $stat['height'] = $img->height : $stat['height'] = 0;
530 }
531 if (!empty($raw->thumbnails)) {
532 if ($raw->thumbnails[0]->small->url) {
533 $stat['tmb'] = substr($raw->thumbnails[0]->small->url, 8); // remove "https://"
534 }
535 } elseif (!empty($raw->file->processingMetadata)) {
536 $stat['tmb'] = '1';
537 }
538 }
539
540 return $stat;
541 }
542
543 /**
544 * Get raw data(onedrive metadata) from OneDrive.
545 *
546 * @param string $path
547 *
548 * @return array|object onedrive metadata
549 */
550 protected function _od_getFileRaw($path)
551 {
552 list(, $itemId) = $this->_od_splitPath($path);
553 try {
554 $res = $this->_od_query($itemId, true, false, $this->queryOptions);
555
556 return $res;
557 } catch (Exception $e) {
558 return array();
559 }
560 }
561
562 /**
563 * Get thumbnail from OneDrive.com.
564 *
565 * @param string $path
566 *
567 * @return string | boolean
568 */
569 protected function _od_getThumbnail($path)
570 {
571 list(, $itemId) = $this->_od_splitPath($path);
572
573 try {
574 $url = self::API_URL . $itemId . '/thumbnails/0/medium/content';
575
576 return $this->_od_createCurl($url, $contents = true);
577 } catch (Exception $e) {
578 return false;
579 }
580 }
581
582 /**
583 * Upload large files with an upload session.
584 *
585 * @param resource $fp source file pointer
586 * @param number $size total size
587 * @param string $name item name
588 * @param string $itemId item identifier
589 * @param string $parent parent
590 * @param string $parentId parent identifier
591 *
592 * @return string The item path
593 */
594 protected function _od_uploadSession($fp, $size, $name, $itemId, $parent, $parentId)
595 {
596 try {
597 $send = $this->_od_getChunkData($fp);
598 if ($send === false) {
599 throw new Exception('Data can not be acquired from the source.');
600 }
601
602 // create upload session
603 if ($itemId) {
604 $url = self::API_URL . $itemId . '/createUploadSession';
605 } else {
606 $url = self::API_URL . $parentId . ':/' . rawurlencode($name) . ':/createUploadSession';
607 }
608 $curl = $this->_od_prepareCurl($url);
609 curl_setopt_array($curl, array(
610 CURLOPT_POST => true,
611 CURLOPT_POSTFIELDS => '{}',
612 ));
613 $sess = json_decode(elFinder::curlExec($curl));
614
615 if ($sess) {
616 if (isset($sess->error)) {
617 throw new Exception($sess->error->message);
618 }
619 $next = strlen($send);
620 $range = '0-' . ($next - 1) . '/' . $size;
621 } else {
622 throw new Exception('API response can not be obtained.');
623 }
624
625 $id = null;
626 $retry = 0;
627 while ($sess) {
628 elFinder::extendTimeLimit();
629 $putFp = tmpfile();
630 fwrite($putFp, $send);
631 rewind($putFp);
632 $_size = strlen($send);
633 $url = $sess->uploadUrl;
634 $curl = curl_init();
635 $options = array(
636 CURLOPT_URL => $url,
637 CURLOPT_PUT => true,
638 CURLOPT_RETURNTRANSFER => true,
639 CURLOPT_INFILE => $putFp,
640 CURLOPT_INFILESIZE => $_size,
641 CURLOPT_HTTPHEADER => array(
642 'Content-Length: ' . $_size,
643 'Content-Range: bytes ' . $range,
644 ),
645 );
646 curl_setopt_array($curl, $options);
647 $sess = json_decode(elFinder::curlExec($curl));
648 if ($sess) {
649 if (isset($sess->error)) {
650 throw new Exception($sess->error->message);
651 }
652 if (isset($sess->id)) {
653 $id = $sess->id;
654 break;
655 }
656 if (isset($sess->nextExpectedRanges)) {
657 list($_next) = explode('-', $sess->nextExpectedRanges[0]);
658 if ($next == $_next) {
659 $send = $this->_od_getChunkData($fp);
660 if ($send === false) {
661 throw new Exception('Data can not be acquired from the source.');
662 }
663 $next += strlen($send);
664 $range = $_next . '-' . ($next - 1) . '/' . $size;
665 $retry = 0;
666 } else {
667 if (++$retry > 3) {
668 throw new Exception('Retry limit exceeded with uploadSession API call.');
669 }
670 }
671 $sess->uploadUrl = $url;
672 }
673 } else {
674 throw new Exception('API response can not be obtained.');
675 }
676 }
677
678 if ($id) {
679 return $this->_joinPath($parent, $id);
680 } else {
681 throw new Exception('An error occurred in the uploadSession API call.');
682 }
683 } catch (Exception $e) {
684 return $this->setError('OneDrive error: ' . $e->getMessage());
685 }
686 }
687
688 /**
689 * Get chunk data by file pointer to upload session.
690 *
691 * @param resource $fp source file pointer
692 *
693 * @return bool|string chunked data
694 */
695 protected function _od_getChunkData($fp)
696 {
697 static $chunkSize = null;
698 if ($chunkSize === null) {
699 $mem = elFinder::getIniBytes('memory_limit');
700 if ($mem < 1) {
701 $mem = 10485760; // 10 MiB
702 } else {
703 $mem -= memory_get_usage() - 1061548;
704 $mem = min($mem, 10485760);
705 }
706 if ($mem > 327680) {
707 $chunkSize = floor($mem / 327680) * 327680;
708 } else {
709 $chunkSize = $mem;
710 }
711 }
712 if ($chunkSize < 8192) {
713 return false;
714 }
715
716 $contents = '';
717 while (!feof($fp) && strlen($contents) < $chunkSize) {
718 $contents .= fread($fp, 8192);
719 }
720
721 return $contents;
722 }
723
724 /**
725 * Get AccessToken file path
726 *
727 * @return string ( description_of_the_return_value )
728 */
729 protected function _od_getATokenFile()
730 {
731 $tmp = $aTokenFile = '';
732 if (!empty($this->token->data->refresh_token)) {
733 if (!$this->tmp) {
734 $tmp = elFinder::getStaticVar('commonTempPath');
735 if (!$tmp) {
736 $tmp = $this->getTempPath();
737 }
738 $this->tmp = $tmp;
739 }
740 if ($tmp) {
741 $aTokenFile = $tmp . DIRECTORY_SEPARATOR . $this->_od_getInitialToken() . '.otoken';
742 }
743 }
744 return $aTokenFile;
745 }
746
747 /**
748 * Get Initial Token (MD5 hash)
749 *
750 * @return string
751 */
752 protected function _od_getInitialToken()
753 {
754 return (empty($this->token->initialToken)? md5($this->options['client_id'] . (!empty($this->token->data->refresh_token)? $this->token->data->refresh_token : $this->token->data->access_token)) : $this->token->initialToken);
755 }
756
757 /*********************************************************************/
758 /* OVERRIDE FUNCTIONS */
759 /*********************************************************************/
760
761 /**
762 * Prepare
763 * Call from elFinder::netmout() before volume->mount().
764 *
765 * @return array
766 * @author Naoki Sawada
767 * @author Raja Sharma updating for OneDrive
768 **/
769 public function netmountPrepare($options)
770 {
771 if (empty($options['client_id']) && defined('ELFINDER_ONEDRIVE_CLIENTID')) {
772 $options['client_id'] = ELFINDER_ONEDRIVE_CLIENTID;
773 }
774 if (empty($options['client_secret']) && defined('ELFINDER_ONEDRIVE_CLIENTSECRET')) {
775 $options['client_secret'] = ELFINDER_ONEDRIVE_CLIENTSECRET;
776 }
777
778 if (isset($options['pass']) && $options['pass'] === 'reauth') {
779 $options['user'] = 'init';
780 $options['pass'] = '';
781 $this->session->remove('OneDriveTokens');
782 }
783
784 if (isset($options['id'])) {
785 $this->session->set('nodeId', $options['id']);
786 } elseif ($_id = $this->session->get('nodeId')) {
787 $options['id'] = $_id;
788 $this->session->set('nodeId', $_id);
789 }
790
791 if (!empty($options['tmpPath'])) {
792 if ((is_dir($options['tmpPath']) || mkdir($this->options['tmpPath'])) && is_writable($options['tmpPath'])) {
793 $this->tmp = $options['tmpPath'];
794 }
795 }
796
797 try {
798 if (empty($options['client_id']) || empty($options['client_secret'])) {
799 return array('exit' => true, 'body' => '{msg:errNetMountNoDriver}');
800 }
801
802 $itpCare = isset($options['code']);
803 $code = $itpCare? $options['code'] : (isset($_GET['code'])? $_GET['code'] : '');
804 if ($code) {
805 try {
806 if (!empty($options['id'])) {
807 // Obtain the token using the code received by the OneDrive API
808 $this->session->set('OneDriveTokens',
809 $this->_od_obtainAccessToken($options['client_id'], $options['client_secret'], $code, $options['id']));
810
811 $out = array(
812 'node' => $options['id'],
813 'json' => '{"protocol": "onedrive", "mode": "done", "reset": 1}',
814 'bind' => 'netmount',
815 );
816 } else {
817 $nodeid = ($_GET['host'] === '1')? 'elfinder' : $_GET['host'];
818 $out = array(
819 'node' => $nodeid,
820 'json' => json_encode(array(
821 'protocol' => 'onedrive',
822 'host' => $nodeid,
823 'mode' => 'redirect',
824 'options' => array(
825 'id' => $nodeid,
826 'code'=> $code
827 )
828 )),
829 'bind' => 'netmount'
830 );
831 }
832 if (!$itpCare) {
833 return array('exit' => 'callback', 'out' => $out);
834 } else {
835 return array('exit' => true, 'body' => $out['json']);
836 }
837 } catch (Exception $e) {
838 $out = array(
839 'node' => $options['id'],
840 'json' => json_encode(array('error' => elFinder::ERROR_ACCESS_DENIED . ' ' . $e->getMessage())),
841 );
842
843 return array('exit' => 'callback', 'out' => $out);
844 }
845 } elseif (!empty($_GET['error'])) {
846 $out = array(
847 'node' => $options['id'],
848 'json' => json_encode(array('error' => elFinder::ERROR_ACCESS_DENIED)),
849 );
850
851 return array('exit' => 'callback', 'out' => $out);
852 }
853
854 if ($options['user'] === 'init') {
855 $this->token = $this->session->get('OneDriveTokens');
856
857 if ($this->token) {
858 try {
859 $this->_od_refreshToken();
860 } catch (Exception $e) {
861 $this->setError($e->getMessage());
862 $this->token = null;
863 $this->session->remove('OneDriveTokens');
864 }
865 }
866
867 if (empty($this->token)) {
868 $result = false;
869 } else {
870 $path = $options['path'];
871 if ($path === '/') {
872 $path = 'root';
873 }
874 $result = $this->_od_query($path, false, false, array(
875 'query' => array(
876 'select' => 'id,name',
877 'filter' => 'folder ne null',
878 ),
879 ));
880 }
881
882 if ($result === false) {
883 try {
884 $this->session->set('OneDriveTokens', (object)array('token' => null));
885
886 $offline = '';
887 // Gets a log in URL with sufficient privileges from the OneDrive API
888 if (!empty($options['offline'])) {
889 $offline = ' offline_access';
890 }
891
892 $redirect_uri = elFinder::getConnectorUrl() . '/netmount/onedrive/' . ($options['id'] === 'elfinder'? '1' : $options['id']);
893 $url = self::AUTH_URL
894 . '?client_id=' . urlencode($options['client_id'])
895 . '&scope=' . urlencode('files.readwrite.all' . $offline)
896 . '&response_type=code'
897 . '&redirect_uri=' . urlencode($redirect_uri);
898
899 } catch (Exception $e) {
900 return array('exit' => true, 'body' => '{msg:errAccess}');
901 }
902
903 $html = '<input id="elf-volumedriver-onedrive-host-btn" class="ui-button ui-widget ui-state-default ui-corner-all ui-button-text-only" value="{msg:btnApprove}" type="button">';
904 $html .= '<script>
905 $("#' . $options['id'] . '").elfinder("instance").trigger("netmount", {protocol: "onedrive", mode: "makebtn", url: "' . $url . '"});
906 </script>';
907
908 return array('exit' => true, 'body' => $html);
909 } else {
910 $folders = [];
911
912 if ($result) {
913 foreach ($result as $res) {
914 $folders[$res->id] = $res->name;
915 }
916 natcasesort($folders);
917 }
918
919 if ($options['pass'] === 'folders') {
920 return ['exit' => true, 'folders' => $folders];
921 }
922
923 $folders = ['root' => 'My OneDrive'] + $folders;
924 $folders = json_encode($folders);
925
926 $expires = empty($this->token->data->refresh_token) ? (int)$this->token->expires : 0;
927 $mnt2res = empty($this->token->data->refresh_token) ? '' : ', "mnt2res": 1';
928 $json = '{"protocol": "onedrive", "mode": "done", "folders": ' . $folders . ', "expires": ' . $expires . $mnt2res .'}';
929 $html = 'OneDrive.com';
930 $html .= '<script>
931 $("#' . $options['id'] . '").elfinder("instance").trigger("netmount", ' . $json . ');
932 </script>';
933
934 return array('exit' => true, 'body' => $html);
935 }
936 }
937 } catch (Exception $e) {
938 return array('exit' => true, 'body' => '{msg:errNetMountNoDriver}');
939 }
940
941 if ($_aToken = $this->session->get('OneDriveTokens')) {
942 $options['accessToken'] = json_encode($_aToken);
943 if ($this->options['path'] === 'root' || !$this->options['path']) {
944 $this->options['path'] = '/';
945 }
946 } else {
947 $this->session->remove('OneDriveTokens');
948 $this->setError(elFinder::ERROR_NETMOUNT, $options['host'], implode(' ', $this->error()));
949
950 return array('exit' => true, 'error' => $this->error());
951 }
952
953 $this->session->remove('nodeId');
954 unset($options['user'], $options['pass'], $options['id']);
955
956 return $options;
957 }
958
959 /**
960 * process of on netunmount
961 * Drop `onedrive` & rm thumbs.
962 *
963 * @param array $options
964 *
965 * @return bool
966 */
967 public function netunmount($netVolumes, $key)
968 {
969 if (!$this->options['useApiThumbnail'] && ($tmbs = glob(rtrim($this->options['tmbPath'], '\\/') . DIRECTORY_SEPARATOR . $this->tmbPrefix . '*.png'))) {
970 foreach ($tmbs as $file) {
971 unlink($file);
972 }
973 }
974
975 return true;
976 }
977
978 /**
979 * Return debug info for client.
980 *
981 * @return array
982 **/
983 public function debug()
984 {
985 $res = parent::debug();
986 if (!empty($this->options['netkey']) && !empty($this->options['accessToken'])) {
987 $res['accessToken'] = $this->options['accessToken'];
988 }
989
990 return $res;
991 }
992
993 /*********************************************************************/
994 /* INIT AND CONFIGURE */
995 /*********************************************************************/
996
997 /**
998 * Prepare FTP connection
999 * Connect to remote server and check if credentials are correct, if so, store the connection id in $ftp_conn.
1000 *
1001 * @return bool
1002 * @throws elFinderAbortException
1003 * @author Dmitry (dio) Levashov
1004 * @author Cem (DiscoFever)
1005 */
1006 protected function init()
1007 {
1008 if (!$this->options['accessToken']) {
1009 return $this->setError('Required option `accessToken` is undefined.');
1010 }
1011
1012 if (!empty($this->options['tmpPath'])) {
1013 if ((is_dir($this->options['tmpPath']) || mkdir($this->options['tmpPath'])) && is_writable($this->options['tmpPath'])) {
1014 $this->tmp = $this->options['tmpPath'];
1015 }
1016 }
1017
1018 $error = false;
1019 try {
1020 $this->token = json_decode($this->options['accessToken']);
1021 if (!is_object($this->token)) {
1022 throw new Exception('Required option `accessToken` is invalid JSON.');
1023 }
1024
1025 // make net mount key
1026 if (empty($this->options['netkey'])) {
1027 $this->netMountKey = $this->_od_getInitialToken();
1028 } else {
1029 $this->netMountKey = $this->options['netkey'];
1030 }
1031
1032 if ($this->aTokenFile = $this->_od_getATokenFile()) {
1033 if (empty($this->options['netkey'])) {
1034 if ($this->aTokenFile) {
1035 if (is_file($this->aTokenFile)) {
1036 $this->token = json_decode(file_get_contents($this->aTokenFile));
1037 if (!is_object($this->token)) {
1038 unlink($this->aTokenFile);
1039 throw new Exception('Required option `accessToken` is invalid JSON.');
1040 }
1041 } else {
1042 file_put_contents($this->aTokenFile, $this->token);
1043 }
1044 }
1045 } else if (is_file($this->aTokenFile)) {
1046 // If the refresh token is the same as the permanent volume
1047 $this->token = json_decode(file_get_contents($this->aTokenFile));
1048 }
1049 }
1050
1051 if ($this->needOnline) {
1052 $this->_od_refreshToken();
1053
1054 $this->expires = empty($this->token->data->refresh_token) ? (int)$this->token->expires : 0;
1055 }
1056 } catch (Exception $e) {
1057 $this->token = null;
1058 $error = true;
1059 $this->setError($e->getMessage());
1060 }
1061
1062 if ($this->netMountKey) {
1063 $this->tmbPrefix = 'onedrive' . base_convert($this->netMountKey, 16, 32);
1064 }
1065
1066 if ($error) {
1067 if (empty($this->options['netkey']) && $this->tmbPrefix) {
1068 // for delete thumbnail
1069 $this->netunmount(null, null);
1070 }
1071 return false;
1072 }
1073
1074 // normalize root path
1075 if ($this->options['path'] == 'root') {
1076 $this->options['path'] = '/';
1077 }
1078
1079 $this->root = $this->options['path'] = $this->_normpath($this->options['path']);
1080
1081 $this->options['root'] = ($this->options['root'] == '')? 'OneDrive.com' : $this->options['root'];
1082
1083 if (empty($this->options['alias'])) {
1084 if ($this->needOnline) {
1085 $this->options['alias'] = ($this->options['path'] === '/') ? $this->options['root'] :
1086 $this->_od_query(basename($this->options['path']), $fetch_self = true)->name . '@OneDrive';
1087 if (!empty($this->options['netkey'])) {
1088 elFinder::$instance->updateNetVolumeOption($this->options['netkey'], 'alias', $this->options['alias']);
1089 }
1090 } else {
1091 $this->options['alias'] = $this->options['root'];
1092 }
1093 }
1094
1095 $this->rootName = $this->options['alias'];
1096
1097 // This driver dose not support `syncChkAsTs`
1098 $this->options['syncChkAsTs'] = false;
1099
1100 // 'lsPlSleep' minmum 10 sec
1101 $this->options['lsPlSleep'] = max(10, $this->options['lsPlSleep']);
1102
1103 $this->queryOptions = array(
1104 'query' => array(
1105 'select' => 'id,name,lastModifiedDateTime,file,folder,size,image',
1106 ),
1107 );
1108
1109 if ($this->options['useApiThumbnail']) {
1110 $this->options['tmbURL'] = 'https://';
1111 $this->options['tmbPath'] = '';
1112 $this->queryOptions['query']['expand'] = 'thumbnails(select=small)';
1113 }
1114
1115 // enable command archive
1116 $this->options['useRemoteArchive'] = true;
1117
1118 return true;
1119 }
1120
1121 /**
1122 * Configure after successfull mount.
1123 *
1124 * @author Dmitry (dio) Levashov
1125 **/
1126 protected function configure()
1127 {
1128 parent::configure();
1129
1130 // fallback of $this->tmp
1131 if (!$this->tmp && $this->tmbPathWritable) {
1132 $this->tmp = $this->tmbPath;
1133 }
1134 }
1135
1136 /*********************************************************************/
1137 /* FS API */
1138 /*********************************************************************/
1139
1140 /**
1141 * Close opened connection.
1142 *
1143 * @author Dmitry (dio) Levashov
1144 **/
1145 public function umount()
1146 {
1147 }
1148
1149 protected function isNameExists($path)
1150 {
1151 list($pid, $name) = $this->_od_splitPath($path);
1152
1153 $raw = $this->_od_query($pid . '/children/' . rawurlencode($name), true);
1154 return $raw ? $this->_od_parseRaw($raw) : false;
1155 }
1156
1157 /**
1158 * Cache dir contents.
1159 *
1160 * @param string $path dir path
1161 *
1162 * @return array
1163 * @throws elFinderAbortException
1164 * @author Dmitry Levashov
1165 */
1166 protected function cacheDir($path)
1167 {
1168 $this->dirsCache[$path] = array();
1169 $hasDir = false;
1170
1171 list(, $itemId) = $this->_od_splitPath($path);
1172
1173 $res = $this->_od_query($itemId, false, false, $this->queryOptions);
1174
1175 if ($res) {
1176 foreach ($res as $raw) {
1177 if ($stat = $this->_od_parseRaw($raw)) {
1178 $itemPath = $this->_joinPath($path, $raw->id);
1179 $stat = $this->updateCache($itemPath, $stat);
1180 if (empty($stat['hidden'])) {
1181 if (!$hasDir && $stat['mime'] === 'directory') {
1182 $hasDir = true;
1183 }
1184 $this->dirsCache[$path][] = $itemPath;
1185 }
1186 }
1187 }
1188 }
1189
1190 if (isset($this->sessionCache['subdirs'])) {
1191 $this->sessionCache['subdirs'][$path] = $hasDir;
1192 }
1193
1194 return $this->dirsCache[$path];
1195 }
1196
1197 /**
1198 * Copy file/recursive copy dir only in current volume.
1199 * Return new file path or false.
1200 *
1201 * @param string $src source path
1202 * @param string $dst destination dir path
1203 * @param string $name new file name (optionaly)
1204 *
1205 * @return string|false
1206 * @throws elFinderAbortException
1207 * @author Dmitry (dio) Levashov
1208 * @author Naoki Sawada
1209 */
1210 protected function copy($src, $dst, $name)
1211 {
1212 $itemId = '';
1213 if ($this->options['copyJoin']) {
1214 $test = $this->joinPathCE($dst, $name);
1215 if ($testStat = $this->isNameExists($test)) {
1216 $this->remove($test);
1217 }
1218 }
1219
1220 if ($path = $this->_copy($src, $dst, $name)) {
1221 $this->added[] = $this->stat($path);
1222 } else {
1223 $this->setError(elFinder::ERROR_COPY, $this->_path($src));
1224 }
1225
1226 return $path;
1227 }
1228
1229 /**
1230 * Remove file/ recursive remove dir.
1231 *
1232 * @param string $path file path
1233 * @param bool $force try to remove even if file locked
1234 *
1235 * @return bool
1236 * @throws elFinderAbortException
1237 * @author Dmitry (dio) Levashov
1238 * @author Naoki Sawada
1239 */
1240 protected function remove($path, $force = false)
1241 {
1242 $stat = $this->stat($path);
1243 $stat['realpath'] = $path;
1244 $this->rmTmb($stat);
1245 $this->clearcache();
1246
1247 if (empty($stat)) {
1248 return $this->setError(elFinder::ERROR_RM, $this->_path($path), elFinder::ERROR_FILE_NOT_FOUND);
1249 }
1250
1251 if (!$force && !empty($stat['locked'])) {
1252 return $this->setError(elFinder::ERROR_LOCKED, $this->_path($path));
1253 }
1254
1255 if ($stat['mime'] == 'directory') {
1256 if (!$this->_rmdir($path)) {
1257 return $this->setError(elFinder::ERROR_RM, $this->_path($path));
1258 }
1259 } else {
1260 if (!$this->_unlink($path)) {
1261 return $this->setError(elFinder::ERROR_RM, $this->_path($path));
1262 }
1263 }
1264
1265 $this->removed[] = $stat;
1266
1267 return true;
1268 }
1269
1270 /**
1271 * Create thumnbnail and return it's URL on success.
1272 *
1273 * @param string $path file path
1274 * @param $stat
1275 *
1276 * @return string|false
1277 * @throws ImagickException
1278 * @throws elFinderAbortException
1279 * @author Dmitry (dio) Levashov
1280 * @author Naoki Sawada
1281 */
1282 protected function createTmb($path, $stat)
1283 {
1284 if ($this->options['useApiThumbnail']) {
1285 if (func_num_args() > 2) {
1286 list(, , $count) = func_get_args();
1287 } else {
1288 $count = 0;
1289 }
1290 if ($count < 10) {
1291 if (isset($stat['tmb']) && $stat['tmb'] != '1') {
1292 return $stat['tmb'];
1293 } else {
1294 sleep(2);
1295 elFinder::extendTimeLimit();
1296 $this->clearcache();
1297 $stat = $this->stat($path);
1298
1299 return $this->createTmb($path, $stat, ++$count);
1300 }
1301 }
1302
1303 return false;
1304 }
1305 if (!$stat || !$this->canCreateTmb($path, $stat)) {
1306 return false;
1307 }
1308
1309 $name = $this->tmbname($stat);
1310 $tmb = $this->tmbPath . DIRECTORY_SEPARATOR . $name;
1311
1312 // copy image into tmbPath so some drivers does not store files on local fs
1313 if (!$data = $this->_od_getThumbnail($path)) {
1314 return false;
1315 }
1316 if (!file_put_contents($tmb, $data)) {
1317 return false;
1318 }
1319
1320 $result = false;
1321
1322 $tmbSize = $this->tmbSize;
1323
1324 if (($s = getimagesize($tmb)) == false) {
1325 return false;
1326 }
1327
1328 /* If image smaller or equal thumbnail size - just fitting to thumbnail square */
1329 if ($s[0] <= $tmbSize && $s[1] <= $tmbSize) {
1330 $result = $this->imgSquareFit($tmb, $tmbSize, $tmbSize, 'center', 'middle', $this->options['tmbBgColor'], 'png');
1331 } else {
1332 if ($this->options['tmbCrop']) {
1333
1334 /* Resize and crop if image bigger than thumbnail */
1335 if (!(($s[0] > $tmbSize && $s[1] <= $tmbSize) || ($s[0] <= $tmbSize && $s[1] > $tmbSize)) || ($s[0] > $tmbSize && $s[1] > $tmbSize)) {
1336 $result = $this->imgResize($tmb, $tmbSize, $tmbSize, true, false, 'png');
1337 }
1338
1339 if (($s = getimagesize($tmb)) != false) {
1340 $x = $s[0] > $tmbSize ? intval(($s[0] - $tmbSize) / 2) : 0;
1341 $y = $s[1] > $tmbSize ? intval(($s[1] - $tmbSize) / 2) : 0;
1342 $result = $this->imgCrop($tmb, $tmbSize, $tmbSize, $x, $y, 'png');
1343 }
1344 } else {
1345 $result = $this->imgResize($tmb, $tmbSize, $tmbSize, true, true, 'png');
1346 }
1347
1348 $result = $this->imgSquareFit($tmb, $tmbSize, $tmbSize, 'center', 'middle', $this->options['tmbBgColor'], 'png');
1349 }
1350
1351 if (!$result) {
1352 unlink($tmb);
1353
1354 return false;
1355 }
1356
1357 return $name;
1358 }
1359
1360 /**
1361 * Return thumbnail file name for required file.
1362 *
1363 * @param array $stat file stat
1364 *
1365 * @return string
1366 * @author Dmitry (dio) Levashov
1367 **/
1368 protected function tmbname($stat)
1369 {
1370 return $this->tmbPrefix . $stat['rev'] . $stat['ts'] . '.png';
1371 }
1372
1373 /**
1374 * Return content URL.
1375 *
1376 * @param string $hash file hash
1377 * @param array $options options
1378 *
1379 * @return string
1380 * @author Naoki Sawada
1381 **/
1382 public function getContentUrl($hash, $options = array())
1383 {
1384 if (!empty($options['onetime']) && $this->options['onetimeUrl']) {
1385 return parent::getContentUrl($hash, $options);
1386 }
1387 if (!empty($options['temporary'])) {
1388 // try make temporary file
1389 $url = parent::getContentUrl($hash, $options);
1390 if ($url) {
1391 return $url;
1392 }
1393 }
1394
1395 if (($file = $this->file($hash)) && !empty($file['url']) && $file['url'] !== '1' && $file['url'] !== 1) {
1396 return $file['url'];
1397 }
1398
1399 $res = '';
1400 $path = $this->decode($hash);
1401
1402 try {
1403 $res = $this->_od_getDownloadUrl($path);
1404 } catch (Exception $e) {
1405 $res = '';
1406 }
1407
1408 return $res;
1409 }
1410
1411 /*********************** paths/urls *************************/
1412
1413 /**
1414 * Return parent directory path.
1415 *
1416 * @param string $path file path
1417 *
1418 * @return string
1419 * @author Dmitry (dio) Levashov
1420 **/
1421 protected function _dirname($path)
1422 {
1423 list(, , $dirname) = $this->_od_splitPath($path);
1424
1425 return $dirname;
1426 }
1427
1428 /**
1429 * Return file name.
1430 *
1431 * @param string $path file path
1432 *
1433 * @return string
1434 * @author Dmitry (dio) Levashov
1435 **/
1436 protected function _basename($path)
1437 {
1438 list(, $basename) = $this->_od_splitPath($path);
1439
1440 return $basename;
1441 }
1442
1443 /**
1444 * Join dir name and file name and retur full path.
1445 *
1446 * @param string $dir
1447 * @param string $name
1448 *
1449 * @return string
1450 * @author Dmitry (dio) Levashov
1451 **/
1452 protected function _joinPath($dir, $name)
1453 {
1454 if ($dir === 'root') {
1455 $dir = '';
1456 }
1457
1458 return $this->_normpath($dir . '/' . $name);
1459 }
1460
1461 /**
1462 * Return normalized path, this works the same as os.path.normpath() in Python.
1463 *
1464 * @param string $path path
1465 *
1466 * @return string
1467 * @author Troex Nevelin
1468 **/
1469 protected function _normpath($path)
1470 {
1471 if (DIRECTORY_SEPARATOR !== '/') {
1472 $path = str_replace(DIRECTORY_SEPARATOR, '/', $path);
1473 }
1474 $path = '/' . ltrim($path, '/');
1475
1476 return $path;
1477 }
1478
1479 /**
1480 * Return file path related to root dir.
1481 *
1482 * @param string $path file path
1483 *
1484 * @return string
1485 * @author Dmitry (dio) Levashov
1486 **/
1487 protected function _relpath($path)
1488 {
1489 return $path;
1490 }
1491
1492 /**
1493 * Convert path related to root dir into real path.
1494 *
1495 * @param string $path file path
1496 *
1497 * @return string
1498 * @author Dmitry (dio) Levashov
1499 **/
1500 protected function _abspath($path)
1501 {
1502 return $path;
1503 }
1504
1505 /**
1506 * Return fake path started from root dir.
1507 *
1508 * @param string $path file path
1509 *
1510 * @return string
1511 * @author Dmitry (dio) Levashov
1512 **/
1513 protected function _path($path)
1514 {
1515 return $this->rootName . $this->_normpath(substr($path, strlen($this->root)));
1516 }
1517
1518 /**
1519 * Return true if $path is children of $parent.
1520 *
1521 * @param string $path path to check
1522 * @param string $parent parent path
1523 *
1524 * @return bool
1525 * @author Dmitry (dio) Levashov
1526 **/
1527 protected function _inpath($path, $parent)
1528 {
1529 return $path == $parent || strpos($path, $parent . '/') === 0;
1530 }
1531
1532 /***************** file stat ********************/
1533 /**
1534 * Return stat for given path.
1535 * Stat contains following fields:
1536 * - (int) size file size in b. required
1537 * - (int) ts file modification time in unix time. required
1538 * - (string) mime mimetype. required for folders, others - optionally
1539 * - (bool) read read permissions. required
1540 * - (bool) write write permissions. required
1541 * - (bool) locked is object locked. optionally
1542 * - (bool) hidden is object hidden. optionally
1543 * - (string) alias for symlinks - link target path relative to root path. optionally
1544 * - (string) target for symlinks - link target path. optionally.
1545 * If file does not exists - returns empty array or false.
1546 *
1547 * @param string $path file path
1548 *
1549 * @return array|false
1550 * @author Dmitry (dio) Levashov
1551 **/
1552 protected function _stat($path)
1553 {
1554 if ($raw = $this->_od_getFileRaw($path)) {
1555 $stat = $this->_od_parseRaw($raw);
1556 if ($path === $this->root) {
1557 $stat['expires'] = $this->expires;
1558 }
1559 return $stat;
1560 }
1561
1562 return false;
1563 }
1564
1565 /**
1566 * Return true if path is dir and has at least one childs directory.
1567 *
1568 * @param string $path dir path
1569 *
1570 * @return bool
1571 * @throws elFinderAbortException
1572 * @author Dmitry (dio) Levashov
1573 */
1574 protected function _subdirs($path)
1575 {
1576 list(, $itemId) = $this->_od_splitPath($path);
1577
1578 return (bool)$this->_od_query($itemId, false, false, array(
1579 'query' => array(
1580 'top' => 1,
1581 'select' => 'id',
1582 'filter' => 'folder ne null',
1583 ),
1584 ));
1585 }
1586
1587 /**
1588 * Return object width and height
1589 * Ususaly used for images, but can be realize for video etc...
1590 *
1591 * @param string $path file path
1592 * @param string $mime file mime type
1593 *
1594 * @return string
1595 * @throws elFinderAbortException
1596 * @author Dmitry (dio) Levashov
1597 */
1598 protected function _dimensions($path, $mime)
1599 {
1600 if (strpos($mime, 'image') !== 0) {
1601 return '';
1602 }
1603
1604 //$cache = $this->_od_getFileRaw($path);
1605 if (func_num_args() > 2) {
1606 $args = func_get_arg(2);
1607 } else {
1608 $args = array();
1609 }
1610 if (!empty($args['substitute'])) {
1611 $tmbSize = intval($args['substitute']);
1612 } else {
1613 $tmbSize = null;
1614 }
1615 list(, $itemId) = $this->_od_splitPath($path);
1616 $options = array(
1617 'query' => array(
1618 'select' => 'id,image',
1619 ),
1620 );
1621 if ($tmbSize) {
1622 $tmb = 'c' . $tmbSize . 'x' . $tmbSize;
1623 $options['query']['expand'] = 'thumbnails(select=' . $tmb . ')';
1624 }
1625 $raw = $this->_od_query($itemId, true, false, $options);
1626
1627 if ($raw && property_exists($raw, 'image') && $img = $raw->image) {
1628 if (isset($img->width) && isset($img->height)) {
1629 $ret = array('dim' => $img->width . 'x' . $img->height);
1630 if ($tmbSize) {
1631 $srcSize = explode('x', $ret['dim']);
1632 if (min(($tmbSize / $srcSize[0]), ($tmbSize / $srcSize[1])) < 1) {
1633 if (!empty($raw->thumbnails)) {
1634 $tmbArr = (array)$raw->thumbnails[0];
1635 if (!empty($tmbArr[$tmb]->url)) {
1636 $ret['url'] = $tmbArr[$tmb]->url;
1637 }
1638 }
1639 }
1640 }
1641
1642 return $ret;
1643 }
1644 }
1645
1646 $ret = '';
1647 if ($work = $this->getWorkFile($path)) {
1648 if ($size = @getimagesize($work)) {
1649 $cache['width'] = $size[0];
1650 $cache['height'] = $size[1];
1651 $ret = $size[0] . 'x' . $size[1];
1652 }
1653 }
1654 is_file($work) && @unlink($work);
1655
1656 return $ret;
1657 }
1658
1659 /******************** file/dir content *********************/
1660
1661 /**
1662 * Return files list in directory.
1663 *
1664 * @param string $path dir path
1665 *
1666 * @return array
1667 * @throws elFinderAbortException
1668 * @author Dmitry (dio) Levashov
1669 * @author Cem (DiscoFever)
1670 */
1671 protected function _scandir($path)
1672 {
1673 return isset($this->dirsCache[$path])
1674 ? $this->dirsCache[$path]
1675 : $this->cacheDir($path);
1676 }
1677
1678 /**
1679 * Open file and return file pointer.
1680 *
1681 * @param string $path file path
1682 * @param bool $write open file for writing
1683 *
1684 * @return resource|false
1685 * @author Dmitry (dio) Levashov
1686 **/
1687 protected function _fopen($path, $mode = 'rb')
1688 {
1689 if ($mode === 'rb' || $mode === 'r') {
1690 // to support range request
1691 if (func_num_args() > 2) {
1692 $opts = func_get_arg(2);
1693 } else {
1694 $opts = array();
1695 }
1696
1697 $downloadUrl = $this->_od_getDownloadUrl($path);
1698 if ($downloadUrl) {
1699 $data = array(
1700 'target' => $downloadUrl,
1701 );
1702 if (!empty($opts['httpheaders'])) {
1703 $data['headers'] = $opts['httpheaders'];
1704 }
1705
1706 return elFinder::getStreamByUrl($data);
1707 }
1708
1709 list(, $itemId) = $this->_od_splitPath($path);
1710 $data = array(
1711 'target' => self::API_URL . $itemId . '/content',
1712 'headers' => array('Authorization: Bearer ' . $this->token->data->access_token),
1713 );
1714
1715 if (!empty($opts['httpheaders'])) {
1716 $data['headers'] = array_merge($opts['httpheaders'], $data['headers']);
1717 }
1718
1719 return elFinder::getStreamByUrl($data);
1720 }
1721
1722 return false;
1723 }
1724
1725 /**
1726 * Close opened file.
1727 *
1728 * @param resource $fp file pointer
1729 *
1730 * @return bool
1731 * @author Dmitry (dio) Levashov
1732 **/
1733 protected function _fclose($fp, $path = '')
1734 {
1735 is_resource($fp) && fclose($fp);
1736 if ($path) {
1737 unlink($this->getTempFile($path));
1738 }
1739 }
1740
1741 /******************** file/dir manipulations *************************/
1742
1743 /**
1744 * Create dir and return created dir path or false on failed.
1745 *
1746 * @param string $path parent dir path
1747 * @param string $name new directory name
1748 *
1749 * @return string|bool
1750 * @author Dmitry (dio) Levashov
1751 **/
1752 protected function _mkdir($path, $name)
1753 {
1754 $namePath = $this->_joinPath($path, $name);
1755 list($parentId) = $this->_od_splitPath($namePath);
1756
1757 try {
1758 $properties = array(
1759 'name' => (string)$name,
1760 'folder' => (object)array(),
1761 );
1762
1763 $data = (object)$properties;
1764
1765 $url = self::API_URL . $parentId . '/children';
1766
1767 $curl = $this->_od_prepareCurl($url);
1768
1769 curl_setopt_array($curl, array(
1770 CURLOPT_POST => true,
1771 CURLOPT_POSTFIELDS => json_encode($data),
1772 ));
1773
1774 //create the Folder in the Parent
1775 $result = elFinder::curlExec($curl);
1776 $folder = json_decode($result);
1777
1778 return $this->_joinPath($path, $folder->id);
1779 } catch (Exception $e) {
1780 return $this->setError('OneDrive error: ' . $e->getMessage());
1781 }
1782 }
1783
1784 /**
1785 * Create file and return it's path or false on failed.
1786 *
1787 * @param string $path parent dir path
1788 * @param string $name new file name
1789 *
1790 * @return string|bool
1791 * @author Dmitry (dio) Levashov
1792 **/
1793 protected function _mkfile($path, $name)
1794 {
1795 return $this->_save($this->tmpfile(), $path, $name, array());
1796 }
1797
1798 /**
1799 * Create symlink. FTP driver does not support symlinks.
1800 *
1801 * @param string $target link target
1802 * @param string $path symlink path
1803 *
1804 * @return bool
1805 * @author Dmitry (dio) Levashov
1806 **/
1807 protected function _symlink($target, $path, $name)
1808 {
1809 return false;
1810 }
1811
1812 /**
1813 * Copy file into another file.
1814 *
1815 * @param string $source source file path
1816 * @param string $targetDir target directory path
1817 * @param string $name new file name
1818 *
1819 * @return bool
1820 * @author Dmitry (dio) Levashov
1821 **/
1822 protected function _copy($source, $targetDir, $name)
1823 {
1824 $path = $this->_joinPath($targetDir, $name);
1825
1826 try {
1827 //Set the Parent id
1828 list(, $parentId) = $this->_od_splitPath($targetDir);
1829 list(, $itemId) = $this->_od_splitPath($source);
1830
1831 $url = self::API_URL . $itemId . '/copy';
1832
1833 $properties = array(
1834 'name' => (string)$name,
1835 );
1836 if ($parentId === 'root') {
1837 $properties['parentReference'] = (object)array('path' => '/drive/root:');
1838 } else {
1839 $properties['parentReference'] = (object)array('id' => (string)$parentId);
1840 }
1841 $data = (object)$properties;
1842 $curl = $this->_od_prepareCurl($url);
1843 curl_setopt_array($curl, array(
1844 CURLOPT_POST => true,
1845 CURLOPT_HEADER => true,
1846 CURLOPT_HTTPHEADER => array(
1847 'Content-Type: application/json',
1848 'Authorization: Bearer ' . $this->token->data->access_token,
1849 'Prefer: respond-async',
1850 ),
1851 CURLOPT_POSTFIELDS => json_encode($data),
1852 ));
1853 $result = elFinder::curlExec($curl);
1854
1855 $res = new stdClass();
1856 if (preg_match('/Location: (.+)/', $result, $m)) {
1857 $monUrl = trim($m[1]);
1858 while ($res) {
1859 usleep(200000);
1860 $curl = curl_init($monUrl);
1861 curl_setopt_array($curl, array(
1862 CURLOPT_RETURNTRANSFER => true,
1863 CURLOPT_HTTPHEADER => array(
1864 'Content-Type: application/json',
1865 ),
1866 ));
1867 $res = json_decode(elFinder::curlExec($curl));
1868 if (isset($res->status)) {
1869 if ($res->status === 'completed' || $res->status === 'failed') {
1870 break;
1871 }
1872 } elseif (isset($res->error)) {
1873 return $this->setError('OneDrive error: ' . $res->error->message);
1874 }
1875 }
1876 }
1877
1878 if ($res && isset($res->resourceId)) {
1879 if (isset($res->folder) && isset($this->sessionCache['subdirs'])) {
1880 $this->sessionCache['subdirs'][$targetDir] = true;
1881 }
1882
1883 return $this->_joinPath($targetDir, $res->resourceId);
1884 }
1885
1886 return false;
1887 } catch (Exception $e) {
1888 return $this->setError('OneDrive error: ' . $e->getMessage());
1889 }
1890
1891 return true;
1892 }
1893
1894 /**
1895 * Move file into another parent dir.
1896 * Return new file path or false.
1897 *
1898 * @param string $source source file path
1899 * @param $targetDir
1900 * @param string $name file name
1901 *
1902 * @return string|bool
1903 * @author Dmitry (dio) Levashov
1904 */
1905 protected function _move($source, $targetDir, $name)
1906 {
1907 try {
1908 list(, $targetParentId) = $this->_od_splitPath($targetDir);
1909 list($sourceParentId, $itemId, $srcParent) = $this->_od_splitPath($source);
1910
1911 $properties = array(
1912 'name' => (string)$name,
1913 );
1914 if ($targetParentId !== $sourceParentId) {
1915 $properties['parentReference'] = (object)array('id' => (string)$targetParentId);
1916 }
1917
1918 $url = self::API_URL . $itemId;
1919 $data = (object)$properties;
1920
1921 $curl = $this->_od_prepareCurl($url);
1922
1923 curl_setopt_array($curl, array(
1924 CURLOPT_CUSTOMREQUEST => 'PATCH',
1925 CURLOPT_POSTFIELDS => json_encode($data),
1926 ));
1927
1928 $result = json_decode(elFinder::curlExec($curl));
1929 if ($result && isset($result->id)) {
1930 return $targetDir . '/' . $result->id;
1931 } else {
1932 return false;
1933 }
1934 } catch (Exception $e) {
1935 return $this->setError('OneDrive error: ' . $e->getMessage());
1936 }
1937
1938 return false;
1939 }
1940
1941 /**
1942 * Remove file.
1943 *
1944 * @param string $path file path
1945 *
1946 * @return bool
1947 * @author Dmitry (dio) Levashov
1948 **/
1949 protected function _unlink($path)
1950 {
1951 $stat = $this->stat($path);
1952 try {
1953 list(, $itemId) = $this->_od_splitPath($path);
1954
1955 $url = self::API_URL . $itemId;
1956
1957 $curl = $this->_od_prepareCurl($url);
1958 curl_setopt_array($curl, array(
1959 CURLOPT_CUSTOMREQUEST => 'DELETE',
1960 ));
1961
1962 //unlink or delete File or Folder in the Parent
1963 $result = elFinder::curlExec($curl);
1964 } catch (Exception $e) {
1965 return $this->setError('OneDrive error: ' . $e->getMessage());
1966 }
1967
1968 return true;
1969 }
1970
1971 /**
1972 * Remove dir.
1973 *
1974 * @param string $path dir path
1975 *
1976 * @return bool
1977 * @author Dmitry (dio) Levashov
1978 **/
1979 protected function _rmdir($path)
1980 {
1981 return $this->_unlink($path);
1982 }
1983
1984 /**
1985 * Create new file and write into it from file pointer.
1986 * Return new file path or false on error.
1987 *
1988 * @param resource $fp file pointer
1989 * @param $path
1990 * @param string $name file name
1991 * @param array $stat file stat (required by some virtual fs)
1992 *
1993 * @return bool|string
1994 * @author Dmitry (dio) Levashov
1995 */
1996 protected function _save($fp, $path, $name, $stat)
1997 {
1998 $itemId = '';
1999 $size = null;
2000 if ($name === '') {
2001 list($parentId, $itemId, $parent) = $this->_od_splitPath($path);
2002 } else {
2003 if ($stat) {
2004 if (isset($stat['name'])) {
2005 $name = $stat['name'];
2006 }
2007 if (isset($stat['rev']) && strpos($stat['hash'], $this->id) === 0) {
2008 $itemId = $stat['rev'];
2009 }
2010 }
2011 list(, $parentId) = $this->_od_splitPath($path);
2012 $parent = $path;
2013 }
2014
2015 if ($stat && isset($stat['size'])) {
2016 $size = $stat['size'];
2017 } else {
2018 $stats = fstat($fp);
2019 if (isset($stats[7])) {
2020 $size = $stats[7];
2021 }
2022 }
2023
2024 if ($size > 4194304) {
2025 return $this->_od_uploadSession($fp, $size, $name, $itemId, $parent, $parentId);
2026 }
2027
2028 try {
2029 // for unseekable file pointer
2030 if (!elFinder::isSeekableStream($fp)) {
2031 if ($tfp = tmpfile()) {
2032 if (stream_copy_to_stream($fp, $tfp, $size? $size : -1) !== false) {
2033 rewind($tfp);
2034 $fp = $tfp;
2035 }
2036 }
2037 }
2038
2039 //Create or Update a file
2040 if ($itemId === '') {
2041 $url = self::API_URL . $parentId . ':/' . rawurlencode($name) . ':/content';
2042 } else {
2043 $url = self::API_URL . $itemId . '/content';
2044 }
2045 $curl = $this->_od_prepareCurl();
2046
2047 $options = array(
2048 CURLOPT_URL => $url,
2049 CURLOPT_PUT => true,
2050 CURLOPT_INFILE => $fp,
2051 );
2052 // Size
2053 if ($size !== null) {
2054 $options[CURLOPT_INFILESIZE] = $size;
2055 }
2056
2057 curl_setopt_array($curl, $options);
2058
2059 //create or update File in the Target
2060 $file = json_decode(elFinder::curlExec($curl));
2061
2062 return $this->_joinPath($parent, $file->id);
2063 } catch (Exception $e) {
2064 return $this->setError('OneDrive error: ' . $e->getMessage());
2065 }
2066 }
2067
2068 /**
2069 * Get file contents.
2070 *
2071 * @param string $path file path
2072 *
2073 * @return string|false
2074 * @author Dmitry (dio) Levashov
2075 **/
2076 protected function _getContents($path)
2077 {
2078 $contents = '';
2079 try {
2080 if ($url = $this->_od_getDownloadUrl($path)) {
2081 $curl = curl_init($url);
2082 curl_setopt_array($curl, array(
2083 CURLOPT_RETURNTRANSFER => true,
2084 ));
2085 $contents = elFinder::curlExec($curl);
2086 } else {
2087 list(, $itemId) = $this->_od_splitPath($path);
2088 $url = self::API_URL . $itemId . '/content';
2089 $contents = $this->_od_createCurl($url, $contents = true);
2090 }
2091 } catch (Exception $e) {
2092 return $this->setError('OneDrive error: ' . $e->getMessage());
2093 }
2094 return $contents;
2095 }
2096
2097 /**
2098 * Write a string to a file.
2099 *
2100 * @param string $path file path
2101 * @param string $content new file content
2102 *
2103 * @return bool
2104 * @author Dmitry (dio) Levashov
2105 **/
2106 protected function _filePutContents($path, $content)
2107 {
2108 $res = false;
2109
2110 if ($local = $this->getTempFile($path)) {
2111 if (file_put_contents($local, $content, LOCK_EX) !== false
2112 && ($fp = fopen($local, 'rb'))) {
2113 clearstatcache();
2114 $res = $this->_save($fp, $path, '', array());
2115 fclose($fp);
2116 }
2117 file_exists($local) && unlink($local);
2118 }
2119
2120 return $res;
2121 }
2122
2123 /**
2124 * Detect available archivers.
2125 **/
2126 protected function _checkArchivers()
2127 {
2128 // die('Not yet implemented. (_checkArchivers)');
2129 return array();
2130 }
2131
2132 /**
2133 * chmod implementation.
2134 *
2135 * @return bool
2136 **/
2137 protected function _chmod($path, $mode)
2138 {
2139 return false;
2140 }
2141
2142 /**
2143 * Unpack archive.
2144 *
2145 * @param string $path archive path
2146 * @param array $arc archiver command and arguments (same as in $this->archivers)
2147 *
2148 * @return void
2149 * @author Dmitry (dio) Levashov
2150 * @author Alexey Sukhotin
2151 */
2152 protected function _unpack($path, $arc)
2153 {
2154 die('Not yet implemented. (_unpack)');
2155 //return false;
2156 }
2157
2158 /**
2159 * Extract files from archive.
2160 *
2161 * @param string $path archive path
2162 * @param array $arc archiver command and arguments (same as in $this->archivers)
2163 *
2164 * @return void
2165 * @author Dmitry (dio) Levashov,
2166 * @author Alexey Sukhotin
2167 */
2168 protected function _extract($path, $arc)
2169 {
2170 die('Not yet implemented. (_extract)');
2171 }
2172
2173 /**
2174 * Create archive and return its path.
2175 *
2176 * @param string $dir target dir
2177 * @param array $files files names list
2178 * @param string $name archive name
2179 * @param array $arc archiver options
2180 *
2181 * @return string|bool
2182 * @author Dmitry (dio) Levashov,
2183 * @author Alexey Sukhotin
2184 **/
2185 protected function _archive($dir, $files, $name, $arc)
2186 {
2187 die('Not yet implemented. (_archive)');
2188 }
2189 } // END class
2190