Completed
Push — add/vr-shortcode ( 1ed1cb...271979 )
by
unknown
47:57 queued 41:56
created

class.jetpack-client.php (2 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
class Jetpack_Client {
4
	/**
5
	 * Makes an authorized remote request using Jetpack_Signature
6
	 *
7
	 * @return array|WP_Error WP HTTP response on success
8
	 */
9
	public static function remote_request( $args, $body = null ) {
10
		$defaults = array(
11
			'url' => '',
12
			'user_id' => 0,
13
			'blog_id' => 0,
14
			'auth_location' => JETPACK_CLIENT__AUTH_LOCATION,
15
			'method' => 'POST',
16
			'timeout' => 10,
17
			'redirection' => 0,
18
		);
19
20
		$args = wp_parse_args( $args, $defaults );
21
22
		$args['blog_id'] = (int) $args['blog_id'];
23
24
		if ( 'header' != $args['auth_location'] ) {
25
			$args['auth_location'] = 'query_string';
26
		}
27
28
		$token = Jetpack_Data::get_access_token( $args['user_id'] );
0 ignored issues
show
$args['user_id'] is of type integer|string, 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...
29
		if ( !$token ) {
30
			return new Jetpack_Error( 'missing_token' );
31
		}
32
33
		$method = strtoupper( $args['method'] );
34
35
		$timeout = intval( $args['timeout'] );
36
37
		$redirection = $args['redirection'];
38
39
		$request = compact( 'method', 'body', 'timeout', 'redirection' );
40
41
		@list( $token_key, $secret ) = explode( '.', $token->secret );
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...
42
		if ( empty( $token ) || empty( $secret ) ) {
43
			return new Jetpack_Error( 'malformed_token' );
44
		}
45
46
		$token_key = sprintf( '%s:%d:%d', $token_key, JETPACK__API_VERSION, $token->external_user_id );
47
48
		require_once JETPACK__PLUGIN_DIR . 'class.jetpack-signature.php';
49
50
		$time_diff = (int) Jetpack_Options::get_option( 'time_diff' );
51
		$jetpack_signature = new Jetpack_Signature( $token->secret, $time_diff );
52
53
		$timestamp = time() + $time_diff;
54
		
55
		if( function_exists( 'wp_generate_password' ) ) {
56
			$nonce = wp_generate_password( 10, false );
57
		} else {
58
			$nonce = substr( sha1( rand( 0, 1000000 ) ), 0, 10);
59
		}
60
61
		// Kind of annoying.  Maybe refactor Jetpack_Signature to handle body-hashing
62 View Code Duplication
		if ( is_null( $body ) ) {
63
			$body_hash = '';
64
		} else {
65
			if ( !is_string( $body ) ) {
66
				return new Jetpack_Error( 'invalid_body', 'Body is malformed.' );
67
			}
68
			$body_hash = jetpack_sha1_base64( $body );
69
		}
70
71
		$auth = array(
72
			'token' => $token_key,
73
			'timestamp' => $timestamp,
74
			'nonce' => $nonce,
75
			'body-hash' => $body_hash,
76
		);
77
78
		if ( false !== strpos( $args['url'], 'xmlrpc.php' ) ) {
79
			$url_args = array(
80
				'for'           => 'jetpack',
81
				'wpcom_blog_id' => Jetpack_Options::get_option( 'id' ),
82
			);
83
		} else {
84
			$url_args = array();
85
		}
86
87
		if ( 'header' != $args['auth_location'] ) {
88
			$url_args += $auth;
89
		}
90
91
		$url = add_query_arg( urlencode_deep( $url_args ), $args['url'] );
92
		$url = Jetpack::fix_url_for_bad_hosts( $url );
93
94
		$signature = $jetpack_signature->sign_request( $token_key, $timestamp, $nonce, $body_hash, $method, $url, $body, false );
95
96
		if ( !$signature || is_wp_error( $signature ) ) {
97
			return $signature;
98
		}
99
100
		// Send an Authorization header so various caches/proxies do the right thing
101
		$auth['signature'] = $signature;
102
		$auth['version'] = JETPACK__VERSION;
103
		$header_pieces = array();
104
		foreach ( $auth as $key => $value ) {
105
			$header_pieces[] = sprintf( '%s="%s"', $key, $value );
106
		}
107
		$request['headers'] = array(
108
			'Authorization' => "X_JETPACK " . join( ' ', $header_pieces ),
109
		);
110
111
		if ( 'header' != $args['auth_location'] ) {
112
			$url = add_query_arg( 'signature', urlencode( $signature ), $url );
113
		}
114
115
		return Jetpack_Client::_wp_remote_request( $url, $request );
116
	}
117
118
	/**
119
	 * Wrapper for wp_remote_request().  Turns off SSL verification for certain SSL errors.
120
	 * This is lame, but many, many, many hosts have misconfigured SSL.
121
	 *
122
	 * When Jetpack is registered, the jetpack_fallback_no_verify_ssl_certs option is set to the current time if:
123
	 * 1. a certificate error is found AND
124
	 * 2. not verifying the certificate works around the problem.
125
	 *
126
	 * The option is checked on each request.
127
	 *
128
	 * @internal
129
	 * @see Jetpack::fix_url_for_bad_hosts()
130
	 *
131
	 * @return array|WP_Error WP HTTP response on success
132
	 */
133
	public static function _wp_remote_request( $url, $args, $set_fallback = false ) {
134
		$fallback = Jetpack_Options::get_option( 'fallback_no_verify_ssl_certs' );
135
		if ( false === $fallback ) {
136
			Jetpack_Options::update_option( 'fallback_no_verify_ssl_certs', 0 );
137
		}
138
139
		if ( (int) $fallback ) {
140
			// We're flagged to fallback
141
			$args['sslverify'] = false;
142
		}
143
144
		$response = wp_remote_request( $url, $args );
145
146
		if (
147
			!$set_fallback                                     // We're not allowed to set the flag on this request, so whatever happens happens
148
		||
149
			isset( $args['sslverify'] ) && !$args['sslverify'] // No verification - no point in doing it again
150
		||
151
			!is_wp_error( $response )                          // Let it ride
152
		) {
153
			Jetpack_Client::set_time_diff( $response, $set_fallback );
154
			return $response;
155
		}
156
157
		// At this point, we're not flagged to fallback and we are allowed to set the flag on this request.
158
159
		$message = $response->get_error_message();
160
161
		// Is it an SSL Certificate verification error?
162
		if (
163
			false === strpos( $message, '14090086' ) // OpenSSL SSL3 certificate error
164
		&&
165
			false === strpos( $message, '1407E086' ) // OpenSSL SSL2 certificate error
166
		&&
167
			false === strpos( $message, 'error setting certificate verify locations' ) // cURL CA bundle not found
168
		&&
169
			false === strpos( $message, 'Peer certificate cannot be authenticated with' ) // cURL CURLE_SSL_CACERT: CA bundle found, but not helpful
170
			                                                                              // different versions of curl have different error messages
171
			                                                                              // this string should catch them all
172
		&&
173
			false === strpos( $message, 'Problem with the SSL CA cert' ) // cURL CURLE_SSL_CACERT_BADFILE: probably access rights
174
		) {
175
			// No, it is not.
176
			return $response;
177
		}
178
179
		// Redo the request without SSL certificate verification.
180
		$args['sslverify'] = false;
181
		$response = wp_remote_request( $url, $args );
182
183
		if ( !is_wp_error( $response ) ) {
184
			// The request went through this time, flag for future fallbacks
185
			Jetpack_Options::update_option( 'fallback_no_verify_ssl_certs', time() );
186
			Jetpack_Client::set_time_diff( $response, $set_fallback );
187
		}
188
189
		return $response;
190
	}
191
192
	public static function set_time_diff( &$response, $force_set = false ) {
193
		$code = wp_remote_retrieve_response_code( $response );
194
195
		// Only trust the Date header on some responses
196
		if ( 200 != $code && 304 != $code && 400 != $code && 401 != $code ) {
197
			return;
198
		}
199
200
		if ( !$date = wp_remote_retrieve_header( $response, 'date' ) ) {
201
			return;
202
		}
203
204
		if ( 0 >= $time = (int) strtotime( $date ) ) {
205
			return;
206
		}
207
208
		$time_diff = $time - time();
209
210
		if ( $force_set ) { // during register
211
			Jetpack_Options::update_option( 'time_diff', $time_diff );
212
		} else { // otherwise
213
			$old_diff = Jetpack_Options::get_option( 'time_diff' );
214
			if ( false === $old_diff || abs( $time_diff - (int) $old_diff ) > 10 ) {
215
				Jetpack_Options::update_option( 'time_diff', $time_diff );
216
			}
217
		}
218
	}
219
}
220