Page MenuHomePhabricator

D18305.id44017.diff
No OneTemporary

D18305.id44017.diff

diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -126,6 +126,8 @@
'PhutilAuthUserAbortedException' => 'auth/exception/PhutilAuthUserAbortedException.php',
'PhutilBacktraceSignalHandler' => 'future/exec/PhutilBacktraceSignalHandler.php',
'PhutilBallOfPHP' => 'phage/util/PhutilBallOfPHP.php',
+ 'PhutilBinaryAnalyzer' => 'filesystem/binary/PhutilBinaryAnalyzer.php',
+ 'PhutilBinaryAnalyzerTestCase' => 'filesystem/binary/__tests__/PhutilBinaryAnalyzerTestCase.php',
'PhutilBitbucketAuthAdapter' => 'auth/PhutilBitbucketAuthAdapter.php',
'PhutilBootloader' => 'moduleutils/PhutilBootloader.php',
'PhutilBootloaderException' => 'moduleutils/PhutilBootloaderException.php',
@@ -195,6 +197,7 @@
'PhutilDefaultSyntaxHighlighterEngineTestCase' => 'markup/syntax/engine/__tests__/PhutilDefaultSyntaxHighlighterEngineTestCase.php',
'PhutilDeferredLog' => 'filesystem/PhutilDeferredLog.php',
'PhutilDeferredLogTestCase' => 'filesystem/__tests__/PhutilDeferredLogTestCase.php',
+ 'PhutilDiffBinaryAnalyzer' => 'filesystem/binary/PhutilDiffBinaryAnalyzer.php',
'PhutilDirectedScalarGraph' => 'utils/PhutilDirectedScalarGraph.php',
'PhutilDirectoryFixture' => 'filesystem/PhutilDirectoryFixture.php',
'PhutilDirectoryKeyValueCache' => 'cache/PhutilDirectoryKeyValueCache.php',
@@ -233,6 +236,7 @@
'PhutilFileTree' => 'filesystem/PhutilFileTree.php',
'PhutilFrenchLocale' => 'internationalization/locales/PhutilFrenchLocale.php',
'PhutilGermanLocale' => 'internationalization/locales/PhutilGermanLocale.php',
+ 'PhutilGitBinaryAnalyzer' => 'filesystem/binary/PhutilGitBinaryAnalyzer.php',
'PhutilGitHubAuthAdapter' => 'auth/PhutilGitHubAuthAdapter.php',
'PhutilGitHubFuture' => 'future/github/PhutilGitHubFuture.php',
'PhutilGitHubResponse' => 'future/github/PhutilGitHubResponse.php',
@@ -300,6 +304,7 @@
'PhutilMarkupEngine' => 'markup/PhutilMarkupEngine.php',
'PhutilMarkupTestCase' => 'markup/__tests__/PhutilMarkupTestCase.php',
'PhutilMemcacheKeyValueCache' => 'cache/PhutilMemcacheKeyValueCache.php',
+ 'PhutilMercurialBinaryAnalyzer' => 'filesystem/binary/PhutilMercurialBinaryAnalyzer.php',
'PhutilMethodNotImplementedException' => 'error/PhutilMethodNotImplementedException.php',
'PhutilMetricsChannel' => 'channel/PhutilMetricsChannel.php',
'PhutilMissingSymbolException' => 'symbols/exception/PhutilMissingSymbolException.php',
@@ -339,6 +344,7 @@
'PhutilProtocolChannel' => 'channel/PhutilProtocolChannel.php',
'PhutilProxyException' => 'error/PhutilProxyException.php',
'PhutilProxyIterator' => 'utils/PhutilProxyIterator.php',
+ 'PhutilPygmentizeBinaryAnalyzer' => 'filesystem/binary/PhutilPygmentizeBinaryAnalyzer.php',
'PhutilPygmentizeParser' => 'parser/PhutilPygmentizeParser.php',
'PhutilPygmentizeParserTestCase' => 'parser/__tests__/PhutilPygmentizeParserTestCase.php',
'PhutilPygmentsSyntaxHighlighter' => 'markup/syntax/highlighter/PhutilPygmentsSyntaxHighlighter.php',
@@ -411,6 +417,7 @@
'PhutilSprite' => 'sprites/PhutilSprite.php',
'PhutilSpriteSheet' => 'sprites/PhutilSpriteSheet.php',
'PhutilStreamIterator' => 'utils/PhutilStreamIterator.php',
+ 'PhutilSubversionBinaryAnalyzer' => 'filesystem/binary/PhutilSubversionBinaryAnalyzer.php',
'PhutilSymbolLoader' => 'symbols/PhutilSymbolLoader.php',
'PhutilSyntaxHighlighter' => 'markup/syntax/highlighter/PhutilSyntaxHighlighter.php',
'PhutilSyntaxHighlighterEngine' => 'markup/syntax/engine/PhutilSyntaxHighlighterEngine.php',
@@ -729,6 +736,8 @@
'PhutilAuthUserAbortedException' => 'PhutilAuthException',
'PhutilBacktraceSignalHandler' => 'PhutilSignalHandler',
'PhutilBallOfPHP' => 'Phobject',
+ 'PhutilBinaryAnalyzer' => 'Phobject',
+ 'PhutilBinaryAnalyzerTestCase' => 'PhutilTestCase',
'PhutilBitbucketAuthAdapter' => 'PhutilOAuth1AuthAdapter',
'PhutilBootloaderException' => 'Exception',
'PhutilBritishEnglishLocale' => 'PhutilLocale',
@@ -803,6 +812,7 @@
'PhutilDefaultSyntaxHighlighterEngineTestCase' => 'PhutilTestCase',
'PhutilDeferredLog' => 'Phobject',
'PhutilDeferredLogTestCase' => 'PhutilTestCase',
+ 'PhutilDiffBinaryAnalyzer' => 'PhutilBinaryAnalyzer',
'PhutilDirectedScalarGraph' => 'AbstractDirectedGraph',
'PhutilDirectoryFixture' => 'Phobject',
'PhutilDirectoryKeyValueCache' => 'PhutilKeyValueCache',
@@ -841,6 +851,7 @@
'PhutilFileTree' => 'Phobject',
'PhutilFrenchLocale' => 'PhutilLocale',
'PhutilGermanLocale' => 'PhutilLocale',
+ 'PhutilGitBinaryAnalyzer' => 'PhutilBinaryAnalyzer',
'PhutilGitHubAuthAdapter' => 'PhutilOAuthAuthAdapter',
'PhutilGitHubFuture' => 'FutureProxy',
'PhutilGitHubResponse' => 'Phobject',
@@ -911,6 +922,7 @@
'PhutilMarkupEngine' => 'Phobject',
'PhutilMarkupTestCase' => 'PhutilTestCase',
'PhutilMemcacheKeyValueCache' => 'PhutilKeyValueCache',
+ 'PhutilMercurialBinaryAnalyzer' => 'PhutilBinaryAnalyzer',
'PhutilMethodNotImplementedException' => 'Exception',
'PhutilMetricsChannel' => 'PhutilChannelChannel',
'PhutilMissingSymbolException' => 'Exception',
@@ -955,6 +967,7 @@
'Phobject',
'Iterator',
),
+ 'PhutilPygmentizeBinaryAnalyzer' => 'PhutilBinaryAnalyzer',
'PhutilPygmentizeParser' => 'Phobject',
'PhutilPygmentizeParserTestCase' => 'PhutilTestCase',
'PhutilPygmentsSyntaxHighlighter' => 'Phobject',
@@ -1028,6 +1041,7 @@
'Phobject',
'Iterator',
),
+ 'PhutilSubversionBinaryAnalyzer' => 'PhutilBinaryAnalyzer',
'PhutilSyntaxHighlighter' => 'Phobject',
'PhutilSyntaxHighlighterEngine' => 'Phobject',
'PhutilSyntaxHighlighterException' => 'Exception',
diff --git a/src/filesystem/binary/PhutilBinaryAnalyzer.php b/src/filesystem/binary/PhutilBinaryAnalyzer.php
new file mode 100644
--- /dev/null
+++ b/src/filesystem/binary/PhutilBinaryAnalyzer.php
@@ -0,0 +1,65 @@
+<?php
+
+abstract class PhutilBinaryAnalyzer
+ extends Phobject {
+
+ public function getBinaryName() {
+ return $this->getBinaryKey();
+ }
+
+ public function getBinaryKey() {
+ return $this->getPhobjectClassConstant('BINARY');
+ }
+
+ public function isBinaryAvailable() {
+ return Filesystem::binaryExists($this->getBinaryName());
+ }
+
+ abstract protected function newBinaryVersion();
+
+ protected function newBinaryPath() {
+ return Filesystem::resolveBinary($this->getBinaryName());
+ }
+
+ final public function getBinaryVersion() {
+ return $this->newBinaryVersion();
+ }
+
+ final public function requireBinaryVersion() {
+ $version = $this->getBinaryVersion();
+ if ($version === null) {
+ throw new Exception(
+ pht(
+ 'Unable to determine the installed version of binary "%s". This '.
+ 'version is required.'));
+ }
+ return $version;
+ }
+
+ final public function getBinaryPath() {
+ return $this->newBinaryPath();
+ }
+
+ final public static function getAllBinaries() {
+ return id(new PhutilClassMapQuery())
+ ->setAncestorClass(__CLASS__)
+ ->setUniqueMethod('getBinaryKey')
+ ->setSortMethod('getBinaryName')
+ ->execute();
+ }
+
+ final public static function getForBinary($binary) {
+ $map = self::getAllBinaries();
+
+ $analyzer = idx($map, $binary);
+ if (!$analyzer) {
+ throw new Exception(
+ pht(
+ 'No analyzer is available for binary "%s".',
+ $binary));
+ }
+
+ return $analyzer;
+ }
+
+}
diff --git a/src/filesystem/binary/PhutilDiffBinaryAnalyzer.php b/src/filesystem/binary/PhutilDiffBinaryAnalyzer.php
new file mode 100644
--- /dev/null
+++ b/src/filesystem/binary/PhutilDiffBinaryAnalyzer.php
@@ -0,0 +1,31 @@
+<?php
+
+final class PhutilDiffBinaryAnalyzer
+ extends PhutilBinaryAnalyzer {
+
+ const BINARY = 'diff';
+
+ protected function newBinaryVersion() {
+ list($err, $stdout) = exec_manual('diff --version');
+
+ if ($err) {
+ return null;
+ }
+
+ return self::parseDiffBinaryVersion($stdout);
+ }
+
+ public static function parseDiffBinaryVersion($stdout) {
+ $prefix = 'diff (GNU diffutils) ';
+
+ if (preg_match('(^'.preg_quote($prefix).')', $stdout)) {
+ $lines = phutil_split_lines($stdout, false);
+ $line = head($lines);
+ $version = substr($line, strlen($prefix));
+ return $version;
+ }
+
+ return null;
+ }
+
+}
diff --git a/src/filesystem/binary/PhutilGitBinaryAnalyzer.php b/src/filesystem/binary/PhutilGitBinaryAnalyzer.php
new file mode 100644
--- /dev/null
+++ b/src/filesystem/binary/PhutilGitBinaryAnalyzer.php
@@ -0,0 +1,28 @@
+<?php
+
+final class PhutilGitBinaryAnalyzer
+ extends PhutilBinaryAnalyzer {
+
+ const BINARY = 'git';
+
+ protected function newBinaryVersion() {
+ list($err, $stdout) = exec_manual('git --version');
+
+ if ($err) {
+ return null;
+ }
+
+ return self::parseGitBinaryVersion($stdout);
+ }
+
+ public static function parseGitBinaryVersion($stdout) {
+ if (preg_match('/^git version /', $stdout)) {
+ $version = substr($stdout, strlen('git version '));
+ $version = trim($version);
+ return $version;
+ }
+
+ return null;
+ }
+
+}
diff --git a/src/filesystem/binary/PhutilMercurialBinaryAnalyzer.php b/src/filesystem/binary/PhutilMercurialBinaryAnalyzer.php
new file mode 100644
--- /dev/null
+++ b/src/filesystem/binary/PhutilMercurialBinaryAnalyzer.php
@@ -0,0 +1,77 @@
+<?php
+
+final class PhutilMercurialBinaryAnalyzer
+ extends PhutilBinaryAnalyzer {
+
+ const BINARY = 'hg';
+
+ const CAPABILITY_FILES = 'files';
+ const CAPABILITY_INJECTION = 'injection';
+
+ protected function newBinaryVersion() {
+ list($err, $stdout) = exec_manual('hg --version --quiet');
+
+ if ($err) {
+ return null;
+ }
+
+ return self::parseMercurialBinaryVersion($stdout);
+ }
+
+ public static function parseMercurialBinaryVersion($stdout) {
+ // NOTE: At least on OSX, recent versions of Mercurial report this
+ // string in this format:
+ //
+ // Mercurial Distributed SCM (version 3.1.1+20140916)
+
+ $matches = null;
+ $pattern = '/^Mercurial Distributed SCM \(version ([\d.]+)/m';
+ if (preg_match($pattern, $stdout, $matches)) {
+ return $matches[1];
+ }
+
+ return null;
+ }
+
+ /**
+ * The `locate` command is deprecated as of Mercurial 3.2, to be replaced
+ * with `files` command, which supports most of the same arguments. This
+ * determines whether the new `files` command should be used instead of
+ * the `locate` command.
+ *
+ * @return boolean True if the version of Mercurial is new enough to support
+ * the `files` command, or false if otherwise.
+ */
+ public function isMercurialFilesCommandAvailable() {
+ return self::versionHasCapability(
+ $this->requireBinaryVersion(),
+ self::CAPABILITY_FILES);
+ }
+
+ public function isMercurialVulnerableToInjection() {
+ return self::versionHasCapability(
+ $this->requireBinaryVersion(),
+ self::CAPABILITY_INJECTION);
+ }
+
+
+ public static function versionHasCapability(
+ $mercurial_version,
+ $capability) {
+
+ switch ($capability) {
+ case self::CAPABILITY_FILES:
+ return version_compare($mercurial_version, '3.2', '>=');
+ case self::CAPABILITY_INJECTION:
+ return version_compare($mercurial_version, '3.2.4', '<');
+ default:
+ throw new Exception(
+ pht(
+ 'Unknown Mercurial capability "%s".',
+ $capability));
+ }
+
+ }
+
+
+}
diff --git a/src/filesystem/binary/PhutilPygmentizeBinaryAnalyzer.php b/src/filesystem/binary/PhutilPygmentizeBinaryAnalyzer.php
new file mode 100644
--- /dev/null
+++ b/src/filesystem/binary/PhutilPygmentizeBinaryAnalyzer.php
@@ -0,0 +1,31 @@
+<?php
+
+final class PhutilPygmentizeBinaryAnalyzer
+ extends PhutilBinaryAnalyzer {
+
+ const BINARY = 'pygmentize';
+
+ protected function newBinaryVersion() {
+ list($err, $stdout) = exec_manual('pygmentize -V');
+
+ if ($err) {
+ return null;
+ }
+
+ return self::parsePygmentizeBinaryVersion($stdout);
+ }
+
+ public static function parsePygmentizeBinaryVersion($stdout) {
+ // These look like:
+ // Pygments version 2.0.1, (c) 2006-2014 by Georg Brandl.
+
+ $pattern = '(^Pygments version ([^,]+),)';
+ $matches = null;
+ if (preg_match($pattern, $stdout, $matches)) {
+ return $matches[1];
+ }
+
+ return null;
+ }
+
+}
diff --git a/src/filesystem/binary/PhutilSubversionBinaryAnalyzer.php b/src/filesystem/binary/PhutilSubversionBinaryAnalyzer.php
new file mode 100644
--- /dev/null
+++ b/src/filesystem/binary/PhutilSubversionBinaryAnalyzer.php
@@ -0,0 +1,22 @@
+<?php
+
+final class PhutilSubversionBinaryAnalyzer
+ extends PhutilBinaryAnalyzer {
+
+ const BINARY = 'svn';
+
+ protected function newBinaryVersion() {
+ list($err, $stdout) = exec_manual('svn --version --quiet');
+
+ if ($err) {
+ return null;
+ }
+
+ return self::parseSubversionBinaryVersion($stdout);
+ }
+
+ public static function parseSubversionBinaryVersion($stdout) {
+ return trim($stdout);
+ }
+
+}
diff --git a/src/filesystem/binary/__tests__/PhutilBinaryAnalyzerTestCase.php b/src/filesystem/binary/__tests__/PhutilBinaryAnalyzerTestCase.php
new file mode 100644
--- /dev/null
+++ b/src/filesystem/binary/__tests__/PhutilBinaryAnalyzerTestCase.php
@@ -0,0 +1,117 @@
+<?php
+
+final class PhutilBinaryAnalyzerTestCase
+ extends PhutilTestCase {
+
+ public function testParseGitBinaryVersions() {
+ $map = array(
+ "git version 2.11.0\n" => '2.11.0',
+ 'definitely git 7.0' => null,
+ );
+
+ foreach ($map as $input => $expect) {
+ $actual = PhutilGitBinaryAnalyzer::parseGitBinaryVersion($input);
+ $this->assertEqual($expect, $actual, $input);
+ }
+ }
+
+ public function getParseMercurialBinaryVersions() {
+ $map = array(
+ "Mercurial Distributed SCM (version 3.5.2+20151001)\n"
+ => '3.5.2',
+ 'This Is Mercurial 22.0' => null,
+ );
+
+ foreach ($map as $input => $expect) {
+ $actual =
+ PhutilMercurialBinaryAnalyzer::parseMercurialBinaryVersion(
+ $input);
+ $this->assertEqual($expect, $actual, $input);
+ }
+ }
+
+ public function testParseSubversionBinaryVersions() {
+ $map = array(
+ "1.7.20\n" => '1.7.20',
+ );
+
+ foreach ($map as $input => $expect) {
+ $actual =
+ PhutilSubversionBinaryAnalyzer::parseSubversionBinaryVersion(
+ $input);
+ $this->assertEqual($expect, $actual, $input);
+ }
+ }
+
+ public function testParseDiffBinaryVersions() {
+ $diff_version_281 = <<<EOTEXT
+diff (GNU diffutils) 2.8.1
+Copyright (C) 2002 Free Software Foundation, Inc.
+
+This program comes with NO WARRANTY, to the extent permitted by law.
+You may redistribute copies of this program
+under the terms of the GNU General Public License.
+For more information about these matters, see the file named COPYING.
+
+Written by Paul Eggert, Mike Haertel, David Hayes,
+Richard Stallman, and Len Tower.
+EOTEXT;
+
+ $map = array(
+ $diff_version_281 => '2.8.1',
+ 'diff version 1.2.3' => null,
+ );
+
+ foreach ($map as $input => $expect) {
+ $actual = PhutilDiffBinaryAnalyzer::parseDiffBinaryVersion($input);
+ $this->assertEqual($expect, $actual, $input);
+ }
+ }
+
+ public function testParsePygmentizeBinaryVersions() {
+ $map = array(
+ "Pygments version 2.0.1, (c) 2006-2014 by Georg Brandl.\n"
+ => '2.0.1',
+ 'pygments 3.4' => null,
+ );
+
+ foreach ($map as $input => $expect) {
+ $actual =
+ PhutilPygmentizeBinaryAnalyzer::parsePygmentizeBinaryVersion(
+ $input);
+ $this->assertEqual($expect, $actual, $input);
+ }
+ }
+
+ public function testMercurialFilesCommandVersions() {
+ $cases = array(
+ PhutilMercurialBinaryAnalyzer::CAPABILITY_FILES => array(
+ '2.6.2' => false,
+ '2.9' => false,
+ '3.1' => false,
+ '3.2' => true,
+ '3.3' => true,
+ '3.5.2' => true,
+ ),
+ PhutilMercurialBinaryAnalyzer::CAPABILITY_INJECTION => array(
+ '2.0' => true,
+ '3.2.3' => true,
+ '3.2.4' => false,
+ ),
+ );
+
+ foreach ($cases as $capability => $map) {
+ foreach ($map as $input => $expect) {
+ $actual = PhutilMercurialBinaryAnalyzer::versionHasCapability(
+ $input,
+ $capability);
+ $this->assertEqual(
+ $expect,
+ $actual,
+ pht('%s on %s', $capability, $input));
+ }
+ }
+
+ }
+
+}

File Metadata

Mime Type
text/plain
Expires
Wed, Nov 27, 2:45 PM (7 h, 13 m)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
6794894
Default Alt Text
D18305.id44017.diff (16 KB)

Event Timeline