PageRenderTime 25ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 0ms

/htdocs/wp-content/plugins/google-analytics-dashboard-for-wp/includes/api-request.php

https://gitlab.com/VTTE/sitios-vtte
PHP | 443 lines | 205 code | 60 blank | 178 comment | 66 complexity | bb500f5967b975ea38e2d08dfdd591c7 MD5 | raw file
  1. <?php
  2. /**
  3. * API Request class.
  4. *
  5. * @since 7.0.0
  6. *
  7. * @package ExactMetrics
  8. * @author Chris Christoff
  9. */
  10. final class ExactMetrics_API_Request {
  11. /**
  12. * Base API route.
  13. *
  14. * @since 7.0.0
  15. *
  16. * @var string
  17. */
  18. public $base = 'api.exactmetrics.com/v2/';
  19. /**
  20. * Current API route.
  21. *
  22. * @since 7.0.0
  23. *
  24. * @var bool|string
  25. */
  26. public $route = false;
  27. /**
  28. * Full API URL endpoint.
  29. *
  30. * @since 7.0.0
  31. *
  32. * @var bool|string
  33. */
  34. public $url = false;
  35. /**
  36. * Current API method.
  37. *
  38. * @since 7.0.0
  39. *
  40. * @var bool|string
  41. */
  42. public $method = false;
  43. /**
  44. * Is a network request.
  45. *
  46. * @since 7.2.0
  47. *
  48. * @var bool
  49. */
  50. public $network = false;
  51. /**
  52. * API token.
  53. *
  54. * @since 7.0.0
  55. *
  56. * @var bool|string
  57. */
  58. public $token = false;
  59. /**
  60. * API Key.
  61. *
  62. * @since 7.0.0
  63. *
  64. * @var bool|string
  65. */
  66. public $key = false;
  67. /**
  68. * API tt.
  69. *
  70. * @since 7.0.0
  71. *
  72. * @var bool|string
  73. */
  74. public $tt = false;
  75. /**
  76. * API return.
  77. *
  78. * @since 7.0.0
  79. *
  80. * @var bool|string
  81. */
  82. public $return = false;
  83. /**
  84. * Start date.
  85. *
  86. * @since 7.0.0
  87. *
  88. * @var string
  89. */
  90. public $start = '';
  91. /**
  92. * End Date.
  93. *
  94. * @since 7.0.0
  95. *
  96. * @var string
  97. */
  98. public $end = '';
  99. /**
  100. * Plugin slug.
  101. *
  102. * @since 7.0.0
  103. *
  104. * @var bool|string
  105. */
  106. public $plugin = false;
  107. /**
  108. * URL to test connection with.
  109. *
  110. * @since 7.3.2
  111. *
  112. * @var string
  113. */
  114. public $testurl = '';
  115. /**
  116. * Additional data to add to request body
  117. *
  118. * @since 7.0.0
  119. *
  120. * @var array
  121. */
  122. protected $additional_data = array();
  123. /**
  124. * Primary class constructor.
  125. *
  126. * @since 7.0.0
  127. *
  128. * @param string $route The API route to target.
  129. * @param array $args Array of API credentials.
  130. * @param string $method The API method.
  131. */
  132. public function __construct( $route, $args, $method = 'POST' ) {
  133. // Set class properties.
  134. $this->base = trailingslashit( exactmetrics_get_api_url() );
  135. $this->route = $route;
  136. $this->protocol = 'https://';
  137. $this->url = trailingslashit( $this->protocol . $this->base . $this->route );
  138. $this->method = $method;
  139. $this->network = is_network_admin() || ! empty( $args['network'] );
  140. $default_token = $this->network ? ExactMetrics()->auth->get_network_token() : ExactMetrics()->auth->get_token();
  141. $default_key = $this->network ? ExactMetrics()->auth->get_network_key() : ExactMetrics()->auth->get_key();
  142. $this->token = ! empty( $args['token'] ) ? $args['token'] : $default_token;
  143. $this->key = ! empty( $args['key'] ) ? $args['key'] : $default_key;
  144. $this->tt = ! empty( $args['tt'] ) ? $args['tt'] : '';
  145. $this->return = ! empty( $args['return'] ) ? $args['return'] : '';
  146. $this->start = ! empty( $args['start'] ) ? $args['start'] : '';
  147. $this->end = ! empty( $args['end'] ) ? $args['end'] : '';
  148. // We need to do this hack so that the network panel + the site_url of the main site are distinct
  149. $this->site_url = is_network_admin() ? network_admin_url() : site_url();
  150. if ( exactmetrics_is_pro_version() ) {
  151. $this->license = $this->network ? ExactMetrics()->license->get_network_license_key() : ExactMetrics()->license->get_site_license_key();
  152. }
  153. $this->plugin = ExactMetrics()->plugin_slug;
  154. $this->miversion = EXACTMETRICS_VERSION;
  155. $this->sitei = ! empty( $args['sitei'] ) ? $args['sitei'] : '';
  156. $this->testurl = ! empty( $args['testurl'] ) ? $args['testurl'] : '';
  157. }
  158. /**
  159. * Processes the API request.
  160. *
  161. * @since 7.0.0
  162. *
  163. * @return mixed $value The response to the API call.
  164. */
  165. public function request() {
  166. // Make sure we're not blocked
  167. $blocked = $this->is_blocked( $this->url );
  168. if ( $blocked || is_wp_error( $blocked ) ) {
  169. if ( is_wp_error( $blocked ) ) {
  170. // Translators: Placeholder gets replaced with the error message.
  171. 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-dashboard-for-wp' ), $blocked->get_error_message() ) );
  172. } else {
  173. 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-dashboard-for-wp' ) );
  174. }
  175. }
  176. // Build the body of the request.
  177. $body = array();
  178. if ( ! empty( $this->token ) ) {
  179. $body['token'] = $this->token;
  180. }
  181. if ( ! empty( $this->key ) ) {
  182. $body['key'] = $this->key;
  183. }
  184. if ( ! empty( $this->tt ) ) {
  185. $body['tt'] = $this->tt;
  186. }
  187. if ( ! empty( $this->return ) ) {
  188. $body['return'] = $this->return;
  189. }
  190. if ( exactmetrics_is_pro_version() && ! empty( $this->license ) ) {
  191. $body['license'] = $this->license;
  192. }
  193. if ( ! empty( $this->start ) ) {
  194. $body['start'] = $this->start;
  195. }
  196. if ( ! empty( $this->end ) ) {
  197. $body['end'] = $this->end;
  198. }
  199. if ( ! empty( $this->sitei ) ) {
  200. $body['sitei'] = $this->sitei;
  201. }
  202. $body['siteurl'] = $this->site_url;
  203. $body['miversion'] = $this->miversion;
  204. // If a plugin API request, add the data.
  205. if ( 'info' == $this->route || 'update' == $this->route ) {
  206. $body['miapi-plugin'] = $this->plugin;
  207. }
  208. // Add in additional data if needed.
  209. if ( ! empty( $this->additional_data ) ) {
  210. $body['miapi-data'] = maybe_serialize( $this->additional_data );
  211. }
  212. if ( 'GET' == $this->method ) {
  213. $body['time'] = time(); // just to avoid caching
  214. }
  215. $body['timezone'] = date('e');
  216. $body['network'] = $this->network ? 'network' : 'site';
  217. $body['ip'] = ! empty( $_SERVER['SERVER_ADDR'] ) ? $_SERVER['SERVER_ADDR'] : '';
  218. // This filter will be removed in the future.
  219. $body = apply_filters( 'exactmetrics_api_request_body', $body );
  220. $string = http_build_query( $body, '', '&' );
  221. // Build the headers of the request.
  222. $headers = array(
  223. 'Content-Type' => 'application/x-www-form-urlencoded',
  224. 'Cache-Control' => 'no-store, no-cache, must-revalidate, max-age=0, post-check=0, pre-check=0',
  225. 'Pragma' => 'no-cache',
  226. 'Expires' => 0,
  227. 'MIAPI-Referer' => is_network_admin() ? network_admin_url() : site_url(),
  228. 'MIAPI-Sender' => 'WordPress',
  229. );
  230. //if ( $this->apikey ) {
  231. // $headers['X-ExactMetrics-ApiKey'] = $this->apikey;
  232. //}
  233. // Setup data to be sent to the API.
  234. $data = array(
  235. 'headers' => $headers,
  236. 'body' => $body,
  237. 'timeout' => 3000,
  238. 'user-agent' => 'MI/' . EXACTMETRICS_VERSION . '; ' . $this->site_url,
  239. 'sslverify' => false
  240. );
  241. // Perform the query and retrieve the response.
  242. $response = 'GET' == $this->method ? wp_remote_get( esc_url_raw( $this->url ) . '?' . $string, $data ) : wp_remote_post( esc_url_raw( $this->url ), $data );
  243. //return new WP_Error( 'debug', '<pre>' . var_export( $response, true ) . '</pre>' );
  244. if ( is_wp_error( $response ) ) {
  245. return $response;
  246. }
  247. $response_code = wp_remote_retrieve_response_code( $response );
  248. $response_body = json_decode( wp_remote_retrieve_body( $response ), true );
  249. //return new WP_Error( 'debug', '<pre>' . var_export( $response_body, true ) . '</pre>' );
  250. //var_dump( $response_body );
  251. // Bail out early if there are any errors.
  252. if ( is_wp_error( $response_body ) ) {
  253. return $response_body;
  254. }
  255. // If not a 200 status header, send back error.
  256. if ( 200 != $response_code ) {
  257. $type = ! empty( $response_body['type'] ) ? $response_body['type'] : 'api-error';
  258. if ( empty( $response_code ) ) {
  259. return new WP_Error( $type, __( 'The API was unreachable.', 'google-analytics-dashboard-for-wp' ) );
  260. }
  261. if ( empty( $response_body ) || ( empty( $response_body['message'] ) && empty( $response_body['error'] ) ) ) {
  262. // Translators: placeholder adds the response code.
  263. return new WP_Error( $type, sprintf( __( 'The API returned a <strong>%s</strong> response', 'google-analytics-dashboard-for-wp' ), $response_code ) );
  264. }
  265. if ( ! empty( $response_body['message'] ) ) {
  266. // Translators: placeholder adds the response code and response message.
  267. return new WP_Error( $type, sprintf( __( 'The API returned a <strong>%1$d</strong> response with this message: <strong>%2$s</strong>', 'google-analytics-dashboard-for-wp' ), $response_code, stripslashes( $response_body['message'] ) ) );
  268. }
  269. if ( ! empty( $response_body['error'] ) ) {
  270. // Translators: placeholder adds the response code and response message.
  271. return new WP_Error( $type, sprintf( __( 'The API returned a <strong>%1$d</strong> response with this message: <strong>%2$s</strong>', 'google-analytics-dashboard-for-wp' ), $response_code, stripslashes( $response_body['error'] ) ) );
  272. }
  273. }
  274. // If TT required
  275. if ( ! empty( $this->tt ) ) {
  276. if ( empty( $response_body['tt'] ) || ! hash_equals( $this->tt, $response_body['tt'] ) ) {
  277. // TT isn't set on return or doesn't match
  278. return new WP_Error( 'validation-error', sprintf( __( 'Improper API request.', 'google-analytics-dashboard-for-wp' ) ) );
  279. }
  280. }
  281. // Return the json decoded content.
  282. return $response_body;
  283. }
  284. /**
  285. * Sets a class property.
  286. *
  287. * @since 7.0.0
  288. *
  289. * @param string $key The property to set.
  290. * @param string $val The value to set for the property.
  291. * @return mixed $value The response to the API call.
  292. */
  293. public function set( $key, $val ) {
  294. $this->{$key} = $val;
  295. }
  296. /**
  297. * Allow additional data to be passed in the request
  298. *
  299. * @since 7.0.0
  300. *
  301. * @param array $data
  302. * return void
  303. */
  304. public function set_additional_data( array $data ) {
  305. $this->additional_data = array_merge( $this->additional_data, $data );
  306. }
  307. /**
  308. * Checks for SSL for making API requests.
  309. *
  310. * @since 7.0.0
  311. *
  312. * return bool True if SSL is enabled, false otherwise.
  313. */
  314. public function is_ssl() {
  315. // Use the base is_ssl check first.
  316. if ( is_ssl() ) {
  317. return true;
  318. } else if ( isset( $_SERVER['HTTP_X_FORWARDED_PROTO'] ) && 'https' == $_SERVER['HTTP_X_FORWARDED_PROTO'] ) {
  319. // Also catch proxies and load balancers.
  320. return true;
  321. } else if ( defined( 'FORCE_SSL_ADMIN' ) && FORCE_SSL_ADMIN ) {
  322. return true;
  323. }
  324. // Otherwise, return false.
  325. return false;
  326. }
  327. private function is_blocked( $url = '' ) {
  328. global $Airplane_Mode_Core;
  329. if ( defined( 'AIRMDE_VER' ) && ! empty( $Airplane_Mode_Core ) && $Airplane_Mode_Core->enabled() ) {
  330. return new WP_Error( 'api-error', __( 'Reason: The API was unreachable because the Airplane Mode plugin is active.', 'google-analytics-dashboard-for-wp' ) );
  331. }
  332. // The below page is a testing empty content HTML page used for firewall/router login detection
  333. // and for image linking purposes in Google Images. We use it to test outbound connections since it is run on google.com
  334. // 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
  335. // use case of quickly testing outbound connections.
  336. $testurl = ! empty( $this->testurl ) ? $this->testurl :'http://www.google.com/blank.html';
  337. if ( defined( 'WP_HTTP_BLOCK_EXTERNAL' ) && WP_HTTP_BLOCK_EXTERNAL ) {
  338. if ( defined( 'WP_ACCESSIBLE_HOSTS' ) ) {
  339. $wp_http = new WP_Http();
  340. $on_blacklist = $wp_http->block_request( $url );
  341. if ( $on_blacklist ) {
  342. return new WP_Error( 'api-error', __( 'Reason: The API was unreachable because the API url is on the WP HTTP blocklist.', 'google-analytics-dashboard-for-wp' ) );
  343. } else {
  344. $params = array(
  345. 'sslverify' => false,
  346. 'timeout' => 2,
  347. 'user-agent' => 'ExactMetrics/' . EXACTMETRICS_VERSION,
  348. 'body' => ''
  349. );
  350. $response = wp_remote_get( $testurl, $params );
  351. if( ! is_wp_error( $response ) && $response['response']['code'] >= 200 && $response['response']['code'] < 300 ) {
  352. return false;
  353. } else {
  354. if ( is_wp_error( $response ) ) {
  355. return $response;
  356. } else {
  357. return new WP_Error( 'api-error', __( 'Reason: The API was unreachable because the call to Google failed.', 'google-analytics-dashboard-for-wp' ) );
  358. }
  359. }
  360. }
  361. } else {
  362. return new WP_Error( 'api-error', __( 'Reason: The API was unreachable because no external hosts are allowed on this site.', 'google-analytics-dashboard-for-wp' ) );
  363. }
  364. } else {
  365. $params = array(
  366. 'sslverify' => false,
  367. 'timeout' => 2,
  368. 'user-agent' => 'ExactMetrics/' . EXACTMETRICS_VERSION,
  369. 'body' => ''
  370. );
  371. $response = wp_remote_get( $testurl, $params );
  372. if( ! is_wp_error( $response ) && $response['response']['code'] >= 200 && $response['response']['code'] < 300 ) {
  373. return false;
  374. } else {
  375. if ( is_wp_error( $response ) ) {
  376. return $response;
  377. } else {
  378. return new WP_Error( 'api-error', __( 'Reason: The API was unreachable because the call to Google failed.', 'google-analytics-dashboard-for-wp' ) );
  379. }
  380. }
  381. }
  382. }
  383. }