Session::__construct()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 3
dl 0
loc 11
rs 9.9
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
    // request timeout in seconds
50
    protected $requestTimeout;
51
52
    // Service URL to which client connects to
53
    protected $vtigerUrl = null;
54
    protected $wsBaseURL = null;
55
56
    // Vtiger CRM and WebServices API version
57
    private $vtigerApiVersion = '0.0';
58
    private $vtigerVersion = '0.0';
59
    
60
    // Webservice login validity
61
    private $serviceExpireTime = null;
62
    private $serviceToken = null;
63
64
    // Webservice user credentials
65
    private $userName = null;
66
    private $accessKey = null;
67
68
    // Webservice login credentials
69
    private $userID = null;
70
    private $sessionName = null;
71
72
    /**
73
     * Class constructor
74
     * @param string $vtigerUrl  The URL of the remote WebServices server
75
     * @param string [$wsBaseURL = 'webservice.php']  WebServices base URL appended to vTiger root URL
76
     * @param int $requestTimeout Number of seconds after which request times out
77
     */
78
    public function __construct($vtigerUrl, $wsBaseURL = 'webservice.php', $requestTimeout = 0)
79
    {
80
        $this->vtigerUrl = self::fixVtigerBaseUrl($vtigerUrl);
81
        $this->wsBaseURL = $wsBaseURL;
82
        $this->requestTimeout = $requestTimeout;
83
84
        // Gets target URL for WebServices API requests
85
        $this->httpClient = new Client([
86
            'base_uri' => $this->vtigerUrl
87
        ]);
88
    }
89
90
    /**
91
     * Login to the server using username and VTiger access key token
92
     * @access public
93
     * @param  string $username VTiger user name
94
     * @param  string $accessKey VTiger access key token (visible on user profile/settings page)
95
     * @return boolean Returns true if login operation has been successful
96
     */
97
    public function login($username, $accessKey)
98
    {
99
        // Do the challenge before loggin in
100
        if ($this->passChallenge($username) === false) {
101
            return false;
102
        }
103
104
        $postdata = [
105
            'operation' => 'login',
106
            'username'  => $username,
107
            'accessKey' => md5($this->serviceToken.$accessKey)
108
        ];
109
110
        $result = $this->sendHttpRequest($postdata);
111
        if (!is_array($result) || empty($result)) {
112
            return false;
113
        }
114
115
        // Backuping logged in user credentials
116
        $this->userName = $username;
117
        $this->accessKey = $accessKey;
118
119
        // Session data
120
        $this->sessionName = $result[ 'sessionName' ];
121
        $this->userID = $result[ 'userId' ];
122
123
        // Vtiger CRM and WebServices API version
124
        $this->vtigerApiVersion = $result[ 'version' ];
125
        $this->vtigerVersion = $result[ 'vtigerVersion' ];
126
127
        return true;
128
    }
129
130
    /**
131
     * Allows you to login using username and password instead of access key (works on some VTige forks)
132
     * @access public
133
     * @param  string $username VTiger user name
134
     * @param  string $password VTiger password (used to access CRM using the standard login page)
135
     * @param  string $accessKey This parameter will be filled with user's VTiger access key
136
     * @return boolean  Returns true if login operation has been successful
137
     */
138
    public function loginPassword($username, $password, &$accessKey = null)
139
    {
140
        // Do the challenge before loggin in
141
        if ($this->passChallenge($username) === false) {
142
            return false;
143
        }
144
145
        $postdata = [
146
            'operation' => 'login_pwd',
147
            'username' => $username,
148
            'password' => $password
149
        ];
150
151
        $result = $this->sendHttpRequest($postdata);
152
        if (!is_array($result) || empty($result)) {
153
            return false;
154
        }
155
156
        $this->accessKey = array_key_exists('accesskey', $result)
157
            ? $result[ 'accesskey' ]
158
            : $result[ 0 ];
159
160
        return $this->login($username, $accessKey);
161
    }
162
163
    /**
164
     * Gets a challenge token from the server and stores for future requests
165
     * @access private
166
     * @param  string $username VTiger user name
167
     * @return boolean Returns false in case of failure
168
     */
169
    private function passChallenge($username)
170
    {
171
        $getdata = [
172
            'operation' => 'getchallenge',
173
            'username'  => $username
174
        ];
175
        $result = $this->sendHttpRequest($getdata, 'GET');
176
        
177
        if (!is_array($result) || !isset($result[ 'token' ])) {
178
            return false;
179
        }
180
181
        $this->serviceExpireTime = $result[ 'expireTime' ];
182
        $this->serviceToken = $result[ 'token' ];
183
184
        return true;
185
    }
186
187
    /**
188
     * Gets an array containing the basic information about current API user
189
     * @access public
190
     * @return array Basic information about current API user
191
     */
192
    public function getUserInfo()
193
    {
194
        return [
195
            'id' => $this->userID,
196
            'userName' => $this->userName,
197
            'accessKey' => $this->accessKey,
198
        ];
199
    }
200
201
    /**
202
     * Gets vTiger version, retrieved on successful login
203
     * @access public
204
     * @return string vTiger version, retrieved on successful login
205
     */
206
    public function getVtigerVersion()
207
    {
208
        return $this->vtigerVersion;
209
    }
210
211
    /**
212
     * Gets vTiger WebServices API version, retrieved on successful login
213
     * @access public
214
     * @return string vTiger WebServices API version, retrieved on successful login
215
     */
216
    public function getVtigerApiVersion()
217
    {
218
        return $this->vtigerApiVersion;
219
    }
220
221
    /**
222
     * Sends HTTP request to VTiger web service API endpoint
223
     * @access private
224
     * @param  array $requestData HTTP request data
225
     * @param  string $method HTTP request method (GET, POST etc)
226
     * @return array Returns request result object (null in case of failure)
227
     */
228
    public function sendHttpRequest(array $requestData, $method = 'POST')
229
    {
230
        // Perform re-login if required.
231
        if ('getchallenge' !== $requestData[ 'operation' ] && time() > $this->serviceExpireTime) {
232
            $this->login($this->userName, $this->accessKey);
233
        }
234
235
        $requestData[ 'sessionName' ] = $this->sessionName;
236
        
237
        try {
238
            switch ($method) {
239
                case 'GET':
240
                    $response = $this->httpClient->get($this->wsBaseURL, [ 'query' => $requestData, 'timeout' => $this->requestTimeout ]);
241
                    break;
242
                case 'POST':
243
                    $response = $this->httpClient->post($this->wsBaseURL, [ 'form_params' => $requestData, 'timeout' => $this->requestTimeout ]);
244
                    break;
245
                default:
246
                    throw new WSException("Unsupported request type {$method}");
247
            }
248
        } catch (RequestException $ex) {
249
            $urlFailed = $this->httpClient->getConfig('base_uri').$this->wsBaseURL;
250
            throw new WSException(
251
                sprintf('Failed to execute %s call on "%s" URL', $method, $urlFailed),
252
                'FAILED_SENDING_REQUEST',
253
                $ex
254
            );
255
        }
256
257
        $jsonRaw = $response->getBody();
258
        $jsonObj = json_decode($jsonRaw, true);
259
260
        $result = (is_array($jsonObj) && !self::checkForError($jsonObj))
261
            ? $jsonObj[ 'result' ]
262
            : null;
263
        return $result;
264
    }
265
266
    /**
267
     *  Cleans and fixes vTiger URL
268
     * @access private
269
     * @static
270
     * @param  string  Base URL of vTiger CRM
271
     * @param string $baseUrl
272
     * @return string Returns cleaned and fixed vTiger URL
273
     */
274
    private static function fixVtigerBaseUrl($baseUrl)
275
    {
276
        if (!preg_match('/^https?:\/\//i', $baseUrl)) {
277
            $baseUrl = sprintf('http://%s', $baseUrl);
278
        }
279
        if (strripos($baseUrl, '/') !== strlen($baseUrl) - 1) {
280
            $baseUrl .= '/';
281
        }
282
        return $baseUrl;
283
    }
284
285
    /**
286
     * Check if server response contains an error, therefore the requested operation has failed
287
     * @access private
288
     * @static
289
     * @param  array $jsonResult Server response object to check for errors
290
     * @return boolean  True if response object contains an error
291
     */
292
    private static function checkForError(array $jsonResult)
293
    {
294
        if (isset($jsonResult[ 'success' ]) && true === (bool) $jsonResult[ 'success' ]) {
295
            return false;
296
        }
297
298
        if (isset($jsonResult[ 'error' ])) {
299
            $error = $jsonResult[ 'error' ];
300
            throw new WSException(
301
                $error[ 'message' ],
302
                $error[ 'code' ]
303
            );
304
        }
305
306
        // This should never happen
307
        throw new WSException('Unknown error');
308
    }
309
}
310