Completed
Push — master ( e11a9c...c41b0b )
by Gareth
02:56
created

NTLMSoapClient::__call()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 20
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 13
CRAP Score 2

Importance

Changes 4
Bugs 2 Features 0
Metric Value
cc 2
eloc 11
c 4
b 2
f 0
nc 2
nop 2
dl 0
loc 20
ccs 13
cts 13
cp 1
crap 2
rs 9.4285
1
<?php
2
3
namespace garethp\ews\API;
4
5
use garethp\ews\API\Type\ExchangeImpersonation;
6
use SoapClient;
7
use SoapHeader;
8
use garethp\HttpPlayback\Factory;
9
10
/**
11
 * Contains NTLMSoapClient.
12
 */
13
14
/**
15
 * Soap Client using Microsoft's NTLM Authentication.
16
 *
17
 * Copyright (c) 2008 Invest-In-France Agency http://www.invest-in-france.org
18
 *
19
 * Author : Thomas Rabaix
20
 *
21
 * Permission to use, copy, modify, and distribute this software for any
22
 * purpose with or without fee is hereby granted, provided that the above
23
 * copyright notice and this permission notice appear in all copies.
24
 *
25
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
26
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
27
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
28
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
29
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
30
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
31
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
32
 *
33
 * @link http://rabaix.net/en/articles/2008/03/13/using-soap-php-with-ntlm-authentication
34
 * @author Thomas Rabaix
35
 *
36
 * @package php-ews\Auth
37
 *
38
 * @property array __default_headers
39
 */
40
class NTLMSoapClient extends SoapClient
41
{
42
    /**
43
     * Username for authentication on the exchnage server
44
     *
45
     * @var string
46
     */
47
    protected $user;
48
49
    /**
50
     * Password for authentication on the exchnage server
51
     *
52
     * @var string
53
     */
54
    protected $password;
55
56
    /**
57
     * Whether or not to validate ssl certificates
58
     *
59
     * @var boolean
60
     */
61
    protected $validate = false;
62
63
    private $httpClient;
64
65
    protected $__last_request_headers;
66
67
    protected $_responseCode;
68
69
    /**
70
     * An array of headers for us to store or use. Since not all requests use all headers (DeleteItem and SyncItems
71
     * don't allow you to pass a Timezone for example), we need to be able to smartly decide what headers to include
72
     * and exclude from a request. Until we have propper selection (an array of all known operations and what headers
73
     * are allowed for example), this seems like a decent solution for storing the headers before we decide if they
74
     * belong in the request or not)
75
     *
76
     * @var array
77
     */
78
    protected $ewsHeaders = array(
79
        'version' => null,
80
        'impersonation' => null,
81
        'timezone' => null
82
    );
83
84
    protected $auth;
85
86
    protected $callsWithoutTimezone = array(
87
        'DeleteItem',
88
        'SyncFolderItems',
89
        'GetServerTimeZones',
90
        'ConvertId'
91
    );
92
93
    /**
94
     * @TODO: Make this smarter. It should know and search what headers to remove on what actions
95
     *
96
     * @param string $name
97
     * @param string $args
98
     * @return mixed
99
     */
100 30
    public function __call($name, $args)
101
    {
102 30
        $this->__setSoapHeaders(null);
103
104
        $headers = array(
105 29
            $this->ewsHeaders['version'],
106 29
            $this->ewsHeaders['impersonation'],
107 29
        );
108
109 29
        if (!in_array($name, $this->callsWithoutTimezone)) {
110 29
            $headers[] = $this->ewsHeaders['timezone'];
111 29
        }
112
113 29
        $headers = array_filter($headers, function ($header) {
114 29
            return $header instanceof SoapHeader;
115 29
        });
116
117 29
        $this->__setSoapHeaders($headers);
118 29
        return parent::__call($name, $args);
119
    }
120
121
    /**
122
     * @param string $location
123
     * @param string $wsdl
124
     * @param array $options
125
     */
126 36
    public function __construct($location, $auth, $wsdl, $options = array())
127
    {
128 36
        $this->auth = $auth;
129
130 36
        $options = array_replace_recursive([
131
            'httpPlayback' => [
132
                'mode' => null
133 36
            ]
134 36
        ], $options);
135
136 36
        $options['location'] = $location;
137
138
        // If a version was set then add it to the headers.
139 36
        if (!empty($options['version'])) {
140 35
            $this->ewsHeaders['version'] = new SoapHeader(
141 35
                'http://schemas.microsoft.com/exchange/services/2006/types',
142 35
                'RequestServerVersion Version="'.$options['version'].'"'
143 35
            );
144 35
        }
145
146
        // If impersonation was set then add it to the headers.
147 36
        if (!empty($options['impersonation'])) {
148 1
            $impersonation = $options['impersonation'];
149 1
            if (is_string($impersonation)) {
150 1
                $impersonation = ExchangeImpersonation::fromEmailAddress($options['impersonation']);
151 1
            }
152
153 1
            $this->ewsHeaders['impersonation'] = new SoapHeader(
154 1
                'http://schemas.microsoft.com/exchange/services/2006/types',
155 1
                'ExchangeImpersonation',
156 1
                $impersonation->toXmlObject()
157 1
            );
158 1
        }
159
160 36
        if (!empty($options['timezone'])) {
161
            $this->ewsHeaders['timezone'] = new SoapHeader(
162
                'http://schemas.microsoft.com/exchange/services/2006/types',
163
                'TimeZoneContext',
164
                array(
165
                    'TimeZoneDefinition' => array(
166
                        'Id' => $options['timezone']
167
                    )
168
                )
169
            );
170
        }
171
172 36
        $this->httpClient = Factory::getInstance($options['httpPlayback']);
173
174 36
        parent::__construct($wsdl, $options);
175 36
    }
176
177
    /**
178
     * Performs a SOAP request
179
     *
180
     * @link http://php.net/manual/en/function.soap-soapclient-dorequest.php
181
     *
182
     * @param string $request the xml soap request
183
     * @param string $location the url to request
184
     * @param string $action the soap action.
185
     * @param integer $version the soap version
186
     * @param integer $one_way
187
     * @return string the xml soap response.
188
     */
189 29
    public function __doRequest($request, $location, $action, $version, $one_way = 0)
190
    {
191
        $postOptions = array(
192 29
            'body' => $request,
193
            'headers' => array(
194 29
                'Connection' => 'Keep-Alive',
195 29
                'User-Agent' => 'PHP-SOAP-CURL',
196 29
                'Content-Type' => 'text/xml; charset=utf-8',
197
                'SOAPAction' => $action
198 29
            ),
199 29
            'verify' => $this->validate,
200
            'http_errors' => false
201 29
        );
202
203 29
        $postOptions = array_replace_recursive($postOptions, $this->auth);
204
205 29
        $response = $this->httpClient->post($location, $postOptions);
206
207 29
        $this->__last_request_headers = $postOptions['headers'];
208 29
        $this->_responseCode = $response->getStatusCode();
209
210 29
        return $response->getBody()->__toString();
211
    }
212
213
    /**
214
     * Returns last SOAP request headers
215
     *
216
     * @link http://php.net/manual/en/function.soap-soapclient-getlastrequestheaders.php
217
     *
218
     * @return string the last soap request headers
219
     */
220
    public function __getLastRequestHeaders()
221
    {
222
        return implode('n', $this->__last_request_headers)."\n";
223
    }
224
225
    /**
226
     * Set validation certificate
227
     *
228
     * @param bool $validate
229
     * @return $this
230
     */
231 1
    public function validateCertificate($validate = true)
232
    {
233 1
        $this->validate = $validate;
234
235 1
        return $this;
236
    }
237
238
    /**
239
     * Returns the response code from the last request
240
     *
241
     * @return integer
242
     */
243 29
    public function getResponseCode()
244
    {
245 29
        return $this->_responseCode;
246
    }
247
}
248