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

class.jetpack-client-server.php (2 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
			JetpackTracking::record_user_event( 'jpc_client_authorize_fail', array(
24
				'error_code' => $result->get_error_code(),
25
				'error_message' => $result->get_error_message()
26
			) );
27
		} else {
28
			/**
29
			 * Fires after the Jetpack client is authorized to communicate with WordPress.com.
30
			 *
31
			 * @since 4.2.0
32
			 *
33
			 * @param int Jetpack Blog ID.
34
			 */
35
			do_action( 'jetpack_client_authorized', Jetpack_Options::get_option( 'id' ) );
36
		}
37
38
		if ( wp_validate_redirect( $redirect ) ) {
39
			// Exit happens below in $this->do_exit()
40
			wp_safe_redirect( $redirect );
41
		} else {
42
			// Exit happens below in $this->do_exit()
43
			wp_safe_redirect( Jetpack::admin_url() );
44
		}
45
46
		JetpackTracking::record_user_event( 'jpc_client_authorize_success' );
47
48
		$this->do_exit();
49
	}
50
51
	function authorize( $data = array() ) {
52
		$redirect = isset( $data['redirect'] ) ? esc_url_raw( (string) $data['redirect'] ) : '';
53
54
		$jetpack_unique_connection = Jetpack_Options::get_option( 'unique_connection' );
55
		// Checking if site has been active/connected previously before recording unique connection
56
		if ( ! $jetpack_unique_connection ) {
57
			// jetpack_unique_connection option has never been set
58
			$jetpack_unique_connection = array(
59
				'connected'     => 0,
60
				'disconnected'  => 0,
61
				'version'       => '3.6.1',
62
			);
63
64
			update_option( 'jetpack_unique_connection', $jetpack_unique_connection );
65
66
			//track unique connection
67
			$jetpack = $this->get_jetpack();
68
69
			$jetpack->stat( 'connections', 'unique-connection' );
70
			$jetpack->do_stats( 'server_side' );
71
		}
72
73
		// increment number of times connected
74
		$jetpack_unique_connection['connected'] += 1;
75
		Jetpack_Options::update_option( 'unique_connection', $jetpack_unique_connection );
76
77
		$role = Jetpack::translate_current_user_to_role();
78
79
		if ( ! $role ) {
80
			return new Jetpack_Error( 'no_role', 'Invalid request.', 400 );
81
		}
82
83
		$cap = Jetpack::translate_role_to_cap( $role );
84
		if ( ! $cap ) {
85
			return new Jetpack_Error( 'no_cap', 'Invalid request.', 400 );
86
		}
87
88
		if ( ! empty( $data['error'] ) ) {
89
			return new Jetpack_Error( $data['error'], 'Error included in the request.', 400 );
90
		}
91
92
		if ( ! isset( $data['state'] ) ) {
93
			return new Jetpack_Error( 'no_state', 'Request must include state.', 400 );
94
		}
95
96
		if ( ! ctype_digit( $data['state'] ) ) {
97
			return new Jetpack_Error( $data['error'], 'State must be an integer.', 400 );
98
		}
99
100
		$current_user_id = get_current_user_id();
101
		if ( $current_user_id != $data['state'] ) {
102
			return new Jetpack_Error( 'wrong_state', 'State does not match current user.', 400 );
103
		}
104
105
		if ( empty( $data['code'] ) ) {
106
			return new Jetpack_Error( 'no_code', 'Request must include an authorization code.', 400 );
107
		}
108
109
		$token = $this->get_token( $data );
110
111
		if ( is_wp_error( $token ) ) {
112
			$code = $token->get_error_code();
113
			if ( empty( $code ) ) {
114
				$code = 'invalid_token';
115
			}
116
			return new Jetpack_Error( $code, $token->get_error_message(), 400 );
117
		}
118
119
		if ( ! $token ) {
120
			return new Jetpack_Error( 'no_token', 'Error generating token.', 400 );
121
		}
122
123
		$is_master_user = ! Jetpack::is_active();
124
125
		Jetpack::update_user_token( $current_user_id, sprintf( '%s.%d', $token, $current_user_id ), $is_master_user );
126
127
		if ( ! $is_master_user ) {
128
			Jetpack::state( 'message', 'linked' );
129
			// Don't activate anything since we are just connecting a user.
130
			return 'linked';
131
		}
132
133
		// If redirect_uri is SSO, ensure SSO module is enabled
134
		parse_str( parse_url( $data['redirect_uri'], PHP_URL_QUERY ), $redirect_options );
135
136
		/** This filter is documented in class.jetpack-cli.php */
137
		$jetpack_start_enable_sso = apply_filters( 'jetpack_start_enable_sso', true );
138
139
		$activate_sso = (
140
			isset( $redirect_options['action'] ) &&
141
			'jetpack-sso' === $redirect_options['action'] &&
142
			$jetpack_start_enable_sso
143
		);
144
		$other_modules = $activate_sso
145
			? array( 'sso' )
146
			: array();
147
148
		$redirect_on_activation_error = ( 'client' === $data['auth_type'] ) ? true : false;
149 View Code Duplication
		if ( $active_modules = Jetpack_Options::get_option( 'active_modules' ) ) {
150
			Jetpack::delete_active_modules();
151
152
			Jetpack::activate_default_modules( 999, 1, array_merge( $active_modules, $other_modules ), $redirect_on_activation_error, false );
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...
153
		} else {
154
			Jetpack::activate_default_modules( false, false, $other_modules, $redirect_on_activation_error, false );
155
		}
156
157
		// Since this is a fresh connection, be sure to clear out IDC options
158
		Jetpack_IDC::clear_all_idc_options();
159
		Jetpack_Options::delete_raw_option( 'jetpack_last_connect_url_check' );
160
161
		// Start nonce cleaner
162
		wp_clear_scheduled_hook( 'jetpack_clean_nonces' );
163
		wp_schedule_event( time(), 'hourly', 'jetpack_clean_nonces' );
164
165
		Jetpack::state( 'message', 'authorized' );
166
		return 'authorized';
167
	}
168
169
	public static function deactivate_plugin( $probable_file, $probable_title ) {
170
		include_once( ABSPATH . 'wp-admin/includes/plugin.php' );
171
		if ( is_plugin_active( $probable_file ) ) {
172
			deactivate_plugins( $probable_file );
173
			return 1;
174
		} else {
175
			// If the plugin is not in the usual place, try looking through all active plugins.
176
			$active_plugins = Jetpack::get_active_plugins();
177
			foreach ( $active_plugins as $plugin ) {
178
				$data = get_plugin_data( WP_PLUGIN_DIR . '/' . $plugin );
179
				if ( $data['Name'] == $probable_title ) {
180
					deactivate_plugins( $plugin );
181
					return 1;
182
				}
183
			}
184
		}
185
186
		return 0;
187
	}
188
189
	/**
190
	 * @return object|WP_Error
191
	 */
192
	function get_token( $data ) {
193
		$role = Jetpack::translate_current_user_to_role();
194
195
		if ( ! $role ) {
196
			return new Jetpack_Error( 'role', __( 'An administrator for this blog must set up the Jetpack connection.', 'jetpack' ) );
197
		}
198
199
		$client_secret = Jetpack_Data::get_access_token();
200
		if ( ! $client_secret ) {
201
			return new Jetpack_Error( 'client_secret', __( 'You need to register your Jetpack before connecting it.', 'jetpack' ) );
202
		}
203
204
		$redirect = isset( $data['redirect'] ) ? esc_url_raw( (string) $data['redirect'] ) : '';
205
		$redirect_uri = ( 'calypso' === $data['auth_type'] )
206
			? $data['redirect_uri']
207
			: add_query_arg( array(
208
				'action' => 'authorize',
209
				'_wpnonce' => wp_create_nonce( "jetpack-authorize_{$role}_{$redirect}" ),
210
				'redirect' => $redirect ? urlencode( $redirect ) : false,
211
			), menu_page_url( 'jetpack', false ) );
212
213
		// inject identity for analytics
214
		$tracks_identity = jetpack_tracks_get_identity( get_current_user_id() );
215
216
		$body = array(
217
			'client_id' => Jetpack_Options::get_option( 'id' ),
218
			'client_secret' => $client_secret->secret,
219
			'grant_type' => 'authorization_code',
220
			'code' => $data['code'],
221
			'redirect_uri' => $redirect_uri,
222
			'_ui' => $tracks_identity['_ui'],
223
			'_ut' => $tracks_identity['_ut'],
224
		);
225
226
		$args = array(
227
			'method' => 'POST',
228
			'body' => $body,
229
			'headers' => array(
230
				'Accept' => 'application/json',
231
			),
232
		);
233
		$response = Jetpack_Client::_wp_remote_request( Jetpack::fix_url_for_bad_hosts( Jetpack::api_url( 'token' ) ), $args );
234
235
		if ( is_wp_error( $response ) ) {
236
			return new Jetpack_Error( 'token_http_request_failed', $response->get_error_message() );
237
		}
238
239
		$code = wp_remote_retrieve_response_code( $response );
240
		$entity = wp_remote_retrieve_body( $response );
241
242
		if ( $entity ) {
243
			$json = json_decode( $entity );
244
		} else {
245
			$json = false;
246
		}
247
248
		if ( 200 != $code || ! empty( $json->error ) ) {
249
			if ( empty( $json->error ) ) {
250
				return new Jetpack_Error( 'unknown', '', $code );
251
			}
252
253
			$error_description = isset( $json->error_description ) ? sprintf( __( 'Error Details: %s', 'jetpack' ), (string) $json->error_description ) : '';
254
255
			return new Jetpack_Error( (string) $json->error, $error_description, $code );
256
		}
257
258
		if ( empty( $json->access_token ) || ! is_scalar( $json->access_token ) ) {
259
			return new Jetpack_Error( 'access_token', '', $code );
260
		}
261
262
		if ( empty( $json->token_type ) || 'X_JETPACK' != strtoupper( $json->token_type ) ) {
263
			return new Jetpack_Error( 'token_type', '', $code );
264
		}
265
266
		if ( empty( $json->scope ) ) {
267
			return new Jetpack_Error( 'scope', 'No Scope', $code );
268
		}
269
270
		@list( $role, $hmac ) = explode( ':', $json->scope );
271
		if ( empty( $role ) || empty( $hmac ) ) {
272
			return new Jetpack_Error( 'scope', 'Malformed Scope', $code );
273
		}
274
275
		if ( Jetpack::sign_role( $role ) !== $json->scope ) {
276
			return new Jetpack_Error( 'scope', 'Invalid Scope', $code );
277
		}
278
279
		if ( ! $cap = Jetpack::translate_role_to_cap( $role ) ) {
280
			return new Jetpack_Error( 'scope', 'No Cap', $code );
281
		}
282
283
		if ( ! current_user_can( $cap ) ) {
284
			return new Jetpack_Error( 'scope', 'current_user_cannot', $code );
285
		}
286
287
		/**
288
		 * Fires after user has successfully received an auth token.
289
		 *
290
		 * @since 3.9.0
291
		 */
292
		do_action( 'jetpack_user_authorized' );
293
294
		return (string) $json->access_token;
295
	}
296
297
	public function get_jetpack() {
298
		return Jetpack::init();
299
	}
300
301
	public function do_exit() {
302
		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...
303
	}
304
}
305