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