1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* Trait HTTPOptionsTrait |
4
|
|
|
* |
5
|
|
|
* @filesource HTTPOptionsTrait.php |
6
|
|
|
* @created 28.08.2018 |
7
|
|
|
* @package chillerlan\HTTP |
8
|
|
|
* @author Smiley <[email protected]> |
9
|
|
|
* @copyright 2018 Smiley |
10
|
|
|
* @license MIT |
11
|
|
|
*/ |
12
|
|
|
|
13
|
|
|
namespace chillerlan\HTTP; |
14
|
|
|
|
15
|
|
|
trait HTTPOptionsTrait{ |
16
|
|
|
|
17
|
|
|
/** |
18
|
|
|
* @var string |
19
|
|
|
*/ |
20
|
|
|
protected $user_agent = 'chillerlanHttpInterface/2.0 +https://github.com/chillerlan/php-httpinterface'; |
21
|
|
|
|
22
|
|
|
/** |
23
|
|
|
* options for each curl instance |
24
|
|
|
* |
25
|
|
|
* this array is being merged into the default options as the last thing before curl_exec(). |
26
|
|
|
* none of the values (except existence of the CA file) will be checked - that's up to the implementation. |
27
|
|
|
* |
28
|
|
|
* @var array |
29
|
|
|
*/ |
30
|
|
|
protected $curl_options = []; |
31
|
|
|
|
32
|
|
|
/** |
33
|
|
|
* CA Root Certificates for use with CURL/SSL (if not configured in php.ini or available in a default path) |
34
|
|
|
* |
35
|
|
|
* @var string |
36
|
|
|
* |
37
|
|
|
* @link https://curl.haxx.se/docs/caextract.html |
38
|
|
|
* @link https://curl.haxx.se/ca/cacert.pem |
39
|
|
|
* @link https://raw.githubusercontent.com/bagder/ca-bundle/master/ca-bundle.crt |
40
|
|
|
*/ |
41
|
|
|
protected $ca_info = null; |
42
|
|
|
|
43
|
|
|
/** |
44
|
|
|
* see CURLOPT_SSL_VERIFYPEER |
45
|
|
|
* requires either HTTPOptions::$ca_info or a properly working system CA file |
46
|
|
|
* |
47
|
|
|
* @var bool |
48
|
|
|
* @link http://php.net/manual/en/function.curl-setopt.php |
49
|
|
|
*/ |
50
|
|
|
protected $ssl_verifypeer = true; |
51
|
|
|
|
52
|
|
|
/** |
53
|
|
|
* HTTPOptionsTrait constructor |
54
|
|
|
* |
55
|
|
|
* @throws \Psr\Http\Client\ClientExceptionInterface |
56
|
|
|
*/ |
57
|
|
|
protected function HTTPOptionsTrait():void{ |
58
|
|
|
|
59
|
|
|
if(!is_array($this->curl_options)){ |
60
|
|
|
$this->curl_options = []; |
61
|
|
|
} |
62
|
|
|
|
63
|
|
|
if(!is_string($this->user_agent) || empty(trim($this->user_agent))){ |
64
|
|
|
throw new ClientException('invalid user agent'); |
65
|
|
|
} |
66
|
|
|
|
67
|
|
|
$this->setCA(); |
68
|
|
|
} |
69
|
|
|
|
70
|
|
|
/** |
71
|
|
|
* @return void |
72
|
|
|
* @throws \Psr\Http\Client\ClientExceptionInterface |
73
|
|
|
*/ |
74
|
|
|
protected function setCA():void{ |
75
|
|
|
|
76
|
|
|
// disable verification if wanted so |
77
|
|
|
if($this->ssl_verifypeer !== true || (isset($this->curl_options[CURLOPT_SSL_VERIFYPEER]) && !$this->curl_options[CURLOPT_SSL_VERIFYPEER])){ |
78
|
|
|
unset($this->curl_options[CURLOPT_CAINFO], $this->curl_options[CURLOPT_CAPATH]); |
79
|
|
|
|
80
|
|
|
$this->curl_options[CURLOPT_SSL_VERIFYHOST] = 0; |
81
|
|
|
$this->curl_options[CURLOPT_SSL_VERIFYPEER] = false; |
82
|
|
|
|
83
|
|
|
return; |
84
|
|
|
} |
85
|
|
|
|
86
|
|
|
$this->curl_options[CURLOPT_SSL_VERIFYHOST] = 2; |
87
|
|
|
$this->curl_options[CURLOPT_SSL_VERIFYPEER] = true; |
88
|
|
|
|
89
|
|
|
// a path/dir/link to a CA bundle is given, let's check that |
90
|
|
|
if(is_string($this->ca_info)){ |
91
|
|
|
|
92
|
|
|
// if you - for whatever obscure reason - need to check Windows .lnk links, |
93
|
|
|
// see http://php.net/manual/en/function.is-link.php#91249 |
94
|
|
|
switch(true){ |
95
|
|
|
case is_dir($this->ca_info): |
96
|
|
View Code Duplication |
case is_link($this->ca_info) && is_dir(readlink($this->ca_info)): // @codeCoverageIgnore |
|
|
|
|
97
|
|
|
$this->curl_options[CURLOPT_CAPATH] = $this->ca_info; |
98
|
|
|
unset($this->curl_options[CURLOPT_CAINFO]); |
99
|
|
|
return; |
100
|
|
|
|
101
|
|
|
case is_file($this->ca_info): |
102
|
|
View Code Duplication |
case is_link($this->ca_info) && is_file(readlink($this->ca_info)): // @codeCoverageIgnore |
|
|
|
|
103
|
|
|
$this->curl_options[CURLOPT_CAINFO] = $this->ca_info; |
104
|
|
|
unset($this->curl_options[CURLOPT_CAPATH]); |
105
|
|
|
return; |
106
|
|
|
} |
107
|
|
|
|
108
|
|
|
throw new ClientException('invalid path to SSL CA bundle (HTTPOptions::$ca_info): '.$this->ca_info); |
109
|
|
|
} |
110
|
|
|
|
111
|
|
|
// we somehow landed here, so let's check if there's a CA bundle given via the cURL options |
112
|
|
|
$ca = $this->curl_options[CURLOPT_CAPATH] ?? $this->curl_options[CURLOPT_CAINFO] ?? false; |
113
|
|
|
|
114
|
|
|
if($ca){ |
115
|
|
|
|
116
|
|
|
// just check if the file/path exists |
117
|
|
|
switch(true){ |
118
|
|
|
case is_dir($ca): |
119
|
|
|
case is_link($ca) && is_dir(readlink($ca)): // @codeCoverageIgnore |
120
|
|
|
unset($this->curl_options[CURLOPT_CAINFO]); |
121
|
|
|
return; |
122
|
|
|
|
123
|
|
|
case is_file($ca): |
124
|
|
|
case is_link($ca) && is_file(readlink($ca)): // @codeCoverageIgnore |
125
|
|
|
return; |
126
|
|
|
} |
127
|
|
|
|
128
|
|
|
throw new ClientException('invalid path to SSL CA bundle (CURLOPT_CAPATH/CURLOPT_CAINFO): '.$ca); |
129
|
|
|
} |
130
|
|
|
|
131
|
|
|
// check php.ini options - PHP should find the file by itself |
132
|
|
|
if(file_exists(ini_get('curl.cainfo'))){ |
133
|
|
|
return; // @codeCoverageIgnore |
134
|
|
|
} |
135
|
|
|
|
136
|
|
|
// this is getting weird. as a last resort, we're going to check some default paths for a CA bundle file |
137
|
|
|
$cafiles = [ |
138
|
|
|
// check other php.ini settings |
139
|
|
|
ini_get('openssl.cafile'), |
140
|
|
|
// Red Hat, CentOS, Fedora (provided by the ca-certificates package) |
141
|
|
|
'/etc/pki/tls/certs/ca-bundle.crt', |
142
|
|
|
// Ubuntu, Debian (provided by the ca-certificates package) |
143
|
|
|
'/etc/ssl/certs/ca-certificates.crt', |
144
|
|
|
// FreeBSD (provided by the ca_root_nss package) |
145
|
|
|
'/usr/local/share/certs/ca-root-nss.crt', |
146
|
|
|
// SLES 12 (provided by the ca-certificates package) |
147
|
|
|
'/var/lib/ca-certificates/ca-bundle.pem', |
148
|
|
|
// OS X provided by homebrew (using the default path) |
149
|
|
|
'/usr/local/etc/openssl/cert.pem', |
150
|
|
|
// Google app engine |
151
|
|
|
'/etc/ca-certificates.crt', |
152
|
|
|
// Windows? |
153
|
|
|
// http://php.net/manual/en/function.curl-setopt.php#110457 |
154
|
|
|
'C:\\Windows\\system32\\curl-ca-bundle.crt', |
155
|
|
|
'C:\\Windows\\curl-ca-bundle.crt', |
156
|
|
|
'C:\\Windows\\system32\\cacert.pem', |
157
|
|
|
'C:\\Windows\\cacert.pem', |
158
|
|
|
// working path |
159
|
|
|
__DIR__.'/cacert.pem', |
160
|
|
|
]; |
161
|
|
|
|
162
|
|
|
foreach($cafiles as $file){ |
163
|
|
|
if(is_file($file) || (is_link($file) && is_file(readlink($file)))){ |
164
|
|
|
$this->curl_options[CURLOPT_CAINFO] = $file; |
165
|
|
|
return; |
166
|
|
|
} |
167
|
|
|
} |
168
|
|
|
|
169
|
|
|
$msg = 'No system CA bundle could be found in any of the the common system locations. ' |
170
|
|
|
.'In order to verify peer certificates, you will need to supply the path on disk to a certificate bundle via ' |
171
|
|
|
.'HTTPOptions::$ca_info or HTTPOptions::$curl_options. If you do not need a specific certificate bundle, ' |
172
|
|
|
.'then you can download a CA bundle over here: https://curl.haxx.se/docs/caextract.html. ' |
173
|
|
|
.'Once you have a CA bundle available on disk, you can set the "curl.cainfo" php.ini setting to point ' |
174
|
|
|
.'to the path to the file, allowing you to omit the $ca_info or $curl_options setting. ' |
175
|
|
|
.'See http://curl.haxx.se/docs/sslcerts.html for more information.'; |
176
|
|
|
|
177
|
|
|
throw new ClientException($msg); // @codeCoverageIgnore |
178
|
|
|
} |
179
|
|
|
|
180
|
|
|
} |
181
|
|
|
|
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.