Completed
Push — fix/full-sync-in-separate-queu... ( c25413...0fc729 )
by
unknown
16:04 queued 06:20
created

class.jetpack-client-server.php (5 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
		$this->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
			$this->wp_safe_redirect( $redirect );
27
		} else {
28
			$this->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'] ) : '';
0 ignored issues
show
$redirect is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
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
		// Start nonce cleaner
135
		wp_clear_scheduled_hook( 'jetpack_clean_nonces' );
136
		wp_schedule_event( time(), 'hourly', 'jetpack_clean_nonces' );
137
138
		Jetpack::state( 'message', 'authorized' );
139
		return 'authorized';
140
	}
141
142
	public static function deactivate_plugin( $probable_file, $probable_title ) {
143
		include_once( ABSPATH . 'wp-admin/includes/plugin.php' );
144
		if ( is_plugin_active( $probable_file ) ) {
145
			deactivate_plugins( $probable_file );
146
			return 1;
147
		} else {
148
			// If the plugin is not in the usual place, try looking through all active plugins.
149
			$active_plugins = Jetpack::get_active_plugins();
150
			foreach ( $active_plugins as $plugin ) {
151
				$data = get_plugin_data( WP_PLUGIN_DIR . '/' . $plugin );
152
				if ( $data['Name'] == $probable_title ) {
153
					deactivate_plugins( $plugin );
154
					return 1;
155
				}
156
			}
157
		}
158
159
		return 0;
160
	}
161
162
	/**
163
	 * @return object|WP_Error
164
	 */
165
	function get_token( $data ) {
166
		$role = Jetpack::translate_current_user_to_role();
167
168
		if ( ! $role ) {
169
			return new Jetpack_Error( 'role', __( 'An administrator for this blog must set up the Jetpack connection.', 'jetpack' ) );
170
		}
171
172
		$client_secret = Jetpack_Data::get_access_token();
173
		if ( ! $client_secret ) {
174
			return new Jetpack_Error( 'client_secret', __( 'You need to register your Jetpack before connecting it.', 'jetpack' ) );
175
		}
176
177
		$redirect = isset( $data['redirect'] ) ? esc_url_raw( (string) $data['redirect'] ) : '';
178
		$redirect_uri = ( 'calypso' === $data['auth_type'] )
179
			? $data['redirect_uri']
180
			: add_query_arg( array(
181
				'action' => 'authorize',
182
				'_wpnonce' => wp_create_nonce( "jetpack-authorize_{$role}_{$redirect}" ),
183
				'redirect' => $redirect ? urlencode( $redirect ) : false,
184
			), menu_page_url( 'jetpack', false ) );
185
186
		$body = array(
187
			'client_id' => Jetpack_Options::get_option( 'id' ),
188
			'client_secret' => $client_secret->secret,
189
			'grant_type' => 'authorization_code',
190
			'code' => $data['code'],
191
			'redirect_uri' => $redirect_uri,
192
		);
193
194
		$args = array(
195
			'method' => 'POST',
196
			'body' => $body,
197
			'headers' => array(
198
				'Accept' => 'application/json',
199
			),
200
		);
201
		$response = Jetpack_Client::_wp_remote_request( Jetpack::fix_url_for_bad_hosts( Jetpack::api_url( 'token' ) ), $args );
202
203
		if ( is_wp_error( $response ) ) {
204
			return new Jetpack_Error( 'token_http_request_failed', $response->get_error_message() );
205
		}
206
207
		$code = wp_remote_retrieve_response_code( $response );
208
		$entity = wp_remote_retrieve_body( $response );
209
210
		if ( $entity ) {
211
			$json = json_decode( $entity );
212
		} else {
213
			$json = false;
214
		}
215
216
		if ( 200 != $code || ! empty( $json->error ) ) {
217
			if ( empty( $json->error ) ) {
218
				return new Jetpack_Error( 'unknown', '', $code );
219
			}
220
221
			$error_description = isset( $json->error_description ) ? sprintf( __( 'Error Details: %s', 'jetpack' ), (string) $json->error_description ) : '';
222
223
			return new Jetpack_Error( (string) $json->error, $error_description, $code );
224
		}
225
226
		if ( empty( $json->access_token ) || ! is_scalar( $json->access_token ) ) {
227
			return new Jetpack_Error( 'access_token', '', $code );
228
		}
229
230
		if ( empty( $json->token_type ) || 'X_JETPACK' != strtoupper( $json->token_type ) ) {
231
			return new Jetpack_Error( 'token_type', '', $code );
232
		}
233
234
		if ( empty( $json->scope ) ) {
235
			return new Jetpack_Error( 'scope', 'No Scope', $code );
236
		}
237
238
		@list( $role, $hmac ) = explode( ':', $json->scope );
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
239
		if ( empty( $role ) || empty( $hmac ) ) {
240
			return new Jetpack_Error( 'scope', 'Malformed Scope', $code );
241
		}
242
243
		if ( Jetpack::sign_role( $role ) !== $json->scope ) {
244
			return new Jetpack_Error( 'scope', 'Invalid Scope', $code );
245
		}
246
247
		if ( ! $cap = Jetpack::translate_role_to_cap( $role ) ) {
248
			return new Jetpack_Error( 'scope', 'No Cap', $code );
249
		}
250
251
		if ( ! current_user_can( $cap ) ) {
252
			return new Jetpack_Error( 'scope', 'current_user_cannot', $code );
253
		}
254
255
		/**
256
		 * Fires after user has successfully received an auth token.
257
		 *
258
		 * @since 3.9.0
259
		 */
260
		do_action( 'jetpack_user_authorized' );
261
262
		return (string) $json->access_token;
263
	}
264
265
	public function get_jetpack() {
266
		return Jetpack::init();
267
	}
268
269
	public function check_admin_referer( $action ) {
270
		return check_admin_referer( $action );
271
	}
272
273
	public function wp_safe_redirect( $redirect ) {
274
		return wp_safe_redirect( $redirect );
275
	}
276
277
	public function do_exit() {
278
		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...
279
	}
280
}
281