1
|
|
|
<?php |
2
|
|
|
/* |
3
|
|
|
* hirak/prestissimo |
4
|
|
|
* @author Hiraku NAKANO |
5
|
|
|
* @license MIT https://github.com/hirak/prestissimo |
6
|
|
|
*/ |
7
|
|
|
namespace Hirak\Prestissimo; |
8
|
|
|
|
9
|
|
|
use Composer\Util; |
10
|
|
|
use Composer\IO; |
11
|
|
|
|
12
|
|
|
class BaseRequest |
13
|
|
|
{ |
14
|
|
|
private $scheme; |
15
|
|
|
private $user; |
16
|
|
|
private $pass; |
17
|
|
|
private $host; |
18
|
|
|
private $port; |
19
|
|
|
private $path; |
20
|
|
|
private $query = array(); |
21
|
|
|
|
22
|
|
|
/** @var [string => string] */ |
23
|
|
|
private $headers = array(); |
24
|
|
|
|
25
|
|
|
private $capath; |
26
|
|
|
private $cafile; |
27
|
|
|
|
28
|
|
|
protected static $defaultCurlOptions = array(); |
29
|
|
|
|
30
|
|
|
private static $NSS_CIPHERS = array( |
31
|
|
|
'rsa_3des_sha', |
32
|
|
|
'rsa_des_sha', |
33
|
|
|
'rsa_null_md5', |
34
|
|
|
'rsa_null_sha', |
35
|
|
|
'rsa_rc2_40_md5', |
36
|
|
|
'rsa_rc4_128_md5', |
37
|
|
|
'rsa_rc4_128_sha', |
38
|
|
|
'rsa_rc4_40_md5', |
39
|
|
|
'fips_des_sha', |
40
|
|
|
'fips_3des_sha', |
41
|
|
|
'rsa_des_56_sha', |
42
|
|
|
'rsa_rc4_56_sha', |
43
|
|
|
'rsa_aes_128_sha', |
44
|
|
|
'rsa_aes_256_sha', |
45
|
|
|
'rsa_aes_128_gcm_sha_256', |
46
|
|
|
'dhe_rsa_aes_128_gcm_sha_256', |
47
|
|
|
'ecdh_ecdsa_null_sha', |
48
|
|
|
'ecdh_ecdsa_rc4_128_sha', |
49
|
|
|
'ecdh_ecdsa_3des_sha', |
50
|
|
|
'ecdh_ecdsa_aes_128_sha', |
51
|
|
|
'ecdh_ecdsa_aes_256_sha', |
52
|
|
|
'ecdhe_ecdsa_null_sha', |
53
|
|
|
'ecdhe_ecdsa_rc4_128_sha', |
54
|
|
|
'ecdhe_ecdsa_3des_sha', |
55
|
|
|
'ecdhe_ecdsa_aes_128_sha', |
56
|
|
|
'ecdhe_ecdsa_aes_256_sha', |
57
|
|
|
'ecdh_rsa_null_sha', |
58
|
|
|
'ecdh_rsa_128_sha', |
59
|
|
|
'ecdh_rsa_3des_sha', |
60
|
|
|
'ecdh_rsa_aes_128_sha', |
61
|
|
|
'ecdh_rsa_aes_256_sha', |
62
|
|
|
'ecdhe_rsa_rc4_128_sha', |
63
|
|
|
'ecdhe_rsa_3des_sha', |
64
|
|
|
'ecdhe_rsa_aes_128_sha', |
65
|
|
|
'ecdhe_rsa_aes_256_sha', |
66
|
|
|
'ecdhe_ecdsa_aes_128_gcm_sha_256', |
67
|
|
|
'ecdhe_rsa_aes_128_gcm_sha_256', |
68
|
|
|
); |
69
|
|
|
|
70
|
|
|
/** |
71
|
|
|
* enable ECC cipher suites in cURL/NSS |
72
|
|
|
* @codeCoverageIgnore |
73
|
|
|
*/ |
74
|
|
|
public static function nssCiphers() |
75
|
|
|
{ |
76
|
|
|
static $cache; |
77
|
|
|
if (isset($cache)) { |
78
|
|
|
return $cache; |
79
|
|
|
} |
80
|
|
|
$ver = curl_version(); |
81
|
|
|
if (preg_match('/^NSS.*Basic ECC$/', $ver['ssl_version'])) { |
82
|
|
|
return $cache = implode(',', self::$NSS_CIPHERS); |
83
|
|
|
} |
84
|
|
|
return $cache = false; |
85
|
|
|
} |
86
|
|
|
|
87
|
4 |
|
protected function getProxy($url) |
88
|
|
|
{ |
89
|
4 |
|
if (isset($_SERVER['no_proxy'])) { |
90
|
1 |
|
$pattern = new Util\NoProxyPattern($_SERVER['no_proxy']); |
91
|
1 |
|
if ($pattern->test($url)) { |
92
|
1 |
|
return null; |
93
|
|
|
} |
94
|
1 |
|
} |
95
|
|
|
|
96
|
|
|
// @see https://httpoxy.org/ |
97
|
4 |
|
if (!defined('PHP_SAPI') || PHP_SAPI !== 'cli') { |
98
|
|
|
return null; |
99
|
|
|
} |
100
|
|
|
|
101
|
4 |
|
foreach (array('https', 'http') as $scheme) { |
102
|
4 |
|
if ($this->scheme === $scheme) { |
103
|
4 |
|
$label = $scheme . '_proxy'; |
104
|
4 |
|
foreach (array(strtoupper($label), $label) as $l) { |
105
|
4 |
|
if (isset($_SERVER[$l])) { |
106
|
1 |
|
return $_SERVER[$l]; |
107
|
|
|
} |
108
|
3 |
|
} |
109
|
3 |
|
} |
110
|
4 |
|
} |
111
|
3 |
|
return null; |
112
|
|
|
} |
113
|
|
|
|
114
|
|
|
/** |
115
|
|
|
* @param $io |
116
|
|
|
* @param bool $useRedirector |
117
|
|
|
* @param $githubDomains |
118
|
|
|
* @param $gitlabDomains |
119
|
|
|
*/ |
120
|
6 |
|
protected function setupAuthentication(IO\IOInterface $io, $useRedirector, array $githubDomains, array $gitlabDomains) |
121
|
|
|
{ |
122
|
6 |
|
if (preg_match('/\.github\.com$/', $this->host)) { |
123
|
1 |
|
$authKey = 'github.com'; |
124
|
1 |
|
if ($useRedirector) { |
125
|
1 |
|
if ($this->host === 'api.github.com' && preg_match('%^/repos(/[^/]+/[^/]+/)zipball(.+)$%', $this->path, $_)) { |
126
|
1 |
|
$this->host = 'codeload.github.com'; |
127
|
1 |
|
$this->path = $_[1] . 'legacy.zip' . $_[2]; |
128
|
1 |
|
} |
129
|
1 |
|
} |
130
|
1 |
|
} else { |
131
|
5 |
|
$authKey = $this->host; |
132
|
|
|
} |
133
|
6 |
|
if (!$io->hasAuthentication($authKey)) { |
134
|
4 |
|
if ($this->user || $this->pass) { |
135
|
1 |
|
$io->setAuthentication($authKey, $this->user, $this->pass); |
136
|
1 |
|
} else { |
137
|
3 |
|
return; |
138
|
|
|
} |
139
|
1 |
|
} |
140
|
|
|
|
141
|
3 |
|
$auth = $io->getAuthentication($authKey); |
142
|
|
|
|
143
|
|
|
// is github |
144
|
3 |
|
if (in_array($authKey, $githubDomains) && 'x-oauth-basic' === $auth['password']) { |
145
|
1 |
|
$this->addParam('access_token', $auth['username']); |
146
|
1 |
|
$this->user = $this->pass = null; |
147
|
1 |
|
return; |
148
|
|
|
} |
149
|
|
|
// is gitlab |
150
|
2 |
|
if (in_array($authKey, $gitlabDomains)) { |
151
|
1 |
View Code Duplication |
if ('oauth2' === $auth['password']) { |
|
|
|
|
152
|
1 |
|
$this->addHeader('authorization', 'Bearer ' . $auth['username']); |
153
|
1 |
|
$this->user = $this->pass = null; |
154
|
1 |
|
return; |
155
|
|
|
} |
156
|
|
View Code Duplication |
if ('private-token' === $auth['password']) { |
|
|
|
|
157
|
|
|
$this->addHeader('PRIVATE-TOKEN', $auth['username']); |
158
|
|
|
$this->user = $this->pass = null; |
159
|
|
|
return; |
160
|
|
|
} |
161
|
|
|
} |
162
|
|
|
|
163
|
|
|
// others, includes bitbucket |
164
|
1 |
|
$this->user = $auth['username']; |
165
|
1 |
|
$this->pass = $auth['password']; |
166
|
1 |
|
} |
167
|
|
|
|
168
|
|
|
/** |
169
|
|
|
* @return array |
170
|
|
|
*/ |
171
|
4 |
|
public function getCurlOptions() |
172
|
|
|
{ |
173
|
4 |
|
$headers = array(); |
174
|
4 |
|
foreach ($this->headers as $key => $val) { |
175
|
1 |
|
$headers[] = strtr(ucwords(strtr($key, '-', ' ')), ' ', '-') . ': ' . $val; |
176
|
4 |
|
} |
177
|
|
|
|
178
|
4 |
|
$url = $this->getURL(); |
179
|
|
|
|
180
|
|
|
$curlOpts = array( |
181
|
4 |
|
CURLOPT_URL => $url, |
182
|
4 |
|
CURLOPT_HTTPHEADER => $headers, |
183
|
4 |
|
CURLOPT_USERAGENT => ConfigFacade::getUserAgent(), |
184
|
|
|
//CURLOPT_VERBOSE => true, //for debug |
185
|
4 |
|
); |
186
|
4 |
|
$curlOpts += static::$defaultCurlOptions; |
187
|
|
|
|
188
|
|
|
// @codeCoverageIgnoreStart |
189
|
|
|
if ($ciphers = $this->nssCiphers()) { |
190
|
|
|
$curlOpts[CURLOPT_SSL_CIPHER_LIST] = $ciphers; |
191
|
|
|
} |
192
|
|
|
// @codeCoverageIgnoreEnd |
193
|
4 |
|
if ($proxy = $this->getProxy($url)) { |
194
|
1 |
|
$curlOpts[CURLOPT_PROXY] = $proxy; |
195
|
1 |
|
} |
196
|
4 |
|
if ($this->capath) { |
197
|
1 |
|
$curlOpts[CURLOPT_CAPATH] = $this->capath; |
198
|
1 |
|
} |
199
|
4 |
|
if ($this->cafile) { |
200
|
1 |
|
$curlOpts[CURLOPT_CAINFO] = $this->cafile; |
201
|
1 |
|
} |
202
|
|
|
|
203
|
4 |
|
return $curlOpts; |
204
|
|
|
} |
205
|
|
|
|
206
|
|
|
/** |
207
|
|
|
* @return string |
208
|
|
|
*/ |
209
|
7 |
|
public function getURL() |
210
|
|
|
{ |
211
|
7 |
|
$url = self::ifOr($this->scheme, '', '://'); |
212
|
7 |
|
if ($this->user) { |
213
|
1 |
|
$user = $this->user; |
214
|
1 |
|
$user .= self::ifOr($this->pass, ':'); |
215
|
1 |
|
$url .= $user . '@'; |
216
|
1 |
|
} |
217
|
7 |
|
$url .= self::ifOr($this->host); |
218
|
7 |
|
$url .= self::ifOr($this->port, ':'); |
219
|
7 |
|
$url .= self::ifOr($this->path); |
220
|
7 |
|
$url .= self::ifOr(http_build_query($this->query), '?'); |
221
|
7 |
|
return $url; |
222
|
|
|
} |
223
|
|
|
|
224
|
|
|
/** |
225
|
|
|
* @return string user/pass/access_token masked url |
226
|
|
|
*/ |
227
|
1 |
View Code Duplication |
public function getMaskedURL() |
|
|
|
|
228
|
|
|
{ |
229
|
1 |
|
$url = self::ifOr($this->scheme, '', '://'); |
230
|
1 |
|
$url .= self::ifOr($this->host); |
231
|
1 |
|
$url .= self::ifOr($this->port, ':'); |
232
|
1 |
|
$url .= self::ifOr($this->path); |
233
|
1 |
|
return $url; |
234
|
|
|
} |
235
|
|
|
|
236
|
|
|
/** |
237
|
|
|
* @return string |
238
|
|
|
*/ |
239
|
2 |
View Code Duplication |
public function getOriginURL() |
|
|
|
|
240
|
|
|
{ |
241
|
2 |
|
$url = self::ifOr($this->scheme, '', '://'); |
242
|
2 |
|
$url .= self::ifOr($this->host); |
243
|
2 |
|
$url .= self::ifOr($this->port, ':'); |
244
|
2 |
|
return $url; |
245
|
|
|
} |
246
|
|
|
|
247
|
9 |
|
private static function ifOr($str, $pre = '', $post = '') |
248
|
|
|
{ |
249
|
9 |
|
if ($str) { |
250
|
9 |
|
return $pre . $str . $post; |
251
|
|
|
} |
252
|
7 |
|
return ''; |
253
|
|
|
} |
254
|
|
|
|
255
|
|
|
/** |
256
|
|
|
* @param string $url |
257
|
|
|
*/ |
258
|
11 |
|
public function setURL($url) |
259
|
|
|
{ |
260
|
11 |
|
$struct = parse_url($url); |
261
|
11 |
|
foreach ($struct as $key => $val) { |
|
|
|
|
262
|
11 |
|
if ($key === 'query') { |
263
|
4 |
|
parse_str($val, $this->query); |
264
|
4 |
|
} else { |
265
|
11 |
|
$this->$key = $val; |
266
|
|
|
} |
267
|
11 |
|
} |
268
|
11 |
|
} |
269
|
|
|
|
270
|
1 |
|
public function addParam($key, $val) |
271
|
|
|
{ |
272
|
1 |
|
$this->query[$key] = $val; |
273
|
1 |
|
} |
274
|
|
|
|
275
|
1 |
|
public function addHeader($key, $val) |
276
|
|
|
{ |
277
|
1 |
|
$this->headers[strtolower($key)] = $val; |
278
|
1 |
|
} |
279
|
|
|
|
280
|
7 |
|
public function setCA($path = null, $file = null) |
281
|
|
|
{ |
282
|
7 |
|
$this->capath = $path; |
283
|
7 |
|
$this->cafile = $file; |
284
|
7 |
|
} |
285
|
|
|
|
286
|
1 |
|
public function isHTTP() |
287
|
|
|
{ |
288
|
1 |
|
return $this->scheme === 'http' || $this->scheme === 'https'; |
289
|
|
|
} |
290
|
|
|
} |
291
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.