Passed
Pull Request — develop-3.2.x (#70)
by Mario
02:25
created

ipn_paypal   A

Complexity

Total Complexity 30

Size/Duplication

Total Lines 346
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 88
dl 0
loc 346
rs 10
c 0
b 0
f 0
wmc 30

16 Methods

Rating   Name   Duplication   Size   Complexity  
A set_u_paypal() 0 3 1
A is_curl_strcmp() 0 3 2
A check_tls() 0 18 4
A get_remote_used() 0 3 1
A check_response_status() 0 3 1
A set_curl_info() 0 7 2
A __construct() 0 13 1
A get_report_response() 0 3 1
A get_remote_uri() 0 3 1
A set_remote_detected() 0 3 1
A check_curl_info() 0 8 2
A is_remote_detected() 0 11 2
A check_curl() 0 19 5
A curl_post() 0 40 3
A initiate_paypal_connection() 0 9 2
A get_response_status() 0 3 1
1
<?php
2
/**
3
 *
4
 * PayPal Donation extension for the phpBB Forum Software package.
5
 *
6
 * @copyright (c) 2015 Skouat
7
 * @license GNU General Public License, version 2 (GPL-2.0)
8
 *
9
 * Special Thanks to the following individuals for their inspiration:
10
 *    David Lewis (Highway of Life) http://startrekguide.com
11
 *    Micah Carrick ([email protected]) http://www.micahcarrick.com
12
 */
13
14
namespace skouat\ppde\controller;
15
16
use phpbb\config\config;
17
use phpbb\language\language;
18
use phpbb\request\request;
19
20
class ipn_paypal
21
{
22
	/** Production and Sandbox Postback URL
23
	 *
24
	 * @var array
25
	 */
26
	private static $remote_uri = array(
27
		array('hostname' => 'www.paypal.com', 'uri' => 'https://www.paypal.com/cgi-bin/webscr', 'type' => 'live'),
28
		array('hostname' => 'www.sandbox.paypal.com', 'uri' => 'https://www.sandbox.paypal.com/cgi-bin/webscr', 'type' => 'sandbox'),
29
		array('hostname' => 'ipnpb.paypal.com', 'uri' => 'https://ipnpb.paypal.com/cgi-bin/webscr', 'type' => 'live'),
30
		array('hostname' => 'ipnpb.sandbox.paypal.com', 'uri' => 'https://ipnpb.sandbox.paypal.com/cgi-bin/webscr', 'type' => 'sandbox'),
31
	);
32
33
	protected $config;
34
	protected $language;
35
	protected $ppde_ext_manager;
36
	protected $ppde_ipn_log;
37
	protected $request;
38
	/**
39
	 * @var array
40
	 */
41
	private $curl_fsock = array('curl' => false,
42
								'none' => true);
43
	/**
44
	 * Full PayPal response for include in text report
45
	 *
46
	 * @var string
47
	 */
48
	private $report_response = '';
49
	/**
50
	 * PayPal response (VERIFIED or INVALID)
51
	 *
52
	 * @var string
53
	 */
54
	private $response = '';
55
	/**
56
	 * PayPal response status (code 200 or other)
57
	 *
58
	 * @var string
59
	 */
60
	private $response_status = '';
61
	/**
62
	 * PayPal URL
63
	 * Could be Sandbox URL ou normal PayPal URL.
64
	 *
65
	 * @var string
66
	 */
67
	private $u_paypal = '';
68
69
	/**
70
	 * Constructor
71
	 *
72
	 * @param config            $config           Config object
73
	 * @param language          $language         Language user object
74
	 * @param extension_manager $ppde_ext_manager Extension manager object
75
	 * @param ipn_log           $ppde_ipn_log     IPN log
76
	 * @param request           $request          Request object
77
	 *
78
	 * @access public
79
	 */
80
	public function __construct(
81
		config $config,
82
		language $language,
83
		extension_manager $ppde_ext_manager,
84
		ipn_log $ppde_ipn_log,
85
		request $request
86
	)
87
	{
88
		$this->config = $config;
89
		$this->language = $language;
90
		$this->ppde_ext_manager = $ppde_ext_manager;
91
		$this->ppde_ipn_log = $ppde_ipn_log;
92
		$this->request = $request;
93
	}
94
95
	/**
96
	 * @return array
97
	 */
98
	public static function get_remote_uri()
99
	{
100
		return self::$remote_uri;
101
	}
102
103
	/**
104
	 * Initiate communication with PayPal.
105
	 * We use cURL. If it is not available we log an error.
106
	 *
107
	 * @param string $args_return_uri
108
	 * @param array  $data
109
	 *
110
	 * @return void
111
	 * @access public
112
	 */
113
	public function initiate_paypal_connection($args_return_uri, $data)
114
	{
115
		if ($this->curl_fsock['curl'])
116
		{
117
			$this->curl_post($args_return_uri);
118
		}
119
		else
120
		{
121
			$this->ppde_ipn_log->log_error($this->language->lang('NO_CONNECTION_DETECTED'), $this->ppde_ipn_log->is_use_log_error(), true, E_USER_ERROR, $data);
122
		}
123
	}
124
125
	/**
126
	 * Post Back Using cURL
127
	 *
128
	 * Sends the post back to PayPal using the cURL library. Called by
129
	 * the validate_transaction() method if the curl_fsock['curl'] property is true.
130
	 * Throws an exception if the post fails. Populates the response and response_status properties on success.
131
	 *
132
	 * @param string $encoded_data The post data as a URL encoded string
133
	 *
134
	 * @return void
135
	 * @access private
136
	 */
137
	private function curl_post($encoded_data)
138
	{
139
		$ch = curl_init($this->u_paypal);
140
		curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
0 ignored issues
show
Bug introduced by
It seems like $ch can also be of type false; however, parameter $ch of curl_setopt() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

140
		curl_setopt(/** @scrutinizer ignore-type */ $ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
Loading history...
141
		curl_setopt($ch, CURLOPT_POST, true);
142
		curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
143
		curl_setopt($ch, CURLOPT_POSTFIELDS, $encoded_data);
144
		curl_setopt($ch, CURLOPT_SSLVERSION, 6);
145
		curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 1);
146
		curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
147
		curl_setopt($ch, CURLOPT_FORBID_REUSE, true);
148
		curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 30);
149
		curl_setopt($ch, CURLOPT_HTTPHEADER, array(
150
			'User-Agent: PHP-IPN-Verification-Script',
151
			'Connection: Close',
152
		));
153
154
		if ($this->ppde_ipn_log->is_use_log_error())
155
		{
156
			curl_setopt($ch, CURLOPT_HEADER, true);
157
			curl_setopt($ch, CURLINFO_HEADER_OUT, true);
158
		}
159
160
		$this->report_response = $this->response = curl_exec($ch);
0 ignored issues
show
Bug introduced by
It seems like $ch can also be of type false; however, parameter $ch of curl_exec() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

160
		$this->report_response = $this->response = curl_exec(/** @scrutinizer ignore-type */ $ch);
Loading history...
Documentation Bug introduced by
It seems like $this->response = curl_exec($ch) can also be of type boolean. However, the property $report_response is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
Documentation Bug introduced by
It seems like curl_exec($ch) can also be of type boolean. However, the property $response is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
161
		if (curl_errno($ch) != 0)
0 ignored issues
show
Bug introduced by
It seems like $ch can also be of type false; however, parameter $ch of curl_errno() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

161
		if (curl_errno(/** @scrutinizer ignore-type */ $ch) != 0)
Loading history...
162
		{
163
			// cURL error
164
			$this->ppde_ipn_log->log_error($this->language->lang('CURL_ERROR', curl_errno($ch) . ' (' . curl_error($ch) . ')'), $this->ppde_ipn_log->is_use_log_error());
0 ignored issues
show
Bug introduced by
It seems like $ch can also be of type false; however, parameter $ch of curl_error() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

164
			$this->ppde_ipn_log->log_error($this->language->lang('CURL_ERROR', curl_errno($ch) . ' (' . curl_error(/** @scrutinizer ignore-type */ $ch) . ')'), $this->ppde_ipn_log->is_use_log_error());
Loading history...
165
			curl_close($ch);
0 ignored issues
show
Bug introduced by
It seems like $ch can also be of type false; however, parameter $ch of curl_close() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

165
			curl_close(/** @scrutinizer ignore-type */ $ch);
Loading history...
166
		}
167
		else
168
		{
169
			$info = curl_getinfo($ch);
0 ignored issues
show
Bug introduced by
It seems like $ch can also be of type false; however, parameter $ch of curl_getinfo() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

169
			$info = curl_getinfo(/** @scrutinizer ignore-type */ $ch);
Loading history...
170
			$this->response_status = $info['http_code'];
171
			curl_close($ch);
172
		}
173
174
		// Split response headers and payload, a better way for strcmp
175
		$tokens = explode("\r\n\r\n", trim($this->response));
176
		$this->response = trim(end($tokens));
177
	}
178
179
	/**
180
	 * Set property 'curl_fsock' to use cURL based on config settings.
181
	 * If cURL is not available we use default value of the property 'curl_fsock'.
182
	 *
183
	 * @return bool
184
	 * @access public
185
	 */
186
	public function is_remote_detected()
187
	{
188
		if ($this->config['ppde_curl_detected'])
189
		{
190
			$this->curl_fsock = array(
191
				'curl' => true,
192
				'none' => false,
193
			);
194
		}
195
196
		return array_search(true, $this->curl_fsock);
197
	}
198
199
	/**
200
	 * Set the property '$u_paypal'
201
	 *
202
	 * @param string $u_paypal
203
	 *
204
	 * @return void
205
	 * @access public
206
	 */
207
	public function set_u_paypal($u_paypal)
208
	{
209
		$this->u_paypal = (string) $u_paypal;
210
	}
211
212
	/**
213
	 * Get the service that will be used to contact PayPal
214
	 * Returns the name of the key that is set to true.
215
	 *
216
	 * @return string
217
	 * @access public
218
	 */
219
	public function get_remote_used()
220
	{
221
		return array_search(true, $this->curl_fsock);
222
	}
223
224
	/**
225
	 * Full PayPal response for include in text report
226
	 *
227
	 * @return string
228
	 * @access public
229
	 */
230
	public function get_report_response()
231
	{
232
		return $this->report_response;
233
	}
234
235
	/**
236
	 * PayPal response status
237
	 *
238
	 * @return string
239
	 * @access public
240
	 */
241
	public function get_response_status()
242
	{
243
		return $this->response_status;
244
	}
245
246
	/**
247
	 * Check if the response status is equal to "200".
248
	 *
249
	 * @return bool
250
	 * @access public
251
	 */
252
	public function check_response_status()
253
	{
254
		return $this->response_status != 200;
255
	}
256
257
	/**
258
	 * If cURL is available we use strcmp() to get the Pay
259
	 *
260
	 * @param string $arg
261
	 *
262
	 * @return bool
263
	 * @access public
264
	 */
265
	public function is_curl_strcmp($arg)
266
	{
267
		return $this->curl_fsock['curl'] && (strcmp($this->response, $arg) === 0);
268
	}
269
270
	/**
271
	 * Check if PayPal connection use TLS 1.2 and HTTP 1.1
272
	 *
273
	 * @return void
274
	 * @access public
275
	 */
276
	public function check_tls()
277
	{
278
		// Reset settings to false
279
		$this->config->set('ppde_tls_detected', false);
0 ignored issues
show
Bug introduced by
false of type false is incompatible with the type string expected by parameter $value of phpbb\config\config::set(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

279
		$this->config->set('ppde_tls_detected', /** @scrutinizer ignore-type */ false);
Loading history...
280
281
		if (function_exists('curl_init') && function_exists('curl_exec'))
282
		{
283
			$ch = curl_init('https://tlstest.paypal.com/');
284
285
			curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
1 ignored issue
show
Bug introduced by
It seems like $ch can also be of type false; however, parameter $ch of curl_setopt() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

285
			curl_setopt(/** @scrutinizer ignore-type */ $ch, CURLOPT_RETURNTRANSFER, true);
Loading history...
286
287
			$response = curl_exec($ch);
1 ignored issue
show
Bug introduced by
It seems like $ch can also be of type false; however, parameter $ch of curl_exec() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

287
			$response = curl_exec(/** @scrutinizer ignore-type */ $ch);
Loading history...
288
289
			curl_close($ch);
1 ignored issue
show
Bug introduced by
It seems like $ch can also be of type false; however, parameter $ch of curl_close() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

289
			curl_close(/** @scrutinizer ignore-type */ $ch);
Loading history...
290
291
			if ($response === 'PayPal_Connection_OK')
292
			{
293
				$this->config->set('ppde_tls_detected', true);
0 ignored issues
show
Bug introduced by
true of type true is incompatible with the type string expected by parameter $value of phpbb\config\config::set(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

293
				$this->config->set('ppde_tls_detected', /** @scrutinizer ignore-type */ true);
Loading history...
294
			}
295
		}
296
	}
297
298
	/**
299
	 * Set config value for cURL
300
	 *
301
	 * @return void
302
	 * @access public
303
	 */
304
	public function set_remote_detected()
305
	{
306
		$this->config->set('ppde_curl_detected', $this->check_curl());
307
	}
308
309
	/**
310
	 * Check if cURL is available
311
	 *
312
	 * @return bool
313
	 * @access public
314
	 */
315
	public function check_curl()
316
	{
317
		if (function_exists('curl_init') && function_exists('curl_exec'))
318
		{
319
			$ext_meta = $this->ppde_ext_manager->get_ext_meta();
320
321
			$ch = curl_init($ext_meta['extra']['version-check']['host']);
322
323
			curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
0 ignored issues
show
Bug introduced by
It seems like $ch can also be of type false; however, parameter $ch of curl_setopt() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

323
			curl_setopt(/** @scrutinizer ignore-type */ $ch, CURLOPT_RETURNTRANSFER, true);
Loading history...
324
325
			$this->response = curl_exec($ch);
0 ignored issues
show
Bug introduced by
It seems like $ch can also be of type false; however, parameter $ch of curl_exec() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

325
			$this->response = curl_exec(/** @scrutinizer ignore-type */ $ch);
Loading history...
Documentation Bug introduced by
It seems like curl_exec($ch) can also be of type boolean. However, the property $response is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
326
			$this->response_status = strval(curl_getinfo($ch, CURLINFO_HTTP_CODE));
0 ignored issues
show
Bug introduced by
It seems like $ch can also be of type false; however, parameter $ch of curl_getinfo() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

326
			$this->response_status = strval(curl_getinfo(/** @scrutinizer ignore-type */ $ch, CURLINFO_HTTP_CODE));
Loading history...
327
328
			curl_close($ch);
0 ignored issues
show
Bug introduced by
It seems like $ch can also be of type false; however, parameter $ch of curl_close() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

328
			curl_close(/** @scrutinizer ignore-type */ $ch);
Loading history...
329
330
			return ($this->response !== false || $this->response_status !== '0') ? true : false;
331
		}
332
333
		return false;
334
	}
335
336
	/**
337
	 * Set config value for cURL version
338
	 *
339
	 * @return void
340
	 * @access public
341
	 */
342
	public function set_curl_info()
343
	{
344
		// Get cURL version informations
345
		if ($curl_info = $this->check_curl_info())
346
		{
347
			$this->config->set('ppde_curl_version', $curl_info['version']);
348
			$this->config->set('ppde_curl_ssl_version', $curl_info['ssl_version']);
349
		}
350
	}
351
352
	/**
353
	 * Get cURL version if available
354
	 *
355
	 * @return array|bool
356
	 * @access public
357
	 */
358
	public function check_curl_info()
359
	{
360
		if (function_exists('curl_version'))
361
		{
362
			return curl_version();
363
		}
364
365
		return false;
366
	}
367
}
368