PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 16.2
Jetpack – WP Security, Backup, Speed, & Growth v16.2
16.3-a.5 16.3-a.7 16.3-a.3 16.3-a.1 16.2 16.2-beta 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 All 506 releases
jetpack / jetpack_vendor / automattic / jetpack-connection / legacy / class-jetpack-xmlrpc-server.php

class-jetpack-xmlrpc-server.php in Jetpack – WP Security, Backup, Speed, & Growth 16.2, at jetpack_vendor/automattic/jetpack-connection/legacy/class-jetpack-xmlrpc-server.php

846 lines 24.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Jetpack XMLRPC Server.
4 *
5 * @package automattic/jetpack-connection
6 */
7
8 use Automattic\Jetpack\Connection\Client;
9 use Automattic\Jetpack\Connection\Manager as Connection_Manager;
10 use Automattic\Jetpack\Connection\Secrets;
11 use Automattic\Jetpack\Connection\Tokens;
12 use Automattic\Jetpack\Connection\Urls;
13 use Automattic\Jetpack\Constants;
14 use Automattic\Jetpack\Roles;
15
16 /**
17 * Just a sack of functions. Not actually an IXR_Server
18 */
19 class Jetpack_XMLRPC_Server {
20 /**
21 * The current error object
22 *
23 * @var \WP_Error
24 */
25 public $error = null;
26
27 /**
28 * The current user
29 *
30 * @var \WP_User
31 */
32 public $user = null;
33
34 /**
35 * The connection manager object.
36 *
37 * @var Automattic\Jetpack\Connection\Manager
38 */
39 private $connection;
40
41 /**
42 * Creates a new XMLRPC server object.
43 */
44 public function __construct() {
45 $this->connection = new Connection_Manager();
46 }
47
48 /**
49 * Whitelist of the XML-RPC methods available to the Jetpack Server. If the
50 * user is not authenticated (->login()) then the methods are never added,
51 * so they will get a "does not exist" error.
52 *
53 * @param array $core_methods Core XMLRPC methods.
54 */
55 public function xmlrpc_methods( $core_methods ) {
56 $jetpack_methods = array(
57 'jetpack.verifyAction' => array( $this, 'verify_action' ),
58 'jetpack.idcUrlValidation' => array( $this, 'validate_urls_for_idc_mitigation' ),
59 'jetpack.unlinkUser' => array( $this, 'unlink_user' ),
60 'jetpack.testConnection' => array( $this, 'test_connection' ),
61 );
62
63 $jetpack_methods = array_merge( $jetpack_methods, $this->provision_xmlrpc_methods() );
64
65 $this->user = $this->login();
66
67 if ( $this->user ) {
68 $jetpack_methods = array_merge(
69 $jetpack_methods,
70 array(
71 'jetpack.testAPIUserCode' => array( $this, 'test_api_user_code' ),
72 )
73 );
74
75 if ( isset( $core_methods['metaWeblog.editPost'] ) ) {
76 $jetpack_methods['metaWeblog.newMediaObject'] = $core_methods['metaWeblog.newMediaObject'];
77 $jetpack_methods['jetpack.updateAttachmentParent'] = array( $this, 'update_attachment_parent' );
78 }
79
80 /**
81 * Filters the XML-RPC methods available to Jetpack for authenticated users.
82 *
83 * @since 1.7.0
84 * @since-jetpack 1.1.0
85 *
86 * @param array $jetpack_methods XML-RPC methods available to the Jetpack Server.
87 * @param array $core_methods Available core XML-RPC methods.
88 * @param \WP_User $user Information about the user authenticated in the request.
89 */
90 $jetpack_methods = apply_filters( 'jetpack_xmlrpc_methods', $jetpack_methods, $core_methods, $this->user );
91 }
92
93 /**
94 * Filters the XML-RPC methods available to Jetpack for requests signed both with a blog token or a user token.
95 *
96 * @since 1.7.0
97 * @since 1.9.5 Introduced the $user parameter.
98 * @since-jetpack 3.0.0
99 *
100 * @param array $jetpack_methods XML-RPC methods available to the Jetpack Server.
101 * @param array $core_methods Available core XML-RPC methods.
102 * @param \WP_User|bool $user Information about the user authenticated in the request. False if authenticated with blog token.
103 */
104 return apply_filters( 'jetpack_xmlrpc_unauthenticated_methods', $jetpack_methods, $core_methods, $this->user );
105 }
106
107 /**
108 * Whitelist of the bootstrap XML-RPC methods
109 */
110 public function bootstrap_xmlrpc_methods() {
111 return array(
112 'jetpack.remoteAuthorize' => array( $this, 'remote_authorize' ),
113 'jetpack.remoteRegister' => array( $this, 'remote_register' ),
114 );
115 }
116
117 /**
118 * Additional method needed for authorization calls.
119 */
120 public function authorize_xmlrpc_methods() {
121 return array(
122 'jetpack.remoteAuthorize' => array( $this, 'remote_authorize' ),
123 'jetpack.remoteRegister' => array( $this, 'remote_already_registered' ),
124 );
125 }
126
127 /**
128 * Remote provisioning methods.
129 */
130 public function provision_xmlrpc_methods() {
131 return array(
132 'jetpack.remoteRegister' => array( $this, 'remote_register' ),
133 'jetpack.remoteProvision' => array( $this, 'remote_provision' ),
134 'jetpack.remoteConnect' => array( $this, 'remote_connect' ),
135 'jetpack.getUser' => array( $this, 'get_user' ),
136 );
137 }
138
139 /**
140 * Used to verify whether a local user exists and what role they have.
141 *
142 * @param int|string|array $request One of:
143 * int|string The local User's ID, username, or email address.
144 * array A request array containing:
145 * 0: int|string The local User's ID, username, or email address.
146 *
147 * @return array|\IXR_Error Information about the user, or error if no such user found:
148 * roles: string[] The user's rols.
149 * login: string The user's username.
150 * email_hash string[] The MD5 hash of the user's normalized email address.
151 * caps string[] The user's capabilities.
152 * allcaps string[] The user's granular capabilities, merged from role capabilities.
153 * token_key string The Token Key of the user's Jetpack token. Empty string if none.
154 */
155 public function get_user( $request ) {
156 $user_id = is_array( $request ) ? $request[0] : $request;
157
158 if ( ! $user_id ) {
159 return $this->error(
160 new \WP_Error(
161 'invalid_user',
162 __( 'Invalid user identifier.', 'jetpack-connection' ),
163 400
164 ),
165 'get_user'
166 );
167 }
168
169 $user = $this->get_user_by_anything( $user_id );
170
171 if ( ! $user ) {
172 return $this->error(
173 new \WP_Error(
174 'user_unknown',
175 __( 'User not found.', 'jetpack-connection' ),
176 404
177 ),
178 'get_user'
179 );
180 }
181
182 $user_token = ( new Tokens() )->get_access_token( $user->ID );
183
184 if ( $user_token ) {
185 list( $user_token_key ) = explode( '.', $user_token->secret );
186 if ( $user_token_key === $user_token->secret ) {
187 $user_token_key = '';
188 }
189 } else {
190 $user_token_key = '';
191 }
192
193 return array(
194 'id' => $user->ID,
195 'login' => $user->user_login,
196 'email_hash' => md5( strtolower( trim( $user->user_email ) ) ),
197 'roles' => $user->roles,
198 'caps' => $user->caps,
199 'allcaps' => $user->allcaps,
200 'token_key' => $user_token_key,
201 );
202 }
203
204 /**
205 * Remote authorization XMLRPC method handler.
206 *
207 * @param array $request the request.
208 */
209 public function remote_authorize( $request ) {
210 $user = get_user_by( 'id', $request['state'] );
211
212 /**
213 * Happens on various request handling events in the Jetpack XMLRPC server.
214 * The action combines several types of events:
215 * - remote_authorize
216 * - remote_provision
217 * - get_user.
218 *
219 * @since 1.7.0
220 * @since-jetpack 8.0.0
221 *
222 * @param String $action the action name, i.e., 'remote_authorize'.
223 * @param String $stage the execution stage, can be 'begin', 'success', 'error', etc.
224 * @param array $parameters extra parameters from the event.
225 * @param WP_User $user the acting user.
226 */
227 do_action( 'jetpack_xmlrpc_server_event', 'remote_authorize', 'begin', array(), $user );
228
229 foreach ( array( 'secret', 'state', 'redirect_uri', 'code' ) as $required ) {
230 if ( ! isset( $request[ $required ] ) || empty( $request[ $required ] ) ) {
231 return $this->error(
232 new \WP_Error( 'missing_parameter', 'One or more parameters is missing from the request.', 400 ),
233 'remote_authorize'
234 );
235 }
236 }
237
238 if ( ! $user ) {
239 return $this->error( new \WP_Error( 'user_unknown', 'User not found.', 404 ), 'remote_authorize' );
240 }
241
242 if ( $this->connection->has_connected_owner() && $this->connection->is_user_connected( $request['state'] ) ) {
243 return $this->error( new \WP_Error( 'already_connected', 'User already connected.', 400 ), 'remote_authorize' );
244 }
245
246 $verified = $this->verify_action( array( 'authorize', $request['secret'], $request['state'] ) );
247
248 if ( is_a( $verified, 'IXR_Error' ) ) {
249 return $this->error( $verified, 'remote_authorize' );
250 }
251
252 wp_set_current_user( $request['state'] );
253
254 $result = $this->connection->authorize( $request );
255
256 if ( is_wp_error( $result ) ) {
257 return $this->error( $result, 'remote_authorize' );
258 }
259
260 // This action is documented in class.jetpack-xmlrpc-server.php.
261 do_action( 'jetpack_xmlrpc_server_event', 'remote_authorize', 'success' );
262
263 return array(
264 'result' => $result,
265 );
266 }
267
268 /**
269 * This XML-RPC method is called from the /jpphp/provision endpoint on WPCOM in order to
270 * register this site so that a plan can be provisioned.
271 *
272 * @param array|ArrayAccess $request An array containing at minimum nonce and local_user keys.
273 *
274 * @return \WP_Error|array
275 */
276 public function remote_register( $request ) {
277 // This action is documented in class.jetpack-xmlrpc-server.php.
278 do_action( 'jetpack_xmlrpc_server_event', 'remote_register', 'begin', array() );
279
280 $user = $this->fetch_and_verify_local_user( $request );
281
282 if ( ! $user ) {
283 return $this->error(
284 new WP_Error( 'input_error', __( 'Valid user is required', 'jetpack-connection' ), 400 ),
285 'remote_register'
286 );
287 }
288
289 if ( is_wp_error( $user ) || is_a( $user, 'IXR_Error' ) ) {
290 return $this->error( $user, 'remote_register' );
291 }
292
293 if ( empty( $request['nonce'] ) ) {
294 return $this->error(
295 new \WP_Error(
296 'nonce_missing',
297 __( 'The required "nonce" parameter is missing.', 'jetpack-connection' ),
298 400
299 ),
300 'remote_register'
301 );
302 }
303
304 $nonce = sanitize_text_field( $request['nonce'] );
305 unset( $request['nonce'] );
306
307 $api_url = $this->connection->api_url( 'partner_provision_nonce_check' );
308 // @phan-suppress-next-line PhanAccessMethodInternal -- Phan is correct, but the usage is intentional.
309 $response = Client::_wp_remote_request(
310 esc_url_raw( add_query_arg( 'nonce', $nonce, $api_url ) ),
311 array( 'method' => 'GET' ),
312 true
313 );
314
315 if (
316 200 !== wp_remote_retrieve_response_code( $response ) ||
317 'OK' !== trim( wp_remote_retrieve_body( $response ) )
318 ) {
319 return $this->error(
320 new \WP_Error(
321 'invalid_nonce',
322 __( 'There was an issue validating this request.', 'jetpack-connection' ),
323 400
324 ),
325 'remote_register'
326 );
327 }
328
329 if ( ! Jetpack_Options::get_option( 'id' ) || ! ( new Tokens() )->get_access_token() || ! empty( $request['force'] ) ) {
330 wp_set_current_user( $user->ID );
331
332 // This code mostly copied from Jetpack::admin_page_load.
333 if ( isset( $request['from'] ) ) {
334 $this->connection->add_register_request_param( 'from', (string) $request['from'] );
335 }
336 $registered = $this->connection->try_registration();
337 if ( is_wp_error( $registered ) ) {
338 return $this->error( $registered, 'remote_register' );
339 } elseif ( ! $registered ) {
340 return $this->error(
341 new \WP_Error(
342 'registration_error',
343 __( 'There was an unspecified error registering the site', 'jetpack-connection' ),
344 400
345 ),
346 'remote_register'
347 );
348 }
349 }
350
351 // This action is documented in class.jetpack-xmlrpc-server.php.
352 do_action( 'jetpack_xmlrpc_server_event', 'remote_register', 'success' );
353
354 return array(
355 'client_id' => Jetpack_Options::get_option( 'id' ),
356 );
357 }
358
359 /**
360 * This is a substitute for remote_register() when the blog is already registered which returns an error code
361 * signifying that state.
362 * This is an unauthorized call and we should not be responding with any data other than the error code.
363 *
364 * @return \IXR_Error
365 */
366 public function remote_already_registered() {
367 return $this->error(
368 new \WP_Error( 'already_registered', __( 'Blog is already registered', 'jetpack-connection' ), 400 ),
369 'remote_register'
370 );
371 }
372
373 /**
374 * This XML-RPC method is called from the /jpphp/provision endpoint on WPCOM in order to
375 * register this site so that a plan can be provisioned.
376 *
377 * @param array|ArrayAccess $request An array containing at minimum a nonce key and a local_username key.
378 *
379 * @return \WP_Error|array
380 */
381 public function remote_provision( $request ) {
382 $user = $this->fetch_and_verify_local_user( $request );
383
384 if ( ! $user ) {
385 return $this->error(
386 new WP_Error( 'input_error', __( 'Valid user is required', 'jetpack-connection' ), 400 ),
387 'remote_provision'
388 );
389 }
390
391 if ( is_wp_error( $user ) || is_a( $user, 'IXR_Error' ) ) {
392 return $this->error( $user, 'remote_provision' );
393 }
394
395 $site_icon = get_site_icon_url();
396
397 /**
398 * Filters the Redirect URI returned by the remote_register XMLRPC method
399 *
400 * @param string $redirect_uri The Redirect URI
401 *
402 * @since 1.9.7
403 */
404 $redirect_uri = apply_filters( 'jetpack_xmlrpc_remote_register_redirect_uri', admin_url() );
405
406 // Generate secrets.
407 $roles = new Roles();
408 $role = $roles->translate_user_to_role( $user );
409 $secrets = ( new Secrets() )->generate( 'authorize', $user->ID );
410
411 $response = array(
412 'jp_version' => Constants::get_constant( 'JETPACK__VERSION' ),
413 'redirect_uri' => $redirect_uri,
414 'user_id' => $user->ID,
415 'user_email' => $user->user_email,
416 'user_login' => $user->user_login,
417 'scope' => $this->connection->sign_role( $role, $user->ID ),
418 'secret' => $secrets['secret_1'],
419 'is_active' => $this->connection->has_connected_owner(),
420 );
421
422 if ( $site_icon ) {
423 $response['site_icon'] = $site_icon;
424 }
425
426 /**
427 * Filters the response of the remote_provision XMLRPC method
428 *
429 * @param array $response The response.
430 * @param array $request An array containing at minimum a nonce key and a local_username key.
431 * @param \WP_User $user The local authenticated user.
432 *
433 * @since 1.9.7
434 */
435 $response = apply_filters( 'jetpack_remote_xmlrpc_provision_response', $response, $request, $user );
436
437 return $response;
438 }
439
440 /**
441 * Given an array containing a local user identifier and a nonce, will attempt to fetch and set
442 * an access token for the given user.
443 *
444 * @param array|ArrayAccess $request An array containing local_user and nonce keys at minimum.
445 * @param \IXR_Client $ixr_client The client object, optional.
446 * @return mixed
447 */
448 public function remote_connect( $request, $ixr_client = false ) {
449 if ( $this->connection->has_connected_owner() ) {
450 return $this->error(
451 new WP_Error(
452 'already_connected',
453 __( 'Jetpack is already connected.', 'jetpack-connection' ),
454 400
455 ),
456 'remote_connect'
457 );
458 }
459
460 $user = $this->fetch_and_verify_local_user( $request );
461
462 if ( ! $user || is_wp_error( $user ) || is_a( $user, 'IXR_Error' ) ) {
463 return $this->error(
464 new WP_Error(
465 'input_error',
466 __( 'Valid user is required.', 'jetpack-connection' ),
467 400
468 ),
469 'remote_connect'
470 );
471 }
472
473 if ( empty( $request['nonce'] ) ) {
474 return $this->error(
475 new WP_Error(
476 'input_error',
477 __( 'A non-empty nonce must be supplied.', 'jetpack-connection' ),
478 400
479 ),
480 'remote_connect'
481 );
482 }
483
484 if ( ! $ixr_client ) {
485 $ixr_client = new Jetpack_IXR_Client();
486 }
487 // TODO: move this query into the Tokens class?
488 $ixr_client->query(
489 'jetpack.getUserAccessToken',
490 array(
491 'nonce' => sanitize_text_field( $request['nonce'] ),
492 'external_user_id' => $user->ID,
493 )
494 );
495
496 $token = $ixr_client->isError() ? false : $ixr_client->getResponse();
497 if ( empty( $token ) ) {
498 return $this->error(
499 new WP_Error(
500 'token_fetch_failed',
501 __( 'Failed to fetch user token from WordPress.com.', 'jetpack-connection' ),
502 400
503 ),
504 'remote_connect'
505 );
506 }
507 $token = sanitize_text_field( $token );
508
509 ( new Tokens() )->update_user_token( $user->ID, sprintf( '%s.%d', $token, $user->ID ), true );
510
511 /**
512 * Hook fired at the end of the jetpack.remoteConnect XML-RPC callback
513 *
514 * @since 1.9.7
515 */
516 do_action( 'jetpack_remote_connect_end' );
517
518 return $this->connection->has_connected_owner();
519 }
520
521 /**
522 * Getter for the local user to act as.
523 *
524 * @param array $request the current request data.
525 * @return WP_User|IXR_Error|false IXR_Error if the request is missing a local_user field, WP_User object on success, or false on failure to find a user.
526 */
527 private function fetch_and_verify_local_user( $request ) {
528 if ( empty( $request['local_user'] ) ) {
529 return $this->error(
530 new \WP_Error(
531 'local_user_missing',
532 __( 'The required "local_user" parameter is missing.', 'jetpack-connection' ),
533 400
534 ),
535 'remote_provision'
536 );
537 }
538
539 // Local user is used to look up by login, email or ID.
540 $local_user_info = $request['local_user'];
541
542 return $this->get_user_by_anything( $local_user_info );
543 }
544
545 /**
546 * Gets the user object by its data.
547 *
548 * @param string $user_id can be any identifying user data.
549 * @return WP_User|false WP_User object on success, false on failure.
550 */
551 private function get_user_by_anything( $user_id ) {
552 $user = get_user_by( 'login', $user_id );
553
554 if ( ! $user ) {
555 $user = get_user_by( 'email', $user_id );
556 }
557
558 if ( ! $user ) {
559 $user = get_user_by( 'ID', $user_id );
560 }
561
562 return $user;
563 }
564
565 /**
566 * Possible error_codes:
567 *
568 * - verify_secret_1_missing
569 * - verify_secret_1_malformed
570 * - verify_secrets_missing: verification secrets are not found in database
571 * - verify_secrets_incomplete: verification secrets are only partially found in database
572 * - verify_secrets_expired: verification secrets have expired
573 * - verify_secrets_mismatch: stored secret_1 does not match secret_1 sent by Jetpack.WordPress.com
574 * - state_missing: required parameter of state not found
575 * - state_malformed: state is not a digit
576 * - invalid_state: state in request does not match the stored state
577 *
578 * The 'authorize' and 'register' actions have additional error codes
579 *
580 * state_missing: a state ( user id ) was not supplied
581 * state_malformed: state is not the correct data type
582 * invalid_state: supplied state does not match the stored state
583 *
584 * @param array $params action An array of 3 parameters:
585 * [0]: string action. Possible values are `authorize`, `publicize` and `register`.
586 * [1]: string secret_1.
587 * [2]: int state.
588 * @return \IXR_Error|string IXR_Error on failure, secret_2 on success.
589 */
590 public function verify_action( $params ) {
591 $action = $params[0] ?? '';
592 $verify_secret = $params[1] ?? '';
593 $state = $params[2] ?? '';
594
595 $result = ( new Secrets() )->verify( $action, $verify_secret, $state );
596
597 if ( is_wp_error( $result ) ) {
598 return $this->error( $result );
599 }
600
601 return $result;
602 }
603
604 /**
605 * Wrapper for wp_authenticate( $username, $password );
606 *
607 * @return \WP_User|bool
608 */
609 public function login() {
610 $this->connection->require_jetpack_authentication();
611 $user = wp_authenticate( 'username', 'password' );
612 if ( is_wp_error( $user ) ) {
613 if ( 'authentication_failed' === $user->get_error_code() ) { // Generic error could mean most anything.
614 $this->error = new \WP_Error( 'invalid_request', 'Invalid Request', 403 );
615 } else {
616 $this->error = $user;
617 }
618 return false;
619 } elseif ( ! $user ) { // Shouldn't happen.
620 $this->error = new \WP_Error( 'invalid_request', 'Invalid Request', 403 );
621 return false;
622 }
623
624 wp_set_current_user( $user->ID );
625
626 return $user;
627 }
628
629 /**
630 * Returns the current error as an \IXR_Error
631 *
632 * @param \WP_Error|\IXR_Error $error The error object, optional.
633 * @param string $event_name The event name.
634 * @param \WP_User $user The user object.
635 * @return bool|\IXR_Error
636 */
637 public function error( $error = null, $event_name = null, $user = null ) {
638 if ( null !== $event_name ) {
639 // This action is documented in class.jetpack-xmlrpc-server.php.
640 do_action( 'jetpack_xmlrpc_server_event', $event_name, 'fail', $error, $user );
641 }
642
643 if ( $error !== null ) {
644 $this->error = $error;
645 }
646
647 if ( is_wp_error( $this->error ) ) {
648 $code = $this->error->get_error_data();
649 if ( ! $code ) {
650 $code = -10520;
651 }
652 $message = sprintf( 'Jetpack: [%s] %s', $this->error->get_error_code(), $this->error->get_error_message() );
653 if ( ! class_exists( \IXR_Error::class ) ) {
654 require_once ABSPATH . WPINC . '/class-IXR.php';
655 }
656 return new \IXR_Error( $code, $message );
657 } elseif ( is_a( $this->error, 'IXR_Error' ) ) {
658 return $this->error;
659 }
660
661 return false;
662 }
663
664 /* API Methods */
665
666 /**
667 * Just authenticates with the given Jetpack credentials.
668 *
669 * @return string A success string. The Jetpack plugin filters it and make it return the Jetpack plugin version.
670 */
671 public function test_connection() {
672 /**
673 * Filters the successful response of the XMLRPC test_connection method
674 *
675 * @param string $response The response string.
676 */
677 return apply_filters( 'jetpack_xmlrpc_test_connection_response', 'success' );
678 }
679
680 /**
681 * Test the API user code.
682 *
683 * @param array $args arguments identifying the test site.
684 */
685 public function test_api_user_code( $args ) {
686 $client_id = (int) $args[0];
687 $user_id = (int) $args[1];
688 $nonce = (string) $args[2];
689 $verify = (string) $args[3];
690
691 if ( ! $client_id || ! $user_id || ! strlen( $nonce ) || 32 !== strlen( $verify ) ) {
692 return false;
693 }
694
695 $user = get_user_by( 'id', $user_id );
696 if ( ! $user || is_wp_error( $user ) ) {
697 return false;
698 }
699
700 /* phpcs:ignore
701 debugging
702 error_log( "CLIENT: $client_id" );
703 error_log( "USER: $user_id" );
704 error_log( "NONCE: $nonce" );
705 error_log( "VERIFY: $verify" );
706 */
707
708 $jetpack_token = ( new Tokens() )->get_access_token( $user_id );
709
710 $api_user_code = get_user_meta( $user_id, "jetpack_json_api_$client_id", true );
711 if ( ! $api_user_code ) {
712 return false;
713 }
714
715 $hmac = hash_hmac(
716 'md5',
717 json_encode( // phpcs:ignore WordPress.WP.AlternativeFunctions.json_encode_json_encode
718 (object) array(
719 'client_id' => $client_id,
720 'user_id' => $user_id,
721 'nonce' => $nonce,
722 'code' => (string) $api_user_code,
723 ),
724 0 // phpcs:ignore Jetpack.Functions.JsonEncodeFlags.ZeroFound -- No `json_encode()` flags because this needs to match whatever is calculating the hash on the other end.
725 ),
726 $jetpack_token->secret
727 );
728
729 if ( ! hash_equals( $hmac, $verify ) ) {
730 return false;
731 }
732
733 return $user_id;
734 }
735
736 /**
737 * Unlink a user from WordPress.com
738 *
739 * When the request is done without any parameter, this XMLRPC callback gets an empty array as input.
740 *
741 * If $user_id is not provided, it will try to disconnect the current logged in user. This will fail if called by the Master User.
742 *
743 * If $user_id is is provided, it will try to disconnect the informed user, even if it's the Master User.
744 *
745 * @param mixed $user_id The user ID to disconnect from this site.
746 */
747 public function unlink_user( $user_id = array() ) {
748 $user_id = (int) $user_id;
749 if ( $user_id < 1 ) {
750 $user_id = null;
751 }
752 /**
753 * Fired when we want to log an event to the Jetpack event log.
754 *
755 * @since 1.7.0
756 * @since-jetpack 7.7.0
757 *
758 * @param string $code Unique name for the event.
759 * @param string $data Optional data about the event.
760 */
761 do_action( 'jetpack_event_log', 'unlink' );
762 return $this->connection->disconnect_user(
763 $user_id,
764 (bool) $user_id
765 );
766 }
767
768 /**
769 * Returns the home URL, site URL, and URL secret for the current site which can be used on the WPCOM side for
770 * IDC mitigation to decide whether sync should be allowed if the home and siteurl values differ between WPCOM
771 * and the remote Jetpack site.
772 *
773 * @since 1.56.0 Additional data may be added via filter `jetpack_connection_validate_urls_for_idc_mitigation_response`.
774 *
775 * @return array
776 */
777 public function validate_urls_for_idc_mitigation() {
778 $response = array(
779 'home' => Urls::home_url(),
780 'siteurl' => Urls::site_url(),
781 );
782
783 /**
784 * Allows modifying the response.
785 *
786 * @since 1.56.0
787 *
788 * @param array $response
789 */
790 return apply_filters( 'jetpack_connection_validate_urls_for_idc_mitigation_response', $response );
791 }
792
793 /**
794 * Updates the attachment parent object.
795 *
796 * @param array $args attachment and parent identifiers.
797 */
798 public function update_attachment_parent( $args ) {
799 $attachment_id = (int) $args[0];
800 $parent_id = (int) $args[1];
801
802 return wp_update_post(
803 array(
804 'ID' => $attachment_id,
805 'post_parent' => $parent_id,
806 )
807 );
808 }
809
810 /**
811 * Deprecated: This method is no longer part of the Connection package and now lives on the Jetpack plugin.
812 *
813 * Disconnect this blog from the connected wordpress.com account
814 *
815 * @deprecated since 1.25.0
816 * @see Jetpack_XMLRPC_Methods::disconnect_blog() in the Jetpack plugin
817 *
818 * @return boolean
819 */
820 public function disconnect_blog() {
821 _deprecated_function( __METHOD__, '1.25.0', 'Jetpack_XMLRPC_Methods::disconnect_blog()' );
822 if ( class_exists( 'Jetpack_XMLRPC_Methods' ) ) {
823 return Jetpack_XMLRPC_Methods::disconnect_blog();
824 }
825 return false;
826 }
827
828 /**
829 * Deprecated: This method is no longer part of the Connection package and now lives on the Jetpack plugin.
830 *
831 * Serve a JSON API request.
832 *
833 * @deprecated since 1.25.0
834 * @see Jetpack_XMLRPC_Methods::json_api() in the Jetpack plugin
835 *
836 * @param array $args request arguments.
837 */
838 public function json_api( $args = array() ) {
839 _deprecated_function( __METHOD__, '1.25.0', 'Jetpack_XMLRPC_Methods::json_api()' );
840 if ( class_exists( 'Jetpack_XMLRPC_Methods' ) ) {
841 return Jetpack_XMLRPC_Methods::json_api( $args );
842 }
843 return array();
844 }
845 }
846