<?php defined('ABSPATH') || exit; class WC_Geolocation { const GEOLITE_DB = 'http://geolite.maxmind.com/download/geoip/database/GeoLiteCountry/GeoIP.dat.gz'; const GEOLITE_IPV6_DB = 'http://geolite.maxmind.com/download/geoip/database/GeoIPv6.dat.gz'; const GEOLITE2_DB = 'http://geolite.maxmind.com/download/geoip/database/GeoLite2-Country.tar.gz'; private static $ip_lookup_apis = array('ipify' => 'http://api.ipify.org/', 'ipecho' => 'http://ipecho.net/plain', 'ident' => 'http://ident.me', 'whatismyipaddress' => 'http://bot.whatismyipaddress.com'); private static $geoip_apis = array('ipinfo.io' => 'https://ipinfo.io/%s/json', 'ip-api.com' => 'http://ip-api.com/json/%s'); private static function is_geolocation_enabled($current_settings) { return in_array($current_settings, array('geolocation', 'geolocation_ajax'), true); } public static function get_ip_address() { if (isset($_SERVER['HTTP_X_REAL_IP'])) { return sanitize_text_field(wp_unslash($_SERVER['HTTP_X_REAL_IP'])); } elseif (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) { return (string) rest_is_ip_address(trim(current(preg_split('/,/', sanitize_text_field(wp_unslash($_SERVER['HTTP_X_FORWARDED_FOR'])))))); } elseif (isset($_SERVER['REMOTE_ADDR'])) { return sanitize_text_field(wp_unslash($_SERVER['REMOTE_ADDR'])); } return ''; } public static function get_external_ip_address() { $external_ip_address = '0.0.0.0'; if ('' !== self::get_ip_address()) { $transient_name = 'external_ip_address_' . self::get_ip_address(); $external_ip_address = get_transient($transient_name); } if (false === $external_ip_address) { $external_ip_address = '0.0.0.0'; $ip_lookup_services = apply_filters('woocommerce_geolocation_ip_lookup_apis', self::$ip_lookup_apis); $ip_lookup_services_keys = array_keys($ip_lookup_services); shuffle($ip_lookup_services_keys); foreach ($ip_lookup_services_keys as $service_name) { $service_endpoint = $ip_lookup_services[$service_name]; $response = wp_safe_remote_get($service_endpoint, array('timeout' => 2)); if (!is_wp_error($response) && rest_is_ip_address($response['body'])) { $external_ip_address = apply_filters('woocommerce_geolocation_ip_lookup_api_response', wc_clean($response['body']), $service_name); break; } } set_transient($transient_name, $external_ip_address, DAY_IN_SECONDS); } return $external_ip_address; } public static function geolocate_ip($ip_address = '', $fallback = false, $api_fallback = true) { $country_code = apply_filters('woocommerce_geolocate_ip', false, $ip_address, $fallback, $api_fallback); if (false !== $country_code) { return array('country' => $country_code, 'state' => '', 'city' => '', 'postcode' => ''); } if (empty($ip_address)) { $ip_address = self::get_ip_address(); } $country_code = self::get_country_code_from_headers(); $geolocation = apply_filters('woocommerce_get_geolocation', array('country' => $country_code, 'state' => '', 'city' => '', 'postcode' => ''), $ip_address); if ('' === $geolocation['country'] && $api_fallback) { $geolocation['country'] = self::geolocate_via_api($ip_address); } if ('' === $geolocation['country'] && $fallback) { $external_ip_address = self::get_external_ip_address(); if ('0.0.0.0' !== $external_ip_address && $external_ip_address !== $ip_address) { return self::geolocate_ip($external_ip_address, false, $api_fallback); } } return array('country' => $geolocation['country'], 'state' => $geolocation['state'], 'city' => $geolocation['city'], 'postcode' => $geolocation['postcode']); } public static function get_local_database_path($deprecated = '2') { wc_deprecated_function('WC_Geolocation::get_local_database_path', '3.9.0'); $integration = wc()->integrations->get_integration('maxmind_geolocation'); return $integration->get_database_service()->get_database_path(); } public static function update_database() { wc_deprecated_function('WC_Geolocation::update_database', '3.9.0'); $integration = wc()->integrations->get_integration('maxmind_geolocation'); $integration->update_database(); } private static function get_country_code_from_headers() { $country_code = ''; $headers = array('MM_COUNTRY_CODE', 'GEOIP_COUNTRY_CODE', 'HTTP_CF_IPCOUNTRY', 'HTTP_X_COUNTRY_CODE'); foreach ($headers as $header) { if (empty($_SERVER[$header])) { continue; } $country_code = strtoupper(sanitize_text_field(wp_unslash($_SERVER[$header]))); break; } return $country_code; } private static function geolocate_via_api($ip_address) { $country_code = get_transient('geoip_' . $ip_address); if (false === $country_code) { $geoip_services = apply_filters('woocommerce_geolocation_geoip_apis', self::$geoip_apis); if (empty($geoip_services)) { return ''; } $geoip_services_keys = array_keys($geoip_services); shuffle($geoip_services_keys); foreach ($geoip_services_keys as $service_name) { $service_endpoint = $geoip_services[$service_name]; $response = wp_safe_remote_get(sprintf($service_endpoint, $ip_address), array('timeout' => 2)); if (!is_wp_error($response) && $response['body']) { switch ($service_name) { case 'ipinfo.io': $data = json_decode($response['body']); $country_code = isset($data->country) ? $data->country : ''; break; case 'ip-api.com': $data = json_decode($response['body']); $country_code = isset($data->countryCode) ? $data->countryCode : ''; break; default: $country_code = apply_filters('woocommerce_geolocation_geoip_response_' . $service_name, '', $response['body']); break; } $country_code = sanitize_text_field(strtoupper($country_code)); if ($country_code) { break; } } } set_transient('geoip_' . $ip_address, $country_code, DAY_IN_SECONDS); } return $country_code; } public static function init() { wc_deprecated_function('WC_Geolocation::init', '3.9.0'); return null; } public static function disable_geolocation_on_legacy_php($default_customer_address) { wc_deprecated_function('WC_Geolocation::disable_geolocation_on_legacy_php', '3.9.0'); if (self::is_geolocation_enabled($default_customer_address)) { $default_customer_address = 'base'; } return $default_customer_address; } public static function maybe_update_database($new_value, $old_value) { wc_deprecated_function('WC_Geolocation::maybe_update_database', '3.9.0'); if ($new_value !== $old_value && self::is_geolocation_enabled($new_value)) { self::update_database(); } return $new_value; } }