files.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402
  1. <?php
  2. /**
  3. * ownCloud
  4. *
  5. * @author Frank Karlitschek
  6. * @copyright 2010 Frank Karlitschek karlitschek@kde.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
  31. */
  32. public static function getDirectoryContent($directory, $mimetype_filter = ''){
  33. if(strpos($directory,OC::$CONFIG_DATADIRECTORY)===0){
  34. $directory=substr($directory,strlen(OC::$CONFIG_DATADIRECTORY));
  35. }
  36. $files=OC_FileCache::getFolderContent($directory, '', $mimetype_filter);
  37. foreach($files as &$file){
  38. $file['directory']=$directory;
  39. $file['type']=($file['mimetype']=='httpd/unix-directory')?'dir':'file';
  40. }
  41. usort($files, "fileCmp");//TODO: remove this once ajax is merged
  42. return $files;
  43. }
  44. /**
  45. * return the content of a file or return a zip file containning multiply files
  46. *
  47. * @param dir $dir
  48. * @param file $file ; seperated list of files to download
  49. */
  50. public static function get($dir,$files){
  51. if(strpos($files,';')){
  52. $files=explode(';',$files);
  53. }
  54. if(is_array($files)){
  55. self::validateZipDownload($dir,$files);
  56. $executionTime = intval(ini_get('max_execution_time'));
  57. set_time_limit(0);
  58. $zip = new ZipArchive();
  59. $filename = OC_Helper::tmpFile('.zip');
  60. if ($zip->open($filename, ZIPARCHIVE::CREATE | ZIPARCHIVE::OVERWRITE)!==TRUE) {
  61. exit("cannot open <$filename>\n");
  62. }
  63. foreach($files as $file){
  64. $file=$dir.'/'.$file;
  65. if(OC_Filesystem::is_file($file)){
  66. $tmpFile=OC_Filesystem::toTmpFile($file);
  67. self::$tmpFiles[]=$tmpFile;
  68. $zip->addFile($tmpFile,basename($file));
  69. }elseif(OC_Filesystem::is_dir($file)){
  70. self::zipAddDir($file,$zip);
  71. }
  72. }
  73. $zip->close();
  74. set_time_limit($executionTime);
  75. }elseif(OC_Filesystem::is_dir($dir.'/'.$files)){
  76. self::validateZipDownload($dir,$files);
  77. $executionTime = intval(ini_get('max_execution_time'));
  78. set_time_limit(0);
  79. $zip = new ZipArchive();
  80. $filename = OC_Helper::tmpFile('.zip');
  81. if ($zip->open($filename, ZIPARCHIVE::CREATE | ZIPARCHIVE::OVERWRITE)!==TRUE) {
  82. exit("cannot open <$filename>\n");
  83. }
  84. $file=$dir.'/'.$files;
  85. self::zipAddDir($file,$zip);
  86. $zip->close();
  87. set_time_limit($executionTime);
  88. }else{
  89. $zip=false;
  90. $filename=$dir.'/'.$files;
  91. }
  92. if($zip or OC_Filesystem::is_readable($filename)){
  93. header('Content-Disposition: attachment; filename="'.basename($filename).'"');
  94. header('Content-Transfer-Encoding: binary');
  95. OC_Response::disableCaching();
  96. if($zip){
  97. header('Content-Type: application/zip');
  98. header('Content-Length: ' . filesize($filename));
  99. }else{
  100. $fileData=OC_FileCache::get($filename);
  101. header('Content-Type: ' . $fileData['mimetype']);
  102. header('Content-Length: ' . $fileData['size']);
  103. }
  104. }elseif($zip or !OC_Filesystem::file_exists($filename)){
  105. header("HTTP/1.0 404 Not Found");
  106. $tmpl = new OC_Template( '', '404', 'guest' );
  107. $tmpl->assign('file',$filename);
  108. $tmpl->printPage();
  109. }else{
  110. header("HTTP/1.0 403 Forbidden");
  111. die('403 Forbidden');
  112. }
  113. @ob_end_clean();
  114. if($zip){
  115. $handle=fopen($filename,'r');
  116. if ($handle) {
  117. $chunkSize = 8*1024;// 1 MB chunks
  118. while (!feof($handle)) {
  119. echo fread($handle, $chunkSize);
  120. flush();
  121. }
  122. }
  123. unlink($filename);
  124. }else{
  125. OC_Filesystem::readfile($filename);
  126. }
  127. foreach(self::$tmpFiles as $tmpFile){
  128. if(file_exists($tmpFile) and is_file($tmpFile)){
  129. unlink($tmpFile);
  130. }
  131. }
  132. }
  133. public static function zipAddDir($dir,$zip,$internalDir=''){
  134. $dirname=basename($dir);
  135. $zip->addEmptyDir($internalDir.$dirname);
  136. $internalDir.=$dirname.='/';
  137. $files=OC_Files::getdirectorycontent($dir);
  138. foreach($files as $file){
  139. $filename=$file['name'];
  140. $file=$dir.'/'.$filename;
  141. if(OC_Filesystem::is_file($file)){
  142. $tmpFile=OC_Filesystem::toTmpFile($file);
  143. OC_Files::$tmpFiles[]=$tmpFile;
  144. $zip->addFile($tmpFile,$internalDir.$filename);
  145. }elseif(OC_Filesystem::is_dir($file)){
  146. self::zipAddDir($file,$zip,$internalDir);
  147. }
  148. }
  149. }
  150. /**
  151. * move a file or folder
  152. *
  153. * @param dir $sourceDir
  154. * @param file $source
  155. * @param dir $targetDir
  156. * @param file $target
  157. */
  158. public static function move($sourceDir,$source,$targetDir,$target){
  159. if(OC_User::isLoggedIn()){
  160. $targetFile=self::normalizePath($targetDir.'/'.$target);
  161. $sourceFile=self::normalizePath($sourceDir.'/'.$source);
  162. return OC_Filesystem::rename($sourceFile,$targetFile);
  163. }
  164. }
  165. /**
  166. * copy a file or folder
  167. *
  168. * @param dir $sourceDir
  169. * @param file $source
  170. * @param dir $targetDir
  171. * @param file $target
  172. */
  173. public static function copy($sourceDir,$source,$targetDir,$target){
  174. if(OC_User::isLoggedIn()){
  175. $targetFile=$targetDir.'/'.$target;
  176. $sourceFile=$sourceDir.'/'.$source;
  177. return OC_Filesystem::copy($sourceFile,$targetFile);
  178. }
  179. }
  180. /**
  181. * create a new file or folder
  182. *
  183. * @param dir $dir
  184. * @param file $name
  185. * @param type $type
  186. */
  187. public static function newFile($dir,$name,$type){
  188. if(OC_User::isLoggedIn()){
  189. $file=$dir.'/'.$name;
  190. if($type=='dir'){
  191. return OC_Filesystem::mkdir($file);
  192. }elseif($type=='file'){
  193. $fileHandle=OC_Filesystem::fopen($file, 'w');
  194. if($fileHandle){
  195. fclose($fileHandle);
  196. return true;
  197. }else{
  198. return false;
  199. }
  200. }
  201. }
  202. }
  203. /**
  204. * deletes a file or folder
  205. *
  206. * @param dir $dir
  207. * @param file $name
  208. */
  209. public static function delete($dir,$file){
  210. if(OC_User::isLoggedIn()){
  211. $file=$dir.'/'.$file;
  212. return OC_Filesystem::unlink($file);
  213. }
  214. }
  215. /**
  216. * checks if the selected files are within the size constraint. If not, outputs an error page.
  217. *
  218. * @param dir $dir
  219. * @param files $files
  220. */
  221. static function validateZipDownload($dir, $files) {
  222. if(!OC_Config::getValue('allowZipDownload', true)) {
  223. $l = OC_L10N::get('files');
  224. header("HTTP/1.0 409 Conflict");
  225. $tmpl = new OC_Template( '', 'error', 'user' );
  226. $errors = array(
  227. array(
  228. 'error' => $l->t('ZIP download is turned off.'),
  229. 'hint' => $l->t('Files need to be downloaded one by one.') . '<br/><a href="javascript:history.back()">' . $l->t('Back to Files') . '</a>',
  230. )
  231. );
  232. $tmpl->assign('errors', $errors);
  233. $tmpl->printPage();
  234. exit;
  235. }
  236. $zipLimit = OC_Config::getValue('maxZipInputSize', OC_Helper::computerFileSize('800 MB'));
  237. if($zipLimit > 0) {
  238. $totalsize = 0;
  239. if(is_array($files)){
  240. foreach($files as $file){
  241. $totalsize += OC_Filesystem::filesize($dir.'/'.$file);
  242. }
  243. }else{
  244. $totalsize += OC_Filesystem::filesize($dir.'/'.$files);
  245. }
  246. if($totalsize > $zipLimit) {
  247. $l = OC_L10N::get('files');
  248. header("HTTP/1.0 409 Conflict");
  249. $tmpl = new OC_Template( '', 'error', 'user' );
  250. $errors = array(
  251. array(
  252. 'error' => $l->t('Selected files too large to generate zip file.'),
  253. '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>',
  254. )
  255. );
  256. $tmpl->assign('errors', $errors);
  257. $tmpl->printPage();
  258. exit;
  259. }
  260. }
  261. }
  262. /**
  263. * try to detect the mime type of a file
  264. *
  265. * @param string path
  266. * @return string guessed mime type
  267. */
  268. static function getMimeType($path){
  269. return OC_Filesystem::getMimeType($path);
  270. }
  271. /**
  272. * get a file tree
  273. *
  274. * @param string path
  275. * @return array
  276. */
  277. static function getTree($path){
  278. return OC_Filesystem::getTree($path);
  279. }
  280. /**
  281. * pull a file from a remote server
  282. * @param string source
  283. * @param string token
  284. * @param string dir
  285. * @param string file
  286. * @return string guessed mime type
  287. */
  288. static function pull($source,$token,$dir,$file){
  289. $tmpfile=tempnam(get_temp_dir(),'remoteCloudFile');
  290. $fp=fopen($tmpfile,'w+');
  291. $url=$source.="/files/pull.php?token=$token";
  292. $ch=curl_init();
  293. curl_setopt($ch,CURLOPT_URL,$url);
  294. curl_setopt($ch, CURLOPT_FILE, $fp);
  295. curl_exec($ch);
  296. fclose($fp);
  297. $info=curl_getinfo($ch);
  298. $httpCode=$info['http_code'];
  299. curl_close($ch);
  300. if($httpCode==200 or $httpCode==0){
  301. OC_Filesystem::fromTmpFile($tmpfile,$dir.'/'.$file);
  302. return true;
  303. }else{
  304. return false;
  305. }
  306. }
  307. /**
  308. * set the maximum upload size limit for apache hosts using .htaccess
  309. * @param int size filesisze in bytes
  310. * @return false on failure, size on success
  311. */
  312. static function setUploadLimit($size){
  313. //don't allow user to break his config -- upper boundary
  314. if($size > PHP_INT_MAX) {
  315. //max size is always 1 byte lower than computerFileSize returns
  316. if($size > PHP_INT_MAX+1)
  317. return false;
  318. $size -=1;
  319. } else {
  320. $size=OC_Helper::humanFileSize($size);
  321. $size=substr($size,0,-1);//strip the B
  322. $size=str_replace(' ','',$size); //remove the space between the size and the postfix
  323. }
  324. //don't allow user to break his config -- broken or malicious size input
  325. if(intval($size) == 0) {
  326. return false;
  327. }
  328. $htaccess = @file_get_contents(OC::$SERVERROOT.'/.htaccess'); //supress errors in case we don't have permissions for
  329. if(!$htaccess) {
  330. return false;
  331. }
  332. $phpValueKeys = array(
  333. 'upload_max_filesize',
  334. 'post_max_size'
  335. );
  336. foreach($phpValueKeys as $key) {
  337. $pattern = '/php_value '.$key.' (\S)*/';
  338. $setting = 'php_value '.$key.' '.$size;
  339. $hasReplaced = 0;
  340. $content = preg_replace($pattern, $setting, $htaccess, 1, $hasReplaced);
  341. if($content !== NULL) {
  342. $htaccess = $content;
  343. }
  344. if($hasReplaced == 0) {
  345. $htaccess .= "\n" . $setting;
  346. }
  347. }
  348. //supress errors in case we don't have permissions for it
  349. if(@file_put_contents(OC::$SERVERROOT.'/.htaccess', $htaccess)) {
  350. return OC_Helper::computerFileSize($size);
  351. }
  352. return false;
  353. }
  354. /**
  355. * normalize a path, removing any double, add leading /, etc
  356. * @param string $path
  357. * @return string
  358. */
  359. static public function normalizePath($path){
  360. $path='/'.$path;
  361. $old='';
  362. while($old!=$path){//replace any multiplicity of slashes with a single one
  363. $old=$path;
  364. $path=str_replace('//','/',$path);
  365. }
  366. return $path;
  367. }
  368. }
  369. function fileCmp($a,$b){
  370. if($a['type']=='dir' and $b['type']!='dir'){
  371. return -1;
  372. }elseif($a['type']!='dir' and $b['type']=='dir'){
  373. return 1;
  374. }else{
  375. return strnatcasecmp($a['name'],$b['name']);
  376. }
  377. }