helper.php 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624
  1. <?php
  2. /**
  3. * ownCloud
  4. *
  5. * @author Frank Karlitschek
  6. * @author Jakob Sack
  7. * @copyright 2012 Frank Karlitschek frank@owncloud.org
  8. *
  9. * This library is free software; you can redistribute it and/or
  10. * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
  11. * License as published by the Free Software Foundation; either
  12. * version 3 of the License, or any later version.
  13. *
  14. * This library is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
  18. *
  19. * You should have received a copy of the GNU Affero General Public
  20. * License along with this library. If not, see <http://www.gnu.org/licenses/>.
  21. *
  22. */
  23. /**
  24. * Collection of useful functions
  25. */
  26. class OC_Helper {
  27. private static $mimetypes=array();
  28. private static $tmpFiles=array();
  29. /**
  30. * @brief Creates an url
  31. * @param $app app
  32. * @param $file file
  33. * @returns the url
  34. *
  35. * Returns a url to the given app and file.
  36. */
  37. public static function linkTo( $app, $file ){
  38. if( $app != '' ){
  39. $app .= '/';
  40. // Check if the app is in the app folder
  41. if( file_exists( OC::$APPSROOT . '/apps/'. $app.$file )){
  42. if(substr($file, -3) == 'php' || substr($file, -3) == 'css'){
  43. if(substr($app, -1, 1) == '/'){
  44. $app = substr($app, 0, strlen($app) - 1);
  45. }
  46. $urlLinkTo = OC::$WEBROOT . '/?app=' . $app;
  47. $urlLinkTo .= ($file!='index.php')?'&getfile=' . urlencode($file):'';
  48. }else{
  49. $urlLinkTo = OC::$APPSWEBROOT . '/apps/' . $app . $file;
  50. }
  51. }
  52. else{
  53. $urlLinkTo = OC::$WEBROOT . '/' . $app . $file;
  54. }
  55. }
  56. else{
  57. if( file_exists( OC::$SERVERROOT . '/core/'. $file )){
  58. $urlLinkTo = OC::$WEBROOT . '/core/'.$file;
  59. }
  60. else{
  61. $urlLinkTo = OC::$WEBROOT . '/'.$file;
  62. }
  63. }
  64. return $urlLinkTo;
  65. }
  66. /**
  67. * @brief Returns the server host
  68. * @returns the server host
  69. *
  70. * Returns the server host, even if the website uses one or more
  71. * reverse proxies
  72. */
  73. public static function serverHost() {
  74. if (isset($_SERVER['HTTP_X_FORWARDED_HOST'])) {
  75. if (strpos($_SERVER['HTTP_X_FORWARDED_HOST'], ",") !== false) {
  76. $host = trim(array_pop(explode(",", $_SERVER['HTTP_X_FORWARDED_HOST'])));
  77. }
  78. else{
  79. $host=$_SERVER['HTTP_X_FORWARDED_HOST'];
  80. }
  81. }
  82. else{
  83. $host = $_SERVER['HTTP_HOST'];
  84. }
  85. return $host;
  86. }
  87. /**
  88. * @brief Returns the server protocol
  89. * @returns the server protocol
  90. *
  91. * Returns the server protocol. It respects reverse proxy servers and load balancers
  92. */
  93. public static function serverProtocol() {
  94. if (isset($_SERVER['HTTP_X_FORWARDED_PROTO'])) {
  95. $proto = strtolower($_SERVER['HTTP_X_FORWARDED_PROTO']);
  96. }else{
  97. if(isset($_SERVER['HTTPS']) and !empty($_SERVER['HTTPS']) and ($_SERVER['HTTPS']!='off')) {
  98. $proto = 'https';
  99. }else{
  100. $proto = 'http';
  101. }
  102. }
  103. return($proto);
  104. }
  105. /**
  106. * @brief Creates an absolute url
  107. * @param $app app
  108. * @param $file file
  109. * @returns the url
  110. *
  111. * Returns a absolute url to the given app and file.
  112. */
  113. public static function linkToAbsolute( $app, $file ) {
  114. $urlLinkTo = self::linkTo( $app, $file );
  115. $urlLinkTo = self::serverProtocol(). '://' . self::serverHost() . $urlLinkTo;
  116. return $urlLinkTo;
  117. }
  118. /**
  119. * @brief Creates an absolute url for remote use
  120. * @param $service id
  121. * @returns the url
  122. *
  123. * Returns a absolute url to the given service.
  124. */
  125. public static function linkToRemote( $service, $add_slash = true ) {
  126. return self::linkToAbsolute( '', 'remote.php') . '/' . $service . (($add_slash && $service[strlen($service)-1]!='/')?'/':'');
  127. }
  128. /**
  129. * @brief Creates path to an image
  130. * @param $app app
  131. * @param $image image name
  132. * @returns the url
  133. *
  134. * Returns the path to the image.
  135. */
  136. public static function imagePath( $app, $image ){
  137. // Read the selected theme from the config file
  138. $theme=OC_Config::getValue( "theme" );
  139. // Check if the app is in the app folder
  140. if( file_exists( OC::$SERVERROOT."/themes/$theme/apps/$app/img/$image" )){
  141. return OC::$WEBROOT."/themes/$theme/apps/$app/img/$image";
  142. }elseif( file_exists( OC::$APPSROOT."/apps/$app/img/$image" )){
  143. return OC::$APPSWEBROOT."/apps/$app/img/$image";
  144. }elseif( !empty( $app ) and file_exists( OC::$SERVERROOT."/themes/$theme/$app/img/$image" )){
  145. return OC::$WEBROOT."/themes/$theme/$app/img/$image";
  146. }elseif( !empty( $app ) and file_exists( OC::$SERVERROOT."/$app/img/$image" )){
  147. return OC::$WEBROOT."/$app/img/$image";
  148. }elseif( file_exists( OC::$SERVERROOT."/themes/$theme/core/img/$image" )){
  149. return OC::$WEBROOT."/themes/$theme/core/img/$image";
  150. }elseif( file_exists( OC::$SERVERROOT."/core/img/$image" )){
  151. return OC::$WEBROOT."/core/img/$image";
  152. }else{
  153. echo('image not found: image:'.$image.' webroot:'.OC::$WEBROOT.' serverroot:'.OC::$SERVERROOT);
  154. die();
  155. }
  156. }
  157. /**
  158. * @brief get path to icon of file type
  159. * @param $mimetype mimetype
  160. * @returns the url
  161. *
  162. * Returns the path to the image of this file type.
  163. */
  164. public static function mimetypeIcon( $mimetype ){
  165. $alias=array('application/xml'=>'code/xml');
  166. // echo $mimetype;
  167. if(isset($alias[$mimetype])){
  168. $mimetype=$alias[$mimetype];
  169. // echo $mimetype;
  170. }
  171. // Replace slash with a minus
  172. $mimetype = str_replace( "/", "-", $mimetype );
  173. // Is it a dir?
  174. if( $mimetype == "dir" ){
  175. return OC::$WEBROOT."/core/img/filetypes/folder.png";
  176. }
  177. // Icon exists?
  178. if( file_exists( OC::$SERVERROOT."/core/img/filetypes/$mimetype.png" )){
  179. return OC::$WEBROOT."/core/img/filetypes/$mimetype.png";
  180. }
  181. //try only the first part of the filetype
  182. $mimetype=substr($mimetype,0,strpos($mimetype,'-'));
  183. if( file_exists( OC::$SERVERROOT."/core/img/filetypes/$mimetype.png" )){
  184. return OC::$WEBROOT."/core/img/filetypes/$mimetype.png";
  185. }
  186. else{
  187. return OC::$WEBROOT."/core/img/filetypes/file.png";
  188. }
  189. }
  190. /**
  191. * @brief Make a human file size
  192. * @param $bytes file size in bytes
  193. * @returns a human readable file size
  194. *
  195. * Makes 2048 to 2 kB.
  196. */
  197. public static function humanFileSize( $bytes ){
  198. if( $bytes < 1024 ){
  199. return "$bytes B";
  200. }
  201. $bytes = round( $bytes / 1024, 1 );
  202. if( $bytes < 1024 ){
  203. return "$bytes kB";
  204. }
  205. $bytes = round( $bytes / 1024, 1 );
  206. if( $bytes < 1024 ){
  207. return "$bytes MB";
  208. }
  209. // Wow, heavy duty for owncloud
  210. $bytes = round( $bytes / 1024, 1 );
  211. return "$bytes GB";
  212. }
  213. /**
  214. * @brief Make a computer file size
  215. * @param $str file size in a fancy format
  216. * @returns a file size in bytes
  217. *
  218. * Makes 2kB to 2048.
  219. *
  220. * Inspired by: http://www.php.net/manual/en/function.filesize.php#92418
  221. */
  222. public static function computerFileSize( $str ){
  223. $bytes = 0;
  224. $str=strtolower($str);
  225. $bytes_array = array(
  226. 'b' => 1,
  227. 'k' => 1024,
  228. 'kb' => 1024,
  229. 'mb' => 1024 * 1024,
  230. 'm' => 1024 * 1024,
  231. 'gb' => 1024 * 1024 * 1024,
  232. 'g' => 1024 * 1024 * 1024,
  233. 'tb' => 1024 * 1024 * 1024 * 1024,
  234. 't' => 1024 * 1024 * 1024 * 1024,
  235. 'pb' => 1024 * 1024 * 1024 * 1024 * 1024,
  236. 'p' => 1024 * 1024 * 1024 * 1024 * 1024,
  237. );
  238. $bytes = floatval($str);
  239. if (preg_match('#([kmgtp]?b?)$#si', $str, $matches) && !empty($bytes_array[$matches[1]])) {
  240. $bytes *= $bytes_array[$matches[1]];
  241. }
  242. $bytes = round($bytes, 2);
  243. return $bytes;
  244. }
  245. /**
  246. * @brief Recusive editing of file permissions
  247. * @param $path path to file or folder
  248. * @param $filemode unix style file permissions as integer
  249. *
  250. */
  251. static function chmodr($path, $filemode) {
  252. if (!is_dir($path))
  253. return chmod($path, $filemode);
  254. $dh = opendir($path);
  255. while (($file = readdir($dh)) !== false) {
  256. if($file != '.' && $file != '..') {
  257. $fullpath = $path.'/'.$file;
  258. if(is_link($fullpath))
  259. return FALSE;
  260. elseif(!is_dir($fullpath) && !@chmod($fullpath, $filemode))
  261. return FALSE;
  262. elseif(!self::chmodr($fullpath, $filemode))
  263. return FALSE;
  264. }
  265. }
  266. closedir($dh);
  267. if(@chmod($path, $filemode))
  268. return TRUE;
  269. else
  270. return FALSE;
  271. }
  272. /**
  273. * @brief Recusive copying of folders
  274. * @param string $src source folder
  275. * @param string $dest target folder
  276. *
  277. */
  278. static function copyr($src, $dest) {
  279. if(is_dir($src)){
  280. if(!is_dir($dest)){
  281. mkdir($dest);
  282. }
  283. $files = scandir($src);
  284. foreach ($files as $file){
  285. if ($file != "." && $file != ".."){
  286. self::copyr("$src/$file", "$dest/$file");
  287. }
  288. }
  289. }elseif(file_exists($src)){
  290. copy($src, $dest);
  291. }
  292. }
  293. /**
  294. * @brief Recusive deletion of folders
  295. * @param string $dir path to the folder
  296. *
  297. */
  298. static function rmdirr($dir) {
  299. if(is_dir($dir)) {
  300. $files=scandir($dir);
  301. foreach($files as $file){
  302. if ($file != "." && $file != ".."){
  303. self::rmdirr("$dir/$file");
  304. }
  305. }
  306. rmdir($dir);
  307. }elseif(file_exists($dir)){
  308. unlink($dir);
  309. }
  310. if(file_exists($dir)) {
  311. return false;
  312. }
  313. }
  314. /**
  315. * get the mimetype form a local file
  316. * @param string path
  317. * @return string
  318. * does NOT work for ownClouds filesystem, use OC_FileSystem::getMimeType instead
  319. */
  320. static function getMimeType($path){
  321. $isWrapped=(strpos($path,'://')!==false) and (substr($path,0,7)=='file://');
  322. $mimeType='application/octet-stream';
  323. if ($mimeType=='application/octet-stream') {
  324. self::$mimetypes = include('mimetypes.fixlist.php');
  325. $extension=strtolower(strrchr(basename($path), "."));
  326. $extension=substr($extension,1);//remove leading .
  327. $mimeType=(isset(self::$mimetypes[$extension]))?self::$mimetypes[$extension]:'application/octet-stream';
  328. }
  329. if (@is_dir($path)) {
  330. // directories are easy
  331. return "httpd/unix-directory";
  332. }
  333. if($mimeType=='application/octet-stream' and function_exists('finfo_open') and function_exists('finfo_file') and $finfo=finfo_open(FILEINFO_MIME)){
  334. $info = @strtolower(finfo_file($finfo,$path));
  335. if($info){
  336. $mimeType=substr($info,0,strpos($info,';'));
  337. }
  338. finfo_close($finfo);
  339. }
  340. if (!$isWrapped and $mimeType=='application/octet-stream' && function_exists("mime_content_type")) {
  341. // use mime magic extension if available
  342. $mimeType = mime_content_type($path);
  343. }
  344. if (!$isWrapped and $mimeType=='application/octet-stream' && OC_Helper::canExecute("file")) {
  345. // it looks like we have a 'file' command,
  346. // lets see it it does have mime support
  347. $path=escapeshellarg($path);
  348. $fp = popen("file -i -b $path 2>/dev/null", "r");
  349. $reply = fgets($fp);
  350. pclose($fp);
  351. //trim the character set from the end of the response
  352. $mimeType=substr($reply,0,strrpos($reply,' '));
  353. //trim ;
  354. if (strpos($mimeType, ';') !== false) {
  355. $mimeType = strstr($mimeType, ';', true);
  356. }
  357. }
  358. if ($mimeType=='application/octet-stream') {
  359. // Fallback solution: (try to guess the type by the file extension
  360. if(!self::$mimetypes || self::$mimetypes != include('mimetypes.list.php')){
  361. self::$mimetypes=include('mimetypes.list.php');
  362. }
  363. $extension=strtolower(strrchr(basename($path), "."));
  364. $extension=substr($extension,1);//remove leading .
  365. $mimeType=(isset(self::$mimetypes[$extension]))?self::$mimetypes[$extension]:'application/octet-stream';
  366. }
  367. return $mimeType;
  368. }
  369. /**
  370. * get the mimetype form a data string
  371. * @param string data
  372. * @return string
  373. */
  374. static function getStringMimeType($data){
  375. if(function_exists('finfo_open') and function_exists('finfo_file')){
  376. $finfo=finfo_open(FILEINFO_MIME);
  377. return finfo_buffer($finfo, $data);
  378. }else{
  379. $tmpFile=OC_Helper::tmpFile();
  380. $fh=fopen($tmpFile,'wb');
  381. fwrite($fh,$data,8024);
  382. fclose($fh);
  383. $mime=self::getMimeType($tmpFile);
  384. unset($tmpFile);
  385. return $mime;
  386. }
  387. }
  388. /**
  389. * @brief 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.
  390. * @param $s name of the var to escape, if set.
  391. * @param $d default value.
  392. * @returns the print-safe value.
  393. *
  394. */
  395. //FIXME: should also check for value validation (i.e. the email is an email).
  396. public static function init_var($s, $d="") {
  397. $r = $d;
  398. if(isset($_REQUEST[$s]) && !empty($_REQUEST[$s]))
  399. $r = stripslashes(htmlspecialchars($_REQUEST[$s]));
  400. return $r;
  401. }
  402. /**
  403. * returns "checked"-attribut if request contains selected radio element OR if radio element is the default one -- maybe?
  404. * @param string $s Name of radio-button element name
  405. * @param string $v Value of current radio-button element
  406. * @param string $d Value of default radio-button element
  407. */
  408. public static function init_radio($s, $v, $d) {
  409. if((isset($_REQUEST[$s]) && $_REQUEST[$s]==$v) || (!isset($_REQUEST[$s]) && $v == $d))
  410. print "checked=\"checked\" ";
  411. }
  412. /**
  413. * detect if a given program is found in the search PATH
  414. *
  415. * @param string program name
  416. * @param string optional search path, defaults to $PATH
  417. * @return bool true if executable program found in path
  418. */
  419. public static function canExecute($name, $path = false){
  420. // path defaults to PATH from environment if not set
  421. if ($path === false) {
  422. $path = getenv("PATH");
  423. }
  424. // check method depends on operating system
  425. if (!strncmp(PHP_OS, "WIN", 3)) {
  426. // on Windows an appropriate COM or EXE file needs to exist
  427. $exts = array(".exe", ".com");
  428. $check_fn = "file_exists";
  429. } else {
  430. // anywhere else we look for an executable file of that name
  431. $exts = array("");
  432. $check_fn = "is_executable";
  433. }
  434. // Default check will be done with $path directories :
  435. $dirs = explode(PATH_SEPARATOR, $path);
  436. // WARNING : We have to check if open_basedir is enabled :
  437. $obd = ini_get('open_basedir');
  438. if($obd != "none")
  439. $obd_values = explode(PATH_SEPARATOR, $obd);
  440. if(count($obd_values) > 0 and $obd_values[0])
  441. {
  442. // open_basedir is in effect !
  443. // We need to check if the program is in one of these dirs :
  444. $dirs = $obd_values;
  445. }
  446. foreach($dirs as $dir)
  447. {
  448. foreach($exts as $ext)
  449. {
  450. if($check_fn("$dir/$name".$ext))
  451. return true;
  452. }
  453. }
  454. return false;
  455. }
  456. /**
  457. * copy the contents of one stream to another
  458. * @param resource source
  459. * @param resource target
  460. * @return int the number of bytes copied
  461. */
  462. public static function streamCopy($source,$target){
  463. if(!$source or !$target){
  464. return false;
  465. }
  466. $count=0;
  467. while(!feof($source)){
  468. $count+=fwrite($target,fread($source,8192));
  469. }
  470. return $count;
  471. }
  472. /**
  473. * create a temporary file with an unique filename
  474. * @param string postfix
  475. * @return string
  476. *
  477. * temporary files are automatically cleaned up after the script is finished
  478. */
  479. public static function tmpFile($postfix=''){
  480. $file=get_temp_dir().'/'.md5(time().rand()).$postfix;
  481. $fh=fopen($file,'w');
  482. fclose($fh);
  483. self::$tmpFiles[]=$file;
  484. return $file;
  485. }
  486. /**
  487. * create a temporary folder with an unique filename
  488. * @return string
  489. *
  490. * temporary files are automatically cleaned up after the script is finished
  491. */
  492. public static function tmpFolder(){
  493. $path=get_temp_dir().'/'.md5(time().rand());
  494. mkdir($path);
  495. self::$tmpFiles[]=$path;
  496. return $path.'/';
  497. }
  498. /**
  499. * remove all files created by self::tmpFile
  500. */
  501. public static function cleanTmp(){
  502. $leftoversFile=get_temp_dir().'/oc-not-deleted';
  503. if(file_exists($leftoversFile)){
  504. $leftovers=file($leftoversFile);
  505. foreach($leftovers as $file) {
  506. self::rmdirr($file);
  507. }
  508. unlink($leftoversFile);
  509. }
  510. foreach(self::$tmpFiles as $file){
  511. if(file_exists($file)){
  512. if(!self::rmdirr($file)) {
  513. file_put_contents($leftoversFile, $file."\n", FILE_APPEND);
  514. }
  515. }
  516. }
  517. }
  518. /**
  519. * Adds a suffix to the name in case the file exists
  520. *
  521. * @param $path
  522. * @param $filename
  523. * @return string
  524. */
  525. public static function buildNotExistingFileName($path, $filename){
  526. if($path==='/'){
  527. $path='';
  528. }
  529. if ($pos = strrpos($filename, '.')) {
  530. $name = substr($filename, 0, $pos);
  531. $ext = substr($filename, $pos);
  532. } else {
  533. $name = $filename;
  534. }
  535. $newpath = $path . '/' . $filename;
  536. $newname = $filename;
  537. $counter = 2;
  538. while (OC_Filesystem::file_exists($newpath)) {
  539. $newname = $name . ' (' . $counter . ')' . $ext;
  540. $newpath = $path . '/' . $newname;
  541. $counter++;
  542. }
  543. return $newpath;
  544. }
  545. /*
  546. * checks if $sub is a subdirectory of $parent
  547. *
  548. * @param $sub
  549. * @param $parent
  550. * @return bool
  551. */
  552. public static function issubdirectory($sub, $parent){
  553. if($sub == null || $sub == '' || $parent == null || $parent == ''){
  554. return false;
  555. }
  556. $realpath_sub = realpath($sub);
  557. $realpath_parent = realpath($parent);
  558. if(($realpath_sub == false && substr_count($realpath_sub, './') != 0) || ($realpath_parent == false && substr_count($realpath_parent, './') != 0)){ //it checks for both ./ and ../
  559. return false;
  560. }
  561. if($realpath_sub && $realpath_sub != '' && $realpath_parent && $realpath_parent != ''){
  562. if(substr($realpath_sub, 0, strlen($realpath_parent)) == $realpath_parent){
  563. return true;
  564. }
  565. }else{
  566. if(substr($sub, 0, strlen($parent)) == $parent){
  567. return true;
  568. }
  569. }
  570. /*echo 'SUB: ' . $sub . "\n";
  571. echo 'PAR: ' . $parent . "\n";
  572. echo 'REALSUB: ' . $realpath_sub . "\n";
  573. echo 'REALPAR: ' . $realpath_parent . "\n";
  574. echo substr($realpath_sub, 0, strlen($realpath_parent));
  575. exit;*/
  576. return false;
  577. }
  578. }