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 ( f797ce...cdadcc )
by Alexander
02:28
created

Scanner::discover()   C

Complexity

Conditions 7
Paths 21

Size

Total Lines 29
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 1
Metric Value
c 2
b 0
f 1
dl 0
loc 29
rs 6.7272
cc 7
eloc 18
nc 21
nop 0
1
<?php
2
3
namespace CyberLine\phUPnP;
4
5
/**
6
 * Class Scanner
7
 *
8
 * @package CyberLine\UPnP
9
 * @author Alexander Over <[email protected]>
10
 */
11
class Scanner implements \JsonSerializable
12
{
13
    /** @var string */
14
    static protected $host = '239.255.255.250';
15
16
    /** @var int */
17
    static protected $port = 1900;
18
19
    /**
20
     * Maximum wait time in seconds. Should be between 1 and 120 inclusive.
21
     *
22
     * @var int
23
     */
24
    protected $delayResponse = 1;
25
26
    /** @var int */
27
    protected $timeout = 5;
28
29
    /** @var string */
30
    protected $userAgent = 'iOS/5.0 UDAP/2.0 iPhone/4';
31
32
    /** @var array */
33
    protected $searchTypes = [
34
        'ssdp:all',
35
        'upnp:rootdevice',
36
    ];
37
38
    /** @var string */
39
    protected $searchType = 'ssdp:all';
40
41
    /** @var array */
42
    private $devices = [];
43
44
    /**
45
     * @param integer $delayResponse
46
     * @return $this
47
     */
48
    public function setDelayResponse($delayResponse)
49
    {
50
        if ((int)$delayResponse >= 1 && (int)$delayResponse <= 120) {
51
            $this->delayResponse = (int)$delayResponse;
52
53
            return $this;
54
        }
55
56
        throw new \OutOfRangeException(
57
            sprintf(
58
                '%d is not a valid delay. Valid delay is between 1 and 120 (seconds)',
59
                $delayResponse
60
            )
61
        );
62
    }
63
64
    /**
65
     * @param int $timeout
66
     * @return Scanner
67
     */
68
    public function setTimeout($timeout)
69
    {
70
        if ((int)$timeout <= (int)$this->delayResponse) {
71
            $this->timeout = (int)$timeout;
72
73
            return $this;
74
        }
75
76
        throw new \OutOfBoundsException(
77
            sprintf(
78
                'Timeout of %d is smaller then delay of %d',
79
                $timeout,
80
                $this->delayResponse
81
            )
82
        );
83
    }
84
85
    /**
86
     * @param string $userAgent
87
     * @return Scanner
88
     */
89
    public function setUserAgent($userAgent)
90
    {
91
        $this->userAgent = $userAgent;
92
93
        return $this;
94
    }
95
96
    /**
97
     * @param string $searchType
98
     * @return $this
99
     */
100
    public function setSearchType($searchType)
101
    {
102
        if (in_array($searchType, $this->searchTypes)) {
103
            $this->searchType = $searchType;
104
105
            return $this;
106
        }
107
108
        throw new \InvalidArgumentException(
109
            sprintf(
110
                '%s is not a valid searchtype. Valid searchtypes are: %s',
111
                $searchType,
112
                implode(', ', $this->searchTypes)
113
            )
114
        );
115
    }
116
117
    /**
118
     * Main scan function
119
     *
120
     * @return array
121
     */
122
    public function discover()
123
    {
124
        $devices = $this->doMSearchRequest();
125
126
        if (empty($devices)) {
127
            return [];
128
        }
129
130
        $targets = [];
131
        foreach ($devices as $key => $device) {
132
            $devices[$key] = $this->parseMSearchResponse($device);
133
            array_push($targets, $devices[$key]['location']);
134
        }
135
136
        foreach ($this->fetchUpnpXmlDeviceInfo($targets) as $location => $xml) {
137
            if (!empty($xml)) {
138
                try {
139
                    $simpleXML = new \SimpleXMLElement($xml);
140
                    if (!property_exists($simpleXML, 'URLBase')) {
141
                        $location = parse_url($location);
142
                        $simpleXML->URLBase = sprintf('%s://%s:%d/', $location['scheme'], $location['host'], $location['port']);
143
                    }
144
                    array_push($this->devices, $simpleXML);
145
                } catch (\Exception $e) { /* SimpleXML parsing failed */ }
146
            }
147
        }
148
149
        return $this->devices;
150
    }
151
152
    /**
153
     * Fetch all available UPnP devices via unicast
154
     *
155
     * @return array
156
     */
157
    protected function doMSearchRequest()
158
    {
159
        $request = $this->getMSearchRequest();
160
161
        $socket = socket_create(AF_INET, SOCK_DGRAM, 0);
162
        @socket_set_option($socket, 1, 6, true);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
163
        socket_sendto($socket, $request, strlen($request), 0, static::$host, static::$port);
164
        socket_set_option($socket, SOL_SOCKET, SO_RCVTIMEO, [
165
            'sec' => $this->timeout,
166
            'usec' => '0'
167
        ]);
168
169
        $response = [];
170
        $from = null;
171
        $port = null;
172
        do {
173
            $buffer = null;
174
            @socket_recvfrom($socket, $buffer, 1024, MSG_WAITALL, $from, $port);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
175
            if (!is_null($buffer)) {
176
                array_push($response, $buffer);
177
            }
178
        } while (!is_null($buffer));
179
180
        socket_close($socket);
181
182
        return $response;
183
    }
184
185
    /**
186
     * Prepare Msearch request string
187
     *
188
     * @return string
189
     */
190
    protected function getMSearchRequest()
191
    {
192
        $ssdpMessage = [
193
            'M-SEARCH * HTTP/1.1',
194
            sprintf('HOST: %s:%d', static::$host, static::$port),
195
            'MAN: "ssdp:discover"',
196
            sprintf('MX: %d', $this->delayResponse),
197
            sprintf('ST: %s', $this->searchType),
198
            sprintf('USER-AGENT: %s', $this->userAgent),
199
        ];
200
201
        return implode("\r\n", $ssdpMessage) . "\r\n";
202
    }
203
204
    /**
205
     * Parse response from device to a more readable format
206
     *
207
     * @param $response
208
     * @return array
209
     */
210
    protected function parseMSearchResponse($response)
211
    {
212
        $mapping = [
213
            'http' => 'http',
214
            'cach' => 'cache-control',
215
            'date' => 'date',
216
            'ext' => 'ext',
217
            'loca' => 'location',
218
            'serv' => 'server',
219
            'st:' => 'st',
220
            'usn:' => 'usn',
221
            'cont' => 'content-length',
222
        ];
223
224
        $parsedResponse = [];
225
        foreach (explode("\r\n", $response) as $resultLine) {
226
            foreach ($mapping as $key => $replace) {
227
                if (stripos($resultLine, $key) === 0) {
228
                    $parsedResponse[$replace] = str_ireplace($replace . ': ', '', $resultLine);
229
                }
230
            }
231
        }
232
233
        return $parsedResponse;
234
    }
235
236
    /**
237
     * Fetch XML's from all devices async
238
     *
239
     * @param array $targets
240
     * @return array
241
     */
242
    protected function fetchUpnpXmlDeviceInfo(array $targets)
243
    {
244
        $targets = array_values(array_unique($targets));
245
        $multi = curl_multi_init();
246
247
        $curl = $xmls = [];
248
        foreach ($targets as $key => $target) {
249
            $curl[$key] = curl_init();
250
            curl_setopt($curl[$key], CURLOPT_URL, $target);
251
            curl_setopt($curl[$key], CURLOPT_TIMEOUT, $this->timeout);
252
            curl_setopt($curl[$key], CURLOPT_RETURNTRANSFER, true);
253
            curl_multi_add_handle($multi, $curl[$key]);
254
        }
255
256
        $active = null;
257
        do {
258
            $mrc = curl_multi_exec($multi, $active);
259
        } while ($mrc == CURLM_CALL_MULTI_PERFORM);
260
261
        while ($active && $mrc == CURLM_OK) {
262
            if (curl_multi_select($multi) != -1) {
263
                do {
264
                    $mrc = curl_multi_exec($multi, $active);
265
                } while ($mrc == CURLM_CALL_MULTI_PERFORM);
266
            }
267
        }
268
269
        foreach ($curl as $key => $handle) {
270
            $xmls[$targets[$key]] = curl_multi_getcontent($handle);
271
            curl_multi_remove_handle($multi, $handle);
272
        }
273
274
        curl_multi_close($multi);
275
276
        return $xmls;
277
    }
278
279
    /**
280
     * @return array
281
     */
282
    public function jsonSerialize()
283
    {
284
        if (empty($this->devices)) {
285
            $this->discover();
286
        }
287
288
        return [
289
            'total' => count($this->devices),
290
            'devices' => $this->devices,
291
        ];
292
    }
293
}
294