Completed
Push — renovate/mocha-6.x ( f901fe...f3dd78 )
by
unknown
56:50 queued 50:25
created

Jetpack_XMLRPC_Server::test_connection()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 0
dl 0
loc 3
rs 10
c 0
b 0
f 0
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\Roles;
11
use Automattic\Jetpack\Sync\Modules;
12
use Automattic\Jetpack\Sync\Functions;
13
use Automattic\Jetpack\Sync\Sender;
14
use Automattic\Jetpack\Tracking;
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 tracking manager object.
36
	 *
37
	 * @var Automattic\Jetpack\Tracking
38
	 */
39
	private $tracking;
40
41
	/**
42
	 * The connection manager object.
43
	 *
44
	 * @var Automattic\Jetpack\Connection\Manager
45
	 */
46
	private $connection;
47
48
	/**
49
	 * Creates a new XMLRPC server object.
50
	 *
51
	 * @param Automattic\Jetpack\Connection\Manager $manager the connection manager object.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $manager not be Connection_Manager|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...
52
	 */
53
	public function __construct( $manager = null ) {
54
		$this->connection = is_null( $manager ) ? new Connection_Manager() : $manager;
55
		$this->tracking   = new Tracking( 'jetpack', $manager );
0 ignored issues
show
Bug introduced by
It seems like $manager defined by parameter $manager on line 53 can also be of type object<Automattic\Jetpack\Connection\Manager>; however, Automattic\Jetpack\Tracking::__construct() does only seem to accept object<Automattic\Jetpac...onnection\Manager>|null, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
56
	}
57
58
	/**
59
	 * Whitelist of the XML-RPC methods available to the Jetpack Server. If the
60
	 * user is not authenticated (->login()) then the methods are never added,
61
	 * so they will get a "does not exist" error.
62
	 *
63
	 * @param array $core_methods Core XMLRPC methods.
64
	 */
65
	public function xmlrpc_methods( $core_methods ) {
66
		$jetpack_methods = array(
67
			'jetpack.jsonAPI'         => array( $this, 'json_api' ),
68
			'jetpack.verifyAction'    => array( $this, 'verify_action' ),
69
			'jetpack.getUser'         => array( $this, 'get_user' ),
70
			'jetpack.remoteRegister'  => array( $this, 'remote_register' ),
71
			'jetpack.remoteProvision' => array( $this, 'remote_provision' ),
72
		);
73
74
		$this->user = $this->login();
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->login() can also be of type boolean. However, the property $user is declared as type object<WP_User>. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
75
76
		if ( $this->user ) {
77
			$jetpack_methods = array_merge(
78
				$jetpack_methods,
79
				array(
80
					'jetpack.testConnection'    => array( $this, 'test_connection' ),
81
					'jetpack.testAPIUserCode'   => array( $this, 'test_api_user_code' ),
82
					'jetpack.featuresAvailable' => array( $this, 'features_available' ),
83
					'jetpack.featuresEnabled'   => array( $this, 'features_enabled' ),
84
					'jetpack.disconnectBlog'    => array( $this, 'disconnect_blog' ),
85
					'jetpack.unlinkUser'        => array( $this, 'unlink_user' ),
86
					'jetpack.syncObject'        => array( $this, 'sync_object' ),
87
					'jetpack.idcUrlValidation'  => array( $this, 'validate_urls_for_idc_mitigation' ),
88
				)
89
			);
90
91
			if ( isset( $core_methods['metaWeblog.editPost'] ) ) {
92
				$jetpack_methods['metaWeblog.newMediaObject']      = $core_methods['metaWeblog.newMediaObject'];
93
				$jetpack_methods['jetpack.updateAttachmentParent'] = array( $this, 'update_attachment_parent' );
94
			}
95
96
			/**
97
			 * Filters the XML-RPC methods available to Jetpack for authenticated users.
98
			 *
99
			 * @since 1.1.0
100
			 *
101
			 * @param array    $jetpack_methods XML-RPC methods available to the Jetpack Server.
102
			 * @param array    $core_methods    Available core XML-RPC methods.
103
			 * @param \WP_User $user            Information about a given WordPress user.
104
			 */
105
			$jetpack_methods = apply_filters( 'jetpack_xmlrpc_methods', $jetpack_methods, $core_methods, $this->user );
106
		}
107
108
		/**
109
		 * Filters the XML-RPC methods available to Jetpack for unauthenticated users.
110
		 *
111
		 * @since 3.0.0
112
		 *
113
		 * @param array $jetpack_methods XML-RPC methods available to the Jetpack Server.
114
		 * @param array $core_methods    Available core XML-RPC methods.
115
		 */
116
		return apply_filters( 'jetpack_xmlrpc_unauthenticated_methods', $jetpack_methods, $core_methods );
117
	}
118
119
	/**
120
	 * Whitelist of the bootstrap XML-RPC methods
121
	 */
122
	public function bootstrap_xmlrpc_methods() {
123
		return array(
124
			'jetpack.remoteAuthorize' => array( $this, 'remote_authorize' ),
125
			'jetpack.remoteRegister'  => array( $this, 'remote_register' ),
126
		);
127
	}
128
129
	/**
130
	 * Additional method needed for authorization calls.
131
	 */
132
	public function authorize_xmlrpc_methods() {
133
		return array(
134
			'jetpack.remoteAuthorize' => array( $this, 'remote_authorize' ),
135
		);
136
	}
137
138
	/**
139
	 * Remote provisioning methods.
140
	 */
141
	public function provision_xmlrpc_methods() {
142
		return array(
143
			'jetpack.remoteRegister'  => array( $this, 'remote_register' ),
144
			'jetpack.remoteProvision' => array( $this, 'remote_provision' ),
145
			'jetpack.remoteConnect'   => array( $this, 'remote_connect' ),
146
			'jetpack.getUser'         => array( $this, 'get_user' ),
147
		);
148
	}
149
150
	/**
151
	 * Used to verify whether a local user exists and what role they have.
152
	 *
153
	 * @param int|string|array $request One of:
154
	 *                         int|string The local User's ID, username, or email address.
155
	 *                         array      A request array containing:
156
	 *                                    0: int|string The local User's ID, username, or email address.
157
	 *
158
	 * @return array|\IXR_Error Information about the user, or error if no such user found:
159
	 *                          roles:     string[] The user's rols.
160
	 *                          login:     string   The user's username.
161
	 *                          email_hash string[] The MD5 hash of the user's normalized email address.
162
	 *                          caps       string[] The user's capabilities.
163
	 *                          allcaps    string[] The user's granular capabilities, merged from role capabilities.
164
	 *                          token_key  string   The Token Key of the user's Jetpack token. Empty string if none.
165
	 */
166
	public function get_user( $request ) {
167
		$user_id = is_array( $request ) ? $request[0] : $request;
168
169
		if ( ! $user_id ) {
170
			return $this->error(
171
				new Jetpack_Error(
172
					'invalid_user',
0 ignored issues
show
Unused Code introduced by
The call to Jetpack_Error::__construct() has too many arguments starting with 'invalid_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...
173
					__( 'Invalid user identifier.', 'jetpack' ),
174
					400
175
				),
176
				'jpc_get_user_fail'
177
			);
178
		}
179
180
		$user = $this->get_user_by_anything( $user_id );
181
182
		if ( ! $user ) {
183
			return $this->error(
184
				new Jetpack_Error(
185
					'user_unknown',
0 ignored issues
show
Unused Code introduced by
The call to Jetpack_Error::__construct() has too many arguments starting with 'user_unknown'.

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...
186
					__( 'User not found.', 'jetpack' ),
187
					404
188
				),
189
				'jpc_get_user_fail'
190
			);
191
		}
192
193
		$connection = Jetpack::connection();
194
		$user_token = $connection->get_access_token( $user->ID );
195
196
		if ( $user_token ) {
197
			list( $user_token_key ) = explode( '.', $user_token->secret );
198
			if ( $user_token_key === $user_token->secret ) {
199
				$user_token_key = '';
200
			}
201
		} else {
202
			$user_token_key = '';
203
		}
204
205
		return array(
206
			'id'         => $user->ID,
207
			'login'      => $user->user_login,
208
			'email_hash' => md5( strtolower( trim( $user->user_email ) ) ),
209
			'roles'      => $user->roles,
210
			'caps'       => $user->caps,
211
			'allcaps'    => $user->allcaps,
212
			'token_key'  => $user_token_key,
213
		);
214
	}
215
216
	/**
217
	 * Remote authorization XMLRPC method handler.
218
	 *
219
	 * @param array $request the request.
220
	 */
221
	public function remote_authorize( $request ) {
222
		$user = get_user_by( 'id', $request['state'] );
223
		$this->tracking->record_user_event( 'jpc_remote_authorize_begin', array(), $user );
224
225
		foreach ( array( 'secret', 'state', 'redirect_uri', 'code' ) as $required ) {
226
			if ( ! isset( $request[ $required ] ) || empty( $request[ $required ] ) ) {
227
				return $this->error( new Jetpack_Error( 'missing_parameter', 'One or more parameters is missing from the request.', 400 ), 'jpc_remote_authorize_fail' );
0 ignored issues
show
Unused Code introduced by
The call to Jetpack_Error::__construct() has too many arguments starting with 'missing_parameter'.

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...
228
			}
229
		}
230
231
		if ( ! $user ) {
232
			return $this->error( new Jetpack_Error( 'user_unknown', 'User not found.', 404 ), 'jpc_remote_authorize_fail' );
0 ignored issues
show
Unused Code introduced by
The call to Jetpack_Error::__construct() has too many arguments starting with 'user_unknown'.

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...
233
		}
234
235
		if ( Jetpack::is_active() && Jetpack::is_user_connected( $request['state'] ) ) {
236
			return $this->error( new Jetpack_Error( 'already_connected', 'User already connected.', 400 ), 'jpc_remote_authorize_fail' );
0 ignored issues
show
Unused Code introduced by
The call to Jetpack_Error::__construct() has too many arguments starting with 'already_connected'.

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...
237
		}
238
239
		$verified = $this->verify_action( array( 'authorize', $request['secret'], $request['state'] ) );
240
241
		if ( is_a( $verified, 'IXR_Error' ) ) {
242
			return $this->error( $verified, 'jpc_remote_authorize_fail' );
0 ignored issues
show
Bug introduced by
It seems like $verified defined by $this->verify_action(arr...'], $request['state'])) on line 239 can also be of type string; however, Jetpack_XMLRPC_Server::error() does only seem to accept object<WP_Error>|object<IXR_Error>|null, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
243
		}
244
245
		wp_set_current_user( $request['state'] );
246
247
		$client_server = new Jetpack_Client_Server();
248
		$result        = $client_server->authorize( $request );
249
250
		if ( is_wp_error( $result ) ) {
251
			return $this->error( $result, 'jpc_remote_authorize_fail' );
0 ignored issues
show
Bug introduced by
It seems like $result defined by $client_server->authorize($request) on line 248 can also be of type string; however, Jetpack_XMLRPC_Server::error() does only seem to accept object<WP_Error>|object<IXR_Error>|null, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
252
		}
253
254
		$this->tracking->record_user_event( 'jpc_remote_authorize_success' );
255
256
		return array(
257
			'result' => $result,
258
		);
259
	}
260
261
	/**
262
	 * This XML-RPC method is called from the /jpphp/provision endpoint on WPCOM in order to
263
	 * register this site so that a plan can be provisioned.
264
	 *
265
	 * @param array $request An array containing at minimum nonce and local_user keys.
266
	 *
267
	 * @return \WP_Error|array
268
	 */
269
	public function remote_register( $request ) {
270
		$this->tracking->record_user_event( 'jpc_remote_register_begin', array() );
271
272
		$user = $this->fetch_and_verify_local_user( $request );
273
274
		if ( ! $user ) {
275
			return $this->error( new WP_Error( 'input_error', __( 'Valid user is required', 'jetpack' ), 400 ), 'jpc_remote_register_fail' );
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with 'input_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...
276
		}
277
278
		if ( is_wp_error( $user ) || is_a( $user, 'IXR_Error' ) ) {
279
			return $this->error( $user, 'jpc_remote_register_fail' );
280
		}
281
282
		if ( empty( $request['nonce'] ) ) {
283
			return $this->error(
284
				new Jetpack_Error(
285
					'nonce_missing',
0 ignored issues
show
Unused Code introduced by
The call to Jetpack_Error::__construct() has too many arguments starting with 'nonce_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...
286
					__( 'The required "nonce" parameter is missing.', 'jetpack' ),
287
					400
288
				),
289
				'jpc_remote_register_fail'
290
			);
291
		}
292
293
		$nonce = sanitize_text_field( $request['nonce'] );
294
		unset( $request['nonce'] );
295
296
		$api_url  = Jetpack::fix_url_for_bad_hosts(
297
			$this->connection->api_url( 'partner_provision_nonce_check' )
298
		);
299
		$response = Client::_wp_remote_request(
300
			esc_url_raw( add_query_arg( 'nonce', $nonce, $api_url ) ),
301
			array( 'method' => 'GET' ),
302
			true
303
		);
304
305
		if (
306
			200 !== wp_remote_retrieve_response_code( $response ) ||
307
			'OK' !== trim( wp_remote_retrieve_body( $response ) )
308
		) {
309
			return $this->error(
310
				new Jetpack_Error(
311
					'invalid_nonce',
0 ignored issues
show
Unused Code introduced by
The call to Jetpack_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...
312
					__( 'There was an issue validating this request.', 'jetpack' ),
313
					400
314
				),
315
				'jpc_remote_register_fail'
316
			);
317
		}
318
319
		if ( ! Jetpack_Options::get_option( 'id' ) || ! Jetpack_Data::get_access_token() || ! empty( $request['force'] ) ) {
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...
320
			wp_set_current_user( $user->ID );
321
322
			// This code mostly copied from Jetpack::admin_page_load.
323
			Jetpack::maybe_set_version_option();
324
			$registered = Jetpack::try_registration();
325
			if ( is_wp_error( $registered ) ) {
326
				return $this->error( $registered, 'jpc_remote_register_fail' );
327
			} elseif ( ! $registered ) {
328
				return $this->error(
329
					new Jetpack_Error(
330
						'registration_error',
0 ignored issues
show
Unused Code introduced by
The call to Jetpack_Error::__construct() has too many arguments starting with 'registration_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...
331
						__( 'There was an unspecified error registering the site', 'jetpack' ),
332
						400
333
					),
334
					'jpc_remote_register_fail'
335
				);
336
			}
337
		}
338
339
		$this->tracking->record_user_event( 'jpc_remote_register_success' );
340
341
		return array(
342
			'client_id' => Jetpack_Options::get_option( 'id' ),
343
		);
344
	}
345
346
	/**
347
	 * This XML-RPC method is called from the /jpphp/provision endpoint on WPCOM in order to
348
	 * register this site so that a plan can be provisioned.
349
	 *
350
	 * @param array $request An array containing at minimum a nonce key and a local_username key.
351
	 *
352
	 * @return \WP_Error|array
353
	 */
354
	public function remote_provision( $request ) {
355
		$user = $this->fetch_and_verify_local_user( $request );
356
357
		if ( ! $user ) {
358
			return $this->error( new WP_Error( 'input_error', __( 'Valid user is required', 'jetpack' ), 400 ), 'jpc_remote_provision_fail' );
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with 'input_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...
359
		}
360
361
		if ( is_wp_error( $user ) || is_a( $user, 'IXR_Error' ) ) {
362
			return $this->error( $user, 'jpc_remote_provision_fail' );
363
		}
364
365
		$site_icon = get_site_icon_url();
366
367
		$auto_enable_sso = ( ! Jetpack::is_active() || Jetpack::is_module_active( 'sso' ) );
368
369
		/** This filter is documented in class.jetpack-cli.php */
370 View Code Duplication
		if ( apply_filters( 'jetpack_start_enable_sso', $auto_enable_sso ) ) {
371
			$redirect_uri = add_query_arg(
372
				array(
373
					'action'      => 'jetpack-sso',
374
					'redirect_to' => rawurlencode( admin_url() ),
375
				),
376
				wp_login_url() // TODO: come back to Jetpack dashboard?
377
			);
378
		} else {
379
			$redirect_uri = admin_url();
380
		}
381
382
		// Generate secrets.
383
		$roles   = new Roles();
384
		$role    = $roles->translate_user_to_role( $user );
385
		$secrets = Jetpack::init()->generate_secrets( 'authorize', $user->ID );
386
387
		$response = array(
388
			'jp_version'   => JETPACK__VERSION,
389
			'redirect_uri' => $redirect_uri,
390
			'user_id'      => $user->ID,
391
			'user_email'   => $user->user_email,
392
			'user_login'   => $user->user_login,
393
			'scope'        => Jetpack::sign_role( $role, $user->ID ),
394
			'secret'       => $secrets['secret_1'],
395
			'is_active'    => Jetpack::is_active(),
396
		);
397
398
		if ( $site_icon ) {
399
			$response['site_icon'] = $site_icon;
400
		}
401
402
		if ( ! empty( $request['onboarding'] ) ) {
403
			Jetpack::create_onboarding_token();
404
			$response['onboarding_token'] = Jetpack_Options::get_option( 'onboarding' );
405
		}
406
407
		return $response;
408
	}
409
410
	/**
411
	 * Given an array containing a local user identifier and a nonce, will attempt to fetch and set
412
	 * an access token for the given user.
413
	 *
414
	 * @param array       $request    An array containing local_user and nonce keys at minimum.
415
	 * @param \IXR_Client $ixr_client The client object, optional.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $ixr_client not be false|IXR_Client?

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...
416
	 * @return mixed
417
	 */
418
	public function remote_connect( $request, $ixr_client = false ) {
419
		if ( Jetpack::is_active() ) {
420
			return $this->error(
421
				new WP_Error(
422
					'already_connected',
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with 'already_connected'.

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...
423
					__( 'Jetpack is already connected.', 'jetpack' ),
424
					400
425
				),
426
				'jpc_remote_connect_fail'
427
			);
428
		}
429
430
		$user = $this->fetch_and_verify_local_user( $request );
431
432
		if ( ! $user || is_wp_error( $user ) || is_a( $user, 'IXR_Error' ) ) {
433
			return $this->error(
434
				new WP_Error(
435
					'input_error',
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with 'input_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...
436
					__( 'Valid user is required.', 'jetpack' ),
437
					400
438
				),
439
				'jpc_remote_connect_fail'
440
			);
441
		}
442
443
		if ( empty( $request['nonce'] ) ) {
444
			return $this->error(
445
				new WP_Error(
446
					'input_error',
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with 'input_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...
447
					__( 'A non-empty nonce must be supplied.', 'jetpack' ),
448
					400
449
				),
450
				'jpc_remote_connect_fail'
451
			);
452
		}
453
454
		if ( ! $ixr_client ) {
455
			Jetpack::load_xml_rpc_client();
456
			$ixr_client = new Jetpack_IXR_Client();
457
		}
458
		$ixr_client->query(
459
			'jetpack.getUserAccessToken',
460
			array(
461
				'nonce'            => sanitize_text_field( $request['nonce'] ),
462
				'external_user_id' => $user->ID,
463
			)
464
		);
465
466
		$token = $ixr_client->isError() ? false : $ixr_client->getResponse();
467
		if ( empty( $token ) ) {
468
			return $this->error(
469
				new WP_Error(
470
					'token_fetch_failed',
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with 'token_fetch_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...
471
					__( 'Failed to fetch user token from WordPress.com.', 'jetpack' ),
472
					400
473
				),
474
				'jpc_remote_connect_fail'
475
			);
476
		}
477
		$token = sanitize_text_field( $token );
478
479
		Jetpack::update_user_token( $user->ID, sprintf( '%s.%d', $token, $user->ID ), true );
480
481
		$this->do_post_authorization();
482
483
		return Jetpack::is_active();
484
	}
485
486
	/**
487
	 * Getter for the local user to act as.
488
	 *
489
	 * @param array $request the current request data.
490
	 */
491
	private function fetch_and_verify_local_user( $request ) {
492
		if ( empty( $request['local_user'] ) ) {
493
			return $this->error(
494
				new Jetpack_Error(
495
					'local_user_missing',
0 ignored issues
show
Unused Code introduced by
The call to Jetpack_Error::__construct() has too many arguments starting with 'local_user_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...
496
					__( 'The required "local_user" parameter is missing.', 'jetpack' ),
497
					400
498
				),
499
				'jpc_remote_provision_fail'
500
			);
501
		}
502
503
		// Local user is used to look up by login, email or ID.
504
		$local_user_info = $request['local_user'];
505
506
		return $this->get_user_by_anything( $local_user_info );
507
	}
508
509
	/**
510
	 * Gets the user object by its data.
511
	 *
512
	 * @param string $user_id can be any identifying user data.
513
	 */
514
	private function get_user_by_anything( $user_id ) {
515
		$user = get_user_by( 'login', $user_id );
516
517
		if ( ! $user ) {
518
			$user = get_user_by( 'email', $user_id );
519
		}
520
521
		if ( ! $user ) {
522
			$user = get_user_by( 'ID', $user_id );
523
		}
524
525
		return $user;
526
	}
527
528
	/**
529
	 * Track an error.
530
	 *
531
	 * @param string               $name  Event name.
532
	 * @param \WP_Error|\IXR_Error $error The error object.
533
	 * @param \WP_User             $user  The user object.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $user not be WP_User|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...
534
	 */
535
	private function tracks_record_error( $name, $error, $user = null ) {
536
		if ( is_wp_error( $error ) ) {
537
			$this->tracking->record_user_event(
538
				$name,
539
				array(
540
					'error_code'    => $error->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...
541
					'error_message' => $error->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...
542
				),
543
				$user
544
			);
545
		} elseif ( is_a( $error, '\\IXR_Error' ) ) {
546
			$this->tracking->record_user_event(
547
				$name,
548
				array(
549
					'error_code'    => $error->code,
550
					'error_message' => $error->message,
551
				),
552
				$user
553
			);
554
		}
555
556
		return $error;
557
	}
558
559
	/**
560
	 * Possible error_codes:
561
	 *
562
	 * - verify_secret_1_missing
563
	 * - verify_secret_1_malformed
564
	 * - verify_secrets_missing: verification secrets are not found in database
565
	 * - verify_secrets_incomplete: verification secrets are only partially found in database
566
	 * - verify_secrets_expired: verification secrets have expired
567
	 * - verify_secrets_mismatch: stored secret_1 does not match secret_1 sent by Jetpack.WordPress.com
568
	 * - state_missing: required parameter of state not found
569
	 * - state_malformed: state is not a digit
570
	 * - invalid_state: state in request does not match the stored state
571
	 *
572
	 * The 'authorize' and 'register' actions have additional error codes
573
	 *
574
	 * Possible values for action are `authorize`, `publicize` and `register`.
575
	 *
576
	 * state_missing: a state ( user id ) was not supplied
577
	 * state_malformed: state is not the correct data type
578
	 * invalid_state: supplied state does not match the stored state
579
	 *
580
	 * @param array $params action parameters.
581
	 * @return \WP_Error|string secret_2 on success, WP_Error( error_code => error_code, error_message => error description, error_data => status code ) on failure
582
	 */
583
	public function verify_action( $params ) {
584
		$action                    = $params[0];
585
		$verify_secret             = $params[1];
586
		$state                     = isset( $params[2] ) ? $params[2] : '';
587
		$user                      = get_user_by( 'id', $state );
588
		$tracks_failure_event_name = '';
589
590
		if ( 'authorize' === $action ) {
591
			$tracks_failure_event_name = 'jpc_verify_authorize_fail';
592
			$this->tracking->record_user_event( 'jpc_verify_authorize_begin', array(), $user );
593
		}
594
		if ( 'publicize' === $action ) {
595
			// This action is used on a response from a direct XML-RPC done from WordPress.com.
596
			$tracks_failure_event_name = 'jpc_verify_publicize_fail';
597
			$this->tracking->record_user_event( 'jpc_verify_publicize_begin', array(), $user );
598
		}
599
		if ( 'register' === $action ) {
600
			$tracks_failure_event_name = 'jpc_verify_register_fail';
601
			$this->tracking->record_user_event( 'jpc_verify_register_begin', array(), $user );
602
		}
603
604
		if ( empty( $verify_secret ) ) {
605
			return $this->error( new Jetpack_Error( 'verify_secret_1_missing', sprintf( 'The required "%s" parameter is missing.', 'secret_1' ), 400 ), $tracks_failure_event_name, $user );
0 ignored issues
show
Unused Code introduced by
The call to Jetpack_Error::__construct() has too many arguments starting with 'verify_secret_1_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...
606
		} elseif ( ! is_string( $verify_secret ) ) {
607
			return $this->error( new Jetpack_Error( 'verify_secret_1_malformed', sprintf( 'The required "%s" parameter is malformed.', 'secret_1' ), 400 ), $tracks_failure_event_name, $user );
0 ignored issues
show
Unused Code introduced by
The call to Jetpack_Error::__construct() has too many arguments starting with 'verify_secret_1_malformed'.

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...
608
		} elseif ( empty( $state ) ) {
609
			return $this->error( new Jetpack_Error( 'state_missing', sprintf( 'The required "%s" parameter is missing.', 'state' ), 400 ), $tracks_failure_event_name, $user );
0 ignored issues
show
Unused Code introduced by
The call to Jetpack_Error::__construct() has too many arguments starting with 'state_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...
610
		} elseif ( ! ctype_digit( $state ) ) {
611
			return $this->error( new Jetpack_Error( 'state_malformed', sprintf( 'The required "%s" parameter is malformed.', 'state' ), 400 ), $tracks_failure_event_name, $user );
0 ignored issues
show
Unused Code introduced by
The call to Jetpack_Error::__construct() has too many arguments starting with 'state_malformed'.

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...
612
		}
613
614
		$secrets = Jetpack::get_secrets( $action, $state );
615
616
		if ( ! $secrets ) {
617
			Jetpack::delete_secrets( $action, $state );
0 ignored issues
show
Deprecated Code introduced by
The method Jetpack::delete_secrets() 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...
618
			return $this->error( new Jetpack_Error( 'verify_secrets_missing', 'Verification secrets not found', 400 ), $tracks_failure_event_name, $user );
0 ignored issues
show
Unused Code introduced by
The call to Jetpack_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...
619
		}
620
621
		if ( is_wp_error( $secrets ) ) {
622
			Jetpack::delete_secrets( $action, $state );
0 ignored issues
show
Deprecated Code introduced by
The method Jetpack::delete_secrets() 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...
623
			return $this->error( new Jetpack_Error( $secrets->get_error_code(), $secrets->get_error_message(), 400 ), $tracks_failure_event_name, $user );
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...
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...
Unused Code introduced by
The call to Jetpack_Error::__construct() has too many arguments starting with $secrets->get_error_code().

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...
624
		}
625
626
		if ( empty( $secrets['secret_1'] ) || empty( $secrets['secret_2'] ) || empty( $secrets['exp'] ) ) {
627
			Jetpack::delete_secrets( $action, $state );
0 ignored issues
show
Deprecated Code introduced by
The method Jetpack::delete_secrets() 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...
628
			return $this->error( new Jetpack_Error( 'verify_secrets_incomplete', 'Verification secrets are incomplete', 400 ), $tracks_failure_event_name, $user );
0 ignored issues
show
Unused Code introduced by
The call to Jetpack_Error::__construct() has too many arguments starting with 'verify_secrets_incomplete'.

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...
629
		}
630
631
		if ( ! hash_equals( $verify_secret, $secrets['secret_1'] ) ) {
632
			Jetpack::delete_secrets( $action, $state );
0 ignored issues
show
Deprecated Code introduced by
The method Jetpack::delete_secrets() 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...
633
			return $this->error( new Jetpack_Error( 'verify_secrets_mismatch', 'Secret mismatch', 400 ), $tracks_failure_event_name, $user );
0 ignored issues
show
Unused Code introduced by
The call to Jetpack_Error::__construct() has too many arguments starting with 'verify_secrets_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...
634
		}
635
636
		Jetpack::delete_secrets( $action, $state );
0 ignored issues
show
Deprecated Code introduced by
The method Jetpack::delete_secrets() 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...
637
638
		if ( 'authorize' === $action ) {
639
			$this->tracking->record_user_event( 'jpc_verify_authorize_success', array(), $user );
640
		}
641
		if ( 'publicize' === $action ) {
642
			$this->tracking->record_user_event( 'jpc_verify_publicize_success', array(), $user );
643
		}
644
		if ( 'register' === $action ) {
645
			$this->tracking->record_user_event( 'jpc_verify_register_success', array(), $user );
646
		}
647
648
		return $secrets['secret_2'];
649
	}
650
651
	/**
652
	 * Wrapper for wp_authenticate( $username, $password );
653
	 *
654
	 * @return \WP_User|bool
655
	 */
656
	public function login() {
657
		Jetpack::connection()->require_jetpack_authentication();
658
		$user = wp_authenticate( 'username', 'password' );
659
		if ( is_wp_error( $user ) ) {
660
			if ( 'authentication_failed' === $user->get_error_code() ) { // Generic error could mean most anything.
661
				$this->error = new Jetpack_Error( 'invalid_request', 'Invalid Request', 403 );
0 ignored issues
show
Unused Code introduced by
The call to Jetpack_Error::__construct() has too many arguments starting with '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...
662
			} else {
663
				$this->error = $user;
664
			}
665
			return false;
666
		} elseif ( ! $user ) { // Shouldn't happen.
667
			$this->error = new Jetpack_Error( 'invalid_request', 'Invalid Request', 403 );
0 ignored issues
show
Unused Code introduced by
The call to Jetpack_Error::__construct() has too many arguments starting with '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...
668
			return false;
669
		}
670
671
		return $user;
672
	}
673
674
	/**
675
	 * Returns the current error as an \IXR_Error
676
	 *
677
	 * @param \WP_Error|\IXR_Error $error             The error object, optional.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $error not be WP_Error|IXR_Error|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...
678
	 * @param string               $tracks_event_name The event name.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $tracks_event_name 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...
679
	 * @param \WP_User             $user              The user object.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $user not be WP_User|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...
680
	 * @return bool|\IXR_Error
681
	 */
682
	public function error( $error = null, $tracks_event_name = null, $user = null ) {
683
		// Record using Tracks.
684
		if ( null !== $tracks_event_name ) {
685
			$this->tracks_record_error( $tracks_event_name, $error, $user );
0 ignored issues
show
Bug introduced by
It seems like $error defined by parameter $error on line 682 can also be of type null; however, Jetpack_XMLRPC_Server::tracks_record_error() does only seem to accept object<WP_Error>|object<IXR_Error>, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
686
		}
687
688
		if ( ! is_null( $error ) ) {
689
			$this->error = $error;
0 ignored issues
show
Documentation Bug introduced by
It seems like $error can also be of type object<IXR_Error>. However, the property $error is declared as type object<WP_Error>. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
690
		}
691
692
		if ( is_wp_error( $this->error ) ) {
693
			$code = $this->error->get_error_data();
0 ignored issues
show
Bug introduced by
The method get_error_data() 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...
694
			if ( ! $code ) {
695
				$code = -10520;
696
			}
697
			$message = sprintf( 'Jetpack: [%s] %s', $this->error->get_error_code(), $this->error->get_error_message() );
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...
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...
698
			return new \IXR_Error( $code, $message );
699
		} elseif ( is_a( $this->error, 'IXR_Error' ) ) {
700
			return $this->error;
701
		}
702
703
		return false;
704
	}
705
706
	/* API Methods */
707
708
	/**
709
	 * Just authenticates with the given Jetpack credentials.
710
	 *
711
	 * @return string The current Jetpack version number
712
	 */
713
	public function test_connection() {
714
		return JETPACK__VERSION;
715
	}
716
717
	/**
718
	 * Test the API user code.
719
	 *
720
	 * @param array $args arguments identifying the test site.
721
	 */
722
	public function test_api_user_code( $args ) {
723
		$client_id = (int) $args[0];
724
		$user_id   = (int) $args[1];
725
		$nonce     = (string) $args[2];
726
		$verify    = (string) $args[3];
727
728
		if ( ! $client_id || ! $user_id || ! strlen( $nonce ) || 32 !== strlen( $verify ) ) {
729
			return false;
730
		}
731
732
		$user = get_user_by( 'id', $user_id );
733
		if ( ! $user || is_wp_error( $user ) ) {
734
			return false;
735
		}
736
737
		/* phpcs:ignore
738
		 debugging
739
		error_log( "CLIENT: $client_id" );
740
		error_log( "USER:   $user_id" );
741
		error_log( "NONCE:  $nonce" );
742
		error_log( "VERIFY: $verify" );
743
		*/
744
745
		$jetpack_token = Jetpack_Data::get_access_token( $user_id );
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...
746
747
		$api_user_code = get_user_meta( $user_id, "jetpack_json_api_$client_id", true );
748
		if ( ! $api_user_code ) {
749
			return false;
750
		}
751
752
		$hmac = hash_hmac(
753
			'md5',
754
			json_encode( // phpcs:ignore WordPress.WP.AlternativeFunctions.json_encode_json_encode
755
				(object) array(
756
					'client_id' => (int) $client_id,
757
					'user_id'   => (int) $user_id,
758
					'nonce'     => (string) $nonce,
759
					'code'      => (string) $api_user_code,
760
				)
761
			),
762
			$jetpack_token->secret
763
		);
764
765
		if ( ! hash_equals( $hmac, $verify ) ) {
766
			return false;
767
		}
768
769
		return $user_id;
770
	}
771
772
	/**
773
	 * Disconnect this blog from the connected wordpress.com account
774
	 *
775
	 * @return boolean
776
	 */
777
	public function disconnect_blog() {
778
779
		// For tracking.
780
		if ( ! empty( $this->user->ID ) ) {
781
			wp_set_current_user( $this->user->ID );
782
		}
783
784
		Jetpack::log( 'disconnect' );
785
		Jetpack::disconnect();
786
787
		return true;
788
	}
789
790
	/**
791
	 * Unlink a user from WordPress.com
792
	 *
793
	 * This will fail if called by the Master User.
794
	 */
795
	public function unlink_user() {
796
		Jetpack::log( 'unlink' );
797
		return Jetpack::unlink_user();
798
	}
799
800
	/**
801
	 * Returns any object that is able to be synced.
802
	 *
803
	 * @param array $args the synchronized object parameters.
804
	 */
805
	public function sync_object( $args ) {
806
		// For example: posts, post, 5.
807
		list( $module_name, $object_type, $id ) = $args;
808
809
		$sync_module = Modules::get_module( $module_name );
810
		$codec       = Sender::get_instance()->get_codec();
811
812
		return $codec->encode( $sync_module->get_object_by_id( $object_type, $id ) );
813
	}
814
815
	/**
816
	 * Returns the home URL and site URL for the current site which can be used on the WPCOM side for
817
	 * IDC mitigation to decide whether sync should be allowed if the home and siteurl values differ between WPCOM
818
	 * and the remote Jetpack site.
819
	 *
820
	 * @return array
821
	 */
822
	public function validate_urls_for_idc_mitigation() {
823
		return array(
824
			'home'    => Functions::home_url(),
825
			'siteurl' => Functions::site_url(),
826
		);
827
	}
828
829
	/**
830
	 * Returns what features are available. Uses the slug of the module files.
831
	 *
832
	 * @return array
833
	 */
834 View Code Duplication
	public function features_available() {
835
		$raw_modules = Jetpack::get_available_modules();
836
		$modules     = array();
837
		foreach ( $raw_modules as $module ) {
838
			$modules[] = Jetpack::get_module_slug( $module );
839
		}
840
841
		return $modules;
842
	}
843
844
	/**
845
	 * Returns what features are enabled. Uses the slug of the modules files.
846
	 *
847
	 * @return array
848
	 */
849 View Code Duplication
	public function features_enabled() {
850
		$raw_modules = Jetpack::get_active_modules();
851
		$modules     = array();
852
		foreach ( $raw_modules as $module ) {
853
			$modules[] = Jetpack::get_module_slug( $module );
854
		}
855
856
		return $modules;
857
	}
858
859
	/**
860
	 * Updates the attachment parent object.
861
	 *
862
	 * @param array $args attachment and parent identifiers.
863
	 */
864
	public function update_attachment_parent( $args ) {
865
		$attachment_id = (int) $args[0];
866
		$parent_id     = (int) $args[1];
867
868
		return wp_update_post(
869
			array(
870
				'ID'          => $attachment_id,
871
				'post_parent' => $parent_id,
872
			)
873
		);
874
	}
875
876
	/**
877
	 * Serve a JSON API request.
878
	 *
879
	 * @param array $args request arguments.
880
	 */
881
	public function json_api( $args = array() ) {
882
		$json_api_args        = $args[0];
883
		$verify_api_user_args = $args[1];
884
885
		$method       = (string) $json_api_args[0];
886
		$url          = (string) $json_api_args[1];
887
		$post_body    = is_null( $json_api_args[2] ) ? null : (string) $json_api_args[2];
888
		$user_details = (array) $json_api_args[4];
889
		$locale       = (string) $json_api_args[5];
890
891
		if ( ! $verify_api_user_args ) {
892
			$user_id = 0;
893
		} elseif ( 'internal' === $verify_api_user_args[0] ) {
894
			$user_id = (int) $verify_api_user_args[1];
895
			if ( $user_id ) {
896
				$user = get_user_by( 'id', $user_id );
897
				if ( ! $user || is_wp_error( $user ) ) {
898
					return false;
899
				}
900
			}
901
		} else {
902
			$user_id = call_user_func( array( $this, 'test_api_user_code' ), $verify_api_user_args );
903
			if ( ! $user_id ) {
904
				return false;
905
			}
906
		}
907
908
		/* phpcs:ignore
909
		 debugging
910
		error_log( "-- begin json api via jetpack debugging -- " );
911
		error_log( "METHOD: $method" );
912
		error_log( "URL: $url" );
913
		error_log( "POST BODY: $post_body" );
914
		error_log( "VERIFY_ARGS: " . print_r( $verify_api_user_args, 1 ) );
915
		error_log( "VERIFIED USER_ID: " . (int) $user_id );
916
		error_log( "-- end json api via jetpack debugging -- " );
917
		*/
918
919
		if ( 'en' !== $locale ) {
920
			// .org mo files are named slightly different from .com, and all we have is this the locale -- try to guess them.
921
			$new_locale = $locale;
922
			if ( strpos( $locale, '-' ) !== false ) {
923
				$locale_pieces = explode( '-', $locale );
924
				$new_locale    = $locale_pieces[0];
925
				$new_locale   .= ( ! empty( $locale_pieces[1] ) ) ? '_' . strtoupper( $locale_pieces[1] ) : '';
926
			} else {
927
				// .com might pass 'fr' because thats what our language files are named as, where core seems
928
				// to do fr_FR - so try that if we don't think we can load the file.
929
				if ( ! file_exists( WP_LANG_DIR . '/' . $locale . '.mo' ) ) {
930
					$new_locale = $locale . '_' . strtoupper( $locale );
931
				}
932
			}
933
934
			if ( file_exists( WP_LANG_DIR . '/' . $new_locale . '.mo' ) ) {
935
				unload_textdomain( 'default' );
936
				load_textdomain( 'default', WP_LANG_DIR . '/' . $new_locale . '.mo' );
937
			}
938
		}
939
940
		$old_user = wp_get_current_user();
941
		wp_set_current_user( $user_id );
942
943
		if ( $user_id ) {
944
			$token_key = false;
945
		} else {
946
			$verified  = Jetpack::connection()->verify_xml_rpc_signature();
947
			$token_key = $verified['token_key'];
948
		}
949
950
		$token = Jetpack_Data::get_access_token( $user_id, $token_key );
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...
951
		if ( ! $token || is_wp_error( $token ) ) {
952
			return false;
953
		}
954
955
		define( 'REST_API_REQUEST', true );
956
		define( 'WPCOM_JSON_API__BASE', 'public-api.wordpress.com/rest/v1' );
957
958
		// needed?
959
		require_once ABSPATH . 'wp-admin/includes/admin.php';
960
961
		require_once JETPACK__PLUGIN_DIR . 'class.json-api.php';
962
		$api                        = WPCOM_JSON_API::init( $method, $url, $post_body );
963
		$api->token_details['user'] = $user_details;
964
		require_once JETPACK__PLUGIN_DIR . 'class.json-api-endpoints.php';
965
966
		$display_errors = ini_set( 'display_errors', 0 ); // phpcs:ignore WordPress.PHP.IniSet
967
		ob_start();
968
		$api->serve( false );
969
		$output = ob_get_clean();
970
		ini_set( 'display_errors', $display_errors ); // phpcs:ignore WordPress.PHP.IniSet
971
972
		$nonce = wp_generate_password( 10, false );
973
		$hmac  = hash_hmac( 'md5', $nonce . $output, $token->secret );
974
975
		wp_set_current_user( isset( $old_user->ID ) ? $old_user->ID : 0 );
976
977
		return array(
978
			(string) $output,
979
			(string) $nonce,
980
			(string) $hmac,
981
		);
982
	}
983
984
	/**
985
	 * Handles authorization actions after connecting a site, such as enabling modules.
986
	 *
987
	 * This do_post_authorization() is used in this class, as opposed to calling
988
	 * Jetpack::handle_post_authorization_actions() directly so that we can mock this method as necessary.
989
	 *
990
	 * @return void
991
	 */
992
	public function do_post_authorization() {
993
		/** This filter is documented in class.jetpack-cli.php */
994
		$enable_sso = apply_filters( 'jetpack_start_enable_sso', true );
995
		Jetpack::handle_post_authorization_actions( $enable_sso, false, false );
996
	}
997
}
998