Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/phabricator
Path: blob/master/src/applications/diffusion/protocol/DiffusionMercurialWireProtocol.php
12242 views
1
<?php
2
3
final class DiffusionMercurialWireProtocol extends Phobject {
4
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?".
9
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'),
29
);
30
31
if (!isset($commands[$command])) {
32
throw new Exception(
33
pht(
34
'Unknown Mercurial command "%s"!',
35
$command));
36
}
37
38
return $commands[$command];
39
}
40
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,
58
);
59
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.
63
64
return isset($read_only[$command]);
65
}
66
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'));
74
}
75
76
// For "batch" we get a "cmds" argument like:
77
//
78
// heads ;known nodes=
79
//
80
// We need to examine the commands (here, "heads" and "known") to make sure
81
// they're all read-only.
82
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):
87
//
88
// lookup key=a:xb;lookup key=z* 0
89
// lookup key=a:;b;lookup key=z* 0
90
// ^
91
// |
92
// +-- Note semicolon.
93
//
94
// So just split unconditionally.
95
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;
101
}
102
}
103
104
return true;
105
}
106
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.
112
*
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
116
*
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.
121
*
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.
125
*/
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;
132
}
133
}
134
return implode(' ', $parts);
135
}
136
137
}
138
139