Correct a parameter order swap in "diffusion.historyquery" for Mercurial
[phabricator.git] / src / applications / diffusion / protocol / DiffusionMercurialWireProtocol.php
blob8a9deb5d84212cac4b0ffdc802a54cc5c62e7fe0
1 <?php
3 final class DiffusionMercurialWireProtocol extends Phobject {
5 public static function getCommandArgs($command) {
6 // We need to enumerate all of the Mercurial wire commands because the
7 // argument encoding varies based on the command. "Why?", you might ask,
8 // "Why would you do this?".
10 $commands = array(
11 'batch' => array('cmds', '*'),
12 'between' => array('pairs'),
13 'branchmap' => array(),
14 'branches' => array('nodes'),
15 'capabilities' => array(),
16 'changegroup' => array('roots'),
17 'changegroupsubset' => array('bases heads'),
18 'debugwireargs' => array('one two *'),
19 'getbundle' => array('*'),
20 'heads' => array(),
21 'hello' => array(),
22 'known' => array('nodes', '*'),
23 'listkeys' => array('namespace'),
24 'lookup' => array('key'),
25 'pushkey' => array('namespace', 'key', 'old', 'new'),
26 'protocaps' => array('caps'),
27 'stream_out' => array(''),
28 'unbundle' => array('heads'),
31 if (!isset($commands[$command])) {
32 throw new Exception(
33 pht(
34 'Unknown Mercurial command "%s"!',
35 $command));
38 return $commands[$command];
41 public static function isReadOnlyCommand($command) {
42 $read_only = array(
43 'between' => true,
44 'branchmap' => true,
45 'branches' => true,
46 'capabilities' => true,
47 'changegroup' => true,
48 'changegroupsubset' => true,
49 'debugwireargs' => true,
50 'getbundle' => true,
51 'heads' => true,
52 'hello' => true,
53 'known' => true,
54 'listkeys' => true,
55 'lookup' => true,
56 'protocaps' => true,
57 'stream_out' => true,
60 // Notably, the write commands are "pushkey" and "unbundle". The
61 // "batch" command is theoretically read only, but we require explicit
62 // analysis of the actual commands.
64 return isset($read_only[$command]);
67 public static function isReadOnlyBatchCommand($cmds) {
68 if (!strlen($cmds)) {
69 // We expect a "batch" command to always have a "cmds" string, so err
70 // on the side of caution and throw if we don't get any data here. This
71 // either indicates a mangled command from the client or a programming
72 // error in our code.
73 throw new Exception(pht("Expected nonempty '%s' specification!", 'cmds'));
76 // For "batch" we get a "cmds" argument like:
78 // heads ;known nodes=
80 // We need to examine the commands (here, "heads" and "known") to make sure
81 // they're all read-only.
83 // NOTE: Mercurial has some code to escape semicolons, but it does not
84 // actually function for command separation. For example, these two batch
85 // commands will produce completely different results (the former will run
86 // the lookup; the latter will fail with a parser error):
88 // lookup key=a:xb;lookup key=z* 0
89 // lookup key=a:;b;lookup key=z* 0
90 // ^
91 // |
92 // +-- Note semicolon.
94 // So just split unconditionally.
96 $cmds = explode(';', $cmds);
97 foreach ($cmds as $sub_cmd) {
98 $name = head(explode(' ', $sub_cmd, 2));
99 if (!self::isReadOnlyCommand($name)) {
100 return false;
104 return true;
107 /** If the server version is running 3.4+ it will respond
108 * with 'bundle2' capability in the format of "bundle2=(url-encoding)".
109 * Until we manage to properly package up bundles to send back we
110 * disallow the client from knowing we speak bundle2 by removing it
111 * from the capabilities listing.
113 * The format of the capabilities string is: "a space separated list
114 * of strings representing what commands the server supports"
115 * @link https://www.mercurial-scm.org/wiki/CommandServer#Protocol
117 * @param string $capabilities - The string of capabilities to
118 * strip the bundle2 capability from. This is expected to be
119 * the space-separated list of strings resulting from the
120 * querying the 'capabilities' command.
122 * @return string The resulting space-separated list of capabilities
123 * which no longer contains the 'bundle2' capability. This is meant
124 * to replace the original $body to send back to client.
126 public static function filterBundle2Capability($capabilities) {
127 $parts = explode(' ', $capabilities);
128 foreach ($parts as $key => $part) {
129 if (preg_match('/^bundle2=/', $part)) {
130 unset($parts[$key]);
131 break;
134 return implode(' ', $parts);