X-Git-Url: https://git.heureux-cyclage.org/?p=lhc%2Fweb%2Fwiklou.git;a=blobdiff_plain;f=includes%2FGlobalFunctions.php;h=e80ecf1c464d3c187035e807d16bbfb955e46078;hp=799e3fc22efd788c01017291b7204c4c22a8ce35;hb=af09cc10883257b872308729b04242b0fe8e2fb8;hpb=990c1f0ad277d3e8ce72b6885b64aa2cdf100fe4 diff --git a/includes/GlobalFunctions.php b/includes/GlobalFunctions.php index 799e3fc22e..e80ecf1c46 100644 --- a/includes/GlobalFunctions.php +++ b/includes/GlobalFunctions.php @@ -26,8 +26,10 @@ if ( !defined( 'MEDIAWIKI' ) ) { use Liuggio\StatsdClient\Sender\SocketSender; use MediaWiki\Logger\LoggerFactory; +use MediaWiki\ProcOpenError; use MediaWiki\Session\SessionManager; use MediaWiki\MediaWikiServices; +use MediaWiki\Shell\Shell; use Wikimedia\ScopedCallback; use Wikimedia\Rdbms\DBReplicationWaitError; @@ -2237,64 +2239,12 @@ function wfIniGetBool( $setting ) { * @param string $args,... strings to escape and glue together, * or a single array of strings parameter * @return string + * @deprecated since 1.30 use MediaWiki\Shell::escape() */ function wfEscapeShellArg( /*...*/ ) { $args = func_get_args(); - if ( count( $args ) === 1 && is_array( reset( $args ) ) ) { - // If only one argument has been passed, and that argument is an array, - // treat it as a list of arguments - $args = reset( $args ); - } - $first = true; - $retVal = ''; - foreach ( $args as $arg ) { - if ( !$first ) { - $retVal .= ' '; - } else { - $first = false; - } - - if ( wfIsWindows() ) { - // Escaping for an MSVC-style command line parser and CMD.EXE - // @codingStandardsIgnoreStart For long URLs - // Refs: - // * https://web.archive.org/web/20020708081031/http://mailman.lyra.org/pipermail/scite-interest/2002-March/000436.html - // * https://technet.microsoft.com/en-us/library/cc723564.aspx - // * T15518 - // * CR r63214 - // Double the backslashes before any double quotes. Escape the double quotes. - // @codingStandardsIgnoreEnd - $tokens = preg_split( '/(\\\\*")/', $arg, -1, PREG_SPLIT_DELIM_CAPTURE ); - $arg = ''; - $iteration = 0; - foreach ( $tokens as $token ) { - if ( $iteration % 2 == 1 ) { - // Delimiter, a double quote preceded by zero or more slashes - $arg .= str_replace( '\\', '\\\\', substr( $token, 0, -1 ) ) . '\\"'; - } elseif ( $iteration % 4 == 2 ) { - // ^ in $token will be outside quotes, need to be escaped - $arg .= str_replace( '^', '^^', $token ); - } else { // $iteration % 4 == 0 - // ^ in $token will appear inside double quotes, so leave as is - $arg .= $token; - } - $iteration++; - } - // Double the backslashes before the end of the string, because - // we will soon add a quote - $m = []; - if ( preg_match( '/^(.*?)(\\\\+)$/', $arg, $m ) ) { - $arg = $m[1] . str_replace( '\\', '\\\\', $m[2] ); - } - - // Add surrounding quotes - $retVal .= '"' . $arg . '"'; - } else { - $retVal .= escapeshellarg( $arg ); - } - } - return $retVal; + return call_user_func_array( Shell::class . '::escape', $args ); } /** @@ -2302,18 +2252,10 @@ function wfEscapeShellArg( /*...*/ ) { * * @return bool|string False or 'disabled' * @since 1.22 + * @deprecated since 1.30 use MediaWiki\Shell::isDisabled() */ function wfShellExecDisabled() { - static $disabled = null; - if ( is_null( $disabled ) ) { - if ( !function_exists( 'proc_open' ) ) { - wfDebug( "proc_open() is disabled\n" ); - $disabled = 'disabled'; - } else { - $disabled = false; - } - } - return $disabled; + return Shell::isDisabled() ? 'disabled' : false; } /** @@ -2337,221 +2279,40 @@ function wfShellExecDisabled() { * method. Set this to a string for an alternative method to profile from * * @return string Collected stdout as a string + * @deprecated since 1.30 use class MediaWiki\Shell\Shell */ function wfShellExec( $cmd, &$retval = null, $environ = [], $limits = [], $options = [] ) { - global $IP, $wgMaxShellMemory, $wgMaxShellFileSize, $wgMaxShellTime, - $wgMaxShellWallClockTime, $wgShellCgroup; - - $disabled = wfShellExecDisabled(); - if ( $disabled ) { + if ( Shell::isDisabled() ) { $retval = 1; + // Backwards compatibility be upon us... return 'Unable to run external programs, proc_open() is disabled.'; } - $includeStderr = isset( $options['duplicateStderr'] ) && $options['duplicateStderr']; - $profileMethod = isset( $options['profileMethod'] ) ? $options['profileMethod'] : wfGetCaller(); - - $envcmd = ''; - foreach ( $environ as $k => $v ) { - if ( wfIsWindows() ) { - /* Surrounding a set in quotes (method used by wfEscapeShellArg) makes the quotes themselves - * appear in the environment variable, so we must use carat escaping as documented in - * https://technet.microsoft.com/en-us/library/cc723564.aspx - * Note however that the quote isn't listed there, but is needed, and the parentheses - * are listed there but doesn't appear to need it. - */ - $envcmd .= "set $k=" . preg_replace( '/([&|()<>^"])/', '^\\1', $v ) . '&& '; - } else { - /* Assume this is a POSIX shell, thus required to accept variable assignments before the command - * http://www.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html#tag_02_09_01 - */ - $envcmd .= "$k=" . escapeshellarg( $v ) . ' '; - } - } if ( is_array( $cmd ) ) { - $cmd = wfEscapeShellArg( $cmd ); + $cmd = Shell::escape( $cmd ); } - $cmd = $envcmd . $cmd; + $includeStderr = isset( $options['duplicateStderr'] ) && $options['duplicateStderr']; + $profileMethod = isset( $options['profileMethod'] ) ? $options['profileMethod'] : wfGetCaller(); - $useLogPipe = false; - if ( is_executable( '/bin/bash' ) ) { - $time = intval( isset( $limits['time'] ) ? $limits['time'] : $wgMaxShellTime ); - if ( isset( $limits['walltime'] ) ) { - $wallTime = intval( $limits['walltime'] ); - } elseif ( isset( $limits['time'] ) ) { - $wallTime = $time; - } else { - $wallTime = intval( $wgMaxShellWallClockTime ); - } - $mem = intval( isset( $limits['memory'] ) ? $limits['memory'] : $wgMaxShellMemory ); - $filesize = intval( isset( $limits['filesize'] ) ? $limits['filesize'] : $wgMaxShellFileSize ); - - if ( $time > 0 || $mem > 0 || $filesize > 0 || $wallTime > 0 ) { - $cmd = '/bin/bash ' . escapeshellarg( "$IP/includes/limit.sh" ) . ' ' . - escapeshellarg( $cmd ) . ' ' . - escapeshellarg( - "MW_INCLUDE_STDERR=" . ( $includeStderr ? '1' : '' ) . ';' . - "MW_CPU_LIMIT=$time; " . - 'MW_CGROUP=' . escapeshellarg( $wgShellCgroup ) . '; ' . - "MW_MEM_LIMIT=$mem; " . - "MW_FILE_SIZE_LIMIT=$filesize; " . - "MW_WALL_CLOCK_LIMIT=$wallTime; " . - "MW_USE_LOG_PIPE=yes" - ); - $useLogPipe = true; - } elseif ( $includeStderr ) { - $cmd .= ' 2>&1'; - } - } elseif ( $includeStderr ) { - $cmd .= ' 2>&1'; - } - wfDebug( "wfShellExec: $cmd\n" ); - - // Don't try to execute commands that exceed Linux's MAX_ARG_STRLEN. - // Other platforms may be more accomodating, but we don't want to be - // accomodating, because very long commands probably include user - // input. See T129506. - if ( strlen( $cmd ) > SHELL_MAX_ARG_STRLEN ) { - throw new Exception( __METHOD__ . - '(): total length of $cmd must not exceed SHELL_MAX_ARG_STRLEN' ); - } - - $desc = [ - 0 => [ 'file', 'php://stdin', 'r' ], - 1 => [ 'pipe', 'w' ], - 2 => [ 'file', 'php://stderr', 'w' ] ]; - if ( $useLogPipe ) { - $desc[3] = [ 'pipe', 'w' ]; - } - $pipes = null; - $scoped = Profiler::instance()->scopedProfileIn( __FUNCTION__ . '-' . $profileMethod ); - $proc = proc_open( $cmd, $desc, $pipes ); - if ( !$proc ) { - wfDebugLog( 'exec', "proc_open() failed: $cmd" ); + try { + $result = Shell::command( [] ) + ->unsafeParams( (array)$cmd ) + ->environment( $environ ) + ->limits( $limits ) + ->includeStderr( $includeStderr ) + ->profileMethod( $profileMethod ) + ->execute(); + } catch ( ProcOpenError $ex ) { $retval = -1; return ''; } - $outBuffer = $logBuffer = ''; - $emptyArray = []; - $status = false; - $logMsg = false; - - /* According to the documentation, it is possible for stream_select() - * to fail due to EINTR. I haven't managed to induce this in testing - * despite sending various signals. If it did happen, the error - * message would take the form: - * - * stream_select(): unable to select [4]: Interrupted system call (max_fd=5) - * - * where [4] is the value of the macro EINTR and "Interrupted system - * call" is string which according to the Linux manual is "possibly" - * localised according to LC_MESSAGES. - */ - $eintr = defined( 'SOCKET_EINTR' ) ? SOCKET_EINTR : 4; - $eintrMessage = "stream_select(): unable to select [$eintr]"; - - $running = true; - $timeout = null; - $numReadyPipes = 0; - - while ( $running === true || $numReadyPipes !== 0 ) { - if ( $running ) { - $status = proc_get_status( $proc ); - // If the process has terminated, switch to nonblocking selects - // for getting any data still waiting to be read. - if ( !$status['running'] ) { - $running = false; - $timeout = 0; - } - } - $readyPipes = $pipes; + $retval = $result->getExitCode(); - // Clear last error - // @codingStandardsIgnoreStart Generic.PHP.NoSilencedErrors.Discouraged - @trigger_error( '' ); - $numReadyPipes = @stream_select( $readyPipes, $emptyArray, $emptyArray, $timeout ); - if ( $numReadyPipes === false ) { - // @codingStandardsIgnoreEnd - $error = error_get_last(); - if ( strncmp( $error['message'], $eintrMessage, strlen( $eintrMessage ) ) == 0 ) { - continue; - } else { - trigger_error( $error['message'], E_USER_WARNING ); - $logMsg = $error['message']; - break; - } - } - foreach ( $readyPipes as $fd => $pipe ) { - $block = fread( $pipe, 65536 ); - if ( $block === '' ) { - // End of file - fclose( $pipes[$fd] ); - unset( $pipes[$fd] ); - if ( !$pipes ) { - break 2; - } - } elseif ( $block === false ) { - // Read error - $logMsg = "Error reading from pipe"; - break 2; - } elseif ( $fd == 1 ) { - // From stdout - $outBuffer .= $block; - } elseif ( $fd == 3 ) { - // From log FD - $logBuffer .= $block; - if ( strpos( $block, "\n" ) !== false ) { - $lines = explode( "\n", $logBuffer ); - $logBuffer = array_pop( $lines ); - foreach ( $lines as $line ) { - wfDebugLog( 'exec', $line ); - } - } - } - } - } - - foreach ( $pipes as $pipe ) { - fclose( $pipe ); - } - - // Use the status previously collected if possible, since proc_get_status() - // just calls waitpid() which will not return anything useful the second time. - if ( $running ) { - $status = proc_get_status( $proc ); - } - - if ( $logMsg !== false ) { - // Read/select error - $retval = -1; - proc_close( $proc ); - } elseif ( $status['signaled'] ) { - $logMsg = "Exited with signal {$status['termsig']}"; - $retval = 128 + $status['termsig']; - proc_close( $proc ); - } else { - if ( $status['running'] ) { - $retval = proc_close( $proc ); - } else { - $retval = $status['exitcode']; - proc_close( $proc ); - } - if ( $retval == 127 ) { - $logMsg = "Possibly missing executable file"; - } elseif ( $retval >= 129 && $retval <= 192 ) { - $logMsg = "Probably exited with signal " . ( $retval - 128 ); - } - } - - if ( $logMsg !== false ) { - wfDebugLog( 'exec', "$logMsg: $cmd" ); - } - - return $outBuffer; + return $result->getStdout(); } /** @@ -2569,6 +2330,7 @@ function wfShellExec( $cmd, &$retval = null, $environ = [], * @param array $limits Optional array with limits(filesize, memory, time, walltime) * this overwrites the global wgMaxShell* limits. * @return string Collected stdout and stderr as a string + * @deprecated since 1.30 use class MediaWiki\Shell\Shell */ function wfShellExecWithStderr( $cmd, &$retval = null, $environ = [], $limits = [] ) { return wfShellExec( $cmd, $retval, $environ, $limits, @@ -2609,7 +2371,7 @@ function wfShellWikiCmd( $script, array $parameters = [], array $options = [] ) } $cmd[] = $script; // Escape each parameter for shell - return wfEscapeShellArg( array_merge( $cmd, $parameters ) ); + return Shell::escape( array_merge( $cmd, $parameters ) ); } /** @@ -2654,7 +2416,7 @@ function wfMerge( $old, $mine, $yours, &$result ) { fclose( $yourtextFile ); # Check for a conflict - $cmd = wfEscapeShellArg( $wgDiff3, '-a', '--overlap-only', $mytextName, + $cmd = Shell::escape( $wgDiff3, '-a', '--overlap-only', $mytextName, $oldtextName, $yourtextName ); $handle = popen( $cmd, 'r' ); @@ -2666,7 +2428,7 @@ function wfMerge( $old, $mine, $yours, &$result ) { pclose( $handle ); # Merge differences - $cmd = wfEscapeShellArg( $wgDiff3, '-a', '-e', '--merge', $mytextName, + $cmd = Shell::escape( $wgDiff3, '-a', '-e', '--merge', $mytextName, $oldtextName, $yourtextName ); $handle = popen( $cmd, 'r' ); $result = ''; @@ -2730,7 +2492,7 @@ function wfDiff( $before, $after, $params = '-u' ) { fclose( $newtextFile ); // Get the diff of the two files - $cmd = "$wgDiff " . $params . ' ' . wfEscapeShellArg( $oldtextName, $newtextName ); + $cmd = "$wgDiff " . $params . ' ' . Shell::escape( $oldtextName, $newtextName ); $h = popen( $cmd, 'r' ); if ( !$h ) { @@ -2781,6 +2543,9 @@ function wfDiff( $before, $after, $params = '-u' ) { * @see perldoc -f use * * @param string|int|float $req_ver The version to check, can be a string, an integer, or a float + * + * @deprecated since 1.30 + * * @throws MWException */ function wfUsePHP( $req_ver ) { @@ -2809,7 +2574,7 @@ function wfUsePHP( $req_ver ) { * * @see perldoc -f use * - * @deprecated since 1.26, use the "requires' property of extension.json + * @deprecated since 1.26, use the "requires" property of extension.json * @param string|int|float $req_ver The version to check, can be a string, an integer, or a float * @throws MWException */ @@ -2916,14 +2681,6 @@ function wfBaseConvert( $input, $sourceBase, $destBase, $pad = 1, return Wikimedia\base_convert( $input, $sourceBase, $destBase, $pad, $lowercase, $engine ); } -/** - * @deprecated since 1.27, PHP's session generation isn't used with - * MediaWiki\Session\SessionManager - */ -function wfFixSessionID() { - wfDeprecated( __FUNCTION__, '1.27' ); -} - /** * Reset the session id *