Completed
Push — revert-12527-feature/jetpack-p... ( 70c6f5 )
by
unknown
44:32 queued 37:52
created

class.jetpack-client-server.php (1 issue)

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 this site has been through the Jetpack Onboarding flow, delete the onboarding token
134
		Jetpack::invalidate_onboarding_token();
135
136
		// If redirect_uri is SSO, ensure SSO module is enabled
137
		parse_str( parse_url( $data['redirect_uri'], PHP_URL_QUERY ), $redirect_options );
138
139
		/** This filter is documented in class.jetpack-cli.php */
140
		$jetpack_start_enable_sso = apply_filters( 'jetpack_start_enable_sso', true );
141
142
		$activate_sso = (
143
			isset( $redirect_options['action'] ) &&
144
			'jetpack-sso' === $redirect_options['action'] &&
145
			$jetpack_start_enable_sso
146
		);
147
148
		$do_redirect_on_error = ( 'client' === $data['auth_type'] );
149
150
		Jetpack::handle_post_authorization_actions( $activate_sso, $do_redirect_on_error );
151
152
		return 'authorized';
153
	}
154
155
	public static function deactivate_plugin( $probable_file, $probable_title ) {
156
		include_once( ABSPATH . 'wp-admin/includes/plugin.php' );
157
		if ( is_plugin_active( $probable_file ) ) {
158
			deactivate_plugins( $probable_file );
159
			return 1;
160
		} else {
161
			// If the plugin is not in the usual place, try looking through all active plugins.
162
			$active_plugins = Jetpack::get_active_plugins();
163
			foreach ( $active_plugins as $plugin ) {
164
				$data = get_plugin_data( WP_PLUGIN_DIR . '/' . $plugin );
165
				if ( $data['Name'] == $probable_title ) {
166
					deactivate_plugins( $plugin );
167
					return 1;
168
				}
169
			}
170
		}
171
172
		return 0;
173
	}
174
175
	/**
176
	 * @return object|WP_Error
177
	 */
178
	function get_token( $data ) {
179
		$role = Jetpack::translate_current_user_to_role();
180
181
		if ( ! $role ) {
182
			return new Jetpack_Error( 'role', __( 'An administrator for this blog must set up the Jetpack connection.', 'jetpack' ) );
183
		}
184
185
		$client_secret = Jetpack_Data::get_access_token();
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...
186
		if ( ! $client_secret ) {
187
			return new Jetpack_Error( 'client_secret', __( 'You need to register your Jetpack before connecting it.', 'jetpack' ) );
188
		}
189
190
		$redirect = isset( $data['redirect'] ) ? esc_url_raw( (string) $data['redirect'] ) : '';
191
		$redirect_uri = ( 'calypso' === $data['auth_type'] )
192
			? $data['redirect_uri']
193
			: add_query_arg( array(
194
				'action' => 'authorize',
195
				'_wpnonce' => wp_create_nonce( "jetpack-authorize_{$role}_{$redirect}" ),
196
				'redirect' => $redirect ? urlencode( $redirect ) : false,
197
			), menu_page_url( 'jetpack', false ) );
198
199
		// inject identity for analytics
200
		$tracks_identity = jetpack_tracks_get_identity( get_current_user_id() );
201
202
		$body = array(
203
			'client_id' => Jetpack_Options::get_option( 'id' ),
204
			'client_secret' => $client_secret->secret,
205
			'grant_type' => 'authorization_code',
206
			'code' => $data['code'],
207
			'redirect_uri' => $redirect_uri,
208
			'_ui' => $tracks_identity['_ui'],
209
			'_ut' => $tracks_identity['_ut'],
210
		);
211
212
		$args = array(
213
			'method' => 'POST',
214
			'body' => $body,
215
			'headers' => array(
216
				'Accept' => 'application/json',
217
			),
218
		);
219
		$response = Jetpack_Client::_wp_remote_request( Jetpack::fix_url_for_bad_hosts( Jetpack::api_url( 'token' ) ), $args );
220
221
		if ( is_wp_error( $response ) ) {
222
			return new Jetpack_Error( 'token_http_request_failed', $response->get_error_message() );
223
		}
224
225
		$code = wp_remote_retrieve_response_code( $response );
226
		$entity = wp_remote_retrieve_body( $response );
227
228
		if ( $entity ) {
229
			$json = json_decode( $entity );
230
		} else {
231
			$json = false;
232
		}
233
234
		if ( 200 != $code || ! empty( $json->error ) ) {
235
			if ( empty( $json->error ) ) {
236
				return new Jetpack_Error( 'unknown', '', $code );
237
			}
238
239
			$error_description = isset( $json->error_description ) ? sprintf( __( 'Error Details: %s', 'jetpack' ), (string) $json->error_description ) : '';
240
241
			return new Jetpack_Error( (string) $json->error, $error_description, $code );
242
		}
243
244
		if ( empty( $json->access_token ) || ! is_scalar( $json->access_token ) ) {
245
			return new Jetpack_Error( 'access_token', '', $code );
246
		}
247
248
		if ( empty( $json->token_type ) || 'X_JETPACK' != strtoupper( $json->token_type ) ) {
249
			return new Jetpack_Error( 'token_type', '', $code );
250
		}
251
252
		if ( empty( $json->scope ) ) {
253
			return new Jetpack_Error( 'scope', 'No Scope', $code );
254
		}
255
256
		@list( $role, $hmac ) = explode( ':', $json->scope );
257
		if ( empty( $role ) || empty( $hmac ) ) {
258
			return new Jetpack_Error( 'scope', 'Malformed Scope', $code );
259
		}
260
261
		if ( Jetpack::sign_role( $role ) !== $json->scope ) {
262
			return new Jetpack_Error( 'scope', 'Invalid Scope', $code );
263
		}
264
265
		if ( ! $cap = Jetpack::translate_role_to_cap( $role ) ) {
266
			return new Jetpack_Error( 'scope', 'No Cap', $code );
267
		}
268
269
		if ( ! current_user_can( $cap ) ) {
270
			return new Jetpack_Error( 'scope', 'current_user_cannot', $code );
271
		}
272
273
		/**
274
		 * Fires after user has successfully received an auth token.
275
		 *
276
		 * @since 3.9.0
277
		 */
278
		do_action( 'jetpack_user_authorized' );
279
280
		return (string) $json->access_token;
281
	}
282
283
	public function get_jetpack() {
284
		return Jetpack::init();
285
	}
286
287
	public function do_exit() {
288
		exit;
289
	}
290
}
291