PluginProbe ʕ •ᴥ•ʔ
MonsterInsights – Google Analytics Dashboard for WordPress (Website Stats Made Easy) / 9.2.0
MonsterInsights – Google Analytics Dashboard for WordPress (Website Stats Made Easy) v9.2.0
10.2.2 10.2.1 10.2.0 10.1.3 trunk 10.0.0 10.0.1 10.0.2 10.0.3 10.1.1 10.1.2 8.1.0 8.10.0 8.10.1 8.11.0 8.12.0 8.12.1 8.13.0 8.13.1 8.14.0 8.14.1 8.15 8.16 8.17 8.18 8.19.0 8.2.0 8.20.0 8.20.1 8.21.0 8.22.0 8.23.0 8.23.1 8.24.0 8.25.0 8.26.0 8.27.0 8.28.0 8.3.0 8.3.1 8.3.2 8.3.3 8.3.4 8.4.0 8.5.0 8.5.1 8.5.2 8.5.3 8.6.0 8.7.0 8.8.0 8.8.1 8.8.2 8.9.0 8.9.1 9.0.0 9.0.1 9.1.0 9.1.1 9.10.0 9.10.1 9.11.0 9.11.1 9.2.0 9.2.1 9.2.2 9.2.3 9.2.4 9.3.0 9.3.1 9.4.0 9.4.1 9.5.1 9.5.2 9.5.3 9.6.0 9.6.1 9.7.0 9.8.0 9.9.0
google-analytics-for-wordpress / includes / api-request.php
google-analytics-for-wordpress / includes Last commit date
admin 1 year ago emails 2 years ago frontend 1 year ago gutenberg 1 year ago popular-posts 2 years ago api-request.php 1 year ago auth.php 2 years ago capabilities.php 3 years ago compatibility-check.php 2 years ago deprecated.php 2 years ago helpers.php 1 year ago index.php 3 years ago install.php 2 years ago measurement-protocol-v4.php 1 year ago options.php 2 years ago
api-request.php
538 lines
1 <?php
2
3 /**
4 * API Request class.
5 *
6 * @since 7.0.0
7 *
8 * @package MonsterInsights
9 * @author Chris Christoff
10 */
11 final class MonsterInsights_API_Request {
12
13 /**
14 * Base API route.
15 *
16 * @since 7.0.0
17 *
18 * @var string
19 */
20 public $base = 'api.monsterinsights.com/v2/';
21
22 /**
23 * Current API route.
24 *
25 * @since 7.0.0
26 *
27 * @var bool|string
28 */
29 public $route = false;
30
31 /**
32 * Full API URL endpoint.
33 *
34 * @since 7.0.0
35 *
36 * @var bool|string
37 */
38 public $url = false;
39
40 /**
41 * Current API method.
42 *
43 * @since 7.0.0
44 *
45 * @var bool|string
46 */
47 public $method = false;
48
49 /**
50 * Is a network request.
51 *
52 * @since 7.2.0
53 *
54 * @var bool
55 */
56 public $network = false;
57
58 /**
59 * API token.
60 *
61 * @since 7.0.0
62 *
63 * @var bool|string
64 */
65 public $token = false;
66
67 /**
68 * API Key.
69 *
70 * @since 7.0.0
71 *
72 * @var bool|string
73 */
74 public $key = false;
75
76 /**
77 * API tt.
78 *
79 * @since 7.0.0
80 *
81 * @var bool|string
82 */
83 public $tt = false;
84
85 /**
86 * API return.
87 *
88 * @since 7.0.0
89 *
90 * @var bool|string
91 */
92 public $return = false;
93
94 /**
95 * Start date.
96 *
97 * @since 7.0.0
98 *
99 * @var string
100 */
101 public $start = '';
102
103 /**
104 * End Date.
105 *
106 * @since 7.0.0
107 *
108 * @var string
109 */
110 public $end = '';
111
112 /**
113 * Plugin slug.
114 *
115 * @since 7.0.0
116 *
117 * @var bool|string
118 */
119 public $plugin = false;
120
121 /**
122 * URL to test connection with.
123 *
124 * @since 7.3.2
125 *
126 * @var string
127 */
128 public $testurl = '';
129
130 /**
131 * Store license.
132 */
133 public $license;
134
135 /**
136 * Store version.
137 */
138 public $miversion;
139
140 /**
141 * Site secret key.
142 */
143 public $sitei;
144
145 /**
146 * Compare end date.
147 *
148 * @var string
149 */
150 protected $compare_end;
151
152 /**
153 * Compare start date.
154 *
155 * @var string
156 */
157 protected $compare_start;
158
159 /**
160 * Site URL.
161 *
162 * @var string
163 */
164 protected $site_url;
165
166 /**
167 * Additional data to add to request body
168 *
169 * @since 7.0.0
170 *
171 * @var array
172 */
173 protected $additional_data = array();
174
175 /**
176 * Primary class constructor.
177 *
178 * @param string $route The API route to target.
179 * @param array $args Array of API credentials.
180 * @param string $method The API method.
181 *
182 * @since 7.0.0
183 */
184 public function __construct( $route, $args, $method = 'POST' ) {
185
186 // Set class properties.
187 $this->base = trailingslashit( monsterinsights_get_api_url() );
188 $this->route = $route;
189 $this->url = trailingslashit( 'https://' . $this->base . $this->route );
190 $this->method = $method;
191 $this->network = is_network_admin() || ! empty( $args['network'] );
192
193 $default_token = $this->network ? MonsterInsights()->auth->get_network_token() : MonsterInsights()->auth->get_token();
194 $default_key = $this->network ? MonsterInsights()->auth->get_network_key() : MonsterInsights()->auth->get_key();
195
196 $this->token = ! empty( $args['token'] ) ? $args['token'] : $default_token;
197 $this->key = ! empty( $args['key'] ) ? $args['key'] : $default_key;
198 $this->tt = ! empty( $args['tt'] ) ? $args['tt'] : '';
199 $this->return = ! empty( $args['return'] ) ? $args['return'] : '';
200 $this->start = ! empty( $args['start'] ) ? $args['start'] : '';
201 $this->end = ! empty( $args['end'] ) ? $args['end'] : '';
202
203 $this->compare_start = ! empty( $args['compare_start'] ) ? $args['compare_start'] : '';
204 $this->compare_end = ! empty( $args['compare_end'] ) ? $args['compare_end'] : '';
205
206 // We need to do this hack so that the network panel + the site_url of the main site are distinct
207 $this->site_url = is_network_admin() ? network_admin_url() : home_url();
208
209 if ( monsterinsights_is_pro_version() ) {
210 $this->license = $this->network ? MonsterInsights()->license->get_network_license_key() : MonsterInsights()->license->get_site_license_key();
211 }
212 $this->plugin = MonsterInsights()->plugin_slug;
213 $this->miversion = MONSTERINSIGHTS_VERSION;
214 $this->sitei = ! empty( $args['sitei'] ) ? $args['sitei'] : '';
215 $this->testurl = ! empty( $args['testurl'] ) ? $args['testurl'] : '';
216 }
217
218 /**
219 * Processes the API request.
220 *
221 * @return mixed $value The response to the API call.
222 * @since 7.0.0
223 */
224 public function request( $extra_params = [] ) {
225 // Make sure we're not blocked
226 $blocked = $this->is_blocked( $this->url );
227 if ( $blocked || is_wp_error( $blocked ) ) {
228 if ( is_wp_error( $blocked ) ) {
229 // Translators: Placeholder gets replaced with the error message.
230 return new WP_Error( 'api-error', sprintf( __( 'The firewall of your server is blocking outbound calls. Please contact your hosting provider to fix this issue. %s', 'google-analytics-for-wordpress' ), $blocked->get_error_message() ) );
231 } else {
232 return new WP_Error( 'api-error', __( 'The firewall of your server is blocking outbound calls. Please contact your hosting provider to fix this issue.', 'google-analytics-for-wordpress' ) );
233 }
234 }
235
236 // Build the body of the request.
237 $body = array();
238
239 if ( ! empty( $this->token ) ) {
240 $body['token'] = $this->token;
241 }
242
243 if ( ! empty( $this->key ) ) {
244 $body['key'] = $this->key;
245 }
246
247 if ( ! empty( $this->tt ) ) {
248 $body['tt'] = $this->tt;
249 }
250
251 if ( ! empty( $this->return ) ) {
252 $body['return'] = $this->return;
253 }
254
255 if ( monsterinsights_is_pro_version() && ! empty( $this->license ) ) {
256 $body['license'] = $this->license;
257 }
258
259 if ( ! empty( $this->start ) ) {
260 $body['start'] = $this->start;
261 }
262
263 if ( ! empty( $this->end ) ) {
264 $body['end'] = $this->end;
265 }
266
267 if ( ! empty( $this->compare_start ) ) {
268 $body['compare_start'] = $this->compare_start;
269 }
270
271 if ( ! empty( $this->compare_end ) ) {
272 $body['compare_end'] = $this->compare_end;
273 }
274
275 if ( ! empty( $this->sitei ) ) {
276 $body['sitei'] = $this->sitei;
277 }
278
279 $body['siteurl'] = $this->site_url;
280 $body['miversion'] = $this->miversion;
281
282 // If a plugin API request, add the data.
283 if ( 'info' == $this->route || 'update' == $this->route ) {
284 $body['miapi-plugin'] = $this->plugin;
285 }
286
287 // Add in additional data if needed.
288 if ( ! empty( $this->additional_data ) ) {
289 $body['miapi-data'] = maybe_serialize( $this->additional_data );
290 }
291
292 if ( 'GET' == $this->method ) {
293 $body['time'] = time(); // just to avoid caching
294 }
295
296 $body['wp_timezone'] = wp_timezone_string(); // Timezone from WP Settings.
297
298 $body['timezone'] = date( 'e' );
299
300 $body['network'] = $this->network ? 'network' : 'site';
301
302 $body['ip'] = ! empty( $_SERVER['SERVER_ADDR'] ) ? sanitize_text_field(wp_unslash($_SERVER['SERVER_ADDR'])) : '';
303
304 // This filter will be removed in the future.
305 $body = apply_filters( 'monsterinsights_api_request_body', $body );
306
307 $body = array_merge($body, $extra_params);
308
309 $string = http_build_query( $body, '', '&' );
310
311 // Build the headers of the request.
312 $headers = array(
313 'Content-Type' => 'application/x-www-form-urlencoded',
314 'Cache-Control' => 'no-store, no-cache, must-revalidate, max-age=0, post-check=0, pre-check=0',
315 'Pragma' => 'no-cache',
316 'Expires' => 0,
317 'MIAPI-Referer' => is_network_admin() ? network_admin_url() : site_url(),
318 'MIAPI-Sender' => 'WordPress',
319 );
320
321 // if ( $this->apikey ) {
322 // $headers['X-MonsterInsights-ApiKey'] = $this->apikey;
323 // }
324
325 // Setup data to be sent to the API.
326 $data = array(
327 'headers' => $headers,
328 'body' => $body,
329 'timeout' => 3000,
330 'user-agent' => 'MI/' . MONSTERINSIGHTS_VERSION . '; ' . $this->site_url,
331 'sslverify' => false,
332 );
333
334 // Perform the query and retrieve the response.
335 $response = 'GET' == $this->method ? wp_remote_get( esc_url_raw( $this->url ) . '?' . $string, $data ) : wp_remote_post( esc_url_raw( $this->url ), $data );
336
337 // return new WP_Error( 'debug', '<pre>' . var_export( $response, true ) . '</pre>' );
338
339 if ( is_wp_error( $response ) ) {
340 return $response;
341 }
342
343 $response_code = wp_remote_retrieve_response_code( $response );
344 $response_body = json_decode( wp_remote_retrieve_body( $response ), true );
345 // return new WP_Error( 'debug', '<pre>' . var_export( $response_body, true ) . '</pre>' );
346 // var_dump( $response_body );
347 // Bail out early if there are any errors.
348 if ( is_wp_error( $response_body ) ) {
349 return $response_body;
350 }
351
352 // If not a 200 status header, send back error.
353 if ( 200 != $response_code && 204 != $response_code) {
354 $type = ! empty( $response_body['type'] ) ? $response_body['type'] : 'api-error';
355
356 if ( empty( $response_code ) ) {
357 // Translators: Support link tag starts with url and Support link tag ends.
358 $message = sprintf(
359 esc_html__( 'Oops! We encountered an error. Please wait a few minutes and try again. If the issue persists, please %1$scontact our support%2$s team.', 'google-analytics-for-wordpress' ),
360 '<a target="_blank" href="' . monsterinsights_get_url( 'notice', 'unknown-api-error', 'https://www.monsterinsights.com/my-account/support/' ) . '">',
361 '</a>'
362 );
363
364 return new WP_Error( $type, $message );
365 }
366
367 if ( empty( $response_body ) || ( empty( $response_body['message'] ) && empty( $response_body['error'] ) ) ) {
368 // Translators: Support link tag starts with url, Support link tag ends and placeholder adds the response code.
369 $message = sprintf(
370 esc_html__( 'Oops! We ran into a problem. Please try again in a few minutes. If the issue persists please %1$scontact our support%2$s team. Error: API returned a %3$s%4$s%5$s response.', 'google-analytics-for-wordpress' ),
371 '<a target="_blank" href="' . monsterinsights_get_url( 'notice', 'unknown-api-error', 'https://www.monsterinsights.com/my-account/support/' ) . '">',
372 '</a>',
373 '<strong>',
374 $response_code,
375 '</strong>'
376 );
377
378 return new WP_Error( $type, $message );
379 }
380
381 if ( ! empty( $response_body['message'] ) ) {
382 // Translators: Support link tag starts with url, Support link tag ends, placeholder adds the response code and response message.
383 $message = sprintf(
384 esc_html__( 'Oops! We ran into a problem. Please try again in a few minutes. If the issue persists please %1$scontact our support%2$s team. Error: API returned a %3$s%4$d: %5$s%6$s', 'google-analytics-for-wordpress' ),
385 '<a target="_blank" href="' . monsterinsights_get_url( 'notice', 'unknown-api-error', 'https://www.monsterinsights.com/my-account/support/' ) . '">',
386 '</a>',
387 '<strong>',
388 $response_code,
389 stripslashes( $response_body['message'] ),
390 '</strong>'
391 );
392
393 return new WP_Error( $type, $message );
394 }
395
396 if ( ! empty( $response_body['error'] ) ) {
397 // Translators: Support link tag starts with url, Support link tag ends, placeholder adds the response code and response message.
398 $message = sprintf(
399 esc_html__( 'Oops! We ran into a problem. Please try again in a few minutes. If the issue persists please %1$scontact our support%2$s team. Error: API returned a %3$s%4$d: %5$s%6$s', 'google-analytics-for-wordpress' ),
400 '<a target="_blank" href="' . monsterinsights_get_url( 'notice', 'unknown-api-error', 'https://www.monsterinsights.com/my-account/support/' ) . '">',
401 '</a>',
402 '<strong>',
403 $response_code,
404 stripslashes( $response_body['error'] ),
405 '</strong>'
406 );
407
408 return new WP_Error( $type, $message );
409 }
410 }
411
412 // If TT required
413 if ( ! empty( $this->tt ) ) {
414 if ( empty( $response_body['tt'] ) || ! hash_equals( $this->tt, $response_body['tt'] ) ) {
415 // TT isn't set on return or doesn't match
416 // Translators: Support link tag starts with url and Support link tag ends.
417 $message = sprintf(
418 esc_html__( 'Oops! We ran into a problem. Please try again in a few minutes. If the issue persists please %1$scontact our support%2$s team. Error: Improper API Request.', 'google-analytics-for-wordpress' ),
419 '<a target="_blank" href="' . monsterinsights_get_url( 'notice', 'cannot-verify-license', 'https://www.monsterinsights.com/my-account/support/' ) . '">',
420 '</a>'
421 );
422
423 return new WP_Error( 'validation-error', $message );
424 }
425 }
426
427 // Return the json decoded content.
428 return $response_body;
429 }
430
431 /**
432 * Sets a class property.
433 *
434 * @param string $key The property to set.
435 * @param string $val The value to set for the property.
436 *
437 * @return mixed $value The response to the API call.
438 * @since 7.0.0
439 */
440 public function set( $key, $val ) {
441 $this->{$key} = $val;
442 }
443
444 /**
445 * Allow additional data to be passed in the request
446 *
447 * @param array $data
448 * return void
449 *
450 * @since 7.0.0
451 */
452 public function set_additional_data( array $data ) {
453 $this->additional_data = array_merge( $this->additional_data, $data );
454 }
455
456 /**
457 * Checks for SSL for making API requests.
458 *
459 * @since 7.0.0
460 *
461 * return bool True if SSL is enabled, false otherwise.
462 */
463 public function is_ssl() {
464 // Use the base is_ssl check first.
465 if ( is_ssl() ) {
466 return true;
467 } elseif ( isset( $_SERVER['HTTP_X_FORWARDED_PROTO'] ) && 'https' == $_SERVER['HTTP_X_FORWARDED_PROTO'] ) {
468 // Also catch proxies and load balancers.
469 return true;
470 } elseif ( defined( 'FORCE_SSL_ADMIN' ) && FORCE_SSL_ADMIN ) {
471 return true;
472 }
473
474 // Otherwise, return false.
475 return false;
476 }
477
478 private function is_blocked( $url = '' ) {
479 global $Airplane_Mode_Core;
480
481 if ( defined( 'AIRMDE_VER' ) && ! empty( $Airplane_Mode_Core ) && $Airplane_Mode_Core->enabled() ) {
482 return new WP_Error( 'api-error', __( 'Oops! The API was unreachable because the plugin, Airplane Mode is active. Please disable and try again.', 'google-analytics-for-wordpress' ) );
483 }
484
485 // The below page is a testing empty content HTML page used for firewall/router login detection
486 // and for image linking purposes in Google Images. We use it to test outbound connections since it is run on google.com
487 // and is only a few bytes large. Plus on Google's main CDN so it loads in most places in 0.07 seconds or less. Perfect for our
488 // use case of quickly testing outbound connections.
489 $testurl = ! empty( $this->testurl ) ? $this->testurl : 'https://www.google.com/blank.html';
490 if ( defined( 'WP_HTTP_BLOCK_EXTERNAL' ) && WP_HTTP_BLOCK_EXTERNAL ) {
491 if ( defined( 'WP_ACCESSIBLE_HOSTS' ) ) {
492 $wp_http = new WP_Http();
493 $on_blacklist = $wp_http->block_request( $url );
494 if ( $on_blacklist ) {
495 return new WP_Error( 'api-error', __( 'Reason: The API was unreachable because the API url is on the WP HTTP blocklist.', 'google-analytics-for-wordpress' ) );
496 } else {
497 $params = array(
498 'sslverify' => false,
499 'timeout' => 2,
500 'user-agent' => 'MonsterInsights/' . MONSTERINSIGHTS_VERSION,
501 'body' => '',
502 );
503 $response = wp_remote_get( $testurl, $params );
504 if ( ! is_wp_error( $response ) && $response['response']['code'] >= 200 && $response['response']['code'] < 300 ) {
505 return false;
506 } else {
507 if ( is_wp_error( $response ) ) {
508 return $response;
509 } else {
510 return new WP_Error( 'api-error', __( 'Reason: The API was unreachable because the call to Google failed.', 'google-analytics-for-wordpress' ) );
511 }
512 }
513 }
514 } else {
515 return new WP_Error( 'api-error', __( 'Reason: The API was unreachable because no external hosts are allowed on this site.', 'google-analytics-for-wordpress' ) );
516 }
517 } else {
518 $params = array(
519 'sslverify' => false,
520 'timeout' => 2,
521 'user-agent' => 'MonsterInsights/' . MONSTERINSIGHTS_VERSION,
522 'body' => '',
523 );
524 $response = wp_remote_get( $testurl, $params );
525
526 if ( ! is_wp_error( $response ) && $response['response']['code'] >= 200 && $response['response']['code'] < 300 ) {
527 return false;
528 } else {
529 if ( is_wp_error( $response ) ) {
530 return $response;
531 } else {
532 return new WP_Error( 'api-error', __( 'Reason: The API was unreachable because the call to Google failed.', 'google-analytics-for-wordpress' ) );
533 }
534 }
535 }
536 }
537 }
538