Completed
Push — update/aag-security-card ( 06ca13...44763d )
by
unknown
204:52 queued 195:48
created

Jetpack::require_jetpack_authentication()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
nc 2
nop 0
dl 0
loc 11
rs 9.9
c 0
b 0
f 0
1
<?php
2
3
use Automattic\Jetpack\Assets\Logo as Jetpack_Logo;
4
use Automattic\Jetpack\Connection\Client;
5
use Automattic\Jetpack\Connection\Manager as Connection_Manager;
6
use Automattic\Jetpack\Connection\REST_Connector as REST_Connector;
7
use Automattic\Jetpack\Connection\XMLRPC_Connector as XMLRPC_Connector;
8
use Automattic\Jetpack\Constants;
9
use Automattic\Jetpack\Sync\Functions;
10
use Automattic\Jetpack\Sync\Sender;
11
use Automattic\Jetpack\Sync\Users;
12
use Automattic\Jetpack\Tracking;
13
14
/*
15
Options:
16
jetpack_options (array)
17
	An array of options.
18
	@see Jetpack_Options::get_option_names()
19
20
jetpack_register (string)
21
	Temporary verification secrets.
22
23
jetpack_activated (int)
24
	1: the plugin was activated normally
25
	2: the plugin was activated on this site because of a network-wide activation
26
	3: the plugin was auto-installed
27
	4: the plugin was manually disconnected (but is still installed)
28
29
jetpack_active_modules (array)
30
	Array of active module slugs.
31
32
jetpack_do_activate (bool)
33
	Flag for "activating" the plugin on sites where the activation hook never fired (auto-installs)
34
*/
35
36
require_once( JETPACK__PLUGIN_DIR . '_inc/lib/class.media.php' );
37
38
class Jetpack {
39
	public $xmlrpc_server = null;
40
41
	private $xmlrpc_verification = null;
42
	private $rest_authentication_status = null;
43
44
	public $HTTP_RAW_POST_DATA = null; // copy of $GLOBALS['HTTP_RAW_POST_DATA']
45
46
	private $tracking;
47
48
	/**
49
	 * @var array The handles of styles that are concatenated into jetpack.css.
50
	 *
51
	 * When making changes to that list, you must also update concat_list in tools/builder/frontend-css.js.
52
	 */
53
	public $concatenated_style_handles = array(
54
		'jetpack-carousel',
55
		'grunion.css',
56
		'the-neverending-homepage',
57
		'jetpack_likes',
58
		'jetpack_related-posts',
59
		'sharedaddy',
60
		'jetpack-slideshow',
61
		'presentations',
62
		'quiz',
63
		'jetpack-subscriptions',
64
		'jetpack-responsive-videos-style',
65
		'jetpack-social-menu',
66
		'tiled-gallery',
67
		'jetpack_display_posts_widget',
68
		'gravatar-profile-widget',
69
		'goodreads-widget',
70
		'jetpack_social_media_icons_widget',
71
		'jetpack-top-posts-widget',
72
		'jetpack_image_widget',
73
		'jetpack-my-community-widget',
74
		'jetpack-authors-widget',
75
		'wordads',
76
		'eu-cookie-law-style',
77
		'flickr-widget-style',
78
		'jetpack-search-widget',
79
		'jetpack-simple-payments-widget-style',
80
		'jetpack-widget-social-icons-styles',
81
	);
82
83
	/**
84
	 * Contains all assets that have had their URL rewritten to minified versions.
85
	 *
86
	 * @var array
87
	 */
88
	static $min_assets = array();
89
90
	public $plugins_to_deactivate = array(
91
		'stats'               => array( 'stats/stats.php', 'WordPress.com Stats' ),
92
		'shortlinks'          => array( 'stats/stats.php', 'WordPress.com Stats' ),
93
		'sharedaddy'          => array( 'sharedaddy/sharedaddy.php', 'Sharedaddy' ),
94
		'twitter-widget'      => array( 'wickett-twitter-widget/wickett-twitter-widget.php', 'Wickett Twitter Widget' ),
95
		'contact-form'        => array( 'grunion-contact-form/grunion-contact-form.php', 'Grunion Contact Form' ),
96
		'contact-form'        => array( 'mullet/mullet-contact-form.php', 'Mullet Contact Form' ),
97
		'custom-css'          => array( 'safecss/safecss.php', 'WordPress.com Custom CSS' ),
98
		'random-redirect'     => array( 'random-redirect/random-redirect.php', 'Random Redirect' ),
99
		'videopress'          => array( 'video/video.php', 'VideoPress' ),
100
		'widget-visibility'   => array( 'jetpack-widget-visibility/widget-visibility.php', 'Jetpack Widget Visibility' ),
101
		'widget-visibility'   => array( 'widget-visibility-without-jetpack/widget-visibility-without-jetpack.php', 'Widget Visibility Without Jetpack' ),
102
		'sharedaddy'          => array( 'jetpack-sharing/sharedaddy.php', 'Jetpack Sharing' ),
103
		'gravatar-hovercards' => array( 'jetpack-gravatar-hovercards/gravatar-hovercards.php', 'Jetpack Gravatar Hovercards' ),
104
		'latex'               => array( 'wp-latex/wp-latex.php', 'WP LaTeX' )
105
	);
106
107
	static $capability_translations = array(
108
		'administrator' => 'manage_options',
109
		'editor'        => 'edit_others_posts',
110
		'author'        => 'publish_posts',
111
		'contributor'   => 'edit_posts',
112
		'subscriber'    => 'read',
113
	);
114
115
	/**
116
	 * Map of modules that have conflicts with plugins and should not be auto-activated
117
	 * if the plugins are active.  Used by filter_default_modules
118
	 *
119
	 * Plugin Authors: If you'd like to prevent a single module from auto-activating,
120
	 * change `module-slug` and add this to your plugin:
121
	 *
122
	 * add_filter( 'jetpack_get_default_modules', 'my_jetpack_get_default_modules' );
123
	 * function my_jetpack_get_default_modules( $modules ) {
124
	 *     return array_diff( $modules, array( 'module-slug' ) );
125
	 * }
126
	 *
127
	 * @var array
128
	 */
129
	private $conflicting_plugins = array(
130
		'comments'          => array(
131
			'Intense Debate'                       => 'intensedebate/intensedebate.php',
132
			'Disqus'                               => 'disqus-comment-system/disqus.php',
133
			'Livefyre'                             => 'livefyre-comments/livefyre.php',
134
			'Comments Evolved for WordPress'       => 'gplus-comments/comments-evolved.php',
135
			'Google+ Comments'                     => 'google-plus-comments/google-plus-comments.php',
136
			'WP-SpamShield Anti-Spam'              => 'wp-spamshield/wp-spamshield.php',
137
		),
138
		'comment-likes' => array(
139
			'Epoch'                                => 'epoch/plugincore.php',
140
		),
141
		'contact-form'      => array(
142
			'Contact Form 7'                       => 'contact-form-7/wp-contact-form-7.php',
143
			'Gravity Forms'                        => 'gravityforms/gravityforms.php',
144
			'Contact Form Plugin'                  => 'contact-form-plugin/contact_form.php',
145
			'Easy Contact Forms'                   => 'easy-contact-forms/easy-contact-forms.php',
146
			'Fast Secure Contact Form'             => 'si-contact-form/si-contact-form.php',
147
			'Ninja Forms'                          => 'ninja-forms/ninja-forms.php',
148
		),
149
		'minileven'         => array(
150
			'WPtouch'                              => 'wptouch/wptouch.php',
151
		),
152
		'latex'             => array(
153
			'LaTeX for WordPress'                  => 'latex/latex.php',
154
			'Youngwhans Simple Latex'              => 'youngwhans-simple-latex/yw-latex.php',
155
			'Easy WP LaTeX'                        => 'easy-wp-latex-lite/easy-wp-latex-lite.php',
156
			'MathJax-LaTeX'                        => 'mathjax-latex/mathjax-latex.php',
157
			'Enable Latex'                         => 'enable-latex/enable-latex.php',
158
			'WP QuickLaTeX'                        => 'wp-quicklatex/wp-quicklatex.php',
159
		),
160
		'protect'           => array(
161
			'Limit Login Attempts'                 => 'limit-login-attempts/limit-login-attempts.php',
162
			'Captcha'                              => 'captcha/captcha.php',
163
			'Brute Force Login Protection'         => 'brute-force-login-protection/brute-force-login-protection.php',
164
			'Login Security Solution'              => 'login-security-solution/login-security-solution.php',
165
			'WPSecureOps Brute Force Protect'      => 'wpsecureops-bruteforce-protect/wpsecureops-bruteforce-protect.php',
166
			'BulletProof Security'                 => 'bulletproof-security/bulletproof-security.php',
167
			'SiteGuard WP Plugin'                  => 'siteguard/siteguard.php',
168
			'Security-protection'                  => 'security-protection/security-protection.php',
169
			'Login Security'                       => 'login-security/login-security.php',
170
			'Botnet Attack Blocker'                => 'botnet-attack-blocker/botnet-attack-blocker.php',
171
			'Wordfence Security'                   => 'wordfence/wordfence.php',
172
			'All In One WP Security & Firewall'    => 'all-in-one-wp-security-and-firewall/wp-security.php',
173
			'iThemes Security'                     => 'better-wp-security/better-wp-security.php',
174
		),
175
		'random-redirect'   => array(
176
			'Random Redirect 2'                    => 'random-redirect-2/random-redirect.php',
177
		),
178
		'related-posts'     => array(
179
			'YARPP'                                => 'yet-another-related-posts-plugin/yarpp.php',
180
			'WordPress Related Posts'              => 'wordpress-23-related-posts-plugin/wp_related_posts.php',
181
			'nrelate Related Content'              => 'nrelate-related-content/nrelate-related.php',
182
			'Contextual Related Posts'             => 'contextual-related-posts/contextual-related-posts.php',
183
			'Related Posts for WordPress'          => 'microkids-related-posts/microkids-related-posts.php',
184
			'outbrain'                             => 'outbrain/outbrain.php',
185
			'Shareaholic'                          => 'shareaholic/shareaholic.php',
186
			'Sexybookmarks'                        => 'sexybookmarks/shareaholic.php',
187
		),
188
		'sharedaddy'        => array(
189
			'AddThis'                              => 'addthis/addthis_social_widget.php',
190
			'Add To Any'                           => 'add-to-any/add-to-any.php',
191
			'ShareThis'                            => 'share-this/sharethis.php',
192
			'Shareaholic'                          => 'shareaholic/shareaholic.php',
193
		),
194
		'seo-tools' => array(
195
			'WordPress SEO by Yoast'               => 'wordpress-seo/wp-seo.php',
196
			'WordPress SEO Premium by Yoast'       => 'wordpress-seo-premium/wp-seo-premium.php',
197
			'All in One SEO Pack'                  => 'all-in-one-seo-pack/all_in_one_seo_pack.php',
198
			'All in One SEO Pack Pro'              => 'all-in-one-seo-pack-pro/all_in_one_seo_pack.php',
199
			'The SEO Framework'                    => 'autodescription/autodescription.php',
200
		),
201
		'verification-tools' => array(
202
			'WordPress SEO by Yoast'               => 'wordpress-seo/wp-seo.php',
203
			'WordPress SEO Premium by Yoast'       => 'wordpress-seo-premium/wp-seo-premium.php',
204
			'All in One SEO Pack'                  => 'all-in-one-seo-pack/all_in_one_seo_pack.php',
205
			'All in One SEO Pack Pro'              => 'all-in-one-seo-pack-pro/all_in_one_seo_pack.php',
206
			'The SEO Framework'                    => 'autodescription/autodescription.php',
207
		),
208
		'widget-visibility' => array(
209
			'Widget Logic'                         => 'widget-logic/widget_logic.php',
210
			'Dynamic Widgets'                      => 'dynamic-widgets/dynamic-widgets.php',
211
		),
212
		'sitemaps' => array(
213
			'Google XML Sitemaps'                  => 'google-sitemap-generator/sitemap.php',
214
			'Better WordPress Google XML Sitemaps' => 'bwp-google-xml-sitemaps/bwp-simple-gxs.php',
215
			'Google XML Sitemaps for qTranslate'   => 'google-xml-sitemaps-v3-for-qtranslate/sitemap.php',
216
			'XML Sitemap & Google News feeds'      => 'xml-sitemap-feed/xml-sitemap.php',
217
			'Google Sitemap by BestWebSoft'        => 'google-sitemap-plugin/google-sitemap-plugin.php',
218
			'WordPress SEO by Yoast'               => 'wordpress-seo/wp-seo.php',
219
			'WordPress SEO Premium by Yoast'       => 'wordpress-seo-premium/wp-seo-premium.php',
220
			'All in One SEO Pack'                  => 'all-in-one-seo-pack/all_in_one_seo_pack.php',
221
			'All in One SEO Pack Pro'              => 'all-in-one-seo-pack-pro/all_in_one_seo_pack.php',
222
			'The SEO Framework'                    => 'autodescription/autodescription.php',
223
			'Sitemap'                              => 'sitemap/sitemap.php',
224
			'Simple Wp Sitemap'                    => 'simple-wp-sitemap/simple-wp-sitemap.php',
225
			'Simple Sitemap'                       => 'simple-sitemap/simple-sitemap.php',
226
			'XML Sitemaps'                         => 'xml-sitemaps/xml-sitemaps.php',
227
			'MSM Sitemaps'                         => 'msm-sitemap/msm-sitemap.php',
228
		),
229
		'lazy-images' => array(
230
			'Lazy Load'              => 'lazy-load/lazy-load.php',
231
			'BJ Lazy Load'           => 'bj-lazy-load/bj-lazy-load.php',
232
			'Lazy Load by WP Rocket' => 'rocket-lazy-load/rocket-lazy-load.php',
233
		),
234
	);
235
236
	/**
237
	 * Plugins for which we turn off our Facebook OG Tags implementation.
238
	 *
239
	 * Note: All in One SEO Pack, All in one SEO Pack Pro, WordPress SEO by Yoast, and WordPress SEO Premium by Yoast automatically deactivate
240
	 * Jetpack's Open Graph tags via filter when their Social Meta modules are active.
241
	 *
242
	 * Plugin authors: If you'd like to prevent Jetpack's Open Graph tag generation in your plugin, you can do so via this filter:
243
	 * add_filter( 'jetpack_enable_open_graph', '__return_false' );
244
	 */
245
	private $open_graph_conflicting_plugins = array(
246
		'2-click-socialmedia-buttons/2-click-socialmedia-buttons.php',
247
		                                                         // 2 Click Social Media Buttons
248
		'add-link-to-facebook/add-link-to-facebook.php',         // Add Link to Facebook
249
		'add-meta-tags/add-meta-tags.php',                       // Add Meta Tags
250
		'easy-facebook-share-thumbnails/esft.php',               // Easy Facebook Share Thumbnail
251
		'heateor-open-graph-meta-tags/heateor-open-graph-meta-tags.php',
252
		                                                         // Open Graph Meta Tags by Heateor
253
		'facebook/facebook.php',                                 // Facebook (official plugin)
254
		'facebook-awd/AWD_facebook.php',                         // Facebook AWD All in one
255
		'facebook-featured-image-and-open-graph-meta-tags/fb-featured-image.php',
256
		                                                         // Facebook Featured Image & OG Meta Tags
257
		'facebook-meta-tags/facebook-metatags.php',              // Facebook Meta Tags
258
		'wonderm00ns-simple-facebook-open-graph-tags/wonderm00n-open-graph.php',
259
		                                                         // Facebook Open Graph Meta Tags for WordPress
260
		'facebook-revised-open-graph-meta-tag/index.php',        // Facebook Revised Open Graph Meta Tag
261
		'facebook-thumb-fixer/_facebook-thumb-fixer.php',        // Facebook Thumb Fixer
262
		'facebook-and-digg-thumbnail-generator/facebook-and-digg-thumbnail-generator.php',
263
		                                                         // Fedmich's Facebook Open Graph Meta
264
		'network-publisher/networkpub.php',                      // Network Publisher
265
		'nextgen-facebook/nextgen-facebook.php',                 // NextGEN Facebook OG
266
		'social-networks-auto-poster-facebook-twitter-g/NextScripts_SNAP.php',
267
		                                                         // NextScripts SNAP
268
		'og-tags/og-tags.php',                                   // OG Tags
269
		'opengraph/opengraph.php',                               // Open Graph
270
		'open-graph-protocol-framework/open-graph-protocol-framework.php',
271
		                                                         // Open Graph Protocol Framework
272
		'seo-facebook-comments/seofacebook.php',                 // SEO Facebook Comments
273
		'seo-ultimate/seo-ultimate.php',                         // SEO Ultimate
274
		'sexybookmarks/sexy-bookmarks.php',                      // Shareaholic
275
		'shareaholic/sexy-bookmarks.php',                        // Shareaholic
276
		'sharepress/sharepress.php',                             // SharePress
277
		'simple-facebook-connect/sfc.php',                       // Simple Facebook Connect
278
		'social-discussions/social-discussions.php',             // Social Discussions
279
		'social-sharing-toolkit/social_sharing_toolkit.php',     // Social Sharing Toolkit
280
		'socialize/socialize.php',                               // Socialize
281
		'squirrly-seo/squirrly.php',                             // SEO by SQUIRRLY™
282
		'only-tweet-like-share-and-google-1/tweet-like-plusone.php',
283
		                                                         // Tweet, Like, Google +1 and Share
284
		'wordbooker/wordbooker.php',                             // Wordbooker
285
		'wpsso/wpsso.php',                                       // WordPress Social Sharing Optimization
286
		'wp-caregiver/wp-caregiver.php',                         // WP Caregiver
287
		'wp-facebook-like-send-open-graph-meta/wp-facebook-like-send-open-graph-meta.php',
288
		                                                         // WP Facebook Like Send & Open Graph Meta
289
		'wp-facebook-open-graph-protocol/wp-facebook-ogp.php',   // WP Facebook Open Graph protocol
290
		'wp-ogp/wp-ogp.php',                                     // WP-OGP
291
		'zoltonorg-social-plugin/zosp.php',                      // Zolton.org Social Plugin
292
		'wp-fb-share-like-button/wp_fb_share-like_widget.php',   // WP Facebook Like Button
293
		'open-graph-metabox/open-graph-metabox.php'              // Open Graph Metabox
294
	);
295
296
	/**
297
	 * Plugins for which we turn off our Twitter Cards Tags implementation.
298
	 */
299
	private $twitter_cards_conflicting_plugins = array(
300
	//	'twitter/twitter.php',                       // The official one handles this on its own.
301
	//	                                             // https://github.com/twitter/wordpress/blob/master/src/Twitter/WordPress/Cards/Compatibility.php
302
		'eewee-twitter-card/index.php',              // Eewee Twitter Card
303
		'ig-twitter-cards/ig-twitter-cards.php',     // IG:Twitter Cards
304
		'jm-twitter-cards/jm-twitter-cards.php',     // JM Twitter Cards
305
		'kevinjohn-gallagher-pure-web-brilliants-social-graph-twitter-cards-extention/kevinjohn_gallagher___social_graph_twitter_output.php',
306
		                                             // Pure Web Brilliant's Social Graph Twitter Cards Extension
307
		'twitter-cards/twitter-cards.php',           // Twitter Cards
308
		'twitter-cards-meta/twitter-cards-meta.php', // Twitter Cards Meta
309
		'wp-to-twitter/wp-to-twitter.php',           // WP to Twitter
310
		'wp-twitter-cards/twitter_cards.php',        // WP Twitter Cards
311
	);
312
313
	/**
314
	 * Message to display in admin_notice
315
	 * @var string
316
	 */
317
	public $message = '';
318
319
	/**
320
	 * Error to display in admin_notice
321
	 * @var string
322
	 */
323
	public $error = '';
324
325
	/**
326
	 * Modules that need more privacy description.
327
	 * @var string
328
	 */
329
	public $privacy_checks = '';
330
331
	/**
332
	 * Stats to record once the page loads
333
	 *
334
	 * @var array
335
	 */
336
	public $stats = array();
337
338
	/**
339
	 * Jetpack_Sync object
340
	 */
341
	public $sync;
342
343
	/**
344
	 * Verified data for JSON authorization request
345
	 */
346
	public $json_api_authorization_request = array();
347
348
	/**
349
	 * @var \Automattic\Jetpack\Connection\Manager
350
	 */
351
	protected $connection_manager;
352
353
	/**
354
	 * @var string Transient key used to prevent multiple simultaneous plugin upgrades
355
	 */
356
	public static $plugin_upgrade_lock_key = 'jetpack_upgrade_lock';
357
358
	/**
359
	 * Holds the singleton instance of this class
360
	 * @since 2.3.3
361
	 * @var Jetpack
362
	 */
363
	static $instance = false;
364
365
	/**
366
	 * Singleton
367
	 * @static
368
	 */
369
	public static function init() {
370
		if ( ! self::$instance ) {
371
			self::$instance = new Jetpack;
372
373
			self::$instance->plugin_upgrade();
374
		}
375
376
		return self::$instance;
377
	}
378
379
	/**
380
	 * Must never be called statically
381
	 */
382
	function plugin_upgrade() {
383
		if ( Jetpack::is_active() ) {
384
			list( $version ) = explode( ':', Jetpack_Options::get_option( 'version' ) );
385
			if ( JETPACK__VERSION != $version ) {
386
				// Prevent multiple upgrades at once - only a single process should trigger
387
				// an upgrade to avoid stampedes
388
				if ( false !== get_transient( self::$plugin_upgrade_lock_key ) ) {
389
					return;
390
				}
391
392
				// Set a short lock to prevent multiple instances of the upgrade
393
				set_transient( self::$plugin_upgrade_lock_key, 1, 10 );
394
395
				// check which active modules actually exist and remove others from active_modules list
396
				$unfiltered_modules = Jetpack::get_active_modules();
397
				$modules = array_filter( $unfiltered_modules, array( 'Jetpack', 'is_module' ) );
398
				if ( array_diff( $unfiltered_modules, $modules ) ) {
399
					Jetpack::update_active_modules( $modules );
400
				}
401
402
				add_action( 'init', array( __CLASS__, 'activate_new_modules' ) );
403
404
				// Upgrade to 4.3.0
405
				if ( Jetpack_Options::get_option( 'identity_crisis_whitelist' ) ) {
406
					Jetpack_Options::delete_option( 'identity_crisis_whitelist' );
407
				}
408
409
				// Make sure Markdown for posts gets turned back on
410
				if ( ! get_option( 'wpcom_publish_posts_with_markdown' ) ) {
411
					update_option( 'wpcom_publish_posts_with_markdown', true );
412
				}
413
414
				if ( did_action( 'wp_loaded' ) ) {
415
					self::upgrade_on_load();
416
				} else {
417
					add_action(
418
						'wp_loaded',
419
						array( __CLASS__, 'upgrade_on_load' )
420
					);
421
				}
422
			}
423
		}
424
	}
425
426
	/**
427
	 * Runs upgrade routines that need to have modules loaded.
428
	 */
429
	static function upgrade_on_load() {
430
431
		// Not attempting any upgrades if jetpack_modules_loaded did not fire.
432
		// This can happen in case Jetpack has been just upgraded and is
433
		// being initialized late during the page load. In this case we wait
434
		// until the next proper admin page load with Jetpack active.
435
		if ( ! did_action( 'jetpack_modules_loaded' ) ) {
436
			delete_transient( self::$plugin_upgrade_lock_key );
437
438
			return;
439
		}
440
441
		Jetpack::maybe_set_version_option();
442
443
		if ( method_exists( 'Jetpack_Widget_Conditions', 'migrate_post_type_rules' ) ) {
444
			Jetpack_Widget_Conditions::migrate_post_type_rules();
445
		}
446
447
		if (
448
			class_exists( 'Jetpack_Sitemap_Manager' )
449
			&& version_compare( JETPACK__VERSION, '5.3', '>=' )
450
		) {
451
			do_action( 'jetpack_sitemaps_purge_data' );
452
		}
453
454
		delete_transient( self::$plugin_upgrade_lock_key );
455
	}
456
457
	/**
458
	 * Saves all the currently active modules to options.
459
	 * Also fires Action hooks for each newly activated and deactivated module.
460
	 *
461
	 * @param $modules Array Array of active modules to be saved in options.
462
	 *
463
	 * @return $success bool true for success, false for failure.
0 ignored issues
show
Documentation introduced by
The doc-type $success could not be parsed: Unknown type name "$success" at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
464
	 */
465
	static function update_active_modules( $modules ) {
466
		$current_modules      = Jetpack_Options::get_option( 'active_modules', array() );
467
		$active_modules       = Jetpack::get_active_modules();
468
		$new_active_modules   = array_diff( $modules, $current_modules );
469
		$new_inactive_modules = array_diff( $active_modules, $modules );
470
		$new_current_modules  = array_diff( array_merge( $current_modules, $new_active_modules ), $new_inactive_modules );
471
		$reindexed_modules    = array_values( $new_current_modules );
472
		$success              = Jetpack_Options::update_option( 'active_modules', array_unique( $reindexed_modules ) );
473
474
		foreach ( $new_active_modules as $module ) {
475
			/**
476
			 * Fires when a specific module is activated.
477
			 *
478
			 * @since 1.9.0
479
			 *
480
			 * @param string $module Module slug.
481
			 * @param boolean $success whether the module was activated. @since 4.2
482
			 */
483
			do_action( 'jetpack_activate_module', $module, $success );
484
			/**
485
			 * Fires when a module is activated.
486
			 * The dynamic part of the filter, $module, is the module slug.
487
			 *
488
			 * @since 1.9.0
489
			 *
490
			 * @param string $module Module slug.
491
			 */
492
			do_action( "jetpack_activate_module_$module", $module );
493
		}
494
495
		foreach ( $new_inactive_modules as $module ) {
496
			/**
497
			 * Fired after a module has been deactivated.
498
			 *
499
			 * @since 4.2.0
500
			 *
501
			 * @param string $module Module slug.
502
			 * @param boolean $success whether the module was deactivated.
503
			 */
504
			do_action( 'jetpack_deactivate_module', $module, $success );
505
			/**
506
			 * Fires when a module is deactivated.
507
			 * The dynamic part of the filter, $module, is the module slug.
508
			 *
509
			 * @since 1.9.0
510
			 *
511
			 * @param string $module Module slug.
512
			 */
513
			do_action( "jetpack_deactivate_module_$module", $module );
514
		}
515
516
		return $success;
517
	}
518
519
	static function delete_active_modules() {
520
		self::update_active_modules( array() );
521
	}
522
523
	/**
524
	 * Constructor.  Initializes WordPress hooks
525
	 */
526
	private function __construct() {
527
		/*
528
		 * Check for and alert any deprecated hooks
529
		 */
530
		add_action( 'init', array( $this, 'deprecated_hooks' ) );
531
532
		/*
533
		 * Enable enhanced handling of previewing sites in Calypso
534
		 */
535
		if ( Jetpack::is_active() ) {
536
			require_once JETPACK__PLUGIN_DIR . '_inc/lib/class.jetpack-iframe-embed.php';
537
			add_action( 'init', array( 'Jetpack_Iframe_Embed', 'init' ), 9, 0 );
538
			require_once JETPACK__PLUGIN_DIR . '_inc/lib/class.jetpack-keyring-service-helper.php';
539
			add_action( 'init', array( 'Jetpack_Keyring_Service_Helper', 'init' ), 9, 0 );
540
		}
541
542
		if ( self::jetpack_tos_agreed() ) {
543
			$tracking = new Automattic\Jetpack\Plugin\Tracking();
544
			add_action( 'init', array( $tracking, 'init' ) );
545
		}
546
547
		/*
548
		 * Load things that should only be in Network Admin.
549
		 *
550
		 * For now blow away everything else until a more full
551
		 * understanding of what is needed at the network level is
552
		 * available
553
		 */
554
		if ( is_multisite() ) {
555
			Jetpack_Network::init();
556
		}
557
558
		add_filter( 'jetpack_connection_secret_generator', function( $callable ) {
559
			return function() {
560
				return wp_generate_password( 32, false );
561
			};
562
		} );
563
564
		$this->connection_manager = new Connection_Manager( );
565
566
		/**
567
		 * Prepare Gutenberg Editor functionality
568
		 */
569
		require_once JETPACK__PLUGIN_DIR . 'class.jetpack-gutenberg.php';
570
		Jetpack_Gutenberg::init();
571
		Jetpack_Gutenberg::load_independent_blocks();
572
		add_action( 'enqueue_block_editor_assets', array( 'Jetpack_Gutenberg', 'enqueue_block_editor_assets' ) );
573
574
		add_action( 'set_user_role', array( $this, 'maybe_clear_other_linked_admins_transient' ), 10, 3 );
575
576
		// Unlink user before deleting the user from .com
577
		add_action( 'deleted_user', array( $this, 'unlink_user' ), 10, 1 );
578
		add_action( 'remove_user_from_blog', array( $this, 'unlink_user' ), 10, 1 );
579
580
		// Alternate XML-RPC, via ?for=jetpack&jetpack=comms
581
		if ( isset( $_GET['jetpack'] ) && 'comms' == $_GET['jetpack'] && isset( $_GET['for'] ) && 'jetpack' == $_GET['for'] ) {
582
			if ( ! defined( 'XMLRPC_REQUEST' ) ) {
583
				define( 'XMLRPC_REQUEST', true );
584
			}
585
586
			add_action( 'template_redirect', array( $this, 'alternate_xmlrpc' ) );
587
588
			add_filter( 'xmlrpc_methods', array( $this, 'remove_non_jetpack_xmlrpc_methods' ), 1000 );
589
		}
590
591
		if ( defined( 'XMLRPC_REQUEST' ) && XMLRPC_REQUEST && isset( $_GET['for'] ) && 'jetpack' == $_GET['for'] ) {
592
			@ini_set( 'display_errors', false ); // Display errors can cause the XML to be not well formed.
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...
593
594
			require_once JETPACK__PLUGIN_DIR . 'class.jetpack-xmlrpc-server.php';
595
			$this->xmlrpc_server = new Jetpack_XMLRPC_Server();
596
597
			$this->require_jetpack_authentication();
598
599
			if ( Jetpack::is_active() ) {
600
				// Hack to preserve $HTTP_RAW_POST_DATA
601
				add_filter( 'xmlrpc_methods', array( $this, 'xmlrpc_methods' ) );
602
603 View Code Duplication
				if ( $this->verify_xml_rpc_signature() ) {
604
					// The actual API methods.
605
					add_filter( 'xmlrpc_methods', array( $this->xmlrpc_server, 'xmlrpc_methods' ) );
606
				} else {
607
					// The jetpack.authorize method should be available for unauthenticated users on a site with an
608
					// active Jetpack connection, so that additional users can link their account.
609
					add_filter( 'xmlrpc_methods', array( $this->xmlrpc_server, 'authorize_xmlrpc_methods' ) );
610
				}
611 View Code Duplication
			} else {
612
				new XMLRPC_Connector( $this->connection_manager );
613
614
				// The bootstrap API methods.
615
				add_filter( 'xmlrpc_methods', array( $this->xmlrpc_server, 'bootstrap_xmlrpc_methods' ) );
616
617
				if ( $this->verify_xml_rpc_signature() ) {
618
					// the jetpack Provision method is available for blog-token-signed requests
619
					add_filter( 'xmlrpc_methods', array( $this->xmlrpc_server, 'provision_xmlrpc_methods' ) );
620
				}
621
			}
622
623
			// Now that no one can authenticate, and we're whitelisting all XML-RPC methods, force enable_xmlrpc on.
624
			add_filter( 'pre_option_enable_xmlrpc', '__return_true' );
625
		} elseif (
626
			is_admin() &&
627
			isset( $_POST['action'] ) && (
628
				'jetpack_upload_file' == $_POST['action'] ||
629
				'jetpack_update_file' == $_POST['action']
630
			)
631
		) {
632
			$this->require_jetpack_authentication();
633
			$this->add_remote_request_handlers();
634
		} else {
635
			if ( Jetpack::is_active() ) {
636
				add_action( 'login_form_jetpack_json_api_authorization', array( &$this, 'login_form_json_api_authorization' ) );
637
				add_filter( 'xmlrpc_methods', array( $this, 'public_xmlrpc_methods' ) );
638
			} else {
639
				add_action( 'rest_api_init', array( $this, 'initialize_rest_api_registration_connector' ) );
640
			}
641
		}
642
643
		if ( Jetpack::is_active() ) {
644
			Jetpack_Heartbeat::init();
645
			if ( Jetpack::is_module_active( 'stats' ) && Jetpack::is_module_active( 'search' ) ) {
646
				require_once JETPACK__PLUGIN_DIR . '_inc/lib/class.jetpack-search-performance-logger.php';
647
				Jetpack_Search_Performance_Logger::init();
648
			}
649
		}
650
651
		add_filter( 'determine_current_user', array( $this, 'wp_rest_authenticate' ) );
652
		add_filter( 'rest_authentication_errors', array( $this, 'wp_rest_authentication_errors' ) );
653
654
		add_action( 'jetpack_clean_nonces', array( 'Jetpack', 'clean_nonces' ) );
655
		if ( ! wp_next_scheduled( 'jetpack_clean_nonces' ) ) {
656
			wp_schedule_event( time(), 'hourly', 'jetpack_clean_nonces' );
657
		}
658
659
		add_filter( 'xmlrpc_blog_options', array( $this, 'xmlrpc_options' ) );
660
661
		add_action( 'admin_init', array( $this, 'admin_init' ) );
662
		add_action( 'admin_init', array( $this, 'dismiss_jetpack_notice' ) );
663
664
		add_filter( 'admin_body_class', array( $this, 'admin_body_class' ) );
665
666
		add_action( 'wp_dashboard_setup', array( $this, 'wp_dashboard_setup' ) );
667
		// Filter the dashboard meta box order to swap the new one in in place of the old one.
668
		add_filter( 'get_user_option_meta-box-order_dashboard', array( $this, 'get_user_option_meta_box_order_dashboard' ) );
669
670
		// returns HTTPS support status
671
		add_action( 'wp_ajax_jetpack-recheck-ssl', array( $this, 'ajax_recheck_ssl' ) );
672
673
		// JITM AJAX callback function
674
		add_action( 'wp_ajax_jitm_ajax',  array( $this, 'jetpack_jitm_ajax_callback' ) );
675
676
		add_action( 'wp_ajax_jetpack_connection_banner', array( $this, 'jetpack_connection_banner_callback' ) );
677
678
		add_action( 'wp_loaded', array( $this, 'register_assets' ) );
679
		add_action( 'wp_enqueue_scripts', array( $this, 'devicepx' ) );
680
		add_action( 'customize_controls_enqueue_scripts', array( $this, 'devicepx' ) );
681
		add_action( 'admin_enqueue_scripts', array( $this, 'devicepx' ) );
682
683
		add_action( 'plugins_loaded', array( $this, 'extra_oembed_providers' ), 100 );
684
685
		/**
686
		 * These actions run checks to load additional files.
687
		 * They check for external files or plugins, so they need to run as late as possible.
688
		 */
689
		add_action( 'wp_head', array( $this, 'check_open_graph' ),       1 );
690
		add_action( 'plugins_loaded', array( $this, 'check_twitter_tags' ),     999 );
691
		add_action( 'plugins_loaded', array( $this, 'check_rest_api_compat' ), 1000 );
692
693
		add_filter( 'plugins_url',      array( 'Jetpack', 'maybe_min_asset' ),     1, 3 );
694
		add_action( 'style_loader_src', array( 'Jetpack', 'set_suffix_on_min' ), 10, 2  );
695
		add_filter( 'style_loader_tag', array( 'Jetpack', 'maybe_inline_style' ), 10, 2 );
696
697
		add_filter( 'map_meta_cap', array( $this, 'jetpack_custom_caps' ), 1, 4 );
698
		add_filter( 'profile_update', array( 'Jetpack', 'user_meta_cleanup' ) );
699
700
		add_filter( 'jetpack_get_default_modules', array( $this, 'filter_default_modules' ) );
701
		add_filter( 'jetpack_get_default_modules', array( $this, 'handle_deprecated_modules' ), 99 );
702
703
		// A filter to control all just in time messages
704
		add_filter( 'jetpack_just_in_time_msgs', array( $this, 'is_active_and_not_development_mode' ), 9 );
705
706
		add_filter( 'jetpack_just_in_time_msg_cache', '__return_true', 9);
707
708
		// If enabled, point edit post, page, and comment links to Calypso instead of WP-Admin.
709
		// We should make sure to only do this for front end links.
710
		if ( Jetpack::get_option( 'edit_links_calypso_redirect' ) && ! is_admin() ) {
711
			add_filter( 'get_edit_post_link', array( $this, 'point_edit_post_links_to_calypso' ), 1, 2 );
712
			add_filter( 'get_edit_comment_link', array( $this, 'point_edit_comment_links_to_calypso' ), 1 );
713
714
			//we'll override wp_notify_postauthor and wp_notify_moderator pluggable functions
715
			//so they point moderation links on emails to Calypso
716
			jetpack_require_lib( 'functions.wp-notify' );
717
		}
718
719
		// Update the Jetpack plan from API on heartbeats
720
		add_action( 'jetpack_heartbeat', array( 'Jetpack_Plan', 'refresh_from_wpcom' ) );
721
722
		/**
723
		 * This is the hack to concatenate all css files into one.
724
		 * For description and reasoning see the implode_frontend_css method
725
		 *
726
		 * Super late priority so we catch all the registered styles
727
		 */
728
		if( !is_admin() ) {
729
			add_action( 'wp_print_styles', array( $this, 'implode_frontend_css' ), -1 ); // Run first
730
			add_action( 'wp_print_footer_scripts', array( $this, 'implode_frontend_css' ), -1 ); // Run first to trigger before `print_late_styles`
731
		}
732
733
734
		/**
735
		 * These are sync actions that we need to keep track of for jitms
736
		 */
737
		add_filter( 'jetpack_sync_before_send_updated_option', array( $this, 'jetpack_track_last_sync_callback' ), 99 );
738
739
		// Actually push the stats on shutdown.
740
		if ( ! has_action( 'shutdown', array( $this, 'push_stats' ) ) ) {
741
			add_action( 'shutdown', array( $this, 'push_stats' ) );
742
		}
743
744
	}
745
746
	function initialize_rest_api_registration_connector() {
747
		new REST_Connector( $this->connection_manager );
748
	}
749
750
	/**
751
	 * This is ported over from the manage module, which has been deprecated and baked in here.
752
	 *
753
	 * @param $domains
754
	 */
755
	function add_wpcom_to_allowed_redirect_hosts( $domains ) {
756
		add_filter( 'allowed_redirect_hosts', array( $this, 'allow_wpcom_domain' ) );
757
	}
758
759
	/**
760
	 * Return $domains, with 'wordpress.com' appended.
761
	 * This is ported over from the manage module, which has been deprecated and baked in here.
762
	 *
763
	 * @param $domains
764
	 * @return array
765
	 */
766
	function allow_wpcom_domain( $domains ) {
767
		if ( empty( $domains ) ) {
768
			$domains = array();
769
		}
770
		$domains[] = 'wordpress.com';
771
		return array_unique( $domains );
772
	}
773
774
	function point_edit_post_links_to_calypso( $default_url, $post_id ) {
775
		$post = get_post( $post_id );
776
777
		if ( empty( $post ) ) {
778
			return $default_url;
779
		}
780
781
		$post_type = $post->post_type;
782
783
		// Mapping the allowed CPTs on WordPress.com to corresponding paths in Calypso.
784
		// https://en.support.wordpress.com/custom-post-types/
785
		$allowed_post_types = array(
786
			'post' => 'post',
787
			'page' => 'page',
788
			'jetpack-portfolio' => 'edit/jetpack-portfolio',
789
			'jetpack-testimonial' => 'edit/jetpack-testimonial',
790
		);
791
792
		if ( ! in_array( $post_type, array_keys( $allowed_post_types ) ) ) {
793
			return $default_url;
794
		}
795
796
		$path_prefix = $allowed_post_types[ $post_type ];
797
798
		$site_slug  = Jetpack::build_raw_urls( get_home_url() );
799
800
		return esc_url( sprintf( 'https://wordpress.com/%s/%s/%d', $path_prefix, $site_slug, $post_id ) );
801
	}
802
803
	function point_edit_comment_links_to_calypso( $url ) {
804
		// Take the `query` key value from the URL, and parse its parts to the $query_args. `amp;c` matches the comment ID.
805
		wp_parse_str( wp_parse_url( $url, PHP_URL_QUERY ), $query_args );
0 ignored issues
show
Bug introduced by
The variable $query_args does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
806
		return esc_url( sprintf( 'https://wordpress.com/comment/%s/%d',
807
			Jetpack::build_raw_urls( get_home_url() ),
808
			$query_args['amp;c']
809
		) );
810
	}
811
812
	function jetpack_track_last_sync_callback( $params ) {
813
		/**
814
		 * Filter to turn off jitm caching
815
		 *
816
		 * @since 5.4.0
817
		 *
818
		 * @param bool false Whether to cache just in time messages
819
		 */
820
		if ( ! apply_filters( 'jetpack_just_in_time_msg_cache', false ) ) {
821
			return $params;
822
		}
823
824
		if ( is_array( $params ) && isset( $params[0] ) ) {
825
			$option = $params[0];
826
			if ( 'active_plugins' === $option ) {
827
				// use the cache if we can, but not terribly important if it gets evicted
828
				set_transient( 'jetpack_last_plugin_sync', time(), HOUR_IN_SECONDS );
829
			}
830
		}
831
832
		return $params;
833
	}
834
835
	function jetpack_connection_banner_callback() {
836
		check_ajax_referer( 'jp-connection-banner-nonce', 'nonce' );
837
838
		if ( isset( $_REQUEST['dismissBanner'] ) ) {
839
			Jetpack_Options::update_option( 'dismissed_connection_banner', 1 );
840
			wp_send_json_success();
841
		}
842
843
		wp_die();
844
	}
845
846
	/**
847
	 * Removes all XML-RPC methods that are not `jetpack.*`.
848
	 * Only used in our alternate XML-RPC endpoint, where we want to
849
	 * ensure that Core and other plugins' methods are not exposed.
850
	 *
851
	 * @param array $methods
852
	 * @return array filtered $methods
853
	 */
854
	function remove_non_jetpack_xmlrpc_methods( $methods ) {
855
		$jetpack_methods = array();
856
857
		foreach ( $methods as $method => $callback ) {
858
			if ( 0 === strpos( $method, 'jetpack.' ) ) {
859
				$jetpack_methods[ $method ] = $callback;
860
			}
861
		}
862
863
		return $jetpack_methods;
864
	}
865
866
	/**
867
	 * Since a lot of hosts use a hammer approach to "protecting" WordPress sites,
868
	 * and just blanket block all requests to /xmlrpc.php, or apply other overly-sensitive
869
	 * security/firewall policies, we provide our own alternate XML RPC API endpoint
870
	 * which is accessible via a different URI. Most of the below is copied directly
871
	 * from /xmlrpc.php so that we're replicating it as closely as possible.
872
	 */
873
	function alternate_xmlrpc() {
874
		// phpcs:disable PHPCompatibility.Variables.RemovedPredefinedGlobalVariables.http_raw_post_dataDeprecatedRemoved
875
		global $HTTP_RAW_POST_DATA;
876
877
		// Some browser-embedded clients send cookies. We don't want them.
878
		$_COOKIE = array();
879
880
		// A bug in PHP < 5.2.2 makes $HTTP_RAW_POST_DATA not set by default,
881
		// but we can do it ourself.
882
		if ( ! isset( $HTTP_RAW_POST_DATA ) ) {
883
			$HTTP_RAW_POST_DATA = file_get_contents( 'php://input' );
884
		}
885
886
		// fix for mozBlog and other cases where '<?xml' isn't on the very first line
887
		if ( isset( $HTTP_RAW_POST_DATA ) ) {
888
			$HTTP_RAW_POST_DATA = trim( $HTTP_RAW_POST_DATA );
889
		}
890
891
		// phpcs:enable
892
893
		include_once( ABSPATH . 'wp-admin/includes/admin.php' );
894
		include_once( ABSPATH . WPINC . '/class-IXR.php' );
895
		include_once( ABSPATH . WPINC . '/class-wp-xmlrpc-server.php' );
896
897
		/**
898
		 * Filters the class used for handling XML-RPC requests.
899
		 *
900
		 * @since 3.1.0
901
		 *
902
		 * @param string $class The name of the XML-RPC server class.
903
		 */
904
		$wp_xmlrpc_server_class = apply_filters( 'wp_xmlrpc_server_class', 'wp_xmlrpc_server' );
905
		$wp_xmlrpc_server = new $wp_xmlrpc_server_class;
906
907
		// Fire off the request
908
		nocache_headers();
909
		$wp_xmlrpc_server->serve_request();
910
911
		exit;
912
	}
913
914
	/**
915
	 * The callback for the JITM ajax requests.
916
	 */
917
	function jetpack_jitm_ajax_callback() {
918
		// Check for nonce
919
		if ( ! isset( $_REQUEST['jitmNonce'] ) || ! wp_verify_nonce( $_REQUEST['jitmNonce'], 'jetpack-jitm-nonce' ) ) {
920
			wp_die( 'Module activation failed due to lack of appropriate permissions' );
921
		}
922
		if ( isset( $_REQUEST['jitmActionToTake'] ) && 'activate' == $_REQUEST['jitmActionToTake'] ) {
923
			$module_slug = $_REQUEST['jitmModule'];
924
			Jetpack::log( 'activate', $module_slug );
925
			Jetpack::activate_module( $module_slug, false, false );
926
			Jetpack::state( 'message', 'no_message' );
927
928
			//A Jetpack module is being activated through a JITM, track it
929
			$this->stat( 'jitm', $module_slug.'-activated-' . JETPACK__VERSION );
930
			$this->do_stats( 'server_side' );
931
932
			wp_send_json_success();
933
		}
934
		if ( isset( $_REQUEST['jitmActionToTake'] ) && 'dismiss' == $_REQUEST['jitmActionToTake'] ) {
935
			// get the hide_jitm options array
936
			$jetpack_hide_jitm = Jetpack_Options::get_option( 'hide_jitm' );
937
			$module_slug = $_REQUEST['jitmModule'];
938
939
			if( ! $jetpack_hide_jitm ) {
940
				$jetpack_hide_jitm = array(
941
					$module_slug => 'hide'
942
				);
943
			} else {
944
				$jetpack_hide_jitm[$module_slug] = 'hide';
945
			}
946
947
			Jetpack_Options::update_option( 'hide_jitm', $jetpack_hide_jitm );
948
949
			//jitm is being dismissed forever, track it
950
			$this->stat( 'jitm', $module_slug.'-dismissed-' . JETPACK__VERSION );
951
			$this->do_stats( 'server_side' );
952
953
			wp_send_json_success();
954
		}
955 View Code Duplication
		if ( isset( $_REQUEST['jitmActionToTake'] ) && 'launch' == $_REQUEST['jitmActionToTake'] ) {
956
			$module_slug = $_REQUEST['jitmModule'];
957
958
			// User went to WordPress.com, track this
959
			$this->stat( 'jitm', $module_slug.'-wordpress-tools-' . JETPACK__VERSION );
960
			$this->do_stats( 'server_side' );
961
962
			wp_send_json_success();
963
		}
964 View Code Duplication
		if ( isset( $_REQUEST['jitmActionToTake'] ) && 'viewed' == $_REQUEST['jitmActionToTake'] ) {
965
			$track = $_REQUEST['jitmModule'];
966
967
			// User is viewing JITM, track it.
968
			$this->stat( 'jitm', $track . '-viewed-' . JETPACK__VERSION );
969
			$this->do_stats( 'server_side' );
970
971
			wp_send_json_success();
972
		}
973
	}
974
975
	/**
976
	 * If there are any stats that need to be pushed, but haven't been, push them now.
977
	 */
978
	function push_stats() {
979
		if ( ! empty( $this->stats ) ) {
980
			$this->do_stats( 'server_side' );
981
		}
982
	}
983
984
	function jetpack_custom_caps( $caps, $cap, $user_id, $args ) {
985
		switch( $cap ) {
986
			case 'jetpack_connect' :
987
			case 'jetpack_reconnect' :
988
				if ( Jetpack::is_development_mode() ) {
989
					$caps = array( 'do_not_allow' );
990
					break;
991
				}
992
				/**
993
				 * Pass through. If it's not development mode, these should match disconnect.
994
				 * Let users disconnect if it's development mode, just in case things glitch.
995
				 */
996
			case 'jetpack_disconnect' :
997
				/**
998
				 * In multisite, can individual site admins manage their own connection?
999
				 *
1000
				 * Ideally, this should be extracted out to a separate filter in the Jetpack_Network class.
1001
				 */
1002
				if ( is_multisite() && ! is_super_admin() && is_plugin_active_for_network( 'jetpack/jetpack.php' ) ) {
1003
					if ( ! Jetpack_Network::init()->get_option( 'sub-site-connection-override' ) ) {
1004
						/**
1005
						 * We need to update the option name -- it's terribly unclear which
1006
						 * direction the override goes.
1007
						 *
1008
						 * @todo: Update the option name to `sub-sites-can-manage-own-connections`
1009
						 */
1010
						$caps = array( 'do_not_allow' );
1011
						break;
1012
					}
1013
				}
1014
1015
				$caps = array( 'manage_options' );
1016
				break;
1017
			case 'jetpack_manage_modules' :
1018
			case 'jetpack_activate_modules' :
1019
			case 'jetpack_deactivate_modules' :
1020
				$caps = array( 'manage_options' );
1021
				break;
1022
			case 'jetpack_configure_modules' :
1023
				$caps = array( 'manage_options' );
1024
				break;
1025
			case 'jetpack_manage_autoupdates' :
1026
				$caps = array(
1027
					'manage_options',
1028
					'update_plugins',
1029
				);
1030
				break;
1031
			case 'jetpack_network_admin_page':
1032
			case 'jetpack_network_settings_page':
1033
				$caps = array( 'manage_network_plugins' );
1034
				break;
1035
			case 'jetpack_network_sites_page':
1036
				$caps = array( 'manage_sites' );
1037
				break;
1038
			case 'jetpack_admin_page' :
1039
				if ( Jetpack::is_development_mode() ) {
1040
					$caps = array( 'manage_options' );
1041
					break;
1042
				} else {
1043
					$caps = array( 'read' );
1044
				}
1045
				break;
1046
			case 'jetpack_connect_user' :
1047
				if ( Jetpack::is_development_mode() ) {
1048
					$caps = array( 'do_not_allow' );
1049
					break;
1050
				}
1051
				$caps = array( 'read' );
1052
				break;
1053
		}
1054
		return $caps;
1055
	}
1056
1057
	function require_jetpack_authentication() {
1058
		// Don't let anyone authenticate
1059
		$_COOKIE = array();
1060
		remove_all_filters( 'authenticate' );
1061
		remove_all_actions( 'wp_login_failed' );
1062
1063
		if ( Jetpack::is_active() ) {
1064
			// Allow Jetpack authentication
1065
			add_filter( 'authenticate', array( $this, 'authenticate_jetpack' ), 10, 3 );
1066
		}
1067
	}
1068
1069
	/**
1070
	 * Load language files
1071
	 * @action plugins_loaded
1072
	 */
1073
	public static function plugin_textdomain() {
1074
		// Note to self, the third argument must not be hardcoded, to account for relocated folders.
1075
		load_plugin_textdomain( 'jetpack', false, dirname( plugin_basename( JETPACK__PLUGIN_FILE ) ) . '/languages/' );
1076
	}
1077
1078
	/**
1079
	 * Register assets for use in various modules and the Jetpack admin page.
1080
	 *
1081
	 * @uses wp_script_is, wp_register_script, plugins_url
1082
	 * @action wp_loaded
1083
	 * @return null
1084
	 */
1085
	public function register_assets() {
1086
		if ( ! wp_script_is( 'spin', 'registered' ) ) {
1087
			wp_register_script(
1088
				'spin',
1089
				self::get_file_url_for_environment( '_inc/build/spin.min.js', '_inc/spin.js' ),
1090
				false,
1091
				'1.3'
1092
			);
1093
		}
1094
1095
		if ( ! wp_script_is( 'jquery.spin', 'registered' ) ) {
1096
			wp_register_script(
1097
				'jquery.spin',
1098
				self::get_file_url_for_environment( '_inc/build/jquery.spin.min.js', '_inc/jquery.spin.js' ),
1099
				array( 'jquery', 'spin' ),
1100
				'1.3'
1101
			);
1102
		}
1103
1104 View Code Duplication
		if ( ! wp_script_is( 'jetpack-gallery-settings', 'registered' ) ) {
1105
			wp_register_script(
1106
				'jetpack-gallery-settings',
1107
				self::get_file_url_for_environment( '_inc/build/gallery-settings.min.js', '_inc/gallery-settings.js' ),
1108
				array( 'media-views' ),
1109
				'20121225'
1110
			);
1111
		}
1112
1113
		if ( ! wp_script_is( 'jetpack-twitter-timeline', 'registered' ) ) {
1114
			wp_register_script(
1115
				'jetpack-twitter-timeline',
1116
				self::get_file_url_for_environment( '_inc/build/twitter-timeline.min.js', '_inc/twitter-timeline.js' ),
1117
				array( 'jquery' ),
1118
				'4.0.0',
1119
				true
1120
			);
1121
		}
1122
1123
		if ( ! wp_script_is( 'jetpack-facebook-embed', 'registered' ) ) {
1124
			wp_register_script(
1125
				'jetpack-facebook-embed',
1126
				self::get_file_url_for_environment( '_inc/build/facebook-embed.min.js', '_inc/facebook-embed.js' ),
1127
				array( 'jquery' ),
1128
				null,
1129
				true
1130
			);
1131
1132
			/** This filter is documented in modules/sharedaddy/sharing-sources.php */
1133
			$fb_app_id = apply_filters( 'jetpack_sharing_facebook_app_id', '249643311490' );
1134
			if ( ! is_numeric( $fb_app_id ) ) {
1135
				$fb_app_id = '';
1136
			}
1137
			wp_localize_script(
1138
				'jetpack-facebook-embed',
1139
				'jpfbembed',
1140
				array(
1141
					'appid' => $fb_app_id,
1142
					'locale' => $this->get_locale(),
1143
				)
1144
			);
1145
		}
1146
1147
		/**
1148
		 * As jetpack_register_genericons is by default fired off a hook,
1149
		 * the hook may have already fired by this point.
1150
		 * So, let's just trigger it manually.
1151
		 */
1152
		require_once( JETPACK__PLUGIN_DIR . '_inc/genericons.php' );
1153
		jetpack_register_genericons();
1154
1155
		/**
1156
		 * Register the social logos
1157
		 */
1158
		require_once( JETPACK__PLUGIN_DIR . '_inc/social-logos.php' );
1159
		jetpack_register_social_logos();
1160
1161 View Code Duplication
		if ( ! wp_style_is( 'jetpack-icons', 'registered' ) )
1162
			wp_register_style( 'jetpack-icons', plugins_url( 'css/jetpack-icons.min.css', JETPACK__PLUGIN_FILE ), false, JETPACK__VERSION );
1163
	}
1164
1165
	/**
1166
	 * Guess locale from language code.
1167
	 *
1168
	 * @param string $lang Language code.
1169
	 * @return string|bool
1170
	 */
1171 View Code Duplication
	function guess_locale_from_lang( $lang ) {
1172
		if ( 'en' === $lang || 'en_US' === $lang || ! $lang ) {
1173
			return 'en_US';
1174
		}
1175
1176
		if ( ! class_exists( 'GP_Locales' ) ) {
1177
			if ( ! defined( 'JETPACK__GLOTPRESS_LOCALES_PATH' ) || ! file_exists( JETPACK__GLOTPRESS_LOCALES_PATH ) ) {
1178
				return false;
1179
			}
1180
1181
			require JETPACK__GLOTPRESS_LOCALES_PATH;
1182
		}
1183
1184
		if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
1185
			// WP.com: get_locale() returns 'it'
1186
			$locale = GP_Locales::by_slug( $lang );
1187
		} else {
1188
			// Jetpack: get_locale() returns 'it_IT';
1189
			$locale = GP_Locales::by_field( 'facebook_locale', $lang );
1190
		}
1191
1192
		if ( ! $locale ) {
1193
			return false;
1194
		}
1195
1196
		if ( empty( $locale->facebook_locale ) ) {
1197
			if ( empty( $locale->wp_locale ) ) {
1198
				return false;
1199
			} else {
1200
				// Facebook SDK is smart enough to fall back to en_US if a
1201
				// locale isn't supported. Since supported Facebook locales
1202
				// can fall out of sync, we'll attempt to use the known
1203
				// wp_locale value and rely on said fallback.
1204
				return $locale->wp_locale;
1205
			}
1206
		}
1207
1208
		return $locale->facebook_locale;
1209
	}
1210
1211
	/**
1212
	 * Get the locale.
1213
	 *
1214
	 * @return string|bool
1215
	 */
1216
	function get_locale() {
1217
		$locale = $this->guess_locale_from_lang( get_locale() );
1218
1219
		if ( ! $locale ) {
1220
			$locale = 'en_US';
1221
		}
1222
1223
		return $locale;
1224
	}
1225
1226
	/**
1227
	 * Device Pixels support
1228
	 * This improves the resolution of gravatars and wordpress.com uploads on hi-res and zoomed browsers.
1229
	 */
1230
	function devicepx() {
1231
		if ( Jetpack::is_active() && ! Jetpack_AMP_Support::is_amp_request() ) {
1232
			wp_enqueue_script( 'devicepx', 'https://s0.wp.com/wp-content/js/devicepx-jetpack.js', array(), gmdate( 'oW' ), true );
1233
		}
1234
	}
1235
1236
	/**
1237
	 * Return the network_site_url so that .com knows what network this site is a part of.
1238
	 * @param  bool $option
1239
	 * @return string
1240
	 */
1241
	public function jetpack_main_network_site_option( $option ) {
1242
		return network_site_url();
1243
	}
1244
	/**
1245
	 * Network Name.
1246
	 */
1247
	static function network_name( $option = null ) {
1248
		global $current_site;
1249
		return $current_site->site_name;
1250
	}
1251
	/**
1252
	 * Does the network allow new user and site registrations.
1253
	 * @return string
1254
	 */
1255
	static function network_allow_new_registrations( $option = null ) {
1256
		return ( in_array( get_site_option( 'registration' ), array('none', 'user', 'blog', 'all' ) ) ? get_site_option( 'registration') : 'none' );
1257
	}
1258
	/**
1259
	 * Does the network allow admins to add new users.
1260
	 * @return boolian
1261
	 */
1262
	static function network_add_new_users( $option = null ) {
1263
		return (bool) get_site_option( 'add_new_users' );
1264
	}
1265
	/**
1266
	 * File upload psace left per site in MB.
1267
	 *  -1 means NO LIMIT.
1268
	 * @return number
1269
	 */
1270
	static function network_site_upload_space( $option = null ) {
1271
		// value in MB
1272
		return ( get_site_option( 'upload_space_check_disabled' ) ? -1 : get_space_allowed() );
1273
	}
1274
1275
	/**
1276
	 * Network allowed file types.
1277
	 * @return string
1278
	 */
1279
	static function network_upload_file_types( $option = null ) {
1280
		return get_site_option( 'upload_filetypes', 'jpg jpeg png gif' );
1281
	}
1282
1283
	/**
1284
	 * Maximum file upload size set by the network.
1285
	 * @return number
1286
	 */
1287
	static function network_max_upload_file_size( $option = null ) {
1288
		// value in KB
1289
		return get_site_option( 'fileupload_maxk', 300 );
1290
	}
1291
1292
	/**
1293
	 * Lets us know if a site allows admins to manage the network.
1294
	 * @return array
1295
	 */
1296
	static function network_enable_administration_menus( $option = null ) {
1297
		return get_site_option( 'menu_items' );
1298
	}
1299
1300
	/**
1301
	 * If a user has been promoted to or demoted from admin, we need to clear the
1302
	 * jetpack_other_linked_admins transient.
1303
	 *
1304
	 * @since 4.3.2
1305
	 * @since 4.4.0  $old_roles is null by default and if it's not passed, the transient is cleared.
1306
	 *
1307
	 * @param int    $user_id   The user ID whose role changed.
1308
	 * @param string $role      The new role.
1309
	 * @param array  $old_roles An array of the user's previous roles.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $old_roles not be array|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
1310
	 */
1311
	function maybe_clear_other_linked_admins_transient( $user_id, $role, $old_roles = null ) {
1312
		if ( 'administrator' == $role
1313
			|| ( is_array( $old_roles ) && in_array( 'administrator', $old_roles ) )
1314
			|| is_null( $old_roles )
1315
		) {
1316
			delete_transient( 'jetpack_other_linked_admins' );
1317
		}
1318
	}
1319
1320
	/**
1321
	 * Checks to see if there are any other users available to become primary
1322
	 * Users must both:
1323
	 * - Be linked to wpcom
1324
	 * - Be an admin
1325
	 *
1326
	 * @return mixed False if no other users are linked, Int if there are.
1327
	 */
1328
	static function get_other_linked_admins() {
1329
		$other_linked_users = get_transient( 'jetpack_other_linked_admins' );
1330
1331
		if ( false === $other_linked_users ) {
1332
			$admins = get_users( array( 'role' => 'administrator' ) );
1333
			if ( count( $admins ) > 1 ) {
1334
				$available = array();
1335
				foreach ( $admins as $admin ) {
1336
					if ( Jetpack::is_user_connected( $admin->ID ) ) {
1337
						$available[] = $admin->ID;
1338
					}
1339
				}
1340
1341
				$count_connected_admins = count( $available );
1342
				if ( count( $available ) > 1 ) {
1343
					$other_linked_users = $count_connected_admins;
1344
				} else {
1345
					$other_linked_users = 0;
1346
				}
1347
			} else {
1348
				$other_linked_users = 0;
1349
			}
1350
1351
			set_transient( 'jetpack_other_linked_admins', $other_linked_users, HOUR_IN_SECONDS );
1352
		}
1353
1354
		return ( 0 === $other_linked_users ) ? false : $other_linked_users;
1355
	}
1356
1357
	/**
1358
	 * Return whether we are dealing with a multi network setup or not.
1359
	 * The reason we are type casting this is because we want to avoid the situation where
1360
	 * the result is false since when is_main_network_option return false it cases
1361
	 * the rest the get_option( 'jetpack_is_multi_network' ); to return the value that is set in the
1362
	 * database which could be set to anything as opposed to what this function returns.
1363
	 * @param  bool  $option
1364
	 *
1365
	 * @return boolean
1366
	 */
1367
	public function is_main_network_option( $option ) {
1368
		// return '1' or ''
1369
		return (string) (bool) Jetpack::is_multi_network();
1370
	}
1371
1372
	/**
1373
	 * Return true if we are with multi-site or multi-network false if we are dealing with single site.
1374
	 *
1375
	 * @param  string  $option
1376
	 * @return boolean
1377
	 */
1378
	public function is_multisite( $option ) {
1379
		return (string) (bool) is_multisite();
1380
	}
1381
1382
	/**
1383
	 * Implemented since there is no core is multi network function
1384
	 * Right now there is no way to tell if we which network is the dominant network on the system
1385
	 *
1386
	 * @since  3.3
1387
	 * @return boolean
1388
	 */
1389
	public static function is_multi_network() {
1390
		global  $wpdb;
1391
1392
		// if we don't have a multi site setup no need to do any more
1393
		if ( ! is_multisite() ) {
1394
			return false;
1395
		}
1396
1397
		$num_sites = $wpdb->get_var( "SELECT COUNT(*) FROM {$wpdb->site}" );
1398
		if ( $num_sites > 1 ) {
1399
			return true;
1400
		} else {
1401
			return false;
1402
		}
1403
	}
1404
1405
	/**
1406
	 * Trigger an update to the main_network_site when we update the siteurl of a site.
1407
	 * @return null
1408
	 */
1409
	function update_jetpack_main_network_site_option() {
1410
		_deprecated_function( __METHOD__, 'jetpack-4.2' );
1411
	}
1412
	/**
1413
	 * Triggered after a user updates the network settings via Network Settings Admin Page
1414
	 *
1415
	 */
1416
	function update_jetpack_network_settings() {
1417
		_deprecated_function( __METHOD__, 'jetpack-4.2' );
1418
		// Only sync this info for the main network site.
1419
	}
1420
1421
	/**
1422
	 * Get back if the current site is single user site.
1423
	 *
1424
	 * @return bool
1425
	 */
1426
	public static function is_single_user_site() {
1427
		global $wpdb;
1428
1429 View Code Duplication
		if ( false === ( $some_users = get_transient( 'jetpack_is_single_user' ) ) ) {
1430
			$some_users = $wpdb->get_var( "SELECT COUNT(*) FROM (SELECT user_id FROM $wpdb->usermeta WHERE meta_key = '{$wpdb->prefix}capabilities' LIMIT 2) AS someusers" );
1431
			set_transient( 'jetpack_is_single_user', (int) $some_users, 12 * HOUR_IN_SECONDS );
1432
		}
1433
		return 1 === (int) $some_users;
1434
	}
1435
1436
	/**
1437
	 * Returns true if the site has file write access false otherwise.
1438
	 * @return string ( '1' | '0' )
1439
	 **/
1440
	public static function file_system_write_access() {
1441
		if ( ! function_exists( 'get_filesystem_method' ) ) {
1442
			require_once( ABSPATH . 'wp-admin/includes/file.php' );
1443
		}
1444
1445
		require_once( ABSPATH . 'wp-admin/includes/template.php' );
1446
1447
		$filesystem_method = get_filesystem_method();
1448
		if ( $filesystem_method === 'direct' ) {
1449
			return 1;
1450
		}
1451
1452
		ob_start();
1453
		$filesystem_credentials_are_stored = request_filesystem_credentials( self_admin_url() );
1454
		ob_end_clean();
1455
		if ( $filesystem_credentials_are_stored ) {
1456
			return 1;
1457
		}
1458
		return 0;
1459
	}
1460
1461
	/**
1462
	 * Finds out if a site is using a version control system.
1463
	 * @return string ( '1' | '0' )
1464
	 **/
1465
	public static function is_version_controlled() {
1466
		_deprecated_function( __METHOD__, 'jetpack-4.2', 'Functions::is_version_controlled' );
1467
		return (string) (int) Functions::is_version_controlled();
1468
	}
1469
1470
	/**
1471
	 * Determines whether the current theme supports featured images or not.
1472
	 * @return string ( '1' | '0' )
1473
	 */
1474
	public static function featured_images_enabled() {
1475
		_deprecated_function( __METHOD__, 'jetpack-4.2' );
1476
		return current_theme_supports( 'post-thumbnails' ) ? '1' : '0';
1477
	}
1478
1479
	/**
1480
	 * Wrapper for core's get_avatar_url().  This one is deprecated.
1481
	 *
1482
	 * @deprecated 4.7 use get_avatar_url instead.
1483
	 * @param int|string|object $id_or_email A user ID,  email address, or comment object
1484
	 * @param int $size Size of the avatar image
1485
	 * @param string $default URL to a default image to use if no avatar is available
1486
	 * @param bool $force_display Whether to force it to return an avatar even if show_avatars is disabled
1487
	 *
1488
	 * @return array
1489
	 */
1490
	public static function get_avatar_url( $id_or_email, $size = 96, $default = '', $force_display = false ) {
1491
		_deprecated_function( __METHOD__, 'jetpack-4.7', 'get_avatar_url' );
1492
		return get_avatar_url( $id_or_email, array(
1493
			'size' => $size,
1494
			'default' => $default,
1495
			'force_default' => $force_display,
1496
		) );
1497
	}
1498
1499
	/**
1500
	 * jetpack_updates is saved in the following schema:
1501
	 *
1502
	 * array (
1503
	 *      'plugins'                       => (int) Number of plugin updates available.
1504
	 *      'themes'                        => (int) Number of theme updates available.
1505
	 *      'wordpress'                     => (int) Number of WordPress core updates available.
1506
	 *      'translations'                  => (int) Number of translation updates available.
1507
	 *      'total'                         => (int) Total of all available updates.
1508
	 *      'wp_update_version'             => (string) The latest available version of WordPress, only present if a WordPress update is needed.
1509
	 * )
1510
	 * @return array
1511
	 */
1512
	public static function get_updates() {
1513
		$update_data = wp_get_update_data();
1514
1515
		// Stores the individual update counts as well as the total count.
1516
		if ( isset( $update_data['counts'] ) ) {
1517
			$updates = $update_data['counts'];
1518
		}
1519
1520
		// If we need to update WordPress core, let's find the latest version number.
1521 View Code Duplication
		if ( ! empty( $updates['wordpress'] ) ) {
1522
			$cur = get_preferred_from_update_core();
1523
			if ( isset( $cur->response ) && 'upgrade' === $cur->response ) {
1524
				$updates['wp_update_version'] = $cur->current;
1525
			}
1526
		}
1527
		return isset( $updates ) ? $updates : array();
1528
	}
1529
1530
	public static function get_update_details() {
1531
		$update_details = array(
1532
			'update_core' => get_site_transient( 'update_core' ),
1533
			'update_plugins' => get_site_transient( 'update_plugins' ),
1534
			'update_themes' => get_site_transient( 'update_themes' ),
1535
		);
1536
		return $update_details;
1537
	}
1538
1539
	public static function refresh_update_data() {
1540
		_deprecated_function( __METHOD__, 'jetpack-4.2' );
1541
1542
	}
1543
1544
	public static function refresh_theme_data() {
1545
		_deprecated_function( __METHOD__, 'jetpack-4.2' );
1546
	}
1547
1548
	/**
1549
	 * Is Jetpack active?
1550
	 */
1551
	public static function is_active() {
1552
		return (bool) Jetpack_Data::get_access_token( JETPACK_MASTER_USER );
0 ignored issues
show
Deprecated Code introduced by
The method Jetpack_Data::get_access_token() has been deprecated with message: 7.5 Use Connection_Manager instead.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
1553
	}
1554
1555
	/**
1556
	 * Make an API call to WordPress.com for plan status
1557
	 *
1558
	 * @deprecated 7.2.0 Use Jetpack_Plan::refresh_from_wpcom.
1559
	 *
1560
	 * @return bool True if plan is updated, false if no update
1561
	 */
1562
	public static function refresh_active_plan_from_wpcom() {
1563
		_deprecated_function( __METHOD__, 'jetpack-7.2.0', 'Jetpack_Plan::refresh_from_wpcom' );
1564
		return Jetpack_Plan::refresh_from_wpcom();
1565
	}
1566
1567
	/**
1568
	 * Get the plan that this Jetpack site is currently using
1569
	 *
1570
	 * @deprecated 7.2.0 Use Jetpack_Plan::get.
1571
	 * @return array Active Jetpack plan details.
1572
	 */
1573
	public static function get_active_plan() {
1574
		_deprecated_function( __METHOD__, 'jetpack-7.2.0', 'Jetpack_Plan::get' );
1575
		return Jetpack_Plan::get();
1576
	}
1577
1578
	/**
1579
	 * Determine whether the active plan supports a particular feature
1580
	 *
1581
	 * @deprecated 7.2.0 Use Jetpack_Plan::supports.
1582
	 * @return bool True if plan supports feature, false if not.
1583
	 */
1584
	public static function active_plan_supports( $feature ) {
1585
		_deprecated_function( __METHOD__, 'jetpack-7.2.0', 'Jetpack_Plan::supports' );
1586
		return Jetpack_Plan::supports( $feature );
1587
	}
1588
1589
	/**
1590
	 * Is Jetpack in development (offline) mode?
1591
	 */
1592 View Code Duplication
	public static function is_development_mode() {
1593
		$development_mode = false;
1594
1595
		if ( defined( 'JETPACK_DEV_DEBUG' ) ) {
1596
			$development_mode = JETPACK_DEV_DEBUG;
1597
		} elseif ( $site_url = site_url() ) {
1598
			$development_mode = false === strpos( $site_url, '.' );
1599
		}
1600
1601
		/**
1602
		 * Filters Jetpack's development mode.
1603
		 *
1604
		 * @see https://jetpack.com/support/development-mode/
1605
		 *
1606
		 * @since 2.2.1
1607
		 *
1608
		 * @param bool $development_mode Is Jetpack's development mode active.
1609
		 */
1610
		$development_mode = ( bool ) apply_filters( 'jetpack_development_mode', $development_mode );
1611
		return $development_mode;
1612
	}
1613
1614
	/**
1615
	 * Whether the site is currently onboarding or not.
1616
	 * A site is considered as being onboarded if it currently has an onboarding token.
1617
	 *
1618
	 * @since 5.8
1619
	 *
1620
	 * @access public
1621
	 * @static
1622
	 *
1623
	 * @return bool True if the site is currently onboarding, false otherwise
1624
	 */
1625
	public static function is_onboarding() {
1626
		return Jetpack_Options::get_option( 'onboarding' ) !== false;
1627
	}
1628
1629
	/**
1630
	 * Determines reason for Jetpack development mode.
1631
	 */
1632
	public static function development_mode_trigger_text() {
1633
		if ( ! Jetpack::is_development_mode() ) {
1634
			return __( 'Jetpack is not in Development Mode.', 'jetpack' );
1635
		}
1636
1637
		if ( defined( 'JETPACK_DEV_DEBUG' ) && JETPACK_DEV_DEBUG ) {
1638
			$notice =  __( 'The JETPACK_DEV_DEBUG constant is defined in wp-config.php or elsewhere.', 'jetpack' );
1639
		} elseif ( site_url() && false === strpos( site_url(), '.' ) ) {
1640
			$notice = __( 'The site URL lacking a dot (e.g. http://localhost).', 'jetpack' );
1641
		} else {
1642
			$notice = __( 'The jetpack_development_mode filter is set to true.', 'jetpack' );
1643
		}
1644
1645
		return $notice;
1646
1647
	}
1648
	/**
1649
	* Get Jetpack development mode notice text and notice class.
1650
	*
1651
	* Mirrors the checks made in Jetpack::is_development_mode
1652
	*
1653
	*/
1654
	public static function show_development_mode_notice() {
1655 View Code Duplication
		if ( Jetpack::is_development_mode() ) {
1656
			$notice = sprintf(
1657
			/* translators: %s is a URL */
1658
				__( 'In <a href="%s" target="_blank">Development Mode</a>:', 'jetpack' ),
1659
				'https://jetpack.com/support/development-mode/'
1660
			);
1661
1662
			$notice .= ' ' . Jetpack::development_mode_trigger_text();
1663
1664
			echo '<div class="updated" style="border-color: #f0821e;"><p>' . $notice . '</p></div>';
1665
		}
1666
1667
		// Throw up a notice if using a development version and as for feedback.
1668
		if ( Jetpack::is_development_version() ) {
1669
			/* translators: %s is a URL */
1670
			$notice = sprintf( __( 'You are currently running a development version of Jetpack. <a href="%s" target="_blank">Submit your feedback</a>', 'jetpack' ), 'https://jetpack.com/contact-support/beta-group/' );
1671
1672
			echo '<div class="updated" style="border-color: #f0821e;"><p>' . $notice . '</p></div>';
1673
		}
1674
		// Throw up a notice if using staging mode
1675
		if ( Jetpack::is_staging_site() ) {
1676
			/* translators: %s is a URL */
1677
			$notice = sprintf( __( 'You are running Jetpack on a <a href="%s" target="_blank">staging server</a>.', 'jetpack' ), 'https://jetpack.com/support/staging-sites/' );
1678
1679
			echo '<div class="updated" style="border-color: #f0821e;"><p>' . $notice . '</p></div>';
1680
		}
1681
	}
1682
1683
	/**
1684
	 * Whether Jetpack's version maps to a public release, or a development version.
1685
	 */
1686
	public static function is_development_version() {
1687
		/**
1688
		 * Allows filtering whether this is a development version of Jetpack.
1689
		 *
1690
		 * This filter is especially useful for tests.
1691
		 *
1692
		 * @since 4.3.0
1693
		 *
1694
		 * @param bool $development_version Is this a develoment version of Jetpack?
1695
		 */
1696
		return (bool) apply_filters(
1697
			'jetpack_development_version',
1698
			! preg_match( '/^\d+(\.\d+)+$/', Constants::get_constant( 'JETPACK__VERSION' ) )
1699
		);
1700
	}
1701
1702
	/**
1703
	 * Is a given user (or the current user if none is specified) linked to a WordPress.com user?
1704
	 */
1705
	public static function is_user_connected( $user_id = false ) {
1706
		return self::connection()->is_user_connected( $user_id );
1707
	}
1708
1709
	/**
1710
	 * Get the wpcom user data of the current|specified connected user.
1711
	 */
1712 View Code Duplication
	public static function get_connected_user_data( $user_id = null ) {
1713
		// TODO: remove in favor of Connection_Manager->get_connected_user_data
1714
		if ( ! $user_id ) {
1715
			$user_id = get_current_user_id();
1716
		}
1717
1718
		$transient_key = "jetpack_connected_user_data_$user_id";
1719
1720
		if ( $cached_user_data = get_transient( $transient_key ) ) {
1721
			return $cached_user_data;
1722
		}
1723
1724
		Jetpack::load_xml_rpc_client();
1725
		$xml = new Jetpack_IXR_Client( array(
1726
			'user_id' => $user_id,
1727
		) );
1728
		$xml->query( 'wpcom.getUser' );
1729
		if ( ! $xml->isError() ) {
1730
			$user_data = $xml->getResponse();
1731
			set_transient( $transient_key, $xml->getResponse(), DAY_IN_SECONDS );
1732
			return $user_data;
1733
		}
1734
1735
		return false;
1736
	}
1737
1738
	/**
1739
	 * Get the wpcom email of the current|specified connected user.
1740
	 */
1741 View Code Duplication
	public static function get_connected_user_email( $user_id = null ) {
1742
		if ( ! $user_id ) {
1743
			$user_id = get_current_user_id();
1744
		}
1745
		Jetpack::load_xml_rpc_client();
1746
		$xml = new Jetpack_IXR_Client( array(
1747
			'user_id' => $user_id,
1748
		) );
1749
		$xml->query( 'wpcom.getUserEmail' );
1750
		if ( ! $xml->isError() ) {
1751
			return $xml->getResponse();
1752
		}
1753
		return false;
1754
	}
1755
1756
	/**
1757
	 * Get the wpcom email of the master user.
1758
	 */
1759
	public static function get_master_user_email() {
1760
		$master_user_id = Jetpack_Options::get_option( 'master_user' );
1761
		if ( $master_user_id ) {
1762
			return self::get_connected_user_email( $master_user_id );
1763
		}
1764
		return '';
1765
	}
1766
1767
	function current_user_is_connection_owner() {
1768
		$user_token = Jetpack_Data::get_access_token( JETPACK_MASTER_USER );
0 ignored issues
show
Deprecated Code introduced by
The method Jetpack_Data::get_access_token() has been deprecated with message: 7.5 Use Connection_Manager instead.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
1769
		return $user_token && is_object( $user_token ) && isset( $user_token->external_user_id ) && get_current_user_id() === $user_token->external_user_id;
1770
	}
1771
1772
	/**
1773
	 * Gets current user IP address.
1774
	 *
1775
	 * @param  bool $check_all_headers Check all headers? Default is `false`.
1776
	 *
1777
	 * @return string                  Current user IP address.
1778
	 */
1779
	public static function current_user_ip( $check_all_headers = false ) {
1780
		if ( $check_all_headers ) {
1781
			foreach ( array(
1782
				'HTTP_CF_CONNECTING_IP',
1783
				'HTTP_CLIENT_IP',
1784
				'HTTP_X_FORWARDED_FOR',
1785
				'HTTP_X_FORWARDED',
1786
				'HTTP_X_CLUSTER_CLIENT_IP',
1787
				'HTTP_FORWARDED_FOR',
1788
				'HTTP_FORWARDED',
1789
				'HTTP_VIA',
1790
			) as $key ) {
1791
				if ( ! empty( $_SERVER[ $key ] ) ) {
1792
					return $_SERVER[ $key ];
1793
				}
1794
			}
1795
		}
1796
1797
		return ! empty( $_SERVER['REMOTE_ADDR'] ) ? $_SERVER['REMOTE_ADDR'] : '';
1798
	}
1799
1800
	/**
1801
	 * Add any extra oEmbed providers that we know about and use on wpcom for feature parity.
1802
	 */
1803
	function extra_oembed_providers() {
1804
		// Cloudup: https://dev.cloudup.com/#oembed
1805
		wp_oembed_add_provider( 'https://cloudup.com/*' , 'https://cloudup.com/oembed' );
1806
		wp_oembed_add_provider( 'https://me.sh/*', 'https://me.sh/oembed?format=json' );
1807
		wp_oembed_add_provider( '#https?://(www\.)?gfycat\.com/.*#i', 'https://api.gfycat.com/v1/oembed', true );
1808
		wp_oembed_add_provider( '#https?://[^.]+\.(wistia\.com|wi\.st)/(medias|embed)/.*#', 'https://fast.wistia.com/oembed', true );
1809
		wp_oembed_add_provider( '#https?://sketchfab\.com/.*#i', 'https://sketchfab.com/oembed', true );
1810
		wp_oembed_add_provider( '#https?://(www\.)?icloud\.com/keynote/.*#i', 'https://iwmb.icloud.com/iwmb/oembed', true );
1811
	}
1812
1813
	/**
1814
	 * Synchronize connected user role changes
1815
	 */
1816
	function user_role_change( $user_id ) {
1817
		_deprecated_function( __METHOD__, 'jetpack-4.2', 'Users::user_role_change()' );
1818
		Users::user_role_change( $user_id );
1819
	}
1820
1821
	/**
1822
	 * Loads the currently active modules.
1823
	 */
1824
	public static function load_modules() {
1825
		if (
1826
			! self::is_active()
1827
			&& ! self::is_development_mode()
1828
			&& ! self::is_onboarding()
1829
			&& (
1830
				! is_multisite()
1831
				|| ! get_site_option( 'jetpack_protect_active' )
1832
			)
1833
		) {
1834
			return;
1835
		}
1836
1837
		$version = Jetpack_Options::get_option( 'version' );
1838 View Code Duplication
		if ( ! $version ) {
1839
			$version = $old_version = JETPACK__VERSION . ':' . time();
1840
			/** This action is documented in class.jetpack.php */
1841
			do_action( 'updating_jetpack_version', $version, false );
1842
			Jetpack_Options::update_options( compact( 'version', 'old_version' ) );
1843
		}
1844
		list( $version ) = explode( ':', $version );
1845
1846
		$modules = array_filter( Jetpack::get_active_modules(), array( 'Jetpack', 'is_module' ) );
1847
1848
		$modules_data = array();
1849
1850
		// Don't load modules that have had "Major" changes since the stored version until they have been deactivated/reactivated through the lint check.
1851
		if ( version_compare( $version, JETPACK__VERSION, '<' ) ) {
1852
			$updated_modules = array();
1853
			foreach ( $modules as $module ) {
1854
				$modules_data[ $module ] = Jetpack::get_module( $module );
1855
				if ( ! isset( $modules_data[ $module ]['changed'] ) ) {
1856
					continue;
1857
				}
1858
1859
				if ( version_compare( $modules_data[ $module ]['changed'], $version, '<=' ) ) {
1860
					continue;
1861
				}
1862
1863
				$updated_modules[] = $module;
1864
			}
1865
1866
			$modules = array_diff( $modules, $updated_modules );
1867
		}
1868
1869
		$is_development_mode = Jetpack::is_development_mode();
1870
1871
		foreach ( $modules as $index => $module ) {
1872
			// If we're in dev mode, disable modules requiring a connection
1873
			if ( $is_development_mode ) {
1874
				// Prime the pump if we need to
1875
				if ( empty( $modules_data[ $module ] ) ) {
1876
					$modules_data[ $module ] = Jetpack::get_module( $module );
1877
				}
1878
				// If the module requires a connection, but we're in local mode, don't include it.
1879
				if ( $modules_data[ $module ]['requires_connection'] ) {
1880
					continue;
1881
				}
1882
			}
1883
1884
			if ( did_action( 'jetpack_module_loaded_' . $module ) ) {
1885
				continue;
1886
			}
1887
1888
			if ( ! include_once( Jetpack::get_module_path( $module ) ) ) {
1889
				unset( $modules[ $index ] );
1890
				self::update_active_modules( array_values( $modules ) );
1891
				continue;
1892
			}
1893
1894
			/**
1895
			 * Fires when a specific module is loaded.
1896
			 * The dynamic part of the hook, $module, is the module slug.
1897
			 *
1898
			 * @since 1.1.0
1899
			 */
1900
			do_action( 'jetpack_module_loaded_' . $module );
1901
		}
1902
1903
		/**
1904
		 * Fires when all the modules are loaded.
1905
		 *
1906
		 * @since 1.1.0
1907
		 */
1908
		do_action( 'jetpack_modules_loaded' );
1909
1910
		// Load module-specific code that is needed even when a module isn't active. Loaded here because code contained therein may need actions such as setup_theme.
1911
		require_once( JETPACK__PLUGIN_DIR . 'modules/module-extras.php' );
1912
	}
1913
1914
	/**
1915
	 * Check if Jetpack's REST API compat file should be included
1916
	 * @action plugins_loaded
1917
	 * @return null
1918
	 */
1919
	public function check_rest_api_compat() {
1920
		/**
1921
		 * Filters the list of REST API compat files to be included.
1922
		 *
1923
		 * @since 2.2.5
1924
		 *
1925
		 * @param array $args Array of REST API compat files to include.
1926
		 */
1927
		$_jetpack_rest_api_compat_includes = apply_filters( 'jetpack_rest_api_compat', array() );
1928
1929
		if ( function_exists( 'bbpress' ) )
1930
			$_jetpack_rest_api_compat_includes[] = JETPACK__PLUGIN_DIR . 'class.jetpack-bbpress-json-api-compat.php';
1931
1932
		foreach ( $_jetpack_rest_api_compat_includes as $_jetpack_rest_api_compat_include )
1933
			require_once $_jetpack_rest_api_compat_include;
1934
	}
1935
1936
	/**
1937
	 * Gets all plugins currently active in values, regardless of whether they're
1938
	 * traditionally activated or network activated.
1939
	 *
1940
	 * @todo Store the result in core's object cache maybe?
1941
	 */
1942
	public static function get_active_plugins() {
1943
		$active_plugins = (array) get_option( 'active_plugins', array() );
1944
1945
		if ( is_multisite() ) {
1946
			// Due to legacy code, active_sitewide_plugins stores them in the keys,
1947
			// whereas active_plugins stores them in the values.
1948
			$network_plugins = array_keys( get_site_option( 'active_sitewide_plugins', array() ) );
1949
			if ( $network_plugins ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $network_plugins of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
1950
				$active_plugins = array_merge( $active_plugins, $network_plugins );
1951
			}
1952
		}
1953
1954
		sort( $active_plugins );
1955
1956
		return array_unique( $active_plugins );
1957
	}
1958
1959
	/**
1960
	 * Gets and parses additional plugin data to send with the heartbeat data
1961
	 *
1962
	 * @since 3.8.1
1963
	 *
1964
	 * @return array Array of plugin data
1965
	 */
1966
	public static function get_parsed_plugin_data() {
1967
		if ( ! function_exists( 'get_plugins' ) ) {
1968
			require_once( ABSPATH . 'wp-admin/includes/plugin.php' );
1969
		}
1970
		/** This filter is documented in wp-admin/includes/class-wp-plugins-list-table.php */
1971
		$all_plugins    = apply_filters( 'all_plugins', get_plugins() );
1972
		$active_plugins = Jetpack::get_active_plugins();
1973
1974
		$plugins = array();
1975
		foreach ( $all_plugins as $path => $plugin_data ) {
1976
			$plugins[ $path ] = array(
1977
					'is_active' => in_array( $path, $active_plugins ),
1978
					'file'      => $path,
1979
					'name'      => $plugin_data['Name'],
1980
					'version'   => $plugin_data['Version'],
1981
					'author'    => $plugin_data['Author'],
1982
			);
1983
		}
1984
1985
		return $plugins;
1986
	}
1987
1988
	/**
1989
	 * Gets and parses theme data to send with the heartbeat data
1990
	 *
1991
	 * @since 3.8.1
1992
	 *
1993
	 * @return array Array of theme data
1994
	 */
1995
	public static function get_parsed_theme_data() {
1996
		$all_themes = wp_get_themes( array( 'allowed' => true ) );
1997
		$header_keys = array( 'Name', 'Author', 'Version', 'ThemeURI', 'AuthorURI', 'Status', 'Tags' );
1998
1999
		$themes = array();
2000
		foreach ( $all_themes as $slug => $theme_data ) {
2001
			$theme_headers = array();
2002
			foreach ( $header_keys as $header_key ) {
2003
				$theme_headers[ $header_key ] = $theme_data->get( $header_key );
2004
			}
2005
2006
			$themes[ $slug ] = array(
2007
					'is_active_theme' => $slug == wp_get_theme()->get_template(),
2008
					'slug' => $slug,
2009
					'theme_root' => $theme_data->get_theme_root_uri(),
2010
					'parent' => $theme_data->parent(),
2011
					'headers' => $theme_headers
2012
			);
2013
		}
2014
2015
		return $themes;
2016
	}
2017
2018
	/**
2019
	 * Checks whether a specific plugin is active.
2020
	 *
2021
	 * We don't want to store these in a static variable, in case
2022
	 * there are switch_to_blog() calls involved.
2023
	 */
2024
	public static function is_plugin_active( $plugin = 'jetpack/jetpack.php' ) {
2025
		return in_array( $plugin, self::get_active_plugins() );
2026
	}
2027
2028
	/**
2029
	 * Check if Jetpack's Open Graph tags should be used.
2030
	 * If certain plugins are active, Jetpack's og tags are suppressed.
2031
	 *
2032
	 * @uses Jetpack::get_active_modules, add_filter, get_option, apply_filters
2033
	 * @action plugins_loaded
2034
	 * @return null
2035
	 */
2036
	public function check_open_graph() {
2037
		if ( in_array( 'publicize', Jetpack::get_active_modules() ) || in_array( 'sharedaddy', Jetpack::get_active_modules() ) ) {
2038
			add_filter( 'jetpack_enable_open_graph', '__return_true', 0 );
2039
		}
2040
2041
		$active_plugins = self::get_active_plugins();
2042
2043
		if ( ! empty( $active_plugins ) ) {
2044
			foreach ( $this->open_graph_conflicting_plugins as $plugin ) {
2045
				if ( in_array( $plugin, $active_plugins ) ) {
2046
					add_filter( 'jetpack_enable_open_graph', '__return_false', 99 );
2047
					break;
2048
				}
2049
			}
2050
		}
2051
2052
		/**
2053
		 * Allow the addition of Open Graph Meta Tags to all pages.
2054
		 *
2055
		 * @since 2.0.3
2056
		 *
2057
		 * @param bool false Should Open Graph Meta tags be added. Default to false.
2058
		 */
2059
		if ( apply_filters( 'jetpack_enable_open_graph', false ) ) {
2060
			require_once JETPACK__PLUGIN_DIR . 'functions.opengraph.php';
2061
		}
2062
	}
2063
2064
	/**
2065
	 * Check if Jetpack's Twitter tags should be used.
2066
	 * If certain plugins are active, Jetpack's twitter tags are suppressed.
2067
	 *
2068
	 * @uses Jetpack::get_active_modules, add_filter, get_option, apply_filters
2069
	 * @action plugins_loaded
2070
	 * @return null
2071
	 */
2072
	public function check_twitter_tags() {
2073
2074
		$active_plugins = self::get_active_plugins();
2075
2076
		if ( ! empty( $active_plugins ) ) {
2077
			foreach ( $this->twitter_cards_conflicting_plugins as $plugin ) {
2078
				if ( in_array( $plugin, $active_plugins ) ) {
2079
					add_filter( 'jetpack_disable_twitter_cards', '__return_true', 99 );
2080
					break;
2081
				}
2082
			}
2083
		}
2084
2085
		/**
2086
		 * Allow Twitter Card Meta tags to be disabled.
2087
		 *
2088
		 * @since 2.6.0
2089
		 *
2090
		 * @param bool true Should Twitter Card Meta tags be disabled. Default to true.
2091
		 */
2092
		if ( ! apply_filters( 'jetpack_disable_twitter_cards', false ) ) {
2093
			require_once JETPACK__PLUGIN_DIR . 'class.jetpack-twitter-cards.php';
2094
		}
2095
	}
2096
2097
	/**
2098
	 * Allows plugins to submit security reports.
2099
 	 *
2100
	 * @param string  $type         Report type (login_form, backup, file_scanning, spam)
2101
	 * @param string  $plugin_file  Plugin __FILE__, so that we can pull plugin data
2102
	 * @param array   $args         See definitions above
2103
	 */
2104
	public static function submit_security_report( $type = '', $plugin_file = '', $args = array() ) {
2105
		_deprecated_function( __FUNCTION__, 'jetpack-4.2', null );
2106
	}
2107
2108
/* Jetpack Options API */
2109
2110
	public static function get_option_names( $type = 'compact' ) {
2111
		return Jetpack_Options::get_option_names( $type );
2112
	}
2113
2114
	/**
2115
	 * Returns the requested option.  Looks in jetpack_options or jetpack_$name as appropriate.
2116
 	 *
2117
	 * @param string $name    Option name
2118
	 * @param mixed  $default (optional)
2119
	 */
2120
	public static function get_option( $name, $default = false ) {
2121
		return Jetpack_Options::get_option( $name, $default );
2122
	}
2123
2124
	/**
2125
	 * Updates the single given option.  Updates jetpack_options or jetpack_$name as appropriate.
2126
 	 *
2127
	 * @deprecated 3.4 use Jetpack_Options::update_option() instead.
2128
	 * @param string $name  Option name
2129
	 * @param mixed  $value Option value
2130
	 */
2131
	public static function update_option( $name, $value ) {
2132
		_deprecated_function( __METHOD__, 'jetpack-3.4', 'Jetpack_Options::update_option()' );
2133
		return Jetpack_Options::update_option( $name, $value );
2134
	}
2135
2136
	/**
2137
	 * Updates the multiple given options.  Updates jetpack_options and/or jetpack_$name as appropriate.
2138
 	 *
2139
	 * @deprecated 3.4 use Jetpack_Options::update_options() instead.
2140
	 * @param array $array array( option name => option value, ... )
2141
	 */
2142
	public static function update_options( $array ) {
2143
		_deprecated_function( __METHOD__, 'jetpack-3.4', 'Jetpack_Options::update_options()' );
2144
		return Jetpack_Options::update_options( $array );
2145
	}
2146
2147
	/**
2148
	 * Deletes the given option.  May be passed multiple option names as an array.
2149
	 * Updates jetpack_options and/or deletes jetpack_$name as appropriate.
2150
	 *
2151
	 * @deprecated 3.4 use Jetpack_Options::delete_option() instead.
2152
	 * @param string|array $names
2153
	 */
2154
	public static function delete_option( $names ) {
2155
		_deprecated_function( __METHOD__, 'jetpack-3.4', 'Jetpack_Options::delete_option()' );
2156
		return Jetpack_Options::delete_option( $names );
2157
	}
2158
2159
	/**
2160
	 * Enters a user token into the user_tokens option
2161
	 *
2162
	 * @param int $user_id
2163
	 * @param string $token
2164
	 * return bool
2165
	 */
2166
	public static function update_user_token( $user_id, $token, $is_master_user ) {
2167
		// not designed for concurrent updates
2168
		$user_tokens = Jetpack_Options::get_option( 'user_tokens' );
2169
		if ( ! is_array( $user_tokens ) )
2170
			$user_tokens = array();
2171
		$user_tokens[$user_id] = $token;
2172
		if ( $is_master_user ) {
2173
			$master_user = $user_id;
2174
			$options     = compact( 'user_tokens', 'master_user' );
2175
		} else {
2176
			$options = compact( 'user_tokens' );
2177
		}
2178
		return Jetpack_Options::update_options( $options );
2179
	}
2180
2181
	/**
2182
	 * Returns an array of all PHP files in the specified absolute path.
2183
	 * Equivalent to glob( "$absolute_path/*.php" ).
2184
	 *
2185
	 * @param string $absolute_path The absolute path of the directory to search.
2186
	 * @return array Array of absolute paths to the PHP files.
2187
	 */
2188
	public static function glob_php( $absolute_path ) {
2189
		if ( function_exists( 'glob' ) ) {
2190
			return glob( "$absolute_path/*.php" );
2191
		}
2192
2193
		$absolute_path = untrailingslashit( $absolute_path );
2194
		$files = array();
2195
		if ( ! $dir = @opendir( $absolute_path ) ) {
2196
			return $files;
2197
		}
2198
2199
		while ( false !== $file = readdir( $dir ) ) {
2200
			if ( '.' == substr( $file, 0, 1 ) || '.php' != substr( $file, -4 ) ) {
2201
				continue;
2202
			}
2203
2204
			$file = "$absolute_path/$file";
2205
2206
			if ( ! is_file( $file ) ) {
2207
				continue;
2208
			}
2209
2210
			$files[] = $file;
2211
		}
2212
2213
		closedir( $dir );
2214
2215
		return $files;
2216
	}
2217
2218
	public static function activate_new_modules( $redirect = false ) {
2219
		if ( ! Jetpack::is_active() && ! Jetpack::is_development_mode() ) {
2220
			return;
2221
		}
2222
2223
		$jetpack_old_version = Jetpack_Options::get_option( 'version' ); // [sic]
2224 View Code Duplication
		if ( ! $jetpack_old_version ) {
2225
			$jetpack_old_version = $version = $old_version = '1.1:' . time();
2226
			/** This action is documented in class.jetpack.php */
2227
			do_action( 'updating_jetpack_version', $version, false );
2228
			Jetpack_Options::update_options( compact( 'version', 'old_version' ) );
2229
		}
2230
2231
		list( $jetpack_version ) = explode( ':', $jetpack_old_version ); // [sic]
2232
2233
		if ( version_compare( JETPACK__VERSION, $jetpack_version, '<=' ) ) {
2234
			return;
2235
		}
2236
2237
		$active_modules     = Jetpack::get_active_modules();
2238
		$reactivate_modules = array();
2239
		foreach ( $active_modules as $active_module ) {
2240
			$module = Jetpack::get_module( $active_module );
2241
			if ( ! isset( $module['changed'] ) ) {
2242
				continue;
2243
			}
2244
2245
			if ( version_compare( $module['changed'], $jetpack_version, '<=' ) ) {
2246
				continue;
2247
			}
2248
2249
			$reactivate_modules[] = $active_module;
2250
			Jetpack::deactivate_module( $active_module );
2251
		}
2252
2253
		$new_version = JETPACK__VERSION . ':' . time();
2254
		/** This action is documented in class.jetpack.php */
2255
		do_action( 'updating_jetpack_version', $new_version, $jetpack_old_version );
2256
		Jetpack_Options::update_options(
2257
			array(
2258
				'version'     => $new_version,
2259
				'old_version' => $jetpack_old_version,
2260
			)
2261
		);
2262
2263
		Jetpack::state( 'message', 'modules_activated' );
2264
		Jetpack::activate_default_modules( $jetpack_version, JETPACK__VERSION, $reactivate_modules );
0 ignored issues
show
Documentation introduced by
JETPACK__VERSION is of type string, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
2265
2266
		if ( $redirect ) {
2267
			$page = 'jetpack'; // make sure we redirect to either settings or the jetpack page
2268
			if ( isset( $_GET['page'] ) && in_array( $_GET['page'], array( 'jetpack', 'jetpack_modules' ) ) ) {
2269
				$page = $_GET['page'];
2270
			}
2271
2272
			wp_safe_redirect( Jetpack::admin_url( 'page=' . $page ) );
2273
			exit;
2274
		}
2275
	}
2276
2277
	/**
2278
	 * List available Jetpack modules. Simply lists .php files in /modules/.
2279
	 * Make sure to tuck away module "library" files in a sub-directory.
2280
	 */
2281
	public static function get_available_modules( $min_version = false, $max_version = false ) {
2282
		static $modules = null;
2283
2284
		if ( ! isset( $modules ) ) {
2285
			$available_modules_option = Jetpack_Options::get_option( 'available_modules', array() );
2286
			// Use the cache if we're on the front-end and it's available...
2287
			if ( ! is_admin() && ! empty( $available_modules_option[ JETPACK__VERSION ] ) ) {
2288
				$modules = $available_modules_option[ JETPACK__VERSION ];
2289
			} else {
2290
				$files = Jetpack::glob_php( JETPACK__PLUGIN_DIR . 'modules' );
2291
2292
				$modules = array();
2293
2294
				foreach ( $files as $file ) {
2295
					if ( ! $headers = Jetpack::get_module( $file ) ) {
2296
						continue;
2297
					}
2298
2299
					$modules[ Jetpack::get_module_slug( $file ) ] = $headers['introduced'];
2300
				}
2301
2302
				Jetpack_Options::update_option( 'available_modules', array(
2303
					JETPACK__VERSION => $modules,
2304
				) );
2305
			}
2306
		}
2307
2308
		/**
2309
		 * Filters the array of modules available to be activated.
2310
		 *
2311
		 * @since 2.4.0
2312
		 *
2313
		 * @param array $modules Array of available modules.
2314
		 * @param string $min_version Minimum version number required to use modules.
2315
		 * @param string $max_version Maximum version number required to use modules.
2316
		 */
2317
		$mods = apply_filters( 'jetpack_get_available_modules', $modules, $min_version, $max_version );
2318
2319
		if ( ! $min_version && ! $max_version ) {
2320
			return array_keys( $mods );
2321
		}
2322
2323
		$r = array();
2324
		foreach ( $mods as $slug => $introduced ) {
2325
			if ( $min_version && version_compare( $min_version, $introduced, '>=' ) ) {
2326
				continue;
2327
			}
2328
2329
			if ( $max_version && version_compare( $max_version, $introduced, '<' ) ) {
2330
				continue;
2331
			}
2332
2333
			$r[] = $slug;
2334
		}
2335
2336
		return $r;
2337
	}
2338
2339
	/**
2340
	 * Default modules loaded on activation.
2341
	 */
2342
	public static function get_default_modules( $min_version = false, $max_version = false ) {
2343
		$return = array();
2344
2345
		foreach ( Jetpack::get_available_modules( $min_version, $max_version ) as $module ) {
2346
			$module_data = Jetpack::get_module( $module );
2347
2348
			switch ( strtolower( $module_data['auto_activate'] ) ) {
2349
				case 'yes' :
2350
					$return[] = $module;
2351
					break;
2352
				case 'public' :
2353
					if ( Jetpack_Options::get_option( 'public' ) ) {
2354
						$return[] = $module;
2355
					}
2356
					break;
2357
				case 'no' :
2358
				default :
2359
					break;
2360
			}
2361
		}
2362
		/**
2363
		 * Filters the array of default modules.
2364
		 *
2365
		 * @since 2.5.0
2366
		 *
2367
		 * @param array $return Array of default modules.
2368
		 * @param string $min_version Minimum version number required to use modules.
2369
		 * @param string $max_version Maximum version number required to use modules.
2370
		 */
2371
		return apply_filters( 'jetpack_get_default_modules', $return, $min_version, $max_version );
2372
	}
2373
2374
	/**
2375
	 * Checks activated modules during auto-activation to determine
2376
	 * if any of those modules are being deprecated.  If so, close
2377
	 * them out, and add any replacement modules.
2378
	 *
2379
	 * Runs at priority 99 by default.
2380
	 *
2381
	 * This is run late, so that it can still activate a module if
2382
	 * the new module is a replacement for another that the user
2383
	 * currently has active, even if something at the normal priority
2384
	 * would kibosh everything.
2385
	 *
2386
	 * @since 2.6
2387
	 * @uses jetpack_get_default_modules filter
2388
	 * @param array $modules
2389
	 * @return array
2390
	 */
2391
	function handle_deprecated_modules( $modules ) {
2392
		$deprecated_modules = array(
2393
			'debug'            => null,  // Closed out and moved to the debugger library.
2394
			'wpcc'             => 'sso', // Closed out in 2.6 -- SSO provides the same functionality.
2395
			'gplus-authorship' => null,  // Closed out in 3.2 -- Google dropped support.
2396
		);
2397
2398
		// Don't activate SSO if they never completed activating WPCC.
2399
		if ( Jetpack::is_module_active( 'wpcc' ) ) {
2400
			$wpcc_options = Jetpack_Options::get_option( 'wpcc_options' );
2401
			if ( empty( $wpcc_options ) || empty( $wpcc_options['client_id'] ) || empty( $wpcc_options['client_id'] ) ) {
2402
				$deprecated_modules['wpcc'] = null;
2403
			}
2404
		}
2405
2406
		foreach ( $deprecated_modules as $module => $replacement ) {
2407
			if ( Jetpack::is_module_active( $module ) ) {
2408
				self::deactivate_module( $module );
2409
				if ( $replacement ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $replacement of type null|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
2410
					$modules[] = $replacement;
2411
				}
2412
			}
2413
		}
2414
2415
		return array_unique( $modules );
2416
	}
2417
2418
	/**
2419
	 * Checks activated plugins during auto-activation to determine
2420
	 * if any of those plugins are in the list with a corresponding module
2421
	 * that is not compatible with the plugin. The module will not be allowed
2422
	 * to auto-activate.
2423
	 *
2424
	 * @since 2.6
2425
	 * @uses jetpack_get_default_modules filter
2426
	 * @param array $modules
2427
	 * @return array
2428
	 */
2429
	function filter_default_modules( $modules ) {
2430
2431
		$active_plugins = self::get_active_plugins();
2432
2433
		if ( ! empty( $active_plugins ) ) {
2434
2435
			// For each module we'd like to auto-activate...
2436
			foreach ( $modules as $key => $module ) {
2437
				// If there are potential conflicts for it...
2438
				if ( ! empty( $this->conflicting_plugins[ $module ] ) ) {
2439
					// For each potential conflict...
2440
					foreach ( $this->conflicting_plugins[ $module ] as $title => $plugin ) {
2441
						// If that conflicting plugin is active...
2442
						if ( in_array( $plugin, $active_plugins ) ) {
2443
							// Remove that item from being auto-activated.
2444
							unset( $modules[ $key ] );
2445
						}
2446
					}
2447
				}
2448
			}
2449
		}
2450
2451
		return $modules;
2452
	}
2453
2454
	/**
2455
	 * Extract a module's slug from its full path.
2456
	 */
2457
	public static function get_module_slug( $file ) {
2458
		return str_replace( '.php', '', basename( $file ) );
2459
	}
2460
2461
	/**
2462
	 * Generate a module's path from its slug.
2463
	 */
2464
	public static function get_module_path( $slug ) {
2465
		/**
2466
		 * Filters the path of a modules.
2467
		 *
2468
		 * @since 7.4.0
2469
		 *
2470
		 * @param array $return The absolute path to a module's root php file
2471
		 * @param string $slug The module slug
2472
		 */
2473
		return apply_filters( 'jetpack_get_module_path', JETPACK__PLUGIN_DIR . "modules/$slug.php", $slug );
2474
	}
2475
2476
	/**
2477
	 * Load module data from module file. Headers differ from WordPress
2478
	 * plugin headers to avoid them being identified as standalone
2479
	 * plugins on the WordPress plugins page.
2480
	 */
2481
	public static function get_module( $module ) {
2482
		$headers = array(
2483
			'name'                      => 'Module Name',
2484
			'description'               => 'Module Description',
2485
			'sort'                      => 'Sort Order',
2486
			'recommendation_order'      => 'Recommendation Order',
2487
			'introduced'                => 'First Introduced',
2488
			'changed'                   => 'Major Changes In',
2489
			'deactivate'                => 'Deactivate',
2490
			'free'                      => 'Free',
2491
			'requires_connection'       => 'Requires Connection',
2492
			'auto_activate'             => 'Auto Activate',
2493
			'module_tags'               => 'Module Tags',
2494
			'feature'                   => 'Feature',
2495
			'additional_search_queries' => 'Additional Search Queries',
2496
			'plan_classes'              => 'Plans',
2497
		);
2498
2499
		$file = Jetpack::get_module_path( Jetpack::get_module_slug( $module ) );
2500
2501
		$mod = Jetpack::get_file_data( $file, $headers );
2502
		if ( empty( $mod['name'] ) ) {
2503
			return false;
2504
		}
2505
2506
		$mod['sort']                    = empty( $mod['sort'] ) ? 10 : (int) $mod['sort'];
2507
		$mod['recommendation_order']    = empty( $mod['recommendation_order'] ) ? 20 : (int) $mod['recommendation_order'];
2508
		$mod['deactivate']              = empty( $mod['deactivate'] );
2509
		$mod['free']                    = empty( $mod['free'] );
2510
		$mod['requires_connection']     = ( ! empty( $mod['requires_connection'] ) && 'No' == $mod['requires_connection'] ) ? false : true;
2511
2512
		if ( empty( $mod['auto_activate'] ) || ! in_array( strtolower( $mod['auto_activate'] ), array( 'yes', 'no', 'public' ) ) ) {
2513
			$mod['auto_activate'] = 'No';
2514
		} else {
2515
			$mod['auto_activate'] = (string) $mod['auto_activate'];
2516
		}
2517
2518
		if ( $mod['module_tags'] ) {
2519
			$mod['module_tags'] = explode( ',', $mod['module_tags'] );
2520
			$mod['module_tags'] = array_map( 'trim', $mod['module_tags'] );
2521
			$mod['module_tags'] = array_map( array( __CLASS__, 'translate_module_tag' ), $mod['module_tags'] );
2522
		} else {
2523
			$mod['module_tags'] = array( self::translate_module_tag( 'Other' ) );
2524
		}
2525
2526 View Code Duplication
		if ( $mod['plan_classes'] ) {
2527
			$mod['plan_classes'] = explode( ',', $mod['plan_classes'] );
2528
			$mod['plan_classes'] = array_map( 'strtolower', array_map( 'trim', $mod['plan_classes'] ) );
2529
		} else {
2530
			$mod['plan_classes'] = array( 'free' );
2531
		}
2532
2533 View Code Duplication
		if ( $mod['feature'] ) {
2534
			$mod['feature'] = explode( ',', $mod['feature'] );
2535
			$mod['feature'] = array_map( 'trim', $mod['feature'] );
2536
		} else {
2537
			$mod['feature'] = array( self::translate_module_tag( 'Other' ) );
2538
		}
2539
2540
		/**
2541
		 * Filters the feature array on a module.
2542
		 *
2543
		 * This filter allows you to control where each module is filtered: Recommended,
2544
		 * and the default "Other" listing.
2545
		 *
2546
		 * @since 3.5.0
2547
		 *
2548
		 * @param array   $mod['feature'] The areas to feature this module:
2549
		 *     'Recommended' shows on the main Jetpack admin screen.
2550
		 *     'Other' should be the default if no other value is in the array.
2551
		 * @param string  $module The slug of the module, e.g. sharedaddy.
2552
		 * @param array   $mod All the currently assembled module data.
2553
		 */
2554
		$mod['feature'] = apply_filters( 'jetpack_module_feature', $mod['feature'], $module, $mod );
2555
2556
		/**
2557
		 * Filter the returned data about a module.
2558
		 *
2559
		 * This filter allows overriding any info about Jetpack modules. It is dangerous,
2560
		 * so please be careful.
2561
		 *
2562
		 * @since 3.6.0
2563
		 *
2564
		 * @param array   $mod    The details of the requested module.
2565
		 * @param string  $module The slug of the module, e.g. sharedaddy
2566
		 * @param string  $file   The path to the module source file.
2567
		 */
2568
		return apply_filters( 'jetpack_get_module', $mod, $module, $file );
2569
	}
2570
2571
	/**
2572
	 * Like core's get_file_data implementation, but caches the result.
2573
	 */
2574
	public static function get_file_data( $file, $headers ) {
2575
		//Get just the filename from $file (i.e. exclude full path) so that a consistent hash is generated
2576
		$file_name = basename( $file );
2577
2578
		$cache_key = 'jetpack_file_data_' . JETPACK__VERSION;
2579
2580
		$file_data_option = get_transient( $cache_key );
2581
2582
		if ( false === $file_data_option ) {
2583
			$file_data_option = array();
2584
		}
2585
2586
		$key           = md5( $file_name . serialize( $headers ) );
2587
		$refresh_cache = is_admin() && isset( $_GET['page'] ) && 'jetpack' === substr( $_GET['page'], 0, 7 );
2588
2589
		// If we don't need to refresh the cache, and already have the value, short-circuit!
2590
		if ( ! $refresh_cache && isset( $file_data_option[ $key ] ) ) {
2591
			return $file_data_option[ $key ];
2592
		}
2593
2594
		$data = get_file_data( $file, $headers );
2595
2596
		$file_data_option[ $key ] = $data;
2597
2598
		set_transient( $cache_key, $file_data_option, 29 * DAY_IN_SECONDS );
2599
2600
		return $data;
2601
	}
2602
2603
2604
	/**
2605
	 * Return translated module tag.
2606
	 *
2607
	 * @param string $tag Tag as it appears in each module heading.
2608
	 *
2609
	 * @return mixed
2610
	 */
2611
	public static function translate_module_tag( $tag ) {
2612
		return jetpack_get_module_i18n_tag( $tag );
2613
	}
2614
2615
	/**
2616
	 * Get i18n strings as a JSON-encoded string
2617
	 *
2618
	 * @return string The locale as JSON
2619
	 */
2620
	public static function get_i18n_data_json() {
2621
2622
		// WordPress 5.0 uses md5 hashes of file paths to associate translation
2623
		// JSON files with the file they should be included for. This is an md5
2624
		// of '_inc/build/admin.js'.
2625
		$path_md5 = '1bac79e646a8bf4081a5011ab72d5807';
2626
2627
		$i18n_json =
2628
				   JETPACK__PLUGIN_DIR
2629
				   . 'languages/json/jetpack-'
2630
				   . get_user_locale()
2631
				   . '-'
2632
				   . $path_md5
2633
				   . '.json';
2634
2635
		if ( is_file( $i18n_json ) && is_readable( $i18n_json ) ) {
2636
			$locale_data = @file_get_contents( $i18n_json );
2637
			if ( $locale_data ) {
2638
				return $locale_data;
2639
			}
2640
		}
2641
2642
		// Return valid empty Jed locale
2643
		return '{ "locale_data": { "messages": { "": {} } } }';
2644
	}
2645
2646
	/**
2647
	 * Add locale data setup to wp-i18n
2648
	 *
2649
	 * Any Jetpack script that depends on wp-i18n should use this method to set up the locale.
2650
	 *
2651
	 * The locale setup depends on an adding inline script. This is error-prone and could easily
2652
	 * result in multiple additions of the same script when exactly 0 or 1 is desireable.
2653
	 *
2654
	 * This method provides a safe way to request the setup multiple times but add the script at
2655
	 * most once.
2656
	 *
2657
	 * @since 6.7.0
2658
	 *
2659
	 * @return void
2660
	 */
2661
	public static function setup_wp_i18n_locale_data() {
2662
		static $script_added = false;
2663
		if ( ! $script_added ) {
2664
			$script_added = true;
2665
			wp_add_inline_script(
2666
				'wp-i18n',
2667
				'wp.i18n.setLocaleData( ' . Jetpack::get_i18n_data_json() . ', \'jetpack\' );'
2668
			);
2669
		}
2670
	}
2671
2672
	/**
2673
	 * Return module name translation. Uses matching string created in modules/module-headings.php.
2674
	 *
2675
	 * @since 3.9.2
2676
	 *
2677
	 * @param array $modules
2678
	 *
2679
	 * @return string|void
2680
	 */
2681
	public static function get_translated_modules( $modules ) {
2682
		foreach ( $modules as $index => $module ) {
2683
			$i18n_module = jetpack_get_module_i18n( $module['module'] );
2684
			if ( isset( $module['name'] ) ) {
2685
				$modules[ $index ]['name'] = $i18n_module['name'];
2686
			}
2687
			if ( isset( $module['description'] ) ) {
2688
				$modules[ $index ]['description'] = $i18n_module['description'];
2689
				$modules[ $index ]['short_description'] = $i18n_module['description'];
2690
			}
2691
		}
2692
		return $modules;
2693
	}
2694
2695
	/**
2696
	 * Get a list of activated modules as an array of module slugs.
2697
	 */
2698
	public static function get_active_modules() {
2699
		$active = Jetpack_Options::get_option( 'active_modules' );
2700
2701
		if ( ! is_array( $active ) ) {
2702
			$active = array();
2703
		}
2704
2705
		if ( class_exists( 'VaultPress' ) || function_exists( 'vaultpress_contact_service' ) ) {
2706
			$active[] = 'vaultpress';
2707
		} else {
2708
			$active = array_diff( $active, array( 'vaultpress' ) );
2709
		}
2710
2711
		//If protect is active on the main site of a multisite, it should be active on all sites.
2712
		if ( ! in_array( 'protect', $active ) && is_multisite() && get_site_option( 'jetpack_protect_active' ) ) {
2713
			$active[] = 'protect';
2714
		}
2715
2716
		/**
2717
		 * Allow filtering of the active modules.
2718
		 *
2719
		 * Gives theme and plugin developers the power to alter the modules that
2720
		 * are activated on the fly.
2721
		 *
2722
		 * @since 5.8.0
2723
		 *
2724
		 * @param array $active Array of active module slugs.
2725
		 */
2726
		$active = apply_filters( 'jetpack_active_modules', $active );
2727
2728
		return array_unique( $active );
2729
	}
2730
2731
	/**
2732
	 * Check whether or not a Jetpack module is active.
2733
	 *
2734
	 * @param string $module The slug of a Jetpack module.
2735
	 * @return bool
2736
	 *
2737
	 * @static
2738
	 */
2739
	public static function is_module_active( $module ) {
2740
		return in_array( $module, self::get_active_modules() );
2741
	}
2742
2743
	public static function is_module( $module ) {
2744
		return ! empty( $module ) && ! validate_file( $module, Jetpack::get_available_modules() );
2745
	}
2746
2747
	/**
2748
	 * Catches PHP errors.  Must be used in conjunction with output buffering.
2749
	 *
2750
	 * @param bool $catch True to start catching, False to stop.
2751
	 *
2752
	 * @static
2753
	 */
2754
	public static function catch_errors( $catch ) {
2755
		static $display_errors, $error_reporting;
2756
2757
		if ( $catch ) {
2758
			$display_errors  = @ini_set( 'display_errors', 1 );
2759
			$error_reporting = @error_reporting( E_ALL );
2760
			add_action( 'shutdown', array( 'Jetpack', 'catch_errors_on_shutdown' ), 0 );
2761
		} else {
2762
			@ini_set( 'display_errors', $display_errors );
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...
2763
			@error_reporting( $error_reporting );
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...
2764
			remove_action( 'shutdown', array( 'Jetpack', 'catch_errors_on_shutdown' ), 0 );
2765
		}
2766
	}
2767
2768
	/**
2769
	 * Saves any generated PHP errors in ::state( 'php_errors', {errors} )
2770
	 */
2771
	public static function catch_errors_on_shutdown() {
2772
		Jetpack::state( 'php_errors', self::alias_directories( ob_get_clean() ) );
2773
	}
2774
2775
	/**
2776
	 * Rewrite any string to make paths easier to read.
2777
	 *
2778
	 * Rewrites ABSPATH (eg `/home/jetpack/wordpress/`) to ABSPATH, and if WP_CONTENT_DIR
2779
	 * is located outside of ABSPATH, rewrites that to WP_CONTENT_DIR.
2780
	 *
2781
	 * @param $string
2782
	 * @return mixed
2783
	 */
2784
	public static function alias_directories( $string ) {
2785
		// ABSPATH has a trailing slash.
2786
		$string = str_replace( ABSPATH, 'ABSPATH/', $string );
2787
		// WP_CONTENT_DIR does not have a trailing slash.
2788
		$string = str_replace( WP_CONTENT_DIR, 'WP_CONTENT_DIR', $string );
2789
2790
		return $string;
2791
	}
2792
2793
	public static function activate_default_modules(
2794
		$min_version = false,
2795
		$max_version = false,
2796
		$other_modules = array(),
2797
		$redirect = true,
2798
		$send_state_messages = true
2799
	) {
2800
		$jetpack = Jetpack::init();
2801
2802
		$modules = Jetpack::get_default_modules( $min_version, $max_version );
2803
		$modules = array_merge( $other_modules, $modules );
2804
2805
		// Look for standalone plugins and disable if active.
2806
2807
		$to_deactivate = array();
2808
		foreach ( $modules as $module ) {
2809
			if ( isset( $jetpack->plugins_to_deactivate[$module] ) ) {
2810
				$to_deactivate[$module] = $jetpack->plugins_to_deactivate[$module];
2811
			}
2812
		}
2813
2814
		$deactivated = array();
2815
		foreach ( $to_deactivate as $module => $deactivate_me ) {
2816
			list( $probable_file, $probable_title ) = $deactivate_me;
2817
			if ( Jetpack_Client_Server::deactivate_plugin( $probable_file, $probable_title ) ) {
2818
				$deactivated[] = $module;
2819
			}
2820
		}
2821
2822
		if ( $deactivated && $redirect ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $deactivated of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
2823
			Jetpack::state( 'deactivated_plugins', join( ',', $deactivated ) );
2824
2825
			$url = add_query_arg(
2826
				array(
2827
					'action'   => 'activate_default_modules',
2828
					'_wpnonce' => wp_create_nonce( 'activate_default_modules' ),
2829
				),
2830
				add_query_arg( compact( 'min_version', 'max_version', 'other_modules' ), Jetpack::admin_url( 'page=jetpack' ) )
2831
			);
2832
			wp_safe_redirect( $url );
2833
			exit;
2834
		}
2835
2836
		/**
2837
		 * Fires before default modules are activated.
2838
		 *
2839
		 * @since 1.9.0
2840
		 *
2841
		 * @param string $min_version Minimum version number required to use modules.
2842
		 * @param string $max_version Maximum version number required to use modules.
2843
		 * @param array $other_modules Array of other modules to activate alongside the default modules.
2844
		 */
2845
		do_action( 'jetpack_before_activate_default_modules', $min_version, $max_version, $other_modules );
2846
2847
		// Check each module for fatal errors, a la wp-admin/plugins.php::activate before activating
2848
		if ( $send_state_messages ) {
2849
			Jetpack::restate();
2850
			Jetpack::catch_errors( true );
2851
		}
2852
2853
		$active = Jetpack::get_active_modules();
2854
2855
		foreach ( $modules as $module ) {
2856
			if ( did_action( "jetpack_module_loaded_$module" ) ) {
2857
				$active[] = $module;
2858
				self::update_active_modules( $active );
2859
				continue;
2860
			}
2861
2862
			if ( $send_state_messages && in_array( $module, $active ) ) {
2863
				$module_info = Jetpack::get_module( $module );
2864 View Code Duplication
				if ( ! $module_info['deactivate'] ) {
2865
					$state = in_array( $module, $other_modules ) ? 'reactivated_modules' : 'activated_modules';
2866
					if ( $active_state = Jetpack::state( $state ) ) {
2867
						$active_state = explode( ',', $active_state );
2868
					} else {
2869
						$active_state = array();
2870
					}
2871
					$active_state[] = $module;
2872
					Jetpack::state( $state, implode( ',', $active_state ) );
2873
				}
2874
				continue;
2875
			}
2876
2877
			$file = Jetpack::get_module_path( $module );
2878
			if ( ! file_exists( $file ) ) {
2879
				continue;
2880
			}
2881
2882
			// we'll override this later if the plugin can be included without fatal error
2883
			if ( $redirect ) {
2884
				wp_safe_redirect( Jetpack::admin_url( 'page=jetpack' ) );
2885
			}
2886
2887
			if ( $send_state_messages ) {
2888
				Jetpack::state( 'error', 'module_activation_failed' );
2889
				Jetpack::state( 'module', $module );
2890
			}
2891
2892
			ob_start();
2893
			require_once $file;
2894
2895
			$active[] = $module;
2896
2897 View Code Duplication
			if ( $send_state_messages ) {
2898
2899
				$state    = in_array( $module, $other_modules ) ? 'reactivated_modules' : 'activated_modules';
2900
				if ( $active_state = Jetpack::state( $state ) ) {
2901
					$active_state = explode( ',', $active_state );
2902
				} else {
2903
					$active_state = array();
2904
				}
2905
				$active_state[] = $module;
2906
				Jetpack::state( $state, implode( ',', $active_state ) );
2907
			}
2908
2909
			Jetpack::update_active_modules( $active );
2910
2911
			ob_end_clean();
2912
		}
2913
2914
		if ( $send_state_messages ) {
2915
			Jetpack::state( 'error', false );
0 ignored issues
show
Documentation introduced by
false is of type boolean, but the function expects a string|null.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
2916
			Jetpack::state( 'module', false );
0 ignored issues
show
Documentation introduced by
false is of type boolean, but the function expects a string|null.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
2917
		}
2918
2919
		Jetpack::catch_errors( false );
2920
		/**
2921
		 * Fires when default modules are activated.
2922
		 *
2923
		 * @since 1.9.0
2924
		 *
2925
		 * @param string $min_version Minimum version number required to use modules.
2926
		 * @param string $max_version Maximum version number required to use modules.
2927
		 * @param array $other_modules Array of other modules to activate alongside the default modules.
2928
		 */
2929
		do_action( 'jetpack_activate_default_modules', $min_version, $max_version, $other_modules );
2930
	}
2931
2932
	public static function activate_module( $module, $exit = true, $redirect = true ) {
2933
		/**
2934
		 * Fires before a module is activated.
2935
		 *
2936
		 * @since 2.6.0
2937
		 *
2938
		 * @param string $module Module slug.
2939
		 * @param bool $exit Should we exit after the module has been activated. Default to true.
2940
		 * @param bool $redirect Should the user be redirected after module activation? Default to true.
2941
		 */
2942
		do_action( 'jetpack_pre_activate_module', $module, $exit, $redirect );
2943
2944
		$jetpack = Jetpack::init();
2945
2946
		if ( ! strlen( $module ) )
2947
			return false;
2948
2949
		if ( ! Jetpack::is_module( $module ) )
2950
			return false;
2951
2952
		// If it's already active, then don't do it again
2953
		$active = Jetpack::get_active_modules();
2954
		foreach ( $active as $act ) {
2955
			if ( $act == $module )
2956
				return true;
2957
		}
2958
2959
		$module_data = Jetpack::get_module( $module );
2960
2961
		if ( ! Jetpack::is_active() ) {
2962
			if ( ! Jetpack::is_development_mode() && ! Jetpack::is_onboarding() )
2963
				return false;
2964
2965
			// If we're not connected but in development mode, make sure the module doesn't require a connection
2966
			if ( Jetpack::is_development_mode() && $module_data['requires_connection'] )
2967
				return false;
2968
		}
2969
2970
		// Check and see if the old plugin is active
2971
		if ( isset( $jetpack->plugins_to_deactivate[ $module ] ) ) {
2972
			// Deactivate the old plugin
2973
			if ( Jetpack_Client_Server::deactivate_plugin( $jetpack->plugins_to_deactivate[ $module ][0], $jetpack->plugins_to_deactivate[ $module ][1] ) ) {
2974
				// If we deactivated the old plugin, remembere that with ::state() and redirect back to this page to activate the module
2975
				// We can't activate the module on this page load since the newly deactivated old plugin is still loaded on this page load.
2976
				Jetpack::state( 'deactivated_plugins', $module );
2977
				wp_safe_redirect( add_query_arg( 'jetpack_restate', 1 ) );
2978
				exit;
2979
			}
2980
		}
2981
2982
		// Protect won't work with mis-configured IPs
2983
		if ( 'protect' === $module ) {
2984
			include_once JETPACK__PLUGIN_DIR . 'modules/protect/shared-functions.php';
2985
			if ( ! jetpack_protect_get_ip() ) {
2986
				Jetpack::state( 'message', 'protect_misconfigured_ip' );
2987
				return false;
2988
			}
2989
		}
2990
2991
		if ( ! Jetpack_Plan::supports( $module ) ) {
2992
			return false;
2993
		}
2994
2995
		// Check the file for fatal errors, a la wp-admin/plugins.php::activate
2996
		Jetpack::state( 'module', $module );
2997
		Jetpack::state( 'error', 'module_activation_failed' ); // we'll override this later if the plugin can be included without fatal error
2998
2999
		Jetpack::catch_errors( true );
3000
		ob_start();
3001
		require Jetpack::get_module_path( $module );
3002
		/** This action is documented in class.jetpack.php */
3003
		do_action( 'jetpack_activate_module', $module );
3004
		$active[] = $module;
3005
		Jetpack::update_active_modules( $active );
3006
3007
		Jetpack::state( 'error', false ); // the override
0 ignored issues
show
Documentation introduced by
false is of type boolean, but the function expects a string|null.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
3008
		ob_end_clean();
3009
		Jetpack::catch_errors( false );
3010
3011
		if ( $redirect ) {
3012
			wp_safe_redirect( Jetpack::admin_url( 'page=jetpack' ) );
3013
		}
3014
		if ( $exit ) {
3015
			exit;
3016
		}
3017
		return true;
3018
	}
3019
3020
	function activate_module_actions( $module ) {
3021
		_deprecated_function( __METHOD__, 'jetpack-4.2' );
3022
	}
3023
3024
	public static function deactivate_module( $module ) {
3025
		/**
3026
		 * Fires when a module is deactivated.
3027
		 *
3028
		 * @since 1.9.0
3029
		 *
3030
		 * @param string $module Module slug.
3031
		 */
3032
		do_action( 'jetpack_pre_deactivate_module', $module );
3033
3034
		$jetpack = Jetpack::init();
0 ignored issues
show
Unused Code introduced by
$jetpack is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
3035
3036
		$active = Jetpack::get_active_modules();
3037
		$new    = array_filter( array_diff( $active, (array) $module ) );
3038
3039
		return self::update_active_modules( $new );
3040
	}
3041
3042
	public static function enable_module_configurable( $module ) {
3043
		$module = Jetpack::get_module_slug( $module );
3044
		add_filter( 'jetpack_module_configurable_' . $module, '__return_true' );
3045
	}
3046
3047
	/**
3048
	 * Composes a module configure URL. It uses Jetpack settings search as default value
3049
	 * It is possible to redefine resulting URL by using "jetpack_module_configuration_url_$module" filter
3050
	 *
3051
	 * @param string $module Module slug
3052
	 * @return string $url module configuration URL
3053
	 */
3054
	public static function module_configuration_url( $module ) {
3055
		$module = Jetpack::get_module_slug( $module );
3056
		$default_url =  Jetpack::admin_url() . "#/settings?term=$module";
3057
		/**
3058
		 * Allows to modify configure_url of specific module to be able to redirect to some custom location.
3059
		 *
3060
		 * @since 6.9.0
3061
		 *
3062
		 * @param string $default_url Default url, which redirects to jetpack settings page.
3063
		 */
3064
		$url = apply_filters( 'jetpack_module_configuration_url_' . $module, $default_url );
3065
3066
		return $url;
3067
	}
3068
3069
/* Installation */
3070
	public static function bail_on_activation( $message, $deactivate = true ) {
3071
?>
3072
<!doctype html>
3073
<html>
3074
<head>
3075
<meta charset="<?php bloginfo( 'charset' ); ?>">
3076
<style>
3077
* {
3078
	text-align: center;
3079
	margin: 0;
3080
	padding: 0;
3081
	font-family: "Lucida Grande",Verdana,Arial,"Bitstream Vera Sans",sans-serif;
3082
}
3083
p {
3084
	margin-top: 1em;
3085
	font-size: 18px;
3086
}
3087
</style>
3088
<body>
3089
<p><?php echo esc_html( $message ); ?></p>
3090
</body>
3091
</html>
3092
<?php
3093
		if ( $deactivate ) {
3094
			$plugins = get_option( 'active_plugins' );
3095
			$jetpack = plugin_basename( JETPACK__PLUGIN_DIR . 'jetpack.php' );
3096
			$update  = false;
3097
			foreach ( $plugins as $i => $plugin ) {
3098
				if ( $plugin === $jetpack ) {
3099
					$plugins[$i] = false;
3100
					$update = true;
3101
				}
3102
			}
3103
3104
			if ( $update ) {
3105
				update_option( 'active_plugins', array_filter( $plugins ) );
3106
			}
3107
		}
3108
		exit;
3109
	}
3110
3111
	/**
3112
	 * Attached to activate_{ plugin_basename( __FILES__ ) } by register_activation_hook()
3113
	 * @static
3114
	 */
3115
	public static function plugin_activation( $network_wide ) {
3116
		Jetpack_Options::update_option( 'activated', 1 );
3117
3118
		if ( version_compare( $GLOBALS['wp_version'], JETPACK__MINIMUM_WP_VERSION, '<' ) ) {
3119
			Jetpack::bail_on_activation( sprintf( __( 'Jetpack requires WordPress version %s or later.', 'jetpack' ), JETPACK__MINIMUM_WP_VERSION ) );
3120
		}
3121
3122
		if ( $network_wide )
3123
			Jetpack::state( 'network_nag', true );
0 ignored issues
show
Documentation introduced by
true is of type boolean, but the function expects a string|null.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
3124
3125
		// For firing one-off events (notices) immediately after activation
3126
		set_transient( 'activated_jetpack', true, .1 * MINUTE_IN_SECONDS );
3127
3128
		update_option( 'jetpack_activation_source', self::get_activation_source( wp_get_referer() ) );
3129
3130
		Jetpack::plugin_initialize();
3131
	}
3132
3133
	public static function get_activation_source( $referer_url ) {
3134
3135
		if ( defined( 'WP_CLI' ) && WP_CLI ) {
3136
			return array( 'wp-cli', null );
3137
		}
3138
3139
		$referer = parse_url( $referer_url );
3140
3141
		$source_type = 'unknown';
3142
		$source_query = null;
3143
3144
		if ( ! is_array( $referer ) ) {
3145
			return array( $source_type, $source_query );
3146
		}
3147
3148
		$plugins_path = parse_url( admin_url( 'plugins.php' ), PHP_URL_PATH );
3149
		$plugins_install_path = parse_url( admin_url( 'plugin-install.php' ), PHP_URL_PATH );// /wp-admin/plugin-install.php
3150
3151
		if ( isset( $referer['query'] ) ) {
3152
			parse_str( $referer['query'], $query_parts );
3153
		} else {
3154
			$query_parts = array();
3155
		}
3156
3157
		if ( $plugins_path === $referer['path'] ) {
3158
			$source_type = 'list';
3159
		} elseif ( $plugins_install_path === $referer['path'] ) {
3160
			$tab = isset( $query_parts['tab'] ) ? $query_parts['tab'] : 'featured';
3161
			switch( $tab ) {
3162
				case 'popular':
3163
					$source_type = 'popular';
3164
					break;
3165
				case 'recommended':
3166
					$source_type = 'recommended';
3167
					break;
3168
				case 'favorites':
3169
					$source_type = 'favorites';
3170
					break;
3171
				case 'search':
3172
					$source_type = 'search-' . ( isset( $query_parts['type'] ) ? $query_parts['type'] : 'term' );
3173
					$source_query = isset( $query_parts['s'] ) ? $query_parts['s'] : null;
3174
					break;
3175
				default:
3176
					$source_type = 'featured';
3177
			}
3178
		}
3179
3180
		return array( $source_type, $source_query );
3181
	}
3182
3183
	/**
3184
	 * Runs before bumping version numbers up to a new version
3185
	 * @param  string $version    Version:timestamp
3186
	 * @param  string $old_version Old Version:timestamp or false if not set yet.
3187
	 * @return null              [description]
3188
	 */
3189
	public static function do_version_bump( $version, $old_version ) {
3190
		if ( ! $old_version ) { // For new sites
3191
			// There used to be stuff here, but this seems like it might  be useful to someone in the future...
3192
		}
3193
	}
3194
3195
	/**
3196
	 * Sets the internal version number and activation state.
3197
	 * @static
3198
	 */
3199
	public static function plugin_initialize() {
3200
		if ( ! Jetpack_Options::get_option( 'activated' ) ) {
3201
			Jetpack_Options::update_option( 'activated', 2 );
3202
		}
3203
3204 View Code Duplication
		if ( ! Jetpack_Options::get_option( 'version' ) ) {
3205
			$version = $old_version = JETPACK__VERSION . ':' . time();
3206
			/** This action is documented in class.jetpack.php */
3207
			do_action( 'updating_jetpack_version', $version, false );
3208
			Jetpack_Options::update_options( compact( 'version', 'old_version' ) );
3209
		}
3210
3211
		Jetpack::load_modules();
3212
3213
		Jetpack_Options::delete_option( 'do_activate' );
3214
		Jetpack_Options::delete_option( 'dismissed_connection_banner' );
3215
	}
3216
3217
	/**
3218
	 * Removes all connection options
3219
	 * @static
3220
	 */
3221
	public static function plugin_deactivation( ) {
3222
		require_once( ABSPATH . '/wp-admin/includes/plugin.php' );
3223
		if( is_plugin_active_for_network( 'jetpack/jetpack.php' ) ) {
3224
			Jetpack_Network::init()->deactivate();
3225
		} else {
3226
			Jetpack::disconnect( false );
3227
			//Jetpack_Heartbeat::init()->deactivate();
3228
		}
3229
	}
3230
3231
	/**
3232
	 * Disconnects from the Jetpack servers.
3233
	 * Forgets all connection details and tells the Jetpack servers to do the same.
3234
	 * @static
3235
	 */
3236
	public static function disconnect( $update_activated_state = true ) {
3237
		wp_clear_scheduled_hook( 'jetpack_clean_nonces' );
3238
		Jetpack::clean_nonces( true );
3239
3240
		// If the site is in an IDC because sync is not allowed,
3241
		// let's make sure to not disconnect the production site.
3242
		if ( ! self::validate_sync_error_idc_option() ) {
3243
			$tracking = new Tracking();
3244
			$tracking->record_user_event( 'disconnect_site', array() );
3245
			Jetpack::load_xml_rpc_client();
3246
			$xml = new Jetpack_IXR_Client();
3247
			$xml->query( 'jetpack.deregister' );
3248
		}
3249
3250
		Jetpack_Options::delete_option(
3251
			array(
3252
				'blog_token',
3253
				'user_token',
3254
				'user_tokens',
3255
				'master_user',
3256
				'time_diff',
3257
				'fallback_no_verify_ssl_certs',
3258
			)
3259
		);
3260
3261
		Jetpack_IDC::clear_all_idc_options();
3262
		Jetpack_Options::delete_raw_option( 'jetpack_secrets' );
3263
3264
		if ( $update_activated_state ) {
3265
			Jetpack_Options::update_option( 'activated', 4 );
3266
		}
3267
3268
		if ( $jetpack_unique_connection = Jetpack_Options::get_option( 'unique_connection' ) ) {
3269
			// Check then record unique disconnection if site has never been disconnected previously
3270
			if ( - 1 == $jetpack_unique_connection['disconnected'] ) {
3271
				$jetpack_unique_connection['disconnected'] = 1;
3272
			} else {
3273
				if ( 0 == $jetpack_unique_connection['disconnected'] ) {
3274
					//track unique disconnect
3275
					$jetpack = Jetpack::init();
3276
3277
					$jetpack->stat( 'connections', 'unique-disconnect' );
3278
					$jetpack->do_stats( 'server_side' );
3279
				}
3280
				// increment number of times disconnected
3281
				$jetpack_unique_connection['disconnected'] += 1;
3282
			}
3283
3284
			Jetpack_Options::update_option( 'unique_connection', $jetpack_unique_connection );
3285
		}
3286
3287
		// Delete cached connected user data
3288
		$transient_key = "jetpack_connected_user_data_" . get_current_user_id();
3289
		delete_transient( $transient_key );
3290
3291
		// Delete all the sync related data. Since it could be taking up space.
3292
		Sender::get_instance()->uninstall();
3293
3294
		// Disable the Heartbeat cron
3295
		Jetpack_Heartbeat::init()->deactivate();
3296
	}
3297
3298
	/**
3299
	 * Unlinks the current user from the linked WordPress.com user
3300
	 */
3301
	public static function unlink_user( $user_id = null ) {
3302
		if ( ! $tokens = Jetpack_Options::get_option( 'user_tokens' ) )
3303
			return false;
3304
3305
		$user_id = empty( $user_id ) ? get_current_user_id() : intval( $user_id );
3306
3307
		if ( Jetpack_Options::get_option( 'master_user' ) == $user_id )
3308
			return false;
3309
3310
		if ( ! isset( $tokens[ $user_id ] ) )
3311
			return false;
3312
3313
		Jetpack::load_xml_rpc_client();
3314
		$xml = new Jetpack_IXR_Client( compact( 'user_id' ) );
3315
		$xml->query( 'jetpack.unlink_user', $user_id );
3316
3317
		unset( $tokens[ $user_id ] );
3318
3319
		Jetpack_Options::update_option( 'user_tokens', $tokens );
3320
3321
		/**
3322
		 * Fires after the current user has been unlinked from WordPress.com.
3323
		 *
3324
		 * @since 4.1.0
3325
		 *
3326
		 * @param int $user_id The current user's ID.
3327
		 */
3328
		do_action( 'jetpack_unlinked_user', $user_id );
3329
3330
		return true;
3331
	}
3332
3333
	/**
3334
	 * Attempts Jetpack registration.  If it fail, a state flag is set: @see ::admin_page_load()
3335
	 */
3336
	public static function try_registration() {
3337
		// The user has agreed to the TOS at some point by now.
3338
		Jetpack_Options::update_option( 'tos_agreed', true );
3339
3340
		// Let's get some testing in beta versions and such.
3341
		if ( self::is_development_version() && defined( 'PHP_URL_HOST' ) ) {
3342
			// Before attempting to connect, let's make sure that the domains are viable.
3343
			$domains_to_check = array_unique( array(
3344
				'siteurl' => parse_url( get_site_url(), PHP_URL_HOST ),
3345
				'homeurl' => parse_url( get_home_url(), PHP_URL_HOST ),
3346
			) );
3347
			foreach ( $domains_to_check as $domain ) {
3348
				$result = self::connection()->is_usable_domain( $domain );
0 ignored issues
show
Security Bug introduced by
It seems like $domain defined by $domain on line 3347 can also be of type false; however, Automattic\Jetpack\Conne...ger::is_usable_domain() does only seem to accept string, did you maybe forget to handle an error condition?

This check looks for type mismatches where the missing type is false. This is usually indicative of an error condtion.

Consider the follow example

<?php

function getDate($date)
{
    if ($date !== null) {
        return new DateTime($date);
    }

    return false;
}

This function either returns a new DateTime object or false, if there was an error. This is a typical pattern in PHP programming to show that an error has occurred without raising an exception. The calling code should check for this returned false before passing on the value to another function or method that may not be able to handle a false.

Loading history...
3349
				if ( is_wp_error( $result ) ) {
3350
					return $result;
3351
				}
3352
			}
3353
		}
3354
3355
		$result = Jetpack::register();
3356
3357
		// If there was an error with registration and the site was not registered, record this so we can show a message.
3358
		if ( ! $result || is_wp_error( $result ) ) {
3359
			return $result;
3360
		} else {
3361
			return true;
3362
		}
3363
	}
3364
3365
	/**
3366
	 * Tracking an internal event log. Try not to put too much chaff in here.
3367
	 *
3368
	 * [Everyone Loves a Log!](https://www.youtube.com/watch?v=2C7mNr5WMjA)
3369
	 */
3370
	public static function log( $code, $data = null ) {
3371
		// only grab the latest 200 entries
3372
		$log = array_slice( Jetpack_Options::get_option( 'log', array() ), -199, 199 );
3373
3374
		// Append our event to the log
3375
		$log_entry = array(
3376
			'time'    => time(),
3377
			'user_id' => get_current_user_id(),
3378
			'blog_id' => Jetpack_Options::get_option( 'id' ),
3379
			'code'    => $code,
3380
		);
3381
		// Don't bother storing it unless we've got some.
3382
		if ( ! is_null( $data ) ) {
3383
			$log_entry['data'] = $data;
3384
		}
3385
		$log[] = $log_entry;
3386
3387
		// Try add_option first, to make sure it's not autoloaded.
3388
		// @todo: Add an add_option method to Jetpack_Options
3389
		if ( ! add_option( 'jetpack_log', $log, null, 'no' ) ) {
3390
			Jetpack_Options::update_option( 'log', $log );
3391
		}
3392
3393
		/**
3394
		 * Fires when Jetpack logs an internal event.
3395
		 *
3396
		 * @since 3.0.0
3397
		 *
3398
		 * @param array $log_entry {
3399
		 *	Array of details about the log entry.
3400
		 *
3401
		 *	@param string time Time of the event.
3402
		 *	@param int user_id ID of the user who trigerred the event.
3403
		 *	@param int blog_id Jetpack Blog ID.
3404
		 *	@param string code Unique name for the event.
3405
		 *	@param string data Data about the event.
3406
		 * }
3407
		 */
3408
		do_action( 'jetpack_log_entry', $log_entry );
3409
	}
3410
3411
	/**
3412
	 * Get the internal event log.
3413
	 *
3414
	 * @param $event (string) - only return the specific log events
3415
	 * @param $num   (int)    - get specific number of latest results, limited to 200
3416
	 *
3417
	 * @return array of log events || WP_Error for invalid params
3418
	 */
3419
	public static function get_log( $event = false, $num = false ) {
3420
		if ( $event && ! is_string( $event ) ) {
3421
			return new WP_Error( __( 'First param must be string or empty', 'jetpack' ) );
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with __('First param must be ...g or empty', 'jetpack').

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.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
3422
		}
3423
3424
		if ( $num && ! is_numeric( $num ) ) {
3425
			return new WP_Error( __( 'Second param must be numeric or empty', 'jetpack' ) );
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with __('Second param must be...c or empty', 'jetpack').

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.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
3426
		}
3427
3428
		$entire_log = Jetpack_Options::get_option( 'log', array() );
3429
3430
		// If nothing set - act as it did before, otherwise let's start customizing the output
3431
		if ( ! $num && ! $event ) {
3432
			return $entire_log;
3433
		} else {
3434
			$entire_log = array_reverse( $entire_log );
3435
		}
3436
3437
		$custom_log_output = array();
3438
3439
		if ( $event ) {
3440
			foreach ( $entire_log as $log_event ) {
3441
				if ( $event == $log_event[ 'code' ] ) {
3442
					$custom_log_output[] = $log_event;
3443
				}
3444
			}
3445
		} else {
3446
			$custom_log_output = $entire_log;
3447
		}
3448
3449
		if ( $num ) {
3450
			$custom_log_output = array_slice( $custom_log_output, 0, $num );
3451
		}
3452
3453
		return $custom_log_output;
3454
	}
3455
3456
	/**
3457
	 * Log modification of important settings.
3458
	 */
3459
	public static function log_settings_change( $option, $old_value, $value ) {
3460
		switch( $option ) {
3461
			case 'jetpack_sync_non_public_post_stati':
3462
				self::log( $option, $value );
3463
				break;
3464
		}
3465
	}
3466
3467
	/**
3468
	 * Return stat data for WPCOM sync
3469
	 */
3470
	public static function get_stat_data( $encode = true, $extended = true ) {
3471
		$data = Jetpack_Heartbeat::generate_stats_array();
3472
3473
		if ( $extended ) {
3474
			$additional_data = self::get_additional_stat_data();
3475
			$data = array_merge( $data, $additional_data );
3476
		}
3477
3478
		if ( $encode ) {
3479
			return json_encode( $data );
3480
		}
3481
3482
		return $data;
3483
	}
3484
3485
	/**
3486
	 * Get additional stat data to sync to WPCOM
3487
	 */
3488
	public static function get_additional_stat_data( $prefix = '' ) {
3489
		$return["{$prefix}themes"]         = Jetpack::get_parsed_theme_data();
0 ignored issues
show
Coding Style Comprehensibility introduced by
$return was never initialized. Although not strictly required by PHP, it is generally a good practice to add $return = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
3490
		$return["{$prefix}plugins-extra"]  = Jetpack::get_parsed_plugin_data();
3491
		$return["{$prefix}users"]          = (int) Jetpack::get_site_user_count();
3492
		$return["{$prefix}site-count"]     = 0;
3493
3494
		if ( function_exists( 'get_blog_count' ) ) {
3495
			$return["{$prefix}site-count"] = get_blog_count();
3496
		}
3497
		return $return;
3498
	}
3499
3500
	private static function get_site_user_count() {
3501
		global $wpdb;
3502
3503
		if ( function_exists( 'wp_is_large_network' ) ) {
3504
			if ( wp_is_large_network( 'users' ) ) {
3505
				return -1; // Not a real value but should tell us that we are dealing with a large network.
3506
			}
3507
		}
3508 View Code Duplication
		if ( false === ( $user_count = get_transient( 'jetpack_site_user_count' ) ) ) {
3509
			// It wasn't there, so regenerate the data and save the transient
3510
			$user_count = $wpdb->get_var( "SELECT COUNT(*) FROM $wpdb->usermeta WHERE meta_key = '{$wpdb->prefix}capabilities'" );
3511
			set_transient( 'jetpack_site_user_count', $user_count, DAY_IN_SECONDS );
3512
		}
3513
		return $user_count;
3514
	}
3515
3516
	/* Admin Pages */
3517
3518
	function admin_init() {
3519
		// If the plugin is not connected, display a connect message.
3520
		if (
3521
			// the plugin was auto-activated and needs its candy
3522
			Jetpack_Options::get_option_and_ensure_autoload( 'do_activate', '0' )
3523
		||
3524
			// the plugin is active, but was never activated.  Probably came from a site-wide network activation
3525
			! Jetpack_Options::get_option( 'activated' )
3526
		) {
3527
			Jetpack::plugin_initialize();
3528
		}
3529
3530
		if ( ! Jetpack::is_active() && ! Jetpack::is_development_mode() ) {
3531
			Jetpack_Connection_Banner::init();
3532
		} elseif ( false === Jetpack_Options::get_option( 'fallback_no_verify_ssl_certs' ) ) {
3533
			// Upgrade: 1.1 -> 1.1.1
3534
			// Check and see if host can verify the Jetpack servers' SSL certificate
3535
			$args = array();
3536
			Client::_wp_remote_request(
3537
				Jetpack::fix_url_for_bad_hosts( Jetpack::api_url( 'test' ) ),
3538
				$args,
3539
				true
3540
			);
3541
		}
3542
3543
		if ( current_user_can( 'manage_options' ) && 'AUTO' == JETPACK_CLIENT__HTTPS && ! self::permit_ssl() ) {
3544
			add_action( 'jetpack_notices', array( $this, 'alert_auto_ssl_fail' ) );
3545
		}
3546
3547
		add_action( 'load-plugins.php', array( $this, 'intercept_plugin_error_scrape_init' ) );
3548
		add_action( 'admin_enqueue_scripts', array( $this, 'admin_menu_css' ) );
3549
		add_filter( 'plugin_action_links_' . plugin_basename( JETPACK__PLUGIN_DIR . 'jetpack.php' ), array( $this, 'plugin_action_links' ) );
3550
3551
		if ( Jetpack::is_active() || Jetpack::is_development_mode() ) {
3552
			// Artificially throw errors in certain whitelisted cases during plugin activation
3553
			add_action( 'activate_plugin', array( $this, 'throw_error_on_activate_plugin' ) );
3554
		}
3555
3556
		// Add custom column in wp-admin/users.php to show whether user is linked.
3557
		add_filter( 'manage_users_columns',       array( $this, 'jetpack_icon_user_connected' ) );
3558
		add_action( 'manage_users_custom_column', array( $this, 'jetpack_show_user_connected_icon' ), 10, 3 );
3559
		add_action( 'admin_print_styles',         array( $this, 'jetpack_user_col_style' ) );
3560
	}
3561
3562
	function admin_body_class( $admin_body_class = '' ) {
3563
		$classes = explode( ' ', trim( $admin_body_class ) );
3564
3565
		$classes[] = self::is_active() ? 'jetpack-connected' : 'jetpack-disconnected';
3566
3567
		$admin_body_class = implode( ' ', array_unique( $classes ) );
3568
		return " $admin_body_class ";
3569
	}
3570
3571
	static function add_jetpack_pagestyles( $admin_body_class = '' ) {
3572
		return $admin_body_class . ' jetpack-pagestyles ';
3573
	}
3574
3575
	/**
3576
	 * Sometimes a plugin can activate without causing errors, but it will cause errors on the next page load.
3577
	 * This function artificially throws errors for such cases (whitelisted).
3578
	 *
3579
	 * @param string $plugin The activated plugin.
3580
	 */
3581
	function throw_error_on_activate_plugin( $plugin ) {
3582
		$active_modules = Jetpack::get_active_modules();
3583
3584
		// The Shortlinks module and the Stats plugin conflict, but won't cause errors on activation because of some function_exists() checks.
3585
		if ( function_exists( 'stats_get_api_key' ) && in_array( 'shortlinks', $active_modules ) ) {
3586
			$throw = false;
3587
3588
			// Try and make sure it really was the stats plugin
3589
			if ( ! class_exists( 'ReflectionFunction' ) ) {
3590
				if ( 'stats.php' == basename( $plugin ) ) {
3591
					$throw = true;
3592
				}
3593
			} else {
3594
				$reflection = new ReflectionFunction( 'stats_get_api_key' );
3595
				if ( basename( $plugin ) == basename( $reflection->getFileName() ) ) {
3596
					$throw = true;
3597
				}
3598
			}
3599
3600
			if ( $throw ) {
3601
				trigger_error( sprintf( __( 'Jetpack contains the most recent version of the old &#8220;%1$s&#8221; plugin.', 'jetpack' ), 'WordPress.com Stats' ), E_USER_ERROR );
3602
			}
3603
		}
3604
	}
3605
3606
	function intercept_plugin_error_scrape_init() {
3607
		add_action( 'check_admin_referer', array( $this, 'intercept_plugin_error_scrape' ), 10, 2 );
3608
	}
3609
3610
	function intercept_plugin_error_scrape( $action, $result ) {
3611
		if ( ! $result ) {
3612
			return;
3613
		}
3614
3615
		foreach ( $this->plugins_to_deactivate as $deactivate_me ) {
3616
			if ( "plugin-activation-error_{$deactivate_me[0]}" == $action ) {
3617
				Jetpack::bail_on_activation( sprintf( __( 'Jetpack contains the most recent version of the old &#8220;%1$s&#8221; plugin.', 'jetpack' ), $deactivate_me[1] ), false );
3618
			}
3619
		}
3620
	}
3621
3622
	function add_remote_request_handlers() {
3623
		add_action( 'wp_ajax_nopriv_jetpack_upload_file', array( $this, 'remote_request_handlers' ) );
3624
		add_action( 'wp_ajax_nopriv_jetpack_update_file', array( $this, 'remote_request_handlers' ) );
3625
	}
3626
3627
	function remote_request_handlers() {
3628
		$action = current_filter();
0 ignored issues
show
Unused Code introduced by
$action is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
3629
3630
		switch ( current_filter() ) {
3631
		case 'wp_ajax_nopriv_jetpack_upload_file' :
3632
			$response = $this->upload_handler();
3633
			break;
3634
3635
		case 'wp_ajax_nopriv_jetpack_update_file' :
3636
			$response = $this->upload_handler( true );
3637
			break;
3638
		default :
3639
			$response = new Jetpack_Error( 'unknown_handler', 'Unknown Handler', 400 );
0 ignored issues
show
Unused Code introduced by
The call to Jetpack_Error::__construct() has too many arguments starting with 'unknown_handler'.

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.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
3640
			break;
3641
		}
3642
3643
		if ( ! $response ) {
3644
			$response = new Jetpack_Error( 'unknown_error', 'Unknown Error', 400 );
0 ignored issues
show
Unused Code introduced by
The call to Jetpack_Error::__construct() has too many arguments starting with 'unknown_error'.

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.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
3645
		}
3646
3647
		if ( is_wp_error( $response ) ) {
3648
			$status_code       = $response->get_error_data();
0 ignored issues
show
Bug introduced by
The method get_error_data() does not seem to exist on object<Jetpack_Error>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
3649
			$error             = $response->get_error_code();
0 ignored issues
show
Bug introduced by
The method get_error_code() does not seem to exist on object<Jetpack_Error>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
3650
			$error_description = $response->get_error_message();
0 ignored issues
show
Bug introduced by
The method get_error_message() does not seem to exist on object<Jetpack_Error>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
3651
3652
			if ( ! is_int( $status_code ) ) {
3653
				$status_code = 400;
3654
			}
3655
3656
			status_header( $status_code );
3657
			die( json_encode( (object) compact( 'error', 'error_description' ) ) );
3658
		}
3659
3660
		status_header( 200 );
3661
		if ( true === $response ) {
3662
			exit;
3663
		}
3664
3665
		die( json_encode( (object) $response ) );
3666
	}
3667
3668
	/**
3669
	 * Uploads a file gotten from the global $_FILES.
3670
	 * If `$update_media_item` is true and `post_id` is defined
3671
	 * the attachment file of the media item (gotten through of the post_id)
3672
	 * will be updated instead of add a new one.
3673
	 *
3674
	 * @param  boolean $update_media_item - update media attachment
3675
	 * @return array - An array describing the uploadind files process
3676
	 */
3677
	function upload_handler( $update_media_item = false ) {
3678
		if ( 'POST' !== strtoupper( $_SERVER['REQUEST_METHOD'] ) ) {
3679
			return new Jetpack_Error( 405, get_status_header_desc( 405 ), 405 );
0 ignored issues
show
Unused Code introduced by
The call to Jetpack_Error::__construct() has too many arguments starting with 405.

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.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
3680
		}
3681
3682
		$user = wp_authenticate( '', '' );
3683
		if ( ! $user || is_wp_error( $user ) ) {
3684
			return new Jetpack_Error( 403, get_status_header_desc( 403 ), 403 );
0 ignored issues
show
Unused Code introduced by
The call to Jetpack_Error::__construct() has too many arguments starting with 403.

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.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
3685
		}
3686
3687
		wp_set_current_user( $user->ID );
3688
3689
		if ( ! current_user_can( 'upload_files' ) ) {
3690
			return new Jetpack_Error( 'cannot_upload_files', 'User does not have permission to upload files', 403 );
0 ignored issues
show
Unused Code introduced by
The call to Jetpack_Error::__construct() has too many arguments starting with 'cannot_upload_files'.

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.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
3691
		}
3692
3693
		if ( empty( $_FILES ) ) {
3694
			return new Jetpack_Error( 'no_files_uploaded', 'No files were uploaded: nothing to process', 400 );
0 ignored issues
show
Unused Code introduced by
The call to Jetpack_Error::__construct() has too many arguments starting with 'no_files_uploaded'.

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.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
3695
		}
3696
3697
		foreach ( array_keys( $_FILES ) as $files_key ) {
3698
			if ( ! isset( $_POST["_jetpack_file_hmac_{$files_key}"] ) ) {
3699
				return new Jetpack_Error( 'missing_hmac', 'An HMAC for one or more files is missing', 400 );
0 ignored issues
show
Unused Code introduced by
The call to Jetpack_Error::__construct() has too many arguments starting with 'missing_hmac'.

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.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
3700
			}
3701
		}
3702
3703
		$media_keys = array_keys( $_FILES['media'] );
3704
3705
		$token = Jetpack_Data::get_access_token( get_current_user_id() );
0 ignored issues
show
Deprecated Code introduced by
The method Jetpack_Data::get_access_token() has been deprecated with message: 7.5 Use Connection_Manager instead.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
3706
		if ( ! $token || is_wp_error( $token ) ) {
3707
			return new Jetpack_Error( 'unknown_token', 'Unknown Jetpack token', 403 );
0 ignored issues
show
Unused Code introduced by
The call to Jetpack_Error::__construct() has too many arguments starting with 'unknown_token'.

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.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
3708
		}
3709
3710
		$uploaded_files = array();
3711
		$global_post    = isset( $GLOBALS['post'] ) ? $GLOBALS['post'] : null;
3712
		unset( $GLOBALS['post'] );
3713
		foreach ( $_FILES['media']['name'] as $index => $name ) {
3714
			$file = array();
3715
			foreach ( $media_keys as $media_key ) {
3716
				$file[$media_key] = $_FILES['media'][$media_key][$index];
3717
			}
3718
3719
			list( $hmac_provided, $salt ) = explode( ':', $_POST['_jetpack_file_hmac_media'][$index] );
3720
3721
			$hmac_file = hash_hmac_file( 'sha1', $file['tmp_name'], $salt . $token->secret );
3722
			if ( $hmac_provided !== $hmac_file ) {
3723
				$uploaded_files[$index] = (object) array( 'error' => 'invalid_hmac', 'error_description' => 'The corresponding HMAC for this file does not match' );
3724
				continue;
3725
			}
3726
3727
			$_FILES['.jetpack.upload.'] = $file;
3728
			$post_id = isset( $_POST['post_id'][$index] ) ? absint( $_POST['post_id'][$index] ) : 0;
3729
			if ( ! current_user_can( 'edit_post', $post_id ) ) {
3730
				$post_id = 0;
3731
			}
3732
3733
			if ( $update_media_item ) {
3734
				if ( ! isset( $post_id ) || $post_id === 0 ) {
3735
					return new Jetpack_Error( 'invalid_input', 'Media ID must be defined.', 400 );
0 ignored issues
show
Unused Code introduced by
The call to Jetpack_Error::__construct() has too many arguments starting with 'invalid_input'.

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.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
3736
				}
3737
3738
				$media_array = $_FILES['media'];
3739
3740
				$file_array['name'] = $media_array['name'][0];
0 ignored issues
show
Coding Style Comprehensibility introduced by
$file_array was never initialized. Although not strictly required by PHP, it is generally a good practice to add $file_array = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
3741
				$file_array['type'] = $media_array['type'][0];
3742
				$file_array['tmp_name'] = $media_array['tmp_name'][0];
3743
				$file_array['error'] = $media_array['error'][0];
3744
				$file_array['size'] = $media_array['size'][0];
3745
3746
				$edited_media_item = Jetpack_Media::edit_media_file( $post_id, $file_array );
3747
3748
				if ( is_wp_error( $edited_media_item ) ) {
3749
					return $edited_media_item;
3750
				}
3751
3752
				$response = (object) array(
3753
					'id'   => (string) $post_id,
3754
					'file' => (string) $edited_media_item->post_title,
3755
					'url'  => (string) wp_get_attachment_url( $post_id ),
3756
					'type' => (string) $edited_media_item->post_mime_type,
3757
					'meta' => (array) wp_get_attachment_metadata( $post_id ),
3758
				);
3759
3760
				return (array) array( $response );
3761
			}
3762
3763
			$attachment_id = media_handle_upload(
3764
				'.jetpack.upload.',
3765
				$post_id,
3766
				array(),
3767
				array(
3768
					'action' => 'jetpack_upload_file',
3769
				)
3770
			);
3771
3772
			if ( ! $attachment_id ) {
3773
				$uploaded_files[$index] = (object) array( 'error' => 'unknown', 'error_description' => 'An unknown problem occurred processing the upload on the Jetpack site' );
3774
			} elseif ( is_wp_error( $attachment_id ) ) {
3775
				$uploaded_files[$index] = (object) array( 'error' => 'attachment_' . $attachment_id->get_error_code(), 'error_description' => $attachment_id->get_error_message() );
3776
			} else {
3777
				$attachment = get_post( $attachment_id );
3778
				$uploaded_files[$index] = (object) array(
3779
					'id'   => (string) $attachment_id,
3780
					'file' => $attachment->post_title,
3781
					'url'  => wp_get_attachment_url( $attachment_id ),
3782
					'type' => $attachment->post_mime_type,
3783
					'meta' => wp_get_attachment_metadata( $attachment_id ),
3784
				);
3785
				// Zip files uploads are not supported unless they are done for installation purposed
3786
				// lets delete them in case something goes wrong in this whole process
3787
				if ( 'application/zip' === $attachment->post_mime_type ) {
3788
					// Schedule a cleanup for 2 hours from now in case of failed install.
3789
					wp_schedule_single_event( time() + 2 * HOUR_IN_SECONDS, 'upgrader_scheduled_cleanup', array( $attachment_id ) );
3790
				}
3791
			}
3792
		}
3793
		if ( ! is_null( $global_post ) ) {
3794
			$GLOBALS['post'] = $global_post;
3795
		}
3796
3797
		return $uploaded_files;
3798
	}
3799
3800
	/**
3801
	 * Add help to the Jetpack page
3802
	 *
3803
	 * @since Jetpack (1.2.3)
3804
	 * @return false if not the Jetpack page
3805
	 */
3806
	function admin_help() {
3807
		$current_screen = get_current_screen();
3808
3809
		// Overview
3810
		$current_screen->add_help_tab(
3811
			array(
3812
				'id'		=> 'home',
3813
				'title'		=> __( 'Home', 'jetpack' ),
3814
				'content'	=>
3815
					'<p><strong>' . __( 'Jetpack by WordPress.com', 'jetpack' ) . '</strong></p>' .
3816
					'<p>' . __( 'Jetpack supercharges your self-hosted WordPress site with the awesome cloud power of WordPress.com.', 'jetpack' ) . '</p>' .
3817
					'<p>' . __( 'On this page, you are able to view the modules available within Jetpack, learn more about them, and activate or deactivate them as needed.', 'jetpack' ) . '</p>',
3818
			)
3819
		);
3820
3821
		// Screen Content
3822
		if ( current_user_can( 'manage_options' ) ) {
3823
			$current_screen->add_help_tab(
3824
				array(
3825
					'id'		=> 'settings',
3826
					'title'		=> __( 'Settings', 'jetpack' ),
3827
					'content'	=>
3828
						'<p><strong>' . __( 'Jetpack by WordPress.com',                                              'jetpack' ) . '</strong></p>' .
3829
						'<p>' . __( 'You can activate or deactivate individual Jetpack modules to suit your needs.', 'jetpack' ) . '</p>' .
3830
						'<ol>' .
3831
							'<li>' . __( 'Each module has an Activate or Deactivate link so you can toggle one individually.',														'jetpack' ) . '</li>' .
3832
							'<li>' . __( 'Using the checkboxes next to each module, you can select multiple modules to toggle via the Bulk Actions menu at the top of the list.',	'jetpack' ) . '</li>' .
3833
						'</ol>' .
3834
						'<p>' . __( 'Using the tools on the right, you can search for specific modules, filter by module categories or which are active, or change the sorting order.', 'jetpack' ) . '</p>'
3835
				)
3836
			);
3837
		}
3838
3839
		// Help Sidebar
3840
		$current_screen->set_help_sidebar(
3841
			'<p><strong>' . __( 'For more information:', 'jetpack' ) . '</strong></p>' .
3842
			'<p><a href="https://jetpack.com/faq/" target="_blank">'     . __( 'Jetpack FAQ',     'jetpack' ) . '</a></p>' .
3843
			'<p><a href="https://jetpack.com/support/" target="_blank">' . __( 'Jetpack Support', 'jetpack' ) . '</a></p>' .
3844
			'<p><a href="' . Jetpack::admin_url( array( 'page' => 'jetpack-debugger' )  ) .'">' . __( 'Jetpack Debugging Center', 'jetpack' ) . '</a></p>'
3845
		);
3846
	}
3847
3848
	function admin_menu_css() {
3849
		wp_enqueue_style( 'jetpack-icons' );
3850
	}
3851
3852
	function admin_menu_order() {
3853
		return true;
3854
	}
3855
3856 View Code Duplication
	function jetpack_menu_order( $menu_order ) {
3857
		$jp_menu_order = array();
3858
3859
		foreach ( $menu_order as $index => $item ) {
3860
			if ( $item != 'jetpack' ) {
3861
				$jp_menu_order[] = $item;
3862
			}
3863
3864
			if ( $index == 0 ) {
3865
				$jp_menu_order[] = 'jetpack';
3866
			}
3867
		}
3868
3869
		return $jp_menu_order;
3870
	}
3871
3872
	function admin_banner_styles() {
3873
		$min = ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) ? '' : '.min';
3874
3875
		if ( ! wp_style_is( 'jetpack-dops-style' ) ) {
3876
			wp_register_style(
3877
				'jetpack-dops-style',
3878
				plugins_url( '_inc/build/admin.css', JETPACK__PLUGIN_FILE ),
3879
				array(),
3880
				JETPACK__VERSION
3881
			);
3882
		}
3883
3884
		wp_enqueue_style(
3885
			'jetpack',
3886
			plugins_url( "css/jetpack-banners{$min}.css", JETPACK__PLUGIN_FILE ),
3887
			array( 'jetpack-dops-style' ),
3888
			 JETPACK__VERSION . '-20121016'
3889
		);
3890
		wp_style_add_data( 'jetpack', 'rtl', 'replace' );
3891
		wp_style_add_data( 'jetpack', 'suffix', $min );
3892
	}
3893
3894
	function plugin_action_links( $actions ) {
3895
3896
		$jetpack_home = array( 'jetpack-home' => sprintf( '<a href="%s">%s</a>', Jetpack::admin_url( 'page=jetpack' ), 'Jetpack' ) );
3897
3898
		if( current_user_can( 'jetpack_manage_modules' ) && ( Jetpack::is_active() || Jetpack::is_development_mode() ) ) {
3899
			return array_merge(
3900
				$jetpack_home,
3901
				array( 'settings' => sprintf( '<a href="%s">%s</a>', Jetpack::admin_url( 'page=jetpack#/settings' ), __( 'Settings', 'jetpack' ) ) ),
3902
				array( 'support' => sprintf( '<a href="%s">%s</a>', Jetpack::admin_url( 'page=jetpack-debugger '), __( 'Support', 'jetpack' ) ) ),
3903
				$actions
3904
				);
3905
			}
3906
3907
		return array_merge( $jetpack_home, $actions );
3908
	}
3909
3910
	/*
3911
	 * Registration flow:
3912
	 * 1 - ::admin_page_load() action=register
3913
	 * 2 - ::try_registration()
3914
	 * 3 - ::register()
3915
	 *     - Creates jetpack_register option containing two secrets and a timestamp
3916
	 *     - Calls https://jetpack.wordpress.com/jetpack.register/1/ with
3917
	 *       siteurl, home, gmt_offset, timezone_string, site_name, secret_1, secret_2, site_lang, timeout, stats_id
3918
	 *     - That request to jetpack.wordpress.com does not immediately respond.  It first makes a request BACK to this site's
3919
	 *       xmlrpc.php?for=jetpack: RPC method: jetpack.verifyRegistration, Parameters: secret_1
3920
	 *     - The XML-RPC request verifies secret_1, deletes both secrets and responds with: secret_2
3921
	 *     - https://jetpack.wordpress.com/jetpack.register/1/ verifies that XML-RPC response (secret_2) then finally responds itself with
3922
	 *       jetpack_id, jetpack_secret, jetpack_public
3923
	 *     - ::register() then stores jetpack_options: id => jetpack_id, blog_token => jetpack_secret
3924
	 * 4 - redirect to https://wordpress.com/start/jetpack-connect
3925
	 * 5 - user logs in with WP.com account
3926
	 * 6 - remote request to this site's xmlrpc.php with action remoteAuthorize, Jetpack_XMLRPC_Server->remote_authorize
3927
	 *		- Jetpack_Client_Server::authorize()
3928
	 *		- Jetpack_Client_Server::get_token()
3929
	 *		- GET https://jetpack.wordpress.com/jetpack.token/1/ with
3930
	 *        client_id, client_secret, grant_type, code, redirect_uri:action=authorize, state, scope, user_email, user_login
3931
	 *			- which responds with access_token, token_type, scope
3932
	 *		- Jetpack_Client_Server::authorize() stores jetpack_options: user_token => access_token.$user_id
3933
	 *		- Jetpack::activate_default_modules()
3934
	 *     		- Deactivates deprecated plugins
3935
	 *     		- Activates all default modules
3936
	 *		- Responds with either error, or 'connected' for new connection, or 'linked' for additional linked users
3937
	 * 7 - For a new connection, user selects a Jetpack plan on wordpress.com
3938
	 * 8 - User is redirected back to wp-admin/index.php?page=jetpack with state:message=authorized
3939
	 *     Done!
3940
	 */
3941
3942
	/**
3943
	 * Handles the page load events for the Jetpack admin page
3944
	 */
3945
	function admin_page_load() {
3946
		$error = false;
3947
3948
		// Make sure we have the right body class to hook stylings for subpages off of.
3949
		add_filter( 'admin_body_class', array( __CLASS__, 'add_jetpack_pagestyles' ) );
3950
3951
		if ( ! empty( $_GET['jetpack_restate'] ) ) {
3952
			// Should only be used in intermediate redirects to preserve state across redirects
3953
			Jetpack::restate();
3954
		}
3955
3956
		if ( isset( $_GET['connect_url_redirect'] ) ) {
3957
			// @todo: Add validation against a known whitelist
3958
			$from = ! empty( $_GET['from'] ) ? $_GET['from'] : 'iframe';
3959
			// User clicked in the iframe to link their accounts
3960
			if ( ! Jetpack::is_user_connected() ) {
3961
				$redirect = ! empty( $_GET['redirect_after_auth'] ) ? $_GET['redirect_after_auth'] : false;
3962
3963
				add_filter( 'allowed_redirect_hosts', array( &$this, 'allow_wpcom_environments' ) );
3964
				$connect_url = $this->build_connect_url( true, $redirect, $from );
3965
				remove_filter( 'allowed_redirect_hosts', array( &$this, 'allow_wpcom_environments' ) );
3966
3967
				if ( isset( $_GET['notes_iframe'] ) )
3968
					$connect_url .= '&notes_iframe';
3969
				wp_redirect( $connect_url );
3970
				exit;
3971
			} else {
3972
				if ( ! isset( $_GET['calypso_env'] ) ) {
3973
					Jetpack::state( 'message', 'already_authorized' );
3974
					wp_safe_redirect( Jetpack::admin_url() );
3975
					exit;
3976
				} else {
3977
					$connect_url = $this->build_connect_url( true, false, $from );
3978
					$connect_url .= '&already_authorized=true';
3979
					wp_redirect( $connect_url );
3980
					exit;
3981
				}
3982
			}
3983
		}
3984
3985
3986
		if ( isset( $_GET['action'] ) ) {
3987
			switch ( $_GET['action'] ) {
3988
			case 'authorize':
3989
				if ( Jetpack::is_active() && Jetpack::is_user_connected() ) {
3990
					Jetpack::state( 'message', 'already_authorized' );
3991
					wp_safe_redirect( Jetpack::admin_url() );
3992
					exit;
3993
				}
3994
				Jetpack::log( 'authorize' );
3995
				$client_server = new Jetpack_Client_Server;
3996
				$client_server->client_authorize();
3997
				exit;
3998
			case 'register' :
3999
				if ( ! current_user_can( 'jetpack_connect' ) ) {
4000
					$error = 'cheatin';
4001
					break;
4002
				}
4003
				check_admin_referer( 'jetpack-register' );
4004
				Jetpack::log( 'register' );
4005
				Jetpack::maybe_set_version_option();
4006
				$registered = Jetpack::try_registration();
4007
				if ( is_wp_error( $registered ) ) {
4008
					$error = $registered->get_error_code();
0 ignored issues
show
Bug introduced by
The method get_error_code() does not seem to exist on object<WP_Error>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
4009
					Jetpack::state( 'error', $error );
4010
					Jetpack::state( 'error', $registered->get_error_message() );
0 ignored issues
show
Bug introduced by
The method get_error_message() does not seem to exist on object<WP_Error>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
4011
4012
					/**
4013
					 * Jetpack registration Error.
4014
					 *
4015
					 * @since 7.5.0
4016
					 *
4017
					 * @param string|int $error The error code.
4018
					 * @param \WP_Error $registered The error object.
4019
					 */
4020
					do_action( 'jetpack_connection_register_fail', $error, $registered );
4021
					break;
4022
				}
4023
4024
				$from = isset( $_GET['from'] ) ? $_GET['from'] : false;
4025
				$redirect = isset( $_GET['redirect'] ) ? $_GET['redirect'] : false;
4026
4027
				/**
4028
				 * Jetpack registration Success.
4029
				 *
4030
				 * @since 7.5.0
4031
				 *
4032
				 * @param string $from 'from' GET parameter;
4033
				 */
4034
				do_action( 'jetpack_connection_register_success', $from );
4035
4036
				$url = $this->build_connect_url( true, $redirect, $from );
4037
4038
				if ( ! empty( $_GET['onboarding'] ) ) {
4039
					$url = add_query_arg( 'onboarding', $_GET['onboarding'], $url );
4040
				}
4041
4042
				if ( ! empty( $_GET['auth_approved'] ) && 'true' === $_GET['auth_approved'] ) {
4043
					$url = add_query_arg( 'auth_approved', 'true', $url );
4044
				}
4045
4046
				wp_redirect( $url );
4047
				exit;
4048
			case 'activate' :
4049
				if ( ! current_user_can( 'jetpack_activate_modules' ) ) {
4050
					$error = 'cheatin';
4051
					break;
4052
				}
4053
4054
				$module = stripslashes( $_GET['module'] );
4055
				check_admin_referer( "jetpack_activate-$module" );
4056
				Jetpack::log( 'activate', $module );
4057
				if ( ! Jetpack::activate_module( $module ) ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression \Jetpack::activate_module($module) of type boolean|null is loosely compared to false; this is ambiguous if the boolean can be false. You might want to explicitly use !== null instead.

If an expression can have both false, and null as possible values. It is generally a good practice to always use strict comparison to clearly distinguish between those two values.

$a = canBeFalseAndNull();

// Instead of
if ( ! $a) { }

// Better use one of the explicit versions:
if ($a !== null) { }
if ($a !== false) { }
if ($a !== null && $a !== false) { }
Loading history...
4058
					Jetpack::state( 'error', sprintf( __( 'Could not activate %s', 'jetpack' ), $module ) );
4059
				}
4060
				// The following two lines will rarely happen, as Jetpack::activate_module normally exits at the end.
4061
				wp_safe_redirect( Jetpack::admin_url( 'page=jetpack' ) );
4062
				exit;
4063
			case 'activate_default_modules' :
4064
				check_admin_referer( 'activate_default_modules' );
4065
				Jetpack::log( 'activate_default_modules' );
4066
				Jetpack::restate();
4067
				$min_version   = isset( $_GET['min_version'] ) ? $_GET['min_version'] : false;
4068
				$max_version   = isset( $_GET['max_version'] ) ? $_GET['max_version'] : false;
4069
				$other_modules = isset( $_GET['other_modules'] ) && is_array( $_GET['other_modules'] ) ? $_GET['other_modules'] : array();
4070
				Jetpack::activate_default_modules( $min_version, $max_version, $other_modules );
4071
				wp_safe_redirect( Jetpack::admin_url( 'page=jetpack' ) );
4072
				exit;
4073
			case 'disconnect' :
4074
				if ( ! current_user_can( 'jetpack_disconnect' ) ) {
4075
					$error = 'cheatin';
4076
					break;
4077
				}
4078
4079
				check_admin_referer( 'jetpack-disconnect' );
4080
				Jetpack::log( 'disconnect' );
4081
				Jetpack::disconnect();
4082
				wp_safe_redirect( Jetpack::admin_url( 'disconnected=true' ) );
4083
				exit;
4084
			case 'reconnect' :
4085
				if ( ! current_user_can( 'jetpack_reconnect' ) ) {
4086
					$error = 'cheatin';
4087
					break;
4088
				}
4089
4090
				check_admin_referer( 'jetpack-reconnect' );
4091
				Jetpack::log( 'reconnect' );
4092
				$this->disconnect();
4093
				wp_redirect( $this->build_connect_url( true, false, 'reconnect' ) );
4094
				exit;
4095 View Code Duplication
			case 'deactivate' :
4096
				if ( ! current_user_can( 'jetpack_deactivate_modules' ) ) {
4097
					$error = 'cheatin';
4098
					break;
4099
				}
4100
4101
				$modules = stripslashes( $_GET['module'] );
4102
				check_admin_referer( "jetpack_deactivate-$modules" );
4103
				foreach ( explode( ',', $modules ) as $module ) {
4104
					Jetpack::log( 'deactivate', $module );
4105
					Jetpack::deactivate_module( $module );
4106
					Jetpack::state( 'message', 'module_deactivated' );
4107
				}
4108
				Jetpack::state( 'module', $modules );
4109
				wp_safe_redirect( Jetpack::admin_url( 'page=jetpack' ) );
4110
				exit;
4111
			case 'unlink' :
4112
				$redirect = isset( $_GET['redirect'] ) ? $_GET['redirect'] : '';
4113
				check_admin_referer( 'jetpack-unlink' );
4114
				Jetpack::log( 'unlink' );
4115
				$this->unlink_user();
4116
				Jetpack::state( 'message', 'unlinked' );
4117
				if ( 'sub-unlink' == $redirect ) {
4118
					wp_safe_redirect( admin_url() );
4119
				} else {
4120
					wp_safe_redirect( Jetpack::admin_url( array( 'page' => $redirect ) ) );
4121
				}
4122
				exit;
4123
			case 'onboard' :
4124
				if ( ! current_user_can( 'manage_options' ) ) {
4125
					wp_safe_redirect( Jetpack::admin_url( 'page=jetpack' ) );
4126
				} else {
4127
					Jetpack::create_onboarding_token();
4128
					$url = $this->build_connect_url( true );
4129
4130
					if ( false !== ( $token = Jetpack_Options::get_option( 'onboarding' ) ) ) {
4131
						$url = add_query_arg( 'onboarding', $token, $url );
4132
					}
4133
4134
					$calypso_env = $this->get_calypso_env();
4135
					if ( ! empty( $calypso_env ) ) {
4136
						$url = add_query_arg( 'calypso_env', $calypso_env, $url );
4137
					}
4138
4139
					wp_redirect( $url );
4140
					exit;
4141
				}
4142
				exit;
4143
			default:
4144
				/**
4145
				 * Fires when a Jetpack admin page is loaded with an unrecognized parameter.
4146
				 *
4147
				 * @since 2.6.0
4148
				 *
4149
				 * @param string sanitize_key( $_GET['action'] ) Unrecognized URL parameter.
4150
				 */
4151
				do_action( 'jetpack_unrecognized_action', sanitize_key( $_GET['action'] ) );
4152
			}
4153
		}
4154
4155
		if ( ! $error = $error ? $error : Jetpack::state( 'error' ) ) {
4156
			self::activate_new_modules( true );
4157
		}
4158
4159
		$message_code = Jetpack::state( 'message' );
4160
		if ( Jetpack::state( 'optin-manage' ) ) {
4161
			$activated_manage = $message_code;
4162
			$message_code = 'jetpack-manage';
4163
		}
4164
4165
		switch ( $message_code ) {
4166
		case 'jetpack-manage':
4167
			$this->message = '<strong>' . sprintf( __( 'You are all set! Your site can now be managed from <a href="%s" target="_blank">wordpress.com/sites</a>.', 'jetpack' ), 'https://wordpress.com/sites' ) . '</strong>';
4168
			if ( $activated_manage ) {
0 ignored issues
show
Bug introduced by
The variable $activated_manage does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
4169
				$this->message .= '<br /><strong>' . __( 'Manage has been activated for you!', 'jetpack'  ) . '</strong>';
4170
			}
4171
			break;
4172
4173
		}
4174
4175
		$deactivated_plugins = Jetpack::state( 'deactivated_plugins' );
4176
4177
		if ( ! empty( $deactivated_plugins ) ) {
4178
			$deactivated_plugins = explode( ',', $deactivated_plugins );
4179
			$deactivated_titles  = array();
4180
			foreach ( $deactivated_plugins as $deactivated_plugin ) {
4181
				if ( ! isset( $this->plugins_to_deactivate[$deactivated_plugin] ) ) {
4182
					continue;
4183
				}
4184
4185
				$deactivated_titles[] = '<strong>' . str_replace( ' ', '&nbsp;', $this->plugins_to_deactivate[$deactivated_plugin][1] ) . '</strong>';
4186
			}
4187
4188
			if ( $deactivated_titles ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $deactivated_titles of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
4189
				if ( $this->message ) {
4190
					$this->message .= "<br /><br />\n";
4191
				}
4192
4193
				$this->message .= wp_sprintf(
4194
					_n(
4195
						'Jetpack contains the most recent version of the old %l plugin.',
4196
						'Jetpack contains the most recent versions of the old %l plugins.',
4197
						count( $deactivated_titles ),
4198
						'jetpack'
4199
					),
4200
					$deactivated_titles
4201
				);
4202
4203
				$this->message .= "<br />\n";
4204
4205
				$this->message .= _n(
4206
					'The old version has been deactivated and can be removed from your site.',
4207
					'The old versions have been deactivated and can be removed from your site.',
4208
					count( $deactivated_titles ),
4209
					'jetpack'
4210
				);
4211
			}
4212
		}
4213
4214
		$this->privacy_checks = Jetpack::state( 'privacy_checks' );
4215
4216
		if ( $this->message || $this->error || $this->privacy_checks ) {
4217
			add_action( 'jetpack_notices', array( $this, 'admin_notices' ) );
4218
		}
4219
4220
		add_filter( 'jetpack_short_module_description', 'wptexturize' );
4221
	}
4222
4223
	function admin_notices() {
4224
4225
		if ( $this->error ) {
4226
?>
4227
<div id="message" class="jetpack-message jetpack-err">
4228
	<div class="squeezer">
4229
		<h2><?php echo wp_kses( $this->error, array( 'a' => array( 'href' => array() ), 'small' => true, 'code' => true, 'strong' => true, 'br' => true, 'b' => true ) ); ?></h2>
4230
<?php	if ( $desc = Jetpack::state( 'error_description' ) ) : ?>
4231
		<p><?php echo esc_html( stripslashes( $desc ) ); ?></p>
4232
<?php	endif; ?>
4233
	</div>
4234
</div>
4235
<?php
4236
		}
4237
4238
		if ( $this->message ) {
4239
?>
4240
<div id="message" class="jetpack-message">
4241
	<div class="squeezer">
4242
		<h2><?php echo wp_kses( $this->message, array( 'strong' => array(), 'a' => array( 'href' => true ), 'br' => true ) ); ?></h2>
4243
	</div>
4244
</div>
4245
<?php
4246
		}
4247
4248
		if ( $this->privacy_checks ) :
4249
			$module_names = $module_slugs = array();
4250
4251
			$privacy_checks = explode( ',', $this->privacy_checks );
4252
			$privacy_checks = array_filter( $privacy_checks, array( 'Jetpack', 'is_module' ) );
4253
			foreach ( $privacy_checks as $module_slug ) {
4254
				$module = Jetpack::get_module( $module_slug );
4255
				if ( ! $module ) {
4256
					continue;
4257
				}
4258
4259
				$module_slugs[] = $module_slug;
4260
				$module_names[] = "<strong>{$module['name']}</strong>";
4261
			}
4262
4263
			$module_slugs = join( ',', $module_slugs );
4264
?>
4265
<div id="message" class="jetpack-message jetpack-err">
4266
	<div class="squeezer">
4267
		<h2><strong><?php esc_html_e( 'Is this site private?', 'jetpack' ); ?></strong></h2><br />
4268
		<p><?php
4269
			echo wp_kses(
4270
				wptexturize(
4271
					wp_sprintf(
4272
						_nx(
4273
							"Like your site's RSS feeds, %l allows access to your posts and other content to third parties.",
4274
							"Like your site's RSS feeds, %l allow access to your posts and other content to third parties.",
4275
							count( $privacy_checks ),
4276
							'%l = list of Jetpack module/feature names',
4277
							'jetpack'
4278
						),
4279
						$module_names
4280
					)
4281
				),
4282
				array( 'strong' => true )
4283
			);
4284
4285
			echo "\n<br />\n";
4286
4287
			echo wp_kses(
4288
				sprintf(
4289
					_nx(
4290
						'If your site is not publicly accessible, consider <a href="%1$s" title="%2$s">deactivating this feature</a>.',
4291
						'If your site is not publicly accessible, consider <a href="%1$s" title="%2$s">deactivating these features</a>.',
4292
						count( $privacy_checks ),
4293
						'%1$s = deactivation URL, %2$s = "Deactivate {list of Jetpack module/feature names}',
4294
						'jetpack'
4295
					),
4296
					wp_nonce_url(
4297
						Jetpack::admin_url(
4298
							array(
4299
								'page'   => 'jetpack',
4300
								'action' => 'deactivate',
4301
								'module' => urlencode( $module_slugs ),
4302
							)
4303
						),
4304
						"jetpack_deactivate-$module_slugs"
4305
					),
4306
					esc_attr( wp_kses( wp_sprintf( _x( 'Deactivate %l', '%l = list of Jetpack module/feature names', 'jetpack' ), $module_names ), array() ) )
4307
				),
4308
				array( 'a' => array( 'href' => true, 'title' => true ) )
4309
			);
4310
		?></p>
4311
	</div>
4312
</div>
4313
<?php endif;
4314
	}
4315
4316
	/**
4317
	 * Record a stat for later output.  This will only currently output in the admin_footer.
4318
	 */
4319
	function stat( $group, $detail ) {
4320
		if ( ! isset( $this->stats[ $group ] ) )
4321
			$this->stats[ $group ] = array();
4322
		$this->stats[ $group ][] = $detail;
4323
	}
4324
4325
	/**
4326
	 * Load stats pixels. $group is auto-prefixed with "x_jetpack-"
4327
	 */
4328
	function do_stats( $method = '' ) {
4329
		if ( is_array( $this->stats ) && count( $this->stats ) ) {
4330
			foreach ( $this->stats as $group => $stats ) {
4331
				if ( is_array( $stats ) && count( $stats ) ) {
4332
					$args = array( "x_jetpack-{$group}" => implode( ',', $stats ) );
4333
					if ( 'server_side' === $method ) {
4334
						self::do_server_side_stat( $args );
4335
					} else {
4336
						echo '<img src="' . esc_url( self::build_stats_url( $args ) ) . '" width="1" height="1" style="display:none;" />';
4337
					}
4338
				}
4339
				unset( $this->stats[ $group ] );
4340
			}
4341
		}
4342
	}
4343
4344
	/**
4345
	 * Runs stats code for a one-off, server-side.
4346
	 *
4347
	 * @param $args array|string The arguments to append to the URL. Should include `x_jetpack-{$group}={$stats}` or whatever we want to store.
4348
	 *
4349
	 * @return bool If it worked.
4350
	 */
4351
	static function do_server_side_stat( $args ) {
4352
		$response = wp_remote_get( esc_url_raw( self::build_stats_url( $args ) ) );
4353
		if ( is_wp_error( $response ) )
4354
			return false;
4355
4356
		if ( 200 !== wp_remote_retrieve_response_code( $response ) )
4357
			return false;
4358
4359
		return true;
4360
	}
4361
4362
	/**
4363
	 * Builds the stats url.
4364
	 *
4365
	 * @param $args array|string The arguments to append to the URL.
4366
	 *
4367
	 * @return string The URL to be pinged.
4368
	 */
4369
	static function build_stats_url( $args ) {
4370
		$defaults = array(
4371
			'v'    => 'wpcom2',
4372
			'rand' => md5( mt_rand( 0, 999 ) . time() ),
4373
		);
4374
		$args     = wp_parse_args( $args, $defaults );
4375
		/**
4376
		 * Filter the URL used as the Stats tracking pixel.
4377
		 *
4378
		 * @since 2.3.2
4379
		 *
4380
		 * @param string $url Base URL used as the Stats tracking pixel.
4381
		 */
4382
		$base_url = apply_filters(
4383
			'jetpack_stats_base_url',
4384
			'https://pixel.wp.com/g.gif'
4385
		);
4386
		$url      = add_query_arg( $args, $base_url );
4387
		return $url;
4388
	}
4389
4390
	static function translate_current_user_to_role() {
4391
		foreach ( self::$capability_translations as $role => $cap ) {
4392
			if ( current_user_can( $role ) || current_user_can( $cap ) ) {
4393
				return $role;
4394
			}
4395
		}
4396
4397
		return false;
4398
	}
4399
4400
	static function translate_user_to_role( $user ) {
4401
		foreach ( self::$capability_translations as $role => $cap ) {
4402
			if ( user_can( $user, $role ) || user_can( $user, $cap ) ) {
4403
				return $role;
4404
			}
4405
		}
4406
4407
		return false;
4408
    }
4409
4410
	static function translate_role_to_cap( $role ) {
4411
		if ( ! isset( self::$capability_translations[$role] ) ) {
4412
			return false;
4413
		}
4414
4415
		return self::$capability_translations[$role];
4416
	}
4417
4418
	static function sign_role( $role, $user_id = null ) {
4419
		if ( empty( $user_id ) ) {
4420
			$user_id = (int) get_current_user_id();
4421
		}
4422
4423
		if ( ! $user_id  ) {
4424
			return false;
4425
		}
4426
4427
		$token = Jetpack_Data::get_access_token();
0 ignored issues
show
Deprecated Code introduced by
The method Jetpack_Data::get_access_token() has been deprecated with message: 7.5 Use Connection_Manager instead.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
4428
		if ( ! $token || is_wp_error( $token ) ) {
4429
			return false;
4430
		}
4431
4432
		return $role . ':' . hash_hmac( 'md5', "{$role}|{$user_id}", $token->secret );
4433
	}
4434
4435
4436
	/**
4437
	 * Builds a URL to the Jetpack connection auth page
4438
	 *
4439
	 * @since 3.9.5
4440
	 *
4441
	 * @param bool $raw If true, URL will not be escaped.
4442
	 * @param bool|string $redirect If true, will redirect back to Jetpack wp-admin landing page after connection.
4443
	 *                              If string, will be a custom redirect.
4444
	 * @param bool|string $from If not false, adds 'from=$from' param to the connect URL.
4445
	 * @param bool $register If true, will generate a register URL regardless of the existing token, since 4.9.0
4446
	 *
4447
	 * @return string Connect URL
4448
	 */
4449
	function build_connect_url( $raw = false, $redirect = false, $from = false, $register = false ) {
4450
		$site_id = Jetpack_Options::get_option( 'id' );
4451
		$blog_token = Jetpack_Data::get_access_token();
0 ignored issues
show
Deprecated Code introduced by
The method Jetpack_Data::get_access_token() has been deprecated with message: 7.5 Use Connection_Manager instead.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
4452
4453
		if ( $register || ! $blog_token || ! $site_id ) {
4454
			$url = Jetpack::nonce_url_no_esc( Jetpack::admin_url( 'action=register' ), 'jetpack-register' );
4455
4456
			if ( ! empty( $redirect ) ) {
4457
				$url = add_query_arg(
4458
					'redirect',
4459
					urlencode( wp_validate_redirect( esc_url_raw( $redirect ) ) ),
4460
					$url
4461
				);
4462
			}
4463
4464
			if( is_network_admin() ) {
4465
				$url = add_query_arg( 'is_multisite', network_admin_url( 'admin.php?page=jetpack-settings' ), $url );
4466
			}
4467
		} else {
4468
4469
			// Let's check the existing blog token to see if we need to re-register. We only check once per minute
4470
			// because otherwise this logic can get us in to a loop.
4471
			$last_connect_url_check = intval( Jetpack_Options::get_raw_option( 'jetpack_last_connect_url_check' ) );
4472
			if ( ! $last_connect_url_check || ( time() - $last_connect_url_check ) > MINUTE_IN_SECONDS ) {
4473
				Jetpack_Options::update_raw_option( 'jetpack_last_connect_url_check', time() );
4474
4475
				$response = Client::wpcom_json_api_request_as_blog(
4476
					sprintf( '/sites/%d', $site_id ) .'?force=wpcom',
4477
					'1.1'
4478
				);
4479
4480
				if ( 200 !== wp_remote_retrieve_response_code( $response ) ) {
4481
4482
					// Generating a register URL instead to refresh the existing token
4483
					return $this->build_connect_url( $raw, $redirect, $from, true );
4484
				}
4485
			}
4486
4487
			if ( defined( 'JETPACK__GLOTPRESS_LOCALES_PATH' ) && include_once JETPACK__GLOTPRESS_LOCALES_PATH ) {
4488
				$gp_locale = GP_Locales::by_field( 'wp_locale', get_locale() );
4489
			}
4490
4491
			$role = self::translate_current_user_to_role();
4492
			$signed_role = self::sign_role( $role );
4493
4494
			$user = wp_get_current_user();
4495
4496
			$jetpack_admin_page = esc_url_raw( admin_url( 'admin.php?page=jetpack' ) );
4497
			$redirect = $redirect
4498
				? wp_validate_redirect( esc_url_raw( $redirect ), $jetpack_admin_page )
4499
				: $jetpack_admin_page;
4500
4501
			if( isset( $_REQUEST['is_multisite'] ) ) {
4502
				$redirect = Jetpack_Network::init()->get_url( 'network_admin_page' );
4503
			}
4504
4505
			$secrets = Jetpack::generate_secrets( 'authorize', false, 2 * HOUR_IN_SECONDS );
4506
4507
			/**
4508
			 * Filter the type of authorization.
4509
			 * 'calypso' completes authorization on wordpress.com/jetpack/connect
4510
			 * while 'jetpack' ( or any other value ) completes the authorization at jetpack.wordpress.com.
4511
			 *
4512
			 * @since 4.3.3
4513
			 *
4514
			 * @param string $auth_type Defaults to 'calypso', can also be 'jetpack'.
4515
			 */
4516
			$auth_type = apply_filters( 'jetpack_auth_type', 'calypso' );
4517
4518
4519
			$tracks = new Tracking();
4520
			$tracks_identity = $tracks->tracks_get_identity( get_current_user_id() );
4521
4522
			$args = urlencode_deep(
4523
				array(
4524
					'response_type' => 'code',
4525
					'client_id'     => Jetpack_Options::get_option( 'id' ),
4526
					'redirect_uri'  => add_query_arg(
4527
						array(
4528
							'action'   => 'authorize',
4529
							'_wpnonce' => wp_create_nonce( "jetpack-authorize_{$role}_{$redirect}" ),
4530
							'redirect' => urlencode( $redirect ),
4531
						),
4532
						esc_url( admin_url( 'admin.php?page=jetpack' ) )
4533
					),
4534
					'state'         => $user->ID,
4535
					'scope'         => $signed_role,
4536
					'user_email'    => $user->user_email,
4537
					'user_login'    => $user->user_login,
4538
					'is_active'     => Jetpack::is_active(),
4539
					'jp_version'    => JETPACK__VERSION,
4540
					'auth_type'     => $auth_type,
4541
					'secret'        => $secrets['secret_1'],
4542
					'locale'        => ( isset( $gp_locale ) && isset( $gp_locale->slug ) ) ? $gp_locale->slug : '',
4543
					'blogname'      => get_option( 'blogname' ),
4544
					'site_url'      => site_url(),
4545
					'home_url'      => home_url(),
4546
					'site_icon'     => get_site_icon_url(),
4547
					'site_lang'     => get_locale(),
4548
					'_ui'           => $tracks_identity['_ui'],
4549
					'_ut'           => $tracks_identity['_ut'],
4550
					'site_created'  => Jetpack::get_assumed_site_creation_date(),
4551
				)
4552
			);
4553
4554
			self::apply_activation_source_to_args( $args );
4555
4556
			$url = add_query_arg( $args, Jetpack::api_url( 'authorize' ) );
4557
		}
4558
4559
		if ( $from ) {
4560
			$url = add_query_arg( 'from', $from, $url );
4561
		}
4562
4563
		// Ensure that class to get the affiliate code is loaded
4564
		if ( ! class_exists( 'Jetpack_Affiliate' ) ) {
4565
			require_once JETPACK__PLUGIN_DIR . 'class.jetpack-affiliate.php';
4566
		}
4567
		// Get affiliate code and add it to the URL
4568
		$url = Jetpack_Affiliate::init()->add_code_as_query_arg( $url );
4569
4570
		$calypso_env = $this->get_calypso_env();
4571
4572
		if ( ! empty( $calypso_env ) ) {
4573
			$url = add_query_arg( 'calypso_env', $calypso_env, $url );
4574
		}
4575
4576
		return $raw ? esc_url_raw( $url ) : esc_url( $url );
4577
	}
4578
4579
	/**
4580
	 * Get our assumed site creation date.
4581
	 * Calculated based on the earlier date of either:
4582
	 * - Earliest admin user registration date.
4583
	 * - Earliest date of post of any post type.
4584
	 *
4585
	 * @since 7.2.0
4586
	 *
4587
	 * @return string Assumed site creation date and time.
4588
	 */
4589
	public static function get_assumed_site_creation_date() {
4590
		$earliest_registered_users = get_users( array(
4591
			'role'    => 'administrator',
4592
			'orderby' => 'user_registered',
4593
			'order'   => 'ASC',
4594
			'fields'  => array( 'user_registered' ),
4595
			'number'  => 1,
4596
		) );
4597
		$earliest_registration_date = $earliest_registered_users[0]->user_registered;
4598
4599
		$earliest_posts = get_posts( array(
4600
			'posts_per_page' => 1,
4601
			'post_type'      => 'any',
4602
			'post_status'    => 'any',
4603
			'orderby'        => 'date',
4604
			'order'          => 'ASC',
4605
		) );
4606
4607
		// If there are no posts at all, we'll count only on user registration date.
4608
		if ( $earliest_posts ) {
4609
			$earliest_post_date = $earliest_posts[0]->post_date;
4610
		} else {
4611
			$earliest_post_date = PHP_INT_MAX;
4612
		}
4613
4614
		return min( $earliest_registration_date, $earliest_post_date );
4615
	}
4616
4617
	public static function apply_activation_source_to_args( &$args ) {
4618
		list( $activation_source_name, $activation_source_keyword ) = get_option( 'jetpack_activation_source' );
4619
4620
		if ( $activation_source_name ) {
4621
			$args['_as'] = urlencode( $activation_source_name );
4622
		}
4623
4624
		if ( $activation_source_keyword ) {
4625
			$args['_ak'] = urlencode( $activation_source_keyword );
4626
		}
4627
	}
4628
4629
	function build_reconnect_url( $raw = false ) {
4630
		$url = wp_nonce_url( Jetpack::admin_url( 'action=reconnect' ), 'jetpack-reconnect' );
4631
		return $raw ? $url : esc_url( $url );
4632
	}
4633
4634
	public static function admin_url( $args = null ) {
4635
		$args = wp_parse_args( $args, array( 'page' => 'jetpack' ) );
4636
		$url = add_query_arg( $args, admin_url( 'admin.php' ) );
4637
		return $url;
4638
	}
4639
4640
	public static function nonce_url_no_esc( $actionurl, $action = -1, $name = '_wpnonce' ) {
4641
		$actionurl = str_replace( '&amp;', '&', $actionurl );
4642
		return add_query_arg( $name, wp_create_nonce( $action ), $actionurl );
4643
	}
4644
4645
	function dismiss_jetpack_notice() {
4646
4647
		if ( ! isset( $_GET['jetpack-notice'] ) ) {
4648
			return;
4649
		}
4650
4651
		switch( $_GET['jetpack-notice'] ) {
4652
			case 'dismiss':
4653
				if ( check_admin_referer( 'jetpack-deactivate' ) && ! is_plugin_active_for_network( plugin_basename( JETPACK__PLUGIN_DIR . 'jetpack.php' ) ) ) {
4654
4655
					require_once ABSPATH . 'wp-admin/includes/plugin.php';
4656
					deactivate_plugins( JETPACK__PLUGIN_DIR . 'jetpack.php', false, false );
4657
					wp_safe_redirect( admin_url() . 'plugins.php?deactivate=true&plugin_status=all&paged=1&s=' );
4658
				}
4659
				break;
4660
			case 'jetpack-protect-multisite-opt-out':
4661
4662
				if ( check_admin_referer( 'jetpack_protect_multisite_banner_opt_out' ) ) {
4663
					// Don't show the banner again
4664
4665
					update_site_option( 'jetpack_dismissed_protect_multisite_banner', true );
4666
					// redirect back to the page that had the notice
4667
					if ( wp_get_referer() ) {
4668
						wp_safe_redirect( wp_get_referer() );
4669
					} else {
4670
						// Take me to Jetpack
4671
						wp_safe_redirect( admin_url( 'admin.php?page=jetpack' ) );
4672
					}
4673
				}
4674
				break;
4675
		}
4676
	}
4677
4678
	public static function sort_modules( $a, $b ) {
4679
		if ( $a['sort'] == $b['sort'] )
4680
			return 0;
4681
4682
		return ( $a['sort'] < $b['sort'] ) ? -1 : 1;
4683
	}
4684
4685
	function ajax_recheck_ssl() {
4686
		check_ajax_referer( 'recheck-ssl', 'ajax-nonce' );
4687
		$result = Jetpack::permit_ssl( true );
4688
		wp_send_json( array(
4689
			'enabled' => $result,
4690
			'message' => get_transient( 'jetpack_https_test_message' )
4691
		) );
4692
	}
4693
4694
/* Client API */
4695
4696
	/**
4697
	 * Returns the requested Jetpack API URL
4698
	 *
4699
	 * @return string
4700
	 */
4701
	public static function api_url( $relative_url ) {
4702
		return trailingslashit( JETPACK__API_BASE . $relative_url  ) . JETPACK__API_VERSION . '/';
4703
	}
4704
4705
	/**
4706
	 * Some hosts disable the OpenSSL extension and so cannot make outgoing HTTPS requsets
4707
	 */
4708
	public static function fix_url_for_bad_hosts( $url ) {
4709
		if ( 0 !== strpos( $url, 'https://' ) ) {
4710
			return $url;
4711
		}
4712
4713
		switch ( JETPACK_CLIENT__HTTPS ) {
4714
			case 'ALWAYS' :
4715
				return $url;
4716
			case 'NEVER' :
4717
				return set_url_scheme( $url, 'http' );
4718
			// default : case 'AUTO' :
4719
		}
4720
4721
		// we now return the unmodified SSL URL by default, as a security precaution
4722
		return $url;
4723
	}
4724
4725
	/**
4726
	 * Create a random secret for validating onboarding payload
4727
	 *
4728
	 * @return string Secret token
4729
	 */
4730
	public static function create_onboarding_token() {
4731
		if ( false === ( $token = Jetpack_Options::get_option( 'onboarding' ) ) ) {
4732
			$token = wp_generate_password( 32, false );
4733
			Jetpack_Options::update_option( 'onboarding', $token );
4734
		}
4735
4736
		return $token;
4737
	}
4738
4739
	/**
4740
	 * Remove the onboarding token
4741
	 *
4742
	 * @return bool True on success, false on failure
4743
	 */
4744
	public static function invalidate_onboarding_token() {
4745
		return Jetpack_Options::delete_option( 'onboarding' );
4746
	}
4747
4748
	/**
4749
	 * Validate an onboarding token for a specific action
4750
	 *
4751
	 * @return boolean True if token/action pair is accepted, false if not
4752
	 */
4753
	public static function validate_onboarding_token_action( $token, $action ) {
4754
		// Compare tokens, bail if tokens do not match
4755
		if ( ! hash_equals( $token, Jetpack_Options::get_option( 'onboarding' ) ) ) {
4756
			return false;
4757
		}
4758
4759
		// List of valid actions we can take
4760
		$valid_actions = array(
4761
			'/jetpack/v4/settings',
4762
		);
4763
4764
		// Whitelist the action
4765
		if ( ! in_array( $action, $valid_actions ) ) {
4766
			return false;
4767
		}
4768
4769
		return true;
4770
	}
4771
4772
	/**
4773
	 * Checks to see if the URL is using SSL to connect with Jetpack
4774
	 *
4775
	 * @since 2.3.3
4776
	 * @return boolean
4777
	 */
4778
	public static function permit_ssl( $force_recheck = false ) {
4779
		// Do some fancy tests to see if ssl is being supported
4780
		if ( $force_recheck || false === ( $ssl = get_transient( 'jetpack_https_test' ) ) ) {
4781
			$message = '';
4782
			if ( 'https' !== substr( JETPACK__API_BASE, 0, 5 ) ) {
4783
				$ssl = 0;
4784
			} else {
4785
				switch ( JETPACK_CLIENT__HTTPS ) {
4786
					case 'NEVER':
4787
						$ssl = 0;
4788
						$message = __( 'JETPACK_CLIENT__HTTPS is set to NEVER', 'jetpack' );
4789
						break;
4790
					case 'ALWAYS':
4791
					case 'AUTO':
4792
					default:
4793
						$ssl = 1;
4794
						break;
4795
				}
4796
4797
				// If it's not 'NEVER', test to see
4798
				if ( $ssl ) {
4799
					if ( ! wp_http_supports( array( 'ssl' => true ) ) ) {
4800
						$ssl = 0;
4801
						$message = __( 'WordPress reports no SSL support', 'jetpack' );
4802
					} else {
4803
						$response = wp_remote_get( JETPACK__API_BASE . 'test/1/' );
4804
						if ( is_wp_error( $response ) ) {
4805
							$ssl = 0;
4806
							$message = __( 'WordPress reports no SSL support', 'jetpack' );
4807
						} elseif ( 'OK' !== wp_remote_retrieve_body( $response ) ) {
4808
							$ssl = 0;
4809
							$message = __( 'Response was not OK: ', 'jetpack' ) . wp_remote_retrieve_body( $response );
4810
						}
4811
					}
4812
				}
4813
			}
4814
			set_transient( 'jetpack_https_test', $ssl, DAY_IN_SECONDS );
4815
			set_transient( 'jetpack_https_test_message', $message, DAY_IN_SECONDS );
4816
		}
4817
4818
		return (bool) $ssl;
4819
	}
4820
4821
	/*
4822
	 * Displays an admin_notice, alerting the user to their JETPACK_CLIENT__HTTPS constant being 'AUTO' but SSL isn't working.
4823
	 */
4824
	public function alert_auto_ssl_fail() {
4825
		if ( ! current_user_can( 'manage_options' ) )
4826
			return;
4827
4828
		$ajax_nonce = wp_create_nonce( 'recheck-ssl' );
4829
		?>
4830
4831
		<div id="jetpack-ssl-warning" class="error jp-identity-crisis">
4832
			<div class="jp-banner__content">
4833
				<h2><?php _e( 'Outbound HTTPS not working', 'jetpack' ); ?></h2>
4834
				<p><?php _e( 'Your site could not connect to WordPress.com via HTTPS. This could be due to any number of reasons, including faulty SSL certificates, misconfigured or missing SSL libraries, or network issues.', 'jetpack' ); ?></p>
4835
				<p>
4836
					<?php _e( 'Jetpack will re-test for HTTPS support once a day, but you can click here to try again immediately: ', 'jetpack' ); ?>
4837
					<a href="#" id="jetpack-recheck-ssl-button"><?php _e( 'Try again', 'jetpack' ); ?></a>
4838
					<span id="jetpack-recheck-ssl-output"><?php echo get_transient( 'jetpack_https_test_message' ); ?></span>
4839
				</p>
4840
				<p>
4841
					<?php printf( __( 'For more help, try our <a href="%1$s">connection debugger</a> or <a href="%2$s" target="_blank">troubleshooting tips</a>.', 'jetpack' ),
4842
							esc_url( Jetpack::admin_url( array( 'page' => 'jetpack-debugger' )  ) ),
4843
							esc_url( 'https://jetpack.com/support/getting-started-with-jetpack/troubleshooting-tips/' ) ); ?>
4844
				</p>
4845
			</div>
4846
		</div>
4847
		<style>
4848
			#jetpack-recheck-ssl-output { margin-left: 5px; color: red; }
4849
		</style>
4850
		<script type="text/javascript">
4851
			jQuery( document ).ready( function( $ ) {
4852
				$( '#jetpack-recheck-ssl-button' ).click( function( e ) {
4853
					var $this = $( this );
4854
					$this.html( <?php echo json_encode( __( 'Checking', 'jetpack' ) ); ?> );
4855
					$( '#jetpack-recheck-ssl-output' ).html( '' );
4856
					e.preventDefault();
4857
					var data = { action: 'jetpack-recheck-ssl', 'ajax-nonce': '<?php echo $ajax_nonce; ?>' };
4858
					$.post( ajaxurl, data )
4859
					  .done( function( response ) {
4860
					  	if ( response.enabled ) {
4861
					  		$( '#jetpack-ssl-warning' ).hide();
4862
					  	} else {
4863
					  		this.html( <?php echo json_encode( __( 'Try again', 'jetpack' ) ); ?> );
4864
					  		$( '#jetpack-recheck-ssl-output' ).html( 'SSL Failed: ' + response.message );
4865
					  	}
4866
					  }.bind( $this ) );
4867
				} );
4868
			} );
4869
		</script>
4870
4871
		<?php
4872
	}
4873
4874
	/**
4875
	 * Returns the Jetpack XML-RPC API
4876
	 *
4877
	 * @return string
4878
	 */
4879
	public static function xmlrpc_api_url() {
4880
		$base = preg_replace( '#(https?://[^?/]+)(/?.*)?$#', '\\1', JETPACK__API_BASE );
4881
		return untrailingslashit( $base ) . '/xmlrpc.php';
4882
	}
4883
4884
	public static function connection() {
4885
		return self::init()->connection_manager;
4886
	}
4887
4888
	/**
4889
	 * Creates two secret tokens and the end of life timestamp for them.
4890
	 *
4891
	 * Note these tokens are unique per call, NOT static per site for connecting.
4892
	 *
4893
	 * @since 2.6
4894
	 * @return array
4895
	 */
4896
	public static function generate_secrets( $action, $user_id = false, $exp = 600 ) {
4897
		if ( false === $user_id ) {
4898
			$user_id = get_current_user_id();
4899
		}
4900
4901
		return self::connection()->generate_secrets( $action, $user_id, $exp );
4902
	}
4903
4904
	public static function get_secrets( $action, $user_id ) {
4905
		$secrets = self::connection()->get_secrets( $action, $user_id );
4906
4907
		if ( Connection_Manager::SECRETS_MISSING === $secrets ) {
4908
			return new WP_Error( 'verify_secrets_missing', 'Verification secrets not found' );
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with 'verify_secrets_missing'.

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.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
4909
		}
4910
4911
		if ( Connection_Manager::SECRETS_EXPIRED === $secrets ) {
4912
			return new WP_Error( 'verify_secrets_expired', 'Verification took too long' );
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with 'verify_secrets_expired'.

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.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
4913
		}
4914
4915
		return $secrets;
4916
	}
4917
4918
	/**
4919
	 * @deprecated 7.5 Use Connection_Manager instead.
4920
	 *
4921
	 * @param $action
4922
	 * @param $user_id
4923
	 */
4924
	public static function delete_secrets( $action, $user_id ) {
4925
		return self::connection()->delete_secrets( $action, $user_id );
4926
	}
4927
4928
	/**
4929
	 * Builds the timeout limit for queries talking with the wpcom servers.
4930
	 *
4931
	 * Based on local php max_execution_time in php.ini
4932
	 *
4933
	 * @since 2.6
4934
	 * @return int
4935
	 * @deprecated
4936
	 **/
4937
	public function get_remote_query_timeout_limit() {
4938
		_deprecated_function( __METHOD__, 'jetpack-5.4' );
4939
		return Jetpack::get_max_execution_time();
4940
	}
4941
4942
	/**
4943
	 * Builds the timeout limit for queries talking with the wpcom servers.
4944
	 *
4945
	 * Based on local php max_execution_time in php.ini
4946
	 *
4947
	 * @since 5.4
4948
	 * @return int
4949
	 **/
4950
	public static function get_max_execution_time() {
4951
		$timeout = (int) ini_get( 'max_execution_time' );
4952
4953
		// Ensure exec time set in php.ini
4954
		if ( ! $timeout ) {
4955
			$timeout = 30;
4956
		}
4957
		return $timeout;
4958
	}
4959
4960
	/**
4961
	 * Sets a minimum request timeout, and returns the current timeout
4962
	 *
4963
	 * @since 5.4
4964
	 **/
4965
	public static function set_min_time_limit( $min_timeout ) {
4966
		$timeout = self::get_max_execution_time();
4967
		if ( $timeout < $min_timeout ) {
4968
			$timeout = $min_timeout;
4969
			set_time_limit( $timeout );
4970
		}
4971
		return $timeout;
4972
	}
4973
4974
4975
	/**
4976
	 * Takes the response from the Jetpack register new site endpoint and
4977
	 * verifies it worked properly.
4978
	 *
4979
	 * @since 2.6
4980
	 * @return string|Jetpack_Error A JSON object on success or Jetpack_Error on failures
4981
	 **/
4982
	public function validate_remote_register_response( $response ) {
4983
	  if ( is_wp_error( $response ) ) {
4984
			return new Jetpack_Error( 'register_http_request_failed', $response->get_error_message() );
0 ignored issues
show
Unused Code introduced by
The call to Jetpack_Error::__construct() has too many arguments starting with 'register_http_request_failed'.

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.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
4985
		}
4986
4987
		$code   = wp_remote_retrieve_response_code( $response );
4988
		$entity = wp_remote_retrieve_body( $response );
4989
		if ( $entity )
4990
			$registration_response = json_decode( $entity );
4991
		else
4992
			$registration_response = false;
4993
4994
		$code_type = intval( $code / 100 );
4995
		if ( 5 == $code_type ) {
4996
			return new Jetpack_Error( 'wpcom_5??', sprintf( __( 'Error Details: %s', 'jetpack' ), $code ), $code );
0 ignored issues
show
Unused Code introduced by
The call to Jetpack_Error::__construct() has too many arguments starting with 'wpcom_5??'.

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.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
4997
		} elseif ( 408 == $code ) {
4998
			return new Jetpack_Error( 'wpcom_408', sprintf( __( 'Error Details: %s', 'jetpack' ), $code ), $code );
0 ignored issues
show
Unused Code introduced by
The call to Jetpack_Error::__construct() has too many arguments starting with 'wpcom_408'.

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.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
4999
		} elseif ( ! empty( $registration_response->error ) ) {
5000
			if ( 'xml_rpc-32700' == $registration_response->error && ! function_exists( 'xml_parser_create' ) ) {
5001
				$error_description = __( "PHP's XML extension is not available. Jetpack requires the XML extension to communicate with WordPress.com. Please contact your hosting provider to enable PHP's XML extension.", 'jetpack' );
5002
			} else {
5003
				$error_description = isset( $registration_response->error_description ) ? sprintf( __( 'Error Details: %s', 'jetpack' ), (string) $registration_response->error_description ) : '';
5004
			}
5005
5006
			return new Jetpack_Error( (string) $registration_response->error, $error_description, $code );
0 ignored issues
show
Unused Code introduced by
The call to Jetpack_Error::__construct() has too many arguments starting with (string) $registration_response->error.

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.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
5007
		} elseif ( 200 != $code ) {
5008
			return new Jetpack_Error( 'wpcom_bad_response', sprintf( __( 'Error Details: %s', 'jetpack' ), $code ), $code );
0 ignored issues
show
Unused Code introduced by
The call to Jetpack_Error::__construct() has too many arguments starting with 'wpcom_bad_response'.

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.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
5009
		}
5010
5011
		// Jetpack ID error block
5012
		if ( empty( $registration_response->jetpack_id ) ) {
5013
			return new Jetpack_Error( 'jetpack_id', sprintf( __( 'Error Details: Jetpack ID is empty. Do not publicly post this error message! %s', 'jetpack' ), $entity ), $entity );
0 ignored issues
show
Unused Code introduced by
The call to Jetpack_Error::__construct() has too many arguments starting with 'jetpack_id'.

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.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
5014
		} elseif ( ! is_scalar( $registration_response->jetpack_id ) ) {
5015
			return new Jetpack_Error( 'jetpack_id', sprintf( __( 'Error Details: Jetpack ID is not a scalar. Do not publicly post this error message! %s', 'jetpack' ) , $entity ), $entity );
0 ignored issues
show
Unused Code introduced by
The call to Jetpack_Error::__construct() has too many arguments starting with 'jetpack_id'.

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.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
5016
		} elseif ( preg_match( '/[^0-9]/', $registration_response->jetpack_id ) ) {
5017
			return new Jetpack_Error( 'jetpack_id', sprintf( __( 'Error Details: Jetpack ID begins with a numeral. Do not publicly post this error message! %s', 'jetpack' ) , $entity ), $entity );
0 ignored issues
show
Unused Code introduced by
The call to Jetpack_Error::__construct() has too many arguments starting with 'jetpack_id'.

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.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
5018
		}
5019
5020
	    return $registration_response;
5021
	}
5022
	/**
5023
	 * @return bool|WP_Error
5024
	 */
5025
	public static function register() {
5026
		$tracking = new Tracking();
5027
		$tracking->record_user_event( 'jpc_register_begin' );
5028
		add_action( 'pre_update_jetpack_option_register', array( 'Jetpack_Options', 'delete_option' ) );
5029
		$secrets = Jetpack::generate_secrets( 'register' );
5030
5031 View Code Duplication
		if (
5032
			empty( $secrets['secret_1'] ) ||
5033
			empty( $secrets['secret_2'] ) ||
5034
			empty( $secrets['exp'] )
5035
		) {
5036
			return new Jetpack_Error( 'missing_secrets' );
0 ignored issues
show
Unused Code introduced by
The call to Jetpack_Error::__construct() has too many arguments starting with 'missing_secrets'.

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.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
5037
		}
5038
5039
		// better to try (and fail) to set a higher timeout than this system
5040
		// supports than to have register fail for more users than it should
5041
		$timeout = Jetpack::set_min_time_limit( 60 ) / 2;
5042
5043
		$gmt_offset = get_option( 'gmt_offset' );
5044
		if ( ! $gmt_offset ) {
5045
			$gmt_offset = 0;
5046
		}
5047
5048
		$stats_options = get_option( 'stats_options' );
5049
		$stats_id = isset($stats_options['blog_id']) ? $stats_options['blog_id'] : null;
5050
5051
		$tracks = new Tracking();
5052
		$tracks_identity = $tracks->tracks_get_identity( get_current_user_id() );
5053
5054
		$args = array(
5055
			'method'  => 'POST',
5056
			'body'    => array(
5057
				'siteurl'         => site_url(),
5058
				'home'            => home_url(),
5059
				'gmt_offset'      => $gmt_offset,
5060
				'timezone_string' => (string) get_option( 'timezone_string' ),
5061
				'site_name'       => (string) get_option( 'blogname' ),
5062
				'secret_1'        => $secrets['secret_1'],
5063
				'secret_2'        => $secrets['secret_2'],
5064
				'site_lang'       => get_locale(),
5065
				'timeout'         => $timeout,
5066
				'stats_id'        => $stats_id,
5067
				'state'           => get_current_user_id(),
5068
				'_ui'             => $tracks_identity['_ui'],
5069
				'_ut'             => $tracks_identity['_ut'],
5070
				'site_created'    => Jetpack::get_assumed_site_creation_date(),
5071
				'jetpack_version' => JETPACK__VERSION,
5072
				'ABSPATH'         => defined( 'ABSPATH' ) ? ABSPATH : '',
5073
			),
5074
			'headers' => array(
5075
				'Accept' => 'application/json',
5076
			),
5077
			'timeout' => $timeout,
5078
		);
5079
5080
		self::apply_activation_source_to_args( $args['body'] );
5081
5082
		$response = Client::_wp_remote_request( Jetpack::fix_url_for_bad_hosts( Jetpack::api_url( 'register' ) ), $args, true );
5083
5084
		// Make sure the response is valid and does not contain any Jetpack errors
5085
		$registration_details = Jetpack::init()->validate_remote_register_response( $response );
5086
		if ( is_wp_error( $registration_details ) ) {
5087
			return $registration_details;
5088
		} elseif ( ! $registration_details ) {
5089
			return new Jetpack_Error( 'unknown_error', __( 'Unknown error registering your Jetpack site', 'jetpack' ), wp_remote_retrieve_response_code( $response ) );
0 ignored issues
show
Unused Code introduced by
The call to Jetpack_Error::__construct() has too many arguments starting with 'unknown_error'.

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.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
5090
		}
5091
5092 View Code Duplication
		if ( empty( $registration_details->jetpack_secret ) || ! is_string( $registration_details->jetpack_secret ) ) {
5093
			return new Jetpack_Error( 'jetpack_secret', '', wp_remote_retrieve_response_code( $response ) );
0 ignored issues
show
Unused Code introduced by
The call to Jetpack_Error::__construct() has too many arguments starting with 'jetpack_secret'.

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.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
5094
		}
5095
5096
		if ( isset( $registration_details->jetpack_public ) ) {
5097
			$jetpack_public = (int) $registration_details->jetpack_public;
5098
		} else {
5099
			$jetpack_public = false;
5100
		}
5101
5102
		Jetpack_Options::update_options(
5103
			array(
5104
				'id'         => (int)    $registration_details->jetpack_id,
5105
				'blog_token' => (string) $registration_details->jetpack_secret,
5106
				'public'     => $jetpack_public,
5107
			)
5108
		);
5109
5110
		/**
5111
		 * Fires when a site is registered on WordPress.com.
5112
		 *
5113
		 * @since 3.7.0
5114
		 *
5115
		 * @param int $json->jetpack_id Jetpack Blog ID.
5116
		 * @param string $json->jetpack_secret Jetpack Blog Token.
5117
		 * @param int|bool $jetpack_public Is the site public.
5118
		 */
5119
		do_action( 'jetpack_site_registered', $registration_details->jetpack_id, $registration_details->jetpack_secret, $jetpack_public );
5120
5121
		$jetpack = Jetpack::init();
0 ignored issues
show
Unused Code introduced by
$jetpack is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
5122
5123
		return true;
5124
	}
5125
5126
	/**
5127
	 * If the db version is showing something other that what we've got now, bump it to current.
5128
	 *
5129
	 * @return bool: True if the option was incorrect and updated, false if nothing happened.
0 ignored issues
show
Documentation introduced by
The doc-type bool: could not be parsed: Unknown type name "bool:" at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
5130
	 */
5131
	public static function maybe_set_version_option() {
5132
		list( $version ) = explode( ':', Jetpack_Options::get_option( 'version' ) );
5133
		if ( JETPACK__VERSION != $version ) {
5134
			Jetpack_Options::update_option( 'version', JETPACK__VERSION . ':' . time() );
5135
5136
			if ( version_compare( JETPACK__VERSION, $version, '>' ) ) {
5137
				/** This action is documented in class.jetpack.php */
5138
				do_action( 'updating_jetpack_version', JETPACK__VERSION, $version );
5139
			}
5140
5141
			return true;
5142
		}
5143
		return false;
5144
	}
5145
5146
/* Client Server API */
5147
5148
	/**
5149
	 * Loads the Jetpack XML-RPC client
5150
	 */
5151
	public static function load_xml_rpc_client() {
5152
		require_once ABSPATH . WPINC . '/class-IXR.php';
5153
		require_once JETPACK__PLUGIN_DIR . 'class.jetpack-ixr-client.php';
5154
	}
5155
5156
	/**
5157
	 * Resets the saved authentication state in between testing requests.
5158
	 */
5159
	public function reset_saved_auth_state() {
5160
		$this->xmlrpc_verification = null;
5161
		$this->rest_authentication_status = null;
5162
	}
5163
5164
	/**
5165
	 * Verifies the signature of the current request.
5166
	 *
5167
	 * @return false|array
5168
	 */
5169
	function verify_xml_rpc_signature() {
5170
		if ( is_null( $this->xmlrpc_verification ) ) {
5171
			$this->xmlrpc_verification = $this->internal_verify_xml_rpc_signature();
5172
5173
			if ( is_wp_error( $this->xmlrpc_verification ) ) {
5174
				/**
5175
				 * Action for logging XMLRPC signature verification errors. This data is sensitive.
5176
				 *
5177
				 * Error codes:
5178
				 * - malformed_token
5179
				 * - malformed_user_id
5180
				 * - unknown_token
5181
				 * - could_not_sign
5182
				 * - invalid_nonce
5183
				 * - signature_mismatch
5184
				 *
5185
				 * @since 7.5.0
5186
				 *
5187
				 * @param WP_Error $signature_verification_error The verification error
5188
				 */
5189
				do_action( 'jetpack_verify_signature_error', $this->xmlrpc_verification );
5190
			}
5191
		}
5192
5193
		return is_wp_error( $this->xmlrpc_verification ) ? false : $this->xmlrpc_verification;
5194
	}
5195
5196
	/**
5197
	 * Verifies the signature of the current request.
5198
	 *
5199
	 * This function has side effects and should not be used. Instead,
5200
	 * use the memoized version `->verify_xml_rpc_signature()`.
5201
	 *
5202
	 * @internal
5203
	 */
5204
	private function internal_verify_xml_rpc_signature() {
5205
		// It's not for us
5206
		if ( ! isset( $_GET['token'] ) || empty( $_GET['signature'] ) ) {
5207
			return false;
5208
		}
5209
5210
		$signature_details = array(
5211
			'token'     => isset( $_GET['token'] )     ? wp_unslash( $_GET['token'] )     : '',
5212
			'timestamp' => isset( $_GET['timestamp'] ) ? wp_unslash( $_GET['timestamp'] ) : '',
5213
			'nonce'     => isset( $_GET['nonce'] )     ? wp_unslash( $_GET['nonce'] )     : '',
5214
			'body_hash' => isset( $_GET['body-hash'] ) ? wp_unslash( $_GET['body-hash'] ) : '',
5215
			'method'    => wp_unslash( $_SERVER['REQUEST_METHOD'] ),
5216
			'url'       => wp_unslash( $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] ), // Temp - will get real signature URL later.
5217
			'signature' => isset( $_GET['signature'] ) ? wp_unslash( $_GET['signature'] ) : '',
5218
		);
5219
5220
		@list( $token_key, $version, $user_id ) = explode( ':', wp_unslash( $_GET['token'] ) );
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...
5221
		if (
5222
			empty( $token_key )
5223
		||
5224
			empty( $version ) || strval( JETPACK__API_VERSION ) !== $version
5225
		) {
5226
			return new WP_Error( 'malformed_token', 'Malformed token in request', compact( 'signature_details' ) );
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with 'malformed_token'.

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.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
5227
		}
5228
5229
		if ( '0' === $user_id ) {
5230
			$token_type = 'blog';
5231
			$user_id = 0;
5232
		} else {
5233
			$token_type = 'user';
5234
			if ( empty( $user_id ) || ! ctype_digit( $user_id ) ) {
5235
				return new WP_Error( 'malformed_user_id', 'Malformed user_id in request', compact( 'signature_details' ) );
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with 'malformed_user_id'.

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.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
5236
			}
5237
			$user_id = (int) $user_id;
5238
5239
			$user = new WP_User( $user_id );
5240
			if ( ! $user || ! $user->exists() ) {
5241
				return new WP_Error( 'unknown_user', sprintf( 'User %d does not exist', $user_id ), compact( 'signature_details' ) );
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with 'unknown_user'.

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.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
5242
			}
5243
		}
5244
5245
		$token = Jetpack_Data::get_access_token( $user_id, $token_key, false );
0 ignored issues
show
Documentation introduced by
$user_id is of type integer, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
Deprecated Code introduced by
The method Jetpack_Data::get_access_token() has been deprecated with message: 7.5 Use Connection_Manager instead.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
5246
		if ( is_wp_error( $token ) ) {
5247
			$token->add_data( compact( 'signature_details' ) );
5248
			return $token;
5249
		} elseif ( ! $token ) {
5250
			return new WP_Error( 'unknown_token', sprintf( 'Token %s:%s:%d does not exist', $token_key, $version, $user_id ), compact( 'signature_details' ) );
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with 'unknown_token'.

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.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
5251
		}
5252
5253
		$jetpack_signature = new Jetpack_Signature( $token->secret, (int) Jetpack_Options::get_option( 'time_diff' ) );
5254
		if ( isset( $_POST['_jetpack_is_multipart'] ) ) {
5255
			$post_data   = $_POST;
5256
			$file_hashes = array();
5257
			foreach ( $post_data as $post_data_key => $post_data_value ) {
5258
				if ( 0 !== strpos( $post_data_key, '_jetpack_file_hmac_' ) ) {
5259
					continue;
5260
				}
5261
				$post_data_key = substr( $post_data_key, strlen( '_jetpack_file_hmac_' ) );
5262
				$file_hashes[$post_data_key] = $post_data_value;
5263
			}
5264
5265
			foreach ( $file_hashes as $post_data_key => $post_data_value ) {
5266
				unset( $post_data["_jetpack_file_hmac_{$post_data_key}"] );
5267
				$post_data[$post_data_key] = $post_data_value;
5268
			}
5269
5270
			ksort( $post_data );
5271
5272
			$body = http_build_query( stripslashes_deep( $post_data ) );
5273
		} elseif ( is_null( $this->HTTP_RAW_POST_DATA ) ) {
5274
			$body = file_get_contents( 'php://input' );
5275
		} else {
5276
			$body = null;
5277
		}
5278
5279
		$signature = $jetpack_signature->sign_current_request(
5280
			array( 'body' => is_null( $body ) ? $this->HTTP_RAW_POST_DATA : $body, )
5281
		);
5282
5283
		$signature_details['url'] = $jetpack_signature->current_request_url;
5284
5285
		if ( ! $signature ) {
5286
			return new WP_Error( 'could_not_sign', 'Unknown signature error', compact( 'signature_details' ) );
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with 'could_not_sign'.

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.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
5287
		} else if ( is_wp_error( $signature ) ) {
5288
			return $signature;
5289
		}
5290
5291
		$timestamp = (int) $_GET['timestamp'];
5292
		$nonce     = stripslashes( (string) $_GET['nonce'] );
5293
5294
		// Use up the nonce regardless of whether the signature matches.
5295
		if ( ! $this->add_nonce( $timestamp, $nonce ) ) {
5296
			return new WP_Error( 'invalid_nonce', 'Could not add nonce', compact( 'signature_details' ) );
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with 'invalid_nonce'.

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.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
5297
		}
5298
5299
		// Be careful about what you do with this debugging data.
5300
		// If a malicious requester has access to the expected signature,
5301
		// bad things might be possible.
5302
		$signature_details['expected'] = $signature;
5303
5304
		if ( ! hash_equals( $signature, $_GET['signature'] ) ) {
5305
			return new WP_Error( 'signature_mismatch', 'Signature mismatch', compact( 'signature_details' ) );
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with 'signature_mismatch'.

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.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
5306
		}
5307
5308
		// Let's see if this is onboarding. In such case, use user token type and the provided user id.
5309
		if ( isset( $this->HTTP_RAW_POST_DATA ) || ! empty( $_GET['onboarding'] ) ) {
5310
			if ( ! empty( $_GET['onboarding'] ) ) {
5311
				$jpo = $_GET;
5312
			} else {
5313
				$jpo = json_decode( $this->HTTP_RAW_POST_DATA, true );
5314
			}
5315
5316
			$jpo_token = ! empty( $jpo['onboarding']['token'] ) ? $jpo['onboarding']['token'] : null;
5317
			$jpo_user = ! empty( $jpo['onboarding']['jpUser'] ) ? $jpo['onboarding']['jpUser'] : null;
5318
5319
			if (
5320
				isset( $jpo_user ) && isset( $jpo_token ) &&
5321
				is_email( $jpo_user ) && ctype_alnum( $jpo_token ) &&
5322
				isset( $_GET['rest_route'] ) &&
5323
				self::validate_onboarding_token_action( $jpo_token, $_GET['rest_route'] )
5324
			) {
5325
				$jpUser = get_user_by( 'email', $jpo_user );
5326
				if ( is_a( $jpUser, 'WP_User' ) ) {
5327
					wp_set_current_user( $jpUser->ID );
5328
					$user_can = is_multisite()
5329
						? current_user_can_for_blog( get_current_blog_id(), 'manage_options' )
5330
						: current_user_can( 'manage_options' );
5331
					if ( $user_can ) {
5332
						$token_type = 'user';
5333
						$token->external_user_id = $jpUser->ID;
5334
					}
5335
				}
5336
			}
5337
		}
5338
5339
		return array(
5340
			'type'      => $token_type,
5341
			'token_key' => $token_key,
5342
			'user_id'   => $token->external_user_id,
5343
		);
5344
	}
5345
5346
	/**
5347
	 * Authenticates XML-RPC and other requests from the Jetpack Server
5348
	 */
5349
	function authenticate_jetpack( $user, $username, $password ) {
5350
		if ( is_a( $user, 'WP_User' ) ) {
5351
			return $user;
5352
		}
5353
5354
		$token_details = $this->verify_xml_rpc_signature();
5355
5356
		if ( ! $token_details ) {
5357
			return $user;
5358
		}
5359
5360
		if ( 'user' !== $token_details['type'] ) {
5361
			return $user;
5362
		}
5363
5364
		if ( ! $token_details['user_id'] ) {
5365
			return $user;
5366
		}
5367
5368
		nocache_headers();
5369
5370
		return new WP_User( $token_details['user_id'] );
5371
	}
5372
5373
	// Authenticates requests from Jetpack server to WP REST API endpoints.
5374
	// Uses the existing XMLRPC request signing implementation.
5375
	function wp_rest_authenticate( $user ) {
5376
		if ( ! empty( $user ) ) {
5377
			// Another authentication method is in effect.
5378
			return $user;
5379
		}
5380
5381
		if ( ! isset( $_GET['_for'] ) || $_GET['_for'] !== 'jetpack' ) {
5382
			// Nothing to do for this authentication method.
5383
			return null;
5384
		}
5385
5386
		if ( ! isset( $_GET['token'] ) && ! isset( $_GET['signature'] ) ) {
5387
			// Nothing to do for this authentication method.
5388
			return null;
5389
		}
5390
5391
		// Ensure that we always have the request body available.  At this
5392
		// point, the WP REST API code to determine the request body has not
5393
		// run yet.  That code may try to read from 'php://input' later, but
5394
		// this can only be done once per request in PHP versions prior to 5.6.
5395
		// So we will go ahead and perform this read now if needed, and save
5396
		// the request body where both the Jetpack signature verification code
5397
		// and the WP REST API code can see it.
5398
		if ( ! isset( $GLOBALS['HTTP_RAW_POST_DATA'] ) ) {
5399
			$GLOBALS['HTTP_RAW_POST_DATA'] = file_get_contents( 'php://input' );
5400
		}
5401
		$this->HTTP_RAW_POST_DATA = $GLOBALS['HTTP_RAW_POST_DATA'];
5402
5403
		// Only support specific request parameters that have been tested and
5404
		// are known to work with signature verification.  A different method
5405
		// can be passed to the WP REST API via the '?_method=' parameter if
5406
		// needed.
5407
		if ( $_SERVER['REQUEST_METHOD'] !== 'GET' && $_SERVER['REQUEST_METHOD'] !== 'POST' ) {
5408
			$this->rest_authentication_status = new WP_Error(
5409
				'rest_invalid_request',
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with 'rest_invalid_request'.

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.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
5410
				__( 'This request method is not supported.', 'jetpack' ),
5411
				array( 'status' => 400 )
5412
			);
5413
			return null;
5414
		}
5415
		if ( $_SERVER['REQUEST_METHOD'] !== 'POST' && ! empty( $this->HTTP_RAW_POST_DATA ) ) {
5416
			$this->rest_authentication_status = new WP_Error(
5417
				'rest_invalid_request',
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with 'rest_invalid_request'.

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.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
5418
				__( 'This request method does not support body parameters.', 'jetpack' ),
5419
				array( 'status' => 400 )
5420
			);
5421
			return null;
5422
		}
5423
5424
		$verified = $this->verify_xml_rpc_signature();
5425
5426
		if (
5427
			$verified &&
5428
			isset( $verified['type'] ) &&
5429
			'user' === $verified['type'] &&
5430
			! empty( $verified['user_id'] )
5431
		) {
5432
			// Authentication successful.
5433
			$this->rest_authentication_status = true;
5434
			return $verified['user_id'];
5435
		}
5436
5437
		// Something else went wrong.  Probably a signature error.
5438
		$this->rest_authentication_status = new WP_Error(
5439
			'rest_invalid_signature',
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with 'rest_invalid_signature'.

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.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
5440
			__( 'The request is not signed correctly.', 'jetpack' ),
5441
			array( 'status' => 400 )
5442
		);
5443
		return null;
5444
	}
5445
5446
	/**
5447
	 * Report authentication status to the WP REST API.
5448
	 *
5449
	 * @param  WP_Error|mixed $result Error from another authentication handler, null if we should handle it, or another value if not
0 ignored issues
show
Bug introduced by
There is no parameter named $result. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
5450
	 * @return WP_Error|boolean|null {@see WP_JSON_Server::check_authentication}
5451
	 */
5452
	public function wp_rest_authentication_errors( $value ) {
5453
		if ( $value !== null ) {
5454
			return $value;
5455
		}
5456
		return $this->rest_authentication_status;
5457
	}
5458
5459
	function add_nonce( $timestamp, $nonce ) {
5460
		global $wpdb;
5461
		static $nonces_used_this_request = array();
5462
5463
		if ( isset( $nonces_used_this_request["$timestamp:$nonce"] ) ) {
5464
			return $nonces_used_this_request["$timestamp:$nonce"];
5465
		}
5466
5467
		// This should always have gone through Jetpack_Signature::sign_request() first to check $timestamp an $nonce
5468
		$timestamp = (int) $timestamp;
5469
		$nonce     = esc_sql( $nonce );
5470
5471
		// Raw query so we can avoid races: add_option will also update
5472
		$show_errors = $wpdb->show_errors( false );
5473
5474
		$old_nonce = $wpdb->get_row(
5475
			$wpdb->prepare( "SELECT * FROM `$wpdb->options` WHERE option_name = %s", "jetpack_nonce_{$timestamp}_{$nonce}" )
5476
		);
5477
5478
		if ( is_null( $old_nonce ) ) {
5479
			$return = $wpdb->query(
5480
				$wpdb->prepare(
5481
					"INSERT INTO `$wpdb->options` (`option_name`, `option_value`, `autoload`) VALUES (%s, %s, %s)",
5482
					"jetpack_nonce_{$timestamp}_{$nonce}",
5483
					time(),
5484
					'no'
5485
				)
5486
			);
5487
		} else {
5488
			$return = false;
5489
		}
5490
5491
		$wpdb->show_errors( $show_errors );
5492
5493
		$nonces_used_this_request["$timestamp:$nonce"] = $return;
5494
5495
		return $return;
5496
	}
5497
5498
	/**
5499
	 * In some setups, $HTTP_RAW_POST_DATA can be emptied during some IXR_Server paths since it is passed by reference to various methods.
5500
	 * Capture it here so we can verify the signature later.
5501
	 */
5502
	function xmlrpc_methods( $methods ) {
5503
		$this->HTTP_RAW_POST_DATA = $GLOBALS['HTTP_RAW_POST_DATA'];
5504
		return $methods;
5505
	}
5506
5507
	function public_xmlrpc_methods( $methods ) {
5508
		if ( array_key_exists( 'wp.getOptions', $methods ) ) {
5509
			$methods['wp.getOptions'] = array( $this, 'jetpack_getOptions' );
5510
		}
5511
		return $methods;
5512
	}
5513
5514
	function jetpack_getOptions( $args ) {
5515
		global $wp_xmlrpc_server;
5516
5517
		$wp_xmlrpc_server->escape( $args );
5518
5519
		$username	= $args[1];
5520
		$password	= $args[2];
5521
5522
		if ( !$user = $wp_xmlrpc_server->login($username, $password) ) {
5523
			return $wp_xmlrpc_server->error;
5524
		}
5525
5526
		$options = array();
5527
		$user_data = $this->get_connected_user_data();
5528
		if ( is_array( $user_data ) ) {
5529
			$options['jetpack_user_id'] = array(
5530
				'desc'          => __( 'The WP.com user ID of the connected user', 'jetpack' ),
5531
				'readonly'      => true,
5532
				'value'         => $user_data['ID'],
5533
			);
5534
			$options['jetpack_user_login'] = array(
5535
				'desc'          => __( 'The WP.com username of the connected user', 'jetpack' ),
5536
				'readonly'      => true,
5537
				'value'         => $user_data['login'],
5538
			);
5539
			$options['jetpack_user_email'] = array(
5540
				'desc'          => __( 'The WP.com user email of the connected user', 'jetpack' ),
5541
				'readonly'      => true,
5542
				'value'         => $user_data['email'],
5543
			);
5544
			$options['jetpack_user_site_count'] = array(
5545
				'desc'          => __( 'The number of sites of the connected WP.com user', 'jetpack' ),
5546
				'readonly'      => true,
5547
				'value'         => $user_data['site_count'],
5548
			);
5549
		}
5550
		$wp_xmlrpc_server->blog_options = array_merge( $wp_xmlrpc_server->blog_options, $options );
5551
		$args = stripslashes_deep( $args );
5552
		return $wp_xmlrpc_server->wp_getOptions( $args );
5553
	}
5554
5555
	function xmlrpc_options( $options ) {
5556
		$jetpack_client_id = false;
5557
		if ( self::is_active() ) {
5558
			$jetpack_client_id = Jetpack_Options::get_option( 'id' );
5559
		}
5560
		$options['jetpack_version'] = array(
5561
				'desc'          => __( 'Jetpack Plugin Version', 'jetpack' ),
5562
				'readonly'      => true,
5563
				'value'         => JETPACK__VERSION,
5564
		);
5565
5566
		$options['jetpack_client_id'] = array(
5567
				'desc'          => __( 'The Client ID/WP.com Blog ID of this site', 'jetpack' ),
5568
				'readonly'      => true,
5569
				'value'         => $jetpack_client_id,
5570
		);
5571
		return $options;
5572
	}
5573
5574
	public static function clean_nonces( $all = false ) {
5575
		global $wpdb;
5576
5577
		$sql = "DELETE FROM `$wpdb->options` WHERE `option_name` LIKE %s";
5578
		$sql_args = array( $wpdb->esc_like( 'jetpack_nonce_' ) . '%' );
5579
5580
		if ( true !== $all ) {
5581
			$sql .= ' AND CAST( `option_value` AS UNSIGNED ) < %d';
5582
			$sql_args[] = time() - 3600;
5583
		}
5584
5585
		$sql .= ' ORDER BY `option_id` LIMIT 100';
5586
5587
		$sql = $wpdb->prepare( $sql, $sql_args );
5588
5589
		for ( $i = 0; $i < 1000; $i++ ) {
5590
			if ( ! $wpdb->query( $sql ) ) {
5591
				break;
5592
			}
5593
		}
5594
	}
5595
5596
	/**
5597
	 * State is passed via cookies from one request to the next, but never to subsequent requests.
5598
	 * SET: state( $key, $value );
5599
	 * GET: $value = state( $key );
5600
	 *
5601
	 * @param string $key
0 ignored issues
show
Documentation introduced by
Should the type for parameter $key not be string|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
5602
	 * @param string $value
0 ignored issues
show
Documentation introduced by
Should the type for parameter $value not be string|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
5603
	 * @param bool $restate private
5604
	 */
5605
	public static function state( $key = null, $value = null, $restate = false ) {
5606
		static $state = array();
5607
		static $path, $domain;
5608
		if ( ! isset( $path ) ) {
5609
			require_once( ABSPATH . 'wp-admin/includes/plugin.php' );
5610
			$admin_url = Jetpack::admin_url();
5611
			$bits      = wp_parse_url( $admin_url );
5612
5613
			if ( is_array( $bits ) ) {
5614
				$path   = ( isset( $bits['path'] ) ) ? dirname( $bits['path'] ) : null;
5615
				$domain = ( isset( $bits['host'] ) ) ? $bits['host'] : null;
5616
			} else {
5617
				$path = $domain = null;
5618
			}
5619
		}
5620
5621
		// Extract state from cookies and delete cookies
5622
		if ( isset( $_COOKIE[ 'jetpackState' ] ) && is_array( $_COOKIE[ 'jetpackState' ] ) ) {
5623
			$yum = $_COOKIE[ 'jetpackState' ];
5624
			unset( $_COOKIE[ 'jetpackState' ] );
5625
			foreach ( $yum as $k => $v ) {
5626
				if ( strlen( $v ) )
5627
					$state[ $k ] = $v;
5628
				setcookie( "jetpackState[$k]", false, 0, $path, $domain );
5629
			}
5630
		}
5631
5632
		if ( $restate ) {
5633
			foreach ( $state as $k => $v ) {
5634
				setcookie( "jetpackState[$k]", $v, 0, $path, $domain );
5635
			}
5636
			return;
5637
		}
5638
5639
		// Get a state variable
5640
		if ( isset( $key ) && ! isset( $value ) ) {
5641
			if ( array_key_exists( $key, $state ) )
5642
				return $state[ $key ];
5643
			return null;
5644
		}
5645
5646
		// Set a state variable
5647
		if ( isset ( $key ) && isset( $value ) ) {
5648
			if( is_array( $value ) && isset( $value[0] ) ) {
5649
				$value = $value[0];
5650
			}
5651
			$state[ $key ] = $value;
5652
			setcookie( "jetpackState[$key]", $value, 0, $path, $domain );
5653
		}
5654
	}
5655
5656
	public static function restate() {
5657
		Jetpack::state( null, null, true );
5658
	}
5659
5660
	public static function check_privacy( $file ) {
5661
		static $is_site_publicly_accessible = null;
5662
5663
		if ( is_null( $is_site_publicly_accessible ) ) {
5664
			$is_site_publicly_accessible = false;
5665
5666
			Jetpack::load_xml_rpc_client();
5667
			$rpc = new Jetpack_IXR_Client();
5668
5669
			$success = $rpc->query( 'jetpack.isSitePubliclyAccessible', home_url() );
5670
			if ( $success ) {
5671
				$response = $rpc->getResponse();
5672
				if ( $response ) {
5673
					$is_site_publicly_accessible = true;
5674
				}
5675
			}
5676
5677
			Jetpack_Options::update_option( 'public', (int) $is_site_publicly_accessible );
5678
		}
5679
5680
		if ( $is_site_publicly_accessible ) {
5681
			return;
5682
		}
5683
5684
		$module_slug = self::get_module_slug( $file );
5685
5686
		$privacy_checks = Jetpack::state( 'privacy_checks' );
5687
		if ( ! $privacy_checks ) {
5688
			$privacy_checks = $module_slug;
5689
		} else {
5690
			$privacy_checks .= ",$module_slug";
5691
		}
5692
5693
		Jetpack::state( 'privacy_checks', $privacy_checks );
5694
	}
5695
5696
	/**
5697
	 * Helper method for multicall XMLRPC.
5698
	 */
5699
	public static function xmlrpc_async_call() {
5700
		global $blog_id;
5701
		static $clients = array();
5702
5703
		$client_blog_id = is_multisite() ? $blog_id : 0;
5704
5705
		if ( ! isset( $clients[$client_blog_id] ) ) {
5706
			Jetpack::load_xml_rpc_client();
5707
			$clients[$client_blog_id] = new Jetpack_IXR_ClientMulticall( array( 'user_id' => JETPACK_MASTER_USER, ) );
5708
			if ( function_exists( 'ignore_user_abort' ) ) {
5709
				ignore_user_abort( true );
5710
			}
5711
			add_action( 'shutdown', array( 'Jetpack', 'xmlrpc_async_call' ) );
5712
		}
5713
5714
		$args = func_get_args();
5715
5716
		if ( ! empty( $args[0] ) ) {
5717
			call_user_func_array( array( $clients[$client_blog_id], 'addCall' ), $args );
5718
		} elseif ( is_multisite() ) {
5719
			foreach ( $clients as $client_blog_id => $client ) {
5720
				if ( ! $client_blog_id || empty( $client->calls ) ) {
5721
					continue;
5722
				}
5723
5724
				$switch_success = switch_to_blog( $client_blog_id, true );
5725
				if ( ! $switch_success ) {
5726
					continue;
5727
				}
5728
5729
				flush();
5730
				$client->query();
5731
5732
				restore_current_blog();
5733
			}
5734
		} else {
5735
			if ( isset( $clients[0] ) && ! empty( $clients[0]->calls ) ) {
5736
				flush();
5737
				$clients[0]->query();
5738
			}
5739
		}
5740
	}
5741
5742
	public static function staticize_subdomain( $url ) {
5743
5744
		// Extract hostname from URL
5745
		$host = parse_url( $url, PHP_URL_HOST );
5746
5747
		// Explode hostname on '.'
5748
		$exploded_host = explode( '.', $host );
5749
5750
		// Retrieve the name and TLD
5751
		if ( count( $exploded_host ) > 1 ) {
5752
			$name = $exploded_host[ count( $exploded_host ) - 2 ];
5753
			$tld = $exploded_host[ count( $exploded_host ) - 1 ];
5754
			// Rebuild domain excluding subdomains
5755
			$domain = $name . '.' . $tld;
5756
		} else {
5757
			$domain = $host;
5758
		}
5759
		// Array of Automattic domains
5760
		$domain_whitelist = array( 'wordpress.com', 'wp.com' );
5761
5762
		// Return $url if not an Automattic domain
5763
		if ( ! in_array( $domain, $domain_whitelist ) ) {
5764
			return $url;
5765
		}
5766
5767
		if ( is_ssl() ) {
5768
			return preg_replace( '|https?://[^/]++/|', 'https://s-ssl.wordpress.com/', $url );
5769
		}
5770
5771
		srand( crc32( basename( $url ) ) );
5772
		$static_counter = rand( 0, 2 );
5773
		srand(); // this resets everything that relies on this, like array_rand() and shuffle()
5774
5775
		return preg_replace( '|://[^/]+?/|', "://s$static_counter.wp.com/", $url );
5776
	}
5777
5778
/* JSON API Authorization */
5779
5780
	/**
5781
	 * Handles the login action for Authorizing the JSON API
5782
	 */
5783
	function login_form_json_api_authorization() {
5784
		$this->verify_json_api_authorization_request();
5785
5786
		add_action( 'wp_login', array( &$this, 'store_json_api_authorization_token' ), 10, 2 );
5787
5788
		add_action( 'login_message', array( &$this, 'login_message_json_api_authorization' ) );
5789
		add_action( 'login_form', array( &$this, 'preserve_action_in_login_form_for_json_api_authorization' ) );
5790
		add_filter( 'site_url', array( &$this, 'post_login_form_to_signed_url' ), 10, 3 );
5791
	}
5792
5793
	// Make sure the login form is POSTed to the signed URL so we can reverify the request
5794
	function post_login_form_to_signed_url( $url, $path, $scheme ) {
5795
		if ( 'wp-login.php' !== $path || ( 'login_post' !== $scheme && 'login' !== $scheme ) ) {
5796
			return $url;
5797
		}
5798
5799
		$parsed_url = parse_url( $url );
5800
		$url = strtok( $url, '?' );
5801
		$url = "$url?{$_SERVER['QUERY_STRING']}";
5802
		if ( ! empty( $parsed_url['query'] ) )
5803
			$url .= "&{$parsed_url['query']}";
5804
5805
		return $url;
5806
	}
5807
5808
	// Make sure the POSTed request is handled by the same action
5809
	function preserve_action_in_login_form_for_json_api_authorization() {
5810
		echo "<input type='hidden' name='action' value='jetpack_json_api_authorization' />\n";
5811
		echo "<input type='hidden' name='jetpack_json_api_original_query' value='" . esc_url( set_url_scheme( $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] ) ) . "' />\n";
5812
	}
5813
5814
	// If someone logs in to approve API access, store the Access Code in usermeta
5815
	function store_json_api_authorization_token( $user_login, $user ) {
5816
		add_filter( 'login_redirect', array( &$this, 'add_token_to_login_redirect_json_api_authorization' ), 10, 3 );
5817
		add_filter( 'allowed_redirect_hosts', array( &$this, 'allow_wpcom_public_api_domain' ) );
5818
		$token = wp_generate_password( 32, false );
5819
		update_user_meta( $user->ID, 'jetpack_json_api_' . $this->json_api_authorization_request['client_id'], $token );
5820
	}
5821
5822
	// Add public-api.wordpress.com to the safe redirect whitelist - only added when someone allows API access
5823
	function allow_wpcom_public_api_domain( $domains ) {
5824
		$domains[] = 'public-api.wordpress.com';
5825
		return $domains;
5826
	}
5827
5828
	static function is_redirect_encoded( $redirect_url ) {
5829
		return preg_match( '/https?%3A%2F%2F/i', $redirect_url ) > 0;
5830
	}
5831
5832
	// Add all wordpress.com environments to the safe redirect whitelist
5833
	function allow_wpcom_environments( $domains ) {
5834
		$domains[] = 'wordpress.com';
5835
		$domains[] = 'wpcalypso.wordpress.com';
5836
		$domains[] = 'horizon.wordpress.com';
5837
		$domains[] = 'calypso.localhost';
5838
		return $domains;
5839
	}
5840
5841
	// Add the Access Code details to the public-api.wordpress.com redirect
5842
	function add_token_to_login_redirect_json_api_authorization( $redirect_to, $original_redirect_to, $user ) {
5843
		return add_query_arg(
5844
			urlencode_deep(
5845
				array(
5846
					'jetpack-code'    => get_user_meta( $user->ID, 'jetpack_json_api_' . $this->json_api_authorization_request['client_id'], true ),
5847
					'jetpack-user-id' => (int) $user->ID,
5848
					'jetpack-state'   => $this->json_api_authorization_request['state'],
5849
				)
5850
			),
5851
			$redirect_to
5852
		);
5853
	}
5854
5855
5856
	/**
5857
	 * Verifies the request by checking the signature
5858
	 *
5859
	 * @since 4.6.0 Method was updated to use `$_REQUEST` instead of `$_GET` and `$_POST`. Method also updated to allow
5860
	 * passing in an `$environment` argument that overrides `$_REQUEST`. This was useful for integrating with SSO.
5861
	 *
5862
	 * @param null|array $environment
5863
	 */
5864
	function verify_json_api_authorization_request( $environment = null ) {
5865
		$environment = is_null( $environment )
5866
			? $_REQUEST
5867
			: $environment;
5868
5869
		list( $envToken, $envVersion, $envUserId ) = explode( ':', $environment['token'] );
0 ignored issues
show
Unused Code introduced by
The assignment to $envVersion is unused. Consider omitting it like so list($first,,$third).

This checks looks for assignemnts to variables using the list(...) function, where not all assigned variables are subsequently used.

Consider the following code example.

<?php

function returnThreeValues() {
    return array('a', 'b', 'c');
}

list($a, $b, $c) = returnThreeValues();

print $a . " - " . $c;

Only the variables $a and $c are used. There was no need to assign $b.

Instead, the list call could have been.

list($a,, $c) = returnThreeValues();
Loading history...
5870
		$token = Jetpack_Data::get_access_token( $envUserId, $envToken );
0 ignored issues
show
Deprecated Code introduced by
The method Jetpack_Data::get_access_token() has been deprecated with message: 7.5 Use Connection_Manager instead.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
5871
		if ( ! $token || empty( $token->secret ) ) {
5872
			wp_die( __( 'You must connect your Jetpack plugin to WordPress.com to use this feature.' , 'jetpack' ) );
5873
		}
5874
5875
		$die_error = __( 'Someone may be trying to trick you into giving them access to your site.  Or it could be you just encountered a bug :).  Either way, please close this window.', 'jetpack' );
5876
5877
		// Host has encoded the request URL, probably as a result of a bad http => https redirect
5878
		if ( Jetpack::is_redirect_encoded( $_GET['redirect_to'] ) ) {
5879
			/**
5880
			 * Jetpack authorisation request Error.
5881
			 *
5882
			 * @since 7.5.0
5883
			 *
5884
			 */
5885
			do_action( 'jetpack_verify_api_authorization_request_error_double_encode' );
5886
			$die_error = sprintf(
5887
				/* translators: %s is a URL */
5888
				__( 'Your site is incorrectly double-encoding redirects from http to https. This is preventing Jetpack from authenticating your connection. Please visit our <a href="%s">support page</a> for details about how to resolve this.', 'jetpack' ),
5889
				'https://jetpack.com/support/double-encoding/'
5890
			);
5891
		}
5892
5893
		$jetpack_signature = new Jetpack_Signature( $token->secret, (int) Jetpack_Options::get_option( 'time_diff' ) );
5894
5895
		if ( isset( $environment['jetpack_json_api_original_query'] ) ) {
5896
			$signature = $jetpack_signature->sign_request(
5897
				$environment['token'],
5898
				$environment['timestamp'],
5899
				$environment['nonce'],
5900
				'',
5901
				'GET',
5902
				$environment['jetpack_json_api_original_query'],
5903
				null,
5904
				true
5905
			);
5906
		} else {
5907
			$signature = $jetpack_signature->sign_current_request( array( 'body' => null, 'method' => 'GET' ) );
5908
		}
5909
5910
		if ( ! $signature ) {
5911
			wp_die( $die_error );
5912
		} else if ( is_wp_error( $signature ) ) {
5913
			wp_die( $die_error );
5914
		} else if ( ! hash_equals( $signature, $environment['signature'] ) ) {
5915
			if ( is_ssl() ) {
5916
				// If we signed an HTTP request on the Jetpack Servers, but got redirected to HTTPS by the local blog, check the HTTP signature as well
5917
				$signature = $jetpack_signature->sign_current_request( array( 'scheme' => 'http', 'body' => null, 'method' => 'GET' ) );
5918
				if ( ! $signature || is_wp_error( $signature ) || ! hash_equals( $signature, $environment['signature'] ) ) {
5919
					wp_die( $die_error );
5920
				}
5921
			} else {
5922
				wp_die( $die_error );
5923
			}
5924
		}
5925
5926
		$timestamp = (int) $environment['timestamp'];
5927
		$nonce     = stripslashes( (string) $environment['nonce'] );
5928
5929
		if ( ! $this->add_nonce( $timestamp, $nonce ) ) {
5930
			// De-nonce the nonce, at least for 5 minutes.
5931
			// We have to reuse this nonce at least once (used the first time when the initial request is made, used a second time when the login form is POSTed)
5932
			$old_nonce_time = get_option( "jetpack_nonce_{$timestamp}_{$nonce}" );
5933
			if ( $old_nonce_time < time() - 300 ) {
5934
				wp_die( __( 'The authorization process expired.  Please go back and try again.' , 'jetpack' ) );
5935
			}
5936
		}
5937
5938
		$data = json_decode( base64_decode( stripslashes( $environment['data'] ) ) );
5939
		$data_filters = array(
5940
			'state'        => 'opaque',
5941
			'client_id'    => 'int',
5942
			'client_title' => 'string',
5943
			'client_image' => 'url',
5944
		);
5945
5946
		foreach ( $data_filters as $key => $sanitation ) {
5947
			if ( ! isset( $data->$key ) ) {
5948
				wp_die( $die_error );
5949
			}
5950
5951
			switch ( $sanitation ) {
5952
			case 'int' :
5953
				$this->json_api_authorization_request[$key] = (int) $data->$key;
5954
				break;
5955
			case 'opaque' :
5956
				$this->json_api_authorization_request[$key] = (string) $data->$key;
5957
				break;
5958
			case 'string' :
5959
				$this->json_api_authorization_request[$key] = wp_kses( (string) $data->$key, array() );
5960
				break;
5961
			case 'url' :
5962
				$this->json_api_authorization_request[$key] = esc_url_raw( (string) $data->$key );
5963
				break;
5964
			}
5965
		}
5966
5967
		if ( empty( $this->json_api_authorization_request['client_id'] ) ) {
5968
			wp_die( $die_error );
5969
		}
5970
	}
5971
5972
	function login_message_json_api_authorization( $message ) {
5973
		return '<p class="message">' . sprintf(
5974
			esc_html__( '%s wants to access your site&#8217;s data.  Log in to authorize that access.' , 'jetpack' ),
5975
			'<strong>' . esc_html( $this->json_api_authorization_request['client_title'] ) . '</strong>'
5976
		) . '<img src="' . esc_url( $this->json_api_authorization_request['client_image'] ) . '" /></p>';
5977
	}
5978
5979
	/**
5980
	 * Get $content_width, but with a <s>twist</s> filter.
5981
	 */
5982
	public static function get_content_width() {
5983
		$content_width = ( isset( $GLOBALS['content_width'] ) && is_numeric( $GLOBALS['content_width'] ) )
5984
			? $GLOBALS['content_width']
5985
			: false;
5986
		/**
5987
		 * Filter the Content Width value.
5988
		 *
5989
		 * @since 2.2.3
5990
		 *
5991
		 * @param string $content_width Content Width value.
5992
		 */
5993
		return apply_filters( 'jetpack_content_width', $content_width );
5994
	}
5995
5996
	/**
5997
	 * Pings the WordPress.com Mirror Site for the specified options.
5998
	 *
5999
	 * @param string|array $option_names The option names to request from the WordPress.com Mirror Site
6000
	 *
6001
	 * @return array An associative array of the option values as stored in the WordPress.com Mirror Site
6002
	 */
6003
	public function get_cloud_site_options( $option_names ) {
6004
		$option_names = array_filter( (array) $option_names, 'is_string' );
6005
6006
		Jetpack::load_xml_rpc_client();
6007
		$xml = new Jetpack_IXR_Client( array( 'user_id' => JETPACK_MASTER_USER, ) );
6008
		$xml->query( 'jetpack.fetchSiteOptions', $option_names );
6009
		if ( $xml->isError() ) {
6010
			return array(
6011
				'error_code' => $xml->getErrorCode(),
6012
				'error_msg'  => $xml->getErrorMessage(),
6013
			);
6014
		}
6015
		$cloud_site_options = $xml->getResponse();
6016
6017
		return $cloud_site_options;
6018
	}
6019
6020
	/**
6021
	 * Checks if the site is currently in an identity crisis.
6022
	 *
6023
	 * @return array|bool Array of options that are in a crisis, or false if everything is OK.
6024
	 */
6025
	public static function check_identity_crisis() {
6026
		if ( ! Jetpack::is_active() || Jetpack::is_development_mode() || ! self::validate_sync_error_idc_option() ) {
6027
			return false;
6028
		}
6029
6030
		return Jetpack_Options::get_option( 'sync_error_idc' );
6031
	}
6032
6033
	/**
6034
	 * Checks whether the home and siteurl specifically are whitelisted
6035
	 * Written so that we don't have re-check $key and $value params every time
6036
	 * we want to check if this site is whitelisted, for example in footer.php
6037
	 *
6038
	 * @since  3.8.0
6039
	 * @return bool True = already whitelisted False = not whitelisted
6040
	 */
6041
	public static function is_staging_site() {
6042
		$is_staging = false;
6043
6044
		$known_staging = array(
6045
			'urls' => array(
6046
				'#\.staging\.wpengine\.com$#i', // WP Engine
6047
				'#\.staging\.kinsta\.com$#i',   // Kinsta.com
6048
				'#\.stage\.site$#i'             // DreamPress
6049
			),
6050
			'constants' => array(
6051
				'IS_WPE_SNAPSHOT',      // WP Engine
6052
				'KINSTA_DEV_ENV',       // Kinsta.com
6053
				'WPSTAGECOACH_STAGING', // WP Stagecoach
6054
				'JETPACK_STAGING_MODE', // Generic
6055
			)
6056
		);
6057
		/**
6058
		 * Filters the flags of known staging sites.
6059
		 *
6060
		 * @since 3.9.0
6061
		 *
6062
		 * @param array $known_staging {
6063
		 *     An array of arrays that each are used to check if the current site is staging.
6064
		 *     @type array $urls      URLs of staging sites in regex to check against site_url.
6065
		 *     @type array $constants PHP constants of known staging/developement environments.
6066
		 *  }
6067
		 */
6068
		$known_staging = apply_filters( 'jetpack_known_staging', $known_staging );
6069
6070
		if ( isset( $known_staging['urls'] ) ) {
6071
			foreach ( $known_staging['urls'] as $url ){
6072
				if ( preg_match( $url, site_url() ) ) {
6073
					$is_staging = true;
6074
					break;
6075
				}
6076
			}
6077
		}
6078
6079
		if ( isset( $known_staging['constants'] ) ) {
6080
			foreach ( $known_staging['constants'] as $constant ) {
6081
				if ( defined( $constant ) && constant( $constant ) ) {
6082
					$is_staging = true;
6083
				}
6084
			}
6085
		}
6086
6087
		// Last, let's check if sync is erroring due to an IDC. If so, set the site to staging mode.
6088
		if ( ! $is_staging && self::validate_sync_error_idc_option() ) {
6089
			$is_staging = true;
6090
		}
6091
6092
		/**
6093
		 * Filters is_staging_site check.
6094
		 *
6095
		 * @since 3.9.0
6096
		 *
6097
		 * @param bool $is_staging If the current site is a staging site.
6098
		 */
6099
		return apply_filters( 'jetpack_is_staging_site', $is_staging );
6100
	}
6101
6102
	/**
6103
	 * Checks whether the sync_error_idc option is valid or not, and if not, will do cleanup.
6104
	 *
6105
	 * @since 4.4.0
6106
	 * @since 5.4.0 Do not call get_sync_error_idc_option() unless site is in IDC
6107
	 *
6108
	 * @return bool
6109
	 */
6110
	public static function validate_sync_error_idc_option() {
6111
		$is_valid = false;
6112
6113
		$idc_allowed = get_transient( 'jetpack_idc_allowed' );
6114
		if ( false === $idc_allowed ) {
6115
			$response = wp_remote_get( 'https://jetpack.com/is-idc-allowed/' );
6116
			if ( 200 === (int) wp_remote_retrieve_response_code( $response ) ) {
6117
				$json = json_decode( wp_remote_retrieve_body( $response ) );
6118
				$idc_allowed = isset( $json, $json->result ) && $json->result ? '1' : '0';
6119
				$transient_duration = HOUR_IN_SECONDS;
6120
			} else {
6121
				// If the request failed for some reason, then assume IDC is allowed and set shorter transient.
6122
				$idc_allowed = '1';
6123
				$transient_duration = 5 * MINUTE_IN_SECONDS;
6124
			}
6125
6126
			set_transient( 'jetpack_idc_allowed', $idc_allowed, $transient_duration );
6127
		}
6128
6129
		// Is the site opted in and does the stored sync_error_idc option match what we now generate?
6130
		$sync_error = Jetpack_Options::get_option( 'sync_error_idc' );
6131
		if ( $idc_allowed && $sync_error && self::sync_idc_optin() ) {
6132
			$local_options = self::get_sync_error_idc_option();
6133
			if ( $sync_error['home'] === $local_options['home'] && $sync_error['siteurl'] === $local_options['siteurl'] ) {
6134
				$is_valid = true;
6135
			}
6136
		}
6137
6138
		/**
6139
		 * Filters whether the sync_error_idc option is valid.
6140
		 *
6141
		 * @since 4.4.0
6142
		 *
6143
		 * @param bool $is_valid If the sync_error_idc is valid or not.
6144
		 */
6145
		$is_valid = (bool) apply_filters( 'jetpack_sync_error_idc_validation', $is_valid );
6146
6147
		if ( ! $idc_allowed || ( ! $is_valid && $sync_error ) ) {
6148
			// Since the option exists, and did not validate, delete it
6149
			Jetpack_Options::delete_option( 'sync_error_idc' );
6150
		}
6151
6152
		return $is_valid;
6153
	}
6154
6155
	/**
6156
	 * Normalizes a url by doing three things:
6157
	 *  - Strips protocol
6158
	 *  - Strips www
6159
	 *  - Adds a trailing slash
6160
	 *
6161
	 * @since 4.4.0
6162
	 * @param string $url
6163
	 * @return WP_Error|string
6164
	 */
6165
	public static function normalize_url_protocol_agnostic( $url ) {
6166
		$parsed_url = wp_parse_url( trailingslashit( esc_url_raw( $url ) ) );
6167
		if ( ! $parsed_url || empty( $parsed_url['host'] ) || empty( $parsed_url['path'] ) ) {
6168
			return new WP_Error( 'cannot_parse_url', sprintf( esc_html__( 'Cannot parse URL %s', 'jetpack' ), $url ) );
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with 'cannot_parse_url'.

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.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
6169
		}
6170
6171
		// Strip www and protocols
6172
		$url = preg_replace( '/^www\./i', '', $parsed_url['host'] . $parsed_url['path'] );
6173
		return $url;
6174
	}
6175
6176
	/**
6177
	 * Gets the value that is to be saved in the jetpack_sync_error_idc option.
6178
	 *
6179
	 * @since 4.4.0
6180
	 * @since 5.4.0 Add transient since home/siteurl retrieved directly from DB
6181
	 *
6182
	 * @param array $response
6183
	 * @return array Array of the local urls, wpcom urls, and error code
6184
	 */
6185
	public static function get_sync_error_idc_option( $response = array() ) {
6186
		// Since the local options will hit the database directly, store the values
6187
		// in a transient to allow for autoloading and caching on subsequent views.
6188
		$local_options = get_transient( 'jetpack_idc_local' );
6189
		if ( false === $local_options ) {
6190
			$local_options = array(
6191
				'home'    => Functions::home_url(),
6192
				'siteurl' => Functions::site_url(),
6193
			);
6194
			set_transient( 'jetpack_idc_local', $local_options, MINUTE_IN_SECONDS );
6195
		}
6196
6197
		$options = array_merge( $local_options, $response );
6198
6199
		$returned_values = array();
6200
		foreach( $options as $key => $option ) {
6201
			if ( 'error_code' === $key ) {
6202
				$returned_values[ $key ] = $option;
6203
				continue;
6204
			}
6205
6206
			if ( is_wp_error( $normalized_url = self::normalize_url_protocol_agnostic( $option ) ) ) {
6207
				continue;
6208
			}
6209
6210
			$returned_values[ $key ] = $normalized_url;
6211
		}
6212
6213
		set_transient( 'jetpack_idc_option', $returned_values, MINUTE_IN_SECONDS );
6214
6215
		return $returned_values;
6216
	}
6217
6218
	/**
6219
	 * Returns the value of the jetpack_sync_idc_optin filter, or constant.
6220
	 * If set to true, the site will be put into staging mode.
6221
	 *
6222
	 * @since 4.3.2
6223
	 * @return bool
6224
	 */
6225
	public static function sync_idc_optin() {
6226
		if ( Constants::is_defined( 'JETPACK_SYNC_IDC_OPTIN' ) ) {
6227
			$default = Constants::get_constant( 'JETPACK_SYNC_IDC_OPTIN' );
6228
		} else {
6229
			$default = ! Constants::is_defined( 'SUNRISE' ) && ! is_multisite();
6230
		}
6231
6232
		/**
6233
		 * Allows sites to optin to IDC mitigation which blocks the site from syncing to WordPress.com when the home
6234
		 * URL or site URL do not match what WordPress.com expects. The default value is either false, or the value of
6235
		 * JETPACK_SYNC_IDC_OPTIN constant if set.
6236
		 *
6237
		 * @since 4.3.2
6238
		 *
6239
		 * @param bool $default Whether the site is opted in to IDC mitigation.
6240
		 */
6241
		return (bool) apply_filters( 'jetpack_sync_idc_optin', $default );
6242
	}
6243
6244
	/**
6245
	 * Maybe Use a .min.css stylesheet, maybe not.
6246
	 *
6247
	 * Hooks onto `plugins_url` filter at priority 1, and accepts all 3 args.
6248
	 */
6249
	public static function maybe_min_asset( $url, $path, $plugin ) {
6250
		// Short out on things trying to find actual paths.
6251
		if ( ! $path || empty( $plugin ) ) {
6252
			return $url;
6253
		}
6254
6255
		$path = ltrim( $path, '/' );
6256
6257
		// Strip out the abspath.
6258
		$base = dirname( plugin_basename( $plugin ) );
6259
6260
		// Short out on non-Jetpack assets.
6261
		if ( 'jetpack/' !== substr( $base, 0, 8 ) ) {
6262
			return $url;
6263
		}
6264
6265
		// File name parsing.
6266
		$file              = "{$base}/{$path}";
6267
		$full_path         = JETPACK__PLUGIN_DIR . substr( $file, 8 );
6268
		$file_name         = substr( $full_path, strrpos( $full_path, '/' ) + 1 );
6269
		$file_name_parts_r = array_reverse( explode( '.', $file_name ) );
6270
		$extension         = array_shift( $file_name_parts_r );
6271
6272
		if ( in_array( strtolower( $extension ), array( 'css', 'js' ) ) ) {
6273
			// Already pointing at the minified version.
6274
			if ( 'min' === $file_name_parts_r[0] ) {
6275
				return $url;
6276
			}
6277
6278
			$min_full_path = preg_replace( "#\.{$extension}$#", ".min.{$extension}", $full_path );
6279
			if ( file_exists( $min_full_path ) ) {
6280
				$url = preg_replace( "#\.{$extension}$#", ".min.{$extension}", $url );
6281
				// If it's a CSS file, stash it so we can set the .min suffix for rtl-ing.
6282
				if ( 'css' === $extension ) {
6283
					$key = str_replace( JETPACK__PLUGIN_DIR, 'jetpack/', $min_full_path );
6284
					self::$min_assets[ $key ] = $path;
6285
				}
6286
			}
6287
		}
6288
6289
		return $url;
6290
	}
6291
6292
	/**
6293
	 * If the asset is minified, let's flag .min as the suffix.
6294
	 *
6295
	 * Attached to `style_loader_src` filter.
6296
	 *
6297
	 * @param string $tag The tag that would link to the external asset.
0 ignored issues
show
Bug introduced by
There is no parameter named $tag. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
6298
	 * @param string $handle The registered handle of the script in question.
6299
	 * @param string $href The url of the asset in question.
0 ignored issues
show
Bug introduced by
There is no parameter named $href. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
6300
	 */
6301
	public static function set_suffix_on_min( $src, $handle ) {
6302
		if ( false === strpos( $src, '.min.css' ) ) {
6303
			return $src;
6304
		}
6305
6306
		if ( ! empty( self::$min_assets ) ) {
6307
			foreach ( self::$min_assets as $file => $path ) {
6308
				if ( false !== strpos( $src, $file ) ) {
6309
					wp_style_add_data( $handle, 'suffix', '.min' );
6310
					return $src;
6311
				}
6312
			}
6313
		}
6314
6315
		return $src;
6316
	}
6317
6318
	/**
6319
	 * Maybe inlines a stylesheet.
6320
	 *
6321
	 * If you'd like to inline a stylesheet instead of printing a link to it,
6322
	 * wp_style_add_data( 'handle', 'jetpack-inline', true );
6323
	 *
6324
	 * Attached to `style_loader_tag` filter.
6325
	 *
6326
	 * @param string $tag The tag that would link to the external asset.
6327
	 * @param string $handle The registered handle of the script in question.
6328
	 *
6329
	 * @return string
6330
	 */
6331
	public static function maybe_inline_style( $tag, $handle ) {
6332
		global $wp_styles;
6333
		$item = $wp_styles->registered[ $handle ];
6334
6335
		if ( ! isset( $item->extra['jetpack-inline'] ) || ! $item->extra['jetpack-inline'] ) {
6336
			return $tag;
6337
		}
6338
6339
		if ( preg_match( '# href=\'([^\']+)\' #i', $tag, $matches ) ) {
6340
			$href = $matches[1];
6341
			// Strip off query string
6342
			if ( $pos = strpos( $href, '?' ) ) {
6343
				$href = substr( $href, 0, $pos );
6344
			}
6345
			// Strip off fragment
6346
			if ( $pos = strpos( $href, '#' ) ) {
6347
				$href = substr( $href, 0, $pos );
6348
			}
6349
		} else {
6350
			return $tag;
6351
		}
6352
6353
		$plugins_dir = plugin_dir_url( JETPACK__PLUGIN_FILE );
6354
		if ( $plugins_dir !== substr( $href, 0, strlen( $plugins_dir ) ) ) {
6355
			return $tag;
6356
		}
6357
6358
		// If this stylesheet has a RTL version, and the RTL version replaces normal...
6359
		if ( isset( $item->extra['rtl'] ) && 'replace' === $item->extra['rtl'] && is_rtl() ) {
6360
			// And this isn't the pass that actually deals with the RTL version...
6361
			if ( false === strpos( $tag, " id='$handle-rtl-css' " ) ) {
6362
				// Short out, as the RTL version will deal with it in a moment.
6363
				return $tag;
6364
			}
6365
		}
6366
6367
		$file = JETPACK__PLUGIN_DIR . substr( $href, strlen( $plugins_dir ) );
6368
		$css  = Jetpack::absolutize_css_urls( file_get_contents( $file ), $href );
6369
		if ( $css ) {
6370
			$tag = "<!-- Inline {$item->handle} -->\r\n";
6371
			if ( empty( $item->extra['after'] ) ) {
6372
				wp_add_inline_style( $handle, $css );
6373
			} else {
6374
				array_unshift( $item->extra['after'], $css );
6375
				wp_style_add_data( $handle, 'after', $item->extra['after'] );
6376
			}
6377
		}
6378
6379
		return $tag;
6380
	}
6381
6382
	/**
6383
	 * Loads a view file from the views
6384
	 *
6385
	 * Data passed in with the $data parameter will be available in the
6386
	 * template file as $data['value']
6387
	 *
6388
	 * @param string $template - Template file to load
6389
	 * @param array $data - Any data to pass along to the template
6390
	 * @return boolean - If template file was found
6391
	 **/
6392
	public function load_view( $template, $data = array() ) {
6393
		$views_dir = JETPACK__PLUGIN_DIR . 'views/';
6394
6395
		if( file_exists( $views_dir . $template ) ) {
6396
			require_once( $views_dir . $template );
6397
			return true;
6398
		}
6399
6400
		error_log( "Jetpack: Unable to find view file $views_dir$template" );
6401
		return false;
6402
	}
6403
6404
	/**
6405
	 * Throws warnings for deprecated hooks to be removed from Jetpack
6406
	 */
6407
	public function deprecated_hooks() {
6408
		global $wp_filter;
6409
6410
		/*
6411
		 * Format:
6412
		 * deprecated_filter_name => replacement_name
6413
		 *
6414
		 * If there is no replacement, use null for replacement_name
6415
		 */
6416
		$deprecated_list = array(
6417
			'jetpack_bail_on_shortcode'                              => 'jetpack_shortcodes_to_include',
6418
			'wpl_sharing_2014_1'                                     => null,
6419
			'jetpack-tools-to-include'                               => 'jetpack_tools_to_include',
6420
			'jetpack_identity_crisis_options_to_check'               => null,
6421
			'update_option_jetpack_single_user_site'                 => null,
6422
			'audio_player_default_colors'                            => null,
6423
			'add_option_jetpack_featured_images_enabled'             => null,
6424
			'add_option_jetpack_update_details'                      => null,
6425
			'add_option_jetpack_updates'                             => null,
6426
			'add_option_jetpack_network_name'                        => null,
6427
			'add_option_jetpack_network_allow_new_registrations'     => null,
6428
			'add_option_jetpack_network_add_new_users'               => null,
6429
			'add_option_jetpack_network_site_upload_space'           => null,
6430
			'add_option_jetpack_network_upload_file_types'           => null,
6431
			'add_option_jetpack_network_enable_administration_menus' => null,
6432
			'add_option_jetpack_is_multi_site'                       => null,
6433
			'add_option_jetpack_is_main_network'                     => null,
6434
			'add_option_jetpack_main_network_site'                   => null,
6435
			'jetpack_sync_all_registered_options'                    => null,
6436
			'jetpack_has_identity_crisis'                            => 'jetpack_sync_error_idc_validation',
6437
			'jetpack_is_post_mailable'                               => null,
6438
			'jetpack_seo_site_host'                                  => null,
6439
			'jetpack_installed_plugin'                               => 'jetpack_plugin_installed',
6440
			'jetpack_holiday_snow_option_name'                       => null,
6441
			'jetpack_holiday_chance_of_snow'                         => null,
6442
			'jetpack_holiday_snow_js_url'                            => null,
6443
			'jetpack_is_holiday_snow_season'                         => null,
6444
			'jetpack_holiday_snow_option_updated'                    => null,
6445
			'jetpack_holiday_snowing'                                => null,
6446
			'jetpack_sso_auth_cookie_expirtation'                    => 'jetpack_sso_auth_cookie_expiration',
6447
			'jetpack_cache_plans'                                    => null,
6448
			'jetpack_updated_theme'                                  => 'jetpack_updated_themes',
6449
			'jetpack_lazy_images_skip_image_with_atttributes'        => 'jetpack_lazy_images_skip_image_with_attributes',
6450
			'jetpack_enable_site_verification'                       => null,
6451
			'can_display_jetpack_manage_notice'                      => null,
6452
			// Removed in Jetpack 7.3.0
6453
			'atd_load_scripts'                                       => null,
6454
			'atd_http_post_timeout'                                  => null,
6455
			'atd_http_post_error'                                    => null,
6456
			'atd_service_domain'                                     => null,
6457
		);
6458
6459
		// This is a silly loop depth. Better way?
6460
		foreach( $deprecated_list AS $hook => $hook_alt ) {
6461
			if ( has_action( $hook ) ) {
6462
				foreach( $wp_filter[ $hook ] AS $func => $values ) {
6463
					foreach( $values AS $hooked ) {
6464
						if ( is_callable( $hooked['function'] ) ) {
6465
							$function_name = 'an anonymous function';
6466
						} else {
6467
							$function_name = $hooked['function'];
6468
						}
6469
						_deprecated_function( $hook . ' used for ' . $function_name, null, $hook_alt );
6470
					}
6471
				}
6472
			}
6473
		}
6474
	}
6475
6476
	/**
6477
	 * Converts any url in a stylesheet, to the correct absolute url.
6478
	 *
6479
	 * Considerations:
6480
	 *  - Normal, relative URLs     `feh.png`
6481
	 *  - Data URLs                 `data:image/gif;base64,eh129ehiuehjdhsa==`
6482
	 *  - Schema-agnostic URLs      `//domain.com/feh.png`
6483
	 *  - Absolute URLs             `http://domain.com/feh.png`
6484
	 *  - Domain root relative URLs `/feh.png`
6485
	 *
6486
	 * @param $css string: The raw CSS -- should be read in directly from the file.
6487
	 * @param $css_file_url : The URL that the file can be accessed at, for calculating paths from.
6488
	 *
6489
	 * @return mixed|string
6490
	 */
6491
	public static function absolutize_css_urls( $css, $css_file_url ) {
6492
		$pattern = '#url\((?P<path>[^)]*)\)#i';
6493
		$css_dir = dirname( $css_file_url );
6494
		$p       = parse_url( $css_dir );
6495
		$domain  = sprintf(
6496
					'%1$s//%2$s%3$s%4$s',
6497
					isset( $p['scheme'] )           ? "{$p['scheme']}:" : '',
6498
					isset( $p['user'], $p['pass'] ) ? "{$p['user']}:{$p['pass']}@" : '',
6499
					$p['host'],
6500
					isset( $p['port'] )             ? ":{$p['port']}" : ''
6501
				);
6502
6503
		if ( preg_match_all( $pattern, $css, $matches, PREG_SET_ORDER ) ) {
6504
			$find = $replace = array();
6505
			foreach ( $matches as $match ) {
6506
				$url = trim( $match['path'], "'\" \t" );
6507
6508
				// If this is a data url, we don't want to mess with it.
6509
				if ( 'data:' === substr( $url, 0, 5 ) ) {
6510
					continue;
6511
				}
6512
6513
				// If this is an absolute or protocol-agnostic url,
6514
				// we don't want to mess with it.
6515
				if ( preg_match( '#^(https?:)?//#i', $url ) ) {
6516
					continue;
6517
				}
6518
6519
				switch ( substr( $url, 0, 1 ) ) {
6520
					case '/':
6521
						$absolute = $domain . $url;
6522
						break;
6523
					default:
6524
						$absolute = $css_dir . '/' . $url;
6525
				}
6526
6527
				$find[]    = $match[0];
6528
				$replace[] = sprintf( 'url("%s")', $absolute );
6529
			}
6530
			$css = str_replace( $find, $replace, $css );
6531
		}
6532
6533
		return $css;
6534
	}
6535
6536
	/**
6537
	 * This methods removes all of the registered css files on the front end
6538
	 * from Jetpack in favor of using a single file. In effect "imploding"
6539
	 * all the files into one file.
6540
	 *
6541
	 * Pros:
6542
	 * - Uses only ONE css asset connection instead of 15
6543
	 * - Saves a minimum of 56k
6544
	 * - Reduces server load
6545
	 * - Reduces time to first painted byte
6546
	 *
6547
	 * Cons:
6548
	 * - Loads css for ALL modules. However all selectors are prefixed so it
6549
	 *		should not cause any issues with themes.
6550
	 * - Plugins/themes dequeuing styles no longer do anything. See
6551
	 *		jetpack_implode_frontend_css filter for a workaround
6552
	 *
6553
	 * For some situations developers may wish to disable css imploding and
6554
	 * instead operate in legacy mode where each file loads seperately and
6555
	 * can be edited individually or dequeued. This can be accomplished with
6556
	 * the following line:
6557
	 *
6558
	 * add_filter( 'jetpack_implode_frontend_css', '__return_false' );
6559
	 *
6560
	 * @since 3.2
6561
	 **/
6562
	public function implode_frontend_css( $travis_test = false ) {
6563
		$do_implode = true;
6564
		if ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) {
6565
			$do_implode = false;
6566
		}
6567
6568
		// Do not implode CSS when the page loads via the AMP plugin.
6569
		if ( Jetpack_AMP_Support::is_amp_request() ) {
6570
			$do_implode = false;
6571
		}
6572
6573
		/**
6574
		 * Allow CSS to be concatenated into a single jetpack.css file.
6575
		 *
6576
		 * @since 3.2.0
6577
		 *
6578
		 * @param bool $do_implode Should CSS be concatenated? Default to true.
6579
		 */
6580
		$do_implode = apply_filters( 'jetpack_implode_frontend_css', $do_implode );
6581
6582
		// Do not use the imploded file when default behavior was altered through the filter
6583
		if ( ! $do_implode ) {
6584
			return;
6585
		}
6586
6587
		// We do not want to use the imploded file in dev mode, or if not connected
6588
		if ( Jetpack::is_development_mode() || ! self::is_active() ) {
6589
			if ( ! $travis_test ) {
6590
				return;
6591
			}
6592
		}
6593
6594
		// Do not use the imploded file if sharing css was dequeued via the sharing settings screen
6595
		if ( get_option( 'sharedaddy_disable_resources' ) ) {
6596
			return;
6597
		}
6598
6599
		/*
6600
		 * Now we assume Jetpack is connected and able to serve the single
6601
		 * file.
6602
		 *
6603
		 * In the future there will be a check here to serve the file locally
6604
		 * or potentially from the Jetpack CDN
6605
		 *
6606
		 * For now:
6607
		 * - Enqueue a single imploded css file
6608
		 * - Zero out the style_loader_tag for the bundled ones
6609
		 * - Be happy, drink scotch
6610
		 */
6611
6612
		add_filter( 'style_loader_tag', array( $this, 'concat_remove_style_loader_tag' ), 10, 2 );
6613
6614
		$version = Jetpack::is_development_version() ? filemtime( JETPACK__PLUGIN_DIR . 'css/jetpack.css' ) : JETPACK__VERSION;
6615
6616
		wp_enqueue_style( 'jetpack_css', plugins_url( 'css/jetpack.css', __FILE__ ), array(), $version );
6617
		wp_style_add_data( 'jetpack_css', 'rtl', 'replace' );
6618
	}
6619
6620
	function concat_remove_style_loader_tag( $tag, $handle ) {
6621
		if ( in_array( $handle, $this->concatenated_style_handles ) ) {
6622
			$tag = '';
6623
			if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
6624
				$tag = "<!-- `" . esc_html( $handle ) . "` is included in the concatenated jetpack.css -->\r\n";
6625
			}
6626
		}
6627
6628
		return $tag;
6629
	}
6630
6631
	/*
6632
	 * Check the heartbeat data
6633
	 *
6634
	 * Organizes the heartbeat data by severity.  For example, if the site
6635
	 * is in an ID crisis, it will be in the $filtered_data['bad'] array.
6636
	 *
6637
	 * Data will be added to "caution" array, if it either:
6638
	 *  - Out of date Jetpack version
6639
	 *  - Out of date WP version
6640
	 *  - Out of date PHP version
6641
	 *
6642
	 * $return array $filtered_data
6643
	 */
6644
	public static function jetpack_check_heartbeat_data() {
6645
		$raw_data = Jetpack_Heartbeat::generate_stats_array();
6646
6647
		$good    = array();
6648
		$caution = array();
6649
		$bad     = array();
6650
6651
		foreach ( $raw_data as $stat => $value ) {
6652
6653
			// Check jetpack version
6654
			if ( 'version' == $stat ) {
6655
				if ( version_compare( $value, JETPACK__VERSION, '<' ) ) {
6656
					$caution[ $stat ] = $value . " - min supported is " . JETPACK__VERSION;
6657
					continue;
6658
				}
6659
			}
6660
6661
			// Check WP version
6662
			if ( 'wp-version' == $stat ) {
6663
				if ( version_compare( $value, JETPACK__MINIMUM_WP_VERSION, '<' ) ) {
6664
					$caution[ $stat ] = $value . " - min supported is " . JETPACK__MINIMUM_WP_VERSION;
6665
					continue;
6666
				}
6667
			}
6668
6669
			// Check PHP version
6670
			if ( 'php-version' == $stat ) {
6671
				if ( version_compare( PHP_VERSION, '5.2.4', '<' ) ) {
6672
					$caution[ $stat ] = $value . " - min supported is 5.2.4";
6673
					continue;
6674
				}
6675
			}
6676
6677
			// Check ID crisis
6678
			if ( 'identitycrisis' == $stat ) {
6679
				if ( 'yes' == $value ) {
6680
					$bad[ $stat ] = $value;
6681
					continue;
6682
				}
6683
			}
6684
6685
			// The rest are good :)
6686
			$good[ $stat ] = $value;
6687
		}
6688
6689
		$filtered_data = array(
6690
			'good'    => $good,
6691
			'caution' => $caution,
6692
			'bad'     => $bad
6693
		);
6694
6695
		return $filtered_data;
6696
	}
6697
6698
6699
	/*
6700
	 * This method is used to organize all options that can be reset
6701
	 * without disconnecting Jetpack.
6702
	 *
6703
	 * It is used in class.jetpack-cli.php to reset options
6704
	 *
6705
	 * @since 5.4.0 Logic moved to Jetpack_Options class. Method left in Jetpack class for backwards compat.
6706
	 *
6707
	 * @return array of options to delete.
6708
	 */
6709
	public static function get_jetpack_options_for_reset() {
6710
		return Jetpack_Options::get_options_for_reset();
6711
	}
6712
6713
	/*
6714
	 * Strip http:// or https:// from a url, replaces forward slash with ::,
6715
	 * so we can bring them directly to their site in calypso.
6716
	 *
6717
	 * @param string | url
6718
	 * @return string | url without the guff
6719
	 */
6720
	public static function build_raw_urls( $url ) {
6721
		$strip_http = '/.*?:\/\//i';
6722
		$url = preg_replace( $strip_http, '', $url  );
6723
		$url = str_replace( '/', '::', $url );
6724
		return $url;
6725
	}
6726
6727
	/**
6728
	 * Stores and prints out domains to prefetch for page speed optimization.
6729
	 *
6730
	 * @param mixed $new_urls
6731
	 */
6732
	public static function dns_prefetch( $new_urls = null ) {
6733
		static $prefetch_urls = array();
6734
		if ( empty( $new_urls ) && ! empty( $prefetch_urls ) ) {
6735
			echo "\r\n";
6736
			foreach ( $prefetch_urls as $this_prefetch_url ) {
6737
				printf( "<link rel='dns-prefetch' href='%s'/>\r\n", esc_attr( $this_prefetch_url ) );
6738
			}
6739
		} elseif ( ! empty( $new_urls ) ) {
6740
			if ( ! has_action( 'wp_head', array( __CLASS__, __FUNCTION__ ) ) ) {
6741
				add_action( 'wp_head', array( __CLASS__, __FUNCTION__ ) );
6742
			}
6743
			foreach ( (array) $new_urls as $this_new_url ) {
6744
				$prefetch_urls[] = strtolower( untrailingslashit( preg_replace( '#^https?://#i', '//', $this_new_url ) ) );
6745
			}
6746
			$prefetch_urls = array_unique( $prefetch_urls );
6747
		}
6748
	}
6749
6750
	public function wp_dashboard_setup() {
6751
		if ( self::is_active() ) {
6752
			add_action( 'jetpack_dashboard_widget', array( __CLASS__, 'dashboard_widget_footer' ), 999 );
6753
		}
6754
6755
		if ( has_action( 'jetpack_dashboard_widget' ) ) {
6756
			$jetpack_logo = new Jetpack_Logo();
6757
			$widget_title = sprintf(
6758
				wp_kses(
6759
					/* translators: Placeholder is a Jetpack logo. */
6760
					__( 'Stats <span>by %s</span>', 'jetpack' ),
6761
					array( 'span' => array() )
6762
				),
6763
				$jetpack_logo->get_jp_emblem( true )
6764
			);
6765
6766
			wp_add_dashboard_widget(
6767
				'jetpack_summary_widget',
6768
				$widget_title,
6769
				array( __CLASS__, 'dashboard_widget' )
6770
			);
6771
			wp_enqueue_style( 'jetpack-dashboard-widget', plugins_url( 'css/dashboard-widget.css', JETPACK__PLUGIN_FILE ), array(), JETPACK__VERSION );
6772
6773
			// If we're inactive and not in development mode, sort our box to the top.
6774
			if ( ! self::is_active() && ! self::is_development_mode() ) {
6775
				global $wp_meta_boxes;
6776
6777
				$dashboard = $wp_meta_boxes['dashboard']['normal']['core'];
6778
				$ours      = array( 'jetpack_summary_widget' => $dashboard['jetpack_summary_widget'] );
6779
6780
				$wp_meta_boxes['dashboard']['normal']['core'] = array_merge( $ours, $dashboard );
6781
			}
6782
		}
6783
	}
6784
6785
	/**
6786
	 * @param mixed $result Value for the user's option
0 ignored issues
show
Bug introduced by
There is no parameter named $result. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
6787
	 * @return mixed
6788
	 */
6789
	function get_user_option_meta_box_order_dashboard( $sorted ) {
6790
		if ( ! is_array( $sorted ) ) {
6791
			return $sorted;
6792
		}
6793
6794
		foreach ( $sorted as $box_context => $ids ) {
6795
			if ( false === strpos( $ids, 'dashboard_stats' ) ) {
6796
				// If the old id isn't anywhere in the ids, don't bother exploding and fail out.
6797
				continue;
6798
			}
6799
6800
			$ids_array = explode( ',', $ids );
6801
			$key = array_search( 'dashboard_stats', $ids_array );
6802
6803
			if ( false !== $key ) {
6804
				// If we've found that exact value in the option (and not `google_dashboard_stats` for example)
6805
				$ids_array[ $key ] = 'jetpack_summary_widget';
6806
				$sorted[ $box_context ] = implode( ',', $ids_array );
6807
				// We've found it, stop searching, and just return.
6808
				break;
6809
			}
6810
		}
6811
6812
		return $sorted;
6813
	}
6814
6815
	public static function dashboard_widget() {
6816
		/**
6817
		 * Fires when the dashboard is loaded.
6818
		 *
6819
		 * @since 3.4.0
6820
		 */
6821
		do_action( 'jetpack_dashboard_widget' );
6822
	}
6823
6824
	public static function dashboard_widget_footer() {
6825
		?>
6826
		<footer>
6827
6828
		<div class="protect">
6829
			<?php if ( Jetpack::is_module_active( 'protect' ) ) : ?>
6830
				<h3><?php echo number_format_i18n( get_site_option( 'jetpack_protect_blocked_attempts', 0 ) ); ?></h3>
6831
				<p><?php echo esc_html_x( 'Blocked malicious login attempts', '{#} Blocked malicious login attempts -- number is on a prior line, text is a caption.', 'jetpack' ); ?></p>
6832
			<?php elseif ( current_user_can( 'jetpack_activate_modules' ) && ! self::is_development_mode() ) : ?>
6833
				<a href="<?php echo esc_url( wp_nonce_url( Jetpack::admin_url( array( 'action' => 'activate', 'module' => 'protect' ) ), 'jetpack_activate-protect' ) ); ?>" class="button button-jetpack" title="<?php esc_attr_e( 'Protect helps to keep you secure from brute-force login attacks.', 'jetpack' ); ?>">
6834
					<?php esc_html_e( 'Activate Protect', 'jetpack' ); ?>
6835
				</a>
6836
			<?php else : ?>
6837
				<?php esc_html_e( 'Protect is inactive.', 'jetpack' ); ?>
6838
			<?php endif; ?>
6839
		</div>
6840
6841
		<div class="akismet">
6842
			<?php if ( is_plugin_active( 'akismet/akismet.php' ) ) : ?>
6843
				<h3><?php echo number_format_i18n( get_option( 'akismet_spam_count', 0 ) ); ?></h3>
6844
				<p><?php echo esc_html_x( 'Spam comments blocked by Akismet.', '{#} Spam comments blocked by Akismet -- number is on a prior line, text is a caption.', 'jetpack' ); ?></p>
6845
			<?php elseif ( current_user_can( 'activate_plugins' ) && ! is_wp_error( validate_plugin( 'akismet/akismet.php' ) ) ) : ?>
6846
				<a href="<?php echo esc_url( wp_nonce_url( add_query_arg( array( 'action' => 'activate', 'plugin' => 'akismet/akismet.php' ), admin_url( 'plugins.php' ) ), 'activate-plugin_akismet/akismet.php' ) ); ?>" class="button button-jetpack">
6847
					<?php esc_html_e( 'Activate Akismet', 'jetpack' ); ?>
6848
				</a>
6849
			<?php else : ?>
6850
				<p><a href="<?php echo esc_url( 'https://akismet.com/?utm_source=jetpack&utm_medium=link&utm_campaign=Jetpack%20Dashboard%20Widget%20Footer%20Link' ); ?>"><?php esc_html_e( 'Akismet can help to keep your blog safe from spam!', 'jetpack' ); ?></a></p>
6851
			<?php endif; ?>
6852
		</div>
6853
6854
		</footer>
6855
		<?php
6856
	}
6857
6858
	/*
6859
	 * Adds a "blank" column in the user admin table to display indication of user connection.
6860
	 */
6861
	function jetpack_icon_user_connected( $columns ) {
6862
		$columns['user_jetpack'] = '';
6863
		return $columns;
6864
	}
6865
6866
	/*
6867
	 * Show Jetpack icon if the user is linked.
6868
	 */
6869
	function jetpack_show_user_connected_icon( $val, $col, $user_id ) {
6870
		if ( 'user_jetpack' == $col && Jetpack::is_user_connected( $user_id ) ) {
6871
			$jetpack_logo = new Jetpack_Logo();
6872
			$emblem_html = sprintf(
6873
				'<a title="%1$s" class="jp-emblem-user-admin">%2$s</a>',
6874
				esc_attr__( 'This user is linked and ready to fly with Jetpack.', 'jetpack' ),
6875
				$jetpack_logo->get_jp_emblem()
6876
			);
6877
			return $emblem_html;
6878
		}
6879
6880
		return $val;
6881
	}
6882
6883
	/*
6884
	 * Style the Jetpack user column
6885
	 */
6886
	function jetpack_user_col_style() {
6887
		global $current_screen;
6888
		if ( ! empty( $current_screen->base ) && 'users' == $current_screen->base ) { ?>
6889
			<style>
6890
				.fixed .column-user_jetpack {
6891
					width: 21px;
6892
				}
6893
				.jp-emblem-user-admin svg {
6894
					width: 20px;
6895
					height: 20px;
6896
				}
6897
				.jp-emblem-user-admin path {
6898
					fill: #00BE28;
6899
				}
6900
			</style>
6901
		<?php }
6902
	}
6903
6904
	/**
6905
	 * Checks if Akismet is active and working.
6906
	 *
6907
	 * We dropped support for Akismet 3.0 with Jetpack 6.1.1 while introducing a check for an Akismet valid key
6908
	 * that implied usage of methods present since more recent version.
6909
	 * See https://github.com/Automattic/jetpack/pull/9585
6910
	 *
6911
	 * @since  5.1.0
6912
	 *
6913
	 * @return bool True = Akismet available. False = Aksimet not available.
6914
	 */
6915
	public static function is_akismet_active() {
6916
		static $status = null;
6917
6918
		if ( ! is_null( $status ) ) {
6919
			return $status;
6920
		}
6921
6922
		// Check if a modern version of Akismet is active.
6923
		if ( ! method_exists( 'Akismet', 'http_post' ) ) {
6924
			$status = false;
6925
			return $status;
6926
		}
6927
6928
		// Make sure there is a key known to Akismet at all before verifying key.
6929
		$akismet_key = Akismet::get_api_key();
6930
		if ( ! $akismet_key ) {
6931
			$status = false;
6932
			return $status;
6933
		}
6934
6935
		// Possible values: valid, invalid, failure via Akismet. false if no status is cached.
6936
		$akismet_key_state = get_transient( 'jetpack_akismet_key_is_valid' );
6937
6938
		// Do not used the cache result in wp-admin or REST API requests if the key isn't valid, in case someone is actively renewing, etc.
6939
		$recheck = ( is_admin() || ( defined( 'REST_REQUEST' ) && REST_REQUEST ) ) && 'valid' !== $akismet_key_state;
6940
		// We cache the result of the Akismet key verification for ten minutes.
6941
		if ( ! $akismet_key_state || $recheck ) {
6942
			$akismet_key_state = Akismet::verify_key( $akismet_key );
6943
			set_transient( 'jetpack_akismet_key_is_valid', $akismet_key_state, 10 * MINUTE_IN_SECONDS );
6944
		}
6945
6946
		$status = 'valid' === $akismet_key_state;
6947
6948
		return $status;
6949
	}
6950
6951
	/**
6952
	 * Checks if one or more function names is in debug_backtrace
6953
	 *
6954
	 * @param $names Mixed string name of function or array of string names of functions
6955
	 *
6956
	 * @return bool
6957
	 */
6958
	public static function is_function_in_backtrace( $names ) {
6959
		$backtrace = debug_backtrace( false ); // phpcs:ignore PHPCompatibility.FunctionUse.NewFunctionParameters.debug_backtrace_optionsFound
6960
		if ( ! is_array( $names ) ) {
6961
			$names = array( $names );
6962
		}
6963
		$names_as_keys = array_flip( $names );
6964
6965
		//Do check in constant O(1) time for PHP5.5+
6966
		if ( function_exists( 'array_column' ) ) {
6967
			$backtrace_functions = array_column( $backtrace, 'function' ); // phpcs:ignore PHPCompatibility.FunctionUse.NewFunctions.array_columnFound
6968
			$backtrace_functions_as_keys = array_flip( $backtrace_functions );
6969
			$intersection = array_intersect_key( $backtrace_functions_as_keys, $names_as_keys );
6970
			return ! empty ( $intersection );
6971
		}
6972
6973
		//Do check in linear O(n) time for < PHP5.5 ( using isset at least prevents O(n^2) )
6974
		foreach ( $backtrace as $call ) {
6975
			if ( isset( $names_as_keys[ $call['function'] ] ) ) {
6976
				return true;
6977
			}
6978
		}
6979
		return false;
6980
	}
6981
6982
	/**
6983
	 * Given a minified path, and a non-minified path, will return
6984
	 * a minified or non-minified file URL based on whether SCRIPT_DEBUG is set and truthy.
6985
	 *
6986
	 * Both `$min_base` and `$non_min_base` are expected to be relative to the
6987
	 * root Jetpack directory.
6988
	 *
6989
	 * @since 5.6.0
6990
	 *
6991
	 * @param string $min_path
6992
	 * @param string $non_min_path
6993
	 * @return string The URL to the file
6994
	 */
6995
	public static function get_file_url_for_environment( $min_path, $non_min_path ) {
6996
		$path = ( Constants::is_defined( 'SCRIPT_DEBUG' ) && Constants::get_constant( 'SCRIPT_DEBUG' ) )
6997
			? $non_min_path
6998
			: $min_path;
6999
7000
		return plugins_url( $path, JETPACK__PLUGIN_FILE );
7001
	}
7002
7003
	/**
7004
	 * Checks for whether Jetpack Backup & Scan is enabled.
7005
	 * Will return true if the state of Backup & Scan is anything except "unavailable".
7006
	 * @return bool|int|mixed
7007
	 */
7008
	public static function is_rewind_enabled() {
7009
		if ( ! Jetpack::is_active() ) {
7010
			return false;
7011
		}
7012
7013
		$rewind_enabled = get_transient( 'jetpack_rewind_enabled' );
7014
		if ( false === $rewind_enabled ) {
7015
			jetpack_require_lib( 'class.core-rest-api-endpoints' );
7016
			$rewind_data = (array) Jetpack_Core_Json_Api_Endpoints::rewind_data();
7017
			$rewind_enabled = ( ! is_wp_error( $rewind_data )
7018
				&& ! empty( $rewind_data['state'] )
7019
				&& 'active' === $rewind_data['state'] )
7020
				? 1
7021
				: 0;
7022
7023
			set_transient( 'jetpack_rewind_enabled', $rewind_enabled, 10 * MINUTE_IN_SECONDS );
7024
		}
7025
		return $rewind_enabled;
7026
	}
7027
7028
	/**
7029
	 * Return Calypso environment value; used for developing Jetpack and pairing
7030
	 * it with different Calypso enrionments, such as localhost.
7031
	 *
7032
	 * @since 7.4.0
7033
	 *
7034
	 * @return string Calypso environment
7035
	 */
7036
	public static function get_calypso_env() {
7037
		if ( isset( $_GET['calypso_env'] ) ) {
7038
			return sanitize_key( $_GET['calypso_env'] );
7039
		}
7040
7041
		if ( getenv( 'CALYPSO_ENV' ) ) {
7042
			return sanitize_key( getenv( 'CALYPSO_ENV' ) );
7043
		}
7044
7045
		if ( defined( 'CALYPSO_ENV' ) && CALYPSO_ENV ) {
7046
			return sanitize_key( CALYPSO_ENV );
7047
		}
7048
7049
		return '';
7050
	}
7051
7052
	/**
7053
	 * Checks whether or not TOS has been agreed upon.
7054
	 * Will return true if a user has clicked to register, or is already connected.
7055
	 */
7056
	public static function jetpack_tos_agreed() {
7057
		return Jetpack_Options::get_option( 'tos_agreed' ) || Jetpack::is_active();
7058
	}
7059
7060
	/**
7061
	 * Handles activating default modules as well general cleanup for the new connection.
7062
	 *
7063
	 * @param boolean $activate_sso                 Whether to activate the SSO module when activating default modules.
7064
	 * @param boolean $redirect_on_activation_error Whether to redirect on activation error.
7065
	 * @param boolean $send_state_messages          Whether to send state messages.
7066
	 * @return void
7067
	 */
7068
	public static function handle_post_authorization_actions(
7069
		$activate_sso = false,
7070
		$redirect_on_activation_error = false,
7071
		$send_state_messages = true
7072
	) {
7073
		$other_modules = $activate_sso
7074
			? array( 'sso' )
7075
			: array();
7076
7077
		if ( $active_modules = Jetpack_Options::get_option( 'active_modules' ) ) {
7078
			Jetpack::delete_active_modules();
7079
7080
			Jetpack::activate_default_modules( 999, 1, array_merge( $active_modules, $other_modules ), $redirect_on_activation_error, $send_state_messages );
0 ignored issues
show
Documentation introduced by
999 is of type integer, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
7081
		} else {
7082
			Jetpack::activate_default_modules( false, false, $other_modules, $redirect_on_activation_error, $send_state_messages );
7083
		}
7084
7085
		// Since this is a fresh connection, be sure to clear out IDC options
7086
		Jetpack_IDC::clear_all_idc_options();
7087
		Jetpack_Options::delete_raw_option( 'jetpack_last_connect_url_check' );
7088
7089
		// Start nonce cleaner
7090
		wp_clear_scheduled_hook( 'jetpack_clean_nonces' );
7091
		wp_schedule_event( time(), 'hourly', 'jetpack_clean_nonces' );
7092
7093
		if ( $send_state_messages ) {
7094
			Jetpack::state( 'message', 'authorized' );
7095
		}
7096
	}
7097
7098
	/**
7099
	 * Returns a boolean for whether backups UI should be displayed or not.
7100
	 *
7101
	 * @return bool Should backups UI be displayed?
7102
	 */
7103
	public static function show_backups_ui() {
7104
		/**
7105
		 * Whether UI for backups should be displayed.
7106
		 *
7107
		 * @since 6.5.0
7108
		 *
7109
		 * @param bool $show_backups Should UI for backups be displayed? True by default.
7110
		 */
7111
		return Jetpack::is_plugin_active( 'vaultpress/vaultpress.php' ) || apply_filters( 'jetpack_show_backups', true );
7112
	}
7113
7114
	/*
7115
	 * Deprecated manage functions
7116
	 */
7117
	function prepare_manage_jetpack_notice() {
7118
		_deprecated_function( __METHOD__, 'jetpack-7.3' );
7119
	}
7120
	function manage_activate_screen() {
7121
		_deprecated_function( __METHOD__, 'jetpack-7.3' );
7122
	}
7123
	function admin_jetpack_manage_notice() {
7124
		_deprecated_function( __METHOD__, 'jetpack-7.3' );
7125
	}
7126
	function opt_out_jetpack_manage_url() {
7127
		_deprecated_function( __METHOD__, 'jetpack-7.3' );
7128
	}
7129
	function opt_in_jetpack_manage_url() {
7130
		_deprecated_function( __METHOD__, 'jetpack-7.3' );
7131
	}
7132
	function opt_in_jetpack_manage_notice() {
7133
		_deprecated_function( __METHOD__, 'jetpack-7.3' );
7134
	}
7135
	function can_display_jetpack_manage_notice() {
7136
		_deprecated_function( __METHOD__, 'jetpack-7.3' );
7137
	}
7138
7139
	/**
7140
	 * Clean leftoveruser meta.
7141
	 *
7142
	 * Delete Jetpack-related user meta when it is no longer needed.
7143
	 *
7144
	 * @since 7.3.0
7145
	 *
7146
	 * @param int $user_id User ID being updated.
7147
	 */
7148
	public static function user_meta_cleanup( $user_id ) {
7149
		$meta_keys = array(
7150
			// AtD removed from Jetpack 7.3
7151
			'AtD_options',
7152
			'AtD_check_when',
7153
			'AtD_guess_lang',
7154
			'AtD_ignored_phrases',
7155
		);
7156
7157
		foreach ( $meta_keys as $meta_key ) {
7158
			if ( get_user_meta( $user_id, $meta_key ) ) {
7159
				delete_user_meta( $user_id, $meta_key );
7160
			}
7161
		}
7162
	}
7163
7164
	function is_active_and_not_development_mode( $maybe ) {
7165
		if ( ! \Jetpack::is_active() || \Jetpack::is_development_mode() ) {
7166
			return false;
7167
		}
7168
		return true;
7169
	}
7170
7171
}
7172