GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Push — master ( bf7445...6bf550 )
by Freek
01:10
created

Downloader::withSocketContextOptions()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 1
dl 0
loc 6
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace Spatie\SslCertificate;
4
5
use Spatie\SslCertificate\Exceptions\CouldNotDownloadCertificate;
6
use Spatie\SslCertificate\Exceptions\InvalidIpAddress;
7
8
class Downloader
9
{
10
    /** @var int */
11
    protected $port = 443;
12
13
    /** @var string */
14
    protected $ipAddress = null;
15
16
    /** @var bool */
17
    protected $usingIpAddress = false;
18
19
    /** @var int */
20
    protected $timeout = 30;
21
22
    /** @var bool */
23
    protected $enableSni = true;
24
25
    /** @var bool */
26
    protected $capturePeerChain = false;
27
28
    /** @var array */
29
    protected $socketContextOptions = [];
30
31
    /** @var bool */
32
    protected $verifyPeer = true;
33
34
    /** @var bool */
35
    protected $verifyPeerName = true;
36
37
    public function usingPort(int $port)
38
    {
39
        $this->port = $port;
40
41
        return $this;
42
    }
43
44
    public function usingSni(bool $sni)
45
    {
46
        $this->enableSni = $sni;
47
48
        return $this;
49
    }
50
51
    public function withSocketContextOptions(array $socketContextOptions)
52
    {
53
        $this->socketContextOptions = $socketContextOptions;
54
55
        return $this;
56
    }
57
58
    public function withFullChain(bool $fullChain)
59
    {
60
        $this->capturePeerChain = $fullChain;
61
62
        return $this;
63
    }
64
65
    public function withVerifyPeer(bool $verifyPeer)
66
    {
67
        $this->verifyPeer = $verifyPeer;
68
69
        return $this;
70
    }
71
72
    public function withVerifyPeerName(bool $verifyPeerName)
73
    {
74
        $this->verifyPeerName = $verifyPeerName;
75
76
        return $this;
77
    }
78
79
    public function setTimeout(int $timeOutInSeconds)
80
    {
81
        $this->timeout = $timeOutInSeconds;
82
83
        return $this;
84
    }
85
86
    public function fromIpAddress(string $ipAddress)
87
    {
88
        if (! filter_var($ipAddress, FILTER_VALIDATE_IP)) {
89
            throw InvalidIpAddress::couldNotValidate($ipAddress);
90
        }
91
92
        $this->ipAddress = $ipAddress;
93
        $this->usingIpAddress = true;
94
95
        return $this;
96
    }
97
98
    public function getCertificates(string $hostName): array
99
    {
100
        $response = $this->fetchCertificates($hostName);
101
        $remoteAddress = $response['remoteAddress'];
102
103
        $peerCertificate = $response['options']['ssl']['peer_certificate'];
104
105
        $peerCertificateChain = $response['options']['ssl']['peer_certificate_chain'] ?? [];
106
107
        $fullCertificateChain = array_merge([$peerCertificate], $peerCertificateChain);
108
109 View Code Duplication
        $certificates = array_map(function ($certificate) use ($remoteAddress) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
110
            $certificateFields = openssl_x509_parse($certificate);
111
112
            $fingerprint = openssl_x509_fingerprint($certificate);
113
            $fingerprintSha256 = openssl_x509_fingerprint($certificate, 'sha256');
114
115
            return new SslCertificate(
116
                $certificateFields,
117
                $fingerprint,
118
                $fingerprintSha256,
119
                $remoteAddress
120
            );
121
        }, $fullCertificateChain);
122
123
        return array_unique($certificates);
124
    }
125
126
    public function forHost(string $hostName): SslCertificate
127
    {
128
        $hostName = (new Url($hostName))->getHostName();
129
130
        $certificates = $this->getCertificates($hostName);
131
132
        return $certificates[0] ?? false;
133
    }
134
135
    public static function downloadCertificateFromUrl(string $url, int $timeout = 30): SslCertificate
136
    {
137
        return (new static())
138
            ->setTimeout($timeout)
139
            ->forHost($url);
140
    }
141
142
    protected function fetchCertificates(string $hostName): array
143
    {
144
        $hostName = (new Url($hostName))->getHostName();
145
146
        $sslOptions = [
147
            'capture_peer_cert' => true,
148
            'capture_peer_cert_chain' => $this->capturePeerChain,
149
            'SNI_enabled' => $this->enableSni,
150
            'peer_name' => $hostName,
151
            'verify_peer' => $this->verifyPeer,
152
            'verify_peer_name' => $this->verifyPeerName,
153
        ];
154
155
        $streamContext = stream_context_create([
156
            'socket' => $this->socketContextOptions,
157
            'ssl' => $sslOptions,
158
        ]);
159
160
        $connectTo = ($this->usingIpAddress)
161
            ? $this->ipAddress
162
            : $hostName;
163
164
        $client = @stream_socket_client(
165
            "ssl://{$connectTo}:{$this->port}",
166
            $errorNumber,
167
            $errorDescription,
168
            $this->timeout,
169
            STREAM_CLIENT_CONNECT,
170
            $streamContext
171
        );
172
173
        if (! empty($errorDescription)) {
174
            throw $this->buildFailureException($connectTo, $errorDescription);
175
        }
176
177
        if (! $client) {
178
            $clientErrorMessage = ($this->usingIpAddress)
179
                ? "Could not connect to `{$connectTo}` or it does not have a certificate matching `${hostName}`."
180
                : "Could not connect to `{$connectTo}`.";
181
182
            throw CouldNotDownloadCertificate::unknownError($hostName, $clientErrorMessage);
183
        }
184
185
        $response = stream_context_get_params($client);
186
187
        $response['remoteAddress'] = stream_socket_get_name($client, true);
188
189
        fclose($client);
190
191
        return $response;
192
    }
193
194
    protected function buildFailureException(string $hostName, string $errorDescription)
195
    {
196
        if (str_contains($errorDescription, 'getaddrinfo failed')) {
197
            return CouldNotDownloadCertificate::hostDoesNotExist($hostName);
198
        }
199
200
        if (str_contains($errorDescription, 'error:14090086')) {
201
            return CouldNotDownloadCertificate::noCertificateInstalled($hostName);
202
        }
203
204
        return CouldNotDownloadCertificate::unknownError($hostName, $errorDescription);
205
    }
206
}
207