Completed
Push — master-stable ( a82972...9baba8 )
by
unknown
27:19 queued 17:47
created

class.jetpack-client-server.php (3 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
/**
4
 * Client = Plugin
5
 * Client Server = API Methods the Plugin must respond to
6
 */
7
class Jetpack_Client_Server {
8
9
	/**
10
	 * Authorizations
11
	 */
12
	function client_authorize() {
13
		$data              = stripslashes_deep( $_GET );
14
		$data['auth_type'] = 'client';
15
		$role              = Jetpack::translate_current_user_to_role();
16
		$redirect          = isset( $data['redirect'] ) ? esc_url_raw( (string) $data['redirect'] ) : '';
17
18
		check_admin_referer( "jetpack-authorize_{$role}_{$redirect}" );
19
20
		$result = $this->authorize( $data );
21
		if ( is_wp_error( $result ) ) {
22
			Jetpack::state( 'error', $result->get_error_code() );
23
		}
24
25
		if ( wp_validate_redirect( $redirect ) ) {
26
			wp_safe_redirect( $redirect );
27
		} else {
28
			wp_safe_redirect( Jetpack::admin_url() );
29
		}
30
31
		/**
32
		 * Fires after the Jetpack client is authorized to communicate with WordPress.com.
33
		 *
34
		 * @since 4.2.0
35
		 *
36
		 * @param int Jetpack Blog ID.
37
		 */
38
		do_action( 'jetpack_client_authorized', Jetpack_Options::get_option( 'id' ) );
39
40
		$this->do_exit();
41
	}
42
43
	function authorize( $data = array() ) {
44
		$redirect = isset( $data['redirect'] ) ? esc_url_raw( (string) $data['redirect'] ) : '';
45
46
		$jetpack_unique_connection = Jetpack_Options::get_option( 'unique_connection' );
47
		// Checking if site has been active/connected previously before recording unique connection
48
		if ( ! $jetpack_unique_connection ) {
49
			// jetpack_unique_connection option has never been set
50
			$jetpack_unique_connection = array(
51
				'connected'     => 0,
52
				'disconnected'  => 0,
53
				'version'       => '3.6.1',
54
			);
55
56
			update_option( 'jetpack_unique_connection', $jetpack_unique_connection );
57
58
			//track unique connection
59
			$jetpack = $this->get_jetpack();;
0 ignored issues
show
It is generally recommended to place each PHP statement on a line by itself.

Let’s take a look at an example:

// Bad
$a = 5; $b = 6; $c = 7;

// Good
$a = 5;
$b = 6;
$c = 7;
Loading history...
60
61
			$jetpack->stat( 'connections', 'unique-connection' );
62
			$jetpack->do_stats( 'server_side' );
63
		}
64
65
		// increment number of times connected
66
		$jetpack_unique_connection['connected'] += 1;
67
		Jetpack_Options::update_option( 'unique_connection', $jetpack_unique_connection );
68
69
		$role = Jetpack::translate_current_user_to_role();
70
71
		if ( ! $role ) {
72
			return new Jetpack_Error( 'no_role', 'Invalid request.', 400 );
73
		}
74
75
		$cap = Jetpack::translate_role_to_cap( $role );
76
		if ( ! $cap ) {
77
			return new Jetpack_Error( 'no_cap', 'Invalid request.', 400 );
78
		}
79
80
		if ( ! empty( $data['error'] ) ) {
81
			return new Jetpack_Error( $data['error'], 'Error included in the request.', 400 );
82
		}
83
84
		if ( ! isset( $data['state'] ) ) {
85
			return new Jetpack_Error( 'no_state', 'Request must include state.', 400 );
86
		}
87
88
		if ( ! ctype_digit( $data['state'] ) ) {
89
			return new Jetpack_Error( $data['error'], 'State must be an integer.', 400 );
90
		}
91
92
		$current_user_id = get_current_user_id();
93
		if ( $current_user_id != $data['state'] ) {
94
			return new Jetpack_Error( 'wrong_state', 'State does not match current user.', 400 );
95
		}
96
97
		if ( empty( $data['code'] ) ) {
98
			return new Jetpack_Error( 'no_code', 'Request must include an authorization code.', 400 );
99
		}
100
101
		$token = $this->get_token( $data );
102
103
		if ( is_wp_error( $token ) ) {
104
			$code = $token->get_error_code();
105
			if ( empty( $code ) ) {
106
				$code = 'invalid_token';
107
			}
108
			return new Jetpack_Error( $code, $token->get_error_message(), 400 );
109
		}
110
111
		if ( ! $token ) {
112
			return new Jetpack_Error( 'no_token', 'Error generating token.', 400 );
113
		}
114
115
		$is_master_user = ! Jetpack::is_active();
116
117
		Jetpack::update_user_token( $current_user_id, sprintf( '%s.%d', $token, $current_user_id ), $is_master_user );
118
119
		if ( ! $is_master_user ) {
120
			Jetpack::state( 'message', 'linked' );
121
			// Don't activate anything since we are just connecting a user.
122
			return 'linked';
123
		}
124
125
		$redirect_on_activation_error = ( 'client' === $data['auth_type'] ) ? true : false;
126
		if ( $active_modules = Jetpack_Options::get_option( 'active_modules' ) ) {
127
			Jetpack::delete_active_modules();
128
129
			Jetpack::activate_default_modules( 999, 1, $active_modules, $redirect_on_activation_error );
0 ignored issues
show
999 is of type integer, but the function expects a boolean.

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

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

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

function acceptsInteger($int) { }

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

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
130
		} else {
131
			Jetpack::activate_default_modules( false, false, array(), $redirect_on_activation_error );
132
		}
133
134
		// If redirect_uri is SSO, ensure SSO module is enabled
135
		parse_str( parse_url( $data['redirect_uri'], PHP_URL_QUERY ), $redirect_options );
136
		if ( isset( $redirect_options['action'] ) && 'jetpack-sso' === $redirect_options['action'] ) {
137
			Jetpack::activate_module( 'sso', false, false );
138
		}
139
140
		// Since this is a fresh connection, be sure to clear out IDC options
141
		Jetpack_IDC::clear_all_idc_options();
142
143
		// Start nonce cleaner
144
		wp_clear_scheduled_hook( 'jetpack_clean_nonces' );
145
		wp_schedule_event( time(), 'hourly', 'jetpack_clean_nonces' );
146
147
		Jetpack::state( 'message', 'authorized' );
148
		return 'authorized';
149
	}
150
151
	public static function deactivate_plugin( $probable_file, $probable_title ) {
152
		include_once( ABSPATH . 'wp-admin/includes/plugin.php' );
153
		if ( is_plugin_active( $probable_file ) ) {
154
			deactivate_plugins( $probable_file );
155
			return 1;
156
		} else {
157
			// If the plugin is not in the usual place, try looking through all active plugins.
158
			$active_plugins = Jetpack::get_active_plugins();
159
			foreach ( $active_plugins as $plugin ) {
160
				$data = get_plugin_data( WP_PLUGIN_DIR . '/' . $plugin );
161
				if ( $data['Name'] == $probable_title ) {
162
					deactivate_plugins( $plugin );
163
					return 1;
164
				}
165
			}
166
		}
167
168
		return 0;
169
	}
170
171
	/**
172
	 * @return object|WP_Error
173
	 */
174
	function get_token( $data ) {
175
		$role = Jetpack::translate_current_user_to_role();
176
177
		if ( ! $role ) {
178
			return new Jetpack_Error( 'role', __( 'An administrator for this blog must set up the Jetpack connection.', 'jetpack' ) );
179
		}
180
181
		$client_secret = Jetpack_Data::get_access_token();
182
		if ( ! $client_secret ) {
183
			return new Jetpack_Error( 'client_secret', __( 'You need to register your Jetpack before connecting it.', 'jetpack' ) );
184
		}
185
186
		$redirect = isset( $data['redirect'] ) ? esc_url_raw( (string) $data['redirect'] ) : '';
187
		$redirect_uri = ( 'calypso' === $data['auth_type'] )
188
			? $data['redirect_uri']
189
			: add_query_arg( array(
190
				'action' => 'authorize',
191
				'_wpnonce' => wp_create_nonce( "jetpack-authorize_{$role}_{$redirect}" ),
192
				'redirect' => $redirect ? urlencode( $redirect ) : false,
193
			), menu_page_url( 'jetpack', false ) );
194
195
		$body = array(
196
			'client_id' => Jetpack_Options::get_option( 'id' ),
197
			'client_secret' => $client_secret->secret,
198
			'grant_type' => 'authorization_code',
199
			'code' => $data['code'],
200
			'redirect_uri' => $redirect_uri,
201
		);
202
203
		$args = array(
204
			'method' => 'POST',
205
			'body' => $body,
206
			'headers' => array(
207
				'Accept' => 'application/json',
208
			),
209
		);
210
		$response = Jetpack_Client::_wp_remote_request( Jetpack::fix_url_for_bad_hosts( Jetpack::api_url( 'token' ) ), $args );
211
212
		if ( is_wp_error( $response ) ) {
213
			return new Jetpack_Error( 'token_http_request_failed', $response->get_error_message() );
214
		}
215
216
		$code = wp_remote_retrieve_response_code( $response );
217
		$entity = wp_remote_retrieve_body( $response );
218
219
		if ( $entity ) {
220
			$json = json_decode( $entity );
221
		} else {
222
			$json = false;
223
		}
224
225
		if ( 200 != $code || ! empty( $json->error ) ) {
226
			if ( empty( $json->error ) ) {
227
				return new Jetpack_Error( 'unknown', '', $code );
228
			}
229
230
			$error_description = isset( $json->error_description ) ? sprintf( __( 'Error Details: %s', 'jetpack' ), (string) $json->error_description ) : '';
231
232
			return new Jetpack_Error( (string) $json->error, $error_description, $code );
233
		}
234
235
		if ( empty( $json->access_token ) || ! is_scalar( $json->access_token ) ) {
236
			return new Jetpack_Error( 'access_token', '', $code );
237
		}
238
239
		if ( empty( $json->token_type ) || 'X_JETPACK' != strtoupper( $json->token_type ) ) {
240
			return new Jetpack_Error( 'token_type', '', $code );
241
		}
242
243
		if ( empty( $json->scope ) ) {
244
			return new Jetpack_Error( 'scope', 'No Scope', $code );
245
		}
246
247
		@list( $role, $hmac ) = explode( ':', $json->scope );
248
		if ( empty( $role ) || empty( $hmac ) ) {
249
			return new Jetpack_Error( 'scope', 'Malformed Scope', $code );
250
		}
251
252
		if ( Jetpack::sign_role( $role ) !== $json->scope ) {
253
			return new Jetpack_Error( 'scope', 'Invalid Scope', $code );
254
		}
255
256
		if ( ! $cap = Jetpack::translate_role_to_cap( $role ) ) {
257
			return new Jetpack_Error( 'scope', 'No Cap', $code );
258
		}
259
260
		if ( ! current_user_can( $cap ) ) {
261
			return new Jetpack_Error( 'scope', 'current_user_cannot', $code );
262
		}
263
264
		/**
265
		 * Fires after user has successfully received an auth token.
266
		 *
267
		 * @since 3.9.0
268
		 */
269
		do_action( 'jetpack_user_authorized' );
270
271
		return (string) $json->access_token;
272
	}
273
274
	public function get_jetpack() {
275
		return Jetpack::init();
276
	}
277
278
	public function do_exit() {
279
		exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The method do_exit() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
280
	}
281
}
282