public function willWriteMessageCallback(PhabricatorSSHPassthruCommand $command, $message)
 {
     $command = $message['command'];
     // Check if this is a readonly command.
     $is_readonly = false;
     if ($command == 'batch') {
         $cmds = idx($message['arguments'], 'cmds');
         if (DiffusionMercurialWireProtocol::isReadOnlyBatchCommand($cmds)) {
             $is_readonly = true;
         }
     } else {
         if (DiffusionMercurialWireProtocol::isReadOnlyCommand($command)) {
             $is_readonly = true;
         }
     }
     if (!$is_readonly) {
         $this->requireWriteAccess();
         $this->didSeeWrite = true;
     }
     $raw_message = $message['raw'];
     if ($command == 'capabilities') {
         $raw_message = DiffusionMercurialWireProtocol::filterBundle2Capability($raw_message);
     }
     // If we're good, return the raw message data.
     return $raw_message;
 }
 public function testFilteringBundle2Capability()
 {
     // this was the result of running 'capabilities' over
     // `hg serve --stdio` on my systems with Mercurial 3.5.1, 2.6.2
     $capabilities_with_bundle2_hg_351 = 'lookup changegroupsubset branchmap pushkey ' . 'known getbundle unbundlehash batch stream ' . 'bundle2=HG20%0Achangegroup%3D01%2C02%0Adigests%3Dmd5%2Csha1%2Csha512' . '%0Aerror%3Dabort%2Cunsupportedcontent%2Cpushraced%2Cpushkey%0A' . 'hgtagsfnodes%0Alistkeys%0Apushkey%0Aremote-changegroup%3Dhttp%2Chttps ' . 'unbundle=HG10GZ,HG10BZ,HG10UN httpheader=1024';
     $capabilities_without_bundle2_hg_351 = 'lookup changegroupsubset branchmap pushkey ' . 'known getbundle unbundlehash batch stream ' . 'unbundle=HG10GZ,HG10BZ,HG10UN httpheader=1024';
     $capabilities_hg_262 = 'lookup changegroupsubset branchmap pushkey ' . 'known getbundle unbundlehash batch stream ' . 'unbundle=HG10GZ,HG10BZ,HG10UN httpheader=1024 largefiles=serve';
     $cases = array(array('name' => pht('Filter bundle2 from Mercurial 3.5.1'), 'input' => $capabilities_with_bundle2_hg_351, 'expect' => $capabilities_without_bundle2_hg_351), array('name' => pht('Filter bundle does not affect Mercurial 2.6.2'), 'input' => $capabilities_hg_262, 'expect' => $capabilities_hg_262));
     foreach ($cases as $case) {
         $actual = DiffusionMercurialWireProtocol::filterBundle2Capability($case['input']);
         $this->assertEqual($case['expect'], $actual, $case['name']);
     }
 }
 private function serveMercurialRequest(PhabricatorRepository $repository, PhabricatorUser $viewer)
 {
     $request = $this->getRequest();
     $bin = Filesystem::resolveBinary('hg');
     if (!$bin) {
         throw new Exception(pht('Unable to find `%s` in %s!', 'hg', '$PATH'));
     }
     $env = $this->getCommonEnvironment($viewer);
     $input = PhabricatorStartup::getRawInput();
     $cmd = $request->getStr('cmd');
     $args = $this->getMercurialArguments();
     $args = $this->formatMercurialArguments($cmd, $args);
     if (strlen($input)) {
         $input = strlen($input) . "\n" . $input . "0\n";
     }
     $command = csprintf('%s serve --stdio', $bin);
     $command = PhabricatorDaemon::sudoCommandAsDaemonUser($command);
     list($err, $stdout, $stderr) = id(new ExecFuture('%C', $command))->setEnv($env, true)->setCWD($repository->getLocalPath())->write("{$cmd}\n{$args}{$input}")->resolve();
     if ($err) {
         return new PhabricatorVCSResponse(500, pht('Error %d: %s', $err, $stderr));
     }
     if ($cmd == 'getbundle' || $cmd == 'changegroup' || $cmd == 'changegroupsubset') {
         // We're not completely sure that "changegroup" and "changegroupsubset"
         // actually work, they're for very old Mercurial.
         $body = gzcompress($stdout);
     } else {
         if ($cmd == 'unbundle') {
             // This includes diagnostic information and anything echoed by commit
             // hooks. We ignore `stdout` since it just has protocol garbage, and
             // substitute `stderr`.
             $body = strlen($stderr) . "\n" . $stderr;
         } else {
             list($length, $body) = explode("\n", $stdout, 2);
             if ($cmd == 'capabilities') {
                 $body = DiffusionMercurialWireProtocol::filterBundle2Capability($body);
             }
         }
     }
     return id(new DiffusionMercurialResponse())->setContent($body);
 }