Completed
Push — add/xmlrpc-user-api ( 457a7a...d35894 )
by
unknown
538:26 queued 531:29
created

Jetpack_Client_Server   B

Complexity

Total Complexity 48

Size/Duplication

Total Lines 284
Duplicated Lines 2.46 %

Coupling/Cohesion

Components 1
Dependencies 6

Importance

Changes 0
Metric Value
wmc 48
lcom 1
cbo 6
dl 7
loc 284
rs 8.5599
c 0
b 0
f 0

6 Methods

Rating   Name   Duplication   Size   Complexity  
A client_authorize() 0 38 4
A deactivate_plugin() 0 19 4
F get_token() 0 104 22
A get_jetpack() 0 3 1
A do_exit() 0 3 1
D authorize() 7 103 16

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like Jetpack_Client_Server often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Jetpack_Client_Server, and based on these observations, apply Extract Interface, too.

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'] ) : '';
0 ignored issues
show
Unused Code introduced by
$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...
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 View Code Duplication
		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 );
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...
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