* @package WB * @subpackage base */ WBClass::load('WBService' , 'WBLog' ); /** * Service VFSFile * * Deliver file * * @version 1.1.0 * @package WB * @subpackage base */ class WBService_VFSFile extends WBService { /** * decide whether to start locale tools * @var bool */ protected $useI18n = false; /** * decide whether to start WBConfig * @var bool */ protected $useConfig = false; /** * Access Control List * @var WBVFS_AccessControl */ private $acl; /** * Display * * Deliver media file by URI. Typically the URI will be appended to file.php/ * or dispatcher.php/file/. Slashes are used as parameter delimiter * * file.php/Yg541/video/x-flv/movie.flv * file.php/zA542/image/jpg/picture.jpg * or * dispatcher.php/file/Yg541/video/x-flv/movie.flv * dispatcher.php/file/zA542/image/jpg/picture.jpg * * The first parameter ($this->part) is the media file's obscure id. This is * required, everything es is optional. * * The second parameter is the major mime type (video, image, text, * application, ...). If this parameter is missing, the major mime type will * be loaded from the media file's data. * * The next step loads the correesponding WBVFS_Mime-Hanlder and injects the * media file (WBVFS_File). This allows to convert WBVFS_Files of one (major) mime * tyĆ¼e to another. E.g. videos to images (to generate a title image), applications * to images (to provide graphical icons) etc. * * * @param int $status * @param mixed $data to be send * @param string $checksum * @return bool true */ public function run( &$status, &$data, &$checksum = null ) { $this->part = ''; if (!empty($this->path)) { $this->part = array_shift($this->path); } if (empty($this->part)) { $data = 'Please tell which file you want.'; $status = 400; return true; } WBParam::set('wb/response/compress', 0); /** @var WBVFS_File */ $loader = WBClass::create('WBVFS_File'); $file = $loader->loadByObscureId($this->part); if (!$file->isOK()) { $data = str_replace('{FILE}', $this->part, 'File "{FILE}" does not exist.'); $status = 404; return true; } // select mime type to deliver $mimeMajor = $file->getMime(); if (!empty($this->path)) { $mimeMajor = array_shift($this->path); } $mimeMajor = strtolower($mimeMajor); $mimeMinor = null; if (!empty($this->path)) { $mimeMinor = array_shift($this->path); } // call special mime handler try{ /** @var WBVFS_Mime */ $mimeHdl = WBClass::create('WBVFS_Mime_' . ucfirst($mimeMajor)); } catch (WBException $e) { $data = str_replace('{FILE}', $this->part, 'Unkown type convert for "{FILE}" selected.'); $status = 404; return true; } if(!$this->isAccessGranted($file, $mimeHdl, $mimeMinor)) { $data = str_replace('{FILE}', $this->part, 'Access for "{FILE}" denied.'); $status = 403; return true; } if (!$mimeHdl->setVirtualFile($file)) { $data = str_replace('{FILE}', $this->part, 'Cannot convert file "{FILE}" to requested type.'); $status = 404; return true; } $redirect = array(); $path = $mimeHdl->getRequestedFile($this->req, $this->res, $redirect, $mimeMinor); // redirect? if (!empty($redirect)) { $reload = $this->req->get('reload'); if (!empty($reload)) { $redirect['reload'] = $reload; } $stub = sprintf('%s://%s%s%s/%s/%s?%%s' , $this->req->protocol , $this->req->getMeta('HTTP_HOST') , WBString::replaceSuperPlaceholders('[[SERVICE_FILE]]') , $file->getObscureId() , $mimeMajor , $mimeMinor ); $target = sprintf($stub, http_build_query($redirect['get'])); $self = sprintf($stub, http_build_query($this->req->export())); if ($self == $target) { $data = str_replace('{URL}', $target, 'Redirect loop detected when redirecting to {URL}. Sorry for any invconvenience.'); $status = 404; return true; } $this->res->addHeader('Location', $target); $data = str_replace('{URL}', $target, 'Please collect file at {URL}. Thank you.'); $status = 301; return true; } // something went wrong? if (!file_exists($path) || !is_readable($path)) { $data = str_replace('{FILE}', $this->part, 'File "{FILE}" not found.'); $status = 404; return true; } // empty file? clearstatcache(); if (!filesize($path)) { $data = str_replace('{FILE}', $this->part, 'File "{FILE}" is not there yet, This file probably is about to be converted to as suitable format - please try again later'); $status = 404; return true; } // increment view if ($file->getMime(WBVFS_File::MIME_MAJOR) == $mimeMajor && $mimeHdl->isIncrementable()) { $range = $this->req->getRange(); if (0 == $range[0]) { $file->incrementView(); } } // set header $mime = $mimeHdl->getMime(WBVFS_File::MIME_MAJOR | WBVFS_File::MIME_MINOR); $this->res->addHeader('Content-Type', $mime); $name = $file->getName(); $ext = $mimeHdl->getExtension(); if (!empty($ext)) { $name = $name . '.' . $ext; } $disposName = str_replace('"', '\\"', htmlspecialchars_decode($name)); $this->res->addHeader('Content-Disposition', 'inline; filename="' . $disposName . '"'); /* * Caching time in seconds * 60 * 60 * 24 * 370 * = 31968000 */ $this->res->setMaxAge(31968000); $this->res->addHeader('Content-Length', filesize($path)); $this->res->addHeader('Accept-Ranges', 'bytes'); $checksum = md5_file($path); $data = fopen($path, 'r'); $status = 200; return true; } /** * Check Access Rules * * @param WBVFS_File * @param WBVFS_Mime * @param string minor mime * @return bool, true if access is granted */ private function isAccessGranted($file, $mime, $mimeMinor) { if (empty($this->acl)) { $this->acl = WBClass::create('WBVFS_AccessControl'); } $this->acl->setVFSFile($file, $mime); return $this->acl->isRequestGranted($this->req, $mimeMinor); } }