Completed
Push — master ( db2e2d...3f3a28 )
by Gareth
04:09 queued 13s
created

ExchangeWebServices::getClient()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 0
crap 1
1
<?php
2
/**
3
 * Contains ExchangeWebServices.
4
 */
5
6
7
namespace garethp\ews\API;
8
9
use garethp\ews\API\Exception\ExchangeException;
10
use garethp\ews\API\Exception\NoResponseReturnedException;
11
use garethp\ews\API\Exception\ServiceUnavailableException;
12
use garethp\ews\API\Exception\UnauthorizedException;
13
use garethp\ews\API\Message;
14
use garethp\ews\API\Type\EmailAddressType;
15
16
/**
17
 * Base class of the Exchange Web Services application.
18
 *
19
 *
20
 *
21
 * @package php-ews\Client
22
 *
23
 * @method Type AddDelegate($request)
24
 * @method Type ApplyConversationAction($request)
25
 * @method Type ConvertId($request)
26
 * @method Type CopyFolder($request)
27
 * @method Type CopyItem($request)
28
 * @method Type CreateAttachment($request)
29
 * @method Type CreateFolder($request)
30
 * @method Type CreateItem($request)
31
 * @method Type CreateManagedFolder($request)
32
 * @method Type CreateUserConfiguration($request)
33
 * @method Type DeleteAttachment($request)
34
 * @method Type DeleteFolder($request)
35
 * @method Type DeleteItem($request)
36
 * @method Type DeleteUserConfiguration($request)
37
 * @method Type DisconnectPhoneCall($request)
38
 * @method Type EmptyFolder($request)
39
 * @method Type ExpandDL($request)
40
 * @method Type ExportItems($request)
41
 * @method Type FindConversation($request)
42
 * @method Type FindFolder($request)
43
 * @method Type FindItem($request)
44
 * @method Type FindMessageTrackingReport($request)
45
 * @method Type GetAttachment($request)
46
 * @method Type GetDelegate($request)
47
 * @method Type GetEvents($request)
48
 * @method Type GetFolder($request)
49
 * @method Type GetInboxRules($request)
50
 * @method Type GetItem($request)
51
 * @method Type GetMailTips($request)
52
 * @method Type GetMessageTrackingReport($request)
53
 * @method Type GetPasswordExpirationDate($request)
54
 * @method Type GetPhoneCallInformation($request)
55
 * @method Type GetRoomLists($request)
56
 * @method Type GetRooms($request)
57
 * @method Type GetServerTimeZones($request)
58
 * @method Type GetServiceConfiguration($request)
59
 * @method Type GetSharingFolder($request)
60
 * @method Type GetSharingMetadata($request)
61
 * @method Type GetStreamingEvents($request)
62
 * @method Type GetUserAvailability($request)
63
 * @method Type GetUserConfiguration($request)
64
 * @method Type GetUserOofSettings($request)
65
 * @method Type MoveFolder($request)
66
 * @method Type MoveItem($request)
67
 * @method Type PlayOnPhone($request)
68
 * @method Type RefreshSharingFolder($request)
69
 * @method Type RemoveDelegate($request)
70
 * @method Type ResolveNames($request)
71
 * @method Type SendItem($request)
72
 * @method Type SetUserOofSettings($request)
73
 * @method Type Subscribe($request)
74
 * @method Type SyncFolderHierarchy($request)
75
 * @method Type SyncFolderItems($request)
76
 * @method Type Unsubscribe($request)
77
 * @method Type UpdateDelegate($request)
78
 * @method Type UpdateFolder($request)
79
 * @method Type UpdateInboxRules($request)
80
 * @method Type UpdateItem($request)
81
 * @method Type UpdateUserConfiguration($request)
82
 * @method Type UploadItems($request)
83
 */
84
class ExchangeWebServices
85
{
86
87
    const VERSION_2007 = 'Exchange2007';
88
89
    const VERSION_2007_SP1 = 'Exchange2007_SP1';
90
91
    const VERSION_2010 = 'Exchange2010';
92
93
    const VERSION_2010_SP1 = 'Exchange2010_SP1';
94
95
    const VERSION_2010_SP2 = 'Exchange2010_SP2';
96
97
    const VERSION_2013 = 'Exchange2013';
98
99
    const VERSION_2013_SP1 = 'Exchange2013_SP1';
100
101
    /**
102
     * Password to use when connecting to the Exchange server.
103
     *
104
     * @var string
105
     */
106
    protected $password = null;
107
108
    /**
109
     * Location of the Exchange server.
110
     *
111
     * @var string
112
     */
113
    protected $server = null;
114
115
    /**
116
     * SOAP client used to make the request
117
     *
118
     * @var NTLMSoapClient
119
     */
120
    protected $soap = null;
121
122
    /**
123
     * Username to use when connecting to the Exchange server.
124
     *
125
     * @var string
126
     */
127
    protected $username = null;
128
129
    /**
130
     * @var EmailAddressType
131
     */
132
    protected $primarySmtpMailbox = null;
133
134
    /**
135
     * A setting to check whether or not responses should be drilled down before being
136
     * returned. Setting this to false
137
     * will return the raw responses without any filtering
138
     *
139
     * @var bool
140
     */
141
    protected $drillDownResponses = true;
142
143
    /**
144
     * Miscrosoft Exchange version that we are going to connect to
145
     *
146
     * @var string
147
     */
148
    protected $version = null;
149
150
    protected $options = null;
151
152
    /**
153
     * The timezone for the client
154
     *
155
     * @var bool
156
     */
157
    protected $timezone = false;
158
159
    /**
160
     * @return EmailAddressType
161
     */
162 25
    public function getPrimarySmtpMailbox()
163
    {
164 25
        return $this->primarySmtpMailbox;
165
    }
166
167 1
    public function getPrimarySmtpEmailAddress()
168
    {
169 1
        if ($this->primarySmtpMailbox == null) {
170 1
            return null;
171
        }
172
173 1
        return $this->primarySmtpMailbox->getEmailAddress();
174
    }
175
176 2
    public function setPrimarySmtpEmailAddress($emailAddress)
177
    {
178 2
        $mailbox = new EmailAddressType();
179 2
        $mailbox->setEmailAddress($emailAddress);
180 2
        $this->primarySmtpMailbox = $mailbox;
181
182 2
        return $this;
183
    }
184
185
    /**
186
     * @param boolean $timezone
187
     */
188
    public function setTimezone($timezone)
189
    {
190
        $this->timezone = $timezone;
191
    }
192
193
    /**
194
     * @return string
195
     */
196 2
    public function getVersion()
197
    {
198 2
        return $this->version;
199
    }
200
201
    /**
202
     * @return string
203
     */
204
    public function getServer()
205
    {
206
        return $this->server;
207
    }
208
209
    /**
210
     * Constructor for the ExchangeWebServices class
211
     *
212
     * @param string $server
213
     * @param string $username
214
     * @param string $password
215
     * @param array $options
216
     */
217 33
    protected function __construct($server = null, $username = null, $password = null, $options = array())
218
    {
219 33
        if ($server !== null) {
220
            $this->createClient(
221
                $server,
222
                ExchangeWebServicesAuth::fromUsernameAndPassword($username, $password),
223
                $options
224
            );
225
        }
226 33
    }
227
228 32
    public static function fromUsernameAndPassword($server, $username, $password, $options)
229
    {
230 32
        $self = new self();
231 32
        $self->createClient($server, ExchangeWebServicesAuth::fromUsernameAndPassword($username, $password), $options);
232
233 32
        return $self;
234
    }
235
236 1
    public static function fromCallbackToken($server, $token, $options)
237
    {
238 1
        $self = new self();
239 1
        $self->createClient($server, ExchangeWebServicesAuth::fromCallbackToken($token), $options);
240
241 1
        return $self;
242
    }
243
244 33
    protected function createClient($server, $auth, $options)
245
    {
246 33
        $location = 'https://'.$this->cleanServerUrl($server).'/EWS/Exchange.asmx';
247
248 33
        $options = array_replace_recursive([
249 33
            'version' => self::VERSION_2007,
250 33
            'trace' => 1,
251 33
            'exceptions' => true,
252 33
            'classmap' => ClassMap::getClassMap(),
253
            'drillDownResponses' => true
254 33
        ], $options);
255
256 33
        $this->server = $server;
257 33
        $this->version = $options['version'];
258
259 33
        $this->soap = new NTLMSoapClient(
260 33
            $location,
261 33
            $auth,
262 33
            dirname(__FILE__).'/../../Resources/wsdl/services.wsdl',
263
            $options
264 33
        );
265
266 33
        if (isset($options['primarySmtpEmailAddress'])) {
267 1
            $this->setPrimarySmtpEmailAddress($options['primarySmtpEmailAddress']);
268 1
        }
269
270 33
        if (isset($options['impersonation'])) {
271 1
            $this->setPrimarySmtpEmailAddress($options['impersonation']);
272 1
        }
273
274 33
        $this->drillDownResponses = $options['drillDownResponses'];
275 33
    }
276
277
    /**
278
     * @codeCoverageIgnore
279
     *
280
     * @param $name
281
     * @param $arguments
282
     * @return Type
283
     * @throws \garethp\ews\API\Exception
284
     */
285
    public function __call($name, $arguments)
286
    {
287
        $response = $this->getClient()->__call($name, $arguments);
288
289
        return $this->processResponse($response);
290
    }
291
292
    /**
293
     * Returns the SOAP Client that may be used to make calls against the server
294
     *
295
     * @return NTLMSoapClient
296
     */
297 31
    public function getClient()
298
    {
299 31
        return $this->soap;
300
    }
301
302
    /**
303
     * Sets the client
304
     *
305
     * @param NTLMSoapClient $client
306
     * @return $this
307
     */
308 2
    public function setClient($client)
309
    {
310 2
        $this->soap = $client;
311
312 2
        return $this;
313
    }
314
315
    /**
316
     * Cleans the server URL for usage
317
     *
318
     * @param $server
319
     * @return string
320
     */
321 40
    public function cleanServerUrl($server)
322
    {
323 40
        $url = parse_url($server);
324 40
        if (!isset($url['host']) && isset($url['path'])) {
325 35
            $url['host'] = $url['path'];
326 35
            unset($url['path']);
327 35
        }
328
329 40
        $server = $url['host'];
330 40
        if (isset($url['port'])) {
331 2
            $server .= ':'.$url['port'];
332 2
        }
333
334 40
        if (isset($url['path'])) {
335 4
            $server .= $url['path'];
336 4
        }
337
338 40
        $server = rtrim($server, "/");
339
340 40
        return $server;
341
    }
342
343
    /**
344
     * Process a response to verify that it succeeded and take the appropriate
345
     * action
346
     *
347
     * @param \garethp\ews\API\Message\BaseResponseMessageType $response
348
     * @return Type[]
349
     * @throws \garethp\ews\API\Exception
350
     */
351 29
    protected function processResponse($response)
352
    {
353
        // If the soap call failed then we need to thow an exception.
354 29
        $code = $this->getClient()->getResponseCode();
355 29
        $this->handleNonSuccessfulResponses($response, $code);
356
357 27
        if (!$this->drillDownResponses) {
358
            return $response;
359
        }
360
361 27
        if (!$response->exists('responseMessages')) {
362
            return $response;
363
        }
364
365 27
        $response = $response->getResponseMessages();
366 27
        $response = $this->drillDownResponseLevels($response);
367
368 27
        return $response;
369
    }
370
371
    /**
372
     * @param $response
373
     * @return array
374
     * @throws \garethp\ews\API\Exception
375
     */
376 27
    public function drillDownResponseLevels($response)
377
    {
378 27
        $items = $this->getItemsFromResponse($response);
379
380 27
        if (count($items) == 1) {
381 27
            reset($items);
382 27
            $key = key($items);
383 27
            $methodName = "get$key";
384 27
            $response = $response->$methodName();
385
386 27
            return $this->drillDownResponseLevels($response);
387
        }
388
389 27
        if (is_array($items) && isset($items[1]) && $items[1] instanceof Message\ResponseMessageType) {
0 ignored issues
show
Bug introduced by
The class garethp\ews\API\Message\ResponseMessageType does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
390 1
            $response = array();
391 1
            foreach ($items as $responseItem) {
392 1
                $response[] = $this->drillDownResponseLevels($responseItem);
393 1
            }
394
395 1
            return $response;
396
        }
397
398 27
        return $response;
399
    }
400
401
    /**
402
     * @param $response
403
     * @return array
404
     * @throws ExchangeException
405
     */
406 27
    protected function getItemsFromResponse($response)
407
    {
408 27
        $items = array();
409 27
        if ($response instanceof Type) {
410 27
            $items = $response->getNonNullItems();
411 27
        }
412
413 27
        if (is_array($response)) {
414 1
            $items = $response;
415 1
        }
416
417 27
        if ($response instanceof Message\ResponseMessageType) {
0 ignored issues
show
Bug introduced by
The class garethp\ews\API\Message\ResponseMessageType does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
418 27
            if ($response->getResponseClass() !== "Success") {
419 1
                throw new ExchangeException($response->getMessageText());
420
            }
421
422 27
            unset($items['responseClass']);
423 27
            unset($items['responseCode']);
424 27
        }
425
426 27
        return $items;
427
    }
428
429
    /**
430
     * @param Message\BaseResponseMessageType $response
431
     * @param $code
432
     * @throws ExchangeException
433
     * @throws NoResponseReturnedException
434
     * @throws ServiceUnavailableException
435
     * @throws UnauthorizedException
436
     */
437 29
    protected function handleNonSuccessfulResponses($response, $code)
438
    {
439 29
        if ($code == 401) {
440
            throw new UnauthorizedException();
441
        }
442
443 29
        if ($code == 503) {
444
            throw new ServiceUnavailableException();
445
        }
446
447 29
        if ($code >= 300) {
448 2
            throw new ExchangeException('SOAP client returned status of ' . $code, $code);
449
        }
450
451 27
        if (empty($response) || empty($response->getNonNullResponseMessages())) {
452
            throw new NoResponseReturnedException();
453
        }
454 27
    }
455
}
456