This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | namespace PhpEws; |
||
3 | |||
4 | /** |
||
5 | * Exchange Web Services Autodiscovery implementation |
||
6 | * |
||
7 | * This class supports POX (Plain Old XML), which is deprecated but functional |
||
8 | * in Exchange 2010. It may make sense for you to combine your Autodiscovery |
||
9 | * efforts with a SOAP Autodiscover request as well. |
||
10 | * |
||
11 | * USAGE: |
||
12 | * |
||
13 | * (after any auto-loading class incantation) |
||
14 | * |
||
15 | * $ews = Autodiscovery::getConnection($email, $password); |
||
16 | * |
||
17 | * -- OR -- |
||
18 | * |
||
19 | * If there are issues with your cURL installation that require you to specify |
||
20 | * a path to a valid Certificate Authority, you can configure that manually. |
||
21 | * |
||
22 | * $auto = new self($email, $password); |
||
23 | * $auto->setCAInfo('/path/to/your/cacert.pem'); |
||
24 | * $ews = $auto->createNewConnection(); |
||
25 | * |
||
26 | * @link http://technet.microsoft.com/en-us/library/bb332063(EXCHG.80).aspx |
||
27 | * @link https://www.testexchangeconnectivity.com/ |
||
28 | */ |
||
29 | class AutodiscoveryManager |
||
30 | { |
||
31 | /** |
||
32 | * The path appended to the various schemes and hostnames used during |
||
33 | * autodiscovery. |
||
34 | * |
||
35 | * @var string |
||
36 | */ |
||
37 | const AUTODISCOVER_PATH = '/autodiscover/autodiscover.xml'; |
||
38 | |||
39 | /** |
||
40 | * Server was discovered using the TLD method. |
||
41 | * |
||
42 | * @var integer |
||
43 | */ |
||
44 | const AUTODISCOVERED_VIA_TLD = 10; |
||
45 | |||
46 | /** |
||
47 | * Server was discovered using the subdomain method. |
||
48 | * |
||
49 | * @var integer |
||
50 | */ |
||
51 | const AUTODISCOVERED_VIA_SUBDOMAIN = 11; |
||
52 | |||
53 | /** |
||
54 | * Server was discovered using the unauthenticated GET method. |
||
55 | * |
||
56 | * @var integer |
||
57 | */ |
||
58 | const AUTODISCOVERED_VIA_UNAUTHENTICATED_GET = 12; |
||
59 | |||
60 | /** |
||
61 | * Server was discovered using the DNS SRV redirect method. |
||
62 | * |
||
63 | * @var integer |
||
64 | */ |
||
65 | const AUTODISCOVERED_VIA_SRV_RECORD = 13; |
||
66 | |||
67 | /** |
||
68 | * Server was discovered using the HTTP redirect method. |
||
69 | * |
||
70 | * @var integer |
||
71 | * |
||
72 | * @todo We do not currently support this. |
||
73 | */ |
||
74 | const AUTODISCOVERED_VIA_RESPONSE_REDIRECT = 14; |
||
75 | |||
76 | /** |
||
77 | * The email address to attempt autodiscovery against. |
||
78 | * |
||
79 | * @var string |
||
80 | */ |
||
81 | protected $email; |
||
82 | |||
83 | /** |
||
84 | * The password to present during autodiscovery. |
||
85 | * |
||
86 | * @var string |
||
87 | */ |
||
88 | protected $password; |
||
89 | |||
90 | /** |
||
91 | * The Exchange username to use during authentication. If unspecified, |
||
92 | * the provided email address will be used as the username. |
||
93 | * |
||
94 | * @var string |
||
95 | */ |
||
96 | protected $username; |
||
97 | |||
98 | /** |
||
99 | * The top-level domain name, extracted from the provided email address. |
||
100 | * |
||
101 | * @var string |
||
102 | */ |
||
103 | protected $tld; |
||
104 | |||
105 | /** |
||
106 | * The Autodiscover XML request. Since it's used repeatedly, it's cached |
||
107 | * in this property to avoid redundant re-generation. |
||
108 | * |
||
109 | * @var string |
||
110 | */ |
||
111 | protected $requestxml; |
||
112 | |||
113 | /** |
||
114 | * The Certificate Authority path. Should point to a directory containing |
||
115 | * one or more certificates to use in SSL verification. |
||
116 | * |
||
117 | * @var string |
||
118 | */ |
||
119 | protected $capath; |
||
120 | |||
121 | /** |
||
122 | * The path to a specific Certificate Authority file. Get one and use it |
||
123 | * for full Autodiscovery compliance. |
||
124 | * |
||
125 | * @var string |
||
126 | * |
||
127 | * @link http://curl.haxx.se/ca/cacert.pem |
||
128 | * @link http://curl.haxx.se/ca/ |
||
129 | */ |
||
130 | protected $cainfo; |
||
131 | |||
132 | /** |
||
133 | * Skip SSL verification. Bad idea, and violates the strict Autodiscover |
||
134 | * protocol. But, here in case you have no other option. |
||
135 | * Defaults to FALSE. |
||
136 | * |
||
137 | * @var boolean |
||
138 | */ |
||
139 | protected $skip_ssl_verification = false; |
||
140 | |||
141 | /** |
||
142 | * An associative array of response headers that resulted from the |
||
143 | * last request. Keys are lowercased for easy checking. |
||
144 | * |
||
145 | * @var array |
||
146 | */ |
||
147 | public $last_response_headers; |
||
148 | |||
149 | /** |
||
150 | * The output of curl_info() relating to the most recent cURL request. |
||
151 | * |
||
152 | * @var array |
||
153 | */ |
||
154 | public $last_info; |
||
155 | |||
156 | /** |
||
157 | * The cURL error code associated with the most recent cURL request. |
||
158 | * |
||
159 | * @var integer |
||
160 | */ |
||
161 | public $last_curl_errno; |
||
162 | |||
163 | /** |
||
164 | * Human-readable description of the most recent cURL error. |
||
165 | * |
||
166 | * @var string |
||
167 | */ |
||
168 | public $last_curl_error; |
||
169 | |||
170 | /** |
||
171 | * The value in seconds to use for Autodiscover host connection timeouts. |
||
172 | * Default connection timeout is 2 seconds, so that unresponsive methods |
||
173 | * can be bypassed quickly. |
||
174 | * |
||
175 | * @var integer |
||
176 | */ |
||
177 | public $connection_timeout = 2; |
||
178 | |||
179 | /** |
||
180 | * Information about an Autodiscover Response containing an error will |
||
181 | * be stored here. |
||
182 | * |
||
183 | * @var mixed |
||
184 | */ |
||
185 | public $error = false; |
||
186 | |||
187 | /** |
||
188 | * Information about an Autodiscover Response with a redirect will be |
||
189 | * retained here. |
||
190 | * |
||
191 | * @var mixed |
||
192 | */ |
||
193 | public $redirect = false; |
||
194 | |||
195 | /** |
||
196 | * A successful, non-error and non-redirect parsed Autodiscover response |
||
197 | * will be stored here. |
||
198 | * |
||
199 | * @var mixed |
||
200 | */ |
||
201 | public $discovered = null; |
||
202 | |||
203 | /** |
||
204 | * Constructor for the Autodiscovery class. |
||
205 | * |
||
206 | * @param string $email |
||
207 | * @param string $password |
||
208 | * @param string $username If left blank, the email provided will be used. |
||
209 | */ |
||
210 | public function __construct($email, $password, $username = null) |
||
211 | { |
||
212 | $this->email = $email; |
||
213 | $this->password = $password; |
||
214 | if ($username === null) { |
||
215 | $this->username = $email; |
||
216 | } else { |
||
217 | $this->username = $username; |
||
218 | } |
||
219 | |||
220 | $this->setTLD(); |
||
221 | } |
||
222 | |||
223 | /** |
||
224 | * Execute the full discovery chain of events in the correct sequence |
||
225 | * until a valid response is received, or all methods have failed. |
||
226 | * |
||
227 | * @return An AUTODISCOVERED_VIA_* constant or FALSE on failure. |
||
228 | */ |
||
229 | public function discover() |
||
230 | { |
||
231 | $result = $this->tryTLD(); |
||
232 | |||
233 | if ($result === false) { |
||
234 | $result = $this->trySubdomain(); |
||
235 | } |
||
236 | |||
237 | if ($result === false) { |
||
238 | $result = $this->trySubdomainUnauthenticatedGet(); |
||
239 | } |
||
240 | |||
241 | if ($result === false) { |
||
242 | $result = $this->trySRVRecord(); |
||
243 | } |
||
244 | |||
245 | return $result; |
||
246 | } |
||
247 | |||
248 | /** |
||
249 | * Return the settings discovered from the Autodiscover process. |
||
250 | * |
||
251 | * NULL indicates discovery hasn't completed (or been attempted) |
||
252 | * FALSE indicates discovery wasn't successful. Check for errors |
||
253 | * or redirects. |
||
254 | * An array will be returned with discovered settings on success. |
||
255 | * |
||
256 | * @return mixed |
||
257 | */ |
||
258 | public function discoveredSettings() |
||
259 | { |
||
260 | return $this->discovered; |
||
261 | } |
||
262 | |||
263 | /** |
||
264 | * Toggle skipping of SSL verification in cURL requests. |
||
265 | * |
||
266 | * @param boolean $skip To skip, or not. |
||
267 | * @return AutodiscoveryManager |
||
268 | */ |
||
269 | public function skipSSLVerification($skip = true) |
||
270 | { |
||
271 | $this->skip_ssl_verification = (bool) $skip; |
||
272 | |||
273 | return $this; |
||
274 | } |
||
275 | |||
276 | /** |
||
277 | * Parse the hex ServerVersion value and return a valid |
||
278 | * \PhpEws\EwsConnection::VERSION_* constant. |
||
279 | * |
||
280 | * @return string|boolean A known version constant, or FALSE if it could not |
||
281 | * be determined. |
||
282 | * |
||
283 | * @link http://msdn.microsoft.com/en-us/library/bb204122(v=exchg.140).aspx |
||
284 | * @link http://blogs.msdn.com/b/pcreehan/archive/2009/09/21/parsing-serverversion-when-an-int-is-really-5-ints.aspx |
||
285 | * @link http://office.microsoft.com/en-us/outlook-help/determine-the-version-of-microsoft-exchange-server-my-account-connects-to-HA001191800.aspx |
||
286 | */ |
||
287 | public function parseServerVersion($version_hex) |
||
288 | { |
||
289 | $svbinary = base_convert($version_hex, 16, 2); |
||
290 | if (strlen($svbinary) == 31) { |
||
291 | $svbinary = '0'.$svbinary; |
||
292 | } |
||
293 | |||
294 | $majorversion = base_convert(substr($svbinary, 4, 6), 2, 10); |
||
295 | $minorversion = base_convert(substr($svbinary, 10, 6), 2, 10); |
||
296 | $buildversion = base_convert(substr($svbinary, 17, 15), 2, 10); |
||
0 ignored issues
–
show
|
|||
297 | |||
298 | if ($majorversion == 8) { |
||
299 | View Code Duplication | switch ($minorversion) { |
|
0 ignored issues
–
show
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. ![]() |
|||
300 | case 0: |
||
301 | return EwsConnection::VERSION_2007; |
||
302 | break; |
||
0 ignored issues
–
show
break is not strictly necessary here and could be removed.
The break statement is not necessary if it is preceded for example by a return statement: switch ($x) {
case 1:
return 'foo';
break; // This break is not necessary and can be left off.
}
If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive. ![]() |
|||
303 | case 1: |
||
304 | return EwsConnection::VERSION_2007_SP1; |
||
305 | break; |
||
0 ignored issues
–
show
break is not strictly necessary here and could be removed.
The break statement is not necessary if it is preceded for example by a return statement: switch ($x) {
case 1:
return 'foo';
break; // This break is not necessary and can be left off.
}
If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive. ![]() |
|||
306 | case 2: |
||
307 | return EwsConnection::VERSION_2007_SP2; |
||
308 | break; |
||
0 ignored issues
–
show
break is not strictly necessary here and could be removed.
The break statement is not necessary if it is preceded for example by a return statement: switch ($x) {
case 1:
return 'foo';
break; // This break is not necessary and can be left off.
}
If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive. ![]() |
|||
309 | case 3: |
||
310 | return EwsConnection::VERSION_2007_SP3; |
||
311 | break; |
||
0 ignored issues
–
show
break is not strictly necessary here and could be removed.
The break statement is not necessary if it is preceded for example by a return statement: switch ($x) {
case 1:
return 'foo';
break; // This break is not necessary and can be left off.
}
If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive. ![]() |
|||
312 | default: |
||
313 | return EwsConnection::VERSION_2007; |
||
314 | } |
||
315 | } elseif ($majorversion == 14) { |
||
316 | View Code Duplication | switch ($minorversion) { |
|
0 ignored issues
–
show
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. ![]() |
|||
317 | case 0: |
||
318 | return EwsConnection::VERSION_2010; |
||
319 | break; |
||
0 ignored issues
–
show
break is not strictly necessary here and could be removed.
The break statement is not necessary if it is preceded for example by a return statement: switch ($x) {
case 1:
return 'foo';
break; // This break is not necessary and can be left off.
}
If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive. ![]() |
|||
320 | case 1: |
||
321 | return EwsConnection::VERSION_2010_SP1; |
||
322 | break; |
||
0 ignored issues
–
show
break is not strictly necessary here and could be removed.
The break statement is not necessary if it is preceded for example by a return statement: switch ($x) {
case 1:
return 'foo';
break; // This break is not necessary and can be left off.
}
If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive. ![]() |
|||
323 | case 2: |
||
324 | return EwsConnection::VERSION_2010_SP2; |
||
325 | break; |
||
0 ignored issues
–
show
break is not strictly necessary here and could be removed.
The break statement is not necessary if it is preceded for example by a return statement: switch ($x) {
case 1:
return 'foo';
break; // This break is not necessary and can be left off.
}
If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive. ![]() |
|||
326 | default: |
||
327 | return EwsConnection::VERSION_2010; |
||
328 | } |
||
329 | } |
||
330 | |||
331 | // Guess we didn't find a known version. |
||
332 | return false; |
||
333 | } |
||
334 | |||
335 | /** |
||
336 | * Method to return a new EwsConnection object, auto-configured |
||
337 | * with the proper hostname. |
||
338 | * |
||
339 | * @return EwsConnection|false EwsConnection on success, false on failure. |
||
340 | */ |
||
341 | public function createNewConnection() |
||
342 | { |
||
343 | // Discovery not yet attempted. |
||
344 | if ($this->discovered === null) { |
||
345 | $this->discover(); |
||
346 | } |
||
347 | |||
348 | // Discovery not successful. |
||
349 | if ($this->discovered === false) { |
||
350 | return false; |
||
351 | } |
||
352 | |||
353 | $server = false; |
||
354 | $version = null; |
||
355 | |||
356 | // Pick out the host from the EXPR (Exchange RPC over HTTP). |
||
357 | foreach ($this->discovered['Account']['Protocol'] as $protocol) { |
||
358 | if ( |
||
359 | ($protocol['Type'] == 'EXCH' || $protocol['Type'] == 'EXPR') |
||
360 | && isset($protocol['ServerVersion']) |
||
361 | ) { |
||
362 | if ($version == null) { |
||
363 | $sv = $this->parseServerVersion($protocol['ServerVersion']); |
||
364 | if ($sv !== false) { |
||
365 | $version = $sv; |
||
366 | } |
||
367 | } |
||
368 | } |
||
369 | |||
370 | if ($protocol['Type'] == 'EXPR' && isset($protocol['Server'])) { |
||
371 | $server = $protocol['Server']; |
||
372 | } |
||
373 | } |
||
374 | |||
375 | if ($server) { |
||
376 | if ($version === null) { |
||
377 | // EWS class default. |
||
378 | $version = EwsConnection::VERSION_2007; |
||
379 | } |
||
380 | return new EwsConnection( |
||
381 | $server, |
||
382 | $this->email, |
||
383 | $this->password, |
||
384 | $version |
||
385 | ); |
||
386 | } |
||
387 | |||
388 | return false; |
||
389 | } |
||
390 | |||
391 | /** |
||
392 | * Static method may fail if there are issues surrounding SSL certificates. |
||
393 | * In such cases, set up the object as needed, and then call newEWS(). |
||
394 | * |
||
395 | * @param string $email |
||
396 | * @param string $password |
||
397 | * @param string $username If left blank, the email provided will be used. |
||
398 | * |
||
399 | * @return EwsConnection |
||
400 | */ |
||
401 | public static function getConnection($email, $password, $username = null) |
||
402 | { |
||
403 | $auto = new self($email, $password, $username); |
||
404 | |||
405 | return $auto->createNewConnection(); |
||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
The expression
$auto->createNewConnection(); of type false|PhpEws\EwsConnection adds false to the return on line 405 which is incompatible with the return type documented by PhpEws\AutodiscoveryManager::getConnection of type PhpEws\EwsConnection . It seems like you forgot to handle an error condition.
![]() |
|||
406 | } |
||
407 | |||
408 | /** |
||
409 | * Perform an NTLM authenticated HTTPS POST to the top-level |
||
410 | * domain of the email address. |
||
411 | * |
||
412 | * @return An AUTODISCOVERED_VIA_* constant or FALSE on failure. |
||
413 | */ |
||
414 | View Code Duplication | public function tryTLD() |
|
0 ignored issues
–
show
This method seems to be duplicated in 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. ![]() |
|||
415 | { |
||
416 | $url = 'https://www.'.$this->tld . self::AUTODISCOVER_PATH; |
||
417 | $result = $this->doNTLMPost($url, 5); |
||
418 | if ($result) { |
||
419 | return self::AUTODISCOVERED_VIA_TLD; |
||
420 | } |
||
421 | |||
422 | return false; |
||
423 | } |
||
424 | |||
425 | /** |
||
426 | * Perform an NTLM authenticated HTTPS POST to the 'autodiscover' |
||
427 | * subdomain of the email address' TLD. |
||
428 | * |
||
429 | * @return An AUTODISCOVERED_VIA_* constant or FALSE on failure. |
||
430 | */ |
||
431 | View Code Duplication | public function trySubdomain() |
|
0 ignored issues
–
show
This method seems to be duplicated in 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. ![]() |
|||
432 | { |
||
433 | $url = 'https://autodiscover.'.$this->tld . self::AUTODISCOVER_PATH; |
||
434 | $result = $this->doNTLMPost($url, 5); |
||
435 | if ($result) { |
||
436 | return self::AUTODISCOVERED_VIA_SUBDOMAIN; |
||
437 | } |
||
438 | |||
439 | return false; |
||
440 | } |
||
441 | |||
442 | /** |
||
443 | * Perform an unauthenticated HTTP GET in an attempt to get redirected |
||
444 | * via 302 to the correct location to perform the HTTPS POST. |
||
445 | * |
||
446 | * @return An AUTODISCOVERED_VIA_* constant or FALSE on failure. |
||
447 | */ |
||
448 | public function trySubdomainUnauthenticatedGet() |
||
449 | { |
||
450 | $this->reset(); |
||
451 | $url = 'http://autodiscover.'.$this->tld . self::AUTODISCOVER_PATH; |
||
452 | $ch = curl_init(); |
||
453 | $opts = array( |
||
454 | CURLOPT_URL => $url, |
||
455 | CURLOPT_HTTPGET => true, |
||
456 | CURLOPT_RETURNTRANSFER => true, |
||
457 | CURLOPT_TIMEOUT => 4, |
||
458 | CURLOPT_CONNECTTIMEOUT => $this->connection_timeout, |
||
459 | CURLOPT_FOLLOWLOCATION => false, |
||
460 | CURLOPT_HEADER => false, |
||
461 | CURLOPT_HEADERFUNCTION => array($this, 'readHeaders'), |
||
462 | CURLOPT_HTTP200ALIASES => array(301, 302), |
||
463 | CURLOPT_IPRESOLVE => CURL_IPRESOLVE_V4 |
||
464 | ); |
||
465 | curl_setopt_array($ch, $opts); |
||
466 | $this->last_response = curl_exec($ch); |
||
0 ignored issues
–
show
The property
last_response does not seem to exist. Did you mean last_response_headers ?
An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name. If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading. ![]() |
|||
467 | $this->last_info = curl_getinfo($ch); |
||
0 ignored issues
–
show
It seems like
curl_getinfo($ch) of type * is incompatible with the declared type array of property $last_info .
Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property. Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property.. ![]() |
|||
468 | $this->last_curl_errno = curl_errno($ch); |
||
469 | $this->last_curl_error = curl_error($ch); |
||
470 | |||
471 | if ( |
||
472 | $this->last_info['http_code'] == 302 |
||
473 | || $this->last_info['http_code'] == 301 |
||
474 | ) { |
||
475 | // Do the NTLM POST to the redirect. |
||
476 | $result = $this->doNTLMPost( |
||
477 | $this->last_response_headers['location'] |
||
478 | ); |
||
479 | |||
480 | if ($result) { |
||
481 | return self::AUTODISCOVERED_VIA_UNAUTHENTICATED_GET; |
||
482 | } |
||
483 | } |
||
484 | |||
485 | return false; |
||
486 | } |
||
487 | |||
488 | /** |
||
489 | * Attempt to retrieve the autodiscover host from an SRV DNS record. |
||
490 | * |
||
491 | * @link http://support.microsoft.com/kb/940881 |
||
492 | * @return AutodiscoveryManager::AUTODISCOVERED_VIA_SRV_RECORD or false |
||
0 ignored issues
–
show
The doc-type
AutodiscoveryManager::AU...SCOVERED_VIA_SRV_RECORD could not be parsed: Unknown type name "AutodiscoveryManager::AUTODISCOVERED_VIA_SRV_RECORD" at position 0. (view supported doc-types)
This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types. ![]() |
|||
493 | */ |
||
494 | public function trySRVRecord() |
||
495 | { |
||
496 | $srvhost = '_autodiscover._tcp.' . $this->tld; |
||
497 | $lookup = dns_get_record($srvhost, DNS_SRV); |
||
498 | if (sizeof($lookup) > 0) { |
||
499 | $host = $lookup[0]['target']; |
||
500 | $url = 'https://' . $host . self::AUTODISCOVER_PATH; |
||
501 | $result = $this->doNTLMPost($url); |
||
502 | if ($result) { |
||
503 | return self::AUTODISCOVERED_VIA_SRV_RECORD; |
||
504 | } |
||
505 | } |
||
506 | |||
507 | return false; |
||
508 | } |
||
509 | |||
510 | /** |
||
511 | * Set the path to the file to be used by CURLOPT_CAINFO. |
||
512 | * |
||
513 | * @param string $path Path to a certificate file such as cacert.pem |
||
514 | * @return AutodiscoveryManager |
||
515 | */ |
||
516 | public function setCAInfo($path) |
||
517 | { |
||
518 | if (file_exists($path) && is_file($path)) { |
||
519 | $this->cainfo = $path; |
||
520 | } |
||
521 | |||
522 | return $this; |
||
523 | } |
||
524 | |||
525 | /** |
||
526 | * Set the path to the file to be used by CURLOPT_CAPATH. |
||
527 | * |
||
528 | * @param string $path Path to a directory containing one or more CA |
||
529 | * certificates. |
||
530 | * @return AutodiscoveryManager |
||
531 | */ |
||
532 | public function setCAPath($path) |
||
533 | { |
||
534 | if (is_dir($path)) { |
||
535 | $this->capath = $path; |
||
536 | } |
||
537 | |||
538 | return $this; |
||
539 | } |
||
540 | |||
541 | /** |
||
542 | * Set a connection timeout for the POST methods. |
||
543 | * |
||
544 | * @param integer $seconds Seconds to wait for a connection. |
||
545 | * @return AutodiscoveryManager |
||
546 | */ |
||
547 | public function setConnectionTimeout($seconds) |
||
548 | { |
||
549 | $this->connection_timeout = intval($seconds); |
||
550 | |||
551 | return $this; |
||
552 | } |
||
553 | |||
554 | /** |
||
555 | * Perform the NTLM authenticated post against one of the chosen |
||
556 | * endpoints. |
||
557 | * |
||
558 | * @param string $url URL to try posting to |
||
559 | * @param integer $timeout Overall cURL timeout for this request |
||
560 | * @return boolean |
||
561 | */ |
||
562 | public function doNTLMPost($url, $timeout = 6) |
||
563 | { |
||
564 | $this->reset(); |
||
565 | |||
566 | $ch = curl_init(); |
||
567 | $opts = array( |
||
568 | CURLOPT_URL => $url, |
||
569 | CURLOPT_HTTPAUTH => CURLAUTH_NTLM, |
||
570 | CURLOPT_CUSTOMREQUEST => 'POST', |
||
571 | CURLOPT_POSTFIELDS => $this->getAutoDiscoverRequest(), |
||
572 | CURLOPT_RETURNTRANSFER => true, |
||
573 | CURLOPT_USERPWD => $this->username.':'.$this->password, |
||
574 | CURLOPT_TIMEOUT => $timeout, |
||
575 | CURLOPT_CONNECTTIMEOUT => $this->connection_timeout, |
||
576 | CURLOPT_FOLLOWLOCATION => true, |
||
577 | CURLOPT_HEADER => false, |
||
578 | CURLOPT_HEADERFUNCTION => array($this, 'readHeaders'), |
||
579 | CURLOPT_IPRESOLVE => CURL_IPRESOLVE_V4, |
||
580 | CURLOPT_SSL_VERIFYPEER => true, |
||
581 | CURLOPT_SSL_VERIFYHOST => 2, |
||
582 | ); |
||
583 | |||
584 | // Set the appropriate content-type. |
||
585 | curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: text/xml; charset=utf-8')); |
||
586 | |||
587 | if (! empty($this->cainfo)) { |
||
588 | $opts[CURLOPT_CAINFO] = $this->cainfo; |
||
589 | } |
||
590 | |||
591 | if (! empty($this->capath)) { |
||
592 | $opts[CURLOPT_CAPATH] = $this->capath; |
||
593 | } |
||
594 | |||
595 | if ($this->skip_ssl_verification) { |
||
596 | $opts[CURLOPT_SSL_VERIFYPEER] = false; |
||
597 | $opts[CURLOPT_SSL_VERIFYHOST] = false; |
||
598 | } |
||
599 | |||
600 | curl_setopt_array($ch, $opts); |
||
601 | $this->last_response = curl_exec($ch); |
||
0 ignored issues
–
show
The property
last_response does not seem to exist. Did you mean last_response_headers ?
An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name. If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading. ![]() |
|||
602 | $this->last_info = curl_getinfo($ch); |
||
0 ignored issues
–
show
It seems like
curl_getinfo($ch) of type * is incompatible with the declared type array of property $last_info .
Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property. Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property.. ![]() |
|||
603 | $this->last_curl_errno = curl_errno($ch); |
||
604 | $this->last_curl_error = curl_error($ch); |
||
605 | |||
606 | if ($this->last_curl_errno != CURLE_OK) { |
||
607 | return false; |
||
608 | } |
||
609 | |||
610 | $discovered = $this->parseAutodiscoverResponse(); |
||
611 | |||
612 | return $discovered; |
||
613 | } |
||
614 | |||
615 | /** |
||
616 | * Parse the Autoresponse Payload, particularly to determine if an |
||
617 | * additional request is necessary. |
||
618 | * |
||
619 | * @return mixed FALSE if response isn't XML or parsed response array |
||
620 | */ |
||
621 | protected function parseAutodiscoverResponse() |
||
622 | { |
||
623 | // Content-type isn't trustworthy, unfortunately. Shame on Microsoft. |
||
624 | if (substr($this->last_response, 0, 5) !== '<?xml') { |
||
0 ignored issues
–
show
The property
last_response does not seem to exist. Did you mean last_response_headers ?
An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name. If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading. ![]() |
|||
625 | return false; |
||
626 | } |
||
627 | |||
628 | $response = $this->responseToArray($this->last_response); |
||
0 ignored issues
–
show
The property
last_response does not seem to exist. Did you mean last_response_headers ?
An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name. If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading. ![]() |
|||
629 | |||
630 | if (isset($response['Error'])) { |
||
631 | $this->error = $response['Error']; |
||
632 | return false; |
||
633 | } |
||
634 | |||
635 | // Check the account action for redirect. |
||
636 | switch ($response['Account']['Action']) { |
||
637 | case 'redirectUrl': |
||
638 | $this->redirect = array( |
||
639 | 'redirectUrl' => $response['Account']['redirectUrl'] |
||
640 | ); |
||
641 | return false; |
||
642 | break; |
||
0 ignored issues
–
show
break is not strictly necessary here and could be removed.
The break statement is not necessary if it is preceded for example by a return statement: switch ($x) {
case 1:
return 'foo';
break; // This break is not necessary and can be left off.
}
If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive. ![]() |
|||
643 | case 'redirectAddr': |
||
644 | $this->redirect = array( |
||
645 | 'redirectAddr' => $response['Account']['redirectAddr'] |
||
646 | ); |
||
647 | return false; |
||
648 | break; |
||
0 ignored issues
–
show
break is not strictly necessary here and could be removed.
The break statement is not necessary if it is preceded for example by a return statement: switch ($x) {
case 1:
return 'foo';
break; // This break is not necessary and can be left off.
}
If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive. ![]() |
|||
649 | case 'settings': |
||
650 | default: |
||
651 | $this->discovered = $response; |
||
652 | return true; |
||
653 | } |
||
654 | } |
||
655 | |||
656 | /** |
||
657 | * Set the top-level domain to be used with autodiscover attempts based |
||
658 | * on the provided email address. |
||
659 | * |
||
660 | * @return boolean |
||
661 | */ |
||
662 | protected function setTLD() |
||
663 | { |
||
664 | $pos = strpos($this->email, '@'); |
||
665 | if ($pos !== false) { |
||
666 | $this->tld = trim(substr($this->email, $pos+1)); |
||
667 | return true; |
||
668 | } |
||
669 | |||
670 | return false; |
||
671 | } |
||
672 | |||
673 | /** |
||
674 | * Reset the response-related structures. Called before making a new |
||
675 | * request. |
||
676 | * |
||
677 | * @return AutodiscoveryManager |
||
678 | */ |
||
679 | public function reset() |
||
680 | { |
||
681 | $this->last_response_headers = array(); |
||
682 | $this->last_info = array(); |
||
683 | $this->last_curl_errno = 0; |
||
684 | $this->last_curl_error = ''; |
||
685 | |||
686 | return $this; |
||
687 | } |
||
688 | |||
689 | /** |
||
690 | * Return the generated Autodiscover XML request body. |
||
691 | * |
||
692 | * @return string |
||
693 | */ |
||
694 | public function getAutodiscoverRequest() |
||
695 | { |
||
696 | if (! empty($this->requestxml)) { |
||
697 | return $this->requestxml; |
||
698 | } |
||
699 | |||
700 | $xml = new \XMLWriter(); |
||
701 | $xml->openMemory(); |
||
702 | $xml->setIndent(true); |
||
703 | $xml->startDocument('1.0', 'UTF-8'); |
||
704 | $xml->startElementNS( |
||
705 | null, |
||
706 | 'Autodiscover', |
||
707 | 'http://schemas.microsoft.com/exchange/autodiscover/outlook/requestschema/2006' |
||
708 | ); |
||
709 | |||
710 | $xml->startElement('Request'); |
||
711 | $xml->writeElement('EMailAddress', $this->email); |
||
712 | $xml->writeElement( |
||
713 | 'AcceptableResponseSchema', |
||
714 | 'http://schemas.microsoft.com/exchange/autodiscover/outlook/responseschema/2006a' |
||
715 | ); |
||
716 | $xml->endElement(); |
||
717 | $xml->endElement(); |
||
718 | |||
719 | $this->requestxml = $xml->outputMemory(); |
||
720 | return $this->requestxml; |
||
721 | } |
||
722 | |||
723 | /** |
||
724 | * Utility function to pick headers off of the incoming cURL response. |
||
725 | * Used with CURLOPT_HEADERFUNCTION. |
||
726 | * |
||
727 | * @param resource $_ch cURL handle |
||
728 | * @param string $str Header string to read |
||
729 | * @return integer Bytes read |
||
730 | */ |
||
731 | public function readHeaders($_ch, $str) |
||
732 | { |
||
733 | $pos = strpos($str, ':'); |
||
734 | if ($pos !== false) { |
||
735 | $key = strtolower(substr($str, 0, $pos)); |
||
736 | $val = trim(substr($str, $pos+1)); |
||
737 | $this->last_response_headers[$key] = $val; |
||
738 | } |
||
739 | |||
740 | return strlen($str); |
||
741 | } |
||
742 | |||
743 | /** |
||
744 | * Utility function to parse XML payloads from the response into easier |
||
745 | * to manage associative arrays. |
||
746 | * |
||
747 | * @param string $xml XML to parse |
||
748 | * @return array |
||
749 | */ |
||
750 | public function responseToArray($xml) |
||
751 | { |
||
752 | $doc = new \DOMDocument(); |
||
753 | $doc->loadXML($xml); |
||
754 | $out = $this->nodeToArray($doc->documentElement); |
||
755 | |||
756 | return $out['Response']; |
||
757 | } |
||
758 | |||
759 | /** |
||
760 | * Recursive method for parsing DOM nodes. |
||
761 | * |
||
762 | * @link https://github.com/gaarf/XML-string-to-PHP-array |
||
763 | * @param object $node DOMNode object |
||
764 | * @return mixed |
||
765 | */ |
||
766 | protected function nodeToArray($node) |
||
767 | { |
||
768 | $output = array(); |
||
769 | switch ($node->nodeType) { |
||
770 | case XML_CDATA_SECTION_NODE: |
||
771 | case XML_TEXT_NODE: |
||
772 | $output = trim($node->textContent); |
||
773 | break; |
||
774 | case XML_ELEMENT_NODE: |
||
775 | for ($i=0, $m = $node->childNodes->length; $i < $m; $i++) { |
||
776 | $child = $node->childNodes->item($i); |
||
777 | $v = $this->nodeToArray($child); |
||
778 | if (isset($child->tagName)) { |
||
779 | $t = $child->tagName; |
||
780 | if (!isset($output[$t])) { |
||
781 | $output[$t] = array(); |
||
782 | } |
||
783 | $output[$t][] = $v; |
||
784 | } elseif ($v || $v === '0') { |
||
785 | $output = (string) $v; |
||
786 | } |
||
787 | } |
||
788 | |||
789 | // Edge case of a node containing a text node, which also has |
||
790 | // attributes. this way we'll retain text and attributes for |
||
791 | // this node. |
||
792 | if (is_string($output) && $node->attributes->length) { |
||
793 | $output = array('@text' => $output); |
||
794 | } |
||
795 | |||
796 | if (is_array($output)) { |
||
797 | if ($node->attributes->length) { |
||
798 | $a = array(); |
||
799 | foreach ($node->attributes as $attrName => $attrNode) { |
||
800 | $a[$attrName] = (string) $attrNode->value; |
||
801 | } |
||
802 | $output['@attributes'] = $a; |
||
803 | } |
||
804 | foreach ($output as $t => $v) { |
||
805 | if (is_array($v) && count($v)==1 && $t!='@attributes') { |
||
806 | $output[$t] = $v[0]; |
||
807 | } |
||
808 | } |
||
809 | } |
||
810 | break; |
||
811 | } |
||
812 | |||
813 | return $output; |
||
814 | } |
||
815 | } |
||
816 |
This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.
Both the
$myVar
assignment in line 1 and the$higher
assignment in line 2 are dead. The first because$myVar
is never used and the second because$higher
is always overwritten for every possible time line.