1<?php
2/**
3 * A simple set of functions to check our version 1.0 update service.
4 *
5 * @package WordPress
6 * @since 2.3.0
7 */
8
9/**
10 * Check WordPress version against the newest version.
11 *
12 * The WordPress version, PHP version, and locale is sent.
13 *
14 * Checks against the WordPress server at api.wordpress.org. Will only check
15 * if WordPress isn't installing.
16 *
17 * @since 2.3.0
18 *
19 * @global string $wp_version       Used to check against the newest WordPress version.
20 * @global wpdb   $wpdb             WordPress database abstraction object.
21 * @global string $wp_local_package Locale code of the package.
22 *
23 * @param array $extra_stats Extra statistics to report to the WordPress.org API.
24 * @param bool  $force_check Whether to bypass the transient cache and force a fresh update check. Defaults to false, true if $extra_stats is set.
25 */
26function wp_version_check( $extra_stats = array(), $force_check = false ) {
27	global $wpdb, $wp_local_package;
28
29	if ( wp_installing() ) {
30		return;
31	}
32
33	// Include an unmodified $wp_version.
34	require ABSPATH . WPINC . '/version.php';
35	$php_version = phpversion();
36
37	$current      = get_site_transient( 'update_core' );
38	$translations = wp_get_installed_translations( 'core' );
39
40	// Invalidate the transient when $wp_version changes.
41	if ( is_object( $current ) && $wp_version !== $current->version_checked ) {
42		$current = false;
43	}
44
45	if ( ! is_object( $current ) ) {
46		$current                  = new stdClass;
47		$current->updates         = array();
48		$current->version_checked = $wp_version;
49	}
50
51	if ( ! empty( $extra_stats ) ) {
52		$force_check = true;
53	}
54
55	// Wait 1 minute between multiple version check requests.
56	$timeout          = MINUTE_IN_SECONDS;
57	$time_not_changed = isset( $current->last_checked ) && $timeout > ( time() - $current->last_checked );
58
59	if ( ! $force_check && $time_not_changed ) {
60		return;
61	}
62
63	/**
64	 * Filters the locale requested for WordPress core translations.
65	 *
66	 * @since 2.8.0
67	 *
68	 * @param string $locale Current locale.
69	 */
70	$locale = apply_filters( 'core_version_check_locale', get_locale() );
71
72	// Update last_checked for current to prevent multiple blocking requests if request hangs.
73	$current->last_checked = time();
74	set_site_transient( 'update_core', $current );
75
76	if ( method_exists( $wpdb, 'db_version' ) ) {
77		$mysql_version = preg_replace( '/[^0-9.].*/', '', $wpdb->db_version() );
78	} else {
79		$mysql_version = 'N/A';
80	}
81
82	if ( is_multisite() ) {
83		$user_count        = get_user_count();
84		$num_blogs         = get_blog_count();
85		$wp_install        = network_site_url();
86		$multisite_enabled = 1;
87	} else {
88		$user_count        = count_users();
89		$user_count        = $user_count['total_users'];
90		$multisite_enabled = 0;
91		$num_blogs         = 1;
92		$wp_install        = home_url( '/' );
93	}
94
95	$query = array(
96		'version'            => $wp_version,
97		'php'                => $php_version,
98		'locale'             => $locale,
99		'mysql'              => $mysql_version,
100		'local_package'      => isset( $wp_local_package ) ? $wp_local_package : '',
101		'blogs'              => $num_blogs,
102		'users'              => $user_count,
103		'multisite_enabled'  => $multisite_enabled,
104		'initial_db_version' => get_site_option( 'initial_db_version' ),
105	);
106
107	/**
108	 * Filters the query arguments sent as part of the core version check.
109	 *
110	 * WARNING: Changing this data may result in your site not receiving security updates.
111	 * Please exercise extreme caution.
112	 *
113	 * @since 4.9.0
114	 *
115	 * @param array $query {
116	 *     Version check query arguments.
117	 *
118	 *     @type string $version            WordPress version number.
119	 *     @type string $php                PHP version number.
120	 *     @type string $locale             The locale to retrieve updates for.
121	 *     @type string $mysql              MySQL version number.
122	 *     @type string $local_package      The value of the $wp_local_package global, when set.
123	 *     @type int    $blogs              Number of sites on this WordPress installation.
124	 *     @type int    $users              Number of users on this WordPress installation.
125	 *     @type int    $multisite_enabled  Whether this WordPress installation uses Multisite.
126	 *     @type int    $initial_db_version Database version of WordPress at time of installation.
127	 * }
128	 */
129	$query = apply_filters( 'core_version_check_query_args', $query );
130
131	$post_body = array(
132		'translations' => wp_json_encode( $translations ),
133	);
134
135	if ( is_array( $extra_stats ) ) {
136		$post_body = array_merge( $post_body, $extra_stats );
137	}
138
139	// Allow for WP_AUTO_UPDATE_CORE to specify beta/RC/development releases.
140	if ( defined( 'WP_AUTO_UPDATE_CORE' )
141		&& in_array( WP_AUTO_UPDATE_CORE, array( 'beta', 'rc', 'development', 'branch-development' ), true )
142	) {
143		$query['channel'] = WP_AUTO_UPDATE_CORE;
144	}
145
146	$url      = 'http://api.wordpress.org/core/version-check/1.7/?' . http_build_query( $query, null, '&' );
147	$http_url = $url;
148	$ssl      = wp_http_supports( array( 'ssl' ) );
149
150	if ( $ssl ) {
151		$url = set_url_scheme( $url, 'https' );
152	}
153
154	$doing_cron = wp_doing_cron();
155
156	$options = array(
157		'timeout'    => $doing_cron ? 30 : 3,
158		'user-agent' => 'WordPress/' . $wp_version . '; ' . home_url( '/' ),
159		'headers'    => array(
160			'wp_install' => $wp_install,
161			'wp_blog'    => home_url( '/' ),
162		),
163		'body'       => $post_body,
164	);
165
166	$response = wp_remote_post( $url, $options );
167
168	if ( $ssl && is_wp_error( $response ) ) {
169		trigger_error(
170			sprintf(
171				/* translators: %s: Support forums URL. */
172				__( 'An unexpected error occurred. Something may be wrong with WordPress.org or this server&#8217;s configuration. If you continue to have problems, please try the <a href="%s">support forums</a>.' ),
173				__( 'https://wordpress.org/support/forums/' )
174			) . ' ' . __( '(WordPress could not establish a secure connection to WordPress.org. Please contact your server administrator.)' ),
175			headers_sent() || WP_DEBUG ? E_USER_WARNING : E_USER_NOTICE
176		);
177		$response = wp_remote_post( $http_url, $options );
178	}
179
180	if ( is_wp_error( $response ) || 200 !== wp_remote_retrieve_response_code( $response ) ) {
181		return;
182	}
183
184	$body = trim( wp_remote_retrieve_body( $response ) );
185	$body = json_decode( $body, true );
186
187	if ( ! is_array( $body ) || ! isset( $body['offers'] ) ) {
188		return;
189	}
190
191	$offers = $body['offers'];
192
193	foreach ( $offers as &$offer ) {
194		foreach ( $offer as $offer_key => $value ) {
195			if ( 'packages' === $offer_key ) {
196				$offer['packages'] = (object) array_intersect_key(
197					array_map( 'esc_url', $offer['packages'] ),
198					array_fill_keys( array( 'full', 'no_content', 'new_bundled', 'partial', 'rollback' ), '' )
199				);
200			} elseif ( 'download' === $offer_key ) {
201				$offer['download'] = esc_url( $value );
202			} else {
203				$offer[ $offer_key ] = esc_html( $value );
204			}
205		}
206		$offer = (object) array_intersect_key(
207			$offer,
208			array_fill_keys(
209				array(
210					'response',
211					'download',
212					'locale',
213					'packages',
214					'current',
215					'version',
216					'php_version',
217					'mysql_version',
218					'new_bundled',
219					'partial_version',
220					'notify_email',
221					'support_email',
222					'new_files',
223				),
224				''
225			)
226		);
227	}
228
229	$updates                  = new stdClass();
230	$updates->updates         = $offers;
231	$updates->last_checked    = time();
232	$updates->version_checked = $wp_version;
233
234	if ( isset( $body['translations'] ) ) {
235		$updates->translations = $body['translations'];
236	}
237
238	set_site_transient( 'update_core', $updates );
239
240	if ( ! empty( $body['ttl'] ) ) {
241		$ttl = (int) $body['ttl'];
242
243		if ( $ttl && ( time() + $ttl < wp_next_scheduled( 'wp_version_check' ) ) ) {
244			// Queue an event to re-run the update check in $ttl seconds.
245			wp_schedule_single_event( time() + $ttl, 'wp_version_check' );
246		}
247	}
248
249	// Trigger background updates if running non-interactively, and we weren't called from the update handler.
250	if ( $doing_cron && ! doing_action( 'wp_maybe_auto_update' ) ) {
251		/**
252		 * Fires during wp_cron, starting the auto-update process.
253		 *
254		 * @since 3.9.0
255		 */
256		do_action( 'wp_maybe_auto_update' );
257	}
258}
259
260/**
261 * Checks for available updates to plugins based on the latest versions hosted on WordPress.org.
262 *
263 * Despite its name this function does not actually perform any updates, it only checks for available updates.
264 *
265 * A list of all plugins installed is sent to WP, along with the site locale.
266 *
267 * Checks against the WordPress server at api.wordpress.org. Will only check
268 * if WordPress isn't installing.
269 *
270 * @since 2.3.0
271 *
272 * @global string $wp_version The WordPress version string.
273 *
274 * @param array $extra_stats Extra statistics to report to the WordPress.org API.
275 */
276function wp_update_plugins( $extra_stats = array() ) {
277	if ( wp_installing() ) {
278		return;
279	}
280
281	// Include an unmodified $wp_version.
282	require ABSPATH . WPINC . '/version.php';
283
284	// If running blog-side, bail unless we've not checked in the last 12 hours.
285	if ( ! function_exists( 'get_plugins' ) ) {
286		require_once ABSPATH . 'wp-admin/includes/plugin.php';
287	}
288
289	$plugins      = get_plugins();
290	$translations = wp_get_installed_translations( 'plugins' );
291
292	$active  = get_option( 'active_plugins', array() );
293	$current = get_site_transient( 'update_plugins' );
294
295	if ( ! is_object( $current ) ) {
296		$current = new stdClass;
297	}
298
299	$updates               = new stdClass;
300	$updates->last_checked = time();
301	$updates->response     = array();
302	$updates->translations = array();
303	$updates->no_update    = array();
304
305	$doing_cron = wp_doing_cron();
306
307	// Check for update on a different schedule, depending on the page.
308	switch ( current_filter() ) {
309		case 'upgrader_process_complete':
310			$timeout = 0;
311			break;
312		case 'load-update-core.php':
313			$timeout = MINUTE_IN_SECONDS;
314			break;
315		case 'load-plugins.php':
316		case 'load-update.php':
317			$timeout = HOUR_IN_SECONDS;
318			break;
319		default:
320			if ( $doing_cron ) {
321				$timeout = 2 * HOUR_IN_SECONDS;
322			} else {
323				$timeout = 12 * HOUR_IN_SECONDS;
324			}
325	}
326
327	$time_not_changed = isset( $current->last_checked ) && $timeout > ( time() - $current->last_checked );
328
329	if ( $time_not_changed && ! $extra_stats ) {
330		$plugin_changed = false;
331
332		foreach ( $plugins as $file => $p ) {
333			$updates->checked[ $file ] = $p['Version'];
334
335			if ( ! isset( $current->checked[ $file ] ) || (string) $current->checked[ $file ] !== (string) $p['Version'] ) {
336				$plugin_changed = true;
337			}
338		}
339
340		if ( isset( $current->response ) && is_array( $current->response ) ) {
341			foreach ( $current->response as $plugin_file => $update_details ) {
342				if ( ! isset( $plugins[ $plugin_file ] ) ) {
343					$plugin_changed = true;
344					break;
345				}
346			}
347		}
348
349		// Bail if we've checked recently and if nothing has changed.
350		if ( ! $plugin_changed ) {
351			return;
352		}
353	}
354
355	// Update last_checked for current to prevent multiple blocking requests if request hangs.
356	$current->last_checked = time();
357	set_site_transient( 'update_plugins', $current );
358
359	$to_send = compact( 'plugins', 'active' );
360
361	$locales = array_values( get_available_languages() );
362
363	/**
364	 * Filters the locales requested for plugin translations.
365	 *
366	 * @since 3.7.0
367	 * @since 4.5.0 The default value of the `$locales` parameter changed to include all locales.
368	 *
369	 * @param array $locales Plugin locales. Default is all available locales of the site.
370	 */
371	$locales = apply_filters( 'plugins_update_check_locales', $locales );
372	$locales = array_unique( $locales );
373
374	if ( $doing_cron ) {
375		$timeout = 30;
376	} else {
377		// Three seconds, plus one extra second for every 10 plugins.
378		$timeout = 3 + (int) ( count( $plugins ) / 10 );
379	}
380
381	$options = array(
382		'timeout'    => $timeout,
383		'body'       => array(
384			'plugins'      => wp_json_encode( $to_send ),
385			'translations' => wp_json_encode( $translations ),
386			'locale'       => wp_json_encode( $locales ),
387			'all'          => wp_json_encode( true ),
388		),
389		'user-agent' => 'WordPress/' . $wp_version . '; ' . home_url( '/' ),
390	);
391
392	if ( $extra_stats ) {
393		$options['body']['update_stats'] = wp_json_encode( $extra_stats );
394	}
395
396	$url      = 'http://api.wordpress.org/plugins/update-check/1.1/';
397	$http_url = $url;
398	$ssl      = wp_http_supports( array( 'ssl' ) );
399
400	if ( $ssl ) {
401		$url = set_url_scheme( $url, 'https' );
402	}
403
404	$raw_response = wp_remote_post( $url, $options );
405
406	if ( $ssl && is_wp_error( $raw_response ) ) {
407		trigger_error(
408			sprintf(
409				/* translators: %s: Support forums URL. */
410				__( 'An unexpected error occurred. Something may be wrong with WordPress.org or this server&#8217;s configuration. If you continue to have problems, please try the <a href="%s">support forums</a>.' ),
411				__( 'https://wordpress.org/support/forums/' )
412			) . ' ' . __( '(WordPress could not establish a secure connection to WordPress.org. Please contact your server administrator.)' ),
413			headers_sent() || WP_DEBUG ? E_USER_WARNING : E_USER_NOTICE
414		);
415		$raw_response = wp_remote_post( $http_url, $options );
416	}
417
418	if ( is_wp_error( $raw_response ) || 200 !== wp_remote_retrieve_response_code( $raw_response ) ) {
419		return;
420	}
421
422	$response = json_decode( wp_remote_retrieve_body( $raw_response ), true );
423
424	if ( $response && is_array( $response ) ) {
425		$updates->response     = $response['plugins'];
426		$updates->translations = $response['translations'];
427		$updates->no_update    = $response['no_update'];
428	}
429
430	// Support updates for any plugins using the `Update URI` header field.
431	foreach ( $plugins as $plugin_file => $plugin_data ) {
432		if ( ! $plugin_data['UpdateURI'] || isset( $updates->response[ $plugin_file ] ) ) {
433			continue;
434		}
435
436		$hostname = wp_parse_url( esc_url_raw( $plugin_data['UpdateURI'] ), PHP_URL_HOST );
437
438		/**
439		 * Filters the update response for a given plugin hostname.
440		 *
441		 * The dynamic portion of the hook name, `$hostname`, refers to the hostname
442		 * of the URI specified in the `Update URI` header field.
443		 *
444		 * @since 5.8.0
445		 *
446		 * @param array|false $update {
447		 *     The plugin update data with the latest details. Default false.
448		 *
449		 *     @type string $id           Optional. ID of the plugin for update purposes, should be a URI
450		 *                                specified in the `Update URI` header field.
451		 *     @type string $slug         Slug of the plugin.
452		 *     @type string $version      The version of the plugin.
453		 *     @type string $url          The URL for details of the plugin.
454		 *     @type string $package      Optional. The update ZIP for the plugin.
455		 *     @type string $tested       Optional. The version of WordPress the plugin is tested against.
456		 *     @type string $requires_php Optional. The version of PHP which the plugin requires.
457		 *     @type bool   $autoupdate   Optional. Whether the plugin should automatically update.
458		 *     @type array  $icons        Optional. Array of plugin icons.
459		 *     @type array  $banners      Optional. Array of plugin banners.
460		 *     @type array  $banners_rtl  Optional. Array of plugin RTL banners.
461		 *     @type array  $translations {
462		 *         Optional. List of translation updates for the plugin.
463		 *
464		 *         @type string $language   The language the translation update is for.
465		 *         @type string $version    The version of the plugin this translation is for.
466		 *                                  This is not the version of the language file.
467		 *         @type string $updated    The update timestamp of the translation file.
468		 *                                  Should be a date in the `YYYY-MM-DD HH:MM:SS` format.
469		 *         @type string $package    The ZIP location containing the translation update.
470		 *         @type string $autoupdate Whether the translation should be automatically installed.
471		 *     }
472		 * }
473		 * @param array       $plugin_data      Plugin headers.
474		 * @param string      $plugin_file      Plugin filename.
475		 * @param array       $locales          Installed locales to look translations for.
476		 */
477		$update = apply_filters( "update_plugins_{$hostname}", false, $plugin_data, $plugin_file, $locales );
478
479		if ( ! $update ) {
480			continue;
481		}
482
483		$update = (object) $update;
484
485		// Is it valid? We require at least a version.
486		if ( ! isset( $update->version ) ) {
487			continue;
488		}
489
490		// These should remain constant.
491		$update->id     = $plugin_data['UpdateURI'];
492		$update->plugin = $plugin_file;
493
494		// WordPress needs the version field specified as 'new_version'.
495		if ( ! isset( $update->new_version ) ) {
496			$update->new_version = $update->version;
497		}
498
499		// Handle any translation updates.
500		if ( ! empty( $update->translations ) ) {
501			foreach ( $update->translations as $translation ) {
502				if ( isset( $translation['language'], $translation['package'] ) ) {
503					$translation['type'] = 'plugin';
504					$translation['slug'] = isset( $update->slug ) ? $update->slug : $update->id;
505
506					$updates->translations[] = $translation;
507				}
508			}
509		}
510
511		unset( $updates->no_update[ $plugin_file ], $updates->response[ $plugin_file ] );
512
513		if ( version_compare( $update->new_version, $plugin_data['Version'], '>' ) ) {
514			$updates->response[ $plugin_file ] = $update;
515		} else {
516			$updates->no_update[ $plugin_file ] = $update;
517		}
518	}
519
520	$sanitize_plugin_update_payload = function( &$item ) {
521		$item = (object) $item;
522
523		unset( $item->translations, $item->compatibility );
524
525		return $item;
526	};
527
528	array_walk( $updates->response, $sanitize_plugin_update_payload );
529	array_walk( $updates->no_update, $sanitize_plugin_update_payload );
530
531	set_site_transient( 'update_plugins', $updates );
532}
533
534/**
535 * Checks for available updates to themes based on the latest versions hosted on WordPress.org.
536 *
537 * Despite its name this function does not actually perform any updates, it only checks for available updates.
538 *
539 * A list of all themes installed is sent to WP, along with the site locale.
540 *
541 * Checks against the WordPress server at api.wordpress.org. Will only check
542 * if WordPress isn't installing.
543 *
544 * @since 2.7.0
545 *
546 * @global string $wp_version The WordPress version string.
547 *
548 * @param array $extra_stats Extra statistics to report to the WordPress.org API.
549 */
550function wp_update_themes( $extra_stats = array() ) {
551	if ( wp_installing() ) {
552		return;
553	}
554
555	// Include an unmodified $wp_version.
556	require ABSPATH . WPINC . '/version.php';
557
558	$installed_themes = wp_get_themes();
559	$translations     = wp_get_installed_translations( 'themes' );
560
561	$last_update = get_site_transient( 'update_themes' );
562
563	if ( ! is_object( $last_update ) ) {
564		$last_update = new stdClass;
565	}
566
567	$themes  = array();
568	$checked = array();
569	$request = array();
570
571	// Put slug of current theme into request.
572	$request['active'] = get_option( 'stylesheet' );
573
574	foreach ( $installed_themes as $theme ) {
575		$checked[ $theme->get_stylesheet() ] = $theme->get( 'Version' );
576
577		$themes[ $theme->get_stylesheet() ] = array(
578			'Name'       => $theme->get( 'Name' ),
579			'Title'      => $theme->get( 'Name' ),
580			'Version'    => $theme->get( 'Version' ),
581			'Author'     => $theme->get( 'Author' ),
582			'Author URI' => $theme->get( 'AuthorURI' ),
583			'Template'   => $theme->get_template(),
584			'Stylesheet' => $theme->get_stylesheet(),
585		);
586	}
587
588	$doing_cron = wp_doing_cron();
589
590	// Check for update on a different schedule, depending on the page.
591	switch ( current_filter() ) {
592		case 'upgrader_process_complete':
593			$timeout = 0;
594			break;
595		case 'load-update-core.php':
596			$timeout = MINUTE_IN_SECONDS;
597			break;
598		case 'load-themes.php':
599		case 'load-update.php':
600			$timeout = HOUR_IN_SECONDS;
601			break;
602		default:
603			if ( $doing_cron ) {
604				$timeout = 2 * HOUR_IN_SECONDS;
605			} else {
606				$timeout = 12 * HOUR_IN_SECONDS;
607			}
608	}
609
610	$time_not_changed = isset( $last_update->last_checked ) && $timeout > ( time() - $last_update->last_checked );
611
612	if ( $time_not_changed && ! $extra_stats ) {
613		$theme_changed = false;
614
615		foreach ( $checked as $slug => $v ) {
616			if ( ! isset( $last_update->checked[ $slug ] ) || (string) $last_update->checked[ $slug ] !== (string) $v ) {
617				$theme_changed = true;
618			}
619		}
620
621		if ( isset( $last_update->response ) && is_array( $last_update->response ) ) {
622			foreach ( $last_update->response as $slug => $update_details ) {
623				if ( ! isset( $checked[ $slug ] ) ) {
624					$theme_changed = true;
625					break;
626				}
627			}
628		}
629
630		// Bail if we've checked recently and if nothing has changed.
631		if ( ! $theme_changed ) {
632			return;
633		}
634	}
635
636	// Update last_checked for current to prevent multiple blocking requests if request hangs.
637	$last_update->last_checked = time();
638	set_site_transient( 'update_themes', $last_update );
639
640	$request['themes'] = $themes;
641
642	$locales = array_values( get_available_languages() );
643
644	/**
645	 * Filters the locales requested for theme translations.
646	 *
647	 * @since 3.7.0
648	 * @since 4.5.0 The default value of the `$locales` parameter changed to include all locales.
649	 *
650	 * @param array $locales Theme locales. Default is all available locales of the site.
651	 */
652	$locales = apply_filters( 'themes_update_check_locales', $locales );
653	$locales = array_unique( $locales );
654
655	if ( $doing_cron ) {
656		$timeout = 30;
657	} else {
658		// Three seconds, plus one extra second for every 10 themes.
659		$timeout = 3 + (int) ( count( $themes ) / 10 );
660	}
661
662	$options = array(
663		'timeout'    => $timeout,
664		'body'       => array(
665			'themes'       => wp_json_encode( $request ),
666			'translations' => wp_json_encode( $translations ),
667			'locale'       => wp_json_encode( $locales ),
668		),
669		'user-agent' => 'WordPress/' . $wp_version . '; ' . home_url( '/' ),
670	);
671
672	if ( $extra_stats ) {
673		$options['body']['update_stats'] = wp_json_encode( $extra_stats );
674	}
675
676	$url      = 'http://api.wordpress.org/themes/update-check/1.1/';
677	$http_url = $url;
678	$ssl      = wp_http_supports( array( 'ssl' ) );
679
680	if ( $ssl ) {
681		$url = set_url_scheme( $url, 'https' );
682	}
683
684	$raw_response = wp_remote_post( $url, $options );
685
686	if ( $ssl && is_wp_error( $raw_response ) ) {
687		trigger_error(
688			sprintf(
689				/* translators: %s: Support forums URL. */
690				__( 'An unexpected error occurred. Something may be wrong with WordPress.org or this server&#8217;s configuration. If you continue to have problems, please try the <a href="%s">support forums</a>.' ),
691				__( 'https://wordpress.org/support/forums/' )
692			) . ' ' . __( '(WordPress could not establish a secure connection to WordPress.org. Please contact your server administrator.)' ),
693			headers_sent() || WP_DEBUG ? E_USER_WARNING : E_USER_NOTICE
694		);
695		$raw_response = wp_remote_post( $http_url, $options );
696	}
697
698	if ( is_wp_error( $raw_response ) || 200 !== wp_remote_retrieve_response_code( $raw_response ) ) {
699		return;
700	}
701
702	$new_update               = new stdClass;
703	$new_update->last_checked = time();
704	$new_update->checked      = $checked;
705
706	$response = json_decode( wp_remote_retrieve_body( $raw_response ), true );
707
708	if ( is_array( $response ) ) {
709		$new_update->response     = $response['themes'];
710		$new_update->no_update    = $response['no_update'];
711		$new_update->translations = $response['translations'];
712	}
713
714	set_site_transient( 'update_themes', $new_update );
715}
716
717/**
718 * Performs WordPress automatic background updates.
719 *
720 * Updates WordPress core plus any plugins and themes that have automatic updates enabled.
721 *
722 * @since 3.7.0
723 */
724function wp_maybe_auto_update() {
725	include_once ABSPATH . 'wp-admin/includes/admin.php';
726	require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
727
728	$upgrader = new WP_Automatic_Updater;
729	$upgrader->run();
730}
731
732/**
733 * Retrieves a list of all language updates available.
734 *
735 * @since 3.7.0
736 *
737 * @return object[] Array of translation objects that have available updates.
738 */
739function wp_get_translation_updates() {
740	$updates    = array();
741	$transients = array(
742		'update_core'    => 'core',
743		'update_plugins' => 'plugin',
744		'update_themes'  => 'theme',
745	);
746
747	foreach ( $transients as $transient => $type ) {
748		$transient = get_site_transient( $transient );
749
750		if ( empty( $transient->translations ) ) {
751			continue;
752		}
753
754		foreach ( $transient->translations as $translation ) {
755			$updates[] = (object) $translation;
756		}
757	}
758
759	return $updates;
760}
761
762/**
763 * Collect counts and UI strings for available updates
764 *
765 * @since 3.3.0
766 *
767 * @return array
768 */
769function wp_get_update_data() {
770	$counts = array(
771		'plugins'      => 0,
772		'themes'       => 0,
773		'wordpress'    => 0,
774		'translations' => 0,
775	);
776
777	$plugins = current_user_can( 'update_plugins' );
778
779	if ( $plugins ) {
780		$update_plugins = get_site_transient( 'update_plugins' );
781
782		if ( ! empty( $update_plugins->response ) ) {
783			$counts['plugins'] = count( $update_plugins->response );
784		}
785	}
786
787	$themes = current_user_can( 'update_themes' );
788
789	if ( $themes ) {
790		$update_themes = get_site_transient( 'update_themes' );
791
792		if ( ! empty( $update_themes->response ) ) {
793			$counts['themes'] = count( $update_themes->response );
794		}
795	}
796
797	$core = current_user_can( 'update_core' );
798
799	if ( $core && function_exists( 'get_core_updates' ) ) {
800		$update_wordpress = get_core_updates( array( 'dismissed' => false ) );
801
802		if ( ! empty( $update_wordpress )
803			&& ! in_array( $update_wordpress[0]->response, array( 'development', 'latest' ), true )
804			&& current_user_can( 'update_core' )
805		) {
806			$counts['wordpress'] = 1;
807		}
808	}
809
810	if ( ( $core || $plugins || $themes ) && wp_get_translation_updates() ) {
811		$counts['translations'] = 1;
812	}
813
814	$counts['total'] = $counts['plugins'] + $counts['themes'] + $counts['wordpress'] + $counts['translations'];
815	$titles          = array();
816
817	if ( $counts['wordpress'] ) {
818		/* translators: %d: Number of available WordPress updates. */
819		$titles['wordpress'] = sprintf( __( '%d WordPress Update' ), $counts['wordpress'] );
820	}
821
822	if ( $counts['plugins'] ) {
823		/* translators: %d: Number of available plugin updates. */
824		$titles['plugins'] = sprintf( _n( '%d Plugin Update', '%d Plugin Updates', $counts['plugins'] ), $counts['plugins'] );
825	}
826
827	if ( $counts['themes'] ) {
828		/* translators: %d: Number of available theme updates. */
829		$titles['themes'] = sprintf( _n( '%d Theme Update', '%d Theme Updates', $counts['themes'] ), $counts['themes'] );
830	}
831
832	if ( $counts['translations'] ) {
833		$titles['translations'] = __( 'Translation Updates' );
834	}
835
836	$update_title = $titles ? esc_attr( implode( ', ', $titles ) ) : '';
837
838	$update_data = array(
839		'counts' => $counts,
840		'title'  => $update_title,
841	);
842	/**
843	 * Filters the returned array of update data for plugins, themes, and WordPress core.
844	 *
845	 * @since 3.5.0
846	 *
847	 * @param array $update_data {
848	 *     Fetched update data.
849	 *
850	 *     @type array   $counts       An array of counts for available plugin, theme, and WordPress updates.
851	 *     @type string  $update_title Titles of available updates.
852	 * }
853	 * @param array $titles An array of update counts and UI strings for available updates.
854	 */
855	return apply_filters( 'wp_get_update_data', $update_data, $titles );
856}
857
858/**
859 * Determines whether core should be updated.
860 *
861 * @since 2.8.0
862 *
863 * @global string $wp_version The WordPress version string.
864 */
865function _maybe_update_core() {
866	// Include an unmodified $wp_version.
867	require ABSPATH . WPINC . '/version.php';
868
869	$current = get_site_transient( 'update_core' );
870
871	if ( isset( $current->last_checked, $current->version_checked )
872		&& 12 * HOUR_IN_SECONDS > ( time() - $current->last_checked )
873		&& $current->version_checked === $wp_version
874	) {
875		return;
876	}
877
878	wp_version_check();
879}
880/**
881 * Check the last time plugins were run before checking plugin versions.
882 *
883 * This might have been backported to WordPress 2.6.1 for performance reasons.
884 * This is used for the wp-admin to check only so often instead of every page
885 * load.
886 *
887 * @since 2.7.0
888 * @access private
889 */
890function _maybe_update_plugins() {
891	$current = get_site_transient( 'update_plugins' );
892
893	if ( isset( $current->last_checked )
894		&& 12 * HOUR_IN_SECONDS > ( time() - $current->last_checked )
895	) {
896		return;
897	}
898
899	wp_update_plugins();
900}
901
902/**
903 * Check themes versions only after a duration of time.
904 *
905 * This is for performance reasons to make sure that on the theme version
906 * checker is not run on every page load.
907 *
908 * @since 2.7.0
909 * @access private
910 */
911function _maybe_update_themes() {
912	$current = get_site_transient( 'update_themes' );
913
914	if ( isset( $current->last_checked )
915		&& 12 * HOUR_IN_SECONDS > ( time() - $current->last_checked )
916	) {
917		return;
918	}
919
920	wp_update_themes();
921}
922
923/**
924 * Schedule core, theme, and plugin update checks.
925 *
926 * @since 3.1.0
927 */
928function wp_schedule_update_checks() {
929	if ( ! wp_next_scheduled( 'wp_version_check' ) && ! wp_installing() ) {
930		wp_schedule_event( time(), 'twicedaily', 'wp_version_check' );
931	}
932
933	if ( ! wp_next_scheduled( 'wp_update_plugins' ) && ! wp_installing() ) {
934		wp_schedule_event( time(), 'twicedaily', 'wp_update_plugins' );
935	}
936
937	if ( ! wp_next_scheduled( 'wp_update_themes' ) && ! wp_installing() ) {
938		wp_schedule_event( time(), 'twicedaily', 'wp_update_themes' );
939	}
940}
941
942/**
943 * Clear existing update caches for plugins, themes, and core.
944 *
945 * @since 4.1.0
946 */
947function wp_clean_update_cache() {
948	if ( function_exists( 'wp_clean_plugins_cache' ) ) {
949		wp_clean_plugins_cache();
950	} else {
951		delete_site_transient( 'update_plugins' );
952	}
953
954	wp_clean_themes_cache();
955
956	delete_site_transient( 'update_core' );
957}
958
959if ( ( ! is_main_site() && ! is_network_admin() ) || wp_doing_ajax() ) {
960	return;
961}
962
963add_action( 'admin_init', '_maybe_update_core' );
964add_action( 'wp_version_check', 'wp_version_check' );
965
966add_action( 'load-plugins.php', 'wp_update_plugins' );
967add_action( 'load-update.php', 'wp_update_plugins' );
968add_action( 'load-update-core.php', 'wp_update_plugins' );
969add_action( 'admin_init', '_maybe_update_plugins' );
970add_action( 'wp_update_plugins', 'wp_update_plugins' );
971
972add_action( 'load-themes.php', 'wp_update_themes' );
973add_action( 'load-update.php', 'wp_update_themes' );
974add_action( 'load-update-core.php', 'wp_update_themes' );
975add_action( 'admin_init', '_maybe_update_themes' );
976add_action( 'wp_update_themes', 'wp_update_themes' );
977
978add_action( 'update_option_WPLANG', 'wp_clean_update_cache', 10, 0 );
979
980add_action( 'wp_maybe_auto_update', 'wp_maybe_auto_update' );
981
982add_action( 'init', 'wp_schedule_update_checks' );
983