‪TYPO3CMS  9.5
CommandUtility.php
Go to the documentation of this file.
1 <?php
3 
4 /*
5  * This file is part of the TYPO3 CMS project.
6  *
7  * It is free software; you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License, either version 2
9  * of the License, or any later version.
10  *
11  * For the full copyright and license information, please read the
12  * LICENSE.txt file that was distributed with this source code.
13  *
14  * The TYPO3 project - inspiring people to share!
15  */
16 
18 
48 {
54  protected static ‪$initialized = false;
55 
65  protected static ‪$applications = [];
66 
72  protected static ‪$paths;
73 
83  public static function ‪exec($command, &‪$output = null, &$returnValue = 0)
84  {
85  return ‪exec($command, ‪$output, $returnValue);
86  }
87 
96  public static function ‪imageMagickCommand($command, $parameters, $path = '')
97  {
98  $gfxConf = ‪$GLOBALS['TYPO3_CONF_VARS']['GFX'];
99  $isExt = ‪Environment::isWindows() ? '.exe' : '';
100  if (!$path) {
101  $path = $gfxConf['processor_path'];
102  }
103  $path = GeneralUtility::fixWindowsFilePath($path);
104  // This is only used internally, has no effect outside
105  if ($command === 'combine') {
106  $command = 'composite';
107  }
108  // Compile the path & command
109  if ($gfxConf['processor'] === 'GraphicsMagick') {
110  $path = ‪self::escapeShellArgument($path . 'gm' . $isExt) . ' ' . ‪self::escapeShellArgument($command);
111  } else {
112  if (‪Environment::isWindows() && !@is_file($path . $command . $isExt)) {
113  $path = ‪self::escapeShellArgument($path . 'magick' . $isExt) . ' ' . ‪self::escapeShellArgument($command);
114  } else {
115  $path = ‪self::escapeShellArgument($path . $command . $isExt);
116  }
117  }
118  // strip profile information for thumbnails and reduce their size
119  if ($parameters && $command !== 'identify') {
120  // Determine whether the strip profile action has be disabled by TypoScript:
121  if ($gfxConf['processor_stripColorProfileByDefault']
122  && $gfxConf['processor_stripColorProfileCommand'] !== ''
123  && strpos($parameters, $gfxConf['processor_stripColorProfileCommand']) === false
124  && $parameters !== '-version'
125  && strpos($parameters, '###SkipStripProfile###') === false
126  ) {
127  $parameters = $gfxConf['processor_stripColorProfileCommand'] . ' ' . $parameters;
128  } else {
129  $parameters = str_replace('###SkipStripProfile###', '', $parameters);
130  }
131  }
132  // Add -auto-orient on convert so IM/GM respects the image orient
133  if ($parameters && $command === 'convert') {
134  $parameters = '-auto-orient ' . $parameters;
135  }
136  // set interlace parameter for convert command
137  if ($command !== 'identify' && $gfxConf['processor_interlace']) {
138  $parameters = '-interlace ' . $gfxConf['processor_interlace'] . ' ' . $parameters;
139  }
140  $cmdLine = $path . ' ' . $parameters;
141  // It is needed to change the parameters order when a mask image has been specified
142  if ($command === 'composite') {
143  $paramsArr = ‪self::unQuoteFilenames($parameters);
144  $paramsArrCount = count($paramsArr);
145  if ($paramsArrCount > 5) {
146  $tmp = $paramsArr[$paramsArrCount - 3];
147  $paramsArr[$paramsArrCount - 3] = $paramsArr[$paramsArrCount - 4];
148  $paramsArr[$paramsArrCount - 4] = $tmp;
149  }
150  $cmdLine = $path . ' ' . implode(' ', $paramsArr);
151  }
152  return $cmdLine;
153  }
154 
162  public static function ‪checkCommand($cmd, $handler = '')
163  {
164  if (!self::init()) {
165  return false;
166  }
167 
168  if ($handler && !self::checkCommand($handler)) {
169  return -1;
170  }
171  // Already checked and valid
172  if (self::$applications[$cmd]['valid'] ?? false) {
173  return true;
174  }
175  // Is set but was (above) not TRUE
176  if (isset(self::$applications[$cmd]['valid'])) {
177  return false;
178  }
179 
180  foreach (self::$paths as $path => $validPath) {
181  // Ignore invalid (FALSE) paths
182  if ($validPath) {
184  // Windows OS
185  // @todo Why is_executable() is not called here?
186  if (@is_file($path . $cmd)) {
187  self::$applications[$cmd]['app'] = $cmd;
188  self::$applications[$cmd]['path'] = $path;
189  self::$applications[$cmd]['valid'] = true;
190  return true;
191  }
192  if (@is_file($path . $cmd . '.exe')) {
193  self::$applications[$cmd]['app'] = $cmd . '.exe';
194  self::$applications[$cmd]['path'] = $path;
195  self::$applications[$cmd]['valid'] = true;
196  return true;
197  }
198  } else {
199  // Unix-like OS
200  $filePath = realpath($path . $cmd);
201  if ($filePath && @is_executable($filePath)) {
202  self::$applications[$cmd]['app'] = $cmd;
203  self::$applications[$cmd]['path'] = $path;
204  self::$applications[$cmd]['valid'] = true;
205  return true;
206  }
207  }
208  }
209  }
210 
211  // Try to get the executable with the command 'which'.
212  // It does the same like already done, but maybe on other paths
213  if (!‪Environment::isWindows()) {
214  $cmd = @‪self::exec('which ' . self::escapeShellArgument($cmd));
215  if (@is_executable($cmd)) {
216  self::$applications[$cmd]['app'] = $cmd;
217  self::$applications[$cmd]['path'] = ‪PathUtility::dirname($cmd) . '/';
218  self::$applications[$cmd]['valid'] = true;
219  return true;
220  }
221  }
222 
223  return false;
224  }
225 
234  public static function ‪getCommand($cmd, $handler = '', $handlerOpt = '')
235  {
236  if (!self::init()) {
237  return false;
238  }
239 
240  // Handler
241  if ($handler) {
242  $handler = ‪self::getCommand($handler);
243 
244  if (!$handler) {
245  return -1;
246  }
247  $handler .= ' ' . escapeshellcmd($handlerOpt) . ' ';
248  }
249 
250  // Command
251  if (!self::checkCommand($cmd)) {
252  return false;
253  }
254  $cmd = self::$applications[$cmd]['path'] . self::$applications[$cmd]['app'] . ' ';
255 
256  return trim($handler . $cmd);
257  }
258 
264  public static function ‪addPaths(‪$paths)
265  {
267  }
268 
275  public static function ‪getPaths($addInvalid = false)
276  {
277  if (!self::init()) {
278  return [];
279  }
280 
282 
283  if (!$addInvalid) {
284  foreach (‪$paths as $path => $validPath) {
285  if (!$validPath) {
286  unset(‪$paths[$path]);
287  }
288  }
289  }
290  return ‪$paths;
291  }
292 
298  protected static function ‪init()
299  {
300  if (‪$GLOBALS['TYPO3_CONF_VARS']['BE']['disable_exec_function']) {
301  return false;
302  }
303  if (!self::$initialized) {
305  self::$applications = ‪self::getConfiguredApps();
306  self::$initialized = true;
307  }
308  return true;
309  }
310 
316  protected static function ‪initPaths(‪$paths = '')
317  {
318  $doCheck = false;
319 
320  // Init global paths array if not already done
321  if (!is_array(self::$paths)) {
322  self::$paths = ‪self::getPathsInternal();
323  $doCheck = true;
324  }
325  // Merge the submitted paths array to the global
326  if (‪$paths) {
327  ‪$paths = GeneralUtility::trimExplode(',', ‪$paths, true);
328  if (is_array(‪$paths)) {
329  foreach (‪$paths as $path) {
330  // Make absolute path of relative
331  if (!preg_match('#^/#', $path)) {
332  $path = ‪Environment::getPublicPath() . '/' . $path;
333  }
334  if (!isset(self::$paths[$path])) {
335  if (@is_dir($path)) {
336  self::$paths[$path] = $path;
337  } else {
338  self::$paths[$path] = false;
339  }
340  }
341  }
342  }
343  }
344  // Check if new paths are invalid
345  if ($doCheck) {
346  foreach (self::$paths as $path => $valid) {
347  // Ignore invalid (FALSE) paths
348  if ($valid && !@is_dir($path)) {
349  self::$paths[$path] = false;
350  }
351  }
352  }
353  }
354 
360  protected static function ‪getConfiguredApps()
361  {
362  $cmdArr = [];
363 
364  if (‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['binSetup']) {
365  $binSetup = str_replace(['\'.chr(10).\'', '\' . LF . \''], LF, ‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['binSetup']);
366  $pathSetup = preg_split('/[\n,]+/', $binSetup);
367  foreach ($pathSetup as $val) {
368  if (trim($val) === '') {
369  continue;
370  }
371  list($cmd, $cmdPath) = GeneralUtility::trimExplode('=', $val, true, 2);
372  $cmdArr[$cmd]['app'] = ‪PathUtility::basename($cmdPath);
373  $cmdArr[$cmd]['path'] = ‪PathUtility::dirname($cmdPath) . '/';
374  $cmdArr[$cmd]['valid'] = true;
375  }
376  }
377 
378  return $cmdArr;
379  }
380 
386  protected static function ‪getPathsInternal()
387  {
388  $pathsArr = [];
389  $sysPathArr = [];
390 
391  // Image magick paths first
392  // processor_path_lzw take precedence over processor_path
393  if ($imPath = ‪$GLOBALS['TYPO3_CONF_VARS']['GFX']['processor_path_lzw'] ?: ‪$GLOBALS['TYPO3_CONF_VARS']['GFX']['processor_path']) {
394  $imPath = ‪self::fixPath($imPath);
395  $pathsArr[$imPath] = $imPath;
396  }
397 
398  // Add configured paths
399  if (‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['binPath']) {
400  $sysPath = GeneralUtility::trimExplode(',', ‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['binPath'], true);
401  foreach ($sysPath as $val) {
402  $val = ‪self::fixPath($val);
403  $sysPathArr[$val] = $val;
404  }
405  }
406 
407  // Add path from environment
408  if (!empty(‪$GLOBALS['_SERVER']['PATH']) || !empty(‪$GLOBALS['_SERVER']['Path'])) {
409  $sep = ‪Environment::isWindows() ? ';' : ':';
410  $serverPath = ‪$GLOBALS['_SERVER']['PATH'] ?? ‪$GLOBALS['_SERVER']['Path'];
411  $envPath = GeneralUtility::trimExplode($sep, $serverPath, true);
412  foreach ($envPath as $val) {
413  $val = ‪self::fixPath($val);
414  $sysPathArr[$val] = $val;
415  }
416  }
417 
418  // Set common paths for Unix (only)
419  if (!‪Environment::isWindows()) {
420  $sysPathArr = array_merge($sysPathArr, [
421  '/usr/bin/' => '/usr/bin/',
422  '/usr/local/bin/' => '/usr/local/bin/',
423  ]);
424  }
425 
426  $pathsArr = array_merge($pathsArr, $sysPathArr);
427 
428  return $pathsArr;
429  }
430 
437  protected static function ‪fixPath($path)
438  {
439  return str_replace('//', '/', $path . '/');
440  }
441 
450  public static function ‪escapeShellArguments(array $input)
451  {
452  $isUTF8Filesystem = !empty(‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['UTF8filesystem']);
453  $currentLocale = false;
454  if ($isUTF8Filesystem) {
455  $currentLocale = setlocale(LC_CTYPE, 0);
456  setlocale(LC_CTYPE, ‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['systemLocale']);
457  }
458 
459  ‪$output = array_map('escapeshellarg', $input);
460 
461  if ($isUTF8Filesystem && $currentLocale !== false) {
462  setlocale(LC_CTYPE, $currentLocale);
463  }
464 
465  return ‪$output;
466  }
467 
474  protected static function ‪unQuoteFilenames(string $parameters): array
475  {
476  $paramsArr = explode(' ', trim($parameters));
477  // Whenever a quote character (") is found, $quoteActive is set to the element number inside of $params.
478  // A value of -1 means that there are not open quotes at the current position.
479  $quoteActive = -1;
480  foreach ($paramsArr as $k => $v) {
481  if ($quoteActive > -1) {
482  $paramsArr[$quoteActive] .= ' ' . $v;
483  unset($paramsArr[$k]);
484  if (substr($v, -1) === $paramsArr[$quoteActive][0]) {
485  $quoteActive = -1;
486  }
487  } elseif (!trim($v)) {
488  // Remove empty elements
489  unset($paramsArr[$k]);
490  } elseif (preg_match('/^(["\'])/', $v) && substr($v, -1) !== $v[0]) {
491  $quoteActive = $k;
492  }
493  }
494  // Return re-indexed array
495  return array_values($paramsArr);
496  }
497 
506  public static function ‪escapeShellArgument($input)
507  {
508  return ‪self::escapeShellArguments([$input])[0];
509  }
510 }
‪TYPO3\CMS\Core\Core\Environment\getPublicPath
‪static string getPublicPath()
Definition: Environment.php:153
‪TYPO3\CMS\Core\Utility\CommandUtility\checkCommand
‪static bool checkCommand($cmd, $handler='')
Definition: CommandUtility.php:159
‪TYPO3\CMS\Core\Utility\PathUtility\dirname
‪static string dirname($path)
Definition: PathUtility.php:185
‪TYPO3\CMS\Core\Utility\CommandUtility\fixPath
‪static string fixPath($path)
Definition: CommandUtility.php:434
‪TYPO3\CMS\Core\Utility\CommandUtility\init
‪static bool init()
Definition: CommandUtility.php:295
‪TYPO3\CMS\Core\Core\Environment\isWindows
‪static bool isWindows()
Definition: Environment.php:266
‪TYPO3\CMS\Core\Utility
Definition: ArrayUtility.php:2
‪TYPO3\CMS\Core\Utility\CommandUtility\unQuoteFilenames
‪static array unQuoteFilenames(string $parameters)
Definition: CommandUtility.php:471
‪TYPO3\CMS\Core\Utility\CommandUtility\getPaths
‪static array getPaths($addInvalid=false)
Definition: CommandUtility.php:272
‪TYPO3\CMS\Core\Utility\PathUtility\basename
‪static string basename($path)
Definition: PathUtility.php:164
‪TYPO3\CMS\Core\Utility\CommandUtility\exec
‪static string exec($command, &$output=null, &$returnValue=0)
Definition: CommandUtility.php:80
‪TYPO3\CMS\Core\Utility\CommandUtility\$applications
‪static array $applications
Definition: CommandUtility.php:63
‪TYPO3\CMS\Core\Utility\CommandUtility\escapeShellArgument
‪static string escapeShellArgument($input)
Definition: CommandUtility.php:503
‪$output
‪$output
Definition: annotationChecker.php:113
‪TYPO3\CMS\Core\Utility\CommandUtility\getConfiguredApps
‪static array getConfiguredApps()
Definition: CommandUtility.php:357
‪TYPO3\CMS\Core\Utility\CommandUtility\initPaths
‪static initPaths($paths='')
Definition: CommandUtility.php:313
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:5
‪TYPO3\CMS\Core\Core\Environment
Definition: Environment.php:39
‪TYPO3\CMS\Core\Utility\CommandUtility\$initialized
‪static bool $initialized
Definition: CommandUtility.php:53
‪TYPO3\CMS\Core\Utility\CommandUtility
Definition: CommandUtility.php:48
‪TYPO3\CMS\Core\Utility\CommandUtility\addPaths
‪static addPaths($paths)
Definition: CommandUtility.php:261
‪TYPO3\CMS\Core\Utility\CommandUtility\imageMagickCommand
‪static string imageMagickCommand($command, $parameters, $path='')
Definition: CommandUtility.php:93
‪TYPO3\CMS\Core\Utility\CommandUtility\escapeShellArguments
‪static string[] escapeShellArguments(array $input)
Definition: CommandUtility.php:447
‪TYPO3\CMS\Core\Utility\CommandUtility\$paths
‪static array $paths
Definition: CommandUtility.php:69
‪TYPO3\CMS\Core\Utility\CommandUtility\getCommand
‪static mixed getCommand($cmd, $handler='', $handlerOpt='')
Definition: CommandUtility.php:231
‪TYPO3\CMS\Core\Utility\CommandUtility\getPathsInternal
‪static array getPathsInternal()
Definition: CommandUtility.php:383