files.php 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444
  1. <?php
  2. /**
  3. * ownCloud
  4. *
  5. * @author Frank Karlitschek
  6. * @copyright 2012 Frank Karlitschek frank@owncloud.org
  7. *
  8. * This library is free software; you can redistribute it and/or
  9. * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
  10. * License as published by the Free Software Foundation; either
  11. * version 3 of the License, or any later version.
  12. *
  13. * This library is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
  17. *
  18. * You should have received a copy of the GNU Affero General Public
  19. * License along with this library. If not, see <http://www.gnu.org/licenses/>.
  20. *
  21. */
  22. /**
  23. * Class for fileserver access
  24. *
  25. */
  26. class OC_Files {
  27. static $tmpFiles=array();
  28. /**
  29. * get the content of a directory
  30. * @param dir $directory path under datadirectory
  31. */
  32. public static function getDirectoryContent($directory, $mimetype_filter = ''){
  33. $directory=OC_Filesystem::normalizePath($directory);
  34. if($directory=='/'){
  35. $directory='';
  36. }
  37. $files = array();
  38. if (($directory == '/Shared' || substr($directory, 0, 8) == '/Shared/') && OC_App::isEnabled('files_sharing')) {
  39. if ($directory == '/Shared') {
  40. $files = OCP\Share::getItemsSharedWith('file', OC_Share_Backend_File::FORMAT_FILE_APP, array('folder' => $directory, 'mimetype_filter' => $mimetype_filter));
  41. } else {
  42. $pos = strpos($directory, '/', 8);
  43. // Get shared folder name
  44. if ($pos !== false) {
  45. $itemTarget = substr($directory, 7, $pos - 7);
  46. } else {
  47. $itemTarget = substr($directory, 7);
  48. }
  49. $files = OCP\Share::getItemSharedWith('folder', $itemTarget, OC_Share_Backend_File::FORMAT_FILE_APP, array('folder' => $directory, 'mimetype_filter' => $mimetype_filter));
  50. }
  51. } else {
  52. $files = OC_FileCache::getFolderContent($directory, false, $mimetype_filter);
  53. foreach ($files as &$file) {
  54. $file['directory'] = $directory;
  55. $file['type'] = ($file['mimetype'] == 'httpd/unix-directory') ? 'dir' : 'file';
  56. $permissions = OCP\Share::PERMISSION_READ;
  57. // NOTE: Remove check when new encryption is merged
  58. if (!$file['encrypted']) {
  59. $permissions |= OCP\Share::PERMISSION_SHARE;
  60. }
  61. if ($file['type'] == 'dir' && $file['writable']) {
  62. $permissions |= OCP\Share::PERMISSION_CREATE;
  63. }
  64. if ($file['writable']) {
  65. $permissions |= OCP\Share::PERMISSION_UPDATE | OCP\Share::PERMISSION_DELETE;
  66. }
  67. $file['permissions'] = $permissions;
  68. }
  69. if ($directory == '' && OC_App::isEnabled('files_sharing')) {
  70. // Add 'Shared' folder
  71. $files = array_merge($files, OCP\Share::getItemsSharedWith('file', OC_Share_Backend_File::FORMAT_FILE_APP_ROOT));
  72. }
  73. }
  74. usort($files, "fileCmp");//TODO: remove this once ajax is merged
  75. return $files;
  76. }
  77. /**
  78. * return the content of a file or return a zip file containning multiply files
  79. *
  80. * @param dir $dir
  81. * @param file $file ; seperated list of files to download
  82. * @param boolean $only_header ; boolean to only send header of the request
  83. */
  84. public static function get($dir,$files, $only_header = false){
  85. if(strpos($files,';')){
  86. $files=explode(';',$files);
  87. }
  88. if(is_array($files)){
  89. self::validateZipDownload($dir,$files);
  90. $executionTime = intval(ini_get('max_execution_time'));
  91. set_time_limit(0);
  92. $zip = new ZipArchive();
  93. $filename = OC_Helper::tmpFile('.zip');
  94. if ($zip->open($filename, ZIPARCHIVE::CREATE | ZIPARCHIVE::OVERWRITE)!==TRUE) {
  95. exit("cannot open <$filename>\n");
  96. }
  97. foreach($files as $file){
  98. $file=$dir.'/'.$file;
  99. if(OC_Filesystem::is_file($file)){
  100. $tmpFile=OC_Filesystem::toTmpFile($file);
  101. self::$tmpFiles[]=$tmpFile;
  102. $zip->addFile($tmpFile,basename($file));
  103. }elseif(OC_Filesystem::is_dir($file)){
  104. self::zipAddDir($file,$zip);
  105. }
  106. }
  107. $zip->close();
  108. set_time_limit($executionTime);
  109. }elseif(OC_Filesystem::is_dir($dir.'/'.$files)){
  110. self::validateZipDownload($dir,$files);
  111. $executionTime = intval(ini_get('max_execution_time'));
  112. set_time_limit(0);
  113. $zip = new ZipArchive();
  114. $filename = OC_Helper::tmpFile('.zip');
  115. if ($zip->open($filename, ZIPARCHIVE::CREATE | ZIPARCHIVE::OVERWRITE)!==TRUE) {
  116. exit("cannot open <$filename>\n");
  117. }
  118. $file=$dir.'/'.$files;
  119. self::zipAddDir($file,$zip);
  120. $zip->close();
  121. set_time_limit($executionTime);
  122. }else{
  123. $zip=false;
  124. $filename=$dir.'/'.$files;
  125. }
  126. @ob_end_clean();
  127. if($zip or OC_Filesystem::is_readable($filename)){
  128. header('Content-Disposition: attachment; filename="'.basename($filename).'"');
  129. header('Content-Transfer-Encoding: binary');
  130. OC_Response::disableCaching();
  131. if($zip){
  132. ini_set('zlib.output_compression', 'off');
  133. header('Content-Type: application/zip');
  134. header('Content-Length: ' . filesize($filename));
  135. }else{
  136. header('Content-Type: '.OC_Filesystem::getMimeType($filename));
  137. }
  138. }elseif($zip or !OC_Filesystem::file_exists($filename)){
  139. header("HTTP/1.0 404 Not Found");
  140. $tmpl = new OC_Template( '', '404', 'guest' );
  141. $tmpl->assign('file',$filename);
  142. $tmpl->printPage();
  143. }else{
  144. header("HTTP/1.0 403 Forbidden");
  145. die('403 Forbidden');
  146. }
  147. if($only_header){
  148. if(!$zip)
  149. header("Content-Length: ".OC_Filesystem::filesize($filename));
  150. return ;
  151. }
  152. if($zip){
  153. $handle=fopen($filename,'r');
  154. if ($handle) {
  155. $chunkSize = 8*1024;// 1 MB chunks
  156. while (!feof($handle)) {
  157. echo fread($handle, $chunkSize);
  158. flush();
  159. }
  160. }
  161. unlink($filename);
  162. }else{
  163. OC_Filesystem::readfile($filename);
  164. }
  165. foreach(self::$tmpFiles as $tmpFile){
  166. if(file_exists($tmpFile) and is_file($tmpFile)){
  167. unlink($tmpFile);
  168. }
  169. }
  170. }
  171. public static function zipAddDir($dir,$zip,$internalDir=''){
  172. $dirname=basename($dir);
  173. $zip->addEmptyDir($internalDir.$dirname);
  174. $internalDir.=$dirname.='/';
  175. $files=OC_Files::getdirectorycontent($dir);
  176. foreach($files as $file){
  177. $filename=$file['name'];
  178. $file=$dir.'/'.$filename;
  179. if(OC_Filesystem::is_file($file)){
  180. $tmpFile=OC_Filesystem::toTmpFile($file);
  181. OC_Files::$tmpFiles[]=$tmpFile;
  182. $zip->addFile($tmpFile,$internalDir.$filename);
  183. }elseif(OC_Filesystem::is_dir($file)){
  184. self::zipAddDir($file,$zip,$internalDir);
  185. }
  186. }
  187. }
  188. /**
  189. * move a file or folder
  190. *
  191. * @param dir $sourceDir
  192. * @param file $source
  193. * @param dir $targetDir
  194. * @param file $target
  195. */
  196. public static function move($sourceDir,$source,$targetDir,$target){
  197. if(OC_User::isLoggedIn() && ($sourceDir != '' || $source != 'Shared') && !OC_Filesystem::file_exists($targetDir.'/'.$target)){
  198. $targetFile=self::normalizePath($targetDir.'/'.$target);
  199. $sourceFile=self::normalizePath($sourceDir.'/'.$source);
  200. return OC_Filesystem::rename($sourceFile,$targetFile);
  201. } else {
  202. return false;
  203. }
  204. }
  205. /**
  206. * copy a file or folder
  207. *
  208. * @param dir $sourceDir
  209. * @param file $source
  210. * @param dir $targetDir
  211. * @param file $target
  212. */
  213. public static function copy($sourceDir,$source,$targetDir,$target){
  214. if(OC_User::isLoggedIn()){
  215. $targetFile=$targetDir.'/'.$target;
  216. $sourceFile=$sourceDir.'/'.$source;
  217. return OC_Filesystem::copy($sourceFile,$targetFile);
  218. }
  219. }
  220. /**
  221. * create a new file or folder
  222. *
  223. * @param dir $dir
  224. * @param file $name
  225. * @param type $type
  226. */
  227. public static function newFile($dir,$name,$type){
  228. if(OC_User::isLoggedIn()){
  229. $file=$dir.'/'.$name;
  230. if($type=='dir'){
  231. return OC_Filesystem::mkdir($file);
  232. }elseif($type=='file'){
  233. $fileHandle=OC_Filesystem::fopen($file, 'w');
  234. if($fileHandle){
  235. fclose($fileHandle);
  236. return true;
  237. }else{
  238. return false;
  239. }
  240. }
  241. }
  242. }
  243. /**
  244. * deletes a file or folder
  245. *
  246. * @param dir $dir
  247. * @param file $name
  248. */
  249. public static function delete($dir,$file){
  250. if(OC_User::isLoggedIn() && ($dir!= '' || $file != 'Shared')) {
  251. $file=$dir.'/'.$file;
  252. return OC_Filesystem::unlink($file);
  253. }
  254. }
  255. /**
  256. * checks if the selected files are within the size constraint. If not, outputs an error page.
  257. *
  258. * @param dir $dir
  259. * @param files $files
  260. */
  261. static function validateZipDownload($dir, $files) {
  262. if(!OC_Config::getValue('allowZipDownload', true)) {
  263. $l = OC_L10N::get('lib');
  264. header("HTTP/1.0 409 Conflict");
  265. $tmpl = new OC_Template( '', 'error', 'user' );
  266. $errors = array(
  267. array(
  268. 'error' => $l->t('ZIP download is turned off.'),
  269. 'hint' => $l->t('Files need to be downloaded one by one.') . '<br/><a href="javascript:history.back()">' . $l->t('Back to Files') . '</a>',
  270. )
  271. );
  272. $tmpl->assign('errors', $errors);
  273. $tmpl->printPage();
  274. exit;
  275. }
  276. $zipLimit = OC_Config::getValue('maxZipInputSize', OC_Helper::computerFileSize('800 MB'));
  277. if($zipLimit > 0) {
  278. $totalsize = 0;
  279. if(is_array($files)){
  280. foreach($files as $file){
  281. $totalsize += OC_Filesystem::filesize($dir.'/'.$file);
  282. }
  283. }else{
  284. $totalsize += OC_Filesystem::filesize($dir.'/'.$files);
  285. }
  286. if($totalsize > $zipLimit) {
  287. $l = OC_L10N::get('lib');
  288. header("HTTP/1.0 409 Conflict");
  289. $tmpl = new OC_Template( '', 'error', 'user' );
  290. $errors = array(
  291. array(
  292. 'error' => $l->t('Selected files too large to generate zip file.'),
  293. 'hint' => 'Download the files in smaller chunks, seperately or kindly ask your administrator.<br/><a href="javascript:history.back()">' . $l->t('Back to Files') . '</a>',
  294. )
  295. );
  296. $tmpl->assign('errors', $errors);
  297. $tmpl->printPage();
  298. exit;
  299. }
  300. }
  301. }
  302. /**
  303. * try to detect the mime type of a file
  304. *
  305. * @param string path
  306. * @return string guessed mime type
  307. */
  308. static function getMimeType($path){
  309. return OC_Filesystem::getMimeType($path);
  310. }
  311. /**
  312. * get a file tree
  313. *
  314. * @param string path
  315. * @return array
  316. */
  317. static function getTree($path){
  318. return OC_Filesystem::getTree($path);
  319. }
  320. /**
  321. * pull a file from a remote server
  322. * @param string source
  323. * @param string token
  324. * @param string dir
  325. * @param string file
  326. * @return string guessed mime type
  327. */
  328. static function pull($source,$token,$dir,$file){
  329. $tmpfile=tempnam(get_temp_dir(),'remoteCloudFile');
  330. $fp=fopen($tmpfile,'w+');
  331. $url=$source.="/files/pull.php?token=$token";
  332. $ch=curl_init();
  333. curl_setopt($ch,CURLOPT_URL,$url);
  334. curl_setopt($ch, CURLOPT_FILE, $fp);
  335. curl_exec($ch);
  336. fclose($fp);
  337. $info=curl_getinfo($ch);
  338. $httpCode=$info['http_code'];
  339. curl_close($ch);
  340. if($httpCode==200 or $httpCode==0){
  341. OC_Filesystem::fromTmpFile($tmpfile,$dir.'/'.$file);
  342. return true;
  343. }else{
  344. return false;
  345. }
  346. }
  347. /**
  348. * set the maximum upload size limit for apache hosts using .htaccess
  349. * @param int size filesisze in bytes
  350. * @return false on failure, size on success
  351. */
  352. static function setUploadLimit($size){
  353. //don't allow user to break his config -- upper boundary
  354. if($size > PHP_INT_MAX) {
  355. //max size is always 1 byte lower than computerFileSize returns
  356. if($size > PHP_INT_MAX+1)
  357. return false;
  358. $size -=1;
  359. } else {
  360. $size=OC_Helper::humanFileSize($size);
  361. $size=substr($size,0,-1);//strip the B
  362. $size=str_replace(' ','',$size); //remove the space between the size and the postfix
  363. }
  364. //don't allow user to break his config -- broken or malicious size input
  365. if(intval($size) == 0) {
  366. return false;
  367. }
  368. $htaccess = @file_get_contents(OC::$SERVERROOT.'/.htaccess'); //supress errors in case we don't have permissions for
  369. if(!$htaccess) {
  370. return false;
  371. }
  372. $phpValueKeys = array(
  373. 'upload_max_filesize',
  374. 'post_max_size'
  375. );
  376. foreach($phpValueKeys as $key) {
  377. $pattern = '/php_value '.$key.' (\S)*/';
  378. $setting = 'php_value '.$key.' '.$size;
  379. $hasReplaced = 0;
  380. $content = preg_replace($pattern, $setting, $htaccess, 1, $hasReplaced);
  381. if($content !== NULL) {
  382. $htaccess = $content;
  383. }
  384. if($hasReplaced == 0) {
  385. $htaccess .= "\n" . $setting;
  386. }
  387. }
  388. //check for write permissions
  389. if(is_writable(OC::$SERVERROOT.'/.htaccess')) {
  390. file_put_contents(OC::$SERVERROOT.'/.htaccess', $htaccess);
  391. return OC_Helper::computerFileSize($size);
  392. } else { OC_Log::write('files','Can\'t write upload limit to '.OC::$SERVERROOT.'/.htaccess. Please check the file permissions',OC_Log::WARN); }
  393. return false;
  394. }
  395. /**
  396. * normalize a path, removing any double, add leading /, etc
  397. * @param string $path
  398. * @return string
  399. */
  400. static public function normalizePath($path){
  401. $path='/'.$path;
  402. $old='';
  403. while($old!=$path){//replace any multiplicity of slashes with a single one
  404. $old=$path;
  405. $path=str_replace('//','/',$path);
  406. }
  407. return $path;
  408. }
  409. }
  410. function fileCmp($a,$b){
  411. if($a['type']=='dir' and $b['type']!='dir'){
  412. return -1;
  413. }elseif($a['type']!='dir' and $b['type']=='dir'){
  414. return 1;
  415. }else{
  416. return strnatcasecmp($a['name'],$b['name']);
  417. }
  418. }