public function send404ImageNotFound() { $this->mSpecial->getOutput()->disable(); # No HTML output StreamFile::prepareForStream(null, null, null, true); # send 404 Not Found }
/** * @see FileBackendStore::doStoreInternal() * @return Status */ protected function doStoreInternal(array $params) { $status = Status::newGood(); list($dstCont, $dstRel) = $this->resolveStoragePathReal($params['dst']); if ($dstRel === null) { $status->fatal('backend-fail-invalidpath', $params['dst']); return $status; } // (a) Check the destination container and object try { $dContObj = $this->getContainer($dstCont); } catch (NoSuchContainerException $e) { $status->fatal('backend-fail-copy', $params['src'], $params['dst']); return $status; } catch (CloudFilesException $e) { // some other exception? $this->handleException($e, $status, __METHOD__, $params); return $status; } // (b) Get a SHA-1 hash of the object wfSuppressWarnings(); $sha1Hash = sha1_file($params['src']); wfRestoreWarnings(); if ($sha1Hash === false) { // source doesn't exist? $status->fatal('backend-fail-copy', $params['src'], $params['dst']); return $status; } $sha1Hash = wfBaseConvert($sha1Hash, 16, 36, 31); // (c) Actually store the object try { // Create a fresh CF_Object with no fields preloaded. // We don't want to preserve headers, metadata, and such. $obj = new CF_Object($dContObj, $dstRel, false, false); // skip HEAD $obj->setMetadataValues(array('Sha1base36' => $sha1Hash)); // The MD5 here will be checked within Swift against its own MD5. $obj->set_etag(md5_file($params['src'])); // Use the same content type as StreamFile for security $obj->content_type = StreamFile::contentTypeFromPath($params['dst']); if (!strlen($obj->content_type)) { // special case $obj->content_type = 'unknown/unknown'; } // Set any other custom headers if requested if (isset($params['headers'])) { $obj->headers += $this->sanitizeHdrs($params['headers']); } if (!empty($params['async'])) { // deferred wfSuppressWarnings(); $fp = fopen($params['src'], 'rb'); wfRestoreWarnings(); if (!$fp) { $status->fatal('backend-fail-copy', $params['src'], $params['dst']); } else { $op = $obj->write_async($fp, filesize($params['src']), true); $status->value = new SwiftFileOpHandle($this, $params, 'Store', $op); $status->value->resourcesToClose[] = $fp; $status->value->affectedObjects[] = $obj; } } else { // actually write the object in Swift $obj->load_from_filename($params['src'], true); // calls $obj->write() $this->purgeCDNCache(array($obj)); } } catch (CDNNotEnabledException $e) { // CDN not enabled; nothing to see here } catch (BadContentTypeException $e) { $status->fatal('backend-fail-contenttype', $params['dst']); } catch (IOException $e) { $status->fatal('backend-fail-copy', $params['src'], $params['dst']); } catch (CloudFilesException $e) { // some other exception? $this->handleException($e, $status, __METHOD__, $params); } return $status; }
/** * @see FileBackendStore::doCreateInternal() */ protected function doCreateInternal(array $params) { $status = Status::newGood(); list($dstCont, $dstRel) = $this->resolveStoragePathReal($params['dst']); if ($dstRel === null) { $status->fatal('backend-fail-invalidpath', $params['dst']); return $status; } // (a) Check the destination container and object try { unset($this->objCache[$params['dst']]); $dContObj = $this->getContainer($dstCont); if (empty($params['overwrite']) && $this->fileExists(array('src' => $params['dst'], 'latest' => 1))) { $status->fatal('backend-fail-alreadyexists', $params['dst']); return $status; } } catch (NoSuchContainerException $e) { $status->fatal('backend-fail-create', $params['dst']); $this->logException($e, __METHOD__, $params); return $status; } catch (InvalidResponseException $e) { $status->fatal('backend-fail-connect', $this->name); $this->logException($e, __METHOD__, $params); return $status; } catch (Exception $e) { // some other exception? $status->fatal('backend-fail-internal', $this->name); $this->logException($e, __METHOD__, $params); return $status; } // (b) Get a SHA-1 hash of the object $sha1Hash = wfBaseConvert(sha1($params['content']), 16, 36, 31); // (c) Actually create the object try { // Create a fresh CF_Object with no fields preloaded. // We don't want to preserve headers, metadata, and such. $obj = new CF_Object($dContObj, $dstRel, false, false); // skip HEAD // Note: metadata keys stored as [Upper case char][[Lower case char]...] $obj->metadata = array('Sha1base36' => $sha1Hash); // Manually set the ETag (https://github.com/rackspace/php-cloudfiles/issues/59). // The MD5 here will be checked within Swift against its own MD5. $obj->set_etag(md5($params['content'])); // Use the same content type as StreamFile for security $obj->content_type = StreamFile::contentTypeFromPath($params['dst']); // Actually write the object in Swift $obj->write($params['content']); } catch (BadContentTypeException $e) { $status->fatal('backend-fail-contenttype', $params['dst']); $this->logException($e, __METHOD__, $params); } catch (InvalidResponseException $e) { $status->fatal('backend-fail-connect', $this->name); $this->logException($e, __METHOD__, $params); } catch (Exception $e) { // some other exception? $status->fatal('backend-fail-internal', $this->name); $this->logException($e, __METHOD__, $params); } return $status; }
public function streamFile(array $params) { // The stream methods use the file extension to determine the // Content-Type (as MediaWiki should already validate it on upload). // The translated SHA1 path has no extension, so this needs to use // the untranslated path extension. $type = StreamFile::contentTypeFromPath($params['src']); if ($type && $type != 'unknown/unknown') { $params['headers'][] = "Content-type: {$type}"; } return $this->translateSrcParams(__FUNCTION__, $params); }
/** * @see FileBackend::streamFile() * @return Status */ public final function streamFile(array $params) { wfProfileIn(__METHOD__); wfProfileIn(__METHOD__ . '-' . $this->name); $status = Status::newGood(); $info = $this->getFileStat($params); if (!$info) { // let StreamFile handle the 404 $status->fatal('backend-fail-notexists', $params['src']); } // Set output buffer and HTTP headers for stream $extraHeaders = isset($params['headers']) ? $params['headers'] : array(); $res = StreamFile::prepareForStream($params['src'], $info, $extraHeaders); if ($res == StreamFile::NOT_MODIFIED) { // do nothing; client cache is up to date } elseif ($res == StreamFile::READY_STREAM) { wfProfileIn(__METHOD__ . '-send'); wfProfileIn(__METHOD__ . '-send-' . $this->name); $status = $this->doStreamFile($params); wfProfileOut(__METHOD__ . '-send-' . $this->name); wfProfileOut(__METHOD__ . '-send'); } else { $status->fatal('backend-fail-stream', $params['src']); } wfProfileOut(__METHOD__ . '-' . $this->name); wfProfileOut(__METHOD__); return $status; }
/** * Stream the file if there were no errors * * @param array $headers Additional HTTP headers to send on success * @return Status * @since 1.27 */ public function streamFileWithStatus($headers = []) { if (!$this->path) { return Status::newFatal('backend-fail-stream', '<no path>'); } elseif (FileBackend::isStoragePath($this->path)) { $be = $this->file->getRepo()->getBackend(); return $be->streamFile(['src' => $this->path, 'headers' => $headers]); } else { // FS-file $success = StreamFile::stream($this->getLocalCopyPath(), $headers); return $success ? Status::newGood() : Status::newFatal('backend-fail-stream', $this->path); } }
/** * Stream the file if there were no errors * * @param array $headers Additional HTTP headers to send on success * @return Bool success */ public function streamFile( $headers = array() ) { if ( !$this->path ) { return false; } elseif ( FileBackend::isStoragePath( $this->path ) ) { $be = $this->file->getRepo()->getBackend(); return $be->streamFile( array( 'src' => $this->path, 'headers' => $headers ) )->isOK(); } else { // FS-file return StreamFile::stream( $this->getLocalCopyPath(), $headers ); } }
/** * Stream a file to the browser. Back-compat alias for StreamFile::stream() * @deprecated since 1.19 */ function wfStreamFile($fname, $headers = array()) { wfDeprecated(__FUNCTION__, '1.19'); StreamFile::stream($fname, $headers); }
/** * Stream the file if there were no errors * * @param $headers Array Additional HTTP headers to send on success * @return Bool success */ public function streamFile($headers = array()) { return $this->path && StreamFile::stream($this->getLocalCopyPath(), $headers); }
public final function streamFile(array $params) { $ps = Profiler::instance()->scopedProfileIn(__METHOD__ . "-{$this->name}"); $status = Status::newGood(); $info = $this->getFileStat($params); if (!$info) { // let StreamFile handle the 404 $status->fatal('backend-fail-notexists', $params['src']); } // Set output buffer and HTTP headers for stream $extraHeaders = isset($params['headers']) ? $params['headers'] : array(); $res = StreamFile::prepareForStream($params['src'], $info, $extraHeaders); if ($res == StreamFile::NOT_MODIFIED) { // do nothing; client cache is up to date } elseif ($res == StreamFile::READY_STREAM) { $status = $this->doStreamFile($params); if (!$status->isOK()) { // Per bug 41113, nasty things can happen if bad cache entries get // stuck in cache. It's also possible that this error can come up // with simple race conditions. Clear out the stat cache to be safe. $this->clearCache(array($params['src'])); $this->deleteFileCache($params['src']); trigger_error("Bad stat cache or race condition for file {$params['src']}."); } } else { $status->fatal('backend-fail-stream', $params['src']); } return $status; }
/** * */ protected function streamThumbnail() { global $wgVipsThumbnailerHost, $wgVipsTestExpiry; $request = $this->getRequest(); # Validate title and file existance $title = Title::makeTitleSafe( NS_FILE, $request->getText( 'thumb' ) ); if ( is_null( $title ) ) { $this->streamError( 404, "VipsScaler: invalid title\n" ); return; } $file = wfFindFile( $title ); if ( !$file || !$file->exists() ) { $this->streamError( 404, "VipsScaler: file not found\n" ); return; } # Check if vips can handle this file if ( VipsScaler::getVipsHandler( $file ) === false ) { $this->streamError( 500, "VipsScaler: VIPS cannot handle this file type\n" ); return; } # Validate param string $handler = $file->getHandler(); $params = array( 'width' => $request->getInt( 'width' ) ); if ( !$handler->normaliseParams( $file, $params ) ) { $this->streamError( 500, "VipsScaler: invalid parameters\n" ); return; } # Get the thumbnail if ( is_null( $wgVipsThumbnailerHost ) || $request->getBool( 'noproxy' ) ) { # No remote scaler, need to do it ourselves. # Emulate the BitmapHandlerTransform hook $dstPath = VipsCommand::makeTemp( $file->getExtension() ); $dstUrl = ''; wfDebug( __METHOD__ . ": Creating vips thumbnail at $dstPath\n" ); $scalerParams = array( # The size to which the image will be resized 'physicalWidth' => $params['physicalWidth'], 'physicalHeight' => $params['physicalHeight'], 'physicalDimensions' => "{$params['physicalWidth']}x{$params['physicalHeight']}", # The size of the image on the page 'clientWidth' => $params['width'], 'clientHeight' => $params['height'], # Comment as will be added to the EXIF of the thumbnail 'comment' => isset( $params['descriptionUrl'] ) ? "File source: {$params['descriptionUrl']}" : '', # Properties of the original image 'srcWidth' => $file->getWidth(), 'srcHeight' => $file->getHeight(), 'mimeType' => $file->getMimeType(), 'srcPath' => $file->getPath(), 'dstPath' => $dstPath, 'dstUrl' => $dstUrl, ); $options = array(); if ( $request->getBool( 'bilinear' ) ) { $options['bilinear'] = true; wfDebug( __METHOD__ . ": using bilinear scaling\n" ); } if ( $request->getVal( 'sharpen' ) && $request->getVal( 'sharpen' ) < 5 ) { # Limit sharpen sigma to 5, otherwise we have to write huge convolution matrices $options['sharpen'] = array( 'sigma' => floatval( $request->getVal( 'sharpen' ) ) ); wfDebug( __METHOD__ . ": sharpening with radius {$options['sharpen']}\n" ); } # Call the hook $mto = null; VipsScaler::doTransform( $handler, $file, $scalerParams, $options, $mto ); if ( $mto && !$mto->isError() ) { wfDebug( __METHOD__ . ": streaming thumbnail...\n" ); $this->getOutput()->disable(); StreamFile::stream( $dstPath, array( "Cache-Control: public, max-age=$wgVipsTestExpiry, s-maxage=$wgVipsTestExpiry", 'Expires: ' . gmdate( 'r ', time() + $wgVipsTestExpiry ) ) ); } else { $this->streamError( 500, $mto->getHtmlMsg() ); } # Cleanup the temporary file wfSuppressWarnings(); unlink( $dstPath ); wfRestoreWarnings(); } else { # Request the thumbnail at a remote scaler $url = wfExpandUrl( $request->getRequestURL(), PROTO_INTERNAL ); $url = wfAppendQuery( $url, array( 'noproxy' => '1' ) ); wfDebug( __METHOD__ . ": Getting vips thumb from remote url $url\n" ); $bits = IP::splitHostAndPort( $wgVipsThumbnailerHost ); if ( !$bits ) { throw new MWException( __METHOD__.': $wgVipsThumbnailerHost is not set to a valid host' ); } list( $host, $port ) = $bits; if ( $port === false ) { $port = 80; } $proxy = IP::combineHostAndPort( $host, $port ); $options = array( 'method' => 'GET', 'proxy' => $proxy, ); $req = MWHttpRequest::factory( $url, $options ); $status = $req->execute(); if ( $status->isOk() ) { # Disable output and stream the file $this->getOutput()->disable(); wfResetOutputBuffers(); header( 'Content-Type: ' . $file->getMimeType() ); header( 'Content-Length: ' . strlen( $req->getContent() ) ); header( "Cache-Control: public, max-age=$wgVipsTestExpiry, s-maxage=$wgVipsTestExpiry" ); header( 'Expires: ' . gmdate( 'r ', time() + $wgVipsTestExpiry ) ); print $req->getContent(); } elseif ( $status->hasMessage( 'http-bad-status' ) ) { $this->streamError( 500, $req->getContent() ); return; } else { global $wgOut; $this->streamError( 500, $wgOut->parse( $status->getWikiText() ) ); return; } } }
/** * @return bool * * @throws \MWException */ public function showImage() { $error = null; $this->wg->Out->disable(); $info = $this->retrieveCaptcha(); if ($info) { $info['viewed'] = wfTimestamp(); $this->storeCaptcha($info); $salt = $info['salt']; $hash = $info['hash']; $file = $this->imagePath($salt, $hash); if (file_exists($file)) { header("Cache-Control: private, s-maxage=0, max-age=3600"); \StreamFile::stream($file); return true; } else { $error = 'File ' . $file . ' does not exist'; } } else { $error = 'Info is empty'; } wfHttpError(404, '404 not found', 'Requested non-existing captcha image'); $this->log('Captcha returned 404: ' . $error); return false; }