|
8 | 8 | use PhpMyAdmin\Controllers\AbstractController; |
9 | 9 | use PhpMyAdmin\Core; |
10 | 10 | use PhpMyAdmin\DatabaseInterface; |
| 11 | +use PhpMyAdmin\Dbal\DatabaseName; |
| 12 | +use PhpMyAdmin\Dbal\TableName; |
11 | 13 | use PhpMyAdmin\DbTableExists; |
| 14 | +use PhpMyAdmin\Http\ServerRequest; |
12 | 15 | use PhpMyAdmin\Image\ImageWrapper; |
13 | 16 | use PhpMyAdmin\ResponseRenderer; |
14 | 17 | use PhpMyAdmin\Template; |
15 | 18 | use PhpMyAdmin\Transformations; |
16 | 19 | use PhpMyAdmin\Util; |
| 20 | +use Webmozart\Assert\InvalidArgumentException; |
17 | 21 |
|
18 | 22 | use function __; |
19 | 23 | use function htmlspecialchars; |
20 | | -use function in_array; |
21 | 24 | use function intval; |
| 25 | +use function is_numeric; |
| 26 | +use function is_string; |
22 | 27 | use function round; |
| 28 | +use function sprintf; |
| 29 | +use function str_contains; |
23 | 30 | use function str_replace; |
24 | | -use function stripos; |
25 | | -use function substr; |
| 31 | +use function str_starts_with; |
| 32 | +use function strtolower; |
26 | 33 |
|
27 | 34 | /** |
28 | 35 | * Wrapper script for rendering transformations |
@@ -51,194 +58,176 @@ public function __construct( |
51 | 58 | $this->dbi = $dbi; |
52 | 59 | } |
53 | 60 |
|
54 | | - public function __invoke(): void |
| 61 | + public function __invoke(ServerRequest $request): void |
55 | 62 | { |
56 | 63 | $this->response->getHeader()->setIsTransformationWrapper(true); |
57 | | - $GLOBALS['cn'] = $GLOBALS['cn'] ?? null; |
58 | | - $GLOBALS['transform_key'] = $GLOBALS['transform_key'] ?? null; |
59 | | - $GLOBALS['request_params'] = $GLOBALS['request_params'] ?? null; |
60 | | - $GLOBALS['size_params'] = $GLOBALS['size_params'] ?? null; |
61 | | - $GLOBALS['where_clause'] = $GLOBALS['where_clause'] ?? null; |
62 | | - $GLOBALS['row'] = $GLOBALS['row'] ?? null; |
63 | | - |
64 | | - $GLOBALS['default_ct'] = $GLOBALS['default_ct'] ?? null; |
65 | | - $GLOBALS['mime_map'] = $GLOBALS['mime_map'] ?? null; |
66 | | - $GLOBALS['mime_options'] = $GLOBALS['mime_options'] ?? null; |
67 | | - $GLOBALS['ct'] = $GLOBALS['ct'] ?? null; |
68 | | - $GLOBALS['mime_type'] = $GLOBALS['mime_type'] ?? null; |
69 | | - $GLOBALS['srcImage'] = $GLOBALS['srcImage'] ?? null; |
70 | | - $GLOBALS['srcWidth'] = $GLOBALS['srcWidth'] ?? null; |
71 | | - $GLOBALS['srcHeight'] = $GLOBALS['srcHeight'] ?? null; |
72 | | - $GLOBALS['ratioWidth'] = $GLOBALS['ratioWidth'] ?? null; |
73 | | - $GLOBALS['ratioHeight'] = $GLOBALS['ratioHeight'] ?? null; |
74 | | - $GLOBALS['destWidth'] = $GLOBALS['destWidth'] ?? null; |
75 | | - $GLOBALS['destHeight'] = $GLOBALS['destHeight'] ?? null; |
76 | | - $GLOBALS['destImage'] = $GLOBALS['destImage'] ?? null; |
77 | 64 |
|
78 | | - $relationParameters = $this->relation->getRelationParameters(); |
79 | | - |
80 | | - DbTableExists::check($GLOBALS['db'], $GLOBALS['table'], true); |
81 | | - |
82 | | - /** |
83 | | - * Sets globals from $_REQUEST |
84 | | - */ |
85 | | - $GLOBALS['request_params'] = [ |
86 | | - 'cn', |
87 | | - 'ct', |
88 | | - 'sql_query', |
89 | | - 'transform_key', |
90 | | - 'where_clause', |
91 | | - ]; |
92 | | - $GLOBALS['size_params'] = [ |
93 | | - 'newHeight', |
94 | | - 'newWidth', |
95 | | - ]; |
96 | | - foreach ($GLOBALS['request_params'] as $one_request_param) { |
97 | | - if (! isset($_REQUEST[$one_request_param])) { |
98 | | - continue; |
99 | | - } |
100 | | - |
101 | | - if (in_array($one_request_param, $GLOBALS['size_params'])) { |
102 | | - $GLOBALS[$one_request_param] = intval($_REQUEST[$one_request_param]); |
103 | | - if ($GLOBALS[$one_request_param] > 2000) { |
104 | | - $GLOBALS[$one_request_param] = 2000; |
105 | | - } |
106 | | - } else { |
107 | | - $GLOBALS[$one_request_param] = $_REQUEST[$one_request_param]; |
108 | | - } |
| 65 | + try { |
| 66 | + $db = DatabaseName::fromValue($request->getParam('db')); |
| 67 | + $table = TableName::fromValue($request->getParam('table')); |
| 68 | + } catch (InvalidArgumentException $exception) { |
| 69 | + return; |
109 | 70 | } |
110 | 71 |
|
111 | | - /** |
112 | | - * Get the list of the fields of the current table |
113 | | - */ |
114 | | - $this->dbi->selectDb($GLOBALS['db']); |
115 | | - if (isset($GLOBALS['where_clause'])) { |
116 | | - if (! Core::checkSqlQuerySignature($GLOBALS['where_clause'], $_GET['where_clause_sign'] ?? '')) { |
117 | | - /* l10n: In case a SQL query did not pass a security check */ |
118 | | - Core::fatalError(__('There is an issue with your request.')); |
| 72 | + DbTableExists::check($db->getName(), $table->getName(), true); |
| 73 | + $this->dbi->selectDb($db); |
119 | 74 |
|
120 | | - return; |
121 | | - } |
| 75 | + $query = $this->getQuery($table, $request->getParam('where_clause'), $request->getParam('where_clause_sign')); |
| 76 | + if ($query === null) { |
| 77 | + /* l10n: In case a SQL query did not pass a security check */ |
| 78 | + Core::fatalError(__('There is an issue with your request.')); |
122 | 79 |
|
123 | | - $result = $this->dbi->query( |
124 | | - 'SELECT * FROM ' . Util::backquote($GLOBALS['table']) |
125 | | - . ' WHERE ' . $GLOBALS['where_clause'] . ';' |
126 | | - ); |
127 | | - $GLOBALS['row'] = $result->fetchAssoc(); |
128 | | - } else { |
129 | | - $result = $this->dbi->query( |
130 | | - 'SELECT * FROM ' . Util::backquote($GLOBALS['table']) . ' LIMIT 1;' |
131 | | - ); |
132 | | - $GLOBALS['row'] = $result->fetchAssoc(); |
| 80 | + return; |
133 | 81 | } |
134 | 82 |
|
135 | | - // No row returned |
136 | | - if ($GLOBALS['row'] === []) { |
| 83 | + $row = $this->dbi->query($query)->fetchAssoc(); |
| 84 | + if ($row === []) { |
137 | 85 | return; |
138 | 86 | } |
139 | 87 |
|
140 | | - $GLOBALS['default_ct'] = 'application/octet-stream'; |
| 88 | + $transformKey = $request->getParam('transform_key'); |
| 89 | + if ( |
| 90 | + ! is_string($transformKey) || $transformKey === '' |
| 91 | + || ! isset($row[$transformKey]) || $row[$transformKey] === '' |
| 92 | + ) { |
| 93 | + return; |
| 94 | + } |
141 | 95 |
|
| 96 | + $mediaTypeMap = []; |
| 97 | + $mediaTypeOptions = []; |
| 98 | + $relationParameters = $this->relation->getRelationParameters(); |
142 | 99 | if ( |
143 | 100 | $relationParameters->columnCommentsFeature !== null |
144 | 101 | && $relationParameters->browserTransformationFeature !== null |
145 | 102 | ) { |
146 | | - $GLOBALS['mime_map'] = $this->transformations->getMime($GLOBALS['db'], $GLOBALS['table']) ?? []; |
147 | | - |
148 | | - $GLOBALS['mime_options'] = $this->transformations->getOptions( |
149 | | - $GLOBALS['mime_map'][$GLOBALS['transform_key']]['transformation_options'] ?? '' |
| 103 | + $mediaTypeMap = $this->transformations->getMime($db->getName(), $table->getName()) ?? []; |
| 104 | + $mediaTypeOptions = $this->transformations->getOptions( |
| 105 | + $mediaTypeMap[$transformKey]['transformation_options'] ?? '' |
150 | 106 | ); |
151 | 107 |
|
152 | | - foreach ($GLOBALS['mime_options'] as $option) { |
153 | | - if (substr($option, 0, 10) !== '; charset=') { |
| 108 | + foreach ($mediaTypeOptions as $option) { |
| 109 | + if (! str_starts_with($option, '; charset=')) { |
154 | 110 | continue; |
155 | 111 | } |
156 | 112 |
|
157 | | - $GLOBALS['mime_options']['charset'] = $option; |
| 113 | + $mediaTypeOptions['charset'] = $option; |
158 | 114 | } |
159 | 115 | } |
160 | 116 |
|
161 | 117 | $this->response->getHeader()->sendHttpHeaders(); |
162 | 118 |
|
163 | | - // [MIME] |
164 | | - if (isset($GLOBALS['ct']) && ! empty($GLOBALS['ct'])) { |
165 | | - $GLOBALS['mime_type'] = $GLOBALS['ct']; |
| 119 | + /** @psalm-suppress MixedAssignment */ |
| 120 | + $contentType = $request->getParam('ct'); |
| 121 | + if (is_string($contentType) && $contentType !== '') { |
| 122 | + $contentMediaType = $contentType; |
166 | 123 | } else { |
167 | | - $GLOBALS['mime_type'] = (! empty($GLOBALS['mime_map'][$GLOBALS['transform_key']]['mimetype']) |
168 | | - ? str_replace('_', '/', $GLOBALS['mime_map'][$GLOBALS['transform_key']]['mimetype']) |
169 | | - : $GLOBALS['default_ct']) |
170 | | - . ($GLOBALS['mime_options']['charset'] ?? ''); |
| 124 | + $contentMediaType = 'application/octet-stream'; |
| 125 | + if (! empty($mediaTypeMap[$transformKey]['mimetype'])) { |
| 126 | + $contentMediaType = str_replace('_', '/', $mediaTypeMap[$transformKey]['mimetype']); |
| 127 | + } |
| 128 | + |
| 129 | + $contentMediaType .= $mediaTypeOptions['charset'] ?? ''; |
171 | 130 | } |
172 | 131 |
|
173 | | - Core::downloadHeader($GLOBALS['cn'] ?? '', $GLOBALS['mime_type']); |
| 132 | + /** @psalm-suppress MixedAssignment */ |
| 133 | + $contentName = $request->getParam('cn'); |
| 134 | + $contentName = is_string($contentName) ? $contentName : ''; |
174 | 135 |
|
175 | | - if (! isset($_REQUEST['resize'])) { |
176 | | - if (stripos($GLOBALS['mime_type'], 'html') === false) { |
177 | | - echo $GLOBALS['row'][$GLOBALS['transform_key']]; |
178 | | - } else { |
179 | | - echo htmlspecialchars($GLOBALS['row'][$GLOBALS['transform_key']]); |
180 | | - } |
181 | | - } else { |
182 | | - // if image_*__inline.inc.php finds that we can resize, |
183 | | - // it sets the resize parameter to jpeg or png |
| 136 | + Core::downloadHeader($contentName, $contentMediaType); |
| 137 | + |
| 138 | + $resize = $request->getParam('resize'); |
| 139 | + if ($resize !== 'jpeg' && $resize !== 'png') { |
| 140 | + if (str_contains(strtolower($contentMediaType), 'html')) { |
| 141 | + echo htmlspecialchars($row[$transformKey]); |
184 | 142 |
|
185 | | - $GLOBALS['srcImage'] = ImageWrapper::fromString($GLOBALS['row'][$GLOBALS['transform_key']]); |
186 | | - if ($GLOBALS['srcImage'] === null) { |
187 | 143 | return; |
188 | 144 | } |
189 | 145 |
|
190 | | - $GLOBALS['srcWidth'] = $GLOBALS['srcImage']->width(); |
191 | | - $GLOBALS['srcHeight'] = $GLOBALS['srcImage']->height(); |
| 146 | + echo $row[$transformKey]; |
192 | 147 |
|
193 | | - // Check to see if the width > height or if width < height |
194 | | - // if so adjust accordingly to make sure the image |
195 | | - // stays smaller than the new width and new height |
| 148 | + return; |
| 149 | + } |
196 | 150 |
|
197 | | - $GLOBALS['ratioWidth'] = $GLOBALS['srcWidth'] / $_REQUEST['newWidth']; |
198 | | - $GLOBALS['ratioHeight'] = $GLOBALS['srcHeight'] / $_REQUEST['newHeight']; |
| 151 | + $srcImage = ImageWrapper::fromString($row[$transformKey]); |
| 152 | + if ($srcImage === null) { |
| 153 | + return; |
| 154 | + } |
199 | 155 |
|
200 | | - if ($GLOBALS['ratioWidth'] < $GLOBALS['ratioHeight']) { |
201 | | - $GLOBALS['destWidth'] = intval(round($GLOBALS['srcWidth'] / $GLOBALS['ratioHeight'])); |
202 | | - $GLOBALS['destHeight'] = intval($_REQUEST['newHeight']); |
203 | | - } else { |
204 | | - $GLOBALS['destWidth'] = intval($_REQUEST['newWidth']); |
205 | | - $GLOBALS['destHeight'] = intval(round($GLOBALS['srcHeight'] / $GLOBALS['ratioWidth'])); |
206 | | - } |
| 156 | + $newHeight = $this->formatSize($request->getParam('newHeight')); |
| 157 | + $newWidth = $this->formatSize($request->getParam('newWidth')); |
207 | 158 |
|
208 | | - if ($_REQUEST['resize']) { |
209 | | - $GLOBALS['destImage'] = ImageWrapper::create($GLOBALS['destWidth'], $GLOBALS['destHeight']); |
210 | | - if ($GLOBALS['destImage'] === null) { |
211 | | - $GLOBALS['srcImage']->destroy(); |
| 159 | + $srcWidth = $srcImage->width(); |
| 160 | + $srcHeight = $srcImage->height(); |
212 | 161 |
|
213 | | - return; |
214 | | - } |
| 162 | + $ratioWidth = $srcWidth / $newWidth; |
| 163 | + $ratioHeight = $srcHeight / $newHeight; |
215 | 164 |
|
216 | | - // ImageCopyResized($destImage, $srcImage, 0, 0, 0, 0, |
217 | | - // $destWidth, $destHeight, $srcWidth, $srcHeight); |
218 | | - // better quality but slower: |
219 | | - $GLOBALS['destImage']->copyResampled( |
220 | | - $GLOBALS['srcImage'], |
221 | | - 0, |
222 | | - 0, |
223 | | - 0, |
224 | | - 0, |
225 | | - $GLOBALS['destWidth'], |
226 | | - $GLOBALS['destHeight'], |
227 | | - $GLOBALS['srcWidth'], |
228 | | - $GLOBALS['srcHeight'] |
229 | | - ); |
230 | | - if ($_REQUEST['resize'] === 'jpeg') { |
231 | | - $GLOBALS['destImage']->jpeg(null, 75); |
232 | | - } |
| 165 | + /** |
| 166 | + * Check to see if the width > height or if width < height |
| 167 | + * if so adjust accordingly to make sure the image |
| 168 | + * stays smaller than the new width and new height |
| 169 | + */ |
| 170 | + if ($ratioWidth < $ratioHeight) { |
| 171 | + $destWidth = intval(round($srcWidth / $ratioHeight)); |
| 172 | + $destHeight = $newHeight; |
| 173 | + } else { |
| 174 | + $destWidth = $newWidth; |
| 175 | + $destHeight = intval(round($srcHeight / $ratioWidth)); |
| 176 | + } |
233 | 177 |
|
234 | | - if ($_REQUEST['resize'] === 'png') { |
235 | | - $GLOBALS['destImage']->png(); |
236 | | - } |
| 178 | + $destImage = ImageWrapper::create($destWidth, $destHeight); |
| 179 | + if ($destImage === null) { |
| 180 | + $srcImage->destroy(); |
237 | 181 |
|
238 | | - $GLOBALS['destImage']->destroy(); |
239 | | - } |
| 182 | + return; |
| 183 | + } |
240 | 184 |
|
241 | | - $GLOBALS['srcImage']->destroy(); |
| 185 | + $destImage->copyResampled($srcImage, 0, 0, 0, 0, $destWidth, $destHeight, $srcWidth, $srcHeight); |
| 186 | + |
| 187 | + if ($resize === 'jpeg') { |
| 188 | + $destImage->jpeg(null, 75); |
| 189 | + } else { |
| 190 | + $destImage->png(); |
242 | 191 | } |
| 192 | + |
| 193 | + $destImage->destroy(); |
| 194 | + $srcImage->destroy(); |
| 195 | + } |
| 196 | + |
| 197 | + /** |
| 198 | + * @param mixed $size |
| 199 | + */ |
| 200 | + private function formatSize($size): int |
| 201 | + { |
| 202 | + if (! is_numeric($size) || $size < 2) { |
| 203 | + return 1; |
| 204 | + } |
| 205 | + |
| 206 | + if ($size >= 2000) { |
| 207 | + return 2000; |
| 208 | + } |
| 209 | + |
| 210 | + return (int) $size; |
| 211 | + } |
| 212 | + |
| 213 | + /** |
| 214 | + * @param mixed $whereClause |
| 215 | + * @param mixed $whereClauseSign |
| 216 | + */ |
| 217 | + private function getQuery(TableName $table, $whereClause, $whereClauseSign): ?string |
| 218 | + { |
| 219 | + if ($whereClause === null) { |
| 220 | + return sprintf('SELECT * FROM %s LIMIT 1;', Util::backquote($table)); |
| 221 | + } |
| 222 | + |
| 223 | + if ( |
| 224 | + ! is_string($whereClause) || $whereClause === '' |
| 225 | + || ! is_string($whereClauseSign) || $whereClauseSign === '' |
| 226 | + || ! Core::checkSqlQuerySignature($whereClause, $whereClauseSign) |
| 227 | + ) { |
| 228 | + return null; |
| 229 | + } |
| 230 | + |
| 231 | + return sprintf('SELECT * FROM %s WHERE %s;', Util::backquote($table), $whereClause); |
243 | 232 | } |
244 | 233 | } |
0 commit comments