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_2007_SP2 = 'Exchange2007_SP2'; |
92
|
|
|
|
93
|
|
|
const VERSION_2007_SP3 = 'Exchange2007_SP3'; |
94
|
|
|
|
95
|
|
|
const VERSION_2010 = 'Exchange2010'; |
96
|
|
|
|
97
|
|
|
const VERSION_2010_SP1 = 'Exchange2010_SP1'; |
98
|
|
|
|
99
|
|
|
const VERSION_2010_SP2 = 'Exchange2010_SP2'; |
100
|
|
|
|
101
|
|
|
const VERSION_2010_SP3 = 'Exchange2010_SP3'; |
102
|
|
|
|
103
|
|
|
const VERSION_2013 = 'Exchange2013'; |
104
|
|
|
|
105
|
|
|
const VERSION_2013_SP1 = 'Exchange2013_SP1'; |
106
|
|
|
|
107
|
|
|
/** |
108
|
|
|
* Password to use when connecting to the Exchange server. |
109
|
|
|
* |
110
|
|
|
* @var string |
111
|
|
|
*/ |
112
|
|
|
protected $password = null; |
113
|
|
|
|
114
|
|
|
/** |
115
|
|
|
* Location of the Exchange server. |
116
|
|
|
* |
117
|
|
|
* @var string |
118
|
|
|
*/ |
119
|
|
|
protected $server = null; |
120
|
|
|
|
121
|
|
|
/** |
122
|
|
|
* SOAP client used to make the request |
123
|
|
|
* |
124
|
|
|
* @var NTLMSoapClient |
125
|
|
|
*/ |
126
|
|
|
protected $soap = null; |
127
|
|
|
|
128
|
|
|
/** |
129
|
|
|
* Username to use when connecting to the Exchange server. |
130
|
|
|
* |
131
|
|
|
* @var string |
132
|
|
|
*/ |
133
|
|
|
protected $username = null; |
134
|
|
|
|
135
|
|
|
/** |
136
|
|
|
* @var EmailAddressType |
137
|
|
|
*/ |
138
|
|
|
protected $primarySmtpMailbox = null; |
139
|
|
|
|
140
|
|
|
/** |
141
|
|
|
* A setting to check whether or not responses should be drilled down before being |
142
|
|
|
* returned. Setting this to false |
143
|
|
|
* will return the raw responses without any filtering |
144
|
|
|
* |
145
|
|
|
* @var bool |
146
|
|
|
*/ |
147
|
|
|
protected $drillDownResponses = true; |
148
|
|
|
|
149
|
|
|
/** |
150
|
|
|
* Miscrosoft Exchange version that we are going to connect to |
151
|
|
|
* |
152
|
|
|
* @var string |
153
|
|
|
*/ |
154
|
|
|
protected $version = null; |
155
|
|
|
|
156
|
|
|
protected $options = null; |
157
|
|
|
|
158
|
|
|
/** |
159
|
|
|
* The timezone for the client |
160
|
|
|
* |
161
|
|
|
* @var bool |
162
|
|
|
*/ |
163
|
|
|
protected $timezone = false; |
164
|
|
|
|
165
|
|
|
/** |
166
|
|
|
* @return EmailAddressType |
167
|
|
|
*/ |
168
|
24 |
|
public function getPrimarySmtpMailbox() |
169
|
|
|
{ |
170
|
24 |
|
return $this->primarySmtpMailbox; |
171
|
|
|
} |
172
|
|
|
|
173
|
1 |
|
public function getPrimarySmtpEmailAddress() |
174
|
|
|
{ |
175
|
1 |
|
if ($this->primarySmtpMailbox == null) { |
176
|
1 |
|
return null; |
177
|
|
|
} |
178
|
|
|
|
179
|
1 |
|
return $this->primarySmtpMailbox->getEmailAddress(); |
180
|
|
|
} |
181
|
|
|
|
182
|
2 |
|
public function setPrimarySmtpEmailAddress($emailAddress) |
183
|
|
|
{ |
184
|
2 |
|
$mailbox = new EmailAddressType(); |
185
|
2 |
|
$mailbox->setEmailAddress($emailAddress); |
186
|
2 |
|
$this->primarySmtpMailbox = $mailbox; |
187
|
|
|
|
188
|
2 |
|
return $this; |
189
|
|
|
} |
190
|
|
|
|
191
|
|
|
/** |
192
|
|
|
* @param boolean $timezone |
193
|
|
|
*/ |
194
|
|
|
public function setTimezone($timezone) |
195
|
|
|
{ |
196
|
|
|
$this->timezone = $timezone; |
197
|
|
|
} |
198
|
|
|
|
199
|
|
|
/** |
200
|
|
|
* @return string |
201
|
|
|
*/ |
202
|
2 |
|
public function getVersion() |
203
|
|
|
{ |
204
|
2 |
|
return $this->version; |
205
|
|
|
} |
206
|
|
|
|
207
|
|
|
/** |
208
|
|
|
* @return string |
209
|
|
|
*/ |
210
|
|
|
public function getServer() |
211
|
|
|
{ |
212
|
|
|
return $this->server; |
213
|
|
|
} |
214
|
|
|
|
215
|
|
|
/** |
216
|
|
|
* Constructor for the ExchangeWebServices class |
217
|
|
|
* |
218
|
|
|
* @deprecated Since 0.6.3 |
219
|
|
|
* @param string $server |
220
|
|
|
* @param string $username |
221
|
|
|
* @param string $password |
222
|
|
|
* @param array $options |
223
|
|
|
*/ |
224
|
33 |
|
public function __construct($server = null, $username = null, $password = null, $options = array()) |
225
|
|
|
{ |
226
|
33 |
|
if ($server !== null) { |
227
|
1 |
|
$this->createClient( |
228
|
|
|
$server, |
229
|
1 |
|
ExchangeWebServicesAuth::fromUsernameAndPassword($username, $password), |
230
|
|
|
$options |
231
|
|
|
); |
232
|
|
|
} |
233
|
|
|
} |
234
|
|
|
|
235
|
31 |
|
public static function fromUsernameAndPassword($server, $username, $password, $options) |
236
|
|
|
{ |
237
|
31 |
|
$self = new self(); |
238
|
31 |
|
$self->createClient($server, ExchangeWebServicesAuth::fromUsernameAndPassword($username, $password), $options); |
239
|
|
|
|
240
|
31 |
|
return $self; |
241
|
|
|
} |
242
|
|
|
|
243
|
1 |
|
public static function fromCallbackToken($server, $token, $options) |
244
|
|
|
{ |
245
|
1 |
|
$self = new self(); |
246
|
1 |
|
$self->createClient($server, ExchangeWebServicesAuth::fromCallbackToken($token), $options); |
247
|
|
|
|
248
|
1 |
|
return $self; |
249
|
|
|
} |
250
|
|
|
|
251
|
33 |
|
protected function createClient($server, $auth, $options) |
252
|
|
|
{ |
253
|
33 |
|
$location = 'https://'.$this->cleanServerUrl($server).'/EWS/Exchange.asmx'; |
254
|
|
|
|
255
|
33 |
|
$options = array_replace_recursive([ |
256
|
33 |
|
'version' => self::VERSION_2007, |
257
|
33 |
|
'trace' => 1, |
258
|
|
|
'exceptions' => true, |
259
|
33 |
|
'classmap' => ClassMap::getClassMap(), |
260
|
|
|
'drillDownResponses' => true |
261
|
|
|
], $options); |
262
|
|
|
|
263
|
33 |
|
$this->server = $server; |
264
|
33 |
|
$this->version = $options['version']; |
265
|
|
|
|
266
|
33 |
|
$this->soap = new NTLMSoapClient( |
267
|
|
|
$location, |
268
|
|
|
$auth, |
269
|
33 |
|
dirname(__FILE__).'/../../Resources/wsdl/services.wsdl', |
270
|
|
|
$options |
271
|
|
|
); |
272
|
|
|
|
273
|
33 |
|
if (isset($options['primarySmtpEmailAddress'])) { |
274
|
1 |
|
$this->setPrimarySmtpEmailAddress($options['primarySmtpEmailAddress']); |
275
|
|
|
} |
276
|
|
|
|
277
|
33 |
|
if (isset($options['impersonation'])) { |
278
|
1 |
|
$this->setPrimarySmtpEmailAddress($options['impersonation']); |
279
|
|
|
} |
280
|
|
|
|
281
|
33 |
|
$this->drillDownResponses = $options['drillDownResponses']; |
282
|
|
|
} |
283
|
|
|
|
284
|
|
|
/** |
285
|
|
|
* @codeCoverageIgnore |
286
|
|
|
* |
287
|
|
|
* @param $name |
288
|
|
|
* @param $arguments |
289
|
|
|
* @return Type |
290
|
|
|
* @throws \garethp\ews\API\Exception |
291
|
|
|
*/ |
292
|
|
|
public function __call($name, $arguments) |
293
|
|
|
{ |
294
|
|
|
$response = $this->getClient()->__call($name, $arguments); |
295
|
|
|
|
296
|
|
|
return $this->processResponse($response); |
297
|
|
|
} |
298
|
|
|
|
299
|
|
|
/** |
300
|
|
|
* Returns the SOAP Client that may be used to make calls against the server |
301
|
|
|
* |
302
|
|
|
* @return NTLMSoapClient |
303
|
|
|
*/ |
304
|
30 |
|
public function getClient() |
305
|
|
|
{ |
306
|
30 |
|
return $this->soap; |
307
|
|
|
} |
308
|
|
|
|
309
|
|
|
/** |
310
|
|
|
* Sets the client |
311
|
|
|
* |
312
|
|
|
* @param NTLMSoapClient $client |
313
|
|
|
* @return $this |
314
|
|
|
*/ |
315
|
2 |
|
public function setClient($client) |
316
|
|
|
{ |
317
|
2 |
|
$this->soap = $client; |
318
|
|
|
|
319
|
2 |
|
return $this; |
320
|
|
|
} |
321
|
|
|
|
322
|
|
|
/** |
323
|
|
|
* Cleans the server URL for usage |
324
|
|
|
* |
325
|
|
|
* @param $server |
326
|
|
|
* @return string |
327
|
|
|
*/ |
328
|
40 |
|
public function cleanServerUrl($server) |
329
|
|
|
{ |
330
|
40 |
|
$url = parse_url($server); |
331
|
40 |
|
if (!isset($url['host']) && isset($url['path'])) { |
332
|
35 |
|
$url['host'] = $url['path']; |
333
|
35 |
|
unset($url['path']); |
334
|
|
|
} |
335
|
|
|
|
336
|
40 |
|
$server = $url['host']; |
337
|
40 |
|
if (isset($url['port'])) { |
338
|
2 |
|
$server .= ':'.$url['port']; |
339
|
|
|
} |
340
|
|
|
|
341
|
40 |
|
if (isset($url['path'])) { |
342
|
4 |
|
$server .= $url['path']; |
343
|
|
|
} |
344
|
|
|
|
345
|
40 |
|
$server = rtrim($server, "/"); |
346
|
|
|
|
347
|
40 |
|
return $server; |
348
|
|
|
} |
349
|
|
|
|
350
|
|
|
/** |
351
|
|
|
* Process a response to verify that it succeeded and take the appropriate |
352
|
|
|
* action |
353
|
|
|
* |
354
|
|
|
* @param \garethp\ews\API\Message\BaseResponseMessageType $response |
355
|
|
|
* @return Type[] |
356
|
|
|
* @throws \garethp\ews\API\Exception |
357
|
|
|
*/ |
358
|
28 |
|
protected function processResponse($response) |
359
|
|
|
{ |
360
|
|
|
// If the soap call failed then we need to thow an exception. |
361
|
28 |
|
$code = $this->getClient()->getResponseCode(); |
362
|
28 |
|
$this->handleNonSuccessfulResponses($response, $code); |
363
|
|
|
|
364
|
26 |
|
if (!$this->drillDownResponses) { |
365
|
|
|
return $response; |
366
|
|
|
} |
367
|
|
|
|
368
|
26 |
|
if (!$response->exists('responseMessages')) { |
369
|
|
|
return $response; |
370
|
|
|
} |
371
|
|
|
|
372
|
26 |
|
$response = $response->getResponseMessages(); |
373
|
26 |
|
$response = $this->drillDownResponseLevels($response); |
374
|
|
|
|
375
|
26 |
|
return $response; |
376
|
|
|
} |
377
|
|
|
|
378
|
|
|
/** |
379
|
|
|
* @param $response |
380
|
|
|
* @return array |
381
|
|
|
* @throws \garethp\ews\API\Exception |
382
|
|
|
*/ |
383
|
26 |
|
public function drillDownResponseLevels($response) |
384
|
|
|
{ |
385
|
26 |
|
$items = $this->getItemsFromResponse($response); |
386
|
|
|
|
387
|
26 |
|
if (count($items) == 1) { |
388
|
26 |
|
reset($items); |
389
|
26 |
|
$key = key($items); |
390
|
26 |
|
$methodName = "get$key"; |
391
|
26 |
|
$response = $response->$methodName(); |
392
|
|
|
|
393
|
26 |
|
return $this->drillDownResponseLevels($response); |
394
|
|
|
} |
395
|
|
|
|
396
|
26 |
|
if (is_array($items) && isset($items[1]) && $items[1] instanceof Message\ResponseMessageType) { |
|
|
|
|
397
|
1 |
|
$response = array(); |
398
|
1 |
|
foreach ($items as $responseItem) { |
399
|
1 |
|
$response[] = $this->drillDownResponseLevels($responseItem); |
400
|
|
|
} |
401
|
|
|
|
402
|
1 |
|
return $response; |
403
|
|
|
} |
404
|
|
|
|
405
|
26 |
|
return $response; |
406
|
|
|
} |
407
|
|
|
|
408
|
|
|
/** |
409
|
|
|
* @param $response |
410
|
|
|
* @return array |
411
|
|
|
* @throws ExchangeException |
412
|
|
|
*/ |
413
|
26 |
|
protected function getItemsFromResponse($response) |
414
|
|
|
{ |
415
|
26 |
|
$items = array(); |
416
|
26 |
|
if ($response instanceof Type) { |
417
|
26 |
|
$items = $response->getNonNullItems(); |
418
|
|
|
} |
419
|
|
|
|
420
|
26 |
|
if (is_array($response)) { |
421
|
1 |
|
$items = $response; |
422
|
|
|
} |
423
|
|
|
|
424
|
26 |
|
if ($response instanceof Message\ResponseMessageType) { |
|
|
|
|
425
|
26 |
|
if ($response->getResponseClass() !== "Success") { |
426
|
1 |
|
throw new ExchangeException($response->getMessageText()); |
427
|
|
|
} |
428
|
|
|
|
429
|
26 |
|
unset($items['responseClass']); |
430
|
26 |
|
unset($items['responseCode']); |
431
|
|
|
} |
432
|
|
|
|
433
|
26 |
|
return $items; |
434
|
|
|
} |
435
|
|
|
|
436
|
|
|
/** |
437
|
|
|
* @param Message\BaseResponseMessageType $response |
438
|
|
|
* @param $code |
439
|
|
|
* @throws ExchangeException |
440
|
|
|
* @throws NoResponseReturnedException |
441
|
|
|
* @throws ServiceUnavailableException |
442
|
|
|
* @throws UnauthorizedException |
443
|
|
|
*/ |
444
|
28 |
|
protected function handleNonSuccessfulResponses($response, $code) |
445
|
|
|
{ |
446
|
28 |
|
if ($code == 401) { |
447
|
|
|
throw new UnauthorizedException(); |
448
|
|
|
} |
449
|
|
|
|
450
|
28 |
|
if ($code == 503) { |
451
|
|
|
throw new ServiceUnavailableException(); |
452
|
|
|
} |
453
|
|
|
|
454
|
28 |
|
if ($code >= 300) { |
455
|
2 |
|
throw new ExchangeException('SOAP client returned status of ' . $code, $code); |
456
|
|
|
} |
457
|
|
|
|
458
|
26 |
|
if (empty($response) || empty($response->getNonNullResponseMessages())) { |
459
|
|
|
throw new NoResponseReturnedException(); |
460
|
|
|
} |
461
|
|
|
} |
462
|
|
|
} |
463
|
|
|
|
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 thecomposer.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
orrequire-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 you have not tested against this specific condition, such errors might go unnoticed.