helper.php 32 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048
  1. <?php
  2. /**
  3. * @author Arthur Schiwon <blizzz@owncloud.com>
  4. * @author Bart Visscher <bartv@thisnet.nl>
  5. * @author Björn Schießle <schiessle@owncloud.com>
  6. * @author Christopher Schäpers <kondou@ts.unde.re>
  7. * @author Fabian Henze <flyser42@gmx.de>
  8. * @author Felix Moeller <mail@felixmoeller.de>
  9. * @author François Kubler <francois@kubler.org>
  10. * @author Frank Karlitschek <frank@owncloud.org>
  11. * @author Georg Ehrke <georg@owncloud.com>
  12. * @author Jakob Sack <mail@jakobsack.de>
  13. * @author Jan-Christoph Borchardt <hey@jancborchardt.net>
  14. * @author Joas Schilling <nickvergessen@owncloud.com>
  15. * @author Jörn Friedrich Dreyer <jfd@butonic.de>
  16. * @author Lukas Reschke <lukas@owncloud.com>
  17. * @author Michael Gapczynski <GapczynskiM@gmail.com>
  18. * @author Morris Jobke <hey@morrisjobke.de>
  19. * @author Olivier Paroz <owncloud@interfasys.ch>
  20. * @author Owen Winkler <a_github@midnightcircus.com>
  21. * @author Pellaeon Lin <nfsmwlin@gmail.com>
  22. * @author Robin Appelman <icewind@owncloud.com>
  23. * @author Robin McCorkell <rmccorkell@karoshi.org.uk>
  24. * @author Roeland Jago Douma <roeland@famdouma.nl>
  25. * @author Simon Könnecke <simonkoennecke@gmail.com>
  26. * @author Thomas Müller <thomas.mueller@tmit.eu>
  27. * @author Thomas Tanghus <thomas@tanghus.net>
  28. * @author Valerio Ponte <valerio.ponte@gmail.com>
  29. * @author Vincent Petry <pvince81@owncloud.com>
  30. *
  31. * @copyright Copyright (c) 2015, ownCloud, Inc.
  32. * @license AGPL-3.0
  33. *
  34. * This code is free software: you can redistribute it and/or modify
  35. * it under the terms of the GNU Affero General Public License, version 3,
  36. * as published by the Free Software Foundation.
  37. *
  38. * This program is distributed in the hope that it will be useful,
  39. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  40. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  41. * GNU Affero General Public License for more details.
  42. *
  43. * You should have received a copy of the GNU Affero General Public License, version 3,
  44. * along with this program. If not, see <http://www.gnu.org/licenses/>
  45. *
  46. */
  47. use Symfony\Component\Process\ExecutableFinder;
  48. /**
  49. * Collection of useful functions
  50. */
  51. class OC_Helper {
  52. private static $mimetypeIcons = array();
  53. private static $mimetypeDetector;
  54. private static $templateManager;
  55. /** @var string[] */
  56. private static $mimeTypeAlias = array(
  57. 'application/octet-stream' => 'file', // use file icon as fallback
  58. 'application/illustrator' => 'image/vector',
  59. 'application/postscript' => 'image/vector',
  60. 'image/svg+xml' => 'image/vector',
  61. 'application/coreldraw' => 'image',
  62. 'application/x-gimp' => 'image',
  63. 'application/x-photoshop' => 'image',
  64. 'application/x-dcraw' => 'image',
  65. 'application/font-sfnt' => 'font',
  66. 'application/x-font' => 'font',
  67. 'application/font-woff' => 'font',
  68. 'application/vnd.ms-fontobject' => 'font',
  69. 'application/json' => 'text/code',
  70. 'application/x-perl' => 'text/code',
  71. 'application/x-php' => 'text/code',
  72. 'text/x-shellscript' => 'text/code',
  73. 'application/yaml' => 'text/code',
  74. 'application/xml' => 'text/html',
  75. 'text/css' => 'text/code',
  76. 'application/x-tex' => 'text',
  77. 'application/x-compressed' => 'package/x-generic',
  78. 'application/x-7z-compressed' => 'package/x-generic',
  79. 'application/x-deb' => 'package/x-generic',
  80. 'application/x-gzip' => 'package/x-generic',
  81. 'application/x-rar-compressed' => 'package/x-generic',
  82. 'application/x-tar' => 'package/x-generic',
  83. 'application/vnd.android.package-archive' => 'package/x-generic',
  84. 'application/zip' => 'package/x-generic',
  85. 'application/msword' => 'x-office/document',
  86. 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' => 'x-office/document',
  87. 'application/vnd.openxmlformats-officedocument.wordprocessingml.template' => 'x-office/document',
  88. 'application/vnd.ms-word.document.macroEnabled.12' => 'x-office/document',
  89. 'application/vnd.ms-word.template.macroEnabled.12' => 'x-office/document',
  90. 'application/vnd.oasis.opendocument.text' => 'x-office/document',
  91. 'application/vnd.oasis.opendocument.text-template' => 'x-office/document',
  92. 'application/vnd.oasis.opendocument.text-web' => 'x-office/document',
  93. 'application/vnd.oasis.opendocument.text-master' => 'x-office/document',
  94. 'application/mspowerpoint' => 'x-office/presentation',
  95. 'application/vnd.ms-powerpoint' => 'x-office/presentation',
  96. 'application/vnd.openxmlformats-officedocument.presentationml.presentation' => 'x-office/presentation',
  97. 'application/vnd.openxmlformats-officedocument.presentationml.template' => 'x-office/presentation',
  98. 'application/vnd.openxmlformats-officedocument.presentationml.slideshow' => 'x-office/presentation',
  99. 'application/vnd.ms-powerpoint.addin.macroEnabled.12' => 'x-office/presentation',
  100. 'application/vnd.ms-powerpoint.presentation.macroEnabled.12' => 'x-office/presentation',
  101. 'application/vnd.ms-powerpoint.template.macroEnabled.12' => 'x-office/presentation',
  102. 'application/vnd.ms-powerpoint.slideshow.macroEnabled.12' => 'x-office/presentation',
  103. 'application/vnd.oasis.opendocument.presentation' => 'x-office/presentation',
  104. 'application/vnd.oasis.opendocument.presentation-template' => 'x-office/presentation',
  105. 'application/msexcel' => 'x-office/spreadsheet',
  106. 'application/vnd.ms-excel' => 'x-office/spreadsheet',
  107. 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' => 'x-office/spreadsheet',
  108. 'application/vnd.openxmlformats-officedocument.spreadsheetml.template' => 'x-office/spreadsheet',
  109. 'application/vnd.ms-excel.sheet.macroEnabled.12' => 'x-office/spreadsheet',
  110. 'application/vnd.ms-excel.template.macroEnabled.12' => 'x-office/spreadsheet',
  111. 'application/vnd.ms-excel.addin.macroEnabled.12' => 'x-office/spreadsheet',
  112. 'application/vnd.ms-excel.sheet.binary.macroEnabled.12' => 'x-office/spreadsheet',
  113. 'application/vnd.oasis.opendocument.spreadsheet' => 'x-office/spreadsheet',
  114. 'application/vnd.oasis.opendocument.spreadsheet-template' => 'x-office/spreadsheet',
  115. 'text/csv' => 'x-office/spreadsheet',
  116. 'application/msaccess' => 'database',
  117. );
  118. /**
  119. * Creates an url using a defined route
  120. * @param string $route
  121. * @param array $parameters with param=>value, will be appended to the returned url
  122. * @return string the url
  123. * @deprecated Use \OC::$server->getURLGenerator()->linkToRoute($route, $parameters)
  124. *
  125. * Returns a url to the given app and file.
  126. */
  127. public static function linkToRoute($route, $parameters = array()) {
  128. return OC::$server->getURLGenerator()->linkToRoute($route, $parameters);
  129. }
  130. /**
  131. * Creates an url
  132. * @param string $app app
  133. * @param string $file file
  134. * @param array $args array with param=>value, will be appended to the returned url
  135. * The value of $args will be urlencoded
  136. * @return string the url
  137. * @deprecated Use \OC::$server->getURLGenerator()->linkTo($app, $file, $args)
  138. *
  139. * Returns a url to the given app and file.
  140. */
  141. public static function linkTo( $app, $file, $args = array() ) {
  142. return OC::$server->getURLGenerator()->linkTo($app, $file, $args);
  143. }
  144. /**
  145. * @param string $key
  146. * @return string url to the online documentation
  147. * @deprecated Use \OC::$server->getURLGenerator()->linkToDocs($key)
  148. */
  149. public static function linkToDocs($key) {
  150. return OC::$server->getURLGenerator()->linkToDocs($key);
  151. }
  152. /**
  153. * Creates an absolute url
  154. * @param string $app app
  155. * @param string $file file
  156. * @param array $args array with param=>value, will be appended to the returned url
  157. * The value of $args will be urlencoded
  158. * @return string the url
  159. *
  160. * Returns a absolute url to the given app and file.
  161. */
  162. public static function linkToAbsolute($app, $file, $args = array()) {
  163. return OC::$server->getURLGenerator()->getAbsoluteURL(
  164. self::linkTo($app, $file, $args)
  165. );
  166. }
  167. /**
  168. * Makes an $url absolute
  169. * @param string $url the url
  170. * @return string the absolute url
  171. * @deprecated Use \OC::$server->getURLGenerator()->getAbsoluteURL($url)
  172. *
  173. * Returns a absolute url to the given app and file.
  174. */
  175. public static function makeURLAbsolute($url) {
  176. return OC::$server->getURLGenerator()->getAbsoluteURL($url);
  177. }
  178. /**
  179. * Creates an url for remote use
  180. * @param string $service id
  181. * @return string the url
  182. *
  183. * Returns a url to the given service.
  184. */
  185. public static function linkToRemoteBase($service) {
  186. return self::linkTo('', 'remote.php') . '/' . $service;
  187. }
  188. /**
  189. * Creates an absolute url for remote use
  190. * @param string $service id
  191. * @param bool $add_slash
  192. * @return string the url
  193. *
  194. * Returns a absolute url to the given service.
  195. */
  196. public static function linkToRemote($service, $add_slash = true) {
  197. return OC::$server->getURLGenerator()->getAbsoluteURL(
  198. self::linkToRemoteBase($service)
  199. . (($add_slash && $service[strlen($service) - 1] != '/') ? '/' : '')
  200. );
  201. }
  202. /**
  203. * Creates an absolute url for public use
  204. * @param string $service id
  205. * @param bool $add_slash
  206. * @return string the url
  207. *
  208. * Returns a absolute url to the given service.
  209. */
  210. public static function linkToPublic($service, $add_slash = false) {
  211. if ($service === 'files') {
  212. $url = OC::$server->getURLGenerator()->getAbsoluteURL('/s');
  213. } else {
  214. $url = OC::$server->getURLGenerator()->getAbsoluteURL(self::linkTo('', 'public.php').'?service='.$service);
  215. }
  216. return $url . (($add_slash && $service[strlen($service) - 1] != '/') ? '/' : '');
  217. }
  218. /**
  219. * Creates path to an image
  220. * @param string $app app
  221. * @param string $image image name
  222. * @return string the url
  223. * @deprecated Use \OC::$server->getURLGenerator()->imagePath($app, $image)
  224. *
  225. * Returns the path to the image.
  226. */
  227. public static function imagePath($app, $image) {
  228. return OC::$server->getURLGenerator()->imagePath($app, $image);
  229. }
  230. /**
  231. * get path to icon of file type
  232. * @param string $mimetype mimetype
  233. * @return string the url
  234. *
  235. * Returns the path to the image of this file type.
  236. */
  237. public static function mimetypeIcon($mimetype) {
  238. if (isset(self::$mimeTypeAlias[$mimetype])) {
  239. $mimetype = self::$mimeTypeAlias[$mimetype];
  240. }
  241. if (isset(self::$mimetypeIcons[$mimetype])) {
  242. return self::$mimetypeIcons[$mimetype];
  243. }
  244. // Replace slash and backslash with a minus
  245. $icon = str_replace('/', '-', $mimetype);
  246. $icon = str_replace('\\', '-', $icon);
  247. // Is it a dir?
  248. if ($mimetype === 'dir') {
  249. self::$mimetypeIcons[$mimetype] = OC::$WEBROOT . '/core/img/filetypes/folder.png';
  250. return OC::$WEBROOT . '/core/img/filetypes/folder.png';
  251. }
  252. if ($mimetype === 'dir-shared') {
  253. self::$mimetypeIcons[$mimetype] = OC::$WEBROOT . '/core/img/filetypes/folder-shared.png';
  254. return OC::$WEBROOT . '/core/img/filetypes/folder-shared.png';
  255. }
  256. if ($mimetype === 'dir-external') {
  257. self::$mimetypeIcons[$mimetype] = OC::$WEBROOT . '/core/img/filetypes/folder-external.png';
  258. return OC::$WEBROOT . '/core/img/filetypes/folder-external.png';
  259. }
  260. // Icon exists?
  261. if (file_exists(OC::$SERVERROOT . '/core/img/filetypes/' . $icon . '.png')) {
  262. self::$mimetypeIcons[$mimetype] = OC::$WEBROOT . '/core/img/filetypes/' . $icon . '.png';
  263. return OC::$WEBROOT . '/core/img/filetypes/' . $icon . '.png';
  264. }
  265. // Try only the first part of the filetype
  266. $mimePart = substr($icon, 0, strpos($icon, '-'));
  267. if (file_exists(OC::$SERVERROOT . '/core/img/filetypes/' . $mimePart . '.png')) {
  268. self::$mimetypeIcons[$mimetype] = OC::$WEBROOT . '/core/img/filetypes/' . $mimePart . '.png';
  269. return OC::$WEBROOT . '/core/img/filetypes/' . $mimePart . '.png';
  270. } else {
  271. self::$mimetypeIcons[$mimetype] = OC::$WEBROOT . '/core/img/filetypes/file.png';
  272. return OC::$WEBROOT . '/core/img/filetypes/file.png';
  273. }
  274. }
  275. /**
  276. * get path to preview of file
  277. * @param string $path path
  278. * @return string the url
  279. *
  280. * Returns the path to the preview of the file.
  281. */
  282. public static function previewIcon($path) {
  283. return self::linkToRoute( 'core_ajax_preview', array('x' => 36, 'y' => 36, 'file' => $path ));
  284. }
  285. public static function publicPreviewIcon( $path, $token ) {
  286. return self::linkToRoute( 'core_ajax_public_preview', array('x' => 36, 'y' => 36, 'file' => $path, 't' => $token));
  287. }
  288. /**
  289. * shows whether the user has an avatar
  290. * @param string $user username
  291. * @return bool avatar set or not
  292. **/
  293. public static function userAvatarSet($user) {
  294. $avatar = new \OC\Avatar($user);
  295. return $avatar->exists();
  296. }
  297. /**
  298. * Make a human file size
  299. * @param int $bytes file size in bytes
  300. * @return string a human readable file size
  301. *
  302. * Makes 2048 to 2 kB.
  303. */
  304. public static function humanFileSize($bytes) {
  305. if ($bytes < 0) {
  306. return "?";
  307. }
  308. if ($bytes < 1024) {
  309. return "$bytes B";
  310. }
  311. $bytes = round($bytes / 1024, 0);
  312. if ($bytes < 1024) {
  313. return "$bytes kB";
  314. }
  315. $bytes = round($bytes / 1024, 1);
  316. if ($bytes < 1024) {
  317. return "$bytes MB";
  318. }
  319. $bytes = round($bytes / 1024, 1);
  320. if ($bytes < 1024) {
  321. return "$bytes GB";
  322. }
  323. $bytes = round($bytes / 1024, 1);
  324. if ($bytes < 1024) {
  325. return "$bytes TB";
  326. }
  327. $bytes = round($bytes / 1024, 1);
  328. return "$bytes PB";
  329. }
  330. /**
  331. * Make a php file size
  332. * @param int $bytes file size in bytes
  333. * @return string a php parseable file size
  334. *
  335. * Makes 2048 to 2k and 2^41 to 2048G
  336. */
  337. public static function phpFileSize($bytes) {
  338. if ($bytes < 0) {
  339. return "?";
  340. }
  341. if ($bytes < 1024) {
  342. return $bytes . "B";
  343. }
  344. $bytes = round($bytes / 1024, 1);
  345. if ($bytes < 1024) {
  346. return $bytes . "K";
  347. }
  348. $bytes = round($bytes / 1024, 1);
  349. if ($bytes < 1024) {
  350. return $bytes . "M";
  351. }
  352. $bytes = round($bytes / 1024, 1);
  353. return $bytes . "G";
  354. }
  355. /**
  356. * Make a computer file size
  357. * @param string $str file size in human readable format
  358. * @return int a file size in bytes
  359. *
  360. * Makes 2kB to 2048.
  361. *
  362. * Inspired by: http://www.php.net/manual/en/function.filesize.php#92418
  363. */
  364. public static function computerFileSize($str) {
  365. $str = strtolower($str);
  366. $bytes_array = array(
  367. 'b' => 1,
  368. 'k' => 1024,
  369. 'kb' => 1024,
  370. 'mb' => 1024 * 1024,
  371. 'm' => 1024 * 1024,
  372. 'gb' => 1024 * 1024 * 1024,
  373. 'g' => 1024 * 1024 * 1024,
  374. 'tb' => 1024 * 1024 * 1024 * 1024,
  375. 't' => 1024 * 1024 * 1024 * 1024,
  376. 'pb' => 1024 * 1024 * 1024 * 1024 * 1024,
  377. 'p' => 1024 * 1024 * 1024 * 1024 * 1024,
  378. );
  379. $bytes = floatval($str);
  380. if (preg_match('#([kmgtp]?b?)$#si', $str, $matches) && !empty($bytes_array[$matches[1]])) {
  381. $bytes *= $bytes_array[$matches[1]];
  382. }
  383. $bytes = round($bytes);
  384. return $bytes;
  385. }
  386. /**
  387. * Recursive copying of folders
  388. * @param string $src source folder
  389. * @param string $dest target folder
  390. *
  391. */
  392. static function copyr($src, $dest) {
  393. if (is_dir($src)) {
  394. if (!is_dir($dest)) {
  395. mkdir($dest);
  396. }
  397. $files = scandir($src);
  398. foreach ($files as $file) {
  399. if ($file != "." && $file != "..") {
  400. self::copyr("$src/$file", "$dest/$file");
  401. }
  402. }
  403. } elseif (file_exists($src) && !\OC\Files\Filesystem::isFileBlacklisted($src)) {
  404. copy($src, $dest);
  405. }
  406. }
  407. /**
  408. * Recursive deletion of folders
  409. * @param string $dir path to the folder
  410. * @param bool $deleteSelf if set to false only the content of the folder will be deleted
  411. * @return bool
  412. */
  413. static function rmdirr($dir, $deleteSelf = true) {
  414. if (is_dir($dir)) {
  415. $files = new RecursiveIteratorIterator(
  416. new RecursiveDirectoryIterator($dir, RecursiveDirectoryIterator::SKIP_DOTS),
  417. RecursiveIteratorIterator::CHILD_FIRST
  418. );
  419. foreach ($files as $fileInfo) {
  420. /** @var SplFileInfo $fileInfo */
  421. if ($fileInfo->isDir()) {
  422. rmdir($fileInfo->getRealPath());
  423. } else {
  424. unlink($fileInfo->getRealPath());
  425. }
  426. }
  427. if ($deleteSelf) {
  428. rmdir($dir);
  429. }
  430. } elseif (file_exists($dir)) {
  431. if ($deleteSelf) {
  432. unlink($dir);
  433. }
  434. }
  435. if (!$deleteSelf) {
  436. return true;
  437. }
  438. return !file_exists($dir);
  439. }
  440. /**
  441. * @return \OC\Files\Type\Detection
  442. */
  443. static public function getMimetypeDetector() {
  444. if (!self::$mimetypeDetector) {
  445. self::$mimetypeDetector = new \OC\Files\Type\Detection();
  446. self::$mimetypeDetector->registerTypeArray(include 'mimetypes.list.php');
  447. }
  448. return self::$mimetypeDetector;
  449. }
  450. /**
  451. * @return \OC\Files\Type\TemplateManager
  452. */
  453. static public function getFileTemplateManager() {
  454. if (!self::$templateManager) {
  455. self::$templateManager = new \OC\Files\Type\TemplateManager();
  456. }
  457. return self::$templateManager;
  458. }
  459. /**
  460. * Try to guess the mimetype based on filename
  461. *
  462. * @param string $path
  463. * @return string
  464. */
  465. static public function getFileNameMimeType($path) {
  466. return self::getMimetypeDetector()->detectPath($path);
  467. }
  468. /**
  469. * get the mimetype form a local file
  470. *
  471. * @param string $path
  472. * @return string
  473. * does NOT work for ownClouds filesystem, use OC_FileSystem::getMimeType instead
  474. */
  475. static function getMimeType($path) {
  476. return self::getMimetypeDetector()->detect($path);
  477. }
  478. /**
  479. * Get a secure mimetype that won't expose potential XSS.
  480. *
  481. * @param string $mimeType
  482. * @return string
  483. */
  484. static function getSecureMimeType($mimeType) {
  485. return self::getMimetypeDetector()->getSecureMimeType($mimeType);
  486. }
  487. /**
  488. * get the mimetype form a data string
  489. *
  490. * @param string $data
  491. * @return string
  492. */
  493. static function getStringMimeType($data) {
  494. return self::getMimetypeDetector()->detectString($data);
  495. }
  496. /**
  497. * Checks $_REQUEST contains a var for the $s key. If so, returns the html-escaped value of this var; otherwise returns the default value provided by $d.
  498. * @param string $s name of the var to escape, if set.
  499. * @param string $d default value.
  500. * @return string the print-safe value.
  501. *
  502. */
  503. /**
  504. * detect if a given program is found in the search PATH
  505. *
  506. * @param string $name
  507. * @param bool $path
  508. * @internal param string $program name
  509. * @internal param string $optional search path, defaults to $PATH
  510. * @return bool true if executable program found in path
  511. */
  512. public static function canExecute($name, $path = false) {
  513. // path defaults to PATH from environment if not set
  514. if ($path === false) {
  515. $path = getenv("PATH");
  516. }
  517. // check method depends on operating system
  518. if (!strncmp(PHP_OS, "WIN", 3)) {
  519. // on Windows an appropriate COM or EXE file needs to exist
  520. $exts = array(".exe", ".com");
  521. $check_fn = "file_exists";
  522. } else {
  523. // anywhere else we look for an executable file of that name
  524. $exts = array("");
  525. $check_fn = "is_executable";
  526. }
  527. // Default check will be done with $path directories :
  528. $dirs = explode(PATH_SEPARATOR, $path);
  529. // WARNING : We have to check if open_basedir is enabled :
  530. $obd = ini_get('open_basedir');
  531. if ($obd != "none") {
  532. $obd_values = explode(PATH_SEPARATOR, $obd);
  533. if (count($obd_values) > 0 and $obd_values[0]) {
  534. // open_basedir is in effect !
  535. // We need to check if the program is in one of these dirs :
  536. $dirs = $obd_values;
  537. }
  538. }
  539. foreach ($dirs as $dir) {
  540. foreach ($exts as $ext) {
  541. if ($check_fn("$dir/$name" . $ext))
  542. return true;
  543. }
  544. }
  545. return false;
  546. }
  547. /**
  548. * copy the contents of one stream to another
  549. *
  550. * @param resource $source
  551. * @param resource $target
  552. * @return array the number of bytes copied and result
  553. */
  554. public static function streamCopy($source, $target) {
  555. if (!$source or !$target) {
  556. return array(0, false);
  557. }
  558. $bufSize = 8192;
  559. $result = true;
  560. $count = 0;
  561. while (!feof($source)) {
  562. $buf = fread($source, $bufSize);
  563. $bytesWritten = fwrite($target, $buf);
  564. if ($bytesWritten !== false) {
  565. $count += $bytesWritten;
  566. }
  567. // note: strlen is expensive so only use it when necessary,
  568. // on the last block
  569. if ($bytesWritten === false
  570. || ($bytesWritten < $bufSize && $bytesWritten < strlen($buf))
  571. ) {
  572. // write error, could be disk full ?
  573. $result = false;
  574. break;
  575. }
  576. }
  577. return array($count, $result);
  578. }
  579. /**
  580. * create a temporary file with an unique filename
  581. *
  582. * @param string $postfix
  583. * @return string
  584. * @deprecated Use the TempManager instead
  585. *
  586. * temporary files are automatically cleaned up after the script is finished
  587. */
  588. public static function tmpFile($postfix = '') {
  589. return \OC::$server->getTempManager()->getTemporaryFile($postfix);
  590. }
  591. /**
  592. * create a temporary folder with an unique filename
  593. *
  594. * @return string
  595. * @deprecated Use the TempManager instead
  596. *
  597. * temporary files are automatically cleaned up after the script is finished
  598. */
  599. public static function tmpFolder() {
  600. return \OC::$server->getTempManager()->getTemporaryFolder();
  601. }
  602. /**
  603. * Adds a suffix to the name in case the file exists
  604. *
  605. * @param string $path
  606. * @param string $filename
  607. * @return string
  608. */
  609. public static function buildNotExistingFileName($path, $filename) {
  610. $view = \OC\Files\Filesystem::getView();
  611. return self::buildNotExistingFileNameForView($path, $filename, $view);
  612. }
  613. /**
  614. * Adds a suffix to the name in case the file exists
  615. *
  616. * @param string $path
  617. * @param string $filename
  618. * @return string
  619. */
  620. public static function buildNotExistingFileNameForView($path, $filename, \OC\Files\View $view) {
  621. if ($path === '/') {
  622. $path = '';
  623. }
  624. if ($pos = strrpos($filename, '.')) {
  625. $name = substr($filename, 0, $pos);
  626. $ext = substr($filename, $pos);
  627. } else {
  628. $name = $filename;
  629. $ext = '';
  630. }
  631. $newpath = $path . '/' . $filename;
  632. if ($view->file_exists($newpath)) {
  633. if (preg_match_all('/\((\d+)\)/', $name, $matches, PREG_OFFSET_CAPTURE)) {
  634. //Replace the last "(number)" with "(number+1)"
  635. $last_match = count($matches[0]) - 1;
  636. $counter = $matches[1][$last_match][0] + 1;
  637. $offset = $matches[0][$last_match][1];
  638. $match_length = strlen($matches[0][$last_match][0]);
  639. } else {
  640. $counter = 2;
  641. $match_length = 0;
  642. $offset = false;
  643. }
  644. do {
  645. if ($offset) {
  646. //Replace the last "(number)" with "(number+1)"
  647. $newname = substr_replace($name, '(' . $counter . ')', $offset, $match_length);
  648. } else {
  649. $newname = $name . ' (' . $counter . ')';
  650. }
  651. $newpath = $path . '/' . $newname . $ext;
  652. $counter++;
  653. } while ($view->file_exists($newpath));
  654. }
  655. return $newpath;
  656. }
  657. /**
  658. * Checks if $sub is a subdirectory of $parent
  659. *
  660. * @param string $sub
  661. * @param string $parent
  662. * @return bool
  663. */
  664. public static function isSubDirectory($sub, $parent) {
  665. $realpathSub = realpath($sub);
  666. $realpathParent = realpath($parent);
  667. // realpath() may return false in case the directory does not exist
  668. // since we can not be sure how different PHP versions may behave here
  669. // we do an additional check whether realpath returned false
  670. if($realpathSub === false || $realpathParent === false) {
  671. return false;
  672. }
  673. // Check whether $sub is a subdirectory of $parent
  674. if (strpos($realpathSub, $realpathParent) === 0) {
  675. return true;
  676. }
  677. return false;
  678. }
  679. /**
  680. * Returns an array with all keys from input lowercased or uppercased. Numbered indices are left as is.
  681. *
  682. * @param array $input The array to work on
  683. * @param int $case Either MB_CASE_UPPER or MB_CASE_LOWER (default)
  684. * @param string $encoding The encoding parameter is the character encoding. Defaults to UTF-8
  685. * @return array
  686. *
  687. * Returns an array with all keys from input lowercased or uppercased. Numbered indices are left as is.
  688. * based on http://www.php.net/manual/en/function.array-change-key-case.php#107715
  689. *
  690. */
  691. public static function mb_array_change_key_case($input, $case = MB_CASE_LOWER, $encoding = 'UTF-8') {
  692. $case = ($case != MB_CASE_UPPER) ? MB_CASE_LOWER : MB_CASE_UPPER;
  693. $ret = array();
  694. foreach ($input as $k => $v) {
  695. $ret[mb_convert_case($k, $case, $encoding)] = $v;
  696. }
  697. return $ret;
  698. }
  699. /**
  700. * replaces a copy of string delimited by the start and (optionally) length parameters with the string given in replacement.
  701. *
  702. * @param string $string
  703. * @param string $replacement The replacement string.
  704. * @param int $start If start is positive, the replacing will begin at the start'th offset into string. If start is negative, the replacing will begin at the start'th character from the end of string.
  705. * @param int $length Length of the part to be replaced
  706. * @param string $encoding The encoding parameter is the character encoding. Defaults to UTF-8
  707. * @internal param string $input The input string. .Opposite to the PHP build-in function does not accept an array.
  708. * @return string
  709. */
  710. public static function mb_substr_replace($string, $replacement, $start, $length = null, $encoding = 'UTF-8') {
  711. $start = intval($start);
  712. $length = intval($length);
  713. $string = mb_substr($string, 0, $start, $encoding) .
  714. $replacement .
  715. mb_substr($string, $start + $length, mb_strlen($string, 'UTF-8') - $start, $encoding);
  716. return $string;
  717. }
  718. /**
  719. * Replace all occurrences of the search string with the replacement string
  720. *
  721. * @param string $search The value being searched for, otherwise known as the needle.
  722. * @param string $replace The replacement
  723. * @param string $subject The string or array being searched and replaced on, otherwise known as the haystack.
  724. * @param string $encoding The encoding parameter is the character encoding. Defaults to UTF-8
  725. * @param int $count If passed, this will be set to the number of replacements performed.
  726. * @return string
  727. *
  728. */
  729. public static function mb_str_replace($search, $replace, $subject, $encoding = 'UTF-8', &$count = null) {
  730. $offset = -1;
  731. $length = mb_strlen($search, $encoding);
  732. while (($i = mb_strrpos($subject, $search, $offset, $encoding)) !== false) {
  733. $subject = OC_Helper::mb_substr_replace($subject, $replace, $i, $length);
  734. $offset = $i - mb_strlen($subject, $encoding);
  735. $count++;
  736. }
  737. return $subject;
  738. }
  739. /**
  740. * performs a search in a nested array
  741. * @param array $haystack the array to be searched
  742. * @param string $needle the search string
  743. * @param string $index optional, only search this key name
  744. * @return mixed the key of the matching field, otherwise false
  745. *
  746. * performs a search in a nested array
  747. *
  748. * taken from http://www.php.net/manual/en/function.array-search.php#97645
  749. */
  750. public static function recursiveArraySearch($haystack, $needle, $index = null) {
  751. $aIt = new RecursiveArrayIterator($haystack);
  752. $it = new RecursiveIteratorIterator($aIt);
  753. while ($it->valid()) {
  754. if (((isset($index) AND ($it->key() == $index)) OR (!isset($index))) AND ($it->current() == $needle)) {
  755. return $aIt->key();
  756. }
  757. $it->next();
  758. }
  759. return false;
  760. }
  761. /**
  762. * Shortens str to maxlen by replacing characters in the middle with '...', eg.
  763. * ellipsis('a very long string with lots of useless info to make a better example', 14) becomes 'a very ...example'
  764. *
  765. * @param string $str the string
  766. * @param string $maxlen the maximum length of the result
  767. * @return string with at most maxlen characters
  768. */
  769. public static function ellipsis($str, $maxlen) {
  770. if (strlen($str) > $maxlen) {
  771. $characters = floor($maxlen / 2);
  772. return substr($str, 0, $characters) . '...' . substr($str, -1 * $characters);
  773. }
  774. return $str;
  775. }
  776. /**
  777. * calculates the maximum upload size respecting system settings, free space and user quota
  778. *
  779. * @param string $dir the current folder where the user currently operates
  780. * @param int $freeSpace the number of bytes free on the storage holding $dir, if not set this will be received from the storage directly
  781. * @return int number of bytes representing
  782. */
  783. public static function maxUploadFilesize($dir, $freeSpace = null) {
  784. if (is_null($freeSpace) || $freeSpace < 0){
  785. $freeSpace = self::freeSpace($dir);
  786. }
  787. return min($freeSpace, self::uploadLimit());
  788. }
  789. /**
  790. * Calculate free space left within user quota
  791. *
  792. * @param string $dir the current folder where the user currently operates
  793. * @return int number of bytes representing
  794. */
  795. public static function freeSpace($dir) {
  796. $freeSpace = \OC\Files\Filesystem::free_space($dir);
  797. if ($freeSpace !== \OCP\Files\FileInfo::SPACE_UNKNOWN) {
  798. $freeSpace = max($freeSpace, 0);
  799. return $freeSpace;
  800. } else {
  801. return INF;
  802. }
  803. }
  804. /**
  805. * Calculate PHP upload limit
  806. *
  807. * @return int PHP upload file size limit
  808. */
  809. public static function uploadLimit() {
  810. $upload_max_filesize = OCP\Util::computerFileSize(ini_get('upload_max_filesize'));
  811. $post_max_size = OCP\Util::computerFileSize(ini_get('post_max_size'));
  812. if ((int)$upload_max_filesize === 0 and (int)$post_max_size === 0) {
  813. return INF;
  814. } elseif ((int)$upload_max_filesize === 0 or (int)$post_max_size === 0) {
  815. return max($upload_max_filesize, $post_max_size); //only the non 0 value counts
  816. } else {
  817. return min($upload_max_filesize, $post_max_size);
  818. }
  819. }
  820. /**
  821. * Checks if a function is available
  822. *
  823. * @param string $function_name
  824. * @return bool
  825. */
  826. public static function is_function_enabled($function_name) {
  827. if (!function_exists($function_name)) {
  828. return false;
  829. }
  830. $disabled = explode(',', ini_get('disable_functions'));
  831. $disabled = array_map('trim', $disabled);
  832. if (in_array($function_name, $disabled)) {
  833. return false;
  834. }
  835. $disabled = explode(',', ini_get('suhosin.executor.func.blacklist'));
  836. $disabled = array_map('trim', $disabled);
  837. if (in_array($function_name, $disabled)) {
  838. return false;
  839. }
  840. return true;
  841. }
  842. /**
  843. * Try to find a program
  844. * Note: currently windows is not supported
  845. *
  846. * @param string $program
  847. * @return null|string
  848. */
  849. public static function findBinaryPath($program) {
  850. $memcache = \OC::$server->getMemCacheFactory()->create('findBinaryPath');
  851. if ($memcache->hasKey($program)) {
  852. return $memcache->get($program);
  853. }
  854. $result = null;
  855. if (!\OC_Util::runningOnWindows() && self::is_function_enabled('exec')) {
  856. $exeSniffer = new ExecutableFinder();
  857. // Returns null if nothing is found
  858. $result = $exeSniffer->find($program);
  859. if (empty($result)) {
  860. $paths = getenv('PATH');
  861. if (empty($paths)) {
  862. $paths = '/usr/local/bin /usr/bin /opt/bin /bin';
  863. } else {
  864. $paths = str_replace(':',' ',getenv('PATH'));
  865. }
  866. $command = 'find ' . $paths . ' -name ' . escapeshellarg($program) . ' 2> /dev/null';
  867. exec($command, $output, $returnCode);
  868. if (count($output) > 0) {
  869. $result = escapeshellcmd($output[0]);
  870. }
  871. }
  872. }
  873. $memcache->set($program, $result, 3600);
  874. return $result;
  875. }
  876. /**
  877. * Calculate the disc space for the given path
  878. *
  879. * @param string $path
  880. * @param \OCP\Files\FileInfo $rootInfo (optional)
  881. * @return array
  882. * @throws \OCP\Files\NotFoundException
  883. */
  884. public static function getStorageInfo($path, $rootInfo = null) {
  885. // return storage info without adding mount points
  886. $includeExtStorage = \OC_Config::getValue('quota_include_external_storage', false);
  887. if (!$rootInfo) {
  888. $rootInfo = \OC\Files\Filesystem::getFileInfo($path, false);
  889. }
  890. if (!$rootInfo instanceof \OCP\Files\FileInfo) {
  891. throw new \OCP\Files\NotFoundException();
  892. }
  893. $used = $rootInfo->getSize();
  894. if ($used < 0) {
  895. $used = 0;
  896. }
  897. $quota = 0;
  898. $storage = $rootInfo->getStorage();
  899. if ($includeExtStorage && $storage->instanceOfStorage('\OC\Files\Storage\Shared')) {
  900. $includeExtStorage = false;
  901. }
  902. if ($includeExtStorage) {
  903. $quota = OC_Util::getUserQuota(\OCP\User::getUser());
  904. if ($quota !== \OCP\Files\FileInfo::SPACE_UNLIMITED) {
  905. // always get free space / total space from root + mount points
  906. return self::getGlobalStorageInfo();
  907. }
  908. }
  909. // TODO: need a better way to get total space from storage
  910. if ($storage->instanceOfStorage('\OC\Files\Storage\Wrapper\Quota')) {
  911. /** @var \OC\Files\Storage\Wrapper\Quota $storage */
  912. $quota = $storage->getQuota();
  913. }
  914. $free = $storage->free_space('');
  915. if ($free >= 0) {
  916. $total = $free + $used;
  917. } else {
  918. $total = $free; //either unknown or unlimited
  919. }
  920. if ($total > 0) {
  921. if ($quota > 0 && $total > $quota) {
  922. $total = $quota;
  923. }
  924. // prevent division by zero or error codes (negative values)
  925. $relative = round(($used / $total) * 10000) / 100;
  926. } else {
  927. $relative = 0;
  928. }
  929. return array('free' => $free, 'used' => $used, 'total' => $total, 'relative' => $relative);
  930. }
  931. /**
  932. * Get storage info including all mount points and quota
  933. *
  934. * @return array
  935. */
  936. private static function getGlobalStorageInfo() {
  937. $quota = OC_Util::getUserQuota(\OCP\User::getUser());
  938. $rootInfo = \OC\Files\Filesystem::getFileInfo('', 'ext');
  939. $used = $rootInfo['size'];
  940. if ($used < 0) {
  941. $used = 0;
  942. }
  943. $total = $quota;
  944. $free = $quota - $used;
  945. if ($total > 0) {
  946. if ($quota > 0 && $total > $quota) {
  947. $total = $quota;
  948. }
  949. // prevent division by zero or error codes (negative values)
  950. $relative = round(($used / $total) * 10000) / 100;
  951. } else {
  952. $relative = 0;
  953. }
  954. return array('free' => $free, 'used' => $used, 'total' => $total, 'relative' => $relative);
  955. }
  956. /**
  957. * Returns whether the config file is set manually to read-only
  958. * @return bool
  959. */
  960. public static function isReadOnlyConfigEnabled() {
  961. return \OC::$server->getConfig()->getSystemValue('config_is_read_only', false);
  962. }
  963. }