Completed
Push — master ( e05426...26bd42 )
by Zhmayev
01:14
created

Session::sendHttpRequest()   C

Complexity

Conditions 8
Paths 22

Size

Total Lines 37
Code Lines 26

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 8
eloc 26
nc 22
nop 2
dl 0
loc 37
rs 5.3846
c 0
b 0
f 0
1
<?php
2
/**
3
* Vtiger Web Services PHP Client Library
4
*
5
* The MIT License (MIT)
6
*
7
* Copyright (c) 2015, Zhmayev Yaroslav <[email protected]>
8
*
9
* Permission is hereby granted, free of charge, to any person obtaining a copy
10
* of this software and associated documentation files (the "Software"), to deal
11
* in the Software without restriction, including without limitation the rights
12
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13
* copies of the Software, and to permit persons to whom the Software is
14
* furnished to do so, subject to the following conditions:
15
*
16
* The above copyright notice and this permission notice shall be included in
17
* all copies or substantial portions of the Software.
18
*
19
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25
* THE SOFTWARE.
26
*
27
* @author    Zhmayev Yaroslav <[email protected]>
28
* @copyright 2015-2016 Zhmayev Yaroslav
29
* @license   The MIT License (MIT)
30
*/
31
32
namespace Salaros\Vtiger\VTWSCLib;
33
34
use GuzzleHttp\Client;
35
use GuzzleHttp\Exception\RequestException;
36
37
/**
38
* Vtiger Web Services PHP Client Session class
39
*
40
* Class Session
41
* @package Salaros\Vtiger\VTWSCLib
42
* @internal
43
*/
44
class Session
45
{
46
    // HTTP Client instance
47
    protected $httpClient;
48
49
    // Service URL to which client connects to
50
    protected $vtigerUrl = null;
51
    protected $wsBaseURL = null;
52
53
    // Vtiger CRM and WebServices API version
54
    private $vtigerApiVersion = '0.0';
55
    private $vtigerVersion = '0.0';
56
    
57
    // Webservice login validity
58
    private $serviceExpireTime = null;
59
    private $serviceToken = null;
60
61
    // Webservice user credentials
62
    private $userName = null;
63
    private $accessKey = null;
64
65
    // Webservice login credentials
66
    private $userID = null;
67
    private $sessionName = null;
68
69
    /**
70
     * Class constructor
71
     * @param string $vtigerUrl  The URL of the remote WebServices server
72
     * @param string [$wsBaseURL = 'webservice.php']  WebServices base URL appended to vTiger root URL
73
     */
74
    public function __construct($vtigerUrl, $wsBaseURL = 'webservice.php')
75
    {
76
        $this->vtigerUrl = self::fixVtigerBaseUrl($vtigerUrl);
77
        $this->wsBaseURL = $wsBaseURL;
78
79
        // Gets target URL for WebServices API requests
80
        $this->httpClient = new Client([
81
            'base_uri' => $this->vtigerUrl
82
        ]);
83
    }
84
85
    /**
86
     * Login to the server using username and VTiger access key token
87
     * @access public
88
     * @param  string $username VTiger user name
89
     * @param  string $accessKey VTiger access key token (visible on user profile/settings page)
90
     * @return boolean Returns true if login operation has been successful
91
     */
92
    public function login($username, $accessKey)
93
    {
94
        // Do the challenge before loggin in
95
        if ($this->passChallenge($username) === false) {
96
            return false;
97
        }
98
99
        $postdata = [
100
            'operation' => 'login',
101
            'username'  => $username,
102
            'accessKey' => md5($this->serviceToken.$accessKey)
103
        ];
104
105
        $result = $this->sendHttpRequest($postdata);
106
        if (!is_array($result) || empty($result)) {
107
            return false;
108
        }
109
110
        // Backuping logged in user credentials
111
        $this->userName = $username;
112
        $this->accessKey = $accessKey;
113
114
        // Session data
115
        $this->sessionName = $result[ 'sessionName' ];
116
        $this->userID = $result[ 'userId' ];
117
118
        // Vtiger CRM and WebServices API version
119
        $this->vtigerApiVersion = $result[ 'version' ];
120
        $this->vtigerVersion = $result[ 'vtigerVersion' ];
121
122
        return true;
123
    }
124
125
    /**
126
     * Allows you to login using username and password instead of access key (works on some VTige forks)
127
     * @access public
128
     * @param  string $username VTiger user name
129
     * @param  string $password VTiger password (used to access CRM using the standard login page)
130
     * @param  string $accessKey This parameter will be filled with user's VTiger access key
131
     * @return boolean  Returns true if login operation has been successful
132
     */
133
    public function loginPassword($username, $password, &$accessKey = null)
134
    {
135
        // Do the challenge before loggin in
136
        if ($this->passChallenge($username) === false) {
137
            return false;
138
        }
139
140
        $postdata = [
141
            'operation' => 'login_pwd',
142
            'username' => $username,
143
            'password' => $password
144
        ];
145
146
        $result = $this->sendHttpRequest($postdata);
147
        if (!is_array($result) || empty($result)) {
148
            return false;
149
        }
150
151
        $this->accessKey = array_key_exists('accesskey', $result)
152
            ? $result[ 'accesskey' ]
153
            : $result[ 0 ];
154
155
        return $this->login($username, $accessKey);
156
    }
157
158
    /**
159
     * Gets a challenge token from the server and stores for future requests
160
     * @access private
161
     * @param  string $username VTiger user name
162
     * @return boolean Returns false in case of failure
163
     */
164
    private function passChallenge($username)
165
    {
166
        $getdata = [
167
            'operation' => 'getchallenge',
168
            'username'  => $username
169
        ];
170
        $result = $this->sendHttpRequest($getdata, 'GET');
171
        
172
        if (!is_array($result) || !isset($result[ 'token' ])) {
173
            return false;
174
        }
175
176
        $this->serviceExpireTime = $result[ 'expireTime' ];
177
        $this->serviceToken = $result[ 'token' ];
178
179
        return true;
180
    }
181
182
    /**
183
     * Gets an array containing the basic information about current API user
184
     * @access public
185
     * @return array Basic information about current API user
186
     */
187
    public function getUserInfo()
188
    {
189
        return [
190
            'id' => $this->userID,
191
            'userName' => $this->userName,
192
            'accessKey' => $this->accessKey,
193
        ];
194
    }
195
196
    /**
197
     * Gets vTiger version, retrieved on successful login
198
     * @access public
199
     * @return string vTiger version, retrieved on successful login
200
     */
201
    public function getVtigerVersion()
202
    {
203
        return $this->vtigerVersion;
204
    }
205
206
    /**
207
     * Gets vTiger WebServices API version, retrieved on successful login
208
     * @access public
209
     * @return string vTiger WebServices API version, retrieved on successful login
210
     */
211
    public function getVtigerApiVersion()
212
    {
213
        return $this->vtigerApiVersion;
214
    }
215
216
    /**
217
     * Sends HTTP request to VTiger web service API endpoint
218
     * @access private
219
     * @param  array $requestData HTTP request data
220
     * @param  string $method HTTP request method (GET, POST etc)
221
     * @return array Returns request result object (null in case of failure)
222
     */
223
    public function sendHttpRequest(array $requestData, $method = 'POST')
224
    {
225
        // Perform re-login if required.
226
        if ('getchallenge' !== $requestData[ 'operation' ] && time() > $this->serviceExpireTime) {
227
            $this->login($this->userName, $this->accessKey);
228
        }
229
230
        $requestData[ 'sessionName' ] = $this->sessionName;
231
        
232
        try {
233
            switch ($method) {
234
                case 'GET':
235
                    $response = $this->httpClient->get($this->wsBaseURL, [ 'query' => $requestData ]);
236
                    break;
237
                case 'POST':
238
                    $response = $this->httpClient->post($this->wsBaseURL, [ 'form_params' => $requestData ]);
239
                    break;
240
                default:
241
                    throw new WSException("Unsupported request type {$method}");
242
            }
243
        } catch (RequestException $ex) {
244
            $urlFailed = $this->httpClient->getConfig('base_uri').$this->wsBaseURL;
245
            throw new WSException(
246
                sprintf('Failed to execute %s call on "%s" URL', $method, $urlFailed),
247
                'FAILED_SENDING_REQUEST',
248
                $ex
249
            );
250
        }
251
252
        $jsonRaw = $response->getBody();
253
        $jsonObj = json_decode($jsonRaw, true);
254
255
        $result = (is_array($jsonObj) && !self::checkForError($jsonObj))
256
            ? $jsonObj[ 'result' ]
257
            : null;
258
        return $result;
259
    }
260
261
    /**
262
     *  Cleans and fixes vTiger URL
263
     * @access private
264
     * @static
265
     * @param  string  Base URL of vTiger CRM
266
     * @param string $baseUrl
267
     * @return string Returns cleaned and fixed vTiger URL
268
     */
269
    private static function fixVtigerBaseUrl($baseUrl)
270
    {
271
        if (!preg_match('/^https?:\/\//i', $baseUrl)) {
272
            $baseUrl = sprintf('http://%s', $baseUrl);
273
        }
274
        if (strripos($baseUrl, '/') !== strlen($baseUrl) - 1) {
275
            $baseUrl .= '/';
276
        }
277
        return $baseUrl;
278
    }
279
280
    /**
281
     * Check if server response contains an error, therefore the requested operation has failed
282
     * @access private
283
     * @static
284
     * @param  array $jsonResult Server response object to check for errors
285
     * @return boolean  True if response object contains an error
286
     */
287
    private static function checkForError(array $jsonResult)
288
    {
289
        if (isset($jsonResult[ 'success' ]) && true === (bool) $jsonResult[ 'success' ]) {
290
            return false;
291
        }
292
293
        if (isset($jsonResult[ 'error' ])) {
294
            $error = $jsonResult[ 'error' ];
295
            throw new WSException(
296
                $error[ 'message' ],
297
                $error[ 'code' ]
298
            );
299
        }
300
301
        // This should never happen
302
        throw new WSException('Unknown error');
303
    }
304
}
305