phpmyadmin/libraries/classes/Controllers/Transformation/WrapperController.php
Maurício Meneghini Fauth 1be42d13de
Create the Identifiers\Identifier interface
Changes Dbal\DatabaseName, Dbal\TableName and Triggers\TriggerName to
implements the new Identifier\Identifier interface.
Moves Dbal\DatabaseName, Dbal\TableName and Triggers\TriggerName to the
Identifiers namespace.
Renames the tryFromValue() and fromValue() methods to just tryFrom() and
from() respectively.

Signed-off-by: Maurício Meneghini Fauth <mauricio@fauth.dev>
2023-06-01 19:09:25 -03:00

212 lines
6.5 KiB
PHP

<?php
declare(strict_types=1);
namespace PhpMyAdmin\Controllers\Transformation;
use PhpMyAdmin\ConfigStorage\Relation;
use PhpMyAdmin\Controllers\AbstractController;
use PhpMyAdmin\Core;
use PhpMyAdmin\DatabaseInterface;
use PhpMyAdmin\DbTableExists;
use PhpMyAdmin\Http\ServerRequest;
use PhpMyAdmin\Identifiers\DatabaseName;
use PhpMyAdmin\Identifiers\InvalidIdentifier;
use PhpMyAdmin\Identifiers\TableName;
use PhpMyAdmin\Image\ImageWrapper;
use PhpMyAdmin\Message;
use PhpMyAdmin\ResponseRenderer;
use PhpMyAdmin\Template;
use PhpMyAdmin\Transformations;
use PhpMyAdmin\Util;
use function __;
use function htmlspecialchars;
use function intval;
use function is_numeric;
use function is_string;
use function round;
use function sprintf;
use function str_contains;
use function str_replace;
use function str_starts_with;
use function strtolower;
/**
* Wrapper script for rendering transformations
*/
class WrapperController extends AbstractController
{
public function __construct(
ResponseRenderer $response,
Template $template,
private Transformations $transformations,
private Relation $relation,
private DatabaseInterface $dbi,
) {
parent::__construct($response, $template);
}
public function __invoke(ServerRequest $request): void
{
$this->response->getHeader()->setIsTransformationWrapper(true);
try {
$db = DatabaseName::from($request->getParam('db'));
$table = TableName::from($request->getParam('table'));
} catch (InvalidIdentifier) {
return;
}
DbTableExists::check($db->getName(), $table->getName(), true);
$this->dbi->selectDb($db);
$query = $this->getQuery($table, $request->getParam('where_clause'), $request->getParam('where_clause_sign'));
if ($query === null) {
$this->response->setRequestStatus(false);
/* l10n: In case a SQL query did not pass a security check */
$this->response->addHTML(Message::error(__('There is an issue with your request.'))->getDisplay());
return;
}
$row = $this->dbi->query($query)->fetchAssoc();
if ($row === []) {
return;
}
$transformKey = $request->getParam('transform_key');
if (
! is_string($transformKey) || $transformKey === ''
|| ! isset($row[$transformKey]) || $row[$transformKey] === ''
) {
return;
}
$mediaTypeMap = [];
$mediaTypeOptions = [];
$relationParameters = $this->relation->getRelationParameters();
if (
$relationParameters->columnCommentsFeature !== null
&& $relationParameters->browserTransformationFeature !== null
) {
$mediaTypeMap = $this->transformations->getMime($db->getName(), $table->getName()) ?? [];
$mediaTypeOptions = $this->transformations->getOptions(
$mediaTypeMap[$transformKey]['transformation_options'] ?? '',
);
foreach ($mediaTypeOptions as $option) {
if (! str_starts_with($option, '; charset=')) {
continue;
}
$mediaTypeOptions['charset'] = $option;
}
}
$this->response->getHeader()->sendHttpHeaders();
/** @psalm-suppress MixedAssignment */
$contentType = $request->getParam('ct');
if (is_string($contentType) && $contentType !== '') {
$contentMediaType = $contentType;
} else {
$contentMediaType = 'application/octet-stream';
if (! empty($mediaTypeMap[$transformKey]['mimetype'])) {
$contentMediaType = str_replace('_', '/', $mediaTypeMap[$transformKey]['mimetype']);
}
$contentMediaType .= $mediaTypeOptions['charset'] ?? '';
}
/** @psalm-suppress MixedAssignment */
$contentName = $request->getParam('cn');
$contentName = is_string($contentName) ? $contentName : '';
Core::downloadHeader($contentName, $contentMediaType);
$resize = $request->getParam('resize');
if ($resize !== 'jpeg' && $resize !== 'png') {
if (str_contains(strtolower($contentMediaType), 'html')) {
echo htmlspecialchars($row[$transformKey]);
return;
}
echo $row[$transformKey];
return;
}
$srcImage = ImageWrapper::fromString($row[$transformKey]);
if ($srcImage === null) {
return;
}
$newHeight = $this->formatSize($request->getParam('newHeight'));
$newWidth = $this->formatSize($request->getParam('newWidth'));
$srcWidth = $srcImage->width();
$srcHeight = $srcImage->height();
$ratioWidth = $srcWidth / $newWidth;
$ratioHeight = $srcHeight / $newHeight;
/**
* Check to see if the width > height or if width < height
* if so adjust accordingly to make sure the image
* stays smaller than the new width and new height
*/
if ($ratioWidth < $ratioHeight) {
$destWidth = intval(round($srcWidth / $ratioHeight));
$destHeight = $newHeight;
} else {
$destWidth = $newWidth;
$destHeight = intval(round($srcHeight / $ratioWidth));
}
$destImage = ImageWrapper::create($destWidth, $destHeight);
if ($destImage === null) {
return;
}
$destImage->copyResampled($srcImage, 0, 0, 0, 0, $destWidth, $destHeight, $srcWidth, $srcHeight);
if ($resize === 'jpeg') {
$destImage->jpeg(null, 75);
} else {
$destImage->png();
}
}
private function formatSize(mixed $size): int
{
if (! is_numeric($size) || $size < 2) {
return 1;
}
if ($size >= 2000) {
return 2000;
}
return (int) $size;
}
private function getQuery(TableName $table, mixed $whereClause, mixed $whereClauseSign): string|null
{
if ($whereClause === null) {
return sprintf('SELECT * FROM %s LIMIT 1;', Util::backquote($table));
}
if (
! is_string($whereClause) || $whereClause === ''
|| ! is_string($whereClauseSign) || $whereClauseSign === ''
|| ! Core::checkSqlQuerySignature($whereClause, $whereClauseSign)
) {
return null;
}
return sprintf('SELECT * FROM %s WHERE %s;', Util::backquote($table), $whereClause);
}
}