Files
DESKTOP-GBA0BK8\Admin 7c8c8b1c76 first commit
2023-04-08 12:19:53 -04:00

473 lines
14 KiB
PHP

<?php
/*
Copyright 2013-2019 Yellow Tree, Siegen, Germany
Author: Benjamin Pick (wp-geoip-detect| |posteo.de)
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
// This file contains function that are necessary for the plugin, but not deemed as API.
// Their name / parameter may change without warning.
use YellowTree\GeoipDetect\DataSources\DataSourceRegistry;
// This file is outside composer root in order to not distribute all the other symfony files
require_once(__DIR__ . '/lib/vendor/symfony/http-foundation/IpUtils.php');
use Symfony\Component\HttpFoundation\IpUtils;
/**
* Take the parameter options and add the default values.
* @param array $options
* return $options
*/
function _geoip_detect2_process_options($options) {
// For backwards compat 2.4.0-2.5.0
if (is_bool($options)) {
_doing_it_wrong('GeoIP Detection Plugin: geoip_detect2_get_info_from_ip()', '$skipCache has been renamed to $options. Instead of TRUE, now use "[\'skipCache\' => TRUE]".', '2.5.0');
$value = $options;
$options = array();
$options['skipCache'] = $value;
}
// Check if source exists
if (isset($options['source'])) {
$registry = DataSourceRegistry::getInstance();
if (!$registry->sourceExists($options['source']))
unset($options['source']);
}
/**
* Filter: geoip_detect2_options
* You can programmatically change the defaults etc.
*
* @param array $options The options array
*/
$options = apply_filters('geoip_detect2_options', $options);
$defaultOptions = array(
'skipCache' => false,
'source' => get_option('geoip-detect-source', DataSourceRegistry::DEFAULT_SOURCE),
);
$options = $options + $defaultOptions;
return $options;
}
/*
* Get the Maxmind Reader
* (Use this if you want to use other methods than "city" or otherwise customize behavior.)
*
* @param array(string) List of locale codes to use in name property
* from most preferred to least preferred. (Default: Site language, en)
* @param boolean If locale filter should be skipped (default: No)
* @return GeoIp2\Database\Reader The reader, ready to do its work. Don't forget to `close()` it afterwards. NULL if file not found (or other problems).
* NULL if initialization went wrong (e.g., File not found.)
*/
function _geoip_detect2_get_reader($locales = null, $skipLocaleFilter = false, &$sourceId = '', $options = array()) {
if (! $skipLocaleFilter) {
/**
* Filter: geoip_detect2_locales
*
* @param array(string) $locales
* Current locales.
*/
$locales = apply_filters ( 'geoip_detect2_locales', $locales );
}
$reader = null;
$source = DataSourceRegistry::getInstance()->getSource($options['source']);
if ($source) {
$reader = $source->getReader($locales, $options);
$sourceId = $source->getId();
}
/**
* Filter: geoip_detect2_reader
* You can customize your reader here.
* This filter will be called for every IP request.
*
* @param
* GeoIp2\Database\ProviderInterface Reader (by default: GeoLite City)
* @param
* array(string) Locale precedence
*/
$reader = apply_filters('geoip_detect2_reader', $reader, $locales );
return $reader;
}
function _ip_to_s($ip) {
$binary = '';
try {
$binary = @inet_pton($ip);
} catch (\Throwable $e) { }
if (empty($binary))
return '';
return base64_encode($binary);
}
function _geoip_detect2_get_data_from_cache($ip, $source) {
if (!DataSourceRegistry::getInstance()->isSourceCachable($source)) {
return null;
}
$ip_s = _ip_to_s($ip);
if (!$ip_s) {
return null;
}
$data = get_transient('geoip_detect_c_' . $source . '_' . $ip_s);
return $data;
}
function _geoip_detect2_add_data_to_cache($data, $ip) {
$source = $data['extra']['source'];
if (!DataSourceRegistry::getInstance()->isSourceCachable($source)) {
return null;
}
if (GEOIP_DETECT_READER_CACHE_TIME === 0) {
// Caching is disabled
return null;
}
$data['extra']['cached'] = time();
unset($data['maxmind']['queries_remaining']);
$ip_s = _ip_to_s($ip);
// Do not cache invalid IPs
if (!$ip_s) {
return;
}
// Do not cache error lookups (they might be temporary)
if (!empty($data['extra']['error'])) {
return;
}
set_transient('geoip_detect_c_' . $source . '_' . $ip_s, $data, GEOIP_DETECT_READER_CACHE_TIME);
}
function _geoip_detect2_get_record_from_reader($reader, $ip, &$error) {
$record = null;
$ip = trim($ip);
if ($reader) {
// When plugin installed on development boxes:
// If the client IP is not a public IP, use the public IP of the server instead.
// Of course this only works if the internet can be accessed.
if ($ip == 'me' || (geoip_detect_is_ip($ip) && !geoip_detect_is_public_ip($ip))) {
$ip = geoip_detect2_get_external_ip_adress();
}
try {
try {
$record = $reader->city($ip);
} catch (\BadMethodCallException $e) {
$record = $reader->country($ip);
}
} catch(\Exception $e) {
$error = 'Lookup Error: ' . $e->getMessage();
}
$reader->close();
} else {
$error = 'No reader was found. Check if the configuration is complete and correct.';
}
return $record;
}
function _geoip_detect2_record_enrich_data($record, $ip, $sourceId, $error) {
$data = array('traits' => array('ip_address' => $ip), 'is_empty' => true);
if (is_object($record) && method_exists($record, 'jsonSerialize')) {
$data = $record->jsonSerialize();
$data['is_empty'] = false;
}
$data['extra']['source'] = $sourceId;
$data['extra']['cached'] = 0;
if ($error || !isset($data['extra']['error'])) {
$data['extra']['error'] = $error;
}
/**
* Filter: geoip_detect2_record_data
* After loading the information from the GeoIP-Database, you can add information to it.
*
* @param array $data Information found.
* @param string $orig_ip IP that originally passed to the function.
* @return array
*/
$data = apply_filters('geoip_detect2_record_data', $data, $ip);
return $data;
}
/**
* GeoIPv2 doesn't always include a timezone when v1 did.
* Region ids have changed, so countries with several time zones are out of luck.
*
* @param array $record
*/
function _geoip_detect2_try_to_fix_timezone($data) {
if (!empty($data['location']['time_zone']))
return $data;
if (!function_exists('_geoip_detect_get_time_zone')) {
require_once(GEOIP_PLUGIN_DIR . '/vendor/timezone.php');
}
if (!empty($data['country']['iso_code'])) {
$data['location']['time_zone'] = _geoip_detect_get_time_zone($data['country']['iso_code'], isset($data['subdivisions'][0]['iso_code']) ? $data['subdivisions'][0]['iso_code'] : null);
} else {
$data['location']['time_zone'] = null;
}
return $data;
}
add_filter('geoip_detect2_record_data', '_geoip_detect2_try_to_fix_timezone');
/**
* Add country name, if not known yet
*/
function _geoip_detect2_add_geonames_data($data) {
static $countryInfo = null;
if (is_null($countryInfo))
$countryInfo = new \YellowTree\GeoipDetect\Geonames\CountryInformation;
if (!empty($data['country']['iso_code'])) {
$geonamesData = $countryInfo->getInformationAboutCountry($data['country']['iso_code']);
$data = array_replace_recursive($geonamesData, $data);
$emoji = $countryInfo->getFlagEmoji($data['country']['iso_code']);
if ($emoji && empty($data['extra']['flag'])) {
$data['extra']['flag'] = $emoji;
}
$tel = $countryInfo->getTelephonePrefix($data['country']['iso_code']);
if ($tel && empty($data['extra']['tel'])) {
$data['extra']['tel'] = $tel;
}
}
return $data;
}
add_filter('geoip_detect2_record_data', '_geoip_detect2_add_geonames_data');
/**
* IPv6-Adresses can be written in different formats. Make sure they are standardized.
* For IPv4-Adresses, spaces are removed.
*/
function geoip_detect_normalize_ip($ip) {
$ip = trim($ip);
$binary = '';
try {
$binary = @inet_pton($ip);
} catch (\Throwable $e) { }
if (empty($binary))
return $ip; // Probably an IPv6 adress & IPv6 is not supported. Or not a valid IP.
$ip = inet_ntop($binary);
return $ip;
}
/**
* Check if the expected IP left matches the actual IP
* @param string $actual IP
* @param string|array $expected IP (can include subnet)
* @return boolean
*/
function geoip_detect_is_ip_equal($actual, $expected) {
try {
return IpUtils::checkIp($actual, $expected);
} catch(Exception $e) {
if (WP_DEBUG) {
throw $e;
}
return false;
}
}
function geoip_detect_is_ip($ip, $noIpv6 = false) {
$flags = FILTER_FLAG_IPV4;
if (GEOIP_DETECT_IPV6_SUPPORTED && !$noIpv6)
$flags = $flags | FILTER_FLAG_IPV6;
return filter_var($ip, FILTER_VALIDATE_IP, $flags) !== false;
}
function geoip_detect_is_ip_in_range($ip, $range_start, $range_end) {
$long_ip = ip2long($ip);
if ($long_ip === false) // Not IPv4
return false;
if($long_ip >= ip2long($range_start) && $long_ip <= ip2long($range_end))
return true;
return false;
}
/**
* Check if IP is not in RFC private IP range
* (for local development)
* @param string $ip IP (IPv4 or IPv6)
* @return boolean TRUE if private
*/
function geoip_detect_is_public_ip($ip) {
// filver_var only detects 127.0.0.1 as local ...
if (geoip_detect_is_ip_equal($ip, '127.0.0.0/8'))
return false;
if (trim($ip) === '0.0.0.0')
return false;
$flags = FILTER_FLAG_IPV4 // IP can be v4 or v6
| FILTER_FLAG_NO_PRIV_RANGE // It may not be in the RFC private range
| FILTER_FLAG_NO_RES_RANGE; // It may not be in the RFC reserved range
if (GEOIP_DETECT_IPV6_SUPPORTED)
$flags = $flags | FILTER_FLAG_IPV6;
$is_public = filter_var($ip, FILTER_VALIDATE_IP, $flags) !== false;
return $is_public;
}
function _geoip_detect2_get_external_ip_services($nb = 3, $needsCORS = false) {
$ipservicesThatAllowCORS = array(
'http://ipv4.icanhazip.com',
'http://v4.ident.me',
);
$ipservicesWithoutCORS = array(
'http://ipecho.net/plain',
'http://bot.whatismyipaddress.com',
);
$ipservices = $ipservicesThatAllowCORS;
if (!$needsCORS) {
$ipservices = array_merge($ipservices, $ipservicesWithoutCORS);
}
// Randomizing to avoid querying the same service each time
shuffle($ipservices);
$ipservices = apply_filters('geiop_detect_ipservices', $ipservices);
$ipservices = array_slice($ipservices, 0, $nb);
return $ipservices;
}
/**
* Sometimes we can only see an local IP adress (local development environment.)
* In this case we need to ask an internet server which IP adress our internet connection has.
* (This function is not cached. Some providers may throttle our requests, that's why caching is enabled by default.)
*
* @return string The detected IPv4 Adress. If none is found, '0.0.0.0' is returned instead.
*/
function _geoip_detect_get_external_ip_adress_without_cache()
{
$ipservices = _geoip_detect2_get_external_ip_services();
foreach ($ipservices as $url)
{
$ret = wp_remote_get($url, array('timeout' => defined('WP_TESTS_TITLE') ? 3 : 1));
if (is_wp_error($ret)) {
if (WP_DEBUG || defined('WP_TESTS_TITLE')) {
trigger_error('_geoip_detect_get_external_ip_adress_without_cache(): Curl error (' . $url . '): ' . $ret->get_error_message(), E_USER_NOTICE);
}
} else if (isset($ret['response']['code']) && $ret['response']['code'] != 200) {
if (WP_DEBUG || defined('WP_TESTS_TITLE')) {
trigger_error('_geoip_detect_get_external_ip_adress_without_cache(): HTTP error (' . $url . '): Returned code ' . $ret['response']['code'], E_USER_NOTICE);
}
} else {
if (isset($ret['body'])) {
$ip = trim($ret['body']);
if (geoip_detect_is_ip($ip))
return $ip;
}
if (WP_DEBUG || defined('WP_TESTS_TITLE')) {
trigger_error('_geoip_detect_get_external_ip_adress_without_cache(): HTTP error (' . $url . '): Did not return an IP: ' . $ret['body'], E_USER_NOTICE);
}
}
}
return '0.0.0.0';
}
// @see https://stackoverflow.com/questions/2637945/getting-relative-path-from-absolute-path-in-php
function geoip_detect_get_relative_path($from, $to)
{
// some compatibility fixes for Windows paths
$from = is_dir($from) ? rtrim($from, '\/') . '/' : $from;
$to = is_dir($to) ? rtrim($to, '\/') . '/' : $to;
$from = str_replace('\\', '/', $from);
$to = str_replace('\\', '/', $to);
$from = explode('/', $from);
$to = explode('/', $to);
$relPath = $to;
foreach($from as $depth => $dir) {
// find first non-matching dir
if($dir === $to[$depth]) {
// ignore this directory
array_shift($relPath);
} else {
// get number of remaining dirs to $from
$remaining = count($from) - $depth;
if($remaining > 1) {
// add traversals up to first matching dir
$padLength = (count($relPath) + $remaining - 1) * -1;
$relPath = array_pad($relPath, $padLength, '..');
break;
} else {
$relPath[0] = $relPath[0];
}
}
}
return implode('/', $relPath);
}
function _geoip_maybe_disable_pagecache() {
if (!get_option('geoip-detect-disable_pagecache'))
return false;
// WP Super Cache, W3 Total Cache
if (!defined('DONOTCACHEPAGE'))
define('DONOTCACHEPAGE', true);
if (!defined('DONOTCACHEOBJECT'))
define('DONOTCACHEOBJECT', true);
if (!defined('DONOTCACHEDB'))
define('DONOTCACHEDB', true);
if (!headers_sent()) {
header('Cache-Control: private, proxy-revalidate, s-maxage=0');
}
return true;
}
function _geoip_dashes_to_camel_case($string, $capitalizeFirstCharacter = false) {
$str = str_replace(' ', '', ucwords(str_replace('_', ' ', $string)));
if (!$capitalizeFirstCharacter) {
$str = lcfirst($str);
}
return $str;
}