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 @@ -635,6 +635,7 @@ 'DrydockSFTPFilesystemInterface' => 'applications/drydock/interface/filesystem/DrydockSFTPFilesystemInterface.php', 'DrydockSSHCommandInterface' => 'applications/drydock/interface/command/DrydockSSHCommandInterface.php', 'DrydockWebrootInterface' => 'applications/drydock/interface/webroot/DrydockWebrootInterface.php', + 'DrydockWinRMCommandInterface' => 'applications/drydock/interface/command/DrydockWinRMCommandInterface.php', 'DrydockWorkingCopyBlueprintImplementation' => 'applications/drydock/blueprint/DrydockWorkingCopyBlueprintImplementation.php', 'FeedConduitAPIMethod' => 'applications/feed/conduit/FeedConduitAPIMethod.php', 'FeedPublishConduitAPIMethod' => 'applications/feed/conduit/FeedPublishConduitAPIMethod.php', @@ -3429,6 +3430,7 @@ 'DrydockSFTPFilesystemInterface' => 'DrydockFilesystemInterface', 'DrydockSSHCommandInterface' => 'DrydockCommandInterface', 'DrydockWebrootInterface' => 'DrydockInterface', + 'DrydockWinRMCommandInterface' => 'DrydockCommandInterface', 'DrydockWorkingCopyBlueprintImplementation' => 'DrydockBlueprintImplementation', 'FeedConduitAPIMethod' => 'ConduitAPIMethod', 'FeedPublishConduitAPIMethod' => 'FeedConduitAPIMethod', diff --git a/src/applications/drydock/blueprint/DrydockPreallocatedHostBlueprintImplementation.php b/src/applications/drydock/blueprint/DrydockPreallocatedHostBlueprintImplementation.php --- a/src/applications/drydock/blueprint/DrydockPreallocatedHostBlueprintImplementation.php +++ b/src/applications/drydock/blueprint/DrydockPreallocatedHostBlueprintImplementation.php @@ -75,19 +75,7 @@ $full_path = $base_path.$separator.$lease_id; $cmd = $lease->getInterface('command'); - - if ($v_platform !== 'windows') { - $cmd->execx('mkdir %s', $full_path); - } else { - // Windows is terrible. The mkdir command doesn't even support putting - // the path in quotes. IN QUOTES. ARGUHRGHUGHHGG!! Do some terribly - // inaccurate sanity checking since we can't safely escape the path. - if (preg_match('/^[A-Z]\\:\\\\[a-zA-Z0-9\\\\\\ ]/', $full_path) === 0) { - throw new Exception( - 'Unsafe path detected for Windows platform: "'.$full_path.'".'); - } - $cmd->execx('mkdir %C', $full_path); - } + $cmd->execx('mkdir %s', $full_path); $lease->setAttribute('path', $full_path); } @@ -103,7 +91,12 @@ switch ($type) { case 'command': - return id(new DrydockSSHCommandInterface()) + $interface = new DrydockSSHCommandInterface(); + if ($resource->getAttribute('platform') === 'windows') { + $interface = new DrydockWinRMCommandInterface(); + } + + return $interface ->setConfiguration(array( 'host' => $resource->getAttribute('host'), 'port' => $resource->getAttribute('port'), diff --git a/src/applications/drydock/interface/command/DrydockSSHCommandInterface.php b/src/applications/drydock/interface/command/DrydockSSHCommandInterface.php --- a/src/applications/drydock/interface/command/DrydockSSHCommandInterface.php +++ b/src/applications/drydock/interface/command/DrydockSSHCommandInterface.php @@ -36,45 +36,10 @@ $argv = func_get_args(); - if ($this->getConfig('platform') === 'windows') { - // Handle Windows by executing the command under PowerShell. - $command = id(new PhutilCommandString($argv)) - ->setEscapingMode(PhutilCommandString::MODE_POWERSHELL); + // Handle UNIX by executing under the native shell. + $argv = $this->applyWorkingDirectoryToArgv($argv); - $change_directory = ''; - if ($this->getWorkingDirectory() !== null) { - $change_directory .= 'cd '.$this->getWorkingDirectory(); - } - - $script = <<applyWorkingDirectoryToArgv($argv); - - $full_command = call_user_func_array('csprintf', $argv); - } + $full_command = call_user_func_array('csprintf', $argv); $command_timeout = ''; if ($this->connectTimeout !== null) { diff --git a/src/applications/drydock/interface/command/DrydockWinRMCommandInterface.php b/src/applications/drydock/interface/command/DrydockWinRMCommandInterface.php new file mode 100644 --- /dev/null +++ b/src/applications/drydock/interface/command/DrydockWinRMCommandInterface.php @@ -0,0 +1,69 @@ +passphraseWinRMPassword !== null) { + return; + } + + $credential = id(new PassphraseCredentialQuery()) + ->setViewer(PhabricatorUser::getOmnipotentUser()) + ->withIDs(array($this->getConfig('credential'))) + ->needSecrets(true) + ->executeOne(); + + if ($credential->getProvidesType() !== + PassphraseCredentialTypePassword::PROVIDES_TYPE) { + throw new Exception('Only password credentials are supported.'); + } + + $this->passphraseWinRMPassword = PassphrasePasswordKey::loadFromPHID( + $credential->getPHID(), + PhabricatorUser::getOmnipotentUser()); + } + + public function getExecFuture($command) { + $this->openCredentialsIfNotOpen(); + + $argv = func_get_args(); + + $change_directory = ''; + if ($this->getWorkingDirectory() !== null) { + $change_directory .= 'cd '.$this->getWorkingDirectory().' & '; + } + + // Encode the command to run under Powershell. + $command = id(new PhutilCommandString($argv)) + ->setEscapingMode(PhutilCommandString::MODE_POWERSHELL); + + // When Microsoft says "Unicode" they don't mean UTF-8. + $command = mb_convert_encoding($command, 'UTF-16LE'); + $command = base64_encode($command); + + $powershell = + 'C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe'; + $powershell .= + ' -ExecutionPolicy Bypass'. + ' -NonInteractive'. + ' -InputFormat Text'. + ' -OutputFormat Text'. + ' -EncodedCommand '.$command; + + return new ExecFuture( + 'winrm '. + '-hostname=%s '. + '-username=%P '. + '-password=%P '. + '-port=%s '. + '%s', + $this->getConfig('host'), + $this->passphraseWinRMPassword->getUsernameEnvelope(), + $this->passphraseWinRMPassword->getPasswordEnvelope(), + $this->getConfig('port'), + $change_directory.$powershell); + } +}