Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F14100668
D18305.id44017.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
16 KB
Referenced Files
None
Subscribers
None
D18305.id44017.diff
View Options
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
Details
Attached
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)
Attached To
Mode
D18305: Consolidate binary (hg, git, svn) analysis code into one place
Attached
Detach File
Event Timeline
Log In to Comment