elFinderVolumeGoogleDrive.class.php 67 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114
  1. <?php
  2. /**
  3. * Simple elFinder driver for GoogleDrive
  4. * google-api-php-client-2.x or above.
  5. *
  6. * @author Dmitry (dio) Levashov
  7. * @author Cem (discofever)
  8. **/
  9. class elFinderVolumeGoogleDrive extends elFinderVolumeDriver
  10. {
  11. /**
  12. * Driver id
  13. * Must be started from letter and contains [a-z0-9]
  14. * Used as part of volume id.
  15. *
  16. * @var string
  17. **/
  18. protected $driverId = 'gd';
  19. /**
  20. * Google API client object.
  21. *
  22. * @var object
  23. **/
  24. protected $client = null;
  25. /**
  26. * GoogleDrive service object.
  27. *
  28. * @var object
  29. **/
  30. protected $service = null;
  31. /**
  32. * Cache of parents of each directories.
  33. *
  34. * @var array
  35. */
  36. protected $parents = [];
  37. /**
  38. * Cache of chiled directories of each directories.
  39. *
  40. * @var array
  41. */
  42. protected $directories = null;
  43. /**
  44. * Cache of itemID => name of each items.
  45. *
  46. * @var array
  47. */
  48. protected $names = [];
  49. /**
  50. * MIME tyoe of directory.
  51. *
  52. * @var string
  53. */
  54. const DIRMIME = 'application/vnd.google-apps.folder';
  55. /**
  56. * Fetch fields for list.
  57. *
  58. * @var string
  59. */
  60. const FETCHFIELDS_LIST = 'files(id,name,mimeType,modifiedTime,parents,permissions,size,imageMediaMetadata(height,width),thumbnailLink,webContentLink,webViewLink),nextPageToken';
  61. /**
  62. * Fetch fields for get.
  63. *
  64. * @var string
  65. */
  66. const FETCHFIELDS_GET = 'id,name,mimeType,modifiedTime,parents,permissions,size,imageMediaMetadata(height,width),thumbnailLink,webContentLink,webViewLink';
  67. /**
  68. * Directory for tmp files
  69. * If not set driver will try to use tmbDir as tmpDir.
  70. *
  71. * @var string
  72. **/
  73. protected $tmp = '';
  74. /**
  75. * Net mount key.
  76. *
  77. * @var string
  78. **/
  79. public $netMountKey = '';
  80. /**
  81. * Current token expires
  82. *
  83. * @var integer
  84. **/
  85. private $expires;
  86. /**
  87. * Constructor
  88. * Extend options with required fields.
  89. *
  90. * @author Dmitry (dio) Levashov
  91. * @author Cem (DiscoFever)
  92. **/
  93. public function __construct()
  94. {
  95. $opts = [
  96. 'client_id' => '',
  97. 'client_secret' => '',
  98. 'access_token' => [],
  99. 'refresh_token' => '',
  100. 'serviceAccountConfigFile' => '',
  101. 'root' => 'My Drive',
  102. 'gdAlias' => '%s@GDrive',
  103. 'googleApiClient' => '',
  104. 'path' => '/',
  105. 'tmbPath' => '',
  106. 'separator' => '/',
  107. 'useGoogleTmb' => true,
  108. 'acceptedName' => '#^[^/\\?*:|"<>]*[^./\\?*:|"<>]$#',
  109. 'rootCssClass' => 'elfinder-navbar-root-googledrive',
  110. 'publishPermission' => [
  111. 'type' => 'anyone',
  112. 'role' => 'reader',
  113. 'withLink' => true,
  114. ],
  115. 'appsExportMap' => [
  116. 'application/vnd.google-apps.document' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
  117. 'application/vnd.google-apps.spreadsheet' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
  118. 'application/vnd.google-apps.drawing' => 'application/pdf',
  119. 'application/vnd.google-apps.presentation' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
  120. 'application/vnd.google-apps.script' => 'application/vnd.google-apps.script+json',
  121. 'default' => 'application/pdf',
  122. ],
  123. ];
  124. $this->options = array_merge($this->options, $opts);
  125. $this->options['mimeDetect'] = 'internal';
  126. }
  127. /*********************************************************************/
  128. /* ORIGINAL FUNCTIONS */
  129. /*********************************************************************/
  130. /**
  131. * Get Parent ID, Item ID, Parent Path as an array from path.
  132. *
  133. * @param string $path
  134. *
  135. * @return array
  136. */
  137. protected function _gd_splitPath($path)
  138. {
  139. $path = trim($path, '/');
  140. $pid = '';
  141. if ($path === '') {
  142. $id = 'root';
  143. $parent = '';
  144. } else {
  145. $paths = explode('/', $path);
  146. $id = array_pop($paths);
  147. if ($paths) {
  148. $parent = '/' . implode('/', $paths);
  149. $pid = array_pop($paths);
  150. } else {
  151. $rootid = ($this->root === '/') ? 'root' : trim($this->root, '/');
  152. if ($id === $rootid) {
  153. $parent = '';
  154. } else {
  155. $parent = $this->root;
  156. $pid = $rootid;
  157. }
  158. }
  159. }
  160. return array($pid, $id, $parent);
  161. }
  162. /**
  163. * Drive query and fetchAll.
  164. *
  165. * @param string $sql
  166. *
  167. * @return bool|array
  168. */
  169. private function _gd_query($opts)
  170. {
  171. $result = [];
  172. $pageToken = null;
  173. $parameters = [
  174. 'fields' => self::FETCHFIELDS_LIST,
  175. 'pageSize' => 1000,
  176. 'spaces' => 'drive',
  177. ];
  178. if (is_array($opts)) {
  179. $parameters = array_merge($parameters, $opts);
  180. }
  181. do {
  182. try {
  183. if ($pageToken) {
  184. $parameters['pageToken'] = $pageToken;
  185. }
  186. $files = $this->service->files->listFiles($parameters);
  187. $result = array_merge($result, $files->getFiles());
  188. $pageToken = $files->getNextPageToken();
  189. } catch (Exception $e) {
  190. $pageToken = null;
  191. }
  192. } while ($pageToken);
  193. return $result;
  194. }
  195. /**
  196. * Get dat(googledrive metadata) from GoogleDrive.
  197. *
  198. * @param string $path
  199. *
  200. * @return array googledrive metadata
  201. */
  202. private function _gd_getFile($path, $fields = '')
  203. {
  204. list(, $itemId) = $this->_gd_splitPath($path);
  205. if (!$fields) {
  206. $fields = self::FETCHFIELDS_GET;
  207. }
  208. try {
  209. $file = $this->service->files->get($itemId, ['fields' => $fields]);
  210. if ($file instanceof Google_Service_Drive_DriveFile) {
  211. return $file;
  212. } else {
  213. return [];
  214. }
  215. } catch (Exception $e) {
  216. return [];
  217. }
  218. }
  219. /**
  220. * Parse line from googledrive metadata output and return file stat (array).
  221. *
  222. * @param array $raw line from ftp_rawlist() output
  223. *
  224. * @return array
  225. * @author Dmitry Levashov
  226. **/
  227. protected function _gd_parseRaw($raw)
  228. {
  229. $stat = [];
  230. $stat['iid'] = isset($raw['id']) ? $raw['id'] : 'root';
  231. $stat['name'] = isset($raw['name']) ? $raw['name'] : '';
  232. if (isset($raw['modifiedTime'])) {
  233. $stat['ts'] = strtotime($raw['modifiedTime']);
  234. }
  235. if ($raw['mimeType'] === self::DIRMIME) {
  236. $stat['mime'] = 'directory';
  237. $stat['size'] = 0;
  238. } else {
  239. $stat['mime'] = $raw['mimeType'] == 'image/bmp' ? 'image/x-ms-bmp' : $raw['mimeType'];
  240. $stat['size'] = (int)$raw['size'];
  241. if ($size = $raw->getImageMediaMetadata()) {
  242. $stat['width'] = $size['width'];
  243. $stat['height'] = $size['height'];
  244. }
  245. $published = $this->_gd_isPublished($raw);
  246. if ($this->options['useGoogleTmb']) {
  247. if (isset($raw['thumbnailLink'])) {
  248. if ($published) {
  249. $stat['tmb'] = 'drive.google.com/thumbnail?authuser=0&sz=s' . $this->options['tmbSize'] . '&id=' . $raw['id'];
  250. } else {
  251. $stat['tmb'] = substr($raw['thumbnailLink'], 8); // remove "https://"
  252. }
  253. } else {
  254. $stat['tmb'] = '';
  255. }
  256. }
  257. if ($published) {
  258. $stat['url'] = $this->_gd_getLink($raw);
  259. } elseif (!$this->disabledGetUrl) {
  260. $stat['url'] = '1';
  261. }
  262. }
  263. return $stat;
  264. }
  265. /**
  266. * Get dat(googledrive metadata) from GoogleDrive.
  267. *
  268. * @param string $path
  269. *
  270. * @return array googledrive metadata
  271. */
  272. private function _gd_getNameByPath($path)
  273. {
  274. list(, $itemId) = $this->_gd_splitPath($path);
  275. if (!$this->names) {
  276. $this->_gd_getDirectoryData();
  277. }
  278. return isset($this->names[$itemId]) ? $this->names[$itemId] : '';
  279. }
  280. /**
  281. * Make cache of $parents, $names and $directories.
  282. *
  283. * @param bool $usecache
  284. */
  285. protected function _gd_getDirectoryData($usecache = true)
  286. {
  287. if ($usecache) {
  288. $cache = $this->session->get($this->id . $this->netMountKey, []);
  289. if ($cache) {
  290. $this->parents = $cache['parents'];
  291. $this->names = $cache['names'];
  292. $this->directories = $cache['directories'];
  293. return;
  294. }
  295. }
  296. $root = '';
  297. if ($this->root === '/') {
  298. // get root id
  299. if ($res = $this->_gd_getFile('/', 'id')) {
  300. $root = $res->getId();
  301. }
  302. }
  303. $data = [];
  304. $opts = [
  305. 'fields' => 'files(id, name, parents)',
  306. 'q' => sprintf('trashed=false and mimeType="%s"', self::DIRMIME),
  307. ];
  308. $res = $this->_gd_query($opts);
  309. foreach ($res as $raw) {
  310. if ($parents = $raw->getParents()) {
  311. $id = $raw->getId();
  312. $this->parents[$id] = $parents;
  313. $this->names[$id] = $raw->getName();
  314. foreach ($parents as $p) {
  315. if (isset($data[$p])) {
  316. $data[$p][] = $id;
  317. } else {
  318. $data[$p] = [$id];
  319. }
  320. }
  321. }
  322. }
  323. if ($root && isset($data[$root])) {
  324. $data['root'] = $data[$root];
  325. }
  326. $this->directories = $data;
  327. $this->session->set($this->id . $this->netMountKey, [
  328. 'parents' => $this->parents,
  329. 'names' => $this->names,
  330. 'directories' => $this->directories,
  331. ]);
  332. }
  333. /**
  334. * Get descendants directories.
  335. *
  336. * @param string $itemId
  337. *
  338. * @return array
  339. */
  340. protected function _gd_getDirectories($itemId)
  341. {
  342. $ret = [];
  343. if ($this->directories === null) {
  344. $this->_gd_getDirectoryData();
  345. }
  346. $data = $this->directories;
  347. if (isset($data[$itemId])) {
  348. $ret = $data[$itemId];
  349. foreach ($data[$itemId] as $cid) {
  350. $ret = array_merge($ret, $this->_gd_getDirectories($cid));
  351. }
  352. }
  353. return $ret;
  354. }
  355. /**
  356. * Get ID based path from item ID.
  357. *
  358. * @param string $id
  359. *
  360. * @return array
  361. */
  362. protected function _gd_getMountPaths($id)
  363. {
  364. $root = false;
  365. if ($this->directories === null) {
  366. $this->_gd_getDirectoryData();
  367. }
  368. list($pid) = explode('/', $id, 2);
  369. $path = $id;
  370. if ('/' . $pid === $this->root) {
  371. $root = true;
  372. } elseif (!isset($this->parents[$pid])) {
  373. $root = true;
  374. $path = ltrim(substr($path, strlen($pid)), '/');
  375. }
  376. $res = [];
  377. if ($root) {
  378. if ($this->root === '/' || strpos('/' . $path, $this->root) === 0) {
  379. $res = [(strpos($path, '/') === false) ? '/' : ('/' . $path)];
  380. }
  381. } else {
  382. foreach ($this->parents[$pid] as $p) {
  383. $_p = $p . '/' . $path;
  384. $res = array_merge($res, $this->_gd_getMountPaths($_p));
  385. }
  386. }
  387. return $res;
  388. }
  389. /**
  390. * Return is published.
  391. *
  392. * @param object $file
  393. *
  394. * @return bool
  395. */
  396. protected function _gd_isPublished($file)
  397. {
  398. $res = false;
  399. $pType = $this->options['publishPermission']['type'];
  400. $pRole = $this->options['publishPermission']['role'];
  401. if ($permissions = $file->getPermissions()) {
  402. foreach ($permissions as $permission) {
  403. if ($permission->type === $pType && $permission->role === $pRole) {
  404. $res = true;
  405. break;
  406. }
  407. }
  408. }
  409. return $res;
  410. }
  411. /**
  412. * return item URL link.
  413. *
  414. * @param object $file
  415. *
  416. * @return string
  417. */
  418. protected function _gd_getLink($file)
  419. {
  420. if (strpos($file->mimeType, 'application/vnd.google-apps.') !== 0) {
  421. if ($url = $file->getWebContentLink()) {
  422. return str_replace('export=download', 'export=media', $url);
  423. }
  424. }
  425. if ($url = $file->getWebViewLink()) {
  426. return $url;
  427. }
  428. return '';
  429. }
  430. /**
  431. * Get download url.
  432. *
  433. * @param Google_Service_Drive_DriveFile $file
  434. *
  435. * @return string|false
  436. */
  437. protected function _gd_getDownloadUrl($file)
  438. {
  439. if (strpos($file->mimeType, 'application/vnd.google-apps.') !== 0) {
  440. return 'https://www.googleapis.com/drive/v3/files/' . $file->getId() . '?alt=media';
  441. } else {
  442. $mimeMap = $this->options['appsExportMap'];
  443. if (isset($mimeMap[$file->getMimeType()])) {
  444. $mime = $mimeMap[$file->getMimeType()];
  445. } else {
  446. $mime = $mimeMap['default'];
  447. }
  448. $mime = rawurlencode($mime);
  449. return 'https://www.googleapis.com/drive/v3/files/' . $file->getId() . '/export?mimeType=' . $mime;
  450. }
  451. return false;
  452. }
  453. /**
  454. * Get thumbnail from GoogleDrive.com.
  455. *
  456. * @param string $path
  457. *
  458. * @return string | boolean
  459. */
  460. protected function _gd_getThumbnail($path)
  461. {
  462. list(, $itemId) = $this->_gd_splitPath($path);
  463. try {
  464. $contents = $this->service->files->get($itemId, [
  465. 'alt' => 'media',
  466. ]);
  467. $contents = $contents->getBody()->detach();
  468. rewind($contents);
  469. return $contents;
  470. } catch (Exception $e) {
  471. return false;
  472. }
  473. }
  474. /**
  475. * Publish permissions specified path item.
  476. *
  477. * @param string $path
  478. *
  479. * @return bool
  480. */
  481. protected function _gd_publish($path)
  482. {
  483. if ($file = $this->_gd_getFile($path)) {
  484. if ($this->_gd_isPublished($file)) {
  485. return true;
  486. }
  487. try {
  488. if ($this->service->permissions->create($file->getId(), new \Google_Service_Drive_Permission($this->options['publishPermission']))) {
  489. return true;
  490. }
  491. } catch (Exception $e) {
  492. return false;
  493. }
  494. }
  495. return false;
  496. }
  497. /**
  498. * unPublish permissions specified path.
  499. *
  500. * @param string $path
  501. *
  502. * @return bool
  503. */
  504. protected function _gd_unPublish($path)
  505. {
  506. if ($file = $this->_gd_getFile($path)) {
  507. if (!$this->_gd_isPublished($file)) {
  508. return true;
  509. }
  510. $permissions = $file->getPermissions();
  511. $pType = $this->options['publishPermission']['type'];
  512. $pRole = $this->options['publishPermission']['role'];
  513. try {
  514. foreach ($permissions as $permission) {
  515. if ($permission->type === $pType && $permission->role === $pRole) {
  516. $this->service->permissions->delete($file->getId(), $permission->getId());
  517. return true;
  518. break;
  519. }
  520. }
  521. } catch (Exception $e) {
  522. return false;
  523. }
  524. }
  525. return false;
  526. }
  527. /**
  528. * Read file chunk.
  529. *
  530. * @param resource $handle
  531. * @param int $chunkSize
  532. *
  533. * @return string
  534. */
  535. protected function _gd_readFileChunk($handle, $chunkSize)
  536. {
  537. $byteCount = 0;
  538. $giantChunk = '';
  539. while (!feof($handle)) {
  540. // fread will never return more than 8192 bytes if the stream is read buffered and it does not represent a plain file
  541. $chunk = fread($handle, 8192);
  542. $byteCount += strlen($chunk);
  543. $giantChunk .= $chunk;
  544. if ($byteCount >= $chunkSize) {
  545. return $giantChunk;
  546. }
  547. }
  548. return $giantChunk;
  549. }
  550. /*********************************************************************/
  551. /* EXTENDED FUNCTIONS */
  552. /*********************************************************************/
  553. /**
  554. * Prepare
  555. * Call from elFinder::netmout() before volume->mount().
  556. *
  557. * @return array
  558. * @author Naoki Sawada
  559. * @author Raja Sharma updating for GoogleDrive
  560. **/
  561. public function netmountPrepare($options)
  562. {
  563. if (empty($options['client_id']) && defined('ELFINDER_GOOGLEDRIVE_CLIENTID')) {
  564. $options['client_id'] = ELFINDER_GOOGLEDRIVE_CLIENTID;
  565. }
  566. if (empty($options['client_secret']) && defined('ELFINDER_GOOGLEDRIVE_CLIENTSECRET')) {
  567. $options['client_secret'] = ELFINDER_GOOGLEDRIVE_CLIENTSECRET;
  568. }
  569. if (empty($options['googleApiClient']) && defined('ELFINDER_GOOGLEDRIVE_GOOGLEAPICLIENT')) {
  570. $options['googleApiClient'] = ELFINDER_GOOGLEDRIVE_GOOGLEAPICLIENT;
  571. include_once $options['googleApiClient'];
  572. }
  573. if (!isset($options['pass'])) {
  574. $options['pass'] = '';
  575. }
  576. try {
  577. $client = new \Google_Client();
  578. $client->setClientId($options['client_id']);
  579. $client->setClientSecret($options['client_secret']);
  580. if ($options['pass'] === 'reauth') {
  581. $options['pass'] = '';
  582. $this->session->set('GoogleDriveAuthParams', [])->set('GoogleDriveTokens', []);
  583. } elseif ($options['pass'] === 'googledrive') {
  584. $options['pass'] = '';
  585. }
  586. $options = array_merge($this->session->get('GoogleDriveAuthParams', []), $options);
  587. if (!isset($options['access_token'])) {
  588. $options['access_token'] = $this->session->get('GoogleDriveTokens', []);
  589. $this->session->remove('GoogleDriveTokens');
  590. }
  591. $aToken = $options['access_token'];
  592. $rootObj = $service = null;
  593. if ($aToken) {
  594. try {
  595. $client->setAccessToken($aToken);
  596. if ($client->isAccessTokenExpired()) {
  597. $aToken = array_merge($aToken, $client->fetchAccessTokenWithRefreshToken());
  598. $client->setAccessToken($aToken);
  599. }
  600. $service = new \Google_Service_Drive($client);
  601. $rootObj = $service->files->get('root');
  602. $options['access_token'] = $aToken;
  603. $this->session->set('GoogleDriveAuthParams', $options);
  604. } catch (Exception $e) {
  605. $aToken = [];
  606. $options['access_token'] = [];
  607. if ($options['user'] !== 'init') {
  608. $this->session->set('GoogleDriveAuthParams', $options);
  609. return ['exit' => true, 'error' => elFinder::ERROR_REAUTH_REQUIRE];
  610. }
  611. }
  612. }
  613. if (isset($options['user']) && $options['user'] === 'init') {
  614. if (empty($options['url'])) {
  615. $options['url'] = elFinder::getConnectorUrl();
  616. }
  617. $callback = $options['url']
  618. . '?cmd=netmount&protocol=googledrive&host=1';
  619. $client->setRedirectUri($callback);
  620. if (!$aToken && empty($_GET['code'])) {
  621. $client->setScopes([Google_Service_Drive::DRIVE]);
  622. if (!empty($options['offline'])) {
  623. $client->setApprovalPrompt('force');
  624. $client->setAccessType('offline');
  625. }
  626. $url = $client->createAuthUrl();
  627. $html = '<input id="elf-volumedriver-googledrive-host-btn" class="ui-button ui-widget ui-state-default ui-corner-all ui-button-text-only" value="{msg:btnApprove}" type="button" onclick="window.open(\'' . $url . '\')">';
  628. $html .= '<script>
  629. $("#' . $options['id'] . '").elfinder("instance").trigger("netmount", {protocol: "googledrive", mode: "makebtn"});
  630. </script>';
  631. if (empty($options['pass']) && $options['host'] !== '1') {
  632. $options['pass'] = 'return';
  633. $this->session->set('GoogleDriveAuthParams', $options);
  634. return ['exit' => true, 'body' => $html];
  635. } else {
  636. $out = [
  637. 'node' => $options['id'],
  638. 'json' => '{"protocol": "googledrive", "mode": "makebtn", "body" : "' . str_replace($html, '"', '\\"') . '", "error" : "' . elFinder::ERROR_ACCESS_DENIED . '"}',
  639. 'bind' => 'netmount',
  640. ];
  641. return ['exit' => 'callback', 'out' => $out];
  642. }
  643. } else {
  644. if (!empty($_GET['code'])) {
  645. $aToken = $client->fetchAccessTokenWithAuthCode($_GET['code']);
  646. $options['access_token'] = $aToken;
  647. $this->session->set('GoogleDriveTokens', $aToken)->set('GoogleDriveAuthParams', $options);
  648. $out = [
  649. 'node' => $options['id'],
  650. 'json' => '{"protocol": "googledrive", "mode": "done", "reset": 1}',
  651. 'bind' => 'netmount',
  652. ];
  653. return ['exit' => 'callback', 'out' => $out];
  654. }
  655. $path = $options['path'];
  656. if ($path === '/') {
  657. $path = 'root';
  658. }
  659. $folders = [];
  660. foreach ($service->files->listFiles([
  661. 'pageSize' => 1000,
  662. 'q' => sprintf('trashed = false and "%s" in parents and mimeType = "application/vnd.google-apps.folder"', $path),
  663. ]) as $f) {
  664. $folders[$f->getId()] = $f->getName();
  665. }
  666. natcasesort($folders);
  667. if ($options['pass'] === 'folders') {
  668. return ['exit' => true, 'folders' => $folders];
  669. }
  670. $folders = ['root' => $rootObj->getName()] + $folders;
  671. $folders = json_encode($folders);
  672. $expires = empty($aToken['refresh_token']) ? $aToken['created'] + $aToken['expires_in'] - 30 : 0;
  673. $json = '{"protocol": "googledrive", "mode": "done", "folders": ' . $folders . ', "expires": ' . $expires . '}';
  674. $options['pass'] = 'return';
  675. $html = 'Google.com';
  676. $html .= '<script>
  677. $("#' . $options['id'] . '").elfinder("instance").trigger("netmount", ' . $json . ');
  678. </script>';
  679. $this->session->set('GoogleDriveAuthParams', $options);
  680. return ['exit' => true, 'body' => $html];
  681. }
  682. }
  683. } catch (Exception $e) {
  684. $this->session->remove('GoogleDriveAuthParams')->remove('GoogleDriveTokens');
  685. if (empty($options['pass'])) {
  686. return ['exit' => true, 'body' => '{msg:' . elFinder::ERROR_ACCESS_DENIED . '}' . ' ' . $e->getMessage()];
  687. } else {
  688. return ['exit' => true, 'error' => [elFinder::ERROR_ACCESS_DENIED, $e->getMessage()]];
  689. }
  690. }
  691. if (!$aToken) {
  692. return ['exit' => true, 'error' => elFinder::ERROR_REAUTH_REQUIRE];
  693. }
  694. if ($options['path'] === '/') {
  695. $options['path'] = 'root';
  696. }
  697. try {
  698. $file = $service->files->get($options['path']);
  699. $options['alias'] = sprintf($this->options['gdAlias'], $file->getName());
  700. } catch (Google_Service_Exception $e) {
  701. $err = json_decode($e->getMessage(), true);
  702. if (isset($err['error']) && $err['error']['code'] == 404) {
  703. return ['exit' => true, 'error' => [elFinder::ERROR_TRGDIR_NOT_FOUND, $options['path']]];
  704. } else {
  705. return ['exit' => true, 'error' => $e->getMessage()];
  706. }
  707. } catch (Exception $e) {
  708. return ['exit' => true, 'error' => $e->getMessage()];
  709. }
  710. foreach (['host', 'user', 'pass', 'id', 'offline'] as $key) {
  711. unset($options[$key]);
  712. }
  713. return $options;
  714. }
  715. /**
  716. * process of on netunmount
  717. * Drop `googledrive` & rm thumbs.
  718. *
  719. * @param $netVolumes
  720. * @param $key
  721. *
  722. * @return bool
  723. */
  724. public function netunmount($netVolumes, $key)
  725. {
  726. if (!$this->options['useGoogleTmb']) {
  727. if ($tmbs = glob(rtrim($this->options['tmbPath'], '\\/') . DIRECTORY_SEPARATOR . $this->netMountKey . '*.png')) {
  728. foreach ($tmbs as $file) {
  729. unlink($file);
  730. }
  731. }
  732. }
  733. $this->session->remove($this->id . $this->netMountKey);
  734. return true;
  735. }
  736. /**
  737. * Return fileinfo based on filename
  738. * For item ID based path file system
  739. * Please override if needed on each drivers.
  740. *
  741. * @param string $path file cache
  742. *
  743. * @return array
  744. */
  745. protected function isNameExists($path)
  746. {
  747. list($parentId, $name) = $this->_gd_splitPath($path);
  748. $opts = [
  749. 'q' => sprintf('trashed=false and "%s" in parents and name="%s"', $parentId, $name),
  750. 'fields' => self::FETCHFIELDS_LIST,
  751. ];
  752. $srcFile = $this->_gd_query($opts);
  753. return empty($srcFile) ? false : $this->_gd_parseRaw($srcFile[0]);
  754. }
  755. /*********************************************************************/
  756. /* INIT AND CONFIGURE */
  757. /*********************************************************************/
  758. /**
  759. * Prepare FTP connection
  760. * Connect to remote server and check if credentials are correct, if so, store the connection id in $ftp_conn.
  761. *
  762. * @return bool
  763. * @author Dmitry (dio) Levashov
  764. * @author Cem (DiscoFever)
  765. **/
  766. protected function init()
  767. {
  768. $serviceAccountConfig = '';
  769. if (empty($this->options['serviceAccountConfigFile'])) {
  770. if (empty($options['client_id'])) {
  771. if (defined('ELFINDER_GOOGLEDRIVE_CLIENTID') && ELFINDER_GOOGLEDRIVE_CLIENTID) {
  772. $this->options['client_id'] = ELFINDER_GOOGLEDRIVE_CLIENTID;
  773. } else {
  774. return $this->setError('Required option "client_id" is undefined.');
  775. }
  776. }
  777. if (empty($options['client_secret'])) {
  778. if (defined('ELFINDER_GOOGLEDRIVE_CLIENTSECRET') && ELFINDER_GOOGLEDRIVE_CLIENTSECRET) {
  779. $this->options['client_secret'] = ELFINDER_GOOGLEDRIVE_CLIENTSECRET;
  780. } else {
  781. return $this->setError('Required option "client_secret" is undefined.');
  782. }
  783. }
  784. if (!$this->options['access_token'] && !$this->options['refresh_token']) {
  785. return $this->setError('Required option "access_token" or "refresh_token" is undefined.');
  786. }
  787. } else {
  788. if (!is_readable($this->options['serviceAccountConfigFile'])) {
  789. return $this->setError('Option "serviceAccountConfigFile" file is not readable.');
  790. }
  791. $serviceAccountConfig = $this->options['serviceAccountConfigFile'];
  792. }
  793. try {
  794. if (!$serviceAccountConfig) {
  795. $aTokenFile = '';
  796. if ($this->options['refresh_token']) {
  797. // permanent mount
  798. $aToken = $this->options['refresh_token'];
  799. $this->options['access_token'] = '';
  800. $tmp = elFinder::getStaticVar('commonTempPath');
  801. if (!$tmp) {
  802. $tmp = $this->getTempPath();
  803. }
  804. if ($tmp) {
  805. $aTokenFile = $tmp . DIRECTORY_SEPARATOR . md5($this->options['client_id'] . $this->options['refresh_token']) . '.gtoken';
  806. if (is_file($aTokenFile)) {
  807. $this->options['access_token'] = json_decode(file_get_contents($aTokenFile), true);
  808. }
  809. }
  810. } else {
  811. // make net mount key for network mount
  812. if (is_array($this->options['access_token'])) {
  813. $aToken = !empty($this->options['access_token']['refresh_token'])
  814. ? $this->options['access_token']['refresh_token']
  815. : $this->options['access_token']['access_token'];
  816. } else {
  817. return $this->setError('Required option "access_token" is not Array or empty.');
  818. }
  819. }
  820. }
  821. $errors = [];
  822. if (!$this->service) {
  823. if (($this->options['googleApiClient'] || defined('ELFINDER_GOOGLEDRIVE_GOOGLEAPICLIENT')) && !class_exists('Google_Client')) {
  824. include_once $this->options['googleApiClient'] ? $this->options['googleApiClient'] : ELFINDER_GOOGLEDRIVE_GOOGLEAPICLIENT;
  825. }
  826. if (!class_exists('Google_Client')) {
  827. return $this->setError('Class Google_Client not found.');
  828. }
  829. $this->client = new \Google_Client();
  830. $client = $this->client;
  831. if (!$serviceAccountConfig) {
  832. if ($this->options['access_token']) {
  833. $client->setAccessToken($this->options['access_token']);
  834. $access_token = $this->options['access_token'];
  835. }
  836. if ($client->isAccessTokenExpired()) {
  837. $client->setClientId($this->options['client_id']);
  838. $client->setClientSecret($this->options['client_secret']);
  839. $access_token = $client->fetchAccessTokenWithRefreshToken($this->options['refresh_token'] ?: null);
  840. $client->setAccessToken($access_token);
  841. if ($aTokenFile) {
  842. file_put_contents($aTokenFile, json_encode($access_token));
  843. } else {
  844. $access_token['refresh_token'] = $this->options['access_token']['refresh_token'];
  845. }
  846. if (!empty($this->options['netkey'])) {
  847. elFinder::$instance->updateNetVolumeOption($this->options['netkey'], 'access_token', $access_token);
  848. }
  849. $this->options['access_token'] = $access_token;
  850. }
  851. $this->expires = empty($access_token['refresh_token']) ? $access_token['created'] + $access_token['expires_in'] - 30 : 0;
  852. } else {
  853. $client->setAuthConfigFile($serviceAccountConfig);
  854. $client->setScopes([Google_Service_Drive::DRIVE]);
  855. $aToken = $client->getClientId();
  856. }
  857. $this->service = new \Google_Service_Drive($client);
  858. }
  859. $this->netMountKey = md5($aToken . '-' . $this->options['path']);
  860. } catch (InvalidArgumentException $e) {
  861. $errors[] = $e->getMessage();
  862. } catch (Google_Service_Exception $e) {
  863. $errors[] = $e->getMessage();
  864. }
  865. if (!$this->service) {
  866. $this->session->remove($this->id . $this->netMountKey);
  867. if ($aTokenFile) {
  868. unlink($aTokenFile);
  869. }
  870. $errors[] = 'Google Drive Service could not be loaded.';
  871. return $this->setError($errors);
  872. }
  873. // normalize root path
  874. if ($this->options['path'] == 'root') {
  875. $this->options['path'] = '/';
  876. }
  877. $this->root = $this->options['path'] = $this->_normpath($this->options['path']);
  878. $this->options['root'] == '' ? $this->options['root'] = $this->_gd_getNameByPath('root') : $this->options['root'];
  879. if (empty($this->options['alias'])) {
  880. $this->options['alias'] = ($this->options['path'] === '/') ? $this->options['root'] : sprintf($this->options['gdAlias'], $this->_gd_getNameByPath($this->options['path']));
  881. }
  882. $this->rootName = $this->options['alias'];
  883. if (!empty($this->options['tmpPath'])) {
  884. if ((is_dir($this->options['tmpPath']) || mkdir($this->options['tmpPath'])) && is_writable($this->options['tmpPath'])) {
  885. $this->tmp = $this->options['tmpPath'];
  886. }
  887. }
  888. if (!$this->tmp && ($tmp = elFinder::getStaticVar('commonTempPath'))) {
  889. $this->tmp = $tmp;
  890. }
  891. // This driver dose not support `syncChkAsTs`
  892. $this->options['syncChkAsTs'] = false;
  893. // 'lsPlSleep' minmum 10 sec
  894. $this->options['lsPlSleep'] = max(10, $this->options['lsPlSleep']);
  895. if ($this->options['useGoogleTmb']) {
  896. $this->options['tmbURL'] = 'https://';
  897. $this->options['tmbPath'] = '';
  898. }
  899. // enable command archive
  900. $this->options['useRemoteArchive'] = true;
  901. return true;
  902. }
  903. /**
  904. * Configure after successfull mount.
  905. *
  906. * @author Dmitry (dio) Levashov
  907. **/
  908. protected function configure()
  909. {
  910. parent::configure();
  911. // fallback of $this->tmp
  912. if (!$this->tmp && $this->tmbPathWritable) {
  913. $this->tmp = $this->tmbPath;
  914. }
  915. if ($this->isMyReload()) {
  916. $this->_gd_getDirectoryData(false);
  917. }
  918. }
  919. /*********************************************************************/
  920. /* FS API */
  921. /*********************************************************************/
  922. /**
  923. * Close opened connection.
  924. *
  925. * @author Dmitry (dio) Levashov
  926. **/
  927. public function umount()
  928. {
  929. }
  930. /**
  931. * Cache dir contents.
  932. *
  933. * @param string $path dir path
  934. *
  935. * @return array
  936. * @author Dmitry Levashov
  937. */
  938. protected function cacheDir($path)
  939. {
  940. $this->dirsCache[$path] = [];
  941. $hasDir = false;
  942. list(, $pid) = $this->_gd_splitPath($path);
  943. $opts = [
  944. 'fields' => self::FETCHFIELDS_LIST,
  945. 'q' => sprintf('trashed=false and "%s" in parents', $pid),
  946. ];
  947. $res = $this->_gd_query($opts);
  948. $mountPath = $this->_normpath($path . '/');
  949. if ($res) {
  950. foreach ($res as $raw) {
  951. if ($stat = $this->_gd_parseRaw($raw)) {
  952. $stat = $this->updateCache($mountPath . $raw->id, $stat);
  953. if (empty($stat['hidden']) && $path !== $mountPath . $raw->id) {
  954. if (!$hasDir && $stat['mime'] === 'directory') {
  955. $hasDir = true;
  956. }
  957. $this->dirsCache[$path][] = $mountPath . $raw->id;
  958. }
  959. }
  960. }
  961. }
  962. if (isset($this->sessionCache['subdirs'])) {
  963. $this->sessionCache['subdirs'][$path] = $hasDir;
  964. }
  965. return $this->dirsCache[$path];
  966. }
  967. /**
  968. * Recursive files search.
  969. *
  970. * @param string $path dir path
  971. * @param string $q search string
  972. * @param array $mimes
  973. *
  974. * @return array
  975. * @throws elFinderAbortException
  976. * @author Naoki Sawada
  977. */
  978. protected function doSearch($path, $q, $mimes)
  979. {
  980. if (!empty($this->doSearchCurrentQuery['matchMethod'])) {
  981. // has custom match method use elFinderVolumeDriver::doSearch()
  982. return parent::doSearch($path, $q, $mimes);
  983. }
  984. list(, $itemId) = $this->_gd_splitPath($path);
  985. $path = $this->_normpath($path . '/');
  986. $result = [];
  987. $query = '';
  988. if ($itemId !== 'root') {
  989. $dirs = array_merge([$itemId], $this->_gd_getDirectories($itemId));
  990. $query = '(\'' . implode('\' in parents or \'', $dirs) . '\' in parents)';
  991. }
  992. $tmp = [];
  993. if (!$mimes) {
  994. foreach (explode(' ', $q) as $_v) {
  995. $tmp[] = 'fullText contains \'' . str_replace('\'', '\\\'', $_v) . '\'';
  996. }
  997. $query .= ($query ? ' and ' : '') . implode(' and ', $tmp);
  998. } else {
  999. foreach ($mimes as $_v) {
  1000. $tmp[] = 'mimeType contains \'' . str_replace('\'', '\\\'', $_v) . '\'';
  1001. }
  1002. $query .= ($query ? ' and ' : '') . '(' . implode(' or ', $tmp) . ')';
  1003. }
  1004. $opts = [
  1005. 'q' => sprintf('trashed=false and (%s)', $query),
  1006. ];
  1007. $res = $this->_gd_query($opts);
  1008. $timeout = $this->options['searchTimeout'] ? $this->searchStart + $this->options['searchTimeout'] : 0;
  1009. foreach ($res as $raw) {
  1010. if ($timeout && $timeout < time()) {
  1011. $this->setError(elFinder::ERROR_SEARCH_TIMEOUT, $this->_path($path));
  1012. break;
  1013. }
  1014. if ($stat = $this->_gd_parseRaw($raw)) {
  1015. if ($parents = $raw->getParents()) {
  1016. foreach ($parents as $parent) {
  1017. $paths = $this->_gd_getMountPaths($parent);
  1018. foreach ($paths as $path) {
  1019. $path = ($path === '') ? '/' : (rtrim($path, '/') . '/');
  1020. if (!isset($this->cache[$path . $raw->id])) {
  1021. $stat = $this->updateCache($path . $raw->id, $stat);
  1022. } else {
  1023. $stat = $this->cache[$path . $raw->id];
  1024. }
  1025. if (empty($stat['hidden'])) {
  1026. $stat['path'] = $this->_path($path) . $stat['name'];
  1027. $result[] = $stat;
  1028. }
  1029. }
  1030. }
  1031. }
  1032. }
  1033. }
  1034. return $result;
  1035. }
  1036. /**
  1037. * Copy file/recursive copy dir only in current volume.
  1038. * Return new file path or false.
  1039. *
  1040. * @param string $src source path
  1041. * @param string $dst destination dir path
  1042. * @param string $name new file name (optionaly)
  1043. *
  1044. * @return string|false
  1045. * @author Dmitry (dio) Levashov
  1046. * @author Naoki Sawada
  1047. **/
  1048. protected function copy($src, $dst, $name)
  1049. {
  1050. $this->clearcache();
  1051. $res = $this->_gd_getFile($src);
  1052. if ($res['mimeType'] == self::DIRMIME) {
  1053. $newDir = $this->_mkdir($dst, $name);
  1054. if ($newDir) {
  1055. list(, $itemId) = $this->_gd_splitPath($newDir);
  1056. list(, $srcId) = $this->_gd_splitPath($src);
  1057. $path = $this->_joinPath($dst, $itemId);
  1058. $opts = [
  1059. 'q' => sprintf('trashed=false and "%s" in parents', $srcId),
  1060. ];
  1061. $res = $this->_gd_query($opts);
  1062. foreach ($res as $raw) {
  1063. $raw['mimeType'] == self::DIRMIME ? $this->copy($src . '/' . $raw['id'], $path, $raw['name']) : $this->_copy($src . '/' . $raw['id'], $path, $raw['name']);
  1064. }
  1065. $ret = $this->_joinPath($dst, $itemId);
  1066. $this->added[] = $this->stat($ret);
  1067. } else {
  1068. $ret = $this->setError(elFinder::ERROR_COPY, $this->_path($src));
  1069. }
  1070. } else {
  1071. if ($itemId = $this->_copy($src, $dst, $name)) {
  1072. $ret = $this->_joinPath($dst, $itemId);
  1073. $this->added[] = $this->stat($ret);
  1074. } else {
  1075. $ret = $this->setError(elFinder::ERROR_COPY, $this->_path($src));
  1076. }
  1077. }
  1078. return $ret;
  1079. }
  1080. /**
  1081. * Remove file/ recursive remove dir.
  1082. *
  1083. * @param string $path file path
  1084. * @param bool $force try to remove even if file locked
  1085. * @param bool $recursive
  1086. *
  1087. * @return bool
  1088. * @throws elFinderAbortException
  1089. * @author Dmitry (dio) Levashov
  1090. * @author Naoki Sawada
  1091. */
  1092. protected function remove($path, $force = false, $recursive = false)
  1093. {
  1094. $stat = $this->stat($path);
  1095. $stat['realpath'] = $path;
  1096. $this->rmTmb($stat);
  1097. $this->clearcache();
  1098. if (empty($stat)) {
  1099. return $this->setError(elFinder::ERROR_RM, $this->_path($path), elFinder::ERROR_FILE_NOT_FOUND);
  1100. }
  1101. if (!$force && !empty($stat['locked'])) {
  1102. return $this->setError(elFinder::ERROR_LOCKED, $this->_path($path));
  1103. }
  1104. if ($stat['mime'] == 'directory') {
  1105. if (!$recursive && !$this->_rmdir($path)) {
  1106. return $this->setError(elFinder::ERROR_RM, $this->_path($path));
  1107. }
  1108. } else {
  1109. if (!$recursive && !$this->_unlink($path)) {
  1110. return $this->setError(elFinder::ERROR_RM, $this->_path($path));
  1111. }
  1112. }
  1113. $this->removed[] = $stat;
  1114. return true;
  1115. }
  1116. /**
  1117. * Create thumnbnail and return it's URL on success.
  1118. *
  1119. * @param string $path file path
  1120. * @param $stat
  1121. *
  1122. * @return string|false
  1123. * @throws ImagickException
  1124. * @throws elFinderAbortException
  1125. * @author Dmitry (dio) Levashov
  1126. * @author Naoki Sawada
  1127. */
  1128. protected function createTmb($path, $stat)
  1129. {
  1130. if (!$stat || !$this->canCreateTmb($path, $stat)) {
  1131. return false;
  1132. }
  1133. $name = $this->tmbname($stat);
  1134. $tmb = $this->tmbPath . DIRECTORY_SEPARATOR . $name;
  1135. // copy image into tmbPath so some drivers does not store files on local fs
  1136. if (!$data = $this->_gd_getThumbnail($path)) {
  1137. return false;
  1138. }
  1139. if (!file_put_contents($tmb, $data)) {
  1140. return false;
  1141. }
  1142. $result = false;
  1143. $tmbSize = $this->tmbSize;
  1144. if (($s = getimagesize($tmb)) == false) {
  1145. return false;
  1146. }
  1147. /* If image smaller or equal thumbnail size - just fitting to thumbnail square */
  1148. if ($s[0] <= $tmbSize && $s[1] <= $tmbSize) {
  1149. $result = $this->imgSquareFit($tmb, $tmbSize, $tmbSize, 'center', 'middle', $this->options['tmbBgColor'], 'png');
  1150. } else {
  1151. if ($this->options['tmbCrop']) {
  1152. /* Resize and crop if image bigger than thumbnail */
  1153. if (!(($s[0] > $tmbSize && $s[1] <= $tmbSize) || ($s[0] <= $tmbSize && $s[1] > $tmbSize)) || ($s[0] > $tmbSize && $s[1] > $tmbSize)) {
  1154. $result = $this->imgResize($tmb, $tmbSize, $tmbSize, true, false, 'png');
  1155. }
  1156. if (($s = getimagesize($tmb)) != false) {
  1157. $x = $s[0] > $tmbSize ? intval(($s[0] - $tmbSize) / 2) : 0;
  1158. $y = $s[1] > $tmbSize ? intval(($s[1] - $tmbSize) / 2) : 0;
  1159. $result = $this->imgCrop($tmb, $tmbSize, $tmbSize, $x, $y, 'png');
  1160. }
  1161. } else {
  1162. $result = $this->imgResize($tmb, $tmbSize, $tmbSize, true, true, 'png');
  1163. }
  1164. $result = $this->imgSquareFit($tmb, $tmbSize, $tmbSize, 'center', 'middle', $this->options['tmbBgColor'], 'png');
  1165. }
  1166. if (!$result) {
  1167. unlink($tmb);
  1168. return false;
  1169. }
  1170. return $name;
  1171. }
  1172. /**
  1173. * Return thumbnail file name for required file.
  1174. *
  1175. * @param array $stat file stat
  1176. *
  1177. * @return string
  1178. * @author Dmitry (dio) Levashov
  1179. **/
  1180. protected function tmbname($stat)
  1181. {
  1182. return $this->netMountKey . $stat['iid'] . $stat['ts'] . '.png';
  1183. }
  1184. /**
  1185. * Return content URL (for netmout volume driver)
  1186. * If file.url == 1 requests from JavaScript client with XHR.
  1187. *
  1188. * @param string $hash file hash
  1189. * @param array $options options array
  1190. *
  1191. * @return bool|string
  1192. * @author Naoki Sawada
  1193. */
  1194. public function getContentUrl($hash, $options = [])
  1195. {
  1196. if (!empty($options['onetime']) && $this->options['onetimeUrl']) {
  1197. return parent::getContentUrl($hash, $options);
  1198. }
  1199. if (!empty($options['temporary'])) {
  1200. // try make temporary file
  1201. $url = parent::getContentUrl($hash, $options);
  1202. if ($url) {
  1203. return $url;
  1204. }
  1205. }
  1206. if (($file = $this->file($hash)) == false || !$file['url'] || $file['url'] == 1) {
  1207. $path = $this->decode($hash);
  1208. if ($this->_gd_publish($path)) {
  1209. if ($raw = $this->_gd_getFile($path)) {
  1210. return $this->_gd_getLink($raw);
  1211. }
  1212. }
  1213. }
  1214. return false;
  1215. }
  1216. /**
  1217. * Return debug info for client.
  1218. *
  1219. * @return array
  1220. **/
  1221. public function debug()
  1222. {
  1223. $res = parent::debug();
  1224. if (empty($this->options['refresh_token']) && $this->options['access_token'] && isset($this->options['access_token']['refresh_token'])) {
  1225. $res['refresh_token'] = $this->options['access_token']['refresh_token'];
  1226. }
  1227. return $res;
  1228. }
  1229. /*********************** paths/urls *************************/
  1230. /**
  1231. * Return parent directory path.
  1232. *
  1233. * @param string $path file path
  1234. *
  1235. * @return string
  1236. * @author Dmitry (dio) Levashov
  1237. **/
  1238. protected function _dirname($path)
  1239. {
  1240. list(, , $parent) = $this->_gd_splitPath($path);
  1241. return $this->_normpath($parent);
  1242. }
  1243. /**
  1244. * Return file name.
  1245. *
  1246. * @param string $path file path
  1247. *
  1248. * @return string
  1249. * @author Dmitry (dio) Levashov
  1250. **/
  1251. protected function _basename($path)
  1252. {
  1253. list(, $basename) = $this->_gd_splitPath($path);
  1254. return $basename;
  1255. }
  1256. /**
  1257. * Join dir name and file name and retur full path.
  1258. *
  1259. * @param string $dir
  1260. * @param string $name
  1261. *
  1262. * @return string
  1263. * @author Dmitry (dio) Levashov
  1264. **/
  1265. protected function _joinPath($dir, $name)
  1266. {
  1267. return $this->_normpath($dir . '/' . $name);
  1268. }
  1269. /**
  1270. * Return normalized path, this works the same as os.path.normpath() in Python.
  1271. *
  1272. * @param string $path path
  1273. *
  1274. * @return string
  1275. * @author Troex Nevelin
  1276. **/
  1277. protected function _normpath($path)
  1278. {
  1279. if (DIRECTORY_SEPARATOR !== '/') {
  1280. $path = str_replace(DIRECTORY_SEPARATOR, '/', $path);
  1281. }
  1282. $path = '/' . ltrim($path, '/');
  1283. return $path;
  1284. }
  1285. /**
  1286. * Return file path related to root dir.
  1287. *
  1288. * @param string $path file path
  1289. *
  1290. * @return string
  1291. * @author Dmitry (dio) Levashov
  1292. **/
  1293. protected function _relpath($path)
  1294. {
  1295. return $path;
  1296. }
  1297. /**
  1298. * Convert path related to root dir into real path.
  1299. *
  1300. * @param string $path file path
  1301. *
  1302. * @return string
  1303. * @author Dmitry (dio) Levashov
  1304. **/
  1305. protected function _abspath($path)
  1306. {
  1307. return $path;
  1308. }
  1309. /**
  1310. * Return fake path started from root dir.
  1311. *
  1312. * @param string $path file path
  1313. *
  1314. * @return string
  1315. * @author Dmitry (dio) Levashov
  1316. **/
  1317. protected function _path($path)
  1318. {
  1319. if (!$this->names) {
  1320. $this->_gd_getDirectoryData();
  1321. }
  1322. $path = $this->_normpath(substr($path, strlen($this->root)));
  1323. $names = [];
  1324. $paths = explode('/', $path);
  1325. foreach ($paths as $_p) {
  1326. $names[] = isset($this->names[$_p]) ? $this->names[$_p] : $_p;
  1327. }
  1328. return $this->rootName . implode('/', $names);
  1329. }
  1330. /**
  1331. * Return true if $path is children of $parent.
  1332. *
  1333. * @param string $path path to check
  1334. * @param string $parent parent path
  1335. *
  1336. * @return bool
  1337. * @author Dmitry (dio) Levashov
  1338. **/
  1339. protected function _inpath($path, $parent)
  1340. {
  1341. return $path == $parent || strpos($path, $parent . '/') === 0;
  1342. }
  1343. /***************** file stat ********************/
  1344. /**
  1345. * Return stat for given path.
  1346. * Stat contains following fields:
  1347. * - (int) size file size in b. required
  1348. * - (int) ts file modification time in unix time. required
  1349. * - (string) mime mimetype. required for folders, others - optionally
  1350. * - (bool) read read permissions. required
  1351. * - (bool) write write permissions. required
  1352. * - (bool) locked is object locked. optionally
  1353. * - (bool) hidden is object hidden. optionally
  1354. * - (string) alias for symlinks - link target path relative to root path. optionally
  1355. * - (string) target for symlinks - link target path. optionally.
  1356. * If file does not exists - returns empty array or false.
  1357. *
  1358. * @param string $path file path
  1359. *
  1360. * @return array|false
  1361. * @author Dmitry (dio) Levashov
  1362. **/
  1363. protected function _stat($path)
  1364. {
  1365. if ($raw = $this->_gd_getFile($path)) {
  1366. $stat = $this->_gd_parseRaw($raw);
  1367. if ($path === $this->root) {
  1368. $stat['expires'] = $this->expires;
  1369. }
  1370. return $stat;
  1371. }
  1372. return false;
  1373. }
  1374. /**
  1375. * Return true if path is dir and has at least one childs directory.
  1376. *
  1377. * @param string $path dir path
  1378. *
  1379. * @return bool
  1380. * @author Dmitry (dio) Levashov
  1381. **/
  1382. protected function _subdirs($path)
  1383. {
  1384. if ($this->directories === null) {
  1385. $this->_gd_getDirectoryData();
  1386. }
  1387. list(, $itemId) = $this->_gd_splitPath($path);
  1388. return isset($this->directories[$itemId]);
  1389. }
  1390. /**
  1391. * Return object width and height
  1392. * Ususaly used for images, but can be realize for video etc...
  1393. *
  1394. * @param string $path file path
  1395. * @param string $mime file mime type
  1396. *
  1397. * @return string
  1398. * @throws ImagickException
  1399. * @throws elFinderAbortException
  1400. * @author Dmitry (dio) Levashov
  1401. */
  1402. protected function _dimensions($path, $mime)
  1403. {
  1404. if (strpos($mime, 'image') !== 0) {
  1405. return '';
  1406. }
  1407. $ret = '';
  1408. if ($file = $this->_gd_getFile($path)) {
  1409. if (isset($file['imageMediaMetadata'])) {
  1410. $ret = array('dim' => $file['imageMediaMetadata']['width'] . 'x' . $file['imageMediaMetadata']['height']);
  1411. if (func_num_args() > 2) {
  1412. $args = func_get_arg(2);
  1413. } else {
  1414. $args = array();
  1415. }
  1416. if (!empty($args['substitute'])) {
  1417. $tmbSize = intval($args['substitute']);
  1418. $srcSize = explode('x', $ret['dim']);
  1419. if ($srcSize[0] && $srcSize[1]) {
  1420. if (min(($tmbSize / $srcSize[0]), ($tmbSize / $srcSize[1])) < 1) {
  1421. if ($this->_gd_isPublished($file)) {
  1422. $tmbSize = strval($tmbSize);
  1423. $ret['url'] = 'https://drive.google.com/thumbnail?authuser=0&sz=s' . $tmbSize . '&id=' . $file['id'];
  1424. } elseif ($subImgLink = $this->getSubstituteImgLink(elFinder::$currentArgs['target'], $srcSize)) {
  1425. $ret['url'] = $subImgLink;
  1426. }
  1427. }
  1428. }
  1429. }
  1430. }
  1431. }
  1432. return $ret;
  1433. }
  1434. /******************** file/dir content *********************/
  1435. /**
  1436. * Return files list in directory.
  1437. *
  1438. * @param string $path dir path
  1439. *
  1440. * @return array
  1441. * @author Dmitry (dio) Levashov
  1442. * @author Cem (DiscoFever)
  1443. **/
  1444. protected function _scandir($path)
  1445. {
  1446. return isset($this->dirsCache[$path])
  1447. ? $this->dirsCache[$path]
  1448. : $this->cacheDir($path);
  1449. }
  1450. /**
  1451. * Open file and return file pointer.
  1452. *
  1453. * @param string $path file path
  1454. * @param bool $write open file for writing
  1455. *
  1456. * @return resource|false
  1457. * @author Dmitry (dio) Levashov
  1458. **/
  1459. protected function _fopen($path, $mode = 'rb')
  1460. {
  1461. if ($mode === 'rb' || $mode === 'r') {
  1462. if ($file = $this->_gd_getFile($path)) {
  1463. if ($dlurl = $this->_gd_getDownloadUrl($file)) {
  1464. $token = $this->client->getAccessToken();
  1465. if (!$token && $this->client->isUsingApplicationDefaultCredentials()) {
  1466. $this->client->fetchAccessTokenWithAssertion();
  1467. $token = $this->client->getAccessToken();
  1468. }
  1469. $access_token = '';
  1470. if (is_array($token)) {
  1471. $access_token = $token['access_token'];
  1472. } else {
  1473. if ($token = json_decode($this->client->getAccessToken())) {
  1474. $access_token = $token->access_token;
  1475. }
  1476. }
  1477. if ($access_token) {
  1478. $data = array(
  1479. 'target' => $dlurl,
  1480. 'headers' => array('Authorization: Bearer ' . $access_token),
  1481. );
  1482. return elFinder::getStreamByUrl($data);
  1483. }
  1484. }
  1485. }
  1486. }
  1487. return false;
  1488. }
  1489. /**
  1490. * Close opened file.
  1491. *
  1492. * @param resource $fp file pointer
  1493. *
  1494. * @return bool
  1495. * @author Dmitry (dio) Levashov
  1496. **/
  1497. protected function _fclose($fp, $path = '')
  1498. {
  1499. fclose($fp);
  1500. if ($path) {
  1501. unlink($this->getTempFile($path));
  1502. }
  1503. }
  1504. /******************** file/dir manipulations *************************/
  1505. /**
  1506. * Create dir and return created dir path or false on failed.
  1507. *
  1508. * @param string $path parent dir path
  1509. * @param string $name new directory name
  1510. *
  1511. * @return string|bool
  1512. * @author Dmitry (dio) Levashov
  1513. **/
  1514. protected function _mkdir($path, $name)
  1515. {
  1516. $path = $this->_joinPath($path, $name);
  1517. list($parentId, , $parent) = $this->_gd_splitPath($path);
  1518. try {
  1519. $file = new \Google_Service_Drive_DriveFile();
  1520. $file->setName($name);
  1521. $file->setMimeType(self::DIRMIME);
  1522. $file->setParents([$parentId]);
  1523. //create the Folder in the Parent
  1524. $obj = $this->service->files->create($file);
  1525. if ($obj instanceof Google_Service_Drive_DriveFile) {
  1526. $path = $this->_joinPath($parent, $obj['id']);
  1527. $this->_gd_getDirectoryData(false);
  1528. return $path;
  1529. } else {
  1530. return false;
  1531. }
  1532. } catch (Exception $e) {
  1533. return $this->setError('GoogleDrive error: ' . $e->getMessage());
  1534. }
  1535. }
  1536. /**
  1537. * Create file and return it's path or false on failed.
  1538. *
  1539. * @param string $path parent dir path
  1540. * @param string $name new file name
  1541. *
  1542. * @return string|bool
  1543. * @author Dmitry (dio) Levashov
  1544. **/
  1545. protected function _mkfile($path, $name)
  1546. {
  1547. return $this->_save($this->tmpfile(), $path, $name, []);
  1548. }
  1549. /**
  1550. * Create symlink. FTP driver does not support symlinks.
  1551. *
  1552. * @param string $target link target
  1553. * @param string $path symlink path
  1554. *
  1555. * @return bool
  1556. * @author Dmitry (dio) Levashov
  1557. **/
  1558. protected function _symlink($target, $path, $name)
  1559. {
  1560. return false;
  1561. }
  1562. /**
  1563. * Copy file into another file.
  1564. *
  1565. * @param string $source source file path
  1566. * @param string $targetDir target directory path
  1567. * @param string $name new file name
  1568. *
  1569. * @return bool
  1570. * @author Dmitry (dio) Levashov
  1571. **/
  1572. protected function _copy($source, $targetDir, $name)
  1573. {
  1574. $source = $this->_normpath($source);
  1575. $targetDir = $this->_normpath($targetDir);
  1576. try {
  1577. $file = new \Google_Service_Drive_DriveFile();
  1578. $file->setName($name);
  1579. //Set the Parent id
  1580. list(, $parentId) = $this->_gd_splitPath($targetDir);
  1581. $file->setParents([$parentId]);
  1582. list(, $srcId) = $this->_gd_splitPath($source);
  1583. $file = $this->service->files->copy($srcId, $file, ['fields' => self::FETCHFIELDS_GET]);
  1584. $itemId = $file->id;
  1585. return $itemId;
  1586. } catch (Exception $e) {
  1587. return $this->setError('GoogleDrive error: ' . $e->getMessage());
  1588. }
  1589. return true;
  1590. }
  1591. /**
  1592. * Move file into another parent dir.
  1593. * Return new file path or false.
  1594. *
  1595. * @param string $source source file path
  1596. * @param string $target target dir path
  1597. * @param string $name file name
  1598. *
  1599. * @return string|bool
  1600. * @author Dmitry (dio) Levashov
  1601. **/
  1602. protected function _move($source, $targetDir, $name)
  1603. {
  1604. list($removeParents, $itemId) = $this->_gd_splitPath($source);
  1605. $target = $this->_normpath($targetDir . '/' . $itemId);
  1606. try {
  1607. //moving and renaming a file or directory
  1608. $files = new \Google_Service_Drive_DriveFile();
  1609. $files->setName($name);
  1610. //Set new Parent and remove old parent
  1611. list(, $addParents) = $this->_gd_splitPath($targetDir);
  1612. $opts = ['addParents' => $addParents, 'removeParents' => $removeParents];
  1613. $file = $this->service->files->update($itemId, $files, $opts);
  1614. if ($file->getMimeType() === self::DIRMIME) {
  1615. $this->_gd_getDirectoryData(false);
  1616. }
  1617. } catch (Exception $e) {
  1618. return $this->setError('GoogleDrive error: ' . $e->getMessage());
  1619. }
  1620. return $target;
  1621. }
  1622. /**
  1623. * Remove file.
  1624. *
  1625. * @param string $path file path
  1626. *
  1627. * @return bool
  1628. * @author Dmitry (dio) Levashov
  1629. **/
  1630. protected function _unlink($path)
  1631. {
  1632. try {
  1633. $files = new \Google_Service_Drive_DriveFile();
  1634. $files->setTrashed(true);
  1635. list($pid, $itemId) = $this->_gd_splitPath($path);
  1636. $opts = ['removeParents' => $pid];
  1637. $this->service->files->update($itemId, $files, $opts);
  1638. } catch (Exception $e) {
  1639. return $this->setError('GoogleDrive error: ' . $e->getMessage());
  1640. }
  1641. return true;
  1642. }
  1643. /**
  1644. * Remove dir.
  1645. *
  1646. * @param string $path dir path
  1647. *
  1648. * @return bool
  1649. * @author Dmitry (dio) Levashov
  1650. **/
  1651. protected function _rmdir($path)
  1652. {
  1653. $res = $this->_unlink($path);
  1654. $res && $this->_gd_getDirectoryData(false);
  1655. return $res;
  1656. }
  1657. /**
  1658. * Create new file and write into it from file pointer.
  1659. * Return new file path or false on error.
  1660. *
  1661. * @param resource $fp file pointer
  1662. * @param $path
  1663. * @param string $name file name
  1664. * @param array $stat file stat (required by some virtual fs)
  1665. *
  1666. * @return bool|string
  1667. * @author Dmitry (dio) Levashov
  1668. */
  1669. protected function _save($fp, $path, $name, $stat)
  1670. {
  1671. if ($name !== '') {
  1672. $path .= '/' . $name;
  1673. }
  1674. list($parentId, $itemId, $parent) = $this->_gd_splitPath($path);
  1675. if ($name === '') {
  1676. $stat['iid'] = $itemId;
  1677. }
  1678. if (!$stat || empty($stat['iid'])) {
  1679. $opts = [
  1680. 'q' => sprintf('trashed=false and "%s" in parents and name="%s"', $parentId, $name),
  1681. 'fields' => self::FETCHFIELDS_LIST,
  1682. ];
  1683. $srcFile = $this->_gd_query($opts);
  1684. $srcFile = empty($srcFile) ? null : $srcFile[0];
  1685. } else {
  1686. $srcFile = $this->_gd_getFile($path);
  1687. }
  1688. try {
  1689. $mode = 'update';
  1690. $mime = isset($stat['mime']) ? $stat['mime'] : '';
  1691. $file = new Google_Service_Drive_DriveFile();
  1692. if ($srcFile) {
  1693. $mime = $srcFile->getMimeType();
  1694. } else {
  1695. $mode = 'insert';
  1696. $file->setName($name);
  1697. $file->setParents([
  1698. $parentId,
  1699. ]);
  1700. }
  1701. if (!$mime) {
  1702. $mime = self::mimetypeInternalDetect($name);
  1703. }
  1704. if ($mime === 'unknown') {
  1705. $mime = 'application/octet-stream';
  1706. }
  1707. $file->setMimeType($mime);
  1708. $size = 0;
  1709. if (isset($stat['size'])) {
  1710. $size = $stat['size'];
  1711. } else {
  1712. $fstat = fstat($fp);
  1713. if (!empty($fstat['size'])) {
  1714. $size = $fstat['size'];
  1715. }
  1716. }
  1717. // set chunk size (max: 100MB)
  1718. $chunkSizeBytes = 100 * 1024 * 1024;
  1719. if ($size > 0) {
  1720. $memory = elFinder::getIniBytes('memory_limit');
  1721. if ($memory > 0) {
  1722. $chunkSizeBytes = max(262144, min([$chunkSizeBytes, (intval($memory / 4 / 256) * 256)]));
  1723. }
  1724. }
  1725. if ($size > $chunkSizeBytes) {
  1726. $client = $this->client;
  1727. // Call the API with the media upload, defer so it doesn't immediately return.
  1728. $client->setDefer(true);
  1729. if ($mode === 'insert') {
  1730. $request = $this->service->files->create($file, [
  1731. 'fields' => self::FETCHFIELDS_GET,
  1732. ]);
  1733. } else {
  1734. $request = $this->service->files->update($srcFile->getId(), $file, [
  1735. 'fields' => self::FETCHFIELDS_GET,
  1736. ]);
  1737. }
  1738. // Create a media file upload to represent our upload process.
  1739. $media = new Google_Http_MediaFileUpload($client, $request, $mime, null, true, $chunkSizeBytes);
  1740. $media->setFileSize($size);
  1741. // Upload the various chunks. $status will be false until the process is
  1742. // complete.
  1743. $status = false;
  1744. while (!$status && !feof($fp)) {
  1745. elFinder::checkAborted();
  1746. // read until you get $chunkSizeBytes from TESTFILE
  1747. // fread will never return more than 8192 bytes if the stream is read buffered and it does not represent a plain file
  1748. // An example of a read buffered file is when reading from a URL
  1749. $chunk = $this->_gd_readFileChunk($fp, $chunkSizeBytes);
  1750. $status = $media->nextChunk($chunk);
  1751. }
  1752. // The final value of $status will be the data from the API for the object
  1753. // that has been uploaded.
  1754. if ($status !== false) {
  1755. $obj = $status;
  1756. }
  1757. $client->setDefer(false);
  1758. } else {
  1759. $params = [
  1760. 'data' => stream_get_contents($fp),
  1761. 'uploadType' => 'media',
  1762. 'fields' => self::FETCHFIELDS_GET,
  1763. ];
  1764. if ($mode === 'insert') {
  1765. $obj = $this->service->files->create($file, $params);
  1766. } else {
  1767. $obj = $this->service->files->update($srcFile->getId(), $file, $params);
  1768. }
  1769. }
  1770. if ($obj instanceof Google_Service_Drive_DriveFile) {
  1771. return $this->_joinPath($parent, $obj->getId());
  1772. } else {
  1773. return false;
  1774. }
  1775. } catch (Exception $e) {
  1776. return $this->setError('GoogleDrive error: ' . $e->getMessage());
  1777. }
  1778. }
  1779. /**
  1780. * Get file contents.
  1781. *
  1782. * @param string $path file path
  1783. *
  1784. * @return string|false
  1785. * @author Dmitry (dio) Levashov
  1786. **/
  1787. protected function _getContents($path)
  1788. {
  1789. $contents = '';
  1790. try {
  1791. list(, $itemId) = $this->_gd_splitPath($path);
  1792. $contents = $this->service->files->get($itemId, [
  1793. 'alt' => 'media',
  1794. ]);
  1795. $contents = (string)$contents->getBody();
  1796. } catch (Exception $e) {
  1797. return $this->setError('GoogleDrive error: ' . $e->getMessage());
  1798. }
  1799. return $contents;
  1800. }
  1801. /**
  1802. * Write a string to a file.
  1803. *
  1804. * @param string $path file path
  1805. * @param string $content new file content
  1806. *
  1807. * @return bool
  1808. * @author Dmitry (dio) Levashov
  1809. **/
  1810. protected function _filePutContents($path, $content)
  1811. {
  1812. $res = false;
  1813. if ($local = $this->getTempFile($path)) {
  1814. if (file_put_contents($local, $content, LOCK_EX) !== false
  1815. && ($fp = fopen($local, 'rb'))) {
  1816. clearstatcache();
  1817. $res = $this->_save($fp, $path, '', []);
  1818. fclose($fp);
  1819. }
  1820. file_exists($local) && unlink($local);
  1821. }
  1822. return $res;
  1823. }
  1824. /**
  1825. * Detect available archivers.
  1826. **/
  1827. protected function _checkArchivers()
  1828. {
  1829. // die('Not yet implemented. (_checkArchivers)');
  1830. return [];
  1831. }
  1832. /**
  1833. * chmod implementation.
  1834. *
  1835. * @return bool
  1836. **/
  1837. protected function _chmod($path, $mode)
  1838. {
  1839. return false;
  1840. }
  1841. /**
  1842. * Unpack archive.
  1843. *
  1844. * @param string $path archive path
  1845. * @param array $arc archiver command and arguments (same as in $this->archivers)
  1846. *
  1847. * @return void
  1848. * @author Dmitry (dio) Levashov
  1849. * @author Alexey Sukhotin
  1850. */
  1851. protected function _unpack($path, $arc)
  1852. {
  1853. die('Not yet implemented. (_unpack)');
  1854. //return false;
  1855. }
  1856. /**
  1857. * Extract files from archive.
  1858. *
  1859. * @param string $path archive path
  1860. * @param array $arc archiver command and arguments (same as in $this->archivers)
  1861. *
  1862. * @return void
  1863. * @author Dmitry (dio) Levashov,
  1864. * @author Alexey Sukhotin
  1865. */
  1866. protected function _extract($path, $arc)
  1867. {
  1868. die('Not yet implemented. (_extract)');
  1869. }
  1870. /**
  1871. * Create archive and return its path.
  1872. *
  1873. * @param string $dir target dir
  1874. * @param array $files files names list
  1875. * @param string $name archive name
  1876. * @param array $arc archiver options
  1877. *
  1878. * @return string|bool
  1879. * @author Dmitry (dio) Levashov,
  1880. * @author Alexey Sukhotin
  1881. **/
  1882. protected function _archive($dir, $files, $name, $arc)
  1883. {
  1884. die('Not yet implemented. (_archive)');
  1885. }
  1886. } // END class