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