PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 6.4
Jetpack – WP Security, Backup, Speed, & Growth v6.4
12.0.3 12.1.3 12.2.3 12.3.2 12.4.2 12.5.2 12.6.4 12.7.3 12.8.3 12.9.5 13.0.2 13.1.5 13.2.4 13.3.3 13.4.5 13.5.2 13.6.2 13.7.2 13.8.3 13.9.2 14.0.1 14.1.1 14.2.2 14.3.1 14.4.2 All 500 releases
jetpack / sync / class.jetpack-sync-listener.php
class.jetpack-sync-listener.php
305 lines 8.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 require_once dirname( __FILE__ ) . '/class.jetpack-sync-settings.php';
4 require_once dirname( __FILE__ ) . '/class.jetpack-sync-queue.php';
5 require_once dirname( __FILE__ ) . '/class.jetpack-sync-modules.php';
6 require_once dirname( __FILE__ ) . '/class.jetpack-sync-actions.php';
7
8 /**
9 * This class monitors actions and logs them to the queue to be sent
10 */
11 class Jetpack_Sync_Listener {
12 const QUEUE_STATE_CHECK_TRANSIENT = 'jetpack_sync_last_checked_queue_state';
13 const QUEUE_STATE_CHECK_TIMEOUT = 300; // 5 minutes
14
15 private $sync_queue;
16 private $full_sync_queue;
17 private $sync_queue_size_limit;
18 private $sync_queue_lag_limit;
19
20 // singleton functions
21 private static $instance;
22
23 public static function get_instance() {
24 if ( null === self::$instance ) {
25 self::$instance = new self();
26 }
27
28 return self::$instance;
29 }
30
31 // this is necessary because you can't use "new" when you declare instance properties >:(
32 protected function __construct() {
33 $this->set_defaults();
34 $this->init();
35 }
36
37 private function init() {
38 $handler = array( $this, 'action_handler' );
39 $full_sync_handler = array( $this, 'full_sync_action_handler' );
40
41 foreach ( Jetpack_Sync_Modules::get_modules() as $module ) {
42 $module->init_listeners( $handler );
43 $module->init_full_sync_listeners( $full_sync_handler );
44 }
45
46 // Module Activation
47 add_action( 'jetpack_activate_module', $handler );
48 add_action( 'jetpack_deactivate_module', $handler );
49
50 // Jetpack Upgrade
51 add_action( 'updating_jetpack_version', $handler, 10, 2 );
52
53 // Send periodic checksum
54 add_action( 'jetpack_sync_checksum', $handler );
55 }
56
57 function get_sync_queue() {
58 return $this->sync_queue;
59 }
60
61 function get_full_sync_queue() {
62 return $this->full_sync_queue;
63 }
64
65 function set_queue_size_limit( $limit ) {
66 $this->sync_queue_size_limit = $limit;
67 }
68
69 function get_queue_size_limit() {
70 return $this->sync_queue_size_limit;
71 }
72
73 function set_queue_lag_limit( $age ) {
74 $this->sync_queue_lag_limit = $age;
75 }
76
77 function get_queue_lag_limit() {
78 return $this->sync_queue_lag_limit;
79 }
80
81 function force_recheck_queue_limit() {
82 delete_transient( self::QUEUE_STATE_CHECK_TRANSIENT . '_' . $this->sync_queue->id );
83 delete_transient( self::QUEUE_STATE_CHECK_TRANSIENT . '_' . $this->full_sync_queue->id );
84 }
85
86 // prevent adding items to the queue if it hasn't sent an item for 15 mins
87 // AND the queue is over 1000 items long (by default)
88 function can_add_to_queue( $queue ) {
89 if ( Jetpack_Sync_Settings::get_setting( 'disable' ) ) {
90 return false;
91 }
92
93 $state_transient_name = self::QUEUE_STATE_CHECK_TRANSIENT . '_' . $queue->id;
94
95 $queue_state = get_transient( $state_transient_name );
96
97 if ( false === $queue_state ) {
98 $queue_state = array( $queue->size(), $queue->lag() );
99 set_transient( $state_transient_name, $queue_state, self::QUEUE_STATE_CHECK_TIMEOUT );
100 }
101
102 list( $queue_size, $queue_age ) = $queue_state;
103
104 return ( $queue_age < $this->sync_queue_lag_limit )
105 ||
106 ( ( $queue_size + 1 ) < $this->sync_queue_size_limit );
107 }
108
109 function full_sync_action_handler() {
110 $args = func_get_args();
111 $this->enqueue_action( current_filter(), $args, $this->full_sync_queue );
112 }
113
114 function action_handler() {
115 $args = func_get_args();
116 $this->enqueue_action( current_filter(), $args, $this->sync_queue );
117 }
118
119 // add many actions to the queue directly, without invoking them
120 function bulk_enqueue_full_sync_actions( $action_name, $args_array ) {
121 $queue = $this->get_full_sync_queue();
122
123 // periodically check the size of the queue, and disable adding to it if
124 // it exceeds some limit AND the oldest item exceeds the age limit (i.e. sending has stopped)
125 if ( ! $this->can_add_to_queue( $queue ) ) {
126 return;
127 }
128
129 // if we add any items to the queue, we should try to ensure that our script
130 // can't be killed before they are sent
131 if ( function_exists( 'ignore_user_abort' ) ) {
132 ignore_user_abort( true );
133 }
134
135 $data_to_enqueue = array();
136 $user_id = get_current_user_id();
137 $currtime = microtime( true );
138 $is_importing = Jetpack_Sync_Settings::is_importing();
139
140 foreach( $args_array as $args ) {
141
142 /**
143 * Modify or reject the data within an action before it is enqueued locally.
144 *
145 * @since 4.2.0
146 *
147 * @module sync
148 *
149 * @param array The action parameters
150 */
151 $args = apply_filters( "jetpack_sync_before_enqueue_$action_name", $args );
152
153 // allow listeners to abort
154 if ( $args === false ) {
155 continue;
156 }
157
158 $data_to_enqueue[] = array(
159 $action_name,
160 array( $args ),
161 $user_id,
162 $currtime,
163 $is_importing,
164 );
165 }
166
167 $queue->add_all( $data_to_enqueue );
168 }
169
170 function enqueue_action( $current_filter, $args, $queue ) {
171 // don't enqueue an action during the outbound http request - this prevents recursion
172 if ( Jetpack_Sync_Settings::is_sending() ) {
173 return;
174 }
175
176 /**
177 * Add an action hook to execute when anything on the whitelist gets sent to the queue to sync.
178 *
179 * @module sync
180 *
181 * @since 5.9.0
182 */
183 do_action( 'jetpack_sync_action_before_enqueue' );
184
185 /**
186 * Modify or reject the data within an action before it is enqueued locally.
187 *
188 * @since 4.2.0
189 *
190 * @param array The action parameters
191 */
192 $args = apply_filters( "jetpack_sync_before_enqueue_$current_filter", $args );
193
194 // allow listeners to abort
195 if ( $args === false ) {
196 return;
197 }
198
199 // periodically check the size of the queue, and disable adding to it if
200 // it exceeds some limit AND the oldest item exceeds the age limit (i.e. sending has stopped)
201 if ( ! $this->can_add_to_queue( $queue ) ) {
202 return;
203 }
204
205 // if we add any items to the queue, we should try to ensure that our script
206 // can't be killed before they are sent
207 if ( function_exists( 'ignore_user_abort' ) ) {
208 ignore_user_abort( true );
209 }
210
211 if (
212 'sync' === $queue->id ||
213 in_array(
214 $current_filter,
215 array(
216 'jetpack_full_sync_start',
217 'jetpack_full_sync_end',
218 'jetpack_full_sync_cancel'
219 )
220 )
221 ) {
222 $queue->add( array(
223 $current_filter,
224 $args,
225 get_current_user_id(),
226 microtime( true ),
227 Jetpack_Sync_Settings::is_importing(),
228 $this->get_actor( $current_filter, $args ),
229 ) );
230 } else {
231 $queue->add( array(
232 $current_filter,
233 $args,
234 get_current_user_id(),
235 microtime( true ),
236 Jetpack_Sync_Settings::is_importing()
237 ) );
238 }
239
240 // since we've added some items, let's try to load the sender so we can send them as quickly as possible
241 if ( ! Jetpack_Sync_Actions::$sender ) {
242 add_filter( 'jetpack_sync_sender_should_load', '__return_true' );
243 if ( did_action( 'init' ) ) {
244 Jetpack_Sync_Actions::add_sender_shutdown();
245 }
246 }
247 }
248
249 function get_actor( $current_filter, $args ) {
250 if ( 'wp_login' === $current_filter ) {
251 $user = get_user_by( 'ID', $args[1]->data->ID );
252 } else {
253 $user = wp_get_current_user();
254 }
255
256 $translated_role = Jetpack::translate_user_to_role( $user );
257
258 $actor = array(
259 'wpcom_user_id' => null,
260 'external_user_id' => isset( $user->ID ) ? $user->ID : null,
261 'display_name' => isset( $user->display_name ) ? $user->display_name : null,
262 'user_email' => isset( $user->user_email ) ? $user->user_email : null,
263 'user_roles' => isset( $user->roles ) ? $user->roles : null,
264 'translated_role' => $translated_role ? $translated_role : null,
265 'is_cron' => defined( 'DOING_CRON' ) ? DOING_CRON : false,
266 'is_rest' => defined( 'REST_API_REQUEST' ) ? REST_API_REQUEST : false,
267 'is_xmlrpc' => defined( 'XMLRPC_REQUEST' ) ? XMLRPC_REQUEST : false,
268 'is_wp_rest' => defined( 'REST_REQUEST' ) ? REST_REQUEST : false,
269 'is_ajax' => defined( 'DOING_AJAX' ) ? DOING_AJAX : false,
270 'is_wp_admin' => is_admin(),
271 'is_cli' => defined( 'WP_CLI' ) ? WP_CLI : false,
272 );
273
274 if ( $this->should_send_user_data_with_actor( $current_filter ) ) {
275 require_once( JETPACK__PLUGIN_DIR . 'modules/protect/shared-functions.php' );
276 $actor['ip'] = jetpack_protect_get_ip();
277 $actor['user_agent'] = isset( $_SERVER['HTTP_USER_AGENT'] ) ? $_SERVER['HTTP_USER_AGENT'] : 'unknown';
278 }
279
280 return $actor;
281 }
282
283 function should_send_user_data_with_actor( $current_filter ) {
284 $should_send = in_array( $current_filter, array( 'wp_login', 'wp_logout', 'jetpack_valid_failed_login_attempt' ) );
285 /**
286 * Allow or deny sending actor's user data ( IP and UA ) during a sync event
287 *
288 * @since 5.8.0
289 *
290 * @module sync
291 *
292 * @param bool True if we should send user data
293 * @param string The current filter that is performing the sync action
294 */
295 return apply_filters( 'jetpack_sync_actor_user_data', $should_send, $current_filter );
296 }
297
298 function set_defaults() {
299 $this->sync_queue = new Jetpack_Sync_Queue( 'sync' );
300 $this->full_sync_queue = new Jetpack_Sync_Queue( 'full_sync' );
301 $this->set_queue_size_limit( Jetpack_Sync_Settings::get_setting( 'max_queue_size' ) );
302 $this->set_queue_lag_limit( Jetpack_Sync_Settings::get_setting( 'max_queue_lag' ) );
303 }
304 }
305