Completed
Push — master ( 03b4d5...456551 )
by Stephanie
02:55
created

FrmAddon::get_defined_license()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
nc 2
nop 0
dl 0
loc 5
rs 10
c 0
b 0
f 0
1
<?php
2
3
if ( ! defined( 'ABSPATH' ) ) {
4
	die( 'You are not allowed to call this page directly.' );
5
}
6
7
class FrmAddon {
8
	public $store_url = 'https://formidableforms.com';
9
	public $download_id;
10
	public $plugin_file;
11
	public $plugin_folder;
12
	public $plugin_name;
13
	public $plugin_slug;
14
	public $option_name;
15
	public $version;
16
	public $author = 'Strategy11';
17
	public $is_parent_licence = false;
18
	private $is_expired_addon = false;
19
	public $license;
20
	protected $get_beta = false;
21
22
	public function __construct() {
23
24
		if ( empty( $this->plugin_slug ) ) {
25
			$this->plugin_slug = preg_replace( '/[^a-zA-Z0-9_\s]/', '', str_replace( ' ', '_', strtolower( $this->plugin_name ) ) );
26
		}
27
		if ( empty( $this->option_name ) ) {
28
			$this->option_name = 'edd_' . $this->plugin_slug . '_license_';
29
		}
30
31
		$this->plugin_folder = plugin_basename( $this->plugin_file );
32
		$this->license       = $this->get_license();
33
34
		add_filter( 'frm_installed_addons', array( &$this, 'insert_installed_addon' ) );
35
		$this->edd_plugin_updater();
36
	}
37
38
	public static function load_hooks() {
39
		add_filter( 'frm_include_addon_page', '__return_true' );
40
		//new static();
41
	}
42
43
	public function insert_installed_addon( $plugins ) {
44
		$plugins[ $this->plugin_slug ] = $this;
45
46
		return $plugins;
47
	}
48
49
	public static function get_addon( $plugin_slug ) {
50
		$plugins = apply_filters( 'frm_installed_addons', array() );
51
		$plugin  = false;
52
		if ( isset( $plugins[ $plugin_slug ] ) ) {
53
			$plugin = $plugins[ $plugin_slug ];
54
		}
55
56
		return $plugin;
57
	}
58
59
	public function edd_plugin_updater() {
60
61
		$this->is_license_revoked();
62
		$license = $this->license;
63
64
		add_action( 'after_plugin_row_' . plugin_basename( $this->plugin_file ), array( $this, 'maybe_show_license_message' ), 10, 2 );
65
66
		if ( ! empty( $license ) ) {
67
68
			if ( 'formidable/formidable.php' !== $this->plugin_folder ) {
69
				add_filter( 'plugins_api', array( &$this, 'plugins_api_filter' ), 10, 3 );
70
			}
71
72
			add_filter( 'site_transient_update_plugins', array( &$this, 'clear_expired_download' ) );
73
		}
74
	}
75
76
	/**
77
	 * Updates information on the "View version x.x details" page with custom data.
78
	 *
79
	 * @uses api_request()
80
	 *
81
	 * @param mixed $_data
82
	 * @param string $_action
83
	 * @param object $_args
84
	 *
85
	 * @return object $_data
86
	 */
87
	public function plugins_api_filter( $_data, $_action = '', $_args = null ) {
88
89
		if ( $_action != 'plugin_information' ) {
90
			return $_data;
91
		}
92
93
		$slug = basename( $this->plugin_file, '.php' );
94
		if ( ! isset( $_args->slug ) || $_args->slug != $slug ) {
95
			return $_data;
96
		}
97
98
		$item_id = $this->download_id;
99
		if ( empty( $item_id ) ) {
100
			$_data = array(
101
				'name'      => $this->plugin_name,
102
				'excerpt'   => '',
103
				'changelog' => 'See the full changelog at <a href="' . esc_url( $this->store_url . '/changelog/' ) . '"></a>',
104
				'banners'   => array(
105
					'high' => '',
106
					'low'  => 'https://ps.w.org/formidable/assets/banner-1544x500.png',
107
				),
108
			);
109
		} else {
110
			$api     = new FrmFormApi( $this->license );
111
			$plugins = $api->get_api_info();
112
			$_data   = $plugins[ $item_id ];
113
		}
114
115
		$_data['sections'] = array(
116
			'description' => $_data['excerpt'],
117
			'changelog'   => $_data['changelog'],
118
		);
119
		$_data['author']   = '<a href="' . esc_url( $this->store_url ) . '">' . esc_html( $this->author ) . '</a>';
120
		$_data['homepage'] = $this->store_url;
121
122
		return (object) $_data;
123
	}
124
125
	public function get_license() {
126
		$license = $this->maybe_get_pro_license();
127
		if ( ! empty( $license ) ) {
128
			return $license;
129
		}
130
131
		$license = trim( get_option( $this->option_name . 'key' ) );
132
		if ( empty( $license ) ) {
133
			$license = $this->activate_defined_license();
134
		}
135
136
		return $license;
137
	}
138
139
	/**
140
	 * @since 3.04.03
141
	 */
142
	protected function maybe_get_pro_license() {
143
		// prevent a loop if $this is the pro plugin
144
		$get_license = FrmAppHelper::pro_is_installed() && is_callable( 'FrmProAppHelper::get_updater' ) && $this->plugin_name != 'Formidable Pro';
145
146
		if ( ! $get_license ) {
147
			return false;
148
		}
149
150
		$api = new FrmFormApi();
151
		$api->get_pro_updater();
152
		$license = $api->get_license();
153
		if ( empty( $license ) ) {
154
			return false;
155
		}
156
157
		$this->get_api_info( $license );
158
		if ( ! $this->is_parent_licence ) {
159
			$license = false;
160
		}
161
162
		return $license;
163
	}
164
165
	/**
166
	 * Activate the license in wp-config.php
167
	 *
168
	 * @since 2.04
169
	 */
170
	public function activate_defined_license() {
171
		$license = $this->get_defined_license();
172
		if ( ! empty( $license ) && ! $this->is_active() && $this->is_time_to_auto_activate() ) {
173
			$response = $this->activate_license( $license );
174
			$this->set_auto_activate_time();
175
			if ( ! $response['success'] ) {
176
				$license = '';
177
			}
178
		}
179
180
		return $license;
181
	}
182
183
	/**
184
	 * Check the wp-config.php for the license key
185
	 *
186
	 * @since 2.04
187
	 */
188
	public function get_defined_license() {
189
		$consant_name = 'FRM_' . strtoupper( $this->plugin_slug ) . '_LICENSE';
190
191
		return defined( $consant_name ) ? constant( $consant_name ) : false;
192
	}
193
194
	public function set_license( $license ) {
195
		update_option( $this->option_name . 'key', $license );
196
	}
197
198
	/**
199
	 * If the license is in the config, limit the frequency of checks.
200
	 * The license may be entered incorrectly, so we don't want to check on every page load.
201
	 *
202
	 * @since 2.04
203
	 */
204
	private function is_time_to_auto_activate() {
205
		$last_try = get_option( $this->option_name . 'last_activate' );
206
207
		return ( ! $last_try || $last_try < strtotime( '-1 day' ) );
208
	}
209
210
	private function set_auto_activate_time() {
211
		update_option( $this->option_name . 'last_activate', time() );
212
	}
213
214
	public function is_active() {
215
		return get_option( $this->option_name . 'active' );
216
	}
217
218
	/**
219
	 * @since 3.04.03
220
	 *
221
	 * @param array error
222
	 */
223
	public function maybe_clear_license( $error ) {
224
		if ( $error['code'] === 'disabled' && $error['license'] === $this->license ) {
225
			$this->clear_license();
226
		}
227
	}
228
229
	public function clear_license() {
230
		delete_option( $this->option_name . 'active' );
231
		delete_option( $this->option_name . 'key' );
232
		delete_site_option( $this->transient_key() );
233
		delete_option( $this->transient_key() );
234
		$this->delete_cache();
235
	}
236
237
	public function set_active( $is_active ) {
238
		update_option( $this->option_name . 'active', $is_active );
239
		$this->delete_cache();
240
		FrmAppHelper::save_combined_js();
241
	}
242
243
	/**
244
	 * @since 3.04.03
245
	 */
246
	protected function delete_cache() {
247
		delete_transient( 'frm_api_licence' );
248
249
		$api = new FrmFormApi( $this->license );
250
		$api->reset_cached();
251
252
		$api = new FrmFormTemplateApi( $this->license );
253
		$api->reset_cached();
254
	}
255
256
	/**
257
	 * The Pro version includes the show_license_message function.
258
	 * We need an extra check before we allow it to show a message.
259
	 *
260
	 * @since 3.04.03
261
	 */
262
	public function maybe_show_license_message( $file, $plugin ) {
263
		if ( $this->is_expired_addon || isset( $plugin['package'] ) ) {
264
			// let's not show a ton of duplicate messages
265
			return;
266
		}
267
268
		$this->show_license_message( $file, $plugin );
269
	}
270
271
	public function show_license_message( $file, $plugin ) {
272
		$message = '';
273
		if ( empty( $this->license ) ) {
274
			/* translators: %1$s: Plugin name, %2$s: Start link HTML, %3$s: end link HTML */
275
			$message = sprintf( esc_html__( 'Your %1$s license key is missing. Please add it on the %2$slicenses page%3$s.', 'formidable' ), esc_html( $this->plugin_name ), '<a href="' . esc_url( admin_url( 'admin.php?page=formidable-settings' ) ) . '">', '</a>' );
276
		} else {
277
			$api    = new FrmFormApi( $this->license );
278
			$errors = $api->error_for_license();
279
			if ( ! empty( $errors ) ) {
280
				$message = reset( $errors );
281
			}
282
		}
283
284
		if ( empty( $message ) ) {
285
			return;
286
		}
287
288
		$wp_list_table = _get_list_table( 'WP_Plugins_List_Table' );
289
		$id            = sanitize_title( $plugin['Name'] ) . '-next';
290
291
		echo '<tr class="plugin-update-tr active" id="' . esc_attr( $id ) . '"><td colspan="' . esc_attr( $wp_list_table->get_column_count() ) . '" class="plugin-update colspanchange"><div class="update-message notice error inline notice-error notice-alt"><p>';
292
		echo FrmAppHelper::kses( $message, 'a' ); // WPCS: XSS ok.
293
		echo '<script type="text/javascript">var d = document.getElementById("' . esc_attr( $id ) . '").previousSibling;if ( d !== null ){ d.className = d.className + " update"; }</script>';
294
		echo '</p></div></td></tr>';
295
	}
296
297
	public function clear_expired_download( $transient ) {
298
		if ( ! is_object( $transient ) ) {
299
			return $transient;
300
		}
301
302
		if ( $this->is_current_version( $transient ) ) {
303
			//make sure it doesn't show there is an update if plugin is up-to-date
304
			if ( isset( $transient->response[ $this->plugin_folder ] ) ) {
305
				unset( $transient->response[ $this->plugin_folder ] );
306
			}
307
		} elseif ( isset( $transient->response ) && isset( $transient->response[ $this->plugin_folder ] ) ) {
308
			$this->prepare_update_details( $transient->response[ $this->plugin_folder ] );
309
310
			// if the transient has expired, clear the update and trigger it again
311
			if ( $transient->response[ $this->plugin_folder ] === false ) {
312
				if ( ! $this->has_been_cleared() ) {
313
					$this->cleared_plugins();
314
					$this->manually_queue_update();
315
				}
316
				unset( $transient->response[ $this->plugin_folder ] );
317
			}
318
		}
319
320
		return $transient;
321
	}
322
323
	/**
324
	 * Check if the plugin information is correct to allow an update
325
	 *
326
	 * @since 3.04.03
327
	 *
328
	 * @param object $transient - the current plugin info saved for update
329
	 */
330
	private function prepare_update_details( &$transient ) {
331
		$version_info = $transient;
332
		$has_beta_url = isset( $version_info->beta ) && ! empty( $version_info->beta );
333
		if ( $this->get_beta && ! $has_beta_url ) {
334
			$version_info = (object) $this->get_api_info( $this->license );
335
		}
336
337
		if ( isset( $version_info->new_version ) && ! empty( $version_info->new_version ) ) {
338
			$this->clear_old_plugin_version( $version_info );
339
			if ( $version_info === false ) { // was cleared with timeout
340
				$transient = false;
341
			} else {
342
				$this->maybe_use_beta_url( $version_info );
343
344
				if ( version_compare( $version_info->new_version, $this->version, '>' ) ) {
345
					$transient = $version_info;
346
				}
347
			}
348
		}
349
	}
350
351
	/**
352
	 * Get the API info for this plugin
353
	 *
354
	 * @since 3.04.03
355
	 */
356
	protected function get_api_info( $license ) {
357
		$api   = new FrmFormApi( $license );
358
		$addon = $api->get_addon_for_license( $this );
359
360
		// if there is no download url, this license does not apply to the addon
361
		if ( isset( $addon['package'] ) ) {
362
			$this->is_parent_licence = true;
363
		} elseif ( isset( $addons['error'] ) ) {
0 ignored issues
show
Bug introduced by
The variable $addons does not exist. Did you mean $addon?

This check looks for variables that are accessed but have not been defined. It raises an issue if it finds another variable that has a similar name.

The variable may have been renamed without also renaming all references.

Loading history...
364
			// if the license is expired, we must assume all add-ons were packaged
365
			$this->is_parent_licence = true;
366
			$this->is_expired_addon  = true;
367
		}
368
369
		return $addon;
370
	}
371
372
	/**
373
	 * Make sure transients don't stick around on sites that
374
	 * don't save the transient expiration
375
	 *
376
	 * @since 2.05.05
377
	 */
378
	private function clear_old_plugin_version( &$version_info ) {
379
		$timeout = ( isset( $version_info->timeout ) && ! empty( $version_info->timeout ) ) ? $version_info->timeout : 0;
380
		if ( ! empty( $timeout ) && time() > $timeout ) {
381
			$version_info = false; // Cache is expired
382
			$api          = new FrmFormApi( $this->license );
383
			$api->reset_cached();
384
		}
385
	}
386
387
	/**
388
	 * The beta url is always included if the download has a beta.
389
	 * Check if the beta should be downloaded.
390
	 *
391
	 * @since 3.04.03
392
	 */
393
	private function maybe_use_beta_url( &$version_info ) {
394
		if ( $this->get_beta && isset( $version_info->beta ) && ! empty( $version_info->beta ) ) {
395
			$version_info->new_version = $version_info->beta['version'];
396
			$version_info->package     = $version_info->beta['package'];
397
			if ( isset( $version_info->plugin ) && ! empty( $version_info->plugin ) ) {
398
				$version_info->plugin = $version_info->beta['plugin'];
399
			}
400
		}
401
	}
402
403
	private function is_current_version( $transient ) {
404
		if ( empty( $transient->checked ) || ! isset( $transient->checked[ $this->plugin_folder ] ) ) {
405
			return false;
406
		}
407
408
		$response = ! isset( $transient->response ) || empty( $transient->response );
409
		if ( $response ) {
410
			return true;
411
		}
412
413
		return isset( $transient->response ) && isset( $transient->response[ $this->plugin_folder ] ) && $transient->checked[ $this->plugin_folder ] === $transient->response[ $this->plugin_folder ]->new_version;
414
	}
415
416
	private function has_been_cleared() {
417
		$last_cleared = get_option( 'frm_last_cleared' );
418
419
		return ( $last_cleared && $last_cleared > date( 'Y-m-d H:i:s', strtotime( '-5 minutes' ) ) );
420
	}
421
422
	private function cleared_plugins() {
423
		update_option( 'frm_last_cleared', date( 'Y-m-d H:i:s' ) );
424
	}
425
426
	private function is_license_revoked() {
427
		if ( empty( $this->license ) || empty( $this->plugin_slug ) || isset( $_POST['license'] ) ) { // WPCS: CSRF ok.
428
			return;
429
		}
430
431
		if ( is_multisite() ) {
432
			$last_checked = get_site_option( $this->transient_key() );
433
		} else {
434
			$last_checked = get_option( $this->transient_key() );
435
		}
436
437
		$seven_days_ago = date( 'Y-m-d H:i:s', strtotime( '-7 days' ) );
438
439
		if ( ! $last_checked || $last_checked < $seven_days_ago ) {
440
			// check weekly
441
			if ( is_multisite() ) {
442
				update_site_option( $this->transient_key(), date( 'Y-m-d H:i:s' ) );
443
			} else {
444
				update_option( $this->transient_key(), date( 'Y-m-d H:i:s' ) );
445
			}
446
447
			$response = $this->get_license_status();
448
			if ( 'revoked' === $response['status'] || 'blocked' === $response['status'] || 'disabled' === $response['status'] ) {
449
				$this->clear_license();
450
			}
451
		}
452
	}
453
454
	/**
455
	 * Use a new cache after the license is changed, or Formidable is updated.
456
	 */
457
	private function transient_key() {
458
		return 'frm_' . md5( sanitize_key( $this->license . '_' . $this->plugin_slug ) );
459
	}
460
461
	public static function activate() {
462
		FrmAppHelper::permission_check( 'frm_change_settings' );
463
		check_ajax_referer( 'frm_ajax', 'nonce' );
464
465
		$license = stripslashes( FrmAppHelper::get_param( 'license', '', 'post', 'sanitize_text_field' ) );
466
		if ( empty( $license ) ) {
467
			wp_die(
468
				json_encode(
469
					array(
470
						'message' => __( 'Oops! You forgot to enter your license number.', 'formidable' ),
471
						'success' => false,
472
					)
473
				)
474
			);
475
		}
476
477
		$plugin_slug = FrmAppHelper::get_param( 'plugin', '', 'post', 'sanitize_text_field' );
478
		$this_plugin = self::get_addon( $plugin_slug );
479
		$response    = $this_plugin->activate_license( $license );
480
481
		echo json_encode( $response );
482
		wp_die();
483
	}
484
485
	private function activate_license( $license ) {
486
		$this->set_license( $license );
487
		$this->license = $license;
488
489
		$response            = $this->get_license_status();
490
		$response['message'] = '';
491
		$response['success'] = false;
492
493
		if ( $response['error'] ) {
494
			$response['message'] = $response['status'];
495
		} else {
496
			$messages = $this->get_messages();
497
			if ( is_string( $response['status'] ) && isset( $messages[ $response['status'] ] ) ) {
498
				$response['message'] = $messages[ $response['status'] ];
499
			} else {
500
				$response['message'] = FrmAppHelper::kses( $response['status'], array( 'a' ) );
501
			}
502
503
			$is_valid = false;
504
			if ( 'valid' === $response['status'] ) {
505
				$is_valid            = 'valid';
506
				$response['success'] = true;
507
			}
508
			$this->set_active( $is_valid );
509
		}
510
511
		return $response;
512
	}
513
514
	private function get_license_status() {
515
		$response = array(
516
			'status' => 'missing',
517
			'error'  => true,
518
		);
519
		if ( empty( $this->license ) ) {
520
			$response['error'] = false;
521
522
			return $response;
523
		}
524
525
		try {
526
			$response['error'] = false;
527
			$license_data      = $this->send_mothership_request( 'activate_license' );
528
529
			// $license_data->license will be either "valid" or "invalid"
530
			if ( is_array( $license_data ) ) {
531
				if ( in_array( $license_data['license'], array( 'valid', 'invalid' ), true ) ) {
532
					$response['status'] = $license_data['license'];
533
				}
534
			} else {
535
				$response['status'] = $license_data;
536
			}
537
		} catch ( Exception $e ) {
538
			$response['status'] = $e->getMessage();
539
		}
540
541
		return $response;
542
	}
543
544
	private function get_messages() {
545
		return array(
546
			'valid'               => __( 'Your license has been activated. Enjoy!', 'formidable' ),
547
			'invalid'             => __( 'That license key is invalid', 'formidable' ),
548
			'expired'             => __( 'That license is expired', 'formidable' ),
549
			'revoked'             => __( 'That license has been refunded', 'formidable' ),
550
			'no_activations_left' => __( 'That license has been used on too many sites', 'formidable' ),
551
			'invalid_item_id'     => __( 'Oops! That is the wrong license key for this plugin.', 'formidable' ),
552
			'missing'             => __( 'That license key is invalid', 'formidable' ),
553
		);
554
	}
555
556
	/**
557
	 * @since 4.03
558
	 */
559
	public static function reset_cache() {
560
		FrmAppHelper::permission_check( 'frm_change_settings' );
561
		check_ajax_referer( 'frm_ajax', 'nonce' );
562
563
		$this_plugin = self::set_license_from_post();
564
		$this_plugin->delete_cache();
565
566
		$response = array(
567
			'success' => true,
568
			'message' => __( 'Refresh downloads', 'formidable' ),
569
		);
570
571
		echo json_encode( $response );
572
		wp_die();
573
	}
574
575
	public static function deactivate() {
576
		FrmAppHelper::permission_check( 'frm_change_settings' );
577
		check_ajax_referer( 'frm_ajax', 'nonce' );
578
579
		$this_plugin = self::set_license_from_post();
580
581
		$response = array(
582
			'success' => false,
583
			'message' => '',
584
		);
585
		try {
586
			// $license_data->license will be either "deactivated" or "failed"
587
			$license_data = $this_plugin->send_mothership_request( 'deactivate_license' );
588
			if ( is_array( $license_data ) && 'deactivated' === $license_data['license'] ) {
589
				$response['success'] = true;
590
				$response['message'] = __( 'That license was removed successfully', 'formidable' );
591
			} else {
592
				$response['message'] = __( 'There was an error deactivating your license.', 'formidable' );
593
			}
594
		} catch ( Exception $e ) {
595
			$response['message'] = $e->getMessage();
596
		}
597
598
		$this_plugin->clear_license();
599
600
		echo json_encode( $response );
601
		wp_die();
602
	}
603
604
	/**
605
	 * @since 4.03
606
	 */
607
	private static function set_license_from_post() {
608
		$plugin_slug          = FrmAppHelper::get_param( 'plugin', '', 'post', 'sanitize_text_field' );
609
		$this_plugin          = self::get_addon( $plugin_slug );
610
		$license              = $this_plugin->get_license();
611
		$this_plugin->license = $license;
612
		return $this_plugin;
613
	}
614
615
	public function send_mothership_request( $action ) {
616
		$api_params = array(
617
			'edd_action' => $action,
618
			'license'    => $this->license,
619
			'url'        => home_url(),
620
		);
621
		if ( is_numeric( $this->download_id ) ) {
622
			$api_params['item_id'] = absint( $this->download_id );
623
		} else {
624
			$api_params['item_name'] = rawurlencode( $this->plugin_name );
625
		}
626
627
		$arg_array = array(
628
			'body'       => $api_params,
629
			'timeout'    => 25,
630
			'user-agent' => $this->plugin_slug . '/' . $this->version . '; ' . get_bloginfo( 'url' ),
631
		);
632
633
		$resp = wp_remote_post( $this->store_url, $arg_array );
634
		$body = wp_remote_retrieve_body( $resp );
635
636
		$message = __( 'Your License Key was invalid', 'formidable' );
637
		if ( is_wp_error( $resp ) ) {
638
			$link = FrmAppHelper::admin_upgrade_link( 'api', 'knowledgebase/why-cant-i-activate-formidable-pro/' );
639
			/* translators: %1$s: Start link HTML, %2$s: End link HTML */
640
			$message = sprintf( __( 'You had an error communicating with the Formidable API. %1$sClick here%2$s for more information.', 'formidable' ), '<a href="' . esc_url( $link ) . '" target="_blank">', '</a>' );
641
			$message .= ' ' . $resp->get_error_message();
642
		} elseif ( 'error' === $body || is_wp_error( $body ) ) {
643
			$message = __( 'You had an HTTP error connecting to the Formidable API', 'formidable' );
644
		} else {
645
			$json_res = json_decode( $body, true );
646
			if ( null !== $json_res ) {
647
				if ( is_array( $json_res ) && isset( $json_res['error'] ) ) {
648
					$message = $json_res['error'];
649
				} else {
650
					$message = $json_res;
651
				}
652
			} elseif ( isset( $resp['response'] ) && isset( $resp['response']['code'] ) ) {
653
				/* translators: %1$s: Error code, %2$s: Error message */
654
				$message = sprintf( __( 'There was a %1$s error: %2$s', 'formidable' ), $resp['response']['code'], $resp['response']['message'] . ' ' . $resp['body'] );
655
			}
656
		}
657
658
		return $message;
659
	}
660
661
	public function manually_queue_update() {
662
		set_site_transient( 'update_plugins', null );
663
	}
664
}
665