| Total Complexity | 73 |
| Total Lines | 379 |
| Duplicated Lines | 0 % |
| Changes | 2 | ||
| Bugs | 0 | Features | 0 |
Complex classes like OC_Files often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use OC_Files, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 51 | class OC_Files { |
||
| 52 | const FILE = 1; |
||
| 53 | const ZIP_FILES = 2; |
||
| 54 | const ZIP_DIR = 3; |
||
| 55 | |||
| 56 | const UPLOAD_MIN_LIMIT_BYTES = 1048576; // 1 MiB |
||
| 57 | |||
| 58 | |||
| 59 | private static $multipartBoundary = ''; |
||
| 60 | |||
| 61 | /** |
||
| 62 | * @return string |
||
| 63 | */ |
||
| 64 | private static function getBoundary() { |
||
| 69 | } |
||
| 70 | |||
| 71 | /** |
||
| 72 | * @param string $filename |
||
| 73 | * @param string $name |
||
| 74 | * @param array $rangeArray ('from'=>int,'to'=>int), ... |
||
| 75 | */ |
||
| 76 | private static function sendHeaders($filename, $name, array $rangeArray) { |
||
| 102 | } |
||
| 103 | |||
| 104 | /** |
||
| 105 | * return the content of a file or return a zip file containing multiple files |
||
| 106 | * |
||
| 107 | * @param string $dir |
||
| 108 | * @param string $files ; separated list of files to download |
||
| 109 | * @param array $params ; 'head' boolean to only send header of the request ; 'range' http range header |
||
| 110 | */ |
||
| 111 | public static function get($dir, $files, $params = null) { |
||
| 112 | |||
| 113 | $view = \OC\Files\Filesystem::getView(); |
||
| 114 | $getType = self::FILE; |
||
| 115 | $filename = $dir; |
||
| 116 | try { |
||
| 117 | |||
| 118 | if (is_array($files) && count($files) === 1) { |
||
|
|
|||
| 119 | $files = $files[0]; |
||
| 120 | } |
||
| 121 | |||
| 122 | if (!is_array($files)) { |
||
| 123 | $filename = $dir . '/' . $files; |
||
| 124 | if (!$view->is_dir($filename)) { |
||
| 125 | self::getSingleFile($view, $dir, $files, is_null($params) ? array() : $params); |
||
| 126 | return; |
||
| 127 | } |
||
| 128 | } |
||
| 129 | |||
| 130 | $name = 'download'; |
||
| 131 | if (is_array($files)) { |
||
| 132 | $getType = self::ZIP_FILES; |
||
| 133 | $basename = basename($dir); |
||
| 134 | if ($basename) { |
||
| 135 | $name = $basename; |
||
| 136 | } |
||
| 137 | |||
| 138 | $filename = $dir . '/' . $name; |
||
| 139 | } else { |
||
| 140 | $filename = $dir . '/' . $files; |
||
| 141 | $getType = self::ZIP_DIR; |
||
| 142 | // downloading root ? |
||
| 143 | if ($files !== '') { |
||
| 144 | $name = $files; |
||
| 145 | } |
||
| 146 | } |
||
| 147 | |||
| 148 | self::lockFiles($view, $dir, $files); |
||
| 149 | |||
| 150 | /* Calculate filesize and number of files */ |
||
| 151 | if ($getType === self::ZIP_FILES) { |
||
| 152 | $fileInfos = array(); |
||
| 153 | $fileSize = 0; |
||
| 154 | foreach ($files as $file) { |
||
| 155 | $fileInfo = \OC\Files\Filesystem::getFileInfo($dir . '/' . $file); |
||
| 156 | $fileSize += $fileInfo->getSize(); |
||
| 157 | $fileInfos[] = $fileInfo; |
||
| 158 | } |
||
| 159 | $numberOfFiles = self::getNumberOfFiles($fileInfos); |
||
| 160 | } elseif ($getType === self::ZIP_DIR) { |
||
| 161 | $fileInfo = \OC\Files\Filesystem::getFileInfo($dir . '/' . $files); |
||
| 162 | $fileSize = $fileInfo->getSize(); |
||
| 163 | $numberOfFiles = self::getNumberOfFiles(array($fileInfo)); |
||
| 164 | } |
||
| 165 | |||
| 166 | $streamer = new Streamer(\OC::$server->getRequest(), $fileSize, $numberOfFiles); |
||
| 167 | OC_Util::obEnd(); |
||
| 168 | |||
| 169 | $streamer->sendHeaders($name); |
||
| 170 | $executionTime = (int)OC::$server->getIniWrapper()->getNumeric('max_execution_time'); |
||
| 171 | if (strpos(@ini_get('disable_functions'), 'set_time_limit') === false) { |
||
| 172 | @set_time_limit(0); |
||
| 173 | } |
||
| 174 | ignore_user_abort(true); |
||
| 175 | |||
| 176 | if ($getType === self::ZIP_FILES) { |
||
| 177 | foreach ($files as $file) { |
||
| 178 | $file = $dir . '/' . $file; |
||
| 179 | if (\OC\Files\Filesystem::is_file($file)) { |
||
| 180 | $userFolder = \OC::$server->getRootFolder()->get(\OC\Files\Filesystem::getRoot()); |
||
| 181 | $file = $userFolder->get($file); |
||
| 182 | if($file instanceof \OC\Files\Node\File) { |
||
| 183 | try { |
||
| 184 | $fh = $file->fopen('r'); |
||
| 185 | } catch (\OCP\Files\NotPermittedException $e) { |
||
| 186 | continue; |
||
| 187 | } |
||
| 188 | $fileSize = $file->getSize(); |
||
| 189 | $fileTime = $file->getMTime(); |
||
| 190 | } else { |
||
| 191 | // File is not a file? … |
||
| 192 | \OC::$server->getLogger()->debug( |
||
| 193 | 'File given, but no Node available. Name {file}', |
||
| 194 | [ 'app' => 'files', 'file' => $file ] |
||
| 195 | ); |
||
| 196 | continue; |
||
| 197 | } |
||
| 198 | $streamer->addFileFromStream($fh, $file->getName(), $fileSize, $fileTime); |
||
| 199 | fclose($fh); |
||
| 200 | } elseif (\OC\Files\Filesystem::is_dir($file)) { |
||
| 201 | $streamer->addDirRecursive($file); |
||
| 202 | } |
||
| 203 | } |
||
| 204 | } elseif ($getType === self::ZIP_DIR) { |
||
| 205 | $file = $dir . '/' . $files; |
||
| 206 | $streamer->addDirRecursive($file); |
||
| 207 | } |
||
| 208 | $streamer->finalize(); |
||
| 209 | set_time_limit($executionTime); |
||
| 210 | self::unlockAllTheFiles($dir, $files, $getType, $view, $filename); |
||
| 211 | } catch (\OCP\Lock\LockedException $ex) { |
||
| 212 | self::unlockAllTheFiles($dir, $files, $getType, $view, $filename); |
||
| 213 | OC::$server->getLogger()->logException($ex); |
||
| 214 | $l = \OC::$server->getL10N('core'); |
||
| 215 | $hint = method_exists($ex, 'getHint') ? $ex->getHint() : ''; |
||
| 216 | \OC_Template::printErrorPage($l->t('File is currently busy, please try again later'), $hint, 200); |
||
| 217 | } catch (\OCP\Files\ForbiddenException $ex) { |
||
| 218 | self::unlockAllTheFiles($dir, $files, $getType, $view, $filename); |
||
| 219 | OC::$server->getLogger()->logException($ex); |
||
| 220 | $l = \OC::$server->getL10N('core'); |
||
| 221 | \OC_Template::printErrorPage($l->t('Can\'t read file'), $ex->getMessage(), 200); |
||
| 222 | } catch (\Exception $ex) { |
||
| 223 | self::unlockAllTheFiles($dir, $files, $getType, $view, $filename); |
||
| 224 | OC::$server->getLogger()->logException($ex); |
||
| 225 | $l = \OC::$server->getL10N('core'); |
||
| 226 | $hint = method_exists($ex, 'getHint') ? $ex->getHint() : ''; |
||
| 227 | \OC_Template::printErrorPage($l->t('Can\'t read file'), $hint, 200); |
||
| 228 | } |
||
| 229 | } |
||
| 230 | |||
| 231 | /** |
||
| 232 | * @param string $rangeHeaderPos |
||
| 233 | * @param int $fileSize |
||
| 234 | * @return array $rangeArray ('from'=>int,'to'=>int), ... |
||
| 235 | */ |
||
| 236 | private static function parseHttpRangeHeader($rangeHeaderPos, $fileSize) { |
||
| 237 | $rArray=explode(',', $rangeHeaderPos); |
||
| 238 | $minOffset = 0; |
||
| 239 | $ind = 0; |
||
| 240 | |||
| 241 | $rangeArray = array(); |
||
| 242 | |||
| 243 | foreach ($rArray as $value) { |
||
| 244 | $ranges = explode('-', $value); |
||
| 245 | if (is_numeric($ranges[0])) { |
||
| 246 | if ($ranges[0] < $minOffset) { // case: bytes=500-700,601-999 |
||
| 247 | $ranges[0] = $minOffset; |
||
| 248 | } |
||
| 249 | if ($ind > 0 && $rangeArray[$ind-1]['to']+1 == $ranges[0]) { // case: bytes=500-600,601-999 |
||
| 250 | $ind--; |
||
| 251 | $ranges[0] = $rangeArray[$ind]['from']; |
||
| 252 | } |
||
| 253 | } |
||
| 254 | |||
| 255 | if (is_numeric($ranges[0]) && is_numeric($ranges[1]) && $ranges[0] < $fileSize && $ranges[0] <= $ranges[1]) { |
||
| 256 | // case: x-x |
||
| 257 | if ($ranges[1] >= $fileSize) { |
||
| 258 | $ranges[1] = $fileSize-1; |
||
| 259 | } |
||
| 260 | $rangeArray[$ind++] = array( 'from' => $ranges[0], 'to' => $ranges[1], 'size' => $fileSize ); |
||
| 261 | $minOffset = $ranges[1] + 1; |
||
| 262 | if ($minOffset >= $fileSize) { |
||
| 263 | break; |
||
| 264 | } |
||
| 265 | } |
||
| 266 | elseif (is_numeric($ranges[0]) && $ranges[0] < $fileSize) { |
||
| 267 | // case: x- |
||
| 268 | $rangeArray[$ind++] = array( 'from' => $ranges[0], 'to' => $fileSize-1, 'size' => $fileSize ); |
||
| 269 | break; |
||
| 270 | } |
||
| 271 | elseif (is_numeric($ranges[1])) { |
||
| 272 | // case: -x |
||
| 273 | if ($ranges[1] > $fileSize) { |
||
| 274 | $ranges[1] = $fileSize; |
||
| 275 | } |
||
| 276 | $rangeArray[$ind++] = array( 'from' => $fileSize-$ranges[1], 'to' => $fileSize-1, 'size' => $fileSize ); |
||
| 277 | break; |
||
| 278 | } |
||
| 279 | } |
||
| 280 | return $rangeArray; |
||
| 281 | } |
||
| 282 | |||
| 283 | /** |
||
| 284 | * @param View $view |
||
| 285 | * @param string $name |
||
| 286 | * @param string $dir |
||
| 287 | * @param array $params ; 'head' boolean to only send header of the request ; 'range' http range header |
||
| 288 | */ |
||
| 289 | private static function getSingleFile($view, $dir, $name, $params) { |
||
| 290 | $filename = $dir . '/' . $name; |
||
| 291 | $file = null; |
||
| 292 | |||
| 293 | try { |
||
| 294 | $userFolder = \OC::$server->getRootFolder()->get(\OC\Files\Filesystem::getRoot()); |
||
| 295 | $file = $userFolder->get($filename); |
||
| 296 | if(!$file instanceof \OC\Files\Node\File || !$file->isReadable()) { |
||
| 297 | http_response_code(403); |
||
| 298 | die('403 Forbidden'); |
||
| 299 | } |
||
| 300 | $fileSize = $file->getSize(); |
||
| 301 | } catch (\OCP\Files\NotPermittedException $e) { |
||
| 302 | http_response_code(403); |
||
| 303 | die('403 Forbidden'); |
||
| 304 | } catch (\OCP\Files\InvalidPathException $e) { |
||
| 305 | http_response_code(403); |
||
| 306 | die('403 Forbidden'); |
||
| 307 | } catch (\OCP\Files\NotFoundException $e) { |
||
| 308 | http_response_code(404); |
||
| 309 | $tmpl = new OC_Template('', '404', 'guest'); |
||
| 310 | $tmpl->printPage(); |
||
| 311 | exit(); |
||
| 312 | } |
||
| 313 | |||
| 314 | OC_Util::obEnd(); |
||
| 315 | $view->lockFile($filename, ILockingProvider::LOCK_SHARED); |
||
| 316 | |||
| 317 | $rangeArray = array(); |
||
| 318 | |||
| 319 | if (isset($params['range']) && substr($params['range'], 0, 6) === 'bytes=') { |
||
| 320 | $rangeArray = self::parseHttpRangeHeader(substr($params['range'], 6), $fileSize); |
||
| 321 | } |
||
| 322 | |||
| 323 | self::sendHeaders($filename, $name, $rangeArray); |
||
| 324 | |||
| 325 | if (isset($params['head']) && $params['head']) { |
||
| 326 | return; |
||
| 327 | } |
||
| 328 | |||
| 329 | if (!empty($rangeArray)) { |
||
| 330 | try { |
||
| 331 | if (count($rangeArray) == 1) { |
||
| 332 | $view->readfilePart($filename, $rangeArray[0]['from'], $rangeArray[0]['to']); |
||
| 333 | } |
||
| 334 | else { |
||
| 335 | // check if file is seekable (if not throw UnseekableException) |
||
| 336 | // we have to check it before body contents |
||
| 337 | $view->readfilePart($filename, $rangeArray[0]['size'], $rangeArray[0]['size']); |
||
| 338 | |||
| 339 | $type = \OC::$server->getMimeTypeDetector()->getSecureMimeType(\OC\Files\Filesystem::getMimeType($filename)); |
||
| 340 | |||
| 341 | foreach ($rangeArray as $range) { |
||
| 342 | echo "\r\n--".self::getBoundary()."\r\n". |
||
| 343 | "Content-type: ".$type."\r\n". |
||
| 344 | "Content-range: bytes ".$range['from']."-".$range['to']."/".$range['size']."\r\n\r\n"; |
||
| 345 | $view->readfilePart($filename, $range['from'], $range['to']); |
||
| 346 | } |
||
| 347 | echo "\r\n--".self::getBoundary()."--\r\n"; |
||
| 348 | } |
||
| 349 | } catch (\OCP\Files\UnseekableException $ex) { |
||
| 350 | // file is unseekable |
||
| 351 | header_remove('Accept-Ranges'); |
||
| 352 | header_remove('Content-Range'); |
||
| 353 | http_response_code(200); |
||
| 354 | self::sendHeaders($filename, $name, array()); |
||
| 355 | $view->readfile($filename); |
||
| 356 | } |
||
| 357 | } |
||
| 358 | else { |
||
| 359 | $view->readfile($filename); |
||
| 360 | } |
||
| 361 | } |
||
| 362 | |||
| 363 | /** |
||
| 364 | * Returns the total (recursive) number of files and folders in the given |
||
| 365 | * FileInfos. |
||
| 366 | * |
||
| 367 | * @param \OCP\Files\FileInfo[] $fileInfos the FileInfos to count |
||
| 368 | * @return int the total number of files and folders |
||
| 369 | */ |
||
| 370 | private static function getNumberOfFiles($fileInfos) { |
||
| 371 | $numberOfFiles = 0; |
||
| 372 | |||
| 373 | $view = new View(); |
||
| 374 | |||
| 375 | while ($fileInfo = array_pop($fileInfos)) { |
||
| 376 | $numberOfFiles++; |
||
| 377 | |||
| 378 | if ($fileInfo->getType() === \OCP\Files\FileInfo::TYPE_FOLDER) { |
||
| 379 | $fileInfos = array_merge($fileInfos, $view->getDirectoryContent($fileInfo->getPath())); |
||
| 380 | } |
||
| 381 | } |
||
| 382 | |||
| 383 | return $numberOfFiles; |
||
| 384 | } |
||
| 385 | |||
| 386 | /** |
||
| 387 | * @param View $view |
||
| 388 | * @param string $dir |
||
| 389 | * @param string[]|string $files |
||
| 390 | */ |
||
| 391 | public static function lockFiles($view, $dir, $files) { |
||
| 406 | } |
||
| 407 | } |
||
| 408 | } |
||
| 409 | |||
| 410 | /** |
||
| 411 | * @param string $dir |
||
| 412 | * @param $files |
||
| 413 | * @param integer $getType |
||
| 414 | * @param View $view |
||
| 415 | * @param string $filename |
||
| 416 | */ |
||
| 417 | private static function unlockAllTheFiles($dir, $files, $getType, $view, $filename) { |
||
| 430 | } |
||
| 431 | } |
||
| 432 | |||
| 433 | } |
||
| 434 |