elFinder.class.php 44 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569
  1. <?php
  2. /**
  3. * elFinder - file manager for web.
  4. * Core class.
  5. *
  6. * @package elfinder
  7. * @author Dmitry (dio) Levashov
  8. * @author Troex Nevelin
  9. * @author Alexey Sukhotin
  10. **/
  11. class elFinder {
  12. /**
  13. * API version number
  14. *
  15. * @var string
  16. **/
  17. protected $version = '2.0';
  18. /**
  19. * Storages (root dirs)
  20. *
  21. * @var array
  22. **/
  23. protected $volumes = array();
  24. public static $netDrivers = array();
  25. /**
  26. * Mounted volumes count
  27. * Required to create unique volume id
  28. *
  29. * @var int
  30. **/
  31. public static $volumesCnt = 1;
  32. /**
  33. * Default root (storage)
  34. *
  35. * @var elFinderStorageDriver
  36. **/
  37. protected $default = null;
  38. /**
  39. * Commands and required arguments list
  40. *
  41. * @var array
  42. **/
  43. protected $commands = array(
  44. 'open' => array('target' => false, 'tree' => false, 'init' => false, 'mimes' => false),
  45. 'ls' => array('target' => true, 'mimes' => false),
  46. 'tree' => array('target' => true),
  47. 'parents' => array('target' => true),
  48. 'tmb' => array('targets' => true),
  49. 'file' => array('target' => true, 'download' => false),
  50. 'size' => array('targets' => true),
  51. 'mkdir' => array('target' => true, 'name' => true),
  52. 'mkfile' => array('target' => true, 'name' => true, 'mimes' => false),
  53. 'rm' => array('targets' => true),
  54. 'rename' => array('target' => true, 'name' => true, 'mimes' => false),
  55. 'duplicate' => array('targets' => true, 'suffix' => false),
  56. 'paste' => array('dst' => true, 'targets' => true, 'cut' => false, 'mimes' => false),
  57. 'upload' => array('target' => true, 'FILES' => true, 'mimes' => false, 'html' => false, 'upload' => false, 'name' => false, 'upload_path' => false),
  58. 'get' => array('target' => true),
  59. 'put' => array('target' => true, 'content' => '', 'mimes' => false),
  60. 'archive' => array('targets' => true, 'type' => true, 'mimes' => false),
  61. 'extract' => array('target' => true, 'mimes' => false),
  62. 'search' => array('q' => true, 'mimes' => false),
  63. 'info' => array('targets' => true),
  64. 'dim' => array('target' => true),
  65. 'resize' => array('target' => true, 'width' => true, 'height' => true, 'mode' => false, 'x' => false, 'y' => false, 'degree' => false),
  66. 'netmount' => array('protocol' => true, 'host' => true, 'path' => false, 'port' => false, 'user' => true, 'pass' => true, 'alias' => false, 'options' => false)
  67. );
  68. /**
  69. * Plugins instance
  70. *
  71. * @var array
  72. **/
  73. protected $plugins = array();
  74. /**
  75. * Commands listeners
  76. *
  77. * @var array
  78. **/
  79. protected $listeners = array();
  80. /**
  81. * script work time for debug
  82. *
  83. * @var string
  84. **/
  85. protected $time = 0;
  86. /**
  87. * Is elFinder init correctly?
  88. *
  89. * @var bool
  90. **/
  91. protected $loaded = false;
  92. /**
  93. * Send debug to client?
  94. *
  95. * @var string
  96. **/
  97. protected $debug = false;
  98. /**
  99. * session expires timeout
  100. *
  101. * @var int
  102. **/
  103. protected $timeout = 0;
  104. /**
  105. * undocumented class variable
  106. *
  107. * @var string
  108. **/
  109. protected $uploadDebug = '';
  110. /**
  111. * Errors from not mounted volumes
  112. *
  113. * @var array
  114. **/
  115. public $mountErrors = array();
  116. // Errors messages
  117. const ERROR_UNKNOWN = 'errUnknown';
  118. const ERROR_UNKNOWN_CMD = 'errUnknownCmd';
  119. const ERROR_CONF = 'errConf';
  120. const ERROR_CONF_NO_JSON = 'errJSON';
  121. const ERROR_CONF_NO_VOL = 'errNoVolumes';
  122. const ERROR_INV_PARAMS = 'errCmdParams';
  123. const ERROR_OPEN = 'errOpen';
  124. const ERROR_DIR_NOT_FOUND = 'errFolderNotFound';
  125. const ERROR_FILE_NOT_FOUND = 'errFileNotFound'; // 'File not found.'
  126. const ERROR_TRGDIR_NOT_FOUND = 'errTrgFolderNotFound'; // 'Target folder "$1" not found.'
  127. const ERROR_NOT_DIR = 'errNotFolder';
  128. const ERROR_NOT_FILE = 'errNotFile';
  129. const ERROR_PERM_DENIED = 'errPerm';
  130. const ERROR_LOCKED = 'errLocked'; // '"$1" is locked and can not be renamed, moved or removed.'
  131. const ERROR_EXISTS = 'errExists'; // 'File named "$1" already exists.'
  132. const ERROR_INVALID_NAME = 'errInvName'; // 'Invalid file name.'
  133. const ERROR_MKDIR = 'errMkdir';
  134. const ERROR_MKFILE = 'errMkfile';
  135. const ERROR_RENAME = 'errRename';
  136. const ERROR_COPY = 'errCopy';
  137. const ERROR_MOVE = 'errMove';
  138. const ERROR_COPY_FROM = 'errCopyFrom';
  139. const ERROR_COPY_TO = 'errCopyTo';
  140. const ERROR_COPY_ITSELF = 'errCopyInItself';
  141. const ERROR_REPLACE = 'errReplace'; // 'Unable to replace "$1".'
  142. const ERROR_RM = 'errRm'; // 'Unable to remove "$1".'
  143. const ERROR_RM_SRC = 'errRmSrc'; // 'Unable remove source file(s)'
  144. const ERROR_UPLOAD = 'errUpload'; // 'Upload error.'
  145. const ERROR_UPLOAD_FILE = 'errUploadFile'; // 'Unable to upload "$1".'
  146. const ERROR_UPLOAD_NO_FILES = 'errUploadNoFiles'; // 'No files found for upload.'
  147. const ERROR_UPLOAD_TOTAL_SIZE = 'errUploadTotalSize'; // 'Data exceeds the maximum allowed size.'
  148. const ERROR_UPLOAD_FILE_SIZE = 'errUploadFileSize'; // 'File exceeds maximum allowed size.'
  149. const ERROR_UPLOAD_FILE_MIME = 'errUploadMime'; // 'File type not allowed.'
  150. const ERROR_UPLOAD_TRANSFER = 'errUploadTransfer'; // '"$1" transfer error.'
  151. // const ERROR_ACCESS_DENIED = 'errAccess';
  152. const ERROR_NOT_REPLACE = 'errNotReplace'; // Object "$1" already exists at this location and can not be replaced with object of another type.
  153. const ERROR_SAVE = 'errSave';
  154. const ERROR_EXTRACT = 'errExtract';
  155. const ERROR_ARCHIVE = 'errArchive';
  156. const ERROR_NOT_ARCHIVE = 'errNoArchive';
  157. const ERROR_ARCHIVE_TYPE = 'errArcType';
  158. const ERROR_ARC_SYMLINKS = 'errArcSymlinks';
  159. const ERROR_ARC_MAXSIZE = 'errArcMaxSize';
  160. const ERROR_RESIZE = 'errResize';
  161. const ERROR_UNSUPPORT_TYPE = 'errUsupportType';
  162. const ERROR_NOT_UTF8_CONTENT = 'errNotUTF8Content';
  163. const ERROR_NETMOUNT = 'errNetMount';
  164. const ERROR_NETMOUNT_NO_DRIVER = 'errNetMountNoDriver';
  165. const ERROR_NETMOUNT_FAILED = 'errNetMountFailed';
  166. const ERROR_SESSION_EXPIRES = 'errSessionExpires';
  167. const ERROR_CREATING_TEMP_DIR = 'errCreatingTempDir';
  168. const ERROR_FTP_DOWNLOAD_FILE = 'errFtpDownloadFile';
  169. const ERROR_FTP_UPLOAD_FILE = 'errFtpUploadFile';
  170. const ERROR_FTP_MKDIR = 'errFtpMkdir';
  171. const ERROR_ARCHIVE_EXEC = 'errArchiveExec';
  172. const ERROR_EXTRACT_EXEC = 'errExtractExec';
  173. /**
  174. * Constructor
  175. *
  176. * @param array elFinder and roots configurations
  177. * @return void
  178. * @author Dmitry (dio) Levashov
  179. **/
  180. public function __construct($opts) {
  181. if (session_id() == '') {
  182. session_start();
  183. }
  184. $this->time = $this->utime();
  185. $this->debug = (isset($opts['debug']) && $opts['debug'] ? true : false);
  186. $this->timeout = (isset($opts['timeout']) ? $opts['timeout'] : 0);
  187. setlocale(LC_ALL, !empty($opts['locale']) ? $opts['locale'] : 'en_US.UTF-8');
  188. // bind events listeners
  189. if (!empty($opts['bind']) && is_array($opts['bind'])) {
  190. $_req = $_SERVER["REQUEST_METHOD"] == 'POST' ? $_POST : $_GET;
  191. $_reqCmd = isset($_req['cmd']) ? $_req['cmd'] : '';
  192. foreach ($opts['bind'] as $cmd => $handlers) {
  193. $doRegist = (strpos($cmd, '*') !== false);
  194. if (! $doRegist) {
  195. $_getcmd = create_function('$cmd', 'list($ret) = explode(\'.\', $cmd);return trim($ret);');
  196. $doRegist = ($_reqCmd && in_array($_reqCmd, array_map($_getcmd, explode(' ', $cmd))));
  197. }
  198. if ($doRegist) {
  199. if (! is_array($handlers) || is_object($handlers[0])) {
  200. $handlers = array($handlers);
  201. }
  202. foreach($handlers as $handler) {
  203. if ($handler) {
  204. if (is_string($handler) && strpos($handler, '.')) {
  205. list($_domain, $_name, $_method) = array_pad(explode('.', $handler), 3, '');
  206. if (strcasecmp($_domain, 'plugin') === 0) {
  207. if ($plugin = $this->getPluginInstance($_name, isset($opts['plugin'][$_name])? $opts['plugin'][$_name] : array())
  208. and method_exists($plugin, $_method)) {
  209. $this->bind($cmd, array($plugin, $_method));
  210. }
  211. }
  212. } else {
  213. $this->bind($cmd, $handler);
  214. }
  215. }
  216. }
  217. }
  218. }
  219. }
  220. if (!isset($opts['roots']) || !is_array($opts['roots'])) {
  221. $opts['roots'] = array();
  222. }
  223. // check for net volumes stored in session
  224. foreach ($this->getNetVolumes() as $root) {
  225. $opts['roots'][] = $root;
  226. }
  227. // "mount" volumes
  228. foreach ($opts['roots'] as $i => $o) {
  229. $class = (isset($o['driver']) ? $o['driver'] : '');
  230. if (class_exists($class)) {
  231. $volume = new $class();
  232. if ($volume->mount($o)) {
  233. // unique volume id (ends on "_") - used as prefix to files hash
  234. $id = $volume->id();
  235. $this->volumes[$id] = $volume;
  236. if (!$this->default && $volume->isReadable()) {
  237. $this->default = $this->volumes[$id];
  238. }
  239. } else {
  240. $this->mountErrors[] = 'Driver "'.$class.'" : '.implode(' ', $volume->error());
  241. }
  242. } else {
  243. $this->mountErrors[] = 'Driver "'.$class.'" does not exists';
  244. }
  245. }
  246. // if at least one redable volume - ii desu >_<
  247. $this->loaded = !empty($this->default);
  248. }
  249. /**
  250. * Return true if fm init correctly
  251. *
  252. * @return bool
  253. * @author Dmitry (dio) Levashov
  254. **/
  255. public function loaded() {
  256. return $this->loaded;
  257. }
  258. /**
  259. * Return version (api) number
  260. *
  261. * @return string
  262. * @author Dmitry (dio) Levashov
  263. **/
  264. public function version() {
  265. return $this->version;
  266. }
  267. /**
  268. * Add handler to elFinder command
  269. *
  270. * @param string command name
  271. * @param string|array callback name or array(object, method)
  272. * @return elFinder
  273. * @author Dmitry (dio) Levashov
  274. **/
  275. public function bind($cmd, $handler) {
  276. $allCmds = array_keys($this->commands);
  277. $cmds = array();
  278. foreach(explode(' ', $cmd) as $_cmd) {
  279. if ($_cmd !== '') {
  280. if ($all = strpos($_cmd, '*') !== false) {
  281. list(, $sub) = array_pad(explode('.', $_cmd), 2, '');
  282. if ($sub) {
  283. $sub = str_replace('\'', '\\\'', $sub);
  284. $addSub = create_function('$cmd', 'return $cmd . \'.\' . trim(\'' . $sub . '\');');
  285. $cmds = array_merge($cmds, array_map($addSub, $allCmds));
  286. } else {
  287. $cmds = array_merge($cmds, $allCmds);
  288. }
  289. } else {
  290. $cmds[] = $_cmd;
  291. }
  292. }
  293. }
  294. $cmds = array_unique($cmds);
  295. foreach ($cmds as $cmd) {
  296. if (!isset($this->listeners[$cmd])) {
  297. $this->listeners[$cmd] = array();
  298. }
  299. if (is_callable($handler)) {
  300. $this->listeners[$cmd][] = $handler;
  301. }
  302. }
  303. return $this;
  304. }
  305. /**
  306. * Remove event (command exec) handler
  307. *
  308. * @param string command name
  309. * @param string|array callback name or array(object, method)
  310. * @return elFinder
  311. * @author Dmitry (dio) Levashov
  312. **/
  313. public function unbind($cmd, $handler) {
  314. if (!empty($this->listeners[$cmd])) {
  315. foreach ($this->listeners[$cmd] as $i => $h) {
  316. if ($h === $handler) {
  317. unset($this->listeners[$cmd][$i]);
  318. return $this;
  319. }
  320. }
  321. }
  322. return $this;
  323. }
  324. /**
  325. * Return true if command exists
  326. *
  327. * @param string command name
  328. * @return bool
  329. * @author Dmitry (dio) Levashov
  330. **/
  331. public function commandExists($cmd) {
  332. return $this->loaded && isset($this->commands[$cmd]) && method_exists($this, $cmd);
  333. }
  334. /**
  335. * Return command required arguments info
  336. *
  337. * @param string command name
  338. * @return array
  339. * @author Dmitry (dio) Levashov
  340. **/
  341. public function commandArgsList($cmd) {
  342. return $this->commandExists($cmd) ? $this->commands[$cmd] : array();
  343. }
  344. private function session_expires() {
  345. if (!isset($_SESSION['LAST_ACTIVITY'])) {
  346. $_SESSION['LAST_ACTIVITY'] = time();
  347. return false;
  348. }
  349. if ( ($this->timeout > 0) && (time() - $_SESSION['LAST_ACTIVITY'] > $this->timeout) ) {
  350. return true;
  351. }
  352. $_SESSION['LAST_ACTIVITY'] = time();
  353. return false;
  354. }
  355. /**
  356. * Exec command and return result
  357. *
  358. * @param string $cmd command name
  359. * @param array $args command arguments
  360. * @return array
  361. * @author Dmitry (dio) Levashov
  362. **/
  363. public function exec($cmd, $args) {
  364. if (!$this->loaded) {
  365. return array('error' => $this->error(self::ERROR_CONF, self::ERROR_CONF_NO_VOL));
  366. }
  367. if ($this->session_expires()) {
  368. return array('error' => $this->error(self::ERROR_SESSION_EXPIRES));
  369. }
  370. if (!$this->commandExists($cmd)) {
  371. return array('error' => $this->error(self::ERROR_UNKNOWN_CMD));
  372. }
  373. if (!empty($args['mimes']) && is_array($args['mimes'])) {
  374. foreach ($this->volumes as $id => $v) {
  375. $this->volumes[$id]->setMimesFilter($args['mimes']);
  376. }
  377. }
  378. // call pre handlers for this command
  379. if (!empty($this->listeners[$cmd.'.pre'])) {
  380. $volume = isset($args['target'])? $this->volume($args['target']) : false;
  381. foreach ($this->listeners[$cmd.'.pre'] as $handler) {
  382. call_user_func_array($handler, array($cmd, &$args, $this, $volume));
  383. }
  384. }
  385. $result = $this->$cmd($args);
  386. if (isset($result['removed'])) {
  387. foreach ($this->volumes as $volume) {
  388. $result['removed'] = array_merge($result['removed'], $volume->removed());
  389. $volume->resetRemoved();
  390. }
  391. }
  392. // call handlers for this command
  393. if (!empty($this->listeners[$cmd])) {
  394. foreach ($this->listeners[$cmd] as $handler) {
  395. if (call_user_func($handler,$cmd,$result,$args,$this)) {
  396. // handler return true to force sync client after command completed
  397. $result['sync'] = true;
  398. }
  399. }
  400. }
  401. // replace removed files info with removed files hashes
  402. if (!empty($result['removed'])) {
  403. $removed = array();
  404. foreach ($result['removed'] as $file) {
  405. $removed[] = $file['hash'];
  406. }
  407. $result['removed'] = array_unique($removed);
  408. }
  409. // remove hidden files and filter files by mimetypes
  410. if (!empty($result['added'])) {
  411. $result['added'] = $this->filter($result['added']);
  412. }
  413. // remove hidden files and filter files by mimetypes
  414. if (!empty($result['changed'])) {
  415. $result['changed'] = $this->filter($result['changed']);
  416. }
  417. if ($this->debug || !empty($args['debug'])) {
  418. $result['debug'] = array(
  419. 'connector' => 'php',
  420. 'phpver' => PHP_VERSION,
  421. 'time' => $this->utime() - $this->time,
  422. 'memory' => (function_exists('memory_get_peak_usage') ? ceil(memory_get_peak_usage()/1024).'Kb / ' : '').ceil(memory_get_usage()/1024).'Kb / '.ini_get('memory_limit'),
  423. 'upload' => $this->uploadDebug,
  424. 'volumes' => array(),
  425. 'mountErrors' => $this->mountErrors
  426. );
  427. foreach ($this->volumes as $id => $volume) {
  428. $result['debug']['volumes'][] = $volume->debug();
  429. }
  430. }
  431. foreach ($this->volumes as $volume) {
  432. $volume->umount();
  433. }
  434. return $result;
  435. }
  436. /**
  437. * Return file real path
  438. *
  439. * @param string $hash file hash
  440. * @return string
  441. * @author Dmitry (dio) Levashov
  442. **/
  443. public function realpath($hash) {
  444. if (($volume = $this->volume($hash)) == false) {
  445. return false;
  446. }
  447. return $volume->realpath($hash);
  448. }
  449. /**
  450. * Return network volumes config.
  451. *
  452. * @return array
  453. * @author Dmitry (dio) Levashov
  454. */
  455. protected function getNetVolumes() {
  456. return isset($_SESSION['elFinderNetVolumes']) && is_array($_SESSION['elFinderNetVolumes']) ? $_SESSION['elFinderNetVolumes'] : array();
  457. }
  458. /**
  459. * Save network volumes config.
  460. *
  461. * @param array $volumes volumes config
  462. * @return void
  463. * @author Dmitry (dio) Levashov
  464. */
  465. protected function saveNetVolumes($volumes) {
  466. $_SESSION['elFinderNetVolumes'] = $volumes;
  467. }
  468. /**
  469. * Get plugin instance & set to $this->plugins
  470. *
  471. * @param string $name Plugin name (dirctory name)
  472. * @param array $opts Plugin options (optional)
  473. * @return object | bool Plugin object instance Or false
  474. * @author Naoki Sawada
  475. */
  476. protected function getPluginInstance($name, $opts = array()) {
  477. $key = strtolower($name);
  478. if (! isset($this->plugins[$key])) {
  479. $p_file = dirname(__FILE__) . DIRECTORY_SEPARATOR . 'plugins' . DIRECTORY_SEPARATOR . $name . DIRECTORY_SEPARATOR . 'plugin.php';
  480. if (is_file($p_file)) {
  481. require_once $p_file;
  482. $class = 'elFinderPlugin' . $name;
  483. $this->plugins[$key] = new $class($opts);
  484. } else {
  485. $this->plugins[$key] = false;
  486. }
  487. }
  488. return $this->plugins[$key];
  489. }
  490. /***************************************************************************/
  491. /* commands */
  492. /***************************************************************************/
  493. /**
  494. * Normalize error messages
  495. *
  496. * @return array
  497. * @author Dmitry (dio) Levashov
  498. **/
  499. public function error() {
  500. $errors = array();
  501. foreach (func_get_args() as $msg) {
  502. if (is_array($msg)) {
  503. $errors = array_merge($errors, $msg);
  504. } else {
  505. $errors[] = $msg;
  506. }
  507. }
  508. return count($errors) ? $errors : array(self::ERROR_UNKNOWN);
  509. }
  510. protected function netmount($args) {
  511. $options = array();
  512. $protocol = $args['protocol'];
  513. $driver = isset(self::$netDrivers[$protocol]) ? $protocol : '';
  514. $class = 'elfindervolume'.$protocol;
  515. if (!$driver) {
  516. return array('error' => $this->error(self::ERROR_NETMOUNT, $args['host'], self::ERROR_NETMOUNT_NO_DRIVER));
  517. }
  518. if (!$args['path']) {
  519. $args['path'] = '/';
  520. }
  521. foreach ($args as $k => $v) {
  522. if ($k != 'options' && $k != 'protocol' && $v) {
  523. $options[$k] = $v;
  524. }
  525. }
  526. if (is_array($args['options'])) {
  527. foreach ($args['options'] as $key => $value) {
  528. $options[$key] = $value;
  529. }
  530. }
  531. $volume = new $class();
  532. if ($volume->mount($options)) {
  533. $netVolumes = $this->getNetVolumes();
  534. $options['driver'] = $driver;
  535. $netVolumes[] = $options;
  536. $netVolumes = array_unique($netVolumes);
  537. $this->saveNetVolumes($netVolumes);
  538. return array('sync' => true);
  539. } else {
  540. return array('error' => $this->error(self::ERROR_NETMOUNT, $args['host'], implode(' ', $volume->error())));
  541. }
  542. }
  543. /**
  544. * "Open" directory
  545. * Return array with following elements
  546. * - cwd - opened dir info
  547. * - files - opened dir content [and dirs tree if $args[tree]]
  548. * - api - api version (if $args[init])
  549. * - uplMaxSize - if $args[init]
  550. * - error - on failed
  551. *
  552. * @param array command arguments
  553. * @return array
  554. * @author Dmitry (dio) Levashov
  555. **/
  556. protected function open($args) {
  557. $target = $args['target'];
  558. $init = !empty($args['init']);
  559. $tree = !empty($args['tree']);
  560. $volume = $this->volume($target);
  561. $cwd = $volume ? $volume->dir($target, true) : false;
  562. $hash = $init ? 'default folder' : '#'.$target;
  563. // on init request we can get invalid dir hash -
  564. // dir which can not be opened now, but remembered by client,
  565. // so open default dir
  566. if ((!$cwd || !$cwd['read']) && $init) {
  567. $volume = $this->default;
  568. $cwd = $volume->dir($volume->defaultPath(), true);
  569. }
  570. if (!$cwd) {
  571. return array('error' => $this->error(self::ERROR_OPEN, $hash, self::ERROR_DIR_NOT_FOUND));
  572. }
  573. if (!$cwd['read']) {
  574. return array('error' => $this->error(self::ERROR_OPEN, $hash, self::ERROR_PERM_DENIED));
  575. }
  576. $files = array();
  577. // get folders trees
  578. if ($args['tree']) {
  579. foreach ($this->volumes as $id => $v) {
  580. if (($tree = $v->tree('', 0, $cwd['hash'])) != false) {
  581. $files = array_merge($files, $tree);
  582. }
  583. }
  584. }
  585. // get current working directory files list and add to $files if not exists in it
  586. if (($ls = $volume->scandir($cwd['hash'])) === false) {
  587. return array('error' => $this->error(self::ERROR_OPEN, $cwd['name'], $volume->error()));
  588. }
  589. foreach ($ls as $file) {
  590. if (!in_array($file, $files)) {
  591. $files[] = $file;
  592. }
  593. }
  594. $result = array(
  595. 'cwd' => $cwd,
  596. 'options' => $volume->options($cwd['hash']),
  597. 'files' => $files
  598. );
  599. if (!empty($args['init'])) {
  600. $result['api'] = $this->version;
  601. $result['uplMaxSize'] = ini_get('upload_max_filesize');
  602. $result['uplMaxFile'] = ini_get('max_file_uploads');
  603. $result['netDrivers'] = array_keys(self::$netDrivers);
  604. }
  605. return $result;
  606. }
  607. /**
  608. * Return dir files names list
  609. *
  610. * @param array command arguments
  611. * @return array
  612. * @author Dmitry (dio) Levashov
  613. **/
  614. protected function ls($args) {
  615. $target = $args['target'];
  616. if (($volume = $this->volume($target)) == false
  617. || ($list = $volume->ls($target)) === false) {
  618. return array('error' => $this->error(self::ERROR_OPEN, '#'.$target));
  619. }
  620. return array('list' => $list);
  621. }
  622. /**
  623. * Return subdirs for required directory
  624. *
  625. * @param array command arguments
  626. * @return array
  627. * @author Dmitry (dio) Levashov
  628. **/
  629. protected function tree($args) {
  630. $target = $args['target'];
  631. if (($volume = $this->volume($target)) == false
  632. || ($tree = $volume->tree($target)) == false) {
  633. return array('error' => $this->error(self::ERROR_OPEN, '#'.$target));
  634. }
  635. return array('tree' => $tree);
  636. }
  637. /**
  638. * Return parents dir for required directory
  639. *
  640. * @param array command arguments
  641. * @return array
  642. * @author Dmitry (dio) Levashov
  643. **/
  644. protected function parents($args) {
  645. $target = $args['target'];
  646. if (($volume = $this->volume($target)) == false
  647. || ($tree = $volume->parents($target)) == false) {
  648. return array('error' => $this->error(self::ERROR_OPEN, '#'.$target));
  649. }
  650. return array('tree' => $tree);
  651. }
  652. /**
  653. * Return new created thumbnails list
  654. *
  655. * @param array command arguments
  656. * @return array
  657. * @author Dmitry (dio) Levashov
  658. **/
  659. protected function tmb($args) {
  660. $result = array('images' => array());
  661. $targets = $args['targets'];
  662. foreach ($targets as $target) {
  663. if (($volume = $this->volume($target)) != false
  664. && (($tmb = $volume->tmb($target)) != false)) {
  665. $result['images'][$target] = $tmb;
  666. }
  667. }
  668. return $result;
  669. }
  670. /**
  671. * Required to output file in browser when volume URL is not set
  672. * Return array contains opened file pointer, root itself and required headers
  673. *
  674. * @param array command arguments
  675. * @return array
  676. * @author Dmitry (dio) Levashov
  677. **/
  678. protected function file($args) {
  679. $target = $args['target'];
  680. $download = !empty($args['download']);
  681. $h403 = 'HTTP/1.x 403 Access Denied';
  682. $h404 = 'HTTP/1.x 404 Not Found';
  683. if (($volume = $this->volume($target)) == false) {
  684. return array('error' => 'File not found', 'header' => $h404, 'raw' => true);
  685. }
  686. if (($file = $volume->file($target)) == false) {
  687. return array('error' => 'File not found', 'header' => $h404, 'raw' => true);
  688. }
  689. if (!$file['read']) {
  690. return array('error' => 'Access denied', 'header' => $h403, 'raw' => true);
  691. }
  692. if (($fp = $volume->open($target)) == false) {
  693. return array('error' => 'File not found', 'header' => $h404, 'raw' => true);
  694. }
  695. if ($download) {
  696. $disp = 'attachment';
  697. $mime = 'application/octet-stream';
  698. } else {
  699. $disp = preg_match('/^(image|text)/i', $file['mime']) || $file['mime'] == 'application/x-shockwave-flash'
  700. ? 'inline'
  701. : 'attachment';
  702. $mime = $file['mime'];
  703. }
  704. $filenameEncoded = rawurlencode($file['name']);
  705. if (strpos($filenameEncoded, '%') === false) { // ASCII only
  706. $filename = 'filename="'.$file['name'].'"';
  707. } else {
  708. $ua = $_SERVER["HTTP_USER_AGENT"];
  709. if (preg_match('/MSIE [4-8]/', $ua)) { // IE < 9 do not support RFC 6266 (RFC 2231/RFC 5987)
  710. $filename = 'filename="'.$filenameEncoded.'"';
  711. } elseif (strpos($ua, 'Chrome') === false && strpos($ua, 'Safari') !== false) { // Safari
  712. $filename = 'filename="'.str_replace('"', '', $file['name']).'"';
  713. } else { // RFC 6266 (RFC 2231/RFC 5987)
  714. $filename = 'filename*=UTF-8\'\''.$filenameEncoded;
  715. }
  716. }
  717. $result = array(
  718. 'volume' => $volume,
  719. 'pointer' => $fp,
  720. 'info' => $file,
  721. 'header' => array(
  722. 'Content-Type: '.$mime,
  723. 'Content-Disposition: '.$disp.'; '.$filename,
  724. 'Content-Location: '.$file['name'],
  725. 'Content-Transfer-Encoding: binary',
  726. 'Content-Length: '.$file['size'],
  727. 'Connection: close'
  728. )
  729. );
  730. return $result;
  731. }
  732. /**
  733. * Count total files size
  734. *
  735. * @param array command arguments
  736. * @return array
  737. * @author Dmitry (dio) Levashov
  738. **/
  739. protected function size($args) {
  740. $size = 0;
  741. foreach ($args['targets'] as $target) {
  742. if (($volume = $this->volume($target)) == false
  743. || ($file = $volume->file($target)) == false
  744. || !$file['read']) {
  745. return array('error' => $this->error(self::ERROR_OPEN, '#'.$target));
  746. }
  747. $size += $volume->size($target);
  748. }
  749. return array('size' => $size);
  750. }
  751. /**
  752. * Create directory
  753. *
  754. * @param array command arguments
  755. * @return array
  756. * @author Dmitry (dio) Levashov
  757. **/
  758. protected function mkdir($args) {
  759. $target = $args['target'];
  760. $name = $args['name'];
  761. if (($volume = $this->volume($target)) == false) {
  762. return array('error' => $this->error(self::ERROR_MKDIR, $name, self::ERROR_TRGDIR_NOT_FOUND, '#'.$target));
  763. }
  764. return ($dir = $volume->mkdir($target, $name)) == false
  765. ? array('error' => $this->error(self::ERROR_MKDIR, $name, $volume->error()))
  766. : array('added' => array($dir));
  767. }
  768. /**
  769. * Create empty file
  770. *
  771. * @param array command arguments
  772. * @return array
  773. * @author Dmitry (dio) Levashov
  774. **/
  775. protected function mkfile($args) {
  776. $target = $args['target'];
  777. $name = $args['name'];
  778. if (($volume = $this->volume($target)) == false) {
  779. return array('error' => $this->error(self::ERROR_MKFILE, $name, self::ERROR_TRGDIR_NOT_FOUND, '#'.$target));
  780. }
  781. return ($file = $volume->mkfile($target, $args['name'])) == false
  782. ? array('error' => $this->error(self::ERROR_MKFILE, $name, $volume->error()))
  783. : array('added' => array($file));
  784. }
  785. /**
  786. * Rename file
  787. *
  788. * @param array $args
  789. * @return array
  790. * @author Dmitry (dio) Levashov
  791. **/
  792. protected function rename($args) {
  793. $target = $args['target'];
  794. $name = $args['name'];
  795. if (($volume = $this->volume($target)) == false
  796. || ($rm = $volume->file($target)) == false) {
  797. return array('error' => $this->error(self::ERROR_RENAME, '#'.$target, self::ERROR_FILE_NOT_FOUND));
  798. }
  799. $rm['realpath'] = $volume->realpath($target);
  800. return ($file = $volume->rename($target, $name)) == false
  801. ? array('error' => $this->error(self::ERROR_RENAME, $rm['name'], $volume->error()))
  802. : array('added' => array($file), 'removed' => array($rm));
  803. }
  804. /**
  805. * Duplicate file - create copy with "copy %d" suffix
  806. *
  807. * @param array $args command arguments
  808. * @return array
  809. * @author Dmitry (dio) Levashov
  810. **/
  811. protected function duplicate($args) {
  812. $targets = is_array($args['targets']) ? $args['targets'] : array();
  813. $result = array('added' => array());
  814. $suffix = empty($args['suffix']) ? 'copy' : $args['suffix'];
  815. foreach ($targets as $target) {
  816. if (($volume = $this->volume($target)) == false
  817. || ($src = $volume->file($target)) == false) {
  818. $result['warning'] = $this->error(self::ERROR_COPY, '#'.$target, self::ERROR_FILE_NOT_FOUND);
  819. break;
  820. }
  821. if (($file = $volume->duplicate($target, $suffix)) == false) {
  822. $result['warning'] = $this->error($volume->error());
  823. break;
  824. }
  825. $result['added'][] = $file;
  826. }
  827. return $result;
  828. }
  829. /**
  830. * Remove dirs/files
  831. *
  832. * @param array command arguments
  833. * @return array
  834. * @author Dmitry (dio) Levashov
  835. **/
  836. protected function rm($args) {
  837. $targets = is_array($args['targets']) ? $args['targets'] : array();
  838. $result = array('removed' => array());
  839. foreach ($targets as $target) {
  840. if (($volume = $this->volume($target)) == false) {
  841. $result['warning'] = $this->error(self::ERROR_RM, '#'.$target, self::ERROR_FILE_NOT_FOUND);
  842. return $result;
  843. }
  844. if (!$volume->rm($target)) {
  845. $result['warning'] = $this->error($volume->error());
  846. return $result;
  847. }
  848. }
  849. return $result;
  850. }
  851. /**
  852. * Get remote contents
  853. *
  854. * @param string $url target url
  855. * @param int $timeout timeout (sec)
  856. * @param int $redirect_max redirect max count
  857. * @param string $ua
  858. * @return string or bool(false)
  859. * @retval string contents
  860. * @retval false error
  861. * @author Naoki Sawada
  862. **/
  863. protected function get_remote_contents( $url, $timeout = 30, $redirect_max = 5, $ua = 'Mozilla/5.0' ) {
  864. $method = (function_exists('curl_exec') && !ini_get('safe_mode'))? 'curl_get_contents' : 'fsock_get_contents';
  865. return $this->$method( $url, $timeout, $redirect_max, $ua );
  866. }
  867. /**
  868. * Get remote contents with cURL
  869. *
  870. * @param string $url target url
  871. * @param int $timeout timeout (sec)
  872. * @param int $redirect_max redirect max count
  873. * @param string $ua
  874. * @return string or bool(false)
  875. * @retval string contents
  876. * @retval false error
  877. * @author Naoki Sawada
  878. **/
  879. protected function curl_get_contents( $url, $timeout, $redirect_max, $ua ){
  880. $ch = curl_init();
  881. curl_setopt( $ch, CURLOPT_URL, $url );
  882. curl_setopt( $ch, CURLOPT_HEADER, false );
  883. curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true );
  884. curl_setopt( $ch, CURLOPT_BINARYTRANSFER, true );
  885. curl_setopt( $ch, CURLOPT_TIMEOUT, $timeout );
  886. curl_setopt( $ch, CURLOPT_SSL_VERIFYPEER, false );
  887. curl_setopt( $ch, CURLOPT_FOLLOWLOCATION, 1);
  888. curl_setopt( $ch, CURLOPT_MAXREDIRS, $redirect_max);
  889. curl_setopt( $ch, CURLOPT_USERAGENT, $ua);
  890. $result = curl_exec( $ch );
  891. curl_close( $ch );
  892. return $result;
  893. }
  894. /**
  895. * Get remote contents with fsockopen()
  896. *
  897. * @param string $url url
  898. * @param int $timeout timeout (sec)
  899. * @param int $redirect_max redirect max count
  900. * @param string $ua
  901. * @return string or bool(false)
  902. * @retval string contents
  903. * @retval false error
  904. * @author Naoki Sawada
  905. */
  906. protected function fsock_get_contents( $url, $timeout, $redirect_max, $ua ) {
  907. $connect_timeout = 3;
  908. $connect_try = 3;
  909. $method = 'GET';
  910. $readsize = 4096;
  911. $getSize = null;
  912. $headers = '';
  913. $arr = parse_url($url);
  914. if (!$arr){
  915. // Bad request
  916. return false;
  917. }
  918. // query
  919. $arr['query'] = isset($arr['query']) ? '?'.$arr['query'] : '';
  920. // port
  921. $arr['port'] = isset($arr['port']) ? $arr['port'] : (!empty($arr['https'])? 443 : 80);
  922. $url_base = $arr['scheme'].'://'.$arr['host'].':'.$arr['port'];
  923. $url_path = isset($arr['path']) ? $arr['path'] : '/';
  924. $uri = $url_path.$arr['query'];
  925. $query = $method.' '.$uri." HTTP/1.0\r\n";
  926. $query .= "Host: ".$arr['host']."\r\n";
  927. if (!empty($ua)) $query .= "User-Agent: ".$ua."\r\n";
  928. if (!is_null($getSize)) $query .= 'Range: bytes=0-' . ($getSize - 1) . "\r\n";
  929. $query .= $headers;
  930. $query .= "\r\n";
  931. $fp = $connect_try_count = 0;
  932. while( !$fp && $connect_try_count < $connect_try ) {
  933. $errno = 0;
  934. $errstr = "";
  935. $fp = @ fsockopen(
  936. $arr['https'].$arr['host'],
  937. $arr['port'],
  938. $errno,$errstr,$connect_timeout);
  939. if ($fp) break;
  940. $connect_try_count++;
  941. if (connection_aborted()) {
  942. exit();
  943. }
  944. sleep(1); // wait 1sec
  945. }
  946. $fwrite = 0;
  947. for ($written = 0; $written < strlen($query); $written += $fwrite) {
  948. $fwrite = fwrite($fp, substr($query, $written));
  949. if (!$fwrite) {
  950. break;
  951. }
  952. }
  953. $response = '';
  954. if ($timeout) {
  955. socket_set_timeout($fp, $timeout);
  956. }
  957. $_response = true;
  958. while ($_response && (is_null($getSize) || strlen($response) < $getSize)) {
  959. if (connection_aborted()) {
  960. exit();
  961. }
  962. if ($_response = fread($fp, $readsize)) {
  963. $response .= $_response;
  964. }
  965. }
  966. if ($timeout) {
  967. $_status = socket_get_status($fp);
  968. if ($_status['timed_out']) {
  969. fclose($fp);
  970. return false; // Request Time-out
  971. }
  972. }
  973. fclose($fp);
  974. $resp = array_pad(explode("\r\n\r\n",$response,2), 2, '');
  975. $rccd = array_pad(explode(' ',$resp[0],3), 3, ''); // array('HTTP/1.1','200','OK\r\n...')
  976. $rc = (int)$rccd[1];
  977. // Redirect
  978. switch ($rc) {
  979. case 307: // Temporary Redirect
  980. case 303: // See Other
  981. case 302: // Moved Temporarily
  982. case 301: // Moved Permanently
  983. $matches = array();
  984. if (preg_match('/^Location: (.+?)(#.+)?$/im',$resp[0],$matches) && --$redirect_max > 0) {
  985. $url = trim($matches[1]);
  986. $hash = isset($matches[2])? trim($matches[2]) : '';
  987. if (!preg_match('/^https?:\//',$url)) { // no scheme
  988. if ($url{0} != '/') { // Relative path
  989. // to Absolute path
  990. $url = substr($url_path,0,strrpos($url_path,'/')).'/'.$url;
  991. }
  992. // add sheme,host
  993. $url = $url_base.$url;
  994. }
  995. return $this->fsock_get_contents( $url, $timeout, $redirect_max, $ua );
  996. }
  997. }
  998. return $resp[1]; // Data
  999. }
  1000. /**
  1001. * Parse Data URI scheme
  1002. *
  1003. * @param string $str
  1004. * @param array $extTable
  1005. * @return array
  1006. * @author Naoki Sawada
  1007. */
  1008. protected function parse_data_scheme( $str, $extTable ) {
  1009. $data = $name = '';
  1010. if ($fp = fopen('data://'.substr($str, 5), 'rb')) {
  1011. if ($data = stream_get_contents($fp)) {
  1012. $meta = stream_get_meta_data($fp);
  1013. $ext = isset($extTable[$meta['mediatype']])? '.' . $extTable[$meta['mediatype']] : '';
  1014. $name = substr(md5($data), 0, 8) . $ext;
  1015. }
  1016. fclose($fp);
  1017. }
  1018. return array($data, $name);
  1019. }
  1020. /**
  1021. * Save uploaded files
  1022. *
  1023. * @param array
  1024. * @return array
  1025. * @author Dmitry (dio) Levashov
  1026. **/
  1027. protected function upload($args) {
  1028. $target = $args['target'];
  1029. $volume = $this->volume($target);
  1030. $files = isset($args['FILES']['upload']) && is_array($args['FILES']['upload']) ? $args['FILES']['upload'] : array();
  1031. $result = array('added' => array(), 'header' => empty($args['html']) ? false : 'Content-Type: text/html; charset=utf-8');
  1032. $paths = $args['upload_path']? $args['upload_path'] : array();
  1033. if (!$volume) {
  1034. return array('error' => $this->error(self::ERROR_UPLOAD, self::ERROR_TRGDIR_NOT_FOUND, '#'.$target), 'header' => $header);
  1035. }
  1036. // file extentions table by MIME
  1037. $extTable = array_flip($volume->getMimeTable());
  1038. $non_uploads = array();
  1039. if (empty($files)) {
  1040. if (isset($args['upload']) && is_array($args['upload'])) {
  1041. foreach($args['upload'] as $i => $url) {
  1042. // check is data:
  1043. if (substr($url, 0, 5) === 'data:') {
  1044. list($data, $args['name'][$i]) = $this->parse_data_scheme($url, $extTable);
  1045. } else {
  1046. $data = $this->get_remote_contents($url);
  1047. }
  1048. if ($data) {
  1049. $_name = isset($args['name'][$i])? $args['name'][$i] : preg_replace('~^.*?([^/#?]+)(?:\?.*)?(?:#.*)?$~', '$1', rawurldecode($url));
  1050. if ($_name) {
  1051. $_ext = '';
  1052. if (preg_match('/(\.[a-z0-9]{1,7})$/', $_name, $_match)) {
  1053. $_ext = $_match[1];
  1054. }
  1055. $tmpfname = DIRECTORY_SEPARATOR . 'ELF_FATCH_' . md5($url.microtime()) . $_ext;
  1056. $tmpPath = sys_get_temp_dir();
  1057. if (! @ touch($tmpPath . $tmpfname)) {
  1058. if ($tmpPath = $volume->getTempPath()) {
  1059. $tmpfname = $tmpPath . $tmpfname;
  1060. } else {
  1061. $tmpfname = '';
  1062. }
  1063. } else {
  1064. $tmpfname = $tmpPath . $tmpfname;
  1065. }
  1066. if ($tmpfname) {
  1067. if (file_put_contents($tmpfname, $data)) {
  1068. $non_uploads[$tmpfname] = true;
  1069. $files['tmp_name'][$i] = $tmpfname;
  1070. $files['name'][$i] = preg_replace('/[\/\\?*:|"<>]/', '_', $_name);
  1071. $files['error'][$i] = 0;
  1072. } else {
  1073. @ unlink($tmpfname);
  1074. }
  1075. }
  1076. }
  1077. }
  1078. }
  1079. }
  1080. if (empty($files)) {
  1081. return array('error' => $this->error(self::ERROR_UPLOAD, self::ERROR_UPLOAD_NO_FILES), 'header' => $header);
  1082. }
  1083. }
  1084. foreach ($files['name'] as $i => $name) {
  1085. if (($error = $files['error'][$i]) > 0) {
  1086. $result['warning'] = $this->error(self::ERROR_UPLOAD_FILE, $name, $error == UPLOAD_ERR_INI_SIZE || $error == UPLOAD_ERR_FORM_SIZE ? self::ERROR_UPLOAD_FILE_SIZE : self::ERROR_UPLOAD_TRANSFER);
  1087. $this->uploadDebug = 'Upload error code: '.$error;
  1088. break;
  1089. }
  1090. $tmpname = $files['tmp_name'][$i];
  1091. $path = ($paths && !empty($paths[$i]))? $paths[$i] : '';
  1092. if ($name === 'blob') {
  1093. // for form clipboard with Google Chrome
  1094. $type = $files['type'][$i];
  1095. $ext = isset($extTable[$type])? '.' . $extTable[$type] : '';
  1096. $name = substr(md5(basename($tmpname)), 0, 8) . $ext;
  1097. }
  1098. // do hook function 'upload.presave'
  1099. if (! empty($this->listeners['upload.presave'])) {
  1100. foreach($this->listeners['upload.presave'] as $handler) {
  1101. call_user_func_array($handler, array(&$path, &$name, $tmpname, $this, $volume));
  1102. }
  1103. }
  1104. if (($fp = fopen($tmpname, 'rb')) == false) {
  1105. $result['warning'] = $this->error(self::ERROR_UPLOAD_FILE, $name, self::ERROR_UPLOAD_TRANSFER);
  1106. $this->uploadDebug = 'Upload error: unable open tmp file';
  1107. if (! is_uploaded_file($tmpname)) {
  1108. if (@ unlink($tmpname)) unset($non_uploads[$tmpfname]);
  1109. continue;
  1110. }
  1111. break;
  1112. }
  1113. if ($path) {
  1114. $_target = $volume->getUploadTaget($target, $path, $result);
  1115. } else {
  1116. $_target = $target;
  1117. }
  1118. if (! $_target || ($file = $volume->upload($fp, $_target, $name, $tmpname)) === false) {
  1119. $result['warning'] = $this->error(self::ERROR_UPLOAD_FILE, $name, $volume->error());
  1120. fclose($fp);
  1121. if (! is_uploaded_file($tmpname)) {
  1122. if (@ unlink($tmpname)) unset($non_uploads[$tmpfname]);;
  1123. continue;
  1124. }
  1125. break;
  1126. }
  1127. fclose($fp);
  1128. if (! is_uploaded_file($tmpname) && @ unlink($tmpname)) unset($non_uploads[$tmpfname]);
  1129. $result['added'][] = $file;
  1130. }
  1131. if ($non_uploads) {
  1132. foreach(array_keys($non_uploads) as $_temp) {
  1133. @ unlink($_temp);
  1134. }
  1135. }
  1136. return $result;
  1137. }
  1138. /**
  1139. * Copy/move files into new destination
  1140. *
  1141. * @param array command arguments
  1142. * @return array
  1143. * @author Dmitry (dio) Levashov
  1144. **/
  1145. protected function paste($args) {
  1146. $dst = $args['dst'];
  1147. $targets = is_array($args['targets']) ? $args['targets'] : array();
  1148. $cut = !empty($args['cut']);
  1149. $error = $cut ? self::ERROR_MOVE : self::ERROR_COPY;
  1150. $result = array('added' => array(), 'removed' => array());
  1151. if (($dstVolume = $this->volume($dst)) == false) {
  1152. return array('error' => $this->error($error, '#'.$targets[0], self::ERROR_TRGDIR_NOT_FOUND, '#'.$dst));
  1153. }
  1154. foreach ($targets as $target) {
  1155. if (($srcVolume = $this->volume($target)) == false) {
  1156. $result['warning'] = $this->error($error, '#'.$target, self::ERROR_FILE_NOT_FOUND);
  1157. break;
  1158. }
  1159. if (($file = $dstVolume->paste($srcVolume, $target, $dst, $cut)) == false) {
  1160. $result['warning'] = $this->error($dstVolume->error());
  1161. break;
  1162. }
  1163. $result['added'][] = $file;
  1164. }
  1165. return $result;
  1166. }
  1167. /**
  1168. * Return file content
  1169. *
  1170. * @param array $args command arguments
  1171. * @return array
  1172. * @author Dmitry (dio) Levashov
  1173. **/
  1174. protected function get($args) {
  1175. $target = $args['target'];
  1176. $volume = $this->volume($target);
  1177. if (!$volume || ($file = $volume->file($target)) == false) {
  1178. return array('error' => $this->error(self::ERROR_OPEN, '#'.$target, self::ERROR_FILE_NOT_FOUND));
  1179. }
  1180. if (($content = $volume->getContents($target)) === false) {
  1181. return array('error' => $this->error(self::ERROR_OPEN, $volume->path($target), $volume->error()));
  1182. }
  1183. $json = json_encode($content);
  1184. if ($json == 'null' && strlen($json) < strlen($content)) {
  1185. return array('error' => $this->error(self::ERROR_NOT_UTF8_CONTENT, $volume->path($target)));
  1186. }
  1187. return array('content' => $content);
  1188. }
  1189. /**
  1190. * Save content into text file
  1191. *
  1192. * @return array
  1193. * @author Dmitry (dio) Levashov
  1194. **/
  1195. protected function put($args) {
  1196. $target = $args['target'];
  1197. if (($volume = $this->volume($target)) == false
  1198. || ($file = $volume->file($target)) == false) {
  1199. return array('error' => $this->error(self::ERROR_SAVE, '#'.$target, self::ERROR_FILE_NOT_FOUND));
  1200. }
  1201. if (($file = $volume->putContents($target, $args['content'])) == false) {
  1202. return array('error' => $this->error(self::ERROR_SAVE, $volume->path($target), $volume->error()));
  1203. }
  1204. return array('changed' => array($file));
  1205. }
  1206. /**
  1207. * Extract files from archive
  1208. *
  1209. * @param array $args command arguments
  1210. * @return array
  1211. * @author Dmitry (dio) Levashov,
  1212. * @author Alexey Sukhotin
  1213. **/
  1214. protected function extract($args) {
  1215. $target = $args['target'];
  1216. $mimes = !empty($args['mimes']) && is_array($args['mimes']) ? $args['mimes'] : array();
  1217. $error = array(self::ERROR_EXTRACT, '#'.$target);
  1218. if (($volume = $this->volume($target)) == false
  1219. || ($file = $volume->file($target)) == false) {
  1220. return array('error' => $this->error(self::ERROR_EXTRACT, '#'.$target, self::ERROR_FILE_NOT_FOUND));
  1221. }
  1222. return ($file = $volume->extract($target))
  1223. ? array('added' => array($file))
  1224. : array('error' => $this->error(self::ERROR_EXTRACT, $volume->path($target), $volume->error()));
  1225. }
  1226. /**
  1227. * Create archive
  1228. *
  1229. * @param array $args command arguments
  1230. * @return array
  1231. * @author Dmitry (dio) Levashov,
  1232. * @author Alexey Sukhotin
  1233. **/
  1234. protected function archive($args) {
  1235. $type = $args['type'];
  1236. $targets = isset($args['targets']) && is_array($args['targets']) ? $args['targets'] : array();
  1237. if (($volume = $this->volume($targets[0])) == false) {
  1238. return $this->error(self::ERROR_ARCHIVE, self::ERROR_TRGDIR_NOT_FOUND);
  1239. }
  1240. return ($file = $volume->archive($targets, $args['type']))
  1241. ? array('added' => array($file))
  1242. : array('error' => $this->error(self::ERROR_ARCHIVE, $volume->error()));
  1243. }
  1244. /**
  1245. * Search files
  1246. *
  1247. * @param array $args command arguments
  1248. * @return array
  1249. * @author Dmitry Levashov
  1250. **/
  1251. protected function search($args) {
  1252. $q = trim($args['q']);
  1253. $mimes = !empty($args['mimes']) && is_array($args['mimes']) ? $args['mimes'] : array();
  1254. $result = array();
  1255. foreach ($this->volumes as $volume) {
  1256. $result = array_merge($result, $volume->search($q, $mimes));
  1257. }
  1258. return array('files' => $result);
  1259. }
  1260. /**
  1261. * Return file info (used by client "places" ui)
  1262. *
  1263. * @param array $args command arguments
  1264. * @return array
  1265. * @author Dmitry Levashov
  1266. **/
  1267. protected function info($args) {
  1268. $files = array();
  1269. foreach ($args['targets'] as $hash) {
  1270. if (($volume = $this->volume($hash)) != false
  1271. && ($info = $volume->file($hash)) != false) {
  1272. $files[] = $info;
  1273. }
  1274. }
  1275. return array('files' => $files);
  1276. }
  1277. /**
  1278. * Return image dimmensions
  1279. *
  1280. * @param array $args command arguments
  1281. * @return array
  1282. * @author Dmitry (dio) Levashov
  1283. **/
  1284. protected function dim($args) {
  1285. $target = $args['target'];
  1286. if (($volume = $this->volume($target)) != false) {
  1287. $dim = $volume->dimensions($target);
  1288. return $dim ? array('dim' => $dim) : array();
  1289. }
  1290. return array();
  1291. }
  1292. /**
  1293. * Resize image
  1294. *
  1295. * @param array command arguments
  1296. * @return array
  1297. * @author Dmitry (dio) Levashov
  1298. * @author Alexey Sukhotin
  1299. **/
  1300. protected function resize($args) {
  1301. $target = $args['target'];
  1302. $width = $args['width'];
  1303. $height = $args['height'];
  1304. $x = (int)$args['x'];
  1305. $y = (int)$args['y'];
  1306. $mode = $args['mode'];
  1307. $bg = null;
  1308. $degree = (int)$args['degree'];
  1309. if (($volume = $this->volume($target)) == false
  1310. || ($file = $volume->file($target)) == false) {
  1311. return array('error' => $this->error(self::ERROR_RESIZE, '#'.$target, self::ERROR_FILE_NOT_FOUND));
  1312. }
  1313. return ($file = $volume->resize($target, $width, $height, $x, $y, $mode, $bg, $degree))
  1314. ? array('changed' => array($file))
  1315. : array('error' => $this->error(self::ERROR_RESIZE, $volume->path($target), $volume->error()));
  1316. }
  1317. /***************************************************************************/
  1318. /* utils */
  1319. /***************************************************************************/
  1320. /**
  1321. * Return root - file's owner
  1322. *
  1323. * @param string file hash
  1324. * @return elFinderStorageDriver
  1325. * @author Dmitry (dio) Levashov
  1326. **/
  1327. protected function volume($hash) {
  1328. foreach ($this->volumes as $id => $v) {
  1329. if (strpos(''.$hash, $id) === 0) {
  1330. return $this->volumes[$id];
  1331. }
  1332. }
  1333. return false;
  1334. }
  1335. /**
  1336. * Return files info array
  1337. *
  1338. * @param array $data one file info or files info
  1339. * @return array
  1340. * @author Dmitry (dio) Levashov
  1341. **/
  1342. protected function toArray($data) {
  1343. return isset($data['hash']) || !is_array($data) ? array($data) : $data;
  1344. }
  1345. /**
  1346. * Return fils hashes list
  1347. *
  1348. * @param array $files files info
  1349. * @return array
  1350. * @author Dmitry (dio) Levashov
  1351. **/
  1352. protected function hashes($files) {
  1353. $ret = array();
  1354. foreach ($files as $file) {
  1355. $ret[] = $file['hash'];
  1356. }
  1357. return $ret;
  1358. }
  1359. /**
  1360. * Remove from files list hidden files and files with required mime types
  1361. *
  1362. * @param array $files files info
  1363. * @return array
  1364. * @author Dmitry (dio) Levashov
  1365. **/
  1366. protected function filter($files) {
  1367. foreach ($files as $i => $file) {
  1368. if (!empty($file['hidden']) || !$this->default->mimeAccepted($file['mime'])) {
  1369. unset($files[$i]);
  1370. }
  1371. }
  1372. return array_merge($files, array());
  1373. }
  1374. protected function utime() {
  1375. $time = explode(" ", microtime());
  1376. return (double)$time[1] + (double)$time[0];
  1377. }
  1378. } // END class