PluginProbe ʕ •ᴥ•ʔ
Advanced File Manager – Ultimate File Manager for WordPress And Document Library Solution / trunk
Advanced File Manager – Ultimate File Manager for WordPress And Document Library Solution vtrunk
trunk 4.1.5 4.1.6 5.0 5.2.13 5.2.14 5.3.0 5.3.1 5.3.2 5.3.3 5.3.4 5.3.5 5.3.6 5.4.0 5.4.1 5.4.10 5.4.11 5.4.12 5.4.2 5.4.3 5.4.4 5.4.5 5.4.6 5.4.7 5.4.8
file-manager-advanced / application / library / php / elFinderVolumeOneDrive.class.php
file-manager-advanced / application / library / php Last commit date
.tmp 10 months ago editors 4 weeks ago libs 7 years ago plugins 3 years ago resources 7 years ago MySQLStorage.sql 1 year ago autoload.php 10 months ago connector.maximal.php-dist 1 year ago connector.minimal.php-dist 1 year ago elFinder.class.php 4 weeks ago elFinderConnector.class.php 4 weeks ago elFinderFlysystemGoogleDriveNetmount.php 5 years ago elFinderPlugin.php 7 years ago elFinderSession.php 4 years ago elFinderSessionInterface.php 7 years ago elFinderVolumeBox.class.php 5 years ago elFinderVolumeDriver.class.php 4 weeks ago elFinderVolumeDropbox.class.php 1 year ago elFinderVolumeDropbox2.class.php 4 weeks ago elFinderVolumeFTP.class.php 5 years ago elFinderVolumeGoogleDrive.class.php 5 years ago elFinderVolumeGroup.class.php 7 years ago elFinderVolumeLocalFileSystem.class.php 2 years ago elFinderVolumeMySQL.class.php 10 months ago elFinderVolumeOneDrive.class.php 4 weeks ago elFinderVolumeSFTPphpseclib.class.php 4 weeks ago elFinderVolumeTrash.class.php 7 years ago elFinderVolumeTrashMySQL.class.php 7 years ago mime.types 3 years ago
elFinderVolumeOneDrive.class.php
2133 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 * Drive query and fetchAll.
392 *
393 * @param $itemId
394 * @param bool $fetch_self
395 * @param bool $recursive
396 * @param array $options
397 *
398 * @return object|array
399 * @throws elFinderAbortException
400 */
401 protected function _od_query($itemId, $fetch_self = false, $recursive = false, $options = array())
402 {
403 $result = array();
404
405 if (null === $itemId) {
406 $itemId = 'root';
407 }
408
409 if ($fetch_self == true) {
410 $path = $itemId;
411 } else {
412 $path = $itemId . '/children';
413 }
414
415 if (isset($options['query'])) {
416 $path .= '?' . http_build_query($options['query']);
417 }
418
419 $url = self::API_URL . $path;
420
421 $res = $this->_od_createCurl($url);
422 if (!$fetch_self && $recursive && is_array($res)) {
423 foreach ($res as $file) {
424 $result[] = $file;
425 if (!empty($file->folder)) {
426 $result = array_merge($result, $this->_od_query($file->id, false, true, $options));
427 }
428 }
429 } else {
430 $result = $res;
431 }
432
433 return isset($result->error) ? array() : $result;
434 }
435
436 /**
437 * Parse line from onedrive metadata output and return file stat (array).
438 *
439 * @param object $raw line from ftp_rawlist() output
440 *
441 * @return array
442 * @author Dmitry Levashov
443 **/
444 protected function _od_parseRaw($raw)
445 {
446 $stat = array();
447
448 $folder = isset($raw->folder) ? $raw->folder : null;
449
450 $stat['rev'] = isset($raw->id) ? $raw->id : 'root';
451 $stat['name'] = $raw->name;
452 if (isset($raw->lastModifiedDateTime)) {
453 $stat['ts'] = strtotime($raw->lastModifiedDateTime);
454 }
455
456 if ($folder) {
457 $stat['mime'] = 'directory';
458 $stat['size'] = 0;
459 if (empty($folder->childCount)) {
460 $stat['dirs'] = 0;
461 } else {
462 $stat['dirs'] = -1;
463 }
464 } else {
465 if (isset($raw->file->mimeType)) {
466 $stat['mime'] = $raw->file->mimeType;
467 }
468 $stat['size'] = (int)$raw->size;
469 if (!$this->disabledGetUrl) {
470 $stat['url'] = '1';
471 }
472 if (isset($raw->image) && $img = $raw->image) {
473 isset($img->width) ? $stat['width'] = $img->width : $stat['width'] = 0;
474 isset($img->height) ? $stat['height'] = $img->height : $stat['height'] = 0;
475 }
476 if (!empty($raw->thumbnails)) {
477 if ($raw->thumbnails[0]->small->url) {
478 $stat['tmb'] = substr($raw->thumbnails[0]->small->url, 8); // remove "https://"
479 }
480 } elseif (!empty($raw->file->processingMetadata)) {
481 $stat['tmb'] = '1';
482 }
483 }
484
485 return $stat;
486 }
487
488 /**
489 * Get raw data(onedrive metadata) from OneDrive.
490 *
491 * @param string $path
492 *
493 * @return array|object onedrive metadata
494 */
495 protected function _od_getFileRaw($path)
496 {
497 list(, $itemId) = $this->_od_splitPath($path);
498 try {
499 $res = $this->_od_query($itemId, true, false, $this->queryOptions);
500
501 return $res;
502 } catch (Exception $e) {
503 return array();
504 }
505 }
506
507 /**
508 * Get thumbnail from OneDrive.com.
509 *
510 * @param string $path
511 *
512 * @return string | boolean
513 */
514 protected function _od_getThumbnail($path)
515 {
516 list(, $itemId) = $this->_od_splitPath($path);
517
518 try {
519 $url = self::API_URL . $itemId . '/thumbnails/0/medium/content';
520
521 return $this->_od_createCurl($url, $contents = true);
522 } catch (Exception $e) {
523 return false;
524 }
525 }
526
527 /**
528 * Upload large files with an upload session.
529 *
530 * @param resource $fp source file pointer
531 * @param number $size total size
532 * @param string $name item name
533 * @param string $itemId item identifier
534 * @param string $parent parent
535 * @param string $parentId parent identifier
536 *
537 * @return string The item path
538 */
539 protected function _od_uploadSession($fp, $size, $name, $itemId, $parent, $parentId)
540 {
541 try {
542 $send = $this->_od_getChunkData($fp);
543 if ($send === false) {
544 throw new Exception('Data can not be acquired from the source.');
545 }
546
547 // create upload session
548 if ($itemId) {
549 $url = self::API_URL . $itemId . '/createUploadSession';
550 } else {
551 $url = self::API_URL . $parentId . ':/' . rawurlencode($name) . ':/createUploadSession';
552 }
553 $curl = $this->_od_prepareCurl($url);
554 curl_setopt_array($curl, array(
555 CURLOPT_POST => true,
556 CURLOPT_POSTFIELDS => '{}',
557 ));
558 $sess = json_decode(elFinder::curlExec($curl));
559
560 if ($sess) {
561 if (isset($sess->error)) {
562 throw new Exception($sess->error->message);
563 }
564 $next = strlen($send);
565 $range = '0-' . ($next - 1) . '/' . $size;
566 } else {
567 throw new Exception('API response can not be obtained.');
568 }
569
570 $id = null;
571 $retry = 0;
572 while ($sess) {
573 elFinder::extendTimeLimit();
574 $putFp = tmpfile();
575 fwrite($putFp, $send);
576 rewind($putFp);
577 $_size = strlen($send);
578 $url = $sess->uploadUrl;
579 $curl = curl_init();
580 $options = array(
581 CURLOPT_URL => $url,
582 CURLOPT_PUT => true,
583 CURLOPT_RETURNTRANSFER => true,
584 CURLOPT_INFILE => $putFp,
585 CURLOPT_INFILESIZE => $_size,
586 CURLOPT_HTTPHEADER => array(
587 'Content-Length: ' . $_size,
588 'Content-Range: bytes ' . $range,
589 ),
590 );
591 curl_setopt_array($curl, $options);
592 $sess = json_decode(elFinder::curlExec($curl));
593 if ($sess) {
594 if (isset($sess->error)) {
595 throw new Exception($sess->error->message);
596 }
597 if (isset($sess->id)) {
598 $id = $sess->id;
599 break;
600 }
601 if (isset($sess->nextExpectedRanges)) {
602 list($_next) = explode('-', $sess->nextExpectedRanges[0]);
603 if ($next == $_next) {
604 $send = $this->_od_getChunkData($fp);
605 if ($send === false) {
606 throw new Exception('Data can not be acquired from the source.');
607 }
608 $next += strlen($send);
609 $range = $_next . '-' . ($next - 1) . '/' . $size;
610 $retry = 0;
611 } else {
612 if (++$retry > 3) {
613 throw new Exception('Retry limit exceeded with uploadSession API call.');
614 }
615 }
616 $sess->uploadUrl = $url;
617 }
618 } else {
619 throw new Exception('API response can not be obtained.');
620 }
621 }
622
623 if ($id) {
624 return $this->_joinPath($parent, $id);
625 } else {
626 throw new Exception('An error occurred in the uploadSession API call.');
627 }
628 } catch (Exception $e) {
629 return $this->setError('OneDrive error: ' . $e->getMessage());
630 }
631 }
632
633 /**
634 * Get chunk data by file pointer to upload session.
635 *
636 * @param resource $fp source file pointer
637 *
638 * @return bool|string chunked data
639 */
640 protected function _od_getChunkData($fp)
641 {
642 static $chunkSize = null;
643 if ($chunkSize === null) {
644 $mem = elFinder::getIniBytes('memory_limit');
645 if ($mem < 1) {
646 $mem = 10485760; // 10 MiB
647 } else {
648 $mem -= memory_get_usage() - 1061548;
649 $mem = min($mem, 10485760);
650 }
651 if ($mem > 327680) {
652 $chunkSize = floor($mem / 327680) * 327680;
653 } else {
654 $chunkSize = $mem;
655 }
656 }
657 if ($chunkSize < 8192) {
658 return false;
659 }
660
661 $contents = '';
662 while (!feof($fp) && strlen($contents) < $chunkSize) {
663 $contents .= fread($fp, 8192);
664 }
665
666 return $contents;
667 }
668
669 /**
670 * Get AccessToken file path
671 *
672 * @return string ( description_of_the_return_value )
673 */
674 protected function _od_getATokenFile()
675 {
676 $tmp = $aTokenFile = '';
677 if (!empty($this->token->data->refresh_token)) {
678 if (!$this->tmp) {
679 $tmp = elFinder::getStaticVar('commonTempPath');
680 if (!$tmp) {
681 $tmp = $this->getTempPath();
682 }
683 $this->tmp = $tmp;
684 }
685 if ($tmp) {
686 $aTokenFile = $tmp . DIRECTORY_SEPARATOR . $this->_od_getInitialToken() . '.otoken';
687 }
688 }
689 return $aTokenFile;
690 }
691
692 /**
693 * Get Initial Token (MD5 hash)
694 *
695 * @return string
696 */
697 protected function _od_getInitialToken()
698 {
699 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);
700 }
701
702 /*********************************************************************/
703 /* OVERRIDE FUNCTIONS */
704 /*********************************************************************/
705
706 /**
707 * Prepare
708 * Call from elFinder::netmout() before volume->mount().
709 *
710 * @return array
711 * @author Naoki Sawada
712 * @author Raja Sharma updating for OneDrive
713 **/
714 public function netmountPrepare($options)
715 {
716 if (empty($options['client_id']) && defined('ELFINDER_ONEDRIVE_CLIENTID')) {
717 $options['client_id'] = ELFINDER_ONEDRIVE_CLIENTID;
718 }
719 if (empty($options['client_secret']) && defined('ELFINDER_ONEDRIVE_CLIENTSECRET')) {
720 $options['client_secret'] = ELFINDER_ONEDRIVE_CLIENTSECRET;
721 }
722
723 if (isset($options['pass']) && $options['pass'] === 'reauth') {
724 $options['user'] = 'init';
725 $options['pass'] = '';
726 $this->session->remove('OneDriveTokens');
727 }
728
729 if (isset($options['id'])) {
730 $this->session->set('nodeId', $options['id']);
731 } elseif ($_id = $this->session->get('nodeId')) {
732 $options['id'] = $_id;
733 $this->session->set('nodeId', $_id);
734 }
735
736 if (!empty($options['tmpPath'])) {
737 if ((is_dir($options['tmpPath']) || mkdir($this->options['tmpPath'])) && is_writable($options['tmpPath'])) {
738 $this->tmp = $options['tmpPath'];
739 }
740 }
741
742 try {
743 if (empty($options['client_id']) || empty($options['client_secret'])) {
744 return array('exit' => true, 'body' => '{msg:errNetMountNoDriver}');
745 }
746
747 $itpCare = isset($options['code']);
748 $code = $itpCare? $options['code'] : (isset($_GET['code'])? $_GET['code'] : '');
749 if ($code) {
750 try {
751 if (!empty($options['id'])) {
752 // Obtain the token using the code received by the OneDrive API
753 $this->session->set('OneDriveTokens',
754 $this->_od_obtainAccessToken($options['client_id'], $options['client_secret'], $code, $options['id']));
755
756 $out = array(
757 'node' => $options['id'],
758 'json' => '{"protocol": "onedrive", "mode": "done", "reset": 1}',
759 'bind' => 'netmount',
760 );
761 } else {
762 $nodeid = ($_GET['host'] === '1')? 'elfinder' : $_GET['host'];
763 $out = array(
764 'node' => $nodeid,
765 'json' => json_encode(array(
766 'protocol' => 'onedrive',
767 'host' => $nodeid,
768 'mode' => 'redirect',
769 'options' => array(
770 'id' => $nodeid,
771 'code'=> $code
772 )
773 )),
774 'bind' => 'netmount'
775 );
776 }
777 if (!$itpCare) {
778 return array('exit' => 'callback', 'out' => $out);
779 } else {
780 return array('exit' => true, 'body' => $out['json']);
781 }
782 } catch (Exception $e) {
783 $out = array(
784 'node' => $options['id'],
785 'json' => json_encode(array('error' => elFinder::ERROR_ACCESS_DENIED . ' ' . $e->getMessage())),
786 );
787
788 return array('exit' => 'callback', 'out' => $out);
789 }
790 } elseif (!empty($_GET['error'])) {
791 $out = array(
792 'node' => $options['id'],
793 'json' => json_encode(array('error' => elFinder::ERROR_ACCESS_DENIED)),
794 );
795
796 return array('exit' => 'callback', 'out' => $out);
797 }
798
799 if ($options['user'] === 'init') {
800 $this->token = $this->session->get('OneDriveTokens');
801
802 if ($this->token) {
803 try {
804 $this->_od_refreshToken();
805 } catch (Exception $e) {
806 $this->setError($e->getMessage());
807 $this->token = null;
808 $this->session->remove('OneDriveTokens');
809 }
810 }
811
812 if (empty($this->token)) {
813 $result = false;
814 } else {
815 $path = $options['path'];
816 if ($path === '/') {
817 $path = 'root';
818 }
819 $result = $this->_od_query($path, false, false, array(
820 'query' => array(
821 'select' => 'id,name',
822 'filter' => 'folder ne null',
823 ),
824 ));
825 }
826
827 if ($result === false) {
828 try {
829 $this->session->set('OneDriveTokens', (object)array('token' => null));
830
831 $offline = '';
832 // Gets a log in URL with sufficient privileges from the OneDrive API
833 if (!empty($options['offline'])) {
834 $offline = ' offline_access';
835 }
836
837 $redirect_uri = elFinder::getConnectorUrl() . '/netmount/onedrive/' . ($options['id'] === 'elfinder'? '1' : $options['id']);
838 $url = self::AUTH_URL
839 . '?client_id=' . urlencode($options['client_id'])
840 . '&scope=' . urlencode('files.readwrite.all' . $offline)
841 . '&response_type=code'
842 . '&redirect_uri=' . urlencode($redirect_uri);
843
844 } catch (Exception $e) {
845 return array('exit' => true, 'body' => '{msg:errAccess}');
846 }
847
848 $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">';
849 $html .= '<script>
850 $("#' . $options['id'] . '").elfinder("instance").trigger("netmount", {protocol: "onedrive", mode: "makebtn", url: "' . $url . '"});
851 </script>';
852
853 return array('exit' => true, 'body' => $html);
854 } else {
855 $folders = [];
856
857 if ($result) {
858 foreach ($result as $res) {
859 $folders[$res->id] = $res->name;
860 }
861 natcasesort($folders);
862 }
863
864 if ($options['pass'] === 'folders') {
865 return ['exit' => true, 'folders' => $folders];
866 }
867
868 $folders = ['root' => 'My OneDrive'] + $folders;
869 $folders = json_encode($folders);
870
871 $expires = empty($this->token->data->refresh_token) ? (int)$this->token->expires : 0;
872 $mnt2res = empty($this->token->data->refresh_token) ? '' : ', "mnt2res": 1';
873 $json = '{"protocol": "onedrive", "mode": "done", "folders": ' . $folders . ', "expires": ' . $expires . $mnt2res .'}';
874 $html = 'OneDrive.com';
875 $html .= '<script>
876 $("#' . $options['id'] . '").elfinder("instance").trigger("netmount", ' . $json . ');
877 </script>';
878
879 return array('exit' => true, 'body' => $html);
880 }
881 }
882 } catch (Exception $e) {
883 return array('exit' => true, 'body' => '{msg:errNetMountNoDriver}');
884 }
885
886 if ($_aToken = $this->session->get('OneDriveTokens')) {
887 $options['accessToken'] = json_encode($_aToken);
888 if ($this->options['path'] === 'root' || !$this->options['path']) {
889 $this->options['path'] = '/';
890 }
891 } else {
892 $this->session->remove('OneDriveTokens');
893 $this->setError(elFinder::ERROR_NETMOUNT, $options['host'], implode(' ', $this->error()));
894
895 return array('exit' => true, 'error' => $this->error());
896 }
897
898 $this->session->remove('nodeId');
899 unset($options['user'], $options['pass'], $options['id']);
900
901 return $options;
902 }
903
904 /**
905 * process of on netunmount
906 * Drop `onedrive` & rm thumbs.
907 *
908 * @param array $options
909 *
910 * @return bool
911 */
912 public function netunmount($netVolumes, $key)
913 {
914 if (!$this->options['useApiThumbnail'] && ($tmbs = glob(rtrim($this->options['tmbPath'], '\\/') . DIRECTORY_SEPARATOR . $this->tmbPrefix . '*.png'))) {
915 foreach ($tmbs as $file) {
916 unlink($file);
917 }
918 }
919
920 return true;
921 }
922
923 /**
924 * Return debug info for client.
925 *
926 * @return array
927 **/
928 public function debug()
929 {
930 $res = parent::debug();
931 if (!empty($this->options['netkey']) && !empty($this->options['accessToken'])) {
932 $res['accessToken'] = $this->options['accessToken'];
933 }
934
935 return $res;
936 }
937
938 /*********************************************************************/
939 /* INIT AND CONFIGURE */
940 /*********************************************************************/
941
942 /**
943 * Prepare FTP connection
944 * Connect to remote server and check if credentials are correct, if so, store the connection id in $ftp_conn.
945 *
946 * @return bool
947 * @throws elFinderAbortException
948 * @author Dmitry (dio) Levashov
949 * @author Cem (DiscoFever)
950 */
951 protected function init()
952 {
953 if (!$this->options['accessToken']) {
954 return $this->setError('Required option `accessToken` is undefined.');
955 }
956
957 if (!empty($this->options['tmpPath'])) {
958 if ((is_dir($this->options['tmpPath']) || mkdir($this->options['tmpPath'])) && is_writable($this->options['tmpPath'])) {
959 $this->tmp = $this->options['tmpPath'];
960 }
961 }
962
963 $error = false;
964 try {
965 $this->token = json_decode($this->options['accessToken']);
966 if (!is_object($this->token)) {
967 throw new Exception('Required option `accessToken` is invalid JSON.');
968 }
969
970 // make net mount key
971 if (empty($this->options['netkey'])) {
972 $this->netMountKey = $this->_od_getInitialToken();
973 } else {
974 $this->netMountKey = $this->options['netkey'];
975 }
976
977 if ($this->aTokenFile = $this->_od_getATokenFile()) {
978 if (empty($this->options['netkey'])) {
979 if ($this->aTokenFile) {
980 if (is_file($this->aTokenFile)) {
981 $this->token = json_decode(file_get_contents($this->aTokenFile));
982 if (!is_object($this->token)) {
983 unlink($this->aTokenFile);
984 throw new Exception('Required option `accessToken` is invalid JSON.');
985 }
986 } else {
987 file_put_contents($this->aTokenFile, $this->token);
988 }
989 }
990 } else if (is_file($this->aTokenFile)) {
991 // If the refresh token is the same as the permanent volume
992 $this->token = json_decode(file_get_contents($this->aTokenFile));
993 }
994 }
995
996 if ($this->needOnline) {
997 $this->_od_refreshToken();
998
999 $this->expires = empty($this->token->data->refresh_token) ? (int)$this->token->expires : 0;
1000 }
1001 } catch (Exception $e) {
1002 $this->token = null;
1003 $error = true;
1004 $this->setError($e->getMessage());
1005 }
1006
1007 if ($this->netMountKey) {
1008 $this->tmbPrefix = 'onedrive' . base_convert($this->netMountKey, 16, 32);
1009 }
1010
1011 if ($error) {
1012 if (empty($this->options['netkey']) && $this->tmbPrefix) {
1013 // for delete thumbnail
1014 $this->netunmount(null, null);
1015 }
1016 return false;
1017 }
1018
1019 // normalize root path
1020 if ($this->options['path'] == 'root') {
1021 $this->options['path'] = '/';
1022 }
1023
1024 $this->root = $this->options['path'] = $this->_normpath($this->options['path']);
1025
1026 $this->options['root'] = ($this->options['root'] == '')? 'OneDrive.com' : $this->options['root'];
1027
1028 if (empty($this->options['alias'])) {
1029 if ($this->needOnline) {
1030 $this->options['alias'] = ($this->options['path'] === '/') ? $this->options['root'] :
1031 $this->_od_query(basename($this->options['path']), $fetch_self = true)->name . '@OneDrive';
1032 if (!empty($this->options['netkey'])) {
1033 elFinder::$instance->updateNetVolumeOption($this->options['netkey'], 'alias', $this->options['alias']);
1034 }
1035 } else {
1036 $this->options['alias'] = $this->options['root'];
1037 }
1038 }
1039
1040 $this->rootName = $this->options['alias'];
1041
1042 // This driver dose not support `syncChkAsTs`
1043 $this->options['syncChkAsTs'] = false;
1044
1045 // 'lsPlSleep' minmum 10 sec
1046 $this->options['lsPlSleep'] = max(10, $this->options['lsPlSleep']);
1047
1048 $this->queryOptions = array(
1049 'query' => array(
1050 'select' => 'id,name,lastModifiedDateTime,file,folder,size,image',
1051 ),
1052 );
1053
1054 if ($this->options['useApiThumbnail']) {
1055 $this->options['tmbURL'] = 'https://';
1056 $this->options['tmbPath'] = '';
1057 $this->queryOptions['query']['expand'] = 'thumbnails(select=small)';
1058 }
1059
1060 // enable command archive
1061 $this->options['useRemoteArchive'] = true;
1062
1063 return true;
1064 }
1065
1066 /**
1067 * Configure after successfull mount.
1068 *
1069 * @author Dmitry (dio) Levashov
1070 **/
1071 protected function configure()
1072 {
1073 parent::configure();
1074
1075 // fallback of $this->tmp
1076 if (!$this->tmp && $this->tmbPathWritable) {
1077 $this->tmp = $this->tmbPath;
1078 }
1079 }
1080
1081 /*********************************************************************/
1082 /* FS API */
1083 /*********************************************************************/
1084
1085 /**
1086 * Close opened connection.
1087 *
1088 * @author Dmitry (dio) Levashov
1089 **/
1090 public function umount()
1091 {
1092 }
1093
1094 protected function isNameExists($path)
1095 {
1096 list($pid, $name) = $this->_od_splitPath($path);
1097
1098 $raw = $this->_od_query($pid . '/children/' . rawurlencode($name), true);
1099 return $raw ? $this->_od_parseRaw($raw) : false;
1100 }
1101
1102 /**
1103 * Cache dir contents.
1104 *
1105 * @param string $path dir path
1106 *
1107 * @return array
1108 * @throws elFinderAbortException
1109 * @author Dmitry Levashov
1110 */
1111 protected function cacheDir($path)
1112 {
1113 $this->dirsCache[$path] = array();
1114 $hasDir = false;
1115
1116 list(, $itemId) = $this->_od_splitPath($path);
1117
1118 $res = $this->_od_query($itemId, false, false, $this->queryOptions);
1119
1120 if ($res) {
1121 foreach ($res as $raw) {
1122 if ($stat = $this->_od_parseRaw($raw)) {
1123 $itemPath = $this->_joinPath($path, $raw->id);
1124 $stat = $this->updateCache($itemPath, $stat);
1125 if (empty($stat['hidden'])) {
1126 if (!$hasDir && $stat['mime'] === 'directory') {
1127 $hasDir = true;
1128 }
1129 $this->dirsCache[$path][] = $itemPath;
1130 }
1131 }
1132 }
1133 }
1134
1135 if (isset($this->sessionCache['subdirs'])) {
1136 $this->sessionCache['subdirs'][$path] = $hasDir;
1137 }
1138
1139 return $this->dirsCache[$path];
1140 }
1141
1142 /**
1143 * Copy file/recursive copy dir only in current volume.
1144 * Return new file path or false.
1145 *
1146 * @param string $src source path
1147 * @param string $dst destination dir path
1148 * @param string $name new file name (optionaly)
1149 *
1150 * @return string|false
1151 * @throws elFinderAbortException
1152 * @author Dmitry (dio) Levashov
1153 * @author Naoki Sawada
1154 */
1155 protected function copy($src, $dst, $name)
1156 {
1157 $itemId = '';
1158 if ($this->options['copyJoin']) {
1159 $test = $this->joinPathCE($dst, $name);
1160 if ($testStat = $this->isNameExists($test)) {
1161 $this->remove($test);
1162 }
1163 }
1164
1165 if ($path = $this->_copy($src, $dst, $name)) {
1166 $this->added[] = $this->stat($path);
1167 } else {
1168 $this->setError(elFinder::ERROR_COPY, $this->_path($src));
1169 }
1170
1171 return $path;
1172 }
1173
1174 /**
1175 * Remove file/ recursive remove dir.
1176 *
1177 * @param string $path file path
1178 * @param bool $force try to remove even if file locked
1179 *
1180 * @return bool
1181 * @throws elFinderAbortException
1182 * @author Dmitry (dio) Levashov
1183 * @author Naoki Sawada
1184 */
1185 protected function remove($path, $force = false)
1186 {
1187 $stat = $this->stat($path);
1188 $stat['realpath'] = $path;
1189 $this->rmTmb($stat);
1190 $this->clearcache();
1191
1192 if (empty($stat)) {
1193 return $this->setError(elFinder::ERROR_RM, $this->_path($path), elFinder::ERROR_FILE_NOT_FOUND);
1194 }
1195
1196 if (!$force && !empty($stat['locked'])) {
1197 return $this->setError(elFinder::ERROR_LOCKED, $this->_path($path));
1198 }
1199
1200 if ($stat['mime'] == 'directory') {
1201 if (!$this->_rmdir($path)) {
1202 return $this->setError(elFinder::ERROR_RM, $this->_path($path));
1203 }
1204 } else {
1205 if (!$this->_unlink($path)) {
1206 return $this->setError(elFinder::ERROR_RM, $this->_path($path));
1207 }
1208 }
1209
1210 $this->removed[] = $stat;
1211
1212 return true;
1213 }
1214
1215 /**
1216 * Create thumnbnail and return it's URL on success.
1217 *
1218 * @param string $path file path
1219 * @param $stat
1220 *
1221 * @return string|false
1222 * @throws ImagickException
1223 * @throws elFinderAbortException
1224 * @author Dmitry (dio) Levashov
1225 * @author Naoki Sawada
1226 */
1227 protected function createTmb($path, $stat)
1228 {
1229 if ($this->options['useApiThumbnail']) {
1230 if (func_num_args() > 2) {
1231 list(, , $count) = func_get_args();
1232 } else {
1233 $count = 0;
1234 }
1235 if ($count < 10) {
1236 if (isset($stat['tmb']) && $stat['tmb'] != '1') {
1237 return $stat['tmb'];
1238 } else {
1239 sleep(2);
1240 elFinder::extendTimeLimit();
1241 $this->clearcache();
1242 $stat = $this->stat($path);
1243
1244 return $this->createTmb($path, $stat, ++$count);
1245 }
1246 }
1247
1248 return false;
1249 }
1250 if (!$stat || !$this->canCreateTmb($path, $stat)) {
1251 return false;
1252 }
1253
1254 $name = $this->tmbname($stat);
1255 $tmb = $this->tmbPath . DIRECTORY_SEPARATOR . $name;
1256
1257 // copy image into tmbPath so some drivers does not store files on local fs
1258 if (!$data = $this->_od_getThumbnail($path)) {
1259 return false;
1260 }
1261 if (!file_put_contents($tmb, $data)) {
1262 return false;
1263 }
1264
1265 $result = false;
1266
1267 $tmbSize = $this->tmbSize;
1268
1269 if (($s = getimagesize($tmb)) == false) {
1270 return false;
1271 }
1272
1273 /* If image smaller or equal thumbnail size - just fitting to thumbnail square */
1274 if ($s[0] <= $tmbSize && $s[1] <= $tmbSize) {
1275 $result = $this->imgSquareFit($tmb, $tmbSize, $tmbSize, 'center', 'middle', $this->options['tmbBgColor'], 'png');
1276 } else {
1277 if ($this->options['tmbCrop']) {
1278
1279 /* Resize and crop if image bigger than thumbnail */
1280 if (!(($s[0] > $tmbSize && $s[1] <= $tmbSize) || ($s[0] <= $tmbSize && $s[1] > $tmbSize)) || ($s[0] > $tmbSize && $s[1] > $tmbSize)) {
1281 $result = $this->imgResize($tmb, $tmbSize, $tmbSize, true, false, 'png');
1282 }
1283
1284 if (($s = getimagesize($tmb)) != false) {
1285 $x = $s[0] > $tmbSize ? intval(($s[0] - $tmbSize) / 2) : 0;
1286 $y = $s[1] > $tmbSize ? intval(($s[1] - $tmbSize) / 2) : 0;
1287 $result = $this->imgCrop($tmb, $tmbSize, $tmbSize, $x, $y, 'png');
1288 }
1289 } else {
1290 $result = $this->imgResize($tmb, $tmbSize, $tmbSize, true, true, 'png');
1291 }
1292
1293 $result = $this->imgSquareFit($tmb, $tmbSize, $tmbSize, 'center', 'middle', $this->options['tmbBgColor'], 'png');
1294 }
1295
1296 if (!$result) {
1297 unlink($tmb);
1298
1299 return false;
1300 }
1301
1302 return $name;
1303 }
1304
1305 /**
1306 * Return thumbnail file name for required file.
1307 *
1308 * @param array $stat file stat
1309 *
1310 * @return string
1311 * @author Dmitry (dio) Levashov
1312 **/
1313 protected function tmbname($stat)
1314 {
1315 return $this->tmbPrefix . $stat['rev'] . $stat['ts'] . '.png';
1316 }
1317
1318 /**
1319 * Return content URL.
1320 *
1321 * @param string $hash file hash
1322 * @param array $options options
1323 *
1324 * @return string
1325 * @author Naoki Sawada
1326 **/
1327 public function getContentUrl($hash, $options = array())
1328 {
1329 if (!empty($options['onetime']) && $this->options['onetimeUrl']) {
1330 return parent::getContentUrl($hash, $options);
1331 }
1332 if (!empty($options['temporary'])) {
1333 // try make temporary file
1334 $url = parent::getContentUrl($hash, $options);
1335 if ($url) {
1336 return $url;
1337 }
1338 }
1339 $res = '';
1340 if (($file = $this->file($hash)) == false || !$file['url'] || $file['url'] == 1) {
1341 $path = $this->decode($hash);
1342
1343 list(, $itemId) = $this->_od_splitPath($path);
1344 try {
1345 $url = self::API_URL . $itemId . '/createLink';
1346 $data = (object)array(
1347 'type' => 'embed',
1348 'scope' => 'anonymous',
1349 );
1350 $curl = $this->_od_prepareCurl($url);
1351 curl_setopt_array($curl, array(
1352 CURLOPT_POST => true,
1353 CURLOPT_POSTFIELDS => json_encode($data),
1354 ));
1355
1356 $result = elFinder::curlExec($curl);
1357 if ($result) {
1358 $result = json_decode($result);
1359 if (isset($result->link)) {
1360 // list(, $res) = explode('?', $result->link->webUrl);
1361 // $res = 'https://onedrive.live.com/download.aspx?' . $res;
1362 $res = $result->link->webUrl;
1363 }
1364 }
1365 } catch (Exception $e) {
1366 $res = '';
1367 }
1368 }
1369
1370 return $res;
1371 }
1372
1373 /*********************** paths/urls *************************/
1374
1375 /**
1376 * Return parent directory path.
1377 *
1378 * @param string $path file path
1379 *
1380 * @return string
1381 * @author Dmitry (dio) Levashov
1382 **/
1383 protected function _dirname($path)
1384 {
1385 list(, , $dirname) = $this->_od_splitPath($path);
1386
1387 return $dirname;
1388 }
1389
1390 /**
1391 * Return file name.
1392 *
1393 * @param string $path file path
1394 *
1395 * @return string
1396 * @author Dmitry (dio) Levashov
1397 **/
1398 protected function _basename($path)
1399 {
1400 list(, $basename) = $this->_od_splitPath($path);
1401
1402 return $basename;
1403 }
1404
1405 /**
1406 * Join dir name and file name and retur full path.
1407 *
1408 * @param string $dir
1409 * @param string $name
1410 *
1411 * @return string
1412 * @author Dmitry (dio) Levashov
1413 **/
1414 protected function _joinPath($dir, $name)
1415 {
1416 if ($dir === 'root') {
1417 $dir = '';
1418 }
1419
1420 return $this->_normpath($dir . '/' . $name);
1421 }
1422
1423 /**
1424 * Return normalized path, this works the same as os.path.normpath() in Python.
1425 *
1426 * @param string $path path
1427 *
1428 * @return string
1429 * @author Troex Nevelin
1430 **/
1431 protected function _normpath($path)
1432 {
1433 if (DIRECTORY_SEPARATOR !== '/') {
1434 $path = str_replace(DIRECTORY_SEPARATOR, '/', $path);
1435 }
1436 $path = '/' . ltrim($path, '/');
1437
1438 return $path;
1439 }
1440
1441 /**
1442 * Return file path related to root dir.
1443 *
1444 * @param string $path file path
1445 *
1446 * @return string
1447 * @author Dmitry (dio) Levashov
1448 **/
1449 protected function _relpath($path)
1450 {
1451 return $path;
1452 }
1453
1454 /**
1455 * Convert path related to root dir into real path.
1456 *
1457 * @param string $path file path
1458 *
1459 * @return string
1460 * @author Dmitry (dio) Levashov
1461 **/
1462 protected function _abspath($path)
1463 {
1464 return $path;
1465 }
1466
1467 /**
1468 * Return fake path started from root dir.
1469 *
1470 * @param string $path file path
1471 *
1472 * @return string
1473 * @author Dmitry (dio) Levashov
1474 **/
1475 protected function _path($path)
1476 {
1477 return $this->rootName . $this->_normpath(substr($path, strlen($this->root)));
1478 }
1479
1480 /**
1481 * Return true if $path is children of $parent.
1482 *
1483 * @param string $path path to check
1484 * @param string $parent parent path
1485 *
1486 * @return bool
1487 * @author Dmitry (dio) Levashov
1488 **/
1489 protected function _inpath($path, $parent)
1490 {
1491 return $path == $parent || strpos($path, $parent . '/') === 0;
1492 }
1493
1494 /***************** file stat ********************/
1495 /**
1496 * Return stat for given path.
1497 * Stat contains following fields:
1498 * - (int) size file size in b. required
1499 * - (int) ts file modification time in unix time. required
1500 * - (string) mime mimetype. required for folders, others - optionally
1501 * - (bool) read read permissions. required
1502 * - (bool) write write permissions. required
1503 * - (bool) locked is object locked. optionally
1504 * - (bool) hidden is object hidden. optionally
1505 * - (string) alias for symlinks - link target path relative to root path. optionally
1506 * - (string) target for symlinks - link target path. optionally.
1507 * If file does not exists - returns empty array or false.
1508 *
1509 * @param string $path file path
1510 *
1511 * @return array|false
1512 * @author Dmitry (dio) Levashov
1513 **/
1514 protected function _stat($path)
1515 {
1516 if ($raw = $this->_od_getFileRaw($path)) {
1517 $stat = $this->_od_parseRaw($raw);
1518 if ($path === $this->root) {
1519 $stat['expires'] = $this->expires;
1520 }
1521 return $stat;
1522 }
1523
1524 return false;
1525 }
1526
1527 /**
1528 * Return true if path is dir and has at least one childs directory.
1529 *
1530 * @param string $path dir path
1531 *
1532 * @return bool
1533 * @throws elFinderAbortException
1534 * @author Dmitry (dio) Levashov
1535 */
1536 protected function _subdirs($path)
1537 {
1538 list(, $itemId) = $this->_od_splitPath($path);
1539
1540 return (bool)$this->_od_query($itemId, false, false, array(
1541 'query' => array(
1542 'top' => 1,
1543 'select' => 'id',
1544 'filter' => 'folder ne null',
1545 ),
1546 ));
1547 }
1548
1549 /**
1550 * Return object width and height
1551 * Ususaly used for images, but can be realize for video etc...
1552 *
1553 * @param string $path file path
1554 * @param string $mime file mime type
1555 *
1556 * @return string
1557 * @throws elFinderAbortException
1558 * @author Dmitry (dio) Levashov
1559 */
1560 protected function _dimensions($path, $mime)
1561 {
1562 if (strpos($mime, 'image') !== 0) {
1563 return '';
1564 }
1565
1566 //$cache = $this->_od_getFileRaw($path);
1567 if (func_num_args() > 2) {
1568 $args = func_get_arg(2);
1569 } else {
1570 $args = array();
1571 }
1572 if (!empty($args['substitute'])) {
1573 $tmbSize = intval($args['substitute']);
1574 } else {
1575 $tmbSize = null;
1576 }
1577 list(, $itemId) = $this->_od_splitPath($path);
1578 $options = array(
1579 'query' => array(
1580 'select' => 'id,image',
1581 ),
1582 );
1583 if ($tmbSize) {
1584 $tmb = 'c' . $tmbSize . 'x' . $tmbSize;
1585 $options['query']['expand'] = 'thumbnails(select=' . $tmb . ')';
1586 }
1587 $raw = $this->_od_query($itemId, true, false, $options);
1588
1589 if ($raw && property_exists($raw, 'image') && $img = $raw->image) {
1590 if (isset($img->width) && isset($img->height)) {
1591 $ret = array('dim' => $img->width . 'x' . $img->height);
1592 if ($tmbSize) {
1593 $srcSize = explode('x', $ret['dim']);
1594 if (min(($tmbSize / $srcSize[0]), ($tmbSize / $srcSize[1])) < 1) {
1595 if (!empty($raw->thumbnails)) {
1596 $tmbArr = (array)$raw->thumbnails[0];
1597 if (!empty($tmbArr[$tmb]->url)) {
1598 $ret['url'] = $tmbArr[$tmb]->url;
1599 }
1600 }
1601 }
1602 }
1603
1604 return $ret;
1605 }
1606 }
1607
1608 $ret = '';
1609 if ($work = $this->getWorkFile($path)) {
1610 if ($size = @getimagesize($work)) {
1611 $cache['width'] = $size[0];
1612 $cache['height'] = $size[1];
1613 $ret = $size[0] . 'x' . $size[1];
1614 }
1615 }
1616 is_file($work) && @unlink($work);
1617
1618 return $ret;
1619 }
1620
1621 /******************** file/dir content *********************/
1622
1623 /**
1624 * Return files list in directory.
1625 *
1626 * @param string $path dir path
1627 *
1628 * @return array
1629 * @throws elFinderAbortException
1630 * @author Dmitry (dio) Levashov
1631 * @author Cem (DiscoFever)
1632 */
1633 protected function _scandir($path)
1634 {
1635 return isset($this->dirsCache[$path])
1636 ? $this->dirsCache[$path]
1637 : $this->cacheDir($path);
1638 }
1639
1640 /**
1641 * Open file and return file pointer.
1642 *
1643 * @param string $path file path
1644 * @param bool $write open file for writing
1645 *
1646 * @return resource|false
1647 * @author Dmitry (dio) Levashov
1648 **/
1649 protected function _fopen($path, $mode = 'rb')
1650 {
1651 if ($mode === 'rb' || $mode === 'r') {
1652 list(, $itemId) = $this->_od_splitPath($path);
1653 $data = array(
1654 'target' => self::API_URL . $itemId . '/content',
1655 'headers' => array('Authorization: Bearer ' . $this->token->data->access_token),
1656 );
1657
1658 // to support range request
1659 if (func_num_args() > 2) {
1660 $opts = func_get_arg(2);
1661 } else {
1662 $opts = array();
1663 }
1664 if (!empty($opts['httpheaders'])) {
1665 $data['headers'] = array_merge($opts['httpheaders'], $data['headers']);
1666 }
1667
1668 return elFinder::getStreamByUrl($data);
1669 }
1670
1671 return false;
1672 }
1673
1674 /**
1675 * Close opened file.
1676 *
1677 * @param resource $fp file pointer
1678 *
1679 * @return bool
1680 * @author Dmitry (dio) Levashov
1681 **/
1682 protected function _fclose($fp, $path = '')
1683 {
1684 is_resource($fp) && fclose($fp);
1685 if ($path) {
1686 unlink($this->getTempFile($path));
1687 }
1688 }
1689
1690 /******************** file/dir manipulations *************************/
1691
1692 /**
1693 * Create dir and return created dir path or false on failed.
1694 *
1695 * @param string $path parent dir path
1696 * @param string $name new directory name
1697 *
1698 * @return string|bool
1699 * @author Dmitry (dio) Levashov
1700 **/
1701 protected function _mkdir($path, $name)
1702 {
1703 $namePath = $this->_joinPath($path, $name);
1704 list($parentId) = $this->_od_splitPath($namePath);
1705
1706 try {
1707 $properties = array(
1708 'name' => (string)$name,
1709 'folder' => (object)array(),
1710 );
1711
1712 $data = (object)$properties;
1713
1714 $url = self::API_URL . $parentId . '/children';
1715
1716 $curl = $this->_od_prepareCurl($url);
1717
1718 curl_setopt_array($curl, array(
1719 CURLOPT_POST => true,
1720 CURLOPT_POSTFIELDS => json_encode($data),
1721 ));
1722
1723 //create the Folder in the Parent
1724 $result = elFinder::curlExec($curl);
1725 $folder = json_decode($result);
1726
1727 return $this->_joinPath($path, $folder->id);
1728 } catch (Exception $e) {
1729 return $this->setError('OneDrive error: ' . $e->getMessage());
1730 }
1731 }
1732
1733 /**
1734 * Create file and return it's path or false on failed.
1735 *
1736 * @param string $path parent dir path
1737 * @param string $name new file name
1738 *
1739 * @return string|bool
1740 * @author Dmitry (dio) Levashov
1741 **/
1742 protected function _mkfile($path, $name)
1743 {
1744 return $this->_save($this->tmpfile(), $path, $name, array());
1745 }
1746
1747 /**
1748 * Create symlink. FTP driver does not support symlinks.
1749 *
1750 * @param string $target link target
1751 * @param string $path symlink path
1752 *
1753 * @return bool
1754 * @author Dmitry (dio) Levashov
1755 **/
1756 protected function _symlink($target, $path, $name)
1757 {
1758 return false;
1759 }
1760
1761 /**
1762 * Copy file into another file.
1763 *
1764 * @param string $source source file path
1765 * @param string $targetDir target directory path
1766 * @param string $name new file name
1767 *
1768 * @return bool
1769 * @author Dmitry (dio) Levashov
1770 **/
1771 protected function _copy($source, $targetDir, $name)
1772 {
1773 $path = $this->_joinPath($targetDir, $name);
1774
1775 try {
1776 //Set the Parent id
1777 list(, $parentId) = $this->_od_splitPath($targetDir);
1778 list(, $itemId) = $this->_od_splitPath($source);
1779
1780 $url = self::API_URL . $itemId . '/copy';
1781
1782 $properties = array(
1783 'name' => (string)$name,
1784 );
1785 if ($parentId === 'root') {
1786 $properties['parentReference'] = (object)array('path' => '/drive/root:');
1787 } else {
1788 $properties['parentReference'] = (object)array('id' => (string)$parentId);
1789 }
1790 $data = (object)$properties;
1791 $curl = $this->_od_prepareCurl($url);
1792 curl_setopt_array($curl, array(
1793 CURLOPT_POST => true,
1794 CURLOPT_HEADER => true,
1795 CURLOPT_HTTPHEADER => array(
1796 'Content-Type: application/json',
1797 'Authorization: Bearer ' . $this->token->data->access_token,
1798 'Prefer: respond-async',
1799 ),
1800 CURLOPT_POSTFIELDS => json_encode($data),
1801 ));
1802 $result = elFinder::curlExec($curl);
1803
1804 $res = new stdClass();
1805 if (preg_match('/Location: (.+)/', $result, $m)) {
1806 $monUrl = trim($m[1]);
1807 while ($res) {
1808 usleep(200000);
1809 $curl = curl_init($monUrl);
1810 curl_setopt_array($curl, array(
1811 CURLOPT_RETURNTRANSFER => true,
1812 CURLOPT_HTTPHEADER => array(
1813 'Content-Type: application/json',
1814 ),
1815 ));
1816 $res = json_decode(elFinder::curlExec($curl));
1817 if (isset($res->status)) {
1818 if ($res->status === 'completed' || $res->status === 'failed') {
1819 break;
1820 }
1821 } elseif (isset($res->error)) {
1822 return $this->setError('OneDrive error: ' . $res->error->message);
1823 }
1824 }
1825 }
1826
1827 if ($res && isset($res->resourceId)) {
1828 if (isset($res->folder) && isset($this->sessionCache['subdirs'])) {
1829 $this->sessionCache['subdirs'][$targetDir] = true;
1830 }
1831
1832 return $this->_joinPath($targetDir, $res->resourceId);
1833 }
1834
1835 return false;
1836 } catch (Exception $e) {
1837 return $this->setError('OneDrive error: ' . $e->getMessage());
1838 }
1839
1840 return true;
1841 }
1842
1843 /**
1844 * Move file into another parent dir.
1845 * Return new file path or false.
1846 *
1847 * @param string $source source file path
1848 * @param $targetDir
1849 * @param string $name file name
1850 *
1851 * @return string|bool
1852 * @author Dmitry (dio) Levashov
1853 */
1854 protected function _move($source, $targetDir, $name)
1855 {
1856 try {
1857 list(, $targetParentId) = $this->_od_splitPath($targetDir);
1858 list($sourceParentId, $itemId, $srcParent) = $this->_od_splitPath($source);
1859
1860 $properties = array(
1861 'name' => (string)$name,
1862 );
1863 if ($targetParentId !== $sourceParentId) {
1864 $properties['parentReference'] = (object)array('id' => (string)$targetParentId);
1865 }
1866
1867 $url = self::API_URL . $itemId;
1868 $data = (object)$properties;
1869
1870 $curl = $this->_od_prepareCurl($url);
1871
1872 curl_setopt_array($curl, array(
1873 CURLOPT_CUSTOMREQUEST => 'PATCH',
1874 CURLOPT_POSTFIELDS => json_encode($data),
1875 ));
1876
1877 $result = json_decode(elFinder::curlExec($curl));
1878 if ($result && isset($result->id)) {
1879 return $targetDir . '/' . $result->id;
1880 } else {
1881 return false;
1882 }
1883 } catch (Exception $e) {
1884 return $this->setError('OneDrive error: ' . $e->getMessage());
1885 }
1886
1887 return false;
1888 }
1889
1890 /**
1891 * Remove file.
1892 *
1893 * @param string $path file path
1894 *
1895 * @return bool
1896 * @author Dmitry (dio) Levashov
1897 **/
1898 protected function _unlink($path)
1899 {
1900 $stat = $this->stat($path);
1901 try {
1902 list(, $itemId) = $this->_od_splitPath($path);
1903
1904 $url = self::API_URL . $itemId;
1905
1906 $curl = $this->_od_prepareCurl($url);
1907 curl_setopt_array($curl, array(
1908 CURLOPT_CUSTOMREQUEST => 'DELETE',
1909 ));
1910
1911 //unlink or delete File or Folder in the Parent
1912 $result = elFinder::curlExec($curl);
1913 } catch (Exception $e) {
1914 return $this->setError('OneDrive error: ' . $e->getMessage());
1915 }
1916
1917 return true;
1918 }
1919
1920 /**
1921 * Remove dir.
1922 *
1923 * @param string $path dir path
1924 *
1925 * @return bool
1926 * @author Dmitry (dio) Levashov
1927 **/
1928 protected function _rmdir($path)
1929 {
1930 return $this->_unlink($path);
1931 }
1932
1933 /**
1934 * Create new file and write into it from file pointer.
1935 * Return new file path or false on error.
1936 *
1937 * @param resource $fp file pointer
1938 * @param $path
1939 * @param string $name file name
1940 * @param array $stat file stat (required by some virtual fs)
1941 *
1942 * @return bool|string
1943 * @author Dmitry (dio) Levashov
1944 */
1945 protected function _save($fp, $path, $name, $stat)
1946 {
1947 $itemId = '';
1948 $size = null;
1949 if ($name === '') {
1950 list($parentId, $itemId, $parent) = $this->_od_splitPath($path);
1951 } else {
1952 if ($stat) {
1953 if (isset($stat['name'])) {
1954 $name = $stat['name'];
1955 }
1956 if (isset($stat['rev']) && strpos($stat['hash'], $this->id) === 0) {
1957 $itemId = $stat['rev'];
1958 }
1959 }
1960 list(, $parentId) = $this->_od_splitPath($path);
1961 $parent = $path;
1962 }
1963
1964 if ($stat && isset($stat['size'])) {
1965 $size = $stat['size'];
1966 } else {
1967 $stats = fstat($fp);
1968 if (isset($stats[7])) {
1969 $size = $stats[7];
1970 }
1971 }
1972
1973 if ($size > 4194304) {
1974 return $this->_od_uploadSession($fp, $size, $name, $itemId, $parent, $parentId);
1975 }
1976
1977 try {
1978 // for unseekable file pointer
1979 if (!elFinder::isSeekableStream($fp)) {
1980 if ($tfp = tmpfile()) {
1981 if (stream_copy_to_stream($fp, $tfp, $size? $size : -1) !== false) {
1982 rewind($tfp);
1983 $fp = $tfp;
1984 }
1985 }
1986 }
1987
1988 //Create or Update a file
1989 if ($itemId === '') {
1990 $url = self::API_URL . $parentId . ':/' . rawurlencode($name) . ':/content';
1991 } else {
1992 $url = self::API_URL . $itemId . '/content';
1993 }
1994 $curl = $this->_od_prepareCurl();
1995
1996 $options = array(
1997 CURLOPT_URL => $url,
1998 CURLOPT_PUT => true,
1999 CURLOPT_INFILE => $fp,
2000 );
2001 // Size
2002 if ($size !== null) {
2003 $options[CURLOPT_INFILESIZE] = $size;
2004 }
2005
2006 curl_setopt_array($curl, $options);
2007
2008 //create or update File in the Target
2009 $file = json_decode(elFinder::curlExec($curl));
2010
2011 return $this->_joinPath($parent, $file->id);
2012 } catch (Exception $e) {
2013 return $this->setError('OneDrive error: ' . $e->getMessage());
2014 }
2015 }
2016
2017 /**
2018 * Get file contents.
2019 *
2020 * @param string $path file path
2021 *
2022 * @return string|false
2023 * @author Dmitry (dio) Levashov
2024 **/
2025 protected function _getContents($path)
2026 {
2027 $contents = '';
2028
2029 try {
2030 list(, $itemId) = $this->_od_splitPath($path);
2031 $url = self::API_URL . $itemId . '/content';
2032 $contents = $this->_od_createCurl($url, $contents = true);
2033 } catch (Exception $e) {
2034 return $this->setError('OneDrive error: ' . $e->getMessage());
2035 }
2036
2037 return $contents;
2038 }
2039
2040 /**
2041 * Write a string to a file.
2042 *
2043 * @param string $path file path
2044 * @param string $content new file content
2045 *
2046 * @return bool
2047 * @author Dmitry (dio) Levashov
2048 **/
2049 protected function _filePutContents($path, $content)
2050 {
2051 $res = false;
2052
2053 if ($local = $this->getTempFile($path)) {
2054 if (file_put_contents($local, $content, LOCK_EX) !== false
2055 && ($fp = fopen($local, 'rb'))) {
2056 clearstatcache();
2057 $res = $this->_save($fp, $path, '', array());
2058 fclose($fp);
2059 }
2060 file_exists($local) && unlink($local);
2061 }
2062
2063 return $res;
2064 }
2065
2066 /**
2067 * Detect available archivers.
2068 **/
2069 protected function _checkArchivers()
2070 {
2071 // die('Not yet implemented. (_checkArchivers)');
2072 return array();
2073 }
2074
2075 /**
2076 * chmod implementation.
2077 *
2078 * @return bool
2079 **/
2080 protected function _chmod($path, $mode)
2081 {
2082 return false;
2083 }
2084
2085 /**
2086 * Unpack archive.
2087 *
2088 * @param string $path archive path
2089 * @param array $arc archiver command and arguments (same as in $this->archivers)
2090 *
2091 * @return void
2092 * @author Dmitry (dio) Levashov
2093 * @author Alexey Sukhotin
2094 */
2095 protected function _unpack($path, $arc)
2096 {
2097 die('Not yet implemented. (_unpack)');
2098 //return false;
2099 }
2100
2101 /**
2102 * Extract files from archive.
2103 *
2104 * @param string $path archive path
2105 * @param array $arc archiver command and arguments (same as in $this->archivers)
2106 *
2107 * @return void
2108 * @author Dmitry (dio) Levashov,
2109 * @author Alexey Sukhotin
2110 */
2111 protected function _extract($path, $arc)
2112 {
2113 die('Not yet implemented. (_extract)');
2114 }
2115
2116 /**
2117 * Create archive and return its path.
2118 *
2119 * @param string $dir target dir
2120 * @param array $files files names list
2121 * @param string $name archive name
2122 * @param array $arc archiver options
2123 *
2124 * @return string|bool
2125 * @author Dmitry (dio) Levashov,
2126 * @author Alexey Sukhotin
2127 **/
2128 protected function _archive($dir, $files, $name, $arc)
2129 {
2130 die('Not yet implemented. (_archive)');
2131 }
2132 } // END class
2133