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