TYPO3  7.6
CommandUtility.php
Go to the documentation of this file.
1 <?php
2 namespace TYPO3\CMS\Core\Utility;
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 
46 {
52  protected static $initialized = false;
53 
63  protected static $applications = array();
64 
70  protected static $paths = null;
71 
82  public static function exec($command, &$output = null, &$returnValue = 0)
83  {
84  $lastLine = exec($command, $output, $returnValue);
85  return $lastLine;
86  }
87 
96  public static function imageMagickCommand($command, $parameters, $path = '')
97  {
98  $gfxConf = $GLOBALS['TYPO3_CONF_VARS']['GFX'];
99  $isExt = TYPO3_OS == 'WIN' ? '.exe' : '';
100  $switchCompositeParameters = false;
101  if (!$path) {
102  $path = $gfxConf['im_path'];
103  }
104  $path = GeneralUtility::fixWindowsFilePath($path);
105  $im_version = strtolower($gfxConf['im_version_5']);
106  // This is only used internally, has no effect outside
107  if ($command === 'combine') {
108  $command = 'composite';
109  }
110  // Compile the path & command
111  if ($im_version === 'gm') {
112  $switchCompositeParameters = true;
113  $path = self::escapeShellArgument($path . 'gm' . $isExt) . ' ' . self::escapeShellArgument($command);
114  } else {
115  if ($im_version === 'im6') {
116  $switchCompositeParameters = true;
117  }
118  $path = self::escapeShellArgument($path . ($command == 'composite' ? 'composite' : $command) . $isExt);
119  }
120  // strip profile information for thumbnails and reduce their size
121  if ($parameters && $command != 'identify' && $gfxConf['im_useStripProfileByDefault'] && $gfxConf['im_stripProfileCommand'] != '') {
122  if (strpos($parameters, $gfxConf['im_stripProfileCommand']) === false) {
123  // Determine whether the strip profile action has be disabled by TypoScript:
124  if ($parameters !== '-version' && strpos($parameters, '###SkipStripProfile###') === false) {
125  $parameters = $gfxConf['im_stripProfileCommand'] . ' ' . $parameters;
126  } else {
127  $parameters = str_replace('###SkipStripProfile###', '', $parameters);
128  }
129  }
130  }
131  $cmdLine = $path . ' ' . $parameters;
132  // Because of some weird incompatibilities between ImageMagick 4 and 6 (plus GraphicsMagick),
133  // it is needed to change the parameters order under some preconditions
134  if ($command == 'composite' && $switchCompositeParameters) {
135  $paramsArr = GeneralUtility::unQuoteFilenames($parameters);
136  // The mask image has been specified => swap the parameters
137  $paramsArrCount = count($paramsArr);
138  if ($paramsArrCount > 5) {
139  $tmp = $paramsArr[$paramsArrCount - 3];
140  $paramsArr[$paramsArrCount - 3] = $paramsArr[$paramsArrCount - 4];
141  $paramsArr[$paramsArrCount - 4] = $tmp;
142  }
143  $cmdLine = $path . ' ' . implode(' ', $paramsArr);
144  }
145  return $cmdLine;
146  }
147 
155  public static function checkCommand($cmd, $handler = '')
156  {
157  if (!self::init()) {
158  return false;
159  }
160 
161  if ($handler && !self::checkCommand($handler)) {
162  return -1;
163  }
164  // Already checked and valid
165  if (self::$applications[$cmd]['valid']) {
166  return true;
167  }
168  // Is set but was (above) not TRUE
169  if (isset(self::$applications[$cmd]['valid'])) {
170  return false;
171  }
172 
173  foreach (self::$paths as $path => $validPath) {
174  // Ignore invalid (FALSE) paths
175  if ($validPath) {
176  if (TYPO3_OS == 'WIN') {
177  // Windows OS
178  // @todo Why is_executable() is not called here?
179  if (@is_file($path . $cmd)) {
180  self::$applications[$cmd]['app'] = $cmd;
181  self::$applications[$cmd]['path'] = $path;
182  self::$applications[$cmd]['valid'] = true;
183  return true;
184  }
185  if (@is_file($path . $cmd . '.exe')) {
186  self::$applications[$cmd]['app'] = $cmd . '.exe';
187  self::$applications[$cmd]['path'] = $path;
188  self::$applications[$cmd]['valid'] = true;
189  return true;
190  }
191  } else {
192  // Unix-like OS
193  $filePath = realpath($path . $cmd);
194  if ($filePath && @is_executable($filePath)) {
195  self::$applications[$cmd]['app'] = $cmd;
196  self::$applications[$cmd]['path'] = $path;
197  self::$applications[$cmd]['valid'] = true;
198  return true;
199  }
200  }
201  }
202  }
203 
204  // Try to get the executable with the command 'which'.
205  // It does the same like already done, but maybe on other paths
206  if (TYPO3_OS != 'WIN') {
207  $cmd = @self::exec('which ' . $cmd);
208  if (@is_executable($cmd)) {
209  self::$applications[$cmd]['app'] = $cmd;
210  self::$applications[$cmd]['path'] = dirname($cmd) . '/';
211  self::$applications[$cmd]['valid'] = true;
212  return true;
213  }
214  }
215 
216  return false;
217  }
218 
227  public static function getCommand($cmd, $handler = '', $handlerOpt = '')
228  {
229  if (!self::init()) {
230  return false;
231  }
232 
233  // Handler
234  if ($handler) {
235  $handler = self::getCommand($handler);
236 
237  if (!$handler) {
238  return -1;
239  }
240  $handler .= ' ' . $handlerOpt . ' ';
241  }
242 
243  // Command
244  if (!self::checkCommand($cmd)) {
245  return false;
246  }
247  $cmd = self::$applications[$cmd]['path'] . self::$applications[$cmd]['app'] . ' ';
248 
249  return trim($handler . $cmd);
250  }
251 
258  public static function addPaths($paths)
259  {
260  self::initPaths($paths);
261  }
262 
269  public static function getPaths($addInvalid = false)
270  {
271  if (!self::init()) {
272  return array();
273  }
274 
275  $paths = self::$paths;
276 
277  if (!$addInvalid) {
278  foreach ($paths as $path => $validPath) {
279  if (!$validPath) {
280  unset($paths[$path]);
281  }
282  }
283  }
284  return $paths;
285  }
286 
292  protected static function init()
293  {
294  if ($GLOBALS['TYPO3_CONF_VARS']['BE']['disable_exec_function']) {
295  return false;
296  }
297  if (!self::$initialized) {
298  self::initPaths();
299  self::$applications = self::getConfiguredApps();
300  self::$initialized = true;
301  }
302  return true;
303  }
304 
311  protected static function initPaths($paths = '')
312  {
313  $doCheck = false;
314 
315  // Init global paths array if not already done
316  if (!is_array(self::$paths)) {
317  self::$paths = self::getPathsInternal();
318  $doCheck = true;
319  }
320  // Merge the submitted paths array to the global
321  if ($paths) {
323  if (is_array($paths)) {
324  foreach ($paths as $path) {
325  // Make absolute path of relative
326  if (!preg_match('#^/#', $path)) {
327  $path = PATH_site . $path;
328  }
329  if (!isset(self::$paths[$path])) {
330  if (@is_dir($path)) {
331  self::$paths[$path] = $path;
332  } else {
333  self::$paths[$path] = false;
334  }
335  }
336  }
337  }
338  }
339  // Check if new paths are invalid
340  if ($doCheck) {
341  foreach (self::$paths as $path => $valid) {
342  // Ignore invalid (FALSE) paths
343  if ($valid and !@is_dir($path)) {
344  self::$paths[$path] = false;
345  }
346  }
347  }
348  }
349 
355  protected static function getConfiguredApps()
356  {
357  $cmdArr = array();
358 
359  if ($GLOBALS['TYPO3_CONF_VARS']['SYS']['binSetup']) {
360  $binSetup = str_replace(array('\'.chr(10).\'', '\' . LF . \''), LF, $GLOBALS['TYPO3_CONF_VARS']['SYS']['binSetup']);
361  $pathSetup = preg_split('/[\n,]+/', $binSetup);
362  foreach ($pathSetup as $val) {
363  if (trim($val) === '') {
364  continue;
365  }
366  list($cmd, $cmdPath) = GeneralUtility::trimExplode('=', $val, true, 2);
367  $cmdArr[$cmd]['app'] = basename($cmdPath);
368  $cmdArr[$cmd]['path'] = dirname($cmdPath) . '/';
369  $cmdArr[$cmd]['valid'] = true;
370  }
371  }
372 
373  return $cmdArr;
374  }
375 
381  protected static function getPathsInternal()
382  {
383  $pathsArr = array();
384  $sysPathArr = array();
385 
386  // Image magick paths first
387  // im_path_lzw take precedence over im_path
388  if (($imPath = ($GLOBALS['TYPO3_CONF_VARS']['GFX']['im_path_lzw'] ?: $GLOBALS['TYPO3_CONF_VARS']['GFX']['im_path']))) {
389  $imPath = self::fixPath($imPath);
390  $pathsArr[$imPath] = $imPath;
391  }
392 
393  // Add configured paths
394  if ($GLOBALS['TYPO3_CONF_VARS']['SYS']['binPath']) {
395  $sysPath = GeneralUtility::trimExplode(',', $GLOBALS['TYPO3_CONF_VARS']['SYS']['binPath'], true);
396  foreach ($sysPath as $val) {
397  $val = self::fixPath($val);
398  $sysPathArr[$val] = $val;
399  }
400  }
401 
402  // Add path from environment
403  // @todo how does this work for WIN
404  if ($GLOBALS['_SERVER']['PATH']) {
405  $sep = (TYPO3_OS == 'WIN' ? ';' : ':');
406  $envPath = GeneralUtility::trimExplode($sep, $GLOBALS['_SERVER']['PATH'], true);
407  foreach ($envPath as $val) {
408  $val = self::fixPath($val);
409  $sysPathArr[$val] = $val;
410  }
411  }
412 
413  // Set common paths for Unix (only)
414  if (TYPO3_OS !== 'WIN') {
415  $sysPathArr = array_merge($sysPathArr, array(
416  '/usr/bin/' => '/usr/bin/',
417  '/usr/local/bin/' => '/usr/local/bin/',
418  ));
419  }
420 
421  $pathsArr = array_merge($pathsArr, $sysPathArr);
422 
423  return $pathsArr;
424  }
425 
432  protected static function fixPath($path)
433  {
434  return str_replace('//', '/', $path . '/');
435  }
436 
445  public static function escapeShellArguments(array $input)
446  {
447  $isUTF8Filesystem = !empty($GLOBALS['TYPO3_CONF_VARS']['SYS']['UTF8filesystem']);
448  if ($isUTF8Filesystem) {
449  $currentLocale = setlocale(LC_CTYPE, 0);
450  setlocale(LC_CTYPE, $GLOBALS['TYPO3_CONF_VARS']['SYS']['systemLocale']);
451  }
452 
453  $output = array_map('escapeshellarg', $input);
454 
455  if ($isUTF8Filesystem) {
456  setlocale(LC_CTYPE, $currentLocale);
457  }
458 
459  return $output;
460  }
461 
470  public static function escapeShellArgument($input)
471  {
472  return self::escapeShellArguments(array($input))[0];
473  }
474 }