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