Issues (850)

Security Analysis    4 potential vulnerabilities

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Code Injection (1)
Code Injection enables an attacker to execute arbitrary code on the server.
  Variable Injection (2)
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Cross-Site Scripting (1)
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  Header Injection
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

includes/class-wpinv.php (1 issue)

1
<?php
2
/**
3
 * Main Invoicing class.
4
 *
5
 * @package Invoicing
6
 * @since   1.0.0
7
 */
8
9
defined( 'ABSPATH' ) || exit;
10
11
/**
12
 * Main Invoicing class.
13
 *
14
 */
15
class WPInv_Plugin {
16
17
	/**
18
	 * GetPaid version.
19
	 *
20
	 * @var string
21
	 */
22
	public $version;
23
24
	/**
25
	 * Data container.
26
	 *
27
	 * @var array
28
	 */
29
	protected $data = array();
30
31
	/**
32
	 * Form elements instance.
33
	 *
34
	 * @var WPInv_Payment_Form_Elements
35
	 */
36
	public $form_elements;
37
38
	/**
39
	 * @var array An array of payment gateways.
40
	 */
41
	public $gateways;
42
43
	/**
44
	 * Class constructor.
45
	 */
46
	public function __construct() {
47
		$this->define_constants();
48
		$this->includes();
49
		$this->init_hooks();
50
		$this->set_properties();
51
	}
52
53
	/**
54
	 * Sets a custom data property.
55
	 *
56
	 * @param string $prop The prop to set.
57
	 * @param mixed $value The value to retrieve.
58
	 */
59
	public function set( $prop, $value ) {
60
		$this->data[ $prop ] = $value;
61
	}
62
63
	/**
64
	 * Gets a custom data property.
65
	 *
66
	 * @param string $prop The prop to set.
67
	 * @return mixed The value.
68
	 */
69
	public function get( $prop ) {
70
		if ( isset( $this->data[ $prop ] ) ) {
71
			return $this->data[ $prop ];
72
		}
73
74
		return null;
75
	}
76
77
	/**
78
	 * Define class properties.
79
	 */
80
	public function set_properties() {
81
		// Sessions.
82
		$this->set( 'session', new WPInv_Session_Handler() );
83
		$GLOBALS['wpi_session'] = $this->get( 'session' ); // Backwards compatibility.
84
		$GLOBALS['wpinv_euvat'] = new WPInv_EUVat(); // Backwards compatibility.
85
86
		// Init other objects.
87
		$this->set( 'notes', new WPInv_Notes() );
88
		$this->set( 'api', new WPInv_API() );
89
		$this->set( 'post_types', new GetPaid_Post_Types() );
90
		$this->set( 'template', new GetPaid_Template() );
91
		$this->set( 'admin', new GetPaid_Admin() );
92
		$this->set( 'subscriptions', new WPInv_Subscriptions() );
93
		$this->set( 'invoice_emails', new GetPaid_Invoice_Notification_Emails() );
94
		$this->set( 'subscription_emails', new GetPaid_Subscription_Notification_Emails() );
95
		$this->set( 'daily_maintenace', new GetPaid_Daily_Maintenance() );
96
		$this->set( 'payment_forms', new GetPaid_Payment_Forms() );
97
		$this->set( 'maxmind', new GetPaid_MaxMind_Geolocation() );
98
		$this->set( 'data_retention', new WPInv_Data_Retention() );
99
	}
100
101
	 /**
102
	 * Define plugin constants.
103
	 */
104
	public function define_constants() {
105
		define( 'WPINV_PLUGIN_DIR', plugin_dir_path( WPINV_PLUGIN_FILE ) );
106
		define( 'WPINV_PLUGIN_URL', plugin_dir_url( WPINV_PLUGIN_FILE ) );
107
		$this->version = WPINV_VERSION;
108
	}
109
110
	/**
111
	 * Hook into actions and filters.
112
	 *
113
	 * @since 1.0.19
114
	 */
115
	protected function init_hooks() {
116
		/* Internationalize the text strings used. */
117
		add_action( 'plugins_loaded', array( &$this, 'plugins_loaded' ) );
118
119
		// Init the plugin after WordPress inits.
120
		add_action( 'init', array( $this, 'init' ), 1 );
121
		add_action( 'init', array( $this, 'maybe_process_ipn' ), 100 );
122
		add_action( 'init', array( $this, 'wpinv_actions' ) );
123
		add_action( 'init', array( $this, 'maybe_do_authenticated_action' ), 100 );
124
		add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_scripts' ), 11 );
125
		add_action( 'wp_footer', array( $this, 'wp_footer' ) );
126
		add_action( 'wp_head', array( $this, 'wp_head' ) );
127
		add_action( 'widgets_init', array( $this, 'register_widgets' ) );
128
		add_filter( 'wpseo_exclude_from_sitemap_by_post_ids', array( $this, 'wpseo_exclude_from_sitemap_by_post_ids' ) );
129
		add_filter( 'the_seo_framework_sitemap_supported_post_types', array( $this, 'exclude_invoicing_post_types' ) );
130
		add_filter( 'pre_get_posts', array( &$this, 'pre_get_posts' ) );
131
132
		add_filter( 'query_vars', array( $this, 'custom_query_vars' ) );
133
		add_action( 'init', array( $this, 'add_rewrite_rule' ), 10, 0 );
134
		add_action( 'pre_get_posts', array( $this, 'maybe_process_new_ipn' ), 1 );
135
136
		// Fires after registering actions.
137
		do_action( 'wpinv_actions', $this );
138
		do_action( 'getpaid_actions', $this );
139
	}
140
141
	public function plugins_loaded() {
142
		/* Internationalize the text strings used. */
143
		$this->load_textdomain();
144
145
		do_action( 'wpinv_loaded' );
146
147
		// Fix oxygen page builder conflict
148
		if ( function_exists( 'ct_css_output' ) ) {
149
			wpinv_oxygen_fix_conflict();
150
		}
151
	}
152
153
	/**
154
	 * Load Localisation files.
155
	 *
156
	 * Note: the first-loaded translation file overrides any following ones if the same translation is present.
157
	 *
158
	 * Locales found in:
159
	 *      - WP_LANG_DIR/plugins/invoicing-LOCALE.mo
160
	 *      - WP_PLUGIN_DIR/invoicing/languages/invoicing-LOCALE.mo
161
	 *
162
	 * @since 1.0.0
163
	 */
164
	public function load_textdomain() {
165
		// Determines the current locale.
166
		if ( function_exists( 'determine_locale' ) ) {
167
			$locale = determine_locale();
168
		} else if ( function_exists( 'get_user_locale' ) ) {
169
			$locale = get_user_locale();
170
		} else {
171
			$locale = get_locale();
172
		}
173
174
		/**
175
		 * Filter the locale to use for translations.
176
		 */
177
		$locale = apply_filters( 'plugin_locale', $locale, 'invoicing' );
0 ignored issues
show
The call to get_locale() has too many arguments starting with $locale. ( Ignorable by Annotation )

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

177
		$locale = /** @scrutinizer ignore-call */ apply_filters( 'plugin_locale', $locale, 'invoicing' );

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
178
179
		unload_textdomain( 'invoicing', true );
180
		load_textdomain( 'invoicing', WP_LANG_DIR . '/invoicing/invoicing-' . $locale . '.mo' );
181
		load_plugin_textdomain( 'invoicing', false, plugin_basename( dirname( WPINV_PLUGIN_FILE ) ) . '/languages/' );
182
	}
183
184
	/**
185
	 * Include required core files used in admin and on the frontend.
186
	 */
187
	public function includes() {
188
		// Start with the settings.
189
		require_once WPINV_PLUGIN_DIR . 'includes/admin/register-settings.php';
190
191
		// Packages/libraries.
192
		require_once WPINV_PLUGIN_DIR . 'vendor/autoload.php';
193
		require_once WPINV_PLUGIN_DIR . 'vendor/ayecode/wp-ayecode-ui/ayecode-ui-loader.php';
194
195
		// Load functions.
196
		require_once WPINV_PLUGIN_DIR . 'includes/deprecated-functions.php';
197
		require_once WPINV_PLUGIN_DIR . 'includes/wpinv-email-functions.php';
198
		require_once WPINV_PLUGIN_DIR . 'includes/wpinv-general-functions.php';
199
		require_once WPINV_PLUGIN_DIR . 'includes/wpinv-helper-functions.php';
200
		require_once WPINV_PLUGIN_DIR . 'includes/wpinv-tax-functions.php';
201
		require_once WPINV_PLUGIN_DIR . 'includes/wpinv-template-functions.php';
202
		require_once WPINV_PLUGIN_DIR . 'includes/wpinv-address-functions.php';
203
		require_once WPINV_PLUGIN_DIR . 'includes/invoice-functions.php';
204
		require_once WPINV_PLUGIN_DIR . 'includes/subscription-functions.php';
205
		require_once WPINV_PLUGIN_DIR . 'includes/wpinv-item-functions.php';
206
		require_once WPINV_PLUGIN_DIR . 'includes/wpinv-discount-functions.php';
207
		require_once WPINV_PLUGIN_DIR . 'includes/wpinv-gateway-functions.php';
208
		require_once WPINV_PLUGIN_DIR . 'includes/wpinv-payment-functions.php';
209
		require_once WPINV_PLUGIN_DIR . 'includes/user-functions.php';
210
		require_once WPINV_PLUGIN_DIR . 'includes/error-functions.php';
211
212
		// Register autoloader.
213
		try {
214
			spl_autoload_register( array( $this, 'autoload' ), true );
215
		} catch ( Exception $e ) {
216
			wpinv_error_log( $e->getMessage(), '', __FILE__, 149, true );
217
		}
218
219
		require_once WPINV_PLUGIN_DIR . 'includes/abstracts/abstract-wpinv-session.php';
220
		require_once WPINV_PLUGIN_DIR . 'includes/class-wpinv-session-handler.php';
221
		require_once WPINV_PLUGIN_DIR . 'includes/class-wpinv-ajax.php';
222
		require_once WPINV_PLUGIN_DIR . 'includes/class-wpinv-api.php';
223
		require_once WPINV_PLUGIN_DIR . 'includes/class-wpinv-cache-helper.php';
224
		require_once WPINV_PLUGIN_DIR . 'includes/class-wpinv-db.php';
225
		require_once WPINV_PLUGIN_DIR . 'includes/admin/subscriptions.php';
226
		require_once WPINV_PLUGIN_DIR . 'includes/class-wpinv-subscriptions-db.php';
227
		require_once WPINV_PLUGIN_DIR . 'includes/wpinv-subscription.php';
228
		require_once WPINV_PLUGIN_DIR . 'includes/abstracts/abstract-wpinv-privacy.php';
229
		require_once WPINV_PLUGIN_DIR . 'includes/class-wpinv-privacy.php';
230
		require_once WPINV_PLUGIN_DIR . 'includes/libraries/class-ayecode-addons.php';
231
		require_once WPINV_PLUGIN_DIR . 'includes/class-wpinv-addons.php';
232
		require_once WPINV_PLUGIN_DIR . 'widgets/checkout.php';
233
		require_once WPINV_PLUGIN_DIR . 'widgets/invoice-history.php';
234
		require_once WPINV_PLUGIN_DIR . 'widgets/invoice-receipt.php';
235
		require_once WPINV_PLUGIN_DIR . 'widgets/invoice-messages.php';
236
		require_once WPINV_PLUGIN_DIR . 'widgets/subscriptions.php';
237
		require_once WPINV_PLUGIN_DIR . 'widgets/buy-item.php';
238
		require_once WPINV_PLUGIN_DIR . 'widgets/getpaid.php';
239
		require_once WPINV_PLUGIN_DIR . 'widgets/invoice.php';
240
		require_once WPINV_PLUGIN_DIR . 'includes/admin/admin-pages.php';
241
242
		if ( is_admin() || ( defined( 'WP_CLI' ) && WP_CLI ) ) {
243
			GetPaid_Post_Types_Admin::init();
244
245
			require_once WPINV_PLUGIN_DIR . 'includes/admin/wpinv-admin-functions.php';
246
			require_once WPINV_PLUGIN_DIR . 'includes/admin/meta-boxes/class-mb-payment-form.php';
247
			require_once WPINV_PLUGIN_DIR . 'includes/admin/meta-boxes/class-mb-invoice-notes.php';
248
			require_once WPINV_PLUGIN_DIR . 'includes/admin/class-wpinv-admin-menus.php';
249
			require_once WPINV_PLUGIN_DIR . 'includes/admin/class-wpinv-users.php';
250
			require_once WPINV_PLUGIN_DIR . 'includes/admin/class-getpaid-admin-profile.php';
251
			// load the user class only on the users.php page
252
			global $pagenow;
253
			if ( $pagenow == 'users.php' ) {
254
				new WPInv_Admin_Users();
255
			}
256
		}
257
258
		// Register cli commands
259
		if ( defined( 'WP_CLI' ) && WP_CLI ) {
260
			require_once WPINV_PLUGIN_DIR . 'includes/class-wpinv-cli.php';
261
			WP_CLI::add_command( 'invoicing', 'WPInv_CLI' );
262
		}
263
	}
264
265
	/**
266
	 * Class autoloader
267
	 *
268
	 * @param       string $class_name The name of the class to load.
269
	 * @access      public
270
	 * @since       1.0.19
271
	 * @return      void
272
	 */
273
	public function autoload( $class_name ) {
274
		// Normalize the class name...
275
		$class_name  = strtolower( $class_name );
276
277
		// ... and make sure it is our class.
278
		if ( false === strpos( $class_name, 'getpaid_' ) && false === strpos( $class_name, 'wpinv_' ) ) {
279
			return;
280
		}
281
282
		// Next, prepare the file name from the class.
283
		$file_name = 'class-' . str_replace( '_', '-', $class_name ) . '.php';
284
285
		// Base path of the classes.
286
		$plugin_path = untrailingslashit( WPINV_PLUGIN_DIR );
287
288
		// And an array of possible locations in order of importance.
289
		$locations = array(
290
			"$plugin_path/includes",
291
			"$plugin_path/includes/data-stores",
292
			"$plugin_path/includes/gateways",
293
			"$plugin_path/includes/payments",
294
			"$plugin_path/includes/geolocation",
295
			"$plugin_path/includes/reports",
296
			"$plugin_path/includes/api",
297
			"$plugin_path/includes/admin",
298
			"$plugin_path/includes/admin/meta-boxes",
299
		);
300
301
		foreach ( apply_filters( 'getpaid_autoload_locations', $locations ) as $location ) {
302
			if ( file_exists( trailingslashit( $location ) . $file_name ) ) {
303
				include trailingslashit( $location ) . $file_name;
304
				break;
305
			}
306
		}
307
	}
308
309
	/**
310
	 * Inits hooks etc.
311
	 */
312
	public function init() {
313
		// Fires before getpaid inits.
314
		do_action( 'before_getpaid_init', $this );
315
316
		// Maybe upgrade.
317
		$this->maybe_upgrade_database();
318
319
		// Load default gateways.
320
		$gateways = apply_filters(
321
			'getpaid_default_gateways',
322
			array(
323
				'manual'        => 'GetPaid_Manual_Gateway',
324
				'paypal'        => 'GetPaid_Paypal_Gateway',
325
				'worldpay'      => 'GetPaid_Worldpay_Gateway',
326
				'bank_transfer' => 'GetPaid_Bank_Transfer_Gateway',
327
				'authorizenet'  => 'GetPaid_Authorize_Net_Gateway',
328
			)
329
		);
330
331
		foreach ( $gateways as $id => $class ) {
332
			$this->gateways[ $id ] = new $class();
333
		}
334
335
		if ( 'yes' != get_option( 'wpinv_renamed_gateways' ) ) {
336
			GetPaid_Installer::rename_gateways_label();
337
			update_option( 'wpinv_renamed_gateways', 'yes' );
338
		}
339
340
		// Fires after getpaid inits.
341
		do_action( 'getpaid_init', $this );
342
	}
343
344
	/**
345
	 * Checks if this is an IPN request and processes it.
346
	 */
347
	public function maybe_process_ipn() {
348
		// Ensure that this is an IPN request.
349
		if ( empty( $_GET['wpi-listener'] ) || 'IPN' !== $_GET['wpi-listener'] || empty( $_GET['wpi-gateway'] ) ) {
350
			return;
351
		}
352
353
		$gateway = sanitize_text_field( $_GET['wpi-gateway'] );
354
355
		do_action( 'wpinv_verify_payment_ipn', $gateway );
356
		do_action( "wpinv_verify_{$gateway}_ipn" );
357
		exit;
358
	}
359
360
	public function enqueue_scripts() {
361
		// Fires before adding scripts.
362
		do_action( 'getpaid_enqueue_scripts' );
363
364
		$localize                         = array();
365
		$localize['ajax_url']             = admin_url( 'admin-ajax.php' );
366
		$localize['thousands']            = wpinv_thousands_separator();
367
		$localize['decimals']             = wpinv_decimal_separator();
368
		$localize['nonce']                = wp_create_nonce( 'wpinv-nonce' );
369
		$localize['txtComplete']          = __( 'Continue', 'invoicing' );
370
		$localize['UseTaxes']             = wpinv_use_taxes();
371
		$localize['formNonce']            = wp_create_nonce( 'getpaid_form_nonce' );
372
		$localize['loading']              = __( 'Loading...', 'invoicing' );
373
		$localize['connectionError']      = __( 'Could not establish a connection to the server.', 'invoicing' );
374
		$localize['recaptchaSettings']    = getpaid_get_recaptcha_settings();
375
376
		$localize = apply_filters( 'wpinv_front_js_localize', $localize );
377
378
		// reCaptcha.
379
		if ( getpaid_is_recaptcha_enabled() && ( $recaptcha_js = getpaid_recaptcha_api_url() ) ) {
380
			wp_enqueue_script( 'recaptcha', $recaptcha_js, array(), null, true ); // phpcs:ignore WordPress.WP.EnqueuedResourceParameters.MissingVersion
381
		}
382
383
		wp_enqueue_script( 'wpinv-front-script', WPINV_PLUGIN_URL . 'assets/js/payment-forms.min.js', array( 'jquery' ), WPINV_VERSION, true );
384
		wp_localize_script( 'wpinv-front-script', 'WPInv', $localize );
385
	}
386
387
	public function wpinv_actions() {
388
		if ( isset( $_REQUEST['wpi_action'] ) ) {
389
			do_action( 'wpinv_' . wpinv_sanitize_key( $_REQUEST['wpi_action'] ), $_REQUEST );
390
		}
391
392
		if ( defined( 'WP_ALL_IMPORT_ROOT_DIR' ) ) {
393
			include plugin_dir_path( __FILE__ ) . 'libraries/wp-all-import/class-getpaid-wp-all-import.php';
394
		}
395
	}
396
397
	/**
398
	 * Fires an action after verifying that a user can fire them.
399
	 *
400
	 * Note: If the action is on an invoice, subscription etc, esure that the
401
	 * current user owns the invoice/subscription.
402
	 */
403
	public function maybe_do_authenticated_action() {
404
		if ( isset( $_REQUEST['getpaid-action'] ) && isset( $_REQUEST['getpaid-nonce'] ) && wp_verify_nonce( $_REQUEST['getpaid-nonce'], 'getpaid-nonce' ) ) {
405
			$key  = sanitize_key( $_REQUEST['getpaid-action'] );
406
			$data = wp_unslash( $_REQUEST );
407
408
			if ( is_user_logged_in() ) {
409
				do_action( "getpaid_authenticated_action_$key", $data );
410
			}
411
412
			do_action( "getpaid_unauthenticated_action_$key", $data );
413
		}
414
	}
415
416
	public function pre_get_posts( $wp_query ) {
417
		if ( ! is_admin() && ! empty( $wp_query->query_vars['post_type'] ) && getpaid_is_invoice_post_type( $wp_query->query_vars['post_type'] ) && is_user_logged_in() && is_single() && $wp_query->is_main_query() ) {
418
			$wp_query->query_vars['post_status'] = array_keys( wpinv_get_invoice_statuses( false, false, $wp_query->query_vars['post_type'] ) );
419
		}
420
421
		return $wp_query;
422
	}
423
424
	/**
425
	 * Register widgets
426
	 *
427
	 */
428
	public function register_widgets() {
429
		global $pagenow;
430
431
		// Currently, UX Builder does not work particulaly well with SuperDuper.
432
		// So we disable our widgets when editing a page with UX Builder.
433
		if ( function_exists( 'ux_builder_is_active' ) && ux_builder_is_active() ) {
434
			return;
435
		}
436
437
		$block_widget_init_screens = function_exists( 'sd_pagenow_exclude' ) ? sd_pagenow_exclude() : array();
438
439
		if ( is_admin() && $pagenow && in_array( $pagenow, $block_widget_init_screens ) ) {
440
			// don't initiate in these conditions.
441
		} else {
442
			// Only load allowed widgets.
443
			$exclude = function_exists( 'sd_widget_exclude' ) ? sd_widget_exclude() : array();
444
			$widgets = apply_filters(
445
				'getpaid_widget_classes',
446
				array(
447
					'WPInv_Checkout_Widget',
448
					'WPInv_History_Widget',
449
					'WPInv_Receipt_Widget',
450
					'WPInv_Subscriptions_Widget',
451
					'WPInv_Buy_Item_Widget',
452
					'WPInv_Messages_Widget',
453
					'WPInv_GetPaid_Widget',
454
					'WPInv_Invoice_Widget',
455
				)
456
			);
457
458
			// For each widget...
459
			foreach ( $widgets as $widget ) {
460
				// Abort early if it is excluded for this page.
461
				if ( in_array( $widget, $exclude ) ) {
462
					continue;
463
				}
464
465
				// SD V1 used to extend the widget class. V2 does not, so we cannot call register widget on it.
466
				if ( is_subclass_of( $widget, 'WP_Widget' ) ) {
467
					register_widget( $widget );
468
				} else {
469
					new $widget();
470
				}
471
			}
472
		}
473
	}
474
475
	/**
476
	 * Upgrades the database.
477
	 *
478
	 * @since 2.0.2
479
	 */
480
	public function maybe_upgrade_database() {
481
		// Ensure the database tables are up to date.
482
		GetPaid_Installer::maybe_create_db_tables();
483
484
		$wpi_version = get_option( 'wpinv_version', 0 );
485
486
		if ( $wpi_version == WPINV_VERSION ) {
487
			return;
488
		}
489
490
		$installer = new GetPaid_Installer();
491
492
		if ( empty( $wpi_version ) ) {
493
			return $installer->upgrade_db( 0 );
494
		}
495
496
		$upgrades  = array(
497
			'0.0.5' => '004',
498
			'1.0.3' => '102',
499
			'2.0.0' => '118',
500
			'2.8.0' => '279',
501
		);
502
503
		foreach ( $upgrades as $key => $method ) {
504
			if ( version_compare( $wpi_version, $key, '<' ) ) {
505
				return $installer->upgrade_db( $method );
506
			}
507
		}
508
	}
509
510
	/**
511
	 * Flushes the permalinks if needed.
512
	 *
513
	 * @since 2.0.8
514
	 */
515
	public function maybe_flush_permalinks() {
516
		$flush = get_option( 'wpinv_flush_permalinks', 0 );
517
518
		if ( ! empty( $flush ) ) {
519
			flush_rewrite_rules();
520
			delete_option( 'wpinv_flush_permalinks' );
521
		}
522
	}
523
524
	/**
525
	 * Remove our pages from yoast sitemaps.
526
	 *
527
	 * @since 1.0.19
528
	 * @param int[] $excluded_posts_ids
529
	 */
530
	public function wpseo_exclude_from_sitemap_by_post_ids( $excluded_posts_ids ) {
531
		// Ensure that we have an array.
532
		if ( ! is_array( $excluded_posts_ids ) ) {
533
			$excluded_posts_ids = array();
534
		}
535
536
		// Prepare our pages.
537
		$our_pages = array();
538
539
		// Checkout page.
540
		$our_pages[] = wpinv_get_option( 'checkout_page', false );
541
542
		// Success page.
543
		$our_pages[] = wpinv_get_option( 'success_page', false );
544
545
		// Failure page.
546
		$our_pages[] = wpinv_get_option( 'failure_page', false );
547
548
		// History page.
549
		$our_pages[] = wpinv_get_option( 'invoice_history_page', false );
550
551
		// Subscriptions page.
552
		$our_pages[] = wpinv_get_option( 'invoice_subscription_page', false );
553
554
		$our_pages   = array_map( 'intval', array_filter( $our_pages ) );
555
556
		$excluded_posts_ids = $excluded_posts_ids + $our_pages;
557
558
		return array_unique( $excluded_posts_ids );
559
	}
560
561
	/**
562
	 * Remove our pages from yoast sitemaps.
563
	 *
564
	 * @since 1.0.19
565
	 * @param string[] $post_types
566
	 */
567
	public function exclude_invoicing_post_types( $post_types ) {
568
		// Ensure that we have an array.
569
		if ( ! is_array( $post_types ) ) {
570
			$post_types = array();
571
		}
572
573
		// Remove our post types.
574
		return array_diff( $post_types, array_keys( getpaid_get_invoice_post_types() ) );
575
	}
576
577
	/**
578
	 * Displays additional footer code.
579
	 *
580
	 * @since 2.0.0
581
	 */
582
	public function wp_footer() {
583
		wpinv_get_template( 'frontend-footer.php' );
584
	}
585
586
	/**
587
	 * Displays additional header code.
588
	 *
589
	 * @since 2.0.0
590
	 */
591
	public function wp_head() {
592
		wpinv_get_template( 'frontend-head.php' );
593
	}
594
595
	/**
596
	 * Custom query vars.
597
	 *
598
	 */
599
	public function custom_query_vars( $vars ) {
600
		$vars[] = 'getpaid-ipn';
601
		return $vars;
602
	}
603
604
	/**
605
	 * Add rewrite tags and rules.
606
	 *
607
	 */
608
	public function add_rewrite_rule() {
609
		$tag = 'getpaid-ipn';
610
		add_rewrite_tag( "%$tag%", '([^&]+)' );
611
		add_rewrite_rule( "^$tag/([^/]*)/?", "index.php?$tag=\$matches[1]", 'top' );
612
	}
613
614
	/**
615
	 * Processes non-query string ipns.
616
	 *
617
	 */
618
	public function maybe_process_new_ipn( $query ) {
619
		if ( is_admin() || ! $query->is_main_query() ) {
620
			return;
621
		}
622
623
		$gateway = get_query_var( 'getpaid-ipn' );
624
625
		if ( ! empty( $gateway ) ) {
626
			$gateway = sanitize_text_field( $gateway );
627
			nocache_headers();
628
			do_action( 'wpinv_verify_payment_ipn', $gateway );
629
			do_action( "wpinv_verify_{$gateway}_ipn" );
630
			exit;
631
		}
632
	}
633
}
634