Test Setup Failed
Push — master ( 546b5d...f4e9ab )
by Ravinder
07:43
created

Give_Email_Access::setup()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
nc 2
nop 0
dl 0
loc 6
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * Email Access
4
 *
5
 * @package     Give
6
 * @subpackage  Classes/Give_Email_Access
7
 * @copyright   Copyright (c) 2016, WordImpress
8
 * @license     https://opensource.org/licenses/gpl-license GNU Public License
9
 * @since       1.4
10
 */
11
12
// Exit if accessed directly.
13
if ( ! defined( 'ABSPATH' ) ) {
14
	exit;
15
}
16
17
/**
18
 * Give_Email_Access class
19
 *
20
 * This class handles email access, allowing donors access to their donation w/o logging in;
21
 *
22
 * Based on the work from Matt Gibbs - https://github.com/FacetWP/edd-no-logins
23
 *
24
 * @since 1.0
25
 */
26
class Give_Email_Access {
27
28
	/**
29
	 * Token exists
30
	 *
31
	 * @since  1.0
32
	 * @access public
33
	 *
34
	 * @var    bool
35
	 */
36
	public $token_exists = false;
37
38
	/**
39
	 * Token email
40
	 *
41
	 * @since  1.0
42
	 * @access public
43
	 *
44
	 * @var    bool
45
	 */
46
	public $token_email = false;
47
48
	/**
49
	 * Token
50
	 *
51
	 * @since  1.0
52
	 * @access public
53
	 *
54
	 * @var    bool
55
	 */
56
	public $token = false;
57
58
	/**
59
	 * Error
60
	 *
61
	 * @since  1.0
62
	 * @access public
63
	 *
64
	 * @var    string
65
	 */
66
	public $error = '';
67
68
	/**
69
	 * Verify throttle
70
	 *
71
	 * @since  1.0
72
	 * @access public
73
	 *
74
	 * @var
75
	 */
76
	public $verify_throttle;
77
78
	/**
79
	 * Limit throttle
80
	 *
81
	 * @since  1.8.17
82
	 * @access public
83
	 *
84
	 * @var
85
	 */
86
	public $limit_throttle;
87
88
	/**
89
	 * Verify expiration
90
	 *
91
	 * @since  1.0
92
	 * @access private
93
	 *
94
	 * @var    string
95
	 */
96
	private $token_expiration;
97
98
	/**
99
	 * Class Constructor
100
	 *
101
	 * Set up the Give Email Access Class.
102
	 *
103
	 * @since  1.0
104
	 * @access public
105
	 */
106
	public function __construct() {
107
108
		// Get it started.
109
		add_action( 'wp', array( $this, 'setup' ) );
110
	}
111
112
	/**
113
	 * Setup hooks
114
	 *
115
	 * @since 2.4.0
116
	 */
117
	public function setup(){
118
		if( give_is_success_page() || give_is_history_page() ){
0 ignored issues
show
introduced by
Space after opening control structure is required
Loading history...
introduced by
No space before opening parenthesis is prohibited
Loading history...
119
			// Get it started.
120
			add_action( 'wp', array( $this, 'init' ), 14 );
121
		}
122
	}
123
124
	/**
125
	 * Init
126
	 *
127
	 * Register defaults and filters
128
	 *
129
	 * @since  1.0
130
	 * @access public
131
	 *
132
	 * @return void
133
	 */
134
	public function init() {
135
136
		// Bail Out, if user is logged in.
137
		if ( is_user_logged_in() ) {
138
			return;
139
		}
140
141
		// Are db columns setup?
142
		$column_exists = Give()->donors->does_column_exist( 'token' );
143
		if ( ! $column_exists ) {
144
			$this->create_columns();
145
		}
146
147
		// Timeouts.
148
		$this->verify_throttle  = apply_filters( 'give_nl_verify_throttle', 300 );
149
		$this->limit_throttle   = apply_filters( 'give_nl_limit_throttle', 3 );
150
		$this->token_expiration = apply_filters( 'give_nl_token_expiration', 7200 );
151
152
		// Setup login.
153
		$this->check_for_token();
154
155
		if ( $this->token_exists ) {
156
			add_filter( 'give_user_pending_verification', '__return_false' );
157
			add_filter( 'give_get_users_donations_args', array( $this, 'users_donations_args' ) );
158
		}
159
160
	}
161
162
	/**
163
	 * Prevent email spamming.
164
	 *
165
	 * @param int $donor_id Donor ID.
166
	 *
167
	 * @since  1.0
168
	 * @access public
169
	 *
170
	 * @return bool
171
	 */
172
	public function can_send_email( $donor_id ) {
173
174
		$donor = Give()->donors->get_donor_by( 'id', $donor_id );
175
176
		if ( is_object( $donor ) ) {
177
178
			$email_throttle_count = (int) give_get_meta( $donor_id, '_give_email_throttle_count', true );
179
180
			$cache_key = "give_cache_email_throttle_limit_exhausted_{$donor_id}";
181
			if (
182
				$email_throttle_count < $this->limit_throttle &&
183
				true !== Give_Cache::get( $cache_key )
184
			) {
185
				give_update_meta( $donor_id, '_give_email_throttle_count', $email_throttle_count + 1 );
186
			} else {
187
				give_update_meta( $donor_id, '_give_email_throttle_count', 0 );
188
				Give_Cache::set( $cache_key, true, $this->verify_throttle );
189
				return false;
190
			}
0 ignored issues
show
introduced by
Blank line found after control structure
Loading history...
191
192
		}
193
194
		return true;
195
	}
196
197
	/**
198
	 * Send the user's token
199
	 *
200
	 * @param int    $donor_id Donor id.
201
	 * @param string $email    Donor email.
202
	 *
203
	 * @since  1.0
204
	 * @access public
205
	 *
206
	 * @return bool
207
	 */
208
	public function send_email( $donor_id, $email ) {
209
		return apply_filters( 'give_email-access_email_notification', $donor_id, $email );
210
	}
211
212
	/**
213
	 * Has the user authenticated?
214
	 *
215
	 * @since  1.0
216
	 * @access public
217
	 *
218
	 * @return bool
219
	 */
220
	public function check_for_token() {
221
222
		$token = isset( $_GET['give_nl'] ) ? give_clean( $_GET['give_nl'] ) : '';
0 ignored issues
show
introduced by
Detected access of super global var $_GET, probably need manual inspection.
Loading history...
introduced by
Detected usage of a non-sanitized input variable: $_GET
Loading history...
223
224
		// Check for cookie.
225
		if ( empty( $token ) ) {
226
			$token = isset( $_COOKIE['give_nl'] ) ? give_clean( $_COOKIE['give_nl'] ) : '';
0 ignored issues
show
introduced by
Due to using Batcache, server side based client related logic will not work, use JS instead.
Loading history...
227
		}
228
229
		// Must have a token.
230
		if ( ! empty( $token ) ) {
231
232
			if ( ! $this->is_valid_token( $token ) ) {
233
				if ( ! $this->is_valid_verify_key( $token ) ) {
234
					return false;
235
				}
236
			}
237
238
			// Set Receipt Access Session.
239
			Give()->session->set( 'receipt_access', true );
240
			$this->token_exists = true;
241
			// Set cookie.
242
			$lifetime = current_time( 'timestamp' ) + Give()->session->set_expiration_time();
243
			@setcookie( 'give_nl', $token, $lifetime, COOKIEPATH, COOKIE_DOMAIN, false );
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
Coding Style introduced by
Silencing errors is discouraged
Loading history...
introduced by
Due to using Batcache, server side based client related logic will not work, use JS instead.
Loading history...
244
245
			return true;
246
		}
247
	}
248
249
	/**
250
	 * Is this a valid token?
251
	 *
252
	 * @since  1.0
253
	 * @access public
254
	 *
255
	 * @param  $token string The token.
256
	 *
257
	 * @return bool
258
	 */
259
	public function is_valid_token( $token ) {
260
261
		global $wpdb;
262
263
		// Make sure token isn't expired.
264
		$expires = date( 'Y-m-d H:i:s', time() - $this->token_expiration );
265
266
		$email = $wpdb->get_var(
0 ignored issues
show
introduced by
Usage of a direct database call is discouraged.
Loading history...
introduced by
Usage of a direct database call without caching is prohibited. Use wp_cache_get / wp_cache_set.
Loading history...
267
			$wpdb->prepare( "SELECT email FROM {$wpdb->donors} WHERE verify_key = %s AND verify_throttle >= %s LIMIT 1", $token, $expires )
268
		);
269
270
		if ( ! empty( $email ) ) {
271
			$this->token_email = $email;
272
			$this->token       = $token;
273
			return true;
274
		}
275
276
		// Set error only if email access form isn't being submitted.
277
		if (
278
			! isset( $_POST['give_email'] ) &&
0 ignored issues
show
introduced by
Detected access of super global var $_POST, probably need manual inspection.
Loading history...
279
			! isset( $_POST['_wpnonce'] )
0 ignored issues
show
introduced by
Detected access of super global var $_POST, probably need manual inspection.
Loading history...
280
		) {
281
			give_set_error( 'give_email_token_expired', apply_filters( 'give_email_token_expired_message', __( 'Your access token has expired. Please request a new one.', 'give' ) ) );
282
		}
283
284
		return false;
285
286
	}
287
288
	/**
289
	 * Add the verify key to DB
290
	 *
291
	 * @param int    $donor_id   Donor id.
292
	 * @param string $email      Donor email.
293
	 * @param string $verify_key The verification key.
294
	 *
295
	 * @since  1.0
296
	 * @access public
297
	 *
298
	 * @return void
299
	 */
300
	public function set_verify_key( $donor_id, $email, $verify_key ) {
0 ignored issues
show
Unused Code introduced by
The parameter $email is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
301
		global $wpdb;
302
303
		$now = date( 'Y-m-d H:i:s' );
304
305
		// Insert or update?
306
		$row_id = (int) $wpdb->get_var(
0 ignored issues
show
introduced by
Usage of a direct database call is discouraged.
Loading history...
introduced by
Usage of a direct database call without caching is prohibited. Use wp_cache_get / wp_cache_set.
Loading history...
307
			$wpdb->prepare( "SELECT id FROM {$wpdb->donors} WHERE id = %d LIMIT 1", $donor_id )
308
		);
309
310
		// Update.
311
		if ( ! empty( $row_id ) ) {
312
			$wpdb->query(
0 ignored issues
show
introduced by
Usage of a direct database call is discouraged.
Loading history...
introduced by
Usage of a direct database call without caching is prohibited. Use wp_cache_get / wp_cache_set.
Loading history...
313
				$wpdb->prepare( "UPDATE {$wpdb->donors} SET verify_key = %s, verify_throttle = %s WHERE id = %d LIMIT 1", $verify_key, $now, $row_id )
314
			);
315
		} // Insert.
316
		else {
317
			$wpdb->query(
0 ignored issues
show
introduced by
Usage of a direct database call is discouraged.
Loading history...
introduced by
Usage of a direct database call without caching is prohibited. Use wp_cache_get / wp_cache_set.
Loading history...
318
				$wpdb->prepare( "INSERT INTO {$wpdb->donors} ( verify_key, verify_throttle) VALUES (%s, %s)", $verify_key, $now )
319
			);
320
		}
321
	}
322
323
	/**
324
	 * Is this a valid verify key?
325
	 *
326
	 * @since  1.0
327
	 * @access public
328
	 *
329
	 * @param  $token string The token.
330
	 *
331
	 * @return bool
332
	 */
333
	public function is_valid_verify_key( $token ) {
334
		/* @var WPDB $wpdb */
335
		global $wpdb;
336
337
		// See if the verify_key exists.
338
		$row = $wpdb->get_row(
0 ignored issues
show
introduced by
Usage of a direct database call is discouraged.
Loading history...
introduced by
Usage of a direct database call without caching is prohibited. Use wp_cache_get / wp_cache_set.
Loading history...
339
			$wpdb->prepare( "SELECT id, email FROM {$wpdb->donors} WHERE verify_key = %s LIMIT 1", $token )
340
		);
341
342
		$now = date( 'Y-m-d H:i:s' );
343
344
		// Set token and remove verify key.
345
		if ( ! empty( $row ) ) {
346
			$wpdb->query(
0 ignored issues
show
introduced by
Usage of a direct database call is discouraged.
Loading history...
introduced by
Usage of a direct database call without caching is prohibited. Use wp_cache_get / wp_cache_set.
Loading history...
347
				$wpdb->prepare( "UPDATE {$wpdb->donors} SET verify_key = '', token = %s, verify_throttle = %s WHERE id = %d LIMIT 1", $token, $now, $row->id )
348
			);
349
350
			$this->token_email = $row->email;
351
			$this->token       = $token;
352
353
			return true;
354
		}
355
356
		return false;
357
	}
358
359
	/**
360
	 * Users donations args
361
	 *
362
	 * Force Give to find donations by email, not user ID.
363
	 *
364
	 * @since  1.0
365
	 * @access public
366
	 *
367
	 * @param  $args array User Donations arguments.
368
	 *
369
	 * @return mixed
370
	 */
371
	public function users_donations_args( $args ) {
372
		$args['user'] = $this->token_email;
373
374
		return $args;
375
	}
376
377
	/**
378
	 * Create required columns
379
	 *
380
	 * Create the necessary columns for email access
381
	 *
382
	 * @since  1.0
383
	 * @access public
384
	 *
385
	 * @return void
386
	 */
387
	public function create_columns() {
388
389
		global $wpdb;
390
391
		// Create columns in donors table.
392
		$wpdb->query( "ALTER TABLE {$wpdb->donors} ADD `token` VARCHAR(255) CHARACTER SET utf8 NOT NULL, ADD `verify_key` VARCHAR(255) CHARACTER SET utf8 NOT NULL AFTER `token`, ADD `verify_throttle` DATETIME NOT NULL AFTER `verify_key`" );
0 ignored issues
show
introduced by
Usage of a direct database call is discouraged.
Loading history...
introduced by
Usage of a direct database call without caching is prohibited. Use wp_cache_get / wp_cache_set.
Loading history...
introduced by
Attempting a database schema change is highly discouraged.
Loading history...
393
	}
394
}
395